@aigne/afs-did-space 1.12.0-beta.5
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/LICENSE.md +26 -0
- package/README.md +129 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/afs-metadata-context.cjs +20 -0
- package/dist/afs-metadata-context.d.cts +21 -0
- package/dist/afs-metadata-context.d.cts.map +1 -0
- package/dist/afs-metadata-context.d.mts +21 -0
- package/dist/afs-metadata-context.d.mts.map +1 -0
- package/dist/afs-metadata-context.mjs +18 -0
- package/dist/afs-metadata-context.mjs.map +1 -0
- package/dist/blocklet-hook.cjs +180 -0
- package/dist/blocklet-hook.d.cts +29 -0
- package/dist/blocklet-hook.d.cts.map +1 -0
- package/dist/blocklet-hook.d.mts +29 -0
- package/dist/blocklet-hook.d.mts.map +1 -0
- package/dist/blocklet-hook.mjs +178 -0
- package/dist/blocklet-hook.mjs.map +1 -0
- package/dist/changelog-schema.cjs +603 -0
- package/dist/changelog-schema.d.cts +395 -0
- package/dist/changelog-schema.d.cts.map +1 -0
- package/dist/changelog-schema.d.mts +395 -0
- package/dist/changelog-schema.d.mts.map +1 -0
- package/dist/changelog-schema.mjs +570 -0
- package/dist/changelog-schema.mjs.map +1 -0
- package/dist/changelog-write.cjs +80 -0
- package/dist/changelog-write.d.cts +70 -0
- package/dist/changelog-write.d.cts.map +1 -0
- package/dist/changelog-write.d.mts +70 -0
- package/dist/changelog-write.d.mts.map +1 -0
- package/dist/changelog-write.mjs +77 -0
- package/dist/changelog-write.mjs.map +1 -0
- package/dist/cid-byte-cache.cjs +126 -0
- package/dist/cid-byte-cache.d.cts +46 -0
- package/dist/cid-byte-cache.d.cts.map +1 -0
- package/dist/cid-byte-cache.d.mts +47 -0
- package/dist/cid-byte-cache.d.mts.map +1 -0
- package/dist/cid-byte-cache.mjs +125 -0
- package/dist/cid-byte-cache.mjs.map +1 -0
- package/dist/cid.cjs +68 -0
- package/dist/cid.d.cts +22 -0
- package/dist/cid.d.cts.map +1 -0
- package/dist/cid.d.mts +22 -0
- package/dist/cid.d.mts.map +1 -0
- package/dist/cid.mjs +65 -0
- package/dist/cid.mjs.map +1 -0
- package/dist/client.cjs +221 -0
- package/dist/client.d.cts +72 -0
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.mts +72 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +220 -0
- package/dist/client.mjs.map +1 -0
- package/dist/cloudflare.cjs +510 -0
- package/dist/cloudflare.d.cts +307 -0
- package/dist/cloudflare.d.cts.map +1 -0
- package/dist/cloudflare.d.mts +308 -0
- package/dist/cloudflare.d.mts.map +1 -0
- package/dist/cloudflare.mjs +507 -0
- package/dist/cloudflare.mjs.map +1 -0
- package/dist/ctime-backfill.cjs +272 -0
- package/dist/ctime-backfill.d.cts +169 -0
- package/dist/ctime-backfill.d.cts.map +1 -0
- package/dist/ctime-backfill.d.mts +169 -0
- package/dist/ctime-backfill.d.mts.map +1 -0
- package/dist/ctime-backfill.mjs +267 -0
- package/dist/ctime-backfill.mjs.map +1 -0
- package/dist/d1-gc-queue.cjs +80 -0
- package/dist/d1-gc-queue.d.cts +59 -0
- package/dist/d1-gc-queue.d.cts.map +1 -0
- package/dist/d1-gc-queue.d.mts +59 -0
- package/dist/d1-gc-queue.d.mts.map +1 -0
- package/dist/d1-gc-queue.mjs +81 -0
- package/dist/d1-gc-queue.mjs.map +1 -0
- package/dist/d1-resolve-store.cjs +189 -0
- package/dist/d1-resolve-store.d.cts +93 -0
- package/dist/d1-resolve-store.d.cts.map +1 -0
- package/dist/d1-resolve-store.d.mts +93 -0
- package/dist/d1-resolve-store.d.mts.map +1 -0
- package/dist/d1-resolve-store.mjs +190 -0
- package/dist/d1-resolve-store.mjs.map +1 -0
- package/dist/d1-schema.cjs +361 -0
- package/dist/d1-schema.d.cts +237 -0
- package/dist/d1-schema.d.cts.map +1 -0
- package/dist/d1-schema.d.mts +237 -0
- package/dist/d1-schema.d.mts.map +1 -0
- package/dist/d1-schema.mjs +341 -0
- package/dist/d1-schema.mjs.map +1 -0
- package/dist/d1-share-store.cjs +0 -0
- package/dist/d1-share-store.d.cts +53 -0
- package/dist/d1-share-store.d.cts.map +1 -0
- package/dist/d1-share-store.d.mts +53 -0
- package/dist/d1-share-store.d.mts.map +1 -0
- package/dist/d1-share-store.mjs +0 -0
- package/dist/d1-share-store.mjs.map +1 -0
- package/dist/d1-tree-index.cjs +1098 -0
- package/dist/d1-tree-index.d.cts +203 -0
- package/dist/d1-tree-index.d.cts.map +1 -0
- package/dist/d1-tree-index.d.mts +203 -0
- package/dist/d1-tree-index.d.mts.map +1 -0
- package/dist/d1-tree-index.mjs +1094 -0
- package/dist/d1-tree-index.mjs.map +1 -0
- package/dist/data-backend.cjs +147 -0
- package/dist/data-backend.d.cts +30 -0
- package/dist/data-backend.d.cts.map +1 -0
- package/dist/data-backend.d.mts +30 -0
- package/dist/data-backend.d.mts.map +1 -0
- package/dist/data-backend.mjs +148 -0
- package/dist/data-backend.mjs.map +1 -0
- package/dist/dedup-backfill.cjs +278 -0
- package/dist/dedup-backfill.d.cts +165 -0
- package/dist/dedup-backfill.d.cts.map +1 -0
- package/dist/dedup-backfill.d.mts +165 -0
- package/dist/dedup-backfill.d.mts.map +1 -0
- package/dist/dedup-backfill.mjs +273 -0
- package/dist/dedup-backfill.mjs.map +1 -0
- package/dist/did-space-like.d.cts +2 -0
- package/dist/did-space-like.d.mts +2 -0
- package/dist/did-utils.cjs +44 -0
- package/dist/did-utils.d.cts +25 -0
- package/dist/did-utils.d.cts.map +1 -0
- package/dist/did-utils.d.mts +25 -0
- package/dist/did-utils.d.mts.map +1 -0
- package/dist/did-utils.mjs +43 -0
- package/dist/did-utils.mjs.map +1 -0
- package/dist/dirty-path-queue.cjs +31 -0
- package/dist/dirty-path-queue.d.cts +104 -0
- package/dist/dirty-path-queue.d.cts.map +1 -0
- package/dist/dirty-path-queue.d.mts +104 -0
- package/dist/dirty-path-queue.d.mts.map +1 -0
- package/dist/dirty-path-queue.mjs +31 -0
- package/dist/dirty-path-queue.mjs.map +1 -0
- package/dist/dirty-path-schema.cjs +142 -0
- package/dist/dirty-path-schema.d.cts +90 -0
- package/dist/dirty-path-schema.d.cts.map +1 -0
- package/dist/dirty-path-schema.d.mts +90 -0
- package/dist/dirty-path-schema.d.mts.map +1 -0
- package/dist/dirty-path-schema.mjs +136 -0
- package/dist/dirty-path-schema.mjs.map +1 -0
- package/dist/download-actions.cjs +43 -0
- package/dist/download-actions.mjs +40 -0
- package/dist/download-actions.mjs.map +1 -0
- package/dist/fs-object-store.cjs +153 -0
- package/dist/fs-object-store.d.cts +21 -0
- package/dist/fs-object-store.d.cts.map +1 -0
- package/dist/fs-object-store.d.mts +22 -0
- package/dist/fs-object-store.d.mts.map +1 -0
- package/dist/fs-object-store.mjs +153 -0
- package/dist/fs-object-store.mjs.map +1 -0
- package/dist/gc-queue.d.cts +43 -0
- package/dist/gc-queue.d.cts.map +1 -0
- package/dist/gc-queue.d.mts +43 -0
- package/dist/gc-queue.d.mts.map +1 -0
- package/dist/index.cjs +2712 -0
- package/dist/index.d.cts +504 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +505 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2537 -0
- package/dist/index.mjs.map +1 -0
- package/dist/inline-content-migration.cjs +212 -0
- package/dist/inline-content-migration.d.cts +110 -0
- package/dist/inline-content-migration.d.cts.map +1 -0
- package/dist/inline-content-migration.d.mts +111 -0
- package/dist/inline-content-migration.d.mts.map +1 -0
- package/dist/inline-content-migration.mjs +207 -0
- package/dist/inline-content-migration.mjs.map +1 -0
- package/dist/inline-content.cjs +145 -0
- package/dist/inline-content.d.cts +98 -0
- package/dist/inline-content.d.cts.map +1 -0
- package/dist/inline-content.d.mts +98 -0
- package/dist/inline-content.d.mts.map +1 -0
- package/dist/inline-content.mjs +137 -0
- package/dist/inline-content.mjs.map +1 -0
- package/dist/local-changelog.cjs +241 -0
- package/dist/local-changelog.d.cts +119 -0
- package/dist/local-changelog.d.cts.map +1 -0
- package/dist/local-changelog.d.mts +119 -0
- package/dist/local-changelog.d.mts.map +1 -0
- package/dist/local-changelog.mjs +240 -0
- package/dist/local-changelog.mjs.map +1 -0
- package/dist/local.cjs +435 -0
- package/dist/local.d.cts +254 -0
- package/dist/local.d.cts.map +1 -0
- package/dist/local.d.mts +254 -0
- package/dist/local.d.mts.map +1 -0
- package/dist/local.mjs +427 -0
- package/dist/local.mjs.map +1 -0
- package/dist/memory-gc-queue.cjs +36 -0
- package/dist/memory-gc-queue.d.cts +22 -0
- package/dist/memory-gc-queue.d.cts.map +1 -0
- package/dist/memory-gc-queue.d.mts +22 -0
- package/dist/memory-gc-queue.d.mts.map +1 -0
- package/dist/memory-gc-queue.mjs +36 -0
- package/dist/memory-gc-queue.mjs.map +1 -0
- package/dist/memory-tree-index.cjs +734 -0
- package/dist/memory-tree-index.d.cts +159 -0
- package/dist/memory-tree-index.d.cts.map +1 -0
- package/dist/memory-tree-index.d.mts +159 -0
- package/dist/memory-tree-index.d.mts.map +1 -0
- package/dist/memory-tree-index.mjs +735 -0
- package/dist/memory-tree-index.mjs.map +1 -0
- package/dist/metadata-db.cjs +562 -0
- package/dist/metadata-db.d.cts +102 -0
- package/dist/metadata-db.d.cts.map +1 -0
- package/dist/metadata-db.d.mts +102 -0
- package/dist/metadata-db.d.mts.map +1 -0
- package/dist/metadata-db.mjs +562 -0
- package/dist/metadata-db.mjs.map +1 -0
- package/dist/mime-utils.cjs +2 -0
- package/dist/mime-utils.d.cts +2 -0
- package/dist/mime-utils.d.mts +2 -0
- package/dist/mime-utils.mjs +3 -0
- package/dist/object-key.cjs +41 -0
- package/dist/object-key.d.cts +25 -0
- package/dist/object-key.d.cts.map +1 -0
- package/dist/object-key.d.mts +25 -0
- package/dist/object-key.d.mts.map +1 -0
- package/dist/object-key.mjs +40 -0
- package/dist/object-key.mjs.map +1 -0
- package/dist/packages/session/dist/index.cjs +788 -0
- package/dist/packages/session/dist/index.mjs +782 -0
- package/dist/packages/session/dist/index.mjs.map +1 -0
- package/dist/platform/r2/dist/types.d.cts +49 -0
- package/dist/platform/r2/dist/types.d.cts.map +1 -0
- package/dist/prefix-range.cjs +33 -0
- package/dist/prefix-range.d.cts +29 -0
- package/dist/prefix-range.d.cts.map +1 -0
- package/dist/prefix-range.d.mts +29 -0
- package/dist/prefix-range.d.mts.map +1 -0
- package/dist/prefix-range.mjs +33 -0
- package/dist/prefix-range.mjs.map +1 -0
- package/dist/prefixed-tree-index.cjs +238 -0
- package/dist/prefixed-tree-index.d.cts +85 -0
- package/dist/prefixed-tree-index.d.cts.map +1 -0
- package/dist/prefixed-tree-index.d.mts +85 -0
- package/dist/prefixed-tree-index.d.mts.map +1 -0
- package/dist/prefixed-tree-index.mjs +239 -0
- package/dist/prefixed-tree-index.mjs.map +1 -0
- package/dist/presign-signer.cjs +7 -0
- package/dist/presign-signer.d.cts +99 -0
- package/dist/presign-signer.d.cts.map +1 -0
- package/dist/presign-signer.d.mts +99 -0
- package/dist/presign-signer.d.mts.map +1 -0
- package/dist/presign-signer.mjs +7 -0
- package/dist/presign-signer.mjs.map +1 -0
- package/dist/providers/platform/r2/dist/index.d.mts +3 -0
- package/dist/providers/platform/r2/dist/types.d.mts +49 -0
- package/dist/providers/platform/r2/dist/types.d.mts.map +1 -0
- package/dist/reconcile.cjs +184 -0
- package/dist/reconcile.d.cts +26 -0
- package/dist/reconcile.d.cts.map +1 -0
- package/dist/reconcile.d.mts +26 -0
- package/dist/reconcile.d.mts.map +1 -0
- package/dist/reconcile.mjs +184 -0
- package/dist/reconcile.mjs.map +1 -0
- package/dist/reference-check.cjs +49 -0
- package/dist/reference-check.d.cts +10 -0
- package/dist/reference-check.d.cts.map +1 -0
- package/dist/reference-check.d.mts +10 -0
- package/dist/reference-check.d.mts.map +1 -0
- package/dist/reference-check.mjs +48 -0
- package/dist/reference-check.mjs.map +1 -0
- package/dist/remote-afs.cjs +681 -0
- package/dist/remote-afs.d.cts +236 -0
- package/dist/remote-afs.d.cts.map +1 -0
- package/dist/remote-afs.d.mts +236 -0
- package/dist/remote-afs.d.mts.map +1 -0
- package/dist/remote-afs.mjs +676 -0
- package/dist/remote-afs.mjs.map +1 -0
- package/dist/rename-guard.cjs +18 -0
- package/dist/rename-guard.mjs +18 -0
- package/dist/rename-guard.mjs.map +1 -0
- package/dist/resolve.cjs +260 -0
- package/dist/resolve.d.cts +113 -0
- package/dist/resolve.d.cts.map +1 -0
- package/dist/resolve.d.mts +113 -0
- package/dist/resolve.d.mts.map +1 -0
- package/dist/resolve.mjs +260 -0
- package/dist/resolve.mjs.map +1 -0
- package/dist/rsync-content.cjs +33 -0
- package/dist/rsync-content.mjs +33 -0
- package/dist/rsync-content.mjs.map +1 -0
- package/dist/rsync.cjs +400 -0
- package/dist/rsync.d.cts +157 -0
- package/dist/rsync.d.cts.map +1 -0
- package/dist/rsync.d.mts +157 -0
- package/dist/rsync.d.mts.map +1 -0
- package/dist/rsync.mjs +397 -0
- package/dist/rsync.mjs.map +1 -0
- package/dist/run-gc.cjs +57 -0
- package/dist/run-gc.d.cts +40 -0
- package/dist/run-gc.d.cts.map +1 -0
- package/dist/run-gc.d.mts +41 -0
- package/dist/run-gc.d.mts.map +1 -0
- package/dist/run-gc.mjs +57 -0
- package/dist/run-gc.mjs.map +1 -0
- package/dist/scope-id.cjs +70 -0
- package/dist/scope-id.d.cts +35 -0
- package/dist/scope-id.d.cts.map +1 -0
- package/dist/scope-id.d.mts +35 -0
- package/dist/scope-id.d.mts.map +1 -0
- package/dist/scope-id.mjs +68 -0
- package/dist/scope-id.mjs.map +1 -0
- package/dist/scope-migration-node.cjs +172 -0
- package/dist/scope-migration-node.d.cts +44 -0
- package/dist/scope-migration-node.d.cts.map +1 -0
- package/dist/scope-migration-node.d.mts +44 -0
- package/dist/scope-migration-node.d.mts.map +1 -0
- package/dist/scope-migration-node.mjs +171 -0
- package/dist/scope-migration-node.mjs.map +1 -0
- package/dist/scope-migration.cjs +155 -0
- package/dist/scope-migration.d.cts +65 -0
- package/dist/scope-migration.d.cts.map +1 -0
- package/dist/scope-migration.d.mts +65 -0
- package/dist/scope-migration.d.mts.map +1 -0
- package/dist/scope-migration.mjs +154 -0
- package/dist/scope-migration.mjs.map +1 -0
- package/dist/share-declaration.cjs +225 -0
- package/dist/share-declaration.d.cts +30 -0
- package/dist/share-declaration.d.cts.map +1 -0
- package/dist/share-declaration.d.mts +32 -0
- package/dist/share-declaration.d.mts.map +1 -0
- package/dist/share-declaration.mjs +221 -0
- package/dist/share-declaration.mjs.map +1 -0
- package/dist/share-hook.cjs +63 -0
- package/dist/share-hook.mjs +62 -0
- package/dist/share-hook.mjs.map +1 -0
- package/dist/share-resolve.cjs +176 -0
- package/dist/share-resolve.d.cts +36 -0
- package/dist/share-resolve.d.cts.map +1 -0
- package/dist/share-resolve.d.mts +36 -0
- package/dist/share-resolve.d.mts.map +1 -0
- package/dist/share-resolve.mjs +177 -0
- package/dist/share-resolve.mjs.map +1 -0
- package/dist/share-serve.cjs +145 -0
- package/dist/share-serve.d.cts +30 -0
- package/dist/share-serve.d.cts.map +1 -0
- package/dist/share-serve.d.mts +30 -0
- package/dist/share-serve.d.mts.map +1 -0
- package/dist/share-serve.mjs +144 -0
- package/dist/share-serve.mjs.map +1 -0
- package/dist/share-store.cjs +25 -0
- package/dist/share-store.d.cts +65 -0
- package/dist/share-store.d.cts.map +1 -0
- package/dist/share-store.d.mts +65 -0
- package/dist/share-store.d.mts.map +1 -0
- package/dist/share-store.mjs +25 -0
- package/dist/share-store.mjs.map +1 -0
- package/dist/share-virtual-path.cjs +102 -0
- package/dist/share-virtual-path.mjs +101 -0
- package/dist/share-virtual-path.mjs.map +1 -0
- package/dist/space-resolver.cjs +54 -0
- package/dist/space-resolver.d.cts +34 -0
- package/dist/space-resolver.d.cts.map +1 -0
- package/dist/space-resolver.d.mts +34 -0
- package/dist/space-resolver.d.mts.map +1 -0
- package/dist/space-resolver.mjs +55 -0
- package/dist/space-resolver.mjs.map +1 -0
- package/dist/sqlite-gc-queue.cjs +51 -0
- package/dist/sqlite-gc-queue.d.cts +22 -0
- package/dist/sqlite-gc-queue.d.cts.map +1 -0
- package/dist/sqlite-gc-queue.d.mts +22 -0
- package/dist/sqlite-gc-queue.d.mts.map +1 -0
- package/dist/sqlite-gc-queue.mjs +52 -0
- package/dist/sqlite-gc-queue.mjs.map +1 -0
- package/dist/sqlite-resolve-store.cjs +163 -0
- package/dist/sqlite-resolve-store.d.cts +50 -0
- package/dist/sqlite-resolve-store.d.cts.map +1 -0
- package/dist/sqlite-resolve-store.d.mts +50 -0
- package/dist/sqlite-resolve-store.d.mts.map +1 -0
- package/dist/sqlite-resolve-store.mjs +164 -0
- package/dist/sqlite-resolve-store.mjs.map +1 -0
- package/dist/sqlite-share-store.cjs +157 -0
- package/dist/sqlite-share-store.d.cts +31 -0
- package/dist/sqlite-share-store.d.cts.map +1 -0
- package/dist/sqlite-share-store.d.mts +31 -0
- package/dist/sqlite-share-store.d.mts.map +1 -0
- package/dist/sqlite-share-store.mjs +157 -0
- package/dist/sqlite-share-store.mjs.map +1 -0
- package/dist/sqlite-tree-index.cjs +901 -0
- package/dist/sqlite-tree-index.d.cts +107 -0
- package/dist/sqlite-tree-index.d.cts.map +1 -0
- package/dist/sqlite-tree-index.d.mts +107 -0
- package/dist/sqlite-tree-index.d.mts.map +1 -0
- package/dist/sqlite-tree-index.mjs +902 -0
- package/dist/sqlite-tree-index.mjs.map +1 -0
- package/dist/sync-engine.cjs +1324 -0
- package/dist/sync-engine.d.cts +473 -0
- package/dist/sync-engine.d.cts.map +1 -0
- package/dist/sync-engine.d.mts +473 -0
- package/dist/sync-engine.d.mts.map +1 -0
- package/dist/sync-engine.mjs +1315 -0
- package/dist/sync-engine.mjs.map +1 -0
- package/dist/sync-state.cjs +303 -0
- package/dist/sync-state.d.cts +224 -0
- package/dist/sync-state.d.cts.map +1 -0
- package/dist/sync-state.d.mts +224 -0
- package/dist/sync-state.d.mts.map +1 -0
- package/dist/sync-state.mjs +297 -0
- package/dist/sync-state.mjs.map +1 -0
- package/dist/timestamp.cjs +92 -0
- package/dist/timestamp.mjs +91 -0
- package/dist/timestamp.mjs.map +1 -0
- package/dist/transports/http.cjs +54 -0
- package/dist/transports/http.d.cts +26 -0
- package/dist/transports/http.d.cts.map +1 -0
- package/dist/transports/http.d.mts +26 -0
- package/dist/transports/http.d.mts.map +1 -0
- package/dist/transports/http.mjs +54 -0
- package/dist/transports/http.mjs.map +1 -0
- package/dist/transports/static.cjs +55 -0
- package/dist/transports/static.d.cts +28 -0
- package/dist/transports/static.d.cts.map +1 -0
- package/dist/transports/static.d.mts +28 -0
- package/dist/transports/static.d.mts.map +1 -0
- package/dist/transports/static.mjs +55 -0
- package/dist/transports/static.mjs.map +1 -0
- package/dist/transports/types.cjs +13 -0
- package/dist/transports/types.d.cts +72 -0
- package/dist/transports/types.d.cts.map +1 -0
- package/dist/transports/types.d.mts +72 -0
- package/dist/transports/types.d.mts.map +1 -0
- package/dist/transports/types.mjs +13 -0
- package/dist/transports/types.mjs.map +1 -0
- package/dist/transports/ws.cjs +207 -0
- package/dist/transports/ws.d.cts +47 -0
- package/dist/transports/ws.d.cts.map +1 -0
- package/dist/transports/ws.d.mts +47 -0
- package/dist/transports/ws.d.mts.map +1 -0
- package/dist/transports/ws.mjs +206 -0
- package/dist/transports/ws.mjs.map +1 -0
- package/dist/tree-index.cjs +333 -0
- package/dist/tree-index.d.cts +524 -0
- package/dist/tree-index.d.cts.map +1 -0
- package/dist/tree-index.d.mts +524 -0
- package/dist/tree-index.d.mts.map +1 -0
- package/dist/tree-index.mjs +322 -0
- package/dist/tree-index.mjs.map +1 -0
- package/dist/tree-query.cjs +655 -0
- package/dist/tree-query.d.cts +70 -0
- package/dist/tree-query.d.cts.map +1 -0
- package/dist/tree-query.d.mts +70 -0
- package/dist/tree-query.d.mts.map +1 -0
- package/dist/tree-query.mjs +648 -0
- package/dist/tree-query.mjs.map +1 -0
- package/dist/upload-actions.cjs +97 -0
- package/dist/upload-actions.d.cts +19 -0
- package/dist/upload-actions.d.cts.map +1 -0
- package/dist/upload-actions.d.mts +21 -0
- package/dist/upload-actions.d.mts.map +1 -0
- package/dist/upload-actions.mjs +90 -0
- package/dist/upload-actions.mjs.map +1 -0
- package/dist/upload-session-store.cjs +185 -0
- package/dist/upload-session-store.d.cts +99 -0
- package/dist/upload-session-store.d.cts.map +1 -0
- package/dist/upload-session-store.d.mts +99 -0
- package/dist/upload-session-store.d.mts.map +1 -0
- package/dist/upload-session-store.mjs +184 -0
- package/dist/upload-session-store.mjs.map +1 -0
- package/dist/url-normalize.cjs +61 -0
- package/dist/url-normalize.d.cts +33 -0
- package/dist/url-normalize.d.cts.map +1 -0
- package/dist/url-normalize.d.mts +33 -0
- package/dist/url-normalize.d.mts.map +1 -0
- package/dist/url-normalize.mjs +61 -0
- package/dist/url-normalize.mjs.map +1 -0
- package/dist/versioned-content-backends.cjs +155 -0
- package/dist/versioned-content-backends.d.cts +49 -0
- package/dist/versioned-content-backends.d.cts.map +1 -0
- package/dist/versioned-content-backends.d.mts +49 -0
- package/dist/versioned-content-backends.d.mts.map +1 -0
- package/dist/versioned-content-backends.mjs +154 -0
- package/dist/versioned-content-backends.mjs.map +1 -0
- package/dist/versioned-content-index.cjs +102 -0
- package/dist/versioned-content-index.d.cts +46 -0
- package/dist/versioned-content-index.d.cts.map +1 -0
- package/dist/versioned-content-index.d.mts +46 -0
- package/dist/versioned-content-index.d.mts.map +1 -0
- package/dist/versioned-content-index.mjs +103 -0
- package/dist/versioned-content-index.mjs.map +1 -0
- package/dist/versioned-content-store.cjs +121 -0
- package/dist/versioned-content-store.d.cts +90 -0
- package/dist/versioned-content-store.d.cts.map +1 -0
- package/dist/versioned-content-store.d.mts +90 -0
- package/dist/versioned-content-store.d.mts.map +1 -0
- package/dist/versioned-content-store.mjs +122 -0
- package/dist/versioned-content-store.mjs.map +1 -0
- package/dist/versioned-scope.cjs +39 -0
- package/dist/versioned-scope.d.cts +57 -0
- package/dist/versioned-scope.d.cts.map +1 -0
- package/dist/versioned-scope.d.mts +57 -0
- package/dist/versioned-scope.d.mts.map +1 -0
- package/dist/versioned-scope.mjs +37 -0
- package/dist/versioned-scope.mjs.map +1 -0
- package/migrations/0001_tree_entries.sql +17 -0
- package/migrations/0002_resolve_entries.sql +7 -0
- package/migrations/0003_entries_v2.sql +38 -0
- package/migrations/0004_gc_queue.sql +5 -0
- package/migrations/0006_resolve_enhance.sql +14 -0
- package/migrations/0007_resolve_created_at.sql +10 -0
- package/migrations/0008_changelog.sql +34 -0
- package/migrations/0009_device_sync_state.sql +29 -0
- package/migrations/0010_scope_meta.sql +33 -0
- package/migrations/0011_shares.sql +25 -0
- package/migrations/0012_scope_meta_file_count.sql +41 -0
- package/migrations/0013_scope_meta_total_bytes.sql +47 -0
- package/migrations/0014_rename_tables.sql +62 -0
- package/package.json +102 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../packages/session/dist/index.mjs"],"sourcesContent":["import { execRootQueryAction, makeNsLog, validateManifestJSON } from \"@aigne/afs\";\nimport { joinURL } from \"ufo\";\nimport * as crypto from \"node:crypto\";\nimport { randomBytes } from \"node:crypto\";\n\n//#region src/protocol/binary-sidecar.ts\n/**\n* Binary sidecar — carry raw bytes alongside a JSON envelope.\n*\n* Wire format for a message that contains a Uint8Array value:\n* [Binary frame: reqId=N, payload=raw bytes]\n* [JSON frame: reqId=N, payload=JSON with {__afs_binary_ref:true,size:N} marker]\n*\n* Ordering: the Binary frame MUST immediately precede the JSON frame with\n* the same reqId. WebSocket preserves message order, so receivers can\n* buffer the last-seen Binary by reqId and splice it in when the matching\n* JSON arrives.\n*\n* Only one binary slot per message is supported today — large reads/writes\n* put the bytes at `result.data.content` / `params.payload.content`. If we\n* ever need multi-slot, extend the marker with an index and have the binary\n* frame carry a length-prefixed table.\n*\n* Marker key is intentionally rare (`__afs_binary_ref`) to avoid colliding\n* with user payload keys. `isBinaryMarker` also requires `size: number` so\n* a stray `{__afs_binary_ref: true}` in user data without a numeric size\n* still won't be mistaken for a marker.\n*/\nconst BINARY_MARKER_KEY = \"__afs_binary_ref\";\nfunction isBinaryMarker(v) {\n\tif (typeof v !== \"object\" || v === null) return false;\n\tconst o = v;\n\treturn o[BINARY_MARKER_KEY] === true && typeof o.size === \"number\";\n}\nfunction toUint8(value) {\n\tif (value instanceof Uint8Array) return value;\n\tif (value instanceof ArrayBuffer) return new Uint8Array(value);\n\treturn null;\n}\n/**\n* Walk a value and replace the first node for which `replace` returns a\n* non-null value. Identity-preserving: if nothing changed, returns the\n* original reference — so the no-binary path allocates nothing.\n*/\nfunction walkReplaceFirst(value, replace) {\n\tlet done = false;\n\tconst walk = (v) => {\n\t\tif (done) return v;\n\t\tconst replaced = replace(v);\n\t\tif (replaced !== null) {\n\t\t\tdone = true;\n\t\t\treturn replaced;\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\tlet changed = false;\n\t\t\tlet out = null;\n\t\t\tfor (let i = 0; i < v.length; i++) {\n\t\t\t\tconst nv = walk(v[i]);\n\t\t\t\tif (nv !== v[i]) {\n\t\t\t\t\tif (!changed) {\n\t\t\t\t\t\tout = v.slice();\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tout[i] = nv;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn changed ? out : v;\n\t\t}\n\t\tif (v && typeof v === \"object\") {\n\t\t\tlet changed = false;\n\t\t\tlet out = null;\n\t\t\tfor (const [k, inner] of Object.entries(v)) {\n\t\t\t\tconst nv = walk(inner);\n\t\t\t\tif (nv !== inner) {\n\t\t\t\t\tif (!changed) {\n\t\t\t\t\t\tout = { ...v };\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tout[k] = nv;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn changed ? out : v;\n\t\t}\n\t\treturn v;\n\t};\n\treturn walk(value);\n}\n/**\n* Walk a message object and extract the first Uint8Array / ArrayBuffer\n* value found (depth-first, keys in insertion order). Replaces the value\n* with a `{__afs_binary_ref:true,size:N}` marker. Returns the cleaned\n* message plus the extracted bytes (if any).\n*\n* Pure — does not mutate the input.\n*/\nfunction extractBinary(message) {\n\tlet extracted;\n\tconst next = walkReplaceFirst(message, (v) => {\n\t\tconst bytes = toUint8(v);\n\t\tif (!bytes) return null;\n\t\textracted = bytes;\n\t\treturn {\n\t\t\t[BINARY_MARKER_KEY]: true,\n\t\t\tsize: bytes.byteLength\n\t\t};\n\t});\n\treturn extracted ? {\n\t\tmessage: next,\n\t\tbinary: extracted\n\t} : { message };\n}\n/**\n* Walk a value and replace the first `{__afs_binary_ref:true,size:N}`\n* marker with the given Uint8Array. Returns a new value if a marker was\n* found, or the original reference if not.\n*/\nfunction injectBinary(value, binary) {\n\treturn walkReplaceFirst(value, (v) => isBinaryMarker(v) ? binary : null);\n}\n/**\n* True if the value contains a sidecar marker somewhere in its tree. Used\n* by receivers to decide whether to consume a buffered sidecar Binary\n* frame.\n*/\nfunction hasBinaryMarker(value) {\n\tif (isBinaryMarker(value)) return true;\n\tif (Array.isArray(value)) return value.some(hasBinaryMarker);\n\tif (value && typeof value === \"object\") return Object.values(value).some(hasBinaryMarker);\n\treturn false;\n}\n\n//#endregion\n//#region src/protocol/types.ts\n/**\n* Protocol constants\n*/\nconst HEADER_SIZE = 9;\nconst MAX_PAYLOAD_SIZE = 16 * 1024 * 1024;\nconst CHUNK_SIZE = 64 * 1024;\nconst MAX_JSON_DEPTH = 100;\nconst MAX_JSON_SIZE = MAX_PAYLOAD_SIZE;\nconst MAX_UINT32 = 4294967295;\n/**\n* Frame types\n*/\nlet FrameType = /* @__PURE__ */ function(FrameType) {\n\tFrameType[FrameType[\"JSON\"] = 1] = \"JSON\";\n\tFrameType[FrameType[\"Binary\"] = 2] = \"Binary\";\n\treturn FrameType;\n}({});\n/**\n* Protocol errors\n*/\nvar ProtocolError = class extends Error {\n\tconstructor(code, message) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.name = \"ProtocolError\";\n\t}\n};\nvar PayloadTooLargeError = class extends ProtocolError {\n\tconstructor(size) {\n\t\tsuper(\"payload_too_large\", `Payload size ${size} exceeds maximum ${MAX_PAYLOAD_SIZE}`);\n\t\tthis.name = \"PayloadTooLargeError\";\n\t}\n};\nvar InvalidPayloadError = class extends ProtocolError {\n\tconstructor(message) {\n\t\tsuper(\"invalid_payload\", message);\n\t\tthis.name = \"InvalidPayloadError\";\n\t}\n};\nvar InvalidFrameTypeError = class extends ProtocolError {\n\tconstructor(type) {\n\t\tsuper(\"invalid_frame_type\", `Invalid frame type: 0x${type.toString(16).padStart(2, \"0\")}`);\n\t\tthis.name = \"InvalidFrameTypeError\";\n\t}\n};\nvar InvalidReqIdError = class extends ProtocolError {\n\tconstructor(reqId) {\n\t\tsuper(\"invalid_req_id\", `Invalid request ID: ${reqId} (must be 0-${MAX_UINT32})`);\n\t\tthis.name = \"InvalidReqIdError\";\n\t}\n};\nvar IncompleteFrameError = class extends ProtocolError {\n\tconstructor(message) {\n\t\tsuper(\"incomplete_frame\", message);\n\t\tthis.name = \"IncompleteFrameError\";\n\t}\n};\n\n//#endregion\n//#region src/protocol/frame.ts\n/**\n* Validates frame type.\n*/\nfunction isValidFrameType(type) {\n\treturn type === FrameType.JSON || type === FrameType.Binary;\n}\n/**\n* Encodes a frame with the given type, request ID, and payload.\n*\n* Frame structure (big-endian):\n* - Length (4 bytes): payload length\n* - Type (1 byte): frame type\n* - ReqId (4 bytes): request ID\n* - Payload (variable): actual data\n*\n* @param type Frame type (JSON or Binary)\n* @param reqId Request ID (0 for events, >0 for request/response)\n* @param payload The payload data\n* @returns Encoded frame as Uint8Array\n*/\nfunction encodeFrame(type, reqId, payload) {\n\tif (payload == null) throw new InvalidPayloadError(\"Payload cannot be null or undefined\");\n\tif (payload.length > MAX_PAYLOAD_SIZE) throw new PayloadTooLargeError(payload.length);\n\tif (!isValidFrameType(type)) throw new InvalidFrameTypeError(type);\n\tif (reqId < 0 || reqId > MAX_UINT32 || !Number.isInteger(reqId)) throw new InvalidReqIdError(reqId);\n\tconst frame = new Uint8Array(HEADER_SIZE + payload.length);\n\tconst view = new DataView(frame.buffer, frame.byteOffset);\n\tview.setUint32(0, payload.length);\n\tview.setUint8(4, type);\n\tview.setUint32(5, reqId);\n\tframe.set(payload, HEADER_SIZE);\n\treturn frame;\n}\n/**\n* Decodes a frame from a buffer.\n*\n* @param buffer The buffer containing the frame data\n* @returns Object containing the decoded frame and any remaining bytes\n* @throws IncompleteFrameError if buffer doesn't contain a complete frame\n* @throws InvalidFrameTypeError if frame type is invalid\n* @throws PayloadTooLargeError if payload length exceeds maximum\n*/\nfunction decodeFrame(buffer) {\n\tif (buffer.length < HEADER_SIZE) throw new IncompleteFrameError(`Buffer too small: ${buffer.length} bytes, need at least ${HEADER_SIZE}`);\n\tconst view = new DataView(buffer.buffer, buffer.byteOffset);\n\tconst length = view.getUint32(0);\n\tconst type = view.getUint8(4);\n\tconst reqId = view.getUint32(5);\n\tif (length > MAX_PAYLOAD_SIZE) throw new PayloadTooLargeError(length);\n\tif (!isValidFrameType(type)) throw new InvalidFrameTypeError(type);\n\tconst totalSize = HEADER_SIZE + length;\n\tif (buffer.length < totalSize) throw new IncompleteFrameError(`Incomplete frame: have ${buffer.length} bytes, need ${totalSize}`);\n\tconst payload = buffer.slice(HEADER_SIZE, totalSize);\n\tconst remaining = buffer.slice(totalSize);\n\treturn {\n\t\tframe: {\n\t\t\ttype,\n\t\t\treqId,\n\t\t\tpayload\n\t\t},\n\t\tremaining\n\t};\n}\n\n//#endregion\n//#region src/protocol/frame-decoder.ts\n/**\n* Error thrown when pending buffer exceeds maximum size\n*/\nvar BufferOverflowError = class extends ProtocolError {\n\tconstructor(size, maxSize) {\n\t\tsuper(\"buffer_overflow\", `Pending buffer size ${size} exceeds maximum ${maxSize}`);\n\t\tthis.name = \"BufferOverflowError\";\n\t}\n};\n/**\n* Streaming frame decoder that handles:\n* - Partial frames across multiple pushes (拆包)\n* - Multiple frames in single push (粘包)\n* - Buffer overflow protection (security)\n*/\nvar FrameDecoder = class {\n\tbuffer = new Uint8Array(0);\n\tmaxPendingSize;\n\tconstructor(options) {\n\t\tthis.maxPendingSize = options?.maxPendingSize ?? MAX_PAYLOAD_SIZE + HEADER_SIZE;\n\t}\n\t/**\n\t* Push data into the decoder and return any complete frames.\n\t*\n\t* @param data New data to append to buffer\n\t* @returns Array of complete frames (may be empty)\n\t* @throws InvalidFrameTypeError if frame type is invalid\n\t* @throws PayloadTooLargeError if payload exceeds maximum\n\t* @throws BufferOverflowError if pending buffer exceeds maximum\n\t*/\n\tpush(data) {\n\t\tif (data.length === 0) return [];\n\t\tconst newBuffer = new Uint8Array(this.buffer.length + data.length);\n\t\tnewBuffer.set(this.buffer, 0);\n\t\tnewBuffer.set(data, this.buffer.length);\n\t\tthis.buffer = newBuffer;\n\t\tif (this.buffer.length > this.maxPendingSize) throw new BufferOverflowError(this.buffer.length, this.maxPendingSize);\n\t\tconst frames = [];\n\t\twhile (this.buffer.length >= HEADER_SIZE) {\n\t\t\tconst payloadLength = new DataView(this.buffer.buffer, this.buffer.byteOffset).getUint32(0);\n\t\t\tif (payloadLength > MAX_PAYLOAD_SIZE) throw new PayloadTooLargeError(payloadLength);\n\t\t\tconst totalFrameSize = HEADER_SIZE + payloadLength;\n\t\t\tif (this.buffer.length < totalFrameSize) break;\n\t\t\tconst { frame, remaining } = decodeFrame(this.buffer);\n\t\t\tframes.push(frame);\n\t\t\tthis.buffer = remaining;\n\t\t}\n\t\treturn frames;\n\t}\n\t/**\n\t* Reset the decoder state, discarding any buffered data.\n\t*/\n\treset() {\n\t\tthis.buffer = new Uint8Array(0);\n\t}\n\t/**\n\t* Get the current pending buffer size.\n\t*/\n\tget pendingSize() {\n\t\treturn this.buffer.length;\n\t}\n};\n\n//#endregion\n//#region src/protocol/message.ts\n/**\n* Error for invalid message structure\n*/\nvar InvalidMessageError = class extends ProtocolError {\n\tconstructor(message) {\n\t\tsuper(\"invalid_message\", message);\n\t\tthis.name = \"InvalidMessageError\";\n\t}\n};\n/**\n* Error for JSON parsing failures\n*/\nvar ParseError = class extends ProtocolError {\n\tconstructor(message) {\n\t\tsuper(\"parse_error\", `Parse error: ${message}`);\n\t\tthis.name = \"ParseError\";\n\t}\n};\n/**\n* Error for depth limit exceeded\n*/\nvar DepthLimitExceededError = class extends ProtocolError {\n\tconstructor() {\n\t\tsuper(\"depth_limit_exceeded\", `JSON depth limit exceeded (max ${MAX_JSON_DEPTH})`);\n\t\tthis.name = \"DepthLimitExceededError\";\n\t}\n};\n/**\n* Error for message too large\n*/\nvar MessageTooLargeError = class extends ProtocolError {\n\tconstructor(size) {\n\t\tsuper(\"message_too_large\", `Message too large: ${size} bytes (max ${MAX_JSON_SIZE})`);\n\t\tthis.name = \"MessageTooLargeError\";\n\t}\n};\n/**\n* Dangerous keys that should be filtered for security\n*/\nconst DANGEROUS_KEYS = new Set([\n\t\"__proto__\",\n\t\"constructor\",\n\t\"prototype\"\n]);\n/**\n* Recursively filter dangerous keys from an object\n*/\nfunction filterDangerousKeys(obj, depth = 0) {\n\tif (depth > MAX_JSON_DEPTH) throw new DepthLimitExceededError();\n\tif (obj === null || typeof obj !== \"object\") return obj;\n\tif (Array.isArray(obj)) return obj.map((item) => filterDangerousKeys(item, depth + 1));\n\tconst filtered = {};\n\tfor (const [key, value] of Object.entries(obj)) if (!DANGEROUS_KEYS.has(key)) filtered[key] = filterDangerousKeys(value, depth + 1);\n\treturn filtered;\n}\n/**\n* Calculate depth of a JSON object\n*/\nfunction calculateDepth(obj, depth = 0) {\n\tif (depth > MAX_JSON_DEPTH) return depth;\n\tif (obj === null || typeof obj !== \"object\") return depth;\n\tlet maxDepth = depth;\n\tif (Array.isArray(obj)) for (const item of obj) {\n\t\tmaxDepth = Math.max(maxDepth, calculateDepth(item, depth + 1));\n\t\tif (maxDepth > MAX_JSON_DEPTH) return maxDepth;\n\t}\n\telse for (const value of Object.values(obj)) {\n\t\tmaxDepth = Math.max(maxDepth, calculateDepth(value, depth + 1));\n\t\tif (maxDepth > MAX_JSON_DEPTH) return maxDepth;\n\t}\n\treturn maxDepth;\n}\n/**\n* Serialize a message to JSON bytes.\n*\n* @param message The message to serialize\n* @returns UTF-8 encoded JSON bytes\n* @throws InvalidMessageError if message is invalid\n*/\nfunction serializeMessage(message) {\n\tif (!message || typeof message !== \"object\" || !(\"type\" in message)) throw new InvalidMessageError(\"Invalid message: missing type\");\n\tconst { type } = message;\n\tif (type === \"request\") {\n\t\tconst req = message;\n\t\tif (!req.id) throw new InvalidMessageError(\"Invalid request: missing id\");\n\t\tif (!req.method) throw new InvalidMessageError(\"Invalid request: missing method\");\n\t}\n\tconst clean = {};\n\tfor (const [key, value] of Object.entries(message)) if (value !== void 0) clean[key] = value;\n\tconst json = JSON.stringify(clean);\n\treturn new TextEncoder().encode(json);\n}\n/**\n* Deserialize JSON bytes to a message.\n*\n* @param data UTF-8 encoded JSON bytes\n* @returns Parsed message\n* @throws ParseError if JSON is invalid\n* @throws InvalidMessageError if message structure is invalid\n* @throws DepthLimitExceededError if JSON is too deeply nested\n* @throws MessageTooLargeError if message exceeds size limit\n*/\nfunction deserializeMessage(data) {\n\tif (data.length > MAX_JSON_SIZE) throw new MessageTooLargeError(data.length);\n\tlet parsed;\n\ttry {\n\t\tconst json = new TextDecoder().decode(data);\n\t\tparsed = JSON.parse(json);\n\t} catch (e) {\n\t\tthrow new ParseError(e instanceof Error ? e.message : \"Invalid JSON\");\n\t}\n\tif (!parsed || typeof parsed !== \"object\") throw new InvalidMessageError(\"Invalid message: not an object\");\n\tif (calculateDepth(parsed) > MAX_JSON_DEPTH) throw new DepthLimitExceededError();\n\tconst filtered = filterDangerousKeys(parsed);\n\tif (!(\"type\" in filtered) || typeof filtered.type !== \"string\") throw new InvalidMessageError(\"Invalid message: missing type\");\n\tconst { type } = filtered;\n\tif (type !== \"request\" && type !== \"response\" && type !== \"event\") throw new InvalidMessageError(`Invalid message type: ${type}`);\n\treturn filtered;\n}\n\n//#endregion\n//#region src/protocol/pending-binary.ts\n/**\n* Bounded buffer for sidecar Binary frames awaiting their JSON sibling.\n*\n* A single logical binary payload may arrive as multiple consecutive Binary\n* frames (§6.4 chunking: each chunk ≤ 64 KB). All chunks for a reqId are\n* accumulated and concatenated before being handed to the JSON handler.\n*\n* Three failure modes we defend against:\n* 1. Orphaned Binary frames (peer sent Binary but then the connection died,\n* or the JSON never arrives for some reason) — capped by MAX_PENDING so\n* a malicious/broken peer can't pin unbounded memory on us.\n* 2. Concurrent inflight requests — up to MAX_PENDING distinct reqIds may\n* have pending binary chunks at any time; oldest is evicted if exceeded.\n* 3. A single reqId reused to accumulate unbounded bytes (#821). MAX_PENDING\n* only bounds the number of DISTINCT reqIds, so a peer reusing ONE reqId\n* could grow a single entry without limit → OOM. maxBytesPerReqId caps the\n* accumulated bytes per reqId; a logical binary payload must never exceed\n* MAX_PAYLOAD_SIZE, so once an entry crosses the cap it is evicted whole.\n*/\nconst MAX_PENDING = 16;\nvar PendingBinaryBuffer = class {\n\tmap = /* @__PURE__ */ new Map();\n\tlogger;\n\tmaxBytesPerReqId;\n\tconstructor(logger, options) {\n\t\tthis.logger = logger;\n\t\tthis.maxBytesPerReqId = options?.maxBytesPerReqId ?? MAX_PAYLOAD_SIZE;\n\t}\n\tset(reqId, payload) {\n\t\tconst existing = this.map.get(reqId);\n\t\tif (existing !== void 0) {\n\t\t\tif (existing.bytes + payload.byteLength > this.maxBytesPerReqId) {\n\t\t\t\tthis.map.delete(reqId);\n\t\t\t\tthis.logger?.warn(\"pendingBinary per-reqId byte cap exceeded — evicted entry\", {\n\t\t\t\t\treqId,\n\t\t\t\t\tattempted: existing.bytes + payload.byteLength,\n\t\t\t\t\tlimit: this.maxBytesPerReqId\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\texisting.chunks.push(payload);\n\t\t\texisting.bytes += payload.byteLength;\n\t\t\treturn;\n\t\t}\n\t\tif (payload.byteLength > this.maxBytesPerReqId) {\n\t\t\tthis.logger?.warn(\"pendingBinary per-reqId byte cap exceeded — dropped frame\", {\n\t\t\t\treqId,\n\t\t\t\tattempted: payload.byteLength,\n\t\t\t\tlimit: this.maxBytesPerReqId\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (this.map.size >= MAX_PENDING) {\n\t\t\tconst oldestKey = this.map.keys().next().value;\n\t\t\tif (oldestKey !== void 0) {\n\t\t\t\tthis.map.delete(oldestKey);\n\t\t\t\tthis.logger?.warn(\"pendingBinary buffer full — evicted oldest\", {\n\t\t\t\t\tevicted: oldestKey,\n\t\t\t\t\tlimit: MAX_PENDING\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.map.set(reqId, {\n\t\t\tchunks: [payload],\n\t\t\tbytes: payload.byteLength\n\t\t});\n\t}\n\ttake(reqId) {\n\t\tconst entry = this.map.get(reqId);\n\t\tif (entry === void 0) return void 0;\n\t\tthis.map.delete(reqId);\n\t\tconst { chunks } = entry;\n\t\tif (chunks.length === 0) return new Uint8Array(0);\n\t\tif (chunks.length === 1) return chunks[0];\n\t\tconst result = new Uint8Array(entry.bytes);\n\t\tlet offset = 0;\n\t\tfor (const chunk of chunks) {\n\t\t\tresult.set(chunk, offset);\n\t\t\toffset += chunk.byteLength;\n\t\t}\n\t\treturn result;\n\t}\n\tclear() {\n\t\tthis.map.clear();\n\t}\n\tget size() {\n\t\treturn this.map.size;\n\t}\n};\n\n//#endregion\n//#region src/protocol/rpc.ts\n/**\n* Bidirectional RPC handler.\n*\n* Manages outgoing requests (send + wait for response) and\n* identifies incoming responses for matching.\n*/\nvar RPCHandler = class {\n\tnextId;\n\tidIncrement;\n\ttimeout;\n\tpending = /* @__PURE__ */ new Map();\n\tconstructor(options = {}) {\n\t\tthis.nextId = options.startId ?? 2;\n\t\tthis.idIncrement = options.idIncrement ?? 2;\n\t\tthis.timeout = options.timeout ?? 3e4;\n\t}\n\t/**\n\t* Send a request via transport and wait for the matching response.\n\t*/\n\tasync sendRequest(transport, method, params) {\n\t\tif (!transport.connected) throw new Error(\"Transport not connected\");\n\t\tconst id = String(this.nextId);\n\t\tthis.nextId += this.idIncrement;\n\t\tif (this.nextId > 4294967294) this.nextId = this.idIncrement === 2 ? this.nextId % 2 === 0 ? 2 : 1 : 1;\n\t\tconst request = {\n\t\t\ttype: \"request\",\n\t\t\tid,\n\t\t\tmethod,\n\t\t\tparams\n\t\t};\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\treject(/* @__PURE__ */ new Error(`RPC timeout: ${method} (id=${id})`));\n\t\t\t}, this.timeout);\n\t\t\tthis.pending.set(id, {\n\t\t\t\tresolve: (response) => {\n\t\t\t\t\tif (response.error) {\n\t\t\t\t\t\tconst errObj = response.error;\n\t\t\t\t\t\tconst isStructured = typeof errObj === \"object\" && errObj !== null;\n\t\t\t\t\t\tconst message = isStructured ? errObj.message || JSON.stringify(errObj) : String(errObj);\n\t\t\t\t\t\tconst err = new Error(message);\n\t\t\t\t\t\tif (isStructured) {\n\t\t\t\t\t\t\tconst code = errObj.code;\n\t\t\t\t\t\t\tconst details = errObj.details;\n\t\t\t\t\t\t\tif (typeof code === \"string\") Object.defineProperty(err, \"code\", {\n\t\t\t\t\t\t\t\tvalue: code,\n\t\t\t\t\t\t\t\tenumerable: false,\n\t\t\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\t\t\twritable: false\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (details !== void 0) Object.defineProperty(err, \"details\", {\n\t\t\t\t\t\t\t\tvalue: details,\n\t\t\t\t\t\t\t\tenumerable: false,\n\t\t\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\t\t\twritable: false\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t} else resolve(response.result);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t\ttimer\n\t\t\t});\n\t\t\tconst reqIdNum = Number.parseInt(id, 10);\n\t\t\tconst { message: cleanRequest, binary } = extractBinary(request);\n\t\t\tconst send = async () => {\n\t\t\t\tif (binary) for (let offset = 0; offset < binary.byteLength; offset += CHUNK_SIZE) await transport.send({\n\t\t\t\t\ttype: FrameType.Binary,\n\t\t\t\t\treqId: reqIdNum,\n\t\t\t\t\tpayload: binary.subarray(offset, Math.min(offset + CHUNK_SIZE, binary.byteLength))\n\t\t\t\t});\n\t\t\t\tawait transport.send({\n\t\t\t\t\ttype: FrameType.JSON,\n\t\t\t\t\treqId: reqIdNum,\n\t\t\t\t\tpayload: serializeMessage(cleanRequest)\n\t\t\t\t});\n\t\t\t};\n\t\t\tsend().catch((err) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\treject(err);\n\t\t\t});\n\t\t});\n\t}\n\t/**\n\t* Try to handle a frame as a response to a pending request.\n\t* Returns true if it was a response we were waiting for, false otherwise.\n\t*\n\t* Callers must splice any sidecar Binary frame into the message before\n\t* calling — by the time we see a `Response`, its result is final.\n\t*/\n\thandleResponse(response) {\n\t\tconst pending = this.pending.get(response.id);\n\t\tif (!pending) return false;\n\t\tclearTimeout(pending.timer);\n\t\tthis.pending.delete(response.id);\n\t\tpending.resolve(response);\n\t\treturn true;\n\t}\n\t/**\n\t* Reject all pending requests (e.g., on disconnect).\n\t*/\n\trejectAll(error) {\n\t\tfor (const [_id, pending] of this.pending) {\n\t\t\tclearTimeout(pending.timer);\n\t\t\tpending.reject(error);\n\t\t}\n\t\tthis.pending.clear();\n\t}\n\t/**\n\t* Number of pending requests.\n\t*/\n\tget pendingCount() {\n\t\treturn this.pending.size;\n\t}\n};\n\n//#endregion\n//#region src/session/proxy-module.ts\nvar SessionProxyModule = class {\n\tname;\n\tdescription;\n\taccessMode = \"readwrite\";\n\tconstructor(name, rpc, getTransport, description) {\n\t\tthis.rpc = rpc;\n\t\tthis.getTransport = getTransport;\n\t\tthis.name = name;\n\t\tthis.description = description;\n\t}\n\trequireTransport() {\n\t\tconst t = this.getTransport();\n\t\tif (!t || !t.connected) throw new Error(`Provider ${this.name} is not connected`);\n\t\treturn t;\n\t}\n\tasync list(path, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.list\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync read(path, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.read\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync write(path, payload, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.write\", {\n\t\t\tpath,\n\t\t\tpayload,\n\t\t\toptions\n\t\t});\n\t}\n\tasync delete(path, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.delete\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync stat(path, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.stat\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync search(path, query, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.search\", {\n\t\t\tpath,\n\t\t\tquery,\n\t\t\toptions\n\t\t});\n\t}\n\tasync exec(path, args, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.exec\", {\n\t\t\tpath,\n\t\t\targs,\n\t\t\toptions\n\t\t});\n\t}\n\t/**\n\t* Collection query — zero protocol change (design §3.1c): rewrites to\n\t* `exec(\"/.actions/query\", { path, ...spec })` over the existing exec\n\t* channel, then unwraps the exec envelope back into the bare hook result.\n\t* The far side's exec handler special-cases the root query action.\n\t*/\n\tasync query(path, spec, options) {\n\t\tconst envelope = await this.exec(\"/.actions/query\", {\n\t\t\t...spec,\n\t\t\tpath\n\t\t}, options);\n\t\tif (envelope?.success !== true) throw new Error(envelope?.error?.message ?? `query failed for ${path}`);\n\t\treturn envelope.data;\n\t}\n\tasync explain(path, options) {\n\t\treturn this.rpc.sendRequest(this.requireTransport(), \"afs.explain\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n};\n\n//#endregion\n//#region src/session/types.ts\n/**\n* Protocol version\n*/\nconst PROTOCOL_VERSION = \"1.0\";\n/**\n* Session error\n*/\nvar SessionError = class extends Error {\n\tconstructor(code, message) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.name = \"SessionError\";\n\t}\n};\n/**\n* Default options\n*/\nconst DEFAULT_SESSION_OPTIONS = {\n\tmaxSessions: 100,\n\trequestTimeout: 3e4,\n\tsessionTimeout: 3e4,\n\tidleTimeout: 3e5,\n\theartbeatInterval: 3e4,\n\theartbeatTimeout: 9e4\n};\n\n//#endregion\n//#region src/session/host.ts\nconst log$2 = makeNsLog(\"session\");\n/**\n* Counter for generating unique session IDs\n*/\nlet sessionCounter = 0;\nconst SESSION_NAMESPACE_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,253}[A-Za-z0-9])?$/;\nfunction isValidSessionNamespace(namespace) {\n\treturn SESSION_NAMESPACE_PATTERN.test(namespace) && !namespace.includes(\"..\");\n}\nfunction assertValidSessionNamespace(namespace) {\n\tif (!isValidSessionNamespace(namespace)) throw new Error(\"Namespace must be 1-255 ASCII letters, numbers, dots, underscores, or hyphens; start and end with a letter or number; and not contain '..'\");\n\treturn namespace;\n}\n/**\n* Generate session ID with uniqueness guarantee.\n*\n* The sessionId is the SOLE reconnect credential (handleReconnect only checks\n* that the id exists in the grace window + namespace matches), so the random\n* component MUST be unguessable. We use a CSPRNG (crypto.randomBytes, 128-bit)\n* instead of Math.random() (~31-bit, predictable) — see #831.\n*/\nfunction generateSessionId(namespace) {\n\tsessionCounter++;\n\tconst random = randomBytes(16).toString(\"hex\");\n\treturn `sess_${namespace}_${Date.now()}_${sessionCounter}_${random}`;\n}\n/**\n* Session implementation\n*/\nvar SessionImpl = class {\n\tid;\n\tnamespace;\n\tname;\n\tcreatedAt;\n\tafs;\n\tmanifest;\n\t_state = \"pending\";\n\tconstructor(params) {\n\t\tthis.id = params.id || generateSessionId(params.namespace);\n\t\tthis.namespace = params.namespace;\n\t\tthis.name = params.name;\n\t\tthis.createdAt = /* @__PURE__ */ new Date();\n\t\tthis.afs = params.afs;\n\t\tthis.manifest = params.manifest;\n\t\tthis._state = \"attached\";\n\t}\n\tget state() {\n\t\treturn this._state;\n\t}\n\tsetState(state) {\n\t\tthis._state = state;\n\t}\n};\n/**\n* Session host manages multiple client sessions (Observer side).\n*/\nvar SessionHost = class {\n\toptions;\n\tlogger;\n\tconnections = /* @__PURE__ */ new Map();\n\tsessions = /* @__PURE__ */ new Map();\n\tnamespaces = /* @__PURE__ */ new Map();\n\tsessionToConnection = /* @__PURE__ */ new Map();\n\tdisconnectedSessions = /* @__PURE__ */ new Map();\n\teventHandlers = {\n\t\tsessionCreated: /* @__PURE__ */ new Set(),\n\t\tsessionClosed: /* @__PURE__ */ new Set()\n\t};\n\tconstructor(options = {}) {\n\t\tconst { logger, ...sessionOptions } = options;\n\t\tthis.options = {\n\t\t\t...DEFAULT_SESSION_OPTIONS,\n\t\t\t...sessionOptions\n\t\t};\n\t\tthis.logger = logger ?? {\n\t\t\tdebug: (d) => log$2.debug(d.message, d),\n\t\t\tinfo: (d) => log$2.info(d.message, d),\n\t\t\twarn: (d) => log$2.warn(d.message, d),\n\t\t\terror: (d) => log$2.error(d.message, d)\n\t\t};\n\t}\n\t/**\n\t* Handle a new transport connection.\n\t*/\n\thandleConnection(transport) {\n\t\tconst state = {\n\t\t\ttransport,\n\t\t\tattached: false,\n\t\t\tlastPong: Date.now(),\n\t\t\trpc: new RPCHandler({\n\t\t\t\tstartId: 2,\n\t\t\t\tidIncrement: 2,\n\t\t\t\ttimeout: this.options.requestTimeout\n\t\t\t}),\n\t\t\tpendingBinary: new PendingBinaryBuffer({ warn: (msg, meta) => this.logger.warn({\n\t\t\t\tmessage: msg,\n\t\t\t\t...meta ?? {}\n\t\t\t}) })\n\t\t};\n\t\tthis.connections.set(transport, state);\n\t\ttransport.on(\"frame\", (frame) => {\n\t\t\tthis.handleFrame(state, frame).catch((err) => {\n\t\t\t\tthis.logger.error({\n\t\t\t\t\tmessage: \"frame handling failed\",\n\t\t\t\t\terror: String(err)\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t\ttransport.on(\"close\", () => {\n\t\t\tthis.handleDisconnect(state);\n\t\t});\n\t\ttransport.on(\"error\", (err) => {\n\t\t\tthis.logger.error({\n\t\t\t\tmessage: \"transport error\",\n\t\t\t\terror: String(err)\n\t\t\t});\n\t\t});\n\t}\n\t/**\n\t* Subscribe to session lifecycle events.\n\t*/\n\ton(event, handler) {\n\t\tthis.eventHandlers[event].add(handler);\n\t}\n\t/**\n\t* Unsubscribe from session lifecycle events.\n\t*/\n\toff(event, handler) {\n\t\tthis.eventHandlers[event].delete(handler);\n\t}\n\temitSessionEvent(event, session) {\n\t\tfor (const handler of this.eventHandlers[event]) try {\n\t\t\thandler(session);\n\t\t} catch (err) {\n\t\t\tthis.logger.error({\n\t\t\t\tmessage: `session event handler failed: ${event}`,\n\t\t\t\terror: String(err)\n\t\t\t});\n\t\t}\n\t}\n\t/**\n\t* Handle incoming frame from a connection.\n\t*/\n\tasync handleFrame(state, frame) {\n\t\tif (frame.type === FrameType.Binary) {\n\t\t\tstate.pendingBinary.set(frame.reqId, frame.payload);\n\t\t\treturn;\n\t\t}\n\t\tif (frame.type !== FrameType.JSON) return;\n\t\tconst binary = state.pendingBinary.take(frame.reqId);\n\t\ttry {\n\t\t\tconst message = deserializeMessage(frame.payload);\n\t\t\tif (message.type === \"response\") {\n\t\t\t\tconst response$1 = message;\n\t\t\t\tif (binary && response$1.result !== void 0) response$1.result = injectBinary(response$1.result, binary);\n\t\t\t\tstate.rpc.handleResponse(response$1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (binary && message.type === \"request\") message.params = injectBinary(message.params, binary);\n\t\t\tif (message.type === \"event\") {\n\t\t\t\tif (message.event === \"session.pong\") state.lastPong = Date.now();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== \"request\") return;\n\t\t\tconst request = message;\n\t\t\tlet response;\n\t\t\tif (request.method === \"session.attach\") response = await this.handleAttach(state, request);\n\t\t\telse if (request.method === \"session.detach\") response = await this.handleDetach(state, request);\n\t\t\telse if (request.method.startsWith(\"afs.\")) response = await this.handleAfsRequest(state, request);\n\t\t\telse response = {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Unknown method: ${request.method}`\n\t\t\t\t}\n\t\t\t};\n\t\t\tawait this.sendResponse(state.transport, response);\n\t\t} catch (err) {\n\t\t\tthis.logger.error({\n\t\t\t\tmessage: \"frame processing failed\",\n\t\t\t\terror: String(err)\n\t\t\t});\n\t\t}\n\t}\n\t/**\n\t* Handle session.attach request.\n\t*/\n\tasync handleAttach(state, request) {\n\t\tif (state.attached) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Already attached to a session\"\n\t\t\t}\n\t\t};\n\t\tconst params = request.params;\n\t\tif (!params || !params.version) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Missing required parameter: version\"\n\t\t\t}\n\t\t};\n\t\tif (!params.namespace) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Missing required parameter: namespace\"\n\t\t\t}\n\t\t};\n\t\tif (params.version !== PROTOCOL_VERSION) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"version_mismatch\",\n\t\t\t\tmessage: `Version mismatch: expected ${PROTOCOL_VERSION}, got ${params.version}`\n\t\t\t}\n\t\t};\n\t\tif (!isValidSessionNamespace(params.namespace)) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Invalid namespace\"\n\t\t\t}\n\t\t};\n\t\tif (params.manifest) {\n\t\t\tif (JSON.stringify(params.manifest).length > 1024 * 1024) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: \"Manifest too large (exceeds 1MB)\"\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst manifestResult = validateManifestJSON(params.manifest);\n\t\t\tif (!manifestResult.valid) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Invalid manifest: ${manifestResult.errors?.join(\", \")}`\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst manifestName = params.manifest.name;\n\t\t\tif (manifestName !== params.namespace) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Manifest name '${manifestName}' must match session namespace '${params.namespace}'`\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tif (params.sessionId) return this.handleReconnect(state, request, params);\n\t\tif (this.sessions.size >= this.options.maxSessions) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Maximum sessions reached\"\n\t\t\t}\n\t\t};\n\t\tconst existingSession = this.namespaces.get(params.namespace);\n\t\tif (existingSession) if (params.replace) {\n\t\t\tconst oldConnectionState = this.sessionToConnection.get(existingSession.id);\n\t\t\tif (oldConnectionState) {\n\t\t\t\tawait this.sendEvent(oldConnectionState.transport, {\n\t\t\t\t\ttype: \"event\",\n\t\t\t\t\tevent: \"session.replaced\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tsessionId: existingSession.id,\n\t\t\t\t\t\tnamespace: existingSession.namespace,\n\t\t\t\t\t\treason: \"replaced by new connection\"\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (oldConnectionState.heartbeatTimer) clearInterval(oldConnectionState.heartbeatTimer);\n\t\t\t\toldConnectionState.session = void 0;\n\t\t\t\toldConnectionState.attached = false;\n\t\t\t\tthis.sessionToConnection.delete(existingSession.id);\n\t\t\t}\n\t\t\texistingSession.setState(\"disconnected\");\n\t\t\tthis.sessions.delete(existingSession.id);\n\t\t\tthis.namespaces.delete(params.namespace);\n\t\t\tthis.emitSessionEvent(\"sessionClosed\", existingSession);\n\t\t} else return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"namespace_conflict\",\n\t\t\t\tmessage: `Namespace '${params.namespace}' already exists`\n\t\t\t}\n\t\t};\n\t\tconst afs = new SessionProxyModule(params.namespace, state.rpc, () => state.transport, params.name);\n\t\tconst session = new SessionImpl({\n\t\t\tnamespace: params.namespace,\n\t\t\tname: params.name,\n\t\t\tafs,\n\t\t\tmanifest: params.manifest\n\t\t});\n\t\tthis.sessions.set(session.id, session);\n\t\tthis.namespaces.set(session.namespace, session);\n\t\tthis.sessionToConnection.set(session.id, state);\n\t\tstate.session = session;\n\t\tstate.attached = true;\n\t\tthis.startHeartbeat(state);\n\t\tthis.emitSessionEvent(\"sessionCreated\", session);\n\t\tconst result = {\n\t\t\tsessionId: session.id,\n\t\t\tnamespace: session.namespace\n\t\t};\n\t\treturn {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\tresult\n\t\t};\n\t}\n\t/**\n\t* Handle reconnect with existing session ID.\n\t*/\n\tasync handleReconnect(state, request, params) {\n\t\tconst sessionId = params.sessionId;\n\t\tconst namespace = params.namespace;\n\t\tconst disconnected = this.disconnectedSessions.get(sessionId);\n\t\tif (disconnected) {\n\t\t\tconst session = disconnected.session;\n\t\t\tif (session.namespace !== namespace) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_session\",\n\t\t\t\t\tmessage: \"Session ID does not match namespace\"\n\t\t\t\t}\n\t\t\t};\n\t\t\tclearTimeout(disconnected.timeoutId);\n\t\t\tthis.disconnectedSessions.delete(sessionId);\n\t\t\tsession.setState(\"attached\");\n\t\t\tthis.sessions.set(session.id, session);\n\t\t\tthis.namespaces.set(session.namespace, session);\n\t\t\tthis.sessionToConnection.set(session.id, state);\n\t\t\tstate.session = session;\n\t\t\tstate.attached = true;\n\t\t\tthis.startHeartbeat(state);\n\t\t\tthis.emitSessionEvent(\"sessionCreated\", session);\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\tresult: {\n\t\t\t\t\tsessionId: session.id,\n\t\t\t\t\tnamespace: session.namespace\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tconst activeSession = this.sessions.get(sessionId);\n\t\tif (activeSession) {\n\t\t\tif (activeSession.namespace !== namespace) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_session\",\n\t\t\t\t\tmessage: \"Session ID does not match namespace\"\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_session\",\n\t\t\t\t\tmessage: \"Session is already active\"\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_session\",\n\t\t\t\tmessage: \"Session not found or expired\"\n\t\t\t}\n\t\t};\n\t}\n\t/**\n\t* Start heartbeat for a connection.\n\t*/\n\tstartHeartbeat(state) {\n\t\tif (this.options.heartbeatInterval <= 0) return;\n\t\tstate.lastPong = Date.now();\n\t\tstate.heartbeatTimer = setInterval(() => {\n\t\t\tif (Date.now() - state.lastPong > this.options.heartbeatTimeout) {\n\t\t\t\tthis.handleHeartbeatTimeout(state);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.sendEvent(state.transport, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tevent: \"session.ping\",\n\t\t\t\tdata: { timestamp: Date.now() }\n\t\t\t}).catch((err) => {\n\t\t\t\tthis.logger.error({\n\t\t\t\t\tmessage: \"ping send failed\",\n\t\t\t\t\terror: String(err)\n\t\t\t\t});\n\t\t\t});\n\t\t}, this.options.heartbeatInterval);\n\t}\n\t/**\n\t* Handle heartbeat timeout.\n\t*/\n\thandleHeartbeatTimeout(state) {\n\t\tif (state.heartbeatTimer) {\n\t\t\tclearInterval(state.heartbeatTimer);\n\t\t\tstate.heartbeatTimer = void 0;\n\t\t}\n\t\tthis.handleDisconnect(state);\n\t\tstate.transport.close().catch(() => {});\n\t}\n\t/**\n\t* Handle session.detach request.\n\t*/\n\tasync handleDetach(state, request) {\n\t\tif (!state.attached || !state.session) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_session\",\n\t\t\t\tmessage: \"Not attached to a session\"\n\t\t\t}\n\t\t};\n\t\tconst session = state.session;\n\t\tif (state.heartbeatTimer) {\n\t\t\tclearInterval(state.heartbeatTimer);\n\t\t\tstate.heartbeatTimer = void 0;\n\t\t}\n\t\tsession.setState(\"disconnected\");\n\t\tthis.sessions.delete(session.id);\n\t\tthis.namespaces.delete(session.namespace);\n\t\tthis.sessionToConnection.delete(session.id);\n\t\tstate.session = void 0;\n\t\tstate.attached = false;\n\t\tthis.emitSessionEvent(\"sessionClosed\", session);\n\t\treturn {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\tresult: {}\n\t\t};\n\t}\n\t/**\n\t* Handle AFS request (passthrough to client).\n\t*/\n\tasync handleAfsRequest(state, request) {\n\t\tif (!state.attached || !state.session) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_session\",\n\t\t\t\tmessage: \"Must attach before sending AFS requests\"\n\t\t\t}\n\t\t};\n\t\tconst operation = request.method.replace(\"afs.\", \"\");\n\t\tconst params = request.params;\n\t\tconst afs = state.session.afs;\n\t\ttry {\n\t\t\tconst path = params?.path ?? \"/\";\n\t\t\tconst options = params?.options;\n\t\t\tlet result;\n\t\t\tswitch (operation) {\n\t\t\t\tcase \"list\":\n\t\t\t\t\tresult = await afs.list?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"read\":\n\t\t\t\t\tresult = await afs.read?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"write\":\n\t\t\t\t\tresult = await afs.write?.(path, params?.payload, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\tresult = await afs.delete?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"stat\":\n\t\t\t\t\tresult = await afs.stat?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"search\":\n\t\t\t\t\tresult = await afs.search?.(path, params?.query, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"exec\": {\n\t\t\t\t\tconst execArgs = params?.args ?? {};\n\t\t\t\t\tif (path === \"/.actions/query\") {\n\t\t\t\t\t\tresult = await execRootQueryAction(execArgs, afs.query ? (p, spec, o) => afs.query(p, spec, o) : void 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tresult = await afs.exec?.(path, execArgs, options);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"explain\":\n\t\t\t\t\tresult = await afs.explain?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: return {\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tid: request.id,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\t\tmessage: `Unknown AFS operation: ${operation}`\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\tresult: result ?? null\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"internal_error\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error)\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\t/**\n\t* Handle transport disconnect.\n\t*/\n\thandleDisconnect(state) {\n\t\tstate.rpc.rejectAll(/* @__PURE__ */ new Error(\"Provider disconnected\"));\n\t\tstate.pendingBinary.clear();\n\t\tif (state.heartbeatTimer) {\n\t\t\tclearInterval(state.heartbeatTimer);\n\t\t\tstate.heartbeatTimer = void 0;\n\t\t}\n\t\tif (state.session) {\n\t\t\tconst session = state.session;\n\t\t\tsession.setState(\"disconnected\");\n\t\t\tthis.sessions.delete(session.id);\n\t\t\tthis.namespaces.delete(session.namespace);\n\t\t\tthis.sessionToConnection.delete(session.id);\n\t\t\tthis.emitSessionEvent(\"sessionClosed\", session);\n\t\t\tif (this.options.sessionTimeout > 0) {\n\t\t\t\tconst timeoutId = setTimeout(() => {\n\t\t\t\t\tthis.disconnectedSessions.delete(session.id);\n\t\t\t\t}, this.options.sessionTimeout);\n\t\t\t\tthis.disconnectedSessions.set(session.id, {\n\t\t\t\t\tsession,\n\t\t\t\t\tdisconnectedAt: Date.now(),\n\t\t\t\t\ttimeoutId\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.connections.delete(state.transport);\n\t}\n\t/**\n\t* Send response back to client.\n\t*\n\t* If the result contains a Uint8Array, emit it as a sibling Binary frame\n\t* that arrives just before the JSON frame — receiver splices it in via\n\t* the `{__binary:true}` marker left behind.\n\t*/\n\tasync sendResponse(transport, response) {\n\t\tconst reqId = Number.parseInt(response.id, 10);\n\t\tif (Number.isNaN(reqId)) throw new Error(`Response ID is not a valid integer: ${response.id}`);\n\t\tconst { message: cleanResponse, binary } = extractBinary(response);\n\t\tif (binary) for (let offset = 0; offset < binary.byteLength; offset += CHUNK_SIZE) await transport.send({\n\t\t\ttype: FrameType.Binary,\n\t\t\treqId,\n\t\t\tpayload: binary.subarray(offset, Math.min(offset + CHUNK_SIZE, binary.byteLength))\n\t\t});\n\t\tawait transport.send({\n\t\t\ttype: FrameType.JSON,\n\t\t\treqId,\n\t\t\tpayload: serializeMessage(cleanResponse)\n\t\t});\n\t}\n\t/**\n\t* Send event to client.\n\t*/\n\tasync sendEvent(transport, event) {\n\t\tconst payload = serializeMessage(event);\n\t\tawait transport.send({\n\t\t\ttype: FrameType.JSON,\n\t\t\treqId: 0,\n\t\t\tpayload\n\t\t});\n\t}\n\t/**\n\t* Get all active sessions.\n\t*/\n\tgetSessions() {\n\t\treturn Array.from(this.sessions.values());\n\t}\n\t/**\n\t* Send a create_instance request to a connected provider.\n\t* Returns the instanceId on success.\n\t*/\n\tasync createInstance(namespace, instanceId, config) {\n\t\tconst session = this.namespaces.get(namespace);\n\t\tif (!session) throw new Error(`Namespace not found: ${namespace}`);\n\t\tconst state = this.sessionToConnection.get(session.id);\n\t\tif (!state) throw new Error(`Session not connected: ${namespace}`);\n\t\tawait state.rpc.sendRequest(state.transport, \"provider.create_instance\", {\n\t\t\tinstanceId,\n\t\t\tconfig\n\t\t});\n\t}\n\t/**\n\t* Send a destroy_instance request to a connected provider.\n\t*/\n\tasync destroyInstance(namespace, instanceId) {\n\t\tconst session = this.namespaces.get(namespace);\n\t\tif (!session) throw new Error(`Namespace not found: ${namespace}`);\n\t\tconst state = this.sessionToConnection.get(session.id);\n\t\tif (!state) throw new Error(`Session not connected: ${namespace}`);\n\t\tawait state.rpc.sendRequest(state.transport, \"provider.destroy_instance\", { instanceId });\n\t}\n\t/**\n\t* Close the session host and all sessions.\n\t*/\n\tasync close() {\n\t\tfor (const state of this.connections.values()) if (state.heartbeatTimer) clearInterval(state.heartbeatTimer);\n\t\tfor (const disconnected of this.disconnectedSessions.values()) clearTimeout(disconnected.timeoutId);\n\t\tfor (const state of this.connections.values()) await state.transport.close();\n\t\tthis.connections.clear();\n\t\tthis.sessions.clear();\n\t\tthis.namespaces.clear();\n\t\tthis.sessionToConnection.clear();\n\t\tthis.disconnectedSessions.clear();\n\t}\n};\n\n//#endregion\n//#region src/session/attach-handler.ts\nconst log$1 = makeNsLog(\"session\");\nfunction createSessionMounter(options) {\n\tconst { afs, mountPrefix, sessionHostOptions, onAttached, onClosed } = options;\n\tconst host = new SessionHost(sessionHostOptions);\n\tconst chains = /* @__PURE__ */ new Map();\n\tconst enqueue = (namespace, op) => {\n\t\tconst next = (chains.get(namespace) ?? Promise.resolve()).then(op, op);\n\t\tchains.set(namespace, next);\n\t\tnext.finally(() => {\n\t\t\tif (chains.get(namespace) === next) chains.delete(namespace);\n\t\t});\n\t};\n\thost.on(\"sessionCreated\", (session) => {\n\t\tconst mountPath = joinURL(mountPrefix, session.namespace);\n\t\tif (session.manifest && afs.registryStore) afs.registryStore.register(session.manifest);\n\t\tenqueue(session.namespace, async () => {\n\t\t\ttry {\n\t\t\t\tawait afs.mount(session.afs, mountPath);\n\t\t\t\tonAttached?.(session, mountPath);\n\t\t\t} catch (err) {\n\t\t\t\tlog$1.error(`[session-attach] mount failed for ${mountPath}: ${err instanceof Error ? err.message : err}`);\n\t\t\t}\n\t\t});\n\t});\n\thost.on(\"sessionClosed\", (session) => {\n\t\tconst mountPath = joinURL(mountPrefix, session.namespace);\n\t\tif (afs.registryStore) afs.registryStore.unregister(session.namespace);\n\t\tenqueue(session.namespace, async () => {\n\t\t\tonClosed?.(session, mountPath);\n\t\t\ttry {\n\t\t\t\tawait afs.unmount(mountPath);\n\t\t\t} catch {}\n\t\t});\n\t});\n\treturn {\n\t\thost,\n\t\tattachTransport(transport) {\n\t\t\thost.handleConnection(transport);\n\t\t},\n\t\tasync close() {\n\t\t\tawait host.close();\n\t\t\twhile (chains.size > 0) await Promise.allSettled([...chains.values()]);\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/session/client.ts\n/**\n* SessionClient — provider-side session participant.\n*\n* Connects to a SessionHost, attaches with a namespace + manifest,\n* and handles incoming AFS requests by routing them to provider instances.\n*/\nconst log = makeNsLog(\"session\");\nvar SessionClient = class {\n\ttransport = null;\n\trpc;\n\tsessionId = null;\n\t_namespace = null;\n\t_connected = false;\n\tproviderFactory;\n\tinstances = /* @__PURE__ */ new Map();\n\t/** Bounded buffer of sidecar Binary frames waiting for their JSON sibling. */\n\tpendingBinary = new PendingBinaryBuffer({ warn: (msg, meta) => log.warn(`SessionClient: ${msg}`, meta ?? {}) });\n\tconstructor(providerFactory, options = {}) {\n\t\tthis.options = options;\n\t\tthis.providerFactory = providerFactory;\n\t\tthis.rpc = new RPCHandler({\n\t\t\tstartId: 1,\n\t\t\tidIncrement: 2,\n\t\t\ttimeout: options.requestTimeout ?? 3e4\n\t\t});\n\t}\n\tget connected() {\n\t\treturn this._connected;\n\t}\n\tget state() {\n\t\treturn {\n\t\t\tconnected: this._connected,\n\t\t\tsessionId: this.sessionId ?? void 0,\n\t\t\tnamespace: this._namespace ?? void 0\n\t\t};\n\t}\n\t/**\n\t* Connect to a SessionHost via an existing transport.\n\t*/\n\tasync connect(transport) {\n\t\tthis.transport = transport;\n\t\tthis._connected = true;\n\t\ttransport.on(\"frame\", (frame) => {\n\t\t\tthis.handleFrame(frame);\n\t\t});\n\t\ttransport.on(\"close\", () => {\n\t\t\tthis._connected = false;\n\t\t\tthis.pendingBinary.clear();\n\t\t\tthis.rpc.rejectAll(/* @__PURE__ */ new Error(\"Transport disconnected\"));\n\t\t});\n\t\ttransport.on(\"error\", (error) => {\n\t\t\tthis.rpc.rejectAll(error);\n\t\t});\n\t}\n\t/**\n\t* Pre-register a provider instance without a `provider.create_instance` RPC\n\t* from the host. Useful when the client already holds a ready-to-serve\n\t* `AFSModule` (e.g. an arc CLI reverse-mounting a local provider into a\n\t* remote AFS). The host's afs.* requests will route to this instance via\n\t* the single-instance fallback in `handleAfsOperation` (or by explicit\n\t* `instanceId`).\n\t*/\n\tregisterInstance(instanceId, instance) {\n\t\tthis.instances.set(instanceId, instance);\n\t}\n\t/**\n\t* Attach to the host with a namespace and optional manifest.\n\t*/\n\tasync attach(params) {\n\t\tif (!this.transport || !this._connected) throw new Error(\"Not connected\");\n\t\tconst fullParams = {\n\t\t\t...params,\n\t\t\tversion: PROTOCOL_VERSION,\n\t\t\tsessionId: this.sessionId ?? params.sessionId\n\t\t};\n\t\tconst result = await this.rpc.sendRequest(this.transport, \"session.attach\", fullParams);\n\t\tthis.sessionId = result.sessionId;\n\t\tthis._namespace = result.namespace;\n\t\treturn result;\n\t}\n\t/**\n\t* Detach from the host.\n\t*/\n\tasync detach() {\n\t\tif (!this.transport || !this._connected) return;\n\t\ttry {\n\t\t\tawait this.rpc.sendRequest(this.transport, \"session.detach\", {});\n\t\t} catch {}\n\t\tthis.sessionId = null;\n\t\tthis._namespace = null;\n\t\tthis._connected = false;\n\t\tthis.instances.clear();\n\t}\n\t/**\n\t* Close the transport connection.\n\t*/\n\tasync close() {\n\t\tthis.rpc.rejectAll(/* @__PURE__ */ new Error(\"Client closed\"));\n\t\tthis.instances.clear();\n\t\tif (this.transport) {\n\t\t\tawait this.transport.close();\n\t\t\tthis.transport = null;\n\t\t}\n\t\tthis._connected = false;\n\t\tthis.sessionId = null;\n\t\tthis._namespace = null;\n\t}\n\thandleFrame(frame) {\n\t\tif (frame.type === FrameType.Binary) {\n\t\t\tthis.pendingBinary.set(frame.reqId, frame.payload);\n\t\t\treturn;\n\t\t}\n\t\tconst binary = this.pendingBinary.take(frame.reqId);\n\t\ttry {\n\t\t\tconst msg = deserializeMessage(frame.payload);\n\t\t\tif (msg.type === \"response\") {\n\t\t\t\tconst response = msg;\n\t\t\t\tif (binary && response.result !== void 0) response.result = injectBinary(response.result, binary);\n\t\t\t\tthis.rpc.handleResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (msg.type === \"request\") {\n\t\t\t\tconst req = msg;\n\t\t\t\tif (binary) req.params = injectBinary(req.params, binary);\n\t\t\t\tthis.handleRequest(req).then((response) => {\n\t\t\t\t\tthis.sendResponse(response);\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (msg.type === \"event\") {\n\t\t\t\tif (msg.event === \"session.ping\") this.sendEvent(\"session.pong\", {});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlog.error(\"SessionClient frame processing error:\", error);\n\t\t}\n\t}\n\tasync handleRequest(request) {\n\t\tconst { method } = request;\n\t\ttry {\n\t\t\tif (method === \"provider.create_instance\") return await this.handleCreateInstance(request);\n\t\t\tif (method === \"provider.destroy_instance\") return await this.handleDestroyInstance(request);\n\t\t\tif (method.startsWith(\"afs.\")) return await this.handleAfsOperation(request);\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Unknown method: ${method}`\n\t\t\t\t}\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"internal_error\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error)\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\tasync handleCreateInstance(request) {\n\t\tconst params = request.params;\n\t\tif (!params?.instanceId || !params?.config) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Missing instanceId or config\"\n\t\t\t}\n\t\t};\n\t\ttry {\n\t\t\tconst instance = await this.providerFactory(params.config);\n\t\t\tthis.instances.set(params.instanceId, instance);\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\tresult: {\n\t\t\t\t\tinstanceId: params.instanceId,\n\t\t\t\t\tstatus: \"ok\"\n\t\t\t\t}\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Failed to create instance: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\tasync handleDestroyInstance(request) {\n\t\tconst params = request.params;\n\t\tif (!params?.instanceId) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Missing instanceId\"\n\t\t\t}\n\t\t};\n\t\tthis.instances.delete(params.instanceId);\n\t\treturn {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\tresult: { status: \"ok\" }\n\t\t};\n\t}\n\tasync handleAfsOperation(request) {\n\t\tconst operation = request.method.replace(\"afs.\", \"\");\n\t\tconst params = request.params;\n\t\tlet instance;\n\t\tif (params?.instanceId) {\n\t\t\tinstance = this.instances.get(params.instanceId);\n\t\t\tif (!instance) return {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\tmessage: `Instance not found: ${params.instanceId}`\n\t\t\t\t}\n\t\t\t};\n\t\t} else if (this.instances.size === 1) instance = this.instances.values().next().value;\n\t\telse if (this.instances.size === 0) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"No provider instances created\"\n\t\t\t}\n\t\t};\n\t\telse return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"instanceId required when multiple instances exist\"\n\t\t\t}\n\t\t};\n\t\tif (!instance) return {\n\t\t\ttype: \"response\",\n\t\t\tid: request.id,\n\t\t\terror: {\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"No provider instance available\"\n\t\t\t}\n\t\t};\n\t\tconst path = params?.path ?? \"/\";\n\t\tconst options = params?.options;\n\t\ttry {\n\t\t\tlet result;\n\t\t\tswitch (operation) {\n\t\t\t\tcase \"list\":\n\t\t\t\t\tresult = await instance.list?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"read\":\n\t\t\t\t\tresult = await instance.read?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"write\":\n\t\t\t\t\tresult = await instance.write?.(path, params?.payload, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\tresult = await instance.delete?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"stat\":\n\t\t\t\t\tresult = await instance.stat?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"search\":\n\t\t\t\t\tresult = await instance.search?.(path, params?.query, options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"exec\": {\n\t\t\t\t\tconst execArgs = params?.args ?? {};\n\t\t\t\t\tif (path === \"/.actions/query\") {\n\t\t\t\t\t\tresult = await execRootQueryAction(execArgs, instance.query ? (p, spec, o) => instance.query(p, spec, o) : void 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tresult = await instance.exec?.(path, execArgs, options);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"explain\":\n\t\t\t\t\tresult = await instance.explain?.(path, options);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: return {\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tid: request.id,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"invalid_request\",\n\t\t\t\t\t\tmessage: `Unknown AFS operation: ${operation}`\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\tresult: result ?? null\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\ttype: \"response\",\n\t\t\t\tid: request.id,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"internal_error\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error)\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\tsendResponse(response) {\n\t\tif (!this.transport || !this._connected) return;\n\t\tconst reqId = Number.parseInt(response.id, 10);\n\t\tconst { message: cleanResponse, binary } = extractBinary(response);\n\t\tconst transport = this.transport;\n\t\tconst send = async () => {\n\t\t\tif (binary) for (let offset = 0; offset < binary.byteLength; offset += CHUNK_SIZE) await transport.send({\n\t\t\t\ttype: FrameType.Binary,\n\t\t\t\treqId,\n\t\t\t\tpayload: binary.subarray(offset, Math.min(offset + CHUNK_SIZE, binary.byteLength))\n\t\t\t});\n\t\t\tawait transport.send({\n\t\t\t\ttype: FrameType.JSON,\n\t\t\t\treqId,\n\t\t\t\tpayload: serializeMessage(cleanResponse)\n\t\t\t});\n\t\t};\n\t\tsend().catch(() => {});\n\t}\n\tsendEvent(event, data) {\n\t\tif (!this.transport || !this._connected) return;\n\t\tconst payload = serializeMessage({\n\t\t\ttype: \"event\",\n\t\t\tevent,\n\t\t\tdata\n\t\t});\n\t\tthis.transport.send({\n\t\t\ttype: FrameType.JSON,\n\t\t\treqId: 0,\n\t\t\tpayload\n\t\t}).catch(() => {});\n\t}\n};\n\n//#endregion\n//#region src/transport/websocket.ts\n/**\n* Error for WebSocket operations\n*/\nvar WebSocketError = class extends Error {\n\tconstructor(code, message) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.name = \"WebSocketError\";\n\t}\n};\n/**\n* Encode an auth token for safe transport inside a\n* `Sec-WebSocket-Protocol` subprotocol name. Per RFC 6455, subprotocol\n* names must be RFC 2616 tokens — alphanumerics and a small set of\n* punctuation. Hex encoding guarantees the value survives the wire\n* unchanged regardless of the original token's characters.\n*/\nfunction encodeBearerToken(raw) {\n\treturn Buffer.from(raw, \"utf8\").toString(\"hex\");\n}\n/**\n* Decode a hex-encoded bearer token from a subprotocol. Throws if the\n* input is not valid hex so callers can fall through to other auth\n* mechanisms rather than treat a malformed value as an empty token.\n*/\nfunction decodeBearerToken(encoded) {\n\tif (encoded === \"\" || !/^[0-9a-fA-F]+$/.test(encoded) || encoded.length % 2 !== 0) throw new Error(\"invalid bearer encoding\");\n\treturn Buffer.from(encoded, \"hex\").toString(\"utf8\");\n}\n/**\n* Constant-time string comparison to prevent timing attacks\n*/\nfunction secureCompare(a, b) {\n\tif (a.length !== b.length) {\n\t\tcrypto.timingSafeEqual(Buffer.from(a), Buffer.from(a));\n\t\treturn false;\n\t}\n\treturn crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));\n}\n/**\n* Rate limiter for auth attempts\n*/\nvar AuthRateLimiter = class {\n\tattempts = /* @__PURE__ */ new Map();\n\tmaxAttempts = 5;\n\twindowMs = 6e4;\n\tisRateLimited(ip) {\n\t\tconst now = Date.now();\n\t\tconst record = this.attempts.get(ip);\n\t\tif (!record || record.resetAt < now) return false;\n\t\treturn record.count >= this.maxAttempts;\n\t}\n\trecordAttempt(ip) {\n\t\tconst now = Date.now();\n\t\tconst record = this.attempts.get(ip);\n\t\tif (!record || record.resetAt < now) this.attempts.set(ip, {\n\t\t\tcount: 1,\n\t\t\tresetAt: now + this.windowMs\n\t\t});\n\t\telse record.count++;\n\t}\n\trecordSuccess(ip) {\n\t\tthis.attempts.delete(ip);\n\t}\n};\n/**\n* WebSocket transport for server-side (Bun WebSocket).\n* Wraps Bun's ServerWebSocket to provide Transport interface.\n*/\nvar WebSocketTransport = class {\n\tws;\n\tdecoder = new FrameDecoder();\n\thandlers = /* @__PURE__ */ new Map();\n\t_connected = true;\n\tconstructor(ws) {\n\t\tthis.ws = ws;\n\t}\n\t/**\n\t* Called by server when message is received.\n\t*/\n\thandleMessage(data) {\n\t\ttry {\n\t\t\tconst bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;\n\t\t\tconst frames = this.decoder.push(bytes);\n\t\t\tfor (const frame of frames) this.emit(\"frame\", frame);\n\t\t} catch (error) {\n\t\t\tthis.emit(\"error\", error);\n\t\t}\n\t}\n\t/**\n\t* Called by server when connection closes.\n\t*/\n\thandleClose() {\n\t\tthis._connected = false;\n\t\tthis.emit(\"close\");\n\t}\n\t/**\n\t* Called by server when error occurs.\n\t*/\n\thandleError(error) {\n\t\tthis.emit(\"error\", error);\n\t}\n\tasync send(frame) {\n\t\tif (!this._connected) throw new WebSocketError(\"not_connected\", \"Transport is not connected\");\n\t\tconst encoded = encodeFrame(frame.type, frame.reqId, frame.payload);\n\t\tthis.ws.sendBinary(encoded);\n\t}\n\tasync close() {\n\t\tif (this._connected) {\n\t\t\tthis.ws.close();\n\t\t\tthis._connected = false;\n\t\t}\n\t}\n\ton(event, handler) {\n\t\tif (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());\n\t\tthis.handlers.get(event).add(handler);\n\t}\n\toff(event, handler) {\n\t\tthis.handlers.get(event)?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tconst handlers = this.handlers.get(event);\n\t\tif (handlers) for (const handler of handlers) handler(...args);\n\t}\n\tget connected() {\n\t\treturn this._connected;\n\t}\n};\n/**\n* WebSocket server implementation using Bun.serve.\n*/\nvar WebSocketServer = class {\n\tserver = null;\n\thost;\n\tport;\n\tauthToken;\n\tmaxConnections;\n\tconnections = /* @__PURE__ */ new Set();\n\twsToTransport = /* @__PURE__ */ new Map();\n\tconnectionHandler;\n\t_listening = false;\n\trateLimiter = new AuthRateLimiter();\n\tconstructor(options = {}) {\n\t\tthis.host = options.host || \"localhost\";\n\t\tthis.port = options.port ?? 9999;\n\t\tthis.authToken = options.authToken;\n\t\tthis.maxConnections = options.maxConnections ?? 100;\n\t}\n\tasync listen() {\n\t\tconst self = this;\n\t\tthis.server = Bun.serve({\n\t\t\thostname: this.host,\n\t\t\tport: this.port,\n\t\t\tfetch(req, server) {\n\t\t\t\tconst ip = server.requestIP(req)?.address || \"unknown\";\n\t\t\t\tif (self.authToken && self.rateLimiter.isRateLimited(ip)) return new Response(\"Too many auth attempts\", { status: 429 });\n\t\t\t\tlet echoProtocol;\n\t\t\t\tif (self.authToken) {\n\t\t\t\t\tconst authHeader = req.headers.get(\"Authorization\");\n\t\t\t\t\tconst headerToken = authHeader?.startsWith(\"Bearer \") ? authHeader.slice(7) : null;\n\t\t\t\t\tlet protocolToken = null;\n\t\t\t\t\tconst wsProtoHeader = req.headers.get(\"Sec-WebSocket-Protocol\");\n\t\t\t\t\tif (wsProtoHeader) {\n\t\t\t\t\t\tconst bearer = wsProtoHeader.split(\",\").map((s) => s.trim()).find((p) => p.startsWith(\"bearer.\"));\n\t\t\t\t\t\tif (bearer) {\n\t\t\t\t\t\t\tconst encoded = bearer.slice(7);\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tprotocolToken = decodeBearerToken(encoded);\n\t\t\t\t\t\t\t\techoProtocol = bearer;\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst queryToken = new URL(req.url).searchParams.get(\"token\");\n\t\t\t\t\tconst providedToken = headerToken ?? protocolToken ?? queryToken;\n\t\t\t\t\tif (!providedToken) {\n\t\t\t\t\t\tself.rateLimiter.recordAttempt(ip);\n\t\t\t\t\t\treturn new Response(\"Authentication required\", { status: 401 });\n\t\t\t\t\t}\n\t\t\t\t\tif (!secureCompare(providedToken, self.authToken)) {\n\t\t\t\t\t\tself.rateLimiter.recordAttempt(ip);\n\t\t\t\t\t\treturn new Response(\"Authentication failed\", { status: 403 });\n\t\t\t\t\t}\n\t\t\t\t\tself.rateLimiter.recordSuccess(ip);\n\t\t\t\t}\n\t\t\t\tif (self.connections.size >= self.maxConnections) return new Response(\"Too many connections\", { status: 503 });\n\t\t\t\tif (!server.upgrade(req, {\n\t\t\t\t\tdata: { ip },\n\t\t\t\t\theaders: echoProtocol ? { \"Sec-WebSocket-Protocol\": echoProtocol } : void 0\n\t\t\t\t})) return new Response(\"WebSocket upgrade failed\", { status: 400 });\n\t\t\t},\n\t\t\twebsocket: {\n\t\t\t\topen(ws) {\n\t\t\t\t\tconst transport = new WebSocketTransport(ws);\n\t\t\t\t\tself.connections.add(transport);\n\t\t\t\t\tself.wsToTransport.set(ws, transport);\n\t\t\t\t\ttransport.on(\"close\", () => {\n\t\t\t\t\t\tself.connections.delete(transport);\n\t\t\t\t\t\tself.wsToTransport.delete(ws);\n\t\t\t\t\t});\n\t\t\t\t\tself.connectionHandler?.(transport);\n\t\t\t\t},\n\t\t\t\tmessage(ws, message) {\n\t\t\t\t\tconst transport = self.wsToTransport.get(ws);\n\t\t\t\t\tif (transport) {\n\t\t\t\t\t\tconst data = typeof message === \"string\" ? new TextEncoder().encode(message) : message instanceof ArrayBuffer ? new Uint8Array(message) : new Uint8Array(message.buffer);\n\t\t\t\t\t\ttransport.handleMessage(data);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclose(ws) {\n\t\t\t\t\tconst transport = self.wsToTransport.get(ws);\n\t\t\t\t\tif (transport) {\n\t\t\t\t\t\ttransport.handleClose();\n\t\t\t\t\t\tself.connections.delete(transport);\n\t\t\t\t\t\tself.wsToTransport.delete(ws);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tperMessageDeflate: false\n\t\t\t}\n\t\t});\n\t\tif (this.server && typeof this.server.port === \"number\") this.port = this.server.port;\n\t\tthis._listening = true;\n\t}\n\tasync close() {\n\t\tfor (const conn of this.connections) await conn.close();\n\t\tthis.connections.clear();\n\t\tthis.wsToTransport.clear();\n\t\tif (this.server) {\n\t\t\tthis.server.stop();\n\t\t\tthis._listening = false;\n\t\t}\n\t}\n\tonConnection(handler) {\n\t\tthis.connectionHandler = handler;\n\t}\n\tget listening() {\n\t\treturn this._listening;\n\t}\n\tget address() {\n\t\treturn `ws://${this.host}:${this.port}`;\n\t}\n};\n/**\n* WebSocket client transport (uses browser WebSocket API).\n*/\nvar ClientWebSocketTransport = class {\n\tws;\n\tdecoder = new FrameDecoder();\n\thandlers = /* @__PURE__ */ new Map();\n\t_connected = false;\n\tconstructor(ws) {\n\t\tthis.ws = ws;\n\t\tthis._connected = ws.readyState === WebSocket.OPEN;\n\t\tthis.setupHandlers();\n\t}\n\tsetupHandlers() {\n\t\tthis.ws.onmessage = (event) => {\n\t\t\ttry {\n\t\t\t\tconst data = event.data instanceof ArrayBuffer ? new Uint8Array(event.data) : typeof event.data === \"string\" ? new TextEncoder().encode(event.data) : new Uint8Array(event.data);\n\t\t\t\tconst frames = this.decoder.push(data);\n\t\t\t\tfor (const frame of frames) this.emit(\"frame\", frame);\n\t\t\t} catch (error) {\n\t\t\t\tthis.emit(\"error\", error);\n\t\t\t}\n\t\t};\n\t\tthis.ws.onclose = () => {\n\t\t\tthis._connected = false;\n\t\t\tthis.emit(\"close\");\n\t\t};\n\t\tthis.ws.onerror = () => {\n\t\t\tthis.emit(\"error\", /* @__PURE__ */ new Error(\"WebSocket error\"));\n\t\t};\n\t\tthis.ws.onopen = () => {\n\t\t\tthis._connected = true;\n\t\t};\n\t}\n\tasync send(frame) {\n\t\tif (!this._connected) throw new WebSocketError(\"not_connected\", \"Transport is not connected\");\n\t\tconst encoded = encodeFrame(frame.type, frame.reqId, frame.payload);\n\t\tthis.ws.send(encoded);\n\t}\n\tasync close() {\n\t\tif (this._connected) {\n\t\t\tthis.ws.close();\n\t\t\tthis._connected = false;\n\t\t}\n\t}\n\ton(event, handler) {\n\t\tif (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());\n\t\tthis.handlers.get(event).add(handler);\n\t}\n\toff(event, handler) {\n\t\tthis.handlers.get(event)?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tconst handlers = this.handlers.get(event);\n\t\tif (handlers) for (const handler of handlers) handler(...args);\n\t}\n\tget connected() {\n\t\treturn this._connected;\n\t}\n};\n/**\n* WebSocket client implementation.\n*/\nvar WebSocketClient = class {\n\tws = null;\n\ttransport = null;\n\thost;\n\tport;\n\tauthToken;\n\thandlers = /* @__PURE__ */ new Map();\n\tconstructor(options = {}) {\n\t\tthis.host = options.host || \"localhost\";\n\t\tthis.port = options.port || 9999;\n\t\tthis.authToken = options.authToken;\n\t}\n\tasync connect() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst url = `ws://${this.host}:${this.port}`;\n\t\t\tconst protocols = this.authToken ? [`bearer.${encodeBearerToken(this.authToken)}`] : void 0;\n\t\t\tthis.ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);\n\t\t\tthis.ws.binaryType = \"arraybuffer\";\n\t\t\tthis.ws.onopen = () => {\n\t\t\t\tthis.transport = new ClientWebSocketTransport(this.ws);\n\t\t\t\tthis.transport.on(\"frame\", (frame) => this.emit(\"frame\", frame));\n\t\t\t\tthis.transport.on(\"error\", (error) => this.emit(\"error\", error));\n\t\t\t\tthis.transport.on(\"close\", () => this.emit(\"close\"));\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tthis.ws.onerror = () => {\n\t\t\t\treject(new WebSocketError(\"connection_failed\", \"Failed to connect\"));\n\t\t\t};\n\t\t\tthis.ws.onclose = (event) => {\n\t\t\t\tif (!this.transport) if (event.code === 1002 || event.code === 1008) reject(new WebSocketError(\"auth_failed\", \"Authentication failed\"));\n\t\t\t\telse reject(new WebSocketError(\"connection_failed\", \"Connection closed\"));\n\t\t\t};\n\t\t});\n\t}\n\tasync send(frame) {\n\t\tif (!this.transport) throw new WebSocketError(\"not_connected\", \"Client is not connected\");\n\t\treturn this.transport.send(frame);\n\t}\n\tasync close() {\n\t\tif (this.transport) await this.transport.close();\n\t\tthis.ws = null;\n\t\tthis.transport = null;\n\t}\n\ton(event, handler) {\n\t\tif (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());\n\t\tthis.handlers.get(event).add(handler);\n\t}\n\toff(event, handler) {\n\t\tthis.handlers.get(event)?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tconst handlers = this.handlers.get(event);\n\t\tif (handlers) for (const handler of handlers) handler(...args);\n\t}\n\tget connected() {\n\t\treturn this.transport?.connected ?? false;\n\t}\n};\n\n//#endregion\n//#region src/session/entry.ts\n/**\n* Create and connect a session entry for an external provider.\n*\n* This is the main entry point for providers that run as separate processes.\n* It handles: WebSocket connection → session attach → provider factory registration.\n*\n* @param providerFactory - Factory function that creates provider instances from config\n* @param options - Connection and manifest options\n* @returns Connected and attached SessionClient\n*/\nasync function createSessionEntry(providerFactory, options) {\n\tconst coreUrl = options.coreUrl || process.env.AFS_CORE_URL || \"ws://localhost:9999\";\n\tconst url = new URL(coreUrl);\n\tconst host = url.hostname;\n\tconst wsClient = new WebSocketClient({\n\t\thost,\n\t\tport: Number.parseInt(url.port || \"9999\", 10)\n\t});\n\tawait wsClient.connect();\n\tconst client = new SessionClient(providerFactory, options.clientOptions);\n\tawait client.connect(wsClient);\n\tawait client.attach({\n\t\tnamespace: options.namespace,\n\t\tmanifest: options.manifest\n\t});\n\treturn client;\n}\n\n//#endregion\n//#region src/transport/service-binding.ts\n/** Session Protocol endpoint path for frame exchange. */\nconst SERVICE_BINDING_SESSION_PATH = \"/__afs_session/frame\";\n/**\n* Host-side AFSModule that forwards operations to a provider Worker\n* via Service Binding HTTP. Each operation is an independent POST request.\n*\n* No WebSocket, no Session handshake, no persistent connection.\n* Both DOs can fully hibernate between calls.\n*/\nvar ServiceBindingProxy = class {\n\tname;\n\taccessMode = \"readwrite\";\n\tconstructor(name, binding, userId) {\n\t\tthis.binding = binding;\n\t\tthis.userId = userId;\n\t\tthis.name = name;\n\t}\n\tasync list(path, options) {\n\t\treturn this.call(\"list\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync read(path, options) {\n\t\treturn this.call(\"read\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync write(path, args, options) {\n\t\treturn this.call(\"write\", {\n\t\t\tpath,\n\t\t\targs,\n\t\t\toptions\n\t\t});\n\t}\n\tasync exec(path, args, options) {\n\t\tconst { onChunk: _, ...serializableOptions } = options ?? {};\n\t\tconst { _scope_afs: __, _authContext: ___, ...serializableArgs } = args;\n\t\treturn this.call(\"exec\", {\n\t\t\tpath,\n\t\t\targs: serializableArgs,\n\t\t\toptions: serializableOptions\n\t\t});\n\t}\n\tasync stat(path, options) {\n\t\treturn this.call(\"stat\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync delete(path, options) {\n\t\treturn this.call(\"delete\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync search(path, query, options) {\n\t\treturn this.call(\"search\", {\n\t\t\tpath,\n\t\t\tquery,\n\t\t\toptions\n\t\t});\n\t}\n\tasync explain(path, options) {\n\t\treturn this.call(\"explain\", {\n\t\t\tpath,\n\t\t\toptions\n\t\t});\n\t}\n\tasync call(method, params) {\n\t\treturn (await this.binding.fetch(new Request(`https://internal/afs/${method}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"X-User-Id\": this.userId\n\t\t\t},\n\t\t\tbody: JSON.stringify(params)\n\t\t}))).json();\n\t}\n};\n/**\n* Host-side Transport that sends frames via Service Binding HTTP POST\n* and receives frames via Queue injection.\n*\n* Each send() is an independent HTTP POST — no persistent connection.\n* DO can hibernate between calls.\n*/\nvar ServiceBindingHostTransport = class {\n\t_connected = true;\n\thandlers = {};\n\tconstructor(binding, userId) {\n\t\tthis.binding = binding;\n\t\tthis.userId = userId;\n\t}\n\tget connected() {\n\t\treturn this._connected;\n\t}\n\tasync send(frame) {\n\t\tif (!this._connected) throw new Error(\"Transport closed\");\n\t\tconst encoded = encodeFrame(frame.type, frame.reqId, frame.payload);\n\t\tconst resp = await this.binding.fetch(new Request(`https://internal${SERVICE_BINDING_SESSION_PATH}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/octet-stream\",\n\t\t\t\t\"X-User-Id\": this.userId\n\t\t\t},\n\t\t\tbody: encoded.buffer\n\t\t}));\n\t\tif (!resp.ok) throw new Error(`Provider returned ${resp.status}: ${await resp.text()}`);\n\t\tconst responseData = new Uint8Array(await resp.arrayBuffer());\n\t\tif (responseData.length > 0) try {\n\t\t\tconst { frame: responseFrame } = decodeFrame(responseData);\n\t\t\tthis.emit(\"frame\", responseFrame);\n\t\t} catch {}\n\t}\n\tasync close() {\n\t\tthis._connected = false;\n\t\tthis.emit(\"close\");\n\t}\n\t/** Inject a frame from Queue (provider → host events). */\n\tinjectFrame(frame) {\n\t\tthis.emit(\"frame\", frame);\n\t}\n\t/** Inject an event from Queue as a Session Protocol event frame. */\n\tinjectEvent(eventName, data) {\n\t\tconst payload = serializeMessage({\n\t\t\ttype: \"event\",\n\t\t\tevent: eventName,\n\t\t\tdata\n\t\t});\n\t\tthis.emit(\"frame\", {\n\t\t\ttype: FrameType.JSON,\n\t\t\treqId: 0,\n\t\t\tpayload\n\t\t});\n\t}\n\ton(event, handler) {\n\t\tconst key = event;\n\t\tif (!this.handlers[key]) this.handlers[key] = /* @__PURE__ */ new Set();\n\t\tthis.handlers[key].add(handler);\n\t}\n\toff(event, handler) {\n\t\tthis.handlers[event]?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tconst set = this.handlers[event];\n\t\tif (!set) return;\n\t\tfor (const handler of set) try {\n\t\t\thandler(...args);\n\t\t} catch {}\n\t}\n};\n/**\n* Provider-side Transport that receives frames via HTTP requests\n* and sends responses as HTTP responses or events via Queue.\n*\n* Stateless — each HTTP request is an independent frame exchange.\n* DO can hibernate between requests.\n*/\nvar ServiceBindingProviderTransport = class {\n\t_connected = true;\n\thandlers = {};\n\tpendingResponse = null;\n\tqueueSender;\n\tconstructor(options) {\n\t\tthis.queueSender = options?.queueSend ?? null;\n\t}\n\tget connected() {\n\t\treturn this._connected;\n\t}\n\t/** Handle an incoming HTTP request containing a Session Protocol frame. */\n\tasync handleRequest(request) {\n\t\tconst data = new Uint8Array(await request.arrayBuffer());\n\t\tif (data.length === 0) return new Response(\"Empty frame\", { status: 400 });\n\t\tlet incomingFrame;\n\t\ttry {\n\t\t\tincomingFrame = decodeFrame(data).frame;\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid frame\", { status: 400 });\n\t\t}\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.pendingResponse = (responseFrame) => {\n\t\t\t\tconst encoded = encodeFrame(responseFrame.type, responseFrame.reqId, responseFrame.payload);\n\t\t\t\tresolve(new Response(encoded.buffer, { headers: { \"Content-Type\": \"application/octet-stream\" } }));\n\t\t\t};\n\t\t\tthis.emit(\"frame\", incomingFrame);\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (this.pendingResponse) {\n\t\t\t\t\tthis.pendingResponse = null;\n\t\t\t\t\tresolve(new Response(null, { status: 204 }));\n\t\t\t\t}\n\t\t\t}, 3e4);\n\t\t});\n\t}\n\tasync send(frame) {\n\t\tif (!this._connected) throw new Error(\"Transport closed\");\n\t\tconst msg = deserializeMessage(frame.payload);\n\t\tif (msg.type === \"response\" && this.pendingResponse) {\n\t\t\tconst resolve = this.pendingResponse;\n\t\t\tthis.pendingResponse = null;\n\t\t\tresolve(frame);\n\t\t\treturn;\n\t\t}\n\t\tif (msg.type === \"event\" && this.queueSender) {\n\t\t\tawait this.queueSender({ frame: Array.from(encodeFrame(frame.type, frame.reqId, frame.payload)) });\n\t\t\treturn;\n\t\t}\n\t\tif (this.pendingResponse) {\n\t\t\tconst resolve = this.pendingResponse;\n\t\t\tthis.pendingResponse = null;\n\t\t\tresolve(frame);\n\t\t}\n\t}\n\tasync close() {\n\t\tthis._connected = false;\n\t\tthis.emit(\"close\");\n\t}\n\ton(event, handler) {\n\t\tconst key = event;\n\t\tif (!this.handlers[key]) this.handlers[key] = /* @__PURE__ */ new Set();\n\t\tthis.handlers[key].add(handler);\n\t}\n\toff(event, handler) {\n\t\tthis.handlers[event]?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tconst set = this.handlers[event];\n\t\tif (!set) return;\n\t\tfor (const handler of set) try {\n\t\t\thandler(...args);\n\t\t} catch {}\n\t}\n};\n\n//#endregion\n//#region src/transport/types.ts\n/**\n* Default WebSocket port\n*/\nconst DEFAULT_WS_PORT = 9999;\n\n//#endregion\nexport { BINARY_MARKER_KEY, BufferOverflowError, DEFAULT_SESSION_OPTIONS, DEFAULT_WS_PORT, DepthLimitExceededError, FrameDecoder, FrameType, HEADER_SIZE, IncompleteFrameError, InvalidFrameTypeError, InvalidMessageError, InvalidPayloadError, InvalidReqIdError, MAX_JSON_DEPTH, MAX_JSON_SIZE, MAX_PAYLOAD_SIZE, MAX_UINT32, MessageTooLargeError, PROTOCOL_VERSION, ParseError, PayloadTooLargeError, PendingBinaryBuffer, ProtocolError, RPCHandler, SERVICE_BINDING_SESSION_PATH, SESSION_NAMESPACE_PATTERN, ServiceBindingHostTransport, ServiceBindingProviderTransport, ServiceBindingProxy, SessionClient, SessionError, SessionHost, SessionProxyModule, WebSocketClient, WebSocketError, WebSocketServer, WebSocketTransport, assertValidSessionNamespace, createSessionEntry, createSessionMounter, decodeFrame, deserializeMessage, encodeFrame, extractBinary, hasBinaryMarker, injectBinary, isValidSessionNamespace, serializeMessage };\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region ../../platform/r2/dist/types.d.cts
|
|
2
|
+
//#region src/types.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* ObjectStore — platform-agnostic interface for R2-compatible object storage.
|
|
5
|
+
*
|
|
6
|
+
* R2Bucket satisfies this interface natively.
|
|
7
|
+
* For Node.js testing, use MemoryObjectStore or miniflare.
|
|
8
|
+
*/
|
|
9
|
+
interface ObjectStoreObject {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly size: number;
|
|
12
|
+
readonly uploaded: Date;
|
|
13
|
+
/** Custom metadata (R2 httpMetadata or custom) */
|
|
14
|
+
readonly customMetadata?: Record<string, string>;
|
|
15
|
+
text(): Promise<string>;
|
|
16
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
17
|
+
}
|
|
18
|
+
interface ObjectStoreHeadResult {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly size: number;
|
|
21
|
+
readonly uploaded: Date;
|
|
22
|
+
readonly customMetadata?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
interface ObjectStoreListResult {
|
|
25
|
+
readonly objects: ReadonlyArray<{
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly size: number;
|
|
28
|
+
readonly uploaded: Date;
|
|
29
|
+
readonly customMetadata?: Record<string, string>;
|
|
30
|
+
}>;
|
|
31
|
+
readonly truncated: boolean;
|
|
32
|
+
readonly cursor?: string;
|
|
33
|
+
}
|
|
34
|
+
interface ObjectStoreListOptions {
|
|
35
|
+
prefix?: string;
|
|
36
|
+
cursor?: string;
|
|
37
|
+
limit?: number;
|
|
38
|
+
delimiter?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ObjectStore {
|
|
41
|
+
get(key: string): Promise<ObjectStoreObject | null>;
|
|
42
|
+
put(key: string, value: string | ArrayBuffer | ReadableStream): Promise<void>;
|
|
43
|
+
list(options?: ObjectStoreListOptions): Promise<ObjectStoreListResult>;
|
|
44
|
+
head(key: string): Promise<ObjectStoreHeadResult | null>;
|
|
45
|
+
delete(key: string): Promise<void>;
|
|
46
|
+
} //#endregion
|
|
47
|
+
//#endregion
|
|
48
|
+
export { ObjectStore, ObjectStoreHeadResult, ObjectStoreListOptions, ObjectStoreListResult, ObjectStoreObject };
|
|
49
|
+
//# sourceMappingURL=types.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":["ObjectStoreObject","Date","Record","Promise","ArrayBuffer","key","size","uploaded","customMetadata","text","arrayBuffer","ObjectStoreHeadResult","ObjectStoreListResult","ReadonlyArray","objects","truncated","cursor","ObjectStoreListOptions","prefix","limit","delimiter","ObjectStore","ReadableStream","get","put","value","list","options","head","delete"],"sources":["../../../../../../platform/r2/dist/types.d.cts"],"mappings":";;;;;;;;UAOUA,iBAAAA;EAAAA,SACCK,GAAAA;EAAAA,SACAC,IAAAA;EAAAA,SACAC,QAAAA,EAAUN,IAAAA;EAFVI;EAAAA,SAIAG,cAAAA,GAAiBN,MAAAA;EAC1BO,IAAAA,IAAQN,OAAAA;EACRO,WAAAA,IAAeP,OAAAA,CAAQC,WAAAA;AAAAA;AAAAA,UAEfO,qBAAAA;EAAAA,SACCN,GAAAA;EAAAA,SACAC,IAAAA;EAAAA,SACAC,QAAAA,EAAUN,IAAAA;EAAAA,SACVO,cAAAA,GAAiBN,MAAAA;AAAAA;AAAAA,UAElBU,qBAAAA;EAAAA,SACCE,OAAAA,EAASD,aAAAA;IAAAA,SACPR,GAAAA;IAAAA,SACAC,IAAAA;IAAAA,SACAC,QAAAA,EAAUN,IAAAA;IAAAA,SACVO,cAAAA,GAAiBN,MAAAA;EAAAA;EAAAA,SAEnBa,SAAAA;EAAAA,SACAC,MAAAA;AAAAA;AAAAA,UAEDC,sBAAAA;EACRC,MAAAA;EACAF,MAAAA;EACAG,KAAAA;EACAC,SAAAA;AAAAA;AAAAA,UAEQC,WAAAA;EACRE,GAAAA,CAAIlB,GAAAA,WAAcF,OAAAA,CAAQH,iBAAAA;EAC1BwB,GAAAA,CAAInB,GAAAA,UAAaoB,KAAAA,WAAgBrB,WAAAA,GAAckB,cAAAA,GAAiBnB,OAAAA;EAChEuB,IAAAA,CAAKC,OAAAA,GAAUV,sBAAAA,GAAyBd,OAAAA,CAAQS,qBAAAA;EAChDgB,IAAAA,CAAKvB,GAAAA,WAAcF,OAAAA,CAAQQ,qBAAAA;EAC3BkB,MAAAA,CAAOxB,GAAAA,WAAcF,OAAAA;AAAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/prefix-range.ts
|
|
3
|
+
/**
|
|
4
|
+
* prefixRange — the ONE canonical half-open `[lo, hi)` key range covering every
|
|
5
|
+
* string that starts with `prefix`, so a `(…, path)` PK/index can bound a
|
|
6
|
+
* subtree scan by an indexed range seek (`path >= lo AND path < hi`) instead of
|
|
7
|
+
* a `LIKE 'prefix%'` residual filter.
|
|
8
|
+
*
|
|
9
|
+
* Why a literal range, not `path LIKE 'prefix%'`: SQLite/D1 do NOT push a
|
|
10
|
+
* case-insensitive LIKE (the default) into a range seek against a BINARY column,
|
|
11
|
+
* so LIKE reads EVERY row for the leading equality columns and residual-filters
|
|
12
|
+
* — the rows_read blow-ups the DID Space schema keeps chasing (#1120). A literal
|
|
13
|
+
* range is also more correct: `_` and `%` inside the prefix are LIKE wildcards,
|
|
14
|
+
* whereas a path prefix is a LITERAL prefix.
|
|
15
|
+
*
|
|
16
|
+
* Empty prefix ⇒ `["", "\xff"]`: "no lower bound, upper-bounded by the single
|
|
17
|
+
* `\xff` sentinel byte". Callers that want a true whole-table scan pass `""` and
|
|
18
|
+
* rely on this sentinel; a NON-empty prefix produces a tight `[prefix, succ)`.
|
|
19
|
+
*
|
|
20
|
+
* History: this exact body was copy-pasted into THREE unrelated DID Space
|
|
21
|
+
* modules (`d1-tree-index.ts`, `d1-resolve-store.ts`, `sqlite-resolve-store.ts`)
|
|
22
|
+
* plus a near-twin in `@aigne/afs-index` (`backend/prefix-range.ts`,
|
|
23
|
+
* non-empty-only). The dirty-path queue (index-follower-dirty-path) needs the
|
|
24
|
+
* same successor logic for its `listDirtyPaths` prefix scan; rather than mint a
|
|
25
|
+
* FOURTH copy this file is the single source the did-space modules import.
|
|
26
|
+
*/
|
|
27
|
+
function prefixRange(prefix) {
|
|
28
|
+
if (prefix.length === 0) return ["", "ÿ"];
|
|
29
|
+
return [prefix, prefix.slice(0, -1) + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1)];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
exports.prefixRange = prefixRange;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/prefix-range.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* prefixRange — the ONE canonical half-open `[lo, hi)` key range covering every
|
|
4
|
+
* string that starts with `prefix`, so a `(…, path)` PK/index can bound a
|
|
5
|
+
* subtree scan by an indexed range seek (`path >= lo AND path < hi`) instead of
|
|
6
|
+
* a `LIKE 'prefix%'` residual filter.
|
|
7
|
+
*
|
|
8
|
+
* Why a literal range, not `path LIKE 'prefix%'`: SQLite/D1 do NOT push a
|
|
9
|
+
* case-insensitive LIKE (the default) into a range seek against a BINARY column,
|
|
10
|
+
* so LIKE reads EVERY row for the leading equality columns and residual-filters
|
|
11
|
+
* — the rows_read blow-ups the DID Space schema keeps chasing (#1120). A literal
|
|
12
|
+
* range is also more correct: `_` and `%` inside the prefix are LIKE wildcards,
|
|
13
|
+
* whereas a path prefix is a LITERAL prefix.
|
|
14
|
+
*
|
|
15
|
+
* Empty prefix ⇒ `["", "\xff"]`: "no lower bound, upper-bounded by the single
|
|
16
|
+
* `\xff` sentinel byte". Callers that want a true whole-table scan pass `""` and
|
|
17
|
+
* rely on this sentinel; a NON-empty prefix produces a tight `[prefix, succ)`.
|
|
18
|
+
*
|
|
19
|
+
* History: this exact body was copy-pasted into THREE unrelated DID Space
|
|
20
|
+
* modules (`d1-tree-index.ts`, `d1-resolve-store.ts`, `sqlite-resolve-store.ts`)
|
|
21
|
+
* plus a near-twin in `@aigne/afs-index` (`backend/prefix-range.ts`,
|
|
22
|
+
* non-empty-only). The dirty-path queue (index-follower-dirty-path) needs the
|
|
23
|
+
* same successor logic for its `listDirtyPaths` prefix scan; rather than mint a
|
|
24
|
+
* FOURTH copy this file is the single source the did-space modules import.
|
|
25
|
+
*/
|
|
26
|
+
declare function prefixRange(prefix: string): [lo: string, hi: string];
|
|
27
|
+
//#endregion
|
|
28
|
+
export { prefixRange };
|
|
29
|
+
//# sourceMappingURL=prefix-range.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefix-range.d.cts","names":[],"sources":["../src/prefix-range.ts"],"mappings":";;AAwBA;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,MAAA,YAAkB,EAAA,UAAY,EAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/prefix-range.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* prefixRange — the ONE canonical half-open `[lo, hi)` key range covering every
|
|
4
|
+
* string that starts with `prefix`, so a `(…, path)` PK/index can bound a
|
|
5
|
+
* subtree scan by an indexed range seek (`path >= lo AND path < hi`) instead of
|
|
6
|
+
* a `LIKE 'prefix%'` residual filter.
|
|
7
|
+
*
|
|
8
|
+
* Why a literal range, not `path LIKE 'prefix%'`: SQLite/D1 do NOT push a
|
|
9
|
+
* case-insensitive LIKE (the default) into a range seek against a BINARY column,
|
|
10
|
+
* so LIKE reads EVERY row for the leading equality columns and residual-filters
|
|
11
|
+
* — the rows_read blow-ups the DID Space schema keeps chasing (#1120). A literal
|
|
12
|
+
* range is also more correct: `_` and `%` inside the prefix are LIKE wildcards,
|
|
13
|
+
* whereas a path prefix is a LITERAL prefix.
|
|
14
|
+
*
|
|
15
|
+
* Empty prefix ⇒ `["", "\xff"]`: "no lower bound, upper-bounded by the single
|
|
16
|
+
* `\xff` sentinel byte". Callers that want a true whole-table scan pass `""` and
|
|
17
|
+
* rely on this sentinel; a NON-empty prefix produces a tight `[prefix, succ)`.
|
|
18
|
+
*
|
|
19
|
+
* History: this exact body was copy-pasted into THREE unrelated DID Space
|
|
20
|
+
* modules (`d1-tree-index.ts`, `d1-resolve-store.ts`, `sqlite-resolve-store.ts`)
|
|
21
|
+
* plus a near-twin in `@aigne/afs-index` (`backend/prefix-range.ts`,
|
|
22
|
+
* non-empty-only). The dirty-path queue (index-follower-dirty-path) needs the
|
|
23
|
+
* same successor logic for its `listDirtyPaths` prefix scan; rather than mint a
|
|
24
|
+
* FOURTH copy this file is the single source the did-space modules import.
|
|
25
|
+
*/
|
|
26
|
+
declare function prefixRange(prefix: string): [lo: string, hi: string];
|
|
27
|
+
//#endregion
|
|
28
|
+
export { prefixRange };
|
|
29
|
+
//# sourceMappingURL=prefix-range.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefix-range.d.mts","names":[],"sources":["../src/prefix-range.ts"],"mappings":";;AAwBA;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,MAAA,YAAkB,EAAA,UAAY,EAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/prefix-range.ts
|
|
2
|
+
/**
|
|
3
|
+
* prefixRange — the ONE canonical half-open `[lo, hi)` key range covering every
|
|
4
|
+
* string that starts with `prefix`, so a `(…, path)` PK/index can bound a
|
|
5
|
+
* subtree scan by an indexed range seek (`path >= lo AND path < hi`) instead of
|
|
6
|
+
* a `LIKE 'prefix%'` residual filter.
|
|
7
|
+
*
|
|
8
|
+
* Why a literal range, not `path LIKE 'prefix%'`: SQLite/D1 do NOT push a
|
|
9
|
+
* case-insensitive LIKE (the default) into a range seek against a BINARY column,
|
|
10
|
+
* so LIKE reads EVERY row for the leading equality columns and residual-filters
|
|
11
|
+
* — the rows_read blow-ups the DID Space schema keeps chasing (#1120). A literal
|
|
12
|
+
* range is also more correct: `_` and `%` inside the prefix are LIKE wildcards,
|
|
13
|
+
* whereas a path prefix is a LITERAL prefix.
|
|
14
|
+
*
|
|
15
|
+
* Empty prefix ⇒ `["", "\xff"]`: "no lower bound, upper-bounded by the single
|
|
16
|
+
* `\xff` sentinel byte". Callers that want a true whole-table scan pass `""` and
|
|
17
|
+
* rely on this sentinel; a NON-empty prefix produces a tight `[prefix, succ)`.
|
|
18
|
+
*
|
|
19
|
+
* History: this exact body was copy-pasted into THREE unrelated DID Space
|
|
20
|
+
* modules (`d1-tree-index.ts`, `d1-resolve-store.ts`, `sqlite-resolve-store.ts`)
|
|
21
|
+
* plus a near-twin in `@aigne/afs-index` (`backend/prefix-range.ts`,
|
|
22
|
+
* non-empty-only). The dirty-path queue (index-follower-dirty-path) needs the
|
|
23
|
+
* same successor logic for its `listDirtyPaths` prefix scan; rather than mint a
|
|
24
|
+
* FOURTH copy this file is the single source the did-space modules import.
|
|
25
|
+
*/
|
|
26
|
+
function prefixRange(prefix) {
|
|
27
|
+
if (prefix.length === 0) return ["", "ÿ"];
|
|
28
|
+
return [prefix, prefix.slice(0, -1) + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1)];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { prefixRange };
|
|
33
|
+
//# sourceMappingURL=prefix-range.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefix-range.mjs","names":[],"sources":["../src/prefix-range.ts"],"sourcesContent":["/**\n * prefixRange — the ONE canonical half-open `[lo, hi)` key range covering every\n * string that starts with `prefix`, so a `(…, path)` PK/index can bound a\n * subtree scan by an indexed range seek (`path >= lo AND path < hi`) instead of\n * a `LIKE 'prefix%'` residual filter.\n *\n * Why a literal range, not `path LIKE 'prefix%'`: SQLite/D1 do NOT push a\n * case-insensitive LIKE (the default) into a range seek against a BINARY column,\n * so LIKE reads EVERY row for the leading equality columns and residual-filters\n * — the rows_read blow-ups the DID Space schema keeps chasing (#1120). A literal\n * range is also more correct: `_` and `%` inside the prefix are LIKE wildcards,\n * whereas a path prefix is a LITERAL prefix.\n *\n * Empty prefix ⇒ `[\"\", \"\\xff\"]`: \"no lower bound, upper-bounded by the single\n * `\\xff` sentinel byte\". Callers that want a true whole-table scan pass `\"\"` and\n * rely on this sentinel; a NON-empty prefix produces a tight `[prefix, succ)`.\n *\n * History: this exact body was copy-pasted into THREE unrelated DID Space\n * modules (`d1-tree-index.ts`, `d1-resolve-store.ts`, `sqlite-resolve-store.ts`)\n * plus a near-twin in `@aigne/afs-index` (`backend/prefix-range.ts`,\n * non-empty-only). The dirty-path queue (index-follower-dirty-path) needs the\n * same successor logic for its `listDirtyPaths` prefix scan; rather than mint a\n * FOURTH copy this file is the single source the did-space modules import.\n */\nexport function prefixRange(prefix: string): [lo: string, hi: string] {\n if (prefix.length === 0) return [\"\", \"\\xff\"];\n const hi = prefix.slice(0, -1) + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1);\n return [prefix, hi];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,YAAY,QAA0C;AACpE,KAAI,OAAO,WAAW,EAAG,QAAO,CAAC,IAAI,IAAO;AAE5C,QAAO,CAAC,QADG,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,aAAa,OAAO,WAAW,OAAO,SAAS,EAAE,GAAG,EAAE,CAC3E"}
|