@backblaze-labs/b2-sdk 0.3.0 → 0.4.0
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/CHANGELOG.md +45 -1
- package/README.md +18 -5
- package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
- package/dist/_virtual/_b2-sdk-version-json.js +1 -1
- package/dist/auth/reauth-coalescer.cjs +65 -0
- package/dist/auth/reauth-coalescer.cjs.map +1 -0
- package/dist/auth/reauth-coalescer.d.cts +34 -0
- package/dist/auth/reauth-coalescer.d.cts.map +1 -0
- package/dist/auth/reauth-coalescer.d.ts +34 -0
- package/dist/auth/reauth-coalescer.d.ts.map +1 -0
- package/dist/auth/reauth-coalescer.js +65 -0
- package/dist/auth/reauth-coalescer.js.map +1 -0
- package/dist/backup/raw.cjs +37 -15
- package/dist/backup/raw.cjs.map +1 -1
- package/dist/backup/raw.d.cts +2 -2
- package/dist/backup/raw.d.cts.map +1 -1
- package/dist/backup/raw.d.ts +2 -2
- package/dist/backup/raw.d.ts.map +1 -1
- package/dist/backup/raw.js +37 -15
- package/dist/backup/raw.js.map +1 -1
- package/dist/bucket-validation.cjs +111 -0
- package/dist/bucket-validation.cjs.map +1 -0
- package/dist/bucket-validation.d.cts +27 -0
- package/dist/bucket-validation.d.cts.map +1 -0
- package/dist/bucket-validation.d.ts +27 -0
- package/dist/bucket-validation.d.ts.map +1 -0
- package/dist/bucket-validation.js +109 -0
- package/dist/bucket-validation.js.map +1 -0
- package/dist/bucket.cjs +123 -73
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +156 -61
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +156 -61
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +123 -73
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +29 -8
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +30 -9
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +30 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -8
- package/dist/client.js.map +1 -1
- package/dist/download/single.cjs +38 -13
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +10 -4
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +10 -4
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +38 -13
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +27 -4
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +15 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +27 -5
- package/dist/errors/index.js.map +1 -1
- package/dist/http/non-idempotent-mutations.cjs +38 -0
- package/dist/http/non-idempotent-mutations.cjs.map +1 -0
- package/dist/http/non-idempotent-mutations.d.cts +25 -0
- package/dist/http/non-idempotent-mutations.d.cts.map +1 -0
- package/dist/http/non-idempotent-mutations.d.ts +25 -0
- package/dist/http/non-idempotent-mutations.d.ts.map +1 -0
- package/dist/http/non-idempotent-mutations.js +35 -0
- package/dist/http/non-idempotent-mutations.js.map +1 -0
- package/dist/http/transport.cjs +9 -8
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +5 -0
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +5 -0
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +9 -8
- package/dist/http/transport.js.map +1 -1
- package/dist/http/user-agent.cjs +12 -1
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.d.cts +8 -0
- package/dist/http/user-agent.d.cts.map +1 -1
- package/dist/http/user-agent.d.ts +8 -0
- package/dist/http/user-agent.d.ts.map +1 -1
- package/dist/http/user-agent.js +13 -3
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +25 -0
- package/dist/index.d.cts +10 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/notifications/index.cjs +3 -0
- package/dist/notifications/index.d.cts +2 -0
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +2 -1
- package/dist/object.cjs +12 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +7 -1
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +7 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +12 -1
- package/dist/object.js.map +1 -1
- package/dist/partner/auth-core.cjs +4 -23
- package/dist/partner/auth-core.cjs.map +1 -1
- package/dist/partner/auth-core.d.cts +1 -1
- package/dist/partner/auth-core.d.cts.map +1 -1
- package/dist/partner/auth-core.d.ts +1 -1
- package/dist/partner/auth-core.d.ts.map +1 -1
- package/dist/partner/auth-core.js +5 -24
- package/dist/partner/auth-core.js.map +1 -1
- package/dist/partner/client.cjs +25 -10
- package/dist/partner/client.cjs.map +1 -1
- package/dist/partner/client.d.cts +32 -10
- package/dist/partner/client.d.cts.map +1 -1
- package/dist/partner/client.d.ts +32 -10
- package/dist/partner/client.d.ts.map +1 -1
- package/dist/partner/client.js +25 -10
- package/dist/partner/client.js.map +1 -1
- package/dist/partner/endpoint-url.cjs +1 -0
- package/dist/partner/endpoint-url.cjs.map +1 -1
- package/dist/partner/endpoint-url.d.cts +2 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -1
- package/dist/partner/endpoint-url.d.ts +2 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -1
- package/dist/partner/endpoint-url.js +1 -0
- package/dist/partner/endpoint-url.js.map +1 -1
- package/dist/partner/in-memory.cjs.map +1 -1
- package/dist/partner/in-memory.js.map +1 -1
- package/dist/partner/index.cjs +9 -0
- package/dist/partner/index.d.cts +11 -2
- package/dist/partner/index.d.cts.map +1 -1
- package/dist/partner/index.d.ts +11 -2
- package/dist/partner/index.d.ts.map +1 -1
- package/dist/partner/index.js +3 -1
- package/dist/partner/raw.cjs +52 -44
- package/dist/partner/raw.cjs.map +1 -1
- package/dist/partner/raw.d.cts +16 -19
- package/dist/partner/raw.d.cts.map +1 -1
- package/dist/partner/raw.d.ts +16 -19
- package/dist/partner/raw.d.ts.map +1 -1
- package/dist/partner/raw.js +52 -44
- package/dist/partner/raw.js.map +1 -1
- package/dist/partner/redaction.cjs +133 -108
- package/dist/partner/redaction.cjs.map +1 -1
- package/dist/partner/redaction.d.cts +47 -13
- package/dist/partner/redaction.d.cts.map +1 -1
- package/dist/partner/redaction.d.ts +47 -13
- package/dist/partner/redaction.d.ts.map +1 -1
- package/dist/partner/redaction.js +133 -109
- package/dist/partner/redaction.js.map +1 -1
- package/dist/raw/encoding.cjs +29 -10
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.d.cts +3 -2
- package/dist/raw/encoding.d.cts.map +1 -1
- package/dist/raw/encoding.d.ts +3 -2
- package/dist/raw/encoding.d.ts.map +1 -1
- package/dist/raw/encoding.js +29 -10
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +64 -39
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +21 -19
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +21 -19
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +47 -40
- package/dist/raw/index.js.map +1 -1
- package/dist/raw/url.cjs.map +1 -1
- package/dist/raw/url.d.cts +3 -3
- package/dist/raw/url.d.ts +3 -3
- package/dist/raw/url.js.map +1 -1
- package/dist/simulator/index.cjs +456 -125
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +16 -1
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +16 -1
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +449 -130
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner-capabilities.cjs +77 -0
- package/dist/simulator/partner-capabilities.cjs.map +1 -0
- package/dist/simulator/partner-capabilities.d.cts +53 -0
- package/dist/simulator/partner-capabilities.d.cts.map +1 -0
- package/dist/simulator/partner-capabilities.d.ts +53 -0
- package/dist/simulator/partner-capabilities.d.ts.map +1 -0
- package/dist/simulator/partner-capabilities.js +73 -0
- package/dist/simulator/partner-capabilities.js.map +1 -0
- package/dist/simulator/partner.cjs +55 -61
- package/dist/simulator/partner.cjs.map +1 -1
- package/dist/simulator/partner.d.cts +5 -2
- package/dist/simulator/partner.d.cts.map +1 -1
- package/dist/simulator/partner.d.ts +5 -2
- package/dist/simulator/partner.d.ts.map +1 -1
- package/dist/simulator/partner.js +56 -61
- package/dist/simulator/partner.js.map +1 -1
- package/dist/simulator/validation.cjs +160 -30
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +28 -6
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +28 -6
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +148 -31
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/file-source.cjs +1 -1
- package/dist/streams/file-source.cjs.map +1 -1
- package/dist/streams/file-source.js +1 -1
- package/dist/streams/file-source.js.map +1 -1
- package/dist/streams/hash.cjs +246 -13
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +37 -12
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +37 -12
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +246 -15
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +2 -0
- package/dist/streams/index.d.cts +1 -1
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +2 -2
- package/dist/streams/progress.cjs +63 -4
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.d.cts +28 -2
- package/dist/streams/progress.d.cts.map +1 -1
- package/dist/streams/progress.d.ts +28 -2
- package/dist/streams/progress.d.ts.map +1 -1
- package/dist/streams/progress.js +63 -4
- package/dist/streams/progress.js.map +1 -1
- package/dist/sync/download-staging.cjs +147 -35
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +25 -2
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +25 -2
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +146 -36
- package/dist/sync/download-staging.js.map +1 -1
- package/dist/sync/index.d.cts +1 -1
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/local-file-io.cjs +5 -1
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +2 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +2 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +5 -1
- package/dist/sync/local-file-io.js.map +1 -1
- package/dist/sync/pairing.cjs +1 -0
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.js +1 -0
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/scanners/b2.cjs +2 -0
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +2 -0
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +150 -33
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +6 -0
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +6 -0
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +150 -33
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +76 -29
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +1 -0
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +1 -0
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +76 -29
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +19 -3
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +19 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/backup.d.cts +6 -1
- package/dist/types/backup.d.cts.map +1 -1
- package/dist/types/backup.d.ts +6 -1
- package/dist/types/backup.d.ts.map +1 -1
- package/dist/types/bucket.cjs +81 -11
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +190 -39
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +190 -39
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js +67 -12
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs.map +1 -1
- package/dist/types/download.d.cts +3 -17
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +3 -17
- package/dist/types/download.d.ts.map +1 -1
- package/dist/types/download.js.map +1 -1
- package/dist/types/encryption.cjs +18 -0
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +21 -11
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +21 -11
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +18 -0
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/file.cjs +3 -0
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +125 -16
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +125 -16
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +3 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/index.d.cts +7 -7
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +4 -3
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +4 -3
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/notifications.cjs +30 -0
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +26 -1
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +26 -1
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +29 -1
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/partner.cjs.map +1 -1
- package/dist/types/partner.d.cts +28 -36
- package/dist/types/partner.d.cts.map +1 -1
- package/dist/types/partner.d.ts +28 -36
- package/dist/types/partner.d.ts.map +1 -1
- package/dist/types/partner.js.map +1 -1
- package/dist/types/upload.d.cts +4 -0
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +4 -0
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/large.cjs +10 -7
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +15 -6
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +15 -6
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +10 -7
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs.map +1 -1
- package/dist/upload/options.d.cts +12 -0
- package/dist/upload/options.d.cts.map +1 -1
- package/dist/upload/options.d.ts +12 -0
- package/dist/upload/options.d.ts.map +1 -1
- package/dist/upload/options.js.map +1 -1
- package/dist/upload/resume.cjs +4 -3
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +6 -4
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +6 -4
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +4 -3
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +17 -10
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.d.cts.map +1 -1
- package/dist/upload/retry.d.ts.map +1 -1
- package/dist/upload/retry.js +17 -10
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +35 -31
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +6 -0
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +6 -0
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +35 -31
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +12 -1
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +6 -0
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +6 -0
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +12 -1
- package/dist/upload/stream.js.map +1 -1
- package/dist/version.cjs +56 -4
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +53 -4
- package/dist/version.d.cts.map +1 -1
- package/dist/version.d.ts +53 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +52 -5
- package/dist/version.js.map +1 -1
- package/package.json +11 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-source.cjs","names":[],"sources":["../../src/streams/file-source.ts"],"sourcesContent":["import type { ContentSource } from './source.ts'\n\nconst FILE_STREAM_CHUNK_SIZE = 16 * 1024 * 1024\n\n/** Filesystem path accepted by {@link FileSource}. */\nexport type FileSourcePath = string | URL\n\ninterface FileStatsLike {\n readonly dev: number\n readonly ino: number\n readonly mode: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n isFile(): boolean\n}\n\ninterface FileHandleLike {\n read(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ bytesRead: number }>\n stat(): Promise<FileStatsLike>\n close(): Promise<void>\n}\n\ninterface NodeFsSync {\n lstatSync(path: FileSourcePath): FileStatsLike\n}\n\ninterface FileIdentity {\n readonly dev: number\n readonly ino: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n}\n\ninterface ToArrayBufferOptions {\n readonly signal?: AbortSignal\n}\n\nconst FILE_SOURCE_INTERNAL = Symbol('FileSource.internal')\n\ninterface FileSourceInternalOptions {\n readonly key: typeof FILE_SOURCE_INTERNAL\n readonly identity: FileIdentity\n}\n\n/** @internal */\nexport const fileSourceTestHooks: {\n afterReadIteration?: (filled: number) => Promise<void> | void\n maxReadSize?: number\n openFile?: (path: FileSourcePath, flags: number) => Promise<FileHandleLike>\n platform?: string\n} = {}\n\nfunction getNodeFsSync(): NodeFsSync {\n // FileSource exposes `size` synchronously, so the constructor cannot use the\n // repo's usual `await import('node:...')` pattern for Node-only APIs. Keep the\n // synchronous lookup here; async callers should prefer `FileSource.fromPath()`.\n const processLike = (\n globalThis as {\n process?: { getBuiltinModule?: (id: string) => unknown }\n }\n ).process\n const fs = processLike?.getBuiltinModule?.('node:fs')\n if (!isNodeFsSync(fs)) {\n throw new Error(\n 'FileSource constructor requires Node.js 22.3+ synchronous filesystem APIs; use FileSource.fromPath() when synchronous filesystem access is unavailable.',\n )\n }\n return fs\n}\n\nfunction isNodeFsSync(value: unknown): value is NodeFsSync {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return typeof candidate['lstatSync'] === 'function'\n}\n\nasync function fileOpenFlags(): Promise<number> {\n const { constants } = await import('node:fs')\n // O_NOFOLLOW and O_NONBLOCK are unavailable on some platforms. When\n // O_NOFOLLOW is absent, the post-open fstat identity check below is the\n // symlink-swap defense. O_NONBLOCK avoids blocking indefinitely if a path is\n // swapped to a FIFO or another blocking special file before fstat rejects it.\n return (\n constants.O_RDONLY |\n (constants.O_NOFOLLOW ?? 0) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n )\n}\n\nfunction normalizeSliceOffset(value: number, size: number): number {\n if (!Number.isFinite(value)) throw new RangeError('FileSource slice offsets must be finite.')\n const integer = Math.trunc(value)\n const offset = integer < 0 ? size + integer : integer\n return Math.min(Math.max(offset, 0), size)\n}\n\nfunction formatFilePath(path: FileSourcePath): string {\n return path instanceof URL ? path.href : path\n}\n\nfunction identityFromStats(stats: FileStatsLike): FileIdentity {\n return {\n dev: stats.dev,\n ino: stats.ino,\n size: stats.size,\n mtimeMs: stats.mtimeMs,\n ctimeMs: stats.ctimeMs,\n }\n}\n\nfunction assertStableIdentity(path: FileSourcePath, stats: FileStatsLike): void {\n if (stats.dev === 0 && stats.ino === 0) {\n throw new Error(\n `FileSource: ${formatFilePath(path)} is on a filesystem that does not expose stable file identity.`,\n )\n }\n}\n\nfunction assertRegularFile(path: FileSourcePath, stats: FileStatsLike): void {\n if (!stats.isFile()) {\n throw new Error(`FileSource: ${formatFilePath(path)} is not a regular file.`)\n }\n}\n\nfunction validatedIdentityFromStats(path: FileSourcePath, stats: FileStatsLike): FileIdentity {\n assertRegularFile(path, stats)\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, stats)\n return identityFromStats(stats)\n}\n\nfunction assertSameIdentity(\n path: FileSourcePath,\n expected: FileIdentity,\n actual: FileStatsLike,\n when: string,\n): void {\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, actual)\n if (\n shouldComparePosixFileIdentity() &&\n (actual.dev !== expected.dev || actual.ino !== expected.ino)\n ) {\n throw new Error(`FileSource: ${formatFilePath(path)} changed ${when}.`)\n }\n if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n if (shouldComparePosixChangeTime() && actual.ctimeMs !== expected.ctimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n}\n\nfunction isWindows(): boolean {\n if (fileSourceTestHooks.platform !== undefined) return fileSourceTestHooks.platform === 'win32'\n const processLike = (globalThis as { process?: { platform?: string } }).process\n return processLike?.platform === 'win32'\n}\n\nfunction shouldComparePosixFileIdentity(): boolean {\n return !isWindows()\n}\n\nfunction shouldComparePosixChangeTime(): boolean {\n return !isWindows()\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal === undefined || !signal.aborted) return\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n}\n\nfunction maxReadSize(): number {\n return fileSourceTestHooks.maxReadSize ?? FILE_STREAM_CHUNK_SIZE\n}\n\n/* v8 ignore start -- Requires a file to pass identity checks and still EOF mid-range. */\nfunction rangeEndedEarlyError(path: FileSourcePath, offset: number, size: number): Error {\n const end = offset + size\n return new Error(\n `FileSource: ${formatFilePath(path)} ended before byte range [${offset}, ${end}) was fully read.`,\n )\n}\n/* v8 ignore stop */\n\nasync function openValidatedFile(\n path: FileSourcePath,\n identity: FileIdentity,\n): Promise<FileHandleLike> {\n const open =\n fileSourceTestHooks.openFile ??\n (\n (await import('node:fs/promises')) as {\n open(path: FileSourcePath, flags: number): Promise<FileHandleLike>\n }\n ).open\n let file: FileHandleLike\n try {\n file = await open(path, await fileOpenFlags())\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`FileSource: ${formatFilePath(path)} could not be opened: ${message}`)\n }\n\n try {\n const stats = await file.stat()\n assertRegularFile(path, stats)\n assertSameIdentity(path, identity, stats, 'before read')\n return file\n } catch (err) {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n throw err\n }\n}\n\nasync function lstatNodeFile(path: FileSourcePath): Promise<FileStatsLike> {\n const { lstat } = (await import('node:fs/promises')) as {\n lstat(path: FileSourcePath): Promise<FileStatsLike>\n }\n return lstat(path)\n}\n\nasync function readFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n signal?: AbortSignal,\n): Promise<Uint8Array> {\n throwIfAborted(signal)\n if (size === 0) {\n await verifyFileIdentityForEmptyRead(path, identity, signal)\n return new Uint8Array(0)\n }\n\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n const data = new Uint8Array(size)\n let filled = 0\n try {\n while (filled < data.byteLength) {\n throwIfAborted(signal)\n const length = Math.min(maxReadSize(), data.byteLength - filled)\n const { bytesRead } = await file.read(data, filled, length, offset + filled)\n if (bytesRead === 0) throw rangeEndedEarlyError(path, offset, size)\n filled += bytesRead\n await fileSourceTestHooks.afterReadIteration?.(filled)\n }\n throwIfAborted(signal)\n const stats = await file.stat()\n assertSameIdentity(path, identity, stats, 'while being read')\n return data\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nasync function verifyFileIdentityForEmptyRead(\n path: FileSourcePath,\n identity: FileIdentity,\n signal?: AbortSignal,\n): Promise<void> {\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n try {\n throwIfAborted(signal)\n return\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nfunction sliceFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n start: number,\n end: number,\n): ContentSource {\n const normalizedStart = normalizeSliceOffset(start, size)\n const normalizedEnd = normalizeSliceOffset(end, size)\n return new FileSliceSource(\n path,\n identity,\n offset + normalizedStart,\n Math.max(normalizedEnd - normalizedStart, 0),\n )\n}\n\nfunction streamFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n): ReadableStream<Uint8Array> {\n let position = offset\n let remaining = size\n let verifiedEmpty = false\n const abortController = new AbortController()\n\n // Open per pull so abandoned public streams do not strand file handles or\n // retain Windows locks. Each chunk still verifies identity around its read.\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n if (remaining === 0) {\n if (!verifiedEmpty) {\n verifiedEmpty = true\n await verifyFileIdentityForEmptyRead(path, identity)\n }\n controller.close()\n return\n }\n\n const length = Math.min(FILE_STREAM_CHUNK_SIZE, remaining)\n const data = await readFileRange(path, identity, position, length, abortController.signal)\n position += data.byteLength\n remaining -= data.byteLength\n controller.enqueue(data)\n if (remaining === 0) controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n cancel(reason) {\n abortController.abort(reason)\n },\n })\n}\n\nasync function fileRangeToArrayBuffer(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n options: ToArrayBufferOptions = {},\n): Promise<ArrayBuffer> {\n const data = await readFileRange(path, identity, offset, size, options.signal)\n return data.buffer as ArrayBuffer\n}\n\nclass FileSliceSource implements ContentSource {\n readonly canSlice = true\n\n constructor(\n private readonly path: FileSourcePath,\n private readonly identity: FileIdentity,\n private readonly offset: number,\n readonly size: number,\n ) {}\n\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, this.offset, this.size, start, end)\n }\n\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, this.offset, this.size)\n }\n\n toArrayBuffer(options: ToArrayBufferOptions = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, this.offset, this.size, options)\n }\n}\n\n/**\n * ContentSource backed by a local filesystem path.\n *\n * `FileSource` is Node-only but safe to import in browser builds: it touches\n * Node filesystem APIs only when constructed or read. The constructor performs\n * synchronous filesystem validation so `size` is immediately available; request\n * handlers, sync loops, and other latency-sensitive code should use\n * {@link FileSource.fromPath}. Both paths capture a best-effort regular file\n * identity and reject a symlink as the final path component; parent directory\n * symlinks are followed by the operating system, so callers that constrain\n * paths under a trusted root should validate those parents separately. Reads\n * reject if the path is replaced, if the filesystem cannot report stable\n * identity on POSIX platforms, or if size/mtime/ctime changes before the\n * configured byte range is read. On Windows, reads avoid unreliable dev/inode\n * identity comparisons and validate size/mtime instead.\n * Slices preserve the captured identity, so multipart uploads can read\n * disjoint ranges without materialising the whole file in memory or following\n * later leaf path swaps.\n */\nexport class FileSource implements ContentSource {\n /** Random-access: file ranges are read by absolute byte offset. */\n readonly canSlice = true\n /** File size captured at construction time. */\n readonly size: number\n private readonly path: FileSourcePath\n private readonly identity: FileIdentity\n\n /**\n * Create a FileSource for a local file path.\n * @param path - Local filesystem path or file URL.\n *\n * @throws If the runtime has no Node-compatible filesystem API.\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n constructor(path: FileSourcePath)\n /**\n * Internal constructor path for async validation.\n * @param path - Local filesystem path or file URL.\n * @param internal - Module-private validated identity payload.\n *\n * @internal\n */\n constructor(path: FileSourcePath, internal?: FileSourceInternalOptions) {\n const resolvedIdentity =\n internal?.key === FILE_SOURCE_INTERNAL\n ? internal.identity\n : validatedIdentityFromStats(path, getNodeFsSync().lstatSync(path))\n this.path = path\n this.identity = resolvedIdentity\n this.size = resolvedIdentity.size\n }\n\n /**\n * Return a new file-backed source covering the specified byte range.\n * @param start - The zero-based byte offset to begin the slice.\n * @param end - The exclusive byte offset where the slice ends.\n *\n * @returns A new ContentSource representing the requested sub-range.\n */\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, 0, this.size, start, end)\n }\n\n /**\n * Open this file as a Web ReadableStream.\n * @returns A ReadableStream that reads the file lazily.\n */\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, 0, this.size)\n }\n\n /**\n * Read this file into an ArrayBuffer.\n * @param options - Optional abort signal used while reading.\n *\n * @returns A promise resolving with the file bytes.\n */\n toArrayBuffer(options: { readonly signal?: AbortSignal } = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, 0, this.size, options)\n }\n\n /**\n * Create a FileSource using asynchronous filesystem validation.\n * @param path - Local filesystem path or file URL.\n *\n * @returns A FileSource bound to the validated file identity.\n *\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n static async fromPath(path: FileSourcePath): Promise<FileSource> {\n const identity = validatedIdentityFromStats(path, await lstatNodeFile(path))\n return constructFileSourceFromIdentity(path, identity)\n }\n}\n\nfunction constructFileSourceFromIdentity(path: FileSourcePath, identity: FileIdentity): FileSource {\n // The public overload is intentionally single-argument; Reflect.construct\n // lets fromPath pass the module-private validated identity without exposing\n // that constructor shape in the public type surface.\n return Reflect.construct(FileSource, [\n path,\n { key: FILE_SOURCE_INTERNAL, identity },\n ]) as FileSource\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-source.cjs","names":[],"sources":["../../src/streams/file-source.ts"],"sourcesContent":["import type { ContentSource } from './source.ts'\n\nconst FILE_STREAM_CHUNK_SIZE = 16 * 1024 * 1024\n\n/** Filesystem path accepted by {@link FileSource}. */\nexport type FileSourcePath = string | URL\n\ninterface FileStatsLike {\n readonly dev: number\n readonly ino: number\n readonly mode: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n isFile(): boolean\n}\n\ninterface FileHandleLike {\n read(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ bytesRead: number }>\n stat(): Promise<FileStatsLike>\n close(): Promise<void>\n}\n\ninterface NodeFsSync {\n lstatSync(path: FileSourcePath): FileStatsLike\n}\n\ninterface FileIdentity {\n readonly dev: number\n readonly ino: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n}\n\ninterface ToArrayBufferOptions {\n readonly signal?: AbortSignal\n}\n\nconst FILE_SOURCE_INTERNAL = Symbol('FileSource.internal')\n\ninterface FileSourceInternalOptions {\n readonly key: typeof FILE_SOURCE_INTERNAL\n readonly identity: FileIdentity\n}\n\n/** @internal */\nexport const fileSourceTestHooks: {\n afterReadIteration?: (filled: number) => Promise<void> | void\n maxReadSize?: number\n openFile?: (path: FileSourcePath, flags: number) => Promise<FileHandleLike>\n platform?: string\n} = {}\n\nfunction getNodeFsSync(): NodeFsSync {\n // FileSource exposes `size` synchronously, so the constructor cannot use the\n // repo's usual `await import('node:...')` pattern for Node-only APIs. Keep the\n // synchronous lookup here; async callers should prefer `FileSource.fromPath()`.\n const processLike = (\n globalThis as {\n process?: { getBuiltinModule?: (id: string) => unknown }\n }\n ).process\n const fs = processLike?.getBuiltinModule?.('node:fs')\n if (!isNodeFsSync(fs)) {\n throw new Error(\n 'FileSource constructor requires process.getBuiltinModule(\"node:fs\") with synchronous filesystem APIs; use FileSource.fromPath() when synchronous filesystem access is unavailable.',\n )\n }\n return fs\n}\n\nfunction isNodeFsSync(value: unknown): value is NodeFsSync {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return typeof candidate['lstatSync'] === 'function'\n}\n\nasync function fileOpenFlags(): Promise<number> {\n const { constants } = await import('node:fs')\n // O_NOFOLLOW and O_NONBLOCK are unavailable on some platforms. When\n // O_NOFOLLOW is absent, the post-open fstat identity check below is the\n // symlink-swap defense. O_NONBLOCK avoids blocking indefinitely if a path is\n // swapped to a FIFO or another blocking special file before fstat rejects it.\n return (\n constants.O_RDONLY |\n (constants.O_NOFOLLOW ?? 0) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n )\n}\n\nfunction normalizeSliceOffset(value: number, size: number): number {\n if (!Number.isFinite(value)) throw new RangeError('FileSource slice offsets must be finite.')\n const integer = Math.trunc(value)\n const offset = integer < 0 ? size + integer : integer\n return Math.min(Math.max(offset, 0), size)\n}\n\nfunction formatFilePath(path: FileSourcePath): string {\n return path instanceof URL ? path.href : path\n}\n\nfunction identityFromStats(stats: FileStatsLike): FileIdentity {\n return {\n dev: stats.dev,\n ino: stats.ino,\n size: stats.size,\n mtimeMs: stats.mtimeMs,\n ctimeMs: stats.ctimeMs,\n }\n}\n\nfunction assertStableIdentity(path: FileSourcePath, stats: FileStatsLike): void {\n if (stats.dev === 0 && stats.ino === 0) {\n throw new Error(\n `FileSource: ${formatFilePath(path)} is on a filesystem that does not expose stable file identity.`,\n )\n }\n}\n\nfunction assertRegularFile(path: FileSourcePath, stats: FileStatsLike): void {\n if (!stats.isFile()) {\n throw new Error(`FileSource: ${formatFilePath(path)} is not a regular file.`)\n }\n}\n\nfunction validatedIdentityFromStats(path: FileSourcePath, stats: FileStatsLike): FileIdentity {\n assertRegularFile(path, stats)\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, stats)\n return identityFromStats(stats)\n}\n\nfunction assertSameIdentity(\n path: FileSourcePath,\n expected: FileIdentity,\n actual: FileStatsLike,\n when: string,\n): void {\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, actual)\n if (\n shouldComparePosixFileIdentity() &&\n (actual.dev !== expected.dev || actual.ino !== expected.ino)\n ) {\n throw new Error(`FileSource: ${formatFilePath(path)} changed ${when}.`)\n }\n if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n if (shouldComparePosixChangeTime() && actual.ctimeMs !== expected.ctimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n}\n\nfunction isWindows(): boolean {\n if (fileSourceTestHooks.platform !== undefined) return fileSourceTestHooks.platform === 'win32'\n const processLike = (globalThis as { process?: { platform?: string } }).process\n return processLike?.platform === 'win32'\n}\n\nfunction shouldComparePosixFileIdentity(): boolean {\n return !isWindows()\n}\n\nfunction shouldComparePosixChangeTime(): boolean {\n return !isWindows()\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal === undefined || !signal.aborted) return\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n}\n\nfunction maxReadSize(): number {\n return fileSourceTestHooks.maxReadSize ?? FILE_STREAM_CHUNK_SIZE\n}\n\n/* v8 ignore start -- Requires a file to pass identity checks and still EOF mid-range. */\nfunction rangeEndedEarlyError(path: FileSourcePath, offset: number, size: number): Error {\n const end = offset + size\n return new Error(\n `FileSource: ${formatFilePath(path)} ended before byte range [${offset}, ${end}) was fully read.`,\n )\n}\n/* v8 ignore stop */\n\nasync function openValidatedFile(\n path: FileSourcePath,\n identity: FileIdentity,\n): Promise<FileHandleLike> {\n const open =\n fileSourceTestHooks.openFile ??\n (\n (await import('node:fs/promises')) as {\n open(path: FileSourcePath, flags: number): Promise<FileHandleLike>\n }\n ).open\n let file: FileHandleLike\n try {\n file = await open(path, await fileOpenFlags())\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`FileSource: ${formatFilePath(path)} could not be opened: ${message}`)\n }\n\n try {\n const stats = await file.stat()\n assertRegularFile(path, stats)\n assertSameIdentity(path, identity, stats, 'before read')\n return file\n } catch (err) {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n throw err\n }\n}\n\nasync function lstatNodeFile(path: FileSourcePath): Promise<FileStatsLike> {\n const { lstat } = (await import('node:fs/promises')) as {\n lstat(path: FileSourcePath): Promise<FileStatsLike>\n }\n return lstat(path)\n}\n\nasync function readFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n signal?: AbortSignal,\n): Promise<Uint8Array> {\n throwIfAborted(signal)\n if (size === 0) {\n await verifyFileIdentityForEmptyRead(path, identity, signal)\n return new Uint8Array(0)\n }\n\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n const data = new Uint8Array(size)\n let filled = 0\n try {\n while (filled < data.byteLength) {\n throwIfAborted(signal)\n const length = Math.min(maxReadSize(), data.byteLength - filled)\n const { bytesRead } = await file.read(data, filled, length, offset + filled)\n if (bytesRead === 0) throw rangeEndedEarlyError(path, offset, size)\n filled += bytesRead\n await fileSourceTestHooks.afterReadIteration?.(filled)\n }\n throwIfAborted(signal)\n const stats = await file.stat()\n assertSameIdentity(path, identity, stats, 'while being read')\n return data\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nasync function verifyFileIdentityForEmptyRead(\n path: FileSourcePath,\n identity: FileIdentity,\n signal?: AbortSignal,\n): Promise<void> {\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n try {\n throwIfAborted(signal)\n return\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nfunction sliceFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n start: number,\n end: number,\n): ContentSource {\n const normalizedStart = normalizeSliceOffset(start, size)\n const normalizedEnd = normalizeSliceOffset(end, size)\n return new FileSliceSource(\n path,\n identity,\n offset + normalizedStart,\n Math.max(normalizedEnd - normalizedStart, 0),\n )\n}\n\nfunction streamFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n): ReadableStream<Uint8Array> {\n let position = offset\n let remaining = size\n let verifiedEmpty = false\n const abortController = new AbortController()\n\n // Open per pull so abandoned public streams do not strand file handles or\n // retain Windows locks. Each chunk still verifies identity around its read.\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n if (remaining === 0) {\n if (!verifiedEmpty) {\n verifiedEmpty = true\n await verifyFileIdentityForEmptyRead(path, identity)\n }\n controller.close()\n return\n }\n\n const length = Math.min(FILE_STREAM_CHUNK_SIZE, remaining)\n const data = await readFileRange(path, identity, position, length, abortController.signal)\n position += data.byteLength\n remaining -= data.byteLength\n controller.enqueue(data)\n if (remaining === 0) controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n cancel(reason) {\n abortController.abort(reason)\n },\n })\n}\n\nasync function fileRangeToArrayBuffer(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n options: ToArrayBufferOptions = {},\n): Promise<ArrayBuffer> {\n const data = await readFileRange(path, identity, offset, size, options.signal)\n return data.buffer as ArrayBuffer\n}\n\nclass FileSliceSource implements ContentSource {\n readonly canSlice = true\n\n constructor(\n private readonly path: FileSourcePath,\n private readonly identity: FileIdentity,\n private readonly offset: number,\n readonly size: number,\n ) {}\n\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, this.offset, this.size, start, end)\n }\n\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, this.offset, this.size)\n }\n\n toArrayBuffer(options: ToArrayBufferOptions = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, this.offset, this.size, options)\n }\n}\n\n/**\n * ContentSource backed by a local filesystem path.\n *\n * `FileSource` is Node-only but safe to import in browser builds: it touches\n * Node filesystem APIs only when constructed or read. The constructor performs\n * synchronous filesystem validation so `size` is immediately available; request\n * handlers, sync loops, and other latency-sensitive code should use\n * {@link FileSource.fromPath}. Both paths capture a best-effort regular file\n * identity and reject a symlink as the final path component; parent directory\n * symlinks are followed by the operating system, so callers that constrain\n * paths under a trusted root should validate those parents separately. Reads\n * reject if the path is replaced, if the filesystem cannot report stable\n * identity on POSIX platforms, or if size/mtime/ctime changes before the\n * configured byte range is read. On Windows, reads avoid unreliable dev/inode\n * identity comparisons and validate size/mtime instead.\n * Slices preserve the captured identity, so multipart uploads can read\n * disjoint ranges without materialising the whole file in memory or following\n * later leaf path swaps.\n */\nexport class FileSource implements ContentSource {\n /** Random-access: file ranges are read by absolute byte offset. */\n readonly canSlice = true\n /** File size captured at construction time. */\n readonly size: number\n private readonly path: FileSourcePath\n private readonly identity: FileIdentity\n\n /**\n * Create a FileSource for a local file path.\n * @param path - Local filesystem path or file URL.\n *\n * @throws If the runtime has no Node-compatible filesystem API.\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n constructor(path: FileSourcePath)\n /**\n * Internal constructor path for async validation.\n * @param path - Local filesystem path or file URL.\n * @param internal - Module-private validated identity payload.\n *\n * @internal\n */\n constructor(path: FileSourcePath, internal?: FileSourceInternalOptions) {\n const resolvedIdentity =\n internal?.key === FILE_SOURCE_INTERNAL\n ? internal.identity\n : validatedIdentityFromStats(path, getNodeFsSync().lstatSync(path))\n this.path = path\n this.identity = resolvedIdentity\n this.size = resolvedIdentity.size\n }\n\n /**\n * Return a new file-backed source covering the specified byte range.\n * @param start - The zero-based byte offset to begin the slice.\n * @param end - The exclusive byte offset where the slice ends.\n *\n * @returns A new ContentSource representing the requested sub-range.\n */\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, 0, this.size, start, end)\n }\n\n /**\n * Open this file as a Web ReadableStream.\n * @returns A ReadableStream that reads the file lazily.\n */\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, 0, this.size)\n }\n\n /**\n * Read this file into an ArrayBuffer.\n * @param options - Optional abort signal used while reading.\n *\n * @returns A promise resolving with the file bytes.\n */\n toArrayBuffer(options: { readonly signal?: AbortSignal } = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, 0, this.size, options)\n }\n\n /**\n * Create a FileSource using asynchronous filesystem validation.\n * @param path - Local filesystem path or file URL.\n *\n * @returns A FileSource bound to the validated file identity.\n *\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n static async fromPath(path: FileSourcePath): Promise<FileSource> {\n const identity = validatedIdentityFromStats(path, await lstatNodeFile(path))\n return constructFileSourceFromIdentity(path, identity)\n }\n}\n\nfunction constructFileSourceFromIdentity(path: FileSourcePath, identity: FileIdentity): FileSource {\n // The public overload is intentionally single-argument; Reflect.construct\n // lets fromPath pass the module-private validated identity without exposing\n // that constructor shape in the public type surface.\n return Reflect.construct(FileSource, [\n path,\n { key: FILE_SOURCE_INTERNAL, identity },\n ]) as FileSource\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ var FILE_SOURCE_INTERNAL = Symbol("FileSource.internal");
|
|
|
5
5
|
var fileSourceTestHooks = {};
|
|
6
6
|
function getNodeFsSync() {
|
|
7
7
|
const fs = globalThis.process?.getBuiltinModule?.("node:fs");
|
|
8
|
-
if (!isNodeFsSync(fs)) throw new Error("FileSource constructor requires
|
|
8
|
+
if (!isNodeFsSync(fs)) throw new Error("FileSource constructor requires process.getBuiltinModule(\"node:fs\") with synchronous filesystem APIs; use FileSource.fromPath() when synchronous filesystem access is unavailable.");
|
|
9
9
|
return fs;
|
|
10
10
|
}
|
|
11
11
|
function isNodeFsSync(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-source.js","names":[],"sources":["../../src/streams/file-source.ts"],"sourcesContent":["import type { ContentSource } from './source.ts'\n\nconst FILE_STREAM_CHUNK_SIZE = 16 * 1024 * 1024\n\n/** Filesystem path accepted by {@link FileSource}. */\nexport type FileSourcePath = string | URL\n\ninterface FileStatsLike {\n readonly dev: number\n readonly ino: number\n readonly mode: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n isFile(): boolean\n}\n\ninterface FileHandleLike {\n read(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ bytesRead: number }>\n stat(): Promise<FileStatsLike>\n close(): Promise<void>\n}\n\ninterface NodeFsSync {\n lstatSync(path: FileSourcePath): FileStatsLike\n}\n\ninterface FileIdentity {\n readonly dev: number\n readonly ino: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n}\n\ninterface ToArrayBufferOptions {\n readonly signal?: AbortSignal\n}\n\nconst FILE_SOURCE_INTERNAL = Symbol('FileSource.internal')\n\ninterface FileSourceInternalOptions {\n readonly key: typeof FILE_SOURCE_INTERNAL\n readonly identity: FileIdentity\n}\n\n/** @internal */\nexport const fileSourceTestHooks: {\n afterReadIteration?: (filled: number) => Promise<void> | void\n maxReadSize?: number\n openFile?: (path: FileSourcePath, flags: number) => Promise<FileHandleLike>\n platform?: string\n} = {}\n\nfunction getNodeFsSync(): NodeFsSync {\n // FileSource exposes `size` synchronously, so the constructor cannot use the\n // repo's usual `await import('node:...')` pattern for Node-only APIs. Keep the\n // synchronous lookup here; async callers should prefer `FileSource.fromPath()`.\n const processLike = (\n globalThis as {\n process?: { getBuiltinModule?: (id: string) => unknown }\n }\n ).process\n const fs = processLike?.getBuiltinModule?.('node:fs')\n if (!isNodeFsSync(fs)) {\n throw new Error(\n 'FileSource constructor requires Node.js 22.3+ synchronous filesystem APIs; use FileSource.fromPath() when synchronous filesystem access is unavailable.',\n )\n }\n return fs\n}\n\nfunction isNodeFsSync(value: unknown): value is NodeFsSync {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return typeof candidate['lstatSync'] === 'function'\n}\n\nasync function fileOpenFlags(): Promise<number> {\n const { constants } = await import('node:fs')\n // O_NOFOLLOW and O_NONBLOCK are unavailable on some platforms. When\n // O_NOFOLLOW is absent, the post-open fstat identity check below is the\n // symlink-swap defense. O_NONBLOCK avoids blocking indefinitely if a path is\n // swapped to a FIFO or another blocking special file before fstat rejects it.\n return (\n constants.O_RDONLY |\n (constants.O_NOFOLLOW ?? 0) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n )\n}\n\nfunction normalizeSliceOffset(value: number, size: number): number {\n if (!Number.isFinite(value)) throw new RangeError('FileSource slice offsets must be finite.')\n const integer = Math.trunc(value)\n const offset = integer < 0 ? size + integer : integer\n return Math.min(Math.max(offset, 0), size)\n}\n\nfunction formatFilePath(path: FileSourcePath): string {\n return path instanceof URL ? path.href : path\n}\n\nfunction identityFromStats(stats: FileStatsLike): FileIdentity {\n return {\n dev: stats.dev,\n ino: stats.ino,\n size: stats.size,\n mtimeMs: stats.mtimeMs,\n ctimeMs: stats.ctimeMs,\n }\n}\n\nfunction assertStableIdentity(path: FileSourcePath, stats: FileStatsLike): void {\n if (stats.dev === 0 && stats.ino === 0) {\n throw new Error(\n `FileSource: ${formatFilePath(path)} is on a filesystem that does not expose stable file identity.`,\n )\n }\n}\n\nfunction assertRegularFile(path: FileSourcePath, stats: FileStatsLike): void {\n if (!stats.isFile()) {\n throw new Error(`FileSource: ${formatFilePath(path)} is not a regular file.`)\n }\n}\n\nfunction validatedIdentityFromStats(path: FileSourcePath, stats: FileStatsLike): FileIdentity {\n assertRegularFile(path, stats)\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, stats)\n return identityFromStats(stats)\n}\n\nfunction assertSameIdentity(\n path: FileSourcePath,\n expected: FileIdentity,\n actual: FileStatsLike,\n when: string,\n): void {\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, actual)\n if (\n shouldComparePosixFileIdentity() &&\n (actual.dev !== expected.dev || actual.ino !== expected.ino)\n ) {\n throw new Error(`FileSource: ${formatFilePath(path)} changed ${when}.`)\n }\n if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n if (shouldComparePosixChangeTime() && actual.ctimeMs !== expected.ctimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n}\n\nfunction isWindows(): boolean {\n if (fileSourceTestHooks.platform !== undefined) return fileSourceTestHooks.platform === 'win32'\n const processLike = (globalThis as { process?: { platform?: string } }).process\n return processLike?.platform === 'win32'\n}\n\nfunction shouldComparePosixFileIdentity(): boolean {\n return !isWindows()\n}\n\nfunction shouldComparePosixChangeTime(): boolean {\n return !isWindows()\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal === undefined || !signal.aborted) return\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n}\n\nfunction maxReadSize(): number {\n return fileSourceTestHooks.maxReadSize ?? FILE_STREAM_CHUNK_SIZE\n}\n\n/* v8 ignore start -- Requires a file to pass identity checks and still EOF mid-range. */\nfunction rangeEndedEarlyError(path: FileSourcePath, offset: number, size: number): Error {\n const end = offset + size\n return new Error(\n `FileSource: ${formatFilePath(path)} ended before byte range [${offset}, ${end}) was fully read.`,\n )\n}\n/* v8 ignore stop */\n\nasync function openValidatedFile(\n path: FileSourcePath,\n identity: FileIdentity,\n): Promise<FileHandleLike> {\n const open =\n fileSourceTestHooks.openFile ??\n (\n (await import('node:fs/promises')) as {\n open(path: FileSourcePath, flags: number): Promise<FileHandleLike>\n }\n ).open\n let file: FileHandleLike\n try {\n file = await open(path, await fileOpenFlags())\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`FileSource: ${formatFilePath(path)} could not be opened: ${message}`)\n }\n\n try {\n const stats = await file.stat()\n assertRegularFile(path, stats)\n assertSameIdentity(path, identity, stats, 'before read')\n return file\n } catch (err) {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n throw err\n }\n}\n\nasync function lstatNodeFile(path: FileSourcePath): Promise<FileStatsLike> {\n const { lstat } = (await import('node:fs/promises')) as {\n lstat(path: FileSourcePath): Promise<FileStatsLike>\n }\n return lstat(path)\n}\n\nasync function readFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n signal?: AbortSignal,\n): Promise<Uint8Array> {\n throwIfAborted(signal)\n if (size === 0) {\n await verifyFileIdentityForEmptyRead(path, identity, signal)\n return new Uint8Array(0)\n }\n\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n const data = new Uint8Array(size)\n let filled = 0\n try {\n while (filled < data.byteLength) {\n throwIfAborted(signal)\n const length = Math.min(maxReadSize(), data.byteLength - filled)\n const { bytesRead } = await file.read(data, filled, length, offset + filled)\n if (bytesRead === 0) throw rangeEndedEarlyError(path, offset, size)\n filled += bytesRead\n await fileSourceTestHooks.afterReadIteration?.(filled)\n }\n throwIfAborted(signal)\n const stats = await file.stat()\n assertSameIdentity(path, identity, stats, 'while being read')\n return data\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nasync function verifyFileIdentityForEmptyRead(\n path: FileSourcePath,\n identity: FileIdentity,\n signal?: AbortSignal,\n): Promise<void> {\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n try {\n throwIfAborted(signal)\n return\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nfunction sliceFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n start: number,\n end: number,\n): ContentSource {\n const normalizedStart = normalizeSliceOffset(start, size)\n const normalizedEnd = normalizeSliceOffset(end, size)\n return new FileSliceSource(\n path,\n identity,\n offset + normalizedStart,\n Math.max(normalizedEnd - normalizedStart, 0),\n )\n}\n\nfunction streamFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n): ReadableStream<Uint8Array> {\n let position = offset\n let remaining = size\n let verifiedEmpty = false\n const abortController = new AbortController()\n\n // Open per pull so abandoned public streams do not strand file handles or\n // retain Windows locks. Each chunk still verifies identity around its read.\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n if (remaining === 0) {\n if (!verifiedEmpty) {\n verifiedEmpty = true\n await verifyFileIdentityForEmptyRead(path, identity)\n }\n controller.close()\n return\n }\n\n const length = Math.min(FILE_STREAM_CHUNK_SIZE, remaining)\n const data = await readFileRange(path, identity, position, length, abortController.signal)\n position += data.byteLength\n remaining -= data.byteLength\n controller.enqueue(data)\n if (remaining === 0) controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n cancel(reason) {\n abortController.abort(reason)\n },\n })\n}\n\nasync function fileRangeToArrayBuffer(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n options: ToArrayBufferOptions = {},\n): Promise<ArrayBuffer> {\n const data = await readFileRange(path, identity, offset, size, options.signal)\n return data.buffer as ArrayBuffer\n}\n\nclass FileSliceSource implements ContentSource {\n readonly canSlice = true\n\n constructor(\n private readonly path: FileSourcePath,\n private readonly identity: FileIdentity,\n private readonly offset: number,\n readonly size: number,\n ) {}\n\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, this.offset, this.size, start, end)\n }\n\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, this.offset, this.size)\n }\n\n toArrayBuffer(options: ToArrayBufferOptions = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, this.offset, this.size, options)\n }\n}\n\n/**\n * ContentSource backed by a local filesystem path.\n *\n * `FileSource` is Node-only but safe to import in browser builds: it touches\n * Node filesystem APIs only when constructed or read. The constructor performs\n * synchronous filesystem validation so `size` is immediately available; request\n * handlers, sync loops, and other latency-sensitive code should use\n * {@link FileSource.fromPath}. Both paths capture a best-effort regular file\n * identity and reject a symlink as the final path component; parent directory\n * symlinks are followed by the operating system, so callers that constrain\n * paths under a trusted root should validate those parents separately. Reads\n * reject if the path is replaced, if the filesystem cannot report stable\n * identity on POSIX platforms, or if size/mtime/ctime changes before the\n * configured byte range is read. On Windows, reads avoid unreliable dev/inode\n * identity comparisons and validate size/mtime instead.\n * Slices preserve the captured identity, so multipart uploads can read\n * disjoint ranges without materialising the whole file in memory or following\n * later leaf path swaps.\n */\nexport class FileSource implements ContentSource {\n /** Random-access: file ranges are read by absolute byte offset. */\n readonly canSlice = true\n /** File size captured at construction time. */\n readonly size: number\n private readonly path: FileSourcePath\n private readonly identity: FileIdentity\n\n /**\n * Create a FileSource for a local file path.\n * @param path - Local filesystem path or file URL.\n *\n * @throws If the runtime has no Node-compatible filesystem API.\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n constructor(path: FileSourcePath)\n /**\n * Internal constructor path for async validation.\n * @param path - Local filesystem path or file URL.\n * @param internal - Module-private validated identity payload.\n *\n * @internal\n */\n constructor(path: FileSourcePath, internal?: FileSourceInternalOptions) {\n const resolvedIdentity =\n internal?.key === FILE_SOURCE_INTERNAL\n ? internal.identity\n : validatedIdentityFromStats(path, getNodeFsSync().lstatSync(path))\n this.path = path\n this.identity = resolvedIdentity\n this.size = resolvedIdentity.size\n }\n\n /**\n * Return a new file-backed source covering the specified byte range.\n * @param start - The zero-based byte offset to begin the slice.\n * @param end - The exclusive byte offset where the slice ends.\n *\n * @returns A new ContentSource representing the requested sub-range.\n */\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, 0, this.size, start, end)\n }\n\n /**\n * Open this file as a Web ReadableStream.\n * @returns A ReadableStream that reads the file lazily.\n */\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, 0, this.size)\n }\n\n /**\n * Read this file into an ArrayBuffer.\n * @param options - Optional abort signal used while reading.\n *\n * @returns A promise resolving with the file bytes.\n */\n toArrayBuffer(options: { readonly signal?: AbortSignal } = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, 0, this.size, options)\n }\n\n /**\n * Create a FileSource using asynchronous filesystem validation.\n * @param path - Local filesystem path or file URL.\n *\n * @returns A FileSource bound to the validated file identity.\n *\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n static async fromPath(path: FileSourcePath): Promise<FileSource> {\n const identity = validatedIdentityFromStats(path, await lstatNodeFile(path))\n return constructFileSourceFromIdentity(path, identity)\n }\n}\n\nfunction constructFileSourceFromIdentity(path: FileSourcePath, identity: FileIdentity): FileSource {\n // The public overload is intentionally single-argument; Reflect.construct\n // lets fromPath pass the module-private validated identity without exposing\n // that constructor shape in the public type surface.\n return Reflect.construct(FileSource, [\n path,\n { key: FILE_SOURCE_INTERNAL, identity },\n ]) as FileSource\n}\n"],"mappings":";AAEA,IAAM,yBAAyB;AA0C/B,IAAM,uBAAuB,OAAO,qBAAqB;;AAQzD,IAAa,sBAKT,CAAC;AAEL,SAAS,gBAA4B;CASnC,MAAM,KAJJ,WAGA,SACsB,mBAAmB,SAAS;CACpD,IAAI,CAAC,aAAa,EAAE,GAClB,MAAM,IAAI,MACR,yJACF;CAEF,OAAO;AACT;AAEA,SAAS,aAAa,OAAqC;CACzD,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CAExD,OAAO,OAAO,MAAU,iBAAiB;AAC3C;AAEA,eAAe,gBAAiC;CAC9C,MAAM,EAAE,cAAc,MAAM,OAAO;CAKnC,OACE,UAAU,YACT,UAAU,cAAc,MACvB,UAAsC,cAAc;AAE1D;AAEA,SAAS,qBAAqB,OAAe,MAAsB;CACjE,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,MAAM,IAAI,WAAW,0CAA0C;CAC5F,MAAM,UAAU,KAAK,MAAM,KAAK;CAChC,MAAM,SAAS,UAAU,IAAI,OAAO,UAAU;CAC9C,OAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,GAAG,IAAI;AAC3C;AAEA,SAAS,eAAe,MAA8B;CACpD,OAAO,gBAAgB,MAAM,KAAK,OAAO;AAC3C;AAEA,SAAS,kBAAkB,OAAoC;CAC7D,OAAO;EACL,KAAK,MAAM;EACX,KAAK,MAAM;EACX,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,SAAS,MAAM;CACjB;AACF;AAEA,SAAS,qBAAqB,MAAsB,OAA4B;CAC9E,IAAI,MAAM,QAAQ,KAAK,MAAM,QAAQ,GACnC,MAAM,IAAI,MACR,eAAe,eAAe,IAAI,EAAE,+DACtC;AAEJ;AAEA,SAAS,kBAAkB,MAAsB,OAA4B;CAC3E,IAAI,CAAC,MAAM,OAAO,GAChB,MAAM,IAAI,MAAM,eAAe,eAAe,IAAI,EAAE,wBAAwB;AAEhF;AAEA,SAAS,2BAA2B,MAAsB,OAAoC;CAC5F,kBAAkB,MAAM,KAAK;CAC7B,IAAI,+BAA+B,GAAG,qBAAqB,MAAM,KAAK;CACtE,OAAO,kBAAkB,KAAK;AAChC;AAEA,SAAS,mBACP,MACA,UACA,QACA,MACM;CACN,IAAI,+BAA+B,GAAG,qBAAqB,MAAM,MAAM;CACvE,IACE,+BAA+B,MAC9B,OAAO,QAAQ,SAAS,OAAO,OAAO,QAAQ,SAAS,MAExD,MAAM,IAAI,MAAM,eAAe,eAAe,IAAI,EAAE,WAAW,KAAK,EAAE;CAExE,IAAI,OAAO,SAAS,SAAS,QAAQ,OAAO,YAAY,SAAS,SAC/D,MAAM,IAAI,MAAM,eAAe,eAAe,IAAI,EAAE,gBAAgB,KAAK,EAAE;CAE7E,IAAI,6BAA6B,KAAK,OAAO,YAAY,SAAS,SAChE,MAAM,IAAI,MAAM,eAAe,eAAe,IAAI,EAAE,gBAAgB,KAAK,EAAE;AAE/E;AAEA,SAAS,YAAqB;CAC5B,IAAI,oBAAoB,aAAa,KAAA,GAAW,OAAO,oBAAoB,aAAa;CAExF,OADqB,WAAmD,SACpD,aAAa;AACnC;AAEA,SAAS,iCAA0C;CACjD,OAAO,CAAC,UAAU;AACpB;AAEA,SAAS,+BAAwC;CAC/C,OAAO,CAAC,UAAU;AACpB;AAEA,SAAS,eAAe,QAAuC;CAC7D,IAAI,WAAW,KAAA,KAAa,CAAC,OAAO,SAAS;CAC7C,MAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AACjE;AAEA,SAAS,cAAsB;CAC7B,OAAO,oBAAoB,eAAe;AAC5C;;AAGA,SAAS,qBAAqB,MAAsB,QAAgB,MAAqB;CACvF,MAAM,MAAM,SAAS;CACrB,uBAAO,IAAI,MACT,eAAe,eAAe,IAAI,EAAE,4BAA4B,OAAO,IAAI,IAAI,kBACjF;AACF;;AAGA,eAAe,kBACb,MACA,UACyB;CACzB,MAAM,OACJ,oBAAoB,aAEjB,MAAM,OAAO,oBAAA,CAGd;CACJ,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,KAAK,MAAM,MAAM,cAAc,CAAC;CAC/C,SAAS,KAAK;EACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;EAC/D,MAAM,IAAI,MAAM,eAAe,eAAe,IAAI,EAAE,wBAAwB,SAAS;CACvF;CAEA,IAAI;EACF,MAAM,QAAQ,MAAM,KAAK,KAAK;EAC9B,kBAAkB,MAAM,KAAK;EAC7B,mBAAmB,MAAM,UAAU,OAAO,aAAa;EACvD,OAAO;CACT,SAAS,KAAK;;EAEZ,MAAM,KAAK,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EACjC,MAAM;CACR;AACF;AAEA,eAAe,cAAc,MAA8C;CACzE,MAAM,EAAE,UAAW,MAAM,OAAO;CAGhC,OAAO,MAAM,IAAI;AACnB;AAEA,eAAe,cACb,MACA,UACA,QACA,MACA,QACqB;CACrB,eAAe,MAAM;CACrB,IAAI,SAAS,GAAG;EACd,MAAM,+BAA+B,MAAM,UAAU,MAAM;EAC3D,uBAAO,IAAI,WAAW,CAAC;CACzB;CAEA,eAAe,MAAM;CACrB,MAAM,OAAO,MAAM,kBAAkB,MAAM,QAAQ;CACnD,MAAM,OAAO,IAAI,WAAW,IAAI;CAChC,IAAI,SAAS;CACb,IAAI;EACF,OAAO,SAAS,KAAK,YAAY;GAC/B,eAAe,MAAM;GACrB,MAAM,SAAS,KAAK,IAAI,YAAY,GAAG,KAAK,aAAa,MAAM;GAC/D,MAAM,EAAE,cAAc,MAAM,KAAK,KAAK,MAAM,QAAQ,QAAQ,SAAS,MAAM;GAC3E,IAAI,cAAc,GAAG,MAAM,qBAAqB,MAAM,QAAQ,IAAI;GAClE,UAAU;GACV,MAAM,oBAAoB,qBAAqB,MAAM;EACvD;EACA,eAAe,MAAM;EAErB,mBAAmB,MAAM,UAAU,MADf,KAAK,KAAK,GACY,kBAAkB;EAC5D,OAAO;CACT,UAAU;;EAER,MAAM,KAAK,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CACnC;AACF;AAEA,eAAe,+BACb,MACA,UACA,QACe;CACf,eAAe,MAAM;CACrB,MAAM,OAAO,MAAM,kBAAkB,MAAM,QAAQ;CACnD,IAAI;EACF,eAAe,MAAM;EACrB;CACF,UAAU;;EAER,MAAM,KAAK,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CACnC;AACF;AAEA,SAAS,eACP,MACA,UACA,QACA,MACA,OACA,KACe;CACf,MAAM,kBAAkB,qBAAqB,OAAO,IAAI;CACxD,MAAM,gBAAgB,qBAAqB,KAAK,IAAI;CACpD,OAAO,IAAI,gBACT,MACA,UACA,SAAS,iBACT,KAAK,IAAI,gBAAgB,iBAAiB,CAAC,CAC7C;AACF;AAEA,SAAS,gBACP,MACA,UACA,QACA,MAC4B;CAC5B,IAAI,WAAW;CACf,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,MAAM,kBAAkB,IAAI,gBAAgB;CAI5C,OAAO,IAAI,eAA2B;EACpC,MAAM,KAAK,YAAY;GACrB,IAAI;IACF,IAAI,cAAc,GAAG;KACnB,IAAI,CAAC,eAAe;MAClB,gBAAgB;MAChB,MAAM,+BAA+B,MAAM,QAAQ;KACrD;KACA,WAAW,MAAM;KACjB;IACF;IAGA,MAAM,OAAO,MAAM,cAAc,MAAM,UAAU,UADlC,KAAK,IAAI,wBAAwB,SACW,GAAQ,gBAAgB,MAAM;IACzF,YAAY,KAAK;IACjB,aAAa,KAAK;IAClB,WAAW,QAAQ,IAAI;IACvB,IAAI,cAAc,GAAG,WAAW,MAAM;GACxC,SAAS,KAAK;IACZ,WAAW,MAAM,GAAG;GACtB;EACF;EACA,OAAO,QAAQ;GACb,gBAAgB,MAAM,MAAM;EAC9B;CACF,CAAC;AACH;AAEA,eAAe,uBACb,MACA,UACA,QACA,MACA,UAAgC,CAAC,GACX;CAEtB,QAAO,MADY,cAAc,MAAM,UAAU,QAAQ,MAAM,QAAQ,MAAM,EAAA,CACjE;AACd;AAEA,IAAM,kBAAN,MAA+C;CAI1B;CACA;CACA;CACR;CANX,WAAoB;CAEpB,YACE,MACA,UACA,QACA,MACA;EAJiB,KAAA,OAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EACR,KAAA,OAAA;CACR;CAEH,MAAM,OAAe,KAA4B;EAC/C,OAAO,eAAe,KAAK,MAAM,KAAK,UAAU,KAAK,QAAQ,KAAK,MAAM,OAAO,GAAG;CACpF;CAEA,SAAqC;EACnC,OAAO,gBAAgB,KAAK,MAAM,KAAK,UAAU,KAAK,QAAQ,KAAK,IAAI;CACzE;CAEA,cAAc,UAAgC,CAAC,GAAyB;EACtE,OAAO,uBAAuB,KAAK,MAAM,KAAK,UAAU,KAAK,QAAQ,KAAK,MAAM,OAAO;CACzF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,aAAb,MAAiD;;CAE/C,WAAoB;;CAEpB;CACA;CACA;;;;;;;;CAkBA,YAAY,MAAsB,UAAsC;EACtE,MAAM,mBACJ,UAAU,QAAQ,uBACd,SAAS,WACT,2BAA2B,MAAM,cAAc,CAAC,CAAC,UAAU,IAAI,CAAC;EACtE,KAAK,OAAO;EACZ,KAAK,WAAW;EAChB,KAAK,OAAO,iBAAiB;CAC/B;;;;;;;;CASA,MAAM,OAAe,KAA4B;EAC/C,OAAO,eAAe,KAAK,MAAM,KAAK,UAAU,GAAG,KAAK,MAAM,OAAO,GAAG;CAC1E;;;;;CAMA,SAAqC;EACnC,OAAO,gBAAgB,KAAK,MAAM,KAAK,UAAU,GAAG,KAAK,IAAI;CAC/D;;;;;;;CAQA,cAAc,UAA6C,CAAC,GAAyB;EACnF,OAAO,uBAAuB,KAAK,MAAM,KAAK,UAAU,GAAG,KAAK,MAAM,OAAO;CAC/E;;;;;;;;;;CAWA,aAAa,SAAS,MAA2C;EAE/D,OAAO,gCAAgC,MADtB,2BAA2B,MAAM,MAAM,cAAc,IAAI,CAC7B,CAAQ;CACvD;AACF;AAEA,SAAS,gCAAgC,MAAsB,UAAoC;CAIjG,OAAO,QAAQ,UAAU,YAAY,CACnC,MACA;EAAE,KAAK;EAAsB;CAAS,CACxC,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"file-source.js","names":[],"sources":["../../src/streams/file-source.ts"],"sourcesContent":["import type { ContentSource } from './source.ts'\n\nconst FILE_STREAM_CHUNK_SIZE = 16 * 1024 * 1024\n\n/** Filesystem path accepted by {@link FileSource}. */\nexport type FileSourcePath = string | URL\n\ninterface FileStatsLike {\n readonly dev: number\n readonly ino: number\n readonly mode: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n isFile(): boolean\n}\n\ninterface FileHandleLike {\n read(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ bytesRead: number }>\n stat(): Promise<FileStatsLike>\n close(): Promise<void>\n}\n\ninterface NodeFsSync {\n lstatSync(path: FileSourcePath): FileStatsLike\n}\n\ninterface FileIdentity {\n readonly dev: number\n readonly ino: number\n readonly size: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n}\n\ninterface ToArrayBufferOptions {\n readonly signal?: AbortSignal\n}\n\nconst FILE_SOURCE_INTERNAL = Symbol('FileSource.internal')\n\ninterface FileSourceInternalOptions {\n readonly key: typeof FILE_SOURCE_INTERNAL\n readonly identity: FileIdentity\n}\n\n/** @internal */\nexport const fileSourceTestHooks: {\n afterReadIteration?: (filled: number) => Promise<void> | void\n maxReadSize?: number\n openFile?: (path: FileSourcePath, flags: number) => Promise<FileHandleLike>\n platform?: string\n} = {}\n\nfunction getNodeFsSync(): NodeFsSync {\n // FileSource exposes `size` synchronously, so the constructor cannot use the\n // repo's usual `await import('node:...')` pattern for Node-only APIs. Keep the\n // synchronous lookup here; async callers should prefer `FileSource.fromPath()`.\n const processLike = (\n globalThis as {\n process?: { getBuiltinModule?: (id: string) => unknown }\n }\n ).process\n const fs = processLike?.getBuiltinModule?.('node:fs')\n if (!isNodeFsSync(fs)) {\n throw new Error(\n 'FileSource constructor requires process.getBuiltinModule(\"node:fs\") with synchronous filesystem APIs; use FileSource.fromPath() when synchronous filesystem access is unavailable.',\n )\n }\n return fs\n}\n\nfunction isNodeFsSync(value: unknown): value is NodeFsSync {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return typeof candidate['lstatSync'] === 'function'\n}\n\nasync function fileOpenFlags(): Promise<number> {\n const { constants } = await import('node:fs')\n // O_NOFOLLOW and O_NONBLOCK are unavailable on some platforms. When\n // O_NOFOLLOW is absent, the post-open fstat identity check below is the\n // symlink-swap defense. O_NONBLOCK avoids blocking indefinitely if a path is\n // swapped to a FIFO or another blocking special file before fstat rejects it.\n return (\n constants.O_RDONLY |\n (constants.O_NOFOLLOW ?? 0) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n )\n}\n\nfunction normalizeSliceOffset(value: number, size: number): number {\n if (!Number.isFinite(value)) throw new RangeError('FileSource slice offsets must be finite.')\n const integer = Math.trunc(value)\n const offset = integer < 0 ? size + integer : integer\n return Math.min(Math.max(offset, 0), size)\n}\n\nfunction formatFilePath(path: FileSourcePath): string {\n return path instanceof URL ? path.href : path\n}\n\nfunction identityFromStats(stats: FileStatsLike): FileIdentity {\n return {\n dev: stats.dev,\n ino: stats.ino,\n size: stats.size,\n mtimeMs: stats.mtimeMs,\n ctimeMs: stats.ctimeMs,\n }\n}\n\nfunction assertStableIdentity(path: FileSourcePath, stats: FileStatsLike): void {\n if (stats.dev === 0 && stats.ino === 0) {\n throw new Error(\n `FileSource: ${formatFilePath(path)} is on a filesystem that does not expose stable file identity.`,\n )\n }\n}\n\nfunction assertRegularFile(path: FileSourcePath, stats: FileStatsLike): void {\n if (!stats.isFile()) {\n throw new Error(`FileSource: ${formatFilePath(path)} is not a regular file.`)\n }\n}\n\nfunction validatedIdentityFromStats(path: FileSourcePath, stats: FileStatsLike): FileIdentity {\n assertRegularFile(path, stats)\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, stats)\n return identityFromStats(stats)\n}\n\nfunction assertSameIdentity(\n path: FileSourcePath,\n expected: FileIdentity,\n actual: FileStatsLike,\n when: string,\n): void {\n if (shouldComparePosixFileIdentity()) assertStableIdentity(path, actual)\n if (\n shouldComparePosixFileIdentity() &&\n (actual.dev !== expected.dev || actual.ino !== expected.ino)\n ) {\n throw new Error(`FileSource: ${formatFilePath(path)} changed ${when}.`)\n }\n if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n if (shouldComparePosixChangeTime() && actual.ctimeMs !== expected.ctimeMs) {\n throw new Error(`FileSource: ${formatFilePath(path)} was modified ${when}.`)\n }\n}\n\nfunction isWindows(): boolean {\n if (fileSourceTestHooks.platform !== undefined) return fileSourceTestHooks.platform === 'win32'\n const processLike = (globalThis as { process?: { platform?: string } }).process\n return processLike?.platform === 'win32'\n}\n\nfunction shouldComparePosixFileIdentity(): boolean {\n return !isWindows()\n}\n\nfunction shouldComparePosixChangeTime(): boolean {\n return !isWindows()\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal === undefined || !signal.aborted) return\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n}\n\nfunction maxReadSize(): number {\n return fileSourceTestHooks.maxReadSize ?? FILE_STREAM_CHUNK_SIZE\n}\n\n/* v8 ignore start -- Requires a file to pass identity checks and still EOF mid-range. */\nfunction rangeEndedEarlyError(path: FileSourcePath, offset: number, size: number): Error {\n const end = offset + size\n return new Error(\n `FileSource: ${formatFilePath(path)} ended before byte range [${offset}, ${end}) was fully read.`,\n )\n}\n/* v8 ignore stop */\n\nasync function openValidatedFile(\n path: FileSourcePath,\n identity: FileIdentity,\n): Promise<FileHandleLike> {\n const open =\n fileSourceTestHooks.openFile ??\n (\n (await import('node:fs/promises')) as {\n open(path: FileSourcePath, flags: number): Promise<FileHandleLike>\n }\n ).open\n let file: FileHandleLike\n try {\n file = await open(path, await fileOpenFlags())\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`FileSource: ${formatFilePath(path)} could not be opened: ${message}`)\n }\n\n try {\n const stats = await file.stat()\n assertRegularFile(path, stats)\n assertSameIdentity(path, identity, stats, 'before read')\n return file\n } catch (err) {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n throw err\n }\n}\n\nasync function lstatNodeFile(path: FileSourcePath): Promise<FileStatsLike> {\n const { lstat } = (await import('node:fs/promises')) as {\n lstat(path: FileSourcePath): Promise<FileStatsLike>\n }\n return lstat(path)\n}\n\nasync function readFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n signal?: AbortSignal,\n): Promise<Uint8Array> {\n throwIfAborted(signal)\n if (size === 0) {\n await verifyFileIdentityForEmptyRead(path, identity, signal)\n return new Uint8Array(0)\n }\n\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n const data = new Uint8Array(size)\n let filled = 0\n try {\n while (filled < data.byteLength) {\n throwIfAborted(signal)\n const length = Math.min(maxReadSize(), data.byteLength - filled)\n const { bytesRead } = await file.read(data, filled, length, offset + filled)\n if (bytesRead === 0) throw rangeEndedEarlyError(path, offset, size)\n filled += bytesRead\n await fileSourceTestHooks.afterReadIteration?.(filled)\n }\n throwIfAborted(signal)\n const stats = await file.stat()\n assertSameIdentity(path, identity, stats, 'while being read')\n return data\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nasync function verifyFileIdentityForEmptyRead(\n path: FileSourcePath,\n identity: FileIdentity,\n signal?: AbortSignal,\n): Promise<void> {\n throwIfAborted(signal)\n const file = await openValidatedFile(path, identity)\n try {\n throwIfAborted(signal)\n return\n } finally {\n /* v8 ignore next -- Cleanup failure is deliberately best-effort. */\n await file.close().catch(() => {})\n }\n}\n\nfunction sliceFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n start: number,\n end: number,\n): ContentSource {\n const normalizedStart = normalizeSliceOffset(start, size)\n const normalizedEnd = normalizeSliceOffset(end, size)\n return new FileSliceSource(\n path,\n identity,\n offset + normalizedStart,\n Math.max(normalizedEnd - normalizedStart, 0),\n )\n}\n\nfunction streamFileRange(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n): ReadableStream<Uint8Array> {\n let position = offset\n let remaining = size\n let verifiedEmpty = false\n const abortController = new AbortController()\n\n // Open per pull so abandoned public streams do not strand file handles or\n // retain Windows locks. Each chunk still verifies identity around its read.\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n if (remaining === 0) {\n if (!verifiedEmpty) {\n verifiedEmpty = true\n await verifyFileIdentityForEmptyRead(path, identity)\n }\n controller.close()\n return\n }\n\n const length = Math.min(FILE_STREAM_CHUNK_SIZE, remaining)\n const data = await readFileRange(path, identity, position, length, abortController.signal)\n position += data.byteLength\n remaining -= data.byteLength\n controller.enqueue(data)\n if (remaining === 0) controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n cancel(reason) {\n abortController.abort(reason)\n },\n })\n}\n\nasync function fileRangeToArrayBuffer(\n path: FileSourcePath,\n identity: FileIdentity,\n offset: number,\n size: number,\n options: ToArrayBufferOptions = {},\n): Promise<ArrayBuffer> {\n const data = await readFileRange(path, identity, offset, size, options.signal)\n return data.buffer as ArrayBuffer\n}\n\nclass FileSliceSource implements ContentSource {\n readonly canSlice = true\n\n constructor(\n private readonly path: FileSourcePath,\n private readonly identity: FileIdentity,\n private readonly offset: number,\n readonly size: number,\n ) {}\n\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, this.offset, this.size, start, end)\n }\n\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, this.offset, this.size)\n }\n\n toArrayBuffer(options: ToArrayBufferOptions = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, this.offset, this.size, options)\n }\n}\n\n/**\n * ContentSource backed by a local filesystem path.\n *\n * `FileSource` is Node-only but safe to import in browser builds: it touches\n * Node filesystem APIs only when constructed or read. The constructor performs\n * synchronous filesystem validation so `size` is immediately available; request\n * handlers, sync loops, and other latency-sensitive code should use\n * {@link FileSource.fromPath}. Both paths capture a best-effort regular file\n * identity and reject a symlink as the final path component; parent directory\n * symlinks are followed by the operating system, so callers that constrain\n * paths under a trusted root should validate those parents separately. Reads\n * reject if the path is replaced, if the filesystem cannot report stable\n * identity on POSIX platforms, or if size/mtime/ctime changes before the\n * configured byte range is read. On Windows, reads avoid unreliable dev/inode\n * identity comparisons and validate size/mtime instead.\n * Slices preserve the captured identity, so multipart uploads can read\n * disjoint ranges without materialising the whole file in memory or following\n * later leaf path swaps.\n */\nexport class FileSource implements ContentSource {\n /** Random-access: file ranges are read by absolute byte offset. */\n readonly canSlice = true\n /** File size captured at construction time. */\n readonly size: number\n private readonly path: FileSourcePath\n private readonly identity: FileIdentity\n\n /**\n * Create a FileSource for a local file path.\n * @param path - Local filesystem path or file URL.\n *\n * @throws If the runtime has no Node-compatible filesystem API.\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n constructor(path: FileSourcePath)\n /**\n * Internal constructor path for async validation.\n * @param path - Local filesystem path or file URL.\n * @param internal - Module-private validated identity payload.\n *\n * @internal\n */\n constructor(path: FileSourcePath, internal?: FileSourceInternalOptions) {\n const resolvedIdentity =\n internal?.key === FILE_SOURCE_INTERNAL\n ? internal.identity\n : validatedIdentityFromStats(path, getNodeFsSync().lstatSync(path))\n this.path = path\n this.identity = resolvedIdentity\n this.size = resolvedIdentity.size\n }\n\n /**\n * Return a new file-backed source covering the specified byte range.\n * @param start - The zero-based byte offset to begin the slice.\n * @param end - The exclusive byte offset where the slice ends.\n *\n * @returns A new ContentSource representing the requested sub-range.\n */\n slice(start: number, end: number): ContentSource {\n return sliceFileRange(this.path, this.identity, 0, this.size, start, end)\n }\n\n /**\n * Open this file as a Web ReadableStream.\n * @returns A ReadableStream that reads the file lazily.\n */\n stream(): ReadableStream<Uint8Array> {\n return streamFileRange(this.path, this.identity, 0, this.size)\n }\n\n /**\n * Read this file into an ArrayBuffer.\n * @param options - Optional abort signal used while reading.\n *\n * @returns A promise resolving with the file bytes.\n */\n toArrayBuffer(options: { readonly signal?: AbortSignal } = {}): Promise<ArrayBuffer> {\n return fileRangeToArrayBuffer(this.path, this.identity, 0, this.size, options)\n }\n\n /**\n * Create a FileSource using asynchronous filesystem validation.\n * @param path - Local filesystem path or file URL.\n *\n * @returns A FileSource bound to the validated file identity.\n *\n * @throws If the path does not reference a regular non-symlink file.\n * @throws If a POSIX filesystem cannot report stable file identity.\n */\n static async fromPath(path: FileSourcePath): Promise<FileSource> {\n const identity = validatedIdentityFromStats(path, await lstatNodeFile(path))\n return constructFileSourceFromIdentity(path, identity)\n }\n}\n\nfunction constructFileSourceFromIdentity(path: FileSourcePath, identity: FileIdentity): FileSource {\n // The public overload is intentionally single-argument; Reflect.construct\n // lets fromPath pass the module-private validated identity without exposing\n // that constructor shape in the public type surface.\n return Reflect.construct(FileSource, [\n path,\n { key: FILE_SOURCE_INTERNAL, identity },\n ]) as FileSource\n}\n"],"mappings":"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"}
|
package/dist/streams/hash.cjs
CHANGED
|
@@ -29,24 +29,20 @@ async function getNodeCreateHash() {
|
|
|
29
29
|
}
|
|
30
30
|
return nodeCreateHash;
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* incremental JavaScript implementation.
|
|
36
|
-
*/
|
|
37
|
-
var IncrementalSha1 = class {
|
|
32
|
+
var IncrementalHash = class {
|
|
33
|
+
algorithm;
|
|
34
|
+
jsHash;
|
|
38
35
|
/** Total bytes fed into the hash so far. */
|
|
39
36
|
totalLength = 0;
|
|
40
37
|
/** Node.js hash instance, or null if using the JavaScript fallback. */
|
|
41
38
|
nodeHash = null;
|
|
42
|
-
/** Streaming JavaScript fallback used when Node crypto is unavailable. */
|
|
43
|
-
jsHash = new JsSha1Hasher();
|
|
44
39
|
/** Resolves once the crypto backend has been loaded. */
|
|
45
40
|
initPromise;
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
constructor(algorithm, jsHash) {
|
|
42
|
+
this.algorithm = algorithm;
|
|
43
|
+
this.jsHash = jsHash;
|
|
48
44
|
this.initPromise = getNodeCreateHash().then((factory) => {
|
|
49
|
-
if (factory) this.nodeHash = factory(
|
|
45
|
+
if (factory) this.nodeHash = factory(this.algorithm);
|
|
50
46
|
});
|
|
51
47
|
}
|
|
52
48
|
/**
|
|
@@ -64,8 +60,8 @@ var IncrementalSha1 = class {
|
|
|
64
60
|
this.totalLength += data.byteLength;
|
|
65
61
|
}
|
|
66
62
|
/**
|
|
67
|
-
* Finalize the hash and return the hex-encoded
|
|
68
|
-
* @returns The lowercase hex-encoded
|
|
63
|
+
* Finalize the hash and return the hex-encoded digest.
|
|
64
|
+
* @returns The lowercase hex-encoded digest of all data fed so far.
|
|
69
65
|
*/
|
|
70
66
|
async digest() {
|
|
71
67
|
await this.initPromise;
|
|
@@ -82,6 +78,28 @@ var IncrementalSha1 = class {
|
|
|
82
78
|
return this.totalLength;
|
|
83
79
|
}
|
|
84
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Incrementally computes SHA-1 hashes over streaming data.
|
|
83
|
+
* Uses Node.js `crypto` when available, falling back to a dependency-free
|
|
84
|
+
* incremental JavaScript implementation.
|
|
85
|
+
*/
|
|
86
|
+
var IncrementalSha1 = class extends IncrementalHash {
|
|
87
|
+
/** Creates a new IncrementalSha1 and lazily initializes the crypto backend. */
|
|
88
|
+
constructor() {
|
|
89
|
+
super("sha1", new JsSha1Hasher());
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Incrementally computes SHA-256 hashes over streaming data.
|
|
94
|
+
* Uses Node.js `crypto` when available, falling back to a dependency-free
|
|
95
|
+
* incremental JavaScript implementation.
|
|
96
|
+
*/
|
|
97
|
+
var IncrementalSha256 = class extends IncrementalHash {
|
|
98
|
+
/** Creates a new IncrementalSha256 and lazily initializes the crypto backend. */
|
|
99
|
+
constructor() {
|
|
100
|
+
super("sha256", new JsSha256Hasher());
|
|
101
|
+
}
|
|
102
|
+
};
|
|
85
103
|
/* v8 ignore start -- JavaScript fallback path, exercised by browser-mode tests */
|
|
86
104
|
var JsSha1Hasher = class {
|
|
87
105
|
h0 = 1732584193;
|
|
@@ -189,6 +207,210 @@ function rotateLeft(value, bits) {
|
|
|
189
207
|
function wordToHex(word) {
|
|
190
208
|
return word.toString(16).padStart(8, "0");
|
|
191
209
|
}
|
|
210
|
+
var SHA256_INITIAL_STATE = [
|
|
211
|
+
1779033703,
|
|
212
|
+
3144134277,
|
|
213
|
+
1013904242,
|
|
214
|
+
2773480762,
|
|
215
|
+
1359893119,
|
|
216
|
+
2600822924,
|
|
217
|
+
528734635,
|
|
218
|
+
1541459225
|
|
219
|
+
];
|
|
220
|
+
var SHA256_K = [
|
|
221
|
+
1116352408,
|
|
222
|
+
1899447441,
|
|
223
|
+
3049323471,
|
|
224
|
+
3921009573,
|
|
225
|
+
961987163,
|
|
226
|
+
1508970993,
|
|
227
|
+
2453635748,
|
|
228
|
+
2870763221,
|
|
229
|
+
3624381080,
|
|
230
|
+
310598401,
|
|
231
|
+
607225278,
|
|
232
|
+
1426881987,
|
|
233
|
+
1925078388,
|
|
234
|
+
2162078206,
|
|
235
|
+
2614888103,
|
|
236
|
+
3248222580,
|
|
237
|
+
3835390401,
|
|
238
|
+
4022224774,
|
|
239
|
+
264347078,
|
|
240
|
+
604807628,
|
|
241
|
+
770255983,
|
|
242
|
+
1249150122,
|
|
243
|
+
1555081692,
|
|
244
|
+
1996064986,
|
|
245
|
+
2554220882,
|
|
246
|
+
2821834349,
|
|
247
|
+
2952996808,
|
|
248
|
+
3210313671,
|
|
249
|
+
3336571891,
|
|
250
|
+
3584528711,
|
|
251
|
+
113926993,
|
|
252
|
+
338241895,
|
|
253
|
+
666307205,
|
|
254
|
+
773529912,
|
|
255
|
+
1294757372,
|
|
256
|
+
1396182291,
|
|
257
|
+
1695183700,
|
|
258
|
+
1986661051,
|
|
259
|
+
2177026350,
|
|
260
|
+
2456956037,
|
|
261
|
+
2730485921,
|
|
262
|
+
2820302411,
|
|
263
|
+
3259730800,
|
|
264
|
+
3345764771,
|
|
265
|
+
3516065817,
|
|
266
|
+
3600352804,
|
|
267
|
+
4094571909,
|
|
268
|
+
275423344,
|
|
269
|
+
430227734,
|
|
270
|
+
506948616,
|
|
271
|
+
659060556,
|
|
272
|
+
883997877,
|
|
273
|
+
958139571,
|
|
274
|
+
1322822218,
|
|
275
|
+
1537002063,
|
|
276
|
+
1747873779,
|
|
277
|
+
1955562222,
|
|
278
|
+
2024104815,
|
|
279
|
+
2227730452,
|
|
280
|
+
2361852424,
|
|
281
|
+
2428436474,
|
|
282
|
+
2756734187,
|
|
283
|
+
3204031479,
|
|
284
|
+
3329325298
|
|
285
|
+
];
|
|
286
|
+
var JsSha256Hasher = class {
|
|
287
|
+
h0 = SHA256_INITIAL_STATE[0];
|
|
288
|
+
h1 = SHA256_INITIAL_STATE[1];
|
|
289
|
+
h2 = SHA256_INITIAL_STATE[2];
|
|
290
|
+
h3 = SHA256_INITIAL_STATE[3];
|
|
291
|
+
h4 = SHA256_INITIAL_STATE[4];
|
|
292
|
+
h5 = SHA256_INITIAL_STATE[5];
|
|
293
|
+
h6 = SHA256_INITIAL_STATE[6];
|
|
294
|
+
h7 = SHA256_INITIAL_STATE[7];
|
|
295
|
+
block = /* @__PURE__ */ new Uint8Array(64);
|
|
296
|
+
blockLength = 0;
|
|
297
|
+
bytesProcessed = 0;
|
|
298
|
+
digested = false;
|
|
299
|
+
words = /* @__PURE__ */ new Uint32Array(64);
|
|
300
|
+
update(data) {
|
|
301
|
+
if (this.digested) throw new Error("SHA-256 digest has already been finalized");
|
|
302
|
+
this.bytesProcessed += data.byteLength;
|
|
303
|
+
let offset = 0;
|
|
304
|
+
if (this.blockLength > 0) {
|
|
305
|
+
const toCopy = Math.min(64 - this.blockLength, data.byteLength);
|
|
306
|
+
this.block.set(data.subarray(0, toCopy), this.blockLength);
|
|
307
|
+
this.blockLength += toCopy;
|
|
308
|
+
offset = toCopy;
|
|
309
|
+
if (this.blockLength === 64) {
|
|
310
|
+
this.processBlock(this.block, 0);
|
|
311
|
+
this.blockLength = 0;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
while (offset + 64 <= data.byteLength) {
|
|
315
|
+
this.processBlock(data, offset);
|
|
316
|
+
offset += 64;
|
|
317
|
+
}
|
|
318
|
+
if (offset < data.byteLength) {
|
|
319
|
+
this.block.set(data.subarray(offset), 0);
|
|
320
|
+
this.blockLength = data.byteLength - offset;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
digest() {
|
|
324
|
+
if (this.digested) throw new Error("SHA-256 digest has already been finalized");
|
|
325
|
+
this.digested = true;
|
|
326
|
+
const bitLengthHigh = Math.floor(this.bytesProcessed / 536870912);
|
|
327
|
+
const bitLengthLow = this.bytesProcessed << 3 >>> 0;
|
|
328
|
+
this.block[this.blockLength] = 128;
|
|
329
|
+
this.blockLength++;
|
|
330
|
+
if (this.blockLength > 56) {
|
|
331
|
+
this.block.fill(0, this.blockLength, 64);
|
|
332
|
+
this.processBlock(this.block, 0);
|
|
333
|
+
this.blockLength = 0;
|
|
334
|
+
}
|
|
335
|
+
this.block.fill(0, this.blockLength, 56);
|
|
336
|
+
writeUint32(this.block, 56, bitLengthHigh);
|
|
337
|
+
writeUint32(this.block, 60, bitLengthLow);
|
|
338
|
+
this.processBlock(this.block, 0);
|
|
339
|
+
return [
|
|
340
|
+
this.h0,
|
|
341
|
+
this.h1,
|
|
342
|
+
this.h2,
|
|
343
|
+
this.h3,
|
|
344
|
+
this.h4,
|
|
345
|
+
this.h5,
|
|
346
|
+
this.h6,
|
|
347
|
+
this.h7
|
|
348
|
+
].map(wordToHex).join("");
|
|
349
|
+
}
|
|
350
|
+
processBlock(block, offset) {
|
|
351
|
+
const words = this.words;
|
|
352
|
+
for (let i = 0; i < 16; i++) {
|
|
353
|
+
const j = offset + i * 4;
|
|
354
|
+
words[i] = (block[j] ?? 0) << 24 | (block[j + 1] ?? 0) << 16 | (block[j + 2] ?? 0) << 8 | (block[j + 3] ?? 0);
|
|
355
|
+
}
|
|
356
|
+
for (let i = 16; i < 64; i++) words[i] = smallSigma1(words[i - 2] ?? 0) + (words[i - 7] ?? 0) + smallSigma0(words[i - 15] ?? 0) + (words[i - 16] ?? 0) >>> 0;
|
|
357
|
+
let a = this.h0;
|
|
358
|
+
let b = this.h1;
|
|
359
|
+
let c = this.h2;
|
|
360
|
+
let d = this.h3;
|
|
361
|
+
let e = this.h4;
|
|
362
|
+
let f = this.h5;
|
|
363
|
+
let g = this.h6;
|
|
364
|
+
let h = this.h7;
|
|
365
|
+
for (let i = 0; i < 64; i++) {
|
|
366
|
+
const t1 = h + bigSigma1(e) + sha256Choose(e, f, g) + (SHA256_K[i] ?? 0) + (words[i] ?? 0) >>> 0;
|
|
367
|
+
const t2 = bigSigma0(a) + sha256Majority(a, b, c) >>> 0;
|
|
368
|
+
h = g;
|
|
369
|
+
g = f;
|
|
370
|
+
f = e;
|
|
371
|
+
e = d + t1 >>> 0;
|
|
372
|
+
d = c;
|
|
373
|
+
c = b;
|
|
374
|
+
b = a;
|
|
375
|
+
a = t1 + t2 >>> 0;
|
|
376
|
+
}
|
|
377
|
+
this.h0 = this.h0 + a >>> 0;
|
|
378
|
+
this.h1 = this.h1 + b >>> 0;
|
|
379
|
+
this.h2 = this.h2 + c >>> 0;
|
|
380
|
+
this.h3 = this.h3 + d >>> 0;
|
|
381
|
+
this.h4 = this.h4 + e >>> 0;
|
|
382
|
+
this.h5 = this.h5 + f >>> 0;
|
|
383
|
+
this.h6 = this.h6 + g >>> 0;
|
|
384
|
+
this.h7 = this.h7 + h >>> 0;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
function writeUint32(block, offset, value) {
|
|
388
|
+
block[offset] = value >>> 24 & 255;
|
|
389
|
+
block[offset + 1] = value >>> 16 & 255;
|
|
390
|
+
block[offset + 2] = value >>> 8 & 255;
|
|
391
|
+
block[offset + 3] = value & 255;
|
|
392
|
+
}
|
|
393
|
+
function rotateRight(value, bits) {
|
|
394
|
+
return (value >>> bits | value << 32 - bits) >>> 0;
|
|
395
|
+
}
|
|
396
|
+
function smallSigma0(value) {
|
|
397
|
+
return rotateRight(value, 7) ^ rotateRight(value, 18) ^ value >>> 3;
|
|
398
|
+
}
|
|
399
|
+
function smallSigma1(value) {
|
|
400
|
+
return rotateRight(value, 17) ^ rotateRight(value, 19) ^ value >>> 10;
|
|
401
|
+
}
|
|
402
|
+
function bigSigma0(value) {
|
|
403
|
+
return rotateRight(value, 2) ^ rotateRight(value, 13) ^ rotateRight(value, 22);
|
|
404
|
+
}
|
|
405
|
+
function bigSigma1(value) {
|
|
406
|
+
return rotateRight(value, 6) ^ rotateRight(value, 11) ^ rotateRight(value, 25);
|
|
407
|
+
}
|
|
408
|
+
function sha256Choose(x, y, z) {
|
|
409
|
+
return x & y ^ ~x & z;
|
|
410
|
+
}
|
|
411
|
+
function sha256Majority(x, y, z) {
|
|
412
|
+
return x & y ^ x & z ^ y & z;
|
|
413
|
+
}
|
|
192
414
|
/* v8 ignore stop */
|
|
193
415
|
/**
|
|
194
416
|
* Compute the SHA-1 hex digest of a complete byte array in one shot.
|
|
@@ -208,8 +430,19 @@ async function sha1Hex(data) {
|
|
|
208
430
|
return require_crypto.hexEncode(new Uint8Array(hashBuffer));
|
|
209
431
|
/* v8 ignore stop */
|
|
210
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Compute the SHA-256 hex digest of a complete byte array in one shot.
|
|
435
|
+
* @param data - The byte array to hash.
|
|
436
|
+
*
|
|
437
|
+
* @returns The lowercase hex-encoded SHA-256 digest of the input.
|
|
438
|
+
*/
|
|
439
|
+
async function sha256Hex(data) {
|
|
440
|
+
return require_crypto.sha256Hex(data);
|
|
441
|
+
}
|
|
211
442
|
//#endregion
|
|
212
443
|
exports.IncrementalSha1 = IncrementalSha1;
|
|
444
|
+
exports.IncrementalSha256 = IncrementalSha256;
|
|
213
445
|
exports.sha1Hex = sha1Hex;
|
|
446
|
+
exports.sha256Hex = sha256Hex;
|
|
214
447
|
|
|
215
448
|
//# sourceMappingURL=hash.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.cjs","names":[],"sources":["../../src/streams/hash.ts"],"sourcesContent":["import { arrayBufferFor } from '../util/bytes.ts'\nimport { hexEncode } from '../util/crypto.ts'\n\n/** Internal wrapper around a Node.js Hash instance. */\ntype NodeHasher = { update(data: Uint8Array): void; digest(encoding: string): string }\n\n/** Factory that creates a NodeHasher for a given algorithm name. */\ntype NodeHashFactory = (algorithm: string) => NodeHasher\n\nlet nodeCreateHash: NodeHashFactory | null | undefined\n\n/**\n * Lazily loads `node:crypto` and caches the factory. Returns null in non-Node runtimes.\n *\n * @returns The cached hash factory, or null if Node crypto is unavailable.\n */\nasync function getNodeCreateHash(): Promise<NodeHashFactory | null> {\n if (nodeCreateHash !== undefined) return nodeCreateHash\n try {\n // biome-ignore lint/suspicious/noTsIgnore: isomorphic import — @ts-ignore is silent when node:crypto resolves (Node) and suppresses the error when it doesn't (Deno/browser); @ts-expect-error can't do both\n // @ts-ignore -- node:crypto may not exist in browser/edge runtimes\n const crypto = await import('node:crypto')\n // Vite's browser shim resolves the import but does not implement\n // `createHash`. Probe explicitly so we fall through to the WebCrypto\n // path instead of returning a broken factory.\n if (typeof crypto.createHash !== 'function') throw new Error('createHash unavailable')\n nodeCreateHash = (algo: string) => {\n const h = crypto.createHash(algo)\n return {\n update(data: Uint8Array) {\n h.update(data)\n },\n digest(encoding: string) {\n return h.digest(encoding as 'hex') as string\n },\n }\n }\n } catch {\n /* v8 ignore next -- non-Node runtime fallback, unreachable in Node coverage. */\n nodeCreateHash = null\n }\n return nodeCreateHash\n}\n\n/**\n * Incrementally computes SHA-1 hashes over streaming data.\n * Uses Node.js `crypto` when available, falling back to a dependency-free\n * incremental JavaScript implementation.\n */\nexport class IncrementalSha1 {\n /** Total bytes fed into the hash so far. */\n private totalLength = 0\n /** Node.js hash instance, or null if using the JavaScript fallback. */\n private nodeHash: NodeHasher | null = null\n /** Streaming JavaScript fallback used when Node crypto is unavailable. */\n private jsHash = new JsSha1Hasher()\n /** Resolves once the crypto backend has been loaded. */\n private initPromise: Promise<void>\n\n /** Creates a new IncrementalSha1 and lazily initializes the crypto backend. */\n constructor() {\n this.initPromise = getNodeCreateHash().then((factory) => {\n if (factory) this.nodeHash = factory('sha1')\n })\n }\n\n /**\n * Feed data into the hash. Async because it lazily initializes the crypto backend.\n * @param data - The bytes to include in the hash computation.\n *\n * @returns A promise that resolves once the data has been consumed.\n */\n async update(data: Uint8Array): Promise<void> {\n await this.initPromise\n if (this.nodeHash) {\n this.nodeHash.update(data)\n } else {\n /* v8 ignore next -- WebCrypto fallback is exercised by browser-mode tests. */\n this.jsHash.update(data)\n }\n this.totalLength += data.byteLength\n }\n\n /**\n * Finalize the hash and return the hex-encoded SHA-1 digest.\n * @returns The lowercase hex-encoded SHA-1 digest of all data fed so far.\n */\n async digest(): Promise<string> {\n await this.initPromise\n if (this.nodeHash) {\n return this.nodeHash.digest('hex')\n }\n\n /* v8 ignore next -- non-Node runtime fallback, exercised by browser-mode tests */\n return this.jsHash.digest()\n }\n\n /**\n * Total number of bytes fed into the hash so far.\n *\n * @returns The cumulative byte count across all update calls.\n */\n get bytesProcessed(): number {\n return this.totalLength\n }\n}\n\n/* v8 ignore start -- JavaScript fallback path, exercised by browser-mode tests */\nclass JsSha1Hasher {\n private h0 = 0x67452301\n private h1 = 0xefcdab89\n private h2 = 0x98badcfe\n private h3 = 0x10325476\n private h4 = 0xc3d2e1f0\n private readonly block = new Uint8Array(64)\n private blockLength = 0\n private bytesProcessed = 0\n private digested = false\n private readonly words = new Uint32Array(80)\n\n update(data: Uint8Array): void {\n if (this.digested) throw new Error('SHA-1 digest has already been finalized')\n this.bytesProcessed += data.byteLength\n\n let offset = 0\n if (this.blockLength > 0) {\n const toCopy = Math.min(64 - this.blockLength, data.byteLength)\n this.block.set(data.subarray(0, toCopy), this.blockLength)\n this.blockLength += toCopy\n offset = toCopy\n if (this.blockLength === 64) {\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n }\n\n while (offset + 64 <= data.byteLength) {\n this.processBlock(data, offset)\n offset += 64\n }\n\n if (offset < data.byteLength) {\n this.block.set(data.subarray(offset), 0)\n this.blockLength = data.byteLength - offset\n }\n }\n\n digest(): string {\n if (this.digested) throw new Error('SHA-1 digest has already been finalized')\n this.digested = true\n\n const bitLengthHigh = Math.floor(this.bytesProcessed / 0x20000000)\n const bitLengthLow = (this.bytesProcessed << 3) >>> 0\n\n this.block[this.blockLength] = 0x80\n this.blockLength++\n\n if (this.blockLength > 56) {\n this.block.fill(0, this.blockLength, 64)\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n\n this.block.fill(0, this.blockLength, 56)\n this.writeUint32(56, bitLengthHigh)\n this.writeUint32(60, bitLengthLow)\n this.processBlock(this.block, 0)\n\n return (\n wordToHex(this.h0) +\n wordToHex(this.h1) +\n wordToHex(this.h2) +\n wordToHex(this.h3) +\n wordToHex(this.h4)\n )\n }\n\n private writeUint32(offset: number, value: number): void {\n this.block[offset] = (value >>> 24) & 0xff\n this.block[offset + 1] = (value >>> 16) & 0xff\n this.block[offset + 2] = (value >>> 8) & 0xff\n this.block[offset + 3] = value & 0xff\n }\n\n private processBlock(block: Uint8Array, offset: number): void {\n const words = this.words\n for (let i = 0; i < 16; i++) {\n const j = offset + i * 4\n words[i] =\n ((block[j] ?? 0) << 24) |\n ((block[j + 1] ?? 0) << 16) |\n ((block[j + 2] ?? 0) << 8) |\n (block[j + 3] ?? 0)\n }\n\n for (let i = 16; i < 80; i++) {\n words[i] = rotateLeft(\n (words[i - 3] ?? 0) ^ (words[i - 8] ?? 0) ^ (words[i - 14] ?? 0) ^ (words[i - 16] ?? 0),\n 1,\n )\n }\n\n let a = this.h0\n let b = this.h1\n let c = this.h2\n let d = this.h3\n let e = this.h4\n\n for (let i = 0; i < 80; i++) {\n let f: number\n let k: number\n if (i < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (i < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (i < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6\n }\n\n const temp = (rotateLeft(a, 5) + f + e + k + (words[i] ?? 0)) >>> 0\n e = d\n d = c\n c = rotateLeft(b, 30)\n b = a\n a = temp\n }\n\n this.h0 = (this.h0 + a) >>> 0\n this.h1 = (this.h1 + b) >>> 0\n this.h2 = (this.h2 + c) >>> 0\n this.h3 = (this.h3 + d) >>> 0\n this.h4 = (this.h4 + e) >>> 0\n }\n}\n\nfunction rotateLeft(value: number, bits: number): number {\n return ((value << bits) | (value >>> (32 - bits))) >>> 0\n}\n\nfunction wordToHex(word: number): string {\n return word.toString(16).padStart(8, '0')\n}\n/* v8 ignore stop */\n\n/**\n * Compute the SHA-1 hex digest of a complete byte array in one shot.\n * @param data - The byte array to hash.\n *\n * @returns The lowercase hex-encoded SHA-1 digest of the input.\n */\nexport async function sha1Hex(data: Uint8Array): Promise<string> {\n const factory = await getNodeCreateHash()\n if (factory) {\n const h = factory('sha1')\n h.update(data)\n return h.digest('hex')\n }\n // Copy subarray and SharedArrayBuffer-backed views so WebCrypto hashes\n // exactly `data`'s visible bytes with a plain ArrayBuffer.\n /* v8 ignore start -- WebCrypto fallback, only reachable when node:crypto is unavailable */\n const hashBuffer = await crypto.subtle.digest('SHA-1', arrayBufferFor(data))\n return hexEncode(new Uint8Array(hashBuffer))\n /* v8 ignore stop */\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hash.cjs","names":[],"sources":["../../src/streams/hash.ts"],"sourcesContent":["import { arrayBufferFor } from '../util/bytes.ts'\nimport { hexEncode, sha256Hex as utilSha256Hex } from '../util/crypto.ts'\n\n/** Internal wrapper around a Node.js Hash instance. */\ntype NodeHasher = { update(data: Uint8Array): void; digest(encoding: string): string }\n\n/** Factory that creates a NodeHasher for a given algorithm name. */\ntype NodeHashFactory = (algorithm: string) => NodeHasher\n\n/** Shared interface for dependency-free streaming hash fallbacks. */\ntype JsHasher = { update(data: Uint8Array): void; digest(): string }\n\nlet nodeCreateHash: NodeHashFactory | null | undefined\n\n/**\n * Lazily loads `node:crypto` and caches the factory. Returns null in non-Node runtimes.\n *\n * @returns The cached hash factory, or null if Node crypto is unavailable.\n */\nasync function getNodeCreateHash(): Promise<NodeHashFactory | null> {\n if (nodeCreateHash !== undefined) return nodeCreateHash\n try {\n // biome-ignore lint/suspicious/noTsIgnore: isomorphic import — @ts-ignore is silent when node:crypto resolves (Node) and suppresses the error when it doesn't (Deno/browser); @ts-expect-error can't do both\n // @ts-ignore -- node:crypto may not exist in browser/edge runtimes\n const crypto = await import('node:crypto')\n // Vite's browser shim resolves the import but does not implement\n // `createHash`. Probe explicitly so we fall through to the WebCrypto\n // path instead of returning a broken factory.\n if (typeof crypto.createHash !== 'function') throw new Error('createHash unavailable')\n nodeCreateHash = (algo: string) => {\n const h = crypto.createHash(algo)\n return {\n update(data: Uint8Array) {\n h.update(data)\n },\n digest(encoding: string) {\n return h.digest(encoding as 'hex') as string\n },\n }\n }\n } catch {\n /* v8 ignore next -- non-Node runtime fallback, unreachable in Node coverage. */\n nodeCreateHash = null\n }\n return nodeCreateHash\n}\n\nclass IncrementalHash {\n /** Total bytes fed into the hash so far. */\n private totalLength = 0\n /** Node.js hash instance, or null if using the JavaScript fallback. */\n private nodeHash: NodeHasher | null = null\n /** Resolves once the crypto backend has been loaded. */\n private initPromise: Promise<void>\n\n constructor(\n private readonly algorithm: 'sha1' | 'sha256',\n private readonly jsHash: JsHasher,\n ) {\n this.initPromise = getNodeCreateHash().then((factory) => {\n if (factory) this.nodeHash = factory(this.algorithm)\n })\n }\n\n /**\n * Feed data into the hash. Async because it lazily initializes the crypto backend.\n * @param data - The bytes to include in the hash computation.\n *\n * @returns A promise that resolves once the data has been consumed.\n */\n async update(data: Uint8Array): Promise<void> {\n await this.initPromise\n if (this.nodeHash) {\n this.nodeHash.update(data)\n } else {\n /* v8 ignore next -- WebCrypto fallback is exercised by browser-mode tests. */\n this.jsHash.update(data)\n }\n this.totalLength += data.byteLength\n }\n\n /**\n * Finalize the hash and return the hex-encoded digest.\n * @returns The lowercase hex-encoded digest of all data fed so far.\n */\n async digest(): Promise<string> {\n await this.initPromise\n if (this.nodeHash) {\n return this.nodeHash.digest('hex')\n }\n\n /* v8 ignore next -- non-Node runtime fallback, exercised by browser-mode tests */\n return this.jsHash.digest()\n }\n\n /**\n * Total number of bytes fed into the hash so far.\n *\n * @returns The cumulative byte count across all update calls.\n */\n get bytesProcessed(): number {\n return this.totalLength\n }\n}\n\n/**\n * Incrementally computes SHA-1 hashes over streaming data.\n * Uses Node.js `crypto` when available, falling back to a dependency-free\n * incremental JavaScript implementation.\n */\nexport class IncrementalSha1 extends IncrementalHash {\n /** Creates a new IncrementalSha1 and lazily initializes the crypto backend. */\n constructor() {\n super('sha1', new JsSha1Hasher())\n }\n}\n\n/**\n * Incrementally computes SHA-256 hashes over streaming data.\n * Uses Node.js `crypto` when available, falling back to a dependency-free\n * incremental JavaScript implementation.\n */\nexport class IncrementalSha256 extends IncrementalHash {\n /** Creates a new IncrementalSha256 and lazily initializes the crypto backend. */\n constructor() {\n super('sha256', new JsSha256Hasher())\n }\n}\n\n/* v8 ignore start -- JavaScript fallback path, exercised by browser-mode tests */\nclass JsSha1Hasher {\n private h0 = 0x67452301\n private h1 = 0xefcdab89\n private h2 = 0x98badcfe\n private h3 = 0x10325476\n private h4 = 0xc3d2e1f0\n private readonly block = new Uint8Array(64)\n private blockLength = 0\n private bytesProcessed = 0\n private digested = false\n private readonly words = new Uint32Array(80)\n\n update(data: Uint8Array): void {\n if (this.digested) throw new Error('SHA-1 digest has already been finalized')\n this.bytesProcessed += data.byteLength\n\n let offset = 0\n if (this.blockLength > 0) {\n const toCopy = Math.min(64 - this.blockLength, data.byteLength)\n this.block.set(data.subarray(0, toCopy), this.blockLength)\n this.blockLength += toCopy\n offset = toCopy\n if (this.blockLength === 64) {\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n }\n\n while (offset + 64 <= data.byteLength) {\n this.processBlock(data, offset)\n offset += 64\n }\n\n if (offset < data.byteLength) {\n this.block.set(data.subarray(offset), 0)\n this.blockLength = data.byteLength - offset\n }\n }\n\n digest(): string {\n if (this.digested) throw new Error('SHA-1 digest has already been finalized')\n this.digested = true\n\n const bitLengthHigh = Math.floor(this.bytesProcessed / 0x20000000)\n const bitLengthLow = (this.bytesProcessed << 3) >>> 0\n\n this.block[this.blockLength] = 0x80\n this.blockLength++\n\n if (this.blockLength > 56) {\n this.block.fill(0, this.blockLength, 64)\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n\n this.block.fill(0, this.blockLength, 56)\n this.writeUint32(56, bitLengthHigh)\n this.writeUint32(60, bitLengthLow)\n this.processBlock(this.block, 0)\n\n return (\n wordToHex(this.h0) +\n wordToHex(this.h1) +\n wordToHex(this.h2) +\n wordToHex(this.h3) +\n wordToHex(this.h4)\n )\n }\n\n private writeUint32(offset: number, value: number): void {\n this.block[offset] = (value >>> 24) & 0xff\n this.block[offset + 1] = (value >>> 16) & 0xff\n this.block[offset + 2] = (value >>> 8) & 0xff\n this.block[offset + 3] = value & 0xff\n }\n\n private processBlock(block: Uint8Array, offset: number): void {\n const words = this.words\n for (let i = 0; i < 16; i++) {\n const j = offset + i * 4\n words[i] =\n ((block[j] ?? 0) << 24) |\n ((block[j + 1] ?? 0) << 16) |\n ((block[j + 2] ?? 0) << 8) |\n (block[j + 3] ?? 0)\n }\n\n for (let i = 16; i < 80; i++) {\n words[i] = rotateLeft(\n (words[i - 3] ?? 0) ^ (words[i - 8] ?? 0) ^ (words[i - 14] ?? 0) ^ (words[i - 16] ?? 0),\n 1,\n )\n }\n\n let a = this.h0\n let b = this.h1\n let c = this.h2\n let d = this.h3\n let e = this.h4\n\n for (let i = 0; i < 80; i++) {\n let f: number\n let k: number\n if (i < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (i < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (i < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6\n }\n\n const temp = (rotateLeft(a, 5) + f + e + k + (words[i] ?? 0)) >>> 0\n e = d\n d = c\n c = rotateLeft(b, 30)\n b = a\n a = temp\n }\n\n this.h0 = (this.h0 + a) >>> 0\n this.h1 = (this.h1 + b) >>> 0\n this.h2 = (this.h2 + c) >>> 0\n this.h3 = (this.h3 + d) >>> 0\n this.h4 = (this.h4 + e) >>> 0\n }\n}\n\nfunction rotateLeft(value: number, bits: number): number {\n return ((value << bits) | (value >>> (32 - bits))) >>> 0\n}\n\nfunction wordToHex(word: number): string {\n return word.toString(16).padStart(8, '0')\n}\n\nconst SHA256_INITIAL_STATE = [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n] as const\n\nconst SHA256_K = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n] as const\n\nclass JsSha256Hasher {\n private h0: number = SHA256_INITIAL_STATE[0]\n private h1: number = SHA256_INITIAL_STATE[1]\n private h2: number = SHA256_INITIAL_STATE[2]\n private h3: number = SHA256_INITIAL_STATE[3]\n private h4: number = SHA256_INITIAL_STATE[4]\n private h5: number = SHA256_INITIAL_STATE[5]\n private h6: number = SHA256_INITIAL_STATE[6]\n private h7: number = SHA256_INITIAL_STATE[7]\n private readonly block = new Uint8Array(64)\n private blockLength = 0\n private bytesProcessed = 0\n private digested = false\n private readonly words = new Uint32Array(64)\n\n update(data: Uint8Array): void {\n if (this.digested) throw new Error('SHA-256 digest has already been finalized')\n this.bytesProcessed += data.byteLength\n\n let offset = 0\n if (this.blockLength > 0) {\n const toCopy = Math.min(64 - this.blockLength, data.byteLength)\n this.block.set(data.subarray(0, toCopy), this.blockLength)\n this.blockLength += toCopy\n offset = toCopy\n if (this.blockLength === 64) {\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n }\n\n while (offset + 64 <= data.byteLength) {\n this.processBlock(data, offset)\n offset += 64\n }\n\n if (offset < data.byteLength) {\n this.block.set(data.subarray(offset), 0)\n this.blockLength = data.byteLength - offset\n }\n }\n\n digest(): string {\n if (this.digested) throw new Error('SHA-256 digest has already been finalized')\n this.digested = true\n\n const bitLengthHigh = Math.floor(this.bytesProcessed / 0x20000000)\n const bitLengthLow = (this.bytesProcessed << 3) >>> 0\n\n this.block[this.blockLength] = 0x80\n this.blockLength++\n\n if (this.blockLength > 56) {\n this.block.fill(0, this.blockLength, 64)\n this.processBlock(this.block, 0)\n this.blockLength = 0\n }\n\n this.block.fill(0, this.blockLength, 56)\n writeUint32(this.block, 56, bitLengthHigh)\n writeUint32(this.block, 60, bitLengthLow)\n this.processBlock(this.block, 0)\n\n return [this.h0, this.h1, this.h2, this.h3, this.h4, this.h5, this.h6, this.h7]\n .map(wordToHex)\n .join('')\n }\n\n private processBlock(block: Uint8Array, offset: number): void {\n const words = this.words\n for (let i = 0; i < 16; i++) {\n const j = offset + i * 4\n words[i] =\n ((block[j] ?? 0) << 24) |\n ((block[j + 1] ?? 0) << 16) |\n ((block[j + 2] ?? 0) << 8) |\n (block[j + 3] ?? 0)\n }\n\n for (let i = 16; i < 64; i++) {\n words[i] =\n (smallSigma1(words[i - 2] ?? 0) +\n (words[i - 7] ?? 0) +\n smallSigma0(words[i - 15] ?? 0) +\n (words[i - 16] ?? 0)) >>>\n 0\n }\n\n let a = this.h0\n let b = this.h1\n let c = this.h2\n let d = this.h3\n let e = this.h4\n let f = this.h5\n let g = this.h6\n let h = this.h7\n\n for (let i = 0; i < 64; i++) {\n const t1 =\n (h + bigSigma1(e) + sha256Choose(e, f, g) + (SHA256_K[i] ?? 0) + (words[i] ?? 0)) >>> 0\n const t2 = (bigSigma0(a) + sha256Majority(a, b, c)) >>> 0\n h = g\n g = f\n f = e\n e = (d + t1) >>> 0\n d = c\n c = b\n b = a\n a = (t1 + t2) >>> 0\n }\n\n this.h0 = (this.h0 + a) >>> 0\n this.h1 = (this.h1 + b) >>> 0\n this.h2 = (this.h2 + c) >>> 0\n this.h3 = (this.h3 + d) >>> 0\n this.h4 = (this.h4 + e) >>> 0\n this.h5 = (this.h5 + f) >>> 0\n this.h6 = (this.h6 + g) >>> 0\n this.h7 = (this.h7 + h) >>> 0\n }\n}\n\nfunction writeUint32(block: Uint8Array, offset: number, value: number): void {\n block[offset] = (value >>> 24) & 0xff\n block[offset + 1] = (value >>> 16) & 0xff\n block[offset + 2] = (value >>> 8) & 0xff\n block[offset + 3] = value & 0xff\n}\n\nfunction rotateRight(value: number, bits: number): number {\n return ((value >>> bits) | (value << (32 - bits))) >>> 0\n}\n\nfunction smallSigma0(value: number): number {\n return rotateRight(value, 7) ^ rotateRight(value, 18) ^ (value >>> 3)\n}\n\nfunction smallSigma1(value: number): number {\n return rotateRight(value, 17) ^ rotateRight(value, 19) ^ (value >>> 10)\n}\n\nfunction bigSigma0(value: number): number {\n return rotateRight(value, 2) ^ rotateRight(value, 13) ^ rotateRight(value, 22)\n}\n\nfunction bigSigma1(value: number): number {\n return rotateRight(value, 6) ^ rotateRight(value, 11) ^ rotateRight(value, 25)\n}\n\nfunction sha256Choose(x: number, y: number, z: number): number {\n return (x & y) ^ (~x & z)\n}\n\nfunction sha256Majority(x: number, y: number, z: number): number {\n return (x & y) ^ (x & z) ^ (y & z)\n}\n/* v8 ignore stop */\n\n/**\n * Compute the SHA-1 hex digest of a complete byte array in one shot.\n * @param data - The byte array to hash.\n *\n * @returns The lowercase hex-encoded SHA-1 digest of the input.\n */\nexport async function sha1Hex(data: Uint8Array): Promise<string> {\n const factory = await getNodeCreateHash()\n if (factory) {\n const h = factory('sha1')\n h.update(data)\n return h.digest('hex')\n }\n // Copy subarray and SharedArrayBuffer-backed views so WebCrypto hashes\n // exactly `data`'s visible bytes with a plain ArrayBuffer.\n /* v8 ignore start -- WebCrypto fallback, only reachable when node:crypto is unavailable */\n const hashBuffer = await crypto.subtle.digest('SHA-1', arrayBufferFor(data))\n return hexEncode(new Uint8Array(hashBuffer))\n /* v8 ignore stop */\n}\n\n/**\n * Compute the SHA-256 hex digest of a complete byte array in one shot.\n * @param data - The byte array to hash.\n *\n * @returns The lowercase hex-encoded SHA-256 digest of the input.\n */\nexport async function sha256Hex(data: Uint8Array): Promise<string> {\n return utilSha256Hex(data)\n}\n"],"mappings":"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"}
|