@fragno-dev/db 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +327 -160
- package/CHANGELOG.md +74 -0
- package/README.md +24 -0
- package/dist/adapters/adapters.d.ts +1 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.js +30 -3
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
- package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +24 -20
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +8 -9
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +10 -6
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-uow.js +236 -13
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
- package/dist/adapters/in-memory/options.d.ts +2 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -1
- package/dist/adapters/in-memory/options.js +3 -2
- package/dist/adapters/in-memory/options.js.map +1 -1
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
- package/dist/adapters/in-memory/store.js +1 -1
- package/dist/adapters/in-memory/store.js.map +1 -1
- package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/browser/adapters/adapters.d.ts +61 -0
- package/dist/browser/adapters/adapters.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
- package/dist/browser/adapters/in-memory/index.d.ts +2 -0
- package/dist/browser/adapters/in-memory/options.d.ts +1 -0
- package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
- package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/browser/durable-hooks.d.ts +3 -0
- package/dist/browser/fragments/internal-fragment.d.ts +317 -0
- package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/browser/hooks/hooks.d.ts +146 -0
- package/dist/browser/hooks/hooks.d.ts.map +1 -0
- package/dist/browser/id.js +1 -0
- package/dist/browser/internal/adapter-registry.d.ts +4 -0
- package/dist/browser/internal/outbox-state.d.ts +2 -0
- package/dist/browser/mod.d.ts +15 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +17 -0
- package/dist/browser/mod.js.map +1 -0
- package/dist/browser/mod2.d.ts +48 -0
- package/dist/browser/mod2.d.ts.map +1 -0
- package/dist/browser/naming/sql-naming.d.ts +19 -0
- package/dist/browser/naming/sql-naming.d.ts.map +1 -0
- package/dist/browser/outbox/outbox.d.ts +21 -0
- package/dist/browser/outbox/outbox.d.ts.map +1 -0
- package/dist/browser/query/column-defaults.js +1 -0
- package/dist/browser/query/condition-builder.d.ts +44 -0
- package/dist/browser/query/condition-builder.d.ts.map +1 -0
- package/dist/browser/query/condition-builder.js +97 -0
- package/dist/browser/query/condition-builder.js.map +1 -0
- package/dist/browser/query/cursor.d.ts +105 -0
- package/dist/browser/query/cursor.d.ts.map +1 -0
- package/dist/browser/query/cursor.js +150 -0
- package/dist/browser/query/cursor.js.map +1 -0
- package/dist/browser/query/db-now.d.ts +22 -0
- package/dist/browser/query/db-now.d.ts.map +1 -0
- package/dist/browser/query/db-now.js +33 -0
- package/dist/browser/query/db-now.js.map +1 -0
- package/dist/browser/query/orm/orm.d.ts +18 -0
- package/dist/browser/query/orm/orm.d.ts.map +1 -0
- package/dist/browser/query/simple-query-interface.d.ts +108 -0
- package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
- package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/browser/query/value-encoding.js +38 -0
- package/dist/browser/query/value-encoding.js.map +1 -0
- package/dist/browser/schema/create.d.ts +326 -0
- package/dist/browser/schema/create.d.ts.map +1 -0
- package/dist/browser/schema/create.js +89 -0
- package/dist/browser/schema/create.js.map +1 -0
- package/dist/browser/schema/generate-id.js +28 -0
- package/dist/browser/schema/generate-id.js.map +1 -0
- package/dist/browser/shared/providers.d.ts +6 -0
- package/dist/browser/shared/providers.d.ts.map +1 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
- package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/browser/sync/commands.d.ts +15 -0
- package/dist/browser/sync/commands.d.ts.map +1 -0
- package/dist/browser/sync/commands.js +27 -0
- package/dist/browser/sync/commands.js.map +1 -0
- package/dist/browser/sync/types.d.ts +63 -0
- package/dist/browser/sync/types.d.ts.map +1 -0
- package/dist/browser/util/types.d.ts +8 -0
- package/dist/browser/util/types.d.ts.map +1 -0
- package/dist/browser/with-database.d.ts +29 -0
- package/dist/browser/with-database.d.ts.map +1 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +5 -0
- package/dist/db-fragment-definition-builder.d.ts +85 -28
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +374 -46
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.js +23 -55
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
- package/dist/dispatchers/node/dispatcher.d.ts +14 -0
- package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/node/dispatcher.js +80 -0
- package/dist/dispatchers/node/dispatcher.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +5 -10
- package/dist/dispatchers/node/index.d.ts.map +1 -1
- package/dist/dispatchers/node/index.js +21 -53
- package/dist/dispatchers/node/index.js.map +1 -1
- package/dist/durable-hooks.d.ts +31 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +23 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +128 -27
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +125 -78
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +138 -3
- package/dist/fragments/internal-fragment.routes.js.map +1 -1
- package/dist/fragments/internal-fragment.schema.d.ts +7 -1
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.schema.js +18 -1
- package/dist/fragments/internal-fragment.schema.js.map +1 -1
- package/dist/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-logger.js +75 -0
- package/dist/hooks/durable-hooks-logger.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +1 -14
- package/dist/hooks/durable-hooks-processor.js +58 -10
- package/dist/hooks/durable-hooks-processor.js.map +1 -1
- package/dist/hooks/durable-hooks-runtime.js +44 -0
- package/dist/hooks/durable-hooks-runtime.js.map +1 -0
- package/dist/hooks/hooks.d.ts +60 -2
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +214 -53
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.js +2 -2
- package/dist/internal/adapter-registry.d.ts +11 -0
- package/dist/internal/adapter-registry.d.ts.map +1 -0
- package/dist/internal/adapter-registry.js +135 -0
- package/dist/internal/adapter-registry.js.map +1 -0
- package/dist/internal/outbox-state.d.ts +2 -0
- package/dist/internal/outbox-state.js +26 -0
- package/dist/internal/outbox-state.js.map +1 -0
- package/dist/migration-engine/auto-from-schema.d.ts +33 -0
- package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
- package/dist/migration-engine/auto-from-schema.js +210 -27
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +17 -5
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.d.ts +113 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +12 -11
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +10 -10
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts.map +1 -1
- package/dist/naming/sql-naming.js.map +1 -1
- package/dist/outbox/outbox-builder.js.map +1 -1
- package/dist/outbox/outbox.d.ts +3 -1
- package/dist/outbox/outbox.d.ts.map +1 -1
- package/dist/outbox/outbox.js.map +1 -1
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/condition-builder.d.ts +7 -1
- package/dist/query/condition-builder.d.ts.map +1 -1
- package/dist/query/condition-builder.js +5 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/cursor-client.d.ts +105 -0
- package/dist/query/cursor-client.d.ts.map +1 -0
- package/dist/query/cursor-client.js +165 -0
- package/dist/query/cursor-client.js.map +1 -0
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +7 -1
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +15 -1
- package/dist/query/db-now.d.ts.map +1 -1
- package/dist/query/db-now.js +30 -2
- package/dist/query/db-now.js.map +1 -1
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +2 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +7 -3
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +50 -6
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +8 -1
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +69 -25
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +91 -16
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts.map +1 -1
- package/dist/schema/validator.js.map +1 -1
- package/dist/schema-output/drizzle.d.ts.map +1 -1
- package/dist/schema-output/drizzle.js +8 -6
- package/dist/schema-output/drizzle.js.map +1 -1
- package/dist/schema-output/prisma.d.ts.map +1 -1
- package/dist/schema-output/prisma.js +21 -10
- package/dist/schema-output/prisma.js.map +1 -1
- package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
- package/dist/sql-driver/sql.js.map +1 -1
- package/dist/sync/commands.d.ts +15 -0
- package/dist/sync/commands.d.ts.map +1 -0
- package/dist/sync/commands.js +27 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/read-tracking.d.ts +25 -0
- package/dist/sync/read-tracking.d.ts.map +1 -0
- package/dist/sync/read-tracking.js +148 -0
- package/dist/sync/read-tracking.js.map +1 -0
- package/dist/sync/submit.js +213 -0
- package/dist/sync/submit.js.map +1 -0
- package/dist/sync/types.d.ts +63 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/util/default-database-adapter.js +6 -1
- package/dist/util/default-database-adapter.js.map +1 -1
- package/dist/with-database.d.ts +3 -6
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +7 -15
- package/dist/with-database.js.map +1 -1
- package/package.json +33 -41
- package/src/adapters/adapters.ts +5 -4
- package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
- package/src/adapters/drizzle/test-utils.ts +2 -1
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
- package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
- package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
- package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
- package/src/adapters/generic-sql/migration/executor.ts +47 -4
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
- package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
- package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
- package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
- package/src/adapters/generic-sql/query/select-builder.ts +2 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
- package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
- package/src/adapters/generic-sql/query/where-builder.ts +48 -29
- package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
- package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
- package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
- package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
- package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
- package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
- package/src/adapters/generic-sql/uow-decoder.ts +3 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
- package/src/adapters/generic-sql/uow-encoder.ts +14 -18
- package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
- package/src/adapters/in-memory/condition-evaluator.ts +9 -4
- package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
- package/src/adapters/in-memory/in-memory-uow.ts +402 -26
- package/src/adapters/in-memory/options.test.ts +1 -0
- package/src/adapters/in-memory/options.ts +5 -1
- package/src/adapters/in-memory/outbox.test.ts +361 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
- package/src/adapters/in-memory/reference-resolution.ts +2 -2
- package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
- package/src/adapters/in-memory/store.test.ts +1 -0
- package/src/adapters/in-memory/store.ts +3 -3
- package/src/adapters/in-memory/value-normalization.test.ts +1 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
- package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
- package/src/adapters/shared/uow-operation-compiler.ts +3 -3
- package/src/browser/mod.ts +64 -0
- package/src/client.ts +19 -0
- package/src/db-fragment-definition-builder.test.ts +821 -47
- package/src/db-fragment-definition-builder.ts +857 -110
- package/src/db-fragment-instantiator.test.ts +114 -90
- package/src/db-fragment-integration.test.ts +9 -6
- package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
- package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
- package/src/dispatchers/cloudflare-do/index.ts +49 -90
- package/src/dispatchers/node/dispatcher.ts +112 -0
- package/src/dispatchers/node/index.test.ts +43 -14
- package/src/dispatchers/node/index.ts +38 -75
- package/src/durable-hooks.test.ts +80 -0
- package/src/durable-hooks.ts +67 -0
- package/src/fragments/internal-fragment.routes.test.ts +570 -0
- package/src/fragments/internal-fragment.routes.ts +297 -5
- package/src/fragments/internal-fragment.schema.ts +45 -1
- package/src/fragments/internal-fragment.test.ts +223 -251
- package/src/fragments/internal-fragment.ts +278 -154
- package/src/hooks/durable-hooks-logger.ts +126 -0
- package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
- package/src/hooks/durable-hooks-processor.test.ts +179 -14
- package/src/hooks/durable-hooks-processor.ts +120 -14
- package/src/hooks/durable-hooks-runtime.test.ts +65 -0
- package/src/hooks/durable-hooks-runtime.ts +81 -0
- package/src/hooks/hooks.test.ts +314 -53
- package/src/hooks/hooks.ts +360 -81
- package/src/id.test.ts +34 -0
- package/src/id.ts +1 -3
- package/src/internal/adapter-registry.test.ts +93 -0
- package/src/internal/adapter-registry.ts +239 -0
- package/src/internal/outbox-state.ts +43 -0
- package/src/migration-engine/auto-from-schema.test.ts +93 -0
- package/src/migration-engine/auto-from-schema.ts +360 -42
- package/src/migration-engine/create.test.ts +2 -1
- package/src/migration-engine/create.ts +1 -1
- package/src/migration-engine/generation-engine.test.ts +66 -9
- package/src/migration-engine/generation-engine.ts +31 -10
- package/src/migration-engine/shared.ts +13 -0
- package/src/mod.ts +45 -27
- package/src/naming/sql-naming.ts +1 -0
- package/src/outbox/outbox-builder.ts +2 -2
- package/src/outbox/outbox.test.ts +216 -45
- package/src/outbox/outbox.ts +3 -1
- package/src/query/column-defaults.ts +1 -1
- package/src/query/condition-builder.test.ts +15 -0
- package/src/query/condition-builder.ts +7 -0
- package/src/query/cursor-client.test.ts +70 -0
- package/src/query/cursor-client.ts +263 -0
- package/src/query/cursor.test.ts +3 -2
- package/src/query/cursor.ts +15 -3
- package/src/query/db-now.ts +69 -2
- package/src/query/orm/orm.ts +2 -2
- package/src/query/query-type.test.ts +2 -1
- package/src/query/serialize/create-sql-serializer.ts +3 -3
- package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
- package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
- package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
- package/src/query/simple-query-interface.ts +10 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
- package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
- package/src/query/unit-of-work/retry-policy.test.ts +1 -0
- package/src/query/unit-of-work/tx-builder.test.ts +73 -1
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
- package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
- package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
- package/src/query/unit-of-work/unit-of-work.ts +105 -19
- package/src/query/value-decoding.test.ts +50 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +1 -0
- package/src/query/value-encoding.ts +1 -1
- package/src/schema/create.test.ts +164 -5
- package/src/schema/create.ts +222 -24
- package/src/schema/generate-id.test.ts +1 -0
- package/src/schema/serialize.test.ts +4 -3
- package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
- package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
- package/src/schema/type-conversion/type-mapping.test.ts +2 -1
- package/src/schema/validator.test.ts +4 -2
- package/src/schema/validator.ts +1 -0
- package/src/schema-output/drizzle.test.ts +72 -19
- package/src/schema-output/drizzle.ts +24 -18
- package/src/schema-output/prisma.test.ts +172 -14
- package/src/schema-output/prisma.ts +34 -14
- package/src/sql-driver/better-sqlite3.test.ts +5 -3
- package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
- package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
- package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
- package/src/sql-driver/query-executor/query-executor.ts +1 -1
- package/src/sql-driver/sql-driver-adapter.ts +2 -2
- package/src/sql-driver/sql.ts +2 -1
- package/src/sql-driver/sqlocal.test.ts +4 -2
- package/src/sync/commands.test.ts +39 -0
- package/src/sync/commands.ts +51 -0
- package/src/sync/conflict-checker.test.ts +450 -0
- package/src/sync/conflict-checker.ts +248 -0
- package/src/sync/index.ts +14 -0
- package/src/sync/plan.ts +9 -0
- package/src/sync/read-tracking.test.ts +177 -0
- package/src/sync/read-tracking.ts +287 -0
- package/src/sync/submit.test.ts +205 -0
- package/src/sync/submit.ts +328 -0
- package/src/sync/types.ts +80 -0
- package/src/util/default-database-adapter.ts +15 -2
- package/src/with-database.ts +20 -50
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +38 -26
- package/vitest.config.ts +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/packages/fragno/dist/api/bind-services.js +0 -20
- package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
- package/dist/packages/fragno/dist/api/error.js +0 -48
- package/dist/packages/fragno/dist/api/error.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
- package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/route.js +0 -10
- package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
- package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
- package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
- package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
- package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/route.js +0 -30
- package/dist/packages/fragno/dist/api/route.js.map +0 -1
- package/dist/packages/fragno/dist/internal/symbols.js +0 -10
- package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
- package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { DurableHooksLogger } from "./durable-hooks-logger";
|
|
4
|
+
import {
|
|
5
|
+
getDurableHooksNotifierByNamespace,
|
|
6
|
+
getDurableHooksRuntimeByConfig,
|
|
7
|
+
getDurableHooksRuntimeByNamespace,
|
|
8
|
+
getDurableHooksRuntimeByToken,
|
|
9
|
+
registerDurableHooksRuntime,
|
|
10
|
+
} from "./durable-hooks-runtime";
|
|
11
|
+
import type { HookNotifier, HookProcessorConfig } from "./hooks";
|
|
12
|
+
|
|
13
|
+
let namespaceCounter = 0;
|
|
14
|
+
const defaultInternalFragment = {} as HookProcessorConfig["internalFragment"];
|
|
15
|
+
|
|
16
|
+
function createConfig(
|
|
17
|
+
namespace: string,
|
|
18
|
+
notifier?: HookNotifier,
|
|
19
|
+
internalFragment: HookProcessorConfig["internalFragment"] = defaultInternalFragment,
|
|
20
|
+
): HookProcessorConfig {
|
|
21
|
+
return {
|
|
22
|
+
namespace,
|
|
23
|
+
hooks: {},
|
|
24
|
+
notifier,
|
|
25
|
+
internalFragment,
|
|
26
|
+
handlerTx: (() => {
|
|
27
|
+
throw new Error("handlerTx should not be called in durable-hooks-runtime tests");
|
|
28
|
+
}) as HookProcessorConfig["handlerTx"],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("durable hooks runtime registry", () => {
|
|
33
|
+
it("returns the same token when registering the same config twice", () => {
|
|
34
|
+
const namespace = `runtime-same-config-${namespaceCounter++}`;
|
|
35
|
+
const config = createConfig(namespace);
|
|
36
|
+
|
|
37
|
+
const tokenA = registerDurableHooksRuntime(config);
|
|
38
|
+
const tokenB = registerDurableHooksRuntime(config);
|
|
39
|
+
|
|
40
|
+
expect(tokenA).toBe(tokenB);
|
|
41
|
+
expect(getDurableHooksRuntimeByToken(tokenA)?.config).toBe(config);
|
|
42
|
+
expect(getDurableHooksRuntimeByConfig(config)?.token).toBe(tokenA);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("keeps the latest runtime for scoped namespace lookup and warns on duplicates", () => {
|
|
46
|
+
const warnSpy = vi.spyOn(DurableHooksLogger, "warn").mockImplementation(() => {});
|
|
47
|
+
const namespace = `runtime-duplicate-${namespaceCounter++}`;
|
|
48
|
+
const internalFragment = {} as HookProcessorConfig["internalFragment"];
|
|
49
|
+
const firstConfig = createConfig(namespace, undefined, internalFragment);
|
|
50
|
+
const notifier: HookNotifier = {
|
|
51
|
+
notify: vi.fn(),
|
|
52
|
+
};
|
|
53
|
+
const secondConfig = createConfig(namespace, notifier, internalFragment);
|
|
54
|
+
|
|
55
|
+
const firstToken = registerDurableHooksRuntime(firstConfig);
|
|
56
|
+
const secondToken = registerDurableHooksRuntime(secondConfig);
|
|
57
|
+
|
|
58
|
+
expect(firstToken).not.toBe(secondToken);
|
|
59
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(getDurableHooksRuntimeByNamespace(namespace, internalFragment)?.token).toBe(secondToken);
|
|
61
|
+
expect(getDurableHooksNotifierByNamespace(namespace, internalFragment)).toBe(notifier);
|
|
62
|
+
|
|
63
|
+
warnSpy.mockRestore();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { DurableHooksLogger } from "./durable-hooks-logger";
|
|
2
|
+
import type { HookProcessorConfig } from "./hooks";
|
|
3
|
+
|
|
4
|
+
type DurableHooksRuntimeState = {
|
|
5
|
+
token: object;
|
|
6
|
+
config: HookProcessorConfig;
|
|
7
|
+
dispatcherRegistered: boolean;
|
|
8
|
+
dispatcherWarningEmitted: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const runtimeByToken = new WeakMap<object, DurableHooksRuntimeState>();
|
|
12
|
+
const runtimeByConfig = new WeakMap<HookProcessorConfig, DurableHooksRuntimeState>();
|
|
13
|
+
const runtimesByInternalFragment = new WeakMap<
|
|
14
|
+
HookProcessorConfig["internalFragment"],
|
|
15
|
+
Map<string, DurableHooksRuntimeState>
|
|
16
|
+
>();
|
|
17
|
+
|
|
18
|
+
function getNamespaceRuntimeMap(
|
|
19
|
+
internalFragment: HookProcessorConfig["internalFragment"],
|
|
20
|
+
createIfMissing = false,
|
|
21
|
+
) {
|
|
22
|
+
const existing = runtimesByInternalFragment.get(internalFragment);
|
|
23
|
+
if (existing || !createIfMissing) {
|
|
24
|
+
return existing;
|
|
25
|
+
}
|
|
26
|
+
const created = new Map<string, DurableHooksRuntimeState>();
|
|
27
|
+
runtimesByInternalFragment.set(internalFragment, created);
|
|
28
|
+
return created;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function registerDurableHooksRuntime(config: HookProcessorConfig): object {
|
|
32
|
+
const existing = runtimeByConfig.get(config);
|
|
33
|
+
if (existing) {
|
|
34
|
+
return existing.token;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const token = {};
|
|
38
|
+
const runtime: DurableHooksRuntimeState = {
|
|
39
|
+
token,
|
|
40
|
+
config,
|
|
41
|
+
dispatcherRegistered: false,
|
|
42
|
+
dispatcherWarningEmitted: false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
runtimeByToken.set(token, runtime);
|
|
46
|
+
runtimeByConfig.set(config, runtime);
|
|
47
|
+
const runtimeByNamespace = getNamespaceRuntimeMap(config.internalFragment, true);
|
|
48
|
+
const existingForNamespace = runtimeByNamespace?.get(config.namespace);
|
|
49
|
+
if (existingForNamespace && existingForNamespace.config !== config) {
|
|
50
|
+
DurableHooksLogger.warn("Durable hooks runtime already registered for namespace", {
|
|
51
|
+
namespace: config.namespace,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
runtimeByNamespace?.set(config.namespace, runtime);
|
|
55
|
+
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getDurableHooksRuntimeByToken(token: object): DurableHooksRuntimeState | undefined {
|
|
60
|
+
return runtimeByToken.get(token);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getDurableHooksRuntimeByConfig(
|
|
64
|
+
config: HookProcessorConfig,
|
|
65
|
+
): DurableHooksRuntimeState | undefined {
|
|
66
|
+
return runtimeByConfig.get(config);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getDurableHooksRuntimeByNamespace(
|
|
70
|
+
namespace: string,
|
|
71
|
+
internalFragment: HookProcessorConfig["internalFragment"],
|
|
72
|
+
): DurableHooksRuntimeState | undefined {
|
|
73
|
+
return getNamespaceRuntimeMap(internalFragment)?.get(namespace);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getDurableHooksNotifierByNamespace(
|
|
77
|
+
namespace: string,
|
|
78
|
+
internalFragment: HookProcessorConfig["internalFragment"],
|
|
79
|
+
) {
|
|
80
|
+
return getNamespaceRuntimeMap(internalFragment)?.get(namespace)?.config.notifier;
|
|
81
|
+
}
|
package/src/hooks/hooks.test.ts
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
+
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
1
3
|
import SQLite from "better-sqlite3";
|
|
2
4
|
import { SqliteDialect } from "kysely";
|
|
3
|
-
|
|
5
|
+
|
|
4
6
|
import { instantiate } from "@fragno-dev/core";
|
|
7
|
+
|
|
8
|
+
import { BetterSQLite3DriverConfig } from "../adapters/generic-sql/driver-config";
|
|
9
|
+
import { SqlAdapter } from "../adapters/generic-sql/generic-sql-adapter";
|
|
10
|
+
import type { FragnoPublicConfigWithDatabase } from "../db-fragment-definition-builder";
|
|
11
|
+
import { internalFragmentDef, internalSchema } from "../fragments/internal-fragment";
|
|
12
|
+
import { getRegistryForAdapterSync } from "../internal/adapter-registry";
|
|
13
|
+
import { ConcurrencyConflictError } from "../query/unit-of-work/execute-unit-of-work";
|
|
14
|
+
import { ExponentialBackoffRetryPolicy, NoRetryPolicy } from "../query/unit-of-work/retry-policy";
|
|
15
|
+
import { FragnoId } from "../schema/create";
|
|
16
|
+
import { DurableHooksLogger } from "./durable-hooks-logger";
|
|
5
17
|
import {
|
|
6
18
|
prepareHookMutations,
|
|
7
19
|
processHooks,
|
|
20
|
+
createDurableHooksRunner,
|
|
8
21
|
type HooksMap,
|
|
9
22
|
type HookContext,
|
|
10
23
|
type HookHandlerTx,
|
|
11
24
|
} from "./hooks";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
|
|
26
|
+
const TEST_NS = "test";
|
|
27
|
+
|
|
28
|
+
type OptionsWithAdapter = FragnoPublicConfigWithDatabase & {
|
|
29
|
+
databaseAdapter: SqlAdapter;
|
|
30
|
+
};
|
|
18
31
|
|
|
19
32
|
describe("Hook System", () => {
|
|
20
33
|
const handlerTx = (() => {
|
|
@@ -24,8 +37,11 @@ describe("Hook System", () => {
|
|
|
24
37
|
let adapter: SqlAdapter;
|
|
25
38
|
let internalFragment: ReturnType<typeof instantiateFragment>;
|
|
26
39
|
|
|
27
|
-
function instantiateFragment(options:
|
|
28
|
-
return instantiate(internalFragmentDef)
|
|
40
|
+
function instantiateFragment(options: OptionsWithAdapter) {
|
|
41
|
+
return instantiate(internalFragmentDef)
|
|
42
|
+
.withConfig({ registry: getRegistryForAdapterSync(options.databaseAdapter) })
|
|
43
|
+
.withOptions(options)
|
|
44
|
+
.build();
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
beforeAll(async () => {
|
|
@@ -41,13 +57,13 @@ describe("Hook System", () => {
|
|
|
41
57
|
});
|
|
42
58
|
|
|
43
59
|
{
|
|
44
|
-
const migrations = adapter.prepareMigrations(internalSchema,
|
|
60
|
+
const migrations = adapter.prepareMigrations(internalSchema, TEST_NS);
|
|
45
61
|
await migrations.executeWithDriver(adapter.driver, 0);
|
|
46
62
|
}
|
|
47
63
|
|
|
48
|
-
const options:
|
|
64
|
+
const options: OptionsWithAdapter = {
|
|
49
65
|
databaseAdapter: adapter,
|
|
50
|
-
databaseNamespace:
|
|
66
|
+
databaseNamespace: TEST_NS,
|
|
51
67
|
};
|
|
52
68
|
|
|
53
69
|
internalFragment = instantiateFragment(options);
|
|
@@ -58,8 +74,11 @@ describe("Hook System", () => {
|
|
|
58
74
|
}, 12000);
|
|
59
75
|
|
|
60
76
|
describe("prepareHookMutations", () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
sqliteDatabase.exec("DELETE FROM fragno_hooks_test");
|
|
79
|
+
});
|
|
80
|
+
|
|
61
81
|
it("should create hook records for triggered hooks", async () => {
|
|
62
|
-
const namespace = "test-namespace";
|
|
63
82
|
const hooks: HooksMap = {
|
|
64
83
|
onTest: vi.fn(),
|
|
65
84
|
};
|
|
@@ -78,13 +97,11 @@ describe("Hook System", () => {
|
|
|
78
97
|
uow.triggerHook("onTest", { data: "test" });
|
|
79
98
|
|
|
80
99
|
// Prepare hook mutations
|
|
81
|
-
prepareHookMutations(
|
|
82
|
-
|
|
83
|
-
namespace,
|
|
100
|
+
prepareHookMutations(
|
|
101
|
+
uow,
|
|
84
102
|
internalFragment,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
103
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
104
|
+
);
|
|
88
105
|
})
|
|
89
106
|
.execute();
|
|
90
107
|
});
|
|
@@ -97,7 +114,7 @@ describe("Hook System", () => {
|
|
|
97
114
|
const events = await internalFragment.inContext(async function () {
|
|
98
115
|
return await this.handlerTx()
|
|
99
116
|
.withServiceCalls(
|
|
100
|
-
() => [internalFragment.services.hookService.
|
|
117
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(TEST_NS)] as const,
|
|
101
118
|
)
|
|
102
119
|
.transform(({ serviceResult: [result] }) => result)
|
|
103
120
|
.execute();
|
|
@@ -112,8 +129,97 @@ describe("Hook System", () => {
|
|
|
112
129
|
});
|
|
113
130
|
});
|
|
114
131
|
|
|
132
|
+
it("should store a provided hook id", async () => {
|
|
133
|
+
const namespace = internalFragment.$internal.deps.namespace ?? internalSchema.name;
|
|
134
|
+
const hookId = "hook-id-1";
|
|
135
|
+
const hooks: HooksMap = {
|
|
136
|
+
onId: vi.fn(),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
await internalFragment.inContext(async function () {
|
|
140
|
+
await this.handlerTx()
|
|
141
|
+
.mutate(({ forSchema }) => {
|
|
142
|
+
const uow = forSchema(internalSchema, hooks);
|
|
143
|
+
|
|
144
|
+
uow.triggerHook("onId", { data: "test" }, { id: hookId });
|
|
145
|
+
|
|
146
|
+
prepareHookMutations(
|
|
147
|
+
uow,
|
|
148
|
+
internalFragment,
|
|
149
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
150
|
+
);
|
|
151
|
+
})
|
|
152
|
+
.execute();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const events = await internalFragment.inContext(async function () {
|
|
156
|
+
return await this.handlerTx()
|
|
157
|
+
.withServiceCalls(
|
|
158
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(namespace)] as const,
|
|
159
|
+
)
|
|
160
|
+
.transform(({ serviceResult: [result] }) => result)
|
|
161
|
+
.execute();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(events).toHaveLength(1);
|
|
165
|
+
expect(events[0]?.id.externalId).toBe(hookId);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should fail when a hook id already exists", async () => {
|
|
169
|
+
const namespace = internalFragment.$internal.deps.namespace ?? internalSchema.name;
|
|
170
|
+
const hookId = "hook-id-conflict";
|
|
171
|
+
const hooks: HooksMap = {
|
|
172
|
+
onIdConflict: vi.fn(),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
await internalFragment.inContext(async function () {
|
|
176
|
+
await this.handlerTx()
|
|
177
|
+
.mutate(({ forSchema }) => {
|
|
178
|
+
const uow = forSchema(internalSchema, hooks);
|
|
179
|
+
|
|
180
|
+
uow.triggerHook("onIdConflict", { data: "first" }, { id: hookId });
|
|
181
|
+
|
|
182
|
+
prepareHookMutations(
|
|
183
|
+
uow,
|
|
184
|
+
internalFragment,
|
|
185
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
186
|
+
);
|
|
187
|
+
})
|
|
188
|
+
.execute();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await expect(
|
|
192
|
+
internalFragment.inContext(async function () {
|
|
193
|
+
await this.handlerTx()
|
|
194
|
+
.mutate(({ forSchema }) => {
|
|
195
|
+
const uow = forSchema(internalSchema, hooks);
|
|
196
|
+
|
|
197
|
+
uow.triggerHook("onIdConflict", { data: "second" }, { id: hookId });
|
|
198
|
+
|
|
199
|
+
prepareHookMutations(
|
|
200
|
+
uow,
|
|
201
|
+
internalFragment,
|
|
202
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
203
|
+
);
|
|
204
|
+
})
|
|
205
|
+
.execute();
|
|
206
|
+
}),
|
|
207
|
+
).rejects.toThrow();
|
|
208
|
+
|
|
209
|
+
const events = await internalFragment.inContext(async function () {
|
|
210
|
+
return await this.handlerTx()
|
|
211
|
+
.withServiceCalls(
|
|
212
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(namespace)] as const,
|
|
213
|
+
)
|
|
214
|
+
.transform(({ serviceResult: [result] }) => result)
|
|
215
|
+
.execute();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(events).toHaveLength(1);
|
|
219
|
+
expect(events[0]?.id.externalId).toBe(hookId);
|
|
220
|
+
});
|
|
221
|
+
|
|
115
222
|
it("should set maxAttempts to 1 when retry policy does not retry", async () => {
|
|
116
|
-
const namespace = "test-no-retry";
|
|
117
223
|
const hooks: HooksMap = {
|
|
118
224
|
onNoRetry: vi.fn(),
|
|
119
225
|
};
|
|
@@ -125,13 +231,7 @@ describe("Hook System", () => {
|
|
|
125
231
|
|
|
126
232
|
uow.triggerHook("onNoRetry", { data: "test" });
|
|
127
233
|
|
|
128
|
-
prepareHookMutations(uow,
|
|
129
|
-
hooks,
|
|
130
|
-
namespace,
|
|
131
|
-
internalFragment,
|
|
132
|
-
handlerTx,
|
|
133
|
-
defaultRetryPolicy: new NoRetryPolicy(),
|
|
134
|
-
});
|
|
234
|
+
prepareHookMutations(uow, internalFragment, new NoRetryPolicy());
|
|
135
235
|
})
|
|
136
236
|
.execute();
|
|
137
237
|
});
|
|
@@ -139,7 +239,7 @@ describe("Hook System", () => {
|
|
|
139
239
|
const events = await internalFragment.inContext(async function () {
|
|
140
240
|
return await this.handlerTx()
|
|
141
241
|
.withServiceCalls(
|
|
142
|
-
() => [internalFragment.services.hookService.
|
|
242
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(TEST_NS)] as const,
|
|
143
243
|
)
|
|
144
244
|
.transform(({ serviceResult: [result] }) => result)
|
|
145
245
|
.execute();
|
|
@@ -150,7 +250,6 @@ describe("Hook System", () => {
|
|
|
150
250
|
});
|
|
151
251
|
|
|
152
252
|
it("should use custom retry policy from trigger options", async () => {
|
|
153
|
-
const namespace = "test-custom-retry";
|
|
154
253
|
const hooks: HooksMap = {
|
|
155
254
|
onCustomRetry: vi.fn(),
|
|
156
255
|
};
|
|
@@ -168,13 +267,11 @@ describe("Hook System", () => {
|
|
|
168
267
|
},
|
|
169
268
|
);
|
|
170
269
|
|
|
171
|
-
prepareHookMutations(
|
|
172
|
-
|
|
173
|
-
namespace,
|
|
270
|
+
prepareHookMutations(
|
|
271
|
+
uow,
|
|
174
272
|
internalFragment,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
273
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 10 }),
|
|
274
|
+
);
|
|
178
275
|
})
|
|
179
276
|
.execute();
|
|
180
277
|
});
|
|
@@ -182,7 +279,7 @@ describe("Hook System", () => {
|
|
|
182
279
|
const events = await internalFragment.inContext(async function () {
|
|
183
280
|
return await this.handlerTx()
|
|
184
281
|
.withServiceCalls(
|
|
185
|
-
() => [internalFragment.services.hookService.
|
|
282
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(TEST_NS)] as const,
|
|
186
283
|
)
|
|
187
284
|
.transform(({ serviceResult: [result] }) => result)
|
|
188
285
|
.execute();
|
|
@@ -192,7 +289,6 @@ describe("Hook System", () => {
|
|
|
192
289
|
});
|
|
193
290
|
|
|
194
291
|
it("should set nextRetryAt when processAt is in the future", async () => {
|
|
195
|
-
const namespace = "test-process-at-future";
|
|
196
292
|
const hooks: HooksMap = {
|
|
197
293
|
onScheduled: vi.fn(),
|
|
198
294
|
};
|
|
@@ -205,13 +301,11 @@ describe("Hook System", () => {
|
|
|
205
301
|
|
|
206
302
|
uow.triggerHook("onScheduled", { data: "test" }, { processAt: futureTime });
|
|
207
303
|
|
|
208
|
-
prepareHookMutations(
|
|
209
|
-
|
|
210
|
-
namespace,
|
|
304
|
+
prepareHookMutations(
|
|
305
|
+
uow,
|
|
211
306
|
internalFragment,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
307
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
308
|
+
);
|
|
215
309
|
})
|
|
216
310
|
.execute();
|
|
217
311
|
});
|
|
@@ -219,7 +313,7 @@ describe("Hook System", () => {
|
|
|
219
313
|
const events = await internalFragment.inContext(async function () {
|
|
220
314
|
return await this.handlerTx()
|
|
221
315
|
.withServiceCalls(
|
|
222
|
-
() => [internalFragment.services.hookService.getHooksByNamespace(
|
|
316
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(TEST_NS)] as const,
|
|
223
317
|
)
|
|
224
318
|
.transform(({ serviceResult: [result] }) => result)
|
|
225
319
|
.execute();
|
|
@@ -231,7 +325,6 @@ describe("Hook System", () => {
|
|
|
231
325
|
});
|
|
232
326
|
|
|
233
327
|
it("should keep processAt in the past while remaining immediately eligible", async () => {
|
|
234
|
-
const namespace = "test-process-at-past";
|
|
235
328
|
const hooks: HooksMap = {
|
|
236
329
|
onImmediate: vi.fn(),
|
|
237
330
|
};
|
|
@@ -244,13 +337,11 @@ describe("Hook System", () => {
|
|
|
244
337
|
|
|
245
338
|
uow.triggerHook("onImmediate", { data: "test" }, { processAt: pastTime });
|
|
246
339
|
|
|
247
|
-
prepareHookMutations(
|
|
248
|
-
|
|
249
|
-
namespace,
|
|
340
|
+
prepareHookMutations(
|
|
341
|
+
uow,
|
|
250
342
|
internalFragment,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
});
|
|
343
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
344
|
+
);
|
|
254
345
|
})
|
|
255
346
|
.execute();
|
|
256
347
|
});
|
|
@@ -258,7 +349,7 @@ describe("Hook System", () => {
|
|
|
258
349
|
const events = await internalFragment.inContext(async function () {
|
|
259
350
|
return await this.handlerTx()
|
|
260
351
|
.withServiceCalls(
|
|
261
|
-
() => [internalFragment.services.hookService.getHooksByNamespace(
|
|
352
|
+
() => [internalFragment.services.hookService.getHooksByNamespace(TEST_NS)] as const,
|
|
262
353
|
)
|
|
263
354
|
.transform(({ serviceResult: [result] }) => result)
|
|
264
355
|
.execute();
|
|
@@ -268,6 +359,54 @@ describe("Hook System", () => {
|
|
|
268
359
|
expect(events[0]?.nextRetryAt).toBeInstanceOf(Date);
|
|
269
360
|
expect(events[0]?.nextRetryAt?.getTime()).toBe(pastTime.getTime());
|
|
270
361
|
});
|
|
362
|
+
|
|
363
|
+
it("should preserve full hookName in queued hooks log summary when it contains colons", async () => {
|
|
364
|
+
const hookName = "on:segment:created";
|
|
365
|
+
const hooks: HooksMap = {
|
|
366
|
+
[hookName]: vi.fn(),
|
|
367
|
+
};
|
|
368
|
+
const debugSpy = vi.spyOn(DurableHooksLogger, "debug").mockImplementation(() => {});
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
await internalFragment.inContext(async function () {
|
|
372
|
+
await this.handlerTx()
|
|
373
|
+
.mutate(({ forSchema }) => {
|
|
374
|
+
const uow = forSchema(internalSchema, hooks);
|
|
375
|
+
uow.triggerHook(hookName, { data: "test" });
|
|
376
|
+
prepareHookMutations(
|
|
377
|
+
uow,
|
|
378
|
+
internalFragment,
|
|
379
|
+
new ExponentialBackoffRetryPolicy({ maxRetries: 5 }),
|
|
380
|
+
);
|
|
381
|
+
})
|
|
382
|
+
.execute();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const queuedCall = debugSpy.mock.calls.find(
|
|
386
|
+
([message]) => message === "Durable hooks queued",
|
|
387
|
+
);
|
|
388
|
+
expect(queuedCall).toBeDefined();
|
|
389
|
+
|
|
390
|
+
const fields = queuedCall?.[1]?.fields;
|
|
391
|
+
expect(typeof fields).toBe("function");
|
|
392
|
+
|
|
393
|
+
const summary = (
|
|
394
|
+
fields as () => {
|
|
395
|
+
hooks: Array<{ namespace: string; hookName: string; count: number }>;
|
|
396
|
+
total: number;
|
|
397
|
+
}
|
|
398
|
+
)();
|
|
399
|
+
|
|
400
|
+
expect(summary.hooks).toContainEqual({
|
|
401
|
+
namespace: TEST_NS,
|
|
402
|
+
hookName,
|
|
403
|
+
count: 1,
|
|
404
|
+
});
|
|
405
|
+
expect(summary.total).toBe(1);
|
|
406
|
+
} finally {
|
|
407
|
+
debugSpy.mockRestore();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
271
410
|
});
|
|
272
411
|
|
|
273
412
|
describe("processHooks", () => {
|
|
@@ -333,6 +472,79 @@ describe("Hook System", () => {
|
|
|
333
472
|
expect(result?.lastAttemptAt).toBeInstanceOf(Date);
|
|
334
473
|
});
|
|
335
474
|
|
|
475
|
+
it("should surface conflicts when a hook mutates its own record", async () => {
|
|
476
|
+
const namespace = "test-claim-conflict";
|
|
477
|
+
let eventId!: FragnoId;
|
|
478
|
+
|
|
479
|
+
const hookFn = vi.fn(async (payload: { externalId: string; version: number }) => {
|
|
480
|
+
const conflictId = new FragnoId({
|
|
481
|
+
externalId: payload.externalId,
|
|
482
|
+
version: payload.version,
|
|
483
|
+
});
|
|
484
|
+
await internalFragment.inContext(async function () {
|
|
485
|
+
await this.handlerTx()
|
|
486
|
+
.mutate(({ forSchema }) => {
|
|
487
|
+
const uow = forSchema(internalSchema);
|
|
488
|
+
uow.update("fragno_hooks", conflictId, (b) => b.set({ status: "processing" }));
|
|
489
|
+
})
|
|
490
|
+
.execute();
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const hooks: HooksMap = {
|
|
495
|
+
onConflict: hookFn,
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
await internalFragment.inContext(async function () {
|
|
499
|
+
const createdId = await this.handlerTx()
|
|
500
|
+
.mutate(({ forSchema }) => {
|
|
501
|
+
const uow = forSchema(internalSchema);
|
|
502
|
+
return uow.create("fragno_hooks", {
|
|
503
|
+
id: "hook-conflict-id",
|
|
504
|
+
namespace,
|
|
505
|
+
hookName: "onConflict",
|
|
506
|
+
payload: { externalId: "hook-conflict-id", version: 0 },
|
|
507
|
+
status: "pending",
|
|
508
|
+
attempts: 0,
|
|
509
|
+
maxAttempts: 5,
|
|
510
|
+
lastAttemptAt: null,
|
|
511
|
+
nextRetryAt: null,
|
|
512
|
+
error: null,
|
|
513
|
+
nonce: "test-nonce-conflict",
|
|
514
|
+
});
|
|
515
|
+
})
|
|
516
|
+
.execute();
|
|
517
|
+
eventId = createdId;
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
await expect(
|
|
521
|
+
processHooks({
|
|
522
|
+
hooks,
|
|
523
|
+
namespace,
|
|
524
|
+
internalFragment,
|
|
525
|
+
handlerTx,
|
|
526
|
+
defaultRetryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3 }),
|
|
527
|
+
}),
|
|
528
|
+
).rejects.toThrow(ConcurrencyConflictError);
|
|
529
|
+
|
|
530
|
+
expect(hookFn).toHaveBeenCalledOnce();
|
|
531
|
+
expect(hookFn).toHaveBeenCalledWith({
|
|
532
|
+
externalId: eventId.externalId,
|
|
533
|
+
version: eventId.version,
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
const event = await internalFragment.inContext(async function () {
|
|
537
|
+
return await this.handlerTx()
|
|
538
|
+
.withServiceCalls(
|
|
539
|
+
() => [internalFragment.services.hookService.getHookById(eventId)] as const,
|
|
540
|
+
)
|
|
541
|
+
.transform(({ serviceResult: [result] }) => result)
|
|
542
|
+
.execute();
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
expect(event?.status).toBe("processing");
|
|
546
|
+
});
|
|
547
|
+
|
|
336
548
|
it("should mark failed hooks for retry", async () => {
|
|
337
549
|
const namespace = "test-failure";
|
|
338
550
|
const hookFn = vi.fn().mockRejectedValue(new Error("Hook failed"));
|
|
@@ -736,4 +948,53 @@ describe("Hook System", () => {
|
|
|
736
948
|
expect(hookFn).not.toHaveBeenCalled();
|
|
737
949
|
});
|
|
738
950
|
});
|
|
951
|
+
|
|
952
|
+
describe("createDurableHooksRunner", () => {
|
|
953
|
+
it("should return the total processed count across queued reruns", async () => {
|
|
954
|
+
const namespace = "test-runner-queued";
|
|
955
|
+
let resolveHook!: () => void;
|
|
956
|
+
const hookDone = new Promise<void>((resolve) => {
|
|
957
|
+
resolveHook = resolve;
|
|
958
|
+
});
|
|
959
|
+
const hookFn = vi.fn(async () => {
|
|
960
|
+
await hookDone;
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
await internalFragment.inContext(async function () {
|
|
964
|
+
await this.handlerTx()
|
|
965
|
+
.mutate(({ forSchema }) => {
|
|
966
|
+
const uow = forSchema(internalSchema);
|
|
967
|
+
uow.create("fragno_hooks", {
|
|
968
|
+
namespace,
|
|
969
|
+
hookName: "onQueued",
|
|
970
|
+
payload: { ok: true },
|
|
971
|
+
status: "pending",
|
|
972
|
+
attempts: 0,
|
|
973
|
+
maxAttempts: 1,
|
|
974
|
+
lastAttemptAt: null,
|
|
975
|
+
nextRetryAt: null,
|
|
976
|
+
error: null,
|
|
977
|
+
nonce: "runner-queued-nonce",
|
|
978
|
+
});
|
|
979
|
+
})
|
|
980
|
+
.execute();
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
const runner = createDurableHooksRunner({
|
|
984
|
+
hooks: { onQueued: hookFn },
|
|
985
|
+
namespace,
|
|
986
|
+
internalFragment,
|
|
987
|
+
handlerTx,
|
|
988
|
+
defaultRetryPolicy: new NoRetryPolicy(),
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
const firstRun = runner.processDue();
|
|
992
|
+
const queuedRun = runner.processDue();
|
|
993
|
+
resolveHook();
|
|
994
|
+
|
|
995
|
+
await expect(firstRun).resolves.toBe(1);
|
|
996
|
+
await expect(queuedRun).resolves.toBe(1);
|
|
997
|
+
expect(hookFn).toHaveBeenCalledTimes(1);
|
|
998
|
+
});
|
|
999
|
+
});
|
|
739
1000
|
});
|