@fragno-dev/db 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +202 -140
- package/CHANGELOG.md +35 -0
- package/README.md +30 -9
- package/dist/adapters/adapters.d.ts +23 -21
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/driver-config.d.ts +16 -1
- package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
- package/dist/adapters/generic-sql/driver-config.js +23 -1
- package/dist/adapters/generic-sql/driver-config.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +27 -9
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +55 -16
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +129 -3
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +24 -5
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +6 -5
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +21 -10
- package/dist/adapters/generic-sql/migration/dialect/sqlite.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 +8 -8
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +74 -51
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +6 -5
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +25 -17
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js +5 -3
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +15 -12
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +39 -29
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
- package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
- package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
- package/dist/adapters/generic-sql/uow-decoder.js +7 -3
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +28 -8
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +131 -0
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
- package/dist/adapters/in-memory/errors.d.ts +13 -0
- package/dist/adapters/in-memory/errors.d.ts.map +1 -0
- package/dist/adapters/in-memory/errors.js +23 -0
- package/dist/adapters/in-memory/errors.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.js +176 -0
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-uow.js +648 -0
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
- package/dist/adapters/in-memory/index.d.ts +4 -0
- package/dist/adapters/in-memory/index.js +4 -0
- package/dist/adapters/in-memory/options.d.ts +28 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -0
- package/dist/adapters/in-memory/options.js +61 -0
- package/dist/adapters/in-memory/options.js.map +1 -0
- package/dist/adapters/in-memory/reference-resolution.js +26 -0
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
- package/dist/adapters/in-memory/sorted-array-index.js +129 -0
- package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
- package/dist/adapters/in-memory/store.js +71 -0
- package/dist/adapters/in-memory/store.js.map +1 -0
- package/dist/adapters/in-memory/value-comparison.js +28 -0
- package/dist/adapters/in-memory/value-comparison.js.map +1 -0
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js +11 -11
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/adapters/sql/index.d.ts +5 -0
- package/dist/adapters/sql/index.js +4 -0
- package/dist/db-fragment-definition-builder.d.ts +18 -7
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +116 -54
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.d.ts +26 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.js +63 -0
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +17 -0
- package/dist/dispatchers/node/index.d.ts.map +1 -0
- package/dist/dispatchers/node/index.js +59 -0
- package/dist/dispatchers/node/index.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +79 -2
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +150 -32
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +29 -0
- package/dist/fragments/internal-fragment.routes.js.map +1 -0
- package/dist/fragments/internal-fragment.schema.d.ts +9 -0
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.schema.js +22 -0
- package/dist/fragments/internal-fragment.schema.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +14 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-processor.js +32 -0
- package/dist/hooks/durable-hooks-processor.js.map +1 -0
- package/dist/hooks/hooks.d.ts +42 -1
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +72 -6
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/migration-engine/auto-from-schema.js +14 -11
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +16 -10
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +72 -33
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +15 -8
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +14 -8
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts +19 -0
- package/dist/naming/sql-naming.d.ts.map +1 -0
- package/dist/naming/sql-naming.js +116 -0
- package/dist/naming/sql-naming.js.map +1 -0
- package/dist/node_modules/.pnpm/{rou3@0.7.10 → rou3@0.7.12}/node_modules/rou3/dist/index.js +8 -5
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/outbox/outbox-builder.js +156 -0
- package/dist/outbox/outbox-builder.js.map +1 -0
- package/dist/outbox/outbox.d.ts +52 -0
- package/dist/outbox/outbox.d.ts.map +1 -0
- package/dist/outbox/outbox.js +37 -0
- package/dist/outbox/outbox.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +3 -2
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +164 -20
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +67 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -1
- package/dist/packages/fragno/dist/api/route.js +14 -1
- package/dist/packages/fragno/dist/api/route.js.map +1 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +12 -0
- package/dist/packages/fragno/dist/internal/trace-context.js.map +1 -0
- package/dist/query/column-defaults.js +20 -4
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/cursor.d.ts +3 -1
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +45 -14
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +8 -0
- package/dist/query/db-now.d.ts.map +1 -0
- package/dist/query/db-now.js +7 -0
- package/dist/query/db-now.js.map +1 -0
- package/dist/query/serialize/create-sql-serializer.js +3 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +55 -11
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/serialize/sql-serializer.js +2 -2
- package/dist/query/serialize/sql-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +6 -1
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- 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 +11 -6
- 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 +50 -14
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +86 -5
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +9 -6
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js +29 -9
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +38 -14
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +81 -42
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/generate-id.js +2 -2
- package/dist/schema/generate-id.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +3 -2
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.d.ts.map +1 -0
- package/dist/schema/validator.js +123 -0
- package/dist/schema/validator.js.map +1 -0
- package/dist/schema-output/drizzle.d.ts +30 -0
- package/dist/schema-output/drizzle.d.ts.map +1 -0
- package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +82 -56
- package/dist/schema-output/drizzle.js.map +1 -0
- package/dist/schema-output/prisma.d.ts +17 -0
- package/dist/schema-output/prisma.d.ts.map +1 -0
- package/dist/schema-output/prisma.js +296 -0
- package/dist/schema-output/prisma.js.map +1 -0
- package/dist/util/default-database-adapter.js +61 -0
- package/dist/util/default-database-adapter.js.map +1 -0
- package/dist/with-database.d.ts +1 -1
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +12 -3
- package/dist/with-database.js.map +1 -1
- package/package.json +43 -28
- package/src/adapters/adapters.ts +30 -24
- package/src/adapters/drizzle/migrate-drizzle.test.ts +54 -33
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +599 -0
- package/src/adapters/drizzle/test-utils.ts +12 -8
- package/src/adapters/generic-sql/driver-config.ts +38 -0
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -5
- package/src/adapters/generic-sql/generic-sql-adapter.ts +110 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +54 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +231 -3
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +118 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +26 -8
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +46 -8
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +25 -7
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +8 -4
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +47 -8
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +27 -12
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +128 -39
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +15 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +142 -65
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +9 -6
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +271 -0
- package/src/adapters/generic-sql/query/cursor-utils.ts +41 -6
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +27 -27
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +38 -24
- package/src/adapters/generic-sql/query/select-builder.test.ts +15 -11
- package/src/adapters/generic-sql/query/select-builder.ts +6 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +52 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +50 -15
- package/src/adapters/generic-sql/query/where-builder.test.ts +91 -17
- package/src/adapters/generic-sql/query/where-builder.ts +90 -38
- package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +6 -6
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +806 -0
- package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +11 -11
- package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +10 -10
- package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +7 -7
- package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +6 -6
- package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
- package/src/adapters/generic-sql/uow-decoder.test.ts +1 -1
- package/src/adapters/generic-sql/uow-decoder.ts +21 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +33 -2
- package/src/adapters/generic-sql/uow-encoder.ts +50 -11
- package/src/adapters/in-memory/condition-evaluator.test.ts +193 -0
- package/src/adapters/in-memory/condition-evaluator.ts +275 -0
- package/src/adapters/in-memory/errors.ts +20 -0
- package/src/adapters/in-memory/in-memory-adapter.ts +277 -0
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +296 -0
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +100 -0
- package/src/adapters/in-memory/in-memory-uow.ts +1348 -0
- package/src/adapters/in-memory/index.ts +3 -0
- package/src/adapters/in-memory/options.test.ts +41 -0
- package/src/adapters/in-memory/options.ts +87 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +50 -0
- package/src/adapters/in-memory/reference-resolution.ts +67 -0
- package/src/adapters/in-memory/sorted-array-index.test.ts +123 -0
- package/src/adapters/in-memory/sorted-array-index.ts +228 -0
- package/src/adapters/in-memory/store.test.ts +68 -0
- package/src/adapters/in-memory/store.ts +145 -0
- package/src/adapters/in-memory/value-comparison.ts +53 -0
- package/src/adapters/in-memory/value-normalization.test.ts +57 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1163 -0
- package/src/adapters/shared/from-unit-of-work-compiler.ts +3 -1
- package/src/adapters/shared/uow-operation-compiler.ts +26 -16
- package/src/adapters/sql/index.ts +12 -0
- package/src/db-fragment-definition-builder.test.ts +30 -12
- package/src/db-fragment-definition-builder.ts +142 -73
- package/src/db-fragment-instantiator.test.ts +105 -13
- package/src/db-fragment-integration.test.ts +9 -7
- package/src/dispatchers/cloudflare-do/index.test.ts +73 -0
- package/src/dispatchers/cloudflare-do/index.ts +104 -0
- package/src/dispatchers/node/index.test.ts +91 -0
- package/src/dispatchers/node/index.ts +87 -0
- package/src/fragments/internal-fragment.routes.ts +42 -0
- package/src/fragments/internal-fragment.schema.ts +51 -0
- package/src/fragments/internal-fragment.test.ts +458 -8
- package/src/fragments/internal-fragment.ts +322 -63
- package/src/hooks/durable-hooks-processor.test.ts +117 -0
- package/src/hooks/durable-hooks-processor.ts +67 -0
- package/src/hooks/hooks.test.ts +165 -5
- package/src/hooks/hooks.ts +197 -9
- package/src/migration-engine/auto-from-schema.test.ts +14 -14
- package/src/migration-engine/auto-from-schema.ts +5 -2
- package/src/migration-engine/create.test.ts +2 -2
- package/src/migration-engine/generation-engine.test.ts +229 -104
- package/src/migration-engine/generation-engine.ts +94 -64
- package/src/migration-engine/shared.ts +1 -0
- package/src/mod.ts +64 -26
- package/src/naming/sql-naming.ts +180 -0
- package/src/outbox/outbox-builder.ts +241 -0
- package/src/outbox/outbox.test.ts +253 -0
- package/src/outbox/outbox.ts +137 -0
- package/src/query/column-defaults.ts +41 -3
- package/src/query/condition-builder.test.ts +3 -3
- package/src/query/cursor.test.ts +116 -18
- package/src/query/cursor.ts +75 -26
- package/src/query/db-now.ts +6 -0
- package/src/query/query-type.test.ts +2 -2
- package/src/query/serialize/create-sql-serializer.ts +7 -2
- package/src/query/serialize/dialect/mysql-serializer.ts +12 -4
- package/src/query/serialize/dialect/postgres-serializer.ts +34 -4
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +51 -1
- package/src/query/serialize/dialect/sqlite-serializer.ts +92 -9
- package/src/query/serialize/sql-serializer.ts +4 -4
- package/src/query/simple-query-interface.ts +5 -0
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +25 -1
- package/src/query/unit-of-work/execute-unit-of-work.ts +25 -8
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +12 -12
- package/src/query/unit-of-work/unit-of-work-types.test.ts +1 -1
- package/src/query/unit-of-work/unit-of-work.test.ts +168 -37
- package/src/query/unit-of-work/unit-of-work.ts +203 -18
- package/src/query/value-decoding.test.ts +13 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +85 -2
- package/src/query/value-encoding.ts +56 -6
- package/src/schema/create.test.ts +129 -42
- package/src/schema/create.ts +185 -47
- package/src/schema/generate-id.test.ts +2 -2
- package/src/schema/generate-id.ts +2 -2
- package/src/schema/serialize.test.ts +14 -2
- package/src/schema/type-conversion/create-sql-type-mapper.ts +7 -2
- package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
- package/src/schema/type-conversion/type-mapping.test.ts +25 -1
- package/src/schema/validator.test.ts +197 -0
- package/src/schema/validator.ts +231 -0
- package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +179 -129
- package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +143 -93
- package/src/schema-output/prisma.test.ts +536 -0
- package/src/schema-output/prisma.ts +573 -0
- package/src/util/default-database-adapter.ts +106 -0
- package/src/with-database.ts +22 -3
- package/tsdown.config.ts +6 -4
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
- package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
- package/dist/adapters/drizzle/generate.d.ts +0 -30
- package/dist/adapters/drizzle/generate.d.ts.map +0 -1
- package/dist/adapters/drizzle/generate.js.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.js +0 -17
- package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
- package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
- package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
- package/dist/adapters/shared/table-name-mapper.js +0 -43
- package/dist/adapters/shared/table-name-mapper.js.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/schema-generator/schema-generator.d.ts +0 -15
- package/dist/schema-generator/schema-generator.d.ts.map +0 -1
- package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
- package/src/adapters/kysely/kysely-adapter.ts +0 -27
- package/src/adapters/shared/table-name-mapper.ts +0 -50
- package/src/schema-generator/schema-generator.ts +0 -12
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import { FragmentDefinitionBuilder } from "../packages/fragno/dist/api/fragment-definition-builder.js";
|
|
2
|
+
import { FragnoId } from "../schema/create.js";
|
|
3
|
+
import { SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME, internalSchema } from "./internal-fragment.schema.js";
|
|
4
|
+
import { dbNow } from "../query/db-now.js";
|
|
2
5
|
import { DatabaseFragmentDefinitionBuilder } from "../db-fragment-definition-builder.js";
|
|
3
|
-
import { column, idColumn, schema } from "../schema/create.js";
|
|
4
6
|
|
|
5
7
|
//#region src/fragments/internal-fragment.ts
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return t.addColumn("id", idColumn()).addColumn("key", column("string")).addColumn("value", column("string")).createIndex("unique_key", ["key"], { unique: true });
|
|
11
|
-
}).addTable("fragno_hooks", (t) => {
|
|
12
|
-
return t.addColumn("id", idColumn()).addColumn("namespace", column("string")).addColumn("hookName", column("string")).addColumn("payload", column("json")).addColumn("status", column("string")).addColumn("attempts", column("integer").defaultTo(0)).addColumn("maxAttempts", column("integer").defaultTo(5)).addColumn("lastAttemptAt", column("timestamp").nullable()).addColumn("nextRetryAt", column("timestamp").nullable()).addColumn("error", column("string").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).addColumn("nonce", column("string")).createIndex("idx_namespace_status_retry", [
|
|
13
|
-
"namespace",
|
|
14
|
-
"status",
|
|
15
|
-
"nextRetryAt"
|
|
16
|
-
]).createIndex("idx_nonce", ["nonce"]);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDefinitionBuilder("$fragno-internal-fragment"), internalSchema, "").providesService("settingsService", ({ defineService }) => {
|
|
8
|
+
const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDefinitionBuilder("$fragno-internal-fragment"), internalSchema).providesBaseService(({ deps }) => ({ getDbNow: async () => {
|
|
9
|
+
if (deps.db.now) return deps.db.now();
|
|
10
|
+
return /* @__PURE__ */ new Date();
|
|
11
|
+
} })).providesService("settingsService", ({ defineService }) => {
|
|
20
12
|
return defineService({
|
|
21
13
|
get(namespace, key) {
|
|
22
14
|
const fullKey = `${namespace}.${key}`;
|
|
@@ -39,12 +31,9 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
39
31
|
}).providesService("hookService", ({ defineService }) => {
|
|
40
32
|
return defineService({
|
|
41
33
|
getPendingHookEvents(namespace) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return events.
|
|
45
|
-
if (!event.nextRetryAt) return true;
|
|
46
|
-
return event.nextRetryAt <= now;
|
|
47
|
-
}).map((event) => ({
|
|
34
|
+
const now = dbNow();
|
|
35
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"), eb.or(eb.isNull("nextRetryAt"), eb("nextRetryAt", "<=", now)))))).transformRetrieve(([events]) => {
|
|
36
|
+
return events.map((event) => ({
|
|
48
37
|
id: event.id,
|
|
49
38
|
hookName: event.hookName,
|
|
50
39
|
payload: event.payload,
|
|
@@ -54,30 +43,130 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
54
43
|
}));
|
|
55
44
|
}).build();
|
|
56
45
|
},
|
|
46
|
+
claimPendingHookEvents(namespace) {
|
|
47
|
+
const now = dbNow();
|
|
48
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"), eb.or(eb.isNull("nextRetryAt"), eb("nextRetryAt", "<=", now)))))).transformRetrieve(([events]) => {
|
|
49
|
+
return events.map((event) => ({
|
|
50
|
+
id: event.id,
|
|
51
|
+
hookName: event.hookName,
|
|
52
|
+
payload: event.payload,
|
|
53
|
+
attempts: event.attempts,
|
|
54
|
+
maxAttempts: event.maxAttempts,
|
|
55
|
+
idempotencyKey: event.nonce
|
|
56
|
+
}));
|
|
57
|
+
}).mutate(({ uow, retrieveResult }) => {
|
|
58
|
+
if (retrieveResult.length === 0) return;
|
|
59
|
+
for (const event of retrieveResult) uow.update("fragno_hooks", event.id, (b) => b.set({
|
|
60
|
+
status: "processing",
|
|
61
|
+
lastAttemptAt: now
|
|
62
|
+
}).check());
|
|
63
|
+
}).transform(({ retrieveResult }) => retrieveResult.map((event) => ({
|
|
64
|
+
...event,
|
|
65
|
+
id: new FragnoId({
|
|
66
|
+
externalId: event.id.externalId,
|
|
67
|
+
internalId: event.id.internalId,
|
|
68
|
+
version: event.id.version + 1
|
|
69
|
+
})
|
|
70
|
+
}))).build();
|
|
71
|
+
},
|
|
72
|
+
requeueStuckProcessingHooks(namespace, staleBefore) {
|
|
73
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"))))).transformRetrieve(([events]) => {
|
|
74
|
+
return events.filter((event) => {
|
|
75
|
+
if (!event.lastAttemptAt) return true;
|
|
76
|
+
return event.lastAttemptAt <= staleBefore;
|
|
77
|
+
}).map((event) => ({
|
|
78
|
+
id: event.id,
|
|
79
|
+
hookName: event.hookName,
|
|
80
|
+
attempts: event.attempts,
|
|
81
|
+
maxAttempts: event.maxAttempts,
|
|
82
|
+
lastAttemptAt: event.lastAttemptAt,
|
|
83
|
+
nextRetryAt: event.nextRetryAt
|
|
84
|
+
}));
|
|
85
|
+
}).mutate(({ uow, retrieveResult }) => {
|
|
86
|
+
for (const event of retrieveResult) uow.update("fragno_hooks", event.id, (b) => b.set({
|
|
87
|
+
status: "pending",
|
|
88
|
+
nextRetryAt: null
|
|
89
|
+
}).check());
|
|
90
|
+
}).transform(({ retrieveResult }) => retrieveResult).build();
|
|
91
|
+
},
|
|
92
|
+
getNextProcessingStaleAt(namespace, timeoutMinutes, now) {
|
|
93
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"))))).transformRetrieve(([events]) => {
|
|
94
|
+
if (events.length === 0) return null;
|
|
95
|
+
const baseNow = now ?? /* @__PURE__ */ new Date();
|
|
96
|
+
const nowMs = baseNow.getTime();
|
|
97
|
+
const timeoutMs = timeoutMinutes * 6e4;
|
|
98
|
+
let earliestStaleAt = null;
|
|
99
|
+
for (const event of events) {
|
|
100
|
+
if (!event.lastAttemptAt) return baseNow;
|
|
101
|
+
const staleAtMs = event.lastAttemptAt.getTime() + timeoutMs;
|
|
102
|
+
if (staleAtMs <= nowMs) return baseNow;
|
|
103
|
+
const staleAt = new Date(staleAtMs);
|
|
104
|
+
if (!earliestStaleAt || staleAt < earliestStaleAt) earliestStaleAt = staleAt;
|
|
105
|
+
}
|
|
106
|
+
return earliestStaleAt;
|
|
107
|
+
}).build();
|
|
108
|
+
},
|
|
109
|
+
getNextHookWakeAt(namespace, timeoutMinutes, now) {
|
|
110
|
+
const baseNow = now ?? /* @__PURE__ */ new Date();
|
|
111
|
+
const includeProcessing = typeof timeoutMinutes === "number" && timeoutMinutes > 0;
|
|
112
|
+
const timeoutMs = includeProcessing ? timeoutMinutes * 6e4 : 0;
|
|
113
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => {
|
|
114
|
+
if (includeProcessing) return eb.and(eb("namespace", "=", namespace), eb.or(eb("status", "=", "pending"), eb("status", "=", "processing")));
|
|
115
|
+
return eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"));
|
|
116
|
+
}).select([
|
|
117
|
+
"status",
|
|
118
|
+
"nextRetryAt",
|
|
119
|
+
"lastAttemptAt"
|
|
120
|
+
]))).transformRetrieve(([events]) => {
|
|
121
|
+
if (events.length === 0) return null;
|
|
122
|
+
const nowMs = baseNow.getTime();
|
|
123
|
+
let earliestPendingAt = null;
|
|
124
|
+
let earliestStaleAt = null;
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
if (event.status === "pending") {
|
|
127
|
+
const nextRetryAt = event.nextRetryAt;
|
|
128
|
+
if (!nextRetryAt || nextRetryAt.getTime() <= nowMs) return baseNow;
|
|
129
|
+
if (!earliestPendingAt || nextRetryAt < earliestPendingAt) earliestPendingAt = nextRetryAt;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!includeProcessing || event.status !== "processing") continue;
|
|
133
|
+
const lastAttemptAt = event.lastAttemptAt;
|
|
134
|
+
if (!lastAttemptAt) return baseNow;
|
|
135
|
+
const staleAtMs = lastAttemptAt.getTime() + timeoutMs;
|
|
136
|
+
if (staleAtMs <= nowMs) return baseNow;
|
|
137
|
+
const staleAt = new Date(staleAtMs);
|
|
138
|
+
if (!earliestStaleAt || staleAt < earliestStaleAt) earliestStaleAt = staleAt;
|
|
139
|
+
}
|
|
140
|
+
if (!earliestPendingAt) return earliestStaleAt ?? null;
|
|
141
|
+
if (!earliestStaleAt) return earliestPendingAt;
|
|
142
|
+
return earliestPendingAt <= earliestStaleAt ? earliestPendingAt : earliestStaleAt;
|
|
143
|
+
}).build();
|
|
144
|
+
},
|
|
57
145
|
markHookCompleted(eventId) {
|
|
58
146
|
return this.serviceTx(internalSchema).mutate(({ uow }) => uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
59
147
|
status: "completed",
|
|
60
|
-
lastAttemptAt:
|
|
148
|
+
lastAttemptAt: dbNow()
|
|
61
149
|
}).check())).build();
|
|
62
150
|
},
|
|
63
|
-
markHookFailed(eventId, error, attempts, retryPolicy) {
|
|
151
|
+
markHookFailed(eventId, error, attempts, retryPolicy, now) {
|
|
64
152
|
const newAttempts = attempts + 1;
|
|
65
153
|
const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
|
|
66
154
|
return this.serviceTx(internalSchema).mutate(({ uow }) => {
|
|
67
155
|
if (shouldRetry) {
|
|
68
156
|
const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
|
|
69
|
-
const
|
|
157
|
+
const baseNow = now ?? /* @__PURE__ */ new Date();
|
|
158
|
+
const nextRetryAt = new Date(baseNow.getTime() + delayMs);
|
|
70
159
|
uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
71
160
|
status: "pending",
|
|
72
161
|
attempts: newAttempts,
|
|
73
|
-
lastAttemptAt:
|
|
162
|
+
lastAttemptAt: dbNow(),
|
|
74
163
|
nextRetryAt,
|
|
75
164
|
error
|
|
76
165
|
}).check());
|
|
77
166
|
} else uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
78
167
|
status: "failed",
|
|
79
168
|
attempts: newAttempts,
|
|
80
|
-
lastAttemptAt:
|
|
169
|
+
lastAttemptAt: dbNow(),
|
|
81
170
|
error
|
|
82
171
|
}).check());
|
|
83
172
|
}).build();
|
|
@@ -85,7 +174,7 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
85
174
|
markHookProcessing(eventId) {
|
|
86
175
|
return this.serviceTx(internalSchema).mutate(({ uow }) => uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
87
176
|
status: "processing",
|
|
88
|
-
lastAttemptAt:
|
|
177
|
+
lastAttemptAt: dbNow()
|
|
89
178
|
}).check())).build();
|
|
90
179
|
},
|
|
91
180
|
getHookById(eventId) {
|
|
@@ -95,18 +184,47 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
95
184
|
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb("namespace", "=", namespace)))).transformRetrieve(([events]) => events).build();
|
|
96
185
|
}
|
|
97
186
|
});
|
|
187
|
+
}).providesService("outboxService", ({ defineService }) => {
|
|
188
|
+
return defineService({ list({ afterVersionstamp, limit } = {}) {
|
|
189
|
+
const afterValue = afterVersionstamp?.toLowerCase();
|
|
190
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_db_outbox", (b) => {
|
|
191
|
+
let builder = afterValue ? b.whereIndex("idx_outbox_versionstamp", (eb) => eb("versionstamp", ">", afterValue)) : b.whereIndex("idx_outbox_versionstamp");
|
|
192
|
+
builder = builder.orderByIndex("idx_outbox_versionstamp", "asc");
|
|
193
|
+
if (limit !== void 0) builder = builder.pageSize(limit);
|
|
194
|
+
return builder;
|
|
195
|
+
})).transformRetrieve(([entries]) => entries.map((entry) => ({
|
|
196
|
+
id: entry.id,
|
|
197
|
+
versionstamp: entry.versionstamp,
|
|
198
|
+
uowId: entry.uowId,
|
|
199
|
+
payload: entry.payload,
|
|
200
|
+
refMap: entry.refMap ?? void 0,
|
|
201
|
+
createdAt: entry.createdAt
|
|
202
|
+
}))).build();
|
|
203
|
+
} });
|
|
98
204
|
}).build();
|
|
99
205
|
async function getSchemaVersionFromDatabase(fragment, namespace) {
|
|
100
206
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
207
|
+
const readSchemaVersion = async (targetNamespace) => {
|
|
208
|
+
const setting = await fragment.inContext(async function() {
|
|
209
|
+
return await this.handlerTx().withServiceCalls(() => [fragment.services.settingsService.get(targetNamespace, "schema_version")]).transform(({ serviceResult: [result] }) => result).execute();
|
|
210
|
+
});
|
|
211
|
+
if (!setting) return;
|
|
212
|
+
const parsed = parseInt(setting.value, 10);
|
|
213
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
214
|
+
};
|
|
215
|
+
const primary = await readSchemaVersion(namespace);
|
|
216
|
+
if (primary !== void 0) return primary;
|
|
217
|
+
const legacyNamespace = namespace === "" ? internalSchema.name : namespace === internalSchema.name ? "" : null;
|
|
218
|
+
if (legacyNamespace !== null) {
|
|
219
|
+
const legacy = await readSchemaVersion(legacyNamespace);
|
|
220
|
+
if (legacy !== void 0) return legacy;
|
|
221
|
+
}
|
|
222
|
+
return 0;
|
|
105
223
|
} catch {
|
|
106
224
|
return 0;
|
|
107
225
|
}
|
|
108
226
|
}
|
|
109
227
|
|
|
110
228
|
//#endregion
|
|
111
|
-
export {
|
|
229
|
+
export { getSchemaVersionFromDatabase, internalFragmentDef };
|
|
112
230
|
//# sourceMappingURL=internal-fragment.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-fragment.js","names":[],"sources":["../../src/fragments/internal-fragment.ts"],"sourcesContent":["import { FragmentDefinitionBuilder } from \"@fragno-dev/core\";\nimport type { InstantiatedFragmentFromDefinition } from \"@fragno-dev/core\";\nimport {\n DatabaseFragmentDefinitionBuilder,\n type DatabaseHandlerContext,\n type DatabaseRequestStorage,\n type DatabaseServiceContext,\n type FragnoPublicConfigWithDatabase,\n type ImplicitDatabaseDependencies,\n} from \"../db-fragment-definition-builder\";\nimport type { FragnoId } from \"../schema/create\";\nimport { schema, idColumn, column } from \"../schema/create\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\n\n// Constants for Fragno's internal settings table\nexport const SETTINGS_TABLE_NAME = \"fragno_db_settings\" as const;\n// FIXME: In some places we simply use empty string \"\" as namespace, which is not correct.\nexport const SETTINGS_NAMESPACE = \"fragno-db-settings\" as const;\n\nexport const internalSchema = schema((s) => {\n return s\n .addTable(SETTINGS_TABLE_NAME, (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"key\", column(\"string\"))\n .addColumn(\"value\", column(\"string\"))\n .createIndex(\"unique_key\", [\"key\"], { unique: true });\n })\n .addTable(\"fragno_hooks\", (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"namespace\", column(\"string\"))\n .addColumn(\"hookName\", column(\"string\"))\n .addColumn(\"payload\", column(\"json\"))\n .addColumn(\"status\", column(\"string\")) // \"pending\" | \"processing\" | \"completed\" | \"failed\"\n .addColumn(\"attempts\", column(\"integer\").defaultTo(0))\n .addColumn(\"maxAttempts\", column(\"integer\").defaultTo(5))\n .addColumn(\"lastAttemptAt\", column(\"timestamp\").nullable())\n .addColumn(\"nextRetryAt\", column(\"timestamp\").nullable())\n .addColumn(\"error\", column(\"string\").nullable())\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .addColumn(\"nonce\", column(\"string\"))\n .createIndex(\"idx_namespace_status_retry\", [\"namespace\", \"status\", \"nextRetryAt\"])\n .createIndex(\"idx_nonce\", [\"nonce\"]);\n });\n});\n\n// This uses DatabaseFragmentDefinitionBuilder directly\n// to avoid circular dependency (it doesn't need to link to itself)\nexport const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(\n new FragmentDefinitionBuilder<\n {},\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies<typeof internalSchema>,\n {},\n {},\n {},\n {},\n DatabaseServiceContext<{}>,\n DatabaseHandlerContext,\n DatabaseRequestStorage\n >(\"$fragno-internal-fragment\"),\n internalSchema,\n \"\", // intentionally blank namespace so there is no prefix\n)\n .providesService(\"settingsService\", ({ defineService }) => {\n return defineService({\n /**\n * Get a setting by namespace and key.\n */\n get(namespace: string, key: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(\n ([result]): { id: FragnoId; key: string; value: string } | undefined =>\n result ?? undefined,\n )\n .build();\n },\n\n /**\n * Set a setting value by namespace and key.\n */\n set(namespace: string, key: string, value: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(([result]) => result)\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult) {\n uow.update(SETTINGS_TABLE_NAME, retrieveResult.id, (b) => b.set({ value }).check());\n } else {\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\n });\n }\n })\n .build();\n },\n\n /**\n * Delete a setting by ID.\n */\n delete(id: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id))\n .build();\n },\n });\n })\n .providesService(\"hookService\", ({ defineService }) => {\n return defineService({\n /**\n * Get pending hook events for processing.\n * Returns all pending events for the given namespace that are ready to be processed.\n */\n getPendingHookEvents(namespace: string) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"pending\")),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n const now = new Date();\n // FIXME(Wilco): this should be handled by the database query, but there seems to be an issue.\n const ready = events.filter((event) => {\n if (!event.nextRetryAt) {\n return true; // Newly created events (nextRetryAt = null) are ready\n }\n return event.nextRetryAt <= now; // Only include if retry time has passed\n });\n\n return ready.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload as unknown,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n }));\n })\n .build();\n },\n\n /**\n * Mark a hook event as completed.\n */\n markHookCompleted(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"completed\", lastAttemptAt: new Date() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Mark a hook event as failed and schedule next retry.\n */\n markHookFailed(eventId: FragnoId, error: string, attempts: number, retryPolicy: RetryPolicy) {\n const newAttempts = attempts + 1;\n const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);\n\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => {\n if (shouldRetry) {\n const delayMs = retryPolicy.getDelayMs(newAttempts - 1);\n const nextRetryAt = new Date(Date.now() + delayMs);\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"pending\",\n attempts: newAttempts,\n lastAttemptAt: new Date(),\n nextRetryAt,\n error,\n })\n .check(),\n );\n } else {\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"failed\",\n attempts: newAttempts,\n lastAttemptAt: new Date(),\n error,\n })\n .check(),\n );\n }\n })\n .build();\n },\n\n /**\n * Mark a hook event as processing (to prevent concurrent execution).\n */\n markHookProcessing(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: new Date() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Get a hook event by ID (for testing/verification purposes).\n */\n getHookById(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(\"fragno_hooks\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", eventId)),\n ),\n )\n .transformRetrieve(([result]) => result ?? undefined)\n .build();\n },\n\n /**\n * Get all hook events for a namespace (for testing/verification purposes).\n */\n getHooksByNamespace(namespace: string) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) => eb(\"namespace\", \"=\", namespace)),\n ),\n )\n .transformRetrieve(([events]) => events)\n .build();\n },\n });\n })\n .build();\n\n/**\n * Type representing an instantiated internal fragment.\n * This is the fragment that manages Fragno's internal settings table.\n */\nexport type InternalFragmentInstance = InstantiatedFragmentFromDefinition<\n typeof internalFragmentDef\n>;\n\nexport async function getSchemaVersionFromDatabase(\n fragment: InternalFragmentInstance,\n namespace: string,\n): Promise<number> {\n try {\n const setting = await fragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () => [fragment.services.settingsService.get(namespace, \"schema_version\")] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n });\n return setting ? parseInt(setting.value, 10) : 0;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"internal-fragment.js","names":["earliestStaleAt: Date | null","earliestPendingAt: Date | null"],"sources":["../../src/fragments/internal-fragment.ts"],"sourcesContent":["import { FragmentDefinitionBuilder } from \"@fragno-dev/core\";\nimport type { InstantiatedFragmentFromDefinition } from \"@fragno-dev/core\";\nimport {\n DatabaseFragmentDefinitionBuilder,\n type DatabaseHandlerContext,\n type DatabaseRequestStorage,\n type DatabaseServiceContext,\n type FragnoPublicConfigWithDatabase,\n type ImplicitDatabaseDependencies,\n} from \"../db-fragment-definition-builder\";\nimport { FragnoId } from \"../schema/create\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { dbNow } from \"../query/db-now\";\nimport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\nexport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\n// This uses DatabaseFragmentDefinitionBuilder directly\n// to avoid circular dependency (it doesn't need to link to itself)\nexport const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(\n new FragmentDefinitionBuilder<\n {},\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies<typeof internalSchema>,\n {},\n {},\n {},\n {},\n DatabaseServiceContext<{}>,\n DatabaseHandlerContext,\n DatabaseRequestStorage\n >(\"$fragno-internal-fragment\"),\n internalSchema,\n)\n .providesBaseService(({ deps }) => ({\n getDbNow: async () => {\n if (deps.db.now) {\n return deps.db.now();\n }\n return new Date();\n },\n }))\n .providesService(\"settingsService\", ({ defineService }) => {\n return defineService({\n /**\n * Get a setting by namespace and key.\n */\n get(namespace: string, key: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(\n ([result]): { id: FragnoId; key: string; value: string } | undefined =>\n result ?? undefined,\n )\n .build();\n },\n\n /**\n * Set a setting value by namespace and key.\n */\n set(namespace: string, key: string, value: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(([result]) => result)\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult) {\n uow.update(SETTINGS_TABLE_NAME, retrieveResult.id, (b) => b.set({ value }).check());\n } else {\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\n });\n }\n })\n .build();\n },\n\n /**\n * Delete a setting by ID.\n */\n delete(id: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id))\n .build();\n },\n });\n })\n .providesService(\"hookService\", ({ defineService }) => {\n return defineService({\n /**\n * Get pending hook events for processing.\n * Returns all pending events for the given namespace that are ready to be processed.\n */\n getPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload as unknown,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n }));\n })\n .build();\n },\n\n /**\n * Claim pending hook events for processing.\n * Returns ready events and marks them as processing in the same transaction.\n */\n claimPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult.length === 0) {\n return;\n }\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: now }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) =>\n retrieveResult.map((event) => ({\n ...event,\n id: new FragnoId({\n externalId: event.id.externalId,\n internalId: event.id.internalId,\n version: event.id.version + 1,\n }),\n })),\n )\n .build();\n },\n\n /**\n * Re-queue hook events that have been stuck in processing for too long.\n */\n requeueStuckProcessingHooks(namespace: string, staleBefore: Date) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"processing\")),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n const stuck = events.filter((event) => {\n if (!event.lastAttemptAt) {\n return true;\n }\n return event.lastAttemptAt <= staleBefore;\n });\n\n return stuck.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"pending\", nextRetryAt: null }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) => retrieveResult)\n .build();\n },\n\n /**\n * Get the next time a processing hook becomes stale.\n */\n getNextProcessingStaleAt(namespace: string, timeoutMinutes: number, now?: Date) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"processing\")),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n if (events.length === 0) {\n return null;\n }\n\n const baseNow = now ?? new Date();\n const nowMs = baseNow.getTime();\n const timeoutMs = timeoutMinutes * 60_000;\n let earliestStaleAt: Date | null = null;\n\n for (const event of events) {\n if (!event.lastAttemptAt) {\n return baseNow;\n }\n\n const staleAtMs = event.lastAttemptAt.getTime() + timeoutMs;\n if (staleAtMs <= nowMs) {\n return baseNow;\n }\n\n const staleAt = new Date(staleAtMs);\n if (!earliestStaleAt || staleAt < earliestStaleAt) {\n earliestStaleAt = staleAt;\n }\n }\n\n return earliestStaleAt;\n })\n .build();\n },\n\n /**\n * Get the earliest pending hook wake time for a namespace.\n * Optionally considers processing hooks becoming stale when timeoutMinutes is provided.\n */\n getNextHookWakeAt(namespace: string, timeoutMinutes?: number | false, now?: Date) {\n const baseNow = now ?? new Date();\n const includeProcessing = typeof timeoutMinutes === \"number\" && timeoutMinutes > 0;\n const timeoutMs = includeProcessing ? timeoutMinutes * 60_000 : 0;\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_retry\", (eb) => {\n if (includeProcessing) {\n return eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb.or(eb(\"status\", \"=\", \"pending\"), eb(\"status\", \"=\", \"processing\")),\n );\n }\n return eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"pending\"));\n })\n .select([\"status\", \"nextRetryAt\", \"lastAttemptAt\"]),\n ),\n )\n .transformRetrieve(([events]) => {\n if (events.length === 0) {\n return null;\n }\n\n const nowMs = baseNow.getTime();\n let earliestPendingAt: Date | null = null;\n let earliestStaleAt: Date | null = null;\n\n for (const event of events) {\n if (event.status === \"pending\") {\n const nextRetryAt = event.nextRetryAt;\n if (!nextRetryAt || nextRetryAt.getTime() <= nowMs) {\n return baseNow;\n }\n if (!earliestPendingAt || nextRetryAt < earliestPendingAt) {\n earliestPendingAt = nextRetryAt;\n }\n continue;\n }\n\n if (!includeProcessing || event.status !== \"processing\") {\n continue;\n }\n\n const lastAttemptAt = event.lastAttemptAt;\n if (!lastAttemptAt) {\n return baseNow;\n }\n\n const staleAtMs = lastAttemptAt.getTime() + timeoutMs;\n if (staleAtMs <= nowMs) {\n return baseNow;\n }\n\n const staleAt = new Date(staleAtMs);\n if (!earliestStaleAt || staleAt < earliestStaleAt) {\n earliestStaleAt = staleAt;\n }\n }\n\n if (!earliestPendingAt) {\n return earliestStaleAt ?? null;\n }\n if (!earliestStaleAt) {\n return earliestPendingAt;\n }\n return earliestPendingAt <= earliestStaleAt ? earliestPendingAt : earliestStaleAt;\n })\n .build();\n },\n\n /**\n * Mark a hook event as completed.\n */\n markHookCompleted(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"completed\", lastAttemptAt: dbNow() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Mark a hook event as failed and schedule next retry.\n */\n markHookFailed(\n eventId: FragnoId,\n error: string,\n attempts: number,\n retryPolicy: RetryPolicy,\n now?: Date,\n ) {\n const newAttempts = attempts + 1;\n const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);\n\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => {\n if (shouldRetry) {\n const delayMs = retryPolicy.getDelayMs(newAttempts - 1);\n const baseNow = now ?? new Date();\n const nextRetryAt = new Date(baseNow.getTime() + delayMs);\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"pending\",\n attempts: newAttempts,\n lastAttemptAt: dbNow(),\n nextRetryAt,\n error,\n })\n .check(),\n );\n } else {\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"failed\",\n attempts: newAttempts,\n lastAttemptAt: dbNow(),\n error,\n })\n .check(),\n );\n }\n })\n .build();\n },\n\n /**\n * Mark a hook event as processing (to prevent concurrent execution).\n */\n markHookProcessing(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: dbNow() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Get a hook event by ID (for testing/verification purposes).\n */\n getHookById(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(\"fragno_hooks\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", eventId)),\n ),\n )\n .transformRetrieve(([result]) => result ?? undefined)\n .build();\n },\n\n /**\n * Get all hook events for a namespace (for testing/verification purposes).\n */\n getHooksByNamespace(namespace: string) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) => eb(\"namespace\", \"=\", namespace)),\n ),\n )\n .transformRetrieve(([events]) => events)\n .build();\n },\n });\n })\n .providesService(\"outboxService\", ({ defineService }) => {\n return defineService({\n /**\n * List outbox entries ordered by versionstamp (ascending).\n */\n list({ afterVersionstamp, limit }: { afterVersionstamp?: string; limit?: number } = {}) {\n const afterValue = afterVersionstamp?.toLowerCase();\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_db_outbox\", (b) => {\n let builder = afterValue\n ? b.whereIndex(\"idx_outbox_versionstamp\", (eb) =>\n eb(\"versionstamp\", \">\", afterValue),\n )\n : b.whereIndex(\"idx_outbox_versionstamp\");\n\n builder = builder.orderByIndex(\"idx_outbox_versionstamp\", \"asc\");\n if (limit !== undefined) {\n builder = builder.pageSize(limit);\n }\n return builder;\n }),\n )\n .transformRetrieve(([entries]) =>\n entries.map((entry) => ({\n id: entry.id,\n versionstamp: entry.versionstamp,\n uowId: entry.uowId,\n payload: entry.payload,\n refMap: entry.refMap ?? undefined,\n createdAt: entry.createdAt,\n })),\n )\n .build();\n },\n });\n })\n .build();\n\n/**\n * Type representing an instantiated internal fragment.\n * This is the fragment that manages Fragno's internal settings table.\n */\nexport type InternalFragmentInstance = InstantiatedFragmentFromDefinition<\n typeof internalFragmentDef\n>;\n\nexport async function getSchemaVersionFromDatabase(\n fragment: InternalFragmentInstance,\n namespace: string,\n): Promise<number> {\n try {\n const readSchemaVersion = async (targetNamespace: string): Promise<number | undefined> => {\n const setting = await fragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () =>\n [fragment.services.settingsService.get(targetNamespace, \"schema_version\")] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n });\n if (!setting) {\n return undefined;\n }\n const parsed = parseInt(setting.value, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n };\n\n const primary = await readSchemaVersion(namespace);\n if (primary !== undefined) {\n return primary;\n }\n\n // Back-compat: some installs stored internal schema version under a different namespace.\n // Check the alternate key (empty string ↔ schema name) so we find the version either way.\n const legacyNamespace =\n namespace === \"\" ? internalSchema.name : namespace === internalSchema.name ? \"\" : null;\n if (legacyNamespace !== null) {\n const legacy = await readSchemaVersion(legacyNamespace);\n if (legacy !== undefined) {\n return legacy;\n }\n }\n\n return 0;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineRoutes } from "../packages/fragno/dist/api/route.js";
|
|
2
|
+
import { internalFragmentDef } from "./internal-fragment.js";
|
|
3
|
+
|
|
4
|
+
//#region src/fragments/internal-fragment.routes.ts
|
|
5
|
+
const internalFragmentRoutes = defineRoutes(internalFragmentDef).create(({ defineRoute, services }) => [defineRoute({
|
|
6
|
+
method: "GET",
|
|
7
|
+
path: "/outbox",
|
|
8
|
+
handler: async function(input, { json }) {
|
|
9
|
+
const afterVersionstamp = input.query.get("afterVersionstamp") ?? void 0;
|
|
10
|
+
const limitValue = input.query.get("limit");
|
|
11
|
+
let limit;
|
|
12
|
+
if (limitValue !== null) {
|
|
13
|
+
const parsed = Number.parseInt(limitValue, 10);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed < 1) return json({
|
|
15
|
+
error: "Invalid limit query parameter.",
|
|
16
|
+
code: "INVALID_LIMIT"
|
|
17
|
+
}, { status: 400 });
|
|
18
|
+
limit = parsed;
|
|
19
|
+
}
|
|
20
|
+
return json(await this.handlerTx().withServiceCalls(() => [services.outboxService.list({
|
|
21
|
+
afterVersionstamp,
|
|
22
|
+
limit
|
|
23
|
+
})]).transform(({ serviceResult: [result] }) => result).execute());
|
|
24
|
+
}
|
|
25
|
+
})]);
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { internalFragmentRoutes };
|
|
29
|
+
//# sourceMappingURL=internal-fragment.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-fragment.routes.js","names":["limit: number | undefined"],"sources":["../../src/fragments/internal-fragment.routes.ts"],"sourcesContent":["import { defineRoutes } from \"@fragno-dev/core\";\nimport { internalFragmentDef } from \"./internal-fragment\";\n\nexport const internalFragmentRoutes = defineRoutes(internalFragmentDef).create(\n ({ defineRoute, services }) => [\n defineRoute({\n method: \"GET\",\n path: \"/outbox\",\n handler: async function (input, { json }) {\n // We intentionally skip input/output schemas here to keep the internal route lightweight.\n // Query params are validated manually and the response shape is stable (OutboxEntry[]),\n // while the public API surface is still gated behind adapter config.\n const afterVersionstamp = input.query.get(\"afterVersionstamp\") ?? undefined;\n const limitValue = input.query.get(\"limit\");\n let limit: number | undefined;\n\n if (limitValue !== null) {\n const parsed = Number.parseInt(limitValue, 10);\n if (!Number.isFinite(parsed) || parsed < 1) {\n return json(\n {\n error: \"Invalid limit query parameter.\",\n code: \"INVALID_LIMIT\",\n },\n { status: 400 },\n );\n }\n limit = parsed;\n }\n\n const entries = await this.handlerTx()\n .withServiceCalls(\n () => [services.outboxService.list({ afterVersionstamp, limit })] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n return json(entries);\n },\n }),\n ],\n);\n"],"mappings":";;;;AAGA,MAAa,yBAAyB,aAAa,oBAAoB,CAAC,QACrE,EAAE,aAAa,eAAe,CAC7B,YAAY;CACV,QAAQ;CACR,MAAM;CACN,SAAS,eAAgB,OAAO,EAAE,QAAQ;EAIxC,MAAM,oBAAoB,MAAM,MAAM,IAAI,oBAAoB,IAAI;EAClE,MAAM,aAAa,MAAM,MAAM,IAAI,QAAQ;EAC3C,IAAIA;AAEJ,MAAI,eAAe,MAAM;GACvB,MAAM,SAAS,OAAO,SAAS,YAAY,GAAG;AAC9C,OAAI,CAAC,OAAO,SAAS,OAAO,IAAI,SAAS,EACvC,QAAO,KACL;IACE,OAAO;IACP,MAAM;IACP,EACD,EAAE,QAAQ,KAAK,CAChB;AAEH,WAAQ;;AAUV,SAAO,KAPS,MAAM,KAAK,WAAW,CACnC,uBACO,CAAC,SAAS,cAAc,KAAK;GAAE;GAAmB;GAAO,CAAC,CAAC,CAClE,CACA,WAAW,EAAE,eAAe,CAAC,cAAc,OAAO,CAClD,SAAS,CAEQ;;CAEvB,CAAC,CACH,CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DbNow } from "../query/db-now.js";
|
|
2
|
+
import { AnyColumn, AnyRelation, Column, FragnoId, IdColumn, Index, Schema, Table } from "../schema/create.js";
|
|
3
|
+
import "../mod.js";
|
|
4
|
+
|
|
5
|
+
//#region src/fragments/internal-fragment.schema.d.ts
|
|
6
|
+
declare const internalSchema: Schema<Record<"fragno_db_settings", Table<Record<string, AnyColumn> & Record<"id", IdColumn<"varchar(30)", string | FragnoId | null, FragnoId>> & Record<"key", Column<"string", string, string>> & Record<"value", Column<"string", string, string>>, Record<string, AnyRelation>, Record<string, Index<AnyColumn[], readonly string[]>> & Record<"unique_key", Index<readonly [Column<"string", string, string>] & AnyColumn[], readonly ["key"]>>>> & Record<"fragno_hooks", Table<Record<string, AnyColumn> & Record<"id", IdColumn<"varchar(30)", string | FragnoId | null, FragnoId>> & Record<"namespace", Column<"string", string, string>> & Record<"hookName", Column<"string", string, string>> & Record<"payload", Column<"json", unknown, unknown>> & Record<"status", Column<"string", string, string>> & Record<"attempts", Column<"integer", number | null, number>> & Record<"maxAttempts", Column<"integer", number | null, number>> & Record<"lastAttemptAt", Column<"timestamp", (DbNow | Date) | null, Date | null>> & Record<"nextRetryAt", Column<"timestamp", (DbNow | Date) | null, Date | null>> & Record<"error", Column<"string", string | null, string | null>> & Record<"createdAt", Column<"timestamp", (DbNow | Date) | null, Date>> & Record<"nonce", Column<"string", string, string>>, Record<string, AnyRelation>, Record<string, Index<AnyColumn[], readonly string[]>> & Record<"idx_namespace_status_retry", Index<readonly [Column<"string", string, string>, Column<"string", string, string>, Column<"timestamp", (DbNow | Date) | null, Date | null>] & AnyColumn[], readonly ["namespace", "status", "nextRetryAt"]>> & Record<"idx_nonce", Index<readonly [Column<"string", string, string>] & AnyColumn[], readonly ["nonce"]>>>> & Record<"fragno_db_outbox", Table<Record<string, AnyColumn> & Record<"id", IdColumn<"varchar(30)", string | FragnoId | null, FragnoId>> & Record<"versionstamp", Column<"string", string, string>> & Record<"uowId", Column<"string", string, string>> & Record<"payload", Column<"json", unknown, unknown>> & Record<"refMap", Column<"json", unknown, unknown>> & Record<"createdAt", Column<"timestamp", (DbNow | Date) | null, Date>>, Record<string, AnyRelation>, Record<string, Index<AnyColumn[], readonly string[]>> & Record<"idx_outbox_versionstamp", Index<readonly [Column<"string", string, string>] & AnyColumn[], readonly ["versionstamp"]>> & Record<"idx_outbox_uow", Index<readonly [Column<"string", string, string>] & AnyColumn[], readonly ["uowId"]>>>>>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { internalSchema };
|
|
9
|
+
//# sourceMappingURL=internal-fragment.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-fragment.schema.d.ts","names":[],"sources":["../../src/fragments/internal-fragment.schema.ts"],"sourcesContent":[],"mappings":";;;;;cAOa,gBAAc,OAAA,6BAAA,MAAA,eA2CzB,SAAA,IA3CyB,aAAA,iCAAA,QAAA,SAAA,QAAA,KAAA,cAAA,oCAAA,gBAAA,mCAAA,eAAA,WAAA,GAAA,eAAA,MAAA,SAAA,0BAAA,qBAAA,gBAAA,oCAAA,SAAA,2BAAA,uBAAA,MAAA,eAAA,SAAA,IAAA,aAAA,iCAAA,QAAA,SAAA,QAAA,KAAA,oBAAA,oCAAA,mBAAA,oCAAA,kBAAA,oCAAA,iBAAA,oCAAA,mBAAA,4CAAA,sBAAA,4CAAA,wBAAA,qBAAA,KAAA,GAAA,cAAA,gBAAA,sBAAA,qBAAA,KAAA,GAAA,cAAA,gBAAA,gBAAA,kDAAA,oBAAA,qBAAA,KAAA,GAAA,cAAA,SAAA,gBAAA,mCAAA,eAAA,WAAA,GAAA,eAAA,MAAA,SAAA,0BAAA,qCAAA,gBAAA,kCAAA,kCAAA,qBAAA,KAAA,GAAA,cAAA,gBAAA,SAAA,wDAAA,oBAAA,gBAAA,oCAAA,SAAA,6BAAA,2BAAA,MAAA,eAAA,SAAA,IAAA,aAAA,iCAAA,QAAA,SAAA,QAAA,KAAA,uBAAA,oCAAA,gBAAA,oCAAA,kBAAA,oCAAA,iBAAA,oCAAA,oBAAA,qBAAA,KAAA,GAAA,cAAA,QAAA,eAAA,WAAA,GAAA,eAAA,MAAA,SAAA,0BAAA,kCAAA,gBAAA,oCAAA,SAAA,kCAAA,yBAAA,gBAAA,oCAAA,SAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { column, idColumn, schema } from "../schema/create.js";
|
|
2
|
+
|
|
3
|
+
//#region src/fragments/internal-fragment.schema.ts
|
|
4
|
+
const SETTINGS_TABLE_NAME = "fragno_db_settings";
|
|
5
|
+
const SETTINGS_NAMESPACE = "fragno-db-settings";
|
|
6
|
+
const internalSchema = schema("fragno_internal", (s) => {
|
|
7
|
+
return s.addTable(SETTINGS_TABLE_NAME, (t) => {
|
|
8
|
+
return t.addColumn("id", idColumn()).addColumn("key", column("string")).addColumn("value", column("string")).createIndex("unique_key", ["key"], { unique: true });
|
|
9
|
+
}).addTable("fragno_hooks", (t) => {
|
|
10
|
+
return t.addColumn("id", idColumn()).addColumn("namespace", column("string")).addColumn("hookName", column("string")).addColumn("payload", column("json")).addColumn("status", column("string")).addColumn("attempts", column("integer").defaultTo(0)).addColumn("maxAttempts", column("integer").defaultTo(5)).addColumn("lastAttemptAt", column("timestamp").nullable()).addColumn("nextRetryAt", column("timestamp").nullable()).addColumn("error", column("string").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).addColumn("nonce", column("string")).createIndex("idx_namespace_status_retry", [
|
|
11
|
+
"namespace",
|
|
12
|
+
"status",
|
|
13
|
+
"nextRetryAt"
|
|
14
|
+
]).createIndex("idx_nonce", ["nonce"]);
|
|
15
|
+
}).addTable("fragno_db_outbox", (t) => {
|
|
16
|
+
return t.addColumn("id", idColumn()).addColumn("versionstamp", column("string")).addColumn("uowId", column("string")).addColumn("payload", column("json")).addColumn("refMap", column("json").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).createIndex("idx_outbox_versionstamp", ["versionstamp"], { unique: true }).createIndex("idx_outbox_uow", ["uowId"]);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME, internalSchema };
|
|
22
|
+
//# sourceMappingURL=internal-fragment.schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-fragment.schema.js","names":[],"sources":["../../src/fragments/internal-fragment.schema.ts"],"sourcesContent":["import { schema, idColumn, column } from \"../schema/create\";\n\n// Constants for Fragno's internal settings table\nexport const SETTINGS_TABLE_NAME = \"fragno_db_settings\" as const;\n// FIXME: In some places we simply use empty string \"\" as namespace, which is not correct.\nexport const SETTINGS_NAMESPACE = \"fragno-db-settings\" as const;\n\nexport const internalSchema = schema(\"fragno_internal\", (s) => {\n return s\n .addTable(SETTINGS_TABLE_NAME, (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"key\", column(\"string\"))\n .addColumn(\"value\", column(\"string\"))\n .createIndex(\"unique_key\", [\"key\"], { unique: true });\n })\n .addTable(\"fragno_hooks\", (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"namespace\", column(\"string\"))\n .addColumn(\"hookName\", column(\"string\"))\n .addColumn(\"payload\", column(\"json\"))\n .addColumn(\"status\", column(\"string\")) // \"pending\" | \"processing\" | \"completed\" | \"failed\"\n .addColumn(\"attempts\", column(\"integer\").defaultTo(0))\n .addColumn(\"maxAttempts\", column(\"integer\").defaultTo(5))\n .addColumn(\"lastAttemptAt\", column(\"timestamp\").nullable())\n .addColumn(\"nextRetryAt\", column(\"timestamp\").nullable())\n .addColumn(\"error\", column(\"string\").nullable())\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .addColumn(\"nonce\", column(\"string\"))\n .createIndex(\"idx_namespace_status_retry\", [\"namespace\", \"status\", \"nextRetryAt\"])\n .createIndex(\"idx_nonce\", [\"nonce\"]);\n })\n .addTable(\"fragno_db_outbox\", (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"versionstamp\", column(\"string\"))\n .addColumn(\"uowId\", column(\"string\"))\n .addColumn(\"payload\", column(\"json\"))\n .addColumn(\"refMap\", column(\"json\").nullable())\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .createIndex(\"idx_outbox_versionstamp\", [\"versionstamp\"], { unique: true })\n .createIndex(\"idx_outbox_uow\", [\"uowId\"]);\n });\n});\n"],"mappings":";;;AAGA,MAAa,sBAAsB;AAEnC,MAAa,qBAAqB;AAElC,MAAa,iBAAiB,OAAO,oBAAoB,MAAM;AAC7D,QAAO,EACJ,SAAS,sBAAsB,MAAM;AACpC,SAAO,EACJ,UAAU,MAAM,UAAU,CAAC,CAC3B,UAAU,OAAO,OAAO,SAAS,CAAC,CAClC,UAAU,SAAS,OAAO,SAAS,CAAC,CACpC,YAAY,cAAc,CAAC,MAAM,EAAE,EAAE,QAAQ,MAAM,CAAC;GACvD,CACD,SAAS,iBAAiB,MAAM;AAC/B,SAAO,EACJ,UAAU,MAAM,UAAU,CAAC,CAC3B,UAAU,aAAa,OAAO,SAAS,CAAC,CACxC,UAAU,YAAY,OAAO,SAAS,CAAC,CACvC,UAAU,WAAW,OAAO,OAAO,CAAC,CACpC,UAAU,UAAU,OAAO,SAAS,CAAC,CACrC,UAAU,YAAY,OAAO,UAAU,CAAC,UAAU,EAAE,CAAC,CACrD,UAAU,eAAe,OAAO,UAAU,CAAC,UAAU,EAAE,CAAC,CACxD,UAAU,iBAAiB,OAAO,YAAY,CAAC,UAAU,CAAC,CAC1D,UAAU,eAAe,OAAO,YAAY,CAAC,UAAU,CAAC,CACxD,UAAU,SAAS,OAAO,SAAS,CAAC,UAAU,CAAC,CAC/C,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,UAAU,SAAS,OAAO,SAAS,CAAC,CACpC,YAAY,8BAA8B;GAAC;GAAa;GAAU;GAAc,CAAC,CACjF,YAAY,aAAa,CAAC,QAAQ,CAAC;GACtC,CACD,SAAS,qBAAqB,MAAM;AACnC,SAAO,EACJ,UAAU,MAAM,UAAU,CAAC,CAC3B,UAAU,gBAAgB,OAAO,SAAS,CAAC,CAC3C,UAAU,SAAS,OAAO,SAAS,CAAC,CACpC,UAAU,WAAW,OAAO,OAAO,CAAC,CACpC,UAAU,UAAU,OAAO,OAAO,CAAC,UAAU,CAAC,CAC9C,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,YAAY,2BAA2B,CAAC,eAAe,EAAE,EAAE,QAAQ,MAAM,CAAC,CAC1E,YAAY,kBAAkB,CAAC,QAAQ,CAAC;GAC3C;EACJ"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AnySchema } from "../schema/create.js";
|
|
2
|
+
import { AnyFragnoInstantiatedDatabaseFragment } from "../mod.js";
|
|
3
|
+
|
|
4
|
+
//#region src/hooks/durable-hooks-processor.d.ts
|
|
5
|
+
type DurableHooksProcessor = {
|
|
6
|
+
process: () => Promise<number>;
|
|
7
|
+
getNextWakeAt: () => Promise<Date | null>;
|
|
8
|
+
drain: () => Promise<void>;
|
|
9
|
+
namespace: string;
|
|
10
|
+
};
|
|
11
|
+
declare function createDurableHooksProcessor<TSchema extends AnySchema>(fragment: AnyFragnoInstantiatedDatabaseFragment<TSchema>): DurableHooksProcessor | null;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DurableHooksProcessor, createDurableHooksProcessor };
|
|
14
|
+
//# sourceMappingURL=durable-hooks-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-hooks-processor.d.ts","names":[],"sources":["../../src/hooks/durable-hooks-processor.ts"],"sourcesContent":[],"mappings":";;;;KAIY,qBAAA;iBACK;EADL,aAAA,EAAA,GAAA,GAEW,OAFU,CAEF,IAFE,GAAA,IAAA,CAAA;EAChB,KAAA,EAAA,GAAA,GAEF,OAFE,CAAA,IAAA,CAAA;EACc,SAAA,EAAA,MAAA;CAAR;AACR,iBAoBC,2BApBD,CAAA,gBAoB6C,SApB7C,CAAA,CAAA,QAAA,EAqBH,qCArBG,CAqBmC,OArBnC,CAAA,CAAA,EAsBZ,qBAtBY,GAAA,IAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createHookScheduler } from "./hooks.js";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/durable-hooks-processor.ts
|
|
4
|
+
const DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;
|
|
5
|
+
function resolveStuckProcessingTimeoutMinutes(value) {
|
|
6
|
+
if (value === false) return false;
|
|
7
|
+
if (typeof value === "number") return value > 0 ? value : false;
|
|
8
|
+
return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;
|
|
9
|
+
}
|
|
10
|
+
function createDurableHooksProcessor(fragment) {
|
|
11
|
+
const durableHooks = fragment.$internal.durableHooks;
|
|
12
|
+
if (!durableHooks) return null;
|
|
13
|
+
const { namespace, internalFragment } = durableHooks;
|
|
14
|
+
const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(durableHooks.stuckProcessingTimeoutMinutes);
|
|
15
|
+
const scheduler = durableHooks.scheduler ?? (durableHooks.scheduler = createHookScheduler(durableHooks));
|
|
16
|
+
return {
|
|
17
|
+
namespace,
|
|
18
|
+
process: async () => scheduler.schedule(),
|
|
19
|
+
drain: async () => scheduler.drain(),
|
|
20
|
+
getNextWakeAt: async () => {
|
|
21
|
+
const services = internalFragment.services;
|
|
22
|
+
const now = services.getDbNow ? await services.getDbNow() : /* @__PURE__ */ new Date();
|
|
23
|
+
return await internalFragment.inContext(async function() {
|
|
24
|
+
return await this.handlerTx().withServiceCalls(() => [internalFragment.services.hookService.getNextHookWakeAt(namespace, stuckProcessingTimeoutMinutes, now)]).transform(({ serviceResult: [result] }) => result).execute();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { createDurableHooksProcessor };
|
|
32
|
+
//# sourceMappingURL=durable-hooks-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-hooks-processor.js","names":[],"sources":["../../src/hooks/durable-hooks-processor.ts"],"sourcesContent":["import type { AnySchema } from \"../schema/create\";\nimport type { AnyFragnoInstantiatedDatabaseFragment } from \"../mod\";\nimport { createHookScheduler, type HookProcessorConfig } from \"./hooks\";\n\nexport type DurableHooksProcessor = {\n process: () => Promise<number>;\n getNextWakeAt: () => Promise<Date | null>;\n drain: () => Promise<void>;\n namespace: string;\n};\n\ntype DurableHooksInternal = {\n durableHooks?: HookProcessorConfig;\n};\n\nconst DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;\n\nfunction resolveStuckProcessingTimeoutMinutes(value: number | false | undefined): number | false {\n if (value === false) {\n return false;\n }\n if (typeof value === \"number\") {\n return value > 0 ? value : false;\n }\n return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;\n}\n\nexport function createDurableHooksProcessor<TSchema extends AnySchema>(\n fragment: AnyFragnoInstantiatedDatabaseFragment<TSchema>,\n): DurableHooksProcessor | null {\n const durableHooks = (fragment.$internal as DurableHooksInternal).durableHooks;\n if (!durableHooks) {\n return null;\n }\n\n const { namespace, internalFragment } = durableHooks;\n const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(\n durableHooks.stuckProcessingTimeoutMinutes,\n );\n const scheduler =\n durableHooks.scheduler ?? (durableHooks.scheduler = createHookScheduler(durableHooks));\n\n return {\n namespace,\n process: async () => scheduler.schedule(),\n drain: async () => scheduler.drain(),\n getNextWakeAt: async () => {\n const services = internalFragment.services as { getDbNow?: () => Promise<Date> };\n const now = services.getDbNow ? await services.getDbNow() : new Date();\n return await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n internalFragment.services.hookService.getNextHookWakeAt(\n namespace,\n stuckProcessingTimeoutMinutes,\n now,\n ),\n ] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n });\n },\n };\n}\n"],"mappings":";;;AAeA,MAAM,2CAA2C;AAEjD,SAAS,qCAAqC,OAAmD;AAC/F,KAAI,UAAU,MACZ,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,QAAQ,IAAI,QAAQ;AAE7B,QAAO;;AAGT,SAAgB,4BACd,UAC8B;CAC9B,MAAM,eAAgB,SAAS,UAAmC;AAClE,KAAI,CAAC,aACH,QAAO;CAGT,MAAM,EAAE,WAAW,qBAAqB;CACxC,MAAM,gCAAgC,qCACpC,aAAa,8BACd;CACD,MAAM,YACJ,aAAa,cAAc,aAAa,YAAY,oBAAoB,aAAa;AAEvF,QAAO;EACL;EACA,SAAS,YAAY,UAAU,UAAU;EACzC,OAAO,YAAY,UAAU,OAAO;EACpC,eAAe,YAAY;GACzB,MAAM,WAAW,iBAAiB;GAClC,MAAM,MAAM,SAAS,WAAW,MAAM,SAAS,UAAU,mBAAG,IAAI,MAAM;AACtE,UAAO,MAAM,iBAAiB,UAAU,iBAAkB;AACxD,WAAO,MAAM,KAAK,WAAW,CAC1B,uBAEG,CACE,iBAAiB,SAAS,YAAY,kBACpC,WACA,+BACA,IACD,CACF,CACJ,CACA,WAAW,EAAE,eAAe,CAAC,cAAc,OAAO,CAClD,SAAS;KACZ;;EAEL"}
|
package/dist/hooks/hooks.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { FragnoId } from "../schema/create.js";
|
|
1
2
|
import { RetryPolicy } from "../query/unit-of-work/retry-policy.js";
|
|
3
|
+
import { ExecuteTxOptions, HandlerTxBuilder } from "../query/unit-of-work/execute-unit-of-work.js";
|
|
2
4
|
import "../fragments/internal-fragment.js";
|
|
3
5
|
import "../query/unit-of-work/unit-of-work.js";
|
|
4
6
|
|
|
@@ -14,6 +16,10 @@ interface HookContext {
|
|
|
14
16
|
* Use this for idempotency checks in your hook implementation.
|
|
15
17
|
*/
|
|
16
18
|
idempotencyKey: string;
|
|
19
|
+
/**
|
|
20
|
+
* Create a handler transaction builder to run atomic operations.
|
|
21
|
+
*/
|
|
22
|
+
handlerTx: HookHandlerTx;
|
|
17
23
|
}
|
|
18
24
|
/**
|
|
19
25
|
* A hook function signature.
|
|
@@ -38,6 +44,11 @@ interface TriggerHookOptions {
|
|
|
38
44
|
* If not provided, uses the default retry policy.
|
|
39
45
|
*/
|
|
40
46
|
retryPolicy?: RetryPolicy;
|
|
47
|
+
/**
|
|
48
|
+
* Absolute time for the first attempt. If in the future, the hook is
|
|
49
|
+
* scheduled for that time; if in the past, it runs immediately.
|
|
50
|
+
*/
|
|
51
|
+
processAt?: Date;
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
43
54
|
* Internal representation of a triggered hook.
|
|
@@ -48,6 +59,36 @@ interface TriggeredHook {
|
|
|
48
59
|
payload: unknown;
|
|
49
60
|
options?: TriggerHookOptions;
|
|
50
61
|
}
|
|
62
|
+
type StuckHookProcessingTimeoutMinutes = number | false;
|
|
63
|
+
type StuckHookProcessingEvent = {
|
|
64
|
+
id: FragnoId;
|
|
65
|
+
hookName: string;
|
|
66
|
+
attempts: number;
|
|
67
|
+
maxAttempts: number;
|
|
68
|
+
lastAttemptAt: Date | null;
|
|
69
|
+
nextRetryAt: Date | null;
|
|
70
|
+
};
|
|
71
|
+
type StuckHookProcessingInfo = {
|
|
72
|
+
namespace: string;
|
|
73
|
+
timeoutMinutes: number;
|
|
74
|
+
events: StuckHookProcessingEvent[];
|
|
75
|
+
};
|
|
76
|
+
type DurableHooksProcessingOptions = {
|
|
77
|
+
/**
|
|
78
|
+
* Re-queue hooks that have been in `processing` for at least this many minutes.
|
|
79
|
+
* Use `false` to disable stuck-processing recovery entirely.
|
|
80
|
+
* Values <= 0 are treated as `false`.
|
|
81
|
+
*
|
|
82
|
+
* Default: 10 minutes.
|
|
83
|
+
*/
|
|
84
|
+
stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
|
|
85
|
+
/**
|
|
86
|
+
* Called when stuck processing hooks are detected and re-queued.
|
|
87
|
+
* Invoked after the hooks are moved back to `pending`.
|
|
88
|
+
*/
|
|
89
|
+
onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
|
|
90
|
+
};
|
|
91
|
+
type HookHandlerTx = (execOptions?: Omit<ExecuteTxOptions, "createUnitOfWork">) => HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, HooksMap>;
|
|
51
92
|
//#endregion
|
|
52
|
-
export { HookContext, HookFn, HookPayload, HooksMap, TriggerHookOptions, TriggeredHook };
|
|
93
|
+
export { DurableHooksProcessingOptions, HookContext, HookFn, HookPayload, HooksMap, StuckHookProcessingEvent, StuckHookProcessingInfo, StuckHookProcessingTimeoutMinutes, TriggerHookOptions, TriggeredHook };
|
|
53
94
|
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAeA;AAgBA;AAOY,UAvBK,WAAA,CAuBqB;EAK1B;;;;EAA8C,cAAA,EAAA,MAAA;EAKzC;AAiBjB;AAuCA;EAEY,SAAA,EAlFC,aAkFD;;;;;AASZ;AAMY,KA1FA,MA0FA,CAAA,WAAA,OAA6B,CAAA,GAAA,CAAA,OAQP,EAlGiB,QAkGjB,EAAA,GAAA,IAAA,GAlGqC,OAkGrC,CAAA,IAAA,CAKA;AAGlC;;;;AAEK,KArGO,QAAA,GAAW,MAqGlB,CAAA,MAAA,EArGiC,MAqGjC,CAAA,GAAA,CAAA,CAAA;;;;KAhGO,iBAAiB,UAAU,kBAAkB;;;;UAKxC,kBAAA;;;;;gBAKD;;;;;cAKF;;;;;;UAOG,aAAA;;;YAGL;;KAoCA,iCAAA;KAEA,wBAAA;MACN;;;;iBAIW;eACF;;KAGH,uBAAA;;;UAGF;;KAGE,6BAAA;;;;;;;;kCAQsB;;;;;kCAKA;;KAGtB,aAAA,kBACI,KAAK,0CAChB,oFAAoF"}
|