@fragno-dev/db 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +327 -160
- package/CHANGELOG.md +74 -0
- package/README.md +24 -0
- package/dist/adapters/adapters.d.ts +1 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.js +30 -3
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
- package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +24 -20
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +8 -9
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +10 -6
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-uow.js +236 -13
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
- package/dist/adapters/in-memory/options.d.ts +2 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -1
- package/dist/adapters/in-memory/options.js +3 -2
- package/dist/adapters/in-memory/options.js.map +1 -1
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
- package/dist/adapters/in-memory/store.js +1 -1
- package/dist/adapters/in-memory/store.js.map +1 -1
- package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/browser/adapters/adapters.d.ts +61 -0
- package/dist/browser/adapters/adapters.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
- package/dist/browser/adapters/in-memory/index.d.ts +2 -0
- package/dist/browser/adapters/in-memory/options.d.ts +1 -0
- package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
- package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/browser/durable-hooks.d.ts +3 -0
- package/dist/browser/fragments/internal-fragment.d.ts +317 -0
- package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/browser/hooks/hooks.d.ts +146 -0
- package/dist/browser/hooks/hooks.d.ts.map +1 -0
- package/dist/browser/id.js +1 -0
- package/dist/browser/internal/adapter-registry.d.ts +4 -0
- package/dist/browser/internal/outbox-state.d.ts +2 -0
- package/dist/browser/mod.d.ts +15 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +17 -0
- package/dist/browser/mod.js.map +1 -0
- package/dist/browser/mod2.d.ts +48 -0
- package/dist/browser/mod2.d.ts.map +1 -0
- package/dist/browser/naming/sql-naming.d.ts +19 -0
- package/dist/browser/naming/sql-naming.d.ts.map +1 -0
- package/dist/browser/outbox/outbox.d.ts +21 -0
- package/dist/browser/outbox/outbox.d.ts.map +1 -0
- package/dist/browser/query/column-defaults.js +1 -0
- package/dist/browser/query/condition-builder.d.ts +44 -0
- package/dist/browser/query/condition-builder.d.ts.map +1 -0
- package/dist/browser/query/condition-builder.js +97 -0
- package/dist/browser/query/condition-builder.js.map +1 -0
- package/dist/browser/query/cursor.d.ts +105 -0
- package/dist/browser/query/cursor.d.ts.map +1 -0
- package/dist/browser/query/cursor.js +150 -0
- package/dist/browser/query/cursor.js.map +1 -0
- package/dist/browser/query/db-now.d.ts +22 -0
- package/dist/browser/query/db-now.d.ts.map +1 -0
- package/dist/browser/query/db-now.js +33 -0
- package/dist/browser/query/db-now.js.map +1 -0
- package/dist/browser/query/orm/orm.d.ts +18 -0
- package/dist/browser/query/orm/orm.d.ts.map +1 -0
- package/dist/browser/query/simple-query-interface.d.ts +108 -0
- package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
- package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/browser/query/value-encoding.js +38 -0
- package/dist/browser/query/value-encoding.js.map +1 -0
- package/dist/browser/schema/create.d.ts +326 -0
- package/dist/browser/schema/create.d.ts.map +1 -0
- package/dist/browser/schema/create.js +89 -0
- package/dist/browser/schema/create.js.map +1 -0
- package/dist/browser/schema/generate-id.js +28 -0
- package/dist/browser/schema/generate-id.js.map +1 -0
- package/dist/browser/shared/providers.d.ts +6 -0
- package/dist/browser/shared/providers.d.ts.map +1 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
- package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/browser/sync/commands.d.ts +15 -0
- package/dist/browser/sync/commands.d.ts.map +1 -0
- package/dist/browser/sync/commands.js +27 -0
- package/dist/browser/sync/commands.js.map +1 -0
- package/dist/browser/sync/types.d.ts +63 -0
- package/dist/browser/sync/types.d.ts.map +1 -0
- package/dist/browser/util/types.d.ts +8 -0
- package/dist/browser/util/types.d.ts.map +1 -0
- package/dist/browser/with-database.d.ts +29 -0
- package/dist/browser/with-database.d.ts.map +1 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +5 -0
- package/dist/db-fragment-definition-builder.d.ts +85 -28
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +374 -46
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.js +23 -55
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
- package/dist/dispatchers/node/dispatcher.d.ts +14 -0
- package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/node/dispatcher.js +80 -0
- package/dist/dispatchers/node/dispatcher.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +5 -10
- package/dist/dispatchers/node/index.d.ts.map +1 -1
- package/dist/dispatchers/node/index.js +21 -53
- package/dist/dispatchers/node/index.js.map +1 -1
- package/dist/durable-hooks.d.ts +31 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +23 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +128 -27
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +125 -78
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +138 -3
- package/dist/fragments/internal-fragment.routes.js.map +1 -1
- package/dist/fragments/internal-fragment.schema.d.ts +7 -1
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.schema.js +18 -1
- package/dist/fragments/internal-fragment.schema.js.map +1 -1
- package/dist/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-logger.js +75 -0
- package/dist/hooks/durable-hooks-logger.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +1 -14
- package/dist/hooks/durable-hooks-processor.js +58 -10
- package/dist/hooks/durable-hooks-processor.js.map +1 -1
- package/dist/hooks/durable-hooks-runtime.js +44 -0
- package/dist/hooks/durable-hooks-runtime.js.map +1 -0
- package/dist/hooks/hooks.d.ts +60 -2
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +214 -53
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.js +2 -2
- package/dist/internal/adapter-registry.d.ts +11 -0
- package/dist/internal/adapter-registry.d.ts.map +1 -0
- package/dist/internal/adapter-registry.js +135 -0
- package/dist/internal/adapter-registry.js.map +1 -0
- package/dist/internal/outbox-state.d.ts +2 -0
- package/dist/internal/outbox-state.js +26 -0
- package/dist/internal/outbox-state.js.map +1 -0
- package/dist/migration-engine/auto-from-schema.d.ts +33 -0
- package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
- package/dist/migration-engine/auto-from-schema.js +210 -27
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +17 -5
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.d.ts +113 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +12 -11
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +10 -10
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts.map +1 -1
- package/dist/naming/sql-naming.js.map +1 -1
- package/dist/outbox/outbox-builder.js.map +1 -1
- package/dist/outbox/outbox.d.ts +3 -1
- package/dist/outbox/outbox.d.ts.map +1 -1
- package/dist/outbox/outbox.js.map +1 -1
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/condition-builder.d.ts +7 -1
- package/dist/query/condition-builder.d.ts.map +1 -1
- package/dist/query/condition-builder.js +5 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/cursor-client.d.ts +105 -0
- package/dist/query/cursor-client.d.ts.map +1 -0
- package/dist/query/cursor-client.js +165 -0
- package/dist/query/cursor-client.js.map +1 -0
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +7 -1
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +15 -1
- package/dist/query/db-now.d.ts.map +1 -1
- package/dist/query/db-now.js +30 -2
- package/dist/query/db-now.js.map +1 -1
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +2 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +7 -3
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +50 -6
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +8 -1
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +69 -25
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +91 -16
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts.map +1 -1
- package/dist/schema/validator.js.map +1 -1
- package/dist/schema-output/drizzle.d.ts.map +1 -1
- package/dist/schema-output/drizzle.js +8 -6
- package/dist/schema-output/drizzle.js.map +1 -1
- package/dist/schema-output/prisma.d.ts.map +1 -1
- package/dist/schema-output/prisma.js +21 -10
- package/dist/schema-output/prisma.js.map +1 -1
- package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
- package/dist/sql-driver/sql.js.map +1 -1
- package/dist/sync/commands.d.ts +15 -0
- package/dist/sync/commands.d.ts.map +1 -0
- package/dist/sync/commands.js +27 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/read-tracking.d.ts +25 -0
- package/dist/sync/read-tracking.d.ts.map +1 -0
- package/dist/sync/read-tracking.js +148 -0
- package/dist/sync/read-tracking.js.map +1 -0
- package/dist/sync/submit.js +213 -0
- package/dist/sync/submit.js.map +1 -0
- package/dist/sync/types.d.ts +63 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/util/default-database-adapter.js +6 -1
- package/dist/util/default-database-adapter.js.map +1 -1
- package/dist/with-database.d.ts +3 -6
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +7 -15
- package/dist/with-database.js.map +1 -1
- package/package.json +33 -41
- package/src/adapters/adapters.ts +5 -4
- package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
- package/src/adapters/drizzle/test-utils.ts +2 -1
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
- package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
- package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
- package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
- package/src/adapters/generic-sql/migration/executor.ts +47 -4
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
- package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
- package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
- package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
- package/src/adapters/generic-sql/query/select-builder.ts +2 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
- package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
- package/src/adapters/generic-sql/query/where-builder.ts +48 -29
- package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
- package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
- package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
- package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
- package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
- package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
- package/src/adapters/generic-sql/uow-decoder.ts +3 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
- package/src/adapters/generic-sql/uow-encoder.ts +14 -18
- package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
- package/src/adapters/in-memory/condition-evaluator.ts +9 -4
- package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
- package/src/adapters/in-memory/in-memory-uow.ts +402 -26
- package/src/adapters/in-memory/options.test.ts +1 -0
- package/src/adapters/in-memory/options.ts +5 -1
- package/src/adapters/in-memory/outbox.test.ts +361 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
- package/src/adapters/in-memory/reference-resolution.ts +2 -2
- package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
- package/src/adapters/in-memory/store.test.ts +1 -0
- package/src/adapters/in-memory/store.ts +3 -3
- package/src/adapters/in-memory/value-normalization.test.ts +1 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
- package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
- package/src/adapters/shared/uow-operation-compiler.ts +3 -3
- package/src/browser/mod.ts +64 -0
- package/src/client.ts +19 -0
- package/src/db-fragment-definition-builder.test.ts +821 -47
- package/src/db-fragment-definition-builder.ts +857 -110
- package/src/db-fragment-instantiator.test.ts +114 -90
- package/src/db-fragment-integration.test.ts +9 -6
- package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
- package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
- package/src/dispatchers/cloudflare-do/index.ts +49 -90
- package/src/dispatchers/node/dispatcher.ts +112 -0
- package/src/dispatchers/node/index.test.ts +43 -14
- package/src/dispatchers/node/index.ts +38 -75
- package/src/durable-hooks.test.ts +80 -0
- package/src/durable-hooks.ts +67 -0
- package/src/fragments/internal-fragment.routes.test.ts +570 -0
- package/src/fragments/internal-fragment.routes.ts +297 -5
- package/src/fragments/internal-fragment.schema.ts +45 -1
- package/src/fragments/internal-fragment.test.ts +223 -251
- package/src/fragments/internal-fragment.ts +278 -154
- package/src/hooks/durable-hooks-logger.ts +126 -0
- package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
- package/src/hooks/durable-hooks-processor.test.ts +179 -14
- package/src/hooks/durable-hooks-processor.ts +120 -14
- package/src/hooks/durable-hooks-runtime.test.ts +65 -0
- package/src/hooks/durable-hooks-runtime.ts +81 -0
- package/src/hooks/hooks.test.ts +314 -53
- package/src/hooks/hooks.ts +360 -81
- package/src/id.test.ts +34 -0
- package/src/id.ts +1 -3
- package/src/internal/adapter-registry.test.ts +93 -0
- package/src/internal/adapter-registry.ts +239 -0
- package/src/internal/outbox-state.ts +43 -0
- package/src/migration-engine/auto-from-schema.test.ts +93 -0
- package/src/migration-engine/auto-from-schema.ts +360 -42
- package/src/migration-engine/create.test.ts +2 -1
- package/src/migration-engine/create.ts +1 -1
- package/src/migration-engine/generation-engine.test.ts +66 -9
- package/src/migration-engine/generation-engine.ts +31 -10
- package/src/migration-engine/shared.ts +13 -0
- package/src/mod.ts +45 -27
- package/src/naming/sql-naming.ts +1 -0
- package/src/outbox/outbox-builder.ts +2 -2
- package/src/outbox/outbox.test.ts +216 -45
- package/src/outbox/outbox.ts +3 -1
- package/src/query/column-defaults.ts +1 -1
- package/src/query/condition-builder.test.ts +15 -0
- package/src/query/condition-builder.ts +7 -0
- package/src/query/cursor-client.test.ts +70 -0
- package/src/query/cursor-client.ts +263 -0
- package/src/query/cursor.test.ts +3 -2
- package/src/query/cursor.ts +15 -3
- package/src/query/db-now.ts +69 -2
- package/src/query/orm/orm.ts +2 -2
- package/src/query/query-type.test.ts +2 -1
- package/src/query/serialize/create-sql-serializer.ts +3 -3
- package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
- package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
- package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
- package/src/query/simple-query-interface.ts +10 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
- package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
- package/src/query/unit-of-work/retry-policy.test.ts +1 -0
- package/src/query/unit-of-work/tx-builder.test.ts +73 -1
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
- package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
- package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
- package/src/query/unit-of-work/unit-of-work.ts +105 -19
- package/src/query/value-decoding.test.ts +50 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +1 -0
- package/src/query/value-encoding.ts +1 -1
- package/src/schema/create.test.ts +164 -5
- package/src/schema/create.ts +222 -24
- package/src/schema/generate-id.test.ts +1 -0
- package/src/schema/serialize.test.ts +4 -3
- package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
- package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
- package/src/schema/type-conversion/type-mapping.test.ts +2 -1
- package/src/schema/validator.test.ts +4 -2
- package/src/schema/validator.ts +1 -0
- package/src/schema-output/drizzle.test.ts +72 -19
- package/src/schema-output/drizzle.ts +24 -18
- package/src/schema-output/prisma.test.ts +172 -14
- package/src/schema-output/prisma.ts +34 -14
- package/src/sql-driver/better-sqlite3.test.ts +5 -3
- package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
- package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
- package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
- package/src/sql-driver/query-executor/query-executor.ts +1 -1
- package/src/sql-driver/sql-driver-adapter.ts +2 -2
- package/src/sql-driver/sql.ts +2 -1
- package/src/sql-driver/sqlocal.test.ts +4 -2
- package/src/sync/commands.test.ts +39 -0
- package/src/sync/commands.ts +51 -0
- package/src/sync/conflict-checker.test.ts +450 -0
- package/src/sync/conflict-checker.ts +248 -0
- package/src/sync/index.ts +14 -0
- package/src/sync/plan.ts +9 -0
- package/src/sync/read-tracking.test.ts +177 -0
- package/src/sync/read-tracking.ts +287 -0
- package/src/sync/submit.test.ts +205 -0
- package/src/sync/submit.ts +328 -0
- package/src/sync/types.ts +80 -0
- package/src/util/default-database-adapter.ts +15 -2
- package/src/with-database.ts +20 -50
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +38 -26
- package/vitest.config.ts +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/packages/fragno/dist/api/bind-services.js +0 -20
- package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
- package/dist/packages/fragno/dist/api/error.js +0 -48
- package/dist/packages/fragno/dist/api/error.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
- package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/route.js +0 -10
- package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
- package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
- package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
- package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
- package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/route.js +0 -30
- package/dist/packages/fragno/dist/api/route.js.map +0 -1
- package/dist/packages/fragno/dist/internal/symbols.js +0 -10
- package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
- package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import type { AnyColumn } from "../schema/create";
|
|
4
|
+
import { Cursor, createCursorFromRecord, decodeCursor } from "./cursor-client";
|
|
5
|
+
|
|
6
|
+
const ensureBase64Helpers = () => {
|
|
7
|
+
const g = globalThis as typeof globalThis & {
|
|
8
|
+
btoa?: (input: string) => string;
|
|
9
|
+
atob?: (input: string) => string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (!g.btoa) {
|
|
13
|
+
g.btoa = (input: string) => Buffer.from(input, "binary").toString("base64");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!g.atob) {
|
|
17
|
+
g.atob = (input: string) => Buffer.from(input, "base64").toString("binary");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
beforeAll(() => {
|
|
22
|
+
ensureBase64Helpers();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("cursor-client", () => {
|
|
26
|
+
it("roundtrips cursor data with unicode values", () => {
|
|
27
|
+
const cursor = new Cursor({
|
|
28
|
+
indexName: "idx_created",
|
|
29
|
+
orderDirection: "asc",
|
|
30
|
+
pageSize: 25,
|
|
31
|
+
indexValues: {
|
|
32
|
+
createdAt: 1730000000000,
|
|
33
|
+
name: "München",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const encoded = cursor.encode();
|
|
38
|
+
const decoded = decodeCursor(encoded);
|
|
39
|
+
|
|
40
|
+
expect(decoded.indexName).toBe("idx_created");
|
|
41
|
+
expect(decoded.orderDirection).toBe("asc");
|
|
42
|
+
expect(decoded.pageSize).toBe(25);
|
|
43
|
+
expect(decoded.indexValues).toEqual({
|
|
44
|
+
createdAt: 1730000000000,
|
|
45
|
+
name: "München",
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("creates a cursor from a record", () => {
|
|
50
|
+
const column: AnyColumn = {
|
|
51
|
+
name: "createdAt",
|
|
52
|
+
type: "timestamp",
|
|
53
|
+
role: "regular",
|
|
54
|
+
isNullable: false,
|
|
55
|
+
} as AnyColumn;
|
|
56
|
+
|
|
57
|
+
const cursor = createCursorFromRecord({ createdAt: 123, other: "ignore" }, [column], {
|
|
58
|
+
indexName: "idx_created",
|
|
59
|
+
orderDirection: "desc",
|
|
60
|
+
pageSize: 10,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(cursor.indexValues).toEqual({ createdAt: 123 });
|
|
64
|
+
expect(cursor.orderDirection).toBe("desc");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("rejects malformed cursor payloads", () => {
|
|
68
|
+
expect(() => decodeCursor("not-base64")).toThrowError(/Invalid cursor/);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type { AnyColumn } from "../schema/create";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cursor object containing all information needed for pagination
|
|
5
|
+
*/
|
|
6
|
+
export class Cursor {
|
|
7
|
+
readonly #indexName: string;
|
|
8
|
+
readonly #orderDirection: "asc" | "desc";
|
|
9
|
+
readonly #pageSize: number;
|
|
10
|
+
readonly #indexValues: Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
constructor(data: {
|
|
13
|
+
indexName: string;
|
|
14
|
+
orderDirection: "asc" | "desc";
|
|
15
|
+
pageSize: number;
|
|
16
|
+
indexValues: Record<string, unknown>;
|
|
17
|
+
}) {
|
|
18
|
+
this.#indexName = data.indexName;
|
|
19
|
+
this.#orderDirection = data.orderDirection;
|
|
20
|
+
this.#pageSize = data.pageSize;
|
|
21
|
+
this.#indexValues = data.indexValues;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the index name being used for pagination
|
|
26
|
+
*/
|
|
27
|
+
get indexName(): string {
|
|
28
|
+
return this.#indexName;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the ordering direction
|
|
33
|
+
*/
|
|
34
|
+
get orderDirection(): "asc" | "desc" {
|
|
35
|
+
return this.#orderDirection;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the page size
|
|
40
|
+
*/
|
|
41
|
+
get pageSize(): number {
|
|
42
|
+
return this.#pageSize;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the cursor position values
|
|
47
|
+
*/
|
|
48
|
+
get indexValues(): Record<string, unknown> {
|
|
49
|
+
return this.#indexValues;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Encode cursor to an opaque base64 string (safe to send to client)
|
|
54
|
+
*/
|
|
55
|
+
encode(): string {
|
|
56
|
+
assertSerializableIndexValues(this.#indexValues);
|
|
57
|
+
const data: CursorData = {
|
|
58
|
+
v: 1,
|
|
59
|
+
indexName: this.#indexName,
|
|
60
|
+
orderDirection: this.#orderDirection,
|
|
61
|
+
pageSize: this.#pageSize,
|
|
62
|
+
indexValues: this.#indexValues,
|
|
63
|
+
};
|
|
64
|
+
return encodeCursorData(data);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Result of a cursor-based query containing items and pagination cursor
|
|
70
|
+
*/
|
|
71
|
+
export interface CursorResult<T> {
|
|
72
|
+
/**
|
|
73
|
+
* The query results
|
|
74
|
+
*/
|
|
75
|
+
items: T[];
|
|
76
|
+
/**
|
|
77
|
+
* Cursor to fetch the next page (undefined if no more results)
|
|
78
|
+
*/
|
|
79
|
+
cursor?: Cursor;
|
|
80
|
+
/**
|
|
81
|
+
* Whether there are more results available after this page
|
|
82
|
+
*/
|
|
83
|
+
hasNextPage: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cursor data structure for serialization
|
|
88
|
+
*/
|
|
89
|
+
export interface CursorData {
|
|
90
|
+
v: number; // version
|
|
91
|
+
indexName: string;
|
|
92
|
+
orderDirection: "asc" | "desc";
|
|
93
|
+
pageSize: number;
|
|
94
|
+
indexValues: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const getBase64Helpers = () => {
|
|
98
|
+
const { btoa, atob, TextEncoder, TextDecoder } = globalThis;
|
|
99
|
+
|
|
100
|
+
if (!btoa || !atob) {
|
|
101
|
+
throw new Error("Base64 helpers (btoa/atob) are not available in this environment.");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!TextEncoder || !TextDecoder) {
|
|
105
|
+
throw new Error("TextEncoder/TextDecoder are required for cursor encoding.");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const encodeBase64 = (input: string): string => {
|
|
109
|
+
const bytes = new TextEncoder().encode(input);
|
|
110
|
+
let binary = "";
|
|
111
|
+
for (const byte of bytes) {
|
|
112
|
+
binary += String.fromCharCode(byte);
|
|
113
|
+
}
|
|
114
|
+
return btoa(binary);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const decodeBase64 = (input: string): string => {
|
|
118
|
+
const binary = atob(input);
|
|
119
|
+
const bytes = new Uint8Array(binary.length);
|
|
120
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
121
|
+
bytes[i] = binary.charCodeAt(i);
|
|
122
|
+
}
|
|
123
|
+
return new TextDecoder().decode(bytes);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return { encodeBase64, decodeBase64 };
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Encode cursor data to a base64 string (internal)
|
|
131
|
+
*/
|
|
132
|
+
function encodeCursorData(data: CursorData): string {
|
|
133
|
+
let json: string;
|
|
134
|
+
try {
|
|
135
|
+
json = JSON.stringify(data);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return getBase64Helpers().encodeBase64(json);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Decode a base64 cursor string back to a Cursor object
|
|
145
|
+
*
|
|
146
|
+
* @param cursor - The base64-encoded cursor string
|
|
147
|
+
* @returns Decoded Cursor object
|
|
148
|
+
* @throws Error if cursor is invalid or malformed
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const cursor = decodeCursor("eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==");
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function decodeCursor(cursor: string): Cursor {
|
|
156
|
+
try {
|
|
157
|
+
const json = getBase64Helpers().decodeBase64(cursor);
|
|
158
|
+
const data = JSON.parse(json);
|
|
159
|
+
const record = data as Record<string, unknown>;
|
|
160
|
+
|
|
161
|
+
// Validate structure
|
|
162
|
+
if (
|
|
163
|
+
!isPlainObject(data) ||
|
|
164
|
+
!isPlainObject(record["indexValues"]) ||
|
|
165
|
+
typeof record["indexName"] !== "string" ||
|
|
166
|
+
record["indexName"].length === 0 ||
|
|
167
|
+
typeof record["orderDirection"] !== "string" ||
|
|
168
|
+
(record["orderDirection"] !== "asc" && record["orderDirection"] !== "desc") ||
|
|
169
|
+
typeof record["pageSize"] !== "number" ||
|
|
170
|
+
!Number.isFinite(record["pageSize"]) ||
|
|
171
|
+
!Number.isInteger(record["pageSize"]) ||
|
|
172
|
+
record["pageSize"] <= 0
|
|
173
|
+
) {
|
|
174
|
+
throw new Error("Invalid cursor structure");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Only support v1
|
|
178
|
+
if (typeof record["v"] !== "number") {
|
|
179
|
+
throw new Error("Unsupported cursor version: missing. Only v1 is supported.");
|
|
180
|
+
}
|
|
181
|
+
const version = record["v"];
|
|
182
|
+
if (version !== 1) {
|
|
183
|
+
throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return new Cursor({
|
|
187
|
+
indexName: record["indexName"],
|
|
188
|
+
orderDirection: record["orderDirection"],
|
|
189
|
+
pageSize: record["pageSize"],
|
|
190
|
+
indexValues: record["indexValues"],
|
|
191
|
+
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Create a cursor from a record and pagination metadata
|
|
199
|
+
*
|
|
200
|
+
* @param record - The database record
|
|
201
|
+
* @param indexColumns - The columns that make up the index
|
|
202
|
+
* @param metadata - Pagination metadata (index name, order direction, page size)
|
|
203
|
+
* @returns Cursor object
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const cursor = createCursorFromRecord(
|
|
208
|
+
* { id: "abc", name: "Alice", createdAt: 123 },
|
|
209
|
+
* [table.columns.createdAt, table.columns.id],
|
|
210
|
+
* {
|
|
211
|
+
* indexName: "idx_created",
|
|
212
|
+
* orderDirection: "asc",
|
|
213
|
+
* pageSize: 10
|
|
214
|
+
* }
|
|
215
|
+
* );
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function createCursorFromRecord(
|
|
219
|
+
record: Record<string, unknown>,
|
|
220
|
+
indexColumns: AnyColumn[],
|
|
221
|
+
metadata: {
|
|
222
|
+
indexName: string;
|
|
223
|
+
orderDirection: "asc" | "desc";
|
|
224
|
+
pageSize: number;
|
|
225
|
+
},
|
|
226
|
+
): Cursor {
|
|
227
|
+
const indexValues: Record<string, unknown> = {};
|
|
228
|
+
|
|
229
|
+
for (const col of indexColumns) {
|
|
230
|
+
const value = record[col.name];
|
|
231
|
+
if (value === undefined) {
|
|
232
|
+
throw new Error(`Record is missing value for index column "${col.name}".`);
|
|
233
|
+
}
|
|
234
|
+
indexValues[col.name] = value;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return new Cursor({
|
|
238
|
+
indexName: metadata.indexName,
|
|
239
|
+
orderDirection: metadata.orderDirection,
|
|
240
|
+
pageSize: metadata.pageSize,
|
|
241
|
+
indexValues,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const isPlainObject = (value: unknown): value is Record<string, unknown> =>
|
|
246
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
247
|
+
|
|
248
|
+
const assertSerializableIndexValues = (values: Record<string, unknown>): void => {
|
|
249
|
+
for (const [key, value] of Object.entries(values)) {
|
|
250
|
+
if (value === undefined) {
|
|
251
|
+
throw new Error(`Cursor index value "${key}" is undefined.`);
|
|
252
|
+
}
|
|
253
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
254
|
+
throw new Error(`Cursor index value "${key}" must be a finite number.`);
|
|
255
|
+
}
|
|
256
|
+
if (typeof value === "bigint") {
|
|
257
|
+
throw new Error(`Cursor index value "${key}" must not be a BigInt.`);
|
|
258
|
+
}
|
|
259
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
260
|
+
throw new Error(`Cursor index value "${key}" is not JSON-serializable.`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
package/src/query/cursor.test.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { column, idColumn, schema } from "../schema/create";
|
|
2
|
+
|
|
4
3
|
import {
|
|
5
4
|
BetterSQLite3DriverConfig,
|
|
6
5
|
MySQL2DriverConfig,
|
|
7
6
|
NodePostgresDriverConfig,
|
|
8
7
|
} from "../adapters/generic-sql/driver-config";
|
|
8
|
+
import { column, idColumn, schema } from "../schema/create";
|
|
9
|
+
import { decodeCursor, createCursorFromRecord, serializeCursorValues, Cursor } from "./cursor";
|
|
9
10
|
|
|
10
11
|
describe("Cursor utilities", () => {
|
|
11
12
|
describe("Cursor class encode and decode", () => {
|
package/src/query/cursor.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { DriverConfig } from "../adapters/generic-sql/driver-config";
|
|
2
|
+
import type { SQLiteStorageMode } from "../adapters/generic-sql/sqlite-storage";
|
|
1
3
|
import type { AnyColumn } from "../schema/create";
|
|
2
4
|
import { createSQLSerializer } from "./serialize/create-sql-serializer";
|
|
3
5
|
import { resolveFragnoIdValue } from "./value-encoding";
|
|
4
|
-
import type { DriverConfig } from "../adapters/generic-sql/driver-config";
|
|
5
|
-
import type { SQLiteStorageMode } from "../adapters/generic-sql/sqlite-storage";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Cursor object containing all information needed for pagination
|
|
@@ -211,7 +211,10 @@ export function createCursorFromRecord(
|
|
|
211
211
|
if (value === undefined) {
|
|
212
212
|
throw new Error(`Record is missing value for index column "${col.name}".`);
|
|
213
213
|
}
|
|
214
|
-
|
|
214
|
+
// Resolve FragnoId/FragnoReference to primitive values for cursor serialization.
|
|
215
|
+
const resolved = resolveFragnoIdValue(value, col);
|
|
216
|
+
// BigInt values are not JSON-serializable, so store them as strings.
|
|
217
|
+
indexValues[col.name] = typeof resolved === "bigint" ? resolved.toString() : resolved;
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
return new Cursor({
|
|
@@ -263,6 +266,15 @@ export function serializeCursorValues(
|
|
|
263
266
|
missingColumns.push(col.name);
|
|
264
267
|
continue;
|
|
265
268
|
}
|
|
269
|
+
|
|
270
|
+
// If the cursor value is already a FragnoId/FragnoReference, resolve it directly
|
|
271
|
+
// to avoid deserializing non-JSON objects.
|
|
272
|
+
const resolvedDirect = resolveFragnoIdValue(value, col);
|
|
273
|
+
if (resolvedDirect !== value) {
|
|
274
|
+
serialized[col.name] = serializer.serialize(resolvedDirect, col);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
266
278
|
// First deserialize from JSON format to application format
|
|
267
279
|
// (e.g., "2025-11-07T09:36:57.959Z" string → Date object)
|
|
268
280
|
const deserialized = serializer.deserialize(value, col);
|
package/src/query/db-now.ts
CHANGED
|
@@ -1,6 +1,73 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type DbInterval = { tag: "db-interval"; ms: number };
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export type DbIntervalInput =
|
|
4
|
+
| number
|
|
5
|
+
| {
|
|
6
|
+
ms?: number;
|
|
7
|
+
seconds?: number;
|
|
8
|
+
minutes?: number;
|
|
9
|
+
hours?: number;
|
|
10
|
+
days?: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type DbNow = {
|
|
14
|
+
tag: "db-now";
|
|
15
|
+
offsetMs?: number;
|
|
16
|
+
plus: (interval: DbInterval | DbIntervalInput) => DbNow;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const toIntervalMs = (input: DbInterval | DbIntervalInput): number => {
|
|
20
|
+
if (typeof input === "number") {
|
|
21
|
+
if (!Number.isFinite(input)) {
|
|
22
|
+
throw new Error("DB_INTERVAL_INVALID");
|
|
23
|
+
}
|
|
24
|
+
return input;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof input === "object" && input !== null && "tag" in input) {
|
|
28
|
+
const tagged = input as DbInterval;
|
|
29
|
+
if (tagged.tag === "db-interval") {
|
|
30
|
+
if (!Number.isFinite(tagged.ms)) {
|
|
31
|
+
throw new Error("DB_INTERVAL_INVALID");
|
|
32
|
+
}
|
|
33
|
+
return tagged.ms;
|
|
34
|
+
}
|
|
35
|
+
throw new Error("DB_INTERVAL_INVALID");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const interval = input as Exclude<DbIntervalInput, number>;
|
|
39
|
+
const totalMs =
|
|
40
|
+
(interval.ms ?? 0) +
|
|
41
|
+
(interval.seconds ?? 0) * 1000 +
|
|
42
|
+
(interval.minutes ?? 0) * 60_000 +
|
|
43
|
+
(interval.hours ?? 0) * 3_600_000 +
|
|
44
|
+
(interval.days ?? 0) * 86_400_000;
|
|
45
|
+
|
|
46
|
+
if (!Number.isFinite(totalMs)) {
|
|
47
|
+
throw new Error("DB_INTERVAL_INVALID");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return totalMs;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const createDbNow = (offsetMs = 0): DbNow => ({
|
|
54
|
+
tag: "db-now",
|
|
55
|
+
offsetMs,
|
|
56
|
+
plus: (interval) => createDbNow(offsetMs + toIntervalMs(interval)),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const dbNow = (): DbNow => createDbNow(0);
|
|
4
60
|
|
|
5
61
|
export const isDbNow = (value: unknown): value is DbNow =>
|
|
6
62
|
typeof value === "object" && value !== null && (value as { tag?: string }).tag === "db-now";
|
|
63
|
+
|
|
64
|
+
export const getDbNowOffsetMs = (value: DbNow): number =>
|
|
65
|
+
typeof value.offsetMs === "number" ? value.offsetMs : 0;
|
|
66
|
+
|
|
67
|
+
export const dbInterval = (input: DbIntervalInput): DbInterval => ({
|
|
68
|
+
tag: "db-interval",
|
|
69
|
+
ms: toIntervalMs(input),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const isDbInterval = (value: unknown): value is DbInterval =>
|
|
73
|
+
typeof value === "object" && value !== null && (value as { tag?: string }).tag === "db-interval";
|
package/src/query/orm/orm.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AnyColumn, AnyRelation, AnyTable } from "../../schema/create";
|
|
2
|
+
import { buildCondition, type Condition } from "../condition-builder";
|
|
1
3
|
import type {
|
|
2
4
|
AnySelectClause,
|
|
3
5
|
FindFirstOptions,
|
|
@@ -5,8 +7,6 @@ import type {
|
|
|
5
7
|
JoinBuilder,
|
|
6
8
|
OrderBy,
|
|
7
9
|
} from "../simple-query-interface";
|
|
8
|
-
import { buildCondition, type Condition } from "../condition-builder";
|
|
9
|
-
import type { AnyColumn, AnyRelation, AnyTable } from "../../schema/create";
|
|
10
10
|
|
|
11
11
|
export interface CompiledJoin {
|
|
12
12
|
relation: AnyRelation;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expectTypeOf, it } from "vitest";
|
|
2
|
+
|
|
2
3
|
import { column, FragnoId, idColumn, referenceColumn, schema } from "../schema/create";
|
|
4
|
+
import type { ConditionBuilder } from "./condition-builder";
|
|
3
5
|
import type {
|
|
4
6
|
SimpleQueryInterface,
|
|
5
7
|
FindFirstOptions,
|
|
@@ -9,7 +11,6 @@ import type {
|
|
|
9
11
|
SelectClause,
|
|
10
12
|
TableToInsertValues,
|
|
11
13
|
} from "./simple-query-interface";
|
|
12
|
-
import type { ConditionBuilder } from "./condition-builder";
|
|
13
14
|
|
|
14
15
|
describe("query type tests", () => {
|
|
15
16
|
// Create test schema
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DriverConfig } from "../../adapters/generic-sql/driver-config";
|
|
2
2
|
import type { SQLiteStorageMode } from "../../adapters/generic-sql/sqlite-storage";
|
|
3
|
-
import { SQLSerializer } from "./sql-serializer";
|
|
4
|
-
import { SQLiteSerializer } from "./dialect/sqlite-serializer";
|
|
5
|
-
import { PostgreSQLSerializer } from "./dialect/postgres-serializer";
|
|
6
3
|
import { MySQLSerializer } from "./dialect/mysql-serializer";
|
|
4
|
+
import { PostgreSQLSerializer } from "./dialect/postgres-serializer";
|
|
5
|
+
import { SQLiteSerializer } from "./dialect/sqlite-serializer";
|
|
6
|
+
import { SQLSerializer } from "./sql-serializer";
|
|
7
7
|
|
|
8
8
|
// Re-export SQLSerializer for convenience
|
|
9
9
|
export { SQLSerializer } from "./sql-serializer";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
1
2
|
import type { AnyColumn } from "../../../schema/create";
|
|
2
3
|
import { SQLSerializer } from "../sql-serializer";
|
|
3
|
-
import type { DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* MySQL-specific serializer.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
1
2
|
import type { AnyColumn } from "../../../schema/create";
|
|
2
3
|
import { SQLSerializer } from "../sql-serializer";
|
|
3
|
-
import type { DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PostgreSQL-specific serializer.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import type { AnyColumn } from "../../../schema/create";
|
|
2
|
+
|
|
4
3
|
import { BetterSQLite3DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
5
4
|
import { sqliteStoragePrisma } from "../../../adapters/generic-sql/sqlite-storage";
|
|
5
|
+
import type { AnyColumn } from "../../../schema/create";
|
|
6
|
+
import { SQLiteSerializer } from "./sqlite-serializer";
|
|
6
7
|
|
|
7
8
|
describe("SQLiteSerializer", () => {
|
|
8
9
|
const mockDriverConfig = new BetterSQLite3DriverConfig();
|
|
@@ -219,6 +220,11 @@ describe("SQLiteSerializer", () => {
|
|
|
219
220
|
const date = prismaSerializer["deserializeDate"]("2024-03-10 12:34:56.789", timestampColumn);
|
|
220
221
|
expect(date.toISOString()).toBe("2024-03-10T12:34:56.789Z");
|
|
221
222
|
});
|
|
223
|
+
|
|
224
|
+
it("should parse numeric timestamp strings with decimals", () => {
|
|
225
|
+
const date = serializer["deserializeDate"]("1772709276409.0", timestampColumn);
|
|
226
|
+
expect(date.getTime()).toBe(1772709276409);
|
|
227
|
+
});
|
|
222
228
|
});
|
|
223
229
|
|
|
224
230
|
describe("deserializeInteger", () => {
|
|
@@ -297,5 +303,36 @@ describe("SQLiteSerializer", () => {
|
|
|
297
303
|
/exceeds Number\.MAX_SAFE_INTEGER/,
|
|
298
304
|
);
|
|
299
305
|
});
|
|
306
|
+
|
|
307
|
+
it("should deserialize bigint from ArrayBuffer blob values", () => {
|
|
308
|
+
const expected = BigInt("9007199254740993");
|
|
309
|
+
const buffer = Buffer.alloc(8);
|
|
310
|
+
buffer.writeBigInt64BE(expected);
|
|
311
|
+
const arrayBuffer = buffer.buffer.slice(
|
|
312
|
+
buffer.byteOffset,
|
|
313
|
+
buffer.byteOffset + buffer.byteLength,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(serializer["deserializeBigInt"](arrayBuffer)).toBe(expected);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should deserialize bigint from Uint8Array blob values", () => {
|
|
320
|
+
const expected = BigInt("123456789012345678");
|
|
321
|
+
const buffer = Buffer.alloc(8);
|
|
322
|
+
buffer.writeBigInt64BE(expected);
|
|
323
|
+
const value = new Uint8Array(buffer);
|
|
324
|
+
|
|
325
|
+
expect(serializer["deserializeBigInt"](value)).toBe(expected);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it.each([
|
|
329
|
+
["Buffer", Buffer.alloc(9)],
|
|
330
|
+
["ArrayBuffer", new ArrayBuffer(9)],
|
|
331
|
+
["Uint8Array", new Uint8Array(9)],
|
|
332
|
+
])("should reject %s blob values when byte length is not exactly 8", (_, value) => {
|
|
333
|
+
expect(() => serializer["deserializeBigInt"](value)).toThrow(
|
|
334
|
+
/Cannot deserialize bigint from 9 bytes; expected 8\./,
|
|
335
|
+
);
|
|
336
|
+
});
|
|
300
337
|
});
|
|
301
338
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { AnyColumn } from "../../../schema/create";
|
|
2
|
-
import { SQLSerializer } from "../sql-serializer";
|
|
3
1
|
import type { DriverConfig } from "../../../adapters/generic-sql/driver-config";
|
|
4
2
|
import type { SQLiteStorageMode } from "../../../adapters/generic-sql/sqlite-storage";
|
|
5
3
|
import { sqliteStorageDefault } from "../../../adapters/generic-sql/sqlite-storage";
|
|
4
|
+
import type { AnyColumn } from "../../../schema/create";
|
|
5
|
+
import { SQLSerializer } from "../sql-serializer";
|
|
6
6
|
|
|
7
7
|
const SQLITE_UTC_TIMESTAMP_REGEX =
|
|
8
8
|
/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/;
|
|
@@ -28,6 +28,19 @@ function parseSQLiteUtcTimestamp(value: string): Date | null {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function readBigInt64BEFromBytes(value: ArrayBuffer | ArrayBufferView): bigint {
|
|
32
|
+
if (value.byteLength !== 8) {
|
|
33
|
+
throw new Error(`Cannot deserialize bigint from ${value.byteLength} bytes; expected 8.`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const view =
|
|
37
|
+
value instanceof ArrayBuffer
|
|
38
|
+
? new DataView(value)
|
|
39
|
+
: new DataView(value.buffer, value.byteOffset, value.byteLength);
|
|
40
|
+
|
|
41
|
+
return view.getBigInt64(0, false);
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
/**
|
|
32
45
|
* SQLite-specific serializer.
|
|
33
46
|
*
|
|
@@ -89,7 +102,7 @@ export class SQLiteSerializer extends SQLSerializer {
|
|
|
89
102
|
return value;
|
|
90
103
|
}
|
|
91
104
|
if (typeof value === "string") {
|
|
92
|
-
if (/^\d
|
|
105
|
+
if (/^\d+(?:\.\d+)?$/.test(value)) {
|
|
93
106
|
const numericDate = new Date(Number(value));
|
|
94
107
|
if (Number.isNaN(numericDate.getTime())) {
|
|
95
108
|
throw new Error(`Cannot deserialize date from value: ${value}`);
|
|
@@ -139,8 +152,8 @@ export class SQLiteSerializer extends SQLSerializer {
|
|
|
139
152
|
if (typeof value === "bigint") {
|
|
140
153
|
return value;
|
|
141
154
|
}
|
|
142
|
-
if (value instanceof Buffer) {
|
|
143
|
-
return value
|
|
155
|
+
if (value instanceof Buffer || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
156
|
+
return readBigInt64BEFromBytes(value);
|
|
144
157
|
}
|
|
145
158
|
if (typeof value === "string") {
|
|
146
159
|
return BigInt(value);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { IdColumn, AnySchema, AnyTable, Relation, FragnoId } from "../schema/create";
|
|
2
|
+
import type { Prettify } from "../util/types";
|
|
2
3
|
import type { Condition, ConditionBuilder } from "./condition-builder";
|
|
4
|
+
import type { CursorResult } from "./cursor";
|
|
3
5
|
import type {
|
|
4
6
|
TypedUnitOfWork,
|
|
5
7
|
FindBuilder,
|
|
@@ -7,12 +9,11 @@ import type {
|
|
|
7
9
|
DeleteBuilder,
|
|
8
10
|
UpdateManyBuilder,
|
|
9
11
|
} from "./unit-of-work/unit-of-work";
|
|
10
|
-
import type {
|
|
11
|
-
import type { CursorResult } from "./cursor";
|
|
12
|
+
import type { IUnitOfWork } from "./unit-of-work/unit-of-work";
|
|
12
13
|
|
|
13
14
|
export type AnySelectClause = SelectClause<AnyTable>;
|
|
14
15
|
|
|
15
|
-
export type SelectClause<T extends AnyTable> = true | (keyof T["columns"])[];
|
|
16
|
+
export type SelectClause<T extends AnyTable> = true | readonly (keyof T["columns"])[];
|
|
16
17
|
|
|
17
18
|
export type RawColumnValues<T extends AnyTable> = {
|
|
18
19
|
[K in keyof T["columns"] as string extends K ? never : K]: T["columns"][K]["$out"];
|
|
@@ -44,7 +45,7 @@ export type TableToUpdateValues<T extends AnyTable> = {
|
|
|
44
45
|
|
|
45
46
|
type MainSelectResult<S extends SelectClause<T>, T extends AnyTable> = S extends true
|
|
46
47
|
? TableToColumnValues<T>
|
|
47
|
-
: S extends (keyof T["columns"])[]
|
|
48
|
+
: S extends readonly (keyof T["columns"])[]
|
|
48
49
|
? Prettify<{
|
|
49
50
|
[K in S[number] as string extends K ? never : K]: K extends keyof T["columns"]
|
|
50
51
|
? T["columns"][K]["$out"]
|
|
@@ -264,4 +265,9 @@ export interface SimpleQueryInterface<TSchema extends AnySchema, TUOWConfig = vo
|
|
|
264
265
|
* Create a Unit of Work bound to this query engine
|
|
265
266
|
*/
|
|
266
267
|
createUnitOfWork: (name?: string, config?: TUOWConfig) => TypedUnitOfWork<TSchema, [], unknown>;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create a base Unit of Work for cross-schema operations.
|
|
271
|
+
*/
|
|
272
|
+
createBaseUnitOfWork: (name?: string, config?: TUOWConfig) => IUnitOfWork;
|
|
267
273
|
}
|