@siafoundation/sia-storage 0.1.3 → 0.1.4
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 +21 -3
- package/dist/{chunk-QICJ5CIY.js → chunk-YOMDUMYW.js} +5 -2
- package/dist/chunk-YOMDUMYW.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +48 -8
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +26 -13
- package/dist/next.js +1 -1
- package/dist/sia-storage-sw.js +2 -2
- package/dist/stream-worker.js +28 -12
- package/dist/stream-worker.js.map +1 -1
- package/package.json +8 -7
- package/wasm/sia_storage_wasm.d.ts +61 -38
- package/wasm/sia_storage_wasm.js +141 -40
- package/wasm/sia_storage_wasm_bg.wasm +0 -0
- package/wasm/sia_storage_wasm_bg.wasm.d.ts +10 -4
- package/dist/chunk-QICJ5CIY.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-YOMDUMYW.js";
|
|
7
7
|
|
|
8
8
|
// src/stream-worker.ts
|
|
9
9
|
import wasmInit, {
|
|
@@ -245,7 +245,7 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
245
245
|
const message = streamErrorMessage(error);
|
|
246
246
|
const aborted = error instanceof Error && error.name === "AbortError";
|
|
247
247
|
const { source, range } = job;
|
|
248
|
-
if (!aborted && source && !source.closed) {
|
|
248
|
+
if (!aborted && !job.quiet && source && !source.closed) {
|
|
249
249
|
try {
|
|
250
250
|
source.client.postMessage({
|
|
251
251
|
type: "sia-stream-error",
|
|
@@ -262,12 +262,13 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
262
262
|
};
|
|
263
263
|
return job;
|
|
264
264
|
}
|
|
265
|
-
async function serve(request, clientId, token, download) {
|
|
265
|
+
async function serve(request, clientId, token, { download, warm }) {
|
|
266
266
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
267
267
|
return errorResponse(405);
|
|
268
268
|
}
|
|
269
269
|
if (!supportsDownloads) return errorResponse(503);
|
|
270
270
|
const job = startJob(request, token);
|
|
271
|
+
job.quiet = warm;
|
|
271
272
|
try {
|
|
272
273
|
const client = await scope.clients.get(clientId);
|
|
273
274
|
if (!client || request.signal.aborted) return errorResponse(404);
|
|
@@ -291,9 +292,9 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
291
292
|
job.finish();
|
|
292
293
|
return new Response(null, { status: range.status, headers });
|
|
293
294
|
}
|
|
294
|
-
const
|
|
295
|
-
job.reading =
|
|
296
|
-
if (++source.reading === 1) reporter?.status("connecting");
|
|
295
|
+
const reporter = warm ? void 0 : source.reporter;
|
|
296
|
+
job.reading = !warm;
|
|
297
|
+
if (!warm && ++source.reading === 1) reporter?.status("connecting");
|
|
297
298
|
if (range.length === 0) {
|
|
298
299
|
job.finish();
|
|
299
300
|
return new Response(null, { status: range.status, headers });
|
|
@@ -353,8 +354,9 @@ function serveStreams(scope, connect, supportsDownloads) {
|
|
|
353
354
|
const stream = parseStreamPath(url.pathname);
|
|
354
355
|
if (!stream) return;
|
|
355
356
|
const download = url.searchParams.get("download") === "1";
|
|
357
|
+
const warm = url.searchParams.get("warm") === "1";
|
|
356
358
|
event.respondWith(
|
|
357
|
-
serve(event.request, stream.clientId, stream.token, download)
|
|
359
|
+
serve(event.request, stream.clientId, stream.token, { download, warm })
|
|
358
360
|
);
|
|
359
361
|
});
|
|
360
362
|
}
|
|
@@ -384,7 +386,7 @@ function rangeBody(job, chunks, length) {
|
|
|
384
386
|
throw new Error("Shared file stream exceeded the requested length.");
|
|
385
387
|
}
|
|
386
388
|
controller.enqueue(value);
|
|
387
|
-
const reporter = job.source?.reporter;
|
|
389
|
+
const reporter = job.quiet ? void 0 : job.source?.reporter;
|
|
388
390
|
reporter?.status("downloading");
|
|
389
391
|
reporter?.bytes(value.byteLength);
|
|
390
392
|
if (remaining === 0) {
|
|
@@ -478,13 +480,27 @@ function installStreams(scope, options = {}) {
|
|
|
478
480
|
}
|
|
479
481
|
async function connectShared({
|
|
480
482
|
indexerUrl,
|
|
481
|
-
seed
|
|
483
|
+
seed,
|
|
484
|
+
sealKey
|
|
482
485
|
}) {
|
|
483
|
-
const
|
|
486
|
+
const key = sealKey === void 0 ? void 0 : new AppKey(fromHex(sealKey));
|
|
487
|
+
let sdk;
|
|
488
|
+
try {
|
|
489
|
+
sdk = await SharedSdk.connect(indexerUrl, seed);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
key?.free();
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
484
494
|
return {
|
|
485
|
-
object
|
|
495
|
+
// The page sealed the object it already had with the share's random key.
|
|
496
|
+
// Opening that saves a round trip to the indexer before every stream's
|
|
497
|
+
// first byte.
|
|
498
|
+
object: async ({ objectId, sealed }) => key && sealed ? PinnedObject.open(key, sealed) : sdk.object(objectId),
|
|
486
499
|
download: (object, options) => sdk.download(object, options),
|
|
487
|
-
free: () =>
|
|
500
|
+
free: () => {
|
|
501
|
+
sdk.free();
|
|
502
|
+
key?.free();
|
|
503
|
+
}
|
|
488
504
|
};
|
|
489
505
|
}
|
|
490
506
|
async function connectApp({
|
|
@@ -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, 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":";;;;;;;;AAQA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;;;ACZP,IAAM,qBAAqB;AAAA,EACzB,iBAAiB;AAAA,EACjB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,mBAAmB;AACrB;AAQO,SAAS,WAAW,OAAsB,MAAyB;AACxE,MAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,GAAG;AAC3C,UAAM,IAAI,WAAW,mBAAmB;AAAA,EAC1C;AACA,MAAI,UAAU,KAAM,QAAO,EAAE,QAAQ,KAAK,QAAQ,GAAG,QAAQ,KAAK;AAElE,QAAM,UAAqB,EAAE,QAAQ,KAAK,QAAQ,GAAG,QAAQ,EAAE;AAC/D,QAAM,QAAQ,uBAAuB,KAAK,MAAM,KAAK,CAAC;AACtD,MAAI,CAAC,SAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAM,SAAS,EAAG,QAAO;AAE7D,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAM,MAAM,MAAM,CAAC;AAGnB,QAAM,QAAQ,OAAO,IAAI;AACzB,MAAI;AACJ,MAAI;AACJ,MAAI,CAAC,OAAO;AACV,UAAM,SAAS,OAAO,GAAG;AACzB,QAAI,WAAW,GAAI,QAAO;AAC1B,YAAQ,UAAU,QAAQ,KAAK,QAAQ;AACvC,WAAO,QAAQ;AAAA,EACjB,OAAO;AACL,YAAQ,OAAO,KAAK;AACpB,WAAO,MAAM,OAAO,GAAG,IAAI,QAAQ;AACnC,QAAI,SAAS,SAAS,OAAO,MAAO,QAAO;AAC3C,QAAI,QAAQ,MAAO,QAAO,QAAQ;AAAA,EACpC;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ,OAAO,KAAK;AAAA,IACpB,QAAQ,OAAO,OAAO,QAAQ,EAAE;AAAA,EAClC;AACF;AAEO,SAAS,mBAAmB,MAAc,UAAmB;AAGlE,QAAM,aAAa,IAAI,YAAY,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAC1E,QAAM,WACJ,WAGG,QAAQ,6BAA6B,GAAG,EACxC,KAAK,KAAK;AACf,QAAM,WAAW,SAAS,QAAQ,0BAA0B,GAAG;AAC/D,QAAM,UAAU,mBAAmB,QAAQ,EAAE;AAAA,IAC3C;AAAA,IACA,CAAC,SAAS,IAAI,KAAK,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE,YAAY,CAAC;AAAA,EAC7D;AACA,SAAO,GAAG,WAAW,eAAe,QAAQ,eAAe,QAAQ,uBAAuB,OAAO;AACnG;AAEO,SAAS,gBACd,UACA,OACA,UACA;AAEA,QAAM,QAAQ,SAAS,KAAK,MAAM,KAAK,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK;AACzD,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,iBAAiB;AAAA,IACjB,kBAAkB,OAAO,MAAM,MAAM;AAAA,IACrC,gBAAgB,iCAAiC,KAAK,IAAI,IACtD,OACA;AAAA,IACJ,uBAAuB,mBAAmB,SAAS,MAAM,QAAQ;AAAA,IACjE,GAAG;AAAA,EACL,CAAC;AACD,MAAI,MAAM,WAAW,KAAK;AACxB,YAAQ;AAAA,MACN;AAAA,MACA,SAAS,MAAM,MAAM,IAAI,MAAM,SAAS,MAAM,SAAS,CAAC,IAAI,SAAS,IAAI;AAAA,IAC3E;AAAA,EACF,WAAW,MAAM,WAAW,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,WAAW,SAAS,IAAI,EAAE;AAAA,EACzD;AACA,SAAO;AACT;AAEO,SAAS,cAAc,QAAgB;AAC5C,QAAM,UAAU,IAAI,QAAQ,kBAAkB;AAC9C,MAAI,WAAW,IAAK,SAAQ,IAAI,SAAS,WAAW;AACpD,SAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,QAAQ,CAAC;AAC/C;;;AC3EA,IAAM,iBAAiB;AACvB,IAAM,eAAe,IAAI;AAGzB,IAAM,kBAAkB;AAgGjB,SAAS,aACd,OACA,SACA,mBACA;AACA,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,WAAW,oBAAI,IAAqB;AAG1C,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,MAAM,CAAC,UAAkB,OAAe,KAAK,UAAU,CAAC,UAAU,EAAE,CAAC;AAE3E,WAAS,KAAK,SAAkB;AAC9B,iBAAa,QAAQ,KAAK;AAC1B,QAAI,CAAC,QAAQ,UAAU,QAAQ,KAAK,SAAS,GAAG;AAC9C,cAAQ,QAAQ;AAAA,QACd,MAAM,KAAK,eAAe,OAAO;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,WAAS,WAAW,UAAkB,SAAwB;AAC5D,UAAM,aAAa,IAAI,UAAU,QAAQ,OAAO;AAChD,QAAI,UAAU,SAAS,IAAI,UAAU;AACrC,QAAI,CAAC,SAAS;AACZ,gBAAU;AAAA,QACR,KAAK;AAAA,QACL,YAAY,QAAQ;AAAA,QACpB,SAAS,oBAAI,IAAI;AAAA,QACjB,SAAS,oBAAI,IAAI;AAAA,QACjB,MAAM,oBAAI,IAAI;AAAA,QACd,QAAQ;AAAA,MACV;AACA,eAAS,IAAI,YAAY,OAAO;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAEA,iBAAe,eAAe,SAAkB;AAC9C,QAAI,QAAQ,OAAQ;AACpB,YAAQ,SAAS;AACjB,aAAS,OAAO,QAAQ,GAAG;AAC3B,iBAAa,QAAQ,KAAK;AAC1B,eAAW,UAAU,QAAQ,QAAS,YAAW,MAAM;AAEvD,UAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAM,QAAQ;AAAA,MACZ,CAAC,GAAG,QAAQ,QAAQ,OAAO,CAAC,EAAE;AAAA,QAAI,OAAO,YACtC,MAAM,QAAQ,KAAK;AAAA,MACtB;AAAA,IACF;AACA,QAAI;AACF;AAAC,OAAC,MAAM,QAAQ,MAAM,KAAK;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF;AAIA,WAAS,OAAO,SAAkB;AAChC,QAAI,QAAQ,IAAK,QAAO,QAAQ;AAChC,UAAM,EAAE,WAAW,IAAI;AACvB,UAAM,aAAa,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,UAAU,CAAC;AACnE,YAAQ,MAAM;AACd,eAAW,MAAM,MAAM;AACrB,UAAI,QAAQ,QAAQ,WAAY,QAAO,QAAQ;AAAA,IACjD,CAAC;AACD,WAAO;AAAA,EACT;AAEA,WAAS,UAAU,SAAkB,KAAgB,KAAgB;AACnE,UAAM,EAAE,SAAS,IAAI;AACrB,UAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ;AAC3C,QAAI,OAAQ,QAAO;AACnB,UAAM,SAAS,QAAQ,QAAQ,EAAE,KAAK,MAAM,IAAI,OAAO,GAAG,CAAC;AAC3D,YAAQ,QAAQ,IAAI,UAAU,MAAM;AACpC,WAAO,MAAM,MAAM;AACjB,UAAI,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAC5C,gBAAQ,QAAQ,OAAO,QAAQ;AAAA,MACjC;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAEA,WAAS,UAAU,QAAsB,OAAe;AACtD,UAAM,YAAY,IAAI,OAAO,IAAI,KAAK;AACtC,UAAM,SAAS,QAAQ,IAAI,SAAS;AACpC,QAAI,OAAQ,QAAO;AACnB,UAAM,QAAQ,IAAI,gBAAgB;AAClC,UAAM,UAAU,EAAE,MAAM,cAAc,MAAM;AAC5C,UAAM,QAAQ,aAAa,QAAQ,SAAS;AAAA,MAC1C,SAAS;AAAA,MACT,QAAQ,MAAM;AAAA,IAChB,CAAC,EAAE,KAAK,CAAC,UAAU;AACjB,UAAI,CAAC,cAAc,KAAK,GAAG;AACzB,cAAM,IAAI,MAAM,iCAAiC;AAAA,MACnD;AACA,UAAI,OAAO,UAAU,SAAS,IAAI,IAAI,OAAO,IAAI,MAAM,OAAO,CAAC,GAAG;AAChE,cAAM,IAAI,aAAa,YAAY,YAAY;AAAA,MACjD;AACA,YAAM,UAAU,WAAW,OAAO,IAAI,KAAK;AAC3C,aAAO,UAAU;AACjB,cAAQ,QAAQ,IAAI,MAAM;AAC1B,UAAI,MAAM,QAAQ,QAAQ;AACxB,eAAO,WAAW,eAAe,QAAQ,OAAO,MAAM,MAAM;AAAA,MAC9D;AACA,WAAK,OAAO;AAEZ,YAAM,EAAE,UAAU,QAAQ,MAAM,MAAM,KAAK,IAAI;AAC/C,aAAO,EAAE,UAAU,QAAQ,MAAM,MAAM,KAAK;AAAA,IAC9C,CAAC;AACD,UAAM,SAAiB;AAAA,MACrB,KAAK;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,UAAU,oBAAI,IAAI;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AACA,YAAQ,IAAI,WAAW,MAAM;AAC7B,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,QAAgB;AAClC,QAAI,OAAO,OAAQ;AACnB,WAAO,SAAS;AAChB,YAAQ,OAAO,OAAO,GAAG;AACzB,WAAO,UAAU,KAAK;AACtB,WAAO,MAAM,MAAM,IAAI,aAAa,YAAY,YAAY,CAAC;AAC7D,eAAW,UAAU,OAAO,SAAU,QAAO;AAC7C,WAAO,SAAS,QAAQ,OAAO,MAAM;AAAA,EACvC;AAMA,WAAS,SAAS,SAAkB,OAAoB;AACtD,QAAI,WAAW,MAAM;AAAA,IAAC;AAGtB,UAAM,WAAW,IAAI,QAAc,CAAC,YAAY;AAC9C,iBAAW;AAAA,IACb,CAAC;AACD,UAAM,MAAW;AAAA,MACf,QAAQ;AAAA,MACR;AAAA,MACA,OAAO,SAAS,OAAO;AACrB,YAAI,IAAI,OAAQ;AAChB,YAAI,SAAS;AACb,gBAAQ,OAAO,oBAAoB,SAAS,IAAI,KAAK;AACrD,cAAM,EAAE,OAAO,IAAI;AACnB,cAAM,eACJ,UAAU,SAAS,OAAO,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC,IAAI;AACvD,aAAK,QAAQ,QAAQ,YAAY,EAAE,QAAQ,MAAM;AAC/C,kBAAQ,YAAY;AACpB,gBAAM,EAAE,OAAO,IAAI;AACnB,kBAAQ,SAAS,OAAO,IAAI,KAAK;AACjC,cAAI,UAAU,IAAI,WAAW,EAAE,OAAO,YAAY,GAAG;AACnD,mBAAO,UAAU,OAAO,MAAM;AAAA,UAChC;AACA,cAAI,SAAS,KAAK,OAAO,QAAQ;AACjC,cAAI,IAAI,QAAS,MAAK,IAAI,OAAO;AACjC,mBAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,MACA,QAAQ;AACN,YAAI,IAAI,OAAQ;AAChB,YAAI,YAAY,MAAM,IAAI,aAAa,WAAW,YAAY,CAAC;AAC/D,YAAI,OAAO,IAAI;AAAA,MACjB;AAAA,MACA,KAAK,OAAO;AACV,YAAI,IAAI,OAAQ;AAChB,cAAM,UAAU,mBAAmB,KAAK;AACxC,cAAM,UAAU,iBAAiB,SAAS,MAAM,SAAS;AACzD,cAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,YAAI,CAAC,WAAW,UAAU,CAAC,OAAO,QAAQ;AAExC,cAAI;AACF,mBAAO,OAAO,YAAY;AAAA,cACxB,MAAM;AAAA,cACN;AAAA,cACA;AAAA,cACA,GAAI,SAAS,EAAE,QAAQ,MAAM,QAAQ,QAAQ,MAAM,OAAO;AAAA,YAC5D,CAAyB;AAAA,UAC3B,QAAQ;AAAA,UAER;AAAA,QACF;AACA,YAAI,YAAY,MAAM,IAAI,MAAM,OAAO,CAAC;AACxC,YAAI,OAAO,IAAI;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,iBAAe,MACb,SACA,UACA,OACA,UACmB;AACnB,QAAI,QAAQ,WAAW,SAAS,QAAQ,WAAW,QAAQ;AACzD,aAAO,cAAc,GAAG;AAAA,IAC1B;AACA,QAAI,CAAC,kBAAmB,QAAO,cAAc,GAAG;AAChD,UAAM,MAAM,SAAS,SAAS,KAAK;AACnC,QAAI;AACF,YAAM,SAAS,MAAM,MAAM,QAAQ,IAAI,QAAQ;AAC/C,UAAI,CAAC,UAAU,QAAQ,OAAO,QAAS,QAAO,cAAc,GAAG;AAC/D,YAAM,SAAS,UAAU,QAAQ,KAAK;AACtC,UAAI,SAAS;AACb,aAAO,SAAS,IAAI,IAAI,KAAK;AAC7B,cAAQ,OAAO,iBAAiB,SAAS,IAAI,OAAO,EAAE,MAAM,KAAK,CAAC;AAClE,YAAM,WAAW,MAAM,OAAO;AAC9B,UAAI,IAAI,OAAQ,QAAO,cAAc,GAAG;AAExC,YAAM,UAAU,OAAO;AACvB,UAAI,UAAU;AACd,cAAQ,KAAK,IAAI,IAAI,QAAQ;AAC7B,mBAAa,QAAQ,KAAK;AAC1B,YAAM,QAAQ;AAAA,QACZ,QAAQ,WAAW,SAAS,OAAO,QAAQ,QAAQ,IAAI,OAAO;AAAA,QAC9D,SAAS;AAAA,MACX;AACA,UAAI,QAAQ;AACZ,YAAM,UAAU,gBAAgB,UAAU,OAAO,QAAQ;AACzD,UAAI,QAAQ,WAAW,UAAU,MAAM,WAAW,KAAK;AACrD,YAAI,OAAO;AACX,eAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAAA,MAC7D;AACA,YAAM,EAAE,SAAS,IAAI;AACrB,UAAI,UAAU;AAGd,UAAI,EAAE,OAAO,YAAY,EAAG,WAAU,OAAO,YAAY;AAGzD,UAAI,MAAM,WAAW,GAAG;AACtB,YAAI,OAAO;AACX,eAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAAA,MAC7D;AACA,YAAM,MAAM,MAAM,OAAO,OAAO;AAChC,UAAI,IAAI,OAAQ,QAAO,cAAc,GAAG;AACxC,YAAM,SAAS,MAAM,UAAU,SAAS,KAAK,QAAQ;AACrD,UAAI,IAAI,OAAQ,QAAO,cAAc,GAAG;AACxC,YAAM,SAAiB,IACpB,SAAS,QAAQ;AAAA,QAChB,QAAQ,MAAM;AAAA,QACd,QAAQ,MAAM;AAAA,QACd,GAAI,UAAU,MAAM,UAAU;AAAA,UAC5B,mBAAmB,CAAC,aAAa,SAAS,MAAM,QAAQ;AAAA,QAC1D;AAAA,MACF,CAAC,EACA,UAAU;AACb,UAAI,SAAS;AACb,aAAO,IAAI,SAAS,UAAU,KAAK,QAAQ,MAAM,MAAM,GAAG;AAAA,QACxD,QAAQ,MAAM;AAAA,QACd;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,KAAK,KAAK;AACd,UAAI,IAAI,UAAU,CAAC,IAAI,OAAO,QAAS,YAAW,IAAI,MAAM;AAC5D,aAAO,cAAc,GAAG;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM;AAAA,IAAiB;AAAA,IAAW,CAAC,UACjC,MAAM,UAAU,MAAM,YAAY,CAAC;AAAA,EACrC;AACA,QAAM;AAAA,IAAiB;AAAA,IAAY,CAAC,UAClC,MAAM,UAAU,MAAM,QAAQ,MAAM,CAAC;AAAA,EACvC;AACA,QAAM,iBAAiB,WAAW,CAAC,UAAU;AAC3C,UAAM,SAAS,MAAM;AACrB,UAAM,WAAW,OAAO,QAAQ,OAAO,WAAW,OAAO,KAAK;AAC9D,QAAI,CAAC,SAAU;AACf,UAAM,OAAO,MAAM;AACnB,QAAI,MAAM,SAAS,cAAc;AAC/B,YAAM,QAAqB,EAAE,UAAU,WAAW,kBAAkB;AACpE,YAAM,MAAM,CAAC,GAAG,YAAY,KAAK;AACjC,YAAM,MAAM,CAAC,GAAG,MAAM;AAAA,IACxB,WAAW,MAAM,SAAS,iBAAiB,OAAO,KAAK,UAAU,UAAU;AAGzE,YAAMA,UAAS,QAAQ,IAAI,IAAI,UAAU,KAAK,KAAK,CAAC;AACpD,UAAIA,QAAQ,YAAWA,OAAM;AAAA,IAC/B,WACE,MAAM,SAAS,uBACf,OAAO,KAAK,YAAY,UACxB;AACA,YAAM,aAAa,IAAI,UAAU,KAAK,OAAO;AAC7C,eAAS,IAAI,UAAU;AACvB,YAAM,UAAU,SAAS,IAAI,UAAU;AACvC,UAAI,QAAS,OAAM,UAAU,eAAe,OAAO,CAAC;AAAA,IACtD;AAAA,EACF,CAAC;AACD,QAAM,iBAAiB,SAAS,CAAC,UAAU;AACzC,UAAM,MAAM,IAAI,IAAI,MAAM,QAAQ,GAAG;AACrC,QAAI,IAAI,WAAW,MAAM,SAAS,OAAQ;AAC1C,UAAM,SAAS,gBAAgB,IAAI,QAAQ;AAC3C,QAAI,CAAC,OAAQ;AACb,UAAM,WAAW,IAAI,aAAa,IAAI,UAAU,MAAM;AACtD,UAAM;AAAA,MACJ,MAAM,MAAM,SAAS,OAAO,UAAU,OAAO,OAAO,QAAQ;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;AAMA,SAAS,UAAU,KAAU,QAAgB,QAAgB;AAC3D,MAAI,YAAY;AAChB,SAAO,IAAI;AAAA,IACT;AAAA,MACE,MAAM,YAAY;AAChB,YAAI,aAAa;AAAA,MACnB;AAAA,MACA,MAAM,KAAK,YAAY;AACrB,YAAI;AACF,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,cAAI,IAAI,OAAQ;AAChB,cAAI,MAAM;AACR,gBAAI,cAAc,GAAG;AACnB,oBAAM,IAAI;AAAA,gBACR;AAAA,cACF;AAAA,YACF;AACA,uBAAW,MAAM;AACjB,gBAAI,OAAO;AACX;AAAA,UACF;AACA,uBAAa,MAAM;AACnB,cAAI,YAAY,GAAG;AACjB,kBAAM,IAAI,MAAM,mDAAmD;AAAA,UACrE;AACA,qBAAW,QAAQ,KAAK;AACxB,gBAAM,WAAW,IAAI,QAAQ;AAC7B,oBAAU,OAAO,aAAa;AAC9B,oBAAU,MAAM,MAAM,UAAU;AAChC,cAAI,cAAc,GAAG;AACnB,uBAAW,MAAM;AACjB,gBAAI,OAAO,IAAI;AAAA,UACjB;AAAA,QACF,SAAS,OAAO;AACd,cAAI,KAAK,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,MACA,SAAS;AACP,YAAI,OAAO,IAAI;AACf,eAAO,IAAI;AAAA,MACb;AAAA,IACF;AAAA,IACA,EAAE,eAAe,EAAE;AAAA,EACrB;AACF;AAgBA,SAAS,eACP,QACA,OACA,QACU;AACV,QAAM,QAAQ;AAAA,IACZ,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAChC,UAAU,OAAO,SAAS,UAAU;AAAA,IACpC,QAAQ,OAAO,SAAS,QAAQ;AAAA,EAClC;AACA,MAAI,UAAU;AACd,MAAI;AACJ,MAAI,QAAQ;AACZ,MAAI,SAA0B,CAAC;AAC/B,MAAI;AAEJ,WAAS,KAAK,SAAwB;AACpC,QAAI;AACF,aAAO,YAAY,OAAO;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,WAAS,QAAQ;AACf,iBAAa,KAAK;AAClB,YAAQ;AACR,QAAI,WAAY,UAAU,KAAK,OAAO,WAAW,EAAI;AACrD,SAAK,EAAE,MAAM,uBAAuB,OAAO,OAAO,OAAO,CAAC;AAC1D,YAAQ;AACR,aAAS,CAAC;AAAA,EACZ;AACA,WAAS,WAAW;AAClB,cAAU,WAAW,OAAO,eAAe;AAAA,EAC7C;AACA,WAAS,OAAO,MAAoB;AAClC,QAAI,WAAW,SAAS,QAAS;AACjC,UAAM;AACN,cAAU;AACV,QAAI,MAAM,OAAQ,MAAK,EAAE,MAAM,qBAAqB,OAAO,QAAQ,KAAK,CAAC;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,OAAO;AACX,UAAI,WAAW,CAAC,MAAM,SAAU;AAChC,eAAS;AACT,eAAS;AAAA,IACX;AAAA,IACA,MAAM,EAAE,SAAS,WAAW,YAAY,WAAW,UAAU,GAAG;AAC9D,UAAI,QAAS;AAEb,aAAO,KAAK,EAAE,SAAS,WAAW,YAAY,WAAW,UAAU,CAAC;AACpE,eAAS;AAAA,IACX;AAAA,IACA,OAAO;AACL,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ;AACR,eAAS,CAAC;AAAA,IACZ;AAAA,EACF;AACF;;;AFthBO,SAAS,eACd,OACA,UAAiC,CAAC,GAClC;AACA,MAAI;AACJ;AAAA,IACE;AAAA,IACA,OAAO,eAAe;AACpB,gBAAU;AAAA,QACR,QAAQ,SAAS,SAAY,SAAY,EAAE,gBAAgB,QAAQ,KAAK;AAAA,MAC1E;AACA,YAAM;AACN,aAAO,WAAW,SAAS,WACvB,cAAc,UAAU,IACxB,WAAW,UAAU;AAAA,IAC3B;AAAA;AAAA;AAAA,IAGA,OAAO,iBAAiB;AAAA,EAC1B;AACF;AAEA,eAAe,cAAc;AAAA,EAC3B;AAAA,EACA;AACF,GAAgE;AAC9D,QAAM,MAAM,MAAM,UAAU,QAAQ,YAAY,IAAI;AACpD,SAAO;AAAA,IACL,QAAQ,CAAC,EAAE,SAAS,MAAM,IAAI,OAAO,QAAQ;AAAA,IAC7C,UAAU,CAAC,QAAQ,YAAY,IAAI,SAAS,QAAQ,OAAO;AAAA,IAC3D,MAAM,MAAM,IAAI,KAAK;AAAA,EACvB;AACF;AAEA,eAAe,WAAW;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAA6D;AAC3D,QAAM,MAAM,IAAI,OAAO,QAAQ,MAAM,CAAC;AACtC,QAAM,UAAU,IAAI,QAAQ,YAAY,OAAO;AAC/C,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,UAAU,GAAG;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,iDAAiD;AAC3E,WAAO;AAAA;AAAA,MAEL,QAAQ,OAAO,EAAE,OAAO,MAAM,aAAa,KAAK,KAAK,MAAO;AAAA,MAC5D,UAAU,CAAC,QAAQ,YAAY,IAAI,SAAS,QAAQ,OAAO;AAAA,MAC3D,MAAM,MAAM;AACV,YAAI,KAAK;AACT,YAAI,KAAK;AAAA,MACX;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,QAAI,KAAK;AACT,UAAM;AAAA,EACR,UAAE;AACA,YAAQ,KAAK;AAAA,EACf;AACF;AAGA,SAAS,QAAQ,KAAa;AAC5B,SAAO,WAAW,KAAK,IAAI,MAAM,KAAK,KAAK,CAAC,GAAG,CAAC,SAAS,SAAS,MAAM,EAAE,CAAC;AAC7E;","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 sealKey,\n}: Extract<Connection, { kind: 'shared' }>): Promise<StreamSdk> {\n // Built before connecting, so a key that fails to load cannot leave a\n // connected SDK behind with nothing to free it.\n const key = sealKey === undefined ? undefined : new AppKey(fromHex(sealKey))\n let sdk: SharedSdk\n try {\n sdk = await SharedSdk.connect(indexerUrl, seed)\n } catch (error) {\n key?.free()\n throw error\n }\n return {\n // The page sealed the object it already had with the share's random key.\n // Opening that saves a round trip to the indexer before every stream's\n // first byte.\n object: async ({ objectId, sealed }) =>\n key && sealed ? PinnedObject.open(key, sealed) : sdk.object(objectId),\n download: (object, options) => sdk.download(object, options),\n free: () => {\n sdk.free()\n key?.free()\n },\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 // A warm-up read, which reports nothing to the page.\n quiet?: 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 // A warm-up is invisible to the page, failures included. The play\n // that follows connects again and reports its own failure.\n if (!aborted && !job.quiet && 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, warm }: { download: boolean; warm: 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 job.quiet = warm\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 // A warm-up is invisible to the page, so a player that warms a file\n // does not see it go connecting and downloading before Play.\n const reporter = warm ? undefined : source.reporter\n job.reading = !warm\n // A read that starts while another is already downloading leaves the\n // status at `downloading`.\n if (!warm && ++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 const warm = url.searchParams.get('warm') === '1'\n event.respondWith(\n serve(event.request, stream.clientId, stream.token, { download, warm }),\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.quiet ? undefined : 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.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -56,10 +56,11 @@
|
|
|
56
56
|
"build-wasm": "bun run scripts/build-wasm.ts",
|
|
57
57
|
"setup": "bun run scripts/setup-rust.ts",
|
|
58
58
|
"setup-napi-test": "bun run scripts/setup-napi-test.ts",
|
|
59
|
-
"typecheck": "tsc --noEmit",
|
|
59
|
+
"typecheck": "tsc --noEmit && tsc -p bench",
|
|
60
60
|
"test": "bun test src/tests/install-integration.test.ts",
|
|
61
61
|
"test:browser": "bun test src/tests/vite-integration.test.ts src/tests/esbuild-integration.test.ts src/tests/webpack-integration.test.ts src/tests/rollup-integration.test.ts src/tests/nextjs-integration.test.ts src/tests/streaming.test.ts src/tests/stream-prebuilt-worker.test.ts",
|
|
62
62
|
"test:all": "bun test",
|
|
63
|
+
"bench": "bun run bench/run.ts",
|
|
63
64
|
"prepublishOnly": "bun run build && bun test",
|
|
64
65
|
"publish-npm": "bun run scripts/publish.ts"
|
|
65
66
|
},
|
|
@@ -71,10 +72,10 @@
|
|
|
71
72
|
"typescript": "5.9.3"
|
|
72
73
|
},
|
|
73
74
|
"optionalDependencies": {
|
|
74
|
-
"@siafoundation/sia-storage-darwin-arm64": "0.1.
|
|
75
|
-
"@siafoundation/sia-storage-darwin-x64": "0.1.
|
|
76
|
-
"@siafoundation/sia-storage-linux-x64-gnu": "0.1.
|
|
77
|
-
"@siafoundation/sia-storage-linux-arm64-gnu": "0.1.
|
|
78
|
-
"@siafoundation/sia-storage-win32-x64-msvc": "0.1.
|
|
75
|
+
"@siafoundation/sia-storage-darwin-arm64": "0.1.4",
|
|
76
|
+
"@siafoundation/sia-storage-darwin-x64": "0.1.4",
|
|
77
|
+
"@siafoundation/sia-storage-linux-x64-gnu": "0.1.4",
|
|
78
|
+
"@siafoundation/sia-storage-linux-arm64-gnu": "0.1.4",
|
|
79
|
+
"@siafoundation/sia-storage-win32-x64-msvc": "0.1.4"
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -31,14 +31,6 @@ export interface Slab {
|
|
|
31
31
|
sectors: Sector[];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export interface PinnedSlab {
|
|
35
|
-
version: number;
|
|
36
|
-
id: string;
|
|
37
|
-
encryptionKey: string;
|
|
38
|
-
minShards: number;
|
|
39
|
-
sectors: Sector[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
34
|
export interface Host {
|
|
43
35
|
publicKey: string;
|
|
44
36
|
addresses: { protocol: string; address: string }[];
|
|
@@ -66,20 +58,6 @@ export interface PackedUploadOptions {
|
|
|
66
58
|
}
|
|
67
59
|
|
|
68
60
|
|
|
69
|
-
|
|
70
|
-
interface Sdk {
|
|
71
|
-
download(object: PinnedObject, options?: DownloadOptions): ReadableStream;
|
|
72
|
-
upload(object: PinnedObject, source: ReadableStream, options?: UploadOptions): Promise<PinnedObject>;
|
|
73
|
-
uploadPacked(options?: PackedUploadOptions): PackedUpload;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
interface SharedSdk {
|
|
79
|
-
download(object: PinnedObject, options?: DownloadOptions): ReadableStream;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
61
|
/**
|
|
84
62
|
* Application info registered with the indexer.
|
|
85
63
|
*/
|
|
@@ -273,7 +251,7 @@ export class Builder {
|
|
|
273
251
|
waitForApproval(): Promise<void>;
|
|
274
252
|
}
|
|
275
253
|
|
|
276
|
-
|
|
254
|
+
declare class IntoUnderlyingByteSource {
|
|
277
255
|
private constructor();
|
|
278
256
|
free(): void;
|
|
279
257
|
[Symbol.dispose](): void;
|
|
@@ -283,8 +261,9 @@ export class IntoUnderlyingByteSource {
|
|
|
283
261
|
readonly autoAllocateChunkSize: number;
|
|
284
262
|
readonly type: ReadableStreamType;
|
|
285
263
|
}
|
|
264
|
+
export type { IntoUnderlyingByteSource };
|
|
286
265
|
|
|
287
|
-
|
|
266
|
+
declare class IntoUnderlyingSink {
|
|
288
267
|
private constructor();
|
|
289
268
|
free(): void;
|
|
290
269
|
[Symbol.dispose](): void;
|
|
@@ -292,14 +271,16 @@ export class IntoUnderlyingSink {
|
|
|
292
271
|
close(): Promise<any>;
|
|
293
272
|
write(chunk: any): Promise<any>;
|
|
294
273
|
}
|
|
274
|
+
export type { IntoUnderlyingSink };
|
|
295
275
|
|
|
296
|
-
|
|
276
|
+
declare class IntoUnderlyingSource {
|
|
297
277
|
private constructor();
|
|
298
278
|
free(): void;
|
|
299
279
|
[Symbol.dispose](): void;
|
|
300
280
|
cancel(): void;
|
|
301
281
|
pull(controller: ReadableStreamDefaultController): Promise<any>;
|
|
302
282
|
}
|
|
283
|
+
export type { IntoUnderlyingSource };
|
|
303
284
|
|
|
304
285
|
/**
|
|
305
286
|
* The indexer's record for a sharing key, including how many objects it grants
|
|
@@ -341,6 +322,37 @@ export class ObjectEvent {
|
|
|
341
322
|
readonly updatedAt: Date;
|
|
342
323
|
}
|
|
343
324
|
|
|
325
|
+
/**
|
|
326
|
+
* An object listed without its slabs, as `SharedSdk.objectSummaries` returns
|
|
327
|
+
* it. It describes the object but cannot be downloaded. Fetch the full
|
|
328
|
+
* object with `SharedSdk.object(id)` to download it.
|
|
329
|
+
*/
|
|
330
|
+
export class ObjectSummary {
|
|
331
|
+
private constructor();
|
|
332
|
+
free(): void;
|
|
333
|
+
[Symbol.dispose](): void;
|
|
334
|
+
/**
|
|
335
|
+
* The creation time.
|
|
336
|
+
*/
|
|
337
|
+
readonly createdAt: Date;
|
|
338
|
+
/**
|
|
339
|
+
* The object's ID as a hex string.
|
|
340
|
+
*/
|
|
341
|
+
readonly id: string;
|
|
342
|
+
/**
|
|
343
|
+
* The object's decrypted metadata.
|
|
344
|
+
*/
|
|
345
|
+
readonly metadata: Uint8Array;
|
|
346
|
+
/**
|
|
347
|
+
* The object's size in bytes.
|
|
348
|
+
*/
|
|
349
|
+
readonly size: number;
|
|
350
|
+
/**
|
|
351
|
+
* The last updated time.
|
|
352
|
+
*/
|
|
353
|
+
readonly updatedAt: Date;
|
|
354
|
+
}
|
|
355
|
+
|
|
344
356
|
/**
|
|
345
357
|
* A packed upload handle for efficiently uploading multiple objects
|
|
346
358
|
* together. Objects are packed into shared slabs to avoid wasting storage.
|
|
@@ -503,7 +515,7 @@ export class Sdk {
|
|
|
503
515
|
* }
|
|
504
516
|
* ```
|
|
505
517
|
*/
|
|
506
|
-
download(object: PinnedObject, options?:
|
|
518
|
+
download(object: PinnedObject, options?: DownloadOptions): ReadableStream;
|
|
507
519
|
/**
|
|
508
520
|
* Returns a list of usable hosts, optionally filtered by a HostQuery.
|
|
509
521
|
*/
|
|
@@ -534,8 +546,11 @@ export class Sdk {
|
|
|
534
546
|
pinObject(object: PinnedObject): Promise<void>;
|
|
535
547
|
/**
|
|
536
548
|
* Prunes unused slabs from the indexer.
|
|
549
|
+
*
|
|
550
|
+
* `before` prunes only slabs pinned before that date. Omitting it leaves
|
|
551
|
+
* the cutoff to the indexer.
|
|
537
552
|
*/
|
|
538
|
-
pruneSlabs(): Promise<void>;
|
|
553
|
+
pruneSlabs(before?: Date | null): Promise<void>;
|
|
539
554
|
/**
|
|
540
555
|
* Deletes a sharing key along with all of its attachments, revoking
|
|
541
556
|
* recipients' access. Account tokens already issued stay valid until they
|
|
@@ -560,10 +575,6 @@ export class Sdk {
|
|
|
560
575
|
* Lists the account's sharing keys, most recently created first.
|
|
561
576
|
*/
|
|
562
577
|
sharingKeys(offset: number, limit: number): Promise<KeyRecord[]>;
|
|
563
|
-
/**
|
|
564
|
-
* Retrieves a pinned slab from the indexer by its hex ID.
|
|
565
|
-
*/
|
|
566
|
-
slab(slab_id: string): Promise<PinnedSlab>;
|
|
567
578
|
/**
|
|
568
579
|
* Detaches an object from a sharing key, leaving the key and its other
|
|
569
580
|
* attachments in place.
|
|
@@ -585,14 +596,14 @@ export class Sdk {
|
|
|
585
596
|
* await sdk.pinObject(obj);
|
|
586
597
|
* ```
|
|
587
598
|
*/
|
|
588
|
-
upload(object: PinnedObject, source: ReadableStream, options?:
|
|
599
|
+
upload(object: PinnedObject, source: ReadableStream, options?: UploadOptions): Promise<PinnedObject>;
|
|
589
600
|
/**
|
|
590
601
|
* Starts a packed upload for efficiently uploading multiple small objects.
|
|
591
602
|
* Objects smaller than the slab size (~40 MiB) are packed into shared slabs
|
|
592
603
|
* to avoid wasting storage. Call `add(data)` for each object, then
|
|
593
604
|
* `finalize()` to get the resulting `PinnedObject` handles.
|
|
594
605
|
*/
|
|
595
|
-
uploadPacked(options?:
|
|
606
|
+
uploadPacked(options?: PackedUploadOptions): PackedUpload;
|
|
596
607
|
}
|
|
597
608
|
|
|
598
609
|
/**
|
|
@@ -613,7 +624,7 @@ export class SharedSdk {
|
|
|
613
624
|
/**
|
|
614
625
|
* Streams a shared object's data.
|
|
615
626
|
*/
|
|
616
|
-
download(object: PinnedObject, options?:
|
|
627
|
+
download(object: PinnedObject, options?: DownloadOptions): ReadableStream;
|
|
617
628
|
/**
|
|
618
629
|
* Returns the hosts serving this key's objects, optionally filtered by a
|
|
619
630
|
* HostQuery. Mirrors `Sdk.hosts()` but is scoped to the sharing key, so the
|
|
@@ -624,6 +635,12 @@ export class SharedSdk {
|
|
|
624
635
|
* Fetches and decrypts one object the key grants access to.
|
|
625
636
|
*/
|
|
626
637
|
object(id: string): Promise<PinnedObject>;
|
|
638
|
+
/**
|
|
639
|
+
* Lists a page of the objects the key grants access to without their
|
|
640
|
+
* slabs, which is much smaller and faster than `objects`. Each summary
|
|
641
|
+
* has the object's id, size, decrypted metadata and timestamps.
|
|
642
|
+
*/
|
|
643
|
+
objectSummaries(offset: number, limit: number): Promise<ObjectSummary[]>;
|
|
627
644
|
/**
|
|
628
645
|
* Lists and decrypts a page of the objects the key grants access to.
|
|
629
646
|
*/
|
|
@@ -700,6 +717,7 @@ export interface InitOutput {
|
|
|
700
717
|
readonly __wbg_builder_free: (a: number, b: number) => void;
|
|
701
718
|
readonly __wbg_keyrecord_free: (a: number, b: number) => void;
|
|
702
719
|
readonly __wbg_objectevent_free: (a: number, b: number) => void;
|
|
720
|
+
readonly __wbg_objectsummary_free: (a: number, b: number) => void;
|
|
703
721
|
readonly __wbg_packedupload_free: (a: number, b: number) => void;
|
|
704
722
|
readonly __wbg_pinnedobject_free: (a: number, b: number) => void;
|
|
705
723
|
readonly __wbg_sdk_free: (a: number, b: number) => void;
|
|
@@ -718,7 +736,7 @@ export interface InitOutput {
|
|
|
718
736
|
readonly builder_requestConnection: (a: number) => any;
|
|
719
737
|
readonly builder_responseUrl: (a: number) => [number, number, number, number];
|
|
720
738
|
readonly builder_waitForApproval: (a: number) => any;
|
|
721
|
-
readonly encodedSize: (a: number, b: number, c: number) => number;
|
|
739
|
+
readonly encodedSize: (a: number, b: number, c: number) => [number, number, number];
|
|
722
740
|
readonly generateRecoveryPhrase: () => [number, number];
|
|
723
741
|
readonly init: () => void;
|
|
724
742
|
readonly keyrecord_description: (a: number) => [number, number];
|
|
@@ -728,6 +746,11 @@ export interface InitOutput {
|
|
|
728
746
|
readonly objectevent_id: (a: number) => [number, number];
|
|
729
747
|
readonly objectevent_object: (a: number) => number;
|
|
730
748
|
readonly objectevent_updatedAt: (a: number) => any;
|
|
749
|
+
readonly objectsummary_createdAt: (a: number) => any;
|
|
750
|
+
readonly objectsummary_id: (a: number) => [number, number];
|
|
751
|
+
readonly objectsummary_metadata: (a: number) => [number, number];
|
|
752
|
+
readonly objectsummary_size: (a: number) => number;
|
|
753
|
+
readonly objectsummary_updatedAt: (a: number) => any;
|
|
731
754
|
readonly packedupload_add: (a: number, b: any) => any;
|
|
732
755
|
readonly packedupload_cancel: (a: number) => void;
|
|
733
756
|
readonly packedupload_finalize: (a: number) => any;
|
|
@@ -758,13 +781,12 @@ export interface InitOutput {
|
|
|
758
781
|
readonly sdk_objectFromShareUrl: (a: number, b: number, c: number) => any;
|
|
759
782
|
readonly sdk_objectShareUrl: (a: number, b: number, c: any) => [number, number, number, number];
|
|
760
783
|
readonly sdk_pinObject: (a: number, b: number) => any;
|
|
761
|
-
readonly sdk_pruneSlabs: (a: number) => any;
|
|
784
|
+
readonly sdk_pruneSlabs: (a: number, b: number) => any;
|
|
762
785
|
readonly sdk_revokeSharingKey: (a: number, b: number) => any;
|
|
763
786
|
readonly sdk_shareObject: (a: number, b: number, c: number) => any;
|
|
764
787
|
readonly sdk_sharedObjects: (a: number, b: number, c: number, d: number) => any;
|
|
765
788
|
readonly sdk_sharingKey: (a: number, b: number) => any;
|
|
766
789
|
readonly sdk_sharingKeys: (a: number, b: number, c: number) => any;
|
|
767
|
-
readonly sdk_slab: (a: number, b: number, c: number) => any;
|
|
768
790
|
readonly sdk_unshareObject: (a: number, b: number, c: number, d: number) => any;
|
|
769
791
|
readonly sdk_updateObjectMetadata: (a: number, b: number) => any;
|
|
770
792
|
readonly sdk_upload: (a: number, b: number, c: any, d: number) => any;
|
|
@@ -774,6 +796,7 @@ export interface InitOutput {
|
|
|
774
796
|
readonly sharedsdk_download: (a: number, b: number, c: number) => [number, number, number];
|
|
775
797
|
readonly sharedsdk_hosts: (a: number, b: number) => any;
|
|
776
798
|
readonly sharedsdk_object: (a: number, b: number, c: number) => any;
|
|
799
|
+
readonly sharedsdk_objectSummaries: (a: number, b: number, c: number) => any;
|
|
777
800
|
readonly sharedsdk_objects: (a: number, b: number, c: number) => any;
|
|
778
801
|
readonly sharedsdk_stats: (a: number) => any;
|
|
779
802
|
readonly sharingkey_fromSeed: (a: number, b: number) => [number, number, number];
|
|
@@ -798,7 +821,7 @@ export interface InitOutput {
|
|
|
798
821
|
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
822
|
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
823
|
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
|
|
824
|
+
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___js_sys_32e95d01f2c64d55___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined___js_sys_32e95d01f2c64d55___Function_fn_wasm_bindgen_a939ac12ad536d64___JsValue_____wasm_bindgen_a939ac12ad536d64___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
|
|
802
825
|
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke___wasm_bindgen_a939ac12ad536d64___JsValue______true_: (a: number, b: number, c: any) => void;
|
|
803
826
|
readonly wasm_bindgen_a939ac12ad536d64___convert__closures_____invoke_______true_: (a: number, b: number) => void;
|
|
804
827
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|