@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,105 @@
|
|
|
1
|
+
import { AnyColumn } from "../schema/create.js";
|
|
2
|
+
|
|
3
|
+
//#region src/query/cursor-client.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cursor object containing all information needed for pagination
|
|
7
|
+
*/
|
|
8
|
+
declare class Cursor {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(data: {
|
|
11
|
+
indexName: string;
|
|
12
|
+
orderDirection: "asc" | "desc";
|
|
13
|
+
pageSize: number;
|
|
14
|
+
indexValues: Record<string, unknown>;
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Get the index name being used for pagination
|
|
18
|
+
*/
|
|
19
|
+
get indexName(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get the ordering direction
|
|
22
|
+
*/
|
|
23
|
+
get orderDirection(): "asc" | "desc";
|
|
24
|
+
/**
|
|
25
|
+
* Get the page size
|
|
26
|
+
*/
|
|
27
|
+
get pageSize(): number;
|
|
28
|
+
/**
|
|
29
|
+
* Get the cursor position values
|
|
30
|
+
*/
|
|
31
|
+
get indexValues(): Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* Encode cursor to an opaque base64 string (safe to send to client)
|
|
34
|
+
*/
|
|
35
|
+
encode(): string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Result of a cursor-based query containing items and pagination cursor
|
|
39
|
+
*/
|
|
40
|
+
interface CursorResult<T> {
|
|
41
|
+
/**
|
|
42
|
+
* The query results
|
|
43
|
+
*/
|
|
44
|
+
items: T[];
|
|
45
|
+
/**
|
|
46
|
+
* Cursor to fetch the next page (undefined if no more results)
|
|
47
|
+
*/
|
|
48
|
+
cursor?: Cursor;
|
|
49
|
+
/**
|
|
50
|
+
* Whether there are more results available after this page
|
|
51
|
+
*/
|
|
52
|
+
hasNextPage: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Cursor data structure for serialization
|
|
56
|
+
*/
|
|
57
|
+
interface CursorData {
|
|
58
|
+
v: number;
|
|
59
|
+
indexName: string;
|
|
60
|
+
orderDirection: "asc" | "desc";
|
|
61
|
+
pageSize: number;
|
|
62
|
+
indexValues: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Decode a base64 cursor string back to a Cursor object
|
|
66
|
+
*
|
|
67
|
+
* @param cursor - The base64-encoded cursor string
|
|
68
|
+
* @returns Decoded Cursor object
|
|
69
|
+
* @throws Error if cursor is invalid or malformed
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const cursor = decodeCursor("eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==");
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
declare function decodeCursor(cursor: string): Cursor;
|
|
77
|
+
/**
|
|
78
|
+
* Create a cursor from a record and pagination metadata
|
|
79
|
+
*
|
|
80
|
+
* @param record - The database record
|
|
81
|
+
* @param indexColumns - The columns that make up the index
|
|
82
|
+
* @param metadata - Pagination metadata (index name, order direction, page size)
|
|
83
|
+
* @returns Cursor object
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* const cursor = createCursorFromRecord(
|
|
88
|
+
* { id: "abc", name: "Alice", createdAt: 123 },
|
|
89
|
+
* [table.columns.createdAt, table.columns.id],
|
|
90
|
+
* {
|
|
91
|
+
* indexName: "idx_created",
|
|
92
|
+
* orderDirection: "asc",
|
|
93
|
+
* pageSize: 10
|
|
94
|
+
* }
|
|
95
|
+
* );
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare function createCursorFromRecord(record: Record<string, unknown>, indexColumns: AnyColumn[], metadata: {
|
|
99
|
+
indexName: string;
|
|
100
|
+
orderDirection: "asc" | "desc";
|
|
101
|
+
pageSize: number;
|
|
102
|
+
}): Cursor;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { Cursor, CursorData, CursorResult, createCursorFromRecord, decodeCursor };
|
|
105
|
+
//# sourceMappingURL=cursor-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-client.d.ts","names":[],"sources":["../../src/query/cursor-client.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAiEiB,cAjEJ,MAAA,CAiEgB;EAkBZ,CAAA,OAAA;EAkED,WAAA,CAAA,IAAY,EAAA;IA+DZ,SAAA,EAAA,MAAA;IACN,cAAA,EAAA,KAAA,GAAA,MAAA;IACM,QAAA,EAAA,MAAA;IAMb,WAAA,EAlNc,MAkNd,CAAA,MAAA,EAAA,OAAA,CAAA;EAAM,CAAA;;;;;;;;;;;;;;;;qBAlLY;;;;;;;;;UAuBJ;;;;SAIR;;;;WAIE;;;;;;;;;UAUM,UAAA;;;;;eAKF;;;;;;;;;;;;;;iBA6DC,YAAA,kBAA8B;;;;;;;;;;;;;;;;;;;;;;iBA+D9B,sBAAA,SACN,uCACM;;;;IAMb"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
//#region src/query/cursor-client.ts
|
|
2
|
+
/**
|
|
3
|
+
* Cursor object containing all information needed for pagination
|
|
4
|
+
*/
|
|
5
|
+
var Cursor = class {
|
|
6
|
+
#indexName;
|
|
7
|
+
#orderDirection;
|
|
8
|
+
#pageSize;
|
|
9
|
+
#indexValues;
|
|
10
|
+
constructor(data) {
|
|
11
|
+
this.#indexName = data.indexName;
|
|
12
|
+
this.#orderDirection = data.orderDirection;
|
|
13
|
+
this.#pageSize = data.pageSize;
|
|
14
|
+
this.#indexValues = data.indexValues;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get the index name being used for pagination
|
|
18
|
+
*/
|
|
19
|
+
get indexName() {
|
|
20
|
+
return this.#indexName;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the ordering direction
|
|
24
|
+
*/
|
|
25
|
+
get orderDirection() {
|
|
26
|
+
return this.#orderDirection;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the page size
|
|
30
|
+
*/
|
|
31
|
+
get pageSize() {
|
|
32
|
+
return this.#pageSize;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the cursor position values
|
|
36
|
+
*/
|
|
37
|
+
get indexValues() {
|
|
38
|
+
return this.#indexValues;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Encode cursor to an opaque base64 string (safe to send to client)
|
|
42
|
+
*/
|
|
43
|
+
encode() {
|
|
44
|
+
assertSerializableIndexValues(this.#indexValues);
|
|
45
|
+
return encodeCursorData({
|
|
46
|
+
v: 1,
|
|
47
|
+
indexName: this.#indexName,
|
|
48
|
+
orderDirection: this.#orderDirection,
|
|
49
|
+
pageSize: this.#pageSize,
|
|
50
|
+
indexValues: this.#indexValues
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const getBase64Helpers = () => {
|
|
55
|
+
const { btoa, atob, TextEncoder, TextDecoder } = globalThis;
|
|
56
|
+
if (!btoa || !atob) throw new Error("Base64 helpers (btoa/atob) are not available in this environment.");
|
|
57
|
+
if (!TextEncoder || !TextDecoder) throw new Error("TextEncoder/TextDecoder are required for cursor encoding.");
|
|
58
|
+
const encodeBase64 = (input) => {
|
|
59
|
+
const bytes = new TextEncoder().encode(input);
|
|
60
|
+
let binary = "";
|
|
61
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
62
|
+
return btoa(binary);
|
|
63
|
+
};
|
|
64
|
+
const decodeBase64 = (input) => {
|
|
65
|
+
const binary = atob(input);
|
|
66
|
+
const bytes = new Uint8Array(binary.length);
|
|
67
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
68
|
+
return new TextDecoder().decode(bytes);
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
encodeBase64,
|
|
72
|
+
decodeBase64
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Encode cursor data to a base64 string (internal)
|
|
77
|
+
*/
|
|
78
|
+
function encodeCursorData(data) {
|
|
79
|
+
let json;
|
|
80
|
+
try {
|
|
81
|
+
json = JSON.stringify(data);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
|
|
84
|
+
}
|
|
85
|
+
return getBase64Helpers().encodeBase64(json);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Decode a base64 cursor string back to a Cursor object
|
|
89
|
+
*
|
|
90
|
+
* @param cursor - The base64-encoded cursor string
|
|
91
|
+
* @returns Decoded Cursor object
|
|
92
|
+
* @throws Error if cursor is invalid or malformed
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const cursor = decodeCursor("eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==");
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
function decodeCursor(cursor) {
|
|
100
|
+
try {
|
|
101
|
+
const json = getBase64Helpers().decodeBase64(cursor);
|
|
102
|
+
const data = JSON.parse(json);
|
|
103
|
+
const record = data;
|
|
104
|
+
if (!isPlainObject(data) || !isPlainObject(record["indexValues"]) || typeof record["indexName"] !== "string" || record["indexName"].length === 0 || typeof record["orderDirection"] !== "string" || record["orderDirection"] !== "asc" && record["orderDirection"] !== "desc" || typeof record["pageSize"] !== "number" || !Number.isFinite(record["pageSize"]) || !Number.isInteger(record["pageSize"]) || record["pageSize"] <= 0) throw new Error("Invalid cursor structure");
|
|
105
|
+
if (typeof record["v"] !== "number") throw new Error("Unsupported cursor version: missing. Only v1 is supported.");
|
|
106
|
+
const version = record["v"];
|
|
107
|
+
if (version !== 1) throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);
|
|
108
|
+
return new Cursor({
|
|
109
|
+
indexName: record["indexName"],
|
|
110
|
+
orderDirection: record["orderDirection"],
|
|
111
|
+
pageSize: record["pageSize"],
|
|
112
|
+
indexValues: record["indexValues"]
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create a cursor from a record and pagination metadata
|
|
120
|
+
*
|
|
121
|
+
* @param record - The database record
|
|
122
|
+
* @param indexColumns - The columns that make up the index
|
|
123
|
+
* @param metadata - Pagination metadata (index name, order direction, page size)
|
|
124
|
+
* @returns Cursor object
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const cursor = createCursorFromRecord(
|
|
129
|
+
* { id: "abc", name: "Alice", createdAt: 123 },
|
|
130
|
+
* [table.columns.createdAt, table.columns.id],
|
|
131
|
+
* {
|
|
132
|
+
* indexName: "idx_created",
|
|
133
|
+
* orderDirection: "asc",
|
|
134
|
+
* pageSize: 10
|
|
135
|
+
* }
|
|
136
|
+
* );
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
function createCursorFromRecord(record, indexColumns, metadata) {
|
|
140
|
+
const indexValues = {};
|
|
141
|
+
for (const col of indexColumns) {
|
|
142
|
+
const value = record[col.name];
|
|
143
|
+
if (value === void 0) throw new Error(`Record is missing value for index column "${col.name}".`);
|
|
144
|
+
indexValues[col.name] = value;
|
|
145
|
+
}
|
|
146
|
+
return new Cursor({
|
|
147
|
+
indexName: metadata.indexName,
|
|
148
|
+
orderDirection: metadata.orderDirection,
|
|
149
|
+
pageSize: metadata.pageSize,
|
|
150
|
+
indexValues
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
154
|
+
const assertSerializableIndexValues = (values) => {
|
|
155
|
+
for (const [key, value] of Object.entries(values)) {
|
|
156
|
+
if (value === void 0) throw new Error(`Cursor index value "${key}" is undefined.`);
|
|
157
|
+
if (typeof value === "number" && !Number.isFinite(value)) throw new Error(`Cursor index value "${key}" must be a finite number.`);
|
|
158
|
+
if (typeof value === "bigint") throw new Error(`Cursor index value "${key}" must not be a BigInt.`);
|
|
159
|
+
if (typeof value === "function" || typeof value === "symbol") throw new Error(`Cursor index value "${key}" is not JSON-serializable.`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
export { Cursor, createCursorFromRecord, decodeCursor };
|
|
165
|
+
//# sourceMappingURL=cursor-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-client.js","names":["#indexName","#orderDirection","#pageSize","#indexValues","json: string","indexValues: Record<string, unknown>"],"sources":["../../src/query/cursor-client.ts"],"sourcesContent":["import type { AnyColumn } from \"../schema/create\";\n\n/**\n * Cursor object containing all information needed for pagination\n */\nexport class Cursor {\n readonly #indexName: string;\n readonly #orderDirection: \"asc\" | \"desc\";\n readonly #pageSize: number;\n readonly #indexValues: Record<string, unknown>;\n\n constructor(data: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n }) {\n this.#indexName = data.indexName;\n this.#orderDirection = data.orderDirection;\n this.#pageSize = data.pageSize;\n this.#indexValues = data.indexValues;\n }\n\n /**\n * Get the index name being used for pagination\n */\n get indexName(): string {\n return this.#indexName;\n }\n\n /**\n * Get the ordering direction\n */\n get orderDirection(): \"asc\" | \"desc\" {\n return this.#orderDirection;\n }\n\n /**\n * Get the page size\n */\n get pageSize(): number {\n return this.#pageSize;\n }\n\n /**\n * Get the cursor position values\n */\n get indexValues(): Record<string, unknown> {\n return this.#indexValues;\n }\n\n /**\n * Encode cursor to an opaque base64 string (safe to send to client)\n */\n encode(): string {\n assertSerializableIndexValues(this.#indexValues);\n const data: CursorData = {\n v: 1,\n indexName: this.#indexName,\n orderDirection: this.#orderDirection,\n pageSize: this.#pageSize,\n indexValues: this.#indexValues,\n };\n return encodeCursorData(data);\n }\n}\n\n/**\n * Result of a cursor-based query containing items and pagination cursor\n */\nexport interface CursorResult<T> {\n /**\n * The query results\n */\n items: T[];\n /**\n * Cursor to fetch the next page (undefined if no more results)\n */\n cursor?: Cursor;\n /**\n * Whether there are more results available after this page\n */\n hasNextPage: boolean;\n}\n\n/**\n * Cursor data structure for serialization\n */\nexport interface CursorData {\n v: number; // version\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n}\n\nconst getBase64Helpers = () => {\n const { btoa, atob, TextEncoder, TextDecoder } = globalThis;\n\n if (!btoa || !atob) {\n throw new Error(\"Base64 helpers (btoa/atob) are not available in this environment.\");\n }\n\n if (!TextEncoder || !TextDecoder) {\n throw new Error(\"TextEncoder/TextDecoder are required for cursor encoding.\");\n }\n\n const encodeBase64 = (input: string): string => {\n const bytes = new TextEncoder().encode(input);\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n };\n\n const decodeBase64 = (input: string): string => {\n const binary = atob(input);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n };\n\n return { encodeBase64, decodeBase64 };\n};\n\n/**\n * Encode cursor data to a base64 string (internal)\n */\nfunction encodeCursorData(data: CursorData): string {\n let json: string;\n try {\n json = JSON.stringify(data);\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n\n return getBase64Helpers().encodeBase64(json);\n}\n\n/**\n * Decode a base64 cursor string back to a Cursor object\n *\n * @param cursor - The base64-encoded cursor string\n * @returns Decoded Cursor object\n * @throws Error if cursor is invalid or malformed\n *\n * @example\n * ```ts\n * const cursor = decodeCursor(\"eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==\");\n * ```\n */\nexport function decodeCursor(cursor: string): Cursor {\n try {\n const json = getBase64Helpers().decodeBase64(cursor);\n const data = JSON.parse(json);\n const record = data as Record<string, unknown>;\n\n // Validate structure\n if (\n !isPlainObject(data) ||\n !isPlainObject(record[\"indexValues\"]) ||\n typeof record[\"indexName\"] !== \"string\" ||\n record[\"indexName\"].length === 0 ||\n typeof record[\"orderDirection\"] !== \"string\" ||\n (record[\"orderDirection\"] !== \"asc\" && record[\"orderDirection\"] !== \"desc\") ||\n typeof record[\"pageSize\"] !== \"number\" ||\n !Number.isFinite(record[\"pageSize\"]) ||\n !Number.isInteger(record[\"pageSize\"]) ||\n record[\"pageSize\"] <= 0\n ) {\n throw new Error(\"Invalid cursor structure\");\n }\n\n // Only support v1\n if (typeof record[\"v\"] !== \"number\") {\n throw new Error(\"Unsupported cursor version: missing. Only v1 is supported.\");\n }\n const version = record[\"v\"];\n if (version !== 1) {\n throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);\n }\n\n return new Cursor({\n indexName: record[\"indexName\"],\n orderDirection: record[\"orderDirection\"],\n pageSize: record[\"pageSize\"],\n indexValues: record[\"indexValues\"],\n });\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n}\n\n/**\n * Create a cursor from a record and pagination metadata\n *\n * @param record - The database record\n * @param indexColumns - The columns that make up the index\n * @param metadata - Pagination metadata (index name, order direction, page size)\n * @returns Cursor object\n *\n * @example\n * ```ts\n * const cursor = createCursorFromRecord(\n * { id: \"abc\", name: \"Alice\", createdAt: 123 },\n * [table.columns.createdAt, table.columns.id],\n * {\n * indexName: \"idx_created\",\n * orderDirection: \"asc\",\n * pageSize: 10\n * }\n * );\n * ```\n */\nexport function createCursorFromRecord(\n record: Record<string, unknown>,\n indexColumns: AnyColumn[],\n metadata: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n },\n): Cursor {\n const indexValues: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n const value = record[col.name];\n if (value === undefined) {\n throw new Error(`Record is missing value for index column \"${col.name}\".`);\n }\n indexValues[col.name] = value;\n }\n\n return new Cursor({\n indexName: metadata.indexName,\n orderDirection: metadata.orderDirection,\n pageSize: metadata.pageSize,\n indexValues,\n });\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst assertSerializableIndexValues = (values: Record<string, unknown>): void => {\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) {\n throw new Error(`Cursor index value \"${key}\" is undefined.`);\n }\n if (typeof value === \"number\" && !Number.isFinite(value)) {\n throw new Error(`Cursor index value \"${key}\" must be a finite number.`);\n }\n if (typeof value === \"bigint\") {\n throw new Error(`Cursor index value \"${key}\" must not be a BigInt.`);\n }\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n throw new Error(`Cursor index value \"${key}\" is not JSON-serializable.`);\n }\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.d.ts","names":[],"sources":["../../src/query/cursor.ts"],"sourcesContent":[],"mappings":";;;;;;;;AASA;AAiEiB,cAjEJ,MAAA,CAiEgB;EAkBZ,CAAA,OAAA;EAqCD,WAAA,CAAA,IAAY,EAAA;IAoEZ,SAAA,EAAA,MAAA;IACN,cAAA,EAAA,KAAA,GAAA,MAAA;IACM,QAAA,EAAA,MAAA;IAMb,WAAA,EA1Lc,MA0Ld,CAAA,MAAA,EAAA,OAAA,CAAA;EAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","names":[],"sources":["../../src/query/cursor.ts"],"sourcesContent":[],"mappings":";;;;;;;;AASA;AAiEiB,cAjEJ,MAAA,CAiEgB;EAkBZ,CAAA,OAAA;EAqCD,WAAA,CAAA,IAAY,EAAA;IAoEZ,SAAA,EAAA,MAAA;IACN,cAAA,EAAA,KAAA,GAAA,MAAA;IACM,QAAA,EAAA,MAAA;IAMb,WAAA,EA1Lc,MA0Ld,CAAA,MAAA,EAAA,OAAA,CAAA;EAAM,CAAA;EA+CO;;;EAGA,IAAA,SAAA,CAAA,CAAA,EAAA,MAAA;EACM;;;;;;;;;;;qBA7MD;;;;;;;;;UAuBJ;;;;SAIR;;;;WAIE;;;;;;;;;UAUM,UAAA;;;;;eAKF;;;;;;;;;;;;;;iBAgCC,YAAA,kBAA8B;;;;;;;;;;;;;;;;;;;;;;iBAoE9B,sBAAA,SACN,uCACM;;;;IAMb;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Ca,qBAAA,SACN,sBACM,2BACA,kCACM,oBACnB"}
|
package/dist/query/cursor.js
CHANGED
|
@@ -126,7 +126,8 @@ function createCursorFromRecord(record, indexColumns, metadata) {
|
|
|
126
126
|
for (const col of indexColumns) {
|
|
127
127
|
const value = record[col.name];
|
|
128
128
|
if (value === void 0) throw new Error(`Record is missing value for index column "${col.name}".`);
|
|
129
|
-
|
|
129
|
+
const resolved = resolveFragnoIdValue(value, col);
|
|
130
|
+
indexValues[col.name] = typeof resolved === "bigint" ? resolved.toString() : resolved;
|
|
130
131
|
}
|
|
131
132
|
return new Cursor({
|
|
132
133
|
indexName: metadata.indexName,
|
|
@@ -170,6 +171,11 @@ function serializeCursorValues(cursor, indexColumns, driverConfig, sqliteStorage
|
|
|
170
171
|
missingColumns.push(col.name);
|
|
171
172
|
continue;
|
|
172
173
|
}
|
|
174
|
+
const resolvedDirect = resolveFragnoIdValue(value, col);
|
|
175
|
+
if (resolvedDirect !== value) {
|
|
176
|
+
serialized[col.name] = serializer.serialize(resolvedDirect, col);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
173
179
|
const resolved = resolveFragnoIdValue(serializer.deserialize(value, col), col);
|
|
174
180
|
serialized[col.name] = serializer.serialize(resolved, col);
|
|
175
181
|
}
|
package/dist/query/cursor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.js","names":["#indexName","#orderDirection","#pageSize","#indexValues","json: string","indexValues: Record<string, unknown>","serialized: Record<string, unknown>","missingColumns: string[]"],"sources":["../../src/query/cursor.ts"],"sourcesContent":["import type { AnyColumn } from \"../schema/create\";\nimport { createSQLSerializer } from \"./serialize/create-sql-serializer\";\nimport { resolveFragnoIdValue } from \"./value-encoding\";\nimport type { DriverConfig } from \"../adapters/generic-sql/driver-config\";\nimport type { SQLiteStorageMode } from \"../adapters/generic-sql/sqlite-storage\";\n\n/**\n * Cursor object containing all information needed for pagination\n */\nexport class Cursor {\n readonly #indexName: string;\n readonly #orderDirection: \"asc\" | \"desc\";\n readonly #pageSize: number;\n readonly #indexValues: Record<string, unknown>;\n\n constructor(data: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n }) {\n this.#indexName = data.indexName;\n this.#orderDirection = data.orderDirection;\n this.#pageSize = data.pageSize;\n this.#indexValues = data.indexValues;\n }\n\n /**\n * Get the index name being used for pagination\n */\n get indexName(): string {\n return this.#indexName;\n }\n\n /**\n * Get the ordering direction\n */\n get orderDirection(): \"asc\" | \"desc\" {\n return this.#orderDirection;\n }\n\n /**\n * Get the page size\n */\n get pageSize(): number {\n return this.#pageSize;\n }\n\n /**\n * Get the cursor position values\n */\n get indexValues(): Record<string, unknown> {\n return this.#indexValues;\n }\n\n /**\n * Encode cursor to an opaque base64 string (safe to send to client)\n */\n encode(): string {\n assertSerializableIndexValues(this.#indexValues);\n const data: CursorData = {\n v: 1,\n indexName: this.#indexName,\n orderDirection: this.#orderDirection,\n pageSize: this.#pageSize,\n indexValues: this.#indexValues,\n };\n return encodeCursorData(data);\n }\n}\n\n/**\n * Result of a cursor-based query containing items and pagination cursor\n */\nexport interface CursorResult<T> {\n /**\n * The query results\n */\n items: T[];\n /**\n * Cursor to fetch the next page (undefined if no more results)\n */\n cursor?: Cursor;\n /**\n * Whether there are more results available after this page\n */\n hasNextPage: boolean;\n}\n\n/**\n * Cursor data structure for serialization\n */\nexport interface CursorData {\n v: number; // version\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n}\n\n/**\n * Encode cursor data to a base64 string (internal)\n */\nfunction encodeCursorData(data: CursorData): string {\n let json: string;\n try {\n json = JSON.stringify(data);\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n // Use Buffer in Node.js or btoa in browsers\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(json, \"utf-8\").toString(\"base64\");\n }\n return btoa(json);\n}\n\n/**\n * Decode a base64 cursor string back to a Cursor object\n *\n * @param cursor - The base64-encoded cursor string\n * @returns Decoded Cursor object\n * @throws Error if cursor is invalid or malformed\n *\n * @example\n * ```ts\n * const cursor = decodeCursor(\"eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==\");\n * ```\n */\nexport function decodeCursor(cursor: string): Cursor {\n try {\n let json: string;\n if (typeof Buffer !== \"undefined\") {\n json = Buffer.from(cursor, \"base64\").toString(\"utf-8\");\n } else {\n json = atob(cursor);\n }\n const data = JSON.parse(json);\n const record = data as Record<string, unknown>;\n\n // Validate structure\n if (\n !isPlainObject(data) ||\n !isPlainObject(record[\"indexValues\"]) ||\n typeof record[\"indexName\"] !== \"string\" ||\n record[\"indexName\"].length === 0 ||\n typeof record[\"orderDirection\"] !== \"string\" ||\n (record[\"orderDirection\"] !== \"asc\" && record[\"orderDirection\"] !== \"desc\") ||\n typeof record[\"pageSize\"] !== \"number\" ||\n !Number.isFinite(record[\"pageSize\"]) ||\n !Number.isInteger(record[\"pageSize\"]) ||\n record[\"pageSize\"] <= 0\n ) {\n throw new Error(\"Invalid cursor structure\");\n }\n\n // Only support v1\n if (typeof record[\"v\"] !== \"number\") {\n throw new Error(\"Unsupported cursor version: missing. Only v1 is supported.\");\n }\n const version = record[\"v\"];\n if (version !== 1) {\n throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);\n }\n\n return new Cursor({\n indexName: record[\"indexName\"],\n orderDirection: record[\"orderDirection\"],\n pageSize: record[\"pageSize\"],\n indexValues: record[\"indexValues\"],\n });\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n}\n\n/**\n * Create a cursor from a record and pagination metadata\n *\n * @param record - The database record\n * @param indexColumns - The columns that make up the index\n * @param metadata - Pagination metadata (index name, order direction, page size)\n * @returns Cursor object\n *\n * @example\n * ```ts\n * const cursor = createCursorFromRecord(\n * { id: \"abc\", name: \"Alice\", createdAt: 123 },\n * [table.columns.createdAt, table.columns.id],\n * {\n * indexName: \"idx_created\",\n * orderDirection: \"asc\",\n * pageSize: 10\n * }\n * );\n * ```\n */\nexport function createCursorFromRecord(\n record: Record<string, unknown>,\n indexColumns: AnyColumn[],\n metadata: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n },\n): Cursor {\n const indexValues: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n const value = record[col.name];\n if (value === undefined) {\n throw new Error(`Record is missing value for index column \"${col.name}\".`);\n }\n indexValues[col.name] = value;\n }\n\n return new Cursor({\n indexName: metadata.indexName,\n orderDirection: metadata.orderDirection,\n pageSize: metadata.pageSize,\n indexValues,\n });\n}\n\n/**\n * Serialize cursor values for database queries\n *\n * Converts cursor values (which are in JSON-compatible format after decode)\n * to database format using the column serialization rules.\n *\n * This function performs a two-step process:\n * 1. Deserialize from JSON format to application format (e.g., ISO string → Date)\n * 2. Serialize from application format to database format (e.g., Date → driver format)\n *\n * @param cursor - The cursor object\n * @param indexColumns - The columns that make up the index\n * @param driverConfig - The driver configuration\n * @param sqliteStorageMode - Optional SQLite storage mode override\n * @returns Serialized values ready for database queries\n *\n * @example\n * ```ts\n * const serialized = serializeCursorValues(\n * cursor,\n * [table.columns.createdAt],\n * driverConfig\n * );\n * ```\n */\nexport function serializeCursorValues(\n cursor: Cursor,\n indexColumns: AnyColumn[],\n driverConfig: DriverConfig,\n sqliteStorageMode?: SQLiteStorageMode,\n): Record<string, unknown> {\n const serializer = createSQLSerializer(driverConfig, sqliteStorageMode);\n const serialized: Record<string, unknown> = {};\n const missingColumns: string[] = [];\n\n for (const col of indexColumns) {\n const value = cursor.indexValues[col.name];\n if (value === undefined) {\n missingColumns.push(col.name);\n continue;\n }\n // First deserialize from JSON format to application format\n // (e.g., \"2025-11-07T09:36:57.959Z\" string → Date object)\n const deserialized = serializer.deserialize(value, col);\n // Resolve FragnoId/FragnoReference to primitive values (if present)\n const resolved = resolveFragnoIdValue(deserialized, col);\n // Then serialize to database format\n // (e.g., Date → database driver format)\n serialized[col.name] = serializer.serialize(resolved, col);\n }\n\n if (missingColumns.length > 0) {\n const suffix = cursor.indexName ? ` for index \"${cursor.indexName}\"` : \"\";\n const columns = missingColumns.map((name) => `\"${name}\"`).join(\", \");\n const plural = missingColumns.length === 1 ? \"\" : \"s\";\n throw new Error(`Cursor is missing values for index column${plural} ${columns}${suffix}.`);\n }\n\n return serialized;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst assertSerializableIndexValues = (values: Record<string, unknown>): void => {\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) {\n throw new Error(`Cursor index value \"${key}\" is undefined.`);\n }\n if (typeof value === \"number\" && !Number.isFinite(value)) {\n throw new Error(`Cursor index value \"${key}\" must be a finite number.`);\n }\n if (typeof value === \"bigint\") {\n throw new Error(`Cursor index value \"${key}\" must not be a BigInt.`);\n }\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n throw new Error(`Cursor index value \"${key}\" is not JSON-serializable.`);\n }\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cursor.js","names":["#indexName","#orderDirection","#pageSize","#indexValues","json: string","indexValues: Record<string, unknown>","serialized: Record<string, unknown>","missingColumns: string[]"],"sources":["../../src/query/cursor.ts"],"sourcesContent":["import type { DriverConfig } from \"../adapters/generic-sql/driver-config\";\nimport type { SQLiteStorageMode } from \"../adapters/generic-sql/sqlite-storage\";\nimport type { AnyColumn } from \"../schema/create\";\nimport { createSQLSerializer } from \"./serialize/create-sql-serializer\";\nimport { resolveFragnoIdValue } from \"./value-encoding\";\n\n/**\n * Cursor object containing all information needed for pagination\n */\nexport class Cursor {\n readonly #indexName: string;\n readonly #orderDirection: \"asc\" | \"desc\";\n readonly #pageSize: number;\n readonly #indexValues: Record<string, unknown>;\n\n constructor(data: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n }) {\n this.#indexName = data.indexName;\n this.#orderDirection = data.orderDirection;\n this.#pageSize = data.pageSize;\n this.#indexValues = data.indexValues;\n }\n\n /**\n * Get the index name being used for pagination\n */\n get indexName(): string {\n return this.#indexName;\n }\n\n /**\n * Get the ordering direction\n */\n get orderDirection(): \"asc\" | \"desc\" {\n return this.#orderDirection;\n }\n\n /**\n * Get the page size\n */\n get pageSize(): number {\n return this.#pageSize;\n }\n\n /**\n * Get the cursor position values\n */\n get indexValues(): Record<string, unknown> {\n return this.#indexValues;\n }\n\n /**\n * Encode cursor to an opaque base64 string (safe to send to client)\n */\n encode(): string {\n assertSerializableIndexValues(this.#indexValues);\n const data: CursorData = {\n v: 1,\n indexName: this.#indexName,\n orderDirection: this.#orderDirection,\n pageSize: this.#pageSize,\n indexValues: this.#indexValues,\n };\n return encodeCursorData(data);\n }\n}\n\n/**\n * Result of a cursor-based query containing items and pagination cursor\n */\nexport interface CursorResult<T> {\n /**\n * The query results\n */\n items: T[];\n /**\n * Cursor to fetch the next page (undefined if no more results)\n */\n cursor?: Cursor;\n /**\n * Whether there are more results available after this page\n */\n hasNextPage: boolean;\n}\n\n/**\n * Cursor data structure for serialization\n */\nexport interface CursorData {\n v: number; // version\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n}\n\n/**\n * Encode cursor data to a base64 string (internal)\n */\nfunction encodeCursorData(data: CursorData): string {\n let json: string;\n try {\n json = JSON.stringify(data);\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n // Use Buffer in Node.js or btoa in browsers\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(json, \"utf-8\").toString(\"base64\");\n }\n return btoa(json);\n}\n\n/**\n * Decode a base64 cursor string back to a Cursor object\n *\n * @param cursor - The base64-encoded cursor string\n * @returns Decoded Cursor object\n * @throws Error if cursor is invalid or malformed\n *\n * @example\n * ```ts\n * const cursor = decodeCursor(\"eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==\");\n * ```\n */\nexport function decodeCursor(cursor: string): Cursor {\n try {\n let json: string;\n if (typeof Buffer !== \"undefined\") {\n json = Buffer.from(cursor, \"base64\").toString(\"utf-8\");\n } else {\n json = atob(cursor);\n }\n const data = JSON.parse(json);\n const record = data as Record<string, unknown>;\n\n // Validate structure\n if (\n !isPlainObject(data) ||\n !isPlainObject(record[\"indexValues\"]) ||\n typeof record[\"indexName\"] !== \"string\" ||\n record[\"indexName\"].length === 0 ||\n typeof record[\"orderDirection\"] !== \"string\" ||\n (record[\"orderDirection\"] !== \"asc\" && record[\"orderDirection\"] !== \"desc\") ||\n typeof record[\"pageSize\"] !== \"number\" ||\n !Number.isFinite(record[\"pageSize\"]) ||\n !Number.isInteger(record[\"pageSize\"]) ||\n record[\"pageSize\"] <= 0\n ) {\n throw new Error(\"Invalid cursor structure\");\n }\n\n // Only support v1\n if (typeof record[\"v\"] !== \"number\") {\n throw new Error(\"Unsupported cursor version: missing. Only v1 is supported.\");\n }\n const version = record[\"v\"];\n if (version !== 1) {\n throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);\n }\n\n return new Cursor({\n indexName: record[\"indexName\"],\n orderDirection: record[\"orderDirection\"],\n pageSize: record[\"pageSize\"],\n indexValues: record[\"indexValues\"],\n });\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n}\n\n/**\n * Create a cursor from a record and pagination metadata\n *\n * @param record - The database record\n * @param indexColumns - The columns that make up the index\n * @param metadata - Pagination metadata (index name, order direction, page size)\n * @returns Cursor object\n *\n * @example\n * ```ts\n * const cursor = createCursorFromRecord(\n * { id: \"abc\", name: \"Alice\", createdAt: 123 },\n * [table.columns.createdAt, table.columns.id],\n * {\n * indexName: \"idx_created\",\n * orderDirection: \"asc\",\n * pageSize: 10\n * }\n * );\n * ```\n */\nexport function createCursorFromRecord(\n record: Record<string, unknown>,\n indexColumns: AnyColumn[],\n metadata: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n },\n): Cursor {\n const indexValues: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n const value = record[col.name];\n if (value === undefined) {\n throw new Error(`Record is missing value for index column \"${col.name}\".`);\n }\n // Resolve FragnoId/FragnoReference to primitive values for cursor serialization.\n const resolved = resolveFragnoIdValue(value, col);\n // BigInt values are not JSON-serializable, so store them as strings.\n indexValues[col.name] = typeof resolved === \"bigint\" ? resolved.toString() : resolved;\n }\n\n return new Cursor({\n indexName: metadata.indexName,\n orderDirection: metadata.orderDirection,\n pageSize: metadata.pageSize,\n indexValues,\n });\n}\n\n/**\n * Serialize cursor values for database queries\n *\n * Converts cursor values (which are in JSON-compatible format after decode)\n * to database format using the column serialization rules.\n *\n * This function performs a two-step process:\n * 1. Deserialize from JSON format to application format (e.g., ISO string → Date)\n * 2. Serialize from application format to database format (e.g., Date → driver format)\n *\n * @param cursor - The cursor object\n * @param indexColumns - The columns that make up the index\n * @param driverConfig - The driver configuration\n * @param sqliteStorageMode - Optional SQLite storage mode override\n * @returns Serialized values ready for database queries\n *\n * @example\n * ```ts\n * const serialized = serializeCursorValues(\n * cursor,\n * [table.columns.createdAt],\n * driverConfig\n * );\n * ```\n */\nexport function serializeCursorValues(\n cursor: Cursor,\n indexColumns: AnyColumn[],\n driverConfig: DriverConfig,\n sqliteStorageMode?: SQLiteStorageMode,\n): Record<string, unknown> {\n const serializer = createSQLSerializer(driverConfig, sqliteStorageMode);\n const serialized: Record<string, unknown> = {};\n const missingColumns: string[] = [];\n\n for (const col of indexColumns) {\n const value = cursor.indexValues[col.name];\n if (value === undefined) {\n missingColumns.push(col.name);\n continue;\n }\n\n // If the cursor value is already a FragnoId/FragnoReference, resolve it directly\n // to avoid deserializing non-JSON objects.\n const resolvedDirect = resolveFragnoIdValue(value, col);\n if (resolvedDirect !== value) {\n serialized[col.name] = serializer.serialize(resolvedDirect, col);\n continue;\n }\n\n // First deserialize from JSON format to application format\n // (e.g., \"2025-11-07T09:36:57.959Z\" string → Date object)\n const deserialized = serializer.deserialize(value, col);\n // Resolve FragnoId/FragnoReference to primitive values (if present)\n const resolved = resolveFragnoIdValue(deserialized, col);\n // Then serialize to database format\n // (e.g., Date → database driver format)\n serialized[col.name] = serializer.serialize(resolved, col);\n }\n\n if (missingColumns.length > 0) {\n const suffix = cursor.indexName ? ` for index \"${cursor.indexName}\"` : \"\";\n const columns = missingColumns.map((name) => `\"${name}\"`).join(\", \");\n const plural = missingColumns.length === 1 ? \"\" : \"s\";\n throw new Error(`Cursor is missing values for index column${plural} ${columns}${suffix}.`);\n }\n\n return serialized;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst assertSerializableIndexValues = (values: Record<string, unknown>): void => {\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) {\n throw new Error(`Cursor index value \"${key}\" is undefined.`);\n }\n if (typeof value === \"number\" && !Number.isFinite(value)) {\n throw new Error(`Cursor index value \"${key}\" must be a finite number.`);\n }\n if (typeof value === \"bigint\") {\n throw new Error(`Cursor index value \"${key}\" must not be a BigInt.`);\n }\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n throw new Error(`Cursor index value \"${key}\" is not JSON-serializable.`);\n }\n }\n};\n"],"mappings":"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"}
|
package/dist/query/db-now.d.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
//#region src/query/db-now.d.ts
|
|
2
|
+
type DbInterval = {
|
|
3
|
+
tag: "db-interval";
|
|
4
|
+
ms: number;
|
|
5
|
+
};
|
|
6
|
+
type DbIntervalInput = number | {
|
|
7
|
+
ms?: number;
|
|
8
|
+
seconds?: number;
|
|
9
|
+
minutes?: number;
|
|
10
|
+
hours?: number;
|
|
11
|
+
days?: number;
|
|
12
|
+
};
|
|
2
13
|
type DbNow = {
|
|
3
14
|
tag: "db-now";
|
|
15
|
+
offsetMs?: number;
|
|
16
|
+
plus: (interval: DbInterval | DbIntervalInput) => DbNow;
|
|
4
17
|
};
|
|
5
18
|
declare const dbNow: () => DbNow;
|
|
19
|
+
declare const dbInterval: (input: DbIntervalInput) => DbInterval;
|
|
6
20
|
//#endregion
|
|
7
|
-
export { DbNow, dbNow };
|
|
21
|
+
export { DbInterval, DbIntervalInput, DbNow, dbInterval, dbNow };
|
|
8
22
|
//# sourceMappingURL=db-now.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-now.d.ts","names":[],"sources":["../../src/query/db-now.ts"],"sourcesContent":[],"mappings":";KAAY,
|
|
1
|
+
{"version":3,"file":"db-now.d.ts","names":[],"sources":["../../src/query/db-now.ts"],"sourcesContent":[],"mappings":";KAAY,UAAA;EAAA,GAAA,EAAA,aAAU;EAEV,EAAA,EAAA,MAAA;AAUZ,CAAA;AAGmB,KAbP,eAAA,GAaO,MAAA,GAAA;EAAa,EAAA,CAAA,EAAA,MAAA;EAAoB,OAAA,CAAA,EAAA,MAAA;EAAK,OAAA,CAAA,EAAA,MAAA;EA2C5C,KAAA,CAAA,EAAmC,MAAA;EAQnC,IAAA,CAAA,EAAA,MAGX;;KAzDU,KAAA;;;mBAGO,aAAa,oBAAoB;;cA2CvC,aAAY;cAQZ,oBAAqB,oBAAkB"}
|
package/dist/query/db-now.js
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
1
|
//#region src/query/db-now.ts
|
|
2
|
-
const
|
|
2
|
+
const toIntervalMs = (input) => {
|
|
3
|
+
if (typeof input === "number") {
|
|
4
|
+
if (!Number.isFinite(input)) throw new Error("DB_INTERVAL_INVALID");
|
|
5
|
+
return input;
|
|
6
|
+
}
|
|
7
|
+
if (typeof input === "object" && input !== null && "tag" in input) {
|
|
8
|
+
const tagged = input;
|
|
9
|
+
if (tagged.tag === "db-interval") {
|
|
10
|
+
if (!Number.isFinite(tagged.ms)) throw new Error("DB_INTERVAL_INVALID");
|
|
11
|
+
return tagged.ms;
|
|
12
|
+
}
|
|
13
|
+
throw new Error("DB_INTERVAL_INVALID");
|
|
14
|
+
}
|
|
15
|
+
const interval = input;
|
|
16
|
+
const totalMs = (interval.ms ?? 0) + (interval.seconds ?? 0) * 1e3 + (interval.minutes ?? 0) * 6e4 + (interval.hours ?? 0) * 36e5 + (interval.days ?? 0) * 864e5;
|
|
17
|
+
if (!Number.isFinite(totalMs)) throw new Error("DB_INTERVAL_INVALID");
|
|
18
|
+
return totalMs;
|
|
19
|
+
};
|
|
20
|
+
const createDbNow = (offsetMs = 0) => ({
|
|
21
|
+
tag: "db-now",
|
|
22
|
+
offsetMs,
|
|
23
|
+
plus: (interval) => createDbNow(offsetMs + toIntervalMs(interval))
|
|
24
|
+
});
|
|
25
|
+
const dbNow = () => createDbNow(0);
|
|
3
26
|
const isDbNow = (value) => typeof value === "object" && value !== null && value.tag === "db-now";
|
|
27
|
+
const getDbNowOffsetMs = (value) => typeof value.offsetMs === "number" ? value.offsetMs : 0;
|
|
28
|
+
const dbInterval = (input) => ({
|
|
29
|
+
tag: "db-interval",
|
|
30
|
+
ms: toIntervalMs(input)
|
|
31
|
+
});
|
|
4
32
|
|
|
5
33
|
//#endregion
|
|
6
|
-
export { dbNow, isDbNow };
|
|
34
|
+
export { dbInterval, dbNow, getDbNowOffsetMs, isDbNow };
|
|
7
35
|
//# sourceMappingURL=db-now.js.map
|
package/dist/query/db-now.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-now.js","names":[],"sources":["../../src/query/db-now.ts"],"sourcesContent":["export type
|
|
1
|
+
{"version":3,"file":"db-now.js","names":[],"sources":["../../src/query/db-now.ts"],"sourcesContent":["export type DbInterval = { tag: \"db-interval\"; ms: number };\n\nexport type DbIntervalInput =\n | number\n | {\n ms?: number;\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n };\n\nexport type DbNow = {\n tag: \"db-now\";\n offsetMs?: number;\n plus: (interval: DbInterval | DbIntervalInput) => DbNow;\n};\n\nconst toIntervalMs = (input: DbInterval | DbIntervalInput): number => {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input)) {\n throw new Error(\"DB_INTERVAL_INVALID\");\n }\n return input;\n }\n\n if (typeof input === \"object\" && input !== null && \"tag\" in input) {\n const tagged = input as DbInterval;\n if (tagged.tag === \"db-interval\") {\n if (!Number.isFinite(tagged.ms)) {\n throw new Error(\"DB_INTERVAL_INVALID\");\n }\n return tagged.ms;\n }\n throw new Error(\"DB_INTERVAL_INVALID\");\n }\n\n const interval = input as Exclude<DbIntervalInput, number>;\n const totalMs =\n (interval.ms ?? 0) +\n (interval.seconds ?? 0) * 1000 +\n (interval.minutes ?? 0) * 60_000 +\n (interval.hours ?? 0) * 3_600_000 +\n (interval.days ?? 0) * 86_400_000;\n\n if (!Number.isFinite(totalMs)) {\n throw new Error(\"DB_INTERVAL_INVALID\");\n }\n\n return totalMs;\n};\n\nconst createDbNow = (offsetMs = 0): DbNow => ({\n tag: \"db-now\",\n offsetMs,\n plus: (interval) => createDbNow(offsetMs + toIntervalMs(interval)),\n});\n\nexport const dbNow = (): DbNow => createDbNow(0);\n\nexport const isDbNow = (value: unknown): value is DbNow =>\n typeof value === \"object\" && value !== null && (value as { tag?: string }).tag === \"db-now\";\n\nexport const getDbNowOffsetMs = (value: DbNow): number =>\n typeof value.offsetMs === \"number\" ? value.offsetMs : 0;\n\nexport const dbInterval = (input: DbIntervalInput): DbInterval => ({\n tag: \"db-interval\",\n ms: toIntervalMs(input),\n});\n\nexport const isDbInterval = (value: unknown): value is DbInterval =>\n typeof value === \"object\" && value !== null && (value as { tag?: string }).tag === \"db-interval\";\n"],"mappings":";AAkBA,MAAM,gBAAgB,UAAgD;AACpE,KAAI,OAAO,UAAU,UAAU;AAC7B,MAAI,CAAC,OAAO,SAAS,MAAM,CACzB,OAAM,IAAI,MAAM,sBAAsB;AAExC,SAAO;;AAGT,KAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,SAAS,OAAO;EACjE,MAAM,SAAS;AACf,MAAI,OAAO,QAAQ,eAAe;AAChC,OAAI,CAAC,OAAO,SAAS,OAAO,GAAG,CAC7B,OAAM,IAAI,MAAM,sBAAsB;AAExC,UAAO,OAAO;;AAEhB,QAAM,IAAI,MAAM,sBAAsB;;CAGxC,MAAM,WAAW;CACjB,MAAM,WACH,SAAS,MAAM,MACf,SAAS,WAAW,KAAK,OACzB,SAAS,WAAW,KAAK,OACzB,SAAS,SAAS,KAAK,QACvB,SAAS,QAAQ,KAAK;AAEzB,KAAI,CAAC,OAAO,SAAS,QAAQ,CAC3B,OAAM,IAAI,MAAM,sBAAsB;AAGxC,QAAO;;AAGT,MAAM,eAAe,WAAW,OAAc;CAC5C,KAAK;CACL;CACA,OAAO,aAAa,YAAY,WAAW,aAAa,SAAS,CAAC;CACnE;AAED,MAAa,cAAqB,YAAY,EAAE;AAEhD,MAAa,WAAW,UACtB,OAAO,UAAU,YAAY,UAAU,QAAS,MAA2B,QAAQ;AAErF,MAAa,oBAAoB,UAC/B,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;AAExD,MAAa,cAAc,WAAwC;CACjE,KAAK;CACL,IAAI,aAAa,MAAM;CACxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orm.js","names":["compiled: CompiledJoin[]","builder: Record<string, unknown>"],"sources":["../../../src/query/orm/orm.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"orm.js","names":["compiled: CompiledJoin[]","builder: Record<string, unknown>"],"sources":["../../../src/query/orm/orm.ts"],"sourcesContent":["import type { AnyColumn, AnyRelation, AnyTable } from \"../../schema/create\";\nimport { buildCondition, type Condition } from \"../condition-builder\";\nimport type {\n AnySelectClause,\n FindFirstOptions,\n FindManyOptions,\n JoinBuilder,\n OrderBy,\n} from \"../simple-query-interface\";\n\nexport interface CompiledJoin {\n relation: AnyRelation;\n options: SimplifyFindOptions<FindManyOptions> | false;\n}\n\nfunction isOrderByArray(v: OrderBy | OrderBy[]): v is OrderBy[] {\n return Array.isArray(v) && Array.isArray(v[0]);\n}\n\nfunction simplifyOrderBy(\n columns: Record<string, AnyColumn>,\n orderBy: OrderBy | OrderBy[] | undefined,\n): OrderBy<AnyColumn>[] | undefined {\n if (!orderBy || orderBy.length === 0) {\n return;\n }\n\n if (!isOrderByArray(orderBy)) {\n orderBy = [orderBy];\n }\n return orderBy.map(([name, value]) => {\n const col = columns[name];\n if (!col) {\n throw new Error(`unknown column name ${name}.`);\n }\n\n return [col, value];\n });\n}\n\nexport function buildFindOptions(\n table: AnyTable,\n { select = true, where, orderBy, join, ...options }: FindManyOptions,\n): SimplifyFindOptions<FindManyOptions> | false {\n let conditions = where ? buildCondition(table.columns, where) : undefined;\n if (conditions === true) {\n conditions = undefined;\n }\n if (conditions === false) {\n return false;\n }\n\n return {\n select,\n where: conditions,\n orderBy: simplifyOrderBy(table.columns, orderBy),\n join: join ? buildJoin(table, join) : undefined,\n ...options,\n };\n}\n\nfunction buildJoin<TTable extends AnyTable>(\n table: AnyTable,\n fn: (builder: JoinBuilder<TTable>) => void,\n): CompiledJoin[] {\n const compiled: CompiledJoin[] = [];\n const builder: Record<string, unknown> = {};\n\n for (const name in table.relations) {\n const relation = table.relations[name]!;\n\n builder[name] = (options: FindFirstOptions | FindManyOptions = {}) => {\n compiled.push({\n relation,\n options: buildFindOptions(relation.table, options),\n });\n\n delete builder[name];\n return builder;\n };\n }\n\n fn(builder as JoinBuilder<TTable>);\n return compiled;\n}\n\nexport type SimplifyFindOptions<O> = Omit<O, \"where\" | \"orderBy\" | \"select\" | \"join\"> & {\n select: AnySelectClause;\n where?: Condition | undefined;\n orderBy?: OrderBy<AnyColumn>[];\n join?: CompiledJoin[];\n};\n"],"mappings":";;;AAeA,SAAS,eAAe,GAAwC;AAC9D,QAAO,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,GAAG;;AAGhD,SAAS,gBACP,SACA,SACkC;AAClC,KAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;AAGF,KAAI,CAAC,eAAe,QAAQ,CAC1B,WAAU,CAAC,QAAQ;AAErB,QAAO,QAAQ,KAAK,CAAC,MAAM,WAAW;EACpC,MAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,uBAAuB,KAAK,GAAG;AAGjD,SAAO,CAAC,KAAK,MAAM;GACnB;;AAGJ,SAAgB,iBACd,OACA,EAAE,SAAS,MAAM,OAAO,SAAS,KAAM,GAAG,WACI;CAC9C,IAAI,aAAa,QAAQ,eAAe,MAAM,SAAS,MAAM,GAAG;AAChE,KAAI,eAAe,KACjB,cAAa;AAEf,KAAI,eAAe,MACjB,QAAO;AAGT,QAAO;EACL;EACA,OAAO;EACP,SAAS,gBAAgB,MAAM,SAAS,QAAQ;EAChD,MAAM,OAAO,UAAU,OAAO,KAAK,GAAG;EACtC,GAAG;EACJ;;AAGH,SAAS,UACP,OACA,IACgB;CAChB,MAAMA,WAA2B,EAAE;CACnC,MAAMC,UAAmC,EAAE;AAE3C,MAAK,MAAM,QAAQ,MAAM,WAAW;EAClC,MAAM,WAAW,MAAM,UAAU;AAEjC,UAAQ,SAAS,UAA8C,EAAE,KAAK;AACpE,YAAS,KAAK;IACZ;IACA,SAAS,iBAAiB,SAAS,OAAO,QAAQ;IACnD,CAAC;AAEF,UAAO,QAAQ;AACf,UAAO;;;AAIX,IAAG,QAA+B;AAClC,QAAO"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SQLSerializer } from "./sql-serializer.js";
|
|
2
|
-
import { SQLiteSerializer } from "./dialect/sqlite-serializer.js";
|
|
3
|
-
import { PostgreSQLSerializer } from "./dialect/postgres-serializer.js";
|
|
4
2
|
import { MySQLSerializer } from "./dialect/mysql-serializer.js";
|
|
3
|
+
import { PostgreSQLSerializer } from "./dialect/postgres-serializer.js";
|
|
4
|
+
import { SQLiteSerializer } from "./dialect/sqlite-serializer.js";
|
|
5
5
|
|
|
6
6
|
//#region src/query/serialize/create-sql-serializer.ts
|
|
7
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-sql-serializer.js","names":["exhaustiveCheck: never"],"sources":["../../../src/query/serialize/create-sql-serializer.ts"],"sourcesContent":["import type { DriverConfig } from \"../../adapters/generic-sql/driver-config\";\nimport type { SQLiteStorageMode } from \"../../adapters/generic-sql/sqlite-storage\";\nimport {
|
|
1
|
+
{"version":3,"file":"create-sql-serializer.js","names":["exhaustiveCheck: never"],"sources":["../../../src/query/serialize/create-sql-serializer.ts"],"sourcesContent":["import type { DriverConfig } from \"../../adapters/generic-sql/driver-config\";\nimport type { SQLiteStorageMode } from \"../../adapters/generic-sql/sqlite-storage\";\nimport { MySQLSerializer } from \"./dialect/mysql-serializer\";\nimport { PostgreSQLSerializer } from \"./dialect/postgres-serializer\";\nimport { SQLiteSerializer } from \"./dialect/sqlite-serializer\";\nimport { SQLSerializer } from \"./sql-serializer\";\n\n// Re-export SQLSerializer for convenience\nexport { SQLSerializer } from \"./sql-serializer\";\n\n/**\n * Factory function to create a dialect-specific SQL serializer from a DriverConfig.\n *\n * Based on the database type, returns the appropriate serializer implementation\n * (PostgreSQL, MySQL, or SQLite).\n *\n * @param driverConfig - The driver configuration\n * @param sqliteStorageMode - Optional SQLite storage mode override\n * @returns Dialect-specific SQLSerializer instance\n */\nexport function createSQLSerializer(\n driverConfig: DriverConfig,\n sqliteStorageMode?: SQLiteStorageMode,\n): SQLSerializer {\n // TODO: The serializers are pretty lenient in what they accept (lost of typeof checks), it may\n // be beneficial to implement serializers per DriverConfig, and be less lenient.\n switch (driverConfig.databaseType) {\n case \"postgresql\":\n return new PostgreSQLSerializer(driverConfig);\n case \"mysql\":\n return new MySQLSerializer(driverConfig);\n case \"sqlite\":\n return new SQLiteSerializer(driverConfig, sqliteStorageMode);\n default: {\n const exhaustiveCheck: never = driverConfig.databaseType;\n throw new Error(`Unsupported database type: ${exhaustiveCheck}`);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,SAAgB,oBACd,cACA,mBACe;AAGf,SAAQ,aAAa,cAArB;EACE,KAAK,aACH,QAAO,IAAI,qBAAqB,aAAa;EAC/C,KAAK,QACH,QAAO,IAAI,gBAAgB,aAAa;EAC1C,KAAK,SACH,QAAO,IAAI,iBAAiB,cAAc,kBAAkB;EAC9D,SAAS;GACP,MAAMA,kBAAyB,aAAa;AAC5C,SAAM,IAAI,MAAM,8BAA8B,kBAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql-serializer.js","names":[],"sources":["../../../../src/query/serialize/dialect/mysql-serializer.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"mysql-serializer.js","names":[],"sources":["../../../../src/query/serialize/dialect/mysql-serializer.ts"],"sourcesContent":["import type { DriverConfig } from \"../../../adapters/generic-sql/driver-config\";\nimport type { AnyColumn } from \"../../../schema/create\";\nimport { SQLSerializer } from \"../sql-serializer\";\n\n/**\n * MySQL-specific serializer.\n *\n * MySQL has good native type support:\n * - Native JSON support (MySQL 5.7+)\n * - Native boolean type (TINYINT(1))\n * - Native timestamp/datetime/date types\n * - Native bigint support\n *\n * Similar to PostgreSQL, most values pass through without conversion.\n * Timestamps/dates are returned as strings and need to be parsed.\n */\nexport class MySQLSerializer extends SQLSerializer {\n constructor(driverConfig: DriverConfig) {\n super(driverConfig);\n }\n\n protected serializeDate(value: Date, _col: AnyColumn): Date {\n void _col;\n // MySQL handles Date objects natively\n return value;\n }\n\n protected serializeBoolean(value: boolean): boolean {\n // MySQL handles boolean natively (as TINYINT(1))\n return value;\n }\n\n protected serializeBigInt(value: bigint, _col: AnyColumn): bigint {\n // MySQL handles bigint natively\n return value;\n }\n\n protected deserializeDate(value: unknown, _col: AnyColumn): Date {\n void _col;\n if (value instanceof Date) {\n return value;\n }\n // MySQL returns timestamps/dates as strings\n if (typeof value === \"string\") {\n return new Date(value);\n }\n throw new Error(`Cannot deserialize date from value: ${value}`);\n }\n\n protected deserializeBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") {\n return value;\n }\n // MySQL may return booleans as numbers (0/1)\n if (typeof value === \"number\") {\n return value === 1;\n }\n throw new Error(`Cannot deserialize boolean from value: ${value}`);\n }\n\n protected deserializeBigInt(value: unknown): bigint {\n if (typeof value === \"bigint\") {\n return value;\n }\n // MySQL may return bigints as strings\n if (typeof value === \"string\") {\n return BigInt(value);\n }\n if (typeof value === \"number\") {\n return BigInt(value);\n }\n throw new Error(`Cannot deserialize bigint from value: ${value}`);\n }\n\n protected serializeJson(value: unknown): unknown {\n // MySQL supports native JSON, pass through as-is\n return value;\n }\n\n protected deserializeJson(value: unknown): unknown {\n // MySQL returns parsed JSON objects, pass through as-is\n return value;\n }\n\n protected deserializeBinary(value: unknown): Uint8Array {\n // MySQL can return Buffer or Uint8Array for BLOB columns\n if (value instanceof Buffer) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n if (value instanceof Uint8Array) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n throw new Error(`Cannot deserialize binary from value: ${typeof value}`);\n }\n\n protected deserializeInteger(value: unknown): number {\n if (typeof value === \"number\") {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new Error(`Cannot deserialize integer from invalid number: ${value}`);\n }\n return value;\n }\n // MySQL may return integers as strings\n if (typeof value === \"string\") {\n const num = Number(value);\n if (Number.isNaN(num) || !Number.isFinite(num)) {\n throw new Error(`Cannot deserialize integer from invalid string: ${value}`);\n }\n return num;\n }\n // MySQL may return bigint for large integers\n if (typeof value === \"bigint\") {\n if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {\n throw new RangeError(\n `Cannot deserialize integer value ${value}: exceeds safe integer range`,\n );\n }\n return Number(value);\n }\n throw new Error(`Cannot deserialize integer from value: ${typeof value}`);\n }\n\n protected deserializeDecimal(value: unknown): number {\n // MySQL can return decimals as numbers or strings\n if (typeof value === \"number\") {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new Error(`Cannot deserialize decimal from invalid number: ${value}`);\n }\n return value;\n }\n if (typeof value === \"string\") {\n const num = parseFloat(value);\n if (Number.isNaN(num) || !Number.isFinite(num)) {\n throw new Error(`Cannot deserialize decimal from invalid string: ${value}`);\n }\n return num;\n }\n throw new Error(`Cannot deserialize decimal from value: ${typeof value}`);\n }\n\n protected deserializeString(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n throw new Error(`Cannot deserialize string from value: ${typeof value}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAa,kBAAb,cAAqC,cAAc;CACjD,YAAY,cAA4B;AACtC,QAAM,aAAa;;CAGrB,AAAU,cAAc,OAAa,MAAuB;AAG1D,SAAO;;CAGT,AAAU,iBAAiB,OAAyB;AAElD,SAAO;;CAGT,AAAU,gBAAgB,OAAe,MAAyB;AAEhE,SAAO;;CAGT,AAAU,gBAAgB,OAAgB,MAAuB;AAE/D,MAAI,iBAAiB,KACnB,QAAO;AAGT,MAAI,OAAO,UAAU,SACnB,QAAO,IAAI,KAAK,MAAM;AAExB,QAAM,IAAI,MAAM,uCAAuC,QAAQ;;CAGjE,AAAU,mBAAmB,OAAyB;AACpD,MAAI,OAAO,UAAU,UACnB,QAAO;AAGT,MAAI,OAAO,UAAU,SACnB,QAAO,UAAU;AAEnB,QAAM,IAAI,MAAM,0CAA0C,QAAQ;;CAGpE,AAAU,kBAAkB,OAAwB;AAClD,MAAI,OAAO,UAAU,SACnB,QAAO;AAGT,MAAI,OAAO,UAAU,SACnB,QAAO,OAAO,MAAM;AAEtB,MAAI,OAAO,UAAU,SACnB,QAAO,OAAO,MAAM;AAEtB,QAAM,IAAI,MAAM,yCAAyC,QAAQ;;CAGnE,AAAU,cAAc,OAAyB;AAE/C,SAAO;;CAGT,AAAU,gBAAgB,OAAyB;AAEjD,SAAO;;CAGT,AAAU,kBAAkB,OAA4B;AAEtD,MAAI,iBAAiB,OACnB,QAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,WAAW;AAEzE,MAAI,iBAAiB,WACnB,QAAO;AAET,MAAI,iBAAiB,YACnB,QAAO,IAAI,WAAW,MAAM;AAE9B,QAAM,IAAI,MAAM,yCAAyC,OAAO,QAAQ;;CAG1E,AAAU,mBAAmB,OAAwB;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,OAAO,MAAM,MAAM,IAAI,CAAC,OAAO,SAAS,MAAM,CAChD,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAGT,MAAI,OAAO,UAAU,UAAU;GAC7B,MAAM,MAAM,OAAO,MAAM;AACzB,OAAI,OAAO,MAAM,IAAI,IAAI,CAAC,OAAO,SAAS,IAAI,CAC5C,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAGT,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,QAAQ,OAAO,oBAAoB,QAAQ,OAAO,iBACpD,OAAM,IAAI,WACR,oCAAoC,MAAM,8BAC3C;AAEH,UAAO,OAAO,MAAM;;AAEtB,QAAM,IAAI,MAAM,0CAA0C,OAAO,QAAQ;;CAG3E,AAAU,mBAAmB,OAAwB;AAEnD,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,OAAO,MAAM,MAAM,IAAI,CAAC,OAAO,SAAS,MAAM,CAChD,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAET,MAAI,OAAO,UAAU,UAAU;GAC7B,MAAM,MAAM,WAAW,MAAM;AAC7B,OAAI,OAAO,MAAM,IAAI,IAAI,CAAC,OAAO,SAAS,IAAI,CAC5C,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAET,QAAM,IAAI,MAAM,0CAA0C,OAAO,QAAQ;;CAG3E,AAAU,kBAAkB,OAAwB;AAClD,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,QAAM,IAAI,MAAM,yCAAyC,OAAO,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-serializer.js","names":[],"sources":["../../../../src/query/serialize/dialect/postgres-serializer.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"postgres-serializer.js","names":[],"sources":["../../../../src/query/serialize/dialect/postgres-serializer.ts"],"sourcesContent":["import type { DriverConfig } from \"../../../adapters/generic-sql/driver-config\";\nimport type { AnyColumn } from \"../../../schema/create\";\nimport { SQLSerializer } from \"../sql-serializer\";\n\n/**\n * PostgreSQL-specific serializer.\n *\n * PostgreSQL has excellent native type support:\n * - Native JSON support\n * - Native boolean type\n * - Native timestamp/date types\n * - Native bigint support\n *\n * Most values pass through without conversion, only strings need to be parsed to Date.\n */\nexport class PostgreSQLSerializer extends SQLSerializer {\n constructor(driverConfig: DriverConfig) {\n super(driverConfig);\n }\n\n protected serializeDate(value: Date, _col: AnyColumn): Date {\n void _col;\n // PostgreSQL handles Date objects natively\n return value;\n }\n\n protected serializeBoolean(value: boolean): boolean {\n // PostgreSQL handles boolean natively\n return value;\n }\n\n protected serializeBigInt(value: bigint, _col: AnyColumn): bigint {\n // PostgreSQL handles bigint natively\n return value;\n }\n\n protected deserializeDate(value: unknown, _col: AnyColumn): Date {\n void _col;\n if (value instanceof Date) {\n if (this.driverConfig.driverType === \"pglite\") {\n return new Date(value.getTime() - value.getTimezoneOffset() * 60_000);\n }\n return value;\n }\n // PostgreSQL returns timestamps/dates as strings\n if (typeof value === \"string\") {\n // Normalize timezone-less timestamps to UTC to avoid local offset drift.\n return new Date(normalizeTimestampString(value));\n }\n throw new Error(`Cannot deserialize date from value: ${value}`);\n }\n\n protected deserializeBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") {\n return value;\n }\n throw new Error(`Cannot deserialize boolean from value: ${value}`);\n }\n\n protected deserializeBigInt(value: unknown): bigint {\n if (typeof value === \"bigint\") {\n return value;\n }\n // PostgreSQL may return bigints as strings\n if (typeof value === \"string\") {\n return BigInt(value);\n }\n if (typeof value === \"number\") {\n return BigInt(value);\n }\n throw new Error(`Cannot deserialize bigint from value: ${value}`);\n }\n\n protected serializeJson(value: unknown): unknown {\n // PostgreSQL supports native JSON, pass through as-is\n return value;\n }\n\n protected deserializeJson(value: unknown): unknown {\n // PostgreSQL returns parsed JSON objects, pass through as-is\n return value;\n }\n\n protected deserializeBinary(value: unknown): Uint8Array {\n // PostgreSQL can return Buffer or Uint8Array for BYTEA columns\n if (value instanceof Buffer) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n if (value instanceof Uint8Array) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n throw new Error(`Cannot deserialize binary from value: ${typeof value}`);\n }\n\n protected deserializeInteger(value: unknown): number {\n if (typeof value === \"number\") {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new Error(`Cannot deserialize integer from invalid number: ${value}`);\n }\n return value;\n }\n // PostgreSQL may return bigint for large integers\n if (typeof value === \"bigint\") {\n if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {\n throw new RangeError(\n `Cannot deserialize integer value ${value}: exceeds safe integer range`,\n );\n }\n return Number(value);\n }\n throw new Error(`Cannot deserialize integer from value: ${typeof value}`);\n }\n\n protected deserializeDecimal(value: unknown): number {\n // PostgreSQL can return decimals as numbers or strings depending on precision\n if (typeof value === \"number\") {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new Error(`Cannot deserialize decimal from invalid number: ${value}`);\n }\n return value;\n }\n if (typeof value === \"string\") {\n const num = parseFloat(value);\n if (Number.isNaN(num) || !Number.isFinite(num)) {\n throw new Error(`Cannot deserialize decimal from invalid string: ${value}`);\n }\n return num;\n }\n throw new Error(`Cannot deserialize decimal from value: ${typeof value}`);\n }\n\n protected deserializeString(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n throw new Error(`Cannot deserialize string from value: ${typeof value}`);\n }\n}\n\nconst normalizeTimestampString = (value: string) => {\n if (!hasTimeComponent(value) || !isTimestampWithoutTimezone(value)) {\n return value;\n }\n const withTimeSeparator = value.includes(\" \") ? value.replace(\" \", \"T\") : value;\n return `${withTimeSeparator}Z`;\n};\n\nconst hasTimeComponent = (value: string) => /\\d{2}:\\d{2}:\\d{2}/.test(value);\n\nconst isTimestampWithoutTimezone = (value: string) => {\n // Detect RFC3339-like timezone indicators: Z or +/-HH(:MM)?\n if (value.endsWith(\"Z\")) {\n return false;\n }\n return !/[+-]\\d{2}(:?\\d{2})?$/.test(value);\n};\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAa,uBAAb,cAA0C,cAAc;CACtD,YAAY,cAA4B;AACtC,QAAM,aAAa;;CAGrB,AAAU,cAAc,OAAa,MAAuB;AAG1D,SAAO;;CAGT,AAAU,iBAAiB,OAAyB;AAElD,SAAO;;CAGT,AAAU,gBAAgB,OAAe,MAAyB;AAEhE,SAAO;;CAGT,AAAU,gBAAgB,OAAgB,MAAuB;AAE/D,MAAI,iBAAiB,MAAM;AACzB,OAAI,KAAK,aAAa,eAAe,SACnC,wBAAO,IAAI,KAAK,MAAM,SAAS,GAAG,MAAM,mBAAmB,GAAG,IAAO;AAEvE,UAAO;;AAGT,MAAI,OAAO,UAAU,SAEnB,QAAO,IAAI,KAAK,yBAAyB,MAAM,CAAC;AAElD,QAAM,IAAI,MAAM,uCAAuC,QAAQ;;CAGjE,AAAU,mBAAmB,OAAyB;AACpD,MAAI,OAAO,UAAU,UACnB,QAAO;AAET,QAAM,IAAI,MAAM,0CAA0C,QAAQ;;CAGpE,AAAU,kBAAkB,OAAwB;AAClD,MAAI,OAAO,UAAU,SACnB,QAAO;AAGT,MAAI,OAAO,UAAU,SACnB,QAAO,OAAO,MAAM;AAEtB,MAAI,OAAO,UAAU,SACnB,QAAO,OAAO,MAAM;AAEtB,QAAM,IAAI,MAAM,yCAAyC,QAAQ;;CAGnE,AAAU,cAAc,OAAyB;AAE/C,SAAO;;CAGT,AAAU,gBAAgB,OAAyB;AAEjD,SAAO;;CAGT,AAAU,kBAAkB,OAA4B;AAEtD,MAAI,iBAAiB,OACnB,QAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,WAAW;AAEzE,MAAI,iBAAiB,WACnB,QAAO;AAET,MAAI,iBAAiB,YACnB,QAAO,IAAI,WAAW,MAAM;AAE9B,QAAM,IAAI,MAAM,yCAAyC,OAAO,QAAQ;;CAG1E,AAAU,mBAAmB,OAAwB;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,OAAO,MAAM,MAAM,IAAI,CAAC,OAAO,SAAS,MAAM,CAChD,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAGT,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,QAAQ,OAAO,oBAAoB,QAAQ,OAAO,iBACpD,OAAM,IAAI,WACR,oCAAoC,MAAM,8BAC3C;AAEH,UAAO,OAAO,MAAM;;AAEtB,QAAM,IAAI,MAAM,0CAA0C,OAAO,QAAQ;;CAG3E,AAAU,mBAAmB,OAAwB;AAEnD,MAAI,OAAO,UAAU,UAAU;AAC7B,OAAI,OAAO,MAAM,MAAM,IAAI,CAAC,OAAO,SAAS,MAAM,CAChD,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAET,MAAI,OAAO,UAAU,UAAU;GAC7B,MAAM,MAAM,WAAW,MAAM;AAC7B,OAAI,OAAO,MAAM,IAAI,IAAI,CAAC,OAAO,SAAS,IAAI,CAC5C,OAAM,IAAI,MAAM,mDAAmD,QAAQ;AAE7E,UAAO;;AAET,QAAM,IAAI,MAAM,0CAA0C,OAAO,QAAQ;;CAG3E,AAAU,kBAAkB,OAAwB;AAClD,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,QAAM,IAAI,MAAM,yCAAyC,OAAO,QAAQ;;;AAI5E,MAAM,4BAA4B,UAAkB;AAClD,KAAI,CAAC,iBAAiB,MAAM,IAAI,CAAC,2BAA2B,MAAM,CAChE,QAAO;AAGT,QAAO,GADmB,MAAM,SAAS,IAAI,GAAG,MAAM,QAAQ,KAAK,IAAI,GAAG,MAC9C;;AAG9B,MAAM,oBAAoB,UAAkB,oBAAoB,KAAK,MAAM;AAE3E,MAAM,8BAA8B,UAAkB;AAEpD,KAAI,MAAM,SAAS,IAAI,CACrB,QAAO;AAET,QAAO,CAAC,uBAAuB,KAAK,MAAM"}
|
|
@@ -10,6 +10,10 @@ function parseSQLiteUtcTimestamp(value) {
|
|
|
10
10
|
const milliseconds = millis ? Number(millis.padEnd(3, "0")) : 0;
|
|
11
11
|
return new Date(Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second), milliseconds));
|
|
12
12
|
}
|
|
13
|
+
function readBigInt64BEFromBytes(value) {
|
|
14
|
+
if (value.byteLength !== 8) throw new Error(`Cannot deserialize bigint from ${value.byteLength} bytes; expected 8.`);
|
|
15
|
+
return (value instanceof ArrayBuffer ? new DataView(value) : new DataView(value.buffer, value.byteOffset, value.byteLength)).getBigInt64(0, false);
|
|
16
|
+
}
|
|
13
17
|
/**
|
|
14
18
|
* SQLite-specific serializer.
|
|
15
19
|
*
|
|
@@ -46,7 +50,7 @@ var SQLiteSerializer = class extends SQLSerializer {
|
|
|
46
50
|
deserializeDate(value, col) {
|
|
47
51
|
if (value instanceof Date) return value;
|
|
48
52
|
if (typeof value === "string") {
|
|
49
|
-
if (/^\d
|
|
53
|
+
if (/^\d+(?:\.\d+)?$/.test(value)) {
|
|
50
54
|
const numericDate = new Date(Number(value));
|
|
51
55
|
if (Number.isNaN(numericDate.getTime())) throw new Error(`Cannot deserialize date from value: ${value}`);
|
|
52
56
|
return numericDate;
|
|
@@ -75,7 +79,7 @@ var SQLiteSerializer = class extends SQLSerializer {
|
|
|
75
79
|
}
|
|
76
80
|
deserializeBigInt(value) {
|
|
77
81
|
if (typeof value === "bigint") return value;
|
|
78
|
-
if (value instanceof Buffer
|
|
82
|
+
if (value instanceof Buffer || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return readBigInt64BEFromBytes(value);
|
|
79
83
|
if (typeof value === "string") return BigInt(value);
|
|
80
84
|
if (typeof value === "number") {
|
|
81
85
|
if (this.sqliteStorageMode.bigintStorage === "integer") {
|