@syncular/server 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -1,1134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/server - Declarative server handler helper
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
BinarySnapshotColumn,
|
|
7
|
-
BinarySnapshotRowsEncoder,
|
|
8
|
-
ScopeValues,
|
|
9
|
-
ScopeValuesFromPatterns,
|
|
10
|
-
ScopeDefinition as SimpleScopeDefinition,
|
|
11
|
-
StoredScopes,
|
|
12
|
-
SyncOperation,
|
|
13
|
-
} from '@syncular/core';
|
|
14
|
-
import {
|
|
15
|
-
applyCodecsFromDbRow,
|
|
16
|
-
applyCodecsToDbRow,
|
|
17
|
-
type ColumnCodecDialect,
|
|
18
|
-
type ColumnCodecSource,
|
|
19
|
-
createSingleVariableScopeMetadata,
|
|
20
|
-
createTableColumnCodecsResolver,
|
|
21
|
-
type SyncularErrorCode,
|
|
22
|
-
toTableColumnCodecs,
|
|
23
|
-
} from '@syncular/core';
|
|
24
|
-
import type {
|
|
25
|
-
DeleteQueryBuilder,
|
|
26
|
-
DeleteResult,
|
|
27
|
-
Insertable,
|
|
28
|
-
InsertQueryBuilder,
|
|
29
|
-
InsertResult,
|
|
30
|
-
Selectable,
|
|
31
|
-
SelectQueryBuilder,
|
|
32
|
-
Updateable,
|
|
33
|
-
UpdateQueryBuilder,
|
|
34
|
-
UpdateResult,
|
|
35
|
-
} from 'kysely';
|
|
36
|
-
import { sql } from 'kysely';
|
|
37
|
-
import { coerceNumber } from '../dialect/helpers';
|
|
38
|
-
import { rowScopesAllowed } from '../helpers/scope-authorization';
|
|
39
|
-
import type { SyncCoreDb } from '../schema';
|
|
40
|
-
import type {
|
|
41
|
-
ApplyOperationResult,
|
|
42
|
-
BatchApplyOperationInput,
|
|
43
|
-
EmittedChange,
|
|
44
|
-
ServerApplyOperationContext,
|
|
45
|
-
ServerContext,
|
|
46
|
-
ServerSnapshotContext,
|
|
47
|
-
ServerTableHandler,
|
|
48
|
-
SyncServerAuth,
|
|
49
|
-
} from './types';
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Authorization result from authorize callback.
|
|
53
|
-
*/
|
|
54
|
-
type AuthorizeResult =
|
|
55
|
-
| true
|
|
56
|
-
| { error: string; code: SyncularErrorCode; retriable?: boolean };
|
|
57
|
-
|
|
58
|
-
function classifyConstraintViolationCode(_message: string): SyncularErrorCode {
|
|
59
|
-
return 'sync.constraint_violation';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function isConstraintViolationError(message: string): boolean {
|
|
63
|
-
const normalized = message.toLowerCase();
|
|
64
|
-
return (
|
|
65
|
-
normalized.includes('constraint') ||
|
|
66
|
-
normalized.includes('not null') ||
|
|
67
|
-
normalized.includes('foreign key') ||
|
|
68
|
-
normalized.includes('unique')
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function isMissingColumnReferenceError(message: string): boolean {
|
|
73
|
-
const normalized = message.toLowerCase();
|
|
74
|
-
return (
|
|
75
|
-
normalized.includes('no such column') ||
|
|
76
|
-
(normalized.includes('column') && normalized.includes('does not exist'))
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function readSafeVersionColumn(
|
|
81
|
-
row: Record<string, unknown>,
|
|
82
|
-
versionColumn: string,
|
|
83
|
-
defaultValue: number
|
|
84
|
-
): number {
|
|
85
|
-
const value = row[versionColumn];
|
|
86
|
-
if (value === null || value === undefined) return defaultValue;
|
|
87
|
-
const version = coerceNumber(value);
|
|
88
|
-
if (version === null || !Number.isSafeInteger(version)) {
|
|
89
|
-
throw new Error(`Version column "${versionColumn}" must be a safe integer`);
|
|
90
|
-
}
|
|
91
|
-
return version;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function normalizeOutboundVersionColumn<Row>(
|
|
95
|
-
row: Row,
|
|
96
|
-
versionColumn: string,
|
|
97
|
-
version: number
|
|
98
|
-
): Row {
|
|
99
|
-
if (row === null || typeof row !== 'object' || Array.isArray(row)) {
|
|
100
|
-
return row;
|
|
101
|
-
}
|
|
102
|
-
if (!Object.hasOwn(row, versionColumn)) {
|
|
103
|
-
return row;
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
...(row as Record<string, unknown>),
|
|
107
|
-
[versionColumn]: version,
|
|
108
|
-
} as Row;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Options for creating a declarative server handler.
|
|
113
|
-
*/
|
|
114
|
-
export interface CreateServerHandlerOptions<
|
|
115
|
-
ServerDB extends SyncCoreDb,
|
|
116
|
-
ClientDB,
|
|
117
|
-
TableName extends keyof ServerDB & keyof ClientDB & string,
|
|
118
|
-
Auth extends SyncServerAuth = SyncServerAuth,
|
|
119
|
-
ScopeDefs extends
|
|
120
|
-
readonly SimpleScopeDefinition[] = readonly SimpleScopeDefinition[],
|
|
121
|
-
> {
|
|
122
|
-
/** Table name in the database */
|
|
123
|
-
table: TableName;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Scope definitions for this table.
|
|
127
|
-
* Can be simple strings (column auto-derived) or objects with explicit mapping.
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* // Simple: column auto-derived from placeholder
|
|
132
|
-
* scopes: ['user:{user_id}', 'org:{org_id}']
|
|
133
|
-
*
|
|
134
|
-
* // Explicit: when column differs from pattern variable
|
|
135
|
-
* scopes: [
|
|
136
|
-
* { pattern: 'user:{user_id}', column: 'owner_id' }
|
|
137
|
-
* ]
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
scopes: ScopeDefs;
|
|
141
|
-
|
|
142
|
-
/** Primary key column name (default: 'id') */
|
|
143
|
-
primaryKey?: string;
|
|
144
|
-
|
|
145
|
-
/** Version column name (default: 'server_version') */
|
|
146
|
-
versionColumn?: string;
|
|
147
|
-
|
|
148
|
-
/** Tables this handler depends on (for bootstrap ordering) */
|
|
149
|
-
dependsOn?: string[];
|
|
150
|
-
|
|
151
|
-
/** TTL for cached snapshot chunks in ms */
|
|
152
|
-
snapshotChunkTtlMs?: number;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Stable binary snapshot column metadata used by binary bootstrap chunks.
|
|
156
|
-
*/
|
|
157
|
-
snapshotBinaryColumns?: readonly BinarySnapshotColumn[];
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Version-aware binary snapshot column metadata. Returning null intentionally
|
|
161
|
-
* disables fallback to current metadata.
|
|
162
|
-
*/
|
|
163
|
-
snapshotBinaryColumnsForVersion?: (
|
|
164
|
-
schemaVersion: number
|
|
165
|
-
) => readonly BinarySnapshotColumn[] | null | undefined;
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Optional generated binary snapshot encoder used by binary bootstrap chunks.
|
|
169
|
-
*/
|
|
170
|
-
snapshotBinaryEncoder?: BinarySnapshotRowsEncoder<
|
|
171
|
-
Selectable<ClientDB[TableName]>
|
|
172
|
-
>;
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Version-aware generated binary snapshot encoder. Returning null
|
|
176
|
-
* intentionally disables fallback to the current encoder.
|
|
177
|
-
*/
|
|
178
|
-
snapshotBinaryEncoderForVersion?: (
|
|
179
|
-
schemaVersion: number
|
|
180
|
-
) =>
|
|
181
|
-
| BinarySnapshotRowsEncoder<Selectable<ClientDB[TableName]>>
|
|
182
|
-
| null
|
|
183
|
-
| undefined;
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Resolve allowed scope values for the current actor.
|
|
187
|
-
* Called per request to determine what the actor can access.
|
|
188
|
-
*
|
|
189
|
-
* @example
|
|
190
|
-
* resolveScopes: async (ctx) => ({
|
|
191
|
-
* user_id: [ctx.actorId],
|
|
192
|
-
* project_id: await getProjectsForUser(ctx.db, ctx.actorId),
|
|
193
|
-
* })
|
|
194
|
-
*/
|
|
195
|
-
resolveScopes: (
|
|
196
|
-
ctx: ServerContext<ServerDB, Auth>
|
|
197
|
-
) => Promise<ScopeValuesFromPatterns<ScopeDefs>>;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Transform inbound row from client to server format.
|
|
201
|
-
* Use ctx.schemaVersion to handle older client versions.
|
|
202
|
-
*/
|
|
203
|
-
transformInbound?: (
|
|
204
|
-
row: Selectable<ClientDB[TableName]>,
|
|
205
|
-
ctx: { schemaVersion?: number }
|
|
206
|
-
) => Updateable<ServerDB[TableName]>;
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Transform outbound row from server to client format.
|
|
210
|
-
*/
|
|
211
|
-
transformOutbound?: (
|
|
212
|
-
row: Selectable<ServerDB[TableName]>
|
|
213
|
-
) => Selectable<ClientDB[TableName]>;
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Optional column codec resolver.
|
|
217
|
-
* Receives `{ table, column, sqlType?, dialect? }` and returns a codec.
|
|
218
|
-
* Only used by default snapshot/apply paths when the corresponding
|
|
219
|
-
* transform hook is not provided.
|
|
220
|
-
*/
|
|
221
|
-
codecs?: ColumnCodecSource;
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Dialect used for codec dialect overrides.
|
|
225
|
-
* Default: 'sqlite'
|
|
226
|
-
*/
|
|
227
|
-
codecDialect?: ColumnCodecDialect;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Authorize an operation before applying.
|
|
231
|
-
* Return true to allow, or an error object to reject.
|
|
232
|
-
*/
|
|
233
|
-
authorize?: (
|
|
234
|
-
ctx: ServerApplyOperationContext<ServerDB, Auth>,
|
|
235
|
-
op: SyncOperation
|
|
236
|
-
) => Promise<AuthorizeResult>;
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Override: Build snapshot query.
|
|
240
|
-
*/
|
|
241
|
-
snapshot?: ServerTableHandler<ServerDB, Auth>['snapshot'];
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Override: Apply operation.
|
|
245
|
-
*/
|
|
246
|
-
applyOperation?: ServerTableHandler<ServerDB, Auth>['applyOperation'];
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Hint for savepoint optimization on single-op commits.
|
|
250
|
-
*
|
|
251
|
-
* `true` means a rejected single operation never leaves durable writes
|
|
252
|
-
* before returning the rejection result.
|
|
253
|
-
*
|
|
254
|
-
* Defaults to `true` for the built-in applyOperation implementation and
|
|
255
|
-
* `false` when a custom applyOperation is provided.
|
|
256
|
-
*/
|
|
257
|
-
canRejectSingleOperationWithoutSavepoint?: boolean;
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Custom scope extraction from row (for complex scope logic).
|
|
261
|
-
*/
|
|
262
|
-
extractScopes?: (row: Record<string, unknown>) => StoredScopes;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Create a declarative server table handler with sensible defaults.
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```typescript
|
|
270
|
-
* import { createServerHandler } from '@syncular/server';
|
|
271
|
-
* import type { ServerDb } from '../db';
|
|
272
|
-
* import type { ClientDb } from '../../shared/client-db.generated';
|
|
273
|
-
*
|
|
274
|
-
* export const tasksHandler = createServerHandler<ServerDb, ClientDb, 'tasks'>({
|
|
275
|
-
* table: 'tasks',
|
|
276
|
-
* scopes: ['user:{user_id}'], // column auto-derived from placeholder
|
|
277
|
-
* resolveScopes: async (ctx) => ({
|
|
278
|
-
* user_id: [ctx.actorId],
|
|
279
|
-
* }),
|
|
280
|
-
* });
|
|
281
|
-
*
|
|
282
|
-
* // With custom column mapping:
|
|
283
|
-
* export const tasksHandler = createServerHandler<ServerDb, ClientDb, 'tasks'>({
|
|
284
|
-
* table: 'tasks',
|
|
285
|
-
* scopes: [{ pattern: 'user:{user_id}', column: 'owner_id' }],
|
|
286
|
-
* resolveScopes: async (ctx) => ({
|
|
287
|
-
* user_id: [ctx.actorId],
|
|
288
|
-
* }),
|
|
289
|
-
* });
|
|
290
|
-
* ```
|
|
291
|
-
*/
|
|
292
|
-
export function createServerHandler<
|
|
293
|
-
ServerDB extends SyncCoreDb,
|
|
294
|
-
ClientDB,
|
|
295
|
-
TableName extends keyof ServerDB & keyof ClientDB & string,
|
|
296
|
-
Auth extends SyncServerAuth = SyncServerAuth,
|
|
297
|
-
ScopeDefs extends
|
|
298
|
-
readonly SimpleScopeDefinition[] = readonly SimpleScopeDefinition[],
|
|
299
|
-
>(
|
|
300
|
-
options: CreateServerHandlerOptions<
|
|
301
|
-
ServerDB,
|
|
302
|
-
ClientDB,
|
|
303
|
-
TableName,
|
|
304
|
-
Auth,
|
|
305
|
-
ScopeDefs
|
|
306
|
-
>
|
|
307
|
-
): ServerTableHandler<ServerDB, Auth> {
|
|
308
|
-
type OverloadParameters<T> = T extends (...args: infer A) => unknown
|
|
309
|
-
? A
|
|
310
|
-
: never;
|
|
311
|
-
|
|
312
|
-
type UpdateSetObject = Extract<
|
|
313
|
-
OverloadParameters<
|
|
314
|
-
UpdateQueryBuilder<ServerDB, TableName, TableName, UpdateResult>['set']
|
|
315
|
-
>,
|
|
316
|
-
[unknown]
|
|
317
|
-
>[0];
|
|
318
|
-
|
|
319
|
-
const {
|
|
320
|
-
table,
|
|
321
|
-
scopes: scopeDefs,
|
|
322
|
-
primaryKey = 'id',
|
|
323
|
-
versionColumn = 'server_version',
|
|
324
|
-
dependsOn,
|
|
325
|
-
snapshotChunkTtlMs,
|
|
326
|
-
snapshotBinaryColumns,
|
|
327
|
-
snapshotBinaryColumnsForVersion,
|
|
328
|
-
snapshotBinaryEncoder,
|
|
329
|
-
snapshotBinaryEncoderForVersion,
|
|
330
|
-
resolveScopes,
|
|
331
|
-
transformInbound,
|
|
332
|
-
transformOutbound,
|
|
333
|
-
codecs,
|
|
334
|
-
codecDialect = 'sqlite',
|
|
335
|
-
authorize,
|
|
336
|
-
extractScopes: customExtractScopes,
|
|
337
|
-
} = options;
|
|
338
|
-
const resolveRowCodecs = createTableColumnCodecsResolver(codecs, {
|
|
339
|
-
dialect: codecDialect,
|
|
340
|
-
});
|
|
341
|
-
const primaryKeyColumn = primaryKey as keyof ServerDB[TableName] & string;
|
|
342
|
-
const qualifiedVersionRef = `${table}.${versionColumn}`;
|
|
343
|
-
const resolveTableCodecs = (row: Record<string, unknown>) =>
|
|
344
|
-
resolveRowCodecs(table, row);
|
|
345
|
-
const usePassthroughInbound = !transformInbound && codecs == null;
|
|
346
|
-
|
|
347
|
-
const { scopePatterns, scopeColumnsByVariable: scopeColumns } =
|
|
348
|
-
createSingleVariableScopeMetadata(scopeDefs);
|
|
349
|
-
const scopeColumnEntries = Object.entries(scopeColumns);
|
|
350
|
-
const requiredScopeKeys = Object.keys(scopeColumns);
|
|
351
|
-
|
|
352
|
-
// Default extractScopes from scope columns
|
|
353
|
-
const defaultExtractScopes = (row: Record<string, unknown>): StoredScopes => {
|
|
354
|
-
const scopes: StoredScopes = {};
|
|
355
|
-
for (const [varName, columnName] of Object.entries(scopeColumns)) {
|
|
356
|
-
const raw = row[columnName];
|
|
357
|
-
if (raw === null || raw === undefined) continue;
|
|
358
|
-
const value = String(raw);
|
|
359
|
-
if (value.length > 0) {
|
|
360
|
-
scopes[varName] = value;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return scopes;
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
const extractScopesImpl = customExtractScopes ?? defaultExtractScopes;
|
|
367
|
-
|
|
368
|
-
const resolveScopesImpl = async (
|
|
369
|
-
ctx: ServerContext<ServerDB, Auth>
|
|
370
|
-
): Promise<ScopeValues> => {
|
|
371
|
-
const resolved = await resolveScopes(ctx);
|
|
372
|
-
const normalized: ScopeValues = {};
|
|
373
|
-
for (const [scopeKey, scopeValue] of Object.entries(resolved)) {
|
|
374
|
-
// null/undefined means "no access for this scope" and is skipped.
|
|
375
|
-
if (scopeValue === null || scopeValue === undefined) continue;
|
|
376
|
-
if (typeof scopeValue === 'string') {
|
|
377
|
-
normalized[scopeKey] = scopeValue;
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
if (Array.isArray(scopeValue)) {
|
|
381
|
-
const invalidIndex = scopeValue.findIndex(
|
|
382
|
-
(entry) => typeof entry !== 'string'
|
|
383
|
-
);
|
|
384
|
-
if (invalidIndex !== -1) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
`resolveScopes() for table "${table}" returned a non-string entry ` +
|
|
387
|
-
`(${typeof scopeValue[invalidIndex]}) at index ${invalidIndex} of scope "${scopeKey}". ` +
|
|
388
|
-
'Scope values must be strings or string arrays.'
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
normalized[scopeKey] = scopeValue;
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
throw new Error(
|
|
395
|
-
`resolveScopes() for table "${table}" returned an invalid value ` +
|
|
396
|
-
`(${typeof scopeValue}) for scope "${scopeKey}". ` +
|
|
397
|
-
'Scope values must be strings or string arrays.'
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
return normalized;
|
|
401
|
-
};
|
|
402
|
-
const writeScopeCache = new WeakMap<
|
|
403
|
-
ServerApplyOperationContext<ServerDB, Auth>,
|
|
404
|
-
Promise<ScopeValues>
|
|
405
|
-
>();
|
|
406
|
-
|
|
407
|
-
const resolveWriteScopes = (
|
|
408
|
-
ctx: ServerApplyOperationContext<ServerDB, Auth>
|
|
409
|
-
): Promise<ScopeValues> => {
|
|
410
|
-
const cached = writeScopeCache.get(ctx);
|
|
411
|
-
if (cached) return cached;
|
|
412
|
-
const loaded = resolveScopesImpl(ctx);
|
|
413
|
-
writeScopeCache.set(ctx, loaded);
|
|
414
|
-
return loaded;
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
const authorizeRowScopes = async (
|
|
418
|
-
ctx: ServerApplyOperationContext<ServerDB, Auth>,
|
|
419
|
-
row: Record<string, unknown>
|
|
420
|
-
): Promise<AuthorizeResult> => {
|
|
421
|
-
if (requiredScopeKeys.length === 0) return true;
|
|
422
|
-
|
|
423
|
-
let allowedScopes: ScopeValues;
|
|
424
|
-
try {
|
|
425
|
-
allowedScopes = await resolveWriteScopes(ctx);
|
|
426
|
-
} catch (resolveErr) {
|
|
427
|
-
console.error(
|
|
428
|
-
`[resolveScopes] Failed for table ${table} during write authorization:`,
|
|
429
|
-
resolveErr
|
|
430
|
-
);
|
|
431
|
-
return {
|
|
432
|
-
error: 'Forbidden',
|
|
433
|
-
code: 'sync.forbidden',
|
|
434
|
-
retriable: false,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const rowScopes = extractScopesImpl(row);
|
|
439
|
-
if (
|
|
440
|
-
rowScopesAllowed({
|
|
441
|
-
rowScopes,
|
|
442
|
-
allowedScopes,
|
|
443
|
-
requiredScopeKeys,
|
|
444
|
-
})
|
|
445
|
-
) {
|
|
446
|
-
return true;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
error: 'Forbidden',
|
|
451
|
-
code: 'sync.forbidden',
|
|
452
|
-
retriable: false,
|
|
453
|
-
};
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
const rejectionResult = (
|
|
457
|
-
opIndex: number,
|
|
458
|
-
rejection: Exclude<AuthorizeResult, true>
|
|
459
|
-
): ApplyOperationResult => ({
|
|
460
|
-
result: {
|
|
461
|
-
opIndex,
|
|
462
|
-
status: 'error',
|
|
463
|
-
error: rejection.error,
|
|
464
|
-
code: rejection.code,
|
|
465
|
-
retriable: rejection.retriable ?? false,
|
|
466
|
-
},
|
|
467
|
-
emittedChanges: [],
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
const applyOutboundTransform = (
|
|
471
|
-
row: Selectable<ServerDB[TableName]>
|
|
472
|
-
): Selectable<ClientDB[TableName]> => {
|
|
473
|
-
if (transformOutbound) {
|
|
474
|
-
return transformOutbound(row);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const recordRow = row as Record<string, unknown>;
|
|
478
|
-
const transformed = applyCodecsFromDbRow(
|
|
479
|
-
recordRow,
|
|
480
|
-
resolveTableCodecs(recordRow),
|
|
481
|
-
codecDialect
|
|
482
|
-
);
|
|
483
|
-
return transformed as Selectable<ClientDB[TableName]>;
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
const applyInboundTransform = (
|
|
487
|
-
row: Record<string, unknown>,
|
|
488
|
-
schemaVersion: number | undefined
|
|
489
|
-
): Updateable<ServerDB[TableName]> => {
|
|
490
|
-
if (usePassthroughInbound) {
|
|
491
|
-
return row as Updateable<ServerDB[TableName]>;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (transformInbound) {
|
|
495
|
-
return transformInbound(row as Selectable<ClientDB[TableName]>, {
|
|
496
|
-
schemaVersion,
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const transformed = applyCodecsToDbRow(
|
|
501
|
-
row,
|
|
502
|
-
resolveTableCodecs(row),
|
|
503
|
-
codecDialect
|
|
504
|
-
);
|
|
505
|
-
return transformed as Updateable<ServerDB[TableName]>;
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// Default snapshot implementation
|
|
509
|
-
const defaultSnapshot = async (
|
|
510
|
-
ctx: ServerSnapshotContext<ServerDB, string, Auth>,
|
|
511
|
-
_params: Record<string, unknown> | undefined
|
|
512
|
-
): Promise<{ rows: unknown[]; nextCursor: string | null }> => {
|
|
513
|
-
const trx = ctx.db;
|
|
514
|
-
const { ref } = trx.dynamic;
|
|
515
|
-
const scopeValues = ctx.scopeValues;
|
|
516
|
-
|
|
517
|
-
const pageSize = Math.max(1, ctx.limit);
|
|
518
|
-
|
|
519
|
-
let q = trx.selectFrom(table) as SelectQueryBuilder<
|
|
520
|
-
ServerDB,
|
|
521
|
-
keyof ServerDB & string,
|
|
522
|
-
Record<string, unknown>
|
|
523
|
-
>;
|
|
524
|
-
|
|
525
|
-
q = q.selectAll() as SelectQueryBuilder<
|
|
526
|
-
ServerDB,
|
|
527
|
-
keyof ServerDB & string,
|
|
528
|
-
Record<string, unknown>
|
|
529
|
-
>;
|
|
530
|
-
|
|
531
|
-
for (const [varName, columnName] of scopeColumnEntries) {
|
|
532
|
-
const values = scopeValues[varName];
|
|
533
|
-
if (values === undefined) continue;
|
|
534
|
-
const normalized = Array.isArray(values) ? values : [values];
|
|
535
|
-
if (normalized.length === 0) continue;
|
|
536
|
-
if (normalized.length === 1) {
|
|
537
|
-
q = q.where(ref<string>(columnName), '=', normalized[0]);
|
|
538
|
-
} else {
|
|
539
|
-
q = q.where(ref<string>(columnName), 'in', normalized);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (ctx.cursor !== null) {
|
|
544
|
-
q = q.where(ref<string>(primaryKey), '>', ctx.cursor);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
const rows = await q
|
|
548
|
-
.orderBy(ref<string>(primaryKey), 'asc')
|
|
549
|
-
.limit(pageSize + 1)
|
|
550
|
-
.execute();
|
|
551
|
-
|
|
552
|
-
const hasMore = rows.length > pageSize;
|
|
553
|
-
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
554
|
-
const lastRow = pageRows[pageRows.length - 1] as
|
|
555
|
-
| (typeof rows)[number]
|
|
556
|
-
| undefined;
|
|
557
|
-
const nextCursor = hasMore
|
|
558
|
-
? ((lastRow as Record<string, unknown> | undefined)?.[primaryKey] as
|
|
559
|
-
| string
|
|
560
|
-
| undefined)
|
|
561
|
-
: null;
|
|
562
|
-
|
|
563
|
-
let outputRows: unknown[];
|
|
564
|
-
if (transformOutbound) {
|
|
565
|
-
outputRows = pageRows.map((r) =>
|
|
566
|
-
applyOutboundTransform(r as Selectable<ServerDB[TableName]>)
|
|
567
|
-
);
|
|
568
|
-
} else {
|
|
569
|
-
const firstRow = pageRows[0] as Record<string, unknown> | undefined;
|
|
570
|
-
const tableCodecs = firstRow
|
|
571
|
-
? toTableColumnCodecs(table, codecs, Object.keys(firstRow), {
|
|
572
|
-
dialect: codecDialect,
|
|
573
|
-
})
|
|
574
|
-
: {};
|
|
575
|
-
if (Object.keys(tableCodecs).length === 0) {
|
|
576
|
-
outputRows = pageRows;
|
|
577
|
-
} else {
|
|
578
|
-
outputRows = pageRows.map((r) =>
|
|
579
|
-
applyCodecsFromDbRow(
|
|
580
|
-
r as Record<string, unknown>,
|
|
581
|
-
tableCodecs,
|
|
582
|
-
codecDialect
|
|
583
|
-
)
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return {
|
|
589
|
-
rows: outputRows,
|
|
590
|
-
nextCursor:
|
|
591
|
-
typeof nextCursor === 'string' && nextCursor.length > 0
|
|
592
|
-
? nextCursor
|
|
593
|
-
: null,
|
|
594
|
-
};
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
// Default applyOperation implementation
|
|
598
|
-
const defaultApplyOperation = async (
|
|
599
|
-
ctx: ServerApplyOperationContext<ServerDB, Auth>,
|
|
600
|
-
op: SyncOperation,
|
|
601
|
-
opIndex: number
|
|
602
|
-
): Promise<ApplyOperationResult> => {
|
|
603
|
-
const trx = ctx.trx;
|
|
604
|
-
const { ref } = trx.dynamic;
|
|
605
|
-
|
|
606
|
-
if (op.table !== table) {
|
|
607
|
-
return {
|
|
608
|
-
result: {
|
|
609
|
-
opIndex,
|
|
610
|
-
status: 'error',
|
|
611
|
-
error: `Unknown table: ${op.table}`,
|
|
612
|
-
code: 'sync.unknown_table',
|
|
613
|
-
retriable: false,
|
|
614
|
-
},
|
|
615
|
-
emittedChanges: [],
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Run authorization if provided
|
|
620
|
-
if (authorize) {
|
|
621
|
-
const authResult = await authorize(ctx, op);
|
|
622
|
-
if (authResult !== true) {
|
|
623
|
-
return {
|
|
624
|
-
result: {
|
|
625
|
-
opIndex,
|
|
626
|
-
status: 'error',
|
|
627
|
-
error: authResult.error,
|
|
628
|
-
code: authResult.code,
|
|
629
|
-
retriable: authResult.retriable ?? false,
|
|
630
|
-
},
|
|
631
|
-
emittedChanges: [],
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
// Handle delete
|
|
637
|
-
if (op.op === 'delete') {
|
|
638
|
-
const existingRow = (await (
|
|
639
|
-
trx.selectFrom(table).selectAll() as SelectQueryBuilder<
|
|
640
|
-
ServerDB,
|
|
641
|
-
keyof ServerDB & string,
|
|
642
|
-
Record<string, unknown>
|
|
643
|
-
>
|
|
644
|
-
)
|
|
645
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
646
|
-
.executeTakeFirst()) as Record<string, unknown> | undefined;
|
|
647
|
-
|
|
648
|
-
if (!existingRow) {
|
|
649
|
-
return { result: { opIndex, status: 'applied' }, emittedChanges: [] };
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const scopeAuth = await authorizeRowScopes(ctx, existingRow);
|
|
653
|
-
if (scopeAuth !== true) {
|
|
654
|
-
return rejectionResult(opIndex, scopeAuth);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const deleted = await (
|
|
658
|
-
trx.deleteFrom(table) as DeleteQueryBuilder<
|
|
659
|
-
ServerDB,
|
|
660
|
-
keyof ServerDB & string,
|
|
661
|
-
DeleteResult
|
|
662
|
-
>
|
|
663
|
-
)
|
|
664
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
665
|
-
.returningAll()
|
|
666
|
-
.executeTakeFirst();
|
|
667
|
-
|
|
668
|
-
const deletedRow = deleted as Record<string, unknown> | undefined;
|
|
669
|
-
if (!deletedRow)
|
|
670
|
-
return { result: { opIndex, status: 'applied' }, emittedChanges: [] };
|
|
671
|
-
|
|
672
|
-
// Extract scopes from existing row for the delete emission
|
|
673
|
-
const scopes = extractScopesImpl(deletedRow);
|
|
674
|
-
|
|
675
|
-
const emitted: EmittedChange = {
|
|
676
|
-
table,
|
|
677
|
-
row_id: op.row_id,
|
|
678
|
-
op: 'delete',
|
|
679
|
-
row_json: null,
|
|
680
|
-
row_version: null,
|
|
681
|
-
scopes,
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
return {
|
|
685
|
-
result: { opIndex, status: 'applied' },
|
|
686
|
-
emittedChanges: [emitted],
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Handle upsert
|
|
691
|
-
const rawPayload = op.payload ?? {};
|
|
692
|
-
const payloadRecord =
|
|
693
|
-
rawPayload !== null && typeof rawPayload === 'object'
|
|
694
|
-
? (rawPayload as Record<string, unknown>)
|
|
695
|
-
: {};
|
|
696
|
-
const payload = applyInboundTransform(payloadRecord, ctx.schemaVersion);
|
|
697
|
-
const rowForInsertScopeCheck: Record<string, unknown> = {
|
|
698
|
-
...payload,
|
|
699
|
-
[primaryKey]: op.row_id,
|
|
700
|
-
[versionColumn]: 1,
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
const existingRowBeforeWrite = (await (
|
|
704
|
-
trx.selectFrom(table).selectAll() as SelectQueryBuilder<
|
|
705
|
-
ServerDB,
|
|
706
|
-
keyof ServerDB & string,
|
|
707
|
-
Record<string, unknown>
|
|
708
|
-
>
|
|
709
|
-
)
|
|
710
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
711
|
-
.executeTakeFirst()) as Record<string, unknown> | undefined;
|
|
712
|
-
|
|
713
|
-
const rowScopeAuth = await authorizeRowScopes(
|
|
714
|
-
ctx,
|
|
715
|
-
existingRowBeforeWrite ?? rowForInsertScopeCheck
|
|
716
|
-
);
|
|
717
|
-
if (rowScopeAuth !== true) {
|
|
718
|
-
return rejectionResult(opIndex, rowScopeAuth);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
let updated: Record<string, unknown> | undefined;
|
|
722
|
-
let constraintError: { message: string; code: string } | null = null;
|
|
723
|
-
|
|
724
|
-
try {
|
|
725
|
-
if (op.base_version != null) {
|
|
726
|
-
const expectedVersion = op.base_version;
|
|
727
|
-
const conditionalUpdateSet: Record<string, unknown> = {
|
|
728
|
-
...payload,
|
|
729
|
-
[versionColumn]: expectedVersion + 1,
|
|
730
|
-
};
|
|
731
|
-
delete conditionalUpdateSet[primaryKey];
|
|
732
|
-
for (const col of Object.values(scopeColumns)) {
|
|
733
|
-
delete conditionalUpdateSet[col];
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
updated = (await (
|
|
737
|
-
trx.updateTable(table) as UpdateQueryBuilder<
|
|
738
|
-
ServerDB,
|
|
739
|
-
TableName,
|
|
740
|
-
TableName,
|
|
741
|
-
UpdateResult
|
|
742
|
-
>
|
|
743
|
-
)
|
|
744
|
-
.set(conditionalUpdateSet as UpdateSetObject)
|
|
745
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
746
|
-
.where(ref<string>(versionColumn), '=', expectedVersion)
|
|
747
|
-
.returningAll()
|
|
748
|
-
.executeTakeFirst()) as Record<string, unknown> | undefined;
|
|
749
|
-
|
|
750
|
-
if (!updated) {
|
|
751
|
-
let conflictRow = await (
|
|
752
|
-
trx.selectFrom(table).selectAll() as SelectQueryBuilder<
|
|
753
|
-
ServerDB,
|
|
754
|
-
keyof ServerDB & string,
|
|
755
|
-
Record<string, unknown>
|
|
756
|
-
>
|
|
757
|
-
)
|
|
758
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
759
|
-
.executeTakeFirst();
|
|
760
|
-
|
|
761
|
-
if (!conflictRow && expectedVersion === 0) {
|
|
762
|
-
const insertValues: Record<string, unknown> = {
|
|
763
|
-
...payload,
|
|
764
|
-
[primaryKey]: op.row_id,
|
|
765
|
-
[versionColumn]: 1,
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
try {
|
|
769
|
-
updated = (await (
|
|
770
|
-
trx.insertInto(table) as InsertQueryBuilder<
|
|
771
|
-
ServerDB,
|
|
772
|
-
TableName,
|
|
773
|
-
InsertResult
|
|
774
|
-
>
|
|
775
|
-
)
|
|
776
|
-
.values(insertValues as Insertable<ServerDB[TableName]>)
|
|
777
|
-
.returningAll()
|
|
778
|
-
.executeTakeFirst()) as Record<string, unknown> | undefined;
|
|
779
|
-
} catch (err) {
|
|
780
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
781
|
-
if (!isConstraintViolationError(message)) {
|
|
782
|
-
throw err;
|
|
783
|
-
}
|
|
784
|
-
conflictRow = await (
|
|
785
|
-
trx.selectFrom(table).selectAll() as SelectQueryBuilder<
|
|
786
|
-
ServerDB,
|
|
787
|
-
keyof ServerDB & string,
|
|
788
|
-
Record<string, unknown>
|
|
789
|
-
>
|
|
790
|
-
)
|
|
791
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
792
|
-
.executeTakeFirst();
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
if (!updated && !conflictRow) {
|
|
797
|
-
return {
|
|
798
|
-
result: {
|
|
799
|
-
opIndex,
|
|
800
|
-
status: 'error',
|
|
801
|
-
error: 'Row not found for base version',
|
|
802
|
-
code: 'sync.row_missing',
|
|
803
|
-
retriable: false,
|
|
804
|
-
},
|
|
805
|
-
emittedChanges: [],
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (!updated && conflictRow) {
|
|
810
|
-
const conflictScopeAuth = await authorizeRowScopes(
|
|
811
|
-
ctx,
|
|
812
|
-
conflictRow
|
|
813
|
-
);
|
|
814
|
-
if (conflictScopeAuth !== true) {
|
|
815
|
-
return rejectionResult(opIndex, conflictScopeAuth);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
const existingVersion = readSafeVersionColumn(
|
|
819
|
-
conflictRow,
|
|
820
|
-
versionColumn,
|
|
821
|
-
0
|
|
822
|
-
);
|
|
823
|
-
return {
|
|
824
|
-
result: {
|
|
825
|
-
opIndex,
|
|
826
|
-
status: 'conflict',
|
|
827
|
-
message: `Version conflict: server=${existingVersion}, base=${expectedVersion}`,
|
|
828
|
-
code: 'sync.version_conflict',
|
|
829
|
-
server_version: existingVersion,
|
|
830
|
-
server_row: normalizeOutboundVersionColumn(
|
|
831
|
-
applyOutboundTransform(
|
|
832
|
-
conflictRow as Selectable<ServerDB[TableName]>
|
|
833
|
-
),
|
|
834
|
-
versionColumn,
|
|
835
|
-
existingVersion
|
|
836
|
-
),
|
|
837
|
-
},
|
|
838
|
-
emittedChanges: [],
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
} else {
|
|
843
|
-
const updateSet: Record<string, unknown> = {
|
|
844
|
-
...payload,
|
|
845
|
-
[versionColumn]: sql`${sql.ref(qualifiedVersionRef)} + 1`,
|
|
846
|
-
};
|
|
847
|
-
delete updateSet[primaryKey];
|
|
848
|
-
for (const col of Object.values(scopeColumns)) {
|
|
849
|
-
delete updateSet[col];
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
const insertValues: Record<string, unknown> = {
|
|
853
|
-
...payload,
|
|
854
|
-
[primaryKey]: op.row_id,
|
|
855
|
-
[versionColumn]: 1,
|
|
856
|
-
};
|
|
857
|
-
|
|
858
|
-
updated = (await (
|
|
859
|
-
trx.insertInto(table) as InsertQueryBuilder<
|
|
860
|
-
ServerDB,
|
|
861
|
-
TableName,
|
|
862
|
-
InsertResult
|
|
863
|
-
>
|
|
864
|
-
)
|
|
865
|
-
.values(insertValues as Insertable<ServerDB[TableName]>)
|
|
866
|
-
.onConflict((oc) =>
|
|
867
|
-
oc
|
|
868
|
-
.column(primaryKeyColumn)
|
|
869
|
-
.doUpdateSet(updateSet as UpdateSetObject)
|
|
870
|
-
)
|
|
871
|
-
.returningAll()
|
|
872
|
-
.executeTakeFirst()) as Record<string, unknown> | undefined;
|
|
873
|
-
}
|
|
874
|
-
} catch (err) {
|
|
875
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
876
|
-
if (op.base_version != null && isMissingColumnReferenceError(message)) {
|
|
877
|
-
const row = await (
|
|
878
|
-
trx.selectFrom(table).selectAll() as SelectQueryBuilder<
|
|
879
|
-
ServerDB,
|
|
880
|
-
keyof ServerDB & string,
|
|
881
|
-
Record<string, unknown>
|
|
882
|
-
>
|
|
883
|
-
)
|
|
884
|
-
.where(ref<string>(primaryKey), '=', op.row_id)
|
|
885
|
-
.executeTakeFirst();
|
|
886
|
-
if (!row) {
|
|
887
|
-
return {
|
|
888
|
-
result: {
|
|
889
|
-
opIndex,
|
|
890
|
-
status: 'error',
|
|
891
|
-
error: 'Row not found for base version',
|
|
892
|
-
code: 'sync.row_missing',
|
|
893
|
-
retriable: false,
|
|
894
|
-
},
|
|
895
|
-
emittedChanges: [],
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
if (!isConstraintViolationError(message)) {
|
|
901
|
-
throw err;
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
constraintError = {
|
|
905
|
-
message,
|
|
906
|
-
code: classifyConstraintViolationCode(message),
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
if (constraintError) {
|
|
911
|
-
return {
|
|
912
|
-
result: {
|
|
913
|
-
opIndex,
|
|
914
|
-
status: 'error',
|
|
915
|
-
error: constraintError.message,
|
|
916
|
-
code: constraintError.code,
|
|
917
|
-
retriable: false,
|
|
918
|
-
},
|
|
919
|
-
emittedChanges: [],
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
if (!updated) {
|
|
924
|
-
throw new Error('Updated row is missing after applyOperation');
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
const updatedRow = updated;
|
|
928
|
-
const rowVersion = readSafeVersionColumn(updatedRow, versionColumn, 1);
|
|
929
|
-
|
|
930
|
-
// Extract scopes from updated row
|
|
931
|
-
const scopes = extractScopesImpl(updatedRow);
|
|
932
|
-
|
|
933
|
-
// Transform outbound for emitted change
|
|
934
|
-
const rowJson = normalizeOutboundVersionColumn(
|
|
935
|
-
applyOutboundTransform(updated as Selectable<ServerDB[TableName]>),
|
|
936
|
-
versionColumn,
|
|
937
|
-
rowVersion
|
|
938
|
-
);
|
|
939
|
-
|
|
940
|
-
const emitted: EmittedChange = {
|
|
941
|
-
table,
|
|
942
|
-
row_id: op.row_id,
|
|
943
|
-
op: 'upsert',
|
|
944
|
-
row_json: rowJson,
|
|
945
|
-
row_version: rowVersion,
|
|
946
|
-
scopes,
|
|
947
|
-
};
|
|
948
|
-
|
|
949
|
-
return {
|
|
950
|
-
result: { opIndex, status: 'applied' },
|
|
951
|
-
emittedChanges: [emitted],
|
|
952
|
-
};
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
const defaultApplyOperationBatch = async (
|
|
956
|
-
ctx: ServerApplyOperationContext<ServerDB, Auth>,
|
|
957
|
-
operations: BatchApplyOperationInput[]
|
|
958
|
-
): Promise<ApplyOperationResult[]> => {
|
|
959
|
-
if (operations.length === 0) return [];
|
|
960
|
-
|
|
961
|
-
const runSequentialFallback = async (): Promise<ApplyOperationResult[]> => {
|
|
962
|
-
const sequential: ApplyOperationResult[] = [];
|
|
963
|
-
for (const entry of operations) {
|
|
964
|
-
const applied = await defaultApplyOperation(
|
|
965
|
-
ctx,
|
|
966
|
-
entry.op,
|
|
967
|
-
entry.opIndex
|
|
968
|
-
);
|
|
969
|
-
sequential.push(applied);
|
|
970
|
-
if (applied.result.status !== 'applied') {
|
|
971
|
-
break;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
return sequential;
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
if (operations.length === 1 || authorize || requiredScopeKeys.length > 0) {
|
|
978
|
-
return runSequentialFallback();
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
const trx = ctx.trx;
|
|
982
|
-
const prepared: Array<{
|
|
983
|
-
entry: BatchApplyOperationInput;
|
|
984
|
-
payload: Record<string, unknown>;
|
|
985
|
-
insertValues: Record<string, unknown>;
|
|
986
|
-
}> = [];
|
|
987
|
-
const seenRowIds = new Set<string>();
|
|
988
|
-
const payloadKeySignatures = new Set<string>();
|
|
989
|
-
|
|
990
|
-
for (const entry of operations) {
|
|
991
|
-
const op = entry.op;
|
|
992
|
-
if (op.table !== table || op.op !== 'upsert' || op.base_version != null) {
|
|
993
|
-
return runSequentialFallback();
|
|
994
|
-
}
|
|
995
|
-
if (seenRowIds.has(op.row_id)) {
|
|
996
|
-
return runSequentialFallback();
|
|
997
|
-
}
|
|
998
|
-
seenRowIds.add(op.row_id);
|
|
999
|
-
|
|
1000
|
-
const rawPayload = op.payload ?? {};
|
|
1001
|
-
const payloadRecord =
|
|
1002
|
-
rawPayload !== null && typeof rawPayload === 'object'
|
|
1003
|
-
? (rawPayload as Record<string, unknown>)
|
|
1004
|
-
: {};
|
|
1005
|
-
const payload = applyInboundTransform(
|
|
1006
|
-
payloadRecord,
|
|
1007
|
-
ctx.schemaVersion
|
|
1008
|
-
) as Record<string, unknown>;
|
|
1009
|
-
|
|
1010
|
-
payloadKeySignatures.add(Object.keys(payload).sort().join('\u0000'));
|
|
1011
|
-
|
|
1012
|
-
const insertValues: Record<string, unknown> = {
|
|
1013
|
-
...payload,
|
|
1014
|
-
[primaryKey]: op.row_id,
|
|
1015
|
-
[versionColumn]: 1,
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
|
-
prepared.push({
|
|
1019
|
-
entry,
|
|
1020
|
-
payload,
|
|
1021
|
-
insertValues,
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
if (prepared.length <= 1 || payloadKeySignatures.size !== 1) {
|
|
1026
|
-
return runSequentialFallback();
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
const basePayload = prepared[0]!.payload;
|
|
1030
|
-
const updateSet: Record<string, unknown> = {
|
|
1031
|
-
...basePayload,
|
|
1032
|
-
[versionColumn]: sql`${sql.ref(qualifiedVersionRef)} + 1`,
|
|
1033
|
-
};
|
|
1034
|
-
delete updateSet[primaryKey];
|
|
1035
|
-
for (const col of Object.values(scopeColumns)) {
|
|
1036
|
-
delete updateSet[col];
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
try {
|
|
1040
|
-
const updatedRows = (await (
|
|
1041
|
-
trx.insertInto(table) as InsertQueryBuilder<
|
|
1042
|
-
ServerDB,
|
|
1043
|
-
TableName,
|
|
1044
|
-
InsertResult
|
|
1045
|
-
>
|
|
1046
|
-
)
|
|
1047
|
-
.values(
|
|
1048
|
-
prepared.map(
|
|
1049
|
-
(item) => item.insertValues as Insertable<ServerDB[TableName]>
|
|
1050
|
-
)
|
|
1051
|
-
)
|
|
1052
|
-
.onConflict((oc) =>
|
|
1053
|
-
oc.column(primaryKeyColumn).doUpdateSet(updateSet as UpdateSetObject)
|
|
1054
|
-
)
|
|
1055
|
-
.returningAll()
|
|
1056
|
-
.execute()) as Record<string, unknown>[];
|
|
1057
|
-
|
|
1058
|
-
const updatedById = new Map<string, Record<string, unknown>>();
|
|
1059
|
-
for (const row of updatedRows) {
|
|
1060
|
-
const rowIdValue = row[primaryKey];
|
|
1061
|
-
if (rowIdValue === null || rowIdValue === undefined) continue;
|
|
1062
|
-
updatedById.set(String(rowIdValue), row);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
const results: ApplyOperationResult[] = [];
|
|
1066
|
-
for (const item of prepared) {
|
|
1067
|
-
const rowId = item.entry.op.row_id;
|
|
1068
|
-
const updated = updatedById.get(rowId);
|
|
1069
|
-
if (!updated) {
|
|
1070
|
-
return runSequentialFallback();
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const rowVersion = readSafeVersionColumn(updated, versionColumn, 1);
|
|
1074
|
-
const scopes = extractScopesImpl(updated);
|
|
1075
|
-
const rowJson = normalizeOutboundVersionColumn(
|
|
1076
|
-
applyOutboundTransform(updated as Selectable<ServerDB[TableName]>),
|
|
1077
|
-
versionColumn,
|
|
1078
|
-
rowVersion
|
|
1079
|
-
);
|
|
1080
|
-
|
|
1081
|
-
results.push({
|
|
1082
|
-
result: { opIndex: item.entry.opIndex, status: 'applied' },
|
|
1083
|
-
emittedChanges: [
|
|
1084
|
-
{
|
|
1085
|
-
table,
|
|
1086
|
-
row_id: rowId,
|
|
1087
|
-
op: 'upsert',
|
|
1088
|
-
row_json: rowJson,
|
|
1089
|
-
row_version: rowVersion,
|
|
1090
|
-
scopes,
|
|
1091
|
-
},
|
|
1092
|
-
],
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
return results;
|
|
1097
|
-
} catch (err) {
|
|
1098
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1099
|
-
if (isConstraintViolationError(message)) {
|
|
1100
|
-
return runSequentialFallback();
|
|
1101
|
-
}
|
|
1102
|
-
throw err;
|
|
1103
|
-
}
|
|
1104
|
-
};
|
|
1105
|
-
|
|
1106
|
-
return {
|
|
1107
|
-
table,
|
|
1108
|
-
primaryKeyColumn,
|
|
1109
|
-
scopePatterns,
|
|
1110
|
-
dependsOn,
|
|
1111
|
-
snapshotChunkTtlMs,
|
|
1112
|
-
snapshotBinaryColumns,
|
|
1113
|
-
snapshotBinaryColumnsForVersion,
|
|
1114
|
-
snapshotBinaryEncoder: snapshotBinaryEncoder as
|
|
1115
|
-
| BinarySnapshotRowsEncoder
|
|
1116
|
-
| undefined,
|
|
1117
|
-
snapshotBinaryEncoderForVersion: snapshotBinaryEncoderForVersion as
|
|
1118
|
-
| ((
|
|
1119
|
-
schemaVersion: number
|
|
1120
|
-
) => BinarySnapshotRowsEncoder | null | undefined)
|
|
1121
|
-
| undefined,
|
|
1122
|
-
canRejectSingleOperationWithoutSavepoint:
|
|
1123
|
-
options.canRejectSingleOperationWithoutSavepoint ??
|
|
1124
|
-
options.applyOperation === undefined,
|
|
1125
|
-
resolveScopes: resolveScopesImpl,
|
|
1126
|
-
extractScopes: extractScopesImpl,
|
|
1127
|
-
snapshot: options.snapshot ?? defaultSnapshot,
|
|
1128
|
-
applyOperation: options.applyOperation ?? defaultApplyOperation,
|
|
1129
|
-
applyOperationBatch:
|
|
1130
|
-
options.applyOperation === undefined
|
|
1131
|
-
? defaultApplyOperationBatch
|
|
1132
|
-
: undefined,
|
|
1133
|
-
};
|
|
1134
|
-
}
|