@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,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blob upload + download handlers (SPEC.md §5.9.3, §5.9.5).
|
|
3
|
+
*
|
|
4
|
+
* Upload verifies the content address (§5.9.3); download re-authorizes on
|
|
5
|
+
* every request against the rows that reference the blob (§5.9.5) — a
|
|
6
|
+
* `blobId` is never a bearer capability. Framework-free; the hono/runtime
|
|
7
|
+
* adapters map these onto `PUT/GET <mount>/blobs/{blobId}`.
|
|
8
|
+
*/
|
|
9
|
+
import { blobIdFor, isBlobId } from './blob-store';
|
|
10
|
+
import type { SyncRequestContext } from './context';
|
|
11
|
+
import { clockOf, RESOLVER_OUTAGE } from './context';
|
|
12
|
+
import { SyncError, syncError } from './errors';
|
|
13
|
+
import { emitEvent } from './events';
|
|
14
|
+
import { compileSchema } from './schema';
|
|
15
|
+
import { authorizeWrite, type ResolvedScopes } from './scopes';
|
|
16
|
+
import { issueBlobUploadUrl, issueBlobUrl } from './signed-url';
|
|
17
|
+
|
|
18
|
+
const DEFAULT_MAX_BLOB_BYTES = 64 * 1024 * 1024;
|
|
19
|
+
|
|
20
|
+
export interface BlobUploadRequest {
|
|
21
|
+
readonly blobId: string;
|
|
22
|
+
readonly bytes: Uint8Array;
|
|
23
|
+
/** Advisory MIME type from the upload `Content-Type`, if any. */
|
|
24
|
+
readonly mediaType?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BlobDownloadResult {
|
|
28
|
+
/**
|
|
29
|
+
* The blob bytes, when served inline. **Absent** when the host configured
|
|
30
|
+
* `blobSignedUrls` and a `url` was issued: the sync server exits the egress
|
|
31
|
+
* path and the client fetches `url` directly (§5.9.5 always-issue).
|
|
32
|
+
*/
|
|
33
|
+
readonly bytes?: Uint8Array;
|
|
34
|
+
readonly headers: Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* §5.9.5 delegated presign (always-issue): a provider-signed GET URL for
|
|
37
|
+
* the bytes, issued only after the row-derived authorization check passed.
|
|
38
|
+
* Present iff the host configured `blobSignedUrls`; when present, `bytes` is
|
|
39
|
+
* absent and the client MUST fetch the URL directly (no host auth), verify
|
|
40
|
+
* the content address, and on failure re-request this endpoint — never fall
|
|
41
|
+
* through (§5.9.5 recovery rule).
|
|
42
|
+
*/
|
|
43
|
+
readonly url?: string;
|
|
44
|
+
readonly urlExpiresAtMs?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `POST <mount>/blobs/{blobId}/upload-grant` request body (§5.9.3). */
|
|
48
|
+
export interface BlobUploadGrantRequest {
|
|
49
|
+
readonly blobId: string;
|
|
50
|
+
/** Declared uncompressed size — the size-cap check runs against this. */
|
|
51
|
+
readonly byteLength: number;
|
|
52
|
+
/** Advisory MIME type, if any. */
|
|
53
|
+
readonly mediaType?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* `POST <mount>/blobs/{blobId}/upload-grant` result (§5.9.3). Exactly one of:
|
|
58
|
+
* `{url, urlExpiresAtMs}` — a presigned PUT the client uses direct-to-storage;
|
|
59
|
+
* or `{present: true}` — the blob already exists (idempotent §5.9.3), so the
|
|
60
|
+
* client skips the PUT; or `{}` (no fields) — the host has no presigned-upload
|
|
61
|
+
* store configured, so the client streams through the direct PUT endpoint
|
|
62
|
+
* (§5.9.3 capability, not fallback).
|
|
63
|
+
*/
|
|
64
|
+
export interface BlobUploadGrantResult {
|
|
65
|
+
readonly url?: string;
|
|
66
|
+
readonly urlExpiresAtMs?: number;
|
|
67
|
+
readonly present?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** `PUT <mount>/blobs/{blobId}` (§5.9.3). Host auth is the adapter's job. */
|
|
71
|
+
export async function handleBlobUpload(
|
|
72
|
+
ctx: SyncRequestContext,
|
|
73
|
+
request: BlobUploadRequest,
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
const store = ctx.blobs;
|
|
76
|
+
if (store === undefined) {
|
|
77
|
+
throw syncError('blob.not_found', 'this server has no blob store (§5.9)');
|
|
78
|
+
}
|
|
79
|
+
if (!isBlobId(request.blobId)) {
|
|
80
|
+
throw syncError(
|
|
81
|
+
'blob.hash_mismatch',
|
|
82
|
+
'blobId path is not "sha256:<64 hex>" (§5.9.1)',
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const maxBytes = ctx.maxBlobBytes ?? DEFAULT_MAX_BLOB_BYTES;
|
|
86
|
+
if (request.bytes.length > maxBytes) {
|
|
87
|
+
throw syncError(
|
|
88
|
+
'blob.too_large',
|
|
89
|
+
`blob exceeds the ${maxBytes}-byte cap (§5.9.3)`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const computed = await blobIdFor(request.bytes);
|
|
93
|
+
if (computed !== request.blobId) {
|
|
94
|
+
throw syncError(
|
|
95
|
+
'blob.hash_mismatch',
|
|
96
|
+
'uploaded bytes do not hash to the blobId path (§5.9.3)',
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const clock = clockOf(ctx);
|
|
100
|
+
await store.put(
|
|
101
|
+
ctx.partition,
|
|
102
|
+
request.blobId,
|
|
103
|
+
request.bytes,
|
|
104
|
+
clock(),
|
|
105
|
+
request.mediaType,
|
|
106
|
+
);
|
|
107
|
+
if (ctx.events !== undefined) {
|
|
108
|
+
emitEvent(ctx.events, {
|
|
109
|
+
type: 'blob.uploaded',
|
|
110
|
+
atMs: clock(),
|
|
111
|
+
partition: ctx.partition,
|
|
112
|
+
actorId: ctx.actorId,
|
|
113
|
+
blobId: request.blobId,
|
|
114
|
+
bytes: request.bytes.length,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `GET <mount>/blobs/{blobId}` (§5.9.5). Authorization derives from the
|
|
121
|
+
* referencing rows: the actor may download iff at least one row in the
|
|
122
|
+
* reference index passes the §3.4 scope check for the actor.
|
|
123
|
+
*/
|
|
124
|
+
export async function handleBlobDownload(
|
|
125
|
+
ctx: SyncRequestContext,
|
|
126
|
+
blobId: string,
|
|
127
|
+
): Promise<BlobDownloadResult> {
|
|
128
|
+
const events = ctx.events;
|
|
129
|
+
if (events === undefined) return downloadBlob(ctx, blobId);
|
|
130
|
+
const clock = clockOf(ctx);
|
|
131
|
+
const startedAtMs = clock();
|
|
132
|
+
try {
|
|
133
|
+
const result = await downloadBlob(ctx, blobId);
|
|
134
|
+
emitEvent(events, {
|
|
135
|
+
type: 'blob.downloaded',
|
|
136
|
+
atMs: clock(),
|
|
137
|
+
partition: ctx.partition,
|
|
138
|
+
actorId: ctx.actorId,
|
|
139
|
+
blobId,
|
|
140
|
+
outcome: 'ok',
|
|
141
|
+
// Presigned delivery carries no inline bytes (§5.9.5 always-issue); the
|
|
142
|
+
// byte count is unknown to the sync server, which exited the egress path.
|
|
143
|
+
bytes: result.bytes?.length ?? 0,
|
|
144
|
+
durationMs: clock() - startedAtMs,
|
|
145
|
+
});
|
|
146
|
+
return result;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
emitEvent(events, {
|
|
149
|
+
type: 'blob.downloaded',
|
|
150
|
+
atMs: clock(),
|
|
151
|
+
partition: ctx.partition,
|
|
152
|
+
actorId: ctx.actorId,
|
|
153
|
+
blobId,
|
|
154
|
+
outcome: 'error',
|
|
155
|
+
errorCode: error instanceof SyncError ? error.code : 'internal',
|
|
156
|
+
durationMs: clock() - startedAtMs,
|
|
157
|
+
});
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function downloadBlob(
|
|
163
|
+
ctx: SyncRequestContext,
|
|
164
|
+
blobId: string,
|
|
165
|
+
): Promise<BlobDownloadResult> {
|
|
166
|
+
const store = ctx.blobs;
|
|
167
|
+
if (store === undefined) {
|
|
168
|
+
throw syncError('blob.not_found', 'this server has no blob store (§5.9)');
|
|
169
|
+
}
|
|
170
|
+
if (!isBlobId(blobId)) {
|
|
171
|
+
throw syncError('blob.not_found', 'malformed blobId (§5.9.1)');
|
|
172
|
+
}
|
|
173
|
+
// §5.9.5 always-issue: when presign is configured the server exits the
|
|
174
|
+
// egress path — it only needs to know the object EXISTS (a HEAD/`has`), not
|
|
175
|
+
// the bytes. Inline delivery loads the bytes; presigned delivery does not.
|
|
176
|
+
const presignConfigured = ctx.blobSignedUrls !== undefined;
|
|
177
|
+
let entry: { record: { mediaType?: string }; bytes: Uint8Array } | undefined;
|
|
178
|
+
let mediaType: string | undefined;
|
|
179
|
+
if (presignConfigured) {
|
|
180
|
+
if (!(await store.has(ctx.partition, blobId))) {
|
|
181
|
+
throw syncError('blob.not_found', 'unknown blob (§5.9.5)');
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
entry = await store.get(ctx.partition, blobId);
|
|
185
|
+
if (entry === undefined) {
|
|
186
|
+
throw syncError('blob.not_found', 'unknown blob (§5.9.5)');
|
|
187
|
+
}
|
|
188
|
+
mediaType = entry.record.mediaType;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// §5.9.5 authorization: resolve once, then test referencing rows.
|
|
192
|
+
const listRows = ctx.storage.listRowsReferencingBlob;
|
|
193
|
+
if (listRows === undefined) {
|
|
194
|
+
// Storage without the reference index cannot authorize a download —
|
|
195
|
+
// fail closed (never serve bytes we cannot authorize).
|
|
196
|
+
throw syncError(
|
|
197
|
+
'blob.forbidden',
|
|
198
|
+
'storage has no blob reference index (§5.9.4)',
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
let resolved: ResolvedScopes;
|
|
202
|
+
try {
|
|
203
|
+
const allowed = await ctx.resolveScopes({
|
|
204
|
+
partition: ctx.partition,
|
|
205
|
+
actorId: ctx.actorId,
|
|
206
|
+
});
|
|
207
|
+
// §7.3.3: leases never authorize a blob download — an outage denies.
|
|
208
|
+
resolved =
|
|
209
|
+
allowed === RESOLVER_OUTAGE ? { ok: false } : { ok: true, allowed };
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (ctx.events !== undefined) {
|
|
212
|
+
emitEvent(ctx.events, {
|
|
213
|
+
type: 'scopes.resolve_failed',
|
|
214
|
+
atMs: clockOf(ctx)(),
|
|
215
|
+
partition: ctx.partition,
|
|
216
|
+
actorId: ctx.actorId,
|
|
217
|
+
phase: 'blob-download',
|
|
218
|
+
message: error instanceof Error ? error.message : String(error),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
resolved = { ok: false };
|
|
222
|
+
}
|
|
223
|
+
if (!resolved.ok) {
|
|
224
|
+
throw syncError('blob.forbidden', 'scope resolution failed (§5.9.5)');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const schema = compileSchema(ctx.schema);
|
|
228
|
+
// A blob download can hit a fresh storage instance before any sync
|
|
229
|
+
// request has run (Workers): make sure the row tables exist first.
|
|
230
|
+
await ctx.storage.ensureSchema(schema);
|
|
231
|
+
const rows = await listRows.call(ctx.storage, ctx.partition, blobId);
|
|
232
|
+
let authorized = false;
|
|
233
|
+
for (const row of rows) {
|
|
234
|
+
const table = schema.tables.get(row.table);
|
|
235
|
+
if (table === undefined) continue;
|
|
236
|
+
if (authorizeWrite(table, row.scopes, resolved)) {
|
|
237
|
+
authorized = true;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (!authorized) {
|
|
242
|
+
// Never 404 here: existence-vs-authorization must not leak (§5.9.5).
|
|
243
|
+
throw syncError(
|
|
244
|
+
'blob.forbidden',
|
|
245
|
+
'no referencing row authorizes this actor (§5.9.5)',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// §5.9.5 delegated presign (always-issue): authorization has passed above,
|
|
250
|
+
// so a signed URL is now a short-TTL bearer grant to exactly these immutable
|
|
251
|
+
// bytes. When configured, issue it and return NO bytes — the client fetches
|
|
252
|
+
// the URL directly. Never before the check, never a capability from the id.
|
|
253
|
+
if (ctx.blobSignedUrls !== undefined) {
|
|
254
|
+
const issued = await issueBlobUrl(ctx.blobSignedUrls, {
|
|
255
|
+
partition: ctx.partition,
|
|
256
|
+
blobId,
|
|
257
|
+
nowMs: clockOf(ctx)(),
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
headers: {
|
|
261
|
+
ETag: `"${blobId}"`,
|
|
262
|
+
'Cache-Control': 'private, max-age=0',
|
|
263
|
+
Vary: 'Authorization, Accept-Encoding',
|
|
264
|
+
},
|
|
265
|
+
url: issued.url,
|
|
266
|
+
urlExpiresAtMs: issued.urlExpiresAtMs,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Inline delivery (no presign store configured): the bytes were loaded.
|
|
271
|
+
const bytes = entry?.bytes;
|
|
272
|
+
if (bytes === undefined) {
|
|
273
|
+
// Unreachable: presign not configured ⇒ `entry` was loaded above.
|
|
274
|
+
throw syncError('blob.not_found', 'blob bytes unavailable (§5.9.5)');
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
bytes,
|
|
278
|
+
headers: {
|
|
279
|
+
'Content-Type': mediaType ?? 'application/octet-stream',
|
|
280
|
+
ETag: `"${blobId}"`,
|
|
281
|
+
'Cache-Control': 'private, max-age=0',
|
|
282
|
+
Vary: 'Authorization, Accept-Encoding',
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* `POST <mount>/blobs/{blobId}/upload-grant` (§5.9.3 presigned upload). Host
|
|
289
|
+
* auth is the adapter's job — uploading is host-auth-only, not scope-bearing,
|
|
290
|
+
* so any authenticated actor may obtain a grant within the size cap (§5.9.3).
|
|
291
|
+
*
|
|
292
|
+
* The size cap is enforced HERE, up front, against the declared `byteLength`
|
|
293
|
+
* (the object-store hop cannot re-check the streamed byte count). Integrity is
|
|
294
|
+
* NOT checked here: the object store places bytes at the content-addressed key,
|
|
295
|
+
* and the address is verified at reference time (§5.9.6 push existence) and on
|
|
296
|
+
* every download (§5.9.5/§5.1). Returns a presigned single PUT, or a
|
|
297
|
+
* `present` marker if the blob already exists (idempotent §5.9.3), or an empty
|
|
298
|
+
* result when no presigned-upload store is configured (client streams direct).
|
|
299
|
+
*/
|
|
300
|
+
export async function handleBlobUploadGrant(
|
|
301
|
+
ctx: SyncRequestContext,
|
|
302
|
+
request: BlobUploadGrantRequest,
|
|
303
|
+
): Promise<BlobUploadGrantResult> {
|
|
304
|
+
const store = ctx.blobs;
|
|
305
|
+
if (store === undefined) {
|
|
306
|
+
throw syncError('blob.not_found', 'this server has no blob store (§5.9)');
|
|
307
|
+
}
|
|
308
|
+
if (!isBlobId(request.blobId)) {
|
|
309
|
+
throw syncError(
|
|
310
|
+
'blob.hash_mismatch',
|
|
311
|
+
'blobId path is not "sha256:<64 hex>" (§5.9.1)',
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const maxBytes = ctx.maxBlobBytes ?? DEFAULT_MAX_BLOB_BYTES;
|
|
315
|
+
if (
|
|
316
|
+
!Number.isInteger(request.byteLength) ||
|
|
317
|
+
request.byteLength < 0 ||
|
|
318
|
+
request.byteLength > maxBytes
|
|
319
|
+
) {
|
|
320
|
+
// §5.9.3: cap enforced at grant issuance, before any URL is minted.
|
|
321
|
+
throw syncError(
|
|
322
|
+
'blob.too_large',
|
|
323
|
+
`blob byteLength exceeds the ${maxBytes}-byte cap (§5.9.3)`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
// §5.9.3 no presigned-upload store: report no grant; the client streams
|
|
327
|
+
// through the direct PUT endpoint (capability, not fallback).
|
|
328
|
+
if (ctx.blobUploadUrls === undefined) return {};
|
|
329
|
+
// Idempotent §5.9.3: an already-present blob needs no PUT.
|
|
330
|
+
if (await store.has(ctx.partition, request.blobId)) {
|
|
331
|
+
return { present: true };
|
|
332
|
+
}
|
|
333
|
+
const issued = await issueBlobUploadUrl(ctx.blobUploadUrls, {
|
|
334
|
+
partition: ctx.partition,
|
|
335
|
+
blobId: request.blobId,
|
|
336
|
+
byteLength: request.byteLength,
|
|
337
|
+
nowMs: clockOf(ctx)(),
|
|
338
|
+
});
|
|
339
|
+
return { url: issued.url, urlExpiresAtMs: issued.urlExpiresAtMs };
|
|
340
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §5.8 shipped compression posture for the direct segment endpoint:
|
|
3
|
+
* negotiate `Content-Encoding` from the request's `Accept-Encoding`
|
|
4
|
+
* (zstd preferred, gzip fallback, identity when the client offers
|
|
5
|
+
* neither), feature-detecting the runtime's codecs — zero dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Segment bytes are stored and content-addressed UNCOMPRESSED (§5.1);
|
|
8
|
+
* this is a per-response serving concern only. It is never applied to
|
|
9
|
+
* stored objects or the signed-URL path (§5.8: presigned GETs serve the
|
|
10
|
+
* addressed bytes verbatim; edge compression is a deployment concern).
|
|
11
|
+
*
|
|
12
|
+
* Decision data (2026-07-03, 100k-row bench table, Bun 1.3): rows
|
|
13
|
+
* segments 6.2× in 8 ms (zstd) / 7.7× in 42 ms (gzip); sqlite images
|
|
14
|
+
* 3.4× in 14 ms (zstd) / 51 ms (gzip); client decompression ≤ 8 ms.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export type SegmentContentEncoding = 'zstd' | 'gzip';
|
|
18
|
+
|
|
19
|
+
interface Codec {
|
|
20
|
+
readonly encoding: SegmentContentEncoding;
|
|
21
|
+
readonly compress: (bytes: Uint8Array) => Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface BunCompressors {
|
|
25
|
+
readonly zstdCompressSync?: (bytes: Uint8Array) => Uint8Array;
|
|
26
|
+
readonly gzipSync?: (bytes: Uint8Array) => Uint8Array;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Preference order is zstd, gzip (§5.8). Non-Bun runtimes without these
|
|
30
|
+
* codecs serve identity — negotiation, never a hard requirement here. */
|
|
31
|
+
function detectCodecs(): readonly Codec[] {
|
|
32
|
+
const bun = (globalThis as { Bun?: BunCompressors }).Bun;
|
|
33
|
+
const codecs: Codec[] = [];
|
|
34
|
+
const zstd = bun?.zstdCompressSync;
|
|
35
|
+
if (typeof zstd === 'function') {
|
|
36
|
+
codecs.push({ encoding: 'zstd', compress: (bytes) => zstd(bytes) });
|
|
37
|
+
}
|
|
38
|
+
const gzip = bun?.gzipSync;
|
|
39
|
+
if (typeof gzip === 'function') {
|
|
40
|
+
codecs.push({ encoding: 'gzip', compress: (bytes) => gzip(bytes) });
|
|
41
|
+
}
|
|
42
|
+
return codecs;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const CODECS = detectCodecs();
|
|
46
|
+
|
|
47
|
+
/** Bodies below this size are served identity (headers outweigh gains). */
|
|
48
|
+
const MIN_COMPRESS_BYTES = 1024;
|
|
49
|
+
|
|
50
|
+
/** Does the `Accept-Encoding` header value accept `encoding` (q > 0)? */
|
|
51
|
+
function accepts(header: string, encoding: SegmentContentEncoding): boolean {
|
|
52
|
+
for (const entry of header.split(',')) {
|
|
53
|
+
const [rawName, ...params] = entry.trim().split(';');
|
|
54
|
+
const name = rawName?.trim().toLowerCase();
|
|
55
|
+
if (name !== encoding) continue;
|
|
56
|
+
for (const param of params) {
|
|
57
|
+
const [key, value] = param.trim().split('=');
|
|
58
|
+
if (key?.trim().toLowerCase() === 'q' && Number(value) === 0) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface EncodedSegmentBody {
|
|
68
|
+
readonly bytes: Uint8Array;
|
|
69
|
+
/** Absent ⇒ identity (no `Content-Encoding` header). */
|
|
70
|
+
readonly contentEncoding?: SegmentContentEncoding;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Compress a buffered segment body per §5.8. Returns the original bytes
|
|
75
|
+
* (identity) when the client offered no supported encoding, the body is
|
|
76
|
+
* tiny, or the runtime has no codec.
|
|
77
|
+
*/
|
|
78
|
+
export function encodeSegmentBody(
|
|
79
|
+
bytes: Uint8Array,
|
|
80
|
+
acceptEncoding: string | undefined,
|
|
81
|
+
): EncodedSegmentBody {
|
|
82
|
+
if (acceptEncoding === undefined || bytes.length < MIN_COMPRESS_BYTES) {
|
|
83
|
+
return { bytes };
|
|
84
|
+
}
|
|
85
|
+
for (const codec of CODECS) {
|
|
86
|
+
if (accepts(acceptEncoding, codec.encoding)) {
|
|
87
|
+
return {
|
|
88
|
+
bytes: codec.compress(bytes),
|
|
89
|
+
contentEncoding: codec.encoding,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { bytes };
|
|
94
|
+
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host context for `handleSyncRequest` (REVISE B2): storage, the
|
|
3
|
+
* `resolveScopes` host callback (runs in the host process — the moat),
|
|
4
|
+
* clock, and a segment store. Framework-free; adapters supply `partition`
|
|
5
|
+
* and `actorId` from host authentication (§1.1).
|
|
6
|
+
*/
|
|
7
|
+
import type { ScopeMap } from '@syncular/core';
|
|
8
|
+
import type { BlobStore } from './blob-store';
|
|
9
|
+
import type { CrdtMergerRegistry } from './crdt-merger';
|
|
10
|
+
import type { SyncularServerEvents } from './events';
|
|
11
|
+
import type { LeaseStore } from './lease-store';
|
|
12
|
+
import type { ServerSchema } from './schema';
|
|
13
|
+
import type { SegmentStore } from './segment-store';
|
|
14
|
+
import type {
|
|
15
|
+
BlobPresignConfig,
|
|
16
|
+
BlobUploadPresignConfig,
|
|
17
|
+
SegmentUrlConfig,
|
|
18
|
+
} from './signed-url';
|
|
19
|
+
import type { SqliteImageBuilder } from './sqlite-image';
|
|
20
|
+
import type { ServerStorage, StoredCommit } from './storage';
|
|
21
|
+
import type { ValidatorRegistry } from './validate';
|
|
22
|
+
|
|
23
|
+
/** SSP2 body content type (§1.1). */
|
|
24
|
+
export const SSP2_CONTENT_TYPE = 'application/vnd.syncular.sync.v2';
|
|
25
|
+
|
|
26
|
+
export interface ResolveScopesArgs {
|
|
27
|
+
readonly partition: string;
|
|
28
|
+
readonly actorId: string;
|
|
29
|
+
/**
|
|
30
|
+
* The requesting client (§1.5). Present on sync rounds (the lease key,
|
|
31
|
+
* §7.3.3); absent on segment/blob downloads, which re-authorize by
|
|
32
|
+
* actor + scopes only (§5.5, §5.9.5) and never consult a lease.
|
|
33
|
+
*/
|
|
34
|
+
readonly clientId?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sentinel a resolver returns to signal a **live-authorization outage**
|
|
39
|
+
* (§7.3.3): the live authority is unreachable, so the server SHOULD
|
|
40
|
+
* authorize this request against the actor's stored lease instead of
|
|
41
|
+
* throwing. Distinct from throwing (§3.2 rule 5, which still fails loud
|
|
42
|
+
* and revokes). Only meaningful when `leases` is configured; without a
|
|
43
|
+
* lease store an outage degrades to `sync.auth_lease_required`.
|
|
44
|
+
*/
|
|
45
|
+
export const RESOLVER_OUTAGE = Symbol.for('syncular.resolver_outage');
|
|
46
|
+
export type ResolverOutage = typeof RESOLVER_OUTAGE;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Host callback: actor → allowed scopes (§3.2 step 3). Resolved at most
|
|
50
|
+
* once per request and memoized (§3.4 step 1). `'*'` means "any value for
|
|
51
|
+
* this variable". A throwing resolver revokes subscriptions and rejects
|
|
52
|
+
* writes with `sync.forbidden` — fail loud, never leak. Returning
|
|
53
|
+
* `RESOLVER_OUTAGE` instead opts the request into lease authorization
|
|
54
|
+
* (§7.3.3) — a deliberate outage signal, not a failure.
|
|
55
|
+
*/
|
|
56
|
+
export type ResolveScopes = (
|
|
57
|
+
args: ResolveScopesArgs,
|
|
58
|
+
) => ScopeMap | ResolverOutage | Promise<ScopeMap | ResolverOutage>;
|
|
59
|
+
|
|
60
|
+
/** §7.3: auth-lease feature config. Absent ⇒ leases off (zero cost). */
|
|
61
|
+
export interface LeaseConfig {
|
|
62
|
+
/** Lease TTL in ms — the sliding window width (§7.3.3). */
|
|
63
|
+
readonly ttlMs: number;
|
|
64
|
+
/** The lease store (§7.3.1); host-owned, optional like blobs. */
|
|
65
|
+
readonly store: LeaseStore;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ServerLimits {
|
|
69
|
+
/** Operation cap per request (§6.1 `sync.too_many_operations`). */
|
|
70
|
+
readonly maxOperationsPerRequest: number;
|
|
71
|
+
/** Inline rows segments up to this size (§5.7 SHOULD: 256 KiB). */
|
|
72
|
+
readonly inlineSegmentMaxBytes: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const DEFAULT_LIMITS: ServerLimits = {
|
|
76
|
+
maxOperationsPerRequest: 500,
|
|
77
|
+
inlineSegmentMaxBytes: 256 * 1024,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Receives applied commits for realtime fanout (§8.2). */
|
|
81
|
+
export interface RealtimeNotifier {
|
|
82
|
+
notifyCommit(partition: string, commit: StoredCommit): void | Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SyncServerConfig {
|
|
86
|
+
readonly schema: ServerSchema;
|
|
87
|
+
readonly storage: ServerStorage;
|
|
88
|
+
readonly segments: SegmentStore;
|
|
89
|
+
/**
|
|
90
|
+
* Blob store (§5.9). Absent ⇒ blobs unsupported: a table with a
|
|
91
|
+
* `blob_ref` column may still sync (the ref is just a string), but the
|
|
92
|
+
* push existence check (§6.6) and the `/blobs` endpoints require it.
|
|
93
|
+
*/
|
|
94
|
+
readonly blobs?: BlobStore;
|
|
95
|
+
/** Max upload size for `/blobs` (§5.9.3 `blob.too_large`); default 64 MiB. */
|
|
96
|
+
readonly maxBlobBytes?: number;
|
|
97
|
+
/**
|
|
98
|
+
* CRDT merger registry (§5.10.2), `crdtType` → merge fn. Absent ⇒ no
|
|
99
|
+
* column can CRDT-merge: a push touching a `crdt` column then fails
|
|
100
|
+
* `sync.crdt_merge_failed` (§5.10.6). Kept out of core — the reference
|
|
101
|
+
* `yjs-doc` merger ships in `@syncular/crdt-yjs` (the blob-store rule).
|
|
102
|
+
*/
|
|
103
|
+
readonly crdtMergers?: CrdtMergerRegistry;
|
|
104
|
+
/**
|
|
105
|
+
* §6.7 per-table write-validation hooks (`table` → validator). Absent ⇒
|
|
106
|
+
* the feature is off: the push path pays only an `undefined` check and
|
|
107
|
+
* builds no validator context — zero cost. Present ⇒ each push operation
|
|
108
|
+
* on a listed table runs its validator after decode + §3.4 scope
|
|
109
|
+
* authorization, inside the commit transaction; a throw rejects the
|
|
110
|
+
* whole commit atomically (§6.4) with the host code (§6.3). Kept a host
|
|
111
|
+
* callback (like `resolveScopes`) — business rules live in the host
|
|
112
|
+
* process, never on the wire.
|
|
113
|
+
*/
|
|
114
|
+
readonly validators?: ValidatorRegistry;
|
|
115
|
+
readonly resolveScopes: ResolveScopes;
|
|
116
|
+
/**
|
|
117
|
+
* §7.3 auth leases. Absent ⇒ the feature is off: no `LEASE` frame is
|
|
118
|
+
* ever emitted and a `RESOLVER_OUTAGE` signal degrades to
|
|
119
|
+
* `sync.auth_lease_required`. Present ⇒ successful authorized rounds
|
|
120
|
+
* issue/refresh a lease and outages authorize against it.
|
|
121
|
+
*/
|
|
122
|
+
readonly leases?: LeaseConfig;
|
|
123
|
+
/** Epoch-ms clock; defaults to `Date.now`. */
|
|
124
|
+
readonly clock?: () => number;
|
|
125
|
+
readonly limits?: Partial<ServerLimits>;
|
|
126
|
+
/** §5.4 signed-URL delivery: native HMAC tokens or delegated presign. */
|
|
127
|
+
readonly signedUrls?: SegmentUrlConfig;
|
|
128
|
+
/**
|
|
129
|
+
* §5.9.5 signed-URL delivery for BLOB downloads (delegated presign,
|
|
130
|
+
* always-issue). When set, `handleBlobDownload` issues a provider-presigned
|
|
131
|
+
* GET URL AFTER the row-derived authorization check and returns
|
|
132
|
+
* `url`/`urlExpiresAtMs` on `BlobDownloadResult` WITHOUT loading the bytes
|
|
133
|
+
* (the sync server exits the download egress path). The reference client
|
|
134
|
+
* fetches the URL directly (§5.9.5 recovery rule: failure ⇒ re-request,
|
|
135
|
+
* never fall-through). Absent ⇒ blobs are served inline (the shipped
|
|
136
|
+
* default).
|
|
137
|
+
*/
|
|
138
|
+
readonly blobSignedUrls?: BlobPresignConfig;
|
|
139
|
+
/**
|
|
140
|
+
* §5.9.3 presigned-upload (direct-to-storage) delivery. When set,
|
|
141
|
+
* `handleBlobUploadGrant` issues a single provider-presigned PUT URL AFTER
|
|
142
|
+
* host authentication + the size-cap check, and the client PUTs bytes
|
|
143
|
+
* straight to the object store (the sync server exits the upload egress
|
|
144
|
+
* path). Absent ⇒ the `/upload-grant` route reports no grant and clients
|
|
145
|
+
* stream through the direct `PUT /blobs/{blobId}` endpoint (§5.9.3 —
|
|
146
|
+
* capability, not fallback). Never a multipart/chunk protocol (single PUT).
|
|
147
|
+
*/
|
|
148
|
+
readonly blobUploadUrls?: BlobUploadPresignConfig;
|
|
149
|
+
/**
|
|
150
|
+
* §5.3 sqlite-image builder (TODO §4.2), injected so the pull path never
|
|
151
|
+
* statically imports `bun:sqlite`. Absent ⇒ the sqlite-image lane is off
|
|
152
|
+
* (bit-2 clients are served the rows lane) — the Workers/edge posture. A
|
|
153
|
+
* Bun/Node host wires `buildSqliteImage` from `@syncular/server`.
|
|
154
|
+
*/
|
|
155
|
+
readonly sqliteImageBuilder?: SqliteImageBuilder;
|
|
156
|
+
readonly realtime?: RealtimeNotifier;
|
|
157
|
+
/**
|
|
158
|
+
* Optional structured-events sink (ops seam). Absent ⇒ zero cost: no
|
|
159
|
+
* event objects are built. A throwing sink never affects processing.
|
|
160
|
+
*/
|
|
161
|
+
readonly events?: SyncularServerEvents;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Per-request context: the config plus host-authenticated identity. */
|
|
165
|
+
export interface SyncRequestContext extends SyncServerConfig {
|
|
166
|
+
readonly partition: string;
|
|
167
|
+
readonly actorId: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function clockOf(ctx: SyncServerConfig): () => number {
|
|
171
|
+
return ctx.clock ?? Date.now;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function limitsOf(ctx: SyncServerConfig): ServerLimits {
|
|
175
|
+
return { ...DEFAULT_LIMITS, ...ctx.limits };
|
|
176
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRDT column merge registry (SPEC.md §5.10.2) — the pluggability seam.
|
|
3
|
+
*
|
|
4
|
+
* The server core NEVER depends on a CRDT library. A `crdt` column (§2.4
|
|
5
|
+
* tag 8) is merged server-side on push (§5.10.3) by a host-supplied
|
|
6
|
+
* `CrdtMerger`, selected per column by its schema-IR `crdtType` (§5.10.1).
|
|
7
|
+
* The reference `yjs-doc` merger lives in `@syncular/crdt-yjs`, keeping
|
|
8
|
+
* Yjs out of core/server (the blob-store placement rule, §5.9.2, applied to
|
|
9
|
+
* CRDT).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Merge a `crdt` column's stored value with an incoming push value,
|
|
14
|
+
* returning the new stored bytes (§5.10.2). `stored` is `null` when the row
|
|
15
|
+
* is new or the column was NULL. Implementations MUST be commutative,
|
|
16
|
+
* associative, and idempotent over the updates they consume — the CRDT
|
|
17
|
+
* contract that makes concurrent-order-independent convergence and
|
|
18
|
+
* idempotent replay hold (§5.10.3).
|
|
19
|
+
*
|
|
20
|
+
* A synchronous return is fine; async is allowed so a native/WASM merger can
|
|
21
|
+
* cross an FFI boundary.
|
|
22
|
+
*/
|
|
23
|
+
export type CrdtMerger = (
|
|
24
|
+
stored: Uint8Array | null,
|
|
25
|
+
incoming: Uint8Array,
|
|
26
|
+
) => Uint8Array | Promise<Uint8Array>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* `crdtType` (§5.10.1) → merger. Supplied through `SyncServerConfig`
|
|
30
|
+
* (§ context). Absent ⇒ no column can CRDT-merge: a push touching a `crdt`
|
|
31
|
+
* column then fails `sync.crdt_merge_failed` (§5.10.2, §5.10.6).
|
|
32
|
+
*/
|
|
33
|
+
export type CrdtMergerRegistry = Readonly<Record<string, CrdtMerger>>;
|
|
Binary file
|