@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
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relational current-row storage (DESIGN-relational-server-storage.md).
|
|
3
|
+
*
|
|
4
|
+
* Every synced table is a REAL table in the server database — the app's
|
|
5
|
+
* columns with proper type affinities, queryable with plain SQL/joins/BI —
|
|
6
|
+
* plus five `_sync_*` meta columns:
|
|
7
|
+
*
|
|
8
|
+
* _sync_partition TEXT partition key (multi-partition servers
|
|
9
|
+
* share one database; same app PK in two
|
|
10
|
+
* partitions must coexist)
|
|
11
|
+
* _sync_row_id TEXT the change rowId (§2.2) — the string
|
|
12
|
+
* rendering of the app PK. The storage key
|
|
13
|
+
* stays TEXT so lookup and scan-pagination
|
|
14
|
+
* semantics are byte-identical to the old
|
|
15
|
+
* generic store regardless of the PK's type.
|
|
16
|
+
* _sync_server_version INTEGER §2.2 server version
|
|
17
|
+
* _sync_scopes TEXT/JSONB the stored-scope map the §3.4 authz read
|
|
18
|
+
* consumes (also present as real columns;
|
|
19
|
+
* the map is cheaper to read back)
|
|
20
|
+
* _sync_payload BLOB/BYTEA the verbatim row-codec bytes — the wire
|
|
21
|
+
* source of truth. The serve path reads
|
|
22
|
+
* THIS, never re-encodes from typed columns,
|
|
23
|
+
* so sync round-trips are byte-identical by
|
|
24
|
+
* construction. Typed columns are a
|
|
25
|
+
* queryable projection.
|
|
26
|
+
*
|
|
27
|
+
* PRIMARY KEY (_sync_partition, _sync_row_id).
|
|
28
|
+
*
|
|
29
|
+
* The typed columns are written from `decodeRow(payload)` in the same
|
|
30
|
+
* statement as the payload, so projection and payload cannot drift except
|
|
31
|
+
* through a codec bug — which the round-trip contract tests would catch.
|
|
32
|
+
*
|
|
33
|
+
* Dialect notes:
|
|
34
|
+
* - postgres maps `json` → JSONB (queryable: `->>`, GIN). JSONB normalizes
|
|
35
|
+
* bytes, which is safe because the wire never reads typed columns.
|
|
36
|
+
* A json value that fails to parse binds NULL on postgres (the payload
|
|
37
|
+
* still holds it verbatim); sqlite stores the text as-is.
|
|
38
|
+
* - D1's JSON transport represents integers as JS doubles; app integer
|
|
39
|
+
* columns beyond 2^53 lose precision in the PROJECTION only — the
|
|
40
|
+
* payload is exact, sync is unaffected.
|
|
41
|
+
* - Migration-added columns (ALTER TABLE ADD COLUMN) are always nullable at
|
|
42
|
+
* the DB layer: SQLite cannot add a NOT NULL column without a default, and
|
|
43
|
+
* the row codec — not the DB — is the type authority. Fresh CREATEs carry
|
|
44
|
+
* NOT NULL per the schema.
|
|
45
|
+
*/
|
|
46
|
+
import {
|
|
47
|
+
decodeRow,
|
|
48
|
+
encodeRow,
|
|
49
|
+
type RowColumn,
|
|
50
|
+
type RowValue,
|
|
51
|
+
} from '@syncular/core';
|
|
52
|
+
import type { CompiledSchema, CompiledTable } from './schema';
|
|
53
|
+
import type { StoredRow } from './storage';
|
|
54
|
+
|
|
55
|
+
export type RelationalDialect = 'sqlite' | 'postgres';
|
|
56
|
+
|
|
57
|
+
export const SYNC_PARTITION_COLUMN = '_sync_partition';
|
|
58
|
+
export const SYNC_ROW_ID_COLUMN = '_sync_row_id';
|
|
59
|
+
export const SYNC_VERSION_COLUMN = '_sync_server_version';
|
|
60
|
+
export const SYNC_SCOPES_COLUMN = '_sync_scopes';
|
|
61
|
+
export const SYNC_PAYLOAD_COLUMN = '_sync_payload';
|
|
62
|
+
|
|
63
|
+
/** SQL-standard identifier quoting (both dialects). */
|
|
64
|
+
export function quoteIdent(name: string): string {
|
|
65
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** §5.3-style type affinities, per dialect (DESIGN type-mapping table). */
|
|
69
|
+
export function columnSqlType(
|
|
70
|
+
column: RowColumn,
|
|
71
|
+
dialect: RelationalDialect,
|
|
72
|
+
): string {
|
|
73
|
+
if (dialect === 'postgres') {
|
|
74
|
+
switch (column.type) {
|
|
75
|
+
case 'string':
|
|
76
|
+
case 'blob_ref':
|
|
77
|
+
return 'TEXT';
|
|
78
|
+
case 'json':
|
|
79
|
+
return 'JSONB';
|
|
80
|
+
case 'integer':
|
|
81
|
+
return 'BIGINT';
|
|
82
|
+
case 'float':
|
|
83
|
+
return 'DOUBLE PRECISION';
|
|
84
|
+
case 'boolean':
|
|
85
|
+
return 'BOOLEAN';
|
|
86
|
+
case 'bytes':
|
|
87
|
+
case 'crdt':
|
|
88
|
+
return 'BYTEA';
|
|
89
|
+
default:
|
|
90
|
+
throw new Error(`unsupported column type: ${String(column.type)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
switch (column.type) {
|
|
94
|
+
case 'string':
|
|
95
|
+
case 'json':
|
|
96
|
+
case 'blob_ref':
|
|
97
|
+
return 'TEXT';
|
|
98
|
+
case 'integer':
|
|
99
|
+
return 'INTEGER';
|
|
100
|
+
case 'float':
|
|
101
|
+
return 'REAL';
|
|
102
|
+
case 'boolean':
|
|
103
|
+
return 'INTEGER';
|
|
104
|
+
case 'bytes':
|
|
105
|
+
case 'crdt':
|
|
106
|
+
return 'BLOB';
|
|
107
|
+
default:
|
|
108
|
+
throw new Error(`unsupported column type: ${String(column.type)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The full column list of a relational row table, in INSERT order:
|
|
114
|
+
* partition, row id, app columns (schema order; only when the table is
|
|
115
|
+
* materialized), version, scopes, payload.
|
|
116
|
+
*/
|
|
117
|
+
export function tableColumnNames(table: CompiledTable): string[] {
|
|
118
|
+
return [
|
|
119
|
+
SYNC_PARTITION_COLUMN,
|
|
120
|
+
SYNC_ROW_ID_COLUMN,
|
|
121
|
+
...(table.materialize ? table.columns.map((column) => column.name) : []),
|
|
122
|
+
SYNC_VERSION_COLUMN,
|
|
123
|
+
SYNC_SCOPES_COLUMN,
|
|
124
|
+
SYNC_PAYLOAD_COLUMN,
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** CREATE TABLE IF NOT EXISTS for one app table. */
|
|
129
|
+
export function createTableDdl(
|
|
130
|
+
table: CompiledTable,
|
|
131
|
+
dialect: RelationalDialect,
|
|
132
|
+
): string {
|
|
133
|
+
const versionType = dialect === 'postgres' ? 'BIGINT' : 'INTEGER';
|
|
134
|
+
const scopesType = dialect === 'postgres' ? 'JSONB' : 'TEXT';
|
|
135
|
+
const payloadType = dialect === 'postgres' ? 'BYTEA' : 'BLOB';
|
|
136
|
+
const defs = [
|
|
137
|
+
`${quoteIdent(SYNC_PARTITION_COLUMN)} TEXT NOT NULL`,
|
|
138
|
+
`${quoteIdent(SYNC_ROW_ID_COLUMN)} TEXT NOT NULL`,
|
|
139
|
+
...(table.materialize
|
|
140
|
+
? table.columns.map((column) => {
|
|
141
|
+
const notNull = column.nullable ? '' : ' NOT NULL';
|
|
142
|
+
return `${quoteIdent(column.name)} ${columnSqlType(column, dialect)}${notNull}`;
|
|
143
|
+
})
|
|
144
|
+
: []),
|
|
145
|
+
`${quoteIdent(SYNC_VERSION_COLUMN)} ${versionType} NOT NULL`,
|
|
146
|
+
`${quoteIdent(SYNC_SCOPES_COLUMN)} ${scopesType} NOT NULL`,
|
|
147
|
+
`${quoteIdent(SYNC_PAYLOAD_COLUMN)} ${payloadType} NOT NULL`,
|
|
148
|
+
`PRIMARY KEY (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)})`,
|
|
149
|
+
];
|
|
150
|
+
return `CREATE TABLE IF NOT EXISTS ${quoteIdent(table.name)} (${defs.join(', ')})`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* ALTER TABLE ADD COLUMN statements for schema columns missing from
|
|
155
|
+
* `existingColumns` (introspected). Added columns are nullable (header note).
|
|
156
|
+
*/
|
|
157
|
+
export function addColumnDdl(
|
|
158
|
+
table: CompiledTable,
|
|
159
|
+
existingColumns: ReadonlySet<string>,
|
|
160
|
+
dialect: RelationalDialect,
|
|
161
|
+
): string[] {
|
|
162
|
+
if (!table.materialize) return [];
|
|
163
|
+
const out: string[] = [];
|
|
164
|
+
for (const column of table.columns) {
|
|
165
|
+
if (existingColumns.has(column.name)) continue;
|
|
166
|
+
out.push(
|
|
167
|
+
`ALTER TABLE ${quoteIdent(table.name)} ADD COLUMN ${quoteIdent(column.name)} ${columnSqlType(column, dialect)}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* CREATE INDEX IF NOT EXISTS for the table's user-declared indexes
|
|
175
|
+
* (DESIGN "user indexes" — the same names/columns the client materializes;
|
|
176
|
+
* cross-table index-name uniqueness is the user's schema concern, exactly
|
|
177
|
+
* as it is client-side).
|
|
178
|
+
*/
|
|
179
|
+
export function createIndexDdl(table: CompiledTable): string[] {
|
|
180
|
+
// User indexes name app columns — nothing to index without the projection.
|
|
181
|
+
if (!table.materialize) return [];
|
|
182
|
+
return table.indexes.map((index) => {
|
|
183
|
+
const unique = index.unique ? 'UNIQUE ' : '';
|
|
184
|
+
const columns = index.columns
|
|
185
|
+
.map((column) => quoteIdent(column))
|
|
186
|
+
.join(', ');
|
|
187
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${quoteIdent(index.name)} ON ${quoteIdent(table.name)} (${columns})`;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Convert one decoded row value to its SQL bind value.
|
|
193
|
+
* - boolean → 0/1 on sqlite (INTEGER affinity), native on postgres;
|
|
194
|
+
* - json → the raw string on sqlite; on postgres, NULL if unparseable
|
|
195
|
+
* (JSONB would reject the INSERT — the payload keeps the verbatim value);
|
|
196
|
+
* - everything else binds naturally (Uint8Array → BLOB/BYTEA).
|
|
197
|
+
*/
|
|
198
|
+
export function toSqlValue(
|
|
199
|
+
column: RowColumn,
|
|
200
|
+
value: RowValue,
|
|
201
|
+
dialect: RelationalDialect,
|
|
202
|
+
): unknown {
|
|
203
|
+
if (value === null) return null;
|
|
204
|
+
if (column.type === 'boolean' && dialect === 'sqlite') {
|
|
205
|
+
return value === true ? 1 : 0;
|
|
206
|
+
}
|
|
207
|
+
if (column.type === 'json' && dialect === 'postgres') {
|
|
208
|
+
if (typeof value !== 'string') return null;
|
|
209
|
+
try {
|
|
210
|
+
JSON.parse(value);
|
|
211
|
+
return value;
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Bind values for an upsert, in `tableColumnNames` order. Decodes the
|
|
221
|
+
* payload once; the typed columns and the verbatim payload land in one
|
|
222
|
+
* statement, so they cannot drift.
|
|
223
|
+
*/
|
|
224
|
+
export function upsertValues(
|
|
225
|
+
table: CompiledTable,
|
|
226
|
+
partition: string,
|
|
227
|
+
row: StoredRow,
|
|
228
|
+
dialect: RelationalDialect,
|
|
229
|
+
): unknown[] {
|
|
230
|
+
// A non-materialized table skips the decode entirely — the upsert is a
|
|
231
|
+
// five-column meta write (the old blob-store cost).
|
|
232
|
+
const projection = table.materialize
|
|
233
|
+
? decodeRow(table.columns, row.payload)
|
|
234
|
+
: undefined;
|
|
235
|
+
return [
|
|
236
|
+
partition,
|
|
237
|
+
row.rowId,
|
|
238
|
+
...(projection !== undefined
|
|
239
|
+
? table.columns.map((column, index) =>
|
|
240
|
+
toSqlValue(column, projection[index] ?? null, dialect),
|
|
241
|
+
)
|
|
242
|
+
: []),
|
|
243
|
+
row.serverVersion,
|
|
244
|
+
JSON.stringify(row.scopes),
|
|
245
|
+
row.payload,
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function placeholderList(count: number, dialect: RelationalDialect): string {
|
|
250
|
+
if (dialect === 'sqlite') return new Array(count).fill('?').join(',');
|
|
251
|
+
const out: string[] = [];
|
|
252
|
+
for (let i = 1; i <= count; i++) out.push(`$${i}`);
|
|
253
|
+
return out.join(',');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Upsert statement text (bind with `upsertValues`). */
|
|
257
|
+
export function upsertSql(
|
|
258
|
+
table: CompiledTable,
|
|
259
|
+
dialect: RelationalDialect,
|
|
260
|
+
): string {
|
|
261
|
+
const names = tableColumnNames(table);
|
|
262
|
+
const quoted = names.map((name) => quoteIdent(name)).join(', ');
|
|
263
|
+
const values = placeholderList(names.length, dialect);
|
|
264
|
+
if (dialect === 'sqlite') {
|
|
265
|
+
// INSERT OR REPLACE keys on the (_sync_partition, _sync_row_id) PK.
|
|
266
|
+
return `INSERT OR REPLACE INTO ${quoteIdent(table.name)} (${quoted}) VALUES (${values})`;
|
|
267
|
+
}
|
|
268
|
+
const updates = names
|
|
269
|
+
.slice(2) // partition + row id are the conflict key
|
|
270
|
+
.map((name) => `${quoteIdent(name)}=EXCLUDED.${quoteIdent(name)}`)
|
|
271
|
+
.join(', ');
|
|
272
|
+
return `INSERT INTO ${quoteIdent(table.name)} (${quoted}) VALUES (${values})
|
|
273
|
+
ON CONFLICT (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}) DO UPDATE SET ${updates}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* SELECT one stored row. Aliased to the legacy record shape
|
|
278
|
+
* (`row_id`/`server_version`/`scopes`/`payload`) so the existing
|
|
279
|
+
* `toStoredRow` converters keep working. Params: [partition, rowId].
|
|
280
|
+
*/
|
|
281
|
+
export function selectRowSql(
|
|
282
|
+
table: CompiledTable,
|
|
283
|
+
dialect: RelationalDialect,
|
|
284
|
+
): string {
|
|
285
|
+
const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
|
|
286
|
+
return `SELECT ${quoteIdent(SYNC_ROW_ID_COLUMN)} AS row_id, ${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, ${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes, ${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* One-round-trip page scan for `scanRows`: candidates from the inverted
|
|
291
|
+
* scope index (ordered + LIMITed at the covering `sync_row_scopes` PK —
|
|
292
|
+
* exactly the old candidate query, so the index-first posture is unchanged)
|
|
293
|
+
* LEFT JOINed to the row table, so a whole page arrives in ONE statement
|
|
294
|
+
* instead of one lookup per candidate. That per-candidate lookup was the
|
|
295
|
+
* cold-bootstrap hot path: a 100k-row snapshot on Postgres paid ~100k
|
|
296
|
+
* network round-trips (~10 s at 0.1 ms each) before this join.
|
|
297
|
+
*
|
|
298
|
+
* LEFT JOIN (not INNER): a candidate whose row vanished (index entry
|
|
299
|
+
* without a row) must still reach the caller — its `row_id` advances the
|
|
300
|
+
* keyset cursor — so it comes back with NULL payload rather than
|
|
301
|
+
* disappearing from the page.
|
|
302
|
+
*
|
|
303
|
+
* Bind order:
|
|
304
|
+
* - postgres: [partition, tbl, var, ...values, afterRowId, limit]
|
|
305
|
+
* (the join reuses `$1` for the partition);
|
|
306
|
+
* - sqlite: [partition, tbl, var, ...values, afterRowId, limit,
|
|
307
|
+
* partition] (positional `?` — the partition binds again for the join).
|
|
308
|
+
*/
|
|
309
|
+
export function scanRowPageSql(
|
|
310
|
+
table: CompiledTable,
|
|
311
|
+
valueCount: number,
|
|
312
|
+
dialect: RelationalDialect,
|
|
313
|
+
): string {
|
|
314
|
+
const p = (n: number) => (dialect === 'sqlite' ? '?' : `$${n}`);
|
|
315
|
+
const values: string[] = [];
|
|
316
|
+
for (let i = 0; i < valueCount; i++) values.push(p(4 + i));
|
|
317
|
+
const after = p(4 + valueCount);
|
|
318
|
+
const limit = p(5 + valueCount);
|
|
319
|
+
const joinPartition = dialect === 'sqlite' ? '?' : '$1';
|
|
320
|
+
return `SELECT c.row_id AS row_id, r.${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, r.${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes, r.${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload
|
|
321
|
+
FROM (SELECT DISTINCT row_id FROM sync_row_scopes
|
|
322
|
+
WHERE partition=${p(1)} AND tbl=${p(2)} AND var=${p(3)} AND value IN (${values.join(',')})
|
|
323
|
+
AND row_id>${after}
|
|
324
|
+
ORDER BY row_id LIMIT ${limit}) c
|
|
325
|
+
LEFT JOIN ${quoteIdent(table.name)} r
|
|
326
|
+
ON r.${quoteIdent(SYNC_PARTITION_COLUMN)}=${joinPartition} AND r.${quoteIdent(SYNC_ROW_ID_COLUMN)}=c.row_id
|
|
327
|
+
ORDER BY c.row_id`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* One-round-trip page read for `readCommitWindow` (sync_* tables only; it
|
|
332
|
+
* lives here beside `scanRowPageSql` because it is the same join-the-index
|
|
333
|
+
* posture, dialect-parameterized the same way): candidates from the inverted
|
|
334
|
+
* change-scope index (ordered + LIMITed at the covering `sync_change_scopes`
|
|
335
|
+
* PK — exactly the old candidate query, so the index-first invariant of
|
|
336
|
+
* `CommitWindowQuery` is unchanged) LEFT JOINed to the commit metadata and
|
|
337
|
+
* to the commit's changes for the table, so a whole window page arrives in
|
|
338
|
+
* ONE statement instead of 1 + 2×candidates round trips. This is the
|
|
339
|
+
* incremental-pull hot path — it runs on every sync round; before the join a
|
|
340
|
+
* 500-candidate catch-up window on Postgres paid ~1000 network round trips.
|
|
341
|
+
*
|
|
342
|
+
* LEFT JOIN (not INNER): a candidate whose commit vanished (scope-index
|
|
343
|
+
* entry without a commit/changes row) must still reach the caller — its
|
|
344
|
+
* `commit_seq` advances the window cursor — so it comes back with NULL
|
|
345
|
+
* meta/change columns rather than disappearing from the page.
|
|
346
|
+
*
|
|
347
|
+
* Rows arrive ordered (commit_seq, idx): oldest-first commits, each commit's
|
|
348
|
+
* change rows consecutive in `idx` order — the caller groups consecutive
|
|
349
|
+
* rows and verifies the full multi-variable scope match in JS.
|
|
350
|
+
*
|
|
351
|
+
* Bind order:
|
|
352
|
+
* - postgres: [partition, tbl, var, ...values, afterSeq, throughSeq,
|
|
353
|
+
* limit] (the joins reuse `$1`/`$2`);
|
|
354
|
+
* - sqlite: [partition, tbl, var, ...values, afterSeq, throughSeq,
|
|
355
|
+
* limit, partition, partition, tbl] (positional `?` — partition/tbl bind
|
|
356
|
+
* again for the joins).
|
|
357
|
+
*/
|
|
358
|
+
export function commitWindowPageSql(
|
|
359
|
+
valueCount: number,
|
|
360
|
+
dialect: RelationalDialect,
|
|
361
|
+
): string {
|
|
362
|
+
const p = (n: number) => (dialect === 'sqlite' ? '?' : `$${n}`);
|
|
363
|
+
const values: string[] = [];
|
|
364
|
+
for (let i = 0; i < valueCount; i++) values.push(p(4 + i));
|
|
365
|
+
const after = p(4 + valueCount);
|
|
366
|
+
const through = p(5 + valueCount);
|
|
367
|
+
const limit = p(6 + valueCount);
|
|
368
|
+
const joinPartition = dialect === 'sqlite' ? '?' : '$1';
|
|
369
|
+
const joinTbl = dialect === 'sqlite' ? '?' : '$2';
|
|
370
|
+
return `SELECT c.commit_seq AS commit_seq, m.actor_id AS actor_id, m.created_at_ms AS created_at_ms,
|
|
371
|
+
ch.tbl AS tbl, ch.row_id AS row_id, ch.op AS op, ch.row_version AS row_version, ch.scopes AS scopes, ch.payload AS payload
|
|
372
|
+
FROM (SELECT DISTINCT commit_seq FROM sync_change_scopes
|
|
373
|
+
WHERE partition=${p(1)} AND tbl=${p(2)} AND var=${p(3)} AND value IN (${values.join(',')})
|
|
374
|
+
AND commit_seq>${after} AND commit_seq<=${through}
|
|
375
|
+
ORDER BY commit_seq LIMIT ${limit}) c
|
|
376
|
+
LEFT JOIN sync_commits m
|
|
377
|
+
ON m.partition=${joinPartition} AND m.commit_seq=c.commit_seq
|
|
378
|
+
LEFT JOIN sync_changes ch
|
|
379
|
+
ON ch.partition=${joinPartition} AND ch.commit_seq=c.commit_seq AND ch.tbl=${joinTbl}
|
|
380
|
+
ORDER BY c.commit_seq, ch.idx`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** SELECT a row's version + scope map (admin/blob authz). Params: [partition, rowId]. */
|
|
384
|
+
export function selectRowScopesSql(
|
|
385
|
+
table: CompiledTable,
|
|
386
|
+
dialect: RelationalDialect,
|
|
387
|
+
): string {
|
|
388
|
+
const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
|
|
389
|
+
return `SELECT ${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, ${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** DELETE one stored row. Params: [partition, rowId]. */
|
|
393
|
+
export function deleteRowSql(
|
|
394
|
+
table: CompiledTable,
|
|
395
|
+
dialect: RelationalDialect,
|
|
396
|
+
): string {
|
|
397
|
+
const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
|
|
398
|
+
return `DELETE FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* The schema-version marker table gating DDL work (DESIGN "server-side
|
|
403
|
+
* schema migration"): `ensureSchema` compares the stored version and skips
|
|
404
|
+
* all introspection/DDL when it matches — one cheap read per storage
|
|
405
|
+
* instance (relevant for D1's per-request instantiation).
|
|
406
|
+
*
|
|
407
|
+
* `layouts` persists each table's column layout (name/type/nullable, the
|
|
408
|
+
* exact inputs the row codec's byte layout depends on) as of the LAST
|
|
409
|
+
* applied schema version. The codec is strict — a payload only decodes
|
|
410
|
+
* under the column list it was encoded with — so a version bump MUST
|
|
411
|
+
* re-encode stored payloads (append trailing NULLs for added columns);
|
|
412
|
+
* decoding the old bytes requires the old layout, and this column is where
|
|
413
|
+
* it lives.
|
|
414
|
+
*/
|
|
415
|
+
export const SCHEMA_META_DDL_SQLITE = `CREATE TABLE IF NOT EXISTS sync_schema_meta(
|
|
416
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
417
|
+
schema_version INTEGER NOT NULL,
|
|
418
|
+
layouts TEXT NOT NULL DEFAULT '{}'
|
|
419
|
+
)`;
|
|
420
|
+
|
|
421
|
+
export const SCHEMA_META_DDL_POSTGRES = `CREATE TABLE IF NOT EXISTS sync_schema_meta(
|
|
422
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
423
|
+
schema_version BIGINT NOT NULL,
|
|
424
|
+
layouts TEXT NOT NULL DEFAULT '{}'
|
|
425
|
+
)`;
|
|
426
|
+
|
|
427
|
+
// -- column layouts + payload migration (version bumps) ----------------------
|
|
428
|
+
|
|
429
|
+
/** The codec-relevant subset of a column, persisted per applied version. */
|
|
430
|
+
export interface StoredColumnLayout {
|
|
431
|
+
readonly name: string;
|
|
432
|
+
readonly type: RowColumn['type'];
|
|
433
|
+
readonly nullable: boolean;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export type StoredLayouts = Record<string, readonly StoredColumnLayout[]>;
|
|
437
|
+
|
|
438
|
+
/** The layouts JSON persisted alongside the schema version marker. */
|
|
439
|
+
export function layoutsOf(schema: CompiledSchema): string {
|
|
440
|
+
const out: Record<string, StoredColumnLayout[]> = {};
|
|
441
|
+
for (const table of schema.tables.values()) {
|
|
442
|
+
out[table.name] = table.columns.map((column) => ({
|
|
443
|
+
name: column.name,
|
|
444
|
+
type: column.type,
|
|
445
|
+
nullable: column.nullable,
|
|
446
|
+
}));
|
|
447
|
+
}
|
|
448
|
+
return JSON.stringify(out);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function parseLayouts(json: string | null | undefined): StoredLayouts {
|
|
452
|
+
if (json === null || json === undefined || json.length === 0) return {};
|
|
453
|
+
return JSON.parse(json) as StoredLayouts;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Enforce the migration subset on a table's column list: the old layout
|
|
458
|
+
* must be an exact prefix (same name, type, nullability) of the new one,
|
|
459
|
+
* and appended columns must be nullable (there is no default to backfill).
|
|
460
|
+
* Returns `true` iff columns were appended.
|
|
461
|
+
*/
|
|
462
|
+
export function assertAppendOnlyMigration(
|
|
463
|
+
tableName: string,
|
|
464
|
+
oldLayout: readonly StoredColumnLayout[],
|
|
465
|
+
table: CompiledTable,
|
|
466
|
+
): boolean {
|
|
467
|
+
if (oldLayout.length > table.columns.length) {
|
|
468
|
+
throw new Error(
|
|
469
|
+
`table ${JSON.stringify(tableName)}: the schema removed columns — only CREATE TABLE / ADD COLUMN / CREATE INDEX migrations are supported`,
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
for (let i = 0; i < oldLayout.length; i++) {
|
|
473
|
+
const before = oldLayout[i];
|
|
474
|
+
const after = table.columns[i];
|
|
475
|
+
if (
|
|
476
|
+
before === undefined ||
|
|
477
|
+
after === undefined ||
|
|
478
|
+
before.name !== after.name ||
|
|
479
|
+
before.type !== after.type ||
|
|
480
|
+
before.nullable !== after.nullable
|
|
481
|
+
) {
|
|
482
|
+
throw new Error(
|
|
483
|
+
`table ${JSON.stringify(tableName)}: column ${i} changed (${JSON.stringify(before)} → ${JSON.stringify({ name: after?.name, type: after?.type, nullable: after?.nullable })}) — only appending nullable columns is supported`,
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (let i = oldLayout.length; i < table.columns.length; i++) {
|
|
488
|
+
const added = table.columns[i];
|
|
489
|
+
if (added !== undefined && !added.nullable) {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`table ${JSON.stringify(tableName)}: added column ${JSON.stringify(added.name)} must be nullable — existing rows have no value to backfill`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return oldLayout.length < table.columns.length;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Re-encode an old-layout payload under the current columns: decode with
|
|
500
|
+
* the layout it was written under, append NULLs for the added columns,
|
|
501
|
+
* encode with the current column list. The write path (§3.4 scope-strip,
|
|
502
|
+
* CRDT merge, conflict serverRow) and the bootstrap serve path both decode
|
|
503
|
+
* stored payloads under the CURRENT schema, so this migration is a
|
|
504
|
+
* correctness requirement of the version bump, not an optimization.
|
|
505
|
+
*/
|
|
506
|
+
export function migratePayload(
|
|
507
|
+
oldLayout: readonly StoredColumnLayout[],
|
|
508
|
+
table: CompiledTable,
|
|
509
|
+
payload: Uint8Array,
|
|
510
|
+
): Uint8Array {
|
|
511
|
+
const values = decodeRow(oldLayout as readonly RowColumn[], payload);
|
|
512
|
+
while (values.length < table.columns.length) values.push(null);
|
|
513
|
+
return encodeRow(table.columns, values);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Keyset-paged scan of a row table for the migration rewrite.
|
|
518
|
+
* Params: [afterPartition, afterRowId, limit].
|
|
519
|
+
*/
|
|
520
|
+
export function selectRowsForRewriteSql(
|
|
521
|
+
table: CompiledTable,
|
|
522
|
+
dialect: RelationalDialect,
|
|
523
|
+
): string {
|
|
524
|
+
const p = dialect === 'sqlite' ? ['?', '?', '?'] : ['$1', '$2', '$3'];
|
|
525
|
+
return `SELECT ${quoteIdent(SYNC_PARTITION_COLUMN)} AS partition, ${quoteIdent(SYNC_ROW_ID_COLUMN)} AS row_id, ${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload
|
|
526
|
+
FROM ${quoteIdent(table.name)}
|
|
527
|
+
WHERE (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}) > (${p[0]}, ${p[1]})
|
|
528
|
+
ORDER BY ${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}
|
|
529
|
+
LIMIT ${p[2]}`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Rewrite one row during a migration: refresh the projection columns (when
|
|
534
|
+
* materialized) and the payload. Bind with `rewriteValues`.
|
|
535
|
+
*/
|
|
536
|
+
export function rewriteRowSql(
|
|
537
|
+
table: CompiledTable,
|
|
538
|
+
dialect: RelationalDialect,
|
|
539
|
+
): string {
|
|
540
|
+
const sets: string[] = [];
|
|
541
|
+
let i = 1;
|
|
542
|
+
if (table.materialize) {
|
|
543
|
+
for (const column of table.columns) {
|
|
544
|
+
sets.push(
|
|
545
|
+
`${quoteIdent(column.name)}=${dialect === 'sqlite' ? '?' : `$${i}`}`,
|
|
546
|
+
);
|
|
547
|
+
i++;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
sets.push(
|
|
551
|
+
`${quoteIdent(SYNC_PAYLOAD_COLUMN)}=${dialect === 'sqlite' ? '?' : `$${i}`}`,
|
|
552
|
+
);
|
|
553
|
+
i++;
|
|
554
|
+
const wherePartition = dialect === 'sqlite' ? '?' : `$${i}`;
|
|
555
|
+
const whereRowId = dialect === 'sqlite' ? '?' : `$${i + 1}`;
|
|
556
|
+
return `UPDATE ${quoteIdent(table.name)} SET ${sets.join(', ')} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${wherePartition} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${whereRowId}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** Bind values for `rewriteRowSql` from the (already migrated) payload. */
|
|
560
|
+
export function rewriteValues(
|
|
561
|
+
table: CompiledTable,
|
|
562
|
+
partition: string,
|
|
563
|
+
rowId: string,
|
|
564
|
+
payload: Uint8Array,
|
|
565
|
+
dialect: RelationalDialect,
|
|
566
|
+
): unknown[] {
|
|
567
|
+
const out: unknown[] = [];
|
|
568
|
+
if (table.materialize) {
|
|
569
|
+
const values = decodeRow(table.columns, payload);
|
|
570
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
571
|
+
const column = table.columns[i];
|
|
572
|
+
if (column === undefined) continue;
|
|
573
|
+
out.push(toSqlValue(column, values[i] ?? null, dialect));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
out.push(payload, partition, rowId);
|
|
577
|
+
return out;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* What the migration rewrite phase must do for one table, derived from the
|
|
582
|
+
* persisted layouts + the physical columns present before this run.
|
|
583
|
+
*
|
|
584
|
+
* - `migrate`: the layout gained columns — every stored payload re-encodes
|
|
585
|
+
* under the new column list (correctness; see `migratePayload`).
|
|
586
|
+
* - `backfill`: the table just gained physical projection columns whose
|
|
587
|
+
* values exist in stored payloads (materialization flipped on) — the
|
|
588
|
+
* projection refreshes from the payload, no re-encode.
|
|
589
|
+
*/
|
|
590
|
+
export function rewritePlan(
|
|
591
|
+
table: CompiledTable,
|
|
592
|
+
oldLayout: readonly StoredColumnLayout[] | undefined,
|
|
593
|
+
physicalColumnsBefore: ReadonlySet<string> | undefined,
|
|
594
|
+
): { migrate: boolean; backfill: boolean } {
|
|
595
|
+
if (oldLayout === undefined || physicalColumnsBefore === undefined) {
|
|
596
|
+
// Fresh table (or a pre-layout database, which cannot be reasoned
|
|
597
|
+
// about): nothing stored to rewrite.
|
|
598
|
+
return { migrate: false, backfill: false };
|
|
599
|
+
}
|
|
600
|
+
const migrate = assertAppendOnlyMigration(table.name, oldLayout, table);
|
|
601
|
+
const backfill =
|
|
602
|
+
table.materialize &&
|
|
603
|
+
table.columns.some(
|
|
604
|
+
(column, index) =>
|
|
605
|
+
index < oldLayout.length && // value exists in old payloads
|
|
606
|
+
!physicalColumnsBefore.has(column.name), // column was just added
|
|
607
|
+
);
|
|
608
|
+
return { migrate, backfill };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Every DDL statement to bring a database from `existingColumnsByTable`
|
|
613
|
+
* (introspected; a missing key = table absent) to `schema`: CREATE TABLE
|
|
614
|
+
* for absent tables, ADD COLUMN for missing columns, CREATE INDEX for the
|
|
615
|
+
* declared user indexes — exactly the migration subset.
|
|
616
|
+
*/
|
|
617
|
+
export function schemaDdl(
|
|
618
|
+
schema: CompiledSchema,
|
|
619
|
+
existingColumnsByTable: ReadonlyMap<string, ReadonlySet<string>>,
|
|
620
|
+
dialect: RelationalDialect,
|
|
621
|
+
): string[] {
|
|
622
|
+
const out: string[] = [];
|
|
623
|
+
for (const table of schema.tables.values()) {
|
|
624
|
+
const existing = existingColumnsByTable.get(table.name);
|
|
625
|
+
if (existing === undefined) {
|
|
626
|
+
out.push(createTableDdl(table, dialect));
|
|
627
|
+
} else {
|
|
628
|
+
out.push(...addColumnDdl(table, existing, dialect));
|
|
629
|
+
}
|
|
630
|
+
out.push(...createIndexDdl(table));
|
|
631
|
+
}
|
|
632
|
+
return out;
|
|
633
|
+
}
|