@siafoundation/sia-storage 0.1.1 → 0.1.3
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 +20 -0
- package/dist/{chunk-QYQQIWFH.js → chunk-QICJ5CIY.js} +3 -2
- package/dist/chunk-QICJ5CIY.js.map +1 -0
- package/dist/index.d.ts +48 -5
- package/dist/index.js +73 -22
- package/dist/index.js.map +1 -1
- package/dist/index.node.cjs +31 -12
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.ts +14 -2
- package/dist/next.js +1 -1
- package/dist/sia-storage-sw.js +2 -2
- package/dist/stream-worker.js +88 -7
- package/dist/stream-worker.js.map +1 -1
- package/package.json +8 -8
- package/wasm/sia_storage_wasm.d.ts +7 -7
- package/wasm/sia_storage_wasm.js +23 -23
- package/wasm/sia_storage_wasm_bg.wasm +0 -0
- package/wasm/sia_storage_wasm_bg.wasm.d.ts +7 -7
- package/dist/chunk-QYQQIWFH.js.map +0 -1
package/dist/stream-worker.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
parseStreamPath,
|
|
4
4
|
requestReply,
|
|
5
5
|
streamErrorMessage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QICJ5CIY.js";
|
|
7
7
|
|
|
8
8
|
// src/stream-worker.ts
|
|
9
9
|
import wasmInit, {
|
|
@@ -88,6 +88,7 @@ function errorResponse(status) {
|
|
|
88
88
|
// src/stream/worker.ts
|
|
89
89
|
var SOURCE_TIMEOUT = 5e3;
|
|
90
90
|
var IDLE_TIMEOUT = 5 * 6e4;
|
|
91
|
+
var REPORT_INTERVAL = 100;
|
|
91
92
|
function serveStreams(scope, connect, supportsDownloads) {
|
|
92
93
|
const sources = /* @__PURE__ */ new Map();
|
|
93
94
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -178,6 +179,9 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
178
179
|
const session = sessionFor(client.id, reply);
|
|
179
180
|
source.session = session;
|
|
180
181
|
session.sources.add(source);
|
|
182
|
+
if (reply.events?.length) {
|
|
183
|
+
source.reporter = createReporter(client, token, reply.events);
|
|
184
|
+
}
|
|
181
185
|
idle(session);
|
|
182
186
|
const { objectId, sealed, name, mime, size } = reply;
|
|
183
187
|
return { objectId, sealed, name, mime, size };
|
|
@@ -188,7 +192,8 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
188
192
|
closed: false,
|
|
189
193
|
requests: /* @__PURE__ */ new Set(),
|
|
190
194
|
ready,
|
|
191
|
-
setup
|
|
195
|
+
setup,
|
|
196
|
+
reading: 0
|
|
192
197
|
};
|
|
193
198
|
sources.set(sourceKey, source);
|
|
194
199
|
return source;
|
|
@@ -197,6 +202,7 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
197
202
|
if (source.closed) return;
|
|
198
203
|
source.closed = true;
|
|
199
204
|
sources.delete(source.key);
|
|
205
|
+
source.reporter?.stop();
|
|
200
206
|
source.setup.abort(new DOMException("Released", "AbortError"));
|
|
201
207
|
for (const cancel of source.requests) cancel();
|
|
202
208
|
source.session?.sources.delete(source);
|
|
@@ -219,7 +225,11 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
219
225
|
}) : void 0;
|
|
220
226
|
void Promise.resolve(cancellation).finally(() => {
|
|
221
227
|
reader?.releaseLock();
|
|
222
|
-
|
|
228
|
+
const { source } = job;
|
|
229
|
+
source?.requests.delete(job.abort);
|
|
230
|
+
if (source && job.reading && --source.reading === 0) {
|
|
231
|
+
source.reporter?.status("idle");
|
|
232
|
+
}
|
|
223
233
|
job.session?.jobs.delete(lifetime);
|
|
224
234
|
if (job.session) idle(job.session);
|
|
225
235
|
complete();
|
|
@@ -277,7 +287,14 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
277
287
|
);
|
|
278
288
|
job.range = range;
|
|
279
289
|
const headers = responseHeaders(metadata, range, download);
|
|
280
|
-
if (request.method === "HEAD" || range.status === 416
|
|
290
|
+
if (request.method === "HEAD" || range.status === 416) {
|
|
291
|
+
job.finish();
|
|
292
|
+
return new Response(null, { status: range.status, headers });
|
|
293
|
+
}
|
|
294
|
+
const { reporter } = source;
|
|
295
|
+
job.reading = true;
|
|
296
|
+
if (++source.reading === 1) reporter?.status("connecting");
|
|
297
|
+
if (range.length === 0) {
|
|
281
298
|
job.finish();
|
|
282
299
|
return new Response(null, { status: range.status, headers });
|
|
283
300
|
}
|
|
@@ -285,7 +302,13 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
285
302
|
if (job.closed) return errorResponse(502);
|
|
286
303
|
const object = await objectFor(session, sdk, metadata);
|
|
287
304
|
if (job.closed) return errorResponse(502);
|
|
288
|
-
const chunks = sdk.download(object, {
|
|
305
|
+
const chunks = sdk.download(object, {
|
|
306
|
+
offset: range.offset,
|
|
307
|
+
length: range.length,
|
|
308
|
+
...reporter?.wants.shards && {
|
|
309
|
+
onShardDownloaded: (progress) => reporter.shard(progress)
|
|
310
|
+
}
|
|
311
|
+
}).getReader();
|
|
289
312
|
job.reader = chunks;
|
|
290
313
|
return new Response(rangeBody(job, chunks, range.length), {
|
|
291
314
|
status: range.status,
|
|
@@ -361,6 +384,9 @@ function rangeBody(job, chunks, length) {
|
|
|
361
384
|
throw new Error("Shared file stream exceeded the requested length.");
|
|
362
385
|
}
|
|
363
386
|
controller.enqueue(value);
|
|
387
|
+
const reporter = job.source?.reporter;
|
|
388
|
+
reporter?.status("downloading");
|
|
389
|
+
reporter?.bytes(value.byteLength);
|
|
364
390
|
if (remaining === 0) {
|
|
365
391
|
controller.close();
|
|
366
392
|
job.finish(true);
|
|
@@ -377,6 +403,61 @@ function rangeBody(job, chunks, length) {
|
|
|
377
403
|
{ highWaterMark: 0 }
|
|
378
404
|
);
|
|
379
405
|
}
|
|
406
|
+
function createReporter(client, token, events) {
|
|
407
|
+
const wants = {
|
|
408
|
+
status: events.includes("status"),
|
|
409
|
+
progress: events.includes("progress"),
|
|
410
|
+
shards: events.includes("shards")
|
|
411
|
+
};
|
|
412
|
+
let stopped = false;
|
|
413
|
+
let current;
|
|
414
|
+
let bytes = 0;
|
|
415
|
+
let shards = [];
|
|
416
|
+
let timer;
|
|
417
|
+
function post(message) {
|
|
418
|
+
try {
|
|
419
|
+
client.postMessage(message);
|
|
420
|
+
} catch {
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function flush() {
|
|
424
|
+
clearTimeout(timer);
|
|
425
|
+
timer = void 0;
|
|
426
|
+
if (stopped || bytes === 0 && shards.length === 0) return;
|
|
427
|
+
post({ type: "sia-stream-progress", token, bytes, shards });
|
|
428
|
+
bytes = 0;
|
|
429
|
+
shards = [];
|
|
430
|
+
}
|
|
431
|
+
function schedule() {
|
|
432
|
+
timer ??= setTimeout(flush, REPORT_INTERVAL);
|
|
433
|
+
}
|
|
434
|
+
function status(next) {
|
|
435
|
+
if (stopped || next === current) return;
|
|
436
|
+
flush();
|
|
437
|
+
current = next;
|
|
438
|
+
if (wants.status) post({ type: "sia-stream-status", token, status: next });
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
wants,
|
|
442
|
+
status,
|
|
443
|
+
bytes(count) {
|
|
444
|
+
if (stopped || !wants.progress) return;
|
|
445
|
+
bytes += count;
|
|
446
|
+
schedule();
|
|
447
|
+
},
|
|
448
|
+
shard({ hostKey, shardSize, shardIndex, slabIndex, elapsedMs }) {
|
|
449
|
+
if (stopped) return;
|
|
450
|
+
shards.push({ hostKey, shardSize, shardIndex, slabIndex, elapsedMs });
|
|
451
|
+
schedule();
|
|
452
|
+
},
|
|
453
|
+
stop() {
|
|
454
|
+
stopped = true;
|
|
455
|
+
clearTimeout(timer);
|
|
456
|
+
timer = void 0;
|
|
457
|
+
shards = [];
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
380
461
|
|
|
381
462
|
// src/stream-worker.ts
|
|
382
463
|
function installStreams(scope, options = {}) {
|
|
@@ -402,7 +483,7 @@ async function connectShared({
|
|
|
402
483
|
const sdk = await SharedSdk.connect(indexerUrl, seed);
|
|
403
484
|
return {
|
|
404
485
|
object: ({ objectId }) => sdk.object(objectId),
|
|
405
|
-
download: (object,
|
|
486
|
+
download: (object, options) => sdk.download(object, options),
|
|
406
487
|
free: () => sdk.free()
|
|
407
488
|
};
|
|
408
489
|
}
|
|
@@ -419,7 +500,7 @@ async function connectApp({
|
|
|
419
500
|
return {
|
|
420
501
|
// The page sent the object sealed with this key, so no lookup is needed.
|
|
421
502
|
object: async ({ sealed }) => PinnedObject.open(key, sealed),
|
|
422
|
-
download: (object,
|
|
503
|
+
download: (object, options) => sdk.download(object, options),
|
|
423
504
|
free: () => {
|
|
424
505
|
sdk.free();
|
|
425
506
|
key.free();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stream-worker.ts","../src/stream/response.ts","../src/stream/worker.ts"],"sourcesContent":["/**\n * The worker side of streaming, for sites that already run a service worker.\n *\n * A page has only one service worker, so a site with its own calls\n * `installStreams(self)` in it and passes that worker's URL to\n * `enableStreaming`. Every other site serves the prebuilt `sia-storage-sw.js`,\n * which is this function with the WebAssembly built in.\n */\nimport wasmInit, {\n AppKey,\n Builder,\n type InitInput,\n PinnedObject,\n SharedSdk,\n} from '../wasm/sia_storage_wasm.js'\nimport type { Connection } from './stream/protocol'\nimport { serveStreams, type StreamScope, type StreamSdk } from './stream/worker'\n\nexport type { StreamScope }\n\nexport type InstallStreamsOptions = {\n /**\n * The SDK's WebAssembly, as a URL, bytes or compiled module. Only needed\n * when the worker's bundler does not emit `sia_storage_wasm_bg.wasm`.\n */\n wasm?: InitInput\n}\n\nexport function installStreams(\n scope: StreamScope,\n options: InstallStreamsOptions = {},\n) {\n let ready: Promise<unknown> | undefined\n serveStreams(\n scope,\n async (connection) => {\n ready ??= wasmInit(\n options.wasm === undefined ? undefined : { module_or_path: options.wasm },\n )\n await ready\n return connection.kind === 'shared'\n ? connectShared(connection)\n : connectApp(connection)\n },\n // Downloads from hosts need WebTransport, which some browsers leave out\n // of service workers. The page then reads objects itself.\n typeof WebTransport === 'function',\n )\n}\n\nasync function connectShared({\n indexerUrl,\n seed,\n}: Extract<Connection, { kind: 'shared' }>): Promise<StreamSdk> {\n const sdk = await SharedSdk.connect(indexerUrl, seed)\n return {\n object: ({ objectId }) => sdk.object(objectId),\n download: (object, range) => sdk.download(object, range),\n free: () => sdk.free(),\n }\n}\n\nasync function connectApp({\n indexerUrl,\n appKey,\n appMeta,\n}: Extract<Connection, { kind: 'app' }>): Promise<StreamSdk> {\n const key = new AppKey(fromHex(appKey))\n const builder = new Builder(indexerUrl, appMeta)\n try {\n const sdk = await builder.connected(key)\n if (!sdk) throw new Error('The app key is not registered with the indexer.')\n return {\n // The page sent the object sealed with this key, so no lookup is needed.\n object: async ({ sealed }) => PinnedObject.open(key, sealed!),\n download: (object, range) => sdk.download(object, range),\n free: () => {\n sdk.free()\n key.free()\n },\n }\n } catch (error) {\n key.free()\n throw error\n } finally {\n builder.free()\n }\n}\n\n// Uint8Array.fromHex is too new for some browsers that run service workers.\nfunction fromHex(hex: string) {\n return Uint8Array.from(hex.match(/../g) ?? [], (byte) => parseInt(byte, 16))\n}\n","// File bytes come from whoever owns the share, so the browser must never cache\n// them or run them as this origin's content.\nconst PROTECTIVE_HEADERS = {\n 'Cache-Control': 'no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'Content-Security-Policy': \"sandbox; default-src 'none'\",\n 'Referrer-Policy': 'no-referrer',\n}\n\nexport type ByteRange = {\n status: 200 | 206 | 416\n offset: number\n length: number\n}\n\nexport function parseRange(range: string | null, size: number): ByteRange {\n if (!Number.isSafeInteger(size) || size < 0) {\n throw new RangeError('Invalid file size')\n }\n if (range === null) return { status: 200, offset: 0, length: size }\n\n const invalid: ByteRange = { status: 416, offset: 0, length: 0 }\n const match = /^bytes=(\\d*)-(\\d*)$/i.exec(range.trim())\n if (!match || (!match[1] && !match[2]) || size === 0) return invalid\n // Both groups always participate in a match, possibly as empty strings.\n const start = match[1]!\n const end = match[2]!\n\n // Range numerals are not limited to JS's safe integers. Compare before converting.\n const total = BigInt(size)\n let first: bigint\n let last: bigint\n if (!start) {\n const suffix = BigInt(end)\n if (suffix === 0n) return invalid\n first = suffix >= total ? 0n : total - suffix\n last = total - 1n\n } else {\n first = BigInt(start)\n last = end ? BigInt(end) : total - 1n\n if (first >= total || last < first) return invalid\n if (last >= total) last = total - 1n\n }\n return {\n status: 206,\n offset: Number(first),\n length: Number(last - first + 1n),\n }\n}\n\nexport function contentDisposition(name: string, download: boolean) {\n // TextEncoder replaces lone surrogates without requiring toWellFormed(),\n // which older browsers with module service workers do not implement.\n const wellFormed = new TextDecoder().decode(new TextEncoder().encode(name))\n const filename =\n wellFormed\n // Filenames must not contain header controls or path separators.\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f/\\\\]/g, '_')\n .trim() || 'download'\n const fallback = filename.replace(/[^\\u0020-\\u007e]|[\";]/g, '_')\n const encoded = encodeURIComponent(filename).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n return `${download ? 'attachment' : 'inline'}; filename=\"${fallback}\"; filename*=UTF-8''${encoded}`\n}\n\nexport function responseHeaders(\n metadata: { name: string; mime: string; size: number },\n range: ByteRange,\n download: boolean,\n) {\n // Only use a media type, never arbitrary header syntax from shared metadata.\n const mime = (metadata.mime.split(';', 1)[0] ?? '').trim()\n const headers = new Headers({\n 'Accept-Ranges': 'bytes',\n 'Content-Length': String(range.length),\n 'Content-Type': /^[\\w!#$&^.+-]+\\/[\\w!#$&^.+-]+$/.test(mime)\n ? mime\n : 'application/octet-stream',\n 'Content-Disposition': contentDisposition(metadata.name, download),\n ...PROTECTIVE_HEADERS,\n })\n if (range.status === 206) {\n headers.set(\n 'Content-Range',\n `bytes ${range.offset}-${range.offset + range.length - 1}/${metadata.size}`,\n )\n } else if (range.status === 416) {\n headers.set('Content-Range', `bytes */${metadata.size}`)\n }\n return headers\n}\n\nexport function errorResponse(status: number) {\n const headers = new Headers(PROTECTIVE_HEADERS)\n if (status === 405) headers.set('Allow', 'GET, HEAD')\n return new Response(null, { status, headers })\n}\n","import type { PinnedObject } from '../../wasm/sia_storage_wasm.js'\nimport { streamErrorMessage } from './errors'\nimport {\n type ClientReply,\n isSourceReply,\n type PageMessage,\n parseStreamPath,\n requestReply,\n type Connection,\n type SourceDetails,\n type WorkerMessage,\n} from './protocol'\nimport {\n type ByteRange,\n errorResponse,\n parseRange,\n responseHeaders,\n} from './response'\n\nconst SOURCE_TIMEOUT = 5_000\nconst IDLE_TIMEOUT = 5 * 60_000\n\n/** An object as the page describes it. See `SourceDetails`. */\nexport type ObjectRef = Pick<SourceDetails, 'objectId' | 'sealed'>\n\n/** A connected SDK of either kind, as the worker uses it. */\nexport type StreamSdk = {\n object(ref: ObjectRef): Promise<PinnedObject>\n download(\n object: PinnedObject,\n range: { offset: number; length: number },\n ): ReadableStream\n free(): void\n}\n\n// The parts of ServiceWorkerGlobalScope this module uses, written out so it\n// type-checks alongside page code, whose DOM types conflict with the service\n// worker ones. A real ServiceWorkerGlobalScope satisfies them.\ntype StreamClient = { id: string; postMessage(message: unknown, transfer?: Transferable[]): void }\ntype WaitUntil = { waitUntil(promise: Promise<unknown>): void }\ntype StreamFetchEvent = { request: Request; respondWith(response: Promise<Response>): void }\ntype StreamMessageEvent = WaitUntil & {\n data: unknown\n source: unknown\n ports: readonly MessagePort[]\n}\nexport type StreamScope = {\n location: { origin: string }\n skipWaiting(): Promise<void>\n clients: {\n get(id: string): Promise<StreamClient | undefined>\n claim(): Promise<void>\n }\n addEventListener(type: 'install' | 'activate', listener: (event: WaitUntil) => void): void\n addEventListener(type: 'message', listener: (event: StreamMessageEvent) => void): void\n addEventListener(type: 'fetch', listener: (event: StreamFetchEvent) => void): void\n}\ntype Metadata = Pick<\n SourceDetails,\n 'objectId' | 'sealed' | 'name' | 'mime' | 'size'\n>\ntype Chunks = ReadableStreamDefaultReader<Uint8Array>\n\n/** One page's open share, and the SDK the worker connects for it on first use. */\ntype Session = {\n key: string\n connection: Connection\n sources: Set<Source>\n objects: Map<string, Promise<PinnedObject>>\n // One `Job.lifetime` per request in flight.\n jobs: Set<Promise<void>>\n closed: boolean\n timer?: ReturnType<typeof setTimeout>\n sdk?: Promise<StreamSdk>\n}\n\n/** One stream token, resolved to its file by asking the page that made it. */\ntype Source = {\n key: string\n client: StreamClient\n closed: boolean\n // The `abort` of each job serving this token.\n requests: Set<() => void>\n ready: Promise<Metadata>\n setup: AbortController\n session?: Session\n}\n\n/** One HTTP request for a stream URL, from the first await to its last byte. */\ntype Job = {\n closed: boolean\n lifetime: Promise<void>\n source?: Source\n session?: Session\n range?: ByteRange\n reader?: Chunks\n controller?: ReadableStreamDefaultController<Uint8Array>\n finish(cancel?: boolean): void\n abort(): void\n fail(error: unknown): void\n}\n\n// The control port carries credentials and metadata only. Bytes never leave\n// this worker, since each response consumes the SDK's own stream with native\n// backpressure.\nexport function serveStreams(\n scope: StreamScope,\n connect: (connection: Connection) => Promise<StreamSdk>,\n supportsDownloads: boolean,\n) {\n const sources = new Map<string, Source>()\n const sessions = new Map<string, Session>()\n // Released session keys. A page reply already in flight when its share is\n // released must not bring the session back.\n const released = new Set<string>()\n const key = (clientId: string, id: string) => JSON.stringify([clientId, id])\n\n function idle(session: Session) {\n clearTimeout(session.timer)\n if (!session.closed && session.jobs.size === 0) {\n session.timer = setTimeout(\n () => void disposeSession(session),\n IDLE_TIMEOUT,\n )\n }\n }\n\n function sessionFor(clientId: string, details: SourceDetails) {\n const sessionKey = key(clientId, details.session)\n let session = sessions.get(sessionKey)\n if (!session) {\n session = {\n key: sessionKey,\n connection: details.connection,\n sources: new Set(),\n objects: new Map(),\n jobs: new Set(),\n closed: false,\n }\n sessions.set(sessionKey, session)\n }\n return session\n }\n\n async function disposeSession(session: Session) {\n if (session.closed) return\n session.closed = true\n sessions.delete(session.key)\n clearTimeout(session.timer)\n for (const source of session.sources) dropSource(source)\n // Do not free wasm handles while a connect, lookup or cancellation uses them.\n await Promise.allSettled(session.jobs)\n await Promise.allSettled(\n [...session.objects.values()].map(async (object) =>\n (await object).free(),\n ),\n )\n try {\n ;(await session.sdk)?.free()\n } catch {\n // Connection failures are reported by the request, not during disposal.\n }\n }\n\n // A failed connect or lookup is forgotten, so the next request retries it\n // instead of failing with the cached error.\n function sdkFor(session: Session) {\n if (session.sdk) return session.sdk\n const { connection } = session\n const connecting = Promise.resolve().then(() => connect(connection))\n session.sdk = connecting\n connecting.catch(() => {\n if (session.sdk === connecting) delete session.sdk\n })\n return connecting\n }\n\n function objectFor(session: Session, sdk: StreamSdk, ref: ObjectRef) {\n const { objectId } = ref\n const cached = session.objects.get(objectId)\n if (cached) return cached\n const lookup = Promise.resolve().then(() => sdk.object(ref))\n session.objects.set(objectId, lookup)\n lookup.catch(() => {\n if (session.objects.get(objectId) === lookup) {\n session.objects.delete(objectId)\n }\n })\n return lookup\n }\n\n function getSource(client: StreamClient, token: string) {\n const sourceKey = key(client.id, token)\n const cached = sources.get(sourceKey)\n if (cached) return cached\n const setup = new AbortController()\n const request = { type: 'sia-source', token } satisfies WorkerMessage\n const ready = requestReply(client, request, {\n timeout: SOURCE_TIMEOUT,\n signal: setup.signal,\n }).then((reply) => {\n if (!isSourceReply(reply)) {\n throw new Error('Shared file source unavailable.')\n }\n if (source.closed || released.has(key(client.id, reply.session))) {\n throw new DOMException('Released', 'AbortError')\n }\n const session = sessionFor(client.id, reply)\n source.session = session\n session.sources.add(source)\n idle(session)\n // Keep credentials in the session record, never in response metadata.\n const { objectId, sealed, name, mime, size } = reply\n return { objectId, sealed, name, mime, size }\n })\n const source: Source = {\n key: sourceKey,\n client,\n closed: false,\n requests: new Set(),\n ready,\n setup,\n }\n sources.set(sourceKey, source)\n return source\n }\n\n function dropSource(source: Source) {\n if (source.closed) return\n source.closed = true\n sources.delete(source.key)\n source.setup.abort(new DOMException('Released', 'AbortError'))\n for (const cancel of source.requests) cancel()\n source.session?.sources.delete(source)\n }\n\n /**\n * Tracks one request until it settles, whichever way that happens: its last\n * byte, the page aborting it, its source being released, or an SDK failure.\n */\n function startJob(request: Request, token: string): Job {\n let complete = () => {}\n // Kept out of event.waitUntil. Chromium kills a worker whose event runs\n // past 5 minutes, which would end any download still running at that mark.\n const lifetime = new Promise<void>((resolve) => {\n complete = resolve\n })\n const job: Job = {\n closed: false,\n lifetime,\n finish(cancel = false) {\n if (job.closed) return\n job.closed = true\n request.signal.removeEventListener('abort', job.abort)\n const { reader } = job\n const cancellation =\n cancel && reader ? reader.cancel().catch(() => {}) : undefined\n void Promise.resolve(cancellation).finally(() => {\n reader?.releaseLock()\n job.source?.requests.delete(job.abort)\n job.session?.jobs.delete(lifetime)\n if (job.session) idle(job.session)\n complete()\n })\n },\n abort() {\n if (job.closed) return\n job.controller?.error(new DOMException('Aborted', 'AbortError'))\n job.finish(true)\n },\n fail(error) {\n if (job.closed) return\n const message = streamErrorMessage(error)\n const aborted = error instanceof Error && error.name === 'AbortError'\n const { source, range } = job\n if (!aborted && source && !source.closed) {\n // Diagnostics go only to the owning page. Never log SDK objects here.\n try {\n source.client.postMessage({\n type: 'sia-stream-error',\n token,\n message,\n ...(range && { offset: range.offset, length: range.length }),\n } satisfies WorkerMessage)\n } catch {\n // The owning tab may already be gone.\n }\n }\n job.controller?.error(new Error(message))\n job.finish(true)\n },\n }\n return job\n }\n\n async function serve(\n request: Request,\n clientId: string,\n token: string,\n download: boolean,\n ): Promise<Response> {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n return errorResponse(405)\n }\n if (!supportsDownloads) return errorResponse(503)\n const job = startJob(request, token)\n try {\n const client = await scope.clients.get(clientId)\n if (!client || request.signal.aborted) return errorResponse(404)\n const source = getSource(client, token)\n job.source = source\n source.requests.add(job.abort)\n request.signal.addEventListener('abort', job.abort, { once: true })\n const metadata = await source.ready\n if (job.closed) return errorResponse(502)\n // Set by `ready` before it resolves.\n const session = source.session!\n job.session = session\n session.jobs.add(job.lifetime)\n clearTimeout(session.timer)\n const range = parseRange(\n request.method === 'HEAD' ? null : request.headers.get('range'),\n metadata.size,\n )\n job.range = range\n const headers = responseHeaders(metadata, range, download)\n if (\n request.method === 'HEAD' ||\n range.status === 416 ||\n range.length === 0\n ) {\n job.finish()\n return new Response(null, { status: range.status, headers })\n }\n const sdk = await sdkFor(session)\n if (job.closed) return errorResponse(502)\n const object = await objectFor(session, sdk, metadata)\n if (job.closed) return errorResponse(502)\n const chunks: Chunks = sdk\n .download(object, { offset: range.offset, length: range.length })\n .getReader()\n job.reader = chunks\n return new Response(rangeBody(job, chunks, range.length), {\n status: range.status,\n headers,\n })\n } catch (error) {\n job.fail(error)\n if (job.source && !job.source.session) dropSource(job.source)\n return errorResponse(502)\n }\n }\n\n scope.addEventListener('install', (event) =>\n event.waitUntil(scope.skipWaiting()),\n )\n scope.addEventListener('activate', (event) =>\n event.waitUntil(scope.clients.claim()),\n )\n scope.addEventListener('message', (event) => {\n const source = event.source as Partial<StreamClient> | null\n const clientId = typeof source?.id === 'string' ? source.id : undefined\n if (!clientId) return\n const data = event.data as PageMessage | undefined\n if (data?.type === 'sia-client') {\n const reply: ClientReply = { clientId, supported: supportsDownloads }\n event.ports[0]?.postMessage(reply)\n event.ports[0]?.close()\n } else if (data?.type === 'sia-release' && typeof data.token === 'string') {\n // The session outlives its last source until IDLE_TIMEOUT, so moving to\n // the next file reuses the connected SDK.\n const source = sources.get(key(clientId, data.token))\n if (source) dropSource(source)\n } else if (\n data?.type === 'sia-release-share' &&\n typeof data.session === 'string'\n ) {\n const sessionKey = key(clientId, data.session)\n released.add(sessionKey)\n const session = sessions.get(sessionKey)\n if (session) event.waitUntil(disposeSession(session))\n }\n })\n scope.addEventListener('fetch', (event) => {\n const url = new URL(event.request.url)\n if (url.origin !== scope.location.origin) return\n const stream = parseStreamPath(url.pathname)\n if (!stream) return\n const download = url.searchParams.get('download') === '1'\n event.respondWith(\n serve(event.request, stream.clientId, stream.token, download),\n )\n })\n}\n\n/**\n * Exactly `length` bytes from `chunks`, or an error if the SDK stream ends\n * early or runs over. Each pull reads one chunk, so backpressure reaches the SDK.\n */\nfunction rangeBody(job: Job, chunks: Chunks, length: number) {\n let remaining = length\n return new ReadableStream<Uint8Array>(\n {\n start(controller) {\n job.controller = controller\n },\n async pull(controller) {\n try {\n const { done, value } = await chunks.read()\n if (job.closed) return\n if (done) {\n if (remaining !== 0) {\n throw new Error(\n 'Shared file stream ended before all bytes arrived.',\n )\n }\n controller.close()\n job.finish()\n return\n }\n remaining -= value.byteLength\n if (remaining < 0) {\n throw new Error('Shared file stream exceeded the requested length.')\n }\n controller.enqueue(value)\n if (remaining === 0) {\n controller.close()\n job.finish(true)\n }\n } catch (error) {\n job.fail(error)\n }\n },\n cancel() {\n job.finish(true)\n return job.lifetime\n },\n },\n { highWaterMark: 0 },\n )\n}\n"],"mappings":"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","names":["source"]}
|
|
1
|
+
{"version":3,"sources":["../src/stream-worker.ts","../src/stream/response.ts","../src/stream/worker.ts"],"sourcesContent":["/**\n * The worker side of streaming, for sites that already run a service worker.\n *\n * A page has only one service worker, so a site with its own calls\n * `installStreams(self)` in it and passes that worker's URL to\n * `enableStreaming`. Every other site serves the prebuilt `sia-storage-sw.js`,\n * which is this function with the WebAssembly built in.\n */\nimport wasmInit, {\n AppKey,\n Builder,\n type InitInput,\n PinnedObject,\n SharedSdk,\n} from '../wasm/sia_storage_wasm.js'\nimport type { Connection } from './stream/protocol'\nimport { serveStreams, type StreamScope, type StreamSdk } from './stream/worker'\n\nexport type { StreamScope }\n\nexport type InstallStreamsOptions = {\n /**\n * The SDK's WebAssembly, as a URL, bytes or compiled module. Only needed\n * when the worker's bundler does not emit `sia_storage_wasm_bg.wasm`.\n */\n wasm?: InitInput\n}\n\nexport function installStreams(\n scope: StreamScope,\n options: InstallStreamsOptions = {},\n) {\n let ready: Promise<unknown> | undefined\n serveStreams(\n scope,\n async (connection) => {\n ready ??= wasmInit(\n options.wasm === undefined ? undefined : { module_or_path: options.wasm },\n )\n await ready\n return connection.kind === 'shared'\n ? connectShared(connection)\n : connectApp(connection)\n },\n // Downloads from hosts need WebTransport, which some browsers leave out\n // of service workers. The page then reads objects itself.\n typeof WebTransport === 'function',\n )\n}\n\nasync function connectShared({\n indexerUrl,\n seed,\n}: Extract<Connection, { kind: 'shared' }>): Promise<StreamSdk> {\n const sdk = await SharedSdk.connect(indexerUrl, seed)\n return {\n object: ({ objectId }) => sdk.object(objectId),\n download: (object, options) => sdk.download(object, options),\n free: () => sdk.free(),\n }\n}\n\nasync function connectApp({\n indexerUrl,\n appKey,\n appMeta,\n}: Extract<Connection, { kind: 'app' }>): Promise<StreamSdk> {\n const key = new AppKey(fromHex(appKey))\n const builder = new Builder(indexerUrl, appMeta)\n try {\n const sdk = await builder.connected(key)\n if (!sdk) throw new Error('The app key is not registered with the indexer.')\n return {\n // The page sent the object sealed with this key, so no lookup is needed.\n object: async ({ sealed }) => PinnedObject.open(key, sealed!),\n download: (object, options) => sdk.download(object, options),\n free: () => {\n sdk.free()\n key.free()\n },\n }\n } catch (error) {\n key.free()\n throw error\n } finally {\n builder.free()\n }\n}\n\n// Uint8Array.fromHex is too new for some browsers that run service workers.\nfunction fromHex(hex: string) {\n return Uint8Array.from(hex.match(/../g) ?? [], (byte) => parseInt(byte, 16))\n}\n","// File bytes come from whoever owns the share, so the browser must never cache\n// them or run them as this origin's content.\nconst PROTECTIVE_HEADERS = {\n 'Cache-Control': 'no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'Content-Security-Policy': \"sandbox; default-src 'none'\",\n 'Referrer-Policy': 'no-referrer',\n}\n\nexport type ByteRange = {\n status: 200 | 206 | 416\n offset: number\n length: number\n}\n\nexport function parseRange(range: string | null, size: number): ByteRange {\n if (!Number.isSafeInteger(size) || size < 0) {\n throw new RangeError('Invalid file size')\n }\n if (range === null) return { status: 200, offset: 0, length: size }\n\n const invalid: ByteRange = { status: 416, offset: 0, length: 0 }\n const match = /^bytes=(\\d*)-(\\d*)$/i.exec(range.trim())\n if (!match || (!match[1] && !match[2]) || size === 0) return invalid\n // Both groups always participate in a match, possibly as empty strings.\n const start = match[1]!\n const end = match[2]!\n\n // Range numerals are not limited to JS's safe integers. Compare before converting.\n const total = BigInt(size)\n let first: bigint\n let last: bigint\n if (!start) {\n const suffix = BigInt(end)\n if (suffix === 0n) return invalid\n first = suffix >= total ? 0n : total - suffix\n last = total - 1n\n } else {\n first = BigInt(start)\n last = end ? BigInt(end) : total - 1n\n if (first >= total || last < first) return invalid\n if (last >= total) last = total - 1n\n }\n return {\n status: 206,\n offset: Number(first),\n length: Number(last - first + 1n),\n }\n}\n\nexport function contentDisposition(name: string, download: boolean) {\n // TextEncoder replaces lone surrogates without requiring toWellFormed(),\n // which older browsers with module service workers do not implement.\n const wellFormed = new TextDecoder().decode(new TextEncoder().encode(name))\n const filename =\n wellFormed\n // Filenames must not contain header controls or path separators.\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f/\\\\]/g, '_')\n .trim() || 'download'\n const fallback = filename.replace(/[^\\u0020-\\u007e]|[\";]/g, '_')\n const encoded = encodeURIComponent(filename).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n return `${download ? 'attachment' : 'inline'}; filename=\"${fallback}\"; filename*=UTF-8''${encoded}`\n}\n\nexport function responseHeaders(\n metadata: { name: string; mime: string; size: number },\n range: ByteRange,\n download: boolean,\n) {\n // Only use a media type, never arbitrary header syntax from shared metadata.\n const mime = (metadata.mime.split(';', 1)[0] ?? '').trim()\n const headers = new Headers({\n 'Accept-Ranges': 'bytes',\n 'Content-Length': String(range.length),\n 'Content-Type': /^[\\w!#$&^.+-]+\\/[\\w!#$&^.+-]+$/.test(mime)\n ? mime\n : 'application/octet-stream',\n 'Content-Disposition': contentDisposition(metadata.name, download),\n ...PROTECTIVE_HEADERS,\n })\n if (range.status === 206) {\n headers.set(\n 'Content-Range',\n `bytes ${range.offset}-${range.offset + range.length - 1}/${metadata.size}`,\n )\n } else if (range.status === 416) {\n headers.set('Content-Range', `bytes */${metadata.size}`)\n }\n return headers\n}\n\nexport function errorResponse(status: number) {\n const headers = new Headers(PROTECTIVE_HEADERS)\n if (status === 405) headers.set('Allow', 'GET, HEAD')\n return new Response(null, { status, headers })\n}\n","import type {\n PinnedObject,\n ShardProgress,\n} from '../../wasm/sia_storage_wasm.js'\nimport { streamErrorMessage } from './errors'\nimport {\n type ClientReply,\n isSourceReply,\n type PageMessage,\n parseStreamPath,\n requestReply,\n type Connection,\n type SourceDetails,\n type StreamEvent,\n type StreamStatus,\n type WorkerMessage,\n} from './protocol'\nimport {\n type ByteRange,\n errorResponse,\n parseRange,\n responseHeaders,\n} from './response'\n\nconst SOURCE_TIMEOUT = 5_000\nconst IDLE_TIMEOUT = 5 * 60_000\n// How long bytes and shard reports for one URL collect before they are sent,\n// so a stream costs a few messages a second rather than one per chunk.\nconst REPORT_INTERVAL = 100\n\n/** An object as the page describes it. See `SourceDetails`. */\nexport type ObjectRef = Pick<SourceDetails, 'objectId' | 'sealed'>\n\n/** A connected SDK of either kind, as the worker uses it. */\nexport type StreamSdk = {\n object(ref: ObjectRef): Promise<PinnedObject>\n download(\n object: PinnedObject,\n options: {\n offset: number\n length: number\n onShardDownloaded?: (progress: ShardProgress) => void\n },\n ): ReadableStream\n free(): void\n}\n\n// The parts of ServiceWorkerGlobalScope this module uses, written out so it\n// type-checks alongside page code, whose DOM types conflict with the service\n// worker ones. A real ServiceWorkerGlobalScope satisfies them.\ntype StreamClient = { id: string; postMessage(message: unknown, transfer?: Transferable[]): void }\ntype WaitUntil = { waitUntil(promise: Promise<unknown>): void }\ntype StreamFetchEvent = { request: Request; respondWith(response: Promise<Response>): void }\ntype StreamMessageEvent = WaitUntil & {\n data: unknown\n source: unknown\n ports: readonly MessagePort[]\n}\nexport type StreamScope = {\n location: { origin: string }\n skipWaiting(): Promise<void>\n clients: {\n get(id: string): Promise<StreamClient | undefined>\n claim(): Promise<void>\n }\n addEventListener(type: 'install' | 'activate', listener: (event: WaitUntil) => void): void\n addEventListener(type: 'message', listener: (event: StreamMessageEvent) => void): void\n addEventListener(type: 'fetch', listener: (event: StreamFetchEvent) => void): void\n}\ntype Metadata = Pick<\n SourceDetails,\n 'objectId' | 'sealed' | 'name' | 'mime' | 'size'\n>\ntype Chunks = ReadableStreamDefaultReader<Uint8Array>\n\n/** One page's open share, and the SDK the worker connects for it on first use. */\ntype Session = {\n key: string\n connection: Connection\n sources: Set<Source>\n objects: Map<string, Promise<PinnedObject>>\n // One `Job.lifetime` per request in flight.\n jobs: Set<Promise<void>>\n closed: boolean\n timer?: ReturnType<typeof setTimeout>\n sdk?: Promise<StreamSdk>\n}\n\n/** One stream token, resolved to its file by asking the page that made it. */\ntype Source = {\n key: string\n client: StreamClient\n closed: boolean\n // The `abort` of each job serving this token.\n requests: Set<() => void>\n ready: Promise<Metadata>\n setup: AbortController\n session?: Session\n // Set when the page asked for status and progress messages.\n reporter?: Reporter\n // Requests for this URL past their HEAD and range checks and not yet\n // settled. Its status is `idle` while this is zero.\n reading: number\n}\n\n/** One HTTP request for a stream URL, from the first await to its last byte. */\ntype Job = {\n closed: boolean\n lifetime: Promise<void>\n source?: Source\n session?: Session\n range?: ByteRange\n // Counted in `source.reading` until the job settles.\n reading?: boolean\n reader?: Chunks\n controller?: ReadableStreamDefaultController<Uint8Array>\n finish(cancel?: boolean): void\n abort(): void\n fail(error: unknown): void\n}\n\n// The control port carries credentials and metadata only. Bytes never leave\n// this worker, since each response consumes the SDK's own stream with native\n// backpressure.\nexport function serveStreams(\n scope: StreamScope,\n connect: (connection: Connection) => Promise<StreamSdk>,\n supportsDownloads: boolean,\n) {\n const sources = new Map<string, Source>()\n const sessions = new Map<string, Session>()\n // Released session keys. A page reply already in flight when its share is\n // released must not bring the session back.\n const released = new Set<string>()\n const key = (clientId: string, id: string) => JSON.stringify([clientId, id])\n\n function idle(session: Session) {\n clearTimeout(session.timer)\n if (!session.closed && session.jobs.size === 0) {\n session.timer = setTimeout(\n () => void disposeSession(session),\n IDLE_TIMEOUT,\n )\n }\n }\n\n function sessionFor(clientId: string, details: SourceDetails) {\n const sessionKey = key(clientId, details.session)\n let session = sessions.get(sessionKey)\n if (!session) {\n session = {\n key: sessionKey,\n connection: details.connection,\n sources: new Set(),\n objects: new Map(),\n jobs: new Set(),\n closed: false,\n }\n sessions.set(sessionKey, session)\n }\n return session\n }\n\n async function disposeSession(session: Session) {\n if (session.closed) return\n session.closed = true\n sessions.delete(session.key)\n clearTimeout(session.timer)\n for (const source of session.sources) dropSource(source)\n // Do not free wasm handles while a connect, lookup or cancellation uses them.\n await Promise.allSettled(session.jobs)\n await Promise.allSettled(\n [...session.objects.values()].map(async (object) =>\n (await object).free(),\n ),\n )\n try {\n ;(await session.sdk)?.free()\n } catch {\n // Connection failures are reported by the request, not during disposal.\n }\n }\n\n // A failed connect or lookup is forgotten, so the next request retries it\n // instead of failing with the cached error.\n function sdkFor(session: Session) {\n if (session.sdk) return session.sdk\n const { connection } = session\n const connecting = Promise.resolve().then(() => connect(connection))\n session.sdk = connecting\n connecting.catch(() => {\n if (session.sdk === connecting) delete session.sdk\n })\n return connecting\n }\n\n function objectFor(session: Session, sdk: StreamSdk, ref: ObjectRef) {\n const { objectId } = ref\n const cached = session.objects.get(objectId)\n if (cached) return cached\n const lookup = Promise.resolve().then(() => sdk.object(ref))\n session.objects.set(objectId, lookup)\n lookup.catch(() => {\n if (session.objects.get(objectId) === lookup) {\n session.objects.delete(objectId)\n }\n })\n return lookup\n }\n\n function getSource(client: StreamClient, token: string) {\n const sourceKey = key(client.id, token)\n const cached = sources.get(sourceKey)\n if (cached) return cached\n const setup = new AbortController()\n const request = { type: 'sia-source', token } satisfies WorkerMessage\n const ready = requestReply(client, request, {\n timeout: SOURCE_TIMEOUT,\n signal: setup.signal,\n }).then((reply) => {\n if (!isSourceReply(reply)) {\n throw new Error('Shared file source unavailable.')\n }\n if (source.closed || released.has(key(client.id, reply.session))) {\n throw new DOMException('Released', 'AbortError')\n }\n const session = sessionFor(client.id, reply)\n source.session = session\n session.sources.add(source)\n if (reply.events?.length) {\n source.reporter = createReporter(client, token, reply.events)\n }\n idle(session)\n // Keep credentials in the session record, never in response metadata.\n const { objectId, sealed, name, mime, size } = reply\n return { objectId, sealed, name, mime, size }\n })\n const source: Source = {\n key: sourceKey,\n client,\n closed: false,\n requests: new Set(),\n ready,\n setup,\n reading: 0,\n }\n sources.set(sourceKey, source)\n return source\n }\n\n function dropSource(source: Source) {\n if (source.closed) return\n source.closed = true\n sources.delete(source.key)\n source.reporter?.stop()\n source.setup.abort(new DOMException('Released', 'AbortError'))\n for (const cancel of source.requests) cancel()\n source.session?.sources.delete(source)\n }\n\n /**\n * Tracks one request until it settles, whichever way that happens: its last\n * byte, the page aborting it, its source being released, or an SDK failure.\n */\n function startJob(request: Request, token: string): Job {\n let complete = () => {}\n // Kept out of event.waitUntil. Chromium kills a worker whose event runs\n // past 5 minutes, which would end any download still running at that mark.\n const lifetime = new Promise<void>((resolve) => {\n complete = resolve\n })\n const job: Job = {\n closed: false,\n lifetime,\n finish(cancel = false) {\n if (job.closed) return\n job.closed = true\n request.signal.removeEventListener('abort', job.abort)\n const { reader } = job\n const cancellation =\n cancel && reader ? reader.cancel().catch(() => {}) : undefined\n void Promise.resolve(cancellation).finally(() => {\n reader?.releaseLock()\n const { source } = job\n source?.requests.delete(job.abort)\n if (source && job.reading && --source.reading === 0) {\n source.reporter?.status('idle')\n }\n job.session?.jobs.delete(lifetime)\n if (job.session) idle(job.session)\n complete()\n })\n },\n abort() {\n if (job.closed) return\n job.controller?.error(new DOMException('Aborted', 'AbortError'))\n job.finish(true)\n },\n fail(error) {\n if (job.closed) return\n const message = streamErrorMessage(error)\n const aborted = error instanceof Error && error.name === 'AbortError'\n const { source, range } = job\n if (!aborted && source && !source.closed) {\n // Diagnostics go only to the owning page. Never log SDK objects here.\n try {\n source.client.postMessage({\n type: 'sia-stream-error',\n token,\n message,\n ...(range && { offset: range.offset, length: range.length }),\n } satisfies WorkerMessage)\n } catch {\n // The owning tab may already be gone.\n }\n }\n job.controller?.error(new Error(message))\n job.finish(true)\n },\n }\n return job\n }\n\n async function serve(\n request: Request,\n clientId: string,\n token: string,\n download: boolean,\n ): Promise<Response> {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n return errorResponse(405)\n }\n if (!supportsDownloads) return errorResponse(503)\n const job = startJob(request, token)\n try {\n const client = await scope.clients.get(clientId)\n if (!client || request.signal.aborted) return errorResponse(404)\n const source = getSource(client, token)\n job.source = source\n source.requests.add(job.abort)\n request.signal.addEventListener('abort', job.abort, { once: true })\n const metadata = await source.ready\n if (job.closed) return errorResponse(502)\n // Set by `ready` before it resolves.\n const session = source.session!\n job.session = session\n session.jobs.add(job.lifetime)\n clearTimeout(session.timer)\n const range = parseRange(\n request.method === 'HEAD' ? null : request.headers.get('range'),\n metadata.size,\n )\n job.range = range\n const headers = responseHeaders(metadata, range, download)\n if (request.method === 'HEAD' || range.status === 416) {\n job.finish()\n return new Response(null, { status: range.status, headers })\n }\n const { reporter } = source\n job.reading = true\n // A read that starts while another is already downloading leaves the\n // status at `downloading`.\n if (++source.reading === 1) reporter?.status('connecting')\n // A zero-byte file reports connecting then idle, as a read in the page\n // does, without starting an SDK download.\n if (range.length === 0) {\n job.finish()\n return new Response(null, { status: range.status, headers })\n }\n const sdk = await sdkFor(session)\n if (job.closed) return errorResponse(502)\n const object = await objectFor(session, sdk, metadata)\n if (job.closed) return errorResponse(502)\n const chunks: Chunks = sdk\n .download(object, {\n offset: range.offset,\n length: range.length,\n ...(reporter?.wants.shards && {\n onShardDownloaded: (progress) => reporter.shard(progress),\n }),\n })\n .getReader()\n job.reader = chunks\n return new Response(rangeBody(job, chunks, range.length), {\n status: range.status,\n headers,\n })\n } catch (error) {\n job.fail(error)\n if (job.source && !job.source.session) dropSource(job.source)\n return errorResponse(502)\n }\n }\n\n scope.addEventListener('install', (event) =>\n event.waitUntil(scope.skipWaiting()),\n )\n scope.addEventListener('activate', (event) =>\n event.waitUntil(scope.clients.claim()),\n )\n scope.addEventListener('message', (event) => {\n const source = event.source as Partial<StreamClient> | null\n const clientId = typeof source?.id === 'string' ? source.id : undefined\n if (!clientId) return\n const data = event.data as PageMessage | undefined\n if (data?.type === 'sia-client') {\n const reply: ClientReply = { clientId, supported: supportsDownloads }\n event.ports[0]?.postMessage(reply)\n event.ports[0]?.close()\n } else if (data?.type === 'sia-release' && typeof data.token === 'string') {\n // The session outlives its last source until IDLE_TIMEOUT, so moving to\n // the next file reuses the connected SDK.\n const source = sources.get(key(clientId, data.token))\n if (source) dropSource(source)\n } else if (\n data?.type === 'sia-release-share' &&\n typeof data.session === 'string'\n ) {\n const sessionKey = key(clientId, data.session)\n released.add(sessionKey)\n const session = sessions.get(sessionKey)\n if (session) event.waitUntil(disposeSession(session))\n }\n })\n scope.addEventListener('fetch', (event) => {\n const url = new URL(event.request.url)\n if (url.origin !== scope.location.origin) return\n const stream = parseStreamPath(url.pathname)\n if (!stream) return\n const download = url.searchParams.get('download') === '1'\n event.respondWith(\n serve(event.request, stream.clientId, stream.token, download),\n )\n })\n}\n\n/**\n * Exactly `length` bytes from `chunks`, or an error if the SDK stream ends\n * early or runs over. Each pull reads one chunk, so backpressure reaches the SDK.\n */\nfunction rangeBody(job: Job, chunks: Chunks, length: number) {\n let remaining = length\n return new ReadableStream<Uint8Array>(\n {\n start(controller) {\n job.controller = controller\n },\n async pull(controller) {\n try {\n const { done, value } = await chunks.read()\n if (job.closed) return\n if (done) {\n if (remaining !== 0) {\n throw new Error(\n 'Shared file stream ended before all bytes arrived.',\n )\n }\n controller.close()\n job.finish()\n return\n }\n remaining -= value.byteLength\n if (remaining < 0) {\n throw new Error('Shared file stream exceeded the requested length.')\n }\n controller.enqueue(value)\n const reporter = job.source?.reporter\n reporter?.status('downloading')\n reporter?.bytes(value.byteLength)\n if (remaining === 0) {\n controller.close()\n job.finish(true)\n }\n } catch (error) {\n job.fail(error)\n }\n },\n cancel() {\n job.finish(true)\n return job.lifetime\n },\n },\n { highWaterMark: 0 },\n )\n}\n\ntype Reporter = {\n wants: { shards: boolean }\n status(status: StreamStatus): void\n bytes(count: number): void\n shard(progress: ShardProgress): void\n stop(): void\n}\n\n/**\n * Status and progress messages for one stream URL, sent to the page that made\n * it. Statuses go at once and only when they change. Bytes and shards collect\n * for REPORT_INTERVAL. After `stop`, every call does nothing, since the SDK\n * can still report a shard from a download being cancelled.\n */\nfunction createReporter(\n client: StreamClient,\n token: string,\n events: StreamEvent[],\n): Reporter {\n const wants = {\n status: events.includes('status'),\n progress: events.includes('progress'),\n shards: events.includes('shards'),\n }\n let stopped = false\n let current: StreamStatus | undefined\n let bytes = 0\n let shards: ShardProgress[] = []\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function post(message: WorkerMessage) {\n try {\n client.postMessage(message)\n } catch {\n // The owning tab may already be gone.\n }\n }\n function flush() {\n clearTimeout(timer)\n timer = undefined\n if (stopped || (bytes === 0 && shards.length === 0)) return\n post({ type: 'sia-stream-progress', token, bytes, shards })\n bytes = 0\n shards = []\n }\n function schedule() {\n timer ??= setTimeout(flush, REPORT_INTERVAL)\n }\n function status(next: StreamStatus) {\n if (stopped || next === current) return\n flush()\n current = next\n if (wants.status) post({ type: 'sia-stream-status', token, status: next })\n }\n\n return {\n wants,\n status,\n bytes(count) {\n if (stopped || !wants.progress) return\n bytes += count\n schedule()\n },\n shard({ hostKey, shardSize, shardIndex, slabIndex, elapsedMs }) {\n if (stopped) return\n // Copied field by field, so only plain data crosses to the page.\n shards.push({ hostKey, shardSize, shardIndex, slabIndex, elapsedMs })\n schedule()\n },\n stop() {\n stopped = true\n clearTimeout(timer)\n timer = undefined\n shards = []\n },\n }\n}\n"],"mappings":"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","names":["source"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siafoundation/sia-storage",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -63,18 +63,18 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build && bun test",
|
|
64
64
|
"publish-npm": "bun run scripts/publish.ts"
|
|
65
65
|
},
|
|
66
|
-
"optionalDependencies": {
|
|
67
|
-
"@siafoundation/sia-storage-darwin-arm64": "0.1.1",
|
|
68
|
-
"@siafoundation/sia-storage-darwin-x64": "0.1.1",
|
|
69
|
-
"@siafoundation/sia-storage-linux-x64-gnu": "0.1.1",
|
|
70
|
-
"@siafoundation/sia-storage-linux-arm64-gnu": "0.1.1",
|
|
71
|
-
"@siafoundation/sia-storage-win32-x64-msvc": "0.1.1"
|
|
72
|
-
},
|
|
73
66
|
"devDependencies": {
|
|
74
67
|
"@types/bun": "^1.3.12",
|
|
75
68
|
"@types/node": "^24.13.2",
|
|
76
69
|
"playwright": "^1.60.0",
|
|
77
70
|
"tsup": "^8.4.0",
|
|
78
71
|
"typescript": "5.9.3"
|
|
72
|
+
},
|
|
73
|
+
"optionalDependencies": {
|
|
74
|
+
"@siafoundation/sia-storage-darwin-arm64": "0.1.3",
|
|
75
|
+
"@siafoundation/sia-storage-darwin-x64": "0.1.3",
|
|
76
|
+
"@siafoundation/sia-storage-linux-x64-gnu": "0.1.3",
|
|
77
|
+
"@siafoundation/sia-storage-linux-arm64-gnu": "0.1.3",
|
|
78
|
+
"@siafoundation/sia-storage-win32-x64-msvc": "0.1.3"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -794,13 +794,13 @@ export interface InitOutput {
|
|
|
794
794
|
readonly intounderlyingsink_write: (a: number, b: any) => any;
|
|
795
795
|
readonly intounderlyingsource_cancel: (a: number) => void;
|
|
796
796
|
readonly intounderlyingsource_pull: (a: number, b: any) => any;
|
|
797
|
-
readonly
|
|
798
|
-
readonly
|
|
799
|
-
readonly
|
|
800
|
-
readonly
|
|
801
|
-
readonly
|
|
802
|
-
readonly
|
|
803
|
-
readonly
|
|
797
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
798
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
799
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__3: (a: number, b: number, c: any) => [number, number];
|
|
800
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__4: (a: number, b: number, c: any) => [number, number];
|
|
801
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
|
|
802
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_: (a: number, b: number, c: any) => void;
|
|
803
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_: (a: number, b: number) => void;
|
|
804
804
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
805
805
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
806
806
|
readonly __wbindgen_exn_store: (a: number) => void;
|
package/wasm/sia_storage_wasm.js
CHANGED
|
@@ -1630,7 +1630,7 @@ function __wbg_get_imports() {
|
|
|
1630
1630
|
const a = state0.a;
|
|
1631
1631
|
state0.a = 0;
|
|
1632
1632
|
try {
|
|
1633
|
-
return
|
|
1633
|
+
return wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_(a, state0.b, arg0, arg1);
|
|
1634
1634
|
} finally {
|
|
1635
1635
|
state0.a = a;
|
|
1636
1636
|
}
|
|
@@ -1668,7 +1668,7 @@ function __wbg_get_imports() {
|
|
|
1668
1668
|
const a = state0.a;
|
|
1669
1669
|
state0.a = 0;
|
|
1670
1670
|
try {
|
|
1671
|
-
return
|
|
1671
|
+
return wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_(a, state0.b, arg0, arg1);
|
|
1672
1672
|
} finally {
|
|
1673
1673
|
state0.a = a;
|
|
1674
1674
|
}
|
|
@@ -1894,33 +1894,33 @@ function __wbg_get_imports() {
|
|
|
1894
1894
|
return ret;
|
|
1895
1895
|
},
|
|
1896
1896
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
1897
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
1898
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1897
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1063, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
1898
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_);
|
|
1899
1899
|
return ret;
|
|
1900
1900
|
},
|
|
1901
1901
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
|
1902
1902
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 525, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
1903
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1903
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_);
|
|
1904
1904
|
return ret;
|
|
1905
1905
|
},
|
|
1906
1906
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
1907
1907
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 581, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
1908
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1908
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_);
|
|
1909
1909
|
return ret;
|
|
1910
1910
|
},
|
|
1911
1911
|
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
|
1912
1912
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WebTransportBidirectionalStream")], shim_idx: 581, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
1913
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1913
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__3);
|
|
1914
1914
|
return ret;
|
|
1915
1915
|
},
|
|
1916
1916
|
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
|
1917
1917
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 581, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
1918
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1918
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__4);
|
|
1919
1919
|
return ret;
|
|
1920
1920
|
},
|
|
1921
1921
|
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
|
1922
1922
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 870, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
1923
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
1923
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_);
|
|
1924
1924
|
return ret;
|
|
1925
1925
|
},
|
|
1926
1926
|
__wbindgen_cast_0000000000000007: function(arg0) {
|
|
@@ -1975,44 +1975,44 @@ function __wbg_get_imports() {
|
|
|
1975
1975
|
};
|
|
1976
1976
|
}
|
|
1977
1977
|
|
|
1978
|
-
function
|
|
1979
|
-
wasm.
|
|
1978
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_(arg0, arg1) {
|
|
1979
|
+
wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_(arg0, arg1);
|
|
1980
1980
|
}
|
|
1981
1981
|
|
|
1982
|
-
function
|
|
1983
|
-
wasm.
|
|
1982
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_(arg0, arg1, arg2) {
|
|
1983
|
+
wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_(arg0, arg1, arg2);
|
|
1984
1984
|
}
|
|
1985
1985
|
|
|
1986
|
-
function
|
|
1987
|
-
const ret = wasm.
|
|
1986
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_(arg0, arg1, arg2) {
|
|
1987
|
+
const ret = wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_(arg0, arg1, arg2);
|
|
1988
1988
|
if (ret[1]) {
|
|
1989
1989
|
throw takeFromExternrefTable0(ret[0]);
|
|
1990
1990
|
}
|
|
1991
1991
|
}
|
|
1992
1992
|
|
|
1993
|
-
function
|
|
1994
|
-
const ret = wasm.
|
|
1993
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_(arg0, arg1, arg2) {
|
|
1994
|
+
const ret = wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_(arg0, arg1, arg2);
|
|
1995
1995
|
if (ret[1]) {
|
|
1996
1996
|
throw takeFromExternrefTable0(ret[0]);
|
|
1997
1997
|
}
|
|
1998
1998
|
}
|
|
1999
1999
|
|
|
2000
|
-
function
|
|
2001
|
-
const ret = wasm.
|
|
2000
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__3(arg0, arg1, arg2) {
|
|
2001
|
+
const ret = wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__3(arg0, arg1, arg2);
|
|
2002
2002
|
if (ret[1]) {
|
|
2003
2003
|
throw takeFromExternrefTable0(ret[0]);
|
|
2004
2004
|
}
|
|
2005
2005
|
}
|
|
2006
2006
|
|
|
2007
|
-
function
|
|
2008
|
-
const ret = wasm.
|
|
2007
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__4(arg0, arg1, arg2) {
|
|
2008
|
+
const ret = wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__4(arg0, arg1, arg2);
|
|
2009
2009
|
if (ret[1]) {
|
|
2010
2010
|
throw takeFromExternrefTable0(ret[0]);
|
|
2011
2011
|
}
|
|
2012
2012
|
}
|
|
2013
2013
|
|
|
2014
|
-
function
|
|
2015
|
-
wasm.
|
|
2014
|
+
function wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_(arg0, arg1, arg2, arg3) {
|
|
2015
|
+
wasm.wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_(arg0, arg1, arg2, arg3);
|
|
2016
2016
|
}
|
|
2017
2017
|
|
|
2018
2018
|
|
|
Binary file
|
|
@@ -99,13 +99,13 @@ export const intounderlyingsink_close: (a: number) => any;
|
|
|
99
99
|
export const intounderlyingsink_write: (a: number, b: any) => any;
|
|
100
100
|
export const intounderlyingsource_cancel: (a: number) => void;
|
|
101
101
|
export const intounderlyingsource_pull: (a: number, b: any) => any;
|
|
102
|
-
export const
|
|
103
|
-
export const
|
|
104
|
-
export const
|
|
105
|
-
export const
|
|
106
|
-
export const
|
|
107
|
-
export const
|
|
108
|
-
export const
|
|
102
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
103
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
104
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__3: (a: number, b: number, c: any) => [number, number];
|
|
105
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___sys__Undefined__core_608f92abc48d28da___result__Result_____wasm_bindgen_a939ac12ad536d64___JsError___true__4: (a: number, b: number, c: any) => [number, number];
|
|
106
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_7efaea6b2290715d___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
|
|
107
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_: (a: number, b: number, c: any) => void;
|
|
108
|
+
export const wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_: (a: number, b: number) => void;
|
|
109
109
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
110
110
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
111
111
|
export const __wbindgen_exn_store: (a: number) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stream/errors.ts","../src/stream/protocol.ts"],"sourcesContent":["/** Only sanitized strings cross the diagnostic boundary, never raw SDK objects. */\nexport function streamErrorMessage(error: unknown): string {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Unknown streaming failure'\n return message\n .replace(/\\b(?:https?|wss?|sia):\\/\\/[^\\s\"'<>]+/gi, '[URL redacted]')\n .replace(/(?:bearer\\s+)[^\\s,;]+/gi, 'Bearer [redacted]')\n .replace(\n /((?:seed|share|token|key|authorization)\\s*[=:]\\s*)[^\\s,;]+/gi,\n '$1[redacted]',\n )\n .replace(/[a-f0-9]{64,}/gi, '[redacted]')\n .replace(/[\\r\\n\\t]/g, ' ')\n .slice(0, 1000)\n}\n\nexport function reportStreamError(\n error: unknown,\n name: string,\n range: { offset: number; length: number } | undefined,\n) {\n const message = streamErrorMessage(error)\n // One string rather than an object, so it reads the same in every console.\n const bytes = range\n ? ` bytes ${range.offset}-${range.offset + range.length - 1}`\n : ''\n console.error(`[Sia stream] ${streamErrorMessage(name)}${bytes}: ${message}`)\n return message\n}\n","/**\n * Messages between a page and its streaming service worker.\n *\n * Stream URLs carry only the page's client id and a random token. When the\n * worker meets a token it does not know, it asks the page that made it for the\n * file and the share's credentials. The page and a worker can come from\n * different deployed versions, so the worker checks the page's reply before\n * using it.\n */\nimport type {\n AppMetadata,\n SealedObject,\n} from '../../wasm/sia_storage_wasm.js'\n\n/**\n * What the worker connects its own SDK with. A `SharedSdk` reconnects from its\n * sharing key's seed, and an `Sdk` from its app key, as hex, and app metadata.\n */\nexport type Connection =\n | { kind: 'shared'; indexerUrl: string; seed: string }\n | { kind: 'app'; indexerUrl: string; appKey: string; appMeta: AppMetadata }\n\nexport type SourceDetails = {\n // One per opened SDK, so the worker can release it as a whole.\n session: string\n connection: Connection\n objectId: string\n // An `Sdk`'s object, sealed with its app key, so the worker can open it\n // without a lookup. A `SharedSdk`'s object is looked up by `objectId`.\n sealed?: SealedObject\n name: string\n mime: string\n size: number\n}\n\n/** Page to worker. */\nexport type PageMessage =\n | { type: 'sia-client' }\n | { type: 'sia-release'; token: string }\n | { type: 'sia-release-share'; session: string }\n\n/** Worker to page. */\nexport type WorkerMessage =\n | { type: 'sia-source'; token: string }\n | {\n type: 'sia-stream-error'\n token: string\n message: string\n offset?: number\n length?: number\n }\n\n/** The worker's answer to `sia-client`. */\nexport type ClientReply = { clientId: string; supported: boolean }\n\n/** The page's answer to `sia-source`. */\nexport type SourceReply =\n | ({ type: 'source' } & SourceDetails)\n | { type: 'error' }\n\n// Stream URLs sit under the worker's scope, so a download's navigation, which\n// only a worker whose scope covers the URL can answer, reaches it on sites\n// served from a sub-path too.\nconst STREAM_PATH = /\\/__sia_stream__\\/([^/]+)\\/([^/]+)\\/[^/]+$/\n\n/** `scope` is the worker registration's scope URL, which ends in a slash. */\nexport function streamPath(scope: string, clientId: string, token: string) {\n return `${scope}__sia_stream__/${clientId}/${token}/file`\n}\n\nexport function parseStreamPath(pathname: string) {\n const match = STREAM_PATH.exec(pathname)\n return match ? { clientId: match[1]!, token: match[2]! } : undefined\n}\n\ntype Fields = Record<string, unknown> | null | undefined\n\nconst strings = (data: Fields, fields: string[]) =>\n fields.every((field) => typeof data?.[field] === 'string')\n\n/** Checked on both sides, so bad metadata fails in `openStreams`, not per request. */\nexport function isAppMetadata(value: unknown): value is AppMetadata {\n return strings(value as Fields, ['appId', 'name', 'description', 'serviceUrl'])\n}\n\nfunction isConnection(value: unknown): value is Connection {\n const data = value as Fields\n if (data?.kind === 'shared') return strings(data, ['indexerUrl', 'seed'])\n return (\n data?.kind === 'app' &&\n strings(data, ['indexerUrl', 'appKey']) &&\n isAppMetadata(data.appMeta)\n )\n}\n\nexport function isSourceReply(\n reply: unknown,\n): reply is { type: 'source' } & SourceDetails {\n const data = reply as Fields\n return (\n data?.type === 'source' &&\n strings(data, ['session', 'objectId', 'name', 'mime']) &&\n isConnection(data.connection) &&\n (data.connection.kind === 'shared' ||\n (typeof data.sealed === 'object' && data.sealed !== null)) &&\n Number.isSafeInteger(data.size) &&\n (data.size as number) >= 0\n )\n}\n\n/**\n * Posts `message` with a reply port and resolves with the first reply. Rejects\n * after `timeout` ms, or when `signal` aborts.\n */\nexport function requestReply(\n target: { postMessage(message: unknown, transfer: Transferable[]): void },\n message: unknown,\n { timeout, signal }: { timeout?: number; signal?: AbortSignal },\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const { port1, port2 } = new MessageChannel()\n const timer =\n timeout === undefined\n ? undefined\n : setTimeout(\n () => settle(() => reject(new Error('No reply in time.'))),\n timeout,\n )\n const abort = () => settle(() => reject(signal?.reason))\n function settle(outcome: () => void) {\n clearTimeout(timer)\n signal?.removeEventListener('abort', abort)\n port1.close()\n port2.close()\n outcome()\n }\n if (signal?.aborted) return abort()\n signal?.addEventListener('abort', abort, { once: true })\n port1.onmessage = ({ data }) => settle(() => resolve(data))\n port1.onmessageerror = () =>\n settle(() => reject(new Error('Unreadable reply.')))\n try {\n target.postMessage(message, [port2])\n } catch (error) {\n settle(() => reject(error))\n }\n })\n}\n"],"mappings":";AACO,SAAS,mBAAmB,OAAwB;AACzD,QAAM,UACJ,iBAAiB,QACb,MAAM,UACN,OAAO,UAAU,WACf,QACA;AACR,SAAO,QACJ,QAAQ,0CAA0C,gBAAgB,EAClE,QAAQ,2BAA2B,mBAAmB,EACtD;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,QAAQ,mBAAmB,YAAY,EACvC,QAAQ,aAAa,GAAG,EACxB,MAAM,GAAG,GAAI;AAClB;AAEO,SAAS,kBACd,OACA,MACA,OACA;AACA,QAAM,UAAU,mBAAmB,KAAK;AAExC,QAAM,QAAQ,QACV,UAAU,MAAM,MAAM,IAAI,MAAM,SAAS,MAAM,SAAS,CAAC,KACzD;AACJ,UAAQ,MAAM,gBAAgB,mBAAmB,IAAI,CAAC,GAAG,KAAK,KAAK,OAAO,EAAE;AAC5E,SAAO;AACT;;;AC+BA,IAAM,cAAc;AAGb,SAAS,WAAW,OAAe,UAAkB,OAAe;AACzE,SAAO,GAAG,KAAK,kBAAkB,QAAQ,IAAI,KAAK;AACpD;AAEO,SAAS,gBAAgB,UAAkB;AAChD,QAAM,QAAQ,YAAY,KAAK,QAAQ;AACvC,SAAO,QAAQ,EAAE,UAAU,MAAM,CAAC,GAAI,OAAO,MAAM,CAAC,EAAG,IAAI;AAC7D;AAIA,IAAM,UAAU,CAAC,MAAc,WAC7B,OAAO,MAAM,CAAC,UAAU,OAAO,OAAO,KAAK,MAAM,QAAQ;AAGpD,SAAS,cAAc,OAAsC;AAClE,SAAO,QAAQ,OAAiB,CAAC,SAAS,QAAQ,eAAe,YAAY,CAAC;AAChF;AAEA,SAAS,aAAa,OAAqC;AACzD,QAAM,OAAO;AACb,MAAI,MAAM,SAAS,SAAU,QAAO,QAAQ,MAAM,CAAC,cAAc,MAAM,CAAC;AACxE,SACE,MAAM,SAAS,SACf,QAAQ,MAAM,CAAC,cAAc,QAAQ,CAAC,KACtC,cAAc,KAAK,OAAO;AAE9B;AAEO,SAAS,cACd,OAC6C;AAC7C,QAAM,OAAO;AACb,SACE,MAAM,SAAS,YACf,QAAQ,MAAM,CAAC,WAAW,YAAY,QAAQ,MAAM,CAAC,KACrD,aAAa,KAAK,UAAU,MAC3B,KAAK,WAAW,SAAS,YACvB,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW,SACtD,OAAO,cAAc,KAAK,IAAI,KAC7B,KAAK,QAAmB;AAE7B;AAMO,SAAS,aACd,QACA,SACA,EAAE,SAAS,OAAO,GACA;AAClB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,EAAE,OAAO,MAAM,IAAI,IAAI,eAAe;AAC5C,UAAM,QACJ,YAAY,SACR,SACA;AAAA,MACE,MAAM,OAAO,MAAM,OAAO,IAAI,MAAM,mBAAmB,CAAC,CAAC;AAAA,MACzD;AAAA,IACF;AACN,UAAM,QAAQ,MAAM,OAAO,MAAM,OAAO,QAAQ,MAAM,CAAC;AACvD,aAAS,OAAO,SAAqB;AACnC,mBAAa,KAAK;AAClB,cAAQ,oBAAoB,SAAS,KAAK;AAC1C,YAAM,MAAM;AACZ,YAAM,MAAM;AACZ,cAAQ;AAAA,IACV;AACA,QAAI,QAAQ,QAAS,QAAO,MAAM;AAClC,YAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;AACvD,UAAM,YAAY,CAAC,EAAE,KAAK,MAAM,OAAO,MAAM,QAAQ,IAAI,CAAC;AAC1D,UAAM,iBAAiB,MACrB,OAAO,MAAM,OAAO,IAAI,MAAM,mBAAmB,CAAC,CAAC;AACrD,QAAI;AACF,aAAO,YAAY,SAAS,CAAC,KAAK,CAAC;AAAA,IACrC,SAAS,OAAO;AACd,aAAO,MAAM,OAAO,KAAK,CAAC;AAAA,IAC5B;AAAA,EACF,CAAC;AACH;","names":[]}
|