@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
package/dist/rsync.d.mts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { MetadataDB } from "./metadata-db.mjs";
|
|
2
|
+
import { AFS } from "@aigne/afs";
|
|
3
|
+
|
|
4
|
+
//#region src/rsync.d.ts
|
|
5
|
+
interface FileFingerprint {
|
|
6
|
+
path: string;
|
|
7
|
+
size: number;
|
|
8
|
+
cid?: string;
|
|
9
|
+
mtime?: number;
|
|
10
|
+
}
|
|
11
|
+
interface SyncManifest {
|
|
12
|
+
/**
|
|
13
|
+
* Legacy file list field — kept REQUIRED for backward compatibility
|
|
14
|
+
* with existing consumers (rsync.ts pipeline, computeDiff, e2e tests).
|
|
15
|
+
*
|
|
16
|
+
* Task 3.2: as of the G5.b manifest endpoint rewrite, the server now
|
|
17
|
+
* also returns this same data under the `entries` field plus a fresh
|
|
18
|
+
* `manifestSeq` cursor. New code should prefer `entries` + `manifestSeq`
|
|
19
|
+
* but reading `files` continues to work — both arrays carry identical
|
|
20
|
+
* data, they are two views of the same underlying snapshot.
|
|
21
|
+
*/
|
|
22
|
+
files: FileFingerprint[];
|
|
23
|
+
/**
|
|
24
|
+
* Task 3.2 — canonical "entries" view of the same data as `files`.
|
|
25
|
+
* Optional because clients constructing a manifest locally (e.g.
|
|
26
|
+
* `buildManifestFromDB`) may omit it; the wire format from the server
|
|
27
|
+
* always includes it.
|
|
28
|
+
*/
|
|
29
|
+
entries?: FileFingerprint[];
|
|
30
|
+
/**
|
|
31
|
+
* Task 3.2 — server-provided changelog cursor at the moment the
|
|
32
|
+
* manifest was assembled. After reading the manifest a client can
|
|
33
|
+
* call `GET /sync/changes?since=manifestSeq` to fetch all subsequent
|
|
34
|
+
* deltas without missing or replaying entries (CTE single-shot
|
|
35
|
+
* snapshot — entries and manifestSeq come from the same point-in-time
|
|
36
|
+
* read).
|
|
37
|
+
*
|
|
38
|
+
* Optional because client-constructed manifests have no server seq.
|
|
39
|
+
* 0 is a valid value (empty changelog == nothing has been written
|
|
40
|
+
* to this scope yet).
|
|
41
|
+
*/
|
|
42
|
+
manifestSeq?: number;
|
|
43
|
+
createdAt: number;
|
|
44
|
+
}
|
|
45
|
+
interface SyncDiffEntry {
|
|
46
|
+
path: string;
|
|
47
|
+
reason: "added" | "modified" | "deleted";
|
|
48
|
+
}
|
|
49
|
+
interface SyncDiff {
|
|
50
|
+
entries: SyncDiffEntry[];
|
|
51
|
+
stats: {
|
|
52
|
+
added: number;
|
|
53
|
+
modified: number;
|
|
54
|
+
deleted: number;
|
|
55
|
+
unchanged: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
interface SyncProgress {
|
|
59
|
+
phase: "scanning" | "diffing" | "transferring" | "verifying" | "done" | "failed";
|
|
60
|
+
filesTotal: number;
|
|
61
|
+
filesProcessed: number;
|
|
62
|
+
bytesTotal: number;
|
|
63
|
+
bytesTransferred: number;
|
|
64
|
+
errors: Array<{
|
|
65
|
+
path: string;
|
|
66
|
+
error: string;
|
|
67
|
+
}>;
|
|
68
|
+
startedAt: number;
|
|
69
|
+
updatedAt: number;
|
|
70
|
+
}
|
|
71
|
+
interface SyncResult {
|
|
72
|
+
success: boolean;
|
|
73
|
+
diff: SyncDiff;
|
|
74
|
+
transferred: number;
|
|
75
|
+
verified: boolean;
|
|
76
|
+
errors: Array<{
|
|
77
|
+
path: string;
|
|
78
|
+
error: string;
|
|
79
|
+
}>;
|
|
80
|
+
durationMs: number;
|
|
81
|
+
}
|
|
82
|
+
interface SyncOptions {
|
|
83
|
+
delete?: boolean;
|
|
84
|
+
dryRun?: boolean;
|
|
85
|
+
verify?: boolean;
|
|
86
|
+
concurrency?: number;
|
|
87
|
+
onProgress?: (progress: SyncProgress) => void;
|
|
88
|
+
/** Filter function to exclude paths from sync. Return true to include. */
|
|
89
|
+
pathFilter?: (path: string) => boolean;
|
|
90
|
+
/**
|
|
91
|
+
* If provided, fetch the src manifest from a remote server endpoint instead of
|
|
92
|
+
* paginating through srcAfs. Useful when the remote side has a manifest endpoint.
|
|
93
|
+
*/
|
|
94
|
+
srcFetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;
|
|
95
|
+
/**
|
|
96
|
+
* If provided, fetch the dst manifest from a remote server endpoint instead of
|
|
97
|
+
* paginating through dstAfs. Useful when the remote side has a manifest endpoint.
|
|
98
|
+
*/
|
|
99
|
+
dstFetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;
|
|
100
|
+
/**
|
|
101
|
+
* Phase 3: If provided, enable checkpoint-based resume-on-failure.
|
|
102
|
+
* transferFiles() will persist progress to sync_checkpoint table and resume
|
|
103
|
+
* from the last completed file if interrupted.
|
|
104
|
+
*/
|
|
105
|
+
checkpointOptions?: {
|
|
106
|
+
metadataDb: MetadataDB;
|
|
107
|
+
direction: string;
|
|
108
|
+
remoteUrl: string;
|
|
109
|
+
basePath: string;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
interface BuildManifestOptions {
|
|
113
|
+
/**
|
|
114
|
+
* If provided, fetch manifest from a remote server endpoint instead of
|
|
115
|
+
* paginating through the AFS. The `afs` parameter is unused when this is set.
|
|
116
|
+
*/
|
|
117
|
+
fetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;
|
|
118
|
+
/** Only return files modified after this timestamp (ms). Passed to fetchManifest. */
|
|
119
|
+
since?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Phase 2: If provided (together with localRootPath + localScope), build the
|
|
122
|
+
* manifest by calling reconcile() then querying _metadata.db directly.
|
|
123
|
+
* This replaces the O(N) full stat+read+CID scan with an O(1) SQL query.
|
|
124
|
+
*/
|
|
125
|
+
localMetadataDb?: MetadataDB;
|
|
126
|
+
/** Root directory of the user's DID space (required when localMetadataDb is set). */
|
|
127
|
+
localRootPath?: string;
|
|
128
|
+
/** userDid / scope for the entries query (required when localMetadataDb is set). */
|
|
129
|
+
localScope?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Task 1.3: explicit opt-out — skip both the explicit DB params and the
|
|
132
|
+
* metadata context attached by DIDSpace, and fall through to the legacy
|
|
133
|
+
* AFS pagination path. Used for back-compat testing + rare callers that
|
|
134
|
+
* must re-scan the filesystem from scratch.
|
|
135
|
+
*/
|
|
136
|
+
skipLocalMetadataDb?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Fetch a file manifest from a remote server's POST /api/space/manifest endpoint.
|
|
140
|
+
*
|
|
141
|
+
* This replaces the N-request paginated list + per-file CID read with a single
|
|
142
|
+
* request that returns the complete manifest directly from the server's D1 index.
|
|
143
|
+
*
|
|
144
|
+
* @param baseUrl Server base URL (e.g. "https://computer.afsd.dev")
|
|
145
|
+
* @param authHeader Authorization header value (e.g. "Bearer <token>")
|
|
146
|
+
* @param basePath Path within the space (e.g. "/" or "/blocklets/z1blk/")
|
|
147
|
+
* @param since Optional: only return files modified after this ms timestamp
|
|
148
|
+
* @param fetchFn Optional fetch override (for testing)
|
|
149
|
+
*/
|
|
150
|
+
declare function fetchRemoteManifest(baseUrl: string, authHeader: string, basePath: string, since?: number, fetchFn?: typeof globalThis.fetch): Promise<SyncManifest>;
|
|
151
|
+
declare function buildManifest(afs: AFS, basePath: string, options?: BuildManifestOptions): Promise<SyncManifest>;
|
|
152
|
+
declare function computeDiff(src: SyncManifest, dst: SyncManifest, options?: Pick<SyncOptions, "delete">): SyncDiff;
|
|
153
|
+
declare function runWithConcurrency(tasks: Array<() => Promise<void>>, limit: number): Promise<void>;
|
|
154
|
+
declare function syncDIDSpace(srcAfs: AFS, dstAfs: AFS, basePath: string, options?: SyncOptions): Promise<SyncResult>;
|
|
155
|
+
//#endregion
|
|
156
|
+
export { BuildManifestOptions, FileFingerprint, SyncDiff, SyncDiffEntry, SyncManifest, SyncOptions, SyncProgress, SyncResult, buildManifest, computeDiff, fetchRemoteManifest, runWithConcurrency, syncDIDSpace };
|
|
157
|
+
//# sourceMappingURL=rsync.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsync.d.mts","names":[],"sources":["../src/rsync.ts"],"mappings":";;;;UAuCiB,eAAA;EACf,IAAA;EACA,IAAA;EACA,GAAA;EACA,KAAA;AAAA;AAAA,UAGe,YAAA;EAgCN;;AAGX;;;;;AAKA;;;EA7BE,KAAA,EAAO,eAAA;EA8BP;;;;;;EAvBA,OAAA,GAAU,eAAA;EAwB0D;;AAGtE;;;;;;;;;;EAdE,WAAA;EACA,SAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA;EACA,MAAA;AAAA;AAAA,UAGe,QAAA;EACf,OAAA,EAAS,aAAA;EACT,KAAA;IAAS,KAAA;IAAe,QAAA;IAAkB,OAAA;IAAiB,SAAA;EAAA;AAAA;AAAA,UAG5C,YAAA;EACf,KAAA;EACA,UAAA;EACA,cAAA;EACA,UAAA;EACA,gBAAA;EACA,MAAA,EAAQ,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC9B,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,OAAA;EACA,IAAA,EAAM,QAAA;EACN,WAAA;EACA,QAAA;EACA,MAAA,EAAQ,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC9B,UAAA;AAAA;AAAA,UAGe,WAAA;EACf,MAAA;EACA,MAAA;EACA,MAAA;EACA,WAAA;EACA,UAAA,IAAc,QAAA,EAAU,YAAA;EAOiC;EALzD,UAAA,IAAc,IAAA;EAUd;;;;EALA,gBAAA,IAAoB,QAAA,UAAkB,KAAA,cAAmB,OAAA,CAAQ,YAAA;EAWjE;;;;EANA,gBAAA,IAAoB,QAAA,UAAkB,KAAA,cAAmB,OAAA,CAAQ,YAAA;EAU/D;;;AAIJ;;EARE,iBAAA;IACE,UAAA,EAAY,UAAA;IACZ,SAAA;IACA,SAAA;IACA,QAAA;EAAA;AAAA;AAAA,UAIa,oBAAA;EAKoB;;;;EAAnC,aAAA,IAAiB,QAAA,UAAkB,KAAA,cAAmB,OAAA,CAAQ,YAAA;EAQ5C;EANlB,KAAA;EAUA;;;;AA6BF;EAjCE,eAAA,GAAkB,UAAA;;EAElB,aAAA;EAqCS;EAnCT,UAAA;EAmCQ;;;;;;EA5BR,mBAAA;AAAA;;;;;;AAoEF;;;;;;;iBA9CsB,mBAAA,CACpB,OAAA,UACA,UAAA,UACA,QAAA,UACA,KAAA,WACA,OAAA,UAAgB,UAAA,CAAW,KAAA,GAC1B,OAAA,CAAQ,YAAA;AAAA,iBAwCW,aAAA,CACpB,GAAA,EAAK,GAAA,EACL,QAAA,UACA,OAAA,GAAU,oBAAA,GACT,OAAA,CAAQ,YAAA;AAAA,iBAwFK,WAAA,CACd,GAAA,EAAK,YAAA,EACL,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,IAAA,CAAK,WAAA,cACd,QAAA;AAAA,iBA4LmB,kBAAA,CACpB,KAAA,EAAO,KAAA,OAAY,OAAA,SACnB,KAAA,WACC,OAAA;AAAA,iBAyDmB,YAAA,CACpB,MAAA,EAAQ,GAAA,EACR,MAAA,EAAQ,GAAA,EACR,QAAA,UACA,OAAA,GAAU,WAAA,GACT,OAAA,CAAQ,UAAA"}
|
package/dist/rsync.mjs
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { computeCID } from "./cid.mjs";
|
|
2
|
+
import { getMetadataContext } from "./afs-metadata-context.mjs";
|
|
3
|
+
import { resolveAFSContent } from "./rsync-content.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/rsync.ts
|
|
6
|
+
/**
|
|
7
|
+
* Compute how many files to sample for verification.
|
|
8
|
+
* Formula: max(1, min(100, ceil(totalTransferred / 10)))
|
|
9
|
+
*/
|
|
10
|
+
function computeSampleCount(totalTransferred) {
|
|
11
|
+
if (totalTransferred === 0) return 0;
|
|
12
|
+
return Math.max(1, Math.min(100, Math.ceil(totalTransferred / 10)));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Select a random sample of `count` items from `files`.
|
|
16
|
+
* Uses partial Fisher-Yates shuffle. Returns a copy.
|
|
17
|
+
*/
|
|
18
|
+
function sampleFiles(files, count) {
|
|
19
|
+
if (count >= files.length) return [...files];
|
|
20
|
+
const shuffled = [...files];
|
|
21
|
+
for (let i = 0; i < count; i++) {
|
|
22
|
+
const j = i + Math.floor(Math.random() * (shuffled.length - i));
|
|
23
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
24
|
+
}
|
|
25
|
+
return shuffled.slice(0, count);
|
|
26
|
+
}
|
|
27
|
+
/** Content-decoding helpers shared with the local metadata subscriber. */
|
|
28
|
+
const resolveContent = resolveAFSContent;
|
|
29
|
+
/**
|
|
30
|
+
* Fetch a file manifest from a remote server's POST /api/space/manifest endpoint.
|
|
31
|
+
*
|
|
32
|
+
* This replaces the N-request paginated list + per-file CID read with a single
|
|
33
|
+
* request that returns the complete manifest directly from the server's D1 index.
|
|
34
|
+
*
|
|
35
|
+
* @param baseUrl Server base URL (e.g. "https://computer.afsd.dev")
|
|
36
|
+
* @param authHeader Authorization header value (e.g. "Bearer <token>")
|
|
37
|
+
* @param basePath Path within the space (e.g. "/" or "/blocklets/z1blk/")
|
|
38
|
+
* @param since Optional: only return files modified after this ms timestamp
|
|
39
|
+
* @param fetchFn Optional fetch override (for testing)
|
|
40
|
+
*/
|
|
41
|
+
async function fetchRemoteManifest(baseUrl, authHeader, basePath, since, fetchFn = globalThis.fetch) {
|
|
42
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/space/manifest`;
|
|
43
|
+
const body = { basePath };
|
|
44
|
+
if (since !== void 0) body.since = since;
|
|
45
|
+
const response = await fetchFn(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
Authorization: authHeader
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify(body)
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) throw new Error(`fetchRemoteManifest failed: ${response.status} ${response.statusText} from ${url}`);
|
|
54
|
+
const raw = await response.json();
|
|
55
|
+
const files = raw.entries ?? raw.files ?? [];
|
|
56
|
+
return {
|
|
57
|
+
files,
|
|
58
|
+
entries: files,
|
|
59
|
+
manifestSeq: raw.manifestSeq,
|
|
60
|
+
createdAt: raw.createdAt
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const BUILD_MANIFEST_PAGE_SIZE = 1e3;
|
|
64
|
+
async function buildManifest(afs, basePath, options) {
|
|
65
|
+
if (options?.fetchManifest) return options.fetchManifest(basePath, options.since);
|
|
66
|
+
if (options?.localMetadataDb && options.localRootPath && options.localScope) return buildManifestFromDB(options.localMetadataDb, options.localRootPath, options.localScope, basePath);
|
|
67
|
+
if (!options?.skipLocalMetadataDb) {
|
|
68
|
+
const ctx = getMetadataContext(afs);
|
|
69
|
+
if (ctx) return buildManifestFromDB(ctx.metadataDb, ctx.userDir, ctx.scope, basePath);
|
|
70
|
+
}
|
|
71
|
+
const SYSTEM_VIRTUAL_PATHS = new Set([
|
|
72
|
+
"/.knowledge",
|
|
73
|
+
"/.meta",
|
|
74
|
+
"/.actions"
|
|
75
|
+
]);
|
|
76
|
+
const allEntries = [];
|
|
77
|
+
let offset = 0;
|
|
78
|
+
let pageEntries = [];
|
|
79
|
+
do {
|
|
80
|
+
const batch = await afs.list(basePath, {
|
|
81
|
+
maxDepth: 99,
|
|
82
|
+
limit: BUILD_MANIFEST_PAGE_SIZE,
|
|
83
|
+
offset
|
|
84
|
+
});
|
|
85
|
+
pageEntries = basePath === "/" ? batch.data.filter((e) => !SYSTEM_VIRTUAL_PATHS.has(e.path)) : batch.data;
|
|
86
|
+
allEntries.push(...pageEntries);
|
|
87
|
+
offset += pageEntries.length;
|
|
88
|
+
} while (pageEntries.length === BUILD_MANIFEST_PAGE_SIZE);
|
|
89
|
+
const files = [];
|
|
90
|
+
for (const entry of allEntries) {
|
|
91
|
+
if (entry.meta?.childrenCount != null) continue;
|
|
92
|
+
let cid = entry.meta?.cid;
|
|
93
|
+
if (!cid) try {
|
|
94
|
+
const readResult = await afs.read(entry.path);
|
|
95
|
+
const content = readResult.data?.content;
|
|
96
|
+
if (content != null) cid = await computeCID(resolveContent(content, readResult.data?.meta));
|
|
97
|
+
} catch {}
|
|
98
|
+
files.push({
|
|
99
|
+
path: entry.path,
|
|
100
|
+
size: entry.meta?.size ?? 0,
|
|
101
|
+
cid,
|
|
102
|
+
mtime: entry.updatedAt?.getTime()
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
files,
|
|
107
|
+
createdAt: Date.now()
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function computeDiff(src, dst, options) {
|
|
111
|
+
const dstMap = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const f of dst.files) dstMap.set(f.path, f);
|
|
113
|
+
const entries = [];
|
|
114
|
+
let added = 0;
|
|
115
|
+
let modified = 0;
|
|
116
|
+
let unchanged = 0;
|
|
117
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
118
|
+
for (const srcFile of src.files) {
|
|
119
|
+
seenPaths.add(srcFile.path);
|
|
120
|
+
const dstFile = dstMap.get(srcFile.path);
|
|
121
|
+
if (!dstFile) {
|
|
122
|
+
entries.push({
|
|
123
|
+
path: srcFile.path,
|
|
124
|
+
reason: "added"
|
|
125
|
+
});
|
|
126
|
+
added++;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (isModified(srcFile, dstFile)) {
|
|
130
|
+
entries.push({
|
|
131
|
+
path: srcFile.path,
|
|
132
|
+
reason: "modified"
|
|
133
|
+
});
|
|
134
|
+
modified++;
|
|
135
|
+
} else unchanged++;
|
|
136
|
+
}
|
|
137
|
+
let deleted = 0;
|
|
138
|
+
if (options?.delete) {
|
|
139
|
+
for (const dstFile of dst.files) if (!seenPaths.has(dstFile.path)) {
|
|
140
|
+
entries.push({
|
|
141
|
+
path: dstFile.path,
|
|
142
|
+
reason: "deleted"
|
|
143
|
+
});
|
|
144
|
+
deleted++;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
entries,
|
|
149
|
+
stats: {
|
|
150
|
+
added,
|
|
151
|
+
modified,
|
|
152
|
+
deleted,
|
|
153
|
+
unchanged
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function isModified(src, dst) {
|
|
158
|
+
if (src.cid && dst.cid) return src.cid !== dst.cid;
|
|
159
|
+
return src.size !== dst.size;
|
|
160
|
+
}
|
|
161
|
+
async function transferFiles(srcAfs, dstAfs, diff, options) {
|
|
162
|
+
const concurrency = options?.concurrency ?? 5;
|
|
163
|
+
const dryRun = options?.dryRun ?? false;
|
|
164
|
+
const errors = [];
|
|
165
|
+
let transferred = 0;
|
|
166
|
+
let sessionId = null;
|
|
167
|
+
const completedPaths = /* @__PURE__ */ new Set();
|
|
168
|
+
const checkpointOpts = options?.checkpointOptions;
|
|
169
|
+
if (checkpointOpts && !dryRun) {
|
|
170
|
+
const { metadataDb, direction, remoteUrl, basePath } = checkpointOpts;
|
|
171
|
+
const existing = await metadataDb.getActiveCheckpoint(direction, remoteUrl, basePath);
|
|
172
|
+
if (existing) {
|
|
173
|
+
sessionId = existing.session_id;
|
|
174
|
+
try {
|
|
175
|
+
const parsed = JSON.parse(existing.completed);
|
|
176
|
+
if (Array.isArray(parsed)) {
|
|
177
|
+
for (const p of parsed) if (typeof p === "string") completedPaths.add(p);
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
await metadataDb.updateCheckpointCompleted(sessionId, []);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
sessionId = crypto.randomUUID();
|
|
184
|
+
await metadataDb.createCheckpoint({
|
|
185
|
+
session_id: sessionId,
|
|
186
|
+
direction,
|
|
187
|
+
remote_url: remoteUrl,
|
|
188
|
+
base_path: basePath,
|
|
189
|
+
status: "active",
|
|
190
|
+
diff_json: JSON.stringify(diff.entries),
|
|
191
|
+
completed: "[]",
|
|
192
|
+
created_at: Date.now(),
|
|
193
|
+
updated_at: Date.now()
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const progress = {
|
|
198
|
+
phase: "transferring",
|
|
199
|
+
filesTotal: diff.entries.length,
|
|
200
|
+
filesProcessed: 0,
|
|
201
|
+
bytesTotal: 0,
|
|
202
|
+
bytesTransferred: 0,
|
|
203
|
+
errors: [],
|
|
204
|
+
startedAt: Date.now(),
|
|
205
|
+
updatedAt: Date.now()
|
|
206
|
+
};
|
|
207
|
+
const toTransfer = diff.entries.filter((e) => e.reason === "added" || e.reason === "modified");
|
|
208
|
+
const toDelete = diff.entries.filter((e) => e.reason === "deleted");
|
|
209
|
+
const transferOne = async (entry) => {
|
|
210
|
+
if (completedPaths.has(entry.path)) {
|
|
211
|
+
transferred++;
|
|
212
|
+
progress.filesProcessed++;
|
|
213
|
+
progress.updatedAt = Date.now();
|
|
214
|
+
options?.onProgress?.(progress);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
if (dryRun) return;
|
|
219
|
+
const readResult = await srcAfs.read(entry.path);
|
|
220
|
+
const rawContent = readResult.data?.content;
|
|
221
|
+
if (rawContent == null) throw new Error(`Failed to read ${entry.path}: no content`);
|
|
222
|
+
const content = resolveContent(rawContent, readResult.data?.meta);
|
|
223
|
+
await dstAfs.write(entry.path, { content });
|
|
224
|
+
transferred++;
|
|
225
|
+
progress.bytesTransferred += readResult.data?.meta?.size ?? 0;
|
|
226
|
+
if (sessionId && checkpointOpts) {
|
|
227
|
+
completedPaths.add(entry.path);
|
|
228
|
+
await checkpointOpts.metadataDb.updateCheckpointCompleted(sessionId, [...completedPaths]);
|
|
229
|
+
}
|
|
230
|
+
} catch (err) {
|
|
231
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
232
|
+
errors.push({
|
|
233
|
+
path: entry.path,
|
|
234
|
+
error: msg
|
|
235
|
+
});
|
|
236
|
+
} finally {
|
|
237
|
+
progress.filesProcessed++;
|
|
238
|
+
progress.updatedAt = Date.now();
|
|
239
|
+
progress.errors = [...errors];
|
|
240
|
+
options?.onProgress?.(progress);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
await runWithConcurrency(toTransfer.map((e) => () => transferOne(e)), concurrency);
|
|
244
|
+
for (const entry of toDelete) {
|
|
245
|
+
if (dryRun) continue;
|
|
246
|
+
try {
|
|
247
|
+
await dstAfs.delete(entry.path);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
250
|
+
errors.push({
|
|
251
|
+
path: entry.path,
|
|
252
|
+
error: msg
|
|
253
|
+
});
|
|
254
|
+
} finally {
|
|
255
|
+
progress.filesProcessed++;
|
|
256
|
+
progress.updatedAt = Date.now();
|
|
257
|
+
progress.errors = [...errors];
|
|
258
|
+
options?.onProgress?.(progress);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (sessionId && checkpointOpts) await checkpointOpts.metadataDb.completeCheckpoint(sessionId);
|
|
262
|
+
return {
|
|
263
|
+
transferred,
|
|
264
|
+
errors
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
async function runWithConcurrency(tasks, limit) {
|
|
268
|
+
const executing = /* @__PURE__ */ new Set();
|
|
269
|
+
for (const task of tasks) {
|
|
270
|
+
const p = task().then(() => {
|
|
271
|
+
executing.delete(p);
|
|
272
|
+
});
|
|
273
|
+
executing.add(p);
|
|
274
|
+
if (executing.size >= limit) await Promise.race(executing);
|
|
275
|
+
}
|
|
276
|
+
await Promise.all(executing);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Verify sync by sampling transferred files and comparing CIDs.
|
|
280
|
+
*
|
|
281
|
+
* Phase 3 change: instead of full buildManifest on dst, we sample
|
|
282
|
+
* max(1, min(100, ceil(transferred/10))) files from the transferred set,
|
|
283
|
+
* read each from dst, compute its CID, and compare against srcManifest CID.
|
|
284
|
+
*
|
|
285
|
+
* @param dstAfs Destination AFS to read files from
|
|
286
|
+
* @param transferredFiles FileFingerprints from srcManifest for transferred files
|
|
287
|
+
*/
|
|
288
|
+
async function verifySync(dstAfs, transferredFiles) {
|
|
289
|
+
if (transferredFiles.length === 0) return true;
|
|
290
|
+
const sample = sampleFiles(transferredFiles, computeSampleCount(transferredFiles.length));
|
|
291
|
+
for (const srcFile of sample) {
|
|
292
|
+
if (!srcFile.cid) continue;
|
|
293
|
+
try {
|
|
294
|
+
const readResult = await dstAfs.read(srcFile.path);
|
|
295
|
+
const rawContent = readResult.data?.content;
|
|
296
|
+
if (rawContent == null) return false;
|
|
297
|
+
if (await computeCID(resolveContent(rawContent, readResult.data?.meta)) !== srcFile.cid) return false;
|
|
298
|
+
} catch {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
async function syncDIDSpace(srcAfs, dstAfs, basePath, options) {
|
|
305
|
+
const startTime = Date.now();
|
|
306
|
+
const verify = options?.verify ?? true;
|
|
307
|
+
const progress = {
|
|
308
|
+
phase: "scanning",
|
|
309
|
+
filesTotal: 0,
|
|
310
|
+
filesProcessed: 0,
|
|
311
|
+
bytesTotal: 0,
|
|
312
|
+
bytesTransferred: 0,
|
|
313
|
+
errors: [],
|
|
314
|
+
startedAt: startTime,
|
|
315
|
+
updatedAt: startTime
|
|
316
|
+
};
|
|
317
|
+
options?.onProgress?.(progress);
|
|
318
|
+
let [srcManifest, dstManifest] = await Promise.all([buildManifest(srcAfs, basePath, options?.srcFetchManifest ? { fetchManifest: options.srcFetchManifest } : void 0), buildManifest(dstAfs, basePath, options?.dstFetchManifest ? { fetchManifest: options.dstFetchManifest } : void 0)]);
|
|
319
|
+
if (options?.pathFilter) {
|
|
320
|
+
const pf = options.pathFilter;
|
|
321
|
+
srcManifest = {
|
|
322
|
+
...srcManifest,
|
|
323
|
+
files: srcManifest.files.filter((f) => pf(f.path))
|
|
324
|
+
};
|
|
325
|
+
dstManifest = {
|
|
326
|
+
...dstManifest,
|
|
327
|
+
files: dstManifest.files.filter((f) => pf(f.path))
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
progress.phase = "diffing";
|
|
331
|
+
progress.updatedAt = Date.now();
|
|
332
|
+
options?.onProgress?.(progress);
|
|
333
|
+
const diff = computeDiff(srcManifest, dstManifest, options);
|
|
334
|
+
progress.phase = "transferring";
|
|
335
|
+
progress.filesTotal = diff.entries.filter((e) => e.reason !== "deleted").length;
|
|
336
|
+
progress.updatedAt = Date.now();
|
|
337
|
+
options?.onProgress?.(progress);
|
|
338
|
+
const { transferred, errors } = await transferFiles(srcAfs, dstAfs, diff, options);
|
|
339
|
+
let verified = false;
|
|
340
|
+
if (verify && !options?.dryRun) {
|
|
341
|
+
progress.phase = "verifying";
|
|
342
|
+
progress.updatedAt = Date.now();
|
|
343
|
+
options?.onProgress?.(progress);
|
|
344
|
+
const transferredPaths = new Set(diff.entries.filter((e) => e.reason === "added" || e.reason === "modified").map((e) => e.path));
|
|
345
|
+
verified = await verifySync(dstAfs, srcManifest.files.filter((f) => transferredPaths.has(f.path)));
|
|
346
|
+
}
|
|
347
|
+
progress.phase = errors.length > 0 ? "failed" : "done";
|
|
348
|
+
progress.updatedAt = Date.now();
|
|
349
|
+
options?.onProgress?.(progress);
|
|
350
|
+
return {
|
|
351
|
+
success: errors.length === 0,
|
|
352
|
+
diff,
|
|
353
|
+
transferred,
|
|
354
|
+
verified,
|
|
355
|
+
errors,
|
|
356
|
+
durationMs: Date.now() - startTime
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Build a manifest purely from `_metadata.db` (the index is the source of
|
|
361
|
+
* truth after the Node backend moved to content-addressed storage, Phase 3).
|
|
362
|
+
*
|
|
363
|
+
* The old `reconcile({ rootPath })` FS-scan prelude is GONE: there is no
|
|
364
|
+
* verbatim file tree to walk anymore — content lives at `objects/{cid}` and the
|
|
365
|
+
* index already reflects every write (the provider writes entries atomically).
|
|
366
|
+
* Reconciling against the CAS layout would mis-scan internal object files.
|
|
367
|
+
* `rootPath` is retained in the signature for caller compatibility but unused.
|
|
368
|
+
*/
|
|
369
|
+
async function buildManifestFromDB(metadataDb, _rootPath, scope, basePath) {
|
|
370
|
+
const dbBasePath = afsBasePathToDbPrefix(basePath);
|
|
371
|
+
return {
|
|
372
|
+
files: (await metadataDb.getFiles(scope, dbBasePath)).map((row) => ({
|
|
373
|
+
path: `/${row.path}`,
|
|
374
|
+
size: row.size,
|
|
375
|
+
cid: row.cid ?? void 0,
|
|
376
|
+
mtime: row.mtime
|
|
377
|
+
})),
|
|
378
|
+
createdAt: Date.now()
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Convert an AFS basePath to a MetadataDB path prefix.
|
|
383
|
+
*
|
|
384
|
+
* Examples:
|
|
385
|
+
* "/" → undefined (match all)
|
|
386
|
+
* "/appDid" → "appDid/"
|
|
387
|
+
* "/appDid/" → "appDid/"
|
|
388
|
+
*/
|
|
389
|
+
function afsBasePathToDbPrefix(basePath) {
|
|
390
|
+
const stripped = basePath.replace(/^\/+/, "").replace(/\/*$/, "");
|
|
391
|
+
if (!stripped) return void 0;
|
|
392
|
+
return `${stripped}/`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
//#endregion
|
|
396
|
+
export { buildManifest, computeDiff, fetchRemoteManifest, runWithConcurrency, syncDIDSpace };
|
|
397
|
+
//# sourceMappingURL=rsync.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsync.mjs","names":[],"sources":["../src/rsync.ts"],"sourcesContent":["/**\n * DIDSpace rsync — single-direction incremental sync between AFS instances.\n *\n * Pipeline: Snapshot → Diff → Transfer → Verify\n */\n\nimport type { AFS, AFSEntry } from \"@aigne/afs\";\nimport { getMetadataContext } from \"./afs-metadata-context.js\";\nimport { computeCID } from \"./cid.js\";\nimport type { MetadataDB } from \"./metadata-db.js\";\nimport { resolveAFSContent } from \"./rsync-content.js\";\n\n// ========== Sampling helpers (exported for testing) ==========\n\n/**\n * Compute how many files to sample for verification.\n * Formula: max(1, min(100, ceil(totalTransferred / 10)))\n */\nexport function computeSampleCount(totalTransferred: number): number {\n if (totalTransferred === 0) return 0;\n return Math.max(1, Math.min(100, Math.ceil(totalTransferred / 10)));\n}\n\n/**\n * Select a random sample of `count` items from `files`.\n * Uses partial Fisher-Yates shuffle. Returns a copy.\n */\nexport function sampleFiles(files: FileFingerprint[], count: number): FileFingerprint[] {\n if (count >= files.length) return [...files];\n const shuffled = [...files];\n for (let i = 0; i < count; i++) {\n const j = i + Math.floor(Math.random() * (shuffled.length - i));\n [shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!];\n }\n return shuffled.slice(0, count);\n}\n\n// ========== Types ==========\n\nexport interface FileFingerprint {\n path: string;\n size: number;\n cid?: string;\n mtime?: number;\n}\n\nexport interface SyncManifest {\n /**\n * Legacy file list field — kept REQUIRED for backward compatibility\n * with existing consumers (rsync.ts pipeline, computeDiff, e2e tests).\n *\n * Task 3.2: as of the G5.b manifest endpoint rewrite, the server now\n * also returns this same data under the `entries` field plus a fresh\n * `manifestSeq` cursor. New code should prefer `entries` + `manifestSeq`\n * but reading `files` continues to work — both arrays carry identical\n * data, they are two views of the same underlying snapshot.\n */\n files: FileFingerprint[];\n /**\n * Task 3.2 — canonical \"entries\" view of the same data as `files`.\n * Optional because clients constructing a manifest locally (e.g.\n * `buildManifestFromDB`) may omit it; the wire format from the server\n * always includes it.\n */\n entries?: FileFingerprint[];\n /**\n * Task 3.2 — server-provided changelog cursor at the moment the\n * manifest was assembled. After reading the manifest a client can\n * call `GET /sync/changes?since=manifestSeq` to fetch all subsequent\n * deltas without missing or replaying entries (CTE single-shot\n * snapshot — entries and manifestSeq come from the same point-in-time\n * read).\n *\n * Optional because client-constructed manifests have no server seq.\n * 0 is a valid value (empty changelog == nothing has been written\n * to this scope yet).\n */\n manifestSeq?: number;\n createdAt: number;\n}\n\nexport interface SyncDiffEntry {\n path: string;\n reason: \"added\" | \"modified\" | \"deleted\";\n}\n\nexport interface SyncDiff {\n entries: SyncDiffEntry[];\n stats: { added: number; modified: number; deleted: number; unchanged: number };\n}\n\nexport interface SyncProgress {\n phase: \"scanning\" | \"diffing\" | \"transferring\" | \"verifying\" | \"done\" | \"failed\";\n filesTotal: number;\n filesProcessed: number;\n bytesTotal: number;\n bytesTransferred: number;\n errors: Array<{ path: string; error: string }>;\n startedAt: number;\n updatedAt: number;\n}\n\nexport interface SyncResult {\n success: boolean;\n diff: SyncDiff;\n transferred: number;\n verified: boolean;\n errors: Array<{ path: string; error: string }>;\n durationMs: number;\n}\n\nexport interface SyncOptions {\n delete?: boolean;\n dryRun?: boolean;\n verify?: boolean;\n concurrency?: number;\n onProgress?: (progress: SyncProgress) => void;\n /** Filter function to exclude paths from sync. Return true to include. */\n pathFilter?: (path: string) => boolean;\n /**\n * If provided, fetch the src manifest from a remote server endpoint instead of\n * paginating through srcAfs. Useful when the remote side has a manifest endpoint.\n */\n srcFetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;\n /**\n * If provided, fetch the dst manifest from a remote server endpoint instead of\n * paginating through dstAfs. Useful when the remote side has a manifest endpoint.\n */\n dstFetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;\n /**\n * Phase 3: If provided, enable checkpoint-based resume-on-failure.\n * transferFiles() will persist progress to sync_checkpoint table and resume\n * from the last completed file if interrupted.\n */\n checkpointOptions?: {\n metadataDb: MetadataDB;\n direction: string;\n remoteUrl: string;\n basePath: string;\n };\n}\n\nexport interface BuildManifestOptions {\n /**\n * If provided, fetch manifest from a remote server endpoint instead of\n * paginating through the AFS. The `afs` parameter is unused when this is set.\n */\n fetchManifest?: (basePath: string, since?: number) => Promise<SyncManifest>;\n /** Only return files modified after this timestamp (ms). Passed to fetchManifest. */\n since?: number;\n /**\n * Phase 2: If provided (together with localRootPath + localScope), build the\n * manifest by calling reconcile() then querying _metadata.db directly.\n * This replaces the O(N) full stat+read+CID scan with an O(1) SQL query.\n */\n localMetadataDb?: MetadataDB;\n /** Root directory of the user's DID space (required when localMetadataDb is set). */\n localRootPath?: string;\n /** userDid / scope for the entries query (required when localMetadataDb is set). */\n localScope?: string;\n /**\n * Task 1.3: explicit opt-out — skip both the explicit DB params and the\n * metadata context attached by DIDSpace, and fall through to the legacy\n * AFS pagination path. Used for back-compat testing + rare callers that\n * must re-scan the filesystem from scratch.\n */\n skipLocalMetadataDb?: boolean;\n}\n\n// ========== Content helpers ==========\n\n/** Content-decoding helpers shared with the local metadata subscriber. */\nconst resolveContent = resolveAFSContent;\n\n// ========== fetchRemoteManifest ==========\n\n/**\n * Fetch a file manifest from a remote server's POST /api/space/manifest endpoint.\n *\n * This replaces the N-request paginated list + per-file CID read with a single\n * request that returns the complete manifest directly from the server's D1 index.\n *\n * @param baseUrl Server base URL (e.g. \"https://computer.afsd.dev\")\n * @param authHeader Authorization header value (e.g. \"Bearer <token>\")\n * @param basePath Path within the space (e.g. \"/\" or \"/blocklets/z1blk/\")\n * @param since Optional: only return files modified after this ms timestamp\n * @param fetchFn Optional fetch override (for testing)\n */\nexport async function fetchRemoteManifest(\n baseUrl: string,\n authHeader: string,\n basePath: string,\n since?: number,\n fetchFn: typeof globalThis.fetch = globalThis.fetch,\n): Promise<SyncManifest> {\n const url = `${baseUrl.replace(/\\/$/, \"\")}/api/space/manifest`;\n const body: { basePath: string; since?: number } = { basePath };\n if (since !== undefined) body.since = since;\n\n const response = await fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: authHeader,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `fetchRemoteManifest failed: ${response.status} ${response.statusText} from ${url}`,\n );\n }\n\n // Task 3.2: server response shape is `{ files, entries, manifestSeq, createdAt }`.\n // Both `files` and `entries` carry the same payload — `files` for legacy\n // consumers, `entries` for new code. Normalize so the returned object\n // always has both populated (even when talking to an older server that\n // only provides `files`), so downstream code can pick whichever it\n // prefers without nil-checking.\n const raw = (await response.json()) as SyncManifest;\n const files = raw.entries ?? raw.files ?? [];\n return {\n files,\n entries: files,\n manifestSeq: raw.manifestSeq,\n createdAt: raw.createdAt,\n };\n}\n\n// ========== buildManifest ==========\n\nconst BUILD_MANIFEST_PAGE_SIZE = 1000;\n\nexport async function buildManifest(\n afs: AFS,\n basePath: string,\n options?: BuildManifestOptions,\n): Promise<SyncManifest> {\n // Remote branch: use the manifest endpoint for a single-request manifest\n if (options?.fetchManifest) {\n return options.fetchManifest(basePath, options.since);\n }\n\n // Local DB branch (Phase 2, explicit): reconcile + SQL query instead of full AFS scan\n if (options?.localMetadataDb && options.localRootPath && options.localScope) {\n return buildManifestFromDB(\n options.localMetadataDb,\n options.localRootPath,\n options.localScope,\n basePath,\n );\n }\n\n // Task 1.3: Local DB branch (implicit) — if the caller didn't pass explicit\n // DB params but DIDSpace attached a metadata context to this AFS, use that\n // for the default O(1) SQL path. `skipLocalMetadataDb` opts out for\n // back-compat callers that need the legacy paginated scan.\n if (!options?.skipLocalMetadataDb) {\n const ctx = getMetadataContext(afs);\n if (ctx) {\n return buildManifestFromDB(ctx.metadataDb, ctx.userDir, ctx.scope, basePath);\n }\n }\n\n // Fallback: paginate through all entries to handle spaces with >1000 files.\n // AFS._list(\"/\") appends 3 system virtual entries (/.knowledge, /.meta, /.actions) to every\n // page AFTER the provider returns its limit-capped results. The provider (AFSFS or D1) returns\n // exactly BUILD_MANIFEST_PAGE_SIZE real entries; AFS then adds 3 virtual → page size = 1003.\n // We filter virtual entries and use the filtered count (always 1000 on full pages) for both\n // offset advancement and loop termination. This correctly handles both local AFSFS and\n // remote D1 cases (where the server also adds 3 virtual before the client AFS adds 3 more).\n const SYSTEM_VIRTUAL_PATHS = new Set([\"/.knowledge\", \"/.meta\", \"/.actions\"]);\n const allEntries: AFSEntry[] = [];\n let offset = 0;\n let pageEntries: AFSEntry[] = [];\n do {\n const batch = await afs.list(basePath, {\n maxDepth: 99,\n limit: BUILD_MANIFEST_PAGE_SIZE,\n offset,\n });\n pageEntries =\n basePath === \"/\" ? batch.data.filter((e) => !SYSTEM_VIRTUAL_PATHS.has(e.path)) : batch.data;\n allEntries.push(...pageEntries);\n offset += pageEntries.length;\n } while (pageEntries.length === BUILD_MANIFEST_PAGE_SIZE);\n\n const files: FileFingerprint[] = [];\n\n for (const entry of allEntries) {\n // Skip directories (childrenCount present means it's a container)\n if (entry.meta?.childrenCount != null) {\n continue;\n }\n\n let cid: string | undefined = entry.meta?.cid;\n\n // Compute CID for files lacking CID metadata (e.g. local AFSFS files).\n // Enables same-size content change detection via CID comparison.\n if (!cid) {\n try {\n const readResult = await afs.read(entry.path);\n const content = readResult.data?.content;\n if (content != null) {\n cid = await computeCID(resolveContent(content, readResult.data?.meta));\n }\n } catch {\n // File may have been deleted between list and read (race condition).\n // Skip CID computation — next sync will re-detect.\n }\n }\n\n files.push({\n path: entry.path,\n size: entry.meta?.size ?? 0,\n cid,\n mtime: entry.updatedAt?.getTime(),\n });\n }\n\n return { files, createdAt: Date.now() };\n}\n\n// ========== computeDiff ==========\n\nexport function computeDiff(\n src: SyncManifest,\n dst: SyncManifest,\n options?: Pick<SyncOptions, \"delete\">,\n): SyncDiff {\n const dstMap = new Map<string, FileFingerprint>();\n for (const f of dst.files) {\n dstMap.set(f.path, f);\n }\n\n const entries: SyncDiffEntry[] = [];\n let added = 0;\n let modified = 0;\n let unchanged = 0;\n const seenPaths = new Set<string>();\n\n for (const srcFile of src.files) {\n seenPaths.add(srcFile.path);\n const dstFile = dstMap.get(srcFile.path);\n\n if (!dstFile) {\n entries.push({ path: srcFile.path, reason: \"added\" });\n added++;\n continue;\n }\n\n if (isModified(srcFile, dstFile)) {\n entries.push({ path: srcFile.path, reason: \"modified\" });\n modified++;\n } else {\n unchanged++;\n }\n }\n\n let deleted = 0;\n if (options?.delete) {\n for (const dstFile of dst.files) {\n if (!seenPaths.has(dstFile.path)) {\n entries.push({ path: dstFile.path, reason: \"deleted\" });\n deleted++;\n }\n }\n }\n\n return { entries, stats: { added, modified, deleted, unchanged } };\n}\n\nfunction isModified(src: FileFingerprint, dst: FileFingerprint): boolean {\n // If both have CID, use content hash (most accurate)\n if (src.cid && dst.cid) {\n return src.cid !== dst.cid;\n }\n // Without CID, use size as the only reliable signal.\n // mtime is unreliable across backends (write creates new mtime).\n return src.size !== dst.size;\n}\n\n// ========== transferFiles ==========\n\nexport async function transferFiles(\n srcAfs: AFS,\n dstAfs: AFS,\n diff: SyncDiff,\n options?: SyncOptions,\n): Promise<Pick<SyncResult, \"transferred\" | \"errors\">> {\n const concurrency = options?.concurrency ?? 5;\n const dryRun = options?.dryRun ?? false;\n const errors: Array<{ path: string; error: string }> = [];\n let transferred = 0;\n\n // ── Phase 3: Checkpoint setup ─────────────────────────────────\n let sessionId: string | null = null;\n const completedPaths = new Set<string>();\n const checkpointOpts = options?.checkpointOptions;\n\n if (checkpointOpts && !dryRun) {\n const { metadataDb, direction, remoteUrl, basePath } = checkpointOpts;\n const existing = await metadataDb.getActiveCheckpoint(direction, remoteUrl, basePath);\n\n if (existing) {\n sessionId = existing.session_id;\n try {\n const parsed = JSON.parse(existing.completed);\n if (Array.isArray(parsed)) {\n for (const p of parsed) {\n if (typeof p === \"string\") completedPaths.add(p);\n }\n }\n } catch {\n // Corrupt completed JSON — treat as empty (re-transfer all)\n await metadataDb.updateCheckpointCompleted(sessionId, []);\n }\n } else {\n sessionId = crypto.randomUUID();\n await metadataDb.createCheckpoint({\n session_id: sessionId,\n direction,\n remote_url: remoteUrl,\n base_path: basePath,\n status: \"active\",\n diff_json: JSON.stringify(diff.entries),\n completed: \"[]\",\n created_at: Date.now(),\n updated_at: Date.now(),\n });\n }\n }\n // ── End checkpoint setup ──────────────────────────────────────\n\n const progress: SyncProgress = {\n phase: \"transferring\",\n filesTotal: diff.entries.length,\n filesProcessed: 0,\n bytesTotal: 0,\n bytesTransferred: 0,\n errors: [],\n startedAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n const toTransfer = diff.entries.filter((e) => e.reason === \"added\" || e.reason === \"modified\");\n const toDelete = diff.entries.filter((e) => e.reason === \"deleted\");\n\n // Transfer files with concurrency limit\n const transferOne = async (entry: SyncDiffEntry): Promise<void> => {\n // Skip already-completed paths on checkpoint resume\n if (completedPaths.has(entry.path)) {\n transferred++;\n progress.filesProcessed++;\n progress.updatedAt = Date.now();\n options?.onProgress?.(progress);\n return;\n }\n\n try {\n if (dryRun) return;\n const readResult = await srcAfs.read(entry.path);\n const rawContent = readResult.data?.content;\n if (rawContent == null) {\n throw new Error(`Failed to read ${entry.path}: no content`);\n }\n const content = resolveContent(rawContent, readResult.data?.meta);\n await dstAfs.write(entry.path, { content });\n transferred++;\n progress.bytesTransferred += readResult.data?.meta?.size ?? 0;\n\n // Persist completed path to checkpoint\n if (sessionId && checkpointOpts) {\n completedPaths.add(entry.path);\n await checkpointOpts.metadataDb.updateCheckpointCompleted(sessionId, [...completedPaths]);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n errors.push({ path: entry.path, error: msg });\n } finally {\n progress.filesProcessed++;\n progress.updatedAt = Date.now();\n progress.errors = [...errors];\n options?.onProgress?.(progress);\n }\n };\n\n // Simple concurrency limiter\n await runWithConcurrency(\n toTransfer.map((e) => () => transferOne(e)),\n concurrency,\n );\n\n // Handle deletions\n for (const entry of toDelete) {\n if (dryRun) continue;\n try {\n await dstAfs.delete(entry.path);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n errors.push({ path: entry.path, error: msg });\n } finally {\n progress.filesProcessed++;\n progress.updatedAt = Date.now();\n progress.errors = [...errors];\n options?.onProgress?.(progress);\n }\n }\n\n // Mark checkpoint done after all transfers complete\n if (sessionId && checkpointOpts) {\n await checkpointOpts.metadataDb.completeCheckpoint(sessionId);\n }\n\n return { transferred, errors };\n}\n\nexport async function runWithConcurrency(\n tasks: Array<() => Promise<void>>,\n limit: number,\n): Promise<void> {\n const executing = new Set<Promise<void>>();\n for (const task of tasks) {\n const p = task().then(() => {\n executing.delete(p);\n });\n executing.add(p);\n if (executing.size >= limit) {\n await Promise.race(executing);\n }\n }\n await Promise.all(executing);\n}\n\n// ========== verifySync ==========\n\n/**\n * Verify sync by sampling transferred files and comparing CIDs.\n *\n * Phase 3 change: instead of full buildManifest on dst, we sample\n * max(1, min(100, ceil(transferred/10))) files from the transferred set,\n * read each from dst, compute its CID, and compare against srcManifest CID.\n *\n * @param dstAfs Destination AFS to read files from\n * @param transferredFiles FileFingerprints from srcManifest for transferred files\n */\nexport async function verifySync(\n dstAfs: AFS,\n transferredFiles: FileFingerprint[],\n): Promise<boolean> {\n if (transferredFiles.length === 0) return true;\n\n const sampleCount = computeSampleCount(transferredFiles.length);\n const sample = sampleFiles(transferredFiles, sampleCount);\n\n for (const srcFile of sample) {\n // Only verify files that have a known CID — can't verify without reference\n if (!srcFile.cid) continue;\n\n try {\n const readResult = await dstAfs.read(srcFile.path);\n const rawContent = readResult.data?.content;\n if (rawContent == null) return false;\n\n const content = resolveContent(rawContent, readResult.data?.meta);\n const dstCid = await computeCID(content);\n if (dstCid !== srcFile.cid) return false;\n } catch {\n return false;\n }\n }\n\n return true;\n}\n\n// ========== syncDIDSpace ==========\n\nexport async function syncDIDSpace(\n srcAfs: AFS,\n dstAfs: AFS,\n basePath: string,\n options?: SyncOptions,\n): Promise<SyncResult> {\n const startTime = Date.now();\n const verify = options?.verify ?? true;\n\n const progress: SyncProgress = {\n phase: \"scanning\",\n filesTotal: 0,\n filesProcessed: 0,\n bytesTotal: 0,\n bytesTransferred: 0,\n errors: [],\n startedAt: startTime,\n updatedAt: startTime,\n };\n options?.onProgress?.(progress);\n\n // Phase 1: Snapshot (use remote manifest fetchers when provided)\n let [srcManifest, dstManifest] = await Promise.all([\n buildManifest(\n srcAfs,\n basePath,\n options?.srcFetchManifest ? { fetchManifest: options.srcFetchManifest } : undefined,\n ),\n buildManifest(\n dstAfs,\n basePath,\n options?.dstFetchManifest ? { fetchManifest: options.dstFetchManifest } : undefined,\n ),\n ]);\n\n // Apply path filter if provided (e.g. exclude vault/db from sync)\n if (options?.pathFilter) {\n const pf = options.pathFilter;\n srcManifest = { ...srcManifest, files: srcManifest.files.filter((f) => pf(f.path)) };\n dstManifest = { ...dstManifest, files: dstManifest.files.filter((f) => pf(f.path)) };\n }\n\n // Phase 2: Diff\n progress.phase = \"diffing\";\n progress.updatedAt = Date.now();\n options?.onProgress?.(progress);\n\n const diff = computeDiff(srcManifest, dstManifest, options);\n\n // Phase 3: Transfer\n progress.phase = \"transferring\";\n progress.filesTotal = diff.entries.filter((e) => e.reason !== \"deleted\").length;\n progress.updatedAt = Date.now();\n options?.onProgress?.(progress);\n\n const { transferred, errors } = await transferFiles(srcAfs, dstAfs, diff, options);\n\n // Phase 4: Verify — sample the transferred files\n let verified = false;\n if (verify && !options?.dryRun) {\n progress.phase = \"verifying\";\n progress.updatedAt = Date.now();\n options?.onProgress?.(progress);\n\n // Build the list of transferred file fingerprints from srcManifest\n const transferredPaths = new Set(\n diff.entries\n .filter((e) => e.reason === \"added\" || e.reason === \"modified\")\n .map((e) => e.path),\n );\n const transferredFiles = srcManifest.files.filter((f) => transferredPaths.has(f.path));\n verified = await verifySync(dstAfs, transferredFiles);\n }\n\n progress.phase = errors.length > 0 ? \"failed\" : \"done\";\n progress.updatedAt = Date.now();\n options?.onProgress?.(progress);\n\n return {\n success: errors.length === 0,\n diff,\n transferred,\n verified,\n errors,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ========== buildManifestFromDB (Phase 2) ==========\n\n/**\n * Build a manifest purely from `_metadata.db` (the index is the source of\n * truth after the Node backend moved to content-addressed storage, Phase 3).\n *\n * The old `reconcile({ rootPath })` FS-scan prelude is GONE: there is no\n * verbatim file tree to walk anymore — content lives at `objects/{cid}` and the\n * index already reflects every write (the provider writes entries atomically).\n * Reconciling against the CAS layout would mis-scan internal object files.\n * `rootPath` is retained in the signature for caller compatibility but unused.\n */\nasync function buildManifestFromDB(\n metadataDb: MetadataDB,\n _rootPath: string,\n scope: string,\n basePath: string,\n): Promise<SyncManifest> {\n // Convert AFS basePath to DB path prefix\n // AFS: \"/\" or \"/appDid\" → DB: undefined or \"appDid/\"\n const dbBasePath = afsBasePathToDbPrefix(basePath);\n\n const dbFiles = await metadataDb.getFiles(scope, dbBasePath);\n\n const files: FileFingerprint[] = dbFiles.map((row) => ({\n path: `/${row.path}`, // restore leading slash for AFS path convention\n size: row.size,\n cid: row.cid ?? undefined,\n mtime: row.mtime,\n }));\n\n return { files, createdAt: Date.now() };\n}\n\n/**\n * Convert an AFS basePath to a MetadataDB path prefix.\n *\n * Examples:\n * \"/\" → undefined (match all)\n * \"/appDid\" → \"appDid/\"\n * \"/appDid/\" → \"appDid/\"\n */\nfunction afsBasePathToDbPrefix(basePath: string): string | undefined {\n const stripped = basePath.replace(/^\\/+/, \"\").replace(/\\/*$/, \"\");\n if (!stripped) return undefined;\n return `${stripped}/`;\n}\n"],"mappings":"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"}
|