diff --git a/packages/components/demo/App.tsx b/packages/components/demo/App.tsx
index 4105088b..4e446963 100644
--- a/packages/components/demo/App.tsx
+++ b/packages/components/demo/App.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { Page } from '../src/index.js'
-import { Source, createHttpFileSystem, getSource } from '../src/index.ts'
+import { getHttpSource } from '../src/index.ts'
export interface Navigation {
col?: number
row?: number
@@ -15,10 +15,6 @@ function getNumberParam(search: URLSearchParams, key: string): number | undefine
return number
}
-const fileSystems = [
- createHttpFileSystem(),
-]
-
export default function App() {
const search = new URLSearchParams(location.search)
const url = search.get('url')
@@ -31,20 +27,13 @@ export default function App() {
const row = getNumberParam(search, 'row')
const col = getNumberParam(search, 'col')
- let source: Source | undefined = undefined
- for (const fileSystem of fileSystems) {
- const fsSource = getSource(url, fileSystem)
- if (fsSource){
- source = fsSource
- break
- }
- }
+ const source = getHttpSource(url)
if (!source) {
return
Could not load a data source. You have to pass a valid source in the url, eg:
{defaultUrl}.
}
return `/?url=${sourceId}`,
getCellRouteUrl: ({ sourceId, col, row }) => `/?url=${sourceId}&col=${col}&row=${row}`,
diff --git a/packages/components/src/components/Breadcrumb.tsx b/packages/components/src/components/Breadcrumb.tsx
index ee39ec13..748f1235 100644
--- a/packages/components/src/components/Breadcrumb.tsx
+++ b/packages/components/src/components/Breadcrumb.tsx
@@ -1,5 +1,5 @@
import { RoutesConfig } from '../lib/routes.js'
-import { Source } from '../lib/source.js'
+import { Source } from '../lib/sources/types.js'
export type BreadcrumbConfig = RoutesConfig
interface BreadcrumbProps {
diff --git a/packages/components/src/components/Cell.tsx b/packages/components/src/components/Cell.tsx
index 3d15a1ef..f7aa573b 100644
--- a/packages/components/src/components/Cell.tsx
+++ b/packages/components/src/components/Cell.tsx
@@ -1,7 +1,7 @@
import { asyncRows } from 'hightable'
import { asyncBufferFromUrl, parquetMetadataAsync } from 'hyparquet'
import { useEffect, useState } from 'react'
-import { FileSource } from '../lib/source.js'
+import { FileSource } from '../lib/sources/types.js'
import { parquetDataFrame } from '../lib/tableProvider.js'
import Breadcrumb, { BreadcrumbConfig } from './Breadcrumb.js'
import Layout from './Layout.js'
diff --git a/packages/components/src/components/File.tsx b/packages/components/src/components/File.tsx
index 24248fd6..743b5cc6 100644
--- a/packages/components/src/components/File.tsx
+++ b/packages/components/src/components/File.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react'
-import { FileSource } from '../lib/source.js'
+import { FileSource } from '../lib/sources/types.js'
import Breadcrumb, { BreadcrumbConfig } from './Breadcrumb.js'
import Layout from './Layout.js'
import Viewer, { ViewerConfig } from './viewers/Viewer.js'
diff --git a/packages/components/src/components/Folder.tsx b/packages/components/src/components/Folder.tsx
index b05b7218..bec5f9b1 100644
--- a/packages/components/src/components/Folder.tsx
+++ b/packages/components/src/components/Folder.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
-import type { DirSource, FileMetadata } from '../lib/source.js'
+import type { DirSource, FileMetadata } from '../lib/sources/types.js'
import { cn, formatFileSize, getFileDate, getFileDateShort } from '../lib/utils.js'
import Breadcrumb, { BreadcrumbConfig } from './Breadcrumb.js'
import Layout, { Spinner } from './Layout.js'
diff --git a/packages/components/src/components/Page.tsx b/packages/components/src/components/Page.tsx
index 3c80de83..37d76a1a 100644
--- a/packages/components/src/components/Page.tsx
+++ b/packages/components/src/components/Page.tsx
@@ -1,4 +1,4 @@
-import { Source } from '../lib/source.js'
+import { Source } from '../lib/sources/types.js'
import { BreadcrumbConfig } from './Breadcrumb.js'
import Cell from './Cell.js'
import File, { FileConfig } from './File.js'
diff --git a/packages/components/src/components/index.ts b/packages/components/src/components/index.ts
index b3464170..567ef046 100644
--- a/packages/components/src/components/index.ts
+++ b/packages/components/src/components/index.ts
@@ -2,9 +2,9 @@ import Breadcrumb, { BreadcrumbConfig } from './Breadcrumb.js'
import Cell, { CellConfig } from './Cell.js'
import File, { FileConfig } from './File.js'
import Folder, { FolderConfig } from './Folder.js'
-import Layout from './Layout.js'
+import Layout, { Spinner } from './Layout.js'
import Markdown from './Markdown.js'
import Page, { PageConfig } from './Page.js'
export * from './viewers/index.js'
-export { Breadcrumb, Cell, File, Folder, Layout, Markdown, Page }
+export { Breadcrumb, Cell, File, Folder, Layout, Markdown, Page, Spinner }
export type { BreadcrumbConfig, CellConfig, FileConfig, FolderConfig, PageConfig }
diff --git a/packages/components/src/components/viewers/ImageView.tsx b/packages/components/src/components/viewers/ImageView.tsx
index 3cb3a583..20063d04 100644
--- a/packages/components/src/components/viewers/ImageView.tsx
+++ b/packages/components/src/components/viewers/ImageView.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
-import { FileSource } from '../../lib/source.js'
+import { FileSource } from '../../lib/sources/types.js'
import { contentTypes, parseFileSize } from '../../lib/utils.js'
import { Spinner } from '../Layout.js'
import ContentHeader from './ContentHeader.js'
diff --git a/packages/components/src/components/viewers/MarkdownView.tsx b/packages/components/src/components/viewers/MarkdownView.tsx
index 17c1acd1..ac98e0fe 100644
--- a/packages/components/src/components/viewers/MarkdownView.tsx
+++ b/packages/components/src/components/viewers/MarkdownView.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
-import { FileSource } from '../../lib/source.js'
+import { FileSource } from '../../lib/sources/types.js'
import { parseFileSize } from '../../lib/utils.js'
import { Spinner } from '../Layout.js'
import Markdown from '../Markdown.js'
diff --git a/packages/components/src/components/viewers/ParquetView.tsx b/packages/components/src/components/viewers/ParquetView.tsx
index d8906fb6..b37b43e2 100644
--- a/packages/components/src/components/viewers/ParquetView.tsx
+++ b/packages/components/src/components/viewers/ParquetView.tsx
@@ -2,7 +2,7 @@ import HighTable, { DataFrame, rowCache } from 'hightable'
import { asyncBufferFromUrl, parquetMetadataAsync } from 'hyparquet'
import React, { useCallback, useEffect, useState } from 'react'
import { RoutesConfig, appendSearchParams } from '../../lib/routes.js'
-import { FileSource } from '../../lib/source.js'
+import { FileSource } from '../../lib/sources/types.js'
import { parquetDataFrame } from '../../lib/tableProvider.js'
import { Spinner } from '../Layout.js'
import CellPanel from './CellPanel.js'
diff --git a/packages/components/src/components/viewers/TextView.tsx b/packages/components/src/components/viewers/TextView.tsx
index 066486de..d6805073 100644
--- a/packages/components/src/components/viewers/TextView.tsx
+++ b/packages/components/src/components/viewers/TextView.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
-import { FileSource } from '../../lib/source.js'
+import { FileSource } from '../../lib/sources/types.js'
import { parseFileSize } from '../../lib/utils.js'
import { Spinner } from '../Layout.js'
import ContentHeader, { TextContent } from './ContentHeader.js'
diff --git a/packages/components/src/components/viewers/Viewer.tsx b/packages/components/src/components/viewers/Viewer.tsx
index 9bf34abf..82020680 100644
--- a/packages/components/src/components/viewers/Viewer.tsx
+++ b/packages/components/src/components/viewers/Viewer.tsx
@@ -1,4 +1,4 @@
-import { FileSource } from '../../lib/source.js'
+import { FileSource } from '../../lib/sources/types.js'
import { imageTypes } from '../../lib/utils.js'
import ImageView from './ImageView.js'
import MarkdownView from './MarkdownView.js'
diff --git a/packages/components/src/lib/filesystem.ts b/packages/components/src/lib/filesystem.ts
deleted file mode 100644
index f16feae9..00000000
--- a/packages/components/src/lib/filesystem.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import type { DirSource, FileKind, FileMetadata, FileSource, SourcePart } from './source.js'
-import { getFileName } from './utils.js'
-
-export interface FileSystem {
- fsId: string
- canParse: (sourceId: string) => boolean
- getKind: (sourceId: string) => FileKind
- getFileName: (sourceId: string) => string
- getPrefix: (sourceId: string) => string
- getResolveUrl: (sourceId: string) => string
- getSourceParts: (sourceId: string) => SourcePart[]
- listFiles: (prefix: string, options?: {requestInit?: RequestInit}) => Promise
- getSource?: (sourceId: string, options?: {requestInit?: RequestInit}) => FileSource | DirSource
-}
-
-export function getSource(sourceId: string, fs: FileSystem, options?: {requestInit?: RequestInit}): FileSource | DirSource | undefined {
- if (fs.getSource) {
- /// if the file system provides an optimized getSource method, use it
- return fs.getSource(sourceId, options)
- }
- if (!fs.canParse(sourceId)) {
- return undefined
- }
- const sourceParts = fs.getSourceParts(sourceId)
- if (fs.getKind(sourceId) === 'file') {
- return {
- kind: 'file',
- sourceId,
- sourceParts,
- fileName: fs.getFileName(sourceId),
- resolveUrl: fs.getResolveUrl(sourceId),
- requestInit: options?.requestInit,
- }
- } else {
- const prefix = fs.getPrefix(sourceId)
- return {
- kind: 'directory',
- sourceId,
- sourceParts,
- prefix,
- listFiles: () => fs.listFiles(prefix, { requestInit: options?.requestInit }),
- }
- }
-}
-
-function notImplemented(): never {
- throw new Error('Not implemented')
-}
-
-// Built-in implementations
-export function createHttpFileSystem(): FileSystem {
- return {
- fsId: 'http' as const,
- canParse: sourceId => URL.canParse(sourceId),
- getKind: () => 'file', /// all the URLs are considered files
- getFileName,
- getPrefix: notImplemented,
- getResolveUrl: sourceId => sourceId,
- getSourceParts: sourceId => [{ text: sourceId, sourceId }],
- listFiles: notImplemented,
- }
-}
-
-export interface HyperparamFileMetadata {
- key: string
- eTag?: string
- fileSize?: number
- lastModified: string
-}
-async function fetchHyperparamFilesList(prefix: string, endpoint: string, options?: {requestInit?: RequestInit}): Promise {
- const url = new URL('/api/store/list', endpoint)
- url.searchParams.append('prefix', prefix)
- const res = await fetch(url, options?.requestInit)
- if (res.ok) {
- return await res.json() as HyperparamFileMetadata[]
- } else {
- throw new Error(await res.text())
- }
-}
-export function createHyperparamFileSystem({ endpoint }: {endpoint: string}): FileSystem {
- if (!URL.canParse(endpoint)) {
- throw new Error('Invalid endpoint')
- }
- function getKind(sourceId: string): FileKind {
- return sourceId === '' || sourceId.endsWith('/') ? 'directory' : 'file'
- }
- return {
- fsId: 'hyperparam' as const,
- canParse: (sourceId: string): boolean => {
- /// we expect relative paths, such as path/to/file or path/to/dir/
- /// let's just check that it is empty or starts with a "word" character
- return sourceId === '' || /^[\w]/.test(sourceId)
- },
- getKind,
- getFileName,
- getPrefix: sourceId => sourceId.replace(/\/$/, ''),
- getResolveUrl: (sourceId: string): string => {
- const url = new URL('/api/store/get', endpoint)
- url.searchParams.append('key', sourceId)
- return url.toString()
- },
- getSourceParts: (sourceId: string): SourcePart[] => {
- const parts = sourceId.split('/')
- return [
- { 'text': '/', 'sourceId': '' },
- ...parts.map((part, depth) => {
- const slashSuffix = depth === parts.length - 1 ? '' : '/'
- return {
- text: part + slashSuffix,
- sourceId: parts.slice(0, depth + 1).join('/') + slashSuffix,
- }
- }),
- ]
- },
- listFiles: async (prefix, options): Promise => {
- const files = await fetchHyperparamFilesList(prefix, endpoint, options)
- return files.map(file => ({
- name: file.key,
- eTag: file.eTag,
- size: file.fileSize,
- lastModified: file.lastModified,
- sourceId: (prefix === '' ? '' : prefix + '/') + file.key,
- kind: getKind(file.key),
- }))
- },
- }
-}
diff --git a/packages/components/src/lib/index.ts b/packages/components/src/lib/index.ts
index ca373038..13bfa866 100644
--- a/packages/components/src/lib/index.ts
+++ b/packages/components/src/lib/index.ts
@@ -1,9 +1,7 @@
-export { createHttpFileSystem, createHyperparamFileSystem, getSource } from './filesystem.js'
-export type { FileSystem } from './filesystem.js'
export { appendSearchParams, replaceSearchParams } from './routes.js'
export type { RoutesConfig } from './routes.js'
-export type { DirSource, FileKind, FileMetadata, FileSource, Source, SourcePart } from './source.js'
+export * from './sources/index.js'
export { parquetDataFrame } from './tableProvider.js'
-export { asyncBufferFrom, cn, contentTypes, formatFileSize, getFileDate, getFileDateShort, getFileName, imageTypes, parseFileSize } from './utils.js'
+export { asyncBufferFrom, cn, contentTypes, formatFileSize, getFileDate, getFileDateShort, imageTypes, parseFileSize } from './utils.js'
export { parquetQueryWorker } from './workers/parquetWorkerClient.js'
export type { AsyncBufferFrom, Row } from './workers/types.js'
diff --git a/packages/components/src/lib/sources/httpSource.ts b/packages/components/src/lib/sources/httpSource.ts
new file mode 100644
index 00000000..a80d9bb3
--- /dev/null
+++ b/packages/components/src/lib/sources/httpSource.ts
@@ -0,0 +1,16 @@
+import { FileSource } from './types.js'
+import { getFileName } from './utils.js'
+
+export function getHttpSource(sourceId: string, options?: {requestInit?: RequestInit}): FileSource | undefined {
+ if (!URL.canParse(sourceId)) {
+ return undefined
+ }
+ return {
+ kind: 'file',
+ sourceId,
+ sourceParts: [{ text: sourceId, sourceId }],
+ fileName: getFileName(sourceId),
+ resolveUrl: sourceId,
+ requestInit: options?.requestInit,
+ }
+}
diff --git a/packages/components/src/lib/sources/hyperparamSource.ts b/packages/components/src/lib/sources/hyperparamSource.ts
new file mode 100644
index 00000000..eb833fab
--- /dev/null
+++ b/packages/components/src/lib/sources/hyperparamSource.ts
@@ -0,0 +1,93 @@
+import type { DirSource, FileKind, FileMetadata, FileSource, SourcePart } from './types.js'
+import { getFileName } from './utils.js'
+
+export interface HyperparamFileMetadata {
+ key: string
+ eTag?: string
+ fileSize?: number
+ lastModified: string
+}
+
+function canParse(sourceId: string): boolean {
+ /// we expect relative paths, such as path/to/file or path/to/dir/
+ /// let's just check that it is empty or starts with a "word" character
+ return sourceId === '' || /^[\w]/.test(sourceId)
+}
+
+function getSourceParts(sourceId: string): SourcePart[] {
+ const parts = sourceId.split('/')
+ return [
+ { 'text': '/', 'sourceId': '' },
+ ...parts.map((part, depth) => {
+ const slashSuffix = depth === parts.length - 1 ? '' : '/'
+ return {
+ text: part + slashSuffix,
+ sourceId: parts.slice(0, depth + 1).join('/') + slashSuffix,
+ }
+ }),
+ ]
+}
+
+function getKind(sourceId: string): FileKind {
+ return sourceId === '' || sourceId.endsWith('/') ? 'directory' : 'file'
+}
+
+function getResolveUrl(sourceId: string, { endpoint }: {endpoint: string}): string {
+ const url = new URL('/api/store/get', endpoint)
+ url.searchParams.append('key', sourceId)
+ return url.toString()
+}
+
+function getPrefix(sourceId: string): string {
+ return sourceId.replace(/\/$/, '')
+}
+
+async function listFiles(prefix: string, { endpoint, requestInit }: {endpoint: string, requestInit?: RequestInit}): Promise {
+ const url = new URL('/api/store/list', endpoint)
+ url.searchParams.append('prefix', prefix)
+ const res = await fetch(url, requestInit)
+ if (res.ok) {
+ const files = await res.json() as HyperparamFileMetadata[]
+ return files.map(file => ({
+ name: file.key,
+ eTag: file.eTag,
+ size: file.fileSize,
+ lastModified: file.lastModified,
+ sourceId: (prefix === '' ? '' : prefix + '/') + file.key,
+ kind: getKind(file.key),
+ }))
+ } else {
+ throw new Error(await res.text())
+ }
+}
+
+export function getHyperparamSource(sourceId: string, { endpoint, requestInit }: {endpoint: string, requestInit?: RequestInit}): FileSource | DirSource | undefined {
+ if (!URL.canParse(endpoint)) {
+ throw new Error('Invalid endpoint')
+ }
+ if (!canParse(sourceId)) {
+ return undefined
+ }
+ const sourceParts = getSourceParts(sourceId)
+ if (getKind(sourceId) === 'file') {
+ return {
+ kind: 'file',
+ sourceId,
+ sourceParts,
+ fileName: getFileName(sourceId),
+ resolveUrl: getResolveUrl(sourceId, { endpoint }),
+ requestInit,
+ }
+ } else {
+ const prefix = getPrefix(sourceId)
+ return {
+ kind: 'directory',
+ sourceId,
+ sourceParts,
+ prefix,
+ listFiles: () => listFiles(prefix, { endpoint, requestInit }),
+ }
+ }
+}
+
+
diff --git a/packages/components/src/lib/sources/index.ts b/packages/components/src/lib/sources/index.ts
new file mode 100644
index 00000000..52437851
--- /dev/null
+++ b/packages/components/src/lib/sources/index.ts
@@ -0,0 +1,5 @@
+export { getHttpSource } from './httpSource.js'
+export { getHyperparamSource } from './hyperparamSource.js'
+export type { HyperparamFileMetadata } from './hyperparamSource.js'
+export type { DirSource, FileKind, FileMetadata, FileSource, Source, SourcePart } from './types.js'
+export { getFileName } from './utils.js'
diff --git a/packages/components/src/lib/source.ts b/packages/components/src/lib/sources/types.ts
similarity index 99%
rename from packages/components/src/lib/source.ts
rename to packages/components/src/lib/sources/types.ts
index 0a2e54f0..c3ccafec 100644
--- a/packages/components/src/lib/source.ts
+++ b/packages/components/src/lib/sources/types.ts
@@ -33,4 +33,3 @@ export interface DirSource extends BaseSource {
}
export type Source = FileSource | DirSource
-
diff --git a/packages/components/src/lib/sources/utils.ts b/packages/components/src/lib/sources/utils.ts
new file mode 100644
index 00000000..4733e93d
--- /dev/null
+++ b/packages/components/src/lib/sources/utils.ts
@@ -0,0 +1,8 @@
+export function getFileName(source: string): string {
+ const fileName = source
+ .replace(/\?.*$/, '') // remove query string
+ .split('/')
+ .at(-1)
+ if (!fileName) throw new Error('Cannot extract a filename')
+ return fileName
+}
diff --git a/packages/components/src/lib/utils.ts b/packages/components/src/lib/utils.ts
index 2c64d891..3d0e9151 100644
--- a/packages/components/src/lib/utils.ts
+++ b/packages/components/src/lib/utils.ts
@@ -82,15 +82,6 @@ export function parseFileSize(headers: Headers): number | undefined {
return contentLength ? Number(contentLength) : undefined
}
-export function getFileName(source: string): string {
- const fileName = source
- .replace(/\?.*$/, '') // remove query string
- .split('/')
- .at(-1)
- if (!fileName) throw new Error('Cannot extract a filename')
- return fileName
-}
-
export const contentTypes: Record = {
png: 'image/png',
jpg: 'image/jpeg',
diff --git a/packages/components/test/components/File.test.tsx b/packages/components/test/components/File.test.tsx
index 985221ef..d310a8be 100644
--- a/packages/components/test/components/File.test.tsx
+++ b/packages/components/test/components/File.test.tsx
@@ -2,13 +2,9 @@ import { render } from '@testing-library/react'
import { strict as assert } from 'assert'
import React, { act } from 'react'
import { describe, expect, it, vi } from 'vitest'
-import File from '../../src/components/File.js'
-import { createHttpFileSystem, createHyperparamFileSystem, getSource } from '../../src/lib/filesystem.js'
-import { RoutesConfig } from '../../src/lib/routes.js'
-
-const hyparamFileSystem = createHyperparamFileSystem({ endpoint: 'http://localhost:3000' })
-const httpFileSystem = createHttpFileSystem()
+import { File, RoutesConfig, getHttpSource, getHyperparamSource } from '../../src/index.js'
+const endpoint = 'http://localhost:3000'
const config: RoutesConfig = {
routes: {
@@ -22,7 +18,7 @@ global.fetch = vi.fn(() => Promise.resolve({ text: vi.fn() } as unknown as Respo
describe('File Component', () => {
it('renders a local file path', async () => {
- const source = getSource('folder/subfolder/test.txt', hyparamFileSystem)
+ const source = getHyperparamSource('folder/subfolder/test.txt', { endpoint })
assert(source?.kind === 'file')
const { getByText } = await act(() => render(
@@ -37,7 +33,7 @@ describe('File Component', () => {
it('renders a URL', async () => {
const url = 'https://example.com/test.txt'
- const source = getSource(url, httpFileSystem)
+ const source = getHttpSource(url)
assert(source?.kind === 'file')
const { getByText } = await act(() => render())
@@ -46,7 +42,7 @@ describe('File Component', () => {
})
it('renders correct breadcrumbs for nested folders', async () => {
- const source = getSource('folder1/folder2/folder3/test.txt', hyparamFileSystem)
+ const source = getHyperparamSource('folder1/folder2/folder3/test.txt', { endpoint })
assert(source?.kind === 'file')
const { getAllByRole } = await act(() => render(
diff --git a/packages/components/test/components/Folder.test.tsx b/packages/components/test/components/Folder.test.tsx
index 1e3428b0..a8a354f4 100644
--- a/packages/components/test/components/Folder.test.tsx
+++ b/packages/components/test/components/Folder.test.tsx
@@ -2,11 +2,9 @@ import { render, waitFor } from '@testing-library/react'
import { strict as assert } from 'assert'
import React from 'react'
import { describe, expect, it, test, vi } from 'vitest'
-import Folder from '../../src/components/Folder.js'
-import { HyperparamFileMetadata, createHyperparamFileSystem, getSource } from '../../src/lib/filesystem.js'
-import { RoutesConfig } from '../../src/lib/routes.js'
+import { Folder, HyperparamFileMetadata, RoutesConfig, getHyperparamSource } from '../../src/index.js'
-const hyparamFileSystem = createHyperparamFileSystem({ endpoint: 'http://localhost:3000' })
+const endpoint = 'http://localhost:3000'
const mockFiles: HyperparamFileMetadata[] = [
{ key: 'folder1/', lastModified: '2023-01-01T00:00:00Z' },
{ key: 'file1.txt', fileSize: 8196, lastModified: '2023-01-01T00:00:00Z' },
@@ -30,7 +28,7 @@ describe('Folder Component', () => {
ok: true,
} as Response)
- const source = getSource(path, hyparamFileSystem)
+ const source = getHyperparamSource(path, { endpoint })
assert(source?.kind === 'directory')
const { findByText, getByText } = render()
@@ -52,7 +50,7 @@ describe('Folder Component', () => {
ok: true,
} as Response)
- const source = getSource('', hyparamFileSystem)
+ const source = getHyperparamSource('', { endpoint })
assert(source?.kind === 'directory')
const { container } = render()
@@ -66,7 +64,7 @@ describe('Folder Component', () => {
ok: false,
} as Response)
- const source = getSource('test-prefix/', hyparamFileSystem)
+ const source = getHyperparamSource('test-prefix/', { endpoint })
assert(source?.kind === 'directory')
const { findByText, queryByText } = render()
@@ -84,7 +82,7 @@ describe('Folder Component', () => {
ok: true,
} as Response)
- const source = getSource('subdir1/subdir2/', hyparamFileSystem)
+ const source = getHyperparamSource('subdir1/subdir2/', { endpoint })
assert(source?.kind === 'directory')
const { findByText, getByText } = render()
diff --git a/packages/components/test/components/Layout.test.tsx b/packages/components/test/components/Layout.test.tsx
index f7b49305..96efb2d6 100644
--- a/packages/components/test/components/Layout.test.tsx
+++ b/packages/components/test/components/Layout.test.tsx
@@ -1,8 +1,7 @@
import { render } from '@testing-library/react'
import React from 'react'
import { describe, expect, it, vi } from 'vitest'
-import Layout, { Spinner } from '../../src/components/Layout.js'
-import { cn } from '../../src/lib/utils.js'
+import { Layout, Spinner, cn } from '../../src/index.js'
vi.mock('next-auth/react', () => ({ signOut: vi.fn(), useSession: vi.fn() }))
vi.mock('next/link', () => ({ default: vi.fn() }))
diff --git a/packages/components/test/components/Markdown.test.tsx b/packages/components/test/components/Markdown.test.tsx
index 17cc027f..22da3b98 100644
--- a/packages/components/test/components/Markdown.test.tsx
+++ b/packages/components/test/components/Markdown.test.tsx
@@ -1,7 +1,7 @@
import { render } from '@testing-library/react'
import React from 'react'
import { describe, expect, it } from 'vitest'
-import Markdown from '../../src/components/Markdown.js'
+import { Markdown } from '../../src/index.js'
describe('Markdown', () => {
it('renders plain text as a paragraph', () => {
diff --git a/packages/components/test/components/viewers/ImageView.test.tsx b/packages/components/test/components/viewers/ImageView.test.tsx
index 962df73b..aa26fccd 100644
--- a/packages/components/test/components/viewers/ImageView.test.tsx
+++ b/packages/components/test/components/viewers/ImageView.test.tsx
@@ -2,10 +2,7 @@ import { render } from '@testing-library/react'
import { strict as assert } from 'assert'
import React from 'react'
import { describe, expect, it, vi } from 'vitest'
-import ImageView from '../../../src/components/viewers/ImageView.js'
-import { createHyperparamFileSystem, getSource } from '../../../src/lib/filesystem.js'
-
-const hyparamFileSystem = createHyperparamFileSystem({ endpoint: 'http://localhost:3000' })
+import { ImageView, getHyperparamSource } from '../../../src/index.js'
global.fetch = vi.fn()
@@ -17,7 +14,7 @@ describe('ImageView Component', () => {
headers: new Map([['content-length', body.byteLength]]),
} as unknown as Response)
- const source = getSource('test.png', hyparamFileSystem)
+ const source = getHyperparamSource('test.png', { endpoint: 'http://localhost:3000' })
assert(source?.kind === 'file')
const { findByRole, findByText } = render(
diff --git a/packages/components/test/components/viewers/MarkdownView.test.tsx b/packages/components/test/components/viewers/MarkdownView.test.tsx
index 4a1ad9b1..18742ef2 100644
--- a/packages/components/test/components/viewers/MarkdownView.test.tsx
+++ b/packages/components/test/components/viewers/MarkdownView.test.tsx
@@ -2,10 +2,7 @@ import { render } from '@testing-library/react'
import { strict as assert } from 'assert'
import React from 'react'
import { describe, expect, it, vi } from 'vitest'
-import MarkdownView from '../../../src/components/viewers/MarkdownView.js'
-import { createHyperparamFileSystem, getSource } from '../../../src/lib/filesystem.js'
-
-const hyparamFileSystem = createHyperparamFileSystem({ endpoint: 'http://localhost:3000' })
+import { MarkdownView, getHyperparamSource } from '../../../src/index.js'
global.fetch = vi.fn()
@@ -17,7 +14,7 @@ describe('MarkdownView Component', () => {
headers: new Map([['content-length', text.length]]),
} as unknown as Response)
- const source = getSource('test.md', hyparamFileSystem)
+ const source = getHyperparamSource('test.md', { endpoint: 'http://localhost:3000' })
assert(source?.kind === 'file')
const { findByText } = render(
diff --git a/packages/components/test/lib/utils.test.ts b/packages/components/test/lib/routes.test.ts
similarity index 100%
rename from packages/components/test/lib/utils.test.ts
rename to packages/components/test/lib/routes.test.ts
diff --git a/packages/components/test/lib/sources/httpSource.test.ts b/packages/components/test/lib/sources/httpSource.test.ts
new file mode 100644
index 00000000..e9ff01a3
--- /dev/null
+++ b/packages/components/test/lib/sources/httpSource.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, it, test, vi } from 'vitest'
+import { getHttpSource } from '../../../src/lib/sources/httpSource.js'
+
+global.fetch = vi.fn()
+
+describe('getHttpSource', () => {
+ test.for([
+ 'http://example.com/test.txt',
+ 'https://example.com/test.txt',
+ 'http://weird',
+ ])('recognizes a URL', (sourceId: string) => {
+ const source = getHttpSource(sourceId)
+ expect(source?.kind).toBe('file')
+ })
+ it('does not support encoded URLs', () => {
+ expect(getHttpSource('https%3A%2F%2Fhyperparam-public.s3.amazonaws.com%2Fbunnies.parquet')).toBeUndefined()
+ })
+})
diff --git a/packages/components/test/lib/filesystem.test.ts b/packages/components/test/lib/sources/hyperparamSource.test.ts
similarity index 50%
rename from packages/components/test/lib/filesystem.test.ts
rename to packages/components/test/lib/sources/hyperparamSource.test.ts
index dffdb4e0..3bb1a4e3 100644
--- a/packages/components/test/lib/filesystem.test.ts
+++ b/packages/components/test/lib/sources/hyperparamSource.test.ts
@@ -1,19 +1,17 @@
-import { describe, expect, it, test, vi } from 'vitest'
-import { HyperparamFileMetadata, createHttpFileSystem, createHyperparamFileSystem } from '../../src/lib/filesystem.js'
+import { assert, describe, expect, it, test, vi } from 'vitest'
+import { HyperparamFileMetadata, getHyperparamSource } from '../../../src/lib/sources/hyperparamSource.js'
global.fetch = vi.fn()
-describe('createHyperparamFileSystem', () => {
+describe('getHyperparamSource', () => {
const endpoint = 'http://localhost:3000'
- const fs = createHyperparamFileSystem({ endpoint })
test.for([
'test.txt',
'no-extension',
'folder/subfolder/test.txt',
])('recognizes a local file path', (sourceId: string) => {
- expect(fs.canParse(sourceId)).toBe(true)
- expect(fs.getKind(sourceId)).toBe('file')
+ expect(getHyperparamSource(sourceId, { endpoint })?.kind).toBe('file')
})
test.for([
@@ -21,22 +19,23 @@ describe('createHyperparamFileSystem', () => {
'folder1/',
'folder1/folder2/',
])('recognizes a folder', (sourceId: string) => {
- expect(fs.canParse(sourceId)).toBe(true)
- expect(fs.getKind(sourceId)).toBe('directory')
+ expect(getHyperparamSource(sourceId, { endpoint })?.kind).toBe('directory')
})
test.for([
'/',
'////',
])('does not support a heading slash', (sourceId: string) => {
- expect(fs.canParse(sourceId)).toBe(false)
+ expect(getHyperparamSource(sourceId, { endpoint })).toBeUndefined()
})
test.for([
'test.txt',
'folder/subfolder/test.txt',
])('encodes the parameters in resolveUrl', (sourceId: string) => {
- expect(fs.getResolveUrl(sourceId)).toBe(endpoint + '/api/store/get?key=' + encodeURIComponent(sourceId))
+ const source = getHyperparamSource(sourceId, { endpoint })
+ assert(source?.kind === 'file')
+ expect(source.resolveUrl).toBe(endpoint + '/api/store/get?key=' + encodeURIComponent(sourceId))
})
it('in listFiles, creates a full source by concatenating the file with the prefix', async () => {
@@ -48,26 +47,12 @@ describe('createHyperparamFileSystem', () => {
json: () => Promise.resolve(mockFiles),
ok: true,
} as Response)
- const files = await fs.listFiles('folder0')
+ const source = getHyperparamSource('folder0/', { endpoint })
+ assert(source?.kind === 'directory')
+ const files = await source.listFiles()
expect(files).to.be.an('array').and.have.length(2)
expect(files[0].sourceId).toBe('folder0/folder1/')
expect(files[1].sourceId).toBe('folder0/file1.txt')
})
})
-
-describe('createHttpFileSystem', () => {
- const fs = createHttpFileSystem()
-
- test.for([
- 'http://example.com/test.txt',
- 'https://example.com/test.txt',
- 'http://weird',
- ])('recognizes a URL', (sourceId: string) => {
- expect(fs.canParse(sourceId)).toBe(true)
- expect(fs.getKind(sourceId)).toBe('file')
- })
- it('does not support encoded URLs', () => {
- expect(fs.canParse('https%3A%2F%2Fhyperparam-public.s3.amazonaws.com%2Fbunnies.parquet')).toBe(false)
- })
-})