@fragno-dev/db 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +327 -160
- package/CHANGELOG.md +74 -0
- package/README.md +24 -0
- package/dist/adapters/adapters.d.ts +1 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.js +30 -3
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
- package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +24 -20
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +8 -9
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +10 -6
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-uow.js +236 -13
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
- package/dist/adapters/in-memory/options.d.ts +2 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -1
- package/dist/adapters/in-memory/options.js +3 -2
- package/dist/adapters/in-memory/options.js.map +1 -1
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
- package/dist/adapters/in-memory/store.js +1 -1
- package/dist/adapters/in-memory/store.js.map +1 -1
- package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/browser/adapters/adapters.d.ts +61 -0
- package/dist/browser/adapters/adapters.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
- package/dist/browser/adapters/in-memory/index.d.ts +2 -0
- package/dist/browser/adapters/in-memory/options.d.ts +1 -0
- package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
- package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/browser/durable-hooks.d.ts +3 -0
- package/dist/browser/fragments/internal-fragment.d.ts +317 -0
- package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/browser/hooks/hooks.d.ts +146 -0
- package/dist/browser/hooks/hooks.d.ts.map +1 -0
- package/dist/browser/id.js +1 -0
- package/dist/browser/internal/adapter-registry.d.ts +4 -0
- package/dist/browser/internal/outbox-state.d.ts +2 -0
- package/dist/browser/mod.d.ts +15 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +17 -0
- package/dist/browser/mod.js.map +1 -0
- package/dist/browser/mod2.d.ts +48 -0
- package/dist/browser/mod2.d.ts.map +1 -0
- package/dist/browser/naming/sql-naming.d.ts +19 -0
- package/dist/browser/naming/sql-naming.d.ts.map +1 -0
- package/dist/browser/outbox/outbox.d.ts +21 -0
- package/dist/browser/outbox/outbox.d.ts.map +1 -0
- package/dist/browser/query/column-defaults.js +1 -0
- package/dist/browser/query/condition-builder.d.ts +44 -0
- package/dist/browser/query/condition-builder.d.ts.map +1 -0
- package/dist/browser/query/condition-builder.js +97 -0
- package/dist/browser/query/condition-builder.js.map +1 -0
- package/dist/browser/query/cursor.d.ts +105 -0
- package/dist/browser/query/cursor.d.ts.map +1 -0
- package/dist/browser/query/cursor.js +150 -0
- package/dist/browser/query/cursor.js.map +1 -0
- package/dist/browser/query/db-now.d.ts +22 -0
- package/dist/browser/query/db-now.d.ts.map +1 -0
- package/dist/browser/query/db-now.js +33 -0
- package/dist/browser/query/db-now.js.map +1 -0
- package/dist/browser/query/orm/orm.d.ts +18 -0
- package/dist/browser/query/orm/orm.d.ts.map +1 -0
- package/dist/browser/query/simple-query-interface.d.ts +108 -0
- package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
- package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/browser/query/value-encoding.js +38 -0
- package/dist/browser/query/value-encoding.js.map +1 -0
- package/dist/browser/schema/create.d.ts +326 -0
- package/dist/browser/schema/create.d.ts.map +1 -0
- package/dist/browser/schema/create.js +89 -0
- package/dist/browser/schema/create.js.map +1 -0
- package/dist/browser/schema/generate-id.js +28 -0
- package/dist/browser/schema/generate-id.js.map +1 -0
- package/dist/browser/shared/providers.d.ts +6 -0
- package/dist/browser/shared/providers.d.ts.map +1 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
- package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/browser/sync/commands.d.ts +15 -0
- package/dist/browser/sync/commands.d.ts.map +1 -0
- package/dist/browser/sync/commands.js +27 -0
- package/dist/browser/sync/commands.js.map +1 -0
- package/dist/browser/sync/types.d.ts +63 -0
- package/dist/browser/sync/types.d.ts.map +1 -0
- package/dist/browser/util/types.d.ts +8 -0
- package/dist/browser/util/types.d.ts.map +1 -0
- package/dist/browser/with-database.d.ts +29 -0
- package/dist/browser/with-database.d.ts.map +1 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +5 -0
- package/dist/db-fragment-definition-builder.d.ts +85 -28
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +374 -46
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.js +23 -55
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
- package/dist/dispatchers/node/dispatcher.d.ts +14 -0
- package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/node/dispatcher.js +80 -0
- package/dist/dispatchers/node/dispatcher.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +5 -10
- package/dist/dispatchers/node/index.d.ts.map +1 -1
- package/dist/dispatchers/node/index.js +21 -53
- package/dist/dispatchers/node/index.js.map +1 -1
- package/dist/durable-hooks.d.ts +31 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +23 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +128 -27
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +125 -78
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +138 -3
- package/dist/fragments/internal-fragment.routes.js.map +1 -1
- package/dist/fragments/internal-fragment.schema.d.ts +7 -1
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.schema.js +18 -1
- package/dist/fragments/internal-fragment.schema.js.map +1 -1
- package/dist/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-logger.js +75 -0
- package/dist/hooks/durable-hooks-logger.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +1 -14
- package/dist/hooks/durable-hooks-processor.js +58 -10
- package/dist/hooks/durable-hooks-processor.js.map +1 -1
- package/dist/hooks/durable-hooks-runtime.js +44 -0
- package/dist/hooks/durable-hooks-runtime.js.map +1 -0
- package/dist/hooks/hooks.d.ts +60 -2
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +214 -53
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.js +2 -2
- package/dist/internal/adapter-registry.d.ts +11 -0
- package/dist/internal/adapter-registry.d.ts.map +1 -0
- package/dist/internal/adapter-registry.js +135 -0
- package/dist/internal/adapter-registry.js.map +1 -0
- package/dist/internal/outbox-state.d.ts +2 -0
- package/dist/internal/outbox-state.js +26 -0
- package/dist/internal/outbox-state.js.map +1 -0
- package/dist/migration-engine/auto-from-schema.d.ts +33 -0
- package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
- package/dist/migration-engine/auto-from-schema.js +210 -27
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +17 -5
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.d.ts +113 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +12 -11
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +10 -10
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts.map +1 -1
- package/dist/naming/sql-naming.js.map +1 -1
- package/dist/outbox/outbox-builder.js.map +1 -1
- package/dist/outbox/outbox.d.ts +3 -1
- package/dist/outbox/outbox.d.ts.map +1 -1
- package/dist/outbox/outbox.js.map +1 -1
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/condition-builder.d.ts +7 -1
- package/dist/query/condition-builder.d.ts.map +1 -1
- package/dist/query/condition-builder.js +5 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/cursor-client.d.ts +105 -0
- package/dist/query/cursor-client.d.ts.map +1 -0
- package/dist/query/cursor-client.js +165 -0
- package/dist/query/cursor-client.js.map +1 -0
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +7 -1
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +15 -1
- package/dist/query/db-now.d.ts.map +1 -1
- package/dist/query/db-now.js +30 -2
- package/dist/query/db-now.js.map +1 -1
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +2 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +7 -3
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +50 -6
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +8 -1
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +69 -25
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +91 -16
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts.map +1 -1
- package/dist/schema/validator.js.map +1 -1
- package/dist/schema-output/drizzle.d.ts.map +1 -1
- package/dist/schema-output/drizzle.js +8 -6
- package/dist/schema-output/drizzle.js.map +1 -1
- package/dist/schema-output/prisma.d.ts.map +1 -1
- package/dist/schema-output/prisma.js +21 -10
- package/dist/schema-output/prisma.js.map +1 -1
- package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
- package/dist/sql-driver/sql.js.map +1 -1
- package/dist/sync/commands.d.ts +15 -0
- package/dist/sync/commands.d.ts.map +1 -0
- package/dist/sync/commands.js +27 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/read-tracking.d.ts +25 -0
- package/dist/sync/read-tracking.d.ts.map +1 -0
- package/dist/sync/read-tracking.js +148 -0
- package/dist/sync/read-tracking.js.map +1 -0
- package/dist/sync/submit.js +213 -0
- package/dist/sync/submit.js.map +1 -0
- package/dist/sync/types.d.ts +63 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/util/default-database-adapter.js +6 -1
- package/dist/util/default-database-adapter.js.map +1 -1
- package/dist/with-database.d.ts +3 -6
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +7 -15
- package/dist/with-database.js.map +1 -1
- package/package.json +33 -41
- package/src/adapters/adapters.ts +5 -4
- package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
- package/src/adapters/drizzle/test-utils.ts +2 -1
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
- package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
- package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
- package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
- package/src/adapters/generic-sql/migration/executor.ts +47 -4
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
- package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
- package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
- package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
- package/src/adapters/generic-sql/query/select-builder.ts +2 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
- package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
- package/src/adapters/generic-sql/query/where-builder.ts +48 -29
- package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
- package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
- package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
- package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
- package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
- package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
- package/src/adapters/generic-sql/uow-decoder.ts +3 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
- package/src/adapters/generic-sql/uow-encoder.ts +14 -18
- package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
- package/src/adapters/in-memory/condition-evaluator.ts +9 -4
- package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
- package/src/adapters/in-memory/in-memory-uow.ts +402 -26
- package/src/adapters/in-memory/options.test.ts +1 -0
- package/src/adapters/in-memory/options.ts +5 -1
- package/src/adapters/in-memory/outbox.test.ts +361 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
- package/src/adapters/in-memory/reference-resolution.ts +2 -2
- package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
- package/src/adapters/in-memory/store.test.ts +1 -0
- package/src/adapters/in-memory/store.ts +3 -3
- package/src/adapters/in-memory/value-normalization.test.ts +1 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
- package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
- package/src/adapters/shared/uow-operation-compiler.ts +3 -3
- package/src/browser/mod.ts +64 -0
- package/src/client.ts +19 -0
- package/src/db-fragment-definition-builder.test.ts +821 -47
- package/src/db-fragment-definition-builder.ts +857 -110
- package/src/db-fragment-instantiator.test.ts +114 -90
- package/src/db-fragment-integration.test.ts +9 -6
- package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
- package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
- package/src/dispatchers/cloudflare-do/index.ts +49 -90
- package/src/dispatchers/node/dispatcher.ts +112 -0
- package/src/dispatchers/node/index.test.ts +43 -14
- package/src/dispatchers/node/index.ts +38 -75
- package/src/durable-hooks.test.ts +80 -0
- package/src/durable-hooks.ts +67 -0
- package/src/fragments/internal-fragment.routes.test.ts +570 -0
- package/src/fragments/internal-fragment.routes.ts +297 -5
- package/src/fragments/internal-fragment.schema.ts +45 -1
- package/src/fragments/internal-fragment.test.ts +223 -251
- package/src/fragments/internal-fragment.ts +278 -154
- package/src/hooks/durable-hooks-logger.ts +126 -0
- package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
- package/src/hooks/durable-hooks-processor.test.ts +179 -14
- package/src/hooks/durable-hooks-processor.ts +120 -14
- package/src/hooks/durable-hooks-runtime.test.ts +65 -0
- package/src/hooks/durable-hooks-runtime.ts +81 -0
- package/src/hooks/hooks.test.ts +314 -53
- package/src/hooks/hooks.ts +360 -81
- package/src/id.test.ts +34 -0
- package/src/id.ts +1 -3
- package/src/internal/adapter-registry.test.ts +93 -0
- package/src/internal/adapter-registry.ts +239 -0
- package/src/internal/outbox-state.ts +43 -0
- package/src/migration-engine/auto-from-schema.test.ts +93 -0
- package/src/migration-engine/auto-from-schema.ts +360 -42
- package/src/migration-engine/create.test.ts +2 -1
- package/src/migration-engine/create.ts +1 -1
- package/src/migration-engine/generation-engine.test.ts +66 -9
- package/src/migration-engine/generation-engine.ts +31 -10
- package/src/migration-engine/shared.ts +13 -0
- package/src/mod.ts +45 -27
- package/src/naming/sql-naming.ts +1 -0
- package/src/outbox/outbox-builder.ts +2 -2
- package/src/outbox/outbox.test.ts +216 -45
- package/src/outbox/outbox.ts +3 -1
- package/src/query/column-defaults.ts +1 -1
- package/src/query/condition-builder.test.ts +15 -0
- package/src/query/condition-builder.ts +7 -0
- package/src/query/cursor-client.test.ts +70 -0
- package/src/query/cursor-client.ts +263 -0
- package/src/query/cursor.test.ts +3 -2
- package/src/query/cursor.ts +15 -3
- package/src/query/db-now.ts +69 -2
- package/src/query/orm/orm.ts +2 -2
- package/src/query/query-type.test.ts +2 -1
- package/src/query/serialize/create-sql-serializer.ts +3 -3
- package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
- package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
- package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
- package/src/query/simple-query-interface.ts +10 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
- package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
- package/src/query/unit-of-work/retry-policy.test.ts +1 -0
- package/src/query/unit-of-work/tx-builder.test.ts +73 -1
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
- package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
- package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
- package/src/query/unit-of-work/unit-of-work.ts +105 -19
- package/src/query/value-decoding.test.ts +50 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +1 -0
- package/src/query/value-encoding.ts +1 -1
- package/src/schema/create.test.ts +164 -5
- package/src/schema/create.ts +222 -24
- package/src/schema/generate-id.test.ts +1 -0
- package/src/schema/serialize.test.ts +4 -3
- package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
- package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
- package/src/schema/type-conversion/type-mapping.test.ts +2 -1
- package/src/schema/validator.test.ts +4 -2
- package/src/schema/validator.ts +1 -0
- package/src/schema-output/drizzle.test.ts +72 -19
- package/src/schema-output/drizzle.ts +24 -18
- package/src/schema-output/prisma.test.ts +172 -14
- package/src/schema-output/prisma.ts +34 -14
- package/src/sql-driver/better-sqlite3.test.ts +5 -3
- package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
- package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
- package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
- package/src/sql-driver/query-executor/query-executor.ts +1 -1
- package/src/sql-driver/sql-driver-adapter.ts +2 -2
- package/src/sql-driver/sql.ts +2 -1
- package/src/sql-driver/sqlocal.test.ts +4 -2
- package/src/sync/commands.test.ts +39 -0
- package/src/sync/commands.ts +51 -0
- package/src/sync/conflict-checker.test.ts +450 -0
- package/src/sync/conflict-checker.ts +248 -0
- package/src/sync/index.ts +14 -0
- package/src/sync/plan.ts +9 -0
- package/src/sync/read-tracking.test.ts +177 -0
- package/src/sync/read-tracking.ts +287 -0
- package/src/sync/submit.test.ts +205 -0
- package/src/sync/submit.ts +328 -0
- package/src/sync/types.ts +80 -0
- package/src/util/default-database-adapter.ts +15 -2
- package/src/with-database.ts +20 -50
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +38 -26
- package/vitest.config.ts +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/packages/fragno/dist/api/bind-services.js +0 -20
- package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
- package/dist/packages/fragno/dist/api/error.js +0 -48
- package/dist/packages/fragno/dist/api/error.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
- package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/route.js +0 -10
- package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
- package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
- package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
- package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
- package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/route.js +0 -30
- package/dist/packages/fragno/dist/api/route.js.map +0 -1
- package/dist/packages/fragno/dist/internal/symbols.js +0 -10
- package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
- package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
|
@@ -1,19 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect, vi, expectTypeOf } from "vitest";
|
|
2
|
+
|
|
1
3
|
import fs from "node:fs";
|
|
2
4
|
import os from "node:os";
|
|
3
5
|
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
+
|
|
7
|
+
import { RequestContextStorage } from "@fragno-dev/core/internal/request-context-storage";
|
|
8
|
+
import SQLite from "better-sqlite3";
|
|
9
|
+
import { SqliteDialect } from "kysely";
|
|
10
|
+
|
|
11
|
+
import { defineFragment, instantiate } from "@fragno-dev/core";
|
|
12
|
+
|
|
13
|
+
import type { DatabaseAdapter, DatabaseContextStorage } from "./adapters/adapters";
|
|
14
|
+
import { BetterSQLite3DriverConfig } from "./adapters/generic-sql/driver-config";
|
|
15
|
+
import { SqlAdapter } from "./adapters/generic-sql/generic-sql-adapter";
|
|
6
16
|
import {
|
|
7
17
|
DatabaseFragmentDefinitionBuilder,
|
|
8
18
|
type ImplicitDatabaseDependencies,
|
|
9
19
|
} from "./db-fragment-definition-builder";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
20
|
+
import { internalSchema } from "./fragments/internal-fragment";
|
|
21
|
+
import { getDurableHooksRuntimeByToken } from "./hooks/durable-hooks-runtime";
|
|
22
|
+
import type { HookFn } from "./hooks/hooks";
|
|
23
|
+
import * as hooks from "./hooks/hooks";
|
|
24
|
+
import { getInternalFragment, getRegistryForAdapterSync } from "./internal/adapter-registry";
|
|
25
|
+
import { suffixNamingStrategy, sanitizeNamespace } from "./naming/sql-naming";
|
|
12
26
|
import type { SimpleQueryInterface } from "./query/simple-query-interface";
|
|
13
|
-
import type { DatabaseAdapter } from "./adapters/adapters";
|
|
14
27
|
import * as executeUnitOfWork from "./query/unit-of-work/execute-unit-of-work";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
28
|
+
import type { IUnitOfWork } from "./query/unit-of-work/unit-of-work";
|
|
29
|
+
import { schema, column, idColumn } from "./schema/create";
|
|
30
|
+
import { defineSyncCommands } from "./sync/commands";
|
|
31
|
+
import { withDatabase } from "./with-database";
|
|
17
32
|
|
|
18
33
|
// Create a test schema
|
|
19
34
|
const testSchema = schema("test", (s) => {
|
|
@@ -29,14 +44,17 @@ type TestSchema = typeof testSchema;
|
|
|
29
44
|
|
|
30
45
|
// Mock database adapter
|
|
31
46
|
function createMockAdapter(): DatabaseAdapter {
|
|
47
|
+
const createMockUow = () => ({
|
|
48
|
+
forSchema: vi.fn(),
|
|
49
|
+
executeRetrieve: vi.fn(),
|
|
50
|
+
executeMutations: vi.fn(),
|
|
51
|
+
registerSchema: vi.fn(),
|
|
52
|
+
reset: vi.fn(),
|
|
53
|
+
});
|
|
54
|
+
|
|
32
55
|
const mockdb = {
|
|
33
|
-
createUnitOfWork: vi.fn(() => (
|
|
34
|
-
|
|
35
|
-
executeRetrieve: vi.fn(),
|
|
36
|
-
executeMutations: vi.fn(),
|
|
37
|
-
registerSchema: vi.fn(),
|
|
38
|
-
reset: vi.fn(),
|
|
39
|
-
})),
|
|
56
|
+
createUnitOfWork: vi.fn(() => createMockUow()),
|
|
57
|
+
createBaseUnitOfWork: vi.fn(() => createMockUow()),
|
|
40
58
|
} as unknown as SimpleQueryInterface<TestSchema>;
|
|
41
59
|
|
|
42
60
|
return {
|
|
@@ -87,8 +105,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
87
105
|
|
|
88
106
|
const definition = defineFragment("test-frag")
|
|
89
107
|
.extend(withDatabase(testSchema))
|
|
90
|
-
.withDependencies(({
|
|
91
|
-
expect(
|
|
108
|
+
.withDependencies(({ databaseAdapter }) => {
|
|
109
|
+
expect(databaseAdapter).toBeDefined();
|
|
92
110
|
return {
|
|
93
111
|
customDep: "value",
|
|
94
112
|
};
|
|
@@ -101,7 +119,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
101
119
|
});
|
|
102
120
|
|
|
103
121
|
expect(deps.customDep).toBe("value");
|
|
104
|
-
expect(deps.
|
|
122
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
123
|
+
expect(deps.createUnitOfWork).toBeDefined();
|
|
105
124
|
expect(deps.schema).toBeDefined();
|
|
106
125
|
});
|
|
107
126
|
|
|
@@ -121,7 +140,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
121
140
|
});
|
|
122
141
|
|
|
123
142
|
expect(deps.apiKey).toBe("key123");
|
|
124
|
-
expect(deps.
|
|
143
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
144
|
+
expect(deps.createUnitOfWork).toBeDefined();
|
|
125
145
|
expect(deps.schema).toBeDefined();
|
|
126
146
|
});
|
|
127
147
|
|
|
@@ -131,7 +151,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
131
151
|
const definition = defineFragment("test-frag")
|
|
132
152
|
.extend(withDatabase(testSchema))
|
|
133
153
|
.providesService("users", ({ deps }) => {
|
|
134
|
-
expect(deps.
|
|
154
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
135
155
|
return {
|
|
136
156
|
list: () => "users list",
|
|
137
157
|
};
|
|
@@ -164,7 +184,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
164
184
|
}))
|
|
165
185
|
.extend(withDatabase(testSchema))
|
|
166
186
|
.providesService("users", ({ deps }) => {
|
|
167
|
-
expect(deps.
|
|
187
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
168
188
|
return {
|
|
169
189
|
list: () => "users list",
|
|
170
190
|
};
|
|
@@ -238,7 +258,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
238
258
|
const definition = defineFragment("test-frag")
|
|
239
259
|
.extend(withDatabase(testSchema))
|
|
240
260
|
.providesBaseService(({ deps }) => {
|
|
241
|
-
expect(deps.
|
|
261
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
242
262
|
return {
|
|
243
263
|
healthCheck: () => "healthy",
|
|
244
264
|
};
|
|
@@ -271,7 +291,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
271
291
|
}))
|
|
272
292
|
.extend(withDatabase(testSchema))
|
|
273
293
|
.providesBaseService(({ deps }) => {
|
|
274
|
-
expect(deps.
|
|
294
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
275
295
|
return {
|
|
276
296
|
status: () => "ok",
|
|
277
297
|
};
|
|
@@ -306,8 +326,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
306
326
|
|
|
307
327
|
const definition = defineFragment("test-frag")
|
|
308
328
|
.extend(withDatabase(testSchema))
|
|
309
|
-
.withDependencies(({
|
|
310
|
-
expect(
|
|
329
|
+
.withDependencies(({ databaseAdapter }) => {
|
|
330
|
+
expect(databaseAdapter).toBeDefined();
|
|
311
331
|
return {
|
|
312
332
|
apiKey: "secret",
|
|
313
333
|
};
|
|
@@ -322,7 +342,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
322
342
|
},
|
|
323
343
|
}))
|
|
324
344
|
.providesService("data", ({ deps }) => {
|
|
325
|
-
expect(deps.
|
|
345
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
326
346
|
return {
|
|
327
347
|
fetch: () => `Fetching with ${deps.apiKey}`,
|
|
328
348
|
};
|
|
@@ -375,15 +395,15 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
375
395
|
baseConfig: "config1",
|
|
376
396
|
}))
|
|
377
397
|
.extend(withDatabase(testSchema))
|
|
378
|
-
.withDependencies(({
|
|
379
|
-
expect(
|
|
398
|
+
.withDependencies(({ databaseAdapter }) => {
|
|
399
|
+
expect(databaseAdapter).toBeDefined();
|
|
380
400
|
// Second withDependencies replaces the first one
|
|
381
401
|
return {
|
|
382
402
|
enhancedConfig: "enhanced",
|
|
383
403
|
};
|
|
384
404
|
})
|
|
385
405
|
.providesService("combined", ({ deps }) => {
|
|
386
|
-
expect(deps.
|
|
406
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
387
407
|
return {
|
|
388
408
|
getConfig: () => deps.enhancedConfig,
|
|
389
409
|
};
|
|
@@ -398,7 +418,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
398
418
|
// Only the second withDependencies is used (plus implicit deps)
|
|
399
419
|
expect(deps).not.toHaveProperty("baseConfig");
|
|
400
420
|
expect(deps.enhancedConfig).toBe("enhanced");
|
|
401
|
-
expect(deps.
|
|
421
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
402
422
|
|
|
403
423
|
const combinedService = definition.namedServices!.combined({
|
|
404
424
|
config: {},
|
|
@@ -428,9 +448,9 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
428
448
|
};
|
|
429
449
|
})
|
|
430
450
|
.extend(withDatabase(testSchema))
|
|
431
|
-
.withDependencies(({ config,
|
|
451
|
+
.withDependencies(({ config, databaseAdapter }) => {
|
|
432
452
|
expectTypeOf(config).toExtend<MyConfig>();
|
|
433
|
-
expect(
|
|
453
|
+
expect(databaseAdapter).toBeDefined();
|
|
434
454
|
// Second withDependencies replaces the first, so combine them here
|
|
435
455
|
return {
|
|
436
456
|
connectionTimeout: config.timeout,
|
|
@@ -446,7 +466,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
446
466
|
|
|
447
467
|
expect(deps.connectionTimeout).toBe(5000);
|
|
448
468
|
expect(deps.dbConnectionString).toBe("postgres://localhost");
|
|
449
|
-
expect(deps.
|
|
469
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
450
470
|
});
|
|
451
471
|
|
|
452
472
|
it("recommended pattern: extend first then configure", () => {
|
|
@@ -463,8 +483,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
463
483
|
// Best practice: .extend() early, then add all your features
|
|
464
484
|
const definition = defineFragment("best-practice")
|
|
465
485
|
.extend(withDatabase(testSchema))
|
|
466
|
-
.withDependencies(({
|
|
467
|
-
expect(
|
|
486
|
+
.withDependencies(({ databaseAdapter }) => {
|
|
487
|
+
expect(databaseAdapter).toBeDefined();
|
|
468
488
|
return {
|
|
469
489
|
apiKey: "secret",
|
|
470
490
|
timeout: 5000,
|
|
@@ -474,7 +494,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
474
494
|
.usesOptionalService<"auth", AuthService>("auth")
|
|
475
495
|
.providesBaseService(({ deps }) => ({
|
|
476
496
|
healthCheck: () => {
|
|
477
|
-
expect(deps.
|
|
497
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
478
498
|
return `OK (timeout: ${deps.timeout})`;
|
|
479
499
|
},
|
|
480
500
|
}))
|
|
@@ -497,7 +517,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
497
517
|
// Verify all dependencies including implicit ones
|
|
498
518
|
expect(deps.apiKey).toBe("secret");
|
|
499
519
|
expect(deps.timeout).toBe(5000);
|
|
500
|
-
expect(deps.
|
|
520
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
501
521
|
expect(deps.schema).toBeDefined();
|
|
502
522
|
expect(deps.createUnitOfWork).toBeDefined();
|
|
503
523
|
|
|
@@ -537,8 +557,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
537
557
|
const mockAdapter = createMockAdapter();
|
|
538
558
|
|
|
539
559
|
const definition = withDatabase(testSchema)(defineFragment("db-frag"))
|
|
540
|
-
.withDependencies(({
|
|
541
|
-
expect(db).toBeDefined();
|
|
560
|
+
.withDependencies(({ databaseAdapter }) => {
|
|
542
561
|
expect(databaseAdapter).toBeDefined();
|
|
543
562
|
return {
|
|
544
563
|
customDep: "test",
|
|
@@ -555,7 +574,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
555
574
|
});
|
|
556
575
|
|
|
557
576
|
// Should have implicit deps
|
|
558
|
-
expect(deps).toHaveProperty("
|
|
577
|
+
expect(deps).toHaveProperty("databaseAdapter");
|
|
559
578
|
expect(deps).toHaveProperty("schema");
|
|
560
579
|
expect(deps).toHaveProperty("createUnitOfWork");
|
|
561
580
|
expect(deps).toHaveProperty("customDep");
|
|
@@ -576,7 +595,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
576
595
|
});
|
|
577
596
|
|
|
578
597
|
// Check implicit dependencies structure
|
|
579
|
-
expect(deps.
|
|
598
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
580
599
|
expect(deps.schema).toBeDefined();
|
|
581
600
|
expect(typeof deps.createUnitOfWork).toBe("function");
|
|
582
601
|
});
|
|
@@ -590,7 +609,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
590
609
|
.withDependencies(() => ({ apiKey: "key" }))
|
|
591
610
|
.providesBaseService(({ deps, defineService }) => {
|
|
592
611
|
expect(deps.apiKey).toBe("key");
|
|
593
|
-
expect(deps.
|
|
612
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
594
613
|
expect(defineService).toBeDefined();
|
|
595
614
|
|
|
596
615
|
return defineService({
|
|
@@ -687,6 +706,43 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
687
706
|
});
|
|
688
707
|
});
|
|
689
708
|
|
|
709
|
+
describe("withSyncCommands", () => {
|
|
710
|
+
it("registers sync commands in adapter registry", async () => {
|
|
711
|
+
const sqlite = new SQLite(":memory:");
|
|
712
|
+
const adapter = new SqlAdapter({
|
|
713
|
+
dialect: new SqliteDialect({ database: sqlite }),
|
|
714
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
const registry = getRegistryForAdapterSync(adapter);
|
|
718
|
+
const syncCommands = defineSyncCommands({ schema: testSchema }).create(
|
|
719
|
+
({ defineCommand }) => [
|
|
720
|
+
defineCommand({
|
|
721
|
+
name: "ping",
|
|
722
|
+
handler: async () => undefined,
|
|
723
|
+
}),
|
|
724
|
+
],
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
const definition = defineFragment("sync-frag")
|
|
728
|
+
.extend(withDatabase(testSchema))
|
|
729
|
+
.withSyncCommands(syncCommands)
|
|
730
|
+
.build();
|
|
731
|
+
|
|
732
|
+
definition.dependencies!({
|
|
733
|
+
config: {},
|
|
734
|
+
options: { databaseAdapter: adapter },
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
const resolved = registry.resolveSyncCommand("sync-frag", testSchema.name, "ping");
|
|
738
|
+
expect(resolved?.command).toBe(syncCommands.commands.get("ping"));
|
|
739
|
+
expect(resolved?.namespace).toBe(sanitizeNamespace(testSchema.name));
|
|
740
|
+
|
|
741
|
+
await adapter.close();
|
|
742
|
+
sqlite.close();
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
|
|
690
746
|
describe("createRequestStorage and createThisContext", () => {
|
|
691
747
|
it("should create request storage with UnitOfWork", () => {
|
|
692
748
|
const mockAdapter = createMockAdapter();
|
|
@@ -826,7 +882,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
826
882
|
options: {},
|
|
827
883
|
});
|
|
828
884
|
|
|
829
|
-
expect(deps.
|
|
885
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
830
886
|
expect(deps.createUnitOfWork).toBeDefined();
|
|
831
887
|
} finally {
|
|
832
888
|
if (previous === undefined) {
|
|
@@ -860,7 +916,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
860
916
|
>();
|
|
861
917
|
|
|
862
918
|
expect(deps.customDep).toBe("test");
|
|
863
|
-
expect(deps.
|
|
919
|
+
expect(deps.databaseAdapter).toBeDefined();
|
|
864
920
|
});
|
|
865
921
|
});
|
|
866
922
|
|
|
@@ -888,21 +944,31 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
888
944
|
getStore: () => ({
|
|
889
945
|
uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
|
|
890
946
|
}),
|
|
891
|
-
|
|
892
|
-
} as any;
|
|
947
|
+
} as unknown as RequestContextStorage<DatabaseContextStorage>;
|
|
893
948
|
|
|
894
949
|
// Spy on createServiceTxBuilder
|
|
895
950
|
const createServiceTxBuilderSpy = vi.spyOn(executeUnitOfWork, "createServiceTxBuilder");
|
|
896
951
|
|
|
952
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
953
|
+
const deps = {} as any;
|
|
954
|
+
|
|
897
955
|
// Get the contexts which includes serviceTx
|
|
898
956
|
const contexts = definition.createThisContext!({
|
|
899
957
|
config: {},
|
|
900
958
|
options: { databaseAdapter: mockAdapter },
|
|
901
|
-
|
|
902
|
-
deps: {} as any,
|
|
959
|
+
deps,
|
|
903
960
|
storage: mockStorage,
|
|
904
961
|
});
|
|
905
962
|
|
|
963
|
+
// Initialize hooks with services so serviceTx receives the hooks map.
|
|
964
|
+
definition.internalDataFactory?.({
|
|
965
|
+
config: {},
|
|
966
|
+
options: { databaseAdapter: mockAdapter },
|
|
967
|
+
deps,
|
|
968
|
+
services: {} as Record<string, never>,
|
|
969
|
+
serviceDeps: {} as Record<string, never>,
|
|
970
|
+
});
|
|
971
|
+
|
|
906
972
|
// Call serviceTx - this should pass hooks to createServiceTxBuilder
|
|
907
973
|
contexts.serviceContext.serviceTx(testSchema);
|
|
908
974
|
|
|
@@ -918,4 +984,712 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
|
|
|
918
984
|
createServiceTxBuilderSpy.mockRestore();
|
|
919
985
|
});
|
|
920
986
|
});
|
|
987
|
+
|
|
988
|
+
describe("provideHooks services access", () => {
|
|
989
|
+
it("should provide bound services to hooks factory", () => {
|
|
990
|
+
const mockAdapter = createMockAdapter();
|
|
991
|
+
let observed: string | null = null;
|
|
992
|
+
|
|
993
|
+
type TestHooks = {
|
|
994
|
+
onPing: HookFn;
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const definition = defineFragment("db-frag-hooks-services")
|
|
998
|
+
.extend(withDatabase(testSchema))
|
|
999
|
+
.providesBaseService(() => ({
|
|
1000
|
+
ping: () => "pong",
|
|
1001
|
+
}))
|
|
1002
|
+
.provideHooks<TestHooks>(({ defineHook, services }) => {
|
|
1003
|
+
observed = services.ping();
|
|
1004
|
+
return {
|
|
1005
|
+
onPing: defineHook(async function () {
|
|
1006
|
+
// no-op
|
|
1007
|
+
}),
|
|
1008
|
+
};
|
|
1009
|
+
})
|
|
1010
|
+
.build();
|
|
1011
|
+
|
|
1012
|
+
const deps = definition.dependencies!({
|
|
1013
|
+
config: {},
|
|
1014
|
+
options: { databaseAdapter: mockAdapter },
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
const internalData = definition.internalDataFactory?.({
|
|
1018
|
+
config: {},
|
|
1019
|
+
options: { databaseAdapter: mockAdapter },
|
|
1020
|
+
deps,
|
|
1021
|
+
services: { ping: () => "pong" },
|
|
1022
|
+
serviceDeps: {},
|
|
1023
|
+
}) as { durableHooksToken?: object } | undefined;
|
|
1024
|
+
|
|
1025
|
+
expect(observed).toBe("pong");
|
|
1026
|
+
expect(internalData?.durableHooksToken).toBeDefined();
|
|
1027
|
+
const runtime = internalData?.durableHooksToken
|
|
1028
|
+
? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
|
|
1029
|
+
: undefined;
|
|
1030
|
+
expect(runtime?.config.hooks).toBeDefined();
|
|
1031
|
+
expect(runtime?.config.hooks).toHaveProperty("onPing");
|
|
1032
|
+
});
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
describe("durable hooks waitUntil forwarding", () => {
|
|
1036
|
+
it("forwards request waitUntil to notifier context", async () => {
|
|
1037
|
+
const mockAdapter = createMockAdapter();
|
|
1038
|
+
const requestSourceSymbol = Symbol.for("fragno-request-source");
|
|
1039
|
+
const requestRouteSymbol = Symbol.for("fragno-request-route");
|
|
1040
|
+
const requestWaitUntilSymbol = Symbol.for("fragno-request-wait-until");
|
|
1041
|
+
const notifySpy = vi.fn().mockResolvedValue(undefined);
|
|
1042
|
+
const waitUntilSpy = vi.fn();
|
|
1043
|
+
|
|
1044
|
+
type TestHooks = {
|
|
1045
|
+
onPing: HookFn<{ value: string }>;
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const definition = defineFragment("db-frag-hooks-waituntil")
|
|
1049
|
+
.extend(withDatabase(testSchema))
|
|
1050
|
+
.provideHooks<TestHooks>(({ defineHook }) => ({
|
|
1051
|
+
onPing: defineHook(async function () {
|
|
1052
|
+
// no-op
|
|
1053
|
+
}),
|
|
1054
|
+
}))
|
|
1055
|
+
.build();
|
|
1056
|
+
|
|
1057
|
+
const deps = definition.dependencies!({
|
|
1058
|
+
config: {},
|
|
1059
|
+
options: { databaseAdapter: mockAdapter },
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
const internalData = definition.internalDataFactory?.({
|
|
1063
|
+
config: {},
|
|
1064
|
+
options: { databaseAdapter: mockAdapter },
|
|
1065
|
+
deps,
|
|
1066
|
+
services: {} as Record<string, never>,
|
|
1067
|
+
serviceDeps: {} as Record<string, never>,
|
|
1068
|
+
}) as { durableHooksToken?: object } | undefined;
|
|
1069
|
+
|
|
1070
|
+
const runtime = internalData?.durableHooksToken
|
|
1071
|
+
? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
|
|
1072
|
+
: undefined;
|
|
1073
|
+
if (!runtime) {
|
|
1074
|
+
throw new Error("Durable hooks runtime missing");
|
|
1075
|
+
}
|
|
1076
|
+
runtime.config.notifier = {
|
|
1077
|
+
notify: notifySpy,
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
const createHandlerTxBuilderSpy = vi
|
|
1081
|
+
.spyOn(executeUnitOfWork, "createHandlerTxBuilder")
|
|
1082
|
+
.mockReturnValue(
|
|
1083
|
+
{} as unknown as ReturnType<typeof executeUnitOfWork.createHandlerTxBuilder>,
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
const mockStorage = {
|
|
1087
|
+
getStore: () => ({
|
|
1088
|
+
uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
|
|
1089
|
+
[requestSourceSymbol]: "route",
|
|
1090
|
+
[requestRouteSymbol]: { method: "POST", path: "/users" },
|
|
1091
|
+
[requestWaitUntilSymbol]: waitUntilSpy,
|
|
1092
|
+
}),
|
|
1093
|
+
} as unknown as RequestContextStorage<DatabaseContextStorage>;
|
|
1094
|
+
|
|
1095
|
+
const contexts = definition.createThisContext!({
|
|
1096
|
+
config: {},
|
|
1097
|
+
options: { databaseAdapter: mockAdapter },
|
|
1098
|
+
deps,
|
|
1099
|
+
storage: mockStorage,
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
contexts.handlerContext.handlerTx();
|
|
1103
|
+
const callArgs = createHandlerTxBuilderSpy.mock.calls[0]?.[0];
|
|
1104
|
+
const mockUow = {
|
|
1105
|
+
getTriggeredHooks: () => [
|
|
1106
|
+
{
|
|
1107
|
+
namespace: runtime.config.namespace,
|
|
1108
|
+
hookName: "onPing",
|
|
1109
|
+
payload: { value: "ping" },
|
|
1110
|
+
},
|
|
1111
|
+
],
|
|
1112
|
+
} as unknown as IUnitOfWork;
|
|
1113
|
+
await callArgs?.onAfterMutate?.(mockUow);
|
|
1114
|
+
|
|
1115
|
+
expect(notifySpy).toHaveBeenCalledTimes(1);
|
|
1116
|
+
const notifyContext = notifySpy.mock.calls[0]?.[0] as
|
|
1117
|
+
| { route?: string; source?: string; waitUntil?: unknown }
|
|
1118
|
+
| undefined;
|
|
1119
|
+
expect(notifyContext).toMatchObject({
|
|
1120
|
+
source: "request",
|
|
1121
|
+
route: "POST /users",
|
|
1122
|
+
});
|
|
1123
|
+
expect(notifyContext?.waitUntil).toBe(waitUntilSpy);
|
|
1124
|
+
expect(waitUntilSpy).toHaveBeenCalledTimes(1);
|
|
1125
|
+
|
|
1126
|
+
createHandlerTxBuilderSpy.mockRestore();
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it("forwards inherited request waitUntil for hook mutations", async () => {
|
|
1130
|
+
const mockAdapter = createMockAdapter();
|
|
1131
|
+
const requestWaitUntilSymbol = Symbol.for("fragno-request-wait-until");
|
|
1132
|
+
const notifySpy = vi.fn().mockResolvedValue(undefined);
|
|
1133
|
+
const waitUntilSpy = vi.fn();
|
|
1134
|
+
|
|
1135
|
+
type TestHooks = {
|
|
1136
|
+
onPing: HookFn<{ value: string }>;
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
const definition = defineFragment("db-frag-hooks-hook-waituntil")
|
|
1140
|
+
.extend(withDatabase(testSchema))
|
|
1141
|
+
.provideHooks<TestHooks>(({ defineHook }) => ({
|
|
1142
|
+
onPing: defineHook(async function () {
|
|
1143
|
+
// no-op
|
|
1144
|
+
}),
|
|
1145
|
+
}))
|
|
1146
|
+
.build();
|
|
1147
|
+
|
|
1148
|
+
const deps = definition.dependencies!({
|
|
1149
|
+
config: {},
|
|
1150
|
+
options: { databaseAdapter: mockAdapter },
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
const internalData = definition.internalDataFactory?.({
|
|
1154
|
+
config: {},
|
|
1155
|
+
options: { databaseAdapter: mockAdapter },
|
|
1156
|
+
deps,
|
|
1157
|
+
services: {} as Record<string, never>,
|
|
1158
|
+
serviceDeps: {} as Record<string, never>,
|
|
1159
|
+
}) as { durableHooksToken?: object } | undefined;
|
|
1160
|
+
|
|
1161
|
+
const runtime = internalData?.durableHooksToken
|
|
1162
|
+
? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
|
|
1163
|
+
: undefined;
|
|
1164
|
+
if (!runtime) {
|
|
1165
|
+
throw new Error("Durable hooks runtime missing");
|
|
1166
|
+
}
|
|
1167
|
+
runtime.config.notifier = {
|
|
1168
|
+
notify: notifySpy,
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
const createHandlerTxBuilderSpy = vi
|
|
1172
|
+
.spyOn(executeUnitOfWork, "createHandlerTxBuilder")
|
|
1173
|
+
.mockImplementation((options) => {
|
|
1174
|
+
return {
|
|
1175
|
+
execute: vi.fn(async () => {
|
|
1176
|
+
const mockUow = {
|
|
1177
|
+
getTriggeredHooks: () => [
|
|
1178
|
+
{
|
|
1179
|
+
namespace: runtime.config.namespace,
|
|
1180
|
+
hookName: "onPing",
|
|
1181
|
+
payload: { value: "ping" },
|
|
1182
|
+
},
|
|
1183
|
+
],
|
|
1184
|
+
} as unknown as IUnitOfWork;
|
|
1185
|
+
await options.onAfterMutate?.(mockUow);
|
|
1186
|
+
}),
|
|
1187
|
+
} as unknown as ReturnType<typeof executeUnitOfWork.createHandlerTxBuilder>;
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
try {
|
|
1191
|
+
const storage = mockAdapter.contextStorage as RequestContextStorage<DatabaseContextStorage>;
|
|
1192
|
+
await storage.runWithInitializer(
|
|
1193
|
+
() =>
|
|
1194
|
+
({
|
|
1195
|
+
uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
|
|
1196
|
+
[requestWaitUntilSymbol]: waitUntilSpy,
|
|
1197
|
+
}) as DatabaseContextStorage,
|
|
1198
|
+
async () => {
|
|
1199
|
+
await runtime.config.handlerTx().execute();
|
|
1200
|
+
},
|
|
1201
|
+
);
|
|
1202
|
+
|
|
1203
|
+
expect(notifySpy).toHaveBeenCalledTimes(1);
|
|
1204
|
+
const notifyContext = notifySpy.mock.calls[0]?.[0] as
|
|
1205
|
+
| { source?: string; waitUntil?: unknown }
|
|
1206
|
+
| undefined;
|
|
1207
|
+
expect(notifyContext?.source).toBe("hook");
|
|
1208
|
+
expect(notifyContext?.waitUntil).toBe(waitUntilSpy);
|
|
1209
|
+
expect(waitUntilSpy).toHaveBeenCalledTimes(1);
|
|
1210
|
+
} finally {
|
|
1211
|
+
createHandlerTxBuilderSpy.mockRestore();
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
describe("durable hooks cross-namespace scheduling", () => {
|
|
1217
|
+
it("should schedule hooks triggered in another namespace when mutation runs inside a hook", async () => {
|
|
1218
|
+
const sqlite = new SQLite(":memory:");
|
|
1219
|
+
const adapter = new SqlAdapter({
|
|
1220
|
+
dialect: new SqliteDialect({ database: sqlite }),
|
|
1221
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
const schemaA = schema("hooks_alpha", (s) =>
|
|
1225
|
+
s.addTable("items", (t) =>
|
|
1226
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1227
|
+
),
|
|
1228
|
+
);
|
|
1229
|
+
const schemaB = schema("hooks_beta", (s) =>
|
|
1230
|
+
s.addTable("items", (t) =>
|
|
1231
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1232
|
+
),
|
|
1233
|
+
);
|
|
1234
|
+
|
|
1235
|
+
type HooksA = {
|
|
1236
|
+
onAlpha: (payload: { value: string }) => void;
|
|
1237
|
+
};
|
|
1238
|
+
type HooksB = {
|
|
1239
|
+
onBeta: (payload: { value: string }) => void;
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
const hooksB: HooksB = {
|
|
1243
|
+
onBeta: () => {},
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
const namespaceA = sanitizeNamespace(schemaA.name);
|
|
1247
|
+
const namespaceB = sanitizeNamespace(schemaB.name);
|
|
1248
|
+
|
|
1249
|
+
const fragmentADef = defineFragment("frag-hooks-a")
|
|
1250
|
+
.extend(withDatabase(schemaA))
|
|
1251
|
+
.provideHooks<HooksA>(({ defineHook }) => ({
|
|
1252
|
+
onAlpha: defineHook(async function () {
|
|
1253
|
+
await this.handlerTx({
|
|
1254
|
+
onBeforeMutate: (uow) => {
|
|
1255
|
+
uow.registerSchema(schemaB, namespaceB);
|
|
1256
|
+
},
|
|
1257
|
+
})
|
|
1258
|
+
.mutate(({ forSchema }) => {
|
|
1259
|
+
const other = forSchema(schemaB, hooksB);
|
|
1260
|
+
other.triggerHook("onBeta", { value: "beta" });
|
|
1261
|
+
})
|
|
1262
|
+
.execute();
|
|
1263
|
+
}),
|
|
1264
|
+
}))
|
|
1265
|
+
.build();
|
|
1266
|
+
|
|
1267
|
+
const fragmentBDef = defineFragment("frag-hooks-b")
|
|
1268
|
+
.extend(withDatabase(schemaB))
|
|
1269
|
+
.provideHooks<HooksB>(({ defineHook }) => ({
|
|
1270
|
+
onBeta: defineHook(async function () {
|
|
1271
|
+
// no-op
|
|
1272
|
+
}),
|
|
1273
|
+
}))
|
|
1274
|
+
.build();
|
|
1275
|
+
|
|
1276
|
+
try {
|
|
1277
|
+
const internalMigrations = adapter.prepareMigrations(internalSchema, null);
|
|
1278
|
+
await internalMigrations.executeWithDriver(adapter.driver, 0);
|
|
1279
|
+
|
|
1280
|
+
const migrationsA = adapter.prepareMigrations(schemaA, namespaceA);
|
|
1281
|
+
await migrationsA.executeWithDriver(adapter.driver, 0);
|
|
1282
|
+
|
|
1283
|
+
const migrationsB = adapter.prepareMigrations(schemaB, namespaceB);
|
|
1284
|
+
await migrationsB.executeWithDriver(adapter.driver, 0);
|
|
1285
|
+
|
|
1286
|
+
const fragmentA = instantiate(fragmentADef)
|
|
1287
|
+
.withConfig({})
|
|
1288
|
+
.withOptions({ databaseAdapter: adapter })
|
|
1289
|
+
.build();
|
|
1290
|
+
const fragmentB = instantiate(fragmentBDef)
|
|
1291
|
+
.withConfig({})
|
|
1292
|
+
.withOptions({ databaseAdapter: adapter })
|
|
1293
|
+
.build();
|
|
1294
|
+
|
|
1295
|
+
const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
|
|
1296
|
+
const tokenB = (fragmentB.$internal as { durableHooksToken?: object }).durableHooksToken;
|
|
1297
|
+
|
|
1298
|
+
expect(tokenA).toBeDefined();
|
|
1299
|
+
expect(tokenB).toBeDefined();
|
|
1300
|
+
|
|
1301
|
+
const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
|
|
1302
|
+
const runtimeB = tokenB ? getDurableHooksRuntimeByToken(tokenB) : undefined;
|
|
1303
|
+
|
|
1304
|
+
if (!runtimeA || !runtimeB) {
|
|
1305
|
+
throw new Error("Durable hooks runtime missing");
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const notifySpy = vi.fn();
|
|
1309
|
+
runtimeB.config.notifier = {
|
|
1310
|
+
notify: notifySpy,
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
const internalFragment = getInternalFragment(adapter);
|
|
1314
|
+
await internalFragment.inContext(async function () {
|
|
1315
|
+
await this.handlerTx()
|
|
1316
|
+
.mutate(({ forSchema }) => {
|
|
1317
|
+
const uow = forSchema(internalSchema);
|
|
1318
|
+
uow.create("fragno_hooks", {
|
|
1319
|
+
namespace: namespaceA,
|
|
1320
|
+
hookName: "onAlpha",
|
|
1321
|
+
payload: { value: "alpha" },
|
|
1322
|
+
status: "pending",
|
|
1323
|
+
attempts: 0,
|
|
1324
|
+
maxAttempts: 1,
|
|
1325
|
+
lastAttemptAt: null,
|
|
1326
|
+
nextRetryAt: null,
|
|
1327
|
+
error: null,
|
|
1328
|
+
nonce: "test-nonce",
|
|
1329
|
+
});
|
|
1330
|
+
})
|
|
1331
|
+
.execute();
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
|
|
1335
|
+
runtimeA.config.runner = runnerA;
|
|
1336
|
+
await runnerA.processDue();
|
|
1337
|
+
|
|
1338
|
+
expect(notifySpy).toHaveBeenCalled();
|
|
1339
|
+
} finally {
|
|
1340
|
+
await adapter.close();
|
|
1341
|
+
sqlite.close();
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it("should skip cross-namespace notify when target namespace has autoSchedule=false", async () => {
|
|
1346
|
+
const sqlite = new SQLite(":memory:");
|
|
1347
|
+
const adapter = new SqlAdapter({
|
|
1348
|
+
dialect: new SqliteDialect({ database: sqlite }),
|
|
1349
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
const schemaA = schema("hooks_alpha_autoschedule", (s) =>
|
|
1353
|
+
s.addTable("items", (t) =>
|
|
1354
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1355
|
+
),
|
|
1356
|
+
);
|
|
1357
|
+
const schemaB = schema("hooks_beta_autoschedule", (s) =>
|
|
1358
|
+
s.addTable("items", (t) =>
|
|
1359
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1360
|
+
),
|
|
1361
|
+
);
|
|
1362
|
+
|
|
1363
|
+
type HooksA = {
|
|
1364
|
+
onAlpha: (payload: { value: string }) => void;
|
|
1365
|
+
};
|
|
1366
|
+
type HooksB = {
|
|
1367
|
+
onBeta: (payload: { value: string }) => void;
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
const hooksB: HooksB = {
|
|
1371
|
+
onBeta: () => {},
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
const namespaceA = sanitizeNamespace(schemaA.name);
|
|
1375
|
+
const namespaceB = sanitizeNamespace(schemaB.name);
|
|
1376
|
+
|
|
1377
|
+
const fragmentADef = defineFragment("frag-hooks-autoschedule-a")
|
|
1378
|
+
.extend(withDatabase(schemaA))
|
|
1379
|
+
.provideHooks<HooksA>(({ defineHook }) => ({
|
|
1380
|
+
onAlpha: defineHook(async function () {
|
|
1381
|
+
await this.handlerTx({
|
|
1382
|
+
onBeforeMutate: (uow) => {
|
|
1383
|
+
uow.registerSchema(schemaB, namespaceB);
|
|
1384
|
+
},
|
|
1385
|
+
})
|
|
1386
|
+
.mutate(({ forSchema }) => {
|
|
1387
|
+
const other = forSchema(schemaB, hooksB);
|
|
1388
|
+
other.triggerHook("onBeta", { value: "beta" });
|
|
1389
|
+
})
|
|
1390
|
+
.execute();
|
|
1391
|
+
}),
|
|
1392
|
+
}))
|
|
1393
|
+
.build();
|
|
1394
|
+
|
|
1395
|
+
const fragmentBDef = defineFragment("frag-hooks-autoschedule-b")
|
|
1396
|
+
.extend(withDatabase(schemaB))
|
|
1397
|
+
.provideHooks<HooksB>(({ defineHook }) => ({
|
|
1398
|
+
onBeta: defineHook(async function () {
|
|
1399
|
+
// no-op
|
|
1400
|
+
}),
|
|
1401
|
+
}))
|
|
1402
|
+
.build();
|
|
1403
|
+
|
|
1404
|
+
try {
|
|
1405
|
+
const internalMigrations = adapter.prepareMigrations(internalSchema, null);
|
|
1406
|
+
await internalMigrations.executeWithDriver(adapter.driver, 0);
|
|
1407
|
+
|
|
1408
|
+
const migrationsA = adapter.prepareMigrations(schemaA, namespaceA);
|
|
1409
|
+
await migrationsA.executeWithDriver(adapter.driver, 0);
|
|
1410
|
+
|
|
1411
|
+
const migrationsB = adapter.prepareMigrations(schemaB, namespaceB);
|
|
1412
|
+
await migrationsB.executeWithDriver(adapter.driver, 0);
|
|
1413
|
+
|
|
1414
|
+
const fragmentA = instantiate(fragmentADef)
|
|
1415
|
+
.withConfig({})
|
|
1416
|
+
.withOptions({ databaseAdapter: adapter })
|
|
1417
|
+
.build();
|
|
1418
|
+
const fragmentB = instantiate(fragmentBDef)
|
|
1419
|
+
.withConfig({})
|
|
1420
|
+
.withOptions({ databaseAdapter: adapter, durableHooks: { autoSchedule: false } })
|
|
1421
|
+
.build();
|
|
1422
|
+
|
|
1423
|
+
const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
|
|
1424
|
+
const tokenB = (fragmentB.$internal as { durableHooksToken?: object }).durableHooksToken;
|
|
1425
|
+
|
|
1426
|
+
expect(tokenA).toBeDefined();
|
|
1427
|
+
expect(tokenB).toBeDefined();
|
|
1428
|
+
|
|
1429
|
+
const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
|
|
1430
|
+
const runtimeB = tokenB ? getDurableHooksRuntimeByToken(tokenB) : undefined;
|
|
1431
|
+
|
|
1432
|
+
if (!runtimeA || !runtimeB) {
|
|
1433
|
+
throw new Error("Durable hooks runtime missing");
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
const notifySpy = vi.fn();
|
|
1437
|
+
runtimeB.config.notifier = {
|
|
1438
|
+
notify: notifySpy,
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
const internalFragment = getInternalFragment(adapter);
|
|
1442
|
+
await internalFragment.inContext(async function () {
|
|
1443
|
+
await this.handlerTx()
|
|
1444
|
+
.mutate(({ forSchema }) => {
|
|
1445
|
+
const uow = forSchema(internalSchema);
|
|
1446
|
+
uow.create("fragno_hooks", {
|
|
1447
|
+
namespace: namespaceA,
|
|
1448
|
+
hookName: "onAlpha",
|
|
1449
|
+
payload: { value: "alpha" },
|
|
1450
|
+
status: "pending",
|
|
1451
|
+
attempts: 0,
|
|
1452
|
+
maxAttempts: 1,
|
|
1453
|
+
lastAttemptAt: null,
|
|
1454
|
+
nextRetryAt: null,
|
|
1455
|
+
error: null,
|
|
1456
|
+
nonce: "auto-schedule-test-nonce",
|
|
1457
|
+
});
|
|
1458
|
+
})
|
|
1459
|
+
.execute();
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
|
|
1463
|
+
runtimeA.config.runner = runnerA;
|
|
1464
|
+
await runnerA.processDue();
|
|
1465
|
+
|
|
1466
|
+
expect(notifySpy).not.toHaveBeenCalled();
|
|
1467
|
+
|
|
1468
|
+
const queuedHooks = await internalFragment.inContext(async function () {
|
|
1469
|
+
return await this.handlerTx()
|
|
1470
|
+
.withServiceCalls(
|
|
1471
|
+
() =>
|
|
1472
|
+
[internalFragment.services.hookService.getHooksByNamespace(namespaceB)] as const,
|
|
1473
|
+
)
|
|
1474
|
+
.transform(({ serviceResult: [result] }) => result)
|
|
1475
|
+
.execute();
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
const pendingBetaHooks = queuedHooks.filter(
|
|
1479
|
+
(hook) => hook.status === "pending" && hook.hookName === "onBeta",
|
|
1480
|
+
);
|
|
1481
|
+
expect(pendingBetaHooks.length).toBeGreaterThan(0);
|
|
1482
|
+
} finally {
|
|
1483
|
+
await adapter.close();
|
|
1484
|
+
sqlite.close();
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
it("should notify the matching runtime instance when namespace has multiple runtimes", async () => {
|
|
1489
|
+
const sqliteA = new SQLite(":memory:");
|
|
1490
|
+
const sqliteB = new SQLite(":memory:");
|
|
1491
|
+
const adapterA = new SqlAdapter({
|
|
1492
|
+
dialect: new SqliteDialect({ database: sqliteA }),
|
|
1493
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
1494
|
+
});
|
|
1495
|
+
const adapterB = new SqlAdapter({
|
|
1496
|
+
dialect: new SqliteDialect({ database: sqliteB }),
|
|
1497
|
+
driverConfig: new BetterSQLite3DriverConfig(),
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
const unique = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1501
|
+
const schemaA = schema(`hooks_alpha_instance_${unique}`, (s) =>
|
|
1502
|
+
s.addTable("items", (t) =>
|
|
1503
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1504
|
+
),
|
|
1505
|
+
);
|
|
1506
|
+
const schemaB = schema(`hooks_shared_${unique}`, (s) =>
|
|
1507
|
+
s.addTable("items", (t) =>
|
|
1508
|
+
t.addColumn("id", idColumn()).addColumn("name", column("string")),
|
|
1509
|
+
),
|
|
1510
|
+
);
|
|
1511
|
+
|
|
1512
|
+
type HooksA = {
|
|
1513
|
+
onAlpha: (payload: { value: string }) => void;
|
|
1514
|
+
};
|
|
1515
|
+
type HooksB = {
|
|
1516
|
+
onBeta: (payload: { value: string }) => void;
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
const hooksB: HooksB = {
|
|
1520
|
+
onBeta: () => {},
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
const namespaceA = sanitizeNamespace(schemaA.name);
|
|
1524
|
+
const namespaceB = sanitizeNamespace(schemaB.name);
|
|
1525
|
+
|
|
1526
|
+
const fragmentADef = defineFragment("frag-hooks-instance-a")
|
|
1527
|
+
.extend(withDatabase(schemaA))
|
|
1528
|
+
.provideHooks<HooksA>(({ defineHook }) => ({
|
|
1529
|
+
onAlpha: defineHook(async function () {
|
|
1530
|
+
await this.handlerTx({
|
|
1531
|
+
onBeforeMutate: (uow) => {
|
|
1532
|
+
uow.registerSchema(schemaB, namespaceB);
|
|
1533
|
+
},
|
|
1534
|
+
})
|
|
1535
|
+
.mutate(({ forSchema }) => {
|
|
1536
|
+
const other = forSchema(schemaB, hooksB);
|
|
1537
|
+
other.triggerHook("onBeta", { value: "beta" });
|
|
1538
|
+
})
|
|
1539
|
+
.execute();
|
|
1540
|
+
}),
|
|
1541
|
+
}))
|
|
1542
|
+
.build();
|
|
1543
|
+
|
|
1544
|
+
const fragmentBDef = defineFragment("frag-hooks-shared")
|
|
1545
|
+
.extend(withDatabase(schemaB))
|
|
1546
|
+
.provideHooks<HooksB>(({ defineHook }) => ({
|
|
1547
|
+
onBeta: defineHook(async function () {
|
|
1548
|
+
// no-op
|
|
1549
|
+
}),
|
|
1550
|
+
}))
|
|
1551
|
+
.build();
|
|
1552
|
+
|
|
1553
|
+
try {
|
|
1554
|
+
const internalMigrationsA = adapterA.prepareMigrations(internalSchema, null);
|
|
1555
|
+
await internalMigrationsA.executeWithDriver(adapterA.driver, 0);
|
|
1556
|
+
const schemaAMigrations = adapterA.prepareMigrations(schemaA, namespaceA);
|
|
1557
|
+
await schemaAMigrations.executeWithDriver(adapterA.driver, 0);
|
|
1558
|
+
const schemaBMigrationsA = adapterA.prepareMigrations(schemaB, namespaceB);
|
|
1559
|
+
await schemaBMigrationsA.executeWithDriver(adapterA.driver, 0);
|
|
1560
|
+
|
|
1561
|
+
const internalMigrationsB = adapterB.prepareMigrations(internalSchema, null);
|
|
1562
|
+
await internalMigrationsB.executeWithDriver(adapterB.driver, 0);
|
|
1563
|
+
const schemaBMigrationsB = adapterB.prepareMigrations(schemaB, namespaceB);
|
|
1564
|
+
await schemaBMigrationsB.executeWithDriver(adapterB.driver, 0);
|
|
1565
|
+
|
|
1566
|
+
const fragmentA = instantiate(fragmentADef)
|
|
1567
|
+
.withConfig({})
|
|
1568
|
+
.withOptions({ databaseAdapter: adapterA })
|
|
1569
|
+
.build();
|
|
1570
|
+
const fragmentBForAdapterA = instantiate(fragmentBDef)
|
|
1571
|
+
.withConfig({})
|
|
1572
|
+
.withOptions({ databaseAdapter: adapterA })
|
|
1573
|
+
.build();
|
|
1574
|
+
const fragmentBForAdapterB = instantiate(fragmentBDef)
|
|
1575
|
+
.withConfig({})
|
|
1576
|
+
.withOptions({ databaseAdapter: adapterB })
|
|
1577
|
+
.build();
|
|
1578
|
+
|
|
1579
|
+
const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
|
|
1580
|
+
const tokenBForAdapterA = (fragmentBForAdapterA.$internal as { durableHooksToken?: object })
|
|
1581
|
+
.durableHooksToken;
|
|
1582
|
+
const tokenBForAdapterB = (fragmentBForAdapterB.$internal as { durableHooksToken?: object })
|
|
1583
|
+
.durableHooksToken;
|
|
1584
|
+
|
|
1585
|
+
expect(tokenA).toBeDefined();
|
|
1586
|
+
expect(tokenBForAdapterA).toBeDefined();
|
|
1587
|
+
expect(tokenBForAdapterB).toBeDefined();
|
|
1588
|
+
|
|
1589
|
+
const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
|
|
1590
|
+
const runtimeBForAdapterA = tokenBForAdapterA
|
|
1591
|
+
? getDurableHooksRuntimeByToken(tokenBForAdapterA)
|
|
1592
|
+
: undefined;
|
|
1593
|
+
const runtimeBForAdapterB = tokenBForAdapterB
|
|
1594
|
+
? getDurableHooksRuntimeByToken(tokenBForAdapterB)
|
|
1595
|
+
: undefined;
|
|
1596
|
+
|
|
1597
|
+
if (!runtimeA || !runtimeBForAdapterA || !runtimeBForAdapterB) {
|
|
1598
|
+
throw new Error("Durable hooks runtime missing");
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const notifySpyForAdapterA = vi.fn();
|
|
1602
|
+
const notifySpyForAdapterB = vi.fn();
|
|
1603
|
+
runtimeBForAdapterA.config.notifier = {
|
|
1604
|
+
notify: notifySpyForAdapterA,
|
|
1605
|
+
};
|
|
1606
|
+
runtimeBForAdapterB.config.notifier = {
|
|
1607
|
+
notify: notifySpyForAdapterB,
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
const internalFragment = getInternalFragment(adapterA);
|
|
1611
|
+
await internalFragment.inContext(async function () {
|
|
1612
|
+
await this.handlerTx()
|
|
1613
|
+
.mutate(({ forSchema }) => {
|
|
1614
|
+
const uow = forSchema(internalSchema);
|
|
1615
|
+
uow.create("fragno_hooks", {
|
|
1616
|
+
namespace: namespaceA,
|
|
1617
|
+
hookName: "onAlpha",
|
|
1618
|
+
payload: { value: "alpha" },
|
|
1619
|
+
status: "pending",
|
|
1620
|
+
attempts: 0,
|
|
1621
|
+
maxAttempts: 1,
|
|
1622
|
+
lastAttemptAt: null,
|
|
1623
|
+
nextRetryAt: null,
|
|
1624
|
+
error: null,
|
|
1625
|
+
nonce: "instance-test-nonce",
|
|
1626
|
+
});
|
|
1627
|
+
})
|
|
1628
|
+
.execute();
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
|
|
1632
|
+
runtimeA.config.runner = runnerA;
|
|
1633
|
+
await runnerA.processDue();
|
|
1634
|
+
|
|
1635
|
+
expect(notifySpyForAdapterA).toHaveBeenCalled();
|
|
1636
|
+
expect(notifySpyForAdapterB).not.toHaveBeenCalled();
|
|
1637
|
+
} finally {
|
|
1638
|
+
await adapterA.close();
|
|
1639
|
+
await adapterB.close();
|
|
1640
|
+
sqliteA.close();
|
|
1641
|
+
sqliteB.close();
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
describe("handlerTx plan mode", () => {
|
|
1647
|
+
it("should suppress hook mutations in plan mode", () => {
|
|
1648
|
+
const mockAdapter = createMockAdapter();
|
|
1649
|
+
|
|
1650
|
+
type TestHooks = {
|
|
1651
|
+
onUserCreated: (payload: { email: string }) => void;
|
|
1652
|
+
};
|
|
1653
|
+
|
|
1654
|
+
const definition = withDatabase(testSchema)(defineFragment("db-frag-plan-mode"))
|
|
1655
|
+
.provideHooks<TestHooks>(({ defineHook }) => ({
|
|
1656
|
+
onUserCreated: defineHook(function () {
|
|
1657
|
+
// no-op
|
|
1658
|
+
}),
|
|
1659
|
+
}))
|
|
1660
|
+
.build();
|
|
1661
|
+
|
|
1662
|
+
const mockStorage = {
|
|
1663
|
+
getStore: () => ({
|
|
1664
|
+
uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
|
|
1665
|
+
}),
|
|
1666
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1667
|
+
} as any;
|
|
1668
|
+
|
|
1669
|
+
const createHandlerTxBuilderSpy = vi
|
|
1670
|
+
.spyOn(executeUnitOfWork, "createHandlerTxBuilder")
|
|
1671
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1672
|
+
.mockReturnValue({} as any);
|
|
1673
|
+
|
|
1674
|
+
const prepareHookMutationsSpy = vi.spyOn(hooks, "prepareHookMutations");
|
|
1675
|
+
|
|
1676
|
+
const contexts = definition.createThisContext!({
|
|
1677
|
+
config: {},
|
|
1678
|
+
options: { databaseAdapter: mockAdapter },
|
|
1679
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1680
|
+
deps: {} as any,
|
|
1681
|
+
storage: mockStorage,
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
contexts.handlerContext.handlerTx({ planMode: true });
|
|
1685
|
+
|
|
1686
|
+
const callArgs = createHandlerTxBuilderSpy.mock.calls[0]?.[0];
|
|
1687
|
+
callArgs?.onBeforeMutate?.({} as unknown as IUnitOfWork);
|
|
1688
|
+
|
|
1689
|
+
expect(prepareHookMutationsSpy).not.toHaveBeenCalled();
|
|
1690
|
+
|
|
1691
|
+
prepareHookMutationsSpy.mockRestore();
|
|
1692
|
+
createHandlerTxBuilderSpy.mockRestore();
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
921
1695
|
});
|