@syncular/server 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +773 -13
- package/dist/admin.d.ts +136 -0
- package/dist/admin.js +168 -0
- package/dist/blob-handlers.d.ts +69 -0
- package/dist/blob-handlers.js +245 -0
- package/dist/blob-store.d.ts +111 -0
- package/dist/blob-store.js +0 -0
- package/dist/content-encoding.d.ts +27 -0
- package/dist/content-encoding.js +69 -0
- package/dist/context.d.ts +151 -0
- package/dist/context.js +21 -0
- package/dist/crdt-merger.d.ts +28 -0
- package/dist/crdt-merger.js +11 -0
- package/dist/d1-storage.d.ts +50 -0
- package/dist/d1-storage.js +0 -0
- package/dist/errors.d.ts +35 -9
- package/dist/errors.js +220 -11
- package/dist/events-ring.d.ts +55 -0
- package/dist/events-ring.js +96 -0
- package/dist/events.d.ts +231 -0
- package/dist/events.js +22 -0
- package/dist/frame-bytes.d.ts +20 -0
- package/dist/frame-bytes.js +75 -0
- package/dist/handler.d.ts +9 -0
- package/dist/handler.js +490 -0
- package/dist/index.d.ts +42 -33
- package/dist/index.js +45 -27
- package/dist/lease-store.d.ts +50 -0
- package/dist/lease-store.js +0 -0
- package/dist/pg-executor-pglite.d.ts +27 -0
- package/dist/pg-executor-pglite.js +33 -0
- package/dist/pg-executor.d.ts +67 -0
- package/dist/pg-executor.js +56 -0
- package/dist/postgres-fanout.d.ts +83 -0
- package/dist/postgres-fanout.js +71 -0
- package/dist/postgres-storage.d.ts +69 -0
- package/dist/postgres-storage.js +766 -0
- package/dist/prune.d.ts +24 -39
- package/dist/prune.js +44 -146
- package/dist/pull.d.ts +42 -67
- package/dist/pull.js +387 -1187
- package/dist/push.d.ts +31 -66
- package/dist/push.js +406 -655
- package/dist/realtime.d.ts +197 -0
- package/dist/realtime.js +848 -0
- package/dist/relational-rows.d.ts +229 -0
- package/dist/relational-rows.js +514 -0
- package/dist/s3-blob-store.d.ts +142 -0
- package/dist/s3-blob-store.js +458 -0
- package/dist/s3-segment-store.d.ts +95 -0
- package/dist/s3-segment-store.js +380 -0
- package/dist/schema.d.ts +82 -286
- package/dist/schema.js +133 -9
- package/dist/scopes.d.ts +48 -0
- package/dist/scopes.js +98 -0
- package/dist/segment-download.d.ts +14 -0
- package/dist/segment-download.js +125 -0
- package/dist/segment-store.d.ts +81 -0
- package/dist/segment-store.js +63 -0
- package/dist/signed-url.d.ts +139 -0
- package/dist/signed-url.js +143 -0
- package/dist/sigv4.d.ts +97 -0
- package/dist/sigv4.js +161 -0
- package/dist/sqlite-blob-store.d.ts +22 -0
- package/dist/sqlite-blob-store.js +86 -0
- package/dist/sqlite-dialect.d.ts +101 -0
- package/dist/sqlite-dialect.js +250 -0
- package/dist/sqlite-image.d.ts +27 -0
- package/dist/sqlite-image.js +83 -0
- package/dist/sqlite-lease-store.d.ts +21 -0
- package/dist/sqlite-lease-store.js +94 -0
- package/dist/sqlite-segment-store.d.ts +25 -0
- package/dist/sqlite-segment-store.js +115 -0
- package/dist/sqlite-storage.d.ts +47 -0
- package/dist/sqlite-storage.js +477 -0
- package/dist/storage.d.ts +252 -0
- package/dist/storage.js +1 -0
- package/dist/validate.d.ts +83 -0
- package/dist/validate.js +44 -0
- package/package.json +17 -325
- package/src/admin.ts +312 -0
- package/src/blob-handlers.ts +340 -0
- package/src/blob-store.ts +0 -0
- package/src/content-encoding.ts +94 -0
- package/src/context.ts +176 -0
- package/src/crdt-merger.ts +33 -0
- package/src/d1-storage.ts +0 -0
- package/src/errors.ts +241 -19
- package/src/events-ring.ts +116 -0
- package/src/events.ts +291 -0
- package/src/frame-bytes.ts +94 -0
- package/src/handler.ts +678 -0
- package/src/index.ts +44 -27
- package/src/lease-store.ts +0 -0
- package/src/pg-executor-pglite.ts +63 -0
- package/src/pg-executor.ts +87 -0
- package/src/postgres-fanout.ts +143 -0
- package/src/postgres-storage.ts +1182 -0
- package/src/prune.ts +63 -289
- package/src/pull.ts +505 -1805
- package/src/push.ts +623 -1006
- package/src/realtime.ts +1082 -0
- package/src/relational-rows.ts +633 -0
- package/src/s3-blob-store.ts +616 -0
- package/src/s3-segment-store.ts +519 -0
- package/src/schema.ts +242 -281
- package/src/scopes.ts +127 -0
- package/src/segment-download.ts +169 -0
- package/src/segment-store.ts +154 -0
- package/src/signed-url.ts +308 -0
- package/src/sigv4.ts +267 -0
- package/src/sqlite-blob-store.ts +125 -0
- package/src/sqlite-dialect.ts +337 -0
- package/src/sqlite-image.ts +124 -0
- package/src/sqlite-lease-store.ts +144 -0
- package/src/sqlite-segment-store.ts +204 -0
- package/src/sqlite-storage.ts +792 -0
- package/src/storage.ts +316 -0
- package/src/validate.ts +123 -0
- package/dist/auth-leases.d.ts +0 -85
- package/dist/auth-leases.d.ts.map +0 -1
- package/dist/auth-leases.js +0 -368
- package/dist/auth-leases.js.map +0 -1
- package/dist/better-sqlite3.d.ts +0 -22
- package/dist/better-sqlite3.d.ts.map +0 -1
- package/dist/better-sqlite3.js +0 -16
- package/dist/better-sqlite3.js.map +0 -1
- package/dist/blobs/access.d.ts +0 -35
- package/dist/blobs/access.d.ts.map +0 -1
- package/dist/blobs/access.js +0 -149
- package/dist/blobs/access.js.map +0 -1
- package/dist/blobs/adapters/database.d.ts +0 -100
- package/dist/blobs/adapters/database.d.ts.map +0 -1
- package/dist/blobs/adapters/database.js +0 -247
- package/dist/blobs/adapters/database.js.map +0 -1
- package/dist/blobs/index.d.ts +0 -9
- package/dist/blobs/index.d.ts.map +0 -1
- package/dist/blobs/index.js +0 -9
- package/dist/blobs/index.js.map +0 -1
- package/dist/blobs/manager.d.ts +0 -220
- package/dist/blobs/manager.d.ts.map +0 -1
- package/dist/blobs/manager.js +0 -625
- package/dist/blobs/manager.js.map +0 -1
- package/dist/blobs/migrate.d.ts +0 -27
- package/dist/blobs/migrate.d.ts.map +0 -1
- package/dist/blobs/migrate.js +0 -127
- package/dist/blobs/migrate.js.map +0 -1
- package/dist/blobs/types.d.ts +0 -58
- package/dist/blobs/types.d.ts.map +0 -1
- package/dist/blobs/types.js +0 -5
- package/dist/blobs/types.js.map +0 -1
- package/dist/bun-sqlite.d.ts +0 -19
- package/dist/bun-sqlite.d.ts.map +0 -1
- package/dist/bun-sqlite.js +0 -19
- package/dist/bun-sqlite.js.map +0 -1
- package/dist/clients.d.ts +0 -15
- package/dist/clients.d.ts.map +0 -1
- package/dist/clients.js +0 -7
- package/dist/clients.js.map +0 -1
- package/dist/cloudflare/durable-object.d.ts +0 -93
- package/dist/cloudflare/durable-object.d.ts.map +0 -1
- package/dist/cloudflare/durable-object.js +0 -210
- package/dist/cloudflare/durable-object.js.map +0 -1
- package/dist/cloudflare/index.d.ts +0 -22
- package/dist/cloudflare/index.d.ts.map +0 -1
- package/dist/cloudflare/index.js +0 -22
- package/dist/cloudflare/index.js.map +0 -1
- package/dist/cloudflare/r2.d.ts +0 -180
- package/dist/cloudflare/r2.d.ts.map +0 -1
- package/dist/cloudflare/r2.js +0 -258
- package/dist/cloudflare/r2.js.map +0 -1
- package/dist/cloudflare/scope-cache.d.ts +0 -54
- package/dist/cloudflare/scope-cache.d.ts.map +0 -1
- package/dist/cloudflare/scope-cache.js +0 -223
- package/dist/cloudflare/scope-cache.js.map +0 -1
- package/dist/cloudflare/sentry.d.ts +0 -47
- package/dist/cloudflare/sentry.d.ts.map +0 -1
- package/dist/cloudflare/sentry.js +0 -163
- package/dist/cloudflare/sentry.js.map +0 -1
- package/dist/cloudflare/worker.d.ts +0 -46
- package/dist/cloudflare/worker.d.ts.map +0 -1
- package/dist/cloudflare/worker.js +0 -63
- package/dist/cloudflare/worker.js.map +0 -1
- package/dist/commit-integrity.d.ts +0 -38
- package/dist/commit-integrity.d.ts.map +0 -1
- package/dist/commit-integrity.js +0 -260
- package/dist/commit-integrity.js.map +0 -1
- package/dist/compaction.d.ts +0 -27
- package/dist/compaction.d.ts.map +0 -1
- package/dist/compaction.js +0 -49
- package/dist/compaction.js.map +0 -1
- package/dist/crdt-yjs/index.d.ts +0 -99
- package/dist/crdt-yjs/index.d.ts.map +0 -1
- package/dist/crdt-yjs/index.js +0 -629
- package/dist/crdt-yjs/index.js.map +0 -1
- package/dist/d1.d.ts +0 -13
- package/dist/d1.d.ts.map +0 -1
- package/dist/d1.js +0 -14
- package/dist/d1.js.map +0 -1
- package/dist/dialect/base.d.ts +0 -93
- package/dist/dialect/base.d.ts.map +0 -1
- package/dist/dialect/base.js +0 -181
- package/dist/dialect/base.js.map +0 -1
- package/dist/dialect/helpers.d.ts +0 -16
- package/dist/dialect/helpers.d.ts.map +0 -1
- package/dist/dialect/helpers.js +0 -83
- package/dist/dialect/helpers.js.map +0 -1
- package/dist/dialect/index.d.ts +0 -7
- package/dist/dialect/index.d.ts.map +0 -1
- package/dist/dialect/index.js +0 -7
- package/dist/dialect/index.js.map +0 -1
- package/dist/dialect/types.d.ts +0 -187
- package/dist/dialect/types.d.ts.map +0 -1
- package/dist/dialect/types.js +0 -8
- package/dist/dialect/types.js.map +0 -1
- package/dist/encrypted-crdt.d.ts +0 -67
- package/dist/encrypted-crdt.d.ts.map +0 -1
- package/dist/encrypted-crdt.js +0 -426
- package/dist/encrypted-crdt.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/filesystem/index.d.ts +0 -44
- package/dist/filesystem/index.d.ts.map +0 -1
- package/dist/filesystem/index.js +0 -164
- package/dist/filesystem/index.js.map +0 -1
- package/dist/handlers/collection.d.ts +0 -20
- package/dist/handlers/collection.d.ts.map +0 -1
- package/dist/handlers/collection.js +0 -42
- package/dist/handlers/collection.js.map +0 -1
- package/dist/handlers/create-handler.d.ts +0 -156
- package/dist/handlers/create-handler.d.ts.map +0 -1
- package/dist/handlers/create-handler.js +0 -626
- package/dist/handlers/create-handler.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -4
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -4
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/types.d.ts +0 -295
- package/dist/handlers/types.d.ts.map +0 -1
- package/dist/handlers/types.js +0 -2
- package/dist/handlers/types.js.map +0 -1
- package/dist/helpers/conflict.d.ts +0 -52
- package/dist/helpers/conflict.d.ts.map +0 -1
- package/dist/helpers/conflict.js +0 -49
- package/dist/helpers/conflict.js.map +0 -1
- package/dist/helpers/emitted-change.d.ts +0 -56
- package/dist/helpers/emitted-change.d.ts.map +0 -1
- package/dist/helpers/emitted-change.js +0 -46
- package/dist/helpers/emitted-change.js.map +0 -1
- package/dist/helpers/index.d.ts +0 -12
- package/dist/helpers/index.d.ts.map +0 -1
- package/dist/helpers/index.js +0 -12
- package/dist/helpers/index.js.map +0 -1
- package/dist/helpers/paginate.d.ts +0 -49
- package/dist/helpers/paginate.d.ts.map +0 -1
- package/dist/helpers/paginate.js +0 -54
- package/dist/helpers/paginate.js.map +0 -1
- package/dist/helpers/scope-authorization.d.ts +0 -7
- package/dist/helpers/scope-authorization.d.ts.map +0 -1
- package/dist/helpers/scope-authorization.js +0 -19
- package/dist/helpers/scope-authorization.js.map +0 -1
- package/dist/helpers/scope-commit-index.d.ts +0 -12
- package/dist/helpers/scope-commit-index.d.ts.map +0 -1
- package/dist/helpers/scope-commit-index.js +0 -35
- package/dist/helpers/scope-commit-index.js.map +0 -1
- package/dist/helpers/scope-strings.d.ts +0 -74
- package/dist/helpers/scope-strings.d.ts.map +0 -1
- package/dist/helpers/scope-strings.js +0 -82
- package/dist/helpers/scope-strings.js.map +0 -1
- package/dist/hono/api-key-auth.d.ts +0 -49
- package/dist/hono/api-key-auth.d.ts.map +0 -1
- package/dist/hono/api-key-auth.js +0 -108
- package/dist/hono/api-key-auth.js.map +0 -1
- package/dist/hono/audit-redaction.d.ts +0 -20
- package/dist/hono/audit-redaction.d.ts.map +0 -1
- package/dist/hono/audit-redaction.js +0 -85
- package/dist/hono/audit-redaction.js.map +0 -1
- package/dist/hono/blobs.d.ts +0 -75
- package/dist/hono/blobs.d.ts.map +0 -1
- package/dist/hono/blobs.js +0 -586
- package/dist/hono/blobs.js.map +0 -1
- package/dist/hono/console/gateway.d.ts +0 -42
- package/dist/hono/console/gateway.d.ts.map +0 -1
- package/dist/hono/console/gateway.js +0 -2158
- package/dist/hono/console/gateway.js.map +0 -1
- package/dist/hono/console/live-auth.d.ts +0 -7
- package/dist/hono/console/live-auth.d.ts.map +0 -1
- package/dist/hono/console/live-auth.js +0 -39
- package/dist/hono/console/live-auth.js.map +0 -1
- package/dist/hono/console/route-descriptor.d.ts +0 -6
- package/dist/hono/console/route-descriptor.d.ts.map +0 -1
- package/dist/hono/console/route-descriptor.js +0 -16
- package/dist/hono/console/route-descriptor.js.map +0 -1
- package/dist/hono/console/routes/api-keys.d.ts +0 -8
- package/dist/hono/console/routes/api-keys.d.ts.map +0 -1
- package/dist/hono/console/routes/api-keys.js +0 -575
- package/dist/hono/console/routes/api-keys.js.map +0 -1
- package/dist/hono/console/routes/clients.d.ts +0 -8
- package/dist/hono/console/routes/clients.d.ts.map +0 -1
- package/dist/hono/console/routes/clients.js +0 -322
- package/dist/hono/console/routes/clients.js.map +0 -1
- package/dist/hono/console/routes/commits.d.ts +0 -8
- package/dist/hono/console/routes/commits.d.ts.map +0 -1
- package/dist/hono/console/routes/commits.js +0 -882
- package/dist/hono/console/routes/commits.js.map +0 -1
- package/dist/hono/console/routes/context.d.ts +0 -188
- package/dist/hono/console/routes/context.d.ts.map +0 -1
- package/dist/hono/console/routes/context.js +0 -633
- package/dist/hono/console/routes/context.js.map +0 -1
- package/dist/hono/console/routes/events.d.ts +0 -8
- package/dist/hono/console/routes/events.d.ts.map +0 -1
- package/dist/hono/console/routes/events.js +0 -501
- package/dist/hono/console/routes/events.js.map +0 -1
- package/dist/hono/console/routes/maintenance.d.ts +0 -8
- package/dist/hono/console/routes/maintenance.d.ts.map +0 -1
- package/dist/hono/console/routes/maintenance.js +0 -341
- package/dist/hono/console/routes/maintenance.js.map +0 -1
- package/dist/hono/console/routes/shared.d.ts +0 -174
- package/dist/hono/console/routes/shared.d.ts.map +0 -1
- package/dist/hono/console/routes/shared.js +0 -583
- package/dist/hono/console/routes/shared.js.map +0 -1
- package/dist/hono/console/routes/stats.d.ts +0 -8
- package/dist/hono/console/routes/stats.d.ts.map +0 -1
- package/dist/hono/console/routes/stats.js +0 -295
- package/dist/hono/console/routes/stats.js.map +0 -1
- package/dist/hono/console/routes/storage.d.ts +0 -8
- package/dist/hono/console/routes/storage.d.ts.map +0 -1
- package/dist/hono/console/routes/storage.js +0 -121
- package/dist/hono/console/routes/storage.js.map +0 -1
- package/dist/hono/console/routes.d.ts +0 -36
- package/dist/hono/console/routes.d.ts.map +0 -1
- package/dist/hono/console/routes.js +0 -112
- package/dist/hono/console/routes.js.map +0 -1
- package/dist/hono/console/schema-errors.d.ts +0 -2
- package/dist/hono/console/schema-errors.d.ts.map +0 -1
- package/dist/hono/console/schema-errors.js +0 -17
- package/dist/hono/console/schema-errors.js.map +0 -1
- package/dist/hono/console/schemas.d.ts +0 -1515
- package/dist/hono/console/schemas.d.ts.map +0 -1
- package/dist/hono/console/schemas.js +0 -661
- package/dist/hono/console/schemas.js.map +0 -1
- package/dist/hono/console/types.d.ts +0 -213
- package/dist/hono/console/types.d.ts.map +0 -1
- package/dist/hono/console/types.js +0 -2
- package/dist/hono/console/types.js.map +0 -1
- package/dist/hono/console/ui.d.ts +0 -38
- package/dist/hono/console/ui.d.ts.map +0 -1
- package/dist/hono/console/ui.js +0 -43
- package/dist/hono/console/ui.js.map +0 -1
- package/dist/hono/create-server.d.ts +0 -71
- package/dist/hono/create-server.d.ts.map +0 -1
- package/dist/hono/create-server.js +0 -121
- package/dist/hono/create-server.js.map +0 -1
- package/dist/hono/errors.d.ts +0 -13
- package/dist/hono/errors.d.ts.map +0 -1
- package/dist/hono/errors.js +0 -21
- package/dist/hono/errors.js.map +0 -1
- package/dist/hono/index.d.ts +0 -20
- package/dist/hono/index.d.ts.map +0 -1
- package/dist/hono/index.js +0 -31
- package/dist/hono/index.js.map +0 -1
- package/dist/hono/openapi.d.ts +0 -72
- package/dist/hono/openapi.d.ts.map +0 -1
- package/dist/hono/openapi.js +0 -99
- package/dist/hono/openapi.js.map +0 -1
- package/dist/hono/proxy/connection-manager.d.ts +0 -78
- package/dist/hono/proxy/connection-manager.d.ts.map +0 -1
- package/dist/hono/proxy/connection-manager.js +0 -251
- package/dist/hono/proxy/connection-manager.js.map +0 -1
- package/dist/hono/proxy/index.d.ts +0 -8
- package/dist/hono/proxy/index.d.ts.map +0 -1
- package/dist/hono/proxy/index.js +0 -8
- package/dist/hono/proxy/index.js.map +0 -1
- package/dist/hono/proxy/routes.d.ts +0 -86
- package/dist/hono/proxy/routes.d.ts.map +0 -1
- package/dist/hono/proxy/routes.js +0 -183
- package/dist/hono/proxy/routes.js.map +0 -1
- package/dist/hono/rate-limit.d.ts +0 -101
- package/dist/hono/rate-limit.d.ts.map +0 -1
- package/dist/hono/rate-limit.js +0 -184
- package/dist/hono/rate-limit.js.map +0 -1
- package/dist/hono/realtime-sync-packs.d.ts +0 -43
- package/dist/hono/realtime-sync-packs.d.ts.map +0 -1
- package/dist/hono/realtime-sync-packs.js +0 -219
- package/dist/hono/realtime-sync-packs.js.map +0 -1
- package/dist/hono/routes/audit.d.ts +0 -12
- package/dist/hono/routes/audit.d.ts.map +0 -1
- package/dist/hono/routes/audit.js +0 -385
- package/dist/hono/routes/audit.js.map +0 -1
- package/dist/hono/routes/auth-leases.d.ts +0 -8
- package/dist/hono/routes/auth-leases.d.ts.map +0 -1
- package/dist/hono/routes/auth-leases.js +0 -88
- package/dist/hono/routes/auth-leases.js.map +0 -1
- package/dist/hono/routes/combined.d.ts +0 -8
- package/dist/hono/routes/combined.d.ts.map +0 -1
- package/dist/hono/routes/combined.js +0 -392
- package/dist/hono/routes/combined.js.map +0 -1
- package/dist/hono/routes/context.d.ts +0 -314
- package/dist/hono/routes/context.d.ts.map +0 -1
- package/dist/hono/routes/context.js +0 -1147
- package/dist/hono/routes/context.js.map +0 -1
- package/dist/hono/routes/health.d.ts +0 -8
- package/dist/hono/routes/health.d.ts.map +0 -1
- package/dist/hono/routes/health.js +0 -16
- package/dist/hono/routes/health.js.map +0 -1
- package/dist/hono/routes/realtime.d.ts +0 -9
- package/dist/hono/routes/realtime.d.ts.map +0 -1
- package/dist/hono/routes/realtime.js +0 -626
- package/dist/hono/routes/realtime.js.map +0 -1
- package/dist/hono/routes/shared.d.ts +0 -689
- package/dist/hono/routes/shared.d.ts.map +0 -1
- package/dist/hono/routes/shared.js +0 -972
- package/dist/hono/routes/shared.js.map +0 -1
- package/dist/hono/routes/snapshots.d.ts +0 -10
- package/dist/hono/routes/snapshots.d.ts.map +0 -1
- package/dist/hono/routes/snapshots.js +0 -281
- package/dist/hono/routes/snapshots.js.map +0 -1
- package/dist/hono/routes.d.ts +0 -19
- package/dist/hono/routes.d.ts.map +0 -1
- package/dist/hono/routes.js +0 -37
- package/dist/hono/routes.js.map +0 -1
- package/dist/hono/validation.d.ts +0 -5
- package/dist/hono/validation.d.ts.map +0 -1
- package/dist/hono/validation.js +0 -47
- package/dist/hono/validation.js.map +0 -1
- package/dist/hono/websocket-origin.d.ts +0 -9
- package/dist/hono/websocket-origin.d.ts.map +0 -1
- package/dist/hono/websocket-origin.js +0 -96
- package/dist/hono/websocket-origin.js.map +0 -1
- package/dist/hono/ws.d.ts +0 -341
- package/dist/hono/ws.d.ts.map +0 -1
- package/dist/hono/ws.js +0 -711
- package/dist/hono/ws.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/libsql.d.ts +0 -29
- package/dist/libsql.d.ts.map +0 -1
- package/dist/libsql.js +0 -25
- package/dist/libsql.js.map +0 -1
- package/dist/migrate.d.ts +0 -14
- package/dist/migrate.d.ts.map +0 -1
- package/dist/migrate.js +0 -13
- package/dist/migrate.js.map +0 -1
- package/dist/neon.d.ts +0 -21
- package/dist/neon.d.ts.map +0 -1
- package/dist/neon.js +0 -22
- package/dist/neon.js.map +0 -1
- package/dist/notify.d.ts +0 -77
- package/dist/notify.d.ts.map +0 -1
- package/dist/notify.js +0 -233
- package/dist/notify.js.map +0 -1
- package/dist/pglite.d.ts +0 -37
- package/dist/pglite.d.ts.map +0 -1
- package/dist/pglite.js +0 -37
- package/dist/pglite.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/index.d.ts.map +0 -1
- package/dist/plugins/index.js +0 -2
- package/dist/plugins/index.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -73
- package/dist/plugins/types.d.ts.map +0 -1
- package/dist/plugins/types.js +0 -30
- package/dist/plugins/types.js.map +0 -1
- package/dist/postgres/index.d.ts +0 -68
- package/dist/postgres/index.d.ts.map +0 -1
- package/dist/postgres/index.js +0 -924
- package/dist/postgres/index.js.map +0 -1
- package/dist/proxy/collection.d.ts +0 -7
- package/dist/proxy/collection.d.ts.map +0 -1
- package/dist/proxy/collection.js +0 -6
- package/dist/proxy/collection.js.map +0 -1
- package/dist/proxy/handler.d.ts +0 -42
- package/dist/proxy/handler.d.ts.map +0 -1
- package/dist/proxy/handler.js +0 -102
- package/dist/proxy/handler.js.map +0 -1
- package/dist/proxy/index.d.ts +0 -9
- package/dist/proxy/index.d.ts.map +0 -1
- package/dist/proxy/index.js +0 -14
- package/dist/proxy/index.js.map +0 -1
- package/dist/proxy/mutation-detector.d.ts +0 -35
- package/dist/proxy/mutation-detector.d.ts.map +0 -1
- package/dist/proxy/mutation-detector.js +0 -246
- package/dist/proxy/mutation-detector.js.map +0 -1
- package/dist/proxy/oplog.d.ts +0 -30
- package/dist/proxy/oplog.d.ts.map +0 -1
- package/dist/proxy/oplog.js +0 -137
- package/dist/proxy/oplog.js.map +0 -1
- package/dist/proxy/types.d.ts +0 -44
- package/dist/proxy/types.d.ts.map +0 -1
- package/dist/proxy/types.js +0 -7
- package/dist/proxy/types.js.map +0 -1
- package/dist/prune.d.ts.map +0 -1
- package/dist/prune.js.map +0 -1
- package/dist/pull.d.ts.map +0 -1
- package/dist/pull.js.map +0 -1
- package/dist/push.d.ts.map +0 -1
- package/dist/push.js.map +0 -1
- package/dist/realtime/in-memory.d.ts +0 -13
- package/dist/realtime/in-memory.d.ts.map +0 -1
- package/dist/realtime/in-memory.js +0 -28
- package/dist/realtime/in-memory.js.map +0 -1
- package/dist/realtime/index.d.ts +0 -4
- package/dist/realtime/index.d.ts.map +0 -1
- package/dist/realtime/index.js +0 -3
- package/dist/realtime/index.js.map +0 -1
- package/dist/realtime/types.d.ts +0 -62
- package/dist/realtime/types.d.ts.map +0 -1
- package/dist/realtime/types.js +0 -19
- package/dist/realtime/types.js.map +0 -1
- package/dist/relay/client-role/forward-engine.d.ts +0 -63
- package/dist/relay/client-role/forward-engine.d.ts.map +0 -1
- package/dist/relay/client-role/forward-engine.js +0 -267
- package/dist/relay/client-role/forward-engine.js.map +0 -1
- package/dist/relay/client-role/index.d.ts +0 -9
- package/dist/relay/client-role/index.d.ts.map +0 -1
- package/dist/relay/client-role/index.js +0 -9
- package/dist/relay/client-role/index.js.map +0 -1
- package/dist/relay/client-role/pull-engine.d.ts +0 -72
- package/dist/relay/client-role/pull-engine.d.ts.map +0 -1
- package/dist/relay/client-role/pull-engine.js +0 -249
- package/dist/relay/client-role/pull-engine.js.map +0 -1
- package/dist/relay/client-role/sequence-mapper.d.ts +0 -65
- package/dist/relay/client-role/sequence-mapper.d.ts.map +0 -1
- package/dist/relay/client-role/sequence-mapper.js +0 -161
- package/dist/relay/client-role/sequence-mapper.js.map +0 -1
- package/dist/relay/evaluation/relay-paths.d.ts +0 -41
- package/dist/relay/evaluation/relay-paths.d.ts.map +0 -1
- package/dist/relay/evaluation/relay-paths.js +0 -504
- package/dist/relay/evaluation/relay-paths.js.map +0 -1
- package/dist/relay/evaluation/rust-boundary.d.ts +0 -47
- package/dist/relay/evaluation/rust-boundary.d.ts.map +0 -1
- package/dist/relay/evaluation/rust-boundary.js +0 -220
- package/dist/relay/evaluation/rust-boundary.js.map +0 -1
- package/dist/relay/index.d.ts +0 -37
- package/dist/relay/index.d.ts.map +0 -1
- package/dist/relay/index.js +0 -44
- package/dist/relay/index.js.map +0 -1
- package/dist/relay/migrate.d.ts +0 -18
- package/dist/relay/migrate.d.ts.map +0 -1
- package/dist/relay/migrate.js +0 -99
- package/dist/relay/migrate.js.map +0 -1
- package/dist/relay/mode-manager.d.ts +0 -60
- package/dist/relay/mode-manager.d.ts.map +0 -1
- package/dist/relay/mode-manager.js +0 -114
- package/dist/relay/mode-manager.js.map +0 -1
- package/dist/relay/realtime.d.ts +0 -90
- package/dist/relay/realtime.d.ts.map +0 -1
- package/dist/relay/realtime.js +0 -147
- package/dist/relay/realtime.js.map +0 -1
- package/dist/relay/relay.d.ts +0 -190
- package/dist/relay/relay.d.ts.map +0 -1
- package/dist/relay/relay.js +0 -320
- package/dist/relay/relay.js.map +0 -1
- package/dist/relay/schema.d.ts +0 -158
- package/dist/relay/schema.d.ts.map +0 -1
- package/dist/relay/schema.js +0 -7
- package/dist/relay/schema.js.map +0 -1
- package/dist/relay/server-role/index.d.ts +0 -56
- package/dist/relay/server-role/index.d.ts.map +0 -1
- package/dist/relay/server-role/index.js +0 -193
- package/dist/relay/server-role/index.js.map +0 -1
- package/dist/relay/server-role/pull.d.ts +0 -27
- package/dist/relay/server-role/pull.d.ts.map +0 -1
- package/dist/relay/server-role/pull.js +0 -24
- package/dist/relay/server-role/pull.js.map +0 -1
- package/dist/relay/server-role/push.d.ts +0 -29
- package/dist/relay/server-role/push.d.ts.map +0 -1
- package/dist/relay/server-role/push.js +0 -122
- package/dist/relay/server-role/push.js.map +0 -1
- package/dist/s3/index.d.ts +0 -83
- package/dist/s3/index.d.ts.map +0 -1
- package/dist/s3/index.js +0 -223
- package/dist/s3/index.js.map +0 -1
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js.map +0 -1
- package/dist/service-worker/index.d.ts +0 -114
- package/dist/service-worker/index.d.ts.map +0 -1
- package/dist/service-worker/index.js +0 -487
- package/dist/service-worker/index.js.map +0 -1
- package/dist/snapshot-artifacts/sqlite-bun.d.ts +0 -15
- package/dist/snapshot-artifacts/sqlite-bun.d.ts.map +0 -1
- package/dist/snapshot-artifacts/sqlite-bun.js +0 -128
- package/dist/snapshot-artifacts/sqlite-bun.js.map +0 -1
- package/dist/snapshot-artifacts.d.ts +0 -206
- package/dist/snapshot-artifacts.d.ts.map +0 -1
- package/dist/snapshot-artifacts.js +0 -545
- package/dist/snapshot-artifacts.js.map +0 -1
- package/dist/snapshot-chunks/db-metadata.d.ts +0 -56
- package/dist/snapshot-chunks/db-metadata.d.ts.map +0 -1
- package/dist/snapshot-chunks/db-metadata.js +0 -360
- package/dist/snapshot-chunks/db-metadata.js.map +0 -1
- package/dist/snapshot-chunks/index.d.ts +0 -8
- package/dist/snapshot-chunks/index.d.ts.map +0 -1
- package/dist/snapshot-chunks/index.js +0 -8
- package/dist/snapshot-chunks/index.js.map +0 -1
- package/dist/snapshot-chunks/types.d.ts +0 -80
- package/dist/snapshot-chunks/types.d.ts.map +0 -1
- package/dist/snapshot-chunks/types.js +0 -8
- package/dist/snapshot-chunks/types.js.map +0 -1
- package/dist/snapshot-chunks.d.ts +0 -90
- package/dist/snapshot-chunks.d.ts.map +0 -1
- package/dist/snapshot-chunks.js +0 -304
- package/dist/snapshot-chunks.js.map +0 -1
- package/dist/sqlite/index.d.ts +0 -53
- package/dist/sqlite/index.d.ts.map +0 -1
- package/dist/sqlite/index.js +0 -795
- package/dist/sqlite/index.js.map +0 -1
- package/dist/sqlite3.d.ts +0 -22
- package/dist/sqlite3.d.ts.map +0 -1
- package/dist/sqlite3.js +0 -99
- package/dist/sqlite3.js.map +0 -1
- package/dist/stats.d.ts +0 -28
- package/dist/stats.d.ts.map +0 -1
- package/dist/stats.js +0 -93
- package/dist/stats.js.map +0 -1
- package/dist/subscriptions/cache.d.ts +0 -58
- package/dist/subscriptions/cache.d.ts.map +0 -1
- package/dist/subscriptions/cache.js +0 -250
- package/dist/subscriptions/cache.js.map +0 -1
- package/dist/subscriptions/index.d.ts +0 -3
- package/dist/subscriptions/index.d.ts.map +0 -1
- package/dist/subscriptions/index.js +0 -3
- package/dist/subscriptions/index.js.map +0 -1
- package/dist/subscriptions/resolve.d.ts +0 -40
- package/dist/subscriptions/resolve.d.ts.map +0 -1
- package/dist/subscriptions/resolve.js +0 -275
- package/dist/subscriptions/resolve.js.map +0 -1
- package/dist/sync.d.ts +0 -24
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -27
- package/dist/sync.js.map +0 -1
- package/src/auth-leases.ts +0 -649
- package/src/better-sqlite3.ts +0 -35
- package/src/blobs/access.ts +0 -244
- package/src/blobs/adapters/database.ts +0 -397
- package/src/blobs/index.ts +0 -9
- package/src/blobs/manager.ts +0 -901
- package/src/blobs/migrate.ts +0 -158
- package/src/blobs/types.ts +0 -74
- package/src/bun-sqlite-ambient.d.ts +0 -19
- package/src/bun-sqlite.ts +0 -27
- package/src/clients.ts +0 -22
- package/src/cloudflare/durable-object.ts +0 -289
- package/src/cloudflare/index.ts +0 -22
- package/src/cloudflare/r2.ts +0 -526
- package/src/cloudflare/scope-cache.ts +0 -341
- package/src/cloudflare/sentry.ts +0 -230
- package/src/cloudflare/worker.ts +0 -77
- package/src/commit-integrity.ts +0 -371
- package/src/compaction.ts +0 -77
- package/src/crdt-yjs/index.ts +0 -931
- package/src/d1.ts +0 -16
- package/src/dialect/base.ts +0 -360
- package/src/dialect/helpers.ts +0 -92
- package/src/dialect/index.ts +0 -7
- package/src/dialect/types.ts +0 -247
- package/src/encrypted-crdt.ts +0 -786
- package/src/filesystem/index.ts +0 -262
- package/src/handlers/collection.ts +0 -121
- package/src/handlers/create-handler.ts +0 -1134
- package/src/handlers/index.ts +0 -3
- package/src/handlers/types.ts +0 -403
- package/src/helpers/conflict.ts +0 -64
- package/src/helpers/emitted-change.ts +0 -69
- package/src/helpers/index.ts +0 -12
- package/src/helpers/paginate.ts +0 -82
- package/src/helpers/scope-authorization.ts +0 -27
- package/src/helpers/scope-commit-index.ts +0 -52
- package/src/helpers/scope-strings.ts +0 -101
- package/src/hono/api-key-auth.ts +0 -177
- package/src/hono/audit-redaction.ts +0 -135
- package/src/hono/blobs.ts +0 -851
- package/src/hono/console/gateway.ts +0 -3046
- package/src/hono/console/live-auth.ts +0 -46
- package/src/hono/console/route-descriptor.ts +0 -22
- package/src/hono/console/routes/api-keys.ts +0 -721
- package/src/hono/console/routes/clients.ts +0 -447
- package/src/hono/console/routes/commits.ts +0 -1137
- package/src/hono/console/routes/context.ts +0 -956
- package/src/hono/console/routes/events.ts +0 -669
- package/src/hono/console/routes/maintenance.ts +0 -461
- package/src/hono/console/routes/shared.ts +0 -816
- package/src/hono/console/routes/stats.ts +0 -392
- package/src/hono/console/routes/storage.ts +0 -159
- package/src/hono/console/routes.ts +0 -146
- package/src/hono/console/schema-errors.ts +0 -23
- package/src/hono/console/schemas.ts +0 -914
- package/src/hono/console/types.ts +0 -223
- package/src/hono/console/ui.ts +0 -100
- package/src/hono/create-server.ts +0 -230
- package/src/hono/errors.ts +0 -50
- package/src/hono/index.ts +0 -54
- package/src/hono/openapi.ts +0 -139
- package/src/hono/proxy/connection-manager.ts +0 -340
- package/src/hono/proxy/index.ts +0 -8
- package/src/hono/proxy/routes.ts +0 -272
- package/src/hono/rate-limit.ts +0 -319
- package/src/hono/realtime-sync-packs.ts +0 -354
- package/src/hono/routes/audit.ts +0 -499
- package/src/hono/routes/auth-leases.ts +0 -119
- package/src/hono/routes/combined.ts +0 -583
- package/src/hono/routes/context.ts +0 -1629
- package/src/hono/routes/health.ts +0 -26
- package/src/hono/routes/realtime.ts +0 -808
- package/src/hono/routes/shared.ts +0 -1626
- package/src/hono/routes/snapshots.ts +0 -345
- package/src/hono/routes.ts +0 -68
- package/src/hono/validation.ts +0 -81
- package/src/hono/websocket-origin.ts +0 -131
- package/src/hono/ws.ts +0 -1134
- package/src/libsql.ts +0 -51
- package/src/migrate.ts +0 -20
- package/src/neon.ts +0 -28
- package/src/notify.ts +0 -341
- package/src/pglite.ts +0 -68
- package/src/plugins/index.ts +0 -1
- package/src/plugins/types.ts +0 -144
- package/src/postgres/index.ts +0 -1291
- package/src/proxy/collection.ts +0 -17
- package/src/proxy/handler.ts +0 -159
- package/src/proxy/index.ts +0 -21
- package/src/proxy/mutation-detector.ts +0 -281
- package/src/proxy/oplog.ts +0 -181
- package/src/proxy/types.ts +0 -46
- package/src/realtime/in-memory.ts +0 -33
- package/src/realtime/index.ts +0 -7
- package/src/realtime/types.ts +0 -90
- package/src/relay/bun-types.d.ts +0 -50
- package/src/relay/client-role/forward-engine.ts +0 -355
- package/src/relay/client-role/index.ts +0 -9
- package/src/relay/client-role/pull-engine.ts +0 -329
- package/src/relay/client-role/sequence-mapper.ts +0 -201
- package/src/relay/evaluation/relay-paths.ts +0 -699
- package/src/relay/evaluation/rust-boundary.ts +0 -464
- package/src/relay/index.ts +0 -50
- package/src/relay/migrate.ts +0 -113
- package/src/relay/mode-manager.ts +0 -142
- package/src/relay/realtime.ts +0 -207
- package/src/relay/relay.ts +0 -431
- package/src/relay/schema.ts +0 -171
- package/src/relay/server-role/index.ts +0 -338
- package/src/relay/server-role/pull.ts +0 -43
- package/src/relay/server-role/push.ts +0 -164
- package/src/s3/index.ts +0 -346
- package/src/service-worker/index.ts +0 -773
- package/src/snapshot-artifacts/sqlite-bun.ts +0 -168
- package/src/snapshot-artifacts.ts +0 -896
- package/src/snapshot-chunks/db-metadata.ts +0 -537
- package/src/snapshot-chunks/index.ts +0 -8
- package/src/snapshot-chunks/types.ts +0 -105
- package/src/snapshot-chunks.ts +0 -453
- package/src/sqlite/index.ts +0 -1064
- package/src/sqlite3.ts +0 -137
- package/src/stats.ts +0 -180
- package/src/subscriptions/cache.ts +0 -376
- package/src/subscriptions/index.ts +0 -2
- package/src/subscriptions/resolve.ts +0 -357
- package/src/sync.ts +0 -111
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SyncRequestContext } from './context.js';
|
|
2
|
+
import type { SegmentRecord } from './segment-store.js';
|
|
3
|
+
export interface SegmentDownloadRequest {
|
|
4
|
+
readonly segmentId: string;
|
|
5
|
+
/** The `X-Syncular-Scopes` header: canonical JSON of the requested map. */
|
|
6
|
+
readonly scopesHeader: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SegmentDownloadResult {
|
|
9
|
+
readonly record: SegmentRecord;
|
|
10
|
+
readonly bytes: Uint8Array;
|
|
11
|
+
/** Response headers per §5.5. */
|
|
12
|
+
readonly headers: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function handleSegmentDownload(ctx: SyncRequestContext, request: SegmentDownloadRequest): Promise<SegmentDownloadResult>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { clockOf, RESOLVER_OUTAGE } from './context.js';
|
|
2
|
+
import { SyncError, syncError } from './errors.js';
|
|
3
|
+
import { emitEvent } from './events.js';
|
|
4
|
+
import { compileSchema } from './schema.js';
|
|
5
|
+
import { computeEffective, scopeDigest } from './scopes.js';
|
|
6
|
+
function parseScopesHeader(header) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(header);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw syncError('sync.invalid_request', 'X-Syncular-Scopes is not JSON');
|
|
13
|
+
}
|
|
14
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
15
|
+
throw syncError('sync.invalid_request', 'X-Syncular-Scopes must be a map');
|
|
16
|
+
}
|
|
17
|
+
const scopes = {};
|
|
18
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
19
|
+
if (!Array.isArray(value) ||
|
|
20
|
+
value.some((v) => typeof v !== 'string')) {
|
|
21
|
+
throw syncError('sync.invalid_request', 'X-Syncular-Scopes values must be string lists (§0)');
|
|
22
|
+
}
|
|
23
|
+
scopes[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return scopes;
|
|
26
|
+
}
|
|
27
|
+
export async function handleSegmentDownload(ctx, request) {
|
|
28
|
+
const events = ctx.events;
|
|
29
|
+
if (events === undefined)
|
|
30
|
+
return downloadSegment(ctx, request);
|
|
31
|
+
const clock = clockOf(ctx);
|
|
32
|
+
const startedAtMs = clock();
|
|
33
|
+
try {
|
|
34
|
+
const result = await downloadSegment(ctx, request);
|
|
35
|
+
emitEvent(events, {
|
|
36
|
+
type: 'segment.downloaded',
|
|
37
|
+
atMs: clock(),
|
|
38
|
+
partition: ctx.partition,
|
|
39
|
+
actorId: ctx.actorId,
|
|
40
|
+
segmentId: request.segmentId,
|
|
41
|
+
outcome: 'ok',
|
|
42
|
+
mediaType: result.record.mediaType,
|
|
43
|
+
bytes: result.bytes.length,
|
|
44
|
+
durationMs: clock() - startedAtMs,
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
emitEvent(events, {
|
|
50
|
+
type: 'segment.downloaded',
|
|
51
|
+
atMs: clock(),
|
|
52
|
+
partition: ctx.partition,
|
|
53
|
+
actorId: ctx.actorId,
|
|
54
|
+
segmentId: request.segmentId,
|
|
55
|
+
outcome: 'error',
|
|
56
|
+
errorCode: error instanceof SyncError ? error.code : 'internal',
|
|
57
|
+
durationMs: clock() - startedAtMs,
|
|
58
|
+
});
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function downloadSegment(ctx, request) {
|
|
63
|
+
const entry = await ctx.segments.get(request.segmentId);
|
|
64
|
+
if (entry === undefined || entry.record.partition !== ctx.partition) {
|
|
65
|
+
throw syncError('sync.not_found', 'unknown segment (§5.5)');
|
|
66
|
+
}
|
|
67
|
+
if (entry.record.expiresAtMs <= clockOf(ctx)()) {
|
|
68
|
+
throw syncError('sync.segment_expired', 'segment TTL elapsed — re-pull to mint fresh descriptors (§5.1)');
|
|
69
|
+
}
|
|
70
|
+
const schema = compileSchema(ctx.schema);
|
|
71
|
+
const table = schema.tables.get(entry.record.table);
|
|
72
|
+
if (table === undefined) {
|
|
73
|
+
throw syncError('sync.not_found', 'segment table no longer served');
|
|
74
|
+
}
|
|
75
|
+
const requested = parseScopesHeader(request.scopesHeader);
|
|
76
|
+
for (const [key, values] of Object.entries(requested)) {
|
|
77
|
+
if (!table.declaredVariables.has(key) || values.includes('*')) {
|
|
78
|
+
throw syncError('sync.invalid_subscription', 'invalid requested scopes (§3.2)');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let resolved;
|
|
82
|
+
try {
|
|
83
|
+
const allowed = await ctx.resolveScopes({
|
|
84
|
+
partition: ctx.partition,
|
|
85
|
+
actorId: ctx.actorId,
|
|
86
|
+
});
|
|
87
|
+
// §7.3.3: leases authorize sync rounds, never downloads — an outage
|
|
88
|
+
// signal here denies (re-authorization needs live scopes, §5.5).
|
|
89
|
+
resolved =
|
|
90
|
+
allowed === RESOLVER_OUTAGE ? { ok: false } : { ok: true, allowed };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const events = ctx.events;
|
|
94
|
+
if (events !== undefined) {
|
|
95
|
+
emitEvent(events, {
|
|
96
|
+
type: 'scopes.resolve_failed',
|
|
97
|
+
atMs: clockOf(ctx)(),
|
|
98
|
+
partition: ctx.partition,
|
|
99
|
+
actorId: ctx.actorId,
|
|
100
|
+
phase: 'segment-download',
|
|
101
|
+
message: error instanceof Error ? error.message : String(error),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
resolved = { ok: false };
|
|
105
|
+
}
|
|
106
|
+
const outcome = computeEffective(requested, resolved);
|
|
107
|
+
if (outcome.status !== 'active') {
|
|
108
|
+
throw syncError('sync.forbidden', 'segment scopes not held (§5.5)');
|
|
109
|
+
}
|
|
110
|
+
const digest = await scopeDigest(outcome.effective);
|
|
111
|
+
if (digest !== entry.record.scopeDigest) {
|
|
112
|
+
throw syncError('sync.forbidden', 'scope digest mismatch (§3.5, §5.5)');
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
record: entry.record,
|
|
116
|
+
bytes: entry.bytes,
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': 'application/octet-stream',
|
|
119
|
+
ETag: `"${entry.record.segmentId}"`,
|
|
120
|
+
'Cache-Control': 'private, max-age=0',
|
|
121
|
+
// Accept-Encoding: the body may be served compressed (§5.8).
|
|
122
|
+
Vary: 'Authorization, X-Syncular-Scopes, Accept-Encoding',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export declare const DEFAULT_SEGMENT_TTL_MS: number;
|
|
2
|
+
export interface SegmentMetadata {
|
|
3
|
+
readonly partition: string;
|
|
4
|
+
readonly table: string;
|
|
5
|
+
readonly schemaVersion: number;
|
|
6
|
+
readonly mediaType: 'rows' | 'sqlite';
|
|
7
|
+
readonly scopeDigest: string;
|
|
8
|
+
readonly asOfCommitSeq: number;
|
|
9
|
+
readonly rowCount: number;
|
|
10
|
+
readonly rowCursor: string | null;
|
|
11
|
+
readonly nextRowCursor: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface SegmentRecord extends SegmentMetadata {
|
|
14
|
+
readonly segmentId: string;
|
|
15
|
+
readonly byteLength: number;
|
|
16
|
+
readonly createdAtMs: number;
|
|
17
|
+
readonly expiresAtMs: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The §5.3 reuse key: sqlite images are not byte-deterministic, so
|
|
21
|
+
* cross-client dedup is a metadata lookup, not hash convergence — one
|
|
22
|
+
* stored image per (partition, table, schemaVersion, scope digest, pin).
|
|
23
|
+
*/
|
|
24
|
+
export interface SegmentFindKey {
|
|
25
|
+
readonly partition: string;
|
|
26
|
+
readonly table: string;
|
|
27
|
+
readonly schemaVersion: number;
|
|
28
|
+
readonly mediaType: 'rows' | 'sqlite';
|
|
29
|
+
readonly scopeDigest: string;
|
|
30
|
+
readonly asOfCommitSeq: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Coarse store-level counters for the admin/console read surface (TODO
|
|
34
|
+
* §2.5). Optional — a store that omits `stats()` simply cannot report them.
|
|
35
|
+
* All counts include expired-but-not-yet-evicted entries (the store's own
|
|
36
|
+
* bytes on disk), split by media type.
|
|
37
|
+
*/
|
|
38
|
+
export interface SegmentStoreStats {
|
|
39
|
+
readonly count: number;
|
|
40
|
+
readonly bytes: number;
|
|
41
|
+
readonly rowsSegments: number;
|
|
42
|
+
readonly sqliteSegments: number;
|
|
43
|
+
/**
|
|
44
|
+
* ADDITIVE marker — present and `true` only on stores whose counters are
|
|
45
|
+
* approximate (the S3/R2 store's LIST-free pointer accumulator, which can
|
|
46
|
+
* drift under concurrent writers or after lifecycle GC). Absent on the
|
|
47
|
+
* exact in-process stores (memory/sqlite). The admin surface carries it
|
|
48
|
+
* through so the console can label the numbers honestly.
|
|
49
|
+
*/
|
|
50
|
+
readonly approximate?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface SegmentStore {
|
|
53
|
+
put(metadata: SegmentMetadata, bytes: Uint8Array, nowMs: number): Promise<SegmentRecord>;
|
|
54
|
+
/** Returns expired records too — expiry is the caller's check (§5.5). */
|
|
55
|
+
get(segmentId: string): Promise<{
|
|
56
|
+
record: SegmentRecord;
|
|
57
|
+
bytes: Uint8Array;
|
|
58
|
+
} | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Unexpired record for the §5.3 reuse key (whole-table segments only:
|
|
61
|
+
* `rowCursor` null), or undefined. Servers MUST reuse instead of
|
|
62
|
+
* rebuilding sqlite images while one exists (§5.3).
|
|
63
|
+
*/
|
|
64
|
+
find(key: SegmentFindKey, nowMs: number): Promise<SegmentRecord | undefined>;
|
|
65
|
+
/** Admin/console counters (TODO §2.5) — ADDITIVE, optional. */
|
|
66
|
+
stats?(): Promise<SegmentStoreStats>;
|
|
67
|
+
}
|
|
68
|
+
export declare function segmentIdFor(bytes: Uint8Array): Promise<string>;
|
|
69
|
+
export declare class MemorySegmentStore implements SegmentStore {
|
|
70
|
+
#private;
|
|
71
|
+
constructor(options?: {
|
|
72
|
+
ttlMs?: number;
|
|
73
|
+
});
|
|
74
|
+
put(metadata: SegmentMetadata, bytes: Uint8Array, nowMs: number): Promise<SegmentRecord>;
|
|
75
|
+
get(segmentId: string): Promise<{
|
|
76
|
+
record: SegmentRecord;
|
|
77
|
+
bytes: Uint8Array;
|
|
78
|
+
} | undefined>;
|
|
79
|
+
find(key: SegmentFindKey, nowMs: number): Promise<SegmentRecord | undefined>;
|
|
80
|
+
stats(): Promise<SegmentStoreStats>;
|
|
81
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressed segment store (SPEC.md §5.1).
|
|
3
|
+
*
|
|
4
|
+
* Segments are cache entries, not durable state: identical bytes share one
|
|
5
|
+
* `segmentId` (`"sha256:" + hex`), entries expire on a TTL (default 24 h),
|
|
6
|
+
* and `get` returns expired records so the download path can distinguish
|
|
7
|
+
* `sync.segment_expired` from `sync.not_found` (§5.5).
|
|
8
|
+
*/
|
|
9
|
+
import { sha256Hex } from './scopes.js';
|
|
10
|
+
export const DEFAULT_SEGMENT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
11
|
+
export async function segmentIdFor(bytes) {
|
|
12
|
+
return `sha256:${await sha256Hex(bytes)}`;
|
|
13
|
+
}
|
|
14
|
+
export class MemorySegmentStore {
|
|
15
|
+
#ttlMs;
|
|
16
|
+
#entries = new Map();
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.#ttlMs = options?.ttlMs ?? DEFAULT_SEGMENT_TTL_MS;
|
|
19
|
+
}
|
|
20
|
+
async put(metadata, bytes, nowMs) {
|
|
21
|
+
const segmentId = await segmentIdFor(bytes);
|
|
22
|
+
const record = {
|
|
23
|
+
...metadata,
|
|
24
|
+
segmentId,
|
|
25
|
+
byteLength: bytes.length,
|
|
26
|
+
createdAtMs: nowMs,
|
|
27
|
+
expiresAtMs: nowMs + this.#ttlMs,
|
|
28
|
+
};
|
|
29
|
+
this.#entries.set(segmentId, { record, bytes });
|
|
30
|
+
return record;
|
|
31
|
+
}
|
|
32
|
+
async get(segmentId) {
|
|
33
|
+
return this.#entries.get(segmentId);
|
|
34
|
+
}
|
|
35
|
+
async find(key, nowMs) {
|
|
36
|
+
for (const { record } of this.#entries.values()) {
|
|
37
|
+
if (record.partition === key.partition &&
|
|
38
|
+
record.table === key.table &&
|
|
39
|
+
record.schemaVersion === key.schemaVersion &&
|
|
40
|
+
record.mediaType === key.mediaType &&
|
|
41
|
+
record.scopeDigest === key.scopeDigest &&
|
|
42
|
+
record.asOfCommitSeq === key.asOfCommitSeq &&
|
|
43
|
+
record.rowCursor === null &&
|
|
44
|
+
record.expiresAtMs > nowMs) {
|
|
45
|
+
return record;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
async stats() {
|
|
51
|
+
let bytes = 0;
|
|
52
|
+
let rowsSegments = 0;
|
|
53
|
+
let sqliteSegments = 0;
|
|
54
|
+
for (const { record } of this.#entries.values()) {
|
|
55
|
+
bytes += record.byteLength;
|
|
56
|
+
if (record.mediaType === 'sqlite')
|
|
57
|
+
sqliteSegments += 1;
|
|
58
|
+
else
|
|
59
|
+
rowsSegments += 1;
|
|
60
|
+
}
|
|
61
|
+
return { count: this.#entries.size, bytes, rowsSegments, sqliteSegments };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export interface SignedUrlConfig {
|
|
2
|
+
/** HMAC key (raw bytes or UTF-8 string). */
|
|
3
|
+
readonly key: string | Uint8Array;
|
|
4
|
+
/** URL prefix; the segment URL is `${baseUrl}/${segmentId}?st=${token}`. */
|
|
5
|
+
readonly baseUrl: string;
|
|
6
|
+
/** Token TTL; SHOULD be ≤ 15 minutes (§5.4). Default 900. */
|
|
7
|
+
readonly ttlSeconds?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Opaque per-partition audience (§5.4): stable per partition, MUST NOT
|
|
10
|
+
* disclose the internal partition id.
|
|
11
|
+
*/
|
|
12
|
+
readonly audience: (partition: string) => string;
|
|
13
|
+
}
|
|
14
|
+
/** What descriptor emission needs: the §5.4 `url`/`urlExpiresAtMs` pair. */
|
|
15
|
+
export interface SegmentUrlIssue {
|
|
16
|
+
readonly url: string;
|
|
17
|
+
readonly urlExpiresAtMs: number;
|
|
18
|
+
}
|
|
19
|
+
export interface SegmentPresignArgs {
|
|
20
|
+
readonly segmentId: string;
|
|
21
|
+
readonly partition: string;
|
|
22
|
+
readonly scopeDigest: string;
|
|
23
|
+
readonly nowMs: number;
|
|
24
|
+
/** Resolved TTL (config value or the 900 s default) — already applied. */
|
|
25
|
+
readonly ttlSeconds: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Delegated presign (§5.4): the provider signs the URL. The callback MUST
|
|
29
|
+
* satisfy the equivalence rule — the signed object key embeds exactly one
|
|
30
|
+
* `segmentId`, and `urlExpiresAtMs` reflects the real provider expiry.
|
|
31
|
+
* `S3SegmentStore.presignSegmentGet` (via `s3PresignedUrls`) is the
|
|
32
|
+
* in-tree implementation.
|
|
33
|
+
*/
|
|
34
|
+
export interface DelegatedPresignConfig {
|
|
35
|
+
readonly presign: (args: SegmentPresignArgs) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
|
|
36
|
+
/** URL TTL; SHOULD be ≤ 15 minutes (§5.4). Default 900. */
|
|
37
|
+
readonly ttlSeconds?: number;
|
|
38
|
+
}
|
|
39
|
+
/** Either §5.4 scheme; the pull emits descriptors identically for both. */
|
|
40
|
+
export type SegmentUrlConfig = SignedUrlConfig | DelegatedPresignConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Issue the §5.4 `url`/`urlExpiresAtMs` pair for one segment descriptor,
|
|
43
|
+
* routing to the native HMAC token or the delegated presigner. Callers
|
|
44
|
+
* (the pull) invoke this only after scope resolution and only when the
|
|
45
|
+
* client advertised accept bit 3.
|
|
46
|
+
*/
|
|
47
|
+
export declare function issueSegmentUrl(config: SegmentUrlConfig, args: {
|
|
48
|
+
readonly segmentId: string;
|
|
49
|
+
readonly partition: string;
|
|
50
|
+
readonly scopeDigest: string;
|
|
51
|
+
readonly nowMs: number;
|
|
52
|
+
}): Promise<SegmentUrlIssue>;
|
|
53
|
+
/**
|
|
54
|
+
* Delegated presign for BLOB downloads (§5.9.5). The blob twin of
|
|
55
|
+
* `DelegatedPresignConfig`: the provider signs the URL and the signed object
|
|
56
|
+
* key embeds the `blobId`. Issuance happens ONLY after the §5.9.5
|
|
57
|
+
* row-derived authorization check (never before), so the URL is a short-TTL
|
|
58
|
+
* bearer grant to exactly those immutable bytes — the SPEC's "authorization
|
|
59
|
+
* was resolved against the referencing rows at issuance". Unlike segments,
|
|
60
|
+
* there is no native-HMAC blob token scheme wired this rung; presign is the
|
|
61
|
+
* shipped path (`S3BlobStore.presignBlobGet` via `s3PresignedBlobUrls`).
|
|
62
|
+
*/
|
|
63
|
+
export interface BlobPresignArgs {
|
|
64
|
+
readonly partition: string;
|
|
65
|
+
readonly blobId: string;
|
|
66
|
+
readonly nowMs: number;
|
|
67
|
+
/** Resolved TTL (config value or the 900 s default) — already applied. */
|
|
68
|
+
readonly ttlSeconds: number;
|
|
69
|
+
}
|
|
70
|
+
export interface BlobPresignConfig {
|
|
71
|
+
readonly presign: (args: BlobPresignArgs) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
|
|
72
|
+
/** URL TTL; SHOULD be ≤ 15 minutes (§5.9.5). Default 900. */
|
|
73
|
+
readonly ttlSeconds?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Issue the §5.9.5 `url`/`urlExpiresAtMs` pair for one blob download. The
|
|
77
|
+
* caller (blob-handlers `downloadBlob`) invokes this only after the
|
|
78
|
+
* row-derived authorization check passes.
|
|
79
|
+
*/
|
|
80
|
+
export declare function issueBlobUrl(config: BlobPresignConfig, args: {
|
|
81
|
+
readonly partition: string;
|
|
82
|
+
readonly blobId: string;
|
|
83
|
+
readonly nowMs: number;
|
|
84
|
+
}): Promise<SegmentUrlIssue>;
|
|
85
|
+
/**
|
|
86
|
+
* Delegated presign for BLOB UPLOADS (§5.9.3 direct-to-storage). The upload
|
|
87
|
+
* twin of `BlobPresignConfig`: the provider signs a single PUT whose object
|
|
88
|
+
* key embeds the `blobId`. Issuance happens ONLY after host authentication in
|
|
89
|
+
* the upload-grant handler (uploading is host-auth-only, not scope-bearing —
|
|
90
|
+
* §5.9.3). The `byteLength` rides through so a provider MAY bind
|
|
91
|
+
* `Content-Length` (S3 conditional) to the granted size. Never a multipart or
|
|
92
|
+
* chunk protocol — single PUT only, this rung and the next (§5.9.3 non-goal).
|
|
93
|
+
* `S3BlobStore.presignBlobPut` (via `s3PresignedBlobUploads`) is the in-tree
|
|
94
|
+
* implementation.
|
|
95
|
+
*/
|
|
96
|
+
export interface BlobUploadPresignArgs {
|
|
97
|
+
readonly partition: string;
|
|
98
|
+
readonly blobId: string;
|
|
99
|
+
readonly byteLength: number;
|
|
100
|
+
readonly nowMs: number;
|
|
101
|
+
/** Resolved TTL (config value or the 900 s default) — already applied. */
|
|
102
|
+
readonly ttlSeconds: number;
|
|
103
|
+
}
|
|
104
|
+
export interface BlobUploadPresignConfig {
|
|
105
|
+
readonly presign: (args: BlobUploadPresignArgs) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
|
|
106
|
+
/** URL TTL; SHOULD be ≤ 15 minutes (§5.9.3). Default 900. */
|
|
107
|
+
readonly ttlSeconds?: number;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Issue the §5.9.3 `url`/`urlExpiresAtMs` pair for one presigned blob upload.
|
|
111
|
+
* The caller (blob-handlers `handleBlobUploadGrant`) invokes this only after
|
|
112
|
+
* host authentication and the size-cap check against the declared byteLength.
|
|
113
|
+
*/
|
|
114
|
+
export declare function issueBlobUploadUrl(config: BlobUploadPresignConfig, args: {
|
|
115
|
+
readonly partition: string;
|
|
116
|
+
readonly blobId: string;
|
|
117
|
+
readonly byteLength: number;
|
|
118
|
+
readonly nowMs: number;
|
|
119
|
+
}): Promise<SegmentUrlIssue>;
|
|
120
|
+
export interface SegmentTokenClaims {
|
|
121
|
+
readonly v: 1;
|
|
122
|
+
readonly seg: string;
|
|
123
|
+
readonly sd: string;
|
|
124
|
+
readonly aud: string;
|
|
125
|
+
/** Unix seconds — the sole non-millisecond timestamp in the spec. */
|
|
126
|
+
readonly exp: number;
|
|
127
|
+
}
|
|
128
|
+
export declare function signSegmentToken(key: string | Uint8Array, claims: SegmentTokenClaims): Promise<string>;
|
|
129
|
+
/**
|
|
130
|
+
* Verify a segment token per §5.4. Throws `SyncError sync.forbidden` on any
|
|
131
|
+
* failure (MAC, expiry, or claim mismatch).
|
|
132
|
+
*/
|
|
133
|
+
export declare function verifySegmentToken(key: string | Uint8Array, token: string, expected: {
|
|
134
|
+
readonly segmentId: string;
|
|
135
|
+
readonly scopeDigest: string;
|
|
136
|
+
readonly audience: string;
|
|
137
|
+
readonly nowMs: number;
|
|
138
|
+
readonly skewSeconds?: number;
|
|
139
|
+
}): Promise<SegmentTokenClaims>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed-URL segment delivery (SPEC.md §5.4): the native HMAC token
|
|
3
|
+
* scheme, plus the delegated-presign alternative (S3/R2/GCS) behind one
|
|
4
|
+
* issuance seam.
|
|
5
|
+
*
|
|
6
|
+
* Native: `st = base64url(payloadJson) + "." +
|
|
7
|
+
* base64url(HMAC-SHA256(key, payloadJson))` with claims
|
|
8
|
+
* `{v, seg, sd, aud, exp}`; `exp` is unix seconds. Issuance happens inside
|
|
9
|
+
* the pull immediately after scope resolution; verification checks MAC,
|
|
10
|
+
* expiry (≤ 60 s skew), and `seg`/`sd`/`aud` equality. Actual CDN serving
|
|
11
|
+
* is out of scope.
|
|
12
|
+
*
|
|
13
|
+
* Delegated presign: the provider mints the URL (§5.4 equivalence rule —
|
|
14
|
+
* the signed object key embeds the `segmentId`, the expiry obeys the same
|
|
15
|
+
* TTL guidance); `issueSegmentUrl` routes to whichever config the host
|
|
16
|
+
* supplied. Both paths issue only after scope resolution, so a URL is
|
|
17
|
+
* never minted for scopes the actor did not hold at that moment.
|
|
18
|
+
*/
|
|
19
|
+
import { syncError } from './errors.js';
|
|
20
|
+
/**
|
|
21
|
+
* Issue the §5.4 `url`/`urlExpiresAtMs` pair for one segment descriptor,
|
|
22
|
+
* routing to the native HMAC token or the delegated presigner. Callers
|
|
23
|
+
* (the pull) invoke this only after scope resolution and only when the
|
|
24
|
+
* client advertised accept bit 3.
|
|
25
|
+
*/
|
|
26
|
+
export async function issueSegmentUrl(config, args) {
|
|
27
|
+
const ttlSeconds = config.ttlSeconds ?? 900;
|
|
28
|
+
if ('presign' in config) {
|
|
29
|
+
return config.presign({ ...args, ttlSeconds });
|
|
30
|
+
}
|
|
31
|
+
const exp = Math.floor(args.nowMs / 1000) + ttlSeconds;
|
|
32
|
+
const token = await signSegmentToken(config.key, {
|
|
33
|
+
v: 1,
|
|
34
|
+
seg: args.segmentId,
|
|
35
|
+
sd: args.scopeDigest,
|
|
36
|
+
aud: config.audience(args.partition),
|
|
37
|
+
exp,
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
url: `${config.baseUrl}/${args.segmentId}?st=${token}`,
|
|
41
|
+
urlExpiresAtMs: exp * 1000,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Issue the §5.9.5 `url`/`urlExpiresAtMs` pair for one blob download. The
|
|
46
|
+
* caller (blob-handlers `downloadBlob`) invokes this only after the
|
|
47
|
+
* row-derived authorization check passes.
|
|
48
|
+
*/
|
|
49
|
+
export async function issueBlobUrl(config, args) {
|
|
50
|
+
const ttlSeconds = config.ttlSeconds ?? 900;
|
|
51
|
+
return config.presign({ ...args, ttlSeconds });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Issue the §5.9.3 `url`/`urlExpiresAtMs` pair for one presigned blob upload.
|
|
55
|
+
* The caller (blob-handlers `handleBlobUploadGrant`) invokes this only after
|
|
56
|
+
* host authentication and the size-cap check against the declared byteLength.
|
|
57
|
+
*/
|
|
58
|
+
export async function issueBlobUploadUrl(config, args) {
|
|
59
|
+
const ttlSeconds = config.ttlSeconds ?? 900;
|
|
60
|
+
return config.presign({ ...args, ttlSeconds });
|
|
61
|
+
}
|
|
62
|
+
const encoder = new TextEncoder();
|
|
63
|
+
// Runtime-neutral base64url (no `Buffer` — Workers/Deno/browser safe; TODO
|
|
64
|
+
// §4.2): `btoa`/`atob` operate on binary strings, present in every runtime.
|
|
65
|
+
function base64url(bytes) {
|
|
66
|
+
let binary = '';
|
|
67
|
+
for (const byte of bytes)
|
|
68
|
+
binary += String.fromCharCode(byte);
|
|
69
|
+
return btoa(binary)
|
|
70
|
+
.replaceAll('+', '-')
|
|
71
|
+
.replaceAll('/', '_')
|
|
72
|
+
.replace(/=+$/, '');
|
|
73
|
+
}
|
|
74
|
+
function fromBase64url(text) {
|
|
75
|
+
const binary = atob(text.replaceAll('-', '+').replaceAll('_', '/'));
|
|
76
|
+
const bytes = new Uint8Array(binary.length);
|
|
77
|
+
for (let i = 0; i < binary.length; i++)
|
|
78
|
+
bytes[i] = binary.charCodeAt(i);
|
|
79
|
+
return bytes;
|
|
80
|
+
}
|
|
81
|
+
async function hmac(key, payload) {
|
|
82
|
+
const keyBytes = typeof key === 'string' ? encoder.encode(key) : key;
|
|
83
|
+
const cryptoKey = await crypto.subtle.importKey('raw', keyBytes.slice().buffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
84
|
+
const mac = await crypto.subtle.sign('HMAC', cryptoKey, payload.slice().buffer);
|
|
85
|
+
return new Uint8Array(mac);
|
|
86
|
+
}
|
|
87
|
+
function constantTimeEqual(a, b) {
|
|
88
|
+
if (a.length !== b.length)
|
|
89
|
+
return false;
|
|
90
|
+
let diff = 0;
|
|
91
|
+
for (let i = 0; i < a.length; i++)
|
|
92
|
+
diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
|
|
93
|
+
return diff === 0;
|
|
94
|
+
}
|
|
95
|
+
export async function signSegmentToken(key, claims) {
|
|
96
|
+
const payloadJson = JSON.stringify({
|
|
97
|
+
v: claims.v,
|
|
98
|
+
seg: claims.seg,
|
|
99
|
+
sd: claims.sd,
|
|
100
|
+
aud: claims.aud,
|
|
101
|
+
exp: claims.exp,
|
|
102
|
+
});
|
|
103
|
+
const payload = encoder.encode(payloadJson);
|
|
104
|
+
const mac = await hmac(key, payload);
|
|
105
|
+
return `${base64url(payload)}.${base64url(mac)}`;
|
|
106
|
+
}
|
|
107
|
+
const DEFAULT_SKEW_SECONDS = 60;
|
|
108
|
+
/**
|
|
109
|
+
* Verify a segment token per §5.4. Throws `SyncError sync.forbidden` on any
|
|
110
|
+
* failure (MAC, expiry, or claim mismatch).
|
|
111
|
+
*/
|
|
112
|
+
export async function verifySegmentToken(key, token, expected) {
|
|
113
|
+
const forbidden = (reason) => syncError('sync.forbidden', `segment token rejected: ${reason}`);
|
|
114
|
+
const dot = token.indexOf('.');
|
|
115
|
+
if (dot < 0)
|
|
116
|
+
throw forbidden('malformed token');
|
|
117
|
+
const payload = fromBase64url(token.slice(0, dot));
|
|
118
|
+
const mac = fromBase64url(token.slice(dot + 1));
|
|
119
|
+
const computed = await hmac(key, payload);
|
|
120
|
+
if (!constantTimeEqual(mac, computed))
|
|
121
|
+
throw forbidden('bad MAC');
|
|
122
|
+
let claims;
|
|
123
|
+
try {
|
|
124
|
+
claims = JSON.parse(new TextDecoder().decode(payload));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
throw forbidden('unparseable claims');
|
|
128
|
+
}
|
|
129
|
+
if (claims.v !== 1)
|
|
130
|
+
throw forbidden('unsupported token version');
|
|
131
|
+
const skew = expected.skewSeconds ?? DEFAULT_SKEW_SECONDS;
|
|
132
|
+
if (typeof claims.exp !== 'number' ||
|
|
133
|
+
claims.exp < expected.nowMs / 1000 - skew) {
|
|
134
|
+
throw forbidden('token expired');
|
|
135
|
+
}
|
|
136
|
+
if (claims.seg !== expected.segmentId)
|
|
137
|
+
throw forbidden('segment mismatch');
|
|
138
|
+
if (claims.sd !== expected.scopeDigest)
|
|
139
|
+
throw forbidden('scope-digest mismatch');
|
|
140
|
+
if (claims.aud !== expected.audience)
|
|
141
|
+
throw forbidden('audience mismatch');
|
|
142
|
+
return claims;
|
|
143
|
+
}
|
package/dist/sigv4.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal AWS Signature Version 4 — exactly the subset the S3 segment
|
|
3
|
+
* store needs (header-signed requests and presigned GET URLs), hand-rolled
|
|
4
|
+
* per the dependency-light doctrine (REVISE): SigV4 is small and
|
|
5
|
+
* well-specified, so no SDK.
|
|
6
|
+
*
|
|
7
|
+
* Runtime-neutral (TODO §4.2): the crypto primitives are Web Crypto
|
|
8
|
+
* (`crypto.subtle`), not `node:crypto`, so this module runs unchanged on
|
|
9
|
+
* Cloudflare Workers / Deno / browsers as well as Bun/Node. `crypto.subtle`
|
|
10
|
+
* is async, so signing is async throughout; the `S3SegmentStore` consumers
|
|
11
|
+
* are already async, and the `DelegatedPresignConfig.presign` seam already
|
|
12
|
+
* accepts a `Promise<SegmentUrlIssue>`.
|
|
13
|
+
*
|
|
14
|
+
* Pinned by the published AWS SigV4 example vectors in
|
|
15
|
+
* `test/sigv4.test.ts`; the hermetic S3 stub re-derives every signature
|
|
16
|
+
* from the incoming request, so an asymmetric signing bug fails tests.
|
|
17
|
+
*/
|
|
18
|
+
export interface SigV4Credentials {
|
|
19
|
+
readonly accessKeyId: string;
|
|
20
|
+
readonly secretAccessKey: string;
|
|
21
|
+
/** Optional STS session token (signed as `x-amz-security-token`). */
|
|
22
|
+
readonly sessionToken?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Hex SHA-256 of an empty body — the payload hash for GET/HEAD/DELETE. */
|
|
25
|
+
export declare const EMPTY_PAYLOAD_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
26
|
+
/** The presigned-URL payload sentinel (S3 presigns never hash the body). */
|
|
27
|
+
export declare const UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
|
|
28
|
+
export declare function sha256Hex(data: Uint8Array | string): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* AWS canonical URI-encoding: RFC 3986 unreserved characters only —
|
|
31
|
+
* stricter than `encodeURIComponent` (which leaves `!'()*` alone).
|
|
32
|
+
*/
|
|
33
|
+
export declare function uriEncode(value: string, encodeSlash: boolean): string;
|
|
34
|
+
/** `YYYYMMDD'T'HHMMSS'Z'` plus its date-only prefix, from epoch ms. */
|
|
35
|
+
export declare function amzTimestamps(nowMs: number): {
|
|
36
|
+
amzDate: string;
|
|
37
|
+
dateStamp: string;
|
|
38
|
+
};
|
|
39
|
+
/** Sorted, fully re-encoded canonical query string from *decoded* pairs. */
|
|
40
|
+
export declare function canonicalQuery(params: Iterable<readonly [string, string]>): string;
|
|
41
|
+
export interface CanonicalRequestArgs {
|
|
42
|
+
readonly method: string;
|
|
43
|
+
/** The URI-encoded absolute path exactly as sent on the request line. */
|
|
44
|
+
readonly canonicalPath: string;
|
|
45
|
+
/** Decoded query pairs; `X-Amz-Signature` must already be excluded. */
|
|
46
|
+
readonly query: Iterable<readonly [string, string]>;
|
|
47
|
+
/** Only the headers being signed (name → raw value). */
|
|
48
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
49
|
+
readonly payloadHash: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function canonicalRequest(args: CanonicalRequestArgs): {
|
|
52
|
+
text: string;
|
|
53
|
+
signedHeaders: string;
|
|
54
|
+
};
|
|
55
|
+
export declare function stringToSign(amzDate: string, scope: string, canonicalRequestText: string): Promise<string>;
|
|
56
|
+
export declare function sigV4Signature(args: {
|
|
57
|
+
readonly secretAccessKey: string;
|
|
58
|
+
readonly dateStamp: string;
|
|
59
|
+
readonly region: string;
|
|
60
|
+
readonly service: string;
|
|
61
|
+
readonly stringToSign: string;
|
|
62
|
+
}): Promise<string>;
|
|
63
|
+
export interface SignRequestArgs {
|
|
64
|
+
readonly method: string;
|
|
65
|
+
/** Full request URL (host is taken from here and always signed). */
|
|
66
|
+
readonly url: URL;
|
|
67
|
+
readonly region: string;
|
|
68
|
+
/** Defaults to `s3`. */
|
|
69
|
+
readonly service?: string;
|
|
70
|
+
readonly credentials: SigV4Credentials;
|
|
71
|
+
readonly nowMs: number;
|
|
72
|
+
/** Hex SHA-256 of the request body (`EMPTY_PAYLOAD_SHA256` if none). */
|
|
73
|
+
readonly payloadHash: string;
|
|
74
|
+
/** Extra headers to send *and* sign (e.g. content-type, x-amz-meta-*). */
|
|
75
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Headers for a header-authenticated request: the caller's headers plus
|
|
79
|
+
* `x-amz-date`, `x-amz-content-sha256`, optional `x-amz-security-token`,
|
|
80
|
+
* and `authorization`. `host` is signed but not returned — fetch derives
|
|
81
|
+
* it from the URL.
|
|
82
|
+
*/
|
|
83
|
+
export declare function signRequest(args: SignRequestArgs): Promise<Record<string, string>>;
|
|
84
|
+
export interface PresignArgs {
|
|
85
|
+
/** Defaults to `GET`. */
|
|
86
|
+
readonly method?: string;
|
|
87
|
+
/** Object URL without any `X-Amz-*` query parameters. */
|
|
88
|
+
readonly url: URL;
|
|
89
|
+
readonly region: string;
|
|
90
|
+
/** Defaults to `s3`. */
|
|
91
|
+
readonly service?: string;
|
|
92
|
+
readonly credentials: SigV4Credentials;
|
|
93
|
+
readonly nowMs: number;
|
|
94
|
+
readonly expiresSeconds: number;
|
|
95
|
+
}
|
|
96
|
+
/** Query-authenticated (presigned) URL; only the `host` header is signed. */
|
|
97
|
+
export declare function presignUrl(args: PresignArgs): Promise<URL>;
|