@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
package/src/hooks/hooks.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { InternalFragmentInstance } from "../fragments/internal-fragment";
|
|
2
|
+
import { dbNow, isDbNow, type DbNow } from "../query/db-now";
|
|
1
3
|
import type {
|
|
2
4
|
ExecuteTxOptions,
|
|
3
5
|
HandlerTxBuilder,
|
|
@@ -5,9 +7,8 @@ import type {
|
|
|
5
7
|
import type { RetryPolicy } from "../query/unit-of-work/retry-policy";
|
|
6
8
|
import { ExponentialBackoffRetryPolicy } from "../query/unit-of-work/retry-policy";
|
|
7
9
|
import type { IUnitOfWork } from "../query/unit-of-work/unit-of-work";
|
|
8
|
-
import type { InternalFragmentInstance } from "../fragments/internal-fragment";
|
|
9
|
-
import type { TxResult } from "../query/unit-of-work/execute-unit-of-work";
|
|
10
10
|
import type { FragnoId } from "../schema/create";
|
|
11
|
+
import { DurableHooksLogger, type DurableHooksLoggerConfig } from "./durable-hooks-logger";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Context available in hook functions via `this`.
|
|
@@ -19,12 +20,58 @@ export interface HookContext {
|
|
|
19
20
|
* Use this for idempotency checks in your hook implementation.
|
|
20
21
|
*/
|
|
21
22
|
idempotencyKey: string;
|
|
23
|
+
/**
|
|
24
|
+
* Hook event identifier (versioned FragnoId).
|
|
25
|
+
*/
|
|
26
|
+
hookId: FragnoId;
|
|
27
|
+
/**
|
|
28
|
+
* Hook name for this event.
|
|
29
|
+
*/
|
|
30
|
+
hookName: string;
|
|
31
|
+
/**
|
|
32
|
+
* Current status of the hook event.
|
|
33
|
+
*/
|
|
34
|
+
status: HookStatus;
|
|
35
|
+
/**
|
|
36
|
+
* Attempt count for this hook event.
|
|
37
|
+
*/
|
|
38
|
+
attempts: number;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum attempts configured for this hook event.
|
|
41
|
+
*/
|
|
42
|
+
maxAttempts: number;
|
|
43
|
+
/**
|
|
44
|
+
* Timestamp of the last attempt (if any).
|
|
45
|
+
*/
|
|
46
|
+
lastAttemptAt: Date | null;
|
|
47
|
+
/**
|
|
48
|
+
* Next scheduled retry timestamp (if any).
|
|
49
|
+
*/
|
|
50
|
+
nextRetryAt: Date | null;
|
|
51
|
+
/**
|
|
52
|
+
* When the hook event was created.
|
|
53
|
+
*/
|
|
54
|
+
createdAt: Date;
|
|
22
55
|
/**
|
|
23
56
|
* Create a handler transaction builder to run atomic operations.
|
|
24
57
|
*/
|
|
25
58
|
handlerTx: HookHandlerTx;
|
|
26
59
|
}
|
|
27
60
|
|
|
61
|
+
export const hookStatusValues = ["pending", "processing", "completed", "failed"] as const;
|
|
62
|
+
|
|
63
|
+
export type HookStatus = (typeof hookStatusValues)[number];
|
|
64
|
+
|
|
65
|
+
export const isHookStatus = (value: unknown): value is HookStatus =>
|
|
66
|
+
typeof value === "string" && hookStatusValues.includes(value as HookStatus);
|
|
67
|
+
|
|
68
|
+
const assertHookStatus = (value: unknown): HookStatus => {
|
|
69
|
+
if (isHookStatus(value)) {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Invalid hook status: ${String(value)}`);
|
|
73
|
+
};
|
|
74
|
+
|
|
28
75
|
/**
|
|
29
76
|
* A hook function signature.
|
|
30
77
|
* Hooks receive a typed payload and access context via `this`.
|
|
@@ -52,11 +99,16 @@ export interface TriggerHookOptions {
|
|
|
52
99
|
* If not provided, uses the default retry policy.
|
|
53
100
|
*/
|
|
54
101
|
retryPolicy?: RetryPolicy;
|
|
102
|
+
/**
|
|
103
|
+
* Optional hook event ID. When provided, the hook insert will fail if a hook
|
|
104
|
+
* with the same ID already exists.
|
|
105
|
+
*/
|
|
106
|
+
id?: string | FragnoId;
|
|
55
107
|
/**
|
|
56
108
|
* Absolute time for the first attempt. If in the future, the hook is
|
|
57
109
|
* scheduled for that time; if in the past, it runs immediately.
|
|
58
110
|
*/
|
|
59
|
-
processAt?: Date;
|
|
111
|
+
processAt?: Date | DbNow;
|
|
60
112
|
}
|
|
61
113
|
|
|
62
114
|
/**
|
|
@@ -64,11 +116,32 @@ export interface TriggerHookOptions {
|
|
|
64
116
|
* Stored in the Unit of Work before execution.
|
|
65
117
|
*/
|
|
66
118
|
export interface TriggeredHook {
|
|
119
|
+
namespace: string;
|
|
67
120
|
hookName: string;
|
|
68
121
|
payload: unknown;
|
|
69
122
|
options?: TriggerHookOptions;
|
|
70
123
|
}
|
|
71
124
|
|
|
125
|
+
export type HookNotifySource = "request" | "hook" | "alarm";
|
|
126
|
+
|
|
127
|
+
export type HookNotifyContext = {
|
|
128
|
+
source: HookNotifySource;
|
|
129
|
+
route?: string | null;
|
|
130
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type HookNotifier = {
|
|
134
|
+
notify: (context: HookNotifyContext) => void | Promise<void>;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type DurableHooksRunner = {
|
|
138
|
+
processDue: () => Promise<number>;
|
|
139
|
+
drain: () => Promise<void>;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated Use DurableHooksRunner.
|
|
144
|
+
*/
|
|
72
145
|
export type HookScheduler = {
|
|
73
146
|
schedule: () => Promise<number>;
|
|
74
147
|
drain: () => Promise<void>;
|
|
@@ -78,12 +151,25 @@ export type HookScheduler = {
|
|
|
78
151
|
* Configuration for hook processing.
|
|
79
152
|
*/
|
|
80
153
|
export interface HookProcessorConfig<THooks extends HooksMap = HooksMap> {
|
|
81
|
-
hooks
|
|
154
|
+
hooks?: THooks;
|
|
82
155
|
namespace: string;
|
|
83
156
|
internalFragment: InternalFragmentInstance;
|
|
84
157
|
handlerTx: HookHandlerTx;
|
|
85
158
|
/**
|
|
86
|
-
*
|
|
159
|
+
* Whether post-mutate notifications should auto-schedule processing.
|
|
160
|
+
* Defaults to true.
|
|
161
|
+
*/
|
|
162
|
+
autoSchedule?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Internal hook runner used to coordinate processing/drain.
|
|
165
|
+
*/
|
|
166
|
+
runner?: DurableHooksRunner;
|
|
167
|
+
/**
|
|
168
|
+
* Post-commit durable hooks notifier.
|
|
169
|
+
*/
|
|
170
|
+
notifier?: HookNotifier;
|
|
171
|
+
/**
|
|
172
|
+
* @deprecated Use `runner`.
|
|
87
173
|
*/
|
|
88
174
|
scheduler?: HookScheduler;
|
|
89
175
|
defaultRetryPolicy?: RetryPolicy;
|
|
@@ -128,11 +214,21 @@ export type DurableHooksProcessingOptions = {
|
|
|
128
214
|
* Default: 10 minutes.
|
|
129
215
|
*/
|
|
130
216
|
stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
|
|
217
|
+
/**
|
|
218
|
+
* Automatically schedule hook processing after successful mutations.
|
|
219
|
+
* Defaults to true. Set false to disable auto scheduling (manual drain only).
|
|
220
|
+
*/
|
|
221
|
+
autoSchedule?: boolean;
|
|
131
222
|
/**
|
|
132
223
|
* Called when stuck processing hooks are detected and re-queued.
|
|
133
224
|
* Invoked after the hooks are moved back to `pending`.
|
|
134
225
|
*/
|
|
135
226
|
onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
|
|
227
|
+
/**
|
|
228
|
+
* Durable hooks logging controls.
|
|
229
|
+
* Defaults: enabled=true, level="warn".
|
|
230
|
+
*/
|
|
231
|
+
logging?: DurableHooksLoggerConfig;
|
|
136
232
|
};
|
|
137
233
|
|
|
138
234
|
export type HookHandlerTx = (
|
|
@@ -157,12 +253,16 @@ function resolveStuckProcessingTimeoutMinutes(
|
|
|
157
253
|
* Add hook events as mutation operations to the UOW.
|
|
158
254
|
* This should be called before executeMutations() so hook records are created
|
|
159
255
|
* in the same transaction as the user's mutations.
|
|
256
|
+
*
|
|
257
|
+
* Each triggered hook carries its own namespace (set at trigger-time), so this
|
|
258
|
+
* function works regardless of which fragment's handler is executing. When a
|
|
259
|
+
* `config` is provided its `defaultRetryPolicy` is used as fallback.
|
|
160
260
|
*/
|
|
161
|
-
export function prepareHookMutations
|
|
162
|
-
uow: IUnitOfWork,
|
|
163
|
-
|
|
261
|
+
export function prepareHookMutations(
|
|
262
|
+
uow: Pick<IUnitOfWork, "getTriggeredHooks" | "forSchema" | "idempotencyKey">,
|
|
263
|
+
internalFragment: InternalFragmentInstance,
|
|
264
|
+
defaultRetryPolicy?: RetryPolicy,
|
|
164
265
|
): void {
|
|
165
|
-
const { namespace, internalFragment, defaultRetryPolicy } = config;
|
|
166
266
|
const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
|
|
167
267
|
|
|
168
268
|
const triggeredHooks = uow.getTriggeredHooks();
|
|
@@ -171,16 +271,45 @@ export function prepareHookMutations<THooks extends HooksMap>(
|
|
|
171
271
|
return;
|
|
172
272
|
}
|
|
173
273
|
|
|
274
|
+
const primaryNamespace = triggeredHooks[0]?.namespace;
|
|
275
|
+
DurableHooksLogger.debug("Durable hooks queued", {
|
|
276
|
+
namespace: primaryNamespace,
|
|
277
|
+
fields: () => {
|
|
278
|
+
const hookSummary = new Map<string, { namespace: string; hookName: string; count: number }>();
|
|
279
|
+
for (const hook of triggeredHooks) {
|
|
280
|
+
const key = `${hook.namespace}:${hook.hookName}`;
|
|
281
|
+
const summary = hookSummary.get(key);
|
|
282
|
+
if (summary) {
|
|
283
|
+
summary.count += 1;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
hookSummary.set(key, {
|
|
287
|
+
namespace: hook.namespace,
|
|
288
|
+
hookName: hook.hookName,
|
|
289
|
+
count: 1,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
const hooks = Array.from(hookSummary.values());
|
|
293
|
+
return {
|
|
294
|
+
hooks,
|
|
295
|
+
total: triggeredHooks.length,
|
|
296
|
+
};
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
174
300
|
const internalSchema = internalFragment.$internal.deps.schema;
|
|
175
301
|
const internalUow = uow.forSchema(internalSchema);
|
|
176
302
|
|
|
177
303
|
for (const hook of triggeredHooks) {
|
|
178
304
|
const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;
|
|
179
305
|
const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;
|
|
180
|
-
const
|
|
306
|
+
const rawProcessAt = hook.options?.processAt ?? null;
|
|
307
|
+
const processAt: Date | DbNow | null =
|
|
308
|
+
rawProcessAt === null ? null : isDbNow(rawProcessAt) ? rawProcessAt : new Date(rawProcessAt);
|
|
181
309
|
const nextRetryAt = processAt ?? null;
|
|
182
|
-
|
|
183
|
-
|
|
310
|
+
const hookValues = {
|
|
311
|
+
...(hook.options?.id !== undefined ? { id: hook.options.id } : {}),
|
|
312
|
+
namespace: hook.namespace,
|
|
184
313
|
hookName: hook.hookName,
|
|
185
314
|
payload: hook.payload,
|
|
186
315
|
status: "pending",
|
|
@@ -190,7 +319,9 @@ export function prepareHookMutations<THooks extends HooksMap>(
|
|
|
190
319
|
nextRetryAt,
|
|
191
320
|
error: null,
|
|
192
321
|
nonce: uow.idempotencyKey,
|
|
193
|
-
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
internalUow.create("fragno_hooks", hookValues);
|
|
194
325
|
}
|
|
195
326
|
}
|
|
196
327
|
|
|
@@ -202,68 +333,113 @@ export async function processHooks<THooks extends HooksMap>(
|
|
|
202
333
|
config: HookProcessorConfig<THooks>,
|
|
203
334
|
): Promise<number> {
|
|
204
335
|
const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
|
|
336
|
+
if (!hooks) {
|
|
337
|
+
throw new Error("Hook processor hooks not initialized.");
|
|
338
|
+
}
|
|
205
339
|
const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
|
|
206
340
|
const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(
|
|
207
341
|
config.stuckProcessingTimeoutMinutes,
|
|
208
342
|
);
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
] as const,
|
|
230
|
-
)
|
|
231
|
-
.transform(({ serviceResult: [events] }) => events)
|
|
232
|
-
.execute();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
if (stuckEvents.length > 0) {
|
|
236
|
-
try {
|
|
237
|
-
config.onStuckProcessingHooks?.({
|
|
238
|
-
namespace,
|
|
239
|
-
timeoutMinutes: stuckProcessingTimeoutMinutes,
|
|
240
|
-
events: stuckEvents,
|
|
241
|
-
});
|
|
242
|
-
} catch (error) {
|
|
243
|
-
console.error("Error calling onStuckProcessingHooks", error);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Claim pending events in the same transaction to avoid double-processing.
|
|
249
|
-
const pendingEvents = await internalFragment.inContext(async function () {
|
|
343
|
+
const internalSchema = internalFragment.$internal.deps.schema;
|
|
344
|
+
const includeStuckProcessing = stuckProcessingTimeoutMinutes !== false;
|
|
345
|
+
const staleBefore = includeStuckProcessing
|
|
346
|
+
? dbNow().plus({ minutes: -stuckProcessingTimeoutMinutes })
|
|
347
|
+
: null;
|
|
348
|
+
let claimedEvents: Array<{
|
|
349
|
+
id: FragnoId;
|
|
350
|
+
hookName: string;
|
|
351
|
+
payload: unknown;
|
|
352
|
+
status: HookStatus;
|
|
353
|
+
attempts: number;
|
|
354
|
+
maxAttempts: number;
|
|
355
|
+
idempotencyKey: string;
|
|
356
|
+
lastAttemptAt: Date | null;
|
|
357
|
+
nextRetryAt: Date | null;
|
|
358
|
+
createdAt: Date;
|
|
359
|
+
}> = [];
|
|
360
|
+
let stuckEvents: StuckHookProcessingEvent[] = [];
|
|
361
|
+
|
|
362
|
+
const result = await internalFragment.inContext(async function () {
|
|
250
363
|
return await this.handlerTx()
|
|
251
|
-
.withServiceCalls(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
364
|
+
.withServiceCalls(() => {
|
|
365
|
+
const pending = internalFragment.services.hookService.claimPendingHookEvents(namespace);
|
|
366
|
+
const stuck = includeStuckProcessing
|
|
367
|
+
? internalFragment.services.hookService.claimStuckProcessingHookEvents(
|
|
368
|
+
namespace,
|
|
369
|
+
staleBefore!,
|
|
370
|
+
)
|
|
371
|
+
: undefined;
|
|
372
|
+
return [pending, stuck] as const;
|
|
373
|
+
})
|
|
374
|
+
.transform(({ serviceResult: [pendingEvents, stuckResult] }) => ({
|
|
375
|
+
pendingEvents,
|
|
376
|
+
stuckResult,
|
|
377
|
+
}))
|
|
255
378
|
.execute();
|
|
256
379
|
});
|
|
257
380
|
|
|
258
|
-
|
|
381
|
+
const pendingCount = result.pendingEvents.length;
|
|
382
|
+
const stuckClaimedCount = result.stuckResult?.events.length ?? 0;
|
|
383
|
+
const stuckRequeuedCount = result.stuckResult?.stuckEvents.length ?? 0;
|
|
384
|
+
const staleBeforeApprox = includeStuckProcessing
|
|
385
|
+
? new Date(Date.now() - stuckProcessingTimeoutMinutes * 60_000).toISOString()
|
|
386
|
+
: null;
|
|
387
|
+
DurableHooksLogger.debug("Durable hooks claimed", {
|
|
388
|
+
namespace,
|
|
389
|
+
fields: {
|
|
390
|
+
pending: pendingCount,
|
|
391
|
+
stuck: stuckClaimedCount,
|
|
392
|
+
requeued: stuckRequeuedCount,
|
|
393
|
+
includeStuckProcessing,
|
|
394
|
+
staleBeforeApprox,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
claimedEvents = [...result.pendingEvents, ...(result.stuckResult?.events ?? [])].map((event) => ({
|
|
399
|
+
...event,
|
|
400
|
+
status: assertHookStatus(event.status),
|
|
401
|
+
}));
|
|
402
|
+
stuckEvents = result.stuckResult?.stuckEvents ?? [];
|
|
403
|
+
|
|
404
|
+
if (includeStuckProcessing && stuckEvents.length > 0) {
|
|
405
|
+
try {
|
|
406
|
+
config.onStuckProcessingHooks?.({
|
|
407
|
+
namespace,
|
|
408
|
+
timeoutMinutes: stuckProcessingTimeoutMinutes,
|
|
409
|
+
events: stuckEvents,
|
|
410
|
+
});
|
|
411
|
+
} catch (error) {
|
|
412
|
+
DurableHooksLogger.error("Error calling onStuckProcessingHooks", {
|
|
413
|
+
namespace,
|
|
414
|
+
fields: {
|
|
415
|
+
error: DurableHooksLogger.toErrorMessage(error),
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (claimedEvents.length === 0) {
|
|
422
|
+
DurableHooksLogger.debug("Durable hooks idle", {
|
|
423
|
+
namespace,
|
|
424
|
+
});
|
|
259
425
|
return 0;
|
|
260
426
|
}
|
|
261
427
|
|
|
262
428
|
// Process events (async work outside transaction)
|
|
263
429
|
const processedEvents = await Promise.allSettled(
|
|
264
|
-
|
|
430
|
+
claimedEvents.map(async (event) => {
|
|
265
431
|
const hookFn = hooks[event.hookName];
|
|
266
432
|
if (!hookFn) {
|
|
433
|
+
DurableHooksLogger.error("Hook missing", {
|
|
434
|
+
namespace,
|
|
435
|
+
fields: {
|
|
436
|
+
eventId: event.id,
|
|
437
|
+
hookName: event.hookName,
|
|
438
|
+
attempts: event.attempts,
|
|
439
|
+
maxAttempts: event.maxAttempts,
|
|
440
|
+
createdAt: event.createdAt.toISOString(),
|
|
441
|
+
},
|
|
442
|
+
});
|
|
267
443
|
return {
|
|
268
444
|
eventId: event.id,
|
|
269
445
|
status: "failed" as const,
|
|
@@ -274,17 +450,56 @@ export async function processHooks<THooks extends HooksMap>(
|
|
|
274
450
|
}
|
|
275
451
|
|
|
276
452
|
try {
|
|
453
|
+
const startedAt = Date.now();
|
|
454
|
+
DurableHooksLogger.debug("Hook start", {
|
|
455
|
+
namespace,
|
|
456
|
+
fields: {
|
|
457
|
+
eventId: event.id,
|
|
458
|
+
hookName: event.hookName,
|
|
459
|
+
status: event.status,
|
|
460
|
+
attempts: event.attempts,
|
|
461
|
+
maxAttempts: event.maxAttempts,
|
|
462
|
+
createdAt: event.createdAt.toISOString(),
|
|
463
|
+
nextRetryAt: event.nextRetryAt ? event.nextRetryAt.toISOString() : null,
|
|
464
|
+
lastAttemptAt: event.lastAttemptAt ? event.lastAttemptAt.toISOString() : null,
|
|
465
|
+
},
|
|
466
|
+
});
|
|
277
467
|
const hookContext: HookContext = {
|
|
278
468
|
idempotencyKey: event.idempotencyKey,
|
|
469
|
+
hookId: event.id,
|
|
470
|
+
hookName: event.hookName,
|
|
471
|
+
status: event.status,
|
|
472
|
+
attempts: event.attempts,
|
|
473
|
+
maxAttempts: event.maxAttempts,
|
|
474
|
+
lastAttemptAt: event.lastAttemptAt,
|
|
475
|
+
nextRetryAt: event.nextRetryAt,
|
|
476
|
+
createdAt: event.createdAt,
|
|
279
477
|
handlerTx: config.handlerTx,
|
|
280
478
|
};
|
|
281
479
|
await hookFn.call(hookContext, event.payload);
|
|
480
|
+
DurableHooksLogger.debug("Hook completed", {
|
|
481
|
+
namespace,
|
|
482
|
+
fields: {
|
|
483
|
+
eventId: event.id,
|
|
484
|
+
hookName: event.hookName,
|
|
485
|
+
ms: Date.now() - startedAt,
|
|
486
|
+
},
|
|
487
|
+
});
|
|
282
488
|
return {
|
|
283
489
|
eventId: event.id,
|
|
284
490
|
status: "completed" as const,
|
|
285
491
|
};
|
|
286
492
|
} catch (error) {
|
|
287
493
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
494
|
+
DurableHooksLogger.error("Hook failed", {
|
|
495
|
+
namespace,
|
|
496
|
+
fields: {
|
|
497
|
+
eventId: event.id.toJSON(),
|
|
498
|
+
hookName: event.hookName,
|
|
499
|
+
error: errorMessage,
|
|
500
|
+
idempotencyKey: event.idempotencyKey,
|
|
501
|
+
},
|
|
502
|
+
});
|
|
288
503
|
return {
|
|
289
504
|
eventId: event.id,
|
|
290
505
|
status: "failed" as const,
|
|
@@ -299,31 +514,61 @@ export async function processHooks<THooks extends HooksMap>(
|
|
|
299
514
|
// Mark events as completed/failed
|
|
300
515
|
await internalFragment.inContext(async function () {
|
|
301
516
|
await this.handlerTx()
|
|
302
|
-
.
|
|
303
|
-
const
|
|
517
|
+
.mutate(({ forSchema }) => {
|
|
518
|
+
const uow = forSchema(internalSchema);
|
|
519
|
+
const now = dbNow();
|
|
520
|
+
|
|
304
521
|
for (const processedEvent of processedEvents) {
|
|
305
522
|
if (processedEvent.status === "rejected") {
|
|
523
|
+
DurableHooksLogger.error("Hook processing promise rejected", {
|
|
524
|
+
namespace,
|
|
525
|
+
fields: {
|
|
526
|
+
error: DurableHooksLogger.toErrorMessage(processedEvent.reason),
|
|
527
|
+
},
|
|
528
|
+
});
|
|
306
529
|
continue;
|
|
307
530
|
}
|
|
308
531
|
|
|
309
532
|
const { eventId, status } = processedEvent.value;
|
|
310
533
|
|
|
311
534
|
if (status === "completed") {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
535
|
+
uow.update("fragno_hooks", eventId, (b) =>
|
|
536
|
+
b.set({ status: "completed", lastAttemptAt: now }).check(),
|
|
537
|
+
);
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const { error, attempts } = processedEvent.value;
|
|
542
|
+
const newAttempts = attempts + 1;
|
|
543
|
+
const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
|
|
544
|
+
|
|
545
|
+
if (shouldRetry) {
|
|
546
|
+
const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
|
|
547
|
+
const nextRetryAt = now.plus({ ms: delayMs });
|
|
548
|
+
uow.update("fragno_hooks", eventId, (b) =>
|
|
549
|
+
b
|
|
550
|
+
.set({
|
|
551
|
+
status: "pending",
|
|
552
|
+
attempts: newAttempts,
|
|
553
|
+
lastAttemptAt: now,
|
|
554
|
+
nextRetryAt,
|
|
555
|
+
error,
|
|
556
|
+
})
|
|
557
|
+
.check(),
|
|
558
|
+
);
|
|
559
|
+
} else {
|
|
560
|
+
uow.update("fragno_hooks", eventId, (b) =>
|
|
561
|
+
b
|
|
562
|
+
.set({
|
|
563
|
+
status: "failed",
|
|
564
|
+
attempts: newAttempts,
|
|
565
|
+
lastAttemptAt: now,
|
|
566
|
+
error,
|
|
567
|
+
})
|
|
568
|
+
.check(),
|
|
323
569
|
);
|
|
324
570
|
}
|
|
325
571
|
}
|
|
326
|
-
return txResults;
|
|
327
572
|
})
|
|
328
573
|
.execute();
|
|
329
574
|
});
|
|
@@ -333,15 +578,37 @@ export async function processHooks<THooks extends HooksMap>(
|
|
|
333
578
|
0,
|
|
334
579
|
);
|
|
335
580
|
|
|
581
|
+
const failed = processedEvents
|
|
582
|
+
.filter((result) => result.status === "fulfilled" && result.value.status === "failed")
|
|
583
|
+
.map((result) => {
|
|
584
|
+
if (result.status !== "fulfilled") {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
eventId: result.value.eventId,
|
|
589
|
+
error: result.value.error ?? null,
|
|
590
|
+
};
|
|
591
|
+
})
|
|
592
|
+
.filter(Boolean);
|
|
593
|
+
|
|
594
|
+
DurableHooksLogger.debug("Durable hooks processed", {
|
|
595
|
+
namespace,
|
|
596
|
+
fields: {
|
|
597
|
+
processed: processedCount,
|
|
598
|
+
failed: failed.length,
|
|
599
|
+
failures: failed.length > 0 ? failed : null,
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
|
|
336
603
|
return processedCount;
|
|
337
604
|
}
|
|
338
605
|
|
|
339
|
-
export function
|
|
606
|
+
export function createDurableHooksRunner(config: HookProcessorConfig): DurableHooksRunner {
|
|
340
607
|
let processing = false;
|
|
341
608
|
let queued = false;
|
|
342
609
|
let currentPromise: Promise<number> | null = null;
|
|
343
610
|
|
|
344
|
-
const
|
|
611
|
+
const processDue = async () => {
|
|
345
612
|
if (processing) {
|
|
346
613
|
queued = true;
|
|
347
614
|
return currentPromise ?? Promise.resolve(0);
|
|
@@ -349,16 +616,17 @@ export function createHookScheduler(config: HookProcessorConfig): HookScheduler
|
|
|
349
616
|
|
|
350
617
|
processing = true;
|
|
351
618
|
currentPromise = (async () => {
|
|
352
|
-
let
|
|
619
|
+
let totalProcessed = 0;
|
|
353
620
|
try {
|
|
354
621
|
do {
|
|
355
622
|
queued = false;
|
|
356
|
-
|
|
623
|
+
totalProcessed += await processHooks(config);
|
|
357
624
|
} while (queued);
|
|
358
|
-
return
|
|
625
|
+
return totalProcessed;
|
|
359
626
|
} finally {
|
|
360
627
|
processing = false;
|
|
361
628
|
queued = false;
|
|
629
|
+
currentPromise = null;
|
|
362
630
|
}
|
|
363
631
|
})();
|
|
364
632
|
|
|
@@ -367,12 +635,23 @@ export function createHookScheduler(config: HookProcessorConfig): HookScheduler
|
|
|
367
635
|
|
|
368
636
|
const drain = async () => {
|
|
369
637
|
while (true) {
|
|
370
|
-
const processed = await
|
|
638
|
+
const processed = await processDue();
|
|
371
639
|
if (processed === 0) {
|
|
372
640
|
return;
|
|
373
641
|
}
|
|
374
642
|
}
|
|
375
643
|
};
|
|
376
644
|
|
|
377
|
-
return {
|
|
645
|
+
return { processDue, drain };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* @deprecated Use createDurableHooksRunner.
|
|
650
|
+
*/
|
|
651
|
+
export function createHookScheduler(config: HookProcessorConfig): HookScheduler {
|
|
652
|
+
const runner = createDurableHooksRunner(config);
|
|
653
|
+
return {
|
|
654
|
+
schedule: () => runner.processDue(),
|
|
655
|
+
drain: () => runner.drain(),
|
|
656
|
+
};
|
|
378
657
|
}
|
package/src/id.test.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { createId, init } from "./id";
|
|
4
|
+
|
|
5
|
+
const idPattern = /^[a-z][0-9a-z]*$/;
|
|
6
|
+
|
|
7
|
+
const createCounter = (start = 0) => {
|
|
8
|
+
let value = start;
|
|
9
|
+
return () => value++;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe("db id", () => {
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.useRealTimers();
|
|
15
|
+
vi.restoreAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("re-exports the shared createId implementation", () => {
|
|
19
|
+
const id = createId();
|
|
20
|
+
|
|
21
|
+
expect(id).toHaveLength(24);
|
|
22
|
+
expect(id).toMatch(idPattern);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("re-exports init for deterministic generators", () => {
|
|
26
|
+
vi.useFakeTimers();
|
|
27
|
+
vi.setSystemTime(new Date("2020-01-01T00:00:00.000Z"));
|
|
28
|
+
|
|
29
|
+
const first = init({ random: () => 0, counter: createCounter(0), fingerprint: "fingerprint" });
|
|
30
|
+
const second = init({ random: () => 0, counter: createCounter(0), fingerprint: "fingerprint" });
|
|
31
|
+
|
|
32
|
+
expect(first()).toBe(second());
|
|
33
|
+
});
|
|
34
|
+
});
|
package/src/id.ts
CHANGED