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