@fragno-dev/db 0.2.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +404 -175
- package/CHANGELOG.md +109 -0
- package/README.md +54 -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 +24 -9
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +60 -22
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +169 -3
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +25 -6
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +7 -6
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +193 -16
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.js +30 -3
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +9 -9
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +75 -52
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +7 -6
- 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/db-now-sql.js +27 -0
- package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +32 -21
- 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 +49 -18
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +43 -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 +6 -2
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +27 -8
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +135 -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 +196 -0
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-uow.js +871 -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 +30 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -0
- package/dist/adapters/in-memory/options.js +62 -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 +51 -24
- 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/browser/adapters/adapters.d.ts +61 -0
- package/dist/browser/adapters/adapters.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
- package/dist/browser/adapters/in-memory/index.d.ts +2 -0
- package/dist/browser/adapters/in-memory/options.d.ts +1 -0
- package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
- package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/browser/durable-hooks.d.ts +3 -0
- package/dist/browser/fragments/internal-fragment.d.ts +317 -0
- package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/browser/hooks/hooks.d.ts +146 -0
- package/dist/browser/hooks/hooks.d.ts.map +1 -0
- package/dist/browser/id.js +1 -0
- package/dist/browser/internal/adapter-registry.d.ts +4 -0
- package/dist/browser/internal/outbox-state.d.ts +2 -0
- package/dist/browser/mod.d.ts +15 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +17 -0
- package/dist/browser/mod.js.map +1 -0
- package/dist/browser/mod2.d.ts +48 -0
- package/dist/browser/mod2.d.ts.map +1 -0
- package/dist/browser/naming/sql-naming.d.ts +19 -0
- package/dist/browser/naming/sql-naming.d.ts.map +1 -0
- package/dist/browser/outbox/outbox.d.ts +21 -0
- package/dist/browser/outbox/outbox.d.ts.map +1 -0
- package/dist/browser/query/column-defaults.js +1 -0
- package/dist/browser/query/condition-builder.d.ts +44 -0
- package/dist/browser/query/condition-builder.d.ts.map +1 -0
- package/dist/browser/query/condition-builder.js +97 -0
- package/dist/browser/query/condition-builder.js.map +1 -0
- package/dist/browser/query/cursor.d.ts +105 -0
- package/dist/browser/query/cursor.d.ts.map +1 -0
- package/dist/browser/query/cursor.js +150 -0
- package/dist/browser/query/cursor.js.map +1 -0
- package/dist/browser/query/db-now.d.ts +22 -0
- package/dist/browser/query/db-now.d.ts.map +1 -0
- package/dist/browser/query/db-now.js +33 -0
- package/dist/browser/query/db-now.js.map +1 -0
- package/dist/browser/query/orm/orm.d.ts +18 -0
- package/dist/browser/query/orm/orm.d.ts.map +1 -0
- package/dist/browser/query/simple-query-interface.d.ts +108 -0
- package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
- package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/browser/query/value-encoding.js +38 -0
- package/dist/browser/query/value-encoding.js.map +1 -0
- package/dist/browser/schema/create.d.ts +326 -0
- package/dist/browser/schema/create.d.ts.map +1 -0
- package/dist/browser/schema/create.js +89 -0
- package/dist/browser/schema/create.js.map +1 -0
- package/dist/browser/schema/generate-id.js +28 -0
- package/dist/browser/schema/generate-id.js.map +1 -0
- package/dist/browser/shared/providers.d.ts +6 -0
- package/dist/browser/shared/providers.d.ts.map +1 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
- package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/browser/sync/commands.d.ts +15 -0
- package/dist/browser/sync/commands.d.ts.map +1 -0
- package/dist/browser/sync/commands.js +27 -0
- package/dist/browser/sync/commands.js.map +1 -0
- package/dist/browser/sync/types.d.ts +63 -0
- package/dist/browser/sync/types.d.ts.map +1 -0
- package/dist/browser/util/types.d.ts +8 -0
- package/dist/browser/util/types.d.ts.map +1 -0
- package/dist/browser/with-database.d.ts +29 -0
- package/dist/browser/with-database.d.ts.map +1 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +5 -0
- package/dist/db-fragment-definition-builder.d.ts +101 -33
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +450 -60
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts +11 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.js +31 -0
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
- package/dist/dispatchers/node/dispatcher.d.ts +14 -0
- package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/node/dispatcher.js +80 -0
- package/dist/dispatchers/node/dispatcher.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +12 -0
- package/dist/dispatchers/node/index.d.ts.map +1 -0
- package/dist/dispatchers/node/index.js +27 -0
- package/dist/dispatchers/node/index.js.map +1 -0
- package/dist/durable-hooks.d.ts +31 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +23 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +186 -8
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +203 -38
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +164 -0
- package/dist/fragments/internal-fragment.routes.js.map +1 -0
- package/dist/fragments/internal-fragment.schema.d.ts +15 -0
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.schema.js +39 -0
- package/dist/fragments/internal-fragment.schema.js.map +1 -0
- package/dist/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-logger.js +75 -0
- package/dist/hooks/durable-hooks-logger.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +1 -0
- package/dist/hooks/durable-hooks-processor.js +80 -0
- package/dist/hooks/durable-hooks-processor.js.map +1 -0
- package/dist/hooks/durable-hooks-runtime.js +44 -0
- package/dist/hooks/durable-hooks-runtime.js.map +1 -0
- package/dist/hooks/hooks.d.ts +100 -1
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +254 -27
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.js +2 -2
- package/dist/internal/adapter-registry.d.ts +11 -0
- package/dist/internal/adapter-registry.d.ts.map +1 -0
- package/dist/internal/adapter-registry.js +135 -0
- package/dist/internal/adapter-registry.js.map +1 -0
- package/dist/internal/outbox-state.d.ts +2 -0
- package/dist/internal/outbox-state.js +26 -0
- package/dist/internal/outbox-state.js.map +1 -0
- package/dist/migration-engine/auto-from-schema.d.ts +33 -0
- package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
- package/dist/migration-engine/auto-from-schema.js +223 -37
- 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 +86 -35
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.d.ts +113 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +20 -12
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +18 -12
- 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/outbox/outbox-builder.js +156 -0
- package/dist/outbox/outbox-builder.js.map +1 -0
- package/dist/outbox/outbox.d.ts +54 -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/query/column-defaults.js +20 -4
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/condition-builder.d.ts +7 -1
- package/dist/query/condition-builder.d.ts.map +1 -1
- package/dist/query/condition-builder.js +5 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/cursor-client.d.ts +105 -0
- package/dist/query/cursor-client.d.ts.map +1 -0
- package/dist/query/cursor-client.js +165 -0
- package/dist/query/cursor-client.js.map +1 -0
- package/dist/query/cursor.d.ts +3 -1
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +51 -14
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +22 -0
- package/dist/query/db-now.d.ts.map +1 -0
- package/dist/query/db-now.js +35 -0
- package/dist/query/db-now.js.map +1 -0
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +5 -4
- 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 +60 -12
- 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 +13 -4
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +50 -24
- 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 +92 -30
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +136 -11
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +16 -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 +103 -35
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +172 -58
- 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 +4 -3
- 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} +88 -60
- 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 +307 -0
- package/dist/schema-output/prisma.js.map +1 -0
- package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
- package/dist/sql-driver/sql.js.map +1 -1
- package/dist/sync/commands.d.ts +15 -0
- package/dist/sync/commands.d.ts.map +1 -0
- package/dist/sync/commands.js +27 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/read-tracking.d.ts +25 -0
- package/dist/sync/read-tracking.d.ts.map +1 -0
- package/dist/sync/read-tracking.js +148 -0
- package/dist/sync/read-tracking.js.map +1 -0
- package/dist/sync/submit.js +213 -0
- package/dist/sync/submit.js.map +1 -0
- package/dist/sync/types.d.ts +63 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/util/default-database-adapter.js +66 -0
- package/dist/util/default-database-adapter.js.map +1 -0
- package/dist/with-database.d.ts +3 -6
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +8 -7
- package/dist/with-database.js.map +1 -1
- package/package.json +62 -55
- package/src/adapters/adapters.ts +33 -26
- package/src/adapters/drizzle/migrate-drizzle.test.ts +99 -41
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +601 -0
- package/src/adapters/drizzle/test-utils.ts +13 -8
- package/src/adapters/generic-sql/driver-config.ts +38 -0
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +10 -8
- package/src/adapters/generic-sql/generic-sql-adapter.ts +117 -34
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +55 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +297 -3
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +120 -0
- package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +27 -8
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +47 -8
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +28 -9
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +9 -4
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +839 -8
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +396 -53
- package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
- package/src/adapters/generic-sql/migration/executor.ts +47 -4
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +238 -46
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +21 -13
- package/src/adapters/generic-sql/migration/sql-generator.ts +145 -66
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +11 -8
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +272 -0
- package/src/adapters/generic-sql/query/cursor-utils.ts +42 -7
- package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +171 -35
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +53 -40
- package/src/adapters/generic-sql/query/select-builder.test.ts +16 -11
- package/src/adapters/generic-sql/query/select-builder.ts +7 -3
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +75 -6
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +129 -24
- package/src/adapters/generic-sql/query/where-builder.test.ts +96 -20
- package/src/adapters/generic-sql/query/where-builder.ts +112 -41
- package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +11 -20
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +851 -0
- package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +18 -15
- package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +282 -14
- package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +129 -12
- package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +9 -7
- package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
- package/src/adapters/generic-sql/uow-decoder.test.ts +5 -4
- package/src/adapters/generic-sql/uow-decoder.ts +23 -5
- package/src/adapters/generic-sql/uow-encoder.test.ts +36 -3
- package/src/adapters/generic-sql/uow-encoder.ts +48 -13
- package/src/adapters/in-memory/condition-evaluator.test.ts +194 -0
- package/src/adapters/in-memory/condition-evaluator.ts +280 -0
- package/src/adapters/in-memory/errors.ts +20 -0
- package/src/adapters/in-memory/in-memory-adapter.ts +388 -0
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +344 -0
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +255 -0
- package/src/adapters/in-memory/in-memory-uow.ts +1724 -0
- package/src/adapters/in-memory/index.ts +3 -0
- package/src/adapters/in-memory/options.test.ts +42 -0
- package/src/adapters/in-memory/options.ts +91 -0
- package/src/adapters/in-memory/outbox.test.ts +361 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +51 -0
- package/src/adapters/in-memory/reference-resolution.ts +67 -0
- package/src/adapters/in-memory/sorted-array-index.test.ts +124 -0
- package/src/adapters/in-memory/sorted-array-index.ts +228 -0
- package/src/adapters/in-memory/store.test.ts +69 -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 +58 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1207 -0
- package/src/adapters/shared/from-unit-of-work-compiler.ts +159 -47
- package/src/adapters/shared/uow-operation-compiler.ts +28 -18
- package/src/adapters/sql/index.ts +12 -0
- package/src/browser/mod.ts +64 -0
- package/src/client.ts +19 -0
- package/src/db-fragment-definition-builder.test.ts +845 -53
- package/src/db-fragment-definition-builder.ts +911 -95
- package/src/db-fragment-instantiator.test.ts +210 -94
- package/src/db-fragment-integration.test.ts +17 -12
- package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
- package/src/dispatchers/cloudflare-do/index.test.ts +206 -0
- package/src/dispatchers/cloudflare-do/index.ts +63 -0
- package/src/dispatchers/node/dispatcher.ts +112 -0
- package/src/dispatchers/node/index.test.ts +120 -0
- package/src/dispatchers/node/index.ts +50 -0
- package/src/durable-hooks.test.ts +80 -0
- package/src/durable-hooks.ts +67 -0
- package/src/fragments/internal-fragment.routes.test.ts +570 -0
- package/src/fragments/internal-fragment.routes.ts +334 -0
- package/src/fragments/internal-fragment.schema.ts +95 -0
- package/src/fragments/internal-fragment.test.ts +505 -83
- package/src/fragments/internal-fragment.ts +453 -70
- package/src/hooks/durable-hooks-logger.ts +126 -0
- package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
- package/src/hooks/durable-hooks-processor.test.ts +282 -0
- package/src/hooks/durable-hooks-processor.ts +173 -0
- package/src/hooks/durable-hooks-runtime.test.ts +65 -0
- package/src/hooks/durable-hooks-runtime.ts +81 -0
- package/src/hooks/hooks.test.ts +455 -34
- package/src/hooks/hooks.ts +501 -34
- package/src/id.test.ts +34 -0
- package/src/id.ts +1 -3
- package/src/internal/adapter-registry.test.ts +93 -0
- package/src/internal/adapter-registry.ts +239 -0
- package/src/internal/outbox-state.ts +43 -0
- package/src/migration-engine/auto-from-schema.test.ts +107 -14
- package/src/migration-engine/auto-from-schema.ts +365 -44
- package/src/migration-engine/create.test.ts +4 -3
- package/src/migration-engine/create.ts +1 -1
- package/src/migration-engine/generation-engine.test.ts +292 -110
- package/src/migration-engine/generation-engine.ts +117 -66
- package/src/migration-engine/shared.ts +14 -0
- package/src/mod.ts +95 -39
- package/src/naming/sql-naming.ts +181 -0
- package/src/outbox/outbox-builder.ts +241 -0
- package/src/outbox/outbox.test.ts +424 -0
- package/src/outbox/outbox.ts +139 -0
- package/src/query/column-defaults.ts +42 -4
- package/src/query/condition-builder.test.ts +18 -3
- package/src/query/condition-builder.ts +7 -0
- package/src/query/cursor-client.test.ts +70 -0
- package/src/query/cursor-client.ts +263 -0
- package/src/query/cursor.test.ts +119 -20
- package/src/query/cursor.ts +88 -27
- package/src/query/db-now.ts +73 -0
- package/src/query/orm/orm.ts +2 -2
- package/src/query/query-type.test.ts +4 -3
- package/src/query/serialize/create-sql-serializer.ts +10 -5
- package/src/query/serialize/dialect/mysql-serializer.ts +13 -5
- package/src/query/serialize/dialect/postgres-serializer.ts +35 -5
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +90 -3
- package/src/query/serialize/dialect/sqlite-serializer.ts +108 -12
- package/src/query/serialize/sql-serializer.ts +4 -4
- package/src/query/simple-query-interface.ts +15 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +372 -10
- package/src/query/unit-of-work/execute-unit-of-work.ts +87 -27
- package/src/query/unit-of-work/retry-policy.test.ts +1 -0
- package/src/query/unit-of-work/tx-builder.test.ts +73 -1
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +17 -16
- package/src/query/unit-of-work/unit-of-work-types.test.ts +42 -12
- package/src/query/unit-of-work/unit-of-work.test.ts +196 -39
- package/src/query/unit-of-work/unit-of-work.ts +309 -38
- package/src/query/value-decoding.test.ts +63 -4
- package/src/query/value-decoding.ts +32 -6
- package/src/query/value-encoding.test.ts +86 -2
- package/src/query/value-encoding.ts +56 -6
- package/src/schema/create.test.ts +293 -47
- package/src/schema/create.ts +406 -70
- package/src/schema/generate-id.test.ts +3 -2
- package/src/schema/generate-id.ts +2 -2
- package/src/schema/serialize.test.ts +18 -5
- package/src/schema/type-conversion/create-sql-type-mapper.ts +8 -3
- package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
- package/src/schema/type-conversion/type-mapping.test.ts +26 -1
- package/src/schema/validator.test.ts +199 -0
- package/src/schema/validator.ts +232 -0
- package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +232 -129
- package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +155 -99
- package/src/schema-output/prisma.test.ts +694 -0
- package/src/schema-output/prisma.ts +593 -0
- package/src/sql-driver/better-sqlite3.test.ts +5 -3
- package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
- package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
- package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
- package/src/sql-driver/query-executor/query-executor.ts +1 -1
- package/src/sql-driver/sql-driver-adapter.ts +2 -2
- package/src/sql-driver/sql.ts +2 -1
- package/src/sql-driver/sqlocal.test.ts +4 -2
- package/src/sync/commands.test.ts +39 -0
- package/src/sync/commands.ts +51 -0
- package/src/sync/conflict-checker.test.ts +450 -0
- package/src/sync/conflict-checker.ts +248 -0
- package/src/sync/index.ts +14 -0
- package/src/sync/plan.ts +9 -0
- package/src/sync/read-tracking.test.ts +177 -0
- package/src/sync/read-tracking.ts +287 -0
- package/src/sync/submit.test.ts +205 -0
- package/src/sync/submit.ts +328 -0
- package/src/sync/types.ts +80 -0
- package/src/util/default-database-adapter.ts +119 -0
- package/src/with-database.ts +20 -31
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +38 -24
- package/vitest.config.ts +1 -0
- 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 +0 -165
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/packages/fragno/dist/api/bind-services.js +0 -20
- package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
- package/dist/packages/fragno/dist/api/error.js +0 -48
- package/dist/packages/fragno/dist/api/error.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -320
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -525
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
- package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/route.js +0 -10
- package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
- package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +0 -118
- package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
- package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
- package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/route.js +0 -17
- package/dist/packages/fragno/dist/api/route.js.map +0 -1
- package/dist/packages/fragno/dist/internal/symbols.js +0 -10
- package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
- package/dist/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,1206 @@
|
|
|
1
|
+
import { generateId } from "../../schema/generate-id.js";
|
|
2
|
+
import { FragnoId } from "../../schema/create.js";
|
|
3
|
+
import { dbInterval, dbNow } from "../db-now.js";
|
|
4
|
+
import { buildCondition } from "../condition-builder.js";
|
|
5
|
+
import { Cursor } from "../cursor.js";
|
|
6
|
+
|
|
7
|
+
//#region src/query/unit-of-work/unit-of-work.ts
|
|
8
|
+
/**
|
|
9
|
+
* Builder for find operations in Unit of Work
|
|
10
|
+
*/
|
|
11
|
+
var FindBuilder = class {
|
|
12
|
+
#table;
|
|
13
|
+
#tableName;
|
|
14
|
+
#indexName;
|
|
15
|
+
#whereClause;
|
|
16
|
+
#orderByIndexClause;
|
|
17
|
+
#afterCursor;
|
|
18
|
+
#beforeCursor;
|
|
19
|
+
#pageSizeValue;
|
|
20
|
+
#selectClause;
|
|
21
|
+
#joinClause;
|
|
22
|
+
#countMode = false;
|
|
23
|
+
#cursorMetadata;
|
|
24
|
+
constructor(tableName, table) {
|
|
25
|
+
this.#tableName = tableName;
|
|
26
|
+
this.#table = table;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Specify which index to use and optionally filter the results
|
|
30
|
+
*/
|
|
31
|
+
whereIndex(indexName, condition) {
|
|
32
|
+
if (indexName !== "primary" && !(indexName in this.#table.indexes)) throw new Error(`Index "${String(indexName)}" not found on table "${this.#tableName}". Available indexes: primary, ${Object.keys(this.#table.indexes).join(", ")}`);
|
|
33
|
+
this.#indexName = indexName === "primary" ? "_primary" : indexName;
|
|
34
|
+
if (condition) this.#whereClause = condition;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Specify columns to select
|
|
39
|
+
* @throws Error if selectCount() has already been called
|
|
40
|
+
*/
|
|
41
|
+
select(columns) {
|
|
42
|
+
if (this.#countMode) throw new Error(`Cannot call select() after selectCount() on table "${this.#tableName}". Use either select() or selectCount(), not both.`);
|
|
43
|
+
this.#selectClause = columns;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Select count instead of records
|
|
48
|
+
* @throws Error if select() has already been called
|
|
49
|
+
*/
|
|
50
|
+
selectCount() {
|
|
51
|
+
if (this.#selectClause !== void 0) throw new Error(`Cannot call selectCount() after select() on table "${this.#tableName}". Use either select() or selectCount(), not both.`);
|
|
52
|
+
this.#countMode = true;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Order results by index in ascending or descending order
|
|
57
|
+
*/
|
|
58
|
+
orderByIndex(indexName, direction) {
|
|
59
|
+
if (indexName !== "primary" && !(indexName in this.#table.indexes)) throw new Error(`Index "${String(indexName)}" not found on table "${this.#tableName}". Available indexes: primary, ${Object.keys(this.#table.indexes).join(", ")}`);
|
|
60
|
+
this.#orderByIndexClause = {
|
|
61
|
+
indexName: indexName === "primary" ? "_primary" : indexName,
|
|
62
|
+
direction
|
|
63
|
+
};
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Set cursor to continue pagination after this point (forward pagination)
|
|
68
|
+
* If a Cursor object is provided, its metadata will be used to set defaults for
|
|
69
|
+
* index, orderByIndex, and pageSize (if not explicitly set)
|
|
70
|
+
*/
|
|
71
|
+
after(cursor) {
|
|
72
|
+
this.#afterCursor = cursor;
|
|
73
|
+
if (cursor instanceof Cursor) this.#cursorMetadata = cursor;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Set cursor to continue pagination before this point (backward pagination)
|
|
78
|
+
* If a Cursor object is provided, its metadata will be used to set defaults for
|
|
79
|
+
* index, orderByIndex, and pageSize (if not explicitly set)
|
|
80
|
+
*/
|
|
81
|
+
before(cursor) {
|
|
82
|
+
this.#beforeCursor = cursor;
|
|
83
|
+
if (cursor instanceof Cursor) this.#cursorMetadata = cursor;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Set the number of results per page
|
|
88
|
+
* @throws {RangeError} If size is not a positive integer
|
|
89
|
+
*/
|
|
90
|
+
pageSize(size) {
|
|
91
|
+
if (!Number.isInteger(size) || size <= 0) throw new RangeError(`pageSize must be a positive integer, received: ${size}`);
|
|
92
|
+
this.#pageSizeValue = size;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Add joins to include related data
|
|
97
|
+
* Join where clauses are restricted to indexed columns only
|
|
98
|
+
*/
|
|
99
|
+
join(joinFn) {
|
|
100
|
+
this.#joinClause = joinFn;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
build() {
|
|
107
|
+
let indexName = this.#indexName;
|
|
108
|
+
let orderByIndex = this.#orderByIndexClause;
|
|
109
|
+
let pageSize = this.#pageSizeValue;
|
|
110
|
+
if (this.#cursorMetadata) {
|
|
111
|
+
if (!indexName) indexName = this.#cursorMetadata.indexName;
|
|
112
|
+
if (!orderByIndex) orderByIndex = {
|
|
113
|
+
indexName: this.#cursorMetadata.indexName,
|
|
114
|
+
direction: this.#cursorMetadata.orderDirection
|
|
115
|
+
};
|
|
116
|
+
if (pageSize === void 0) pageSize = this.#cursorMetadata.pageSize;
|
|
117
|
+
if (indexName && indexName !== this.#cursorMetadata.indexName) throw new Error(`Index mismatch: builder specifies "${indexName}" but cursor specifies "${this.#cursorMetadata.indexName}"`);
|
|
118
|
+
if (orderByIndex && (orderByIndex.indexName !== this.#cursorMetadata.indexName || orderByIndex.direction !== this.#cursorMetadata.orderDirection)) throw new Error(`Order mismatch: builder and cursor specify different ordering`);
|
|
119
|
+
if (pageSize !== void 0 && pageSize !== this.#cursorMetadata.pageSize) throw new Error(`Page size mismatch: builder specifies ${pageSize} but cursor specifies ${this.#cursorMetadata.pageSize}`);
|
|
120
|
+
}
|
|
121
|
+
if (!indexName) throw new Error(`Must specify an index using .whereIndex() before finalizing find operation on table "${this.#tableName}"`);
|
|
122
|
+
if (this.#countMode) return {
|
|
123
|
+
type: "count",
|
|
124
|
+
indexName,
|
|
125
|
+
options: {
|
|
126
|
+
useIndex: indexName,
|
|
127
|
+
where: this.#whereClause
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
let compiledJoins;
|
|
131
|
+
if (this.#joinClause) compiledJoins = buildJoinIndexed(this.#table, this.#joinClause);
|
|
132
|
+
const afterCursor = this.#afterCursor instanceof Cursor ? this.#afterCursor.encode() : this.#afterCursor;
|
|
133
|
+
const beforeCursor = this.#beforeCursor instanceof Cursor ? this.#beforeCursor.encode() : this.#beforeCursor;
|
|
134
|
+
const options = {
|
|
135
|
+
useIndex: indexName,
|
|
136
|
+
select: this.#selectClause,
|
|
137
|
+
where: this.#whereClause,
|
|
138
|
+
orderByIndex,
|
|
139
|
+
after: afterCursor,
|
|
140
|
+
before: beforeCursor,
|
|
141
|
+
pageSize,
|
|
142
|
+
joins: compiledJoins
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
type: "find",
|
|
146
|
+
indexName,
|
|
147
|
+
options
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Builder for update operations in Unit of Work
|
|
153
|
+
*/
|
|
154
|
+
var UpdateBuilder = class {
|
|
155
|
+
#tableName;
|
|
156
|
+
#id;
|
|
157
|
+
#checkVersion = false;
|
|
158
|
+
#setValues;
|
|
159
|
+
constructor(tableName, id) {
|
|
160
|
+
this.#tableName = tableName;
|
|
161
|
+
this.#id = id;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Specify values to update
|
|
165
|
+
*/
|
|
166
|
+
set(values) {
|
|
167
|
+
this.#setValues = values;
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Database timestamp helper for mutation values.
|
|
172
|
+
*/
|
|
173
|
+
now() {
|
|
174
|
+
return dbNow();
|
|
175
|
+
}
|
|
176
|
+
interval(input) {
|
|
177
|
+
return dbInterval(input);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Enable version checking for optimistic concurrency control
|
|
181
|
+
* @throws Error if the ID is just a string (no version available)
|
|
182
|
+
*/
|
|
183
|
+
check() {
|
|
184
|
+
if (typeof this.#id === "string") throw new Error(`Cannot use check() with a string ID on table "${this.#tableName}". Version checking requires a FragnoId with version information.`);
|
|
185
|
+
this.#checkVersion = true;
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* @internal
|
|
190
|
+
*/
|
|
191
|
+
build() {
|
|
192
|
+
if (!this.#setValues) throw new Error(`Must specify values using .set() before finalizing update operation on table "${this.#tableName}"`);
|
|
193
|
+
return {
|
|
194
|
+
id: this.#id,
|
|
195
|
+
checkVersion: this.#checkVersion,
|
|
196
|
+
set: this.#setValues
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Builder for delete operations in Unit of Work
|
|
202
|
+
*/
|
|
203
|
+
var DeleteBuilder = class {
|
|
204
|
+
#tableName;
|
|
205
|
+
#id;
|
|
206
|
+
#checkVersion = false;
|
|
207
|
+
constructor(tableName, id) {
|
|
208
|
+
this.#tableName = tableName;
|
|
209
|
+
this.#id = id;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Enable version checking for optimistic concurrency control
|
|
213
|
+
* @throws Error if the ID is just a string (no version available)
|
|
214
|
+
*/
|
|
215
|
+
check() {
|
|
216
|
+
if (typeof this.#id === "string") throw new Error(`Cannot use check() with a string ID on table "${this.#tableName}". Version checking requires a FragnoId with version information.`);
|
|
217
|
+
this.#checkVersion = true;
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
build() {
|
|
224
|
+
return {
|
|
225
|
+
id: this.#id,
|
|
226
|
+
checkVersion: this.#checkVersion
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Builder for join operations in Unit of Work
|
|
232
|
+
* Similar to FindBuilder but tailored for joins (no cursor pagination, no count mode)
|
|
233
|
+
*/
|
|
234
|
+
var JoinFindBuilder = class {
|
|
235
|
+
#table;
|
|
236
|
+
#tableName;
|
|
237
|
+
#indexName;
|
|
238
|
+
#whereClause;
|
|
239
|
+
#orderByIndexClause;
|
|
240
|
+
#pageSizeValue;
|
|
241
|
+
#selectClause;
|
|
242
|
+
#joinClause;
|
|
243
|
+
constructor(tableName, table) {
|
|
244
|
+
this.#tableName = tableName;
|
|
245
|
+
this.#table = table;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Specify which index to use and optionally filter the results
|
|
249
|
+
*/
|
|
250
|
+
whereIndex(indexName, condition) {
|
|
251
|
+
if (indexName !== "primary" && !(indexName in this.#table.indexes)) throw new Error(`Index "${String(indexName)}" not found on table "${this.#tableName}". Available indexes: primary, ${Object.keys(this.#table.indexes).join(", ")}`);
|
|
252
|
+
this.#indexName = indexName === "primary" ? "_primary" : indexName;
|
|
253
|
+
if (condition) this.#whereClause = condition;
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Specify columns to select
|
|
258
|
+
*/
|
|
259
|
+
select(columns) {
|
|
260
|
+
this.#selectClause = columns;
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Order results by index in ascending or descending order
|
|
265
|
+
*/
|
|
266
|
+
orderByIndex(indexName, direction) {
|
|
267
|
+
if (indexName !== "primary" && !(indexName in this.#table.indexes)) throw new Error(`Index "${String(indexName)}" not found on table "${this.#tableName}". Available indexes: primary, ${Object.keys(this.#table.indexes).join(", ")}`);
|
|
268
|
+
this.#orderByIndexClause = {
|
|
269
|
+
indexName: indexName === "primary" ? "_primary" : indexName,
|
|
270
|
+
direction
|
|
271
|
+
};
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Set the number of results to return
|
|
276
|
+
* @throws {RangeError} If size is not a positive integer
|
|
277
|
+
*/
|
|
278
|
+
pageSize(size) {
|
|
279
|
+
if (!Number.isInteger(size) || size <= 0) throw new RangeError(`pageSize must be a positive integer, received: ${size}`);
|
|
280
|
+
this.#pageSizeValue = size;
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Add joins to include related data
|
|
285
|
+
* Join where clauses are restricted to indexed columns only
|
|
286
|
+
*/
|
|
287
|
+
join(joinFn) {
|
|
288
|
+
this.#joinClause = joinFn;
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* @internal
|
|
293
|
+
*/
|
|
294
|
+
build() {
|
|
295
|
+
let compiledJoins;
|
|
296
|
+
if (this.#joinClause) compiledJoins = buildJoinIndexed(this.#table, this.#joinClause);
|
|
297
|
+
return {
|
|
298
|
+
indexName: this.#indexName,
|
|
299
|
+
select: this.#selectClause,
|
|
300
|
+
where: this.#whereClause,
|
|
301
|
+
orderByIndex: this.#orderByIndexClause,
|
|
302
|
+
pageSize: this.#pageSizeValue,
|
|
303
|
+
joins: compiledJoins
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
/**
|
|
308
|
+
* Build join operations with indexed-only where clauses for Unit of Work
|
|
309
|
+
* This ensures all join conditions can leverage indexes for optimal performance
|
|
310
|
+
*/
|
|
311
|
+
function buildJoinIndexed(table, fn) {
|
|
312
|
+
const compiled = [];
|
|
313
|
+
const builder = {};
|
|
314
|
+
for (const name in table.relations) {
|
|
315
|
+
const relation = table.relations[name];
|
|
316
|
+
builder[name] = (builderFn) => {
|
|
317
|
+
const joinBuilder = new JoinFindBuilder(relation.table.name, relation.table);
|
|
318
|
+
if (builderFn) builderFn(joinBuilder);
|
|
319
|
+
const config = joinBuilder.build();
|
|
320
|
+
let conditions;
|
|
321
|
+
if (config.where) {
|
|
322
|
+
const cond = buildCondition(relation.table.columns, config.where);
|
|
323
|
+
if (cond === true) conditions = void 0;
|
|
324
|
+
else if (cond === false) {
|
|
325
|
+
compiled.push({
|
|
326
|
+
relation,
|
|
327
|
+
options: false
|
|
328
|
+
});
|
|
329
|
+
delete builder[name];
|
|
330
|
+
return builder;
|
|
331
|
+
} else conditions = cond;
|
|
332
|
+
}
|
|
333
|
+
let orderBy;
|
|
334
|
+
if (config.orderByIndex) {
|
|
335
|
+
const index = relation.table.indexes[config.orderByIndex.indexName];
|
|
336
|
+
if (index) orderBy = index.columns.map((col) => [col, config.orderByIndex.direction]);
|
|
337
|
+
else orderBy = [[relation.table.getIdColumn(), config.orderByIndex.direction]];
|
|
338
|
+
}
|
|
339
|
+
compiled.push({
|
|
340
|
+
relation,
|
|
341
|
+
options: {
|
|
342
|
+
select: config.select ?? true,
|
|
343
|
+
where: conditions,
|
|
344
|
+
orderBy,
|
|
345
|
+
join: config.joins,
|
|
346
|
+
limit: config.pageSize
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
delete builder[name];
|
|
350
|
+
return builder;
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
fn(builder);
|
|
354
|
+
return compiled;
|
|
355
|
+
}
|
|
356
|
+
function createUnitOfWork(compiler, executor, decoder, schemaNamespaceMap, name) {
|
|
357
|
+
return new UnitOfWork(compiler, executor, decoder, name, void 0, schemaNamespaceMap);
|
|
358
|
+
}
|
|
359
|
+
function isUowInstrumentationInjection(value) {
|
|
360
|
+
if (!value || typeof value !== "object") return false;
|
|
361
|
+
const injection = value;
|
|
362
|
+
return injection.type === "conflict" || injection.type === "error";
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Encapsulates a promise with its resolver/rejecter functions.
|
|
366
|
+
* Simplifies management of deferred promises with built-in error handling.
|
|
367
|
+
*/
|
|
368
|
+
var DeferredPromise = class {
|
|
369
|
+
#resolve;
|
|
370
|
+
#reject;
|
|
371
|
+
#promise;
|
|
372
|
+
constructor() {
|
|
373
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
374
|
+
this.#promise = promise;
|
|
375
|
+
this.#resolve = resolve;
|
|
376
|
+
this.#reject = reject;
|
|
377
|
+
this.#promise.catch(() => {});
|
|
378
|
+
}
|
|
379
|
+
get promise() {
|
|
380
|
+
return this.#promise;
|
|
381
|
+
}
|
|
382
|
+
resolve(value) {
|
|
383
|
+
this.#resolve?.(value);
|
|
384
|
+
}
|
|
385
|
+
reject(error) {
|
|
386
|
+
this.#reject?.(error);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Reset to a new promise
|
|
390
|
+
*/
|
|
391
|
+
reset() {
|
|
392
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
393
|
+
this.#promise = promise;
|
|
394
|
+
this.#resolve = resolve;
|
|
395
|
+
this.#reject = reject;
|
|
396
|
+
this.#promise.catch(() => {});
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Tracks readiness signals from a group of children.
|
|
401
|
+
* Maintains a promise that resolves when all registered children have signaled.
|
|
402
|
+
*/
|
|
403
|
+
var ReadinessTracker = class {
|
|
404
|
+
#expectedCount = 0;
|
|
405
|
+
#signalCount = 0;
|
|
406
|
+
#resolve;
|
|
407
|
+
#promise = Promise.resolve();
|
|
408
|
+
get promise() {
|
|
409
|
+
return this.#promise;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Register that we're expecting a signal from a child
|
|
413
|
+
*/
|
|
414
|
+
registerChild() {
|
|
415
|
+
if (this.#expectedCount === 0) {
|
|
416
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
417
|
+
this.#promise = promise;
|
|
418
|
+
this.#resolve = resolve;
|
|
419
|
+
}
|
|
420
|
+
this.#expectedCount++;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Signal that one child is ready
|
|
424
|
+
*/
|
|
425
|
+
signal() {
|
|
426
|
+
this.#signalCount++;
|
|
427
|
+
if (this.#signalCount >= this.#expectedCount && this.#resolve) this.#resolve();
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Reset to initial state
|
|
431
|
+
*/
|
|
432
|
+
reset() {
|
|
433
|
+
this.#expectedCount = 0;
|
|
434
|
+
this.#signalCount = 0;
|
|
435
|
+
this.#resolve = void 0;
|
|
436
|
+
this.#promise = Promise.resolve();
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* Manages parent-child relationships and readiness coordination for Unit of Work instances.
|
|
441
|
+
* This allows parent UOWs to wait for all child UOWs to signal readiness before executing phases.
|
|
442
|
+
*/
|
|
443
|
+
var UOWChildCoordinator = class {
|
|
444
|
+
#parent = null;
|
|
445
|
+
#parentCoordinator = null;
|
|
446
|
+
#children = /* @__PURE__ */ new Set();
|
|
447
|
+
#isRestricted = false;
|
|
448
|
+
#retrievalTracker = new ReadinessTracker();
|
|
449
|
+
#mutationTracker = new ReadinessTracker();
|
|
450
|
+
get isRestricted() {
|
|
451
|
+
return this.#isRestricted;
|
|
452
|
+
}
|
|
453
|
+
get parent() {
|
|
454
|
+
return this.#parent;
|
|
455
|
+
}
|
|
456
|
+
get children() {
|
|
457
|
+
return this.#children;
|
|
458
|
+
}
|
|
459
|
+
get retrievalReadinessPromise() {
|
|
460
|
+
return this.#retrievalTracker.promise;
|
|
461
|
+
}
|
|
462
|
+
get mutationReadinessPromise() {
|
|
463
|
+
return this.#mutationTracker.promise;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Mark this UOW as a restricted child of the given parent
|
|
467
|
+
*/
|
|
468
|
+
setAsRestricted(parent, parentCoordinator) {
|
|
469
|
+
this.#parent = parent;
|
|
470
|
+
this.#parentCoordinator = parentCoordinator;
|
|
471
|
+
this.#isRestricted = true;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Register a child UOW
|
|
475
|
+
*/
|
|
476
|
+
addChild(child) {
|
|
477
|
+
this.#children.add(child);
|
|
478
|
+
this.#retrievalTracker.registerChild();
|
|
479
|
+
this.#mutationTracker.registerChild();
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Signal that this child is ready for retrieval phase execution.
|
|
483
|
+
* Only valid for restricted (child) UOWs.
|
|
484
|
+
*/
|
|
485
|
+
signalReadyForRetrieval() {
|
|
486
|
+
if (!this.#parentCoordinator) throw new Error("signalReadyForRetrieval() can only be called on restricted child UOWs");
|
|
487
|
+
this.#parentCoordinator.notifyChildReadyForRetrieval();
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Signal that this child is ready for mutation phase execution.
|
|
491
|
+
* Only valid for restricted (child) UOWs.
|
|
492
|
+
*/
|
|
493
|
+
signalReadyForMutation() {
|
|
494
|
+
if (!this.#parentCoordinator) throw new Error("signalReadyForMutation() can only be called on restricted child UOWs");
|
|
495
|
+
this.#parentCoordinator.notifyChildReadyForMutation();
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Notify this coordinator that a child is ready for retrieval (internal use).
|
|
499
|
+
* Called by child UOWs when they signal readiness.
|
|
500
|
+
*/
|
|
501
|
+
notifyChildReadyForRetrieval() {
|
|
502
|
+
this.#retrievalTracker.signal();
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Notify this coordinator that a child is ready for mutation (internal use).
|
|
506
|
+
* Called by child UOWs when they signal readiness.
|
|
507
|
+
*/
|
|
508
|
+
notifyChildReadyForMutation() {
|
|
509
|
+
this.#mutationTracker.signal();
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Reset coordination state for retry support
|
|
513
|
+
*/
|
|
514
|
+
reset() {
|
|
515
|
+
this.#children.clear();
|
|
516
|
+
this.#retrievalTracker.reset();
|
|
517
|
+
this.#mutationTracker.reset();
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* Unit of Work implementation with optimistic concurrency control
|
|
522
|
+
*
|
|
523
|
+
* UOW has two phases:
|
|
524
|
+
* 1. Retrieval phase: Read operations to fetch entities with their versions
|
|
525
|
+
* 2. Mutation phase: Write operations that check versions before committing
|
|
526
|
+
*
|
|
527
|
+
* This is the untyped base storage. Use TypedUnitOfWork for type-safe operations.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```ts
|
|
531
|
+
* const uow = queryEngine.createUnitOfWork("update-user-balance");
|
|
532
|
+
* const typedUow = uow.forSchema(mySchema);
|
|
533
|
+
*
|
|
534
|
+
* // Retrieval phase
|
|
535
|
+
* typedUow.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userId)));
|
|
536
|
+
*
|
|
537
|
+
* // Execute retrieval and transition to mutation phase
|
|
538
|
+
* const [users] = await uow.executeRetrieve();
|
|
539
|
+
*
|
|
540
|
+
* // Mutation phase with version check
|
|
541
|
+
* const user = users[0];
|
|
542
|
+
* typedUow.update("users", user.id, (b) => b.set({ balance: newBalance }).check());
|
|
543
|
+
*
|
|
544
|
+
* // Execute mutations
|
|
545
|
+
* const { success } = await uow.executeMutations();
|
|
546
|
+
* if (!success) {
|
|
547
|
+
* // Handle version conflict
|
|
548
|
+
* }
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
var UnitOfWork = class UnitOfWork {
|
|
552
|
+
#name;
|
|
553
|
+
#config;
|
|
554
|
+
#idempotencyKey;
|
|
555
|
+
#state = "building-retrieval";
|
|
556
|
+
#retrievalOps = [];
|
|
557
|
+
#mutationOps = [];
|
|
558
|
+
#compiler;
|
|
559
|
+
#executor;
|
|
560
|
+
#decoder;
|
|
561
|
+
#schemaNamespaceMap;
|
|
562
|
+
#retrievalResults;
|
|
563
|
+
#createdInternalIds = [];
|
|
564
|
+
#readTrackingEnabled = false;
|
|
565
|
+
#retrievalPhaseDeferred = new DeferredPromise();
|
|
566
|
+
#mutationPhaseDeferred = new DeferredPromise();
|
|
567
|
+
#retrievalError = null;
|
|
568
|
+
#mutationError = null;
|
|
569
|
+
#coordinator = new UOWChildCoordinator();
|
|
570
|
+
#triggeredHooks = [];
|
|
571
|
+
constructor(compiler, executor, decoder, name, config, schemaNamespaceMap) {
|
|
572
|
+
this.#compiler = compiler;
|
|
573
|
+
this.#executor = executor;
|
|
574
|
+
this.#decoder = decoder;
|
|
575
|
+
this.#schemaNamespaceMap = schemaNamespaceMap ?? /* @__PURE__ */ new WeakMap();
|
|
576
|
+
this.#name = name;
|
|
577
|
+
this.#config = config;
|
|
578
|
+
this.#idempotencyKey = config?.idempotencyKey ?? crypto.randomUUID();
|
|
579
|
+
}
|
|
580
|
+
#createInstrumentationContext(phase) {
|
|
581
|
+
return {
|
|
582
|
+
phase,
|
|
583
|
+
uowName: this.#name,
|
|
584
|
+
idempotencyKey: this.#idempotencyKey,
|
|
585
|
+
retrievalOpsCount: this.#retrievalOps.length,
|
|
586
|
+
mutationOpsCount: this.#mutationOps.length,
|
|
587
|
+
uow: this
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
async #runInstrumentation(phase) {
|
|
591
|
+
const hook = this.#config?.instrumentation?.[phase];
|
|
592
|
+
if (!hook) return null;
|
|
593
|
+
const result = await hook(this.#createInstrumentationContext(phase));
|
|
594
|
+
if (isUowInstrumentationInjection(result)) return result;
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
async #runInstrumentationFinalizer(phase) {
|
|
598
|
+
const hook = this.#config?.instrumentationFinalizer?.[phase];
|
|
599
|
+
if (!hook) return;
|
|
600
|
+
await hook(this.#createInstrumentationContext(phase));
|
|
601
|
+
}
|
|
602
|
+
#handleRetrieveInjection(injection) {
|
|
603
|
+
if (injection.type === "error") throw injection.error;
|
|
604
|
+
throw new Error(injection.reason ?? "Injected conflict");
|
|
605
|
+
}
|
|
606
|
+
#handleMutationInjection(injection) {
|
|
607
|
+
if (injection.type === "error") throw injection.error;
|
|
608
|
+
this.#state = "executed";
|
|
609
|
+
this.#createdInternalIds.length = 0;
|
|
610
|
+
this.#mutationPhaseDeferred.resolve();
|
|
611
|
+
return { success: false };
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Register a schema with its namespace for cross-fragment operations.
|
|
615
|
+
* This is used for internal fragments like hooks that need to create
|
|
616
|
+
* records in a different schema during the same transaction.
|
|
617
|
+
*/
|
|
618
|
+
registerSchema(schema, namespace) {
|
|
619
|
+
this.#schemaNamespaceMap.set(schema, namespace);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Get a schema-specific typed view of this UOW for type-safe operations.
|
|
623
|
+
* Returns a wrapper that provides typed operations for the given schema.
|
|
624
|
+
* The namespace is automatically resolved from the schema-namespace map.
|
|
625
|
+
* The optional hooks parameter is for type inference only - pass your hooks map
|
|
626
|
+
* to get proper typing for triggerHook. The value is not used at runtime.
|
|
627
|
+
*/
|
|
628
|
+
forSchema(schema, _hooks) {
|
|
629
|
+
return new TypedUnitOfWork(schema, this.#schemaNamespaceMap.get(schema), this);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Create a restricted child UOW that cannot execute phases.
|
|
633
|
+
* The child shares the same operation storage but must signal readiness
|
|
634
|
+
* before the parent can execute each phase.
|
|
635
|
+
*
|
|
636
|
+
* @param options.readyFor - Controls automatic readiness signaling:
|
|
637
|
+
* - "mutation" (default): Signals ready for both retrieval and mutation immediately
|
|
638
|
+
* - "retrieval": Signals ready for retrieval only
|
|
639
|
+
* - "none": No automatic signaling, caller must signal manually
|
|
640
|
+
*/
|
|
641
|
+
restrict(options) {
|
|
642
|
+
const readyFor = options?.readyFor ?? "mutation";
|
|
643
|
+
const child = new UnitOfWork(this.#compiler, this.#executor, this.#decoder, this.#name, {
|
|
644
|
+
...this.#config,
|
|
645
|
+
idempotencyKey: this.#idempotencyKey
|
|
646
|
+
}, this.#schemaNamespaceMap);
|
|
647
|
+
child.#coordinator.setAsRestricted(this, this.#coordinator);
|
|
648
|
+
child.#retrievalOps = this.#retrievalOps;
|
|
649
|
+
child.#mutationOps = this.#mutationOps;
|
|
650
|
+
child.#retrievalResults = this.#retrievalResults;
|
|
651
|
+
child.#createdInternalIds = this.#createdInternalIds;
|
|
652
|
+
child.#readTrackingEnabled = this.#readTrackingEnabled;
|
|
653
|
+
child.#retrievalPhaseDeferred = this.#retrievalPhaseDeferred;
|
|
654
|
+
child.#mutationPhaseDeferred = this.#mutationPhaseDeferred;
|
|
655
|
+
child.#retrievalError = this.#retrievalError;
|
|
656
|
+
child.#mutationError = this.#mutationError;
|
|
657
|
+
child.#triggeredHooks = this.#triggeredHooks;
|
|
658
|
+
this.#coordinator.addChild(child);
|
|
659
|
+
if (readyFor === "mutation" || readyFor === "retrieval") child.signalReadyForRetrieval();
|
|
660
|
+
if (readyFor === "mutation") child.signalReadyForMutation();
|
|
661
|
+
return child;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Signal that this child is ready for retrieval phase execution.
|
|
665
|
+
* Only valid for restricted (child) UOWs.
|
|
666
|
+
*/
|
|
667
|
+
signalReadyForRetrieval() {
|
|
668
|
+
this.#coordinator.signalReadyForRetrieval();
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Signal that this child is ready for mutation phase execution.
|
|
672
|
+
* Only valid for restricted (child) UOWs.
|
|
673
|
+
*/
|
|
674
|
+
signalReadyForMutation() {
|
|
675
|
+
this.#coordinator.signalReadyForMutation();
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Reset the UOW to initial state for retry support.
|
|
679
|
+
* Clears operations, resets state, and resets phase promises.
|
|
680
|
+
*/
|
|
681
|
+
reset() {
|
|
682
|
+
if (this.#coordinator.isRestricted) throw new Error("reset() cannot be called on restricted child UOWs");
|
|
683
|
+
this.#retrievalOps = [];
|
|
684
|
+
this.#mutationOps = [];
|
|
685
|
+
this.#retrievalResults = void 0;
|
|
686
|
+
this.#createdInternalIds = [];
|
|
687
|
+
this.#readTrackingEnabled = false;
|
|
688
|
+
this.#state = "building-retrieval";
|
|
689
|
+
this.#retrievalError = null;
|
|
690
|
+
this.#mutationError = null;
|
|
691
|
+
this.#retrievalPhaseDeferred.reset();
|
|
692
|
+
this.#mutationPhaseDeferred.reset();
|
|
693
|
+
this.#coordinator.reset();
|
|
694
|
+
this.#triggeredHooks = [];
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Trigger a hook to be executed after the transaction commits.
|
|
698
|
+
*/
|
|
699
|
+
triggerHook(namespace, hookName, payload, options) {
|
|
700
|
+
this.#triggeredHooks.push({
|
|
701
|
+
namespace,
|
|
702
|
+
hookName,
|
|
703
|
+
payload,
|
|
704
|
+
options
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Get all triggered hooks for this UOW.
|
|
709
|
+
*/
|
|
710
|
+
getTriggeredHooks() {
|
|
711
|
+
return this.#triggeredHooks;
|
|
712
|
+
}
|
|
713
|
+
get state() {
|
|
714
|
+
return this.#coordinator.parent?.state ?? this.#state;
|
|
715
|
+
}
|
|
716
|
+
enableReadTracking() {
|
|
717
|
+
this.#readTrackingEnabled = true;
|
|
718
|
+
}
|
|
719
|
+
get readTrackingEnabled() {
|
|
720
|
+
return this.#readTrackingEnabled;
|
|
721
|
+
}
|
|
722
|
+
get name() {
|
|
723
|
+
return this.#name;
|
|
724
|
+
}
|
|
725
|
+
get idempotencyKey() {
|
|
726
|
+
return this.#idempotencyKey;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Promise that resolves when the retrieval phase is executed
|
|
730
|
+
* Service methods can await this to coordinate multi-phase logic
|
|
731
|
+
*/
|
|
732
|
+
get retrievalPhase() {
|
|
733
|
+
return this.#retrievalPhaseDeferred.promise;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Promise that resolves when the mutation phase is executed
|
|
737
|
+
* Service methods can await this to coordinate multi-phase logic
|
|
738
|
+
*/
|
|
739
|
+
get mutationPhase() {
|
|
740
|
+
return this.#mutationPhaseDeferred.promise;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Execute the retrieval phase and transition to mutation phase
|
|
744
|
+
* Returns all results from find operations
|
|
745
|
+
*/
|
|
746
|
+
async executeRetrieve() {
|
|
747
|
+
if (this.#coordinator.isRestricted) throw new Error("executeRetrieve() cannot be called on restricted child UOWs");
|
|
748
|
+
if (this.#state !== "building-retrieval") throw new Error(`Cannot execute retrieval from state ${this.#state}. Must be in building-retrieval state.`);
|
|
749
|
+
let afterRan = false;
|
|
750
|
+
let failed = false;
|
|
751
|
+
try {
|
|
752
|
+
await this.#coordinator.retrievalReadinessPromise;
|
|
753
|
+
const beforeInjection = await this.#runInstrumentation("beforeRetrieve");
|
|
754
|
+
if (beforeInjection) return this.#handleRetrieveInjection(beforeInjection);
|
|
755
|
+
if (this.#retrievalOps.length === 0) {
|
|
756
|
+
this.#state = "building-mutation";
|
|
757
|
+
const emptyResults = [];
|
|
758
|
+
afterRan = true;
|
|
759
|
+
const afterInjection$1 = await this.#runInstrumentation("afterRetrieve");
|
|
760
|
+
if (afterInjection$1) return this.#handleRetrieveInjection(afterInjection$1);
|
|
761
|
+
this.#retrievalPhaseDeferred.resolve(emptyResults);
|
|
762
|
+
return emptyResults;
|
|
763
|
+
}
|
|
764
|
+
const retrievalBatch = [];
|
|
765
|
+
for (const op of this.#retrievalOps) {
|
|
766
|
+
const compiled = this.#compiler.compileRetrievalOperation(op);
|
|
767
|
+
if (compiled !== null) {
|
|
768
|
+
this.#config?.onQuery?.(compiled);
|
|
769
|
+
retrievalBatch.push(compiled);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (this.#config?.dryRun) {
|
|
773
|
+
this.#state = "executed";
|
|
774
|
+
const emptyResults = [];
|
|
775
|
+
afterRan = true;
|
|
776
|
+
const afterInjection$1 = await this.#runInstrumentation("afterRetrieve");
|
|
777
|
+
if (afterInjection$1) return this.#handleRetrieveInjection(afterInjection$1);
|
|
778
|
+
this.#retrievalPhaseDeferred.resolve(emptyResults);
|
|
779
|
+
return emptyResults;
|
|
780
|
+
}
|
|
781
|
+
const rawResults = await this.#executor.executeRetrievalPhase(retrievalBatch);
|
|
782
|
+
this.#retrievalResults = this.#decoder.decode(rawResults, this.#retrievalOps);
|
|
783
|
+
this.#state = "building-mutation";
|
|
784
|
+
afterRan = true;
|
|
785
|
+
const afterInjection = await this.#runInstrumentation("afterRetrieve");
|
|
786
|
+
if (afterInjection) return this.#handleRetrieveInjection(afterInjection);
|
|
787
|
+
this.#retrievalPhaseDeferred.resolve(this.#retrievalResults);
|
|
788
|
+
return this.#retrievalResults;
|
|
789
|
+
} catch (error) {
|
|
790
|
+
this.#retrievalError = error instanceof Error ? error : new Error(String(error));
|
|
791
|
+
failed = true;
|
|
792
|
+
throw error;
|
|
793
|
+
} finally {
|
|
794
|
+
try {
|
|
795
|
+
await this.#runInstrumentationFinalizer("afterRetrieve");
|
|
796
|
+
} catch {}
|
|
797
|
+
if (!afterRan && failed) try {
|
|
798
|
+
await this.#runInstrumentation("afterRetrieve");
|
|
799
|
+
} catch {}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Execute the mutation phase
|
|
804
|
+
* Returns success flag indicating if mutations completed without conflicts
|
|
805
|
+
*/
|
|
806
|
+
async executeMutations() {
|
|
807
|
+
if (this.#coordinator.isRestricted) throw new Error("executeMutations() cannot be called on restricted child UOWs");
|
|
808
|
+
if (this.#state === "executed") throw new Error(`Cannot execute mutations from state ${this.#state}.`);
|
|
809
|
+
let afterRan = false;
|
|
810
|
+
let failed = false;
|
|
811
|
+
try {
|
|
812
|
+
await this.#coordinator.mutationReadinessPromise;
|
|
813
|
+
const beforeInjection = await this.#runInstrumentation("beforeMutate");
|
|
814
|
+
if (beforeInjection) return this.#handleMutationInjection(beforeInjection);
|
|
815
|
+
const mutationBatch = [];
|
|
816
|
+
for (const op of this.#mutationOps) {
|
|
817
|
+
const compiled = this.#compiler.compileMutationOperation(op);
|
|
818
|
+
if (compiled !== null) {
|
|
819
|
+
compiled.uowId = this.#idempotencyKey;
|
|
820
|
+
this.#config?.onQuery?.(compiled);
|
|
821
|
+
mutationBatch.push(compiled);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (this.#config?.dryRun) {
|
|
825
|
+
this.#state = "executed";
|
|
826
|
+
afterRan = true;
|
|
827
|
+
const afterInjection$1 = await this.#runInstrumentation("afterMutate");
|
|
828
|
+
if (afterInjection$1) return this.#handleMutationInjection(afterInjection$1);
|
|
829
|
+
this.#mutationPhaseDeferred.resolve();
|
|
830
|
+
return { success: true };
|
|
831
|
+
}
|
|
832
|
+
const result = await this.#executor.executeMutationPhase(mutationBatch);
|
|
833
|
+
this.#state = "executed";
|
|
834
|
+
if (result.success) {
|
|
835
|
+
this.#createdInternalIds.length = 0;
|
|
836
|
+
this.#createdInternalIds.push(...result.createdInternalIds);
|
|
837
|
+
}
|
|
838
|
+
afterRan = true;
|
|
839
|
+
const afterInjection = await this.#runInstrumentation("afterMutate");
|
|
840
|
+
if (afterInjection) return this.#handleMutationInjection(afterInjection);
|
|
841
|
+
this.#mutationPhaseDeferred.resolve();
|
|
842
|
+
return { success: result.success };
|
|
843
|
+
} catch (error) {
|
|
844
|
+
this.#mutationError = error instanceof Error ? error : new Error(String(error));
|
|
845
|
+
failed = true;
|
|
846
|
+
throw error;
|
|
847
|
+
} finally {
|
|
848
|
+
try {
|
|
849
|
+
await this.#runInstrumentationFinalizer("afterMutate");
|
|
850
|
+
} catch {}
|
|
851
|
+
if (!afterRan && failed) try {
|
|
852
|
+
await this.#runInstrumentation("afterMutate");
|
|
853
|
+
} catch {}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Get the retrieval operations (for inspection/debugging)
|
|
858
|
+
*/
|
|
859
|
+
getRetrievalOperations() {
|
|
860
|
+
return this.#retrievalOps;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Get the mutation operations (for inspection/debugging)
|
|
864
|
+
*/
|
|
865
|
+
getMutationOperations() {
|
|
866
|
+
return this.#mutationOps;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* @internal
|
|
870
|
+
* Add a retrieval operation (used by TypedUnitOfWork)
|
|
871
|
+
*/
|
|
872
|
+
addRetrievalOperation(op) {
|
|
873
|
+
if (this.state !== "building-retrieval") throw new Error(`Cannot add retrieval operation in state ${this.state}. Must be in building-retrieval state.`);
|
|
874
|
+
this.#retrievalOps.push(op);
|
|
875
|
+
return this.#retrievalOps.length - 1;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* @internal
|
|
879
|
+
* Add a mutation operation (used by TypedUnitOfWork)
|
|
880
|
+
*/
|
|
881
|
+
addMutationOperation(op) {
|
|
882
|
+
if (this.state === "executed") throw new Error(`Cannot add mutation operation in executed state.`);
|
|
883
|
+
this.#mutationOps.push(op);
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Get the IDs of created entities after executeMutations() has been called.
|
|
887
|
+
* Returns FragnoId objects with external IDs (always available) and internal IDs
|
|
888
|
+
* (available when database supports RETURNING).
|
|
889
|
+
*
|
|
890
|
+
* @throws Error if called before executeMutations()
|
|
891
|
+
* @returns Array of FragnoIds in the same order as create() calls
|
|
892
|
+
*/
|
|
893
|
+
getCreatedIds() {
|
|
894
|
+
if (this.state !== "executed") throw new Error(`getCreatedIds() can only be called after executeMutations(). Current state: ${this.state}`);
|
|
895
|
+
const createdIds = [];
|
|
896
|
+
let createIndex = 0;
|
|
897
|
+
for (const op of this.#mutationOps) if (op.type === "create") {
|
|
898
|
+
const internalId = this.#createdInternalIds[createIndex] ?? void 0;
|
|
899
|
+
createdIds.push(new FragnoId({
|
|
900
|
+
externalId: op.generatedExternalId,
|
|
901
|
+
internalId,
|
|
902
|
+
version: 0
|
|
903
|
+
}));
|
|
904
|
+
createIndex++;
|
|
905
|
+
}
|
|
906
|
+
return createdIds;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* @internal
|
|
910
|
+
* Compile the unit of work to executable queries for testing
|
|
911
|
+
*/
|
|
912
|
+
compile(compiler) {
|
|
913
|
+
const retrievalBatch = [];
|
|
914
|
+
for (const op of this.#retrievalOps) {
|
|
915
|
+
const compiled = compiler.compileRetrievalOperation(op);
|
|
916
|
+
if (compiled !== null) retrievalBatch.push(compiled);
|
|
917
|
+
}
|
|
918
|
+
const mutationBatch = [];
|
|
919
|
+
for (const op of this.#mutationOps) {
|
|
920
|
+
const compiled = compiler.compileMutationOperation(op);
|
|
921
|
+
if (compiled !== null) {
|
|
922
|
+
compiled.uowId = this.#idempotencyKey;
|
|
923
|
+
mutationBatch.push(compiled);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
name: this.#name,
|
|
928
|
+
retrievalBatch,
|
|
929
|
+
mutationBatch
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
/**
|
|
934
|
+
* A typed facade around a UnitOfWork that provides type-safe operations for a specific schema.
|
|
935
|
+
* All operations are stored in the underlying UOW, but this facade ensures type safety and
|
|
936
|
+
* filters retrieval results to only include operations added through this facade.
|
|
937
|
+
*/
|
|
938
|
+
var TypedUnitOfWork = class {
|
|
939
|
+
#schema;
|
|
940
|
+
#namespace;
|
|
941
|
+
#uow;
|
|
942
|
+
#operationIndices = [];
|
|
943
|
+
#cachedRetrievalPhase;
|
|
944
|
+
constructor(schema, namespace, uow) {
|
|
945
|
+
this.#schema = schema;
|
|
946
|
+
this.#namespace = namespace;
|
|
947
|
+
this.#uow = uow;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Type-only access to retrieval results for this typed UOW.
|
|
951
|
+
*/
|
|
952
|
+
get $results() {
|
|
953
|
+
throw new Error("type only");
|
|
954
|
+
}
|
|
955
|
+
get schema() {
|
|
956
|
+
return this.#schema;
|
|
957
|
+
}
|
|
958
|
+
get name() {
|
|
959
|
+
return this.#uow.name;
|
|
960
|
+
}
|
|
961
|
+
get idempotencyKey() {
|
|
962
|
+
return this.#uow.idempotencyKey;
|
|
963
|
+
}
|
|
964
|
+
get state() {
|
|
965
|
+
return this.#uow.state;
|
|
966
|
+
}
|
|
967
|
+
get retrievalPhase() {
|
|
968
|
+
if (!this.#cachedRetrievalPhase) this.#cachedRetrievalPhase = this.#uow.retrievalPhase.then((allResults) => {
|
|
969
|
+
const allOperations = this.#uow.getRetrievalOperations();
|
|
970
|
+
return this.#operationIndices.map((opIndex) => {
|
|
971
|
+
const result = allResults[opIndex];
|
|
972
|
+
const operation = allOperations[opIndex];
|
|
973
|
+
if (operation?.type === "find" && operation.withSingleResult) return Array.isArray(result) ? result[0] ?? null : result;
|
|
974
|
+
return result;
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
return this.#cachedRetrievalPhase;
|
|
978
|
+
}
|
|
979
|
+
get mutationPhase() {
|
|
980
|
+
return this.#uow.mutationPhase;
|
|
981
|
+
}
|
|
982
|
+
getRetrievalOperations() {
|
|
983
|
+
return this.#uow.getRetrievalOperations();
|
|
984
|
+
}
|
|
985
|
+
getMutationOperations() {
|
|
986
|
+
return this.#uow.getMutationOperations();
|
|
987
|
+
}
|
|
988
|
+
getCreatedIds() {
|
|
989
|
+
return this.#uow.getCreatedIds();
|
|
990
|
+
}
|
|
991
|
+
async executeRetrieve() {
|
|
992
|
+
return this.#uow.executeRetrieve();
|
|
993
|
+
}
|
|
994
|
+
async executeMutations() {
|
|
995
|
+
return this.#uow.executeMutations();
|
|
996
|
+
}
|
|
997
|
+
restrict(options) {
|
|
998
|
+
return this.#uow.restrict(options);
|
|
999
|
+
}
|
|
1000
|
+
signalReadyForRetrieval() {
|
|
1001
|
+
this.#uow.signalReadyForRetrieval();
|
|
1002
|
+
}
|
|
1003
|
+
signalReadyForMutation() {
|
|
1004
|
+
this.#uow.signalReadyForMutation();
|
|
1005
|
+
}
|
|
1006
|
+
reset() {
|
|
1007
|
+
return this.#uow.reset();
|
|
1008
|
+
}
|
|
1009
|
+
forSchema(schema, hooks) {
|
|
1010
|
+
return this.#uow.forSchema(schema, hooks);
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Database timestamp helper for inserts.
|
|
1014
|
+
*/
|
|
1015
|
+
now() {
|
|
1016
|
+
return dbNow();
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Build a database interval for use with now().plus(...).
|
|
1020
|
+
*/
|
|
1021
|
+
interval(input) {
|
|
1022
|
+
return dbInterval(input);
|
|
1023
|
+
}
|
|
1024
|
+
registerSchema(schema, namespace) {
|
|
1025
|
+
this.#uow.registerSchema(schema, namespace);
|
|
1026
|
+
}
|
|
1027
|
+
compile(compiler) {
|
|
1028
|
+
return this.#uow.compile(compiler);
|
|
1029
|
+
}
|
|
1030
|
+
find(tableName, builderFn) {
|
|
1031
|
+
const table = this.#schema.tables[tableName];
|
|
1032
|
+
if (!table) throw new Error(`Table ${tableName} not found in schema`);
|
|
1033
|
+
const builder = new FindBuilder(tableName, table);
|
|
1034
|
+
if (builderFn) builderFn(builder);
|
|
1035
|
+
else builder.whereIndex("primary");
|
|
1036
|
+
const { indexName, options, type } = builder.build();
|
|
1037
|
+
const operationIndex = this.#uow.addRetrievalOperation({
|
|
1038
|
+
type,
|
|
1039
|
+
schema: this.#schema,
|
|
1040
|
+
namespace: this.#namespace,
|
|
1041
|
+
table,
|
|
1042
|
+
indexName,
|
|
1043
|
+
options,
|
|
1044
|
+
readTracking: this.#uow.readTrackingEnabled
|
|
1045
|
+
});
|
|
1046
|
+
this.#operationIndices.push(operationIndex);
|
|
1047
|
+
return this;
|
|
1048
|
+
}
|
|
1049
|
+
findFirst(tableName, builderFn) {
|
|
1050
|
+
const table = this.#schema.tables[tableName];
|
|
1051
|
+
if (!table) throw new Error(`Table ${tableName} not found in schema`);
|
|
1052
|
+
const builder = new FindBuilder(tableName, table);
|
|
1053
|
+
if (builderFn) builderFn(builder);
|
|
1054
|
+
else builder.whereIndex("primary");
|
|
1055
|
+
builder.pageSize(1);
|
|
1056
|
+
const { indexName, options, type } = builder.build();
|
|
1057
|
+
const operationIndex = this.#uow.addRetrievalOperation({
|
|
1058
|
+
type,
|
|
1059
|
+
schema: this.#schema,
|
|
1060
|
+
namespace: this.#namespace,
|
|
1061
|
+
table,
|
|
1062
|
+
indexName,
|
|
1063
|
+
options,
|
|
1064
|
+
withSingleResult: true,
|
|
1065
|
+
readTracking: this.#uow.readTrackingEnabled
|
|
1066
|
+
});
|
|
1067
|
+
this.#operationIndices.push(operationIndex);
|
|
1068
|
+
return this;
|
|
1069
|
+
}
|
|
1070
|
+
findWithCursor(tableName, builderFn) {
|
|
1071
|
+
const table = this.#schema.tables[tableName];
|
|
1072
|
+
if (!table) throw new Error(`Table ${tableName} not found in schema`);
|
|
1073
|
+
const builder = new FindBuilder(tableName, table);
|
|
1074
|
+
builderFn(builder);
|
|
1075
|
+
const { indexName, options, type } = builder.build();
|
|
1076
|
+
const operationIndex = this.#uow.addRetrievalOperation({
|
|
1077
|
+
type,
|
|
1078
|
+
schema: this.#schema,
|
|
1079
|
+
namespace: this.#namespace,
|
|
1080
|
+
table,
|
|
1081
|
+
indexName,
|
|
1082
|
+
options,
|
|
1083
|
+
withCursor: true,
|
|
1084
|
+
readTracking: this.#uow.readTrackingEnabled
|
|
1085
|
+
});
|
|
1086
|
+
this.#operationIndices.push(operationIndex);
|
|
1087
|
+
return this;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Generate a new ID for a table without creating a record.
|
|
1091
|
+
* This is useful when you need to reference an ID before actually creating the record,
|
|
1092
|
+
* or when you need to pass the ID to external services.
|
|
1093
|
+
*
|
|
1094
|
+
* @example
|
|
1095
|
+
* ```ts
|
|
1096
|
+
* const userId = uow.generateId("users");
|
|
1097
|
+
* // Use userId in related records or pass to external services
|
|
1098
|
+
* uow.create("users", { id: userId, name: "John" });
|
|
1099
|
+
* ```
|
|
1100
|
+
*/
|
|
1101
|
+
generateId(tableName) {
|
|
1102
|
+
return generateId(this.#schema, tableName);
|
|
1103
|
+
}
|
|
1104
|
+
create(tableName, values) {
|
|
1105
|
+
const tableSchema = this.#schema.tables[tableName];
|
|
1106
|
+
if (!tableSchema) throw new Error(`Table ${tableName} not found in schema`);
|
|
1107
|
+
const idColumn = tableSchema.getIdColumn();
|
|
1108
|
+
let externalId;
|
|
1109
|
+
let updatedValues = values;
|
|
1110
|
+
const providedIdValue = values[idColumn.name];
|
|
1111
|
+
if (providedIdValue !== void 0) if (typeof providedIdValue === "object" && providedIdValue !== null && "externalId" in providedIdValue) externalId = providedIdValue.externalId;
|
|
1112
|
+
else externalId = providedIdValue;
|
|
1113
|
+
else {
|
|
1114
|
+
const generated = idColumn.generateDefaultValue();
|
|
1115
|
+
if (generated === void 0) throw new Error(`No ID value provided and ID column ${idColumn.name} has no default generator`);
|
|
1116
|
+
externalId = generated;
|
|
1117
|
+
updatedValues = {
|
|
1118
|
+
...values,
|
|
1119
|
+
[idColumn.name]: externalId
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
this.#uow.addMutationOperation({
|
|
1123
|
+
type: "create",
|
|
1124
|
+
schema: this.#schema,
|
|
1125
|
+
namespace: this.#namespace,
|
|
1126
|
+
table: tableName,
|
|
1127
|
+
values: updatedValues,
|
|
1128
|
+
generatedExternalId: externalId
|
|
1129
|
+
});
|
|
1130
|
+
return FragnoId.fromExternal(externalId, 0);
|
|
1131
|
+
}
|
|
1132
|
+
update(tableName, id, builderFn) {
|
|
1133
|
+
const builder = new UpdateBuilder(tableName, id);
|
|
1134
|
+
builderFn(builder);
|
|
1135
|
+
const { id: opId, checkVersion, set } = builder.build();
|
|
1136
|
+
this.#uow.addMutationOperation({
|
|
1137
|
+
type: "update",
|
|
1138
|
+
schema: this.#schema,
|
|
1139
|
+
namespace: this.#namespace,
|
|
1140
|
+
table: tableName,
|
|
1141
|
+
id: opId,
|
|
1142
|
+
checkVersion,
|
|
1143
|
+
set
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
delete(tableName, id, builderFn) {
|
|
1147
|
+
const builder = new DeleteBuilder(tableName, id);
|
|
1148
|
+
builderFn?.(builder);
|
|
1149
|
+
const { id: opId, checkVersion } = builder.build();
|
|
1150
|
+
this.#uow.addMutationOperation({
|
|
1151
|
+
type: "delete",
|
|
1152
|
+
schema: this.#schema,
|
|
1153
|
+
namespace: this.#namespace,
|
|
1154
|
+
table: tableName,
|
|
1155
|
+
id: opId,
|
|
1156
|
+
checkVersion
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Check that a record's version hasn't changed since retrieval.
|
|
1161
|
+
* This is useful for ensuring related records remain unchanged during a transaction.
|
|
1162
|
+
*
|
|
1163
|
+
* @param tableName - The table name
|
|
1164
|
+
* @param id - The FragnoId with version information (string IDs are not allowed)
|
|
1165
|
+
* @throws Error if the ID is a string without version information
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* ```ts
|
|
1169
|
+
* // Ensure both accounts haven't changed before creating a transfer
|
|
1170
|
+
* uow.check("accounts", fromAccount.id);
|
|
1171
|
+
* uow.check("accounts", toAccount.id);
|
|
1172
|
+
* uow.create("transactions", { fromAccountId, toAccountId, amount });
|
|
1173
|
+
* ```
|
|
1174
|
+
*/
|
|
1175
|
+
check(tableName, id) {
|
|
1176
|
+
this.#uow.addMutationOperation({
|
|
1177
|
+
type: "check",
|
|
1178
|
+
schema: this.#schema,
|
|
1179
|
+
namespace: this.#namespace,
|
|
1180
|
+
table: tableName,
|
|
1181
|
+
id
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
get $hooks() {
|
|
1185
|
+
throw new Error("type only");
|
|
1186
|
+
}
|
|
1187
|
+
get namespace() {
|
|
1188
|
+
return this.#namespace;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Trigger a hook to be executed after the transaction commits.
|
|
1192
|
+
* The namespace is automatically injected from this typed view's schema registration.
|
|
1193
|
+
*/
|
|
1194
|
+
triggerHook(hookName, payload, options) {
|
|
1195
|
+
if (this.#namespace === void 0) throw new Error("Cannot trigger hook: schema has no registered namespace. Ensure the schema is registered with a namespace via registerSchema().");
|
|
1196
|
+
const hookNamespace = this.#namespace ?? this.#schema.name;
|
|
1197
|
+
this.#uow.triggerHook(hookNamespace, hookName, payload, options);
|
|
1198
|
+
}
|
|
1199
|
+
getTriggeredHooks() {
|
|
1200
|
+
return this.#uow.getTriggeredHooks();
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
//#endregion
|
|
1205
|
+
export { TypedUnitOfWork, UnitOfWork, createUnitOfWork };
|
|
1206
|
+
//# sourceMappingURL=unit-of-work.js.map
|