@fragno-dev/db 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +327 -160
- package/CHANGELOG.md +74 -0
- package/README.md +24 -0
- package/dist/adapters/adapters.d.ts +1 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/executor.js +30 -3
- package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
- package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +24 -20
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js +1 -1
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +8 -9
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +10 -6
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
- package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
- package/dist/adapters/in-memory/in-memory-uow.js +236 -13
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
- package/dist/adapters/in-memory/options.d.ts +2 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -1
- package/dist/adapters/in-memory/options.js +3 -2
- package/dist/adapters/in-memory/options.js.map +1 -1
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
- package/dist/adapters/in-memory/store.js +1 -1
- package/dist/adapters/in-memory/store.js.map +1 -1
- package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/browser/adapters/adapters.d.ts +61 -0
- package/dist/browser/adapters/adapters.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
- package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
- package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
- package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
- package/dist/browser/adapters/in-memory/index.d.ts +2 -0
- package/dist/browser/adapters/in-memory/options.d.ts +1 -0
- package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
- package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/browser/durable-hooks.d.ts +3 -0
- package/dist/browser/fragments/internal-fragment.d.ts +317 -0
- package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/browser/hooks/hooks.d.ts +146 -0
- package/dist/browser/hooks/hooks.d.ts.map +1 -0
- package/dist/browser/id.js +1 -0
- package/dist/browser/internal/adapter-registry.d.ts +4 -0
- package/dist/browser/internal/outbox-state.d.ts +2 -0
- package/dist/browser/mod.d.ts +15 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +17 -0
- package/dist/browser/mod.js.map +1 -0
- package/dist/browser/mod2.d.ts +48 -0
- package/dist/browser/mod2.d.ts.map +1 -0
- package/dist/browser/naming/sql-naming.d.ts +19 -0
- package/dist/browser/naming/sql-naming.d.ts.map +1 -0
- package/dist/browser/outbox/outbox.d.ts +21 -0
- package/dist/browser/outbox/outbox.d.ts.map +1 -0
- package/dist/browser/query/column-defaults.js +1 -0
- package/dist/browser/query/condition-builder.d.ts +44 -0
- package/dist/browser/query/condition-builder.d.ts.map +1 -0
- package/dist/browser/query/condition-builder.js +97 -0
- package/dist/browser/query/condition-builder.js.map +1 -0
- package/dist/browser/query/cursor.d.ts +105 -0
- package/dist/browser/query/cursor.d.ts.map +1 -0
- package/dist/browser/query/cursor.js +150 -0
- package/dist/browser/query/cursor.js.map +1 -0
- package/dist/browser/query/db-now.d.ts +22 -0
- package/dist/browser/query/db-now.d.ts.map +1 -0
- package/dist/browser/query/db-now.js +33 -0
- package/dist/browser/query/db-now.js.map +1 -0
- package/dist/browser/query/orm/orm.d.ts +18 -0
- package/dist/browser/query/orm/orm.d.ts.map +1 -0
- package/dist/browser/query/simple-query-interface.d.ts +108 -0
- package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
- package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
- package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
- package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
- package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
- package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
- package/dist/browser/query/value-encoding.js +38 -0
- package/dist/browser/query/value-encoding.js.map +1 -0
- package/dist/browser/schema/create.d.ts +326 -0
- package/dist/browser/schema/create.d.ts.map +1 -0
- package/dist/browser/schema/create.js +89 -0
- package/dist/browser/schema/create.js.map +1 -0
- package/dist/browser/schema/generate-id.js +28 -0
- package/dist/browser/schema/generate-id.js.map +1 -0
- package/dist/browser/shared/providers.d.ts +6 -0
- package/dist/browser/shared/providers.d.ts.map +1 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
- package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
- package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
- package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
- package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
- package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
- package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
- package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
- package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
- package/dist/browser/sync/commands.d.ts +15 -0
- package/dist/browser/sync/commands.d.ts.map +1 -0
- package/dist/browser/sync/commands.js +27 -0
- package/dist/browser/sync/commands.js.map +1 -0
- package/dist/browser/sync/types.d.ts +63 -0
- package/dist/browser/sync/types.d.ts.map +1 -0
- package/dist/browser/util/types.d.ts +8 -0
- package/dist/browser/util/types.d.ts.map +1 -0
- package/dist/browser/with-database.d.ts +29 -0
- package/dist/browser/with-database.d.ts.map +1 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +5 -0
- package/dist/db-fragment-definition-builder.d.ts +85 -28
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +374 -46
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
- package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.js +23 -55
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
- package/dist/dispatchers/node/dispatcher.d.ts +14 -0
- package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
- package/dist/dispatchers/node/dispatcher.js +80 -0
- package/dist/dispatchers/node/dispatcher.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +5 -10
- package/dist/dispatchers/node/index.d.ts.map +1 -1
- package/dist/dispatchers/node/index.js +21 -53
- package/dist/dispatchers/node/index.js.map +1 -1
- package/dist/durable-hooks.d.ts +31 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +23 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +128 -27
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +125 -78
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +138 -3
- package/dist/fragments/internal-fragment.routes.js.map +1 -1
- package/dist/fragments/internal-fragment.schema.d.ts +7 -1
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.schema.js +18 -1
- package/dist/fragments/internal-fragment.schema.js.map +1 -1
- package/dist/hooks/durable-hooks-logger.d.ts +10 -0
- package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-logger.js +75 -0
- package/dist/hooks/durable-hooks-logger.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +1 -14
- package/dist/hooks/durable-hooks-processor.js +58 -10
- package/dist/hooks/durable-hooks-processor.js.map +1 -1
- package/dist/hooks/durable-hooks-runtime.js +44 -0
- package/dist/hooks/durable-hooks-runtime.js.map +1 -0
- package/dist/hooks/hooks.d.ts +60 -2
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +214 -53
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.js +2 -2
- package/dist/internal/adapter-registry.d.ts +11 -0
- package/dist/internal/adapter-registry.d.ts.map +1 -0
- package/dist/internal/adapter-registry.js +135 -0
- package/dist/internal/adapter-registry.js.map +1 -0
- package/dist/internal/outbox-state.d.ts +2 -0
- package/dist/internal/outbox-state.js +26 -0
- package/dist/internal/outbox-state.js.map +1 -0
- package/dist/migration-engine/auto-from-schema.d.ts +33 -0
- package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
- package/dist/migration-engine/auto-from-schema.js +210 -27
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +17 -5
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.d.ts +113 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +12 -11
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +10 -10
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts.map +1 -1
- package/dist/naming/sql-naming.js.map +1 -1
- package/dist/outbox/outbox-builder.js.map +1 -1
- package/dist/outbox/outbox.d.ts +3 -1
- package/dist/outbox/outbox.d.ts.map +1 -1
- package/dist/outbox/outbox.js.map +1 -1
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/condition-builder.d.ts +7 -1
- package/dist/query/condition-builder.d.ts.map +1 -1
- package/dist/query/condition-builder.js +5 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/cursor-client.d.ts +105 -0
- package/dist/query/cursor-client.d.ts.map +1 -0
- package/dist/query/cursor-client.js +165 -0
- package/dist/query/cursor-client.js.map +1 -0
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +7 -1
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +15 -1
- package/dist/query/db-now.d.ts.map +1 -1
- package/dist/query/db-now.js +30 -2
- package/dist/query/db-now.js.map +1 -1
- package/dist/query/orm/orm.js.map +1 -1
- package/dist/query/serialize/create-sql-serializer.js +2 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +7 -3
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +50 -6
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +8 -1
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +69 -25
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +91 -16
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts.map +1 -1
- package/dist/schema/validator.js.map +1 -1
- package/dist/schema-output/drizzle.d.ts.map +1 -1
- package/dist/schema-output/drizzle.js +8 -6
- package/dist/schema-output/drizzle.js.map +1 -1
- package/dist/schema-output/prisma.d.ts.map +1 -1
- package/dist/schema-output/prisma.js +21 -10
- package/dist/schema-output/prisma.js.map +1 -1
- package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
- package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
- package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
- package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
- package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
- package/dist/sql-driver/sql.js.map +1 -1
- package/dist/sync/commands.d.ts +15 -0
- package/dist/sync/commands.d.ts.map +1 -0
- package/dist/sync/commands.js +27 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/read-tracking.d.ts +25 -0
- package/dist/sync/read-tracking.d.ts.map +1 -0
- package/dist/sync/read-tracking.js +148 -0
- package/dist/sync/read-tracking.js.map +1 -0
- package/dist/sync/submit.js +213 -0
- package/dist/sync/submit.js.map +1 -0
- package/dist/sync/types.d.ts +63 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/util/default-database-adapter.js +6 -1
- package/dist/util/default-database-adapter.js.map +1 -1
- package/dist/with-database.d.ts +3 -6
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +7 -15
- package/dist/with-database.js.map +1 -1
- package/package.json +33 -41
- package/src/adapters/adapters.ts +5 -4
- package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
- package/src/adapters/drizzle/test-utils.ts +2 -1
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
- package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
- package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
- package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
- package/src/adapters/generic-sql/migration/executor.ts +47 -4
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
- package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
- package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
- package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
- package/src/adapters/generic-sql/query/select-builder.ts +2 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
- package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
- package/src/adapters/generic-sql/query/where-builder.ts +48 -29
- package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
- package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
- package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
- package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
- package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
- package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
- package/src/adapters/generic-sql/uow-decoder.ts +3 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
- package/src/adapters/generic-sql/uow-encoder.ts +14 -18
- package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
- package/src/adapters/in-memory/condition-evaluator.ts +9 -4
- package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
- package/src/adapters/in-memory/in-memory-uow.ts +402 -26
- package/src/adapters/in-memory/options.test.ts +1 -0
- package/src/adapters/in-memory/options.ts +5 -1
- package/src/adapters/in-memory/outbox.test.ts +361 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
- package/src/adapters/in-memory/reference-resolution.ts +2 -2
- package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
- package/src/adapters/in-memory/store.test.ts +1 -0
- package/src/adapters/in-memory/store.ts +3 -3
- package/src/adapters/in-memory/value-normalization.test.ts +1 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
- package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
- package/src/adapters/shared/uow-operation-compiler.ts +3 -3
- package/src/browser/mod.ts +64 -0
- package/src/client.ts +19 -0
- package/src/db-fragment-definition-builder.test.ts +821 -47
- package/src/db-fragment-definition-builder.ts +857 -110
- package/src/db-fragment-instantiator.test.ts +114 -90
- package/src/db-fragment-integration.test.ts +9 -6
- package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
- package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
- package/src/dispatchers/cloudflare-do/index.ts +49 -90
- package/src/dispatchers/node/dispatcher.ts +112 -0
- package/src/dispatchers/node/index.test.ts +43 -14
- package/src/dispatchers/node/index.ts +38 -75
- package/src/durable-hooks.test.ts +80 -0
- package/src/durable-hooks.ts +67 -0
- package/src/fragments/internal-fragment.routes.test.ts +570 -0
- package/src/fragments/internal-fragment.routes.ts +297 -5
- package/src/fragments/internal-fragment.schema.ts +45 -1
- package/src/fragments/internal-fragment.test.ts +223 -251
- package/src/fragments/internal-fragment.ts +278 -154
- package/src/hooks/durable-hooks-logger.ts +126 -0
- package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
- package/src/hooks/durable-hooks-processor.test.ts +179 -14
- package/src/hooks/durable-hooks-processor.ts +120 -14
- package/src/hooks/durable-hooks-runtime.test.ts +65 -0
- package/src/hooks/durable-hooks-runtime.ts +81 -0
- package/src/hooks/hooks.test.ts +314 -53
- package/src/hooks/hooks.ts +360 -81
- package/src/id.test.ts +34 -0
- package/src/id.ts +1 -3
- package/src/internal/adapter-registry.test.ts +93 -0
- package/src/internal/adapter-registry.ts +239 -0
- package/src/internal/outbox-state.ts +43 -0
- package/src/migration-engine/auto-from-schema.test.ts +93 -0
- package/src/migration-engine/auto-from-schema.ts +360 -42
- package/src/migration-engine/create.test.ts +2 -1
- package/src/migration-engine/create.ts +1 -1
- package/src/migration-engine/generation-engine.test.ts +66 -9
- package/src/migration-engine/generation-engine.ts +31 -10
- package/src/migration-engine/shared.ts +13 -0
- package/src/mod.ts +45 -27
- package/src/naming/sql-naming.ts +1 -0
- package/src/outbox/outbox-builder.ts +2 -2
- package/src/outbox/outbox.test.ts +216 -45
- package/src/outbox/outbox.ts +3 -1
- package/src/query/column-defaults.ts +1 -1
- package/src/query/condition-builder.test.ts +15 -0
- package/src/query/condition-builder.ts +7 -0
- package/src/query/cursor-client.test.ts +70 -0
- package/src/query/cursor-client.ts +263 -0
- package/src/query/cursor.test.ts +3 -2
- package/src/query/cursor.ts +15 -3
- package/src/query/db-now.ts +69 -2
- package/src/query/orm/orm.ts +2 -2
- package/src/query/query-type.test.ts +2 -1
- package/src/query/serialize/create-sql-serializer.ts +3 -3
- package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
- package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
- package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
- package/src/query/simple-query-interface.ts +10 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
- package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
- package/src/query/unit-of-work/retry-policy.test.ts +1 -0
- package/src/query/unit-of-work/tx-builder.test.ts +73 -1
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
- package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
- package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
- package/src/query/unit-of-work/unit-of-work.ts +105 -19
- package/src/query/value-decoding.test.ts +50 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +1 -0
- package/src/query/value-encoding.ts +1 -1
- package/src/schema/create.test.ts +164 -5
- package/src/schema/create.ts +222 -24
- package/src/schema/generate-id.test.ts +1 -0
- package/src/schema/serialize.test.ts +4 -3
- package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
- package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
- package/src/schema/type-conversion/type-mapping.test.ts +2 -1
- package/src/schema/validator.test.ts +4 -2
- package/src/schema/validator.ts +1 -0
- package/src/schema-output/drizzle.test.ts +72 -19
- package/src/schema-output/drizzle.ts +24 -18
- package/src/schema-output/prisma.test.ts +172 -14
- package/src/schema-output/prisma.ts +34 -14
- package/src/sql-driver/better-sqlite3.test.ts +5 -3
- package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
- package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
- package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
- package/src/sql-driver/query-executor/query-executor.ts +1 -1
- package/src/sql-driver/sql-driver-adapter.ts +2 -2
- package/src/sql-driver/sql.ts +2 -1
- package/src/sql-driver/sqlocal.test.ts +4 -2
- package/src/sync/commands.test.ts +39 -0
- package/src/sync/commands.ts +51 -0
- package/src/sync/conflict-checker.test.ts +450 -0
- package/src/sync/conflict-checker.ts +248 -0
- package/src/sync/index.ts +14 -0
- package/src/sync/plan.ts +9 -0
- package/src/sync/read-tracking.test.ts +177 -0
- package/src/sync/read-tracking.ts +287 -0
- package/src/sync/submit.test.ts +205 -0
- package/src/sync/submit.ts +328 -0
- package/src/sync/types.ts +80 -0
- package/src/util/default-database-adapter.ts +15 -2
- package/src/with-database.ts +20 -50
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +38 -26
- package/vitest.config.ts +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/packages/fragno/dist/api/bind-services.js +0 -20
- package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
- package/dist/packages/fragno/dist/api/error.js +0 -48
- package/dist/packages/fragno/dist/api/error.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
- package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
- package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
- package/dist/packages/fragno/dist/api/internal/route.js +0 -10
- package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
- package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
- package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
- package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
- package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
- package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
- package/dist/packages/fragno/dist/api/route.js +0 -30
- package/dist/packages/fragno/dist/api/route.js.map +0 -1
- package/dist/packages/fragno/dist/internal/symbols.js +0 -10
- package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
- package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
|
@@ -1,14 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dbNow } from "../query/db-now.js";
|
|
2
2
|
import { FragnoId } from "../schema/create.js";
|
|
3
|
+
import { isHookStatus } from "../hooks/hooks.js";
|
|
3
4
|
import { SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME, internalSchema } from "./internal-fragment.schema.js";
|
|
4
|
-
import { dbNow } from "../query/db-now.js";
|
|
5
5
|
import { DatabaseFragmentDefinitionBuilder } from "../db-fragment-definition-builder.js";
|
|
6
|
+
import { FragmentDefinitionBuilder } from "@fragno-dev/core";
|
|
6
7
|
|
|
7
8
|
//#region src/fragments/internal-fragment.ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
var SchemaRegistryCollisionError = class extends Error {
|
|
10
|
+
code = "SCHEMA_REGISTRY_COLLISION";
|
|
11
|
+
namespaceKey;
|
|
12
|
+
existing;
|
|
13
|
+
attempted;
|
|
14
|
+
constructor({ namespaceKey, existing, attempted }) {
|
|
15
|
+
super(`Schema namespace "${namespaceKey}" is already owned by "${existing.name}" (${existing.namespace ?? "null"}).`);
|
|
16
|
+
this.name = "SchemaRegistryCollisionError";
|
|
17
|
+
this.namespaceKey = namespaceKey;
|
|
18
|
+
this.existing = existing;
|
|
19
|
+
this.attempted = attempted;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const INTERNAL_SCHEMA_MIN_VERSION = 4;
|
|
23
|
+
if (internalSchema.version < INTERNAL_SCHEMA_MIN_VERSION) internalSchema.version = INTERNAL_SCHEMA_MIN_VERSION;
|
|
24
|
+
const describeHookStatusSource = (event) => `fragno_hooks id=${event.id} hook=${event.hookName}`;
|
|
25
|
+
const coerceHookStatus = (status, context) => {
|
|
26
|
+
if (isHookStatus(status)) return status;
|
|
27
|
+
throw new Error(`Invalid hook status from database (${context}): ${status}`);
|
|
28
|
+
};
|
|
29
|
+
const DEFAULT_HOOKS_PAGE_SIZE = 50;
|
|
30
|
+
const resolveHookPageSize = (pageSize) => {
|
|
31
|
+
if (typeof pageSize !== "number" || !Number.isInteger(pageSize) || pageSize <= 0) return DEFAULT_HOOKS_PAGE_SIZE;
|
|
32
|
+
return pageSize;
|
|
33
|
+
};
|
|
34
|
+
const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDefinitionBuilder("$fragno-internal-fragment"), internalSchema).providesService("settingsService", ({ defineService }) => {
|
|
12
35
|
return defineService({
|
|
13
36
|
get(namespace, key) {
|
|
14
37
|
const fullKey = `${namespace}.${key}`;
|
|
@@ -24,6 +47,16 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
24
47
|
});
|
|
25
48
|
}).build();
|
|
26
49
|
},
|
|
50
|
+
setIfMissing(namespace, key, value) {
|
|
51
|
+
const fullKey = `${namespace}.${key}`;
|
|
52
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst(SETTINGS_TABLE_NAME, (b) => b.whereIndex("unique_key", (eb) => eb("key", "=", fullKey)))).transformRetrieve(([result]) => result).mutate(({ uow, retrieveResult }) => {
|
|
53
|
+
if (retrieveResult) return;
|
|
54
|
+
uow.create(SETTINGS_TABLE_NAME, {
|
|
55
|
+
key: fullKey,
|
|
56
|
+
value
|
|
57
|
+
});
|
|
58
|
+
}).build();
|
|
59
|
+
},
|
|
27
60
|
delete(id) {
|
|
28
61
|
return this.serviceTx(internalSchema).mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id)).build();
|
|
29
62
|
}
|
|
@@ -37,8 +70,12 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
37
70
|
id: event.id,
|
|
38
71
|
hookName: event.hookName,
|
|
39
72
|
payload: event.payload,
|
|
73
|
+
status: coerceHookStatus(event.status, describeHookStatusSource(event)),
|
|
40
74
|
attempts: event.attempts,
|
|
41
75
|
maxAttempts: event.maxAttempts,
|
|
76
|
+
lastAttemptAt: event.lastAttemptAt,
|
|
77
|
+
nextRetryAt: event.nextRetryAt,
|
|
78
|
+
createdAt: event.createdAt,
|
|
42
79
|
idempotencyKey: event.nonce
|
|
43
80
|
}));
|
|
44
81
|
}).build();
|
|
@@ -50,8 +87,12 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
50
87
|
id: event.id,
|
|
51
88
|
hookName: event.hookName,
|
|
52
89
|
payload: event.payload,
|
|
90
|
+
status: coerceHookStatus(event.status, describeHookStatusSource(event)),
|
|
53
91
|
attempts: event.attempts,
|
|
54
92
|
maxAttempts: event.maxAttempts,
|
|
93
|
+
lastAttemptAt: event.lastAttemptAt,
|
|
94
|
+
nextRetryAt: event.nextRetryAt,
|
|
95
|
+
createdAt: event.createdAt,
|
|
55
96
|
idempotencyKey: event.nonce
|
|
56
97
|
}));
|
|
57
98
|
}).mutate(({ uow, retrieveResult }) => {
|
|
@@ -62,6 +103,7 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
62
103
|
}).check());
|
|
63
104
|
}).transform(({ retrieveResult }) => retrieveResult.map((event) => ({
|
|
64
105
|
...event,
|
|
106
|
+
status: "processing",
|
|
65
107
|
id: new FragnoId({
|
|
66
108
|
externalId: event.id.externalId,
|
|
67
109
|
internalId: event.id.internalId,
|
|
@@ -69,104 +111,89 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
69
111
|
})
|
|
70
112
|
}))).build();
|
|
71
113
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return event.lastAttemptAt <= staleBefore;
|
|
77
|
-
}).map((event) => ({
|
|
114
|
+
claimStuckProcessingHookEvents(namespace, staleBefore) {
|
|
115
|
+
const now = dbNow();
|
|
116
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"), eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)))))).transformRetrieve(([events]) => {
|
|
117
|
+
return events.map((event) => ({
|
|
78
118
|
id: event.id,
|
|
79
119
|
hookName: event.hookName,
|
|
120
|
+
payload: event.payload,
|
|
121
|
+
status: coerceHookStatus(event.status, describeHookStatusSource(event)),
|
|
80
122
|
attempts: event.attempts,
|
|
81
123
|
maxAttempts: event.maxAttempts,
|
|
124
|
+
idempotencyKey: event.nonce,
|
|
82
125
|
lastAttemptAt: event.lastAttemptAt,
|
|
83
|
-
nextRetryAt: event.nextRetryAt
|
|
126
|
+
nextRetryAt: event.nextRetryAt,
|
|
127
|
+
createdAt: event.createdAt
|
|
84
128
|
}));
|
|
85
129
|
}).mutate(({ uow, retrieveResult }) => {
|
|
130
|
+
if (retrieveResult.length === 0) return;
|
|
86
131
|
for (const event of retrieveResult) uow.update("fragno_hooks", event.id, (b) => b.set({
|
|
87
|
-
status: "
|
|
132
|
+
status: "processing",
|
|
133
|
+
lastAttemptAt: now,
|
|
88
134
|
nextRetryAt: null
|
|
89
135
|
}).check());
|
|
90
|
-
}).transform(({ retrieveResult }) =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
}).transform(({ retrieveResult }) => {
|
|
137
|
+
return {
|
|
138
|
+
events: retrieveResult.map((event) => ({
|
|
139
|
+
...event,
|
|
140
|
+
id: new FragnoId({
|
|
141
|
+
externalId: event.id.externalId,
|
|
142
|
+
internalId: event.id.internalId,
|
|
143
|
+
version: event.id.version + 1
|
|
144
|
+
})
|
|
145
|
+
})),
|
|
146
|
+
stuckEvents: retrieveResult.map((event) => ({
|
|
147
|
+
id: event.id,
|
|
148
|
+
hookName: event.hookName,
|
|
149
|
+
attempts: event.attempts,
|
|
150
|
+
maxAttempts: event.maxAttempts,
|
|
151
|
+
lastAttemptAt: event.lastAttemptAt,
|
|
152
|
+
nextRetryAt: event.nextRetryAt
|
|
153
|
+
}))
|
|
154
|
+
};
|
|
107
155
|
}).build();
|
|
108
156
|
},
|
|
109
|
-
getNextHookWakeAt(namespace, timeoutMinutes
|
|
110
|
-
const
|
|
111
|
-
const includeProcessing =
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
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;
|
|
157
|
+
getNextHookWakeAt(namespace, timeoutMinutes) {
|
|
158
|
+
const timeoutMinutesValue = typeof timeoutMinutes === "number" && timeoutMinutes > 0 ? timeoutMinutes : 0;
|
|
159
|
+
const includeProcessing = timeoutMinutesValue > 0;
|
|
160
|
+
const now = dbNow();
|
|
161
|
+
const timeoutMs = timeoutMinutesValue * 6e4;
|
|
162
|
+
const processingStatus = includeProcessing ? "processing" : "__disabled__";
|
|
163
|
+
const staleBefore = now.plus({ minutes: -timeoutMinutesValue });
|
|
164
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.forSchema(internalSchema).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)))).pageSize(1)).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"), eb.isNotNull("nextRetryAt"), eb("nextRetryAt", ">", now))).orderByIndex("idx_namespace_status_retry", "asc").pageSize(1).select(["nextRetryAt"])).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", processingStatus), eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)))).pageSize(1)).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", processingStatus), eb.isNotNull("lastAttemptAt"), eb("lastAttemptAt", ">", staleBefore))).orderByIndex("idx_namespace_status_last_attempt", "asc").pageSize(1).select(["lastAttemptAt"]))).transformRetrieve(([pendingImmediate, pendingNext, processingImmediate, processingNext]) => {
|
|
165
|
+
const hasProcessingImmediate = includeProcessing && processingImmediate.length > 0;
|
|
166
|
+
if (pendingImmediate.length > 0 || hasProcessingImmediate) return /* @__PURE__ */ new Date();
|
|
167
|
+
const pendingNextAt = pendingNext[0]?.nextRetryAt ?? null;
|
|
168
|
+
let processingNextAt = null;
|
|
169
|
+
if (includeProcessing) {
|
|
170
|
+
const lastAttemptAt = processingNext[0]?.lastAttemptAt;
|
|
171
|
+
if (lastAttemptAt) processingNextAt = new Date(lastAttemptAt.getTime() + timeoutMs);
|
|
139
172
|
}
|
|
140
|
-
if (!
|
|
141
|
-
if (!
|
|
142
|
-
return
|
|
173
|
+
if (!pendingNextAt) return processingNextAt ?? null;
|
|
174
|
+
if (!processingNextAt) return pendingNextAt;
|
|
175
|
+
return pendingNextAt <= processingNextAt ? pendingNextAt : processingNextAt;
|
|
143
176
|
}).build();
|
|
144
177
|
},
|
|
145
|
-
|
|
146
|
-
return this.serviceTx(internalSchema).mutate(({ uow }) => uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
147
|
-
status: "completed",
|
|
148
|
-
lastAttemptAt: dbNow()
|
|
149
|
-
}).check())).build();
|
|
150
|
-
},
|
|
151
|
-
markHookFailed(eventId, error, attempts, retryPolicy, now) {
|
|
178
|
+
markHookFailed(eventId, error, attempts, retryPolicy) {
|
|
152
179
|
const newAttempts = attempts + 1;
|
|
153
180
|
const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
|
|
181
|
+
const now = dbNow();
|
|
154
182
|
return this.serviceTx(internalSchema).mutate(({ uow }) => {
|
|
155
183
|
if (shouldRetry) {
|
|
156
184
|
const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
|
|
157
|
-
const
|
|
158
|
-
const nextRetryAt = new Date(baseNow.getTime() + delayMs);
|
|
185
|
+
const nextRetryAt = now.plus({ ms: delayMs });
|
|
159
186
|
uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
160
187
|
status: "pending",
|
|
161
188
|
attempts: newAttempts,
|
|
162
|
-
lastAttemptAt:
|
|
189
|
+
lastAttemptAt: now,
|
|
163
190
|
nextRetryAt,
|
|
164
191
|
error
|
|
165
192
|
}).check());
|
|
166
193
|
} else uow.update("fragno_hooks", eventId, (b) => b.set({
|
|
167
194
|
status: "failed",
|
|
168
195
|
attempts: newAttempts,
|
|
169
|
-
lastAttemptAt:
|
|
196
|
+
lastAttemptAt: now,
|
|
170
197
|
error
|
|
171
198
|
}).check());
|
|
172
199
|
}).build();
|
|
@@ -178,10 +205,30 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
|
|
|
178
205
|
}).check())).build();
|
|
179
206
|
},
|
|
180
207
|
getHookById(eventId) {
|
|
181
|
-
return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst("fragno_hooks", (b) => b.whereIndex("primary", (eb) => eb("id", "=", eventId)))).transformRetrieve(([result]) => result
|
|
208
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst("fragno_hooks", (b) => b.whereIndex("primary", (eb) => eb("id", "=", eventId)))).transformRetrieve(([result]) => result ? {
|
|
209
|
+
...result,
|
|
210
|
+
status: coerceHookStatus(result.status, describeHookStatusSource(result))
|
|
211
|
+
} : void 0).build();
|
|
212
|
+
},
|
|
213
|
+
getHooksByNamespacePage(namespace, options = {}) {
|
|
214
|
+
const pageSize = resolveHookPageSize(options.pageSize);
|
|
215
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.findWithCursor("fragno_hooks", (b) => {
|
|
216
|
+
const query = b.whereIndex("idx_namespace_created_at", (eb) => eb("namespace", "=", namespace)).orderByIndex("idx_namespace_created_at", "desc").pageSize(pageSize);
|
|
217
|
+
return options.cursor ? query.after(options.cursor) : query;
|
|
218
|
+
})).transformRetrieve(([page]) => ({
|
|
219
|
+
items: page.items.map((event) => ({
|
|
220
|
+
...event,
|
|
221
|
+
status: coerceHookStatus(event.status, describeHookStatusSource(event))
|
|
222
|
+
})),
|
|
223
|
+
cursor: page.cursor,
|
|
224
|
+
hasNextPage: page.hasNextPage
|
|
225
|
+
})).build();
|
|
182
226
|
},
|
|
183
227
|
getHooksByNamespace(namespace) {
|
|
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
|
|
228
|
+
return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb("namespace", "=", namespace)))).transformRetrieve(([events]) => events.map((event) => ({
|
|
229
|
+
...event,
|
|
230
|
+
status: coerceHookStatus(event.status, describeHookStatusSource(event))
|
|
231
|
+
}))).build();
|
|
185
232
|
}
|
|
186
233
|
});
|
|
187
234
|
}).providesService("outboxService", ({ defineService }) => {
|
|
@@ -226,5 +273,5 @@ async function getSchemaVersionFromDatabase(fragment, namespace) {
|
|
|
226
273
|
}
|
|
227
274
|
|
|
228
275
|
//#endregion
|
|
229
|
-
export { getSchemaVersionFromDatabase, internalFragmentDef };
|
|
276
|
+
export { SchemaRegistryCollisionError, getSchemaVersionFromDatabase, internalFragmentDef };
|
|
230
277
|
//# sourceMappingURL=internal-fragment.js.map
|
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
1
|
+
{"version":3,"file":"internal-fragment.js","names":["processingNextAt: Date | null"],"sources":["../../src/fragments/internal-fragment.ts"],"sourcesContent":["import { FragmentDefinitionBuilder } from \"@fragno-dev/core\";\nimport type { InstantiatedFragmentFromDefinition } from \"@fragno-dev/core\";\n\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 { isHookStatus, type HookStatus } from \"../hooks/hooks\";\nimport type { Cursor } from \"../query/cursor\";\nimport { dbNow, type DbNow } from \"../query/db-now\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { FragnoId } from \"../schema/create\";\nimport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\ntype AdapterRegistry = {\n listSchemas: () => Array<{\n name: string;\n namespace: string | null;\n version: number;\n tables: string[];\n }>;\n listOutboxFragments: () => Array<{ name: string; mountRoute: string }>;\n isOutboxEnabled: () => boolean;\n resolveSyncCommand: (\n fragmentName: string,\n schemaName: string,\n commandName: string,\n ) => { command: unknown; namespace: string | null } | undefined;\n};\n\nexport class SchemaRegistryCollisionError extends Error {\n readonly code = \"SCHEMA_REGISTRY_COLLISION\" as const;\n readonly namespaceKey: string;\n readonly existing: { name: string; namespace: string | null };\n readonly attempted: { name: string; namespace: string | null };\n\n constructor({\n namespaceKey,\n existing,\n attempted,\n }: {\n namespaceKey: string;\n existing: { name: string; namespace: string | null };\n attempted: { name: string; namespace: string | null };\n }) {\n super(\n `Schema namespace \"${namespaceKey}\" is already owned by \"${existing.name}\" (${existing.namespace ?? \"null\"}).`,\n );\n this.name = \"SchemaRegistryCollisionError\";\n this.namespaceKey = namespaceKey;\n this.existing = existing;\n this.attempted = attempted;\n }\n}\n\nexport type InternalFragmentConfig = {\n registry?: AdapterRegistry;\n};\n\nexport { internalSchema, SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME };\n\nconst INTERNAL_SCHEMA_MIN_VERSION = 4;\nif (internalSchema.version < INTERNAL_SCHEMA_MIN_VERSION) {\n // Keep the internal schema version monotonic after removing fragno_db_schemas.\n internalSchema.version = INTERNAL_SCHEMA_MIN_VERSION;\n}\n\nconst describeHookStatusSource = (event: { id: FragnoId; hookName: string }) =>\n `fragno_hooks id=${event.id} hook=${event.hookName}`;\n\nconst coerceHookStatus = (status: string, context: string): HookStatus => {\n if (isHookStatus(status)) {\n return status;\n }\n throw new Error(`Invalid hook status from database (${context}): ${status}`);\n};\n\nconst DEFAULT_HOOKS_PAGE_SIZE = 50;\n\nconst resolveHookPageSize = (pageSize?: number): number => {\n if (typeof pageSize !== \"number\" || !Number.isInteger(pageSize) || pageSize <= 0) {\n return DEFAULT_HOOKS_PAGE_SIZE;\n }\n return pageSize;\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 InternalFragmentConfig,\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies<typeof internalSchema>,\n {},\n {},\n {},\n {},\n DatabaseServiceContext<{}>,\n DatabaseHandlerContext,\n DatabaseRequestStorage\n >(\"$fragno-internal-fragment\"),\n internalSchema,\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 * Set a setting value only if it does not already exist.\n */\n setIfMissing(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 return;\n }\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\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 status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\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 status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\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 status: \"processing\" as const,\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 * Claim stale processing hook events for processing.\n * Returns ready events and marks them as processing in the same transaction.\n */\n claimStuckProcessingHookEvents(namespace: string, staleBefore: DbNow) {\n const now = dbNow();\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"processing\"),\n eb.or(eb.isNull(\"lastAttemptAt\"), eb(\"lastAttemptAt\", \"<=\", staleBefore)),\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 status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult.length === 0) {\n return;\n }\n\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: now, nextRetryAt: null }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) => {\n return {\n events: 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 stuckEvents: retrieveResult.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 })\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) {\n const timeoutMinutesValue =\n typeof timeoutMinutes === \"number\" && timeoutMinutes > 0 ? timeoutMinutes : 0;\n const includeProcessing = timeoutMinutesValue > 0;\n const now = dbNow();\n const timeoutMs = timeoutMinutesValue * 60_000;\n // Sentinel to keep query shape stable when processing checks are disabled.\n const processingStatus = includeProcessing ? \"processing\" : \"__disabled__\";\n const staleBefore = now.plus({ minutes: -timeoutMinutesValue });\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow\n .forSchema(internalSchema)\n .find(\"fragno_hooks\", (b) =>\n b\n .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 .pageSize(1),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.isNotNull(\"nextRetryAt\"),\n eb(\"nextRetryAt\", \">\", now),\n ),\n )\n .orderByIndex(\"idx_namespace_status_retry\", \"asc\")\n .pageSize(1)\n .select([\"nextRetryAt\"]),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", processingStatus),\n eb.or(eb.isNull(\"lastAttemptAt\"), eb(\"lastAttemptAt\", \"<=\", staleBefore)),\n ),\n )\n .pageSize(1),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", processingStatus),\n eb.isNotNull(\"lastAttemptAt\"),\n eb(\"lastAttemptAt\", \">\", staleBefore),\n ),\n )\n .orderByIndex(\"idx_namespace_status_last_attempt\", \"asc\")\n .pageSize(1)\n .select([\"lastAttemptAt\"]),\n ),\n )\n .transformRetrieve(\n ([pendingImmediate, pendingNext, processingImmediate, processingNext]) => {\n const hasProcessingImmediate = includeProcessing && processingImmediate.length > 0;\n\n if (pendingImmediate.length > 0 || hasProcessingImmediate) {\n return new Date();\n }\n\n const pendingNextAt = pendingNext[0]?.nextRetryAt ?? null;\n let processingNextAt: Date | null = null;\n\n if (includeProcessing) {\n const lastAttemptAt = processingNext[0]?.lastAttemptAt;\n if (lastAttemptAt) {\n processingNextAt = new Date(lastAttemptAt.getTime() + timeoutMs);\n }\n }\n\n if (!pendingNextAt) {\n return processingNextAt ?? null;\n }\n if (!processingNextAt) {\n return pendingNextAt;\n }\n return pendingNextAt <= processingNextAt ? pendingNextAt : processingNextAt;\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 const now = dbNow();\n\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => {\n if (shouldRetry) {\n const delayMs = retryPolicy.getDelayMs(newAttempts - 1);\n const nextRetryAt = now.plus({ ms: delayMs });\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"pending\",\n attempts: newAttempts,\n lastAttemptAt: now,\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: now,\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]) =>\n result\n ? {\n ...result,\n status: coerceHookStatus(result.status, describeHookStatusSource(result)),\n }\n : undefined,\n )\n .build();\n },\n\n /**\n * Get hook events for a namespace in newest-first order with pagination.\n */\n getHooksByNamespacePage(\n namespace: string,\n options: { cursor?: Cursor | string; pageSize?: number } = {},\n ) {\n const pageSize = resolveHookPageSize(options.pageSize);\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findWithCursor(\"fragno_hooks\", (b) => {\n const query = b\n .whereIndex(\"idx_namespace_created_at\", (eb) => eb(\"namespace\", \"=\", namespace))\n .orderByIndex(\"idx_namespace_created_at\", \"desc\")\n .pageSize(pageSize);\n\n return options.cursor ? query.after(options.cursor) : query;\n }),\n )\n .transformRetrieve(([page]) => ({\n items: page.items.map((event) => ({\n ...event,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n })),\n cursor: page.cursor,\n hasNextPage: page.hasNextPage,\n }))\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]) =>\n events.map((event) => ({\n ...event,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n })),\n )\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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"}
|
|
@@ -1,11 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SETTINGS_NAMESPACE, internalSchema } from "./internal-fragment.schema.js";
|
|
2
2
|
import { internalFragmentDef } from "./internal-fragment.js";
|
|
3
|
+
import { submitSyncRequest } from "../sync/submit.js";
|
|
4
|
+
import { defineRoutes } from "@fragno-dev/core";
|
|
3
5
|
|
|
4
6
|
//#region src/fragments/internal-fragment.routes.ts
|
|
5
|
-
const
|
|
7
|
+
const ADAPTER_IDENTITY_KEY = "adapter_identity";
|
|
8
|
+
const passthroughInputSchema = { "~standard": {
|
|
9
|
+
version: 1,
|
|
10
|
+
vendor: "fragno",
|
|
11
|
+
validate: async (value) => ({ value })
|
|
12
|
+
} };
|
|
13
|
+
const getOrCreateAdapterIdentity = async (handlerTx, services) => {
|
|
14
|
+
const readIdentity = async () => handlerTx().withServiceCalls(() => [services.settingsService.get(SETTINGS_NAMESPACE, ADAPTER_IDENTITY_KEY)]).transform(({ serviceResult: [result] }) => result?.value).execute();
|
|
15
|
+
try {
|
|
16
|
+
const existingIdentity = await readIdentity();
|
|
17
|
+
if (existingIdentity) return {
|
|
18
|
+
ok: true,
|
|
19
|
+
value: existingIdentity
|
|
20
|
+
};
|
|
21
|
+
const adapterIdentity = crypto.randomUUID();
|
|
22
|
+
try {
|
|
23
|
+
await handlerTx().withServiceCalls(() => [services.settingsService.setIfMissing(SETTINGS_NAMESPACE, ADAPTER_IDENTITY_KEY, adapterIdentity)]).execute();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const recoveredIdentity = await readIdentity();
|
|
26
|
+
if (recoveredIdentity) return {
|
|
27
|
+
ok: true,
|
|
28
|
+
value: recoveredIdentity
|
|
29
|
+
};
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
ok: true,
|
|
34
|
+
value: await readIdentity() ?? adapterIdentity
|
|
35
|
+
};
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: { error: {
|
|
40
|
+
code: "SETTINGS_UNAVAILABLE",
|
|
41
|
+
message: "Internal settings table is not available.",
|
|
42
|
+
detail: error instanceof Error ? error.message : void 0
|
|
43
|
+
} }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const createInternalFragmentDescribeRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, config, services }) => [defineRoute({
|
|
48
|
+
method: "GET",
|
|
49
|
+
path: "/",
|
|
50
|
+
handler: async function(_input, { json }) {
|
|
51
|
+
const registry = config.registry;
|
|
52
|
+
if (!registry) return json({ error: {
|
|
53
|
+
code: "REGISTRY_UNAVAILABLE",
|
|
54
|
+
message: "Adapter registry is not configured."
|
|
55
|
+
} }, { status: 500 });
|
|
56
|
+
const adapterIdentityResult = await getOrCreateAdapterIdentity(() => this.handlerTx(), services);
|
|
57
|
+
if (!adapterIdentityResult.ok) return json(adapterIdentityResult.error, { status: 500 });
|
|
58
|
+
const outboxEnabled = registry.isOutboxEnabled();
|
|
59
|
+
return json({
|
|
60
|
+
adapterIdentity: adapterIdentityResult.value,
|
|
61
|
+
fragments: outboxEnabled ? registry.listOutboxFragments() : [],
|
|
62
|
+
schemas: registry.listSchemas(),
|
|
63
|
+
routes: {
|
|
64
|
+
internal: "/_internal",
|
|
65
|
+
outbox: outboxEnabled ? "/_internal/outbox" : void 0
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
})]);
|
|
70
|
+
const createInternalFragmentOutboxRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, services, config }) => [defineRoute({
|
|
6
71
|
method: "GET",
|
|
7
72
|
path: "/outbox",
|
|
8
73
|
handler: async function(input, { json }) {
|
|
74
|
+
const registry = config.registry;
|
|
75
|
+
if (!registry || !registry.isOutboxEnabled()) return json({ error: {
|
|
76
|
+
code: "OUTBOX_UNAVAILABLE",
|
|
77
|
+
message: "Outbox is not enabled for this adapter."
|
|
78
|
+
} }, { status: 404 });
|
|
9
79
|
const afterVersionstamp = input.query.get("afterVersionstamp") ?? void 0;
|
|
10
80
|
const limitValue = input.query.get("limit");
|
|
11
81
|
let limit;
|
|
@@ -23,7 +93,72 @@ const internalFragmentRoutes = defineRoutes(internalFragmentDef).create(({ defin
|
|
|
23
93
|
})]).transform(({ serviceResult: [result] }) => result).execute());
|
|
24
94
|
}
|
|
25
95
|
})]);
|
|
96
|
+
const createInternalFragmentSyncRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, services, config }) => [defineRoute({
|
|
97
|
+
method: "POST",
|
|
98
|
+
path: "/sync",
|
|
99
|
+
inputSchema: passthroughInputSchema,
|
|
100
|
+
handler: async function(input, { json }) {
|
|
101
|
+
const registry = config.registry;
|
|
102
|
+
if (!registry || !registry.isOutboxEnabled()) return json({ error: {
|
|
103
|
+
code: "SYNC_UNAVAILABLE",
|
|
104
|
+
message: "Sync is not enabled for this adapter."
|
|
105
|
+
} }, { status: 404 });
|
|
106
|
+
const adapterIdentityResult = await getOrCreateAdapterIdentity(() => this.handlerTx(), services);
|
|
107
|
+
if (!adapterIdentityResult.ok) return json(adapterIdentityResult.error, { status: 500 });
|
|
108
|
+
const result = await submitSyncRequest(await input.input?.valid(), {
|
|
109
|
+
getAdapterIdentity: async () => adapterIdentityResult.value,
|
|
110
|
+
listOutboxEntries: async (afterVersionstamp) => await this.handlerTx().withServiceCalls(() => [services.outboxService.list({
|
|
111
|
+
afterVersionstamp,
|
|
112
|
+
limit: void 0
|
|
113
|
+
})]).transform(({ serviceResult: [entries] }) => entries).execute(),
|
|
114
|
+
countOutboxMutations: async (afterVersionstamp) => {
|
|
115
|
+
return await this.handlerTx().retrieve(({ forSchema }) => {
|
|
116
|
+
return afterVersionstamp ? forSchema(internalSchema).find("fragno_db_outbox_mutations", (b) => b.whereIndex("idx_outbox_mutations_entry", (eb) => eb("entryVersionstamp", ">", afterVersionstamp)).selectCount()) : forSchema(internalSchema).find("fragno_db_outbox_mutations", (b) => b.whereIndex("idx_outbox_mutations_entry").selectCount());
|
|
117
|
+
}).transformRetrieve(([result$1]) => typeof result$1 === "number" ? result$1 : 0).execute();
|
|
118
|
+
},
|
|
119
|
+
getSyncRequest: async (requestId) => await this.handlerTx().retrieve(({ forSchema }) => forSchema(internalSchema).findFirst("fragno_db_sync_requests", (b) => b.whereIndex("idx_sync_request_id", (eb) => eb("requestId", "=", requestId)))).transformRetrieve(([result$1]) => {
|
|
120
|
+
if (!result$1) return;
|
|
121
|
+
const confirmed = Array.isArray(result$1.confirmedCommandIds) ? result$1.confirmedCommandIds : [];
|
|
122
|
+
const status = result$1.status === "applied" ? "applied" : "conflict";
|
|
123
|
+
return {
|
|
124
|
+
requestId: result$1.requestId,
|
|
125
|
+
status,
|
|
126
|
+
confirmedCommandIds: confirmed,
|
|
127
|
+
conflictCommandId: result$1.conflictCommandId ?? void 0,
|
|
128
|
+
baseVersionstamp: result$1.baseVersionstamp ?? void 0,
|
|
129
|
+
lastVersionstamp: result$1.lastVersionstamp ?? void 0
|
|
130
|
+
};
|
|
131
|
+
}).execute(),
|
|
132
|
+
storeSyncRequest: async (record) => {
|
|
133
|
+
await this.handlerTx().mutate(({ forSchema }) => {
|
|
134
|
+
forSchema(internalSchema).create("fragno_db_sync_requests", {
|
|
135
|
+
requestId: record.requestId,
|
|
136
|
+
status: record.status,
|
|
137
|
+
confirmedCommandIds: record.confirmedCommandIds,
|
|
138
|
+
conflictCommandId: record.conflictCommandId ?? null,
|
|
139
|
+
baseVersionstamp: record.baseVersionstamp ?? null,
|
|
140
|
+
lastVersionstamp: record.lastVersionstamp ?? null
|
|
141
|
+
});
|
|
142
|
+
}).execute();
|
|
143
|
+
},
|
|
144
|
+
resolveCommand: (fragment, schema, name) => registry.resolveSyncCommand(fragment, schema, name),
|
|
145
|
+
createCommandContext: (command) => command.createServerContext?.(this) ?? { mode: "server" },
|
|
146
|
+
executeCommand: async (command, inputPayload, ctx) => {
|
|
147
|
+
await command.handler({
|
|
148
|
+
input: inputPayload,
|
|
149
|
+
ctx,
|
|
150
|
+
tx: (options) => this.handlerTx(options)
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (result.status === "error") {
|
|
155
|
+
const statusCode = result.statusCode;
|
|
156
|
+
return json(result.body, { status: statusCode });
|
|
157
|
+
}
|
|
158
|
+
return json(result.response);
|
|
159
|
+
}
|
|
160
|
+
})]);
|
|
26
161
|
|
|
27
162
|
//#endregion
|
|
28
|
-
export {
|
|
163
|
+
export { createInternalFragmentDescribeRoutes, createInternalFragmentOutboxRoutes, createInternalFragmentSyncRoutes };
|
|
29
164
|
//# sourceMappingURL=internal-fragment.routes.js.map
|