@chunkd/source-http 10.1.2 → 11.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -9
- package/build/{__test__ → src/__test__}/source.http.test.d.ts +0 -1
- package/build/src/__test__/source.http.test.js +97 -0
- package/build/src/__test__/source.http.test.js.map +1 -0
- package/build/src/index.d.ts +32 -0
- package/build/src/index.js +70 -0
- package/build/src/index.js.map +1 -0
- package/package.json +15 -7
- package/CHANGELOG.md +0 -504
- package/build/__test__/source.http.test.d.ts.map +0 -1
- package/build/__test__/source.http.test.js +0 -85
- package/build/http.fs.d.ts +0 -24
- package/build/http.fs.d.ts.map +0 -1
- package/build/http.fs.js +0 -51
- package/build/http.source.d.ts +0 -33
- package/build/http.source.d.ts.map +0 -1
- package/build/http.source.js +0 -56
- package/build/index.d.ts +0 -3
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -3
- package/src/__test__/source.http.test.ts +0 -104
- package/src/http.fs.ts +0 -55
- package/src/http.source.ts +0 -81
- package/src/index.ts +0 -2
- package/tsconfig.json +0 -10
package/build/http.fs.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { CompositeError, isRecord } from '@chunkd/core';
|
|
2
|
-
import { SourceHttp } from './http.source.js';
|
|
3
|
-
function getCompositeError(e, msg) {
|
|
4
|
-
if (!isRecord(e))
|
|
5
|
-
return new CompositeError(msg, 500, e);
|
|
6
|
-
if (e.code === 'ENOENT')
|
|
7
|
-
return new CompositeError(msg, 404, e);
|
|
8
|
-
if (e.code === 'EACCES')
|
|
9
|
-
return new CompositeError(msg, 403, e);
|
|
10
|
-
return new CompositeError(msg, 500, e);
|
|
11
|
-
}
|
|
12
|
-
export class FsHttp {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.protocol = FsHttp.protocol;
|
|
15
|
-
}
|
|
16
|
-
static is(fs) {
|
|
17
|
-
return fs.protocol === FsHttp.protocol;
|
|
18
|
-
}
|
|
19
|
-
source(filePath) {
|
|
20
|
-
return new SourceHttp(filePath);
|
|
21
|
-
}
|
|
22
|
-
async *list(filePath) {
|
|
23
|
-
throw new Error(`Unable to "list" on ${filePath}`);
|
|
24
|
-
}
|
|
25
|
-
async *details(filePath) {
|
|
26
|
-
throw new Error(`Unable to "details" on ${filePath}`);
|
|
27
|
-
}
|
|
28
|
-
async head(filePath) {
|
|
29
|
-
const res = await SourceHttp.fetch(filePath, { method: 'HEAD' });
|
|
30
|
-
if (!res.ok)
|
|
31
|
-
throw getCompositeError(new Error(res.statusText), `Failed to head: ${filePath}`);
|
|
32
|
-
return { path: filePath, size: Number(res.headers.get('content-length')), isDirectory: false };
|
|
33
|
-
}
|
|
34
|
-
async read(filePath) {
|
|
35
|
-
const res = await SourceHttp.fetch(filePath, { method: 'GET' });
|
|
36
|
-
if (!res.ok)
|
|
37
|
-
throw getCompositeError(new Error(res.statusText), `Failed to head: ${filePath}`);
|
|
38
|
-
return Buffer.from(await res.arrayBuffer());
|
|
39
|
-
}
|
|
40
|
-
async write(filePath) {
|
|
41
|
-
throw new Error(`Unable to "write" on ${filePath}`);
|
|
42
|
-
}
|
|
43
|
-
async delete(filePath) {
|
|
44
|
-
throw new Error(`Unable to "delete" on ${filePath}`);
|
|
45
|
-
}
|
|
46
|
-
stream(filePath) {
|
|
47
|
-
throw new Error(`Unable to "stream" on ${filePath}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
FsHttp.protocol = 'http';
|
|
51
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5mcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9odHRwLmZzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxjQUFjLEVBQXdCLFFBQVEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUU5RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFHOUMsU0FBUyxpQkFBaUIsQ0FBQyxDQUFVLEVBQUUsR0FBVztJQUNoRCxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUFFLE9BQU8sSUFBSSxjQUFjLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN6RCxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssUUFBUTtRQUFFLE9BQU8sSUFBSSxjQUFjLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNoRSxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssUUFBUTtRQUFFLE9BQU8sSUFBSSxjQUFjLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNoRSxPQUFPLElBQUksY0FBYyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDekMsQ0FBQztBQUVELE1BQU0sT0FBTyxNQUFNO0lBQW5CO1FBRUUsYUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUM7SUF3QzdCLENBQUM7SUF0Q0MsTUFBTSxDQUFDLEVBQUUsQ0FBQyxFQUFjO1FBQ3RCLE9BQU8sRUFBRSxDQUFDLFFBQVEsS0FBSyxNQUFNLENBQUMsUUFBUSxDQUFDO0lBQ3pDLENBQUM7SUFFRCxNQUFNLENBQUMsUUFBZ0I7UUFDckIsT0FBTyxJQUFJLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRUQsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQWdCO1FBQzFCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUNELEtBQUssQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFnQjtRQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLDBCQUEwQixRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQWdCO1FBQ3pCLE1BQU0sR0FBRyxHQUFHLE1BQU0sVUFBVSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNqRSxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFBRSxNQUFNLGlCQUFpQixDQUFDLElBQUksS0FBSyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRSxtQkFBbUIsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUMvRixPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDakcsQ0FBQztJQUVELEtBQUssQ0FBQyxJQUFJLENBQUMsUUFBZ0I7UUFDekIsTUFBTSxHQUFHLEdBQUcsTUFBTSxVQUFVLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2hFLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRTtZQUFFLE1BQU0saUJBQWlCLENBQUMsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxFQUFFLG1CQUFtQixRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQy9GLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLFFBQWdCO1FBQzFCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELEtBQUssQ0FBQyxNQUFNLENBQUMsUUFBZ0I7UUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRUQsTUFBTSxDQUFDLFFBQWdCO1FBQ3JCLE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQzs7QUF4Q00sZUFBUSxHQUFHLE1BQU0sQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvc2l0ZUVycm9yLCBGaWxlSW5mbywgRmlsZVN5c3RlbSwgaXNSZWNvcmQgfSBmcm9tICdAY2h1bmtkL2NvcmUnO1xuaW1wb3J0IHR5cGUgeyBSZWFkYWJsZSB9IGZyb20gJ3N0cmVhbSc7XG5pbXBvcnQgeyBTb3VyY2VIdHRwIH0gZnJvbSAnLi9odHRwLnNvdXJjZS5qcyc7XG5cbmV4cG9ydCB0eXBlIEZzRXJyb3IgPSB7IGNvZGU6IHN0cmluZyB9ICYgRXJyb3I7XG5mdW5jdGlvbiBnZXRDb21wb3NpdGVFcnJvcihlOiB1bmtub3duLCBtc2c6IHN0cmluZyk6IENvbXBvc2l0ZUVycm9yIHtcbiAgaWYgKCFpc1JlY29yZChlKSkgcmV0dXJuIG5ldyBDb21wb3NpdGVFcnJvcihtc2csIDUwMCwgZSk7XG4gIGlmIChlLmNvZGUgPT09ICdFTk9FTlQnKSByZXR1cm4gbmV3IENvbXBvc2l0ZUVycm9yKG1zZywgNDA0LCBlKTtcbiAgaWYgKGUuY29kZSA9PT0gJ0VBQ0NFUycpIHJldHVybiBuZXcgQ29tcG9zaXRlRXJyb3IobXNnLCA0MDMsIGUpO1xuICByZXR1cm4gbmV3IENvbXBvc2l0ZUVycm9yKG1zZywgNTAwLCBlKTtcbn1cblxuZXhwb3J0IGNsYXNzIEZzSHR0cCBpbXBsZW1lbnRzIEZpbGVTeXN0ZW08U291cmNlSHR0cD4ge1xuICBzdGF0aWMgcHJvdG9jb2wgPSAnaHR0cCc7XG4gIHByb3RvY29sID0gRnNIdHRwLnByb3RvY29sO1xuXG4gIHN0YXRpYyBpcyhmczogRmlsZVN5c3RlbSk6IGZzIGlzIEZzSHR0cCB7XG4gICAgcmV0dXJuIGZzLnByb3RvY29sID09PSBGc0h0dHAucHJvdG9jb2w7XG4gIH1cblxuICBzb3VyY2UoZmlsZVBhdGg6IHN0cmluZyk6IFNvdXJjZUh0dHAge1xuICAgIHJldHVybiBuZXcgU291cmNlSHR0cChmaWxlUGF0aCk7XG4gIH1cblxuICBhc3luYyAqbGlzdChmaWxlUGF0aDogc3RyaW5nKTogQXN5bmNHZW5lcmF0b3I8c3RyaW5nPiB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbmFibGUgdG8gXCJsaXN0XCIgb24gJHtmaWxlUGF0aH1gKTtcbiAgfVxuICBhc3luYyAqZGV0YWlscyhmaWxlUGF0aDogc3RyaW5nKTogQXN5bmNHZW5lcmF0b3I8RmlsZUluZm8+IHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBcImRldGFpbHNcIiBvbiAke2ZpbGVQYXRofWApO1xuICB9XG5cbiAgYXN5bmMgaGVhZChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTwoRmlsZUluZm8gJiB7IGlzRGlyZWN0b3J5OiBib29sZWFuIH0pIHwgbnVsbD4ge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IFNvdXJjZUh0dHAuZmV0Y2goZmlsZVBhdGgsIHsgbWV0aG9kOiAnSEVBRCcgfSk7XG4gICAgaWYgKCFyZXMub2spIHRocm93IGdldENvbXBvc2l0ZUVycm9yKG5ldyBFcnJvcihyZXMuc3RhdHVzVGV4dCksIGBGYWlsZWQgdG8gaGVhZDogJHtmaWxlUGF0aH1gKTtcbiAgICByZXR1cm4geyBwYXRoOiBmaWxlUGF0aCwgc2l6ZTogTnVtYmVyKHJlcy5oZWFkZXJzLmdldCgnY29udGVudC1sZW5ndGgnKSksIGlzRGlyZWN0b3J5OiBmYWxzZSB9O1xuICB9XG5cbiAgYXN5bmMgcmVhZChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgICBjb25zdCByZXMgPSBhd2FpdCBTb3VyY2VIdHRwLmZldGNoKGZpbGVQYXRoLCB7IG1ldGhvZDogJ0dFVCcgfSk7XG4gICAgaWYgKCFyZXMub2spIHRocm93IGdldENvbXBvc2l0ZUVycm9yKG5ldyBFcnJvcihyZXMuc3RhdHVzVGV4dCksIGBGYWlsZWQgdG8gaGVhZDogJHtmaWxlUGF0aH1gKTtcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oYXdhaXQgcmVzLmFycmF5QnVmZmVyKCkpO1xuICB9XG5cbiAgYXN5bmMgd3JpdGUoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIFwid3JpdGVcIiBvbiAke2ZpbGVQYXRofWApO1xuICB9XG5cbiAgYXN5bmMgZGVsZXRlKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBcImRlbGV0ZVwiIG9uICR7ZmlsZVBhdGh9YCk7XG4gIH1cblxuICBzdHJlYW0oZmlsZVBhdGg6IHN0cmluZyk6IFJlYWRhYmxlIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBcInN0cmVhbVwiIG9uICR7ZmlsZVBhdGh9YCk7XG4gIH1cbn1cbiJdfQ==
|
package/build/http.source.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { ChunkSource, ChunkSourceBase, ChunkSourceMetadata } from '@chunkd/core';
|
|
2
|
-
export interface FetchLikeOptions {
|
|
3
|
-
method?: string;
|
|
4
|
-
headers?: Record<string, string>;
|
|
5
|
-
}
|
|
6
|
-
export interface FetchLikeResponse {
|
|
7
|
-
ok: boolean;
|
|
8
|
-
statusText: string;
|
|
9
|
-
status: number;
|
|
10
|
-
headers: {
|
|
11
|
-
get(k: string): string | null;
|
|
12
|
-
};
|
|
13
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
|
14
|
-
}
|
|
15
|
-
export type FetchLike = (url: string, opts?: FetchLikeOptions) => Promise<FetchLikeResponse>;
|
|
16
|
-
export declare class SourceHttp extends ChunkSourceBase {
|
|
17
|
-
type: string;
|
|
18
|
-
protocol: string;
|
|
19
|
-
static DefaultChunkSize: number;
|
|
20
|
-
chunkSize: number;
|
|
21
|
-
uri: string;
|
|
22
|
-
constructor(uri: string);
|
|
23
|
-
static isSource(source: ChunkSource): source is SourceHttp;
|
|
24
|
-
metadata: ChunkSourceMetadata | null;
|
|
25
|
-
/** Load the ETag and content-range from the response */
|
|
26
|
-
parseResponse(response: FetchLikeResponse): ChunkSourceMetadata;
|
|
27
|
-
private _head;
|
|
28
|
-
head(): Promise<FetchLikeResponse>;
|
|
29
|
-
get size(): Promise<number>;
|
|
30
|
-
fetchBytes(offset: number, length?: number): Promise<ArrayBuffer>;
|
|
31
|
-
static fetch: FetchLike;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=http.source.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.source.d.ts","sourceRoot":"","sources":["../src/http.source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAA8B,MAAM,cAAc,CAAC;AAE7G,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AACD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QAAE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC3C,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC;AACD,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE7F,qBAAa,UAAW,SAAQ,eAAe;IAC7C,IAAI,SAAS;IACb,QAAQ,SAAU;IAElB,MAAM,CAAC,gBAAgB,SAAa;IACpC,SAAS,EAAE,MAAM,CAA+B;IAEhD,GAAG,EAAE,MAAM,CAAC;gBAEA,GAAG,EAAE,MAAM;IAKvB,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,IAAI,UAAU;IAI1D,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,wDAAwD;IACxD,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,mBAAmB;IAQ/D,OAAO,CAAC,KAAK,CAAoC;IACjD,IAAI,IAAI,OAAO,CAAC,iBAAiB,CAAC;IASlC,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAG1B;IAEK,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBvE,MAAM,CAAC,KAAK,EAAE,SAAS,CAAyB;CACjD"}
|
package/build/http.source.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ChunkSourceBase, CompositeError } from '@chunkd/core';
|
|
2
|
-
export class SourceHttp extends ChunkSourceBase {
|
|
3
|
-
constructor(uri) {
|
|
4
|
-
super();
|
|
5
|
-
this.type = 'url';
|
|
6
|
-
this.protocol = 'http';
|
|
7
|
-
this.chunkSize = SourceHttp.DefaultChunkSize;
|
|
8
|
-
this.uri = uri;
|
|
9
|
-
}
|
|
10
|
-
static isSource(source) {
|
|
11
|
-
return source.type === 'url';
|
|
12
|
-
}
|
|
13
|
-
/** Load the ETag and content-range from the response */
|
|
14
|
-
parseResponse(response) {
|
|
15
|
-
var _a;
|
|
16
|
-
const metadata = {};
|
|
17
|
-
const contentRange = response.headers.get('content-range');
|
|
18
|
-
if (contentRange != null)
|
|
19
|
-
metadata.size = this.parseContentRange(contentRange);
|
|
20
|
-
metadata.etag = (_a = response.headers.get('etag')) !== null && _a !== void 0 ? _a : undefined;
|
|
21
|
-
return metadata;
|
|
22
|
-
}
|
|
23
|
-
head() {
|
|
24
|
-
if (this._head)
|
|
25
|
-
return this._head;
|
|
26
|
-
this._head = SourceHttp.fetch(this.uri, { method: 'HEAD' }).then((res) => {
|
|
27
|
-
this.metadata = this.parseResponse(res);
|
|
28
|
-
return res;
|
|
29
|
-
});
|
|
30
|
-
return this._head;
|
|
31
|
-
}
|
|
32
|
-
get size() {
|
|
33
|
-
var _a;
|
|
34
|
-
if ((_a = this.metadata) === null || _a === void 0 ? void 0 : _a.size)
|
|
35
|
-
return Promise.resolve(this.metadata.size);
|
|
36
|
-
return this.head().then(() => { var _a, _b; return (_b = (_a = this.metadata) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : -1; });
|
|
37
|
-
}
|
|
38
|
-
async fetchBytes(offset, length) {
|
|
39
|
-
const Range = this.toRange(offset, length);
|
|
40
|
-
const headers = { Range };
|
|
41
|
-
const response = await SourceHttp.fetch(this.uri, { headers });
|
|
42
|
-
if (response.ok) {
|
|
43
|
-
const metadata = this.parseResponse(response);
|
|
44
|
-
if (this.metadata == null)
|
|
45
|
-
this.metadata = metadata;
|
|
46
|
-
else if (this.metadata.etag && this.metadata.etag !== metadata.etag)
|
|
47
|
-
throw new CompositeError(`ETag conflict ${this.uri} ${Range} expected: ${this.metadata.etag} got: ${metadata.etag}`, 409 /* ErrorCodes.Conflict */, null);
|
|
48
|
-
return response.arrayBuffer();
|
|
49
|
-
}
|
|
50
|
-
throw new CompositeError(`Failed to fetch ${this.uri} ${Range}`, response.status, response.statusText);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
SourceHttp.DefaultChunkSize = 32 * 1024;
|
|
54
|
-
// Allow overwriting the fetcher used (eg testing/node-js)
|
|
55
|
-
SourceHttp.fetch = (a, b) => fetch(a, b);
|
|
56
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5zb3VyY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaHR0cC5zb3VyY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFlLGVBQWUsRUFBdUIsY0FBYyxFQUFjLE1BQU0sY0FBYyxDQUFDO0FBZTdHLE1BQU0sT0FBTyxVQUFXLFNBQVEsZUFBZTtJQVM3QyxZQUFZLEdBQVc7UUFDckIsS0FBSyxFQUFFLENBQUM7UUFUVixTQUFJLEdBQUcsS0FBSyxDQUFDO1FBQ2IsYUFBUSxHQUFHLE1BQU0sQ0FBQztRQUdsQixjQUFTLEdBQVcsVUFBVSxDQUFDLGdCQUFnQixDQUFDO1FBTTlDLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQW1CO1FBQ2pDLE9BQU8sTUFBTSxDQUFDLElBQUksS0FBSyxLQUFLLENBQUM7SUFDL0IsQ0FBQztJQUdELHdEQUF3RDtJQUN4RCxhQUFhLENBQUMsUUFBMkI7O1FBQ3ZDLE1BQU0sUUFBUSxHQUF3QixFQUFFLENBQUM7UUFDekMsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDM0QsSUFBSSxZQUFZLElBQUksSUFBSTtZQUFFLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQy9FLFFBQVEsQ0FBQyxJQUFJLEdBQUcsTUFBQSxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsbUNBQUksU0FBUyxDQUFDO1FBQzFELE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7SUFHRCxJQUFJO1FBQ0YsSUFBSSxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQztRQUNsQyxJQUFJLENBQUMsS0FBSyxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ3ZFLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QyxPQUFPLEdBQUcsQ0FBQztRQUNiLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ3BCLENBQUM7SUFFRCxJQUFJLElBQUk7O1FBQ04sSUFBSSxNQUFBLElBQUksQ0FBQyxRQUFRLDBDQUFFLElBQUk7WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRSxPQUFPLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLGVBQUMsT0FBQSxNQUFBLE1BQUEsSUFBSSxDQUFDLFFBQVEsMENBQUUsSUFBSSxtQ0FBSSxDQUFDLENBQUMsQ0FBQSxFQUFBLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsS0FBSyxDQUFDLFVBQVUsQ0FBQyxNQUFjLEVBQUUsTUFBZTtRQUM5QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMzQyxNQUFNLE9BQU8sR0FBRyxFQUFFLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sUUFBUSxHQUFHLE1BQU0sVUFBVSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUUvRCxJQUFJLFFBQVEsQ0FBQyxFQUFFLEVBQUU7WUFDZixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzlDLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJO2dCQUFFLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO2lCQUMvQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJO2dCQUNqRSxNQUFNLElBQUksY0FBYyxDQUN0QixpQkFBaUIsSUFBSSxDQUFDLEdBQUcsSUFBSSxLQUFLLGNBQWMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLFNBQVMsUUFBUSxDQUFDLElBQUksRUFBRSxpQ0FFMUYsSUFBSSxDQUNMLENBQUM7WUFDSixPQUFPLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztTQUMvQjtRQUVELE1BQU0sSUFBSSxjQUFjLENBQUMsbUJBQW1CLElBQUksQ0FBQyxHQUFHLElBQUksS0FBSyxFQUFFLEVBQUUsUUFBUSxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDekcsQ0FBQzs7QUF6RE0sMkJBQWdCLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQTJEcEMsMERBQTBEO0FBQ25ELGdCQUFLLEdBQWMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2h1bmtTb3VyY2UsIENodW5rU291cmNlQmFzZSwgQ2h1bmtTb3VyY2VNZXRhZGF0YSwgQ29tcG9zaXRlRXJyb3IsIEVycm9yQ29kZXMgfSBmcm9tICdAY2h1bmtkL2NvcmUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEZldGNoTGlrZU9wdGlvbnMge1xuICBtZXRob2Q/OiBzdHJpbmc7XG4gIGhlYWRlcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xufVxuZXhwb3J0IGludGVyZmFjZSBGZXRjaExpa2VSZXNwb25zZSB7XG4gIG9rOiBib29sZWFuO1xuICBzdGF0dXNUZXh0OiBzdHJpbmc7XG4gIHN0YXR1czogbnVtYmVyO1xuICBoZWFkZXJzOiB7IGdldChrOiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIH07XG4gIGFycmF5QnVmZmVyKCk6IFByb21pc2U8QXJyYXlCdWZmZXI+O1xufVxuZXhwb3J0IHR5cGUgRmV0Y2hMaWtlID0gKHVybDogc3RyaW5nLCBvcHRzPzogRmV0Y2hMaWtlT3B0aW9ucykgPT4gUHJvbWlzZTxGZXRjaExpa2VSZXNwb25zZT47XG5cbmV4cG9ydCBjbGFzcyBTb3VyY2VIdHRwIGV4dGVuZHMgQ2h1bmtTb3VyY2VCYXNlIHtcbiAgdHlwZSA9ICd1cmwnO1xuICBwcm90b2NvbCA9ICdodHRwJztcblxuICBzdGF0aWMgRGVmYXVsdENodW5rU2l6ZSA9IDMyICogMTAyNDtcbiAgY2h1bmtTaXplOiBudW1iZXIgPSBTb3VyY2VIdHRwLkRlZmF1bHRDaHVua1NpemU7XG5cbiAgdXJpOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IodXJpOiBzdHJpbmcpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMudXJpID0gdXJpO1xuICB9XG5cbiAgc3RhdGljIGlzU291cmNlKHNvdXJjZTogQ2h1bmtTb3VyY2UpOiBzb3VyY2UgaXMgU291cmNlSHR0cCB7XG4gICAgcmV0dXJuIHNvdXJjZS50eXBlID09PSAndXJsJztcbiAgfVxuXG4gIG1ldGFkYXRhOiBDaHVua1NvdXJjZU1ldGFkYXRhIHwgbnVsbDtcbiAgLyoqIExvYWQgdGhlIEVUYWcgYW5kIGNvbnRlbnQtcmFuZ2UgZnJvbSB0aGUgcmVzcG9uc2UgKi9cbiAgcGFyc2VSZXNwb25zZShyZXNwb25zZTogRmV0Y2hMaWtlUmVzcG9uc2UpOiBDaHVua1NvdXJjZU1ldGFkYXRhIHtcbiAgICBjb25zdCBtZXRhZGF0YTogQ2h1bmtTb3VyY2VNZXRhZGF0YSA9IHt9O1xuICAgIGNvbnN0IGNvbnRlbnRSYW5nZSA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdjb250ZW50LXJhbmdlJyk7XG4gICAgaWYgKGNvbnRlbnRSYW5nZSAhPSBudWxsKSBtZXRhZGF0YS5zaXplID0gdGhpcy5wYXJzZUNvbnRlbnRSYW5nZShjb250ZW50UmFuZ2UpO1xuICAgIG1ldGFkYXRhLmV0YWcgPSByZXNwb25zZS5oZWFkZXJzLmdldCgnZXRhZycpID8/IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gbWV0YWRhdGE7XG4gIH1cblxuICBwcml2YXRlIF9oZWFkOiBQcm9taXNlPEZldGNoTGlrZVJlc3BvbnNlPiB8IG51bGw7XG4gIGhlYWQoKTogUHJvbWlzZTxGZXRjaExpa2VSZXNwb25zZT4ge1xuICAgIGlmICh0aGlzLl9oZWFkKSByZXR1cm4gdGhpcy5faGVhZDtcbiAgICB0aGlzLl9oZWFkID0gU291cmNlSHR0cC5mZXRjaCh0aGlzLnVyaSwgeyBtZXRob2Q6ICdIRUFEJyB9KS50aGVuKChyZXMpID0+IHtcbiAgICAgIHRoaXMubWV0YWRhdGEgPSB0aGlzLnBhcnNlUmVzcG9uc2UocmVzKTtcbiAgICAgIHJldHVybiByZXM7XG4gICAgfSk7XG4gICAgcmV0dXJuIHRoaXMuX2hlYWQ7XG4gIH1cblxuICBnZXQgc2l6ZSgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGlmICh0aGlzLm1ldGFkYXRhPy5zaXplKSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHRoaXMubWV0YWRhdGEuc2l6ZSk7XG4gICAgcmV0dXJuIHRoaXMuaGVhZCgpLnRoZW4oKCkgPT4gdGhpcy5tZXRhZGF0YT8uc2l6ZSA/PyAtMSk7XG4gIH1cblxuICBhc3luYyBmZXRjaEJ5dGVzKG9mZnNldDogbnVtYmVyLCBsZW5ndGg/OiBudW1iZXIpOiBQcm9taXNlPEFycmF5QnVmZmVyPiB7XG4gICAgY29uc3QgUmFuZ2UgPSB0aGlzLnRvUmFuZ2Uob2Zmc2V0LCBsZW5ndGgpO1xuICAgIGNvbnN0IGhlYWRlcnMgPSB7IFJhbmdlIH07XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBTb3VyY2VIdHRwLmZldGNoKHRoaXMudXJpLCB7IGhlYWRlcnMgfSk7XG5cbiAgICBpZiAocmVzcG9uc2Uub2spIHtcbiAgICAgIGNvbnN0IG1ldGFkYXRhID0gdGhpcy5wYXJzZVJlc3BvbnNlKHJlc3BvbnNlKTtcbiAgICAgIGlmICh0aGlzLm1ldGFkYXRhID09IG51bGwpIHRoaXMubWV0YWRhdGEgPSBtZXRhZGF0YTtcbiAgICAgIGVsc2UgaWYgKHRoaXMubWV0YWRhdGEuZXRhZyAmJiB0aGlzLm1ldGFkYXRhLmV0YWcgIT09IG1ldGFkYXRhLmV0YWcpXG4gICAgICAgIHRocm93IG5ldyBDb21wb3NpdGVFcnJvcihcbiAgICAgICAgICBgRVRhZyBjb25mbGljdCAke3RoaXMudXJpfSAke1JhbmdlfSBleHBlY3RlZDogJHt0aGlzLm1ldGFkYXRhLmV0YWd9IGdvdDogJHttZXRhZGF0YS5ldGFnfWAsXG4gICAgICAgICAgRXJyb3JDb2Rlcy5Db25mbGljdCxcbiAgICAgICAgICBudWxsLFxuICAgICAgICApO1xuICAgICAgcmV0dXJuIHJlc3BvbnNlLmFycmF5QnVmZmVyKCk7XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IENvbXBvc2l0ZUVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggJHt0aGlzLnVyaX0gJHtSYW5nZX1gLCByZXNwb25zZS5zdGF0dXMsIHJlc3BvbnNlLnN0YXR1c1RleHQpO1xuICB9XG5cbiAgLy8gQWxsb3cgb3ZlcndyaXRpbmcgdGhlIGZldGNoZXIgdXNlZCAoZWcgdGVzdGluZy9ub2RlLWpzKVxuICBzdGF0aWMgZmV0Y2g6IEZldGNoTGlrZSA9IChhLCBiKSA9PiBmZXRjaChhLCBiKTtcbn1cbiJdfQ==
|
package/build/index.d.ts
DELETED
package/build/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
package/build/index.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export { SourceHttp } from './http.source.js';
|
|
2
|
-
export { FsHttp } from './http.fs.js';
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBTb3VyY2VIdHRwIH0gZnJvbSAnLi9odHRwLnNvdXJjZS5qcyc7XG5leHBvcnQgeyBGc0h0dHAgfSBmcm9tICcuL2h0dHAuZnMuanMnO1xuIl19
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { isRecord } from '@chunkd/core';
|
|
2
|
-
import o from 'ospec';
|
|
3
|
-
import { FetchLikeOptions, SourceHttp } from '../http.source.js';
|
|
4
|
-
|
|
5
|
-
export interface HttpHeaders {
|
|
6
|
-
Range: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function getCB(source: SourceHttp, index: number): DataView {
|
|
10
|
-
const chunk = source.chunks.get(index);
|
|
11
|
-
if (chunk != null) return chunk;
|
|
12
|
-
throw Error(`Missing chunk ${index}`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
o.spec('SourceHttp', () => {
|
|
16
|
-
let source: SourceHttp;
|
|
17
|
-
let ranges: string[];
|
|
18
|
-
|
|
19
|
-
// Fake fetch that returns the number of the byte that was requested
|
|
20
|
-
SourceHttp.fetch = (input, obj?: FetchLikeOptions): any => {
|
|
21
|
-
if (!isRecord(obj) || !isRecord<string>(obj.headers)) throw new Error('Invalid fetch');
|
|
22
|
-
const [startByte, endByte] = obj.headers.Range.split('=')[1]
|
|
23
|
-
.split('-')
|
|
24
|
-
.map((i) => parseInt(i, 10));
|
|
25
|
-
const bytes = [];
|
|
26
|
-
ranges.push(obj.headers.Range);
|
|
27
|
-
for (let i = startByte; i < endByte; i++) {
|
|
28
|
-
bytes.push(i);
|
|
29
|
-
}
|
|
30
|
-
const buffer = new Uint8Array(bytes).buffer;
|
|
31
|
-
const arrayBuffer = (): any => Promise.resolve(buffer);
|
|
32
|
-
return Promise.resolve({ arrayBuffer, ok: true, headers: new Map() }) as any;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
o.beforeEach(() => {
|
|
36
|
-
source = new SourceHttp('https://foo');
|
|
37
|
-
source.chunkSize = 1;
|
|
38
|
-
ranges = [];
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
o('should group fetches together', async () => {
|
|
42
|
-
await source.loadBytes(0, 2);
|
|
43
|
-
|
|
44
|
-
o([...(source.chunks as Map<number, DataView>).keys()]).deepEquals([0, 1]);
|
|
45
|
-
o(source.getUint8(0)).equals(0);
|
|
46
|
-
o(source.getUint8(1)).equals(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
o('should group big fetches', async () => {
|
|
50
|
-
await source.loadBytes(0, 2);
|
|
51
|
-
await source.loadBytes(0, 5);
|
|
52
|
-
|
|
53
|
-
o([...(source.chunks as Map<number, DataView>).keys()]).deepEquals([0, 1, 2, 3, 4]);
|
|
54
|
-
|
|
55
|
-
for (let i = 0; i < 5; i++) {
|
|
56
|
-
o(source.getUint8(i)).equals(i);
|
|
57
|
-
o(source.chunks.get(i)?.getUint8(0)).equals(i);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
o('should handle bigger buffers', async () => {
|
|
62
|
-
const MAX_BYTE = 256;
|
|
63
|
-
source.chunkSize = 32;
|
|
64
|
-
|
|
65
|
-
await source.loadBytes(0, MAX_BYTE);
|
|
66
|
-
|
|
67
|
-
o([...(source.chunks as Map<number, DataView>).keys()]).deepEquals([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < MAX_BYTE; i++) {
|
|
70
|
-
o(source.getUint8(i)).equals(i);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < MAX_BYTE / source.chunkSize; i++) {
|
|
74
|
-
const view = getCB(source, i);
|
|
75
|
-
for (let x = 0; x < source.chunkSize; x++) {
|
|
76
|
-
o(view.getUint8(x)).equals(i * source.chunkSize + x);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
o('should handle part requests', async () => {
|
|
82
|
-
source.chunkSize = 2;
|
|
83
|
-
await source.loadBytes(2, 3);
|
|
84
|
-
|
|
85
|
-
o(source.getUint8(2)).equals(2);
|
|
86
|
-
o(source.getUint8(3)).equals(3);
|
|
87
|
-
o(source.getUint8(4)).equals(4);
|
|
88
|
-
|
|
89
|
-
o(getCB(source, 1).byteLength).equals(2);
|
|
90
|
-
o(getCB(source, 2).byteLength).equals(2);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
o('should handle out of order requests', async () => {
|
|
94
|
-
source.chunkSize = 2;
|
|
95
|
-
await Promise.all([source.loadBytes(8, 3), source.loadBytes(1, 3)]);
|
|
96
|
-
|
|
97
|
-
o(source.getUint8(0)).equals(0);
|
|
98
|
-
o(source.getUint8(1)).equals(1);
|
|
99
|
-
o(source.getUint8(2)).equals(2);
|
|
100
|
-
o(source.getUint8(8)).equals(8);
|
|
101
|
-
o(source.getUint8(9)).equals(9);
|
|
102
|
-
o(source.getUint8(10)).equals(10);
|
|
103
|
-
});
|
|
104
|
-
});
|
package/src/http.fs.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { CompositeError, FileInfo, FileSystem, isRecord } from '@chunkd/core';
|
|
2
|
-
import type { Readable } from 'stream';
|
|
3
|
-
import { SourceHttp } from './http.source.js';
|
|
4
|
-
|
|
5
|
-
export type FsError = { code: string } & Error;
|
|
6
|
-
function getCompositeError(e: unknown, msg: string): CompositeError {
|
|
7
|
-
if (!isRecord(e)) return new CompositeError(msg, 500, e);
|
|
8
|
-
if (e.code === 'ENOENT') return new CompositeError(msg, 404, e);
|
|
9
|
-
if (e.code === 'EACCES') return new CompositeError(msg, 403, e);
|
|
10
|
-
return new CompositeError(msg, 500, e);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class FsHttp implements FileSystem<SourceHttp> {
|
|
14
|
-
static protocol = 'http';
|
|
15
|
-
protocol = FsHttp.protocol;
|
|
16
|
-
|
|
17
|
-
static is(fs: FileSystem): fs is FsHttp {
|
|
18
|
-
return fs.protocol === FsHttp.protocol;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
source(filePath: string): SourceHttp {
|
|
22
|
-
return new SourceHttp(filePath);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async *list(filePath: string): AsyncGenerator<string> {
|
|
26
|
-
throw new Error(`Unable to "list" on ${filePath}`);
|
|
27
|
-
}
|
|
28
|
-
async *details(filePath: string): AsyncGenerator<FileInfo> {
|
|
29
|
-
throw new Error(`Unable to "details" on ${filePath}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async head(filePath: string): Promise<(FileInfo & { isDirectory: boolean }) | null> {
|
|
33
|
-
const res = await SourceHttp.fetch(filePath, { method: 'HEAD' });
|
|
34
|
-
if (!res.ok) throw getCompositeError(new Error(res.statusText), `Failed to head: ${filePath}`);
|
|
35
|
-
return { path: filePath, size: Number(res.headers.get('content-length')), isDirectory: false };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async read(filePath: string): Promise<Buffer> {
|
|
39
|
-
const res = await SourceHttp.fetch(filePath, { method: 'GET' });
|
|
40
|
-
if (!res.ok) throw getCompositeError(new Error(res.statusText), `Failed to head: ${filePath}`);
|
|
41
|
-
return Buffer.from(await res.arrayBuffer());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async write(filePath: string): Promise<void> {
|
|
45
|
-
throw new Error(`Unable to "write" on ${filePath}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async delete(filePath: string): Promise<void> {
|
|
49
|
-
throw new Error(`Unable to "delete" on ${filePath}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
stream(filePath: string): Readable {
|
|
53
|
-
throw new Error(`Unable to "stream" on ${filePath}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/http.source.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ChunkSource, ChunkSourceBase, ChunkSourceMetadata, CompositeError, ErrorCodes } from '@chunkd/core';
|
|
2
|
-
|
|
3
|
-
export interface FetchLikeOptions {
|
|
4
|
-
method?: string;
|
|
5
|
-
headers?: Record<string, string>;
|
|
6
|
-
}
|
|
7
|
-
export interface FetchLikeResponse {
|
|
8
|
-
ok: boolean;
|
|
9
|
-
statusText: string;
|
|
10
|
-
status: number;
|
|
11
|
-
headers: { get(k: string): string | null };
|
|
12
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
|
13
|
-
}
|
|
14
|
-
export type FetchLike = (url: string, opts?: FetchLikeOptions) => Promise<FetchLikeResponse>;
|
|
15
|
-
|
|
16
|
-
export class SourceHttp extends ChunkSourceBase {
|
|
17
|
-
type = 'url';
|
|
18
|
-
protocol = 'http';
|
|
19
|
-
|
|
20
|
-
static DefaultChunkSize = 32 * 1024;
|
|
21
|
-
chunkSize: number = SourceHttp.DefaultChunkSize;
|
|
22
|
-
|
|
23
|
-
uri: string;
|
|
24
|
-
|
|
25
|
-
constructor(uri: string) {
|
|
26
|
-
super();
|
|
27
|
-
this.uri = uri;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static isSource(source: ChunkSource): source is SourceHttp {
|
|
31
|
-
return source.type === 'url';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
metadata: ChunkSourceMetadata | null;
|
|
35
|
-
/** Load the ETag and content-range from the response */
|
|
36
|
-
parseResponse(response: FetchLikeResponse): ChunkSourceMetadata {
|
|
37
|
-
const metadata: ChunkSourceMetadata = {};
|
|
38
|
-
const contentRange = response.headers.get('content-range');
|
|
39
|
-
if (contentRange != null) metadata.size = this.parseContentRange(contentRange);
|
|
40
|
-
metadata.etag = response.headers.get('etag') ?? undefined;
|
|
41
|
-
return metadata;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private _head: Promise<FetchLikeResponse> | null;
|
|
45
|
-
head(): Promise<FetchLikeResponse> {
|
|
46
|
-
if (this._head) return this._head;
|
|
47
|
-
this._head = SourceHttp.fetch(this.uri, { method: 'HEAD' }).then((res) => {
|
|
48
|
-
this.metadata = this.parseResponse(res);
|
|
49
|
-
return res;
|
|
50
|
-
});
|
|
51
|
-
return this._head;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get size(): Promise<number> {
|
|
55
|
-
if (this.metadata?.size) return Promise.resolve(this.metadata.size);
|
|
56
|
-
return this.head().then(() => this.metadata?.size ?? -1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async fetchBytes(offset: number, length?: number): Promise<ArrayBuffer> {
|
|
60
|
-
const Range = this.toRange(offset, length);
|
|
61
|
-
const headers = { Range };
|
|
62
|
-
const response = await SourceHttp.fetch(this.uri, { headers });
|
|
63
|
-
|
|
64
|
-
if (response.ok) {
|
|
65
|
-
const metadata = this.parseResponse(response);
|
|
66
|
-
if (this.metadata == null) this.metadata = metadata;
|
|
67
|
-
else if (this.metadata.etag && this.metadata.etag !== metadata.etag)
|
|
68
|
-
throw new CompositeError(
|
|
69
|
-
`ETag conflict ${this.uri} ${Range} expected: ${this.metadata.etag} got: ${metadata.etag}`,
|
|
70
|
-
ErrorCodes.Conflict,
|
|
71
|
-
null,
|
|
72
|
-
);
|
|
73
|
-
return response.arrayBuffer();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
throw new CompositeError(`Failed to fetch ${this.uri} ${Range}`, response.status, response.statusText);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Allow overwriting the fetcher used (eg testing/node-js)
|
|
80
|
-
static fetch: FetchLike = (a, b) => fetch(a, b);
|
|
81
|
-
}
|
package/src/index.ts
DELETED