@fragno-dev/db 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +202 -140
- package/CHANGELOG.md +35 -0
- package/README.md +30 -9
- package/dist/adapters/adapters.d.ts +23 -21
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/driver-config.d.ts +16 -1
- package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
- package/dist/adapters/generic-sql/driver-config.js +23 -1
- package/dist/adapters/generic-sql/driver-config.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +27 -9
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +55 -16
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +129 -3
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +24 -5
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +6 -5
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +21 -10
- package/dist/adapters/generic-sql/migration/dialect/sqlite.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 +8 -8
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +74 -51
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +6 -5
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +25 -17
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js +5 -3
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +15 -12
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +39 -29
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
- package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
- package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
- package/dist/adapters/generic-sql/uow-decoder.js +7 -3
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +28 -8
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +131 -0
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
- package/dist/adapters/in-memory/errors.d.ts +13 -0
- package/dist/adapters/in-memory/errors.d.ts.map +1 -0
- package/dist/adapters/in-memory/errors.js +23 -0
- package/dist/adapters/in-memory/errors.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.js +176 -0
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-uow.js +648 -0
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
- package/dist/adapters/in-memory/index.d.ts +4 -0
- package/dist/adapters/in-memory/index.js +4 -0
- package/dist/adapters/in-memory/options.d.ts +28 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -0
- package/dist/adapters/in-memory/options.js +61 -0
- package/dist/adapters/in-memory/options.js.map +1 -0
- package/dist/adapters/in-memory/reference-resolution.js +26 -0
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
- package/dist/adapters/in-memory/sorted-array-index.js +129 -0
- package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
- package/dist/adapters/in-memory/store.js +71 -0
- package/dist/adapters/in-memory/store.js.map +1 -0
- package/dist/adapters/in-memory/value-comparison.js +28 -0
- package/dist/adapters/in-memory/value-comparison.js.map +1 -0
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js +11 -11
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/adapters/sql/index.d.ts +5 -0
- package/dist/adapters/sql/index.js +4 -0
- package/dist/db-fragment-definition-builder.d.ts +18 -7
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +116 -54
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.d.ts +26 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.js +63 -0
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +17 -0
- package/dist/dispatchers/node/index.d.ts.map +1 -0
- package/dist/dispatchers/node/index.js +59 -0
- package/dist/dispatchers/node/index.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +79 -2
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +150 -32
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +29 -0
- package/dist/fragments/internal-fragment.routes.js.map +1 -0
- package/dist/fragments/internal-fragment.schema.d.ts +9 -0
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.schema.js +22 -0
- package/dist/fragments/internal-fragment.schema.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +14 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-processor.js +32 -0
- package/dist/hooks/durable-hooks-processor.js.map +1 -0
- package/dist/hooks/hooks.d.ts +42 -1
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +72 -6
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/migration-engine/auto-from-schema.js +14 -11
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +16 -10
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +72 -33
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +15 -8
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +14 -8
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts +19 -0
- package/dist/naming/sql-naming.d.ts.map +1 -0
- package/dist/naming/sql-naming.js +116 -0
- package/dist/naming/sql-naming.js.map +1 -0
- package/dist/node_modules/.pnpm/{rou3@0.7.10 → rou3@0.7.12}/node_modules/rou3/dist/index.js +8 -5
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/outbox/outbox-builder.js +156 -0
- package/dist/outbox/outbox-builder.js.map +1 -0
- package/dist/outbox/outbox.d.ts +52 -0
- package/dist/outbox/outbox.d.ts.map +1 -0
- package/dist/outbox/outbox.js +37 -0
- package/dist/outbox/outbox.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +3 -2
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +164 -20
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +67 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -1
- package/dist/packages/fragno/dist/api/route.js +14 -1
- package/dist/packages/fragno/dist/api/route.js.map +1 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +12 -0
- package/dist/packages/fragno/dist/internal/trace-context.js.map +1 -0
- package/dist/query/column-defaults.js +20 -4
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/cursor.d.ts +3 -1
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +45 -14
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +8 -0
- package/dist/query/db-now.d.ts.map +1 -0
- package/dist/query/db-now.js +7 -0
- package/dist/query/db-now.js.map +1 -0
- package/dist/query/serialize/create-sql-serializer.js +3 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +55 -11
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/serialize/sql-serializer.js +2 -2
- package/dist/query/serialize/sql-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +6 -1
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- 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 +11 -6
- 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 +50 -14
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +86 -5
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +9 -6
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js +29 -9
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +38 -14
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +81 -42
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/generate-id.js +2 -2
- package/dist/schema/generate-id.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +3 -2
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.d.ts.map +1 -0
- package/dist/schema/validator.js +123 -0
- package/dist/schema/validator.js.map +1 -0
- package/dist/schema-output/drizzle.d.ts +30 -0
- package/dist/schema-output/drizzle.d.ts.map +1 -0
- package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +82 -56
- package/dist/schema-output/drizzle.js.map +1 -0
- package/dist/schema-output/prisma.d.ts +17 -0
- package/dist/schema-output/prisma.d.ts.map +1 -0
- package/dist/schema-output/prisma.js +296 -0
- package/dist/schema-output/prisma.js.map +1 -0
- package/dist/util/default-database-adapter.js +61 -0
- package/dist/util/default-database-adapter.js.map +1 -0
- package/dist/with-database.d.ts +1 -1
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +12 -3
- package/dist/with-database.js.map +1 -1
- package/package.json +43 -28
- package/src/adapters/adapters.ts +30 -24
- package/src/adapters/drizzle/migrate-drizzle.test.ts +54 -33
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +599 -0
- package/src/adapters/drizzle/test-utils.ts +12 -8
- package/src/adapters/generic-sql/driver-config.ts +38 -0
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -5
- package/src/adapters/generic-sql/generic-sql-adapter.ts +110 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +54 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +231 -3
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +118 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +26 -8
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +46 -8
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +25 -7
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +8 -4
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +47 -8
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +27 -12
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +128 -39
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +15 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +142 -65
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +9 -6
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +271 -0
- package/src/adapters/generic-sql/query/cursor-utils.ts +41 -6
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +27 -27
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +38 -24
- package/src/adapters/generic-sql/query/select-builder.test.ts +15 -11
- package/src/adapters/generic-sql/query/select-builder.ts +6 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +52 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +50 -15
- package/src/adapters/generic-sql/query/where-builder.test.ts +91 -17
- package/src/adapters/generic-sql/query/where-builder.ts +90 -38
- package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +6 -6
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +806 -0
- package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +11 -11
- package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +10 -10
- package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +7 -7
- package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +6 -6
- package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
- package/src/adapters/generic-sql/uow-decoder.test.ts +1 -1
- package/src/adapters/generic-sql/uow-decoder.ts +21 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +33 -2
- package/src/adapters/generic-sql/uow-encoder.ts +50 -11
- package/src/adapters/in-memory/condition-evaluator.test.ts +193 -0
- package/src/adapters/in-memory/condition-evaluator.ts +275 -0
- package/src/adapters/in-memory/errors.ts +20 -0
- package/src/adapters/in-memory/in-memory-adapter.ts +277 -0
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +296 -0
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +100 -0
- package/src/adapters/in-memory/in-memory-uow.ts +1348 -0
- package/src/adapters/in-memory/index.ts +3 -0
- package/src/adapters/in-memory/options.test.ts +41 -0
- package/src/adapters/in-memory/options.ts +87 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +50 -0
- package/src/adapters/in-memory/reference-resolution.ts +67 -0
- package/src/adapters/in-memory/sorted-array-index.test.ts +123 -0
- package/src/adapters/in-memory/sorted-array-index.ts +228 -0
- package/src/adapters/in-memory/store.test.ts +68 -0
- package/src/adapters/in-memory/store.ts +145 -0
- package/src/adapters/in-memory/value-comparison.ts +53 -0
- package/src/adapters/in-memory/value-normalization.test.ts +57 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1163 -0
- package/src/adapters/shared/from-unit-of-work-compiler.ts +3 -1
- package/src/adapters/shared/uow-operation-compiler.ts +26 -16
- package/src/adapters/sql/index.ts +12 -0
- package/src/db-fragment-definition-builder.test.ts +30 -12
- package/src/db-fragment-definition-builder.ts +142 -73
- package/src/db-fragment-instantiator.test.ts +105 -13
- package/src/db-fragment-integration.test.ts +9 -7
- package/src/dispatchers/cloudflare-do/index.test.ts +73 -0
- package/src/dispatchers/cloudflare-do/index.ts +104 -0
- package/src/dispatchers/node/index.test.ts +91 -0
- package/src/dispatchers/node/index.ts +87 -0
- package/src/fragments/internal-fragment.routes.ts +42 -0
- package/src/fragments/internal-fragment.schema.ts +51 -0
- package/src/fragments/internal-fragment.test.ts +458 -8
- package/src/fragments/internal-fragment.ts +322 -63
- package/src/hooks/durable-hooks-processor.test.ts +117 -0
- package/src/hooks/durable-hooks-processor.ts +67 -0
- package/src/hooks/hooks.test.ts +165 -5
- package/src/hooks/hooks.ts +197 -9
- package/src/migration-engine/auto-from-schema.test.ts +14 -14
- package/src/migration-engine/auto-from-schema.ts +5 -2
- package/src/migration-engine/create.test.ts +2 -2
- package/src/migration-engine/generation-engine.test.ts +229 -104
- package/src/migration-engine/generation-engine.ts +94 -64
- package/src/migration-engine/shared.ts +1 -0
- package/src/mod.ts +64 -26
- package/src/naming/sql-naming.ts +180 -0
- package/src/outbox/outbox-builder.ts +241 -0
- package/src/outbox/outbox.test.ts +253 -0
- package/src/outbox/outbox.ts +137 -0
- package/src/query/column-defaults.ts +41 -3
- package/src/query/condition-builder.test.ts +3 -3
- package/src/query/cursor.test.ts +116 -18
- package/src/query/cursor.ts +75 -26
- package/src/query/db-now.ts +6 -0
- package/src/query/query-type.test.ts +2 -2
- package/src/query/serialize/create-sql-serializer.ts +7 -2
- package/src/query/serialize/dialect/mysql-serializer.ts +12 -4
- package/src/query/serialize/dialect/postgres-serializer.ts +34 -4
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +51 -1
- package/src/query/serialize/dialect/sqlite-serializer.ts +92 -9
- package/src/query/serialize/sql-serializer.ts +4 -4
- package/src/query/simple-query-interface.ts +5 -0
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +25 -1
- package/src/query/unit-of-work/execute-unit-of-work.ts +25 -8
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +12 -12
- package/src/query/unit-of-work/unit-of-work-types.test.ts +1 -1
- package/src/query/unit-of-work/unit-of-work.test.ts +168 -37
- package/src/query/unit-of-work/unit-of-work.ts +203 -18
- package/src/query/value-decoding.test.ts +13 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +85 -2
- package/src/query/value-encoding.ts +56 -6
- package/src/schema/create.test.ts +129 -42
- package/src/schema/create.ts +185 -47
- package/src/schema/generate-id.test.ts +2 -2
- package/src/schema/generate-id.ts +2 -2
- package/src/schema/serialize.test.ts +14 -2
- package/src/schema/type-conversion/create-sql-type-mapper.ts +7 -2
- package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
- package/src/schema/type-conversion/type-mapping.test.ts +25 -1
- package/src/schema/validator.test.ts +197 -0
- package/src/schema/validator.ts +231 -0
- package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +179 -129
- package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +143 -93
- package/src/schema-output/prisma.test.ts +536 -0
- package/src/schema-output/prisma.ts +573 -0
- package/src/util/default-database-adapter.ts +106 -0
- package/src/with-database.ts +22 -3
- package/tsdown.config.ts +6 -4
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
- package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
- package/dist/adapters/drizzle/generate.d.ts +0 -30
- package/dist/adapters/drizzle/generate.d.ts.map +0 -1
- package/dist/adapters/drizzle/generate.js.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.js +0 -17
- package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
- package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
- package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
- package/dist/adapters/shared/table-name-mapper.js +0 -43
- package/dist/adapters/shared/table-name-mapper.js.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/schema-generator/schema-generator.d.ts +0 -15
- package/dist/schema-generator/schema-generator.d.ts.map +0 -1
- package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
- package/src/adapters/kysely/kysely-adapter.ts +0 -27
- package/src/adapters/shared/table-name-mapper.ts +0 -50
- package/src/schema-generator/schema-generator.ts +0 -12
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
import SQLite from "better-sqlite3";
|
|
2
|
+
import { SqliteDialect } from "kysely";
|
|
3
|
+
import { beforeAll, describe, expect, expectTypeOf, it } from "vitest";
|
|
4
|
+
import { SqlAdapter } from "../generic-sql/generic-sql-adapter";
|
|
5
|
+
import { column, idColumn, referenceColumn, schema, type FragnoId } from "../../schema/create";
|
|
6
|
+
import { Cursor } from "../../query/cursor";
|
|
7
|
+
import {
|
|
8
|
+
createHandlerTxBuilder,
|
|
9
|
+
createServiceTxBuilder,
|
|
10
|
+
} from "../../query/unit-of-work/execute-unit-of-work";
|
|
11
|
+
import { ExponentialBackoffRetryPolicy } from "../../query/unit-of-work/retry-policy";
|
|
12
|
+
import { BetterSQLite3DriverConfig } from "../generic-sql/driver-config";
|
|
13
|
+
import { internalSchema } from "../../fragments/internal-fragment";
|
|
14
|
+
import { sqliteStorageDefault, sqliteStoragePrisma } from "../generic-sql/sqlite-storage";
|
|
15
|
+
import { FragnoDatabase } from "../../mod";
|
|
16
|
+
import { generateSchemaArtifacts } from "../../migration-engine/generation-engine";
|
|
17
|
+
|
|
18
|
+
describe("SqlAdapter SQLite (prisma profile)", () => {
|
|
19
|
+
const testSchema = schema("test", (s) => {
|
|
20
|
+
return s
|
|
21
|
+
.addTable("users", (t) => {
|
|
22
|
+
return t
|
|
23
|
+
.addColumn("id", idColumn())
|
|
24
|
+
.addColumn("name", column("string"))
|
|
25
|
+
.addColumn("age", column("integer").nullable())
|
|
26
|
+
.createIndex("name_idx", ["name"]);
|
|
27
|
+
})
|
|
28
|
+
.addTable("emails", (t) => {
|
|
29
|
+
return t
|
|
30
|
+
.addColumn("id", idColumn())
|
|
31
|
+
.addColumn("user_id", referenceColumn())
|
|
32
|
+
.addColumn("email", column("string"))
|
|
33
|
+
.addColumn("is_primary", column("bool").defaultTo(false))
|
|
34
|
+
.createIndex("unique_email", ["email"], { unique: true })
|
|
35
|
+
.createIndex("user_emails", ["user_id"]);
|
|
36
|
+
})
|
|
37
|
+
.addTable("posts", (t) => {
|
|
38
|
+
return t
|
|
39
|
+
.addColumn("id", idColumn())
|
|
40
|
+
.addColumn("user_id", referenceColumn())
|
|
41
|
+
.addColumn("title", column("string"))
|
|
42
|
+
.addColumn("content", column("string"))
|
|
43
|
+
.createIndex("posts_user_idx", ["user_id"]);
|
|
44
|
+
})
|
|
45
|
+
.addTable("comments", (t) => {
|
|
46
|
+
return t
|
|
47
|
+
.addColumn("id", idColumn())
|
|
48
|
+
.addColumn("post_id", referenceColumn())
|
|
49
|
+
.addColumn("user_id", referenceColumn())
|
|
50
|
+
.addColumn("text", column("string"))
|
|
51
|
+
.createIndex("comments_post_idx", ["post_id"])
|
|
52
|
+
.createIndex("comments_user_idx", ["user_id"]);
|
|
53
|
+
})
|
|
54
|
+
.addTable("events", (t) => {
|
|
55
|
+
return t
|
|
56
|
+
.addColumn("id", idColumn())
|
|
57
|
+
.addColumn("name", column("string"))
|
|
58
|
+
.addColumn(
|
|
59
|
+
"created_at",
|
|
60
|
+
column("timestamp").defaultTo((b) => b.now()),
|
|
61
|
+
)
|
|
62
|
+
.addColumn("happened_on", column("date"))
|
|
63
|
+
.addColumn("payload", column("json").nullable())
|
|
64
|
+
.addColumn("big_score", column("bigint"))
|
|
65
|
+
.createIndex("events_name_idx", ["name"]);
|
|
66
|
+
})
|
|
67
|
+
.addReference("user", {
|
|
68
|
+
type: "one",
|
|
69
|
+
from: { table: "emails", column: "user_id" },
|
|
70
|
+
to: { table: "users", column: "id" },
|
|
71
|
+
})
|
|
72
|
+
.addReference("author", {
|
|
73
|
+
type: "one",
|
|
74
|
+
from: { table: "posts", column: "user_id" },
|
|
75
|
+
to: { table: "users", column: "id" },
|
|
76
|
+
})
|
|
77
|
+
.addReference("post", {
|
|
78
|
+
type: "one",
|
|
79
|
+
from: { table: "comments", column: "post_id" },
|
|
80
|
+
to: { table: "posts", column: "id" },
|
|
81
|
+
})
|
|
82
|
+
.addReference("commenter", {
|
|
83
|
+
type: "one",
|
|
84
|
+
from: { table: "comments", column: "user_id" },
|
|
85
|
+
to: { table: "users", column: "id" },
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const schema2 = schema("schema2", (s) => {
|
|
90
|
+
return s
|
|
91
|
+
.addTable("products", (t) => {
|
|
92
|
+
return t
|
|
93
|
+
.addColumn("id", idColumn())
|
|
94
|
+
.addColumn("name", column("string"))
|
|
95
|
+
.addColumn("price", column("integer"))
|
|
96
|
+
.createIndex("name_idx", ["name"]);
|
|
97
|
+
})
|
|
98
|
+
.addTable("orders", (t) => {
|
|
99
|
+
return t
|
|
100
|
+
.addColumn("id", idColumn())
|
|
101
|
+
.addColumn("product_id", referenceColumn())
|
|
102
|
+
.addColumn("quantity", column("integer"))
|
|
103
|
+
.createIndex("product_orders_idx", ["product_id"]);
|
|
104
|
+
})
|
|
105
|
+
.addReference("product", {
|
|
106
|
+
type: "one",
|
|
107
|
+
from: { table: "orders", column: "product_id" },
|
|
108
|
+
to: { table: "products", column: "id" },
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
let adapter: SqlAdapter;
|
|
113
|
+
let sqliteDatabase: InstanceType<typeof SQLite>;
|
|
114
|
+
|
|
115
|
+
beforeAll(async () => {
|
|
116
|
+
sqliteDatabase = new SQLite(":memory:");
|
|
117
|
+
|
|
118
|
+
const dialect = new SqliteDialect({
|
|
119
|
+
database: sqliteDatabase,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
adapter = new SqlAdapter({
|
|
123
|
+
dialect,
|
|
124
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
125
|
+
sqliteProfile: "prisma",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
{
|
|
129
|
+
const migrations = adapter.prepareMigrations(internalSchema, "");
|
|
130
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
{
|
|
134
|
+
const migrations = adapter.prepareMigrations(testSchema, "namespace");
|
|
135
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
const migrations = adapter.prepareMigrations(schema2, "namespace2");
|
|
140
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return async () => {
|
|
144
|
+
await adapter.close();
|
|
145
|
+
sqliteDatabase.close();
|
|
146
|
+
};
|
|
147
|
+
}, 12000);
|
|
148
|
+
|
|
149
|
+
it("should use prisma sqlite storage mode", () => {
|
|
150
|
+
expect(adapter.sqliteStorageMode).toBe(sqliteStoragePrisma);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should default schema output path to fragno.prisma", async () => {
|
|
154
|
+
const fragnoDb = new FragnoDatabase({
|
|
155
|
+
namespace: "namespace",
|
|
156
|
+
schema: testSchema,
|
|
157
|
+
adapter,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const [result] = await generateSchemaArtifacts([fragnoDb], { format: "prisma" });
|
|
161
|
+
|
|
162
|
+
expect(result.path).toBe("fragno.prisma");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should store prisma storage values using sqlite-friendly types", async () => {
|
|
166
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
167
|
+
const happenedOn = new Date("2024-06-18T12:34:56.789Z");
|
|
168
|
+
const bigScore = 12345n;
|
|
169
|
+
|
|
170
|
+
const createUow = queryEngine.createUnitOfWork("create-prisma-storage-event");
|
|
171
|
+
createUow.create("events", {
|
|
172
|
+
name: "Prisma Storage Event",
|
|
173
|
+
happened_on: happenedOn,
|
|
174
|
+
payload: { level: "info", tags: ["sqlite", "prisma"] },
|
|
175
|
+
big_score: bigScore,
|
|
176
|
+
});
|
|
177
|
+
await createUow.executeMutations();
|
|
178
|
+
|
|
179
|
+
const tableName = adapter.namingStrategy.tableName("events", "namespace");
|
|
180
|
+
const row = sqliteDatabase
|
|
181
|
+
.prepare(`SELECT happened_on, big_score FROM ${tableName} WHERE name = ?`)
|
|
182
|
+
.get("Prisma Storage Event") as { happened_on?: unknown; big_score?: unknown } | undefined;
|
|
183
|
+
|
|
184
|
+
expect(typeof row?.happened_on).toBe("string");
|
|
185
|
+
expect(row?.happened_on).toBe(happenedOn.toISOString());
|
|
186
|
+
expect(row?.big_score).not.toBeInstanceOf(Buffer);
|
|
187
|
+
expect(["number", "bigint"]).toContain(typeof row?.big_score);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should honor explicit sqlite storage overrides", async () => {
|
|
191
|
+
const fragnoDatabase = new SQLite(":memory:");
|
|
192
|
+
const fragnoDialect = new SqliteDialect({ database: fragnoDatabase });
|
|
193
|
+
const fragnoAdapter = new SqlAdapter({
|
|
194
|
+
dialect: fragnoDialect,
|
|
195
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
196
|
+
sqliteStorageMode: sqliteStorageDefault,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const internalMigrations = fragnoAdapter.prepareMigrations(internalSchema, "");
|
|
201
|
+
await internalMigrations.executeWithDriver(fragnoAdapter.driver, 0);
|
|
202
|
+
|
|
203
|
+
const migrations = fragnoAdapter.prepareMigrations(testSchema, "namespace");
|
|
204
|
+
await migrations.executeWithDriver(fragnoAdapter.driver, 0);
|
|
205
|
+
|
|
206
|
+
expect(fragnoAdapter.sqliteStorageMode).toBe(sqliteStorageDefault);
|
|
207
|
+
|
|
208
|
+
const queryEngine = fragnoAdapter.createQueryEngine(testSchema, "namespace");
|
|
209
|
+
const happenedOn = new Date("2024-06-18T12:34:56.789Z");
|
|
210
|
+
const bigScore = 1234567890123n;
|
|
211
|
+
|
|
212
|
+
const createUow = queryEngine.createUnitOfWork("create-fragno-storage-event");
|
|
213
|
+
createUow.create("events", {
|
|
214
|
+
name: "Fragno Storage Event",
|
|
215
|
+
happened_on: happenedOn,
|
|
216
|
+
payload: { level: "info", tags: ["sqlite", "fragno"] },
|
|
217
|
+
big_score: bigScore,
|
|
218
|
+
});
|
|
219
|
+
await createUow.executeMutations();
|
|
220
|
+
|
|
221
|
+
const tableName = fragnoAdapter.namingStrategy.tableName("events", "namespace");
|
|
222
|
+
const row = fragnoDatabase
|
|
223
|
+
.prepare(`SELECT happened_on, big_score FROM ${tableName} WHERE name = ?`)
|
|
224
|
+
.get("Fragno Storage Event") as { happened_on?: number; big_score?: Buffer } | undefined;
|
|
225
|
+
|
|
226
|
+
expect(typeof row?.happened_on).toBe("number");
|
|
227
|
+
expect(row?.happened_on).toBe(happenedOn.getTime());
|
|
228
|
+
expect(row?.big_score).toBeInstanceOf(Buffer);
|
|
229
|
+
expect(row?.big_score?.readBigInt64BE(0)).toBe(bigScore);
|
|
230
|
+
} finally {
|
|
231
|
+
await fragnoAdapter.close();
|
|
232
|
+
fragnoDatabase.close();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should execute Unit of Work with version checking", async () => {
|
|
237
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
238
|
+
|
|
239
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
240
|
+
createUow.create("users", {
|
|
241
|
+
name: "Prisma SQLite Alice",
|
|
242
|
+
age: 25,
|
|
243
|
+
});
|
|
244
|
+
createUow.create("users", {
|
|
245
|
+
name: "Prisma SQLite Bob",
|
|
246
|
+
age: 30,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
|
|
250
|
+
Parameters<typeof createUow.find>[0]
|
|
251
|
+
>();
|
|
252
|
+
expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
|
|
253
|
+
"users" | "emails" | "posts" | "comments" | "events"
|
|
254
|
+
>();
|
|
255
|
+
|
|
256
|
+
const { success: createSuccess } = await createUow.executeMutations();
|
|
257
|
+
expect(createSuccess).toBe(true);
|
|
258
|
+
|
|
259
|
+
const createdIds = createUow.getCreatedIds();
|
|
260
|
+
expect(createdIds).toHaveLength(2);
|
|
261
|
+
|
|
262
|
+
const [createdUsers] = await queryEngine
|
|
263
|
+
.createUnitOfWork("get-created-users")
|
|
264
|
+
.find("users", (b) =>
|
|
265
|
+
b.whereIndex("name_idx", (eb) =>
|
|
266
|
+
eb("name", "in", ["Prisma SQLite Alice", "Prisma SQLite Bob"]),
|
|
267
|
+
),
|
|
268
|
+
)
|
|
269
|
+
.executeRetrieve();
|
|
270
|
+
|
|
271
|
+
expect(createdUsers).toHaveLength(2);
|
|
272
|
+
|
|
273
|
+
const initialUserId = createdIds[0];
|
|
274
|
+
|
|
275
|
+
const uow = queryEngine
|
|
276
|
+
.createUnitOfWork("update-user-age")
|
|
277
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)));
|
|
278
|
+
|
|
279
|
+
const [users] = await uow.executeRetrieve();
|
|
280
|
+
|
|
281
|
+
uow.update("users", initialUserId, (b) => b.set({ age: 26 }).check());
|
|
282
|
+
|
|
283
|
+
const { success } = await uow.executeMutations();
|
|
284
|
+
expect(success).toBe(true);
|
|
285
|
+
expect(users).toHaveLength(1);
|
|
286
|
+
expect(users[0].name).toBe("Prisma SQLite Alice");
|
|
287
|
+
|
|
288
|
+
const [[updatedUser]] = await queryEngine
|
|
289
|
+
.createUnitOfWork("get-updated-user")
|
|
290
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
|
|
291
|
+
.executeRetrieve();
|
|
292
|
+
|
|
293
|
+
expect(updatedUser).toMatchObject({
|
|
294
|
+
id: expect.objectContaining({
|
|
295
|
+
externalId: initialUserId.externalId,
|
|
296
|
+
version: 1,
|
|
297
|
+
}),
|
|
298
|
+
age: 26,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const uow2 = queryEngine.createUnitOfWork("update-user-stale");
|
|
302
|
+
uow2.update("users", initialUserId, (b) => b.set({ age: 27 }).check());
|
|
303
|
+
|
|
304
|
+
const { success: success2 } = await uow2.executeMutations();
|
|
305
|
+
expect(success2).toBe(false);
|
|
306
|
+
|
|
307
|
+
const [[unchangedUser]] = await queryEngine
|
|
308
|
+
.createUnitOfWork("verify-unchanged")
|
|
309
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
|
|
310
|
+
.executeRetrieve();
|
|
311
|
+
|
|
312
|
+
expect(unchangedUser).toMatchObject({
|
|
313
|
+
id: expect.objectContaining({
|
|
314
|
+
version: 1,
|
|
315
|
+
}),
|
|
316
|
+
age: 26,
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("should support count operations", async () => {
|
|
321
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
322
|
+
|
|
323
|
+
const createUow = queryEngine.createUnitOfWork("create-count-users");
|
|
324
|
+
createUow.create("users", { name: "Prisma SQLite Count 1", age: 20 });
|
|
325
|
+
createUow.create("users", { name: "Prisma SQLite Count 2", age: 30 });
|
|
326
|
+
createUow.create("users", { name: "Prisma SQLite Count 3", age: 40 });
|
|
327
|
+
await createUow.executeMutations();
|
|
328
|
+
|
|
329
|
+
const [totalCount] = await queryEngine
|
|
330
|
+
.createUnitOfWork("count-all")
|
|
331
|
+
.find("users", (b) => b.whereIndex("primary").selectCount())
|
|
332
|
+
.executeRetrieve();
|
|
333
|
+
|
|
334
|
+
expect(totalCount).toBeGreaterThanOrEqual(3);
|
|
335
|
+
expect(typeof totalCount).toBe("number");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should support cursor-based pagination", async () => {
|
|
339
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
340
|
+
const prefix = "Prisma SQLite Cursor";
|
|
341
|
+
|
|
342
|
+
const createUow = queryEngine.createUnitOfWork("create-cursor-users");
|
|
343
|
+
createUow.create("users", { name: `${prefix} A`, age: 20 });
|
|
344
|
+
createUow.create("users", { name: `${prefix} B`, age: 30 });
|
|
345
|
+
createUow.create("users", { name: `${prefix} C`, age: 40 });
|
|
346
|
+
createUow.create("users", { name: `${prefix} D`, age: 50 });
|
|
347
|
+
createUow.create("users", { name: `${prefix} E`, age: 60 });
|
|
348
|
+
|
|
349
|
+
await createUow.executeMutations();
|
|
350
|
+
|
|
351
|
+
const [firstPage] = await queryEngine
|
|
352
|
+
.createUnitOfWork("first-page")
|
|
353
|
+
.find("users", (b) =>
|
|
354
|
+
b
|
|
355
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
356
|
+
.orderByIndex("name_idx", "asc")
|
|
357
|
+
.pageSize(2),
|
|
358
|
+
)
|
|
359
|
+
.executeRetrieve();
|
|
360
|
+
|
|
361
|
+
expect(firstPage).toHaveLength(2);
|
|
362
|
+
expect(firstPage.map((u) => u.name)).toEqual([`${prefix} A`, `${prefix} B`]);
|
|
363
|
+
|
|
364
|
+
const lastItem = firstPage[firstPage.length - 1]!;
|
|
365
|
+
const cursor = new Cursor({
|
|
366
|
+
indexName: "name_idx",
|
|
367
|
+
orderDirection: "asc",
|
|
368
|
+
pageSize: 2,
|
|
369
|
+
indexValues: { name: lastItem.name },
|
|
370
|
+
}).encode();
|
|
371
|
+
|
|
372
|
+
const [secondPage] = await queryEngine
|
|
373
|
+
.createUnitOfWork("second-page")
|
|
374
|
+
.find("users", (b) =>
|
|
375
|
+
b
|
|
376
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
377
|
+
.orderByIndex("name_idx", "asc")
|
|
378
|
+
.after(cursor)
|
|
379
|
+
.pageSize(2),
|
|
380
|
+
)
|
|
381
|
+
.executeRetrieve();
|
|
382
|
+
|
|
383
|
+
expect(secondPage).toHaveLength(2);
|
|
384
|
+
expect(secondPage.map((u) => u.name)).toEqual([`${prefix} C`, `${prefix} D`]);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should verify hasNextPage in cursor pagination", async () => {
|
|
388
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
389
|
+
const prefix = "Prisma SQLite HasNextPage";
|
|
390
|
+
|
|
391
|
+
for (let i = 1; i <= 15; i++) {
|
|
392
|
+
await queryEngine.create("users", {
|
|
393
|
+
name: `${prefix} ${i.toString().padStart(2, "0")}`,
|
|
394
|
+
age: 20 + i,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const firstPage = await queryEngine.findWithCursor("users", (b) =>
|
|
399
|
+
b
|
|
400
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
401
|
+
.orderByIndex("name_idx", "asc")
|
|
402
|
+
.pageSize(10),
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
expect(firstPage.items).toHaveLength(10);
|
|
406
|
+
expect(firstPage.hasNextPage).toBe(true);
|
|
407
|
+
expect(firstPage.cursor).toBeInstanceOf(Cursor);
|
|
408
|
+
|
|
409
|
+
const secondPage = await queryEngine.findWithCursor("users", (b) =>
|
|
410
|
+
b
|
|
411
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
412
|
+
.after(firstPage.cursor!)
|
|
413
|
+
.orderByIndex("name_idx", "asc")
|
|
414
|
+
.pageSize(10),
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
expect(secondPage.items).toHaveLength(5);
|
|
418
|
+
expect(secondPage.hasNextPage).toBe(false);
|
|
419
|
+
expect(secondPage.cursor).toBeUndefined();
|
|
420
|
+
|
|
421
|
+
const emptyPage = await queryEngine.findWithCursor("users", (b) =>
|
|
422
|
+
b
|
|
423
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "NoMatchPrefix"))
|
|
424
|
+
.orderByIndex("name_idx", "asc")
|
|
425
|
+
.pageSize(10),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
expect(emptyPage.items).toHaveLength(0);
|
|
429
|
+
expect(emptyPage.hasNextPage).toBe(false);
|
|
430
|
+
expect(emptyPage.cursor).toBeUndefined();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("should support findWithCursor() in Unit of Work", async () => {
|
|
434
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
435
|
+
const prefix = "Prisma SQLite UOW Cursor";
|
|
436
|
+
|
|
437
|
+
for (let i = 1; i <= 6; i++) {
|
|
438
|
+
await queryEngine.create("users", {
|
|
439
|
+
name: `${prefix} ${i}`,
|
|
440
|
+
age: 40 + i,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const uow = queryEngine.createUnitOfWork("cursor-test").findWithCursor("users", (b) =>
|
|
445
|
+
b
|
|
446
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
447
|
+
.orderByIndex("name_idx", "asc")
|
|
448
|
+
.pageSize(5),
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const [result] = await uow.executeRetrieve();
|
|
452
|
+
|
|
453
|
+
expect(result.items).toHaveLength(5);
|
|
454
|
+
expect(typeof result.hasNextPage).toBe("boolean");
|
|
455
|
+
expect(result.cursor).toBeInstanceOf(Cursor);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("should support forSchema for multi-schema queries", async () => {
|
|
459
|
+
const queryEngine1 = adapter.createQueryEngine(testSchema, "namespace");
|
|
460
|
+
const queryEngine2 = adapter.createQueryEngine(schema2, "namespace2");
|
|
461
|
+
|
|
462
|
+
const createUsersUow = queryEngine1.createUnitOfWork("create-users-for-multi-schema");
|
|
463
|
+
createUsersUow.create("users", { name: "Prisma Multi Schema User 1", age: 25 });
|
|
464
|
+
createUsersUow.create("users", { name: "Prisma Multi Schema User 2", age: 30 });
|
|
465
|
+
const { success: usersSuccess } = await createUsersUow.executeMutations();
|
|
466
|
+
expect(usersSuccess).toBe(true);
|
|
467
|
+
|
|
468
|
+
const createProductsUow = queryEngine2.createUnitOfWork("create-products-for-multi-schema");
|
|
469
|
+
createProductsUow.create("products", { name: "Prisma Product A", price: 100 });
|
|
470
|
+
createProductsUow.create("products", { name: "Prisma Product B", price: 200 });
|
|
471
|
+
const { success: productsSuccess } = await createProductsUow.executeMutations();
|
|
472
|
+
expect(productsSuccess).toBe(true);
|
|
473
|
+
|
|
474
|
+
const uow = queryEngine1.createUnitOfWork("multi-schema-query");
|
|
475
|
+
|
|
476
|
+
const view1 = uow
|
|
477
|
+
.forSchema(testSchema)
|
|
478
|
+
.find("users", (b) =>
|
|
479
|
+
b
|
|
480
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "Prisma Multi Schema User"))
|
|
481
|
+
.select(["id", "name"]),
|
|
482
|
+
)
|
|
483
|
+
.find("users", (b) =>
|
|
484
|
+
b
|
|
485
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "Prisma Multi Schema User"))
|
|
486
|
+
.select(["name", "age"]),
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
const view2 = uow
|
|
490
|
+
.forSchema(schema2)
|
|
491
|
+
.find("products", (b) => b.whereIndex("primary").select(["name", "price"]));
|
|
492
|
+
|
|
493
|
+
await uow.executeRetrieve();
|
|
494
|
+
|
|
495
|
+
const [users1, users2] = await view1.retrievalPhase;
|
|
496
|
+
const [user1] = users1;
|
|
497
|
+
expectTypeOf(user1).toMatchObjectType<{ id: FragnoId; name: string }>();
|
|
498
|
+
|
|
499
|
+
const [user2] = users2;
|
|
500
|
+
expectTypeOf(user2).toMatchObjectType<{ name: string; age: number | null }>();
|
|
501
|
+
|
|
502
|
+
const [products] = await view2.retrievalPhase;
|
|
503
|
+
const [product1] = products;
|
|
504
|
+
expectTypeOf(product1).toMatchObjectType<{ name: string; price: number }>();
|
|
505
|
+
|
|
506
|
+
expect(users1).toHaveLength(2);
|
|
507
|
+
expect(users1[0]).toMatchObject({
|
|
508
|
+
id: expect.any(Object),
|
|
509
|
+
name: "Prisma Multi Schema User 1",
|
|
510
|
+
});
|
|
511
|
+
expect(users1[1]).toMatchObject({
|
|
512
|
+
id: expect.any(Object),
|
|
513
|
+
name: "Prisma Multi Schema User 2",
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
expect(users2).toHaveLength(2);
|
|
517
|
+
expect(users2[0]).toMatchObject({
|
|
518
|
+
name: "Prisma Multi Schema User 1",
|
|
519
|
+
age: 25,
|
|
520
|
+
});
|
|
521
|
+
expect(users2[1]).toMatchObject({
|
|
522
|
+
name: "Prisma Multi Schema User 2",
|
|
523
|
+
age: 30,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
expect(products).toHaveLength(2);
|
|
527
|
+
expect(products[0]).toMatchObject({
|
|
528
|
+
name: "Prisma Product A",
|
|
529
|
+
price: 100,
|
|
530
|
+
});
|
|
531
|
+
expect(products[1]).toMatchObject({
|
|
532
|
+
name: "Prisma Product B",
|
|
533
|
+
price: 200,
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("should support joins", async () => {
|
|
538
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
539
|
+
|
|
540
|
+
const createUserUow = queryEngine.createUnitOfWork("create-join-user");
|
|
541
|
+
createUserUow.create("users", { name: "Prisma SQLite Email User", age: 20 });
|
|
542
|
+
|
|
543
|
+
const { success } = await createUserUow.executeMutations();
|
|
544
|
+
expect(success).toBe(true);
|
|
545
|
+
|
|
546
|
+
const [usersResult] = await queryEngine
|
|
547
|
+
.createUnitOfWork("get-created-user")
|
|
548
|
+
.find("users", (b) =>
|
|
549
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Email User")),
|
|
550
|
+
)
|
|
551
|
+
.executeRetrieve();
|
|
552
|
+
|
|
553
|
+
expect(usersResult).toHaveLength(1);
|
|
554
|
+
const createdUser = usersResult[0];
|
|
555
|
+
expect(createdUser.name).toBe("Prisma SQLite Email User");
|
|
556
|
+
|
|
557
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
|
|
558
|
+
createEmailUow.create("emails", {
|
|
559
|
+
user_id: createdUser.id,
|
|
560
|
+
email: "prisma-sqlite@example.com",
|
|
561
|
+
is_primary: true,
|
|
562
|
+
});
|
|
563
|
+
await createEmailUow.executeMutations();
|
|
564
|
+
|
|
565
|
+
const uow = queryEngine
|
|
566
|
+
.createUnitOfWork("test-joins")
|
|
567
|
+
.find("emails", (b) =>
|
|
568
|
+
b
|
|
569
|
+
.whereIndex("user_emails", (eb) => eb("user_id", "=", createdUser.id))
|
|
570
|
+
.join((jb) => jb.user((builder) => builder.select(["name", "id", "age"]))),
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
const [[email]] = await uow.executeRetrieve();
|
|
574
|
+
|
|
575
|
+
expect(email).toMatchObject({
|
|
576
|
+
id: expect.objectContaining({
|
|
577
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
578
|
+
internalId: expect.any(BigInt),
|
|
579
|
+
}),
|
|
580
|
+
user_id: expect.objectContaining({
|
|
581
|
+
internalId: expect.any(BigInt),
|
|
582
|
+
}),
|
|
583
|
+
email: "prisma-sqlite@example.com",
|
|
584
|
+
is_primary: true,
|
|
585
|
+
user: {
|
|
586
|
+
id: expect.objectContaining({
|
|
587
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
588
|
+
internalId: expect.any(BigInt),
|
|
589
|
+
}),
|
|
590
|
+
name: "Prisma SQLite Email User",
|
|
591
|
+
age: 20,
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it("should support inserting with external id string", async () => {
|
|
597
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
598
|
+
|
|
599
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-external-id");
|
|
600
|
+
createUserUow.create("users", { name: "Prisma SQLite External ID User", age: 35 });
|
|
601
|
+
await createUserUow.executeMutations();
|
|
602
|
+
|
|
603
|
+
const [[user]] = await queryEngine
|
|
604
|
+
.createUnitOfWork("get-user-for-external-id")
|
|
605
|
+
.find("users", (b) =>
|
|
606
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite External ID User")),
|
|
607
|
+
)
|
|
608
|
+
.executeRetrieve();
|
|
609
|
+
|
|
610
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-email-with-external-id");
|
|
611
|
+
createEmailUow.create("emails", {
|
|
612
|
+
user_id: user.id.externalId,
|
|
613
|
+
email: "prisma-sqlite-external-id@example.com",
|
|
614
|
+
is_primary: false,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const { success } = await createEmailUow.executeMutations();
|
|
618
|
+
expect(success).toBe(true);
|
|
619
|
+
|
|
620
|
+
const [[email]] = await queryEngine
|
|
621
|
+
.createUnitOfWork("get-email-by-external-id")
|
|
622
|
+
.find("emails", (b) =>
|
|
623
|
+
b
|
|
624
|
+
.whereIndex("unique_email", (eb) =>
|
|
625
|
+
eb("email", "=", "prisma-sqlite-external-id@example.com"),
|
|
626
|
+
)
|
|
627
|
+
.join((jb) => jb.user((builder) => builder.select(["name", "id"]))),
|
|
628
|
+
)
|
|
629
|
+
.executeRetrieve();
|
|
630
|
+
|
|
631
|
+
expect(email).toMatchObject({
|
|
632
|
+
email: "prisma-sqlite-external-id@example.com",
|
|
633
|
+
is_primary: false,
|
|
634
|
+
user_id: expect.objectContaining({
|
|
635
|
+
internalId: user.id.internalId,
|
|
636
|
+
}),
|
|
637
|
+
user: {
|
|
638
|
+
id: expect.objectContaining({
|
|
639
|
+
externalId: user.id.externalId,
|
|
640
|
+
}),
|
|
641
|
+
name: "Prisma SQLite External ID User",
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it("should create user and post in same transaction using returned ID", async () => {
|
|
647
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
648
|
+
|
|
649
|
+
const uow = queryEngine.createUnitOfWork("create-user-and-post");
|
|
650
|
+
const userId = uow.create("users", {
|
|
651
|
+
name: "Prisma SQLite UOW User",
|
|
652
|
+
age: 35,
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
const postId = uow.create("posts", {
|
|
656
|
+
user_id: userId,
|
|
657
|
+
title: "Prisma SQLite UOW Post",
|
|
658
|
+
content: "This post was created in the same transaction as the user",
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const { success } = await uow.executeMutations();
|
|
662
|
+
expect(success).toBe(true);
|
|
663
|
+
|
|
664
|
+
const userIdStr = userId.toString();
|
|
665
|
+
const postIdStr = postId.toString();
|
|
666
|
+
|
|
667
|
+
const [[user]] = await queryEngine
|
|
668
|
+
.createUnitOfWork("verify-user")
|
|
669
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userIdStr)))
|
|
670
|
+
.executeRetrieve();
|
|
671
|
+
|
|
672
|
+
const [[post]] = await queryEngine
|
|
673
|
+
.createUnitOfWork("verify-post")
|
|
674
|
+
.find("posts", (b) => b.whereIndex("primary", (eb) => eb("id", "=", postIdStr)))
|
|
675
|
+
.executeRetrieve();
|
|
676
|
+
|
|
677
|
+
expect(user.name).toBe("Prisma SQLite UOW User");
|
|
678
|
+
expect(user.age).toBe(35);
|
|
679
|
+
expect(post.title).toBe("Prisma SQLite UOW Post");
|
|
680
|
+
expect(post.content).toBe("This post was created in the same transaction as the user");
|
|
681
|
+
expect(post.user_id.internalId).toBe(user.id.internalId);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it("should support complex nested joins (comments -> post -> author)", async () => {
|
|
685
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
686
|
+
|
|
687
|
+
const createAuthorUow = queryEngine.createUnitOfWork("create-author");
|
|
688
|
+
createAuthorUow.create("users", { name: "Prisma SQLite Author", age: 30 });
|
|
689
|
+
await createAuthorUow.executeMutations();
|
|
690
|
+
|
|
691
|
+
const [[author]] = await queryEngine
|
|
692
|
+
.createUnitOfWork("get-author")
|
|
693
|
+
.find("users", (b) =>
|
|
694
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Author")),
|
|
695
|
+
)
|
|
696
|
+
.executeRetrieve();
|
|
697
|
+
|
|
698
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post");
|
|
699
|
+
createPostUow.create("posts", {
|
|
700
|
+
user_id: author.id,
|
|
701
|
+
title: "Prisma SQLite Post",
|
|
702
|
+
content: "Nested join content",
|
|
703
|
+
});
|
|
704
|
+
await createPostUow.executeMutations();
|
|
705
|
+
|
|
706
|
+
const [[post]] = await queryEngine
|
|
707
|
+
.createUnitOfWork("get-post")
|
|
708
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", author.id)))
|
|
709
|
+
.executeRetrieve();
|
|
710
|
+
|
|
711
|
+
const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
|
|
712
|
+
createCommenterUow.create("users", { name: "Prisma SQLite Commenter", age: 25 });
|
|
713
|
+
await createCommenterUow.executeMutations();
|
|
714
|
+
|
|
715
|
+
const [[commenter]] = await queryEngine
|
|
716
|
+
.createUnitOfWork("get-commenter")
|
|
717
|
+
.find("users", (b) =>
|
|
718
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Commenter")),
|
|
719
|
+
)
|
|
720
|
+
.executeRetrieve();
|
|
721
|
+
|
|
722
|
+
const createCommentUow = queryEngine.createUnitOfWork("create-comment");
|
|
723
|
+
createCommentUow.create("comments", {
|
|
724
|
+
post_id: post.id,
|
|
725
|
+
user_id: commenter.id,
|
|
726
|
+
text: "Great Prisma post!",
|
|
727
|
+
});
|
|
728
|
+
await createCommentUow.executeMutations();
|
|
729
|
+
|
|
730
|
+
const uow = queryEngine.createUnitOfWork("test-complex-joins").find("comments", (b) =>
|
|
731
|
+
b.whereIndex("primary").join((jb) =>
|
|
732
|
+
jb
|
|
733
|
+
.post((postBuilder) =>
|
|
734
|
+
postBuilder
|
|
735
|
+
.select(["id", "title", "content"])
|
|
736
|
+
.orderByIndex("primary", "desc")
|
|
737
|
+
.pageSize(1)
|
|
738
|
+
.join((jb2) =>
|
|
739
|
+
jb2.author((authorBuilder) =>
|
|
740
|
+
authorBuilder.select(["id", "name", "age"]).orderByIndex("name_idx", "asc"),
|
|
741
|
+
),
|
|
742
|
+
),
|
|
743
|
+
)
|
|
744
|
+
.commenter((commenterBuilder) => commenterBuilder.select(["id", "name"])),
|
|
745
|
+
),
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
const [[comment]] = await uow.executeRetrieve();
|
|
749
|
+
|
|
750
|
+
expect(comment).toMatchObject({
|
|
751
|
+
id: expect.objectContaining({
|
|
752
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
753
|
+
internalId: expect.any(BigInt),
|
|
754
|
+
}),
|
|
755
|
+
text: "Great Prisma post!",
|
|
756
|
+
post: {
|
|
757
|
+
id: expect.objectContaining({
|
|
758
|
+
externalId: post.id.externalId,
|
|
759
|
+
}),
|
|
760
|
+
title: "Prisma SQLite Post",
|
|
761
|
+
content: "Nested join content",
|
|
762
|
+
author: {
|
|
763
|
+
id: expect.objectContaining({
|
|
764
|
+
externalId: author.id.externalId,
|
|
765
|
+
}),
|
|
766
|
+
name: "Prisma SQLite Author",
|
|
767
|
+
age: 30,
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
commenter: {
|
|
771
|
+
id: expect.objectContaining({
|
|
772
|
+
externalId: commenter.id.externalId,
|
|
773
|
+
}),
|
|
774
|
+
name: "Prisma SQLite Commenter",
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("should return created IDs from UOW create operations", async () => {
|
|
780
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
781
|
+
|
|
782
|
+
const uow1 = queryEngine.createUnitOfWork("create-multiple-users");
|
|
783
|
+
uow1.create("users", { name: "Prisma ID User 1", age: 30 });
|
|
784
|
+
uow1.create("users", { name: "Prisma ID User 2", age: 35 });
|
|
785
|
+
uow1.create("users", { name: "Prisma ID User 3", age: 40 });
|
|
786
|
+
|
|
787
|
+
const { success: success1 } = await uow1.executeMutations();
|
|
788
|
+
expect(success1).toBe(true);
|
|
789
|
+
|
|
790
|
+
const createdIds1 = uow1.getCreatedIds();
|
|
791
|
+
expect(createdIds1).toMatchObject([
|
|
792
|
+
expect.objectContaining({
|
|
793
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
794
|
+
internalId: expect.any(BigInt),
|
|
795
|
+
}),
|
|
796
|
+
expect.objectContaining({
|
|
797
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
798
|
+
internalId: expect.any(BigInt),
|
|
799
|
+
}),
|
|
800
|
+
expect.objectContaining({
|
|
801
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
802
|
+
internalId: expect.any(BigInt),
|
|
803
|
+
}),
|
|
804
|
+
]);
|
|
805
|
+
|
|
806
|
+
const externalIds = createdIds1.map((id) => id.externalId);
|
|
807
|
+
expect(new Set(externalIds).size).toBe(3);
|
|
808
|
+
|
|
809
|
+
const user1 = await queryEngine.findFirst("users", (b) =>
|
|
810
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[0].externalId)),
|
|
811
|
+
);
|
|
812
|
+
const user2 = await queryEngine.findFirst("users", (b) =>
|
|
813
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[1].externalId)),
|
|
814
|
+
);
|
|
815
|
+
const user3 = await queryEngine.findFirst("users", (b) =>
|
|
816
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[2].externalId)),
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
expect(user1).toMatchObject({
|
|
820
|
+
id: expect.objectContaining({
|
|
821
|
+
externalId: createdIds1[0].externalId,
|
|
822
|
+
}),
|
|
823
|
+
name: "Prisma ID User 1",
|
|
824
|
+
age: 30,
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
expect(user2).toMatchObject({
|
|
828
|
+
id: expect.objectContaining({
|
|
829
|
+
externalId: createdIds1[1].externalId,
|
|
830
|
+
}),
|
|
831
|
+
name: "Prisma ID User 2",
|
|
832
|
+
age: 35,
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
expect(user3).toMatchObject({
|
|
836
|
+
id: expect.objectContaining({
|
|
837
|
+
externalId: createdIds1[2].externalId,
|
|
838
|
+
}),
|
|
839
|
+
name: "Prisma ID User 3",
|
|
840
|
+
age: 40,
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
const uow2 = queryEngine.createUnitOfWork("mixed-operations");
|
|
844
|
+
uow2.create("users", { name: "Prisma New User", age: 50 });
|
|
845
|
+
uow2.update("users", createdIds1[0], (b) => b.set({ age: 31 }));
|
|
846
|
+
uow2.create("users", { name: "Prisma Another New User", age: 55 });
|
|
847
|
+
uow2.delete("users", createdIds1[2]);
|
|
848
|
+
|
|
849
|
+
const { success: success2 } = await uow2.executeMutations();
|
|
850
|
+
expect(success2).toBe(true);
|
|
851
|
+
|
|
852
|
+
const createdIds2 = uow2.getCreatedIds();
|
|
853
|
+
expect(createdIds2).toHaveLength(2);
|
|
854
|
+
expect(createdIds2[0].externalId).toBeDefined();
|
|
855
|
+
expect(createdIds2[1].externalId).toBeDefined();
|
|
856
|
+
|
|
857
|
+
const customId = "prisma-custom-user-id-12345";
|
|
858
|
+
const uow3 = queryEngine.createUnitOfWork("create-with-custom-id");
|
|
859
|
+
uow3.create("users", { id: customId, name: "Prisma Custom ID User", age: 60 });
|
|
860
|
+
|
|
861
|
+
const { success: success3 } = await uow3.executeMutations();
|
|
862
|
+
expect(success3).toBe(true);
|
|
863
|
+
|
|
864
|
+
const createdIds3 = uow3.getCreatedIds();
|
|
865
|
+
expect(createdIds3).toHaveLength(1);
|
|
866
|
+
expect(createdIds3[0].externalId).toBe(customId);
|
|
867
|
+
expect(createdIds3[0].internalId).toBeDefined();
|
|
868
|
+
|
|
869
|
+
const customIdUser = await queryEngine.findFirst("users", (b) =>
|
|
870
|
+
b.whereIndex("primary", (eb) => eb("id", "=", customId)),
|
|
871
|
+
);
|
|
872
|
+
|
|
873
|
+
expect(customIdUser).toMatchObject({
|
|
874
|
+
id: expect.objectContaining({
|
|
875
|
+
externalId: customId,
|
|
876
|
+
}),
|
|
877
|
+
name: "Prisma Custom ID User",
|
|
878
|
+
age: 60,
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
it("should handle timestamps and timezones correctly", async () => {
|
|
883
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
884
|
+
|
|
885
|
+
const createUow = queryEngine.createUnitOfWork("create-event-for-timestamp");
|
|
886
|
+
createUow.create("events", {
|
|
887
|
+
name: "Timestamp Event SQLite",
|
|
888
|
+
happened_on: new Date("2024-06-18T00:00:00.000Z"),
|
|
889
|
+
payload: { level: "info", tags: ["timestamp", "sqlite"] },
|
|
890
|
+
big_score: 42n,
|
|
891
|
+
});
|
|
892
|
+
await createUow.executeMutations();
|
|
893
|
+
|
|
894
|
+
const [[event]] = await queryEngine
|
|
895
|
+
.createUnitOfWork("get-event-for-timestamp")
|
|
896
|
+
.find("events", (b) =>
|
|
897
|
+
b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Timestamp Event SQLite")),
|
|
898
|
+
)
|
|
899
|
+
.executeRetrieve();
|
|
900
|
+
|
|
901
|
+
expect(event.created_at).toBeInstanceOf(Date);
|
|
902
|
+
|
|
903
|
+
const now = Date.now();
|
|
904
|
+
const createdTime = event.created_at.getTime();
|
|
905
|
+
expect(createdTime).toBeGreaterThan(now - 24 * 60 * 60 * 1000);
|
|
906
|
+
expect(createdTime).toBeLessThan(now + 24 * 60 * 60 * 1000);
|
|
907
|
+
|
|
908
|
+
expect(typeof event.created_at.toISOString).toBe("function");
|
|
909
|
+
expect(typeof event.created_at.getTime).toBe("function");
|
|
910
|
+
expect(typeof event.created_at.getTimezoneOffset).toBe("function");
|
|
911
|
+
|
|
912
|
+
const isoString = event.created_at.toISOString();
|
|
913
|
+
expect(new Date(isoString).getTime()).toBe(event.created_at.getTime());
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it("should store Date values as UTC ISO strings for sqlite prisma storage", async () => {
|
|
917
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
918
|
+
const happenedOn = new Date("2024-06-18T12:34:56.789Z");
|
|
919
|
+
|
|
920
|
+
const createUow = queryEngine.createUnitOfWork("create-iso-event");
|
|
921
|
+
createUow.create("events", {
|
|
922
|
+
name: "ISO Stored Event",
|
|
923
|
+
happened_on: happenedOn,
|
|
924
|
+
payload: { level: "info", tags: ["sqlite", "iso"] },
|
|
925
|
+
big_score: 7n,
|
|
926
|
+
});
|
|
927
|
+
await createUow.executeMutations();
|
|
928
|
+
|
|
929
|
+
const tableName = adapter.namingStrategy.tableName("events", "namespace");
|
|
930
|
+
const row = sqliteDatabase
|
|
931
|
+
.prepare(`SELECT happened_on FROM ${tableName} WHERE name = ?`)
|
|
932
|
+
.get("ISO Stored Event") as { happened_on?: string } | undefined;
|
|
933
|
+
|
|
934
|
+
expect(row?.happened_on).toBe(happenedOn.toISOString());
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it("should support handlerTx with retry logic", async () => {
|
|
938
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
939
|
+
|
|
940
|
+
const createUow = queryEngine.createUnitOfWork("create-user-for-execute-uow");
|
|
941
|
+
createUow.create("users", { name: "Prisma Execute UOW User", age: 42 });
|
|
942
|
+
await createUow.executeMutations();
|
|
943
|
+
|
|
944
|
+
const [[user]] = await queryEngine
|
|
945
|
+
.createUnitOfWork("get-user-for-execute-uow")
|
|
946
|
+
.find("users", (b) =>
|
|
947
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma Execute UOW User")),
|
|
948
|
+
)
|
|
949
|
+
.executeRetrieve();
|
|
950
|
+
|
|
951
|
+
let currentUow: ReturnType<typeof queryEngine.createUnitOfWork> | null = null;
|
|
952
|
+
|
|
953
|
+
const getUserById = (userId: typeof user.id) => {
|
|
954
|
+
return createServiceTxBuilder(testSchema, currentUow!)
|
|
955
|
+
.retrieve((uow) =>
|
|
956
|
+
uow.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userId))),
|
|
957
|
+
)
|
|
958
|
+
.transformRetrieve(([users]) => users[0] ?? null)
|
|
959
|
+
.build();
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
const result = await createHandlerTxBuilder({
|
|
963
|
+
createUnitOfWork: () => {
|
|
964
|
+
currentUow = queryEngine.createUnitOfWork("execute-uow-update");
|
|
965
|
+
return currentUow;
|
|
966
|
+
},
|
|
967
|
+
retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3, initialDelayMs: 1 }),
|
|
968
|
+
})
|
|
969
|
+
.withServiceCalls(() => [getUserById(user.id)])
|
|
970
|
+
.mutate(({ forSchema, serviceIntermediateResult: [foundUser] }) => {
|
|
971
|
+
if (!foundUser) {
|
|
972
|
+
throw new Error("User not found");
|
|
973
|
+
}
|
|
974
|
+
const newAge = foundUser.age! + 1;
|
|
975
|
+
forSchema(testSchema).update("users", foundUser.id, (b) => b.set({ age: newAge }).check());
|
|
976
|
+
return { previousAge: foundUser.age, newAge };
|
|
977
|
+
})
|
|
978
|
+
.transform(({ mutateResult }) => {
|
|
979
|
+
expect(mutateResult.newAge).toBe(mutateResult.previousAge! + 1);
|
|
980
|
+
return mutateResult;
|
|
981
|
+
})
|
|
982
|
+
.execute();
|
|
983
|
+
|
|
984
|
+
expect(result).toEqual({
|
|
985
|
+
previousAge: 42,
|
|
986
|
+
newAge: 43,
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
const updatedUser = await queryEngine.findFirst("users", (b) =>
|
|
990
|
+
b.whereIndex("primary", (eb) => eb("id", "=", user.id)),
|
|
991
|
+
);
|
|
992
|
+
|
|
993
|
+
expect(updatedUser).toMatchObject({
|
|
994
|
+
id: expect.objectContaining({
|
|
995
|
+
externalId: user.id.externalId,
|
|
996
|
+
version: 1,
|
|
997
|
+
}),
|
|
998
|
+
name: "Prisma Execute UOW User",
|
|
999
|
+
age: 43,
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
it("should fail check() when version changes", async () => {
|
|
1004
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
1005
|
+
|
|
1006
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-version-conflict");
|
|
1007
|
+
createUserUow.create("users", {
|
|
1008
|
+
name: "Prisma Version Conflict User",
|
|
1009
|
+
age: 40,
|
|
1010
|
+
});
|
|
1011
|
+
await createUserUow.executeMutations();
|
|
1012
|
+
|
|
1013
|
+
const [[user]] = await queryEngine
|
|
1014
|
+
.createUnitOfWork("get-user-for-version-conflict")
|
|
1015
|
+
.find("users", (b) =>
|
|
1016
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma Version Conflict User")),
|
|
1017
|
+
)
|
|
1018
|
+
.executeRetrieve();
|
|
1019
|
+
|
|
1020
|
+
const updateUow = queryEngine.createUnitOfWork("update-user-version");
|
|
1021
|
+
updateUow.update("users", user.id, (b) => b.set({ age: 41 }));
|
|
1022
|
+
await updateUow.executeMutations();
|
|
1023
|
+
|
|
1024
|
+
const uow = queryEngine.createUnitOfWork("check-stale-version");
|
|
1025
|
+
uow.check("users", user.id);
|
|
1026
|
+
uow.create("posts", {
|
|
1027
|
+
user_id: user.id,
|
|
1028
|
+
title: "Prisma Should Not Be Created",
|
|
1029
|
+
content: "Content",
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
const { success } = await uow.executeMutations();
|
|
1033
|
+
expect(success).toBe(false);
|
|
1034
|
+
|
|
1035
|
+
const [posts] = await queryEngine
|
|
1036
|
+
.createUnitOfWork("get-posts-for-version-conflict")
|
|
1037
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
|
|
1038
|
+
.executeRetrieve();
|
|
1039
|
+
|
|
1040
|
+
const conflictPosts = posts.filter((p) => p.title === "Prisma Should Not Be Created");
|
|
1041
|
+
expect(conflictPosts).toHaveLength(0);
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it("should roundtrip Prisma SQLite DateTime, Date, JSON, and BigInt", async () => {
|
|
1045
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
1046
|
+
const beforeCreate = Date.now();
|
|
1047
|
+
const happenedOn = new Date("2024-06-15T00:00:00.000Z");
|
|
1048
|
+
const payload = { level: "info", tags: ["launch", "sqlite"] };
|
|
1049
|
+
const bigScore = 1234567890123n;
|
|
1050
|
+
|
|
1051
|
+
const createUow = queryEngine.createUnitOfWork("create-event");
|
|
1052
|
+
createUow.create("events", {
|
|
1053
|
+
name: "Launch",
|
|
1054
|
+
happened_on: happenedOn,
|
|
1055
|
+
payload,
|
|
1056
|
+
big_score: bigScore,
|
|
1057
|
+
});
|
|
1058
|
+
await createUow.executeMutations();
|
|
1059
|
+
|
|
1060
|
+
const [[event]] = await queryEngine
|
|
1061
|
+
.createUnitOfWork("get-event")
|
|
1062
|
+
.find("events", (b) => b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Launch")))
|
|
1063
|
+
.executeRetrieve();
|
|
1064
|
+
|
|
1065
|
+
expect(event).toBeDefined();
|
|
1066
|
+
expect(event.name).toBe("Launch");
|
|
1067
|
+
expect(event.payload).toEqual(payload);
|
|
1068
|
+
expect(event.big_score).toBe(bigScore);
|
|
1069
|
+
expect(event.happened_on).toBeInstanceOf(Date);
|
|
1070
|
+
expect(event.happened_on.toISOString()).toBe(happenedOn.toISOString());
|
|
1071
|
+
expect(event.created_at).toBeInstanceOf(Date);
|
|
1072
|
+
|
|
1073
|
+
const createdAtMs = event.created_at.getTime();
|
|
1074
|
+
const afterFetch = Date.now();
|
|
1075
|
+
expect(createdAtMs).toBeGreaterThanOrEqual(beforeCreate - 5 * 60 * 1000);
|
|
1076
|
+
expect(createdAtMs).toBeLessThanOrEqual(afterFetch + 5 * 60 * 1000);
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
it("should parse CURRENT_TIMESTAMP strings as UTC", async () => {
|
|
1080
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
1081
|
+
const tableName = adapter.namingStrategy.tableName("events", "namespace");
|
|
1082
|
+
|
|
1083
|
+
const createUow = queryEngine.createUnitOfWork("create-utc-event");
|
|
1084
|
+
createUow.create("events", {
|
|
1085
|
+
name: "UTC Timestamp",
|
|
1086
|
+
happened_on: new Date("2024-06-15T00:00:00.000Z"),
|
|
1087
|
+
payload: { level: "info", tags: ["sqlite", "utc"] },
|
|
1088
|
+
big_score: 42n,
|
|
1089
|
+
});
|
|
1090
|
+
await createUow.executeMutations();
|
|
1091
|
+
|
|
1092
|
+
sqliteDatabase
|
|
1093
|
+
.prepare(`UPDATE ${tableName} SET created_at = ? WHERE name = ?`)
|
|
1094
|
+
.run("2024-06-15 14:30:00", "UTC Timestamp");
|
|
1095
|
+
|
|
1096
|
+
const [[event]] = await queryEngine
|
|
1097
|
+
.createUnitOfWork("get-utc-event")
|
|
1098
|
+
.find("events", (b) =>
|
|
1099
|
+
b.whereIndex("events_name_idx", (eb) => eb("name", "=", "UTC Timestamp")),
|
|
1100
|
+
)
|
|
1101
|
+
.executeRetrieve();
|
|
1102
|
+
|
|
1103
|
+
expect(event.created_at).toBeInstanceOf(Date);
|
|
1104
|
+
expect(event.created_at.toISOString()).toBe("2024-06-15T14:30:00.000Z");
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
it("should roundtrip BigInt when sqlite returns bigint values", async () => {
|
|
1108
|
+
sqliteDatabase.defaultSafeIntegers(true);
|
|
1109
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
1110
|
+
const safeIntegerLimit = BigInt(Number.MAX_SAFE_INTEGER);
|
|
1111
|
+
const bigScore = safeIntegerLimit + 42n;
|
|
1112
|
+
|
|
1113
|
+
try {
|
|
1114
|
+
const createUow = queryEngine.createUnitOfWork("create-safe-bigint-event");
|
|
1115
|
+
createUow.create("events", {
|
|
1116
|
+
name: "Safe BigInt",
|
|
1117
|
+
happened_on: new Date("2024-06-17T00:00:00.000Z"),
|
|
1118
|
+
payload: { level: "info", tags: ["sqlite", "safe-bigint"] },
|
|
1119
|
+
big_score: bigScore,
|
|
1120
|
+
});
|
|
1121
|
+
await createUow.executeMutations();
|
|
1122
|
+
|
|
1123
|
+
const [[event]] = await queryEngine
|
|
1124
|
+
.createUnitOfWork("get-safe-bigint-event")
|
|
1125
|
+
.find("events", (b) =>
|
|
1126
|
+
b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Safe BigInt")),
|
|
1127
|
+
)
|
|
1128
|
+
.executeRetrieve();
|
|
1129
|
+
|
|
1130
|
+
expect(event.big_score).toBe(bigScore);
|
|
1131
|
+
} finally {
|
|
1132
|
+
sqliteDatabase.defaultSafeIntegers(false);
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
it("should throw when sqlite returns unsafe BigInt numbers", async () => {
|
|
1137
|
+
sqliteDatabase.defaultSafeIntegers(false);
|
|
1138
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
1139
|
+
const unsafeBigScore = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
|
|
1140
|
+
|
|
1141
|
+
try {
|
|
1142
|
+
const createUow = queryEngine.createUnitOfWork("create-unsafe-event");
|
|
1143
|
+
createUow.create("events", {
|
|
1144
|
+
name: "Unsafe BigInt",
|
|
1145
|
+
happened_on: new Date("2024-06-16T00:00:00.000Z"),
|
|
1146
|
+
payload: { level: "warn", tags: ["sqlite", "unsafe-bigint"] },
|
|
1147
|
+
big_score: unsafeBigScore,
|
|
1148
|
+
});
|
|
1149
|
+
await createUow.executeMutations();
|
|
1150
|
+
|
|
1151
|
+
await expect(
|
|
1152
|
+
queryEngine
|
|
1153
|
+
.createUnitOfWork("get-unsafe-event")
|
|
1154
|
+
.find("events", (b) =>
|
|
1155
|
+
b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Unsafe BigInt")),
|
|
1156
|
+
)
|
|
1157
|
+
.executeRetrieve(),
|
|
1158
|
+
).rejects.toThrow(/Number\.MAX_SAFE_INTEGER/);
|
|
1159
|
+
} finally {
|
|
1160
|
+
sqliteDatabase.defaultSafeIntegers(false);
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
});
|