@fragno-dev/db 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +242 -139
- package/CHANGELOG.md +47 -0
- package/README.md +123 -8
- package/dist/adapters/adapters.d.ts +19 -5
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +6 -19
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +7 -47
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/generate.d.ts +7 -1
- package/dist/adapters/drizzle/generate.d.ts.map +1 -1
- package/dist/adapters/drizzle/generate.js +46 -45
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/generic-sql/driver-config.d.ts +74 -0
- package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -0
- package/dist/adapters/generic-sql/driver-config.js +94 -0
- package/dist/adapters/generic-sql/driver-config.js.map +1 -0
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +43 -0
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -0
- package/dist/adapters/generic-sql/generic-sql-adapter.js +87 -0
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -0
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +67 -0
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -0
- package/dist/adapters/generic-sql/migration/cold-kysely.js +33 -0
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -0
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +60 -0
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -0
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +59 -0
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -0
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +96 -0
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -0
- package/dist/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/adapters/generic-sql/migration/executor.js +18 -0
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -0
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +68 -0
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -0
- package/dist/adapters/generic-sql/migration/sql-generator.js +212 -0
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -0
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +32 -0
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -0
- package/dist/adapters/generic-sql/query/cursor-utils.js +37 -0
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -0
- package/dist/adapters/generic-sql/query/dialect/mysql.js +33 -0
- package/dist/adapters/generic-sql/query/dialect/mysql.js.map +1 -0
- package/dist/adapters/generic-sql/query/dialect/postgres.js +32 -0
- package/dist/adapters/generic-sql/query/dialect/postgres.js.map +1 -0
- package/dist/adapters/generic-sql/query/dialect/sqlite.js +32 -0
- package/dist/adapters/generic-sql/query/dialect/sqlite.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +152 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -0
- package/dist/adapters/generic-sql/query/select-builder.js +69 -0
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -0
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +145 -0
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -0
- package/dist/adapters/generic-sql/query/where-builder.js +129 -0
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -0
- package/dist/adapters/generic-sql/result-interpreter.js +74 -0
- package/dist/adapters/generic-sql/result-interpreter.js.map +1 -0
- package/dist/adapters/generic-sql/uow-decoder.js +105 -0
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -0
- package/dist/adapters/generic-sql/uow-encoder.js +93 -0
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +5 -16
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +6 -159
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/{drizzle/drizzle-query.js → shared/from-unit-of-work-compiler.js} +48 -62
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -0
- package/dist/adapters/{kysely/kysely-shared.d.ts → shared/table-name-mapper.d.ts} +3 -2
- package/dist/adapters/shared/table-name-mapper.d.ts.map +1 -0
- package/dist/adapters/shared/table-name-mapper.js +43 -0
- package/dist/adapters/shared/table-name-mapper.js.map +1 -0
- package/dist/adapters/shared/uow-operation-compiler.js +105 -0
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -0
- package/dist/db-fragment-definition-builder.d.ts +186 -0
- package/dist/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/db-fragment-definition-builder.js +207 -0
- package/dist/db-fragment-definition-builder.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +53 -0
- package/dist/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.js +111 -0
- package/dist/fragments/internal-fragment.js.map +1 -0
- package/dist/hooks/hooks.d.ts +51 -0
- package/dist/hooks/hooks.d.ts.map +1 -0
- package/dist/hooks/hooks.js +88 -0
- package/dist/hooks/hooks.js.map +1 -0
- package/dist/migration-engine/generation-engine.d.ts +0 -2
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +38 -56
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/mod.d.ts +35 -23
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +48 -45
- package/dist/mod.js.map +1 -1
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js +165 -0
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/packages/fragno/dist/api/bind-services.js +20 -0
- package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
- package/dist/packages/fragno/dist/api/error.js +48 -0
- package/dist/packages/fragno/dist/api/error.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +525 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
- package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/route.js +10 -0
- package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
- package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
- package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/route.js +17 -0
- package/dist/packages/fragno/dist/api/route.js.map +1 -0
- package/dist/packages/fragno/dist/internal/symbols.js +10 -0
- package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
- package/dist/query/column-defaults.js +27 -0
- package/dist/query/column-defaults.js.map +1 -0
- package/dist/query/cursor.d.ts +14 -6
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +16 -7
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/orm/orm.d.ts +1 -1
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +30 -0
- package/dist/query/serialize/create-sql-serializer.js.map +1 -0
- package/dist/query/serialize/dialect/mysql-serializer.js +87 -0
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -0
- package/dist/query/serialize/dialect/postgres-serializer.js +80 -0
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -0
- package/dist/query/serialize/dialect/sqlite-serializer.js +93 -0
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -0
- package/dist/query/serialize/sql-serializer.js +67 -0
- package/dist/query/serialize/sql-serializer.js.map +1 -0
- package/dist/query/{query.d.ts → simple-query-interface.d.ts} +6 -6
- package/dist/query/simple-query-interface.d.ts.map +1 -0
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +133 -0
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/query/unit-of-work/execute-unit-of-work.js +197 -0
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/query/unit-of-work/retry-policy.d.ts +88 -0
- package/dist/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/query/unit-of-work/retry-policy.js +61 -0
- package/dist/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/query/{unit-of-work.d.ts → unit-of-work/unit-of-work.d.ts} +145 -58
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/query/{unit-of-work.js → unit-of-work/unit-of-work.js} +435 -198
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/query/value-decoding.js +71 -0
- package/dist/query/value-decoding.js.map +1 -0
- package/dist/query/value-encoding.js +124 -0
- package/dist/query/value-encoding.js.map +1 -0
- package/dist/schema/create.d.ts +3 -0
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +4 -0
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +29 -0
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -0
- package/dist/schema/type-conversion/dialect/mysql.js +57 -0
- package/dist/schema/type-conversion/dialect/mysql.js.map +1 -0
- package/dist/schema/type-conversion/dialect/postgres.js +56 -0
- package/dist/schema/type-conversion/dialect/postgres.js.map +1 -0
- package/dist/schema/type-conversion/dialect/sqlite.js +52 -0
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -0
- package/dist/schema/type-conversion/type-mapping.js +63 -0
- package/dist/schema/type-conversion/type-mapping.js.map +1 -0
- package/dist/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/sql-driver/connection/connection-provider.js +19 -0
- package/dist/sql-driver/connection/connection-provider.js.map +1 -0
- package/dist/sql-driver/connection/single-connection-provider.js +23 -0
- package/dist/sql-driver/connection/single-connection-provider.js.map +1 -0
- package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/sql-driver/dialects/dialects.d.ts +2 -0
- package/dist/sql-driver/dialects/dialects.js +3 -0
- package/dist/sql-driver/dialects/durable-object-dialect.d.ts +72 -0
- package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -0
- package/dist/sql-driver/dialects/durable-object-dialect.js +130 -0
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -0
- package/dist/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/sql-driver/driver/runtime-driver.js +56 -0
- package/dist/sql-driver/driver/runtime-driver.js.map +1 -0
- package/dist/sql-driver/query-executor/default-query-executor.js +26 -0
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -0
- package/dist/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/sql-driver/query-executor/query-executor-base.js +25 -0
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -0
- package/dist/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/sql-driver/sql-driver-adapter.js +68 -0
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -0
- package/dist/sql-driver/sql-driver.d.ts +38 -0
- package/dist/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/sql-driver/sql-driver.js +1 -0
- package/dist/sql-driver/sql.js +50 -0
- package/dist/sql-driver/sql.js.map +1 -0
- package/dist/with-database.d.ts +32 -0
- package/dist/with-database.d.ts.map +1 -0
- package/dist/with-database.js +34 -0
- package/dist/with-database.js.map +1 -0
- package/package.json +43 -9
- package/src/adapters/adapters.ts +23 -4
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +140 -185
- package/src/adapters/drizzle/{drizzle-adapter-sqlite.test.ts → drizzle-adapter-sqlite3.test.ts} +187 -55
- package/src/adapters/drizzle/drizzle-adapter.ts +14 -93
- package/src/adapters/drizzle/generate.test.ts +102 -269
- package/src/adapters/drizzle/generate.ts +89 -63
- package/src/adapters/drizzle/migrate-drizzle.test.ts +19 -0
- package/src/adapters/drizzle/shared.ts +0 -34
- package/src/adapters/drizzle/test-utils.ts +36 -5
- package/src/adapters/generic-sql/README.md +14 -0
- package/src/adapters/generic-sql/driver-config.ts +144 -0
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +50 -0
- package/src/adapters/generic-sql/generic-sql-adapter.ts +146 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +130 -0
- package/src/adapters/generic-sql/migration/cold-kysely.ts +55 -0
- package/src/adapters/{kysely/migration/execute-mysql.test.ts → generic-sql/migration/dialect/mysql.test.ts} +342 -484
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +104 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +1008 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +113 -0
- package/src/adapters/{kysely/migration/execute-sqlite.test.ts → generic-sql/migration/dialect/sqlite.test.ts} +307 -510
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +189 -0
- package/src/adapters/generic-sql/migration/executor.ts +33 -0
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +661 -0
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +214 -0
- package/src/adapters/generic-sql/migration/sql-generator.ts +413 -0
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +36 -0
- package/src/adapters/generic-sql/query/cursor-utils.ts +56 -0
- package/src/adapters/generic-sql/query/dialect/mysql.ts +34 -0
- package/src/adapters/generic-sql/query/dialect/postgres.ts +32 -0
- package/src/adapters/generic-sql/query/dialect/sqlite.ts +32 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +1568 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +314 -0
- package/src/adapters/generic-sql/query/select-builder.test.ts +256 -0
- package/src/adapters/generic-sql/query/select-builder.ts +137 -0
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +195 -0
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +367 -0
- package/src/adapters/generic-sql/query/where-builder.test.ts +744 -0
- package/src/adapters/generic-sql/query/where-builder.ts +211 -0
- package/src/adapters/generic-sql/result-interpreter.ts +102 -0
- package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +899 -0
- package/src/adapters/generic-sql/uow-decoder.test.ts +399 -0
- package/src/adapters/generic-sql/uow-decoder.ts +152 -0
- package/src/adapters/generic-sql/uow-encoder.test.ts +183 -0
- package/src/adapters/generic-sql/uow-encoder.ts +131 -0
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +90 -96
- package/src/adapters/kysely/kysely-adapter-sqlocal.test.ts +215 -0
- package/src/adapters/kysely/kysely-adapter.ts +10 -242
- package/src/adapters/{drizzle/drizzle-query.ts → shared/from-unit-of-work-compiler.ts} +111 -106
- package/src/adapters/shared/table-name-mapper.ts +50 -0
- package/src/adapters/shared/uow-operation-compiler.ts +211 -0
- package/src/db-fragment-definition-builder.test.ts +887 -0
- package/src/db-fragment-definition-builder.ts +737 -0
- package/src/db-fragment-instantiator.test.ts +543 -0
- package/src/db-fragment-integration.test.ts +406 -0
- package/src/fragments/internal-fragment.test.ts +549 -0
- package/src/fragments/internal-fragment.ts +249 -0
- package/src/hooks/hooks.test.ts +575 -0
- package/src/hooks/hooks.ts +179 -0
- package/src/migration-engine/generation-engine.test.ts +60 -27
- package/src/migration-engine/generation-engine.ts +99 -92
- package/src/mod.ts +139 -78
- package/src/query/column-defaults.ts +49 -0
- package/src/query/cursor.test.ts +147 -3
- package/src/query/cursor.ts +25 -8
- package/src/query/orm/orm.ts +1 -1
- package/src/query/query-type.test.ts +9 -9
- package/src/query/serialize/create-sql-serializer.ts +34 -0
- package/src/query/serialize/dialect/mysql-serializer.ts +142 -0
- package/src/query/serialize/dialect/postgres-serializer.ts +129 -0
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +251 -0
- package/src/query/serialize/dialect/sqlite-serializer.ts +156 -0
- package/src/query/serialize/sql-serializer.ts +143 -0
- package/src/query/{query.ts → simple-query-interface.ts} +4 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +1310 -0
- package/src/query/unit-of-work/execute-unit-of-work.ts +504 -0
- package/src/query/unit-of-work/retry-policy.test.ts +217 -0
- package/src/query/unit-of-work/retry-policy.ts +141 -0
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +831 -0
- package/src/query/{unit-of-work-types.test.ts → unit-of-work/unit-of-work-types.test.ts} +7 -5
- package/src/query/unit-of-work/unit-of-work.test.ts +1716 -0
- package/src/query/{unit-of-work.ts → unit-of-work/unit-of-work.ts} +716 -420
- package/src/query/{result-transform.test.ts → value-decoding.test.ts} +45 -298
- package/src/query/value-decoding.ts +113 -0
- package/src/query/value-encoding.test.ts +390 -0
- package/src/query/value-encoding.ts +168 -0
- package/src/schema/create.test.ts +5 -1
- package/src/schema/create.ts +5 -0
- package/src/schema/serialize.test.ts +165 -407
- package/src/schema/type-conversion/create-sql-type-mapper.ts +28 -0
- package/src/schema/type-conversion/dialect/mysql.ts +64 -0
- package/src/schema/type-conversion/dialect/postgres.ts +62 -0
- package/src/schema/type-conversion/dialect/sqlite.ts +63 -0
- package/src/schema/type-conversion/type-mapping.test.ts +137 -0
- package/src/schema/type-conversion/type-mapping.ts +153 -0
- package/src/shared/connection-pool.ts +5 -5
- package/src/sql-driver/better-sqlite3.test.ts +126 -0
- package/src/sql-driver/connection/connection-provider.ts +27 -0
- package/src/sql-driver/connection/single-connection-provider.ts +42 -0
- package/src/sql-driver/dialect-adapter/dialect-adapter.ts +9 -0
- package/src/sql-driver/dialect-adapter/sqlite-dialect-adapter.ts +7 -0
- package/src/sql-driver/dialects/dialects.ts +1 -0
- package/src/sql-driver/dialects/durable-object-dialect.ts +260 -0
- package/src/sql-driver/driver/runtime-driver.ts +91 -0
- package/src/sql-driver/query-executor/default-query-executor.ts +38 -0
- package/src/sql-driver/query-executor/plugin.ts +22 -0
- package/src/sql-driver/query-executor/query-executor-base.ts +53 -0
- package/src/sql-driver/query-executor/query-executor.ts +44 -0
- package/src/sql-driver/sql-driver-adapter.ts +96 -0
- package/src/sql-driver/sql-driver.ts +53 -0
- package/src/sql-driver/sql.ts +57 -0
- package/src/sql-driver/sqlocal.test.ts +117 -0
- package/src/with-database.ts +152 -0
- package/tsdown.config.ts +8 -2
- package/dist/adapters/drizzle/drizzle-connection-pool.js +0 -40
- package/dist/adapters/drizzle/drizzle-connection-pool.js.map +0 -1
- package/dist/adapters/drizzle/drizzle-query.d.ts +0 -23
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +0 -1
- package/dist/adapters/drizzle/drizzle-query.js.map +0 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -10
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +0 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +0 -315
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +0 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +0 -116
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +0 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js +0 -149
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +0 -1
- package/dist/adapters/drizzle/join-column-utils.js +0 -28
- package/dist/adapters/drizzle/join-column-utils.js.map +0 -1
- package/dist/adapters/drizzle/shared.d.ts +0 -14
- package/dist/adapters/drizzle/shared.d.ts.map +0 -1
- package/dist/adapters/drizzle/shared.js +0 -35
- package/dist/adapters/drizzle/shared.js.map +0 -1
- package/dist/adapters/kysely/kysely-connection-pool.js +0 -41
- package/dist/adapters/kysely/kysely-connection-pool.js.map +0 -1
- package/dist/adapters/kysely/kysely-query-builder.js +0 -321
- package/dist/adapters/kysely/kysely-query-builder.js.map +0 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +0 -66
- package/dist/adapters/kysely/kysely-query-compiler.js.map +0 -1
- package/dist/adapters/kysely/kysely-query.d.ts +0 -22
- package/dist/adapters/kysely/kysely-query.d.ts.map +0 -1
- package/dist/adapters/kysely/kysely-query.js +0 -223
- package/dist/adapters/kysely/kysely-query.js.map +0 -1
- package/dist/adapters/kysely/kysely-shared.d.ts.map +0 -1
- package/dist/adapters/kysely/kysely-shared.js +0 -18
- package/dist/adapters/kysely/kysely-shared.js.map +0 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js +0 -170
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +0 -1
- package/dist/adapters/kysely/kysely-uow-executor.js +0 -89
- package/dist/adapters/kysely/kysely-uow-executor.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-base.js +0 -128
- package/dist/adapters/kysely/migration/execute-base.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-factory.js +0 -34
- package/dist/adapters/kysely/migration/execute-factory.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-mssql.js +0 -112
- package/dist/adapters/kysely/migration/execute-mssql.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-mysql.js +0 -93
- package/dist/adapters/kysely/migration/execute-mysql.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-postgres.js +0 -104
- package/dist/adapters/kysely/migration/execute-postgres.js.map +0 -1
- package/dist/adapters/kysely/migration/execute-sqlite.js +0 -123
- package/dist/adapters/kysely/migration/execute-sqlite.js.map +0 -1
- package/dist/adapters/kysely/migration/execute.js +0 -34
- package/dist/adapters/kysely/migration/execute.js.map +0 -1
- package/dist/bind-services.d.ts +0 -7
- package/dist/bind-services.d.ts.map +0 -1
- package/dist/bind-services.js +0 -14
- package/dist/bind-services.js.map +0 -1
- package/dist/fragment.d.ts +0 -173
- package/dist/fragment.d.ts.map +0 -1
- package/dist/fragment.js +0 -191
- package/dist/fragment.js.map +0 -1
- package/dist/migration-engine/create.d.ts +0 -37
- package/dist/migration-engine/create.d.ts.map +0 -1
- package/dist/migration-engine/create.js +0 -58
- package/dist/migration-engine/create.js.map +0 -1
- package/dist/migration-engine/shared.d.ts +0 -112
- package/dist/migration-engine/shared.d.ts.map +0 -1
- package/dist/query/query.d.ts.map +0 -1
- package/dist/query/result-transform.js +0 -168
- package/dist/query/result-transform.js.map +0 -1
- package/dist/query/unit-of-work.d.ts.map +0 -1
- package/dist/query/unit-of-work.js.map +0 -1
- package/dist/schema/serialize.js +0 -106
- package/dist/schema/serialize.js.map +0 -1
- package/dist/shared/settings-schema.js +0 -36
- package/dist/shared/settings-schema.js.map +0 -1
- package/src/adapters/drizzle/drizzle-adapter.test.ts +0 -170
- package/src/adapters/drizzle/drizzle-connection-pool.ts +0 -66
- package/src/adapters/drizzle/drizzle-query.test.ts +0 -499
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +0 -1383
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +0 -636
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +0 -218
- package/src/adapters/drizzle/drizzle-uow-executor.ts +0 -276
- package/src/adapters/drizzle/join-column-utils.test.ts +0 -79
- package/src/adapters/drizzle/join-column-utils.ts +0 -39
- package/src/adapters/kysely/kysely-connection-pool.ts +0 -70
- package/src/adapters/kysely/kysely-query-builder.test.ts +0 -1344
- package/src/adapters/kysely/kysely-query-builder.ts +0 -666
- package/src/adapters/kysely/kysely-query-compiler.ts +0 -132
- package/src/adapters/kysely/kysely-query.test.ts +0 -498
- package/src/adapters/kysely/kysely-query.ts +0 -390
- package/src/adapters/kysely/kysely-shared.ts +0 -23
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +0 -998
- package/src/adapters/kysely/kysely-uow-compiler.ts +0 -318
- package/src/adapters/kysely/kysely-uow-executor.ts +0 -145
- package/src/adapters/kysely/kysely-uow-joins.test.ts +0 -811
- package/src/adapters/kysely/migration/execute-base.ts +0 -256
- package/src/adapters/kysely/migration/execute-factory.ts +0 -53
- package/src/adapters/kysely/migration/execute-mssql.ts +0 -250
- package/src/adapters/kysely/migration/execute-mysql.ts +0 -211
- package/src/adapters/kysely/migration/execute-postgres.test.ts +0 -2657
- package/src/adapters/kysely/migration/execute-postgres.ts +0 -234
- package/src/adapters/kysely/migration/execute-sqlite.ts +0 -247
- package/src/adapters/kysely/migration/execute.ts +0 -50
- package/src/adapters/kysely/migration/kysely-migrator.test.ts +0 -261
- package/src/bind-services.test.ts +0 -214
- package/src/bind-services.ts +0 -37
- package/src/db-fragment.test.ts +0 -800
- package/src/fragment.ts +0 -727
- package/src/query/result-transform.ts +0 -271
- package/src/query/unit-of-work-multi-schema.test.ts +0 -64
- package/src/query/unit-of-work.test.ts +0 -943
- package/src/schema/serialize.ts +0 -396
- package/src/shared/settings-schema.ts +0 -61
- package/src/uow-context-integration.test.ts +0 -102
- package/src/uow-context.test.ts +0 -182
- /package/dist/query/{query.js → simple-query-interface.js} +0 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
import SQLite from "better-sqlite3";
|
|
2
|
+
import { beforeAll, describe, expect, expectTypeOf, it, assert } from "vitest";
|
|
3
|
+
import { column, idColumn, referenceColumn, schema, type FragnoId } from "../../../schema/create";
|
|
4
|
+
import {
|
|
5
|
+
Cursor,
|
|
6
|
+
executeUnitOfWork,
|
|
7
|
+
ExponentialBackoffRetryPolicy,
|
|
8
|
+
type DatabaseAdapter,
|
|
9
|
+
} from "../../../mod";
|
|
10
|
+
import { SqliteDialect } from "kysely";
|
|
11
|
+
import { SqlDriverAdapter } from "../../../sql-driver/sql-driver-adapter";
|
|
12
|
+
import { BetterSQLite3DriverConfig } from "../driver-config";
|
|
13
|
+
import { GenericSQLAdapter } from "../generic-sql-adapter";
|
|
14
|
+
import { internalSchema } from "../../../fragments/internal-fragment";
|
|
15
|
+
|
|
16
|
+
describe("GenericSQLAdapter with DrizzleAdapter better-sqlite3", () => {
|
|
17
|
+
const testSchema = schema((s) => {
|
|
18
|
+
return s
|
|
19
|
+
.addTable("users", (t) => {
|
|
20
|
+
return t
|
|
21
|
+
.addColumn("id", idColumn())
|
|
22
|
+
.addColumn("name", column("string"))
|
|
23
|
+
.addColumn("age", column("integer").nullable())
|
|
24
|
+
.createIndex("name_idx", ["name"]);
|
|
25
|
+
})
|
|
26
|
+
.addTable("emails", (t) => {
|
|
27
|
+
return t
|
|
28
|
+
.addColumn("id", idColumn())
|
|
29
|
+
.addColumn("user_id", referenceColumn())
|
|
30
|
+
.addColumn("email", column("string"))
|
|
31
|
+
.addColumn("is_primary", column("bool").defaultTo(false))
|
|
32
|
+
.createIndex("unique_email", ["email"], { unique: true })
|
|
33
|
+
.createIndex("user_emails", ["user_id"]);
|
|
34
|
+
})
|
|
35
|
+
.addTable("posts", (t) => {
|
|
36
|
+
return t
|
|
37
|
+
.addColumn("id", idColumn())
|
|
38
|
+
.addColumn("user_id", referenceColumn())
|
|
39
|
+
.addColumn("title", column("string"))
|
|
40
|
+
.addColumn("content", column("string"))
|
|
41
|
+
.createIndex("posts_user_idx", ["user_id"]);
|
|
42
|
+
})
|
|
43
|
+
.addTable("comments", (t) => {
|
|
44
|
+
return t
|
|
45
|
+
.addColumn("id", idColumn())
|
|
46
|
+
.addColumn("post_id", referenceColumn())
|
|
47
|
+
.addColumn("user_id", referenceColumn())
|
|
48
|
+
.addColumn("text", column("string"))
|
|
49
|
+
.createIndex("comments_post_idx", ["post_id"])
|
|
50
|
+
.createIndex("comments_user_idx", ["user_id"]);
|
|
51
|
+
})
|
|
52
|
+
.addReference("user", {
|
|
53
|
+
type: "one",
|
|
54
|
+
from: { table: "emails", column: "user_id" },
|
|
55
|
+
to: { table: "users", column: "id" },
|
|
56
|
+
})
|
|
57
|
+
.addReference("author", {
|
|
58
|
+
type: "one",
|
|
59
|
+
from: { table: "posts", column: "user_id" },
|
|
60
|
+
to: { table: "users", column: "id" },
|
|
61
|
+
})
|
|
62
|
+
.addReference("post", {
|
|
63
|
+
type: "one",
|
|
64
|
+
from: { table: "comments", column: "post_id" },
|
|
65
|
+
to: { table: "posts", column: "id" },
|
|
66
|
+
})
|
|
67
|
+
.addReference("commenter", {
|
|
68
|
+
type: "one",
|
|
69
|
+
from: { table: "comments", column: "user_id" },
|
|
70
|
+
to: { table: "users", column: "id" },
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Second schema for multi-schema testing
|
|
75
|
+
const schema2 = schema((s) => {
|
|
76
|
+
return s
|
|
77
|
+
.addTable("products", (t) => {
|
|
78
|
+
return t
|
|
79
|
+
.addColumn("id", idColumn())
|
|
80
|
+
.addColumn("name", column("string"))
|
|
81
|
+
.addColumn("price", column("integer"))
|
|
82
|
+
.createIndex("name_idx", ["name"]);
|
|
83
|
+
})
|
|
84
|
+
.addTable("orders", (t) => {
|
|
85
|
+
return t
|
|
86
|
+
.addColumn("id", idColumn())
|
|
87
|
+
.addColumn("product_id", referenceColumn())
|
|
88
|
+
.addColumn("quantity", column("integer"))
|
|
89
|
+
.createIndex("product_orders_idx", ["product_id"]);
|
|
90
|
+
})
|
|
91
|
+
.addReference("product", {
|
|
92
|
+
type: "one",
|
|
93
|
+
from: { table: "orders", column: "product_id" },
|
|
94
|
+
to: { table: "products", column: "id" },
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// oxlint-disable-next-line no-explicit-any
|
|
99
|
+
let adapter: DatabaseAdapter<any>;
|
|
100
|
+
|
|
101
|
+
beforeAll(async () => {
|
|
102
|
+
const dialect = new SqliteDialect({
|
|
103
|
+
database: new SQLite(":memory:"),
|
|
104
|
+
});
|
|
105
|
+
const sqlAdapter = new SqlDriverAdapter(dialect);
|
|
106
|
+
const driverConfig = new BetterSQLite3DriverConfig();
|
|
107
|
+
const genericAdapter = new GenericSQLAdapter({ dialect, driverConfig });
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
const migrations = genericAdapter.prepareMigrations(internalSchema, "");
|
|
111
|
+
await migrations.executeWithDriver(sqlAdapter, 0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
{
|
|
115
|
+
const migrations = genericAdapter.prepareMigrations(testSchema, "namespace");
|
|
116
|
+
await migrations.executeWithDriver(sqlAdapter, 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
const migrations = genericAdapter.prepareMigrations(schema2, "namespace2");
|
|
121
|
+
await migrations.executeWithDriver(sqlAdapter, 0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
adapter = genericAdapter;
|
|
125
|
+
return async () => {
|
|
126
|
+
await sqlAdapter.destroy();
|
|
127
|
+
};
|
|
128
|
+
}, 12000);
|
|
129
|
+
|
|
130
|
+
it("should execute Unit of Work with version checking", async () => {
|
|
131
|
+
// Pass namespace to ensure mapper translates logical table names to physical (prefixed) names
|
|
132
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
133
|
+
|
|
134
|
+
// Create two users at once using UOW
|
|
135
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
136
|
+
createUow.create("users", {
|
|
137
|
+
name: "Alice",
|
|
138
|
+
age: 25,
|
|
139
|
+
});
|
|
140
|
+
createUow.create("users", {
|
|
141
|
+
name: "Bob",
|
|
142
|
+
age: 30,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
|
|
146
|
+
Parameters<typeof createUow.find>[0]
|
|
147
|
+
>();
|
|
148
|
+
expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
|
|
149
|
+
"users" | "emails" | "posts" | "comments"
|
|
150
|
+
>();
|
|
151
|
+
|
|
152
|
+
const { success: createSuccess } = await createUow.executeMutations();
|
|
153
|
+
expect(createSuccess).toBe(true);
|
|
154
|
+
|
|
155
|
+
const createdIds = createUow.getCreatedIds();
|
|
156
|
+
expect(createdIds).toHaveLength(2);
|
|
157
|
+
|
|
158
|
+
// Verify both users were created by fetching them
|
|
159
|
+
const [allUsers] = await queryEngine
|
|
160
|
+
.createUnitOfWork("get-all-users")
|
|
161
|
+
.find("users")
|
|
162
|
+
.executeRetrieve();
|
|
163
|
+
|
|
164
|
+
expect(allUsers).toHaveLength(2);
|
|
165
|
+
|
|
166
|
+
// Verify Alice (first created user)
|
|
167
|
+
expect(allUsers[0]).toMatchObject({
|
|
168
|
+
name: "Alice",
|
|
169
|
+
age: 25,
|
|
170
|
+
id: expect.objectContaining({
|
|
171
|
+
externalId: createdIds[0].externalId,
|
|
172
|
+
version: 0,
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Verify Bob (second created user)
|
|
177
|
+
expect(allUsers[1]).toMatchObject({
|
|
178
|
+
name: "Bob",
|
|
179
|
+
age: 30,
|
|
180
|
+
id: expect.objectContaining({
|
|
181
|
+
externalId: createdIds[1].externalId,
|
|
182
|
+
version: 0,
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Use Alice (first user) for the rest of the test
|
|
187
|
+
const initialUserId = createdIds[0];
|
|
188
|
+
|
|
189
|
+
// Build a UOW to update Alice with optimistic locking
|
|
190
|
+
const uow = queryEngine
|
|
191
|
+
.createUnitOfWork("update-user-age")
|
|
192
|
+
// Retrieval phase: find Alice
|
|
193
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)));
|
|
194
|
+
|
|
195
|
+
// Execute retrieval and transition to mutation phase
|
|
196
|
+
const [users] = await uow.executeRetrieve();
|
|
197
|
+
|
|
198
|
+
// Mutation phase: update with version check
|
|
199
|
+
uow.update("users", initialUserId, (b) => b.set({ age: 26 }).check());
|
|
200
|
+
|
|
201
|
+
// Execute mutations
|
|
202
|
+
const { success } = await uow.executeMutations();
|
|
203
|
+
|
|
204
|
+
// Should succeed
|
|
205
|
+
expect(success).toBe(true);
|
|
206
|
+
expect(users).toHaveLength(1);
|
|
207
|
+
expect(users[0].name).toBe("Alice");
|
|
208
|
+
|
|
209
|
+
// Verify Alice was updated
|
|
210
|
+
const [[updatedUser]] = await queryEngine
|
|
211
|
+
.createUnitOfWork("get-updated-user")
|
|
212
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
|
|
213
|
+
.executeRetrieve();
|
|
214
|
+
|
|
215
|
+
expect(updatedUser).toMatchObject({
|
|
216
|
+
id: expect.objectContaining({
|
|
217
|
+
externalId: initialUserId.externalId,
|
|
218
|
+
version: 1, // Version incremented
|
|
219
|
+
}),
|
|
220
|
+
name: "Alice",
|
|
221
|
+
age: 26,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Try to update Alice again with stale version (should fail)
|
|
225
|
+
const uow2 = queryEngine.createUnitOfWork("update-user-stale");
|
|
226
|
+
|
|
227
|
+
// Use the old version (0) which is now stale
|
|
228
|
+
uow2.update("users", initialUserId, (b) => b.set({ age: 27 }).check());
|
|
229
|
+
|
|
230
|
+
const { success: success2 } = await uow2.executeMutations();
|
|
231
|
+
|
|
232
|
+
// Should fail due to version conflict
|
|
233
|
+
expect(success2).toBe(false);
|
|
234
|
+
|
|
235
|
+
// Verify Alice was NOT updated
|
|
236
|
+
const [[unchangedUser]] = await queryEngine
|
|
237
|
+
.createUnitOfWork("verify-unchanged")
|
|
238
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
|
|
239
|
+
.executeRetrieve();
|
|
240
|
+
|
|
241
|
+
expect(unchangedUser).toMatchObject({
|
|
242
|
+
id: expect.objectContaining({
|
|
243
|
+
version: 1, // Still version 1
|
|
244
|
+
}),
|
|
245
|
+
age: 26, // Still 26, not 27
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should support count operations", async () => {
|
|
250
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
251
|
+
|
|
252
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
253
|
+
createUow.create("users", { name: "User1", age: 20 });
|
|
254
|
+
createUow.create("users", { name: "User2", age: 30 });
|
|
255
|
+
createUow.create("users", { name: "User3", age: 40 });
|
|
256
|
+
await createUow.executeMutations();
|
|
257
|
+
|
|
258
|
+
// Count all users
|
|
259
|
+
const [totalCount] = await queryEngine
|
|
260
|
+
.createUnitOfWork("count-all")
|
|
261
|
+
.find("users", (b) => b.whereIndex("primary").selectCount())
|
|
262
|
+
.executeRetrieve();
|
|
263
|
+
|
|
264
|
+
// Tests are not isolated, so we can't use expect(totalCount).toBe(3)
|
|
265
|
+
expect(totalCount).toBeGreaterThanOrEqual(5); // At least Alice, Bob, and 3 new users
|
|
266
|
+
expect(typeof totalCount).toBe("number");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("should support cursor-based pagination", async () => {
|
|
270
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
271
|
+
|
|
272
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
273
|
+
createUow.create("users", { name: "Page User A", age: 20 });
|
|
274
|
+
createUow.create("users", { name: "Page User B", age: 30 });
|
|
275
|
+
createUow.create("users", { name: "Page User C", age: 40 });
|
|
276
|
+
createUow.create("users", { name: "Page User D", age: 50 });
|
|
277
|
+
createUow.create("users", { name: "Page User E", age: 60 });
|
|
278
|
+
|
|
279
|
+
await createUow.executeMutations();
|
|
280
|
+
|
|
281
|
+
// Fetch first page ordered by name
|
|
282
|
+
const [firstPage] = await queryEngine
|
|
283
|
+
.createUnitOfWork("first-page")
|
|
284
|
+
.find("users", (b) => b.whereIndex("name_idx").orderByIndex("name_idx", "asc").pageSize(2))
|
|
285
|
+
.executeRetrieve();
|
|
286
|
+
|
|
287
|
+
// Verify first page contains the first 2 users alphabetically
|
|
288
|
+
expect(firstPage).toHaveLength(2);
|
|
289
|
+
expect(firstPage.map((u) => u.name)).toEqual(["Alice", "Bob"]);
|
|
290
|
+
|
|
291
|
+
// Create cursor from last item of first page
|
|
292
|
+
const lastItem = firstPage[firstPage.length - 1]!;
|
|
293
|
+
const cursor = new Cursor({
|
|
294
|
+
indexName: "name_idx",
|
|
295
|
+
orderDirection: "asc",
|
|
296
|
+
pageSize: 2,
|
|
297
|
+
indexValues: { name: lastItem.name },
|
|
298
|
+
}).encode();
|
|
299
|
+
|
|
300
|
+
// Fetch next page using cursor
|
|
301
|
+
const [secondPage] = await queryEngine
|
|
302
|
+
.createUnitOfWork("second-page")
|
|
303
|
+
.find("users", (b) =>
|
|
304
|
+
b.whereIndex("name_idx").orderByIndex("name_idx", "asc").after(cursor).pageSize(2),
|
|
305
|
+
)
|
|
306
|
+
.executeRetrieve();
|
|
307
|
+
|
|
308
|
+
// Verify page 2 continues alphabetically
|
|
309
|
+
expect(secondPage).toHaveLength(2);
|
|
310
|
+
expect(secondPage.map((u) => u.name)).toEqual(["Page User A", "Page User B"]);
|
|
311
|
+
|
|
312
|
+
// Ensure no overlap between pages
|
|
313
|
+
const firstPageNames = new Set(firstPage.map((u) => u.name));
|
|
314
|
+
for (const user of secondPage) {
|
|
315
|
+
expect(firstPageNames.has(user.name)).toBe(false);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should support joins", async () => {
|
|
320
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
321
|
+
|
|
322
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
323
|
+
createUow.create("users", { name: "Email User", age: 20 });
|
|
324
|
+
|
|
325
|
+
const { success } = await createUow.executeMutations();
|
|
326
|
+
expect(success).toBe(true);
|
|
327
|
+
|
|
328
|
+
// Fetch the created user to get the proper ID
|
|
329
|
+
const [usersResult] = await queryEngine
|
|
330
|
+
.createUnitOfWork("get-created-user")
|
|
331
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Email User")))
|
|
332
|
+
.executeRetrieve();
|
|
333
|
+
|
|
334
|
+
expect(usersResult).toHaveLength(1);
|
|
335
|
+
const createdUser = usersResult[0];
|
|
336
|
+
expect(createdUser).toBeDefined();
|
|
337
|
+
expect(createdUser.name).toBe("Email User");
|
|
338
|
+
|
|
339
|
+
// Create an email for testing joins
|
|
340
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
|
|
341
|
+
createEmailUow.create("emails", {
|
|
342
|
+
user_id: createdUser.id,
|
|
343
|
+
email: "test@example.com",
|
|
344
|
+
is_primary: true,
|
|
345
|
+
});
|
|
346
|
+
await createEmailUow.executeMutations();
|
|
347
|
+
|
|
348
|
+
// Test join query
|
|
349
|
+
const uow = queryEngine
|
|
350
|
+
.createUnitOfWork("test-joins")
|
|
351
|
+
.find("emails", (b) =>
|
|
352
|
+
b
|
|
353
|
+
.whereIndex("user_emails", (eb) => eb("user_id", "=", createdUser.id))
|
|
354
|
+
.join((jb) => jb.user((builder) => builder.select(["name", "id", "age"]))),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const [[email]] = await uow.executeRetrieve();
|
|
358
|
+
|
|
359
|
+
expect(email).toMatchObject({
|
|
360
|
+
id: expect.objectContaining({
|
|
361
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
362
|
+
internalId: expect.any(BigInt),
|
|
363
|
+
}),
|
|
364
|
+
user_id: expect.objectContaining({
|
|
365
|
+
internalId: expect.any(BigInt),
|
|
366
|
+
}),
|
|
367
|
+
email: "test@example.com",
|
|
368
|
+
is_primary: true,
|
|
369
|
+
user: {
|
|
370
|
+
id: expect.objectContaining({
|
|
371
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
372
|
+
internalId: expect.any(BigInt),
|
|
373
|
+
}),
|
|
374
|
+
name: "Email User",
|
|
375
|
+
age: 20,
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should support complex nested joins (comments -> post -> author)", async () => {
|
|
381
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
382
|
+
|
|
383
|
+
// Create a user (author)
|
|
384
|
+
const createAuthorUow = queryEngine.createUnitOfWork("create-author");
|
|
385
|
+
createAuthorUow.create("users", { name: "Blog Author", age: 30 });
|
|
386
|
+
await createAuthorUow.executeMutations();
|
|
387
|
+
|
|
388
|
+
// Fetch the created author to get the proper ID
|
|
389
|
+
const [[author]] = await queryEngine
|
|
390
|
+
.createUnitOfWork("get-author")
|
|
391
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Blog Author")))
|
|
392
|
+
.executeRetrieve();
|
|
393
|
+
|
|
394
|
+
// Create a post by the author
|
|
395
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post");
|
|
396
|
+
createPostUow.create("posts", {
|
|
397
|
+
user_id: author.id,
|
|
398
|
+
title: "My First Post",
|
|
399
|
+
content: "This is the content of my first post",
|
|
400
|
+
});
|
|
401
|
+
await createPostUow.executeMutations();
|
|
402
|
+
|
|
403
|
+
// Fetch the created post to get the proper ID
|
|
404
|
+
const [[post]] = await queryEngine
|
|
405
|
+
.createUnitOfWork("get-post")
|
|
406
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", author.id)))
|
|
407
|
+
.executeRetrieve();
|
|
408
|
+
|
|
409
|
+
// Create a commenter
|
|
410
|
+
const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
|
|
411
|
+
createCommenterUow.create("users", { name: "Commenter User", age: 25 });
|
|
412
|
+
await createCommenterUow.executeMutations();
|
|
413
|
+
|
|
414
|
+
// Fetch the created commenter to get the proper ID
|
|
415
|
+
const [[commenter]] = await queryEngine
|
|
416
|
+
.createUnitOfWork("get-commenter")
|
|
417
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Commenter User")))
|
|
418
|
+
.executeRetrieve();
|
|
419
|
+
|
|
420
|
+
// Create a comment on the post
|
|
421
|
+
const createCommentUow = queryEngine.createUnitOfWork("create-comment");
|
|
422
|
+
createCommentUow.create("comments", {
|
|
423
|
+
post_id: post.id,
|
|
424
|
+
user_id: commenter.id,
|
|
425
|
+
text: "Great post!",
|
|
426
|
+
});
|
|
427
|
+
await createCommentUow.executeMutations();
|
|
428
|
+
|
|
429
|
+
// Now perform a complex nested join: comments -> post -> author, and comments -> commenter
|
|
430
|
+
const uow = queryEngine.createUnitOfWork("test-complex-joins").find("comments", (b) =>
|
|
431
|
+
b.whereIndex("primary").join((jb) =>
|
|
432
|
+
jb
|
|
433
|
+
.post((postBuilder) =>
|
|
434
|
+
postBuilder
|
|
435
|
+
.select(["id", "title", "content"])
|
|
436
|
+
.orderByIndex("primary", "desc")
|
|
437
|
+
.pageSize(1)
|
|
438
|
+
.join((jb2) =>
|
|
439
|
+
// Nested join to the post's author
|
|
440
|
+
jb2.author((authorBuilder) =>
|
|
441
|
+
authorBuilder.select(["id", "name", "age"]).orderByIndex("name_idx", "asc"),
|
|
442
|
+
),
|
|
443
|
+
),
|
|
444
|
+
)
|
|
445
|
+
.commenter((commenterBuilder) => commenterBuilder.select(["id", "name"])),
|
|
446
|
+
),
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
const [[comment]] = await uow.executeRetrieve();
|
|
450
|
+
|
|
451
|
+
// Verify the result structure with nested joins
|
|
452
|
+
expect(comment).toMatchObject({
|
|
453
|
+
id: expect.objectContaining({
|
|
454
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
455
|
+
internalId: expect.any(BigInt),
|
|
456
|
+
}),
|
|
457
|
+
text: "Great post!",
|
|
458
|
+
// Post join (first level)
|
|
459
|
+
post: {
|
|
460
|
+
id: expect.objectContaining({
|
|
461
|
+
externalId: post.id.externalId,
|
|
462
|
+
}),
|
|
463
|
+
title: "My First Post",
|
|
464
|
+
content: "This is the content of my first post",
|
|
465
|
+
// Nested author join (second level)
|
|
466
|
+
author: {
|
|
467
|
+
id: expect.objectContaining({
|
|
468
|
+
externalId: author.id.externalId,
|
|
469
|
+
}),
|
|
470
|
+
name: "Blog Author",
|
|
471
|
+
age: 30,
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
// Commenter join (first level)
|
|
475
|
+
commenter: {
|
|
476
|
+
id: expect.objectContaining({
|
|
477
|
+
externalId: commenter.id.externalId,
|
|
478
|
+
}),
|
|
479
|
+
name: "Commenter User",
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("should return created IDs from UOW create operations", async () => {
|
|
485
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
486
|
+
|
|
487
|
+
// Test 1: Create operations return IDs with both external and internal IDs
|
|
488
|
+
const uow1 = queryEngine.createUnitOfWork("create-multiple-users");
|
|
489
|
+
|
|
490
|
+
uow1.create("users", { name: "Test User 1", age: 30 });
|
|
491
|
+
uow1.create("users", { name: "Test User 2", age: 35 });
|
|
492
|
+
uow1.create("users", { name: "Test User 3", age: 40 });
|
|
493
|
+
|
|
494
|
+
const { success: success1 } = await uow1.executeMutations();
|
|
495
|
+
expect(success1).toBe(true);
|
|
496
|
+
|
|
497
|
+
const createdIds1 = uow1.getCreatedIds();
|
|
498
|
+
expect(createdIds1).toMatchObject([
|
|
499
|
+
expect.objectContaining({
|
|
500
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
501
|
+
internalId: expect.any(BigInt),
|
|
502
|
+
}),
|
|
503
|
+
expect.objectContaining({
|
|
504
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
505
|
+
internalId: expect.any(BigInt),
|
|
506
|
+
}),
|
|
507
|
+
expect.objectContaining({
|
|
508
|
+
externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
|
|
509
|
+
internalId: expect.any(BigInt),
|
|
510
|
+
}),
|
|
511
|
+
]);
|
|
512
|
+
|
|
513
|
+
// All external IDs should be unique
|
|
514
|
+
const externalIds = createdIds1.map((id) => id.externalId);
|
|
515
|
+
expect(new Set(externalIds).size).toBe(3);
|
|
516
|
+
|
|
517
|
+
// Verify we can use these IDs to query the created users
|
|
518
|
+
const user1 = await queryEngine.findFirst("users", (b) =>
|
|
519
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[0].externalId)),
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const user2 = await queryEngine.findFirst("users", (b) =>
|
|
523
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[1].externalId)),
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const user3 = await queryEngine.findFirst("users", (b) =>
|
|
527
|
+
b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[2].externalId)),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
expect(user1).toMatchObject({
|
|
531
|
+
id: expect.objectContaining({
|
|
532
|
+
externalId: createdIds1[0].externalId,
|
|
533
|
+
}),
|
|
534
|
+
name: "Test User 1",
|
|
535
|
+
age: 30,
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
expect(user2).toMatchObject({
|
|
539
|
+
id: expect.objectContaining({
|
|
540
|
+
externalId: createdIds1[1].externalId,
|
|
541
|
+
}),
|
|
542
|
+
name: "Test User 2",
|
|
543
|
+
age: 35,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
expect(user3).toMatchObject({
|
|
547
|
+
id: expect.objectContaining({
|
|
548
|
+
externalId: createdIds1[2].externalId,
|
|
549
|
+
}),
|
|
550
|
+
name: "Test User 3",
|
|
551
|
+
age: 40,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Test 2: Mixed operations (creates, updates, deletes) - only creates return IDs
|
|
555
|
+
const uow2 = queryEngine.createUnitOfWork("mixed-operations");
|
|
556
|
+
|
|
557
|
+
uow2.create("users", { name: "New User", age: 50 });
|
|
558
|
+
uow2.update("users", createdIds1[0], (b) => b.set({ age: 31 }));
|
|
559
|
+
uow2.create("users", { name: "Another New User", age: 55 });
|
|
560
|
+
uow2.delete("users", createdIds1[2]);
|
|
561
|
+
|
|
562
|
+
const { success: success2 } = await uow2.executeMutations();
|
|
563
|
+
expect(success2).toBe(true);
|
|
564
|
+
|
|
565
|
+
const createdIds2 = uow2.getCreatedIds();
|
|
566
|
+
|
|
567
|
+
// Only 2 creates, so only 2 IDs
|
|
568
|
+
expect(createdIds2).toHaveLength(2);
|
|
569
|
+
expect(createdIds2[0].externalId).toBeDefined();
|
|
570
|
+
expect(createdIds2[1].externalId).toBeDefined();
|
|
571
|
+
|
|
572
|
+
// Test 3: User-provided IDs are preserved
|
|
573
|
+
const customId = "my-custom-user-id-12345";
|
|
574
|
+
const uow3 = queryEngine.createUnitOfWork("create-with-custom-id");
|
|
575
|
+
|
|
576
|
+
uow3.create("users", { id: customId, name: "Custom ID User", age: 60 });
|
|
577
|
+
|
|
578
|
+
const { success: success3 } = await uow3.executeMutations();
|
|
579
|
+
expect(success3).toBe(true);
|
|
580
|
+
|
|
581
|
+
const createdIds3 = uow3.getCreatedIds();
|
|
582
|
+
|
|
583
|
+
expect(createdIds3).toHaveLength(1);
|
|
584
|
+
expect(createdIds3[0].externalId).toBe(customId);
|
|
585
|
+
expect(createdIds3[0].internalId).toBeDefined();
|
|
586
|
+
|
|
587
|
+
// Verify the user was created with the custom ID
|
|
588
|
+
const customIdUser = await queryEngine.findFirst("users", (b) =>
|
|
589
|
+
b.whereIndex("primary", (eb) => eb("id", "=", customId)),
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
expect(customIdUser).toMatchObject({
|
|
593
|
+
id: expect.objectContaining({
|
|
594
|
+
externalId: customId,
|
|
595
|
+
}),
|
|
596
|
+
name: "Custom ID User",
|
|
597
|
+
age: 60,
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it("should handle timestamps and timezones correctly", async () => {
|
|
602
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
603
|
+
|
|
604
|
+
// Create a user
|
|
605
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-timestamp");
|
|
606
|
+
createUserUow.create("users", { name: "Timestamp User", age: 28 });
|
|
607
|
+
await createUserUow.executeMutations();
|
|
608
|
+
|
|
609
|
+
const [[user]] = await queryEngine
|
|
610
|
+
.createUnitOfWork("get-user-for-timestamp")
|
|
611
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Timestamp User")))
|
|
612
|
+
.executeRetrieve();
|
|
613
|
+
|
|
614
|
+
// Create a post (note: SQLite schema doesn't have created_at column)
|
|
615
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post-for-timestamp");
|
|
616
|
+
createPostUow.create("posts", {
|
|
617
|
+
user_id: user.id,
|
|
618
|
+
title: "Timestamp Test Post",
|
|
619
|
+
content: "Testing timestamp handling",
|
|
620
|
+
});
|
|
621
|
+
await createPostUow.executeMutations();
|
|
622
|
+
|
|
623
|
+
// Retrieve the post
|
|
624
|
+
const [[post]] = await queryEngine
|
|
625
|
+
.createUnitOfWork("get-post-for-timestamp")
|
|
626
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
|
|
627
|
+
.executeRetrieve();
|
|
628
|
+
|
|
629
|
+
expect(post).toBeDefined();
|
|
630
|
+
expect(post.title).toBe("Timestamp Test Post");
|
|
631
|
+
|
|
632
|
+
// Test general Date handling (SQLite stores timestamps as integers)
|
|
633
|
+
const now = new Date();
|
|
634
|
+
expect(now).toBeInstanceOf(Date);
|
|
635
|
+
expect(typeof now.getTime).toBe("function");
|
|
636
|
+
expect(typeof now.toISOString).toBe("function");
|
|
637
|
+
|
|
638
|
+
// Verify date serialization/deserialization works
|
|
639
|
+
const isoString = now.toISOString();
|
|
640
|
+
expect(typeof isoString).toBe("string");
|
|
641
|
+
expect(new Date(isoString).getTime()).toBe(now.getTime());
|
|
642
|
+
|
|
643
|
+
// Test timezone preservation
|
|
644
|
+
const specificDate = new Date("2024-06-15T14:30:00Z");
|
|
645
|
+
expect(specificDate.toISOString()).toBe("2024-06-15T14:30:00.000Z");
|
|
646
|
+
|
|
647
|
+
// Verify SQLite numeric timestamp conversion
|
|
648
|
+
const timestamp = Date.now();
|
|
649
|
+
const dateFromTimestamp = new Date(timestamp);
|
|
650
|
+
expect(dateFromTimestamp.getTime()).toBe(timestamp);
|
|
651
|
+
|
|
652
|
+
// Verify that dates from different timezones are handled correctly
|
|
653
|
+
const localDate = new Date("2024-06-15T14:30:00");
|
|
654
|
+
expect(localDate).toBeInstanceOf(Date);
|
|
655
|
+
expect(typeof localDate.getTimezoneOffset()).toBe("number");
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it("should support forSchema for multi-schema queries", async () => {
|
|
659
|
+
const queryEngine1 = adapter.createQueryEngine(testSchema, "namespace");
|
|
660
|
+
const queryEngine2 = adapter.createQueryEngine(schema2, "namespace2");
|
|
661
|
+
|
|
662
|
+
// Create test data in schema1 (users)
|
|
663
|
+
const createUsersUow = queryEngine1.createUnitOfWork("create-users-for-multi-schema");
|
|
664
|
+
createUsersUow.create("users", { name: "Multi Schema User 1", age: 25 });
|
|
665
|
+
createUsersUow.create("users", { name: "Multi Schema User 2", age: 30 });
|
|
666
|
+
const { success: success1 } = await createUsersUow.executeMutations();
|
|
667
|
+
expect(success1).toBe(true);
|
|
668
|
+
|
|
669
|
+
// Create test data in schema2 (products)
|
|
670
|
+
const createProductsUow = queryEngine2.createUnitOfWork("create-products-for-multi-schema");
|
|
671
|
+
createProductsUow.create("products", { name: "Product A", price: 100 });
|
|
672
|
+
createProductsUow.create("products", { name: "Product B", price: 200 });
|
|
673
|
+
const { success: success2 } = await createProductsUow.executeMutations();
|
|
674
|
+
expect(success2).toBe(true);
|
|
675
|
+
|
|
676
|
+
// Now use forSchema to query from both schemas
|
|
677
|
+
const uow = queryEngine1.createUnitOfWork("multi-schema-query");
|
|
678
|
+
|
|
679
|
+
const view1 = uow
|
|
680
|
+
.forSchema(testSchema)
|
|
681
|
+
.find("users", (b) =>
|
|
682
|
+
b
|
|
683
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "Multi Schema User"))
|
|
684
|
+
.select(["id", "name"]),
|
|
685
|
+
)
|
|
686
|
+
.find("users", (b) =>
|
|
687
|
+
b
|
|
688
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "Multi Schema User"))
|
|
689
|
+
.select(["name", "age"]),
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
const view2 = uow
|
|
693
|
+
.forSchema(schema2)
|
|
694
|
+
.find("products", (b) => b.whereIndex("primary").select(["name", "price"]));
|
|
695
|
+
|
|
696
|
+
// Execute the retrieval phase once
|
|
697
|
+
await uow.executeRetrieve();
|
|
698
|
+
|
|
699
|
+
// Get results from view1
|
|
700
|
+
const [users1, users2] = await view1.retrievalPhase;
|
|
701
|
+
const [user1] = users1;
|
|
702
|
+
expectTypeOf(user1).toMatchObjectType<{ id: FragnoId; name: string }>();
|
|
703
|
+
|
|
704
|
+
const [user2] = users2;
|
|
705
|
+
expectTypeOf(user2).toMatchObjectType<{ name: string; age: number | null }>();
|
|
706
|
+
|
|
707
|
+
// Get results from view2
|
|
708
|
+
const [products] = await view2.retrievalPhase;
|
|
709
|
+
const [product1] = products;
|
|
710
|
+
expectTypeOf(product1).toMatchObjectType<{ name: string; price: number }>();
|
|
711
|
+
|
|
712
|
+
// Verify users from schema1
|
|
713
|
+
expect(users1).toHaveLength(2);
|
|
714
|
+
expect(users1[0]).toMatchObject({
|
|
715
|
+
id: expect.any(Object),
|
|
716
|
+
name: "Multi Schema User 1",
|
|
717
|
+
});
|
|
718
|
+
expect(users1[1]).toMatchObject({
|
|
719
|
+
id: expect.any(Object),
|
|
720
|
+
name: "Multi Schema User 2",
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
expect(users2).toHaveLength(2);
|
|
724
|
+
expect(users2[0]).toMatchObject({
|
|
725
|
+
name: "Multi Schema User 1",
|
|
726
|
+
age: 25,
|
|
727
|
+
});
|
|
728
|
+
expect(users2[1]).toMatchObject({
|
|
729
|
+
name: "Multi Schema User 2",
|
|
730
|
+
age: 30,
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// Verify products from schema2
|
|
734
|
+
expect(products).toHaveLength(2);
|
|
735
|
+
expect(products[0]).toMatchObject({
|
|
736
|
+
name: "Product A",
|
|
737
|
+
price: 100,
|
|
738
|
+
});
|
|
739
|
+
expect(products[1]).toMatchObject({
|
|
740
|
+
name: "Product B",
|
|
741
|
+
price: 200,
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it("should verify hasNextPage in cursor pagination", async () => {
|
|
746
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
747
|
+
|
|
748
|
+
// Create exactly 15 users for precise pagination testing
|
|
749
|
+
const prefix = "HasNextPageTest";
|
|
750
|
+
|
|
751
|
+
for (let i = 1; i <= 15; i++) {
|
|
752
|
+
await queryEngine.create("users", {
|
|
753
|
+
name: `${prefix} ${i.toString().padStart(2, "0")}`,
|
|
754
|
+
age: 20 + i,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Test 1: First page with more results available (pageSize=10, total=15)
|
|
759
|
+
const firstPage = await queryEngine.findWithCursor("users", (b) =>
|
|
760
|
+
b
|
|
761
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
762
|
+
.orderByIndex("name_idx", "asc")
|
|
763
|
+
.pageSize(10),
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
expect(firstPage.items).toHaveLength(10);
|
|
767
|
+
expect(firstPage.hasNextPage).toBe(true);
|
|
768
|
+
expect(firstPage.cursor).toBeInstanceOf(Cursor);
|
|
769
|
+
|
|
770
|
+
// Test 2: Second page (last page, partial results: 5 items remaining)
|
|
771
|
+
const secondPage = await queryEngine.findWithCursor("users", (b) =>
|
|
772
|
+
b
|
|
773
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
|
|
774
|
+
.after(firstPage.cursor!)
|
|
775
|
+
.orderByIndex("name_idx", "asc")
|
|
776
|
+
.pageSize(10),
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
expect(secondPage.items).toHaveLength(5);
|
|
780
|
+
expect(secondPage.hasNextPage).toBe(false);
|
|
781
|
+
expect(secondPage.cursor).toBeUndefined();
|
|
782
|
+
|
|
783
|
+
// Test 3: Empty results
|
|
784
|
+
const emptyPage = await queryEngine.findWithCursor("users", (b) =>
|
|
785
|
+
b
|
|
786
|
+
.whereIndex("name_idx", (eb) => eb("name", "starts with", "NonExistentPrefix"))
|
|
787
|
+
.orderByIndex("name_idx", "asc")
|
|
788
|
+
.pageSize(10),
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
expect(emptyPage.items).toHaveLength(0);
|
|
792
|
+
expect(emptyPage.hasNextPage).toBe(false);
|
|
793
|
+
expect(emptyPage.cursor).toBeUndefined();
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it("should support executeUnitOfWork with retry logic", async () => {
|
|
797
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
798
|
+
|
|
799
|
+
// Create a test user
|
|
800
|
+
const createUow = queryEngine.createUnitOfWork("create-user-for-execute-uow");
|
|
801
|
+
createUow.create("users", { name: "Execute UOW User", age: 42 });
|
|
802
|
+
await createUow.executeMutations();
|
|
803
|
+
|
|
804
|
+
// Fetch the user to get their ID
|
|
805
|
+
const [[user]] = await queryEngine
|
|
806
|
+
.createUnitOfWork("get-user-for-execute-uow")
|
|
807
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Execute UOW User")))
|
|
808
|
+
.executeRetrieve();
|
|
809
|
+
|
|
810
|
+
// Use executeUnitOfWork to increment age with optimistic locking
|
|
811
|
+
const result = await executeUnitOfWork(
|
|
812
|
+
{
|
|
813
|
+
retrieve: (uow) =>
|
|
814
|
+
uow.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", user.id))),
|
|
815
|
+
mutate: (uow, [users]) => {
|
|
816
|
+
const foundUser = users[0];
|
|
817
|
+
const newAge = foundUser.age! + 1;
|
|
818
|
+
uow.update("users", foundUser.id, (b) => b.set({ age: newAge }).check());
|
|
819
|
+
return { previousAge: foundUser.age, newAge };
|
|
820
|
+
},
|
|
821
|
+
onSuccess: ({ mutationResult }) => {
|
|
822
|
+
// Verify the age was incremented correctly
|
|
823
|
+
expect(mutationResult.newAge).toBe(mutationResult.previousAge! + 1);
|
|
824
|
+
},
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
createUnitOfWork: () => queryEngine.createUnitOfWork("execute-uow-update"),
|
|
828
|
+
retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3, initialDelayMs: 1 }),
|
|
829
|
+
},
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
// Verify the operation succeeded
|
|
833
|
+
assert(result.success);
|
|
834
|
+
expect(result.mutationResult).toEqual({
|
|
835
|
+
previousAge: 42,
|
|
836
|
+
newAge: 43,
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// Verify the user was actually updated in the database
|
|
840
|
+
const updatedUser = await queryEngine.findFirst("users", (b) =>
|
|
841
|
+
b.whereIndex("primary", (eb) => eb("id", "=", user.id)),
|
|
842
|
+
);
|
|
843
|
+
|
|
844
|
+
expect(updatedUser).toMatchObject({
|
|
845
|
+
id: expect.objectContaining({
|
|
846
|
+
externalId: user.id.externalId,
|
|
847
|
+
version: 1, // Version incremented due to check()
|
|
848
|
+
}),
|
|
849
|
+
name: "Execute UOW User",
|
|
850
|
+
age: 43,
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it("should fail check() when version changes", async () => {
|
|
855
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
856
|
+
|
|
857
|
+
// Create a user
|
|
858
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-version-conflict");
|
|
859
|
+
createUserUow.create("users", {
|
|
860
|
+
name: "Version Conflict User SQLite",
|
|
861
|
+
age: 40,
|
|
862
|
+
});
|
|
863
|
+
await createUserUow.executeMutations();
|
|
864
|
+
|
|
865
|
+
// Get the user
|
|
866
|
+
const [[user]] = await queryEngine
|
|
867
|
+
.createUnitOfWork("get-user-for-version-conflict")
|
|
868
|
+
.find("users", (b) =>
|
|
869
|
+
b.whereIndex("name_idx", (eb) => eb("name", "=", "Version Conflict User SQLite")),
|
|
870
|
+
)
|
|
871
|
+
.executeRetrieve();
|
|
872
|
+
|
|
873
|
+
// Update the user to increment their version
|
|
874
|
+
const updateUow = queryEngine.createUnitOfWork("update-user-version");
|
|
875
|
+
updateUow.update("users", user.id, (b) => b.set({ age: 41 }));
|
|
876
|
+
await updateUow.executeMutations();
|
|
877
|
+
|
|
878
|
+
// Try to check with the old version (should fail)
|
|
879
|
+
const uow = queryEngine.createUnitOfWork("check-stale-version");
|
|
880
|
+
uow.check("users", user.id); // This has version 0, but the user now has version 1
|
|
881
|
+
uow.create("posts", {
|
|
882
|
+
user_id: user.id,
|
|
883
|
+
title: "Should Not Be Created SQLite",
|
|
884
|
+
content: "Content",
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
const { success } = await uow.executeMutations();
|
|
888
|
+
expect(success).toBe(false);
|
|
889
|
+
|
|
890
|
+
// Verify the post was NOT created
|
|
891
|
+
const [posts] = await queryEngine
|
|
892
|
+
.createUnitOfWork("get-posts-for-version-conflict")
|
|
893
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
|
|
894
|
+
.executeRetrieve();
|
|
895
|
+
|
|
896
|
+
const conflictPosts = posts.filter((p) => p.title === "Should Not Be Created SQLite");
|
|
897
|
+
expect(conflictPosts).toHaveLength(0);
|
|
898
|
+
});
|
|
899
|
+
});
|