Skip to content

Commit 7421a81

Browse files
authored
Fix ESM (#5)
1 parent c538ea7 commit 7421a81

File tree

7 files changed

+541
-1096
lines changed

7 files changed

+541
-1096
lines changed

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
"name": "generic-filehandle2",
33
"description": "uniform interface for accessing binary data from local files, remote HTTP resources, and browser Blob data",
44
"version": "2.0.3",
5-
"main": "dist/index.js",
6-
"module": "esm/index.js",
5+
"type": "module",
6+
"module": "./esm/index.js",
7+
"main": "./esm/index.js",
8+
"exports": "./esm/index.js",
79
"repository": "GMOD/generic-filehandle2",
810
"license": "MIT",
911
"author": {
@@ -12,7 +14,7 @@
1214
"url": "https://github.com/cmdcolin"
1315
},
1416
"engines": {
15-
"node": ">=12"
17+
"node": ">=14"
1618
},
1719
"files": [
1820
"dist",
@@ -39,19 +41,17 @@
3941
"genomics"
4042
],
4143
"devDependencies": {
42-
"@types/fetch-mock": "^7.3.8",
4344
"@types/node": "^22.15.3",
4445
"@types/range-parser": "^1.2.7",
4546
"@vitest/coverage-v8": "^3.0.1",
4647
"eslint": "^9.16.0",
4748
"eslint-plugin-import": "^2.31.0",
4849
"eslint-plugin-unicorn": "^59.0.0",
49-
"fetch-mock": "^9.0.0",
5050
"node-fetch": "^2.0.0",
5151
"prettier": "^3.4.1",
5252
"range-parser": "^1.2.1",
5353
"rimraf": "^6.0.0",
54-
"standard-changelog": "^6.0.0",
54+
"standard-changelog": "^7.0.1",
5555
"typescript": "^5.7.0",
5656
"typescript-eslint": "^8.18.0",
5757
"vitest": "^3.0.1"
@@ -61,6 +61,8 @@
6161
},
6262
"browser": {
6363
"./dist/localFile.js": false,
64-
"./esm/localFile.js": false
64+
"./esm/localFile.js": false,
65+
"fs": false,
66+
"fs/promises": false
6567
}
6668
}

src/index.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,5 @@
1-
import LocalFile from './localFile.ts'
2-
import RemoteFile from './remoteFile.ts'
3-
4-
import type { FilehandleOptions, GenericFilehandle } from './filehandle.ts'
5-
61
export * from './filehandle.ts'
72

8-
function fromUrl(
9-
source: string,
10-
opts: FilehandleOptions = {},
11-
): GenericFilehandle {
12-
return new RemoteFile(source, opts)
13-
}
14-
function open(
15-
maybeUrl?: string,
16-
maybePath?: string,
17-
maybeFilehandle?: GenericFilehandle,
18-
opts: FilehandleOptions = {},
19-
): GenericFilehandle {
20-
if (maybeFilehandle !== undefined) {
21-
return maybeFilehandle
22-
}
23-
if (maybeUrl !== undefined) {
24-
return fromUrl(maybeUrl, opts)
25-
}
26-
if (maybePath !== undefined) {
27-
return new LocalFile(maybePath, opts)
28-
}
29-
throw new Error('no url, path, or filehandle provided, cannot open')
30-
}
31-
32-
export { fromUrl, open }
333
export { default as BlobFile } from './blobFile.ts'
344
export { default as RemoteFile } from './remoteFile.ts'
355
export { default as LocalFile } from './localFile.ts'

src/mockLocalFile.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { FilehandleOptions, GenericFilehandle, Stats } from './filehandle'
2+
3+
export default class LocalFile implements GenericFilehandle {
4+
public constructor(_source: string, _opts: FilehandleOptions = {}) {
5+
throw new Error('unimplemented')
6+
}
7+
8+
read(): Promise<Uint8Array<ArrayBuffer>> {
9+
throw new Error('unimplemented')
10+
}
11+
12+
public async readFile(): Promise<Uint8Array<ArrayBuffer>>
13+
public async readFile(options: BufferEncoding): Promise<string>
14+
public async readFile<T extends undefined>(
15+
options:
16+
| Omit<FilehandleOptions, 'encoding'>
17+
| (Omit<FilehandleOptions, 'encoding'> & { encoding: T }),
18+
): Promise<Uint8Array<ArrayBuffer>>
19+
public async readFile<T extends BufferEncoding>(
20+
options: Omit<FilehandleOptions, 'encoding'> & { encoding: T },
21+
): Promise<string>
22+
readFile<T extends BufferEncoding>(
23+
options: Omit<FilehandleOptions, 'encoding'> & { encoding: T },
24+
): T extends BufferEncoding
25+
? Promise<Uint8Array<ArrayBuffer>>
26+
: Promise<Uint8Array<ArrayBuffer> | string>
27+
public async readFile(
28+
_options: FilehandleOptions | BufferEncoding = {},
29+
): Promise<Uint8Array<ArrayBuffer> | string> {
30+
throw new Error('unimplemented')
31+
}
32+
33+
stat(): Promise<Stats> {
34+
throw new Error('unimplemented')
35+
}
36+
37+
close(): Promise<void> {
38+
throw new Error('unimplemented')
39+
}
40+
}

test/auth.test.ts

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,66 @@
11
import { TextDecoder } from 'util'
22

3-
import fetchMock from 'fetch-mock'
4-
import { afterEach, expect, test } from 'vitest'
3+
import { afterEach, beforeEach, expect, test, vi } from 'vitest'
54

65
import { RemoteFile } from '../src/'
76

8-
fetchMock.config.sendAsJson = false
9-
107
function toString(a: Uint8Array<ArrayBuffer>) {
118
return new TextDecoder('utf8').decode(a)
129
}
13-
afterEach(() => fetchMock.restore())
10+
11+
// Create a Response object from a buffer or string
12+
function createResponse(
13+
body: Uint8Array<ArrayBuffer> | string,
14+
status: number,
15+
headers: Record<string, string> = {},
16+
) {
17+
return {
18+
ok: status >= 200 && status < 300,
19+
status,
20+
headers: {
21+
get(name: string) {
22+
return headers[name] || null
23+
},
24+
},
25+
arrayBuffer: async () => {
26+
if (typeof body === 'string') {
27+
const encoder = new TextEncoder()
28+
return encoder.encode(body).buffer
29+
}
30+
return body.buffer
31+
},
32+
text: async () => {
33+
if (typeof body === 'string') {
34+
return body
35+
}
36+
return toString(body)
37+
},
38+
}
39+
}
40+
41+
// Mock implementation for fetch
42+
let mockFetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>
43+
44+
beforeEach(() => {
45+
// Reset the mock fetch implementation before each test
46+
mockFetch = vi.fn().mockImplementation(async (url: string) => {
47+
throw new Error(`Unhandled fetch request to ${url}`)
48+
})
49+
})
50+
51+
afterEach(() => {
52+
vi.resetAllMocks()
53+
})
1454

1555
test('auth token', async () => {
16-
fetchMock.mock('http://fakehost/test.txt', (url: string, args: any) => {
56+
mockFetch = vi.fn().mockImplementation(async (_url: string, args: any) => {
1757
return args.headers.Authorization
18-
? {
19-
status: 200,
20-
body: 'hello world',
21-
}
22-
: {
23-
status: 403,
24-
}
58+
? createResponse('hello world', 200)
59+
: createResponse('Unauthorized', 403)
2560
})
61+
2662
const f = new RemoteFile('http://fakehost/test.txt', {
63+
fetch: mockFetch,
2764
overrides: {
2865
headers: {
2966
Authorization: 'Basic YWxhZGRpbjpvcGVuc2VzYW1l',
@@ -33,20 +70,21 @@ test('auth token', async () => {
3370
const stat = await f.readFile('utf8')
3471
expect(stat).toBe('hello world')
3572
})
73+
3674
test('auth token with range request', async () => {
37-
fetchMock.mock('http://fakehost/test.txt', (url: string, args: any) => {
75+
mockFetch = vi.fn().mockImplementation(async (_url: string, args: any) => {
3876
if (args.headers.Authorization && args.headers.range) {
39-
return {
40-
status: 206,
41-
body: 'hello',
42-
}
77+
return createResponse('hello', 206)
4378
} else if (!args.headers.Authorization) {
44-
return { status: 403 }
79+
return createResponse('Unauthorized', 403)
4580
} else if (!args.headers.Range) {
46-
return { status: 400 }
81+
return createResponse('Bad Request', 400)
4782
}
83+
return createResponse('Unknown error', 500)
4884
})
85+
4986
const f = new RemoteFile('http://fakehost/test.txt', {
87+
fetch: mockFetch,
5088
overrides: {
5189
headers: {
5290
Authorization: 'Basic YWxhZGRpbjpvcGVuc2VzYW1l',

test/index.test.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)