effect 4.0.0-rc.112 → 4.0.0-rc.113
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/AGENTS.md +24 -9
- package/CLAUDE.md +24 -9
- package/ai-docs/package.json +2 -2
- package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +18 -5
- package/ai-docs/src/01_effect/01_basics/index.md +5 -3
- package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +1 -1
- package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +2 -2
- package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +2 -2
- package/ai-docs/src/03_stream/30_encoding.ts +5 -7
- package/ai-docs/src/08_observability/10_logging.ts +1 -1
- package/ai-docs/src/70_cli/10_basics.ts +7 -7
- package/ai-docs/src/71_ai/10_language-model.ts +2 -2
- package/ai-docs/src/71_ai/20_tools.ts +1 -1
- package/ai-docs/src/71_ai/30_chat.ts +1 -1
- package/dist/Array.d.ts +86 -50
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +58 -32
- package/dist/Array.js.map +1 -1
- package/dist/BigDecimal.d.ts +5 -1
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +40 -23
- package/dist/BigDecimal.js.map +1 -1
- package/dist/ByteSize.d.ts +633 -0
- package/dist/ByteSize.d.ts.map +1 -0
- package/dist/ByteSize.js +524 -0
- package/dist/ByteSize.js.map +1 -0
- package/dist/Cache.d.ts.map +1 -1
- package/dist/Cache.js +17 -3
- package/dist/Cache.js.map +1 -1
- package/dist/Cause.d.ts +1 -1
- package/dist/Cause.d.ts.map +1 -1
- package/dist/Channel.d.ts +97 -24
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +66 -39
- package/dist/Channel.js.map +1 -1
- package/dist/Chunk.d.ts +49 -28
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Chunk.js +27 -18
- package/dist/Chunk.js.map +1 -1
- package/dist/Config.d.ts +162 -201
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +103 -212
- package/dist/Config.js.map +1 -1
- package/dist/ConfigProvider.d.ts +4 -4
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +7 -7
- package/dist/ConfigProvider.js.map +1 -1
- package/dist/Context.d.ts +31 -0
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js.map +1 -1
- package/dist/Cron.d.ts +2 -2
- package/dist/Cron.d.ts.map +1 -1
- package/dist/Cron.js +2 -2
- package/dist/Cron.js.map +1 -1
- package/dist/Crypto.d.ts +1 -1
- package/dist/Crypto.d.ts.map +1 -1
- package/dist/Crypto.js +3 -2
- package/dist/Crypto.js.map +1 -1
- package/dist/DateTime.d.ts +2 -2
- package/dist/DateTime.d.ts.map +1 -1
- package/dist/Deferred.d.ts.map +1 -1
- package/dist/Deferred.js +6 -6
- package/dist/Deferred.js.map +1 -1
- package/dist/Duration.d.ts +1 -1
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +1 -1
- package/dist/Duration.js.map +1 -1
- package/dist/Effect.d.ts +220 -113
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +27 -26
- package/dist/Effect.js.map +1 -1
- package/dist/Effectable.d.ts +58 -2
- package/dist/Effectable.d.ts.map +1 -1
- package/dist/Effectable.js +58 -6
- package/dist/Effectable.js.map +1 -1
- package/dist/Encoding.d.ts +3 -1
- package/dist/Encoding.d.ts.map +1 -1
- package/dist/Encoding.js +40 -10
- package/dist/Encoding.js.map +1 -1
- package/dist/Equal.d.ts +1 -1
- package/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +1 -1
- package/dist/Equal.js.map +1 -1
- package/dist/ExecutionPlan.d.ts.map +1 -1
- package/dist/ExecutionPlan.js +1 -0
- package/dist/ExecutionPlan.js.map +1 -1
- package/dist/Fiber.d.ts +32 -8
- package/dist/Fiber.d.ts.map +1 -1
- package/dist/Fiber.js.map +1 -1
- package/dist/FiberHandle.js +3 -3
- package/dist/FiberHandle.js.map +1 -1
- package/dist/FiberMap.d.ts +12 -6
- package/dist/FiberMap.d.ts.map +1 -1
- package/dist/FiberMap.js +11 -6
- package/dist/FiberMap.js.map +1 -1
- package/dist/FileSystem.d.ts +42 -210
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js +16 -148
- package/dist/FileSystem.js.map +1 -1
- package/dist/Formatter.d.ts +1 -0
- package/dist/Formatter.d.ts.map +1 -1
- package/dist/Formatter.js +19 -3
- package/dist/Formatter.js.map +1 -1
- package/dist/Graph.d.ts +48 -20
- package/dist/Graph.d.ts.map +1 -1
- package/dist/Graph.js +20 -5
- package/dist/Graph.js.map +1 -1
- package/dist/Hash.d.ts +1 -1
- package/dist/Hash.d.ts.map +1 -1
- package/dist/Hash.js +5 -14
- package/dist/Hash.js.map +1 -1
- package/dist/HashMap.d.ts +1 -1
- package/dist/HashMap.d.ts.map +1 -1
- package/dist/HashRing.d.ts +6 -1
- package/dist/HashRing.d.ts.map +1 -1
- package/dist/HashRing.js +9 -2
- package/dist/HashRing.js.map +1 -1
- package/dist/HashSet.d.ts +1 -1
- package/dist/HashSet.d.ts.map +1 -1
- package/dist/HashSet.js.map +1 -1
- package/dist/Iterable.d.ts +33 -15
- package/dist/Iterable.d.ts.map +1 -1
- package/dist/Iterable.js +58 -45
- package/dist/Iterable.js.map +1 -1
- package/dist/JsonPatch.js +4 -4
- package/dist/JsonPatch.js.map +1 -1
- package/dist/JsonPointer.d.ts +68 -4
- package/dist/JsonPointer.d.ts.map +1 -1
- package/dist/JsonPointer.js +90 -4
- package/dist/JsonPointer.js.map +1 -1
- package/dist/JsonSchema.js +8 -22
- package/dist/JsonSchema.js.map +1 -1
- package/dist/Layer.d.ts +47 -4
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +4 -6
- package/dist/Layer.js.map +1 -1
- package/dist/LayerMap.d.ts +1 -3
- package/dist/LayerMap.d.ts.map +1 -1
- package/dist/LayerMap.js +7 -5
- package/dist/LayerMap.js.map +1 -1
- package/dist/LayerRef.d.ts.map +1 -1
- package/dist/LayerRef.js +7 -5
- package/dist/LayerRef.js.map +1 -1
- package/dist/Logger.d.ts +114 -9
- package/dist/Logger.d.ts.map +1 -1
- package/dist/Logger.js +109 -9
- package/dist/Logger.js.map +1 -1
- package/dist/ManagedRuntime.js +1 -1
- package/dist/ManagedRuntime.js.map +1 -1
- package/dist/Match.d.ts +43 -31
- package/dist/Match.d.ts.map +1 -1
- package/dist/Match.js +5 -2
- package/dist/Match.js.map +1 -1
- package/dist/Metric.d.ts +11 -4
- package/dist/Metric.d.ts.map +1 -1
- package/dist/Metric.js +14 -18
- package/dist/Metric.js.map +1 -1
- package/dist/MutableHashMap.d.ts +1 -1
- package/dist/MutableHashMap.d.ts.map +1 -1
- package/dist/MutableHashMap.js +1 -1
- package/dist/MutableHashMap.js.map +1 -1
- package/dist/MutableHashSet.d.ts +1 -1
- package/dist/MutableHashSet.d.ts.map +1 -1
- package/dist/MutableHashSet.js +1 -1
- package/dist/MutableHashSet.js.map +1 -1
- package/dist/MutableList.d.ts +13 -2
- package/dist/MutableList.d.ts.map +1 -1
- package/dist/MutableList.js +18 -2
- package/dist/MutableList.js.map +1 -1
- package/dist/Number.d.ts +6 -0
- package/dist/Number.d.ts.map +1 -1
- package/dist/Number.js +3 -1
- package/dist/Number.js.map +1 -1
- package/dist/Optic.d.ts.map +1 -1
- package/dist/Optic.js +5 -4
- package/dist/Optic.js.map +1 -1
- package/dist/Option.d.ts +1 -1
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +1 -1
- package/dist/Option.js.map +1 -1
- package/dist/Order.d.ts +1 -0
- package/dist/Order.d.ts.map +1 -1
- package/dist/Order.js +3 -1
- package/dist/Order.js.map +1 -1
- package/dist/PartitionedSemaphore.d.ts.map +1 -1
- package/dist/PartitionedSemaphore.js +1 -2
- package/dist/PartitionedSemaphore.js.map +1 -1
- package/dist/Path.d.ts +1 -1
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +1 -1
- package/dist/Path.js.map +1 -1
- package/dist/PlatformError.d.ts +1 -1
- package/dist/PlatformError.d.ts.map +1 -1
- package/dist/PlatformError.js +1 -1
- package/dist/PlatformError.js.map +1 -1
- package/dist/Pool.d.ts +38 -1
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +81 -5
- package/dist/Pool.js.map +1 -1
- package/dist/PrimaryKey.d.ts +1 -1
- package/dist/PrimaryKey.d.ts.map +1 -1
- package/dist/PrimaryKey.js +1 -1
- package/dist/PrimaryKey.js.map +1 -1
- package/dist/PubSub.d.ts +21 -9
- package/dist/PubSub.d.ts.map +1 -1
- package/dist/PubSub.js +18 -14
- package/dist/PubSub.js.map +1 -1
- package/dist/Queue.d.ts +82 -6
- package/dist/Queue.d.ts.map +1 -1
- package/dist/Queue.js +116 -27
- package/dist/Queue.js.map +1 -1
- package/dist/Random.d.ts +17 -4
- package/dist/Random.d.ts.map +1 -1
- package/dist/Random.js +1 -1
- package/dist/Random.js.map +1 -1
- package/dist/RcMap.d.ts.map +1 -1
- package/dist/RcMap.js +15 -9
- package/dist/RcMap.js.map +1 -1
- package/dist/Redacted.d.ts +1 -1
- package/dist/Redacted.d.ts.map +1 -1
- package/dist/Redacted.js +1 -1
- package/dist/Redacted.js.map +1 -1
- package/dist/RequestResolver.d.ts +15 -9
- package/dist/RequestResolver.d.ts.map +1 -1
- package/dist/RequestResolver.js +33 -15
- package/dist/RequestResolver.js.map +1 -1
- package/dist/Result.d.ts +1 -1
- package/dist/Result.d.ts.map +1 -1
- package/dist/Result.js +1 -1
- package/dist/Result.js.map +1 -1
- package/dist/Scheduler.d.ts.map +1 -1
- package/dist/Scheduler.js +19 -10
- package/dist/Scheduler.js.map +1 -1
- package/dist/Schema.d.ts +2155 -2415
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2755 -4174
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +354 -183
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +277 -601
- package/dist/SchemaAST.js.map +1 -1
- package/dist/SchemaGetter.d.ts +46 -22
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +56 -53
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/SchemaIssue.d.ts +122 -188
- package/dist/SchemaIssue.d.ts.map +1 -1
- package/dist/SchemaIssue.js +48 -263
- package/dist/SchemaIssue.js.map +1 -1
- package/dist/SchemaParser.js +2 -9
- package/dist/SchemaParser.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +1099 -16
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +1341 -20
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +49 -16
- package/dist/SchemaTransformation.d.ts.map +1 -1
- package/dist/SchemaTransformation.js +75 -98
- package/dist/SchemaTransformation.js.map +1 -1
- package/dist/ScopedCache.d.ts.map +1 -1
- package/dist/ScopedCache.js +10 -3
- package/dist/ScopedCache.js.map +1 -1
- package/dist/Sink.d.ts +9 -4
- package/dist/Sink.d.ts.map +1 -1
- package/dist/Sink.js +49 -31
- package/dist/Sink.js.map +1 -1
- package/dist/Stream.d.ts +221 -35
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +168 -84
- package/dist/Stream.js.map +1 -1
- package/dist/String.d.ts +16 -6
- package/dist/String.d.ts.map +1 -1
- package/dist/String.js +12 -5
- package/dist/String.js.map +1 -1
- package/dist/Struct.d.ts.map +1 -1
- package/dist/Struct.js +7 -4
- package/dist/Struct.js.map +1 -1
- package/dist/SynchronizedRef.d.ts +3 -2
- package/dist/SynchronizedRef.d.ts.map +1 -1
- package/dist/SynchronizedRef.js.map +1 -1
- package/dist/Terminal.d.ts +3 -3
- package/dist/Terminal.d.ts.map +1 -1
- package/dist/Terminal.js +3 -3
- package/dist/Terminal.js.map +1 -1
- package/dist/Tracer.d.ts +27 -7
- package/dist/Tracer.d.ts.map +1 -1
- package/dist/Tracer.js +38 -13
- package/dist/Tracer.js.map +1 -1
- package/dist/Trie.d.ts +1 -1
- package/dist/Trie.d.ts.map +1 -1
- package/dist/Tuple.d.ts +2 -1
- package/dist/Tuple.d.ts.map +1 -1
- package/dist/Tuple.js.map +1 -1
- package/dist/TxChunk.d.ts +1 -1
- package/dist/TxChunk.d.ts.map +1 -1
- package/dist/TxChunk.js +1 -1
- package/dist/TxChunk.js.map +1 -1
- package/dist/TxDeferred.d.ts +1 -1
- package/dist/TxDeferred.d.ts.map +1 -1
- package/dist/TxDeferred.js +1 -1
- package/dist/TxDeferred.js.map +1 -1
- package/dist/TxHashMap.d.ts +1 -1
- package/dist/TxHashMap.d.ts.map +1 -1
- package/dist/TxHashMap.js +1 -1
- package/dist/TxHashMap.js.map +1 -1
- package/dist/TxHashSet.d.ts +1 -1
- package/dist/TxHashSet.d.ts.map +1 -1
- package/dist/TxHashSet.js +1 -1
- package/dist/TxHashSet.js.map +1 -1
- package/dist/TxPriorityQueue.d.ts +1 -1
- package/dist/TxPriorityQueue.d.ts.map +1 -1
- package/dist/TxPriorityQueue.js +1 -1
- package/dist/TxPriorityQueue.js.map +1 -1
- package/dist/TxPubSub.d.ts +1 -1
- package/dist/TxPubSub.d.ts.map +1 -1
- package/dist/TxPubSub.js +1 -1
- package/dist/TxPubSub.js.map +1 -1
- package/dist/TxQueue.d.ts +9 -9
- package/dist/TxQueue.d.ts.map +1 -1
- package/dist/TxQueue.js +93 -86
- package/dist/TxQueue.js.map +1 -1
- package/dist/TxReentrantLock.d.ts +29 -1
- package/dist/TxReentrantLock.d.ts.map +1 -1
- package/dist/TxReentrantLock.js +29 -1
- package/dist/TxReentrantLock.js.map +1 -1
- package/dist/TxRef.d.ts +1 -1
- package/dist/TxRef.d.ts.map +1 -1
- package/dist/TxRef.js +1 -1
- package/dist/TxRef.js.map +1 -1
- package/dist/TxSemaphore.d.ts +1 -1
- package/dist/TxSemaphore.d.ts.map +1 -1
- package/dist/TxSemaphore.js +1 -1
- package/dist/TxSemaphore.js.map +1 -1
- package/dist/TxSubscriptionRef.d.ts +1 -1
- package/dist/TxSubscriptionRef.d.ts.map +1 -1
- package/dist/TxSubscriptionRef.js +1 -1
- package/dist/TxSubscriptionRef.js.map +1 -1
- package/dist/Types.d.ts +33 -19
- package/dist/Types.d.ts.map +1 -1
- package/dist/Utils.d.ts.map +1 -1
- package/dist/Utils.js +2 -1
- package/dist/Utils.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/arbitrary/model.d.ts +2 -0
- package/dist/internal/arbitrary/model.d.ts.map +1 -0
- package/dist/internal/arbitrary/model.js +495 -0
- package/dist/internal/arbitrary/model.js.map +1 -0
- package/dist/internal/arbitrary/regexp.d.ts +2 -0
- package/dist/internal/arbitrary/regexp.d.ts.map +1 -0
- package/dist/internal/arbitrary/regexp.js +789 -0
- package/dist/internal/arbitrary/regexp.js.map +1 -0
- package/dist/internal/arbitrary/runner.d.ts +2 -0
- package/dist/internal/arbitrary/runner.d.ts.map +1 -0
- package/dist/internal/arbitrary/runner.js +496 -0
- package/dist/internal/arbitrary/runner.js.map +1 -0
- package/dist/internal/arbitrary/schema.d.ts.map +1 -0
- package/dist/internal/arbitrary/schema.js +1340 -0
- package/dist/internal/arbitrary/schema.js.map +1 -0
- package/dist/internal/core.js +27 -13
- package/dist/internal/core.js.map +1 -1
- package/dist/internal/count.d.ts +2 -0
- package/dist/internal/count.d.ts.map +1 -0
- package/dist/internal/count.js +14 -0
- package/dist/internal/count.js.map +1 -0
- package/dist/internal/dateTime.js +4 -10
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +386 -251
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/graph.js +3 -1
- package/dist/internal/graph.js.map +1 -1
- package/dist/internal/hashMap.js +5 -3
- package/dist/internal/hashMap.js.map +1 -1
- package/dist/internal/hashSet.js +1 -1
- package/dist/internal/hashSet.js.map +1 -1
- package/dist/internal/matcher.js +1 -1
- package/dist/internal/matcher.js.map +1 -1
- package/dist/internal/metric.js +1 -1
- package/dist/internal/metric.js.map +1 -1
- package/dist/internal/option.js +6 -5
- package/dist/internal/option.js.map +1 -1
- package/dist/internal/random.js +17 -0
- package/dist/internal/random.js.map +1 -1
- package/dist/internal/rcRef.js +10 -7
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/references.js +1 -0
- package/dist/internal/references.js.map +1 -1
- package/dist/internal/request.js +13 -6
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/result.js +11 -9
- package/dist/internal/result.js.map +1 -1
- package/dist/internal/schema/annotations.js +1 -1
- package/dist/internal/schema/annotations.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +14 -12
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/fromRepresentation.js +5 -5
- package/dist/internal/schema/fromRepresentation.js.map +1 -1
- package/dist/internal/schema/make.d.ts +2 -0
- package/dist/internal/schema/make.d.ts.map +1 -0
- package/dist/internal/schema/{schema.js → make.js} +1 -25
- package/dist/internal/schema/make.js.map +1 -0
- package/dist/internal/schema/parser.d.ts +0 -12
- package/dist/internal/schema/parser.d.ts.map +1 -1
- package/dist/internal/schema/parser.js +7 -0
- package/dist/internal/schema/parser.js.map +1 -1
- package/dist/internal/schema/standardSchema.d.ts +2 -0
- package/dist/internal/schema/standardSchema.d.ts.map +1 -0
- package/dist/internal/schema/standardSchema.js +112 -0
- package/dist/internal/schema/standardSchema.js.map +1 -0
- package/dist/internal/schema/toCodeDocument.js +34 -22
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/internal/schema/toCodec.d.ts +2 -0
- package/dist/internal/schema/toCodec.d.ts.map +1 -0
- package/dist/internal/schema/toCodec.js +242 -0
- package/dist/internal/schema/toCodec.js.map +1 -0
- package/dist/internal/schema/toDifferJsonPatch.d.ts +2 -0
- package/dist/internal/schema/toDifferJsonPatch.d.ts.map +1 -0
- package/dist/internal/schema/toDifferJsonPatch.js +20 -0
- package/dist/internal/schema/toDifferJsonPatch.js.map +1 -0
- package/dist/internal/schema/toEncoderXml.d.ts +2 -0
- package/dist/internal/schema/toEncoderXml.d.ts.map +1 -0
- package/dist/internal/schema/toEncoderXml.js +104 -0
- package/dist/internal/schema/toEncoderXml.js.map +1 -0
- package/dist/internal/schema/toEquivalence.js +66 -0
- package/dist/internal/schema/toEquivalence.js.map +1 -1
- package/dist/internal/schema/toFormatter.d.ts +2 -0
- package/dist/internal/schema/toFormatter.d.ts.map +1 -0
- package/dist/internal/schema/toFormatter.js +227 -0
- package/dist/internal/schema/toFormatter.js.map +1 -0
- package/dist/internal/schema/toIso.d.ts +2 -0
- package/dist/internal/schema/toIso.d.ts.map +1 -0
- package/dist/internal/schema/toIso.js +17 -0
- package/dist/internal/schema/toIso.js.map +1 -0
- package/dist/internal/schema/toJsonSchemaDocument.js +122 -71
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toRepresentation.js +3 -1
- package/dist/internal/schema/toRepresentation.js.map +1 -1
- package/dist/internal/schemaError.d.ts +4 -0
- package/dist/internal/schemaError.d.ts.map +1 -0
- package/dist/internal/schemaError.js +6 -0
- package/dist/internal/schemaError.js.map +1 -0
- package/dist/internal/stream.js +5 -6
- package/dist/internal/stream.js.map +1 -1
- package/dist/internal/tracer.d.ts +1 -3
- package/dist/internal/tracer.d.ts.map +1 -1
- package/dist/internal/tracer.js +6 -0
- package/dist/internal/tracer.js.map +1 -1
- package/dist/internal/trie.js +2 -2
- package/dist/internal/trie.js.map +1 -1
- package/dist/internal/uuid.d.ts +1 -2
- package/dist/internal/uuid.d.ts.map +1 -1
- package/dist/internal/uuid.js +2 -0
- package/dist/internal/uuid.js.map +1 -1
- package/dist/testing/TestSchema.d.ts +9 -11
- package/dist/testing/TestSchema.d.ts.map +1 -1
- package/dist/testing/TestSchema.js +20 -18
- package/dist/testing/TestSchema.js.map +1 -1
- package/dist/testing/index.d.ts +0 -4
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +0 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/unstable/ai/AiError.d.ts +74 -23
- package/dist/unstable/ai/AiError.d.ts.map +1 -1
- package/dist/unstable/ai/AiError.js +59 -5
- package/dist/unstable/ai/AiError.js.map +1 -1
- package/dist/unstable/ai/AnthropicStructuredOutput.d.ts.map +1 -1
- package/dist/unstable/ai/AnthropicStructuredOutput.js +2 -1
- package/dist/unstable/ai/AnthropicStructuredOutput.js.map +1 -1
- package/dist/unstable/ai/Chat.d.ts +27 -32
- package/dist/unstable/ai/Chat.d.ts.map +1 -1
- package/dist/unstable/ai/Chat.js +10 -13
- package/dist/unstable/ai/Chat.js.map +1 -1
- package/dist/unstable/ai/EmbeddingModel.d.ts +23 -16
- package/dist/unstable/ai/EmbeddingModel.d.ts.map +1 -1
- package/dist/unstable/ai/EmbeddingModel.js +13 -11
- package/dist/unstable/ai/EmbeddingModel.js.map +1 -1
- package/dist/unstable/ai/LanguageModel.d.ts +48 -37
- package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
- package/dist/unstable/ai/LanguageModel.js +150 -47
- package/dist/unstable/ai/LanguageModel.js.map +1 -1
- package/dist/unstable/ai/McpServer.d.ts +2 -2
- package/dist/unstable/ai/McpServer.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.js +21 -12
- package/dist/unstable/ai/McpServer.js.map +1 -1
- package/dist/unstable/ai/OpenAiStructuredOutput.d.ts.map +1 -1
- package/dist/unstable/ai/OpenAiStructuredOutput.js +2 -1
- package/dist/unstable/ai/OpenAiStructuredOutput.js.map +1 -1
- package/dist/unstable/ai/Prompt.d.ts +1 -1
- package/dist/unstable/ai/Prompt.d.ts.map +1 -1
- package/dist/unstable/ai/Prompt.js +15 -4
- package/dist/unstable/ai/Prompt.js.map +1 -1
- package/dist/unstable/ai/Response.d.ts +37 -94
- package/dist/unstable/ai/Response.d.ts.map +1 -1
- package/dist/unstable/ai/Response.js +46 -96
- package/dist/unstable/ai/Response.js.map +1 -1
- package/dist/unstable/ai/Tokenizer.js +4 -5
- package/dist/unstable/ai/Tokenizer.js.map +1 -1
- package/dist/unstable/ai/Tool.d.ts +38 -20
- package/dist/unstable/ai/Tool.d.ts.map +1 -1
- package/dist/unstable/ai/Tool.js +27 -1
- package/dist/unstable/ai/Tool.js.map +1 -1
- package/dist/unstable/ai/Toolkit.d.ts +1 -1
- package/dist/unstable/ai/Toolkit.d.ts.map +1 -1
- package/dist/unstable/ai/Toolkit.js +30 -18
- package/dist/unstable/ai/Toolkit.js.map +1 -1
- package/dist/unstable/ai/internal/structured-output.js +8 -5
- package/dist/unstable/ai/internal/structured-output.js.map +1 -1
- package/dist/unstable/arbitrary/Arbitrary.d.ts +626 -0
- package/dist/unstable/arbitrary/Arbitrary.d.ts.map +1 -0
- package/dist/unstable/arbitrary/Arbitrary.js +238 -0
- package/dist/unstable/arbitrary/Arbitrary.js.map +1 -0
- package/dist/unstable/arbitrary/index.d.ts +8 -0
- package/dist/unstable/arbitrary/index.d.ts.map +1 -0
- package/dist/unstable/arbitrary/index.js +9 -0
- package/dist/unstable/arbitrary/index.js.map +1 -0
- package/dist/unstable/cli/Argument.d.ts +104 -108
- package/dist/unstable/cli/Argument.d.ts.map +1 -1
- package/dist/unstable/cli/Argument.js +56 -57
- package/dist/unstable/cli/Argument.js.map +1 -1
- package/dist/unstable/cli/CliError.d.ts +2 -0
- package/dist/unstable/cli/CliError.d.ts.map +1 -1
- package/dist/unstable/cli/CliError.js +2 -0
- package/dist/unstable/cli/CliError.js.map +1 -1
- package/dist/unstable/cli/CliOutput.js +26 -3
- package/dist/unstable/cli/CliOutput.js.map +1 -1
- package/dist/unstable/cli/Command.d.ts +73 -73
- package/dist/unstable/cli/Command.js +18 -18
- package/dist/unstable/cli/Completions.d.ts +4 -4
- package/dist/unstable/cli/Completions.d.ts.map +1 -1
- package/dist/unstable/cli/Flag.d.ts +150 -161
- package/dist/unstable/cli/Flag.d.ts.map +1 -1
- package/dist/unstable/cli/Flag.js +79 -90
- package/dist/unstable/cli/Flag.js.map +1 -1
- package/dist/unstable/cli/GlobalFlag.d.ts +3 -3
- package/dist/unstable/cli/GlobalFlag.js +13 -13
- package/dist/unstable/cli/GlobalFlag.js.map +1 -1
- package/dist/unstable/cli/Param.d.ts +118 -126
- package/dist/unstable/cli/Param.d.ts.map +1 -1
- package/dist/unstable/cli/Param.js +104 -113
- package/dist/unstable/cli/Param.js.map +1 -1
- package/dist/unstable/cli/Primitive.d.ts +69 -58
- package/dist/unstable/cli/Primitive.d.ts.map +1 -1
- package/dist/unstable/cli/Primitive.js +65 -66
- package/dist/unstable/cli/Primitive.js.map +1 -1
- package/dist/unstable/cli/Prompt.d.ts +23 -24
- package/dist/unstable/cli/Prompt.d.ts.map +1 -1
- package/dist/unstable/cli/Prompt.js +68 -63
- package/dist/unstable/cli/Prompt.js.map +1 -1
- package/dist/unstable/cli/internal/completions/descriptor.js +23 -11
- package/dist/unstable/cli/internal/completions/descriptor.js.map +1 -1
- package/dist/unstable/cli/internal/completions/zsh.js +2 -2
- package/dist/unstable/cli/internal/completions/zsh.js.map +1 -1
- package/dist/unstable/cli/internal/config.js +4 -4
- package/dist/unstable/cli/internal/parser.js +1 -1
- package/dist/unstable/cli/internal/parser.js.map +1 -1
- package/dist/unstable/cli/internal/wizard.d.ts.map +1 -1
- package/dist/unstable/cli/internal/wizard.js +14 -13
- package/dist/unstable/cli/internal/wizard.js.map +1 -1
- package/dist/unstable/cluster/ClusterSchema.d.ts +13 -0
- package/dist/unstable/cluster/ClusterSchema.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterSchema.js +16 -1
- package/dist/unstable/cluster/ClusterSchema.js.map +1 -1
- package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterWorkflowEngine.js +57 -14
- package/dist/unstable/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/unstable/cluster/Entity.d.ts.map +1 -1
- package/dist/unstable/cluster/Entity.js +2 -0
- package/dist/unstable/cluster/Entity.js.map +1 -1
- package/dist/unstable/cluster/EntityProxyServer.d.ts +2 -2
- package/dist/unstable/cluster/EntityProxyServer.d.ts.map +1 -1
- package/dist/unstable/cluster/EntityProxyServer.js.map +1 -1
- package/dist/unstable/cluster/Envelope.d.ts +4 -4
- package/dist/unstable/cluster/Envelope.d.ts.map +1 -1
- package/dist/unstable/cluster/Envelope.js +2 -3
- package/dist/unstable/cluster/Envelope.js.map +1 -1
- package/dist/unstable/cluster/HttpRunner.d.ts.map +1 -1
- package/dist/unstable/cluster/HttpRunner.js +5 -2
- package/dist/unstable/cluster/HttpRunner.js.map +1 -1
- package/dist/unstable/cluster/MessageStorage.d.ts +34 -4
- package/dist/unstable/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/unstable/cluster/MessageStorage.js +28 -9
- package/dist/unstable/cluster/MessageStorage.js.map +1 -1
- package/dist/unstable/cluster/Reply.d.ts +1 -1
- package/dist/unstable/cluster/Sharding.d.ts +1 -1
- package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
- package/dist/unstable/cluster/Sharding.js +32 -17
- package/dist/unstable/cluster/Sharding.js.map +1 -1
- package/dist/unstable/cluster/ShardingConfig.js +23 -23
- package/dist/unstable/cluster/ShardingConfig.js.map +1 -1
- package/dist/unstable/cluster/SocketRunner.d.ts +2 -2
- package/dist/unstable/cluster/SocketRunner.d.ts.map +1 -1
- package/dist/unstable/cluster/SocketRunner.js +4 -3
- package/dist/unstable/cluster/SocketRunner.js.map +1 -1
- package/dist/unstable/cluster/SqlMessageStorage.js +11 -8
- package/dist/unstable/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/unstable/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/unstable/cluster/SqlRunnerStorage.js +11 -4
- package/dist/unstable/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/unstable/cluster/internal/clusterAbandon.d.ts +5 -0
- package/dist/unstable/cluster/internal/clusterAbandon.d.ts.map +1 -0
- package/dist/unstable/cluster/internal/clusterAbandon.js +9 -0
- package/dist/unstable/cluster/internal/clusterAbandon.js.map +1 -0
- package/dist/unstable/cluster/internal/entityManager.js +7 -4
- package/dist/unstable/cluster/internal/entityManager.js.map +1 -1
- package/dist/unstable/cluster/internal/interruptors.js +30 -1
- package/dist/unstable/cluster/internal/interruptors.js.map +1 -1
- package/dist/unstable/cluster/internal/resourceRef.js +12 -7
- package/dist/unstable/cluster/internal/resourceRef.js.map +1 -1
- package/dist/unstable/devtools/DevToolsClient.d.ts.map +1 -1
- package/dist/unstable/devtools/DevToolsClient.js +12 -7
- package/dist/unstable/devtools/DevToolsClient.js.map +1 -1
- package/dist/unstable/encoding/Ini.d.ts +2 -0
- package/dist/unstable/encoding/Ini.d.ts.map +1 -1
- package/dist/unstable/encoding/Ini.js +2 -0
- package/dist/unstable/encoding/Ini.js.map +1 -1
- package/dist/unstable/encoding/SchemaBinary.d.ts +27 -3
- package/dist/unstable/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/unstable/encoding/SchemaBinary.js +108 -39
- package/dist/unstable/encoding/SchemaBinary.js.map +1 -1
- package/dist/unstable/encoding/Sse.js +1 -1
- package/dist/unstable/encoding/Sse.js.map +1 -1
- package/dist/unstable/encoding/Toml.d.ts +2 -0
- package/dist/unstable/encoding/Toml.d.ts.map +1 -1
- package/dist/unstable/encoding/Toml.js +6 -4
- package/dist/unstable/encoding/Toml.js.map +1 -1
- package/dist/unstable/encoding/Yaml.d.ts +2 -0
- package/dist/unstable/encoding/Yaml.d.ts.map +1 -1
- package/dist/unstable/encoding/Yaml.js +16 -5
- package/dist/unstable/encoding/Yaml.js.map +1 -1
- package/dist/unstable/encoding/index.d.ts +0 -4
- package/dist/unstable/encoding/index.d.ts.map +1 -1
- package/dist/unstable/encoding/index.js +0 -4
- package/dist/unstable/encoding/index.js.map +1 -1
- package/dist/unstable/eventlog/Event.d.ts +5 -5
- package/dist/unstable/eventlog/Event.d.ts.map +1 -1
- package/dist/unstable/eventlog/Event.js +3 -3
- package/dist/unstable/eventlog/Event.js.map +1 -1
- package/dist/unstable/eventlog/EventJournal.d.ts +7 -7
- package/dist/unstable/eventlog/EventJournal.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventJournal.js +9 -9
- package/dist/unstable/eventlog/EventJournal.js.map +1 -1
- package/dist/unstable/eventlog/EventLog.d.ts +1 -1
- package/dist/unstable/eventlog/EventLog.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLog.js +4 -4
- package/dist/unstable/eventlog/EventLog.js.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.d.ts +11 -5
- package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.js +25 -15
- package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
- package/dist/unstable/eventlog/EventLogRemote.d.ts +7 -1
- package/dist/unstable/eventlog/EventLogRemote.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogRemote.js +13 -3
- package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
- package/dist/unstable/eventlog/EventLogServerUnencrypted.js +2 -2
- package/dist/unstable/eventlog/EventLogServerUnencrypted.js.map +1 -1
- package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -1
- package/dist/unstable/eventlog/SqlEventJournal.js +26 -13
- package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +5 -1
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
- package/dist/unstable/http/Cookies.d.ts +1 -45
- package/dist/unstable/http/Cookies.d.ts.map +1 -1
- package/dist/unstable/http/Cookies.js +2 -64
- package/dist/unstable/http/Cookies.js.map +1 -1
- package/dist/unstable/http/FetchHttpClient.js +1 -1
- package/dist/unstable/http/FetchHttpClient.js.map +1 -1
- package/dist/unstable/http/FindMyWay/internal/router.js +124 -33
- package/dist/unstable/http/FindMyWay/internal/router.js.map +1 -1
- package/dist/unstable/http/FindMyWay.d.ts +15 -5
- package/dist/unstable/http/FindMyWay.d.ts.map +1 -1
- package/dist/unstable/http/FindMyWay.js +3 -1
- package/dist/unstable/http/FindMyWay.js.map +1 -1
- package/dist/unstable/http/Headers.d.ts +0 -22
- package/dist/unstable/http/Headers.d.ts.map +1 -1
- package/dist/unstable/http/Headers.js +3 -34
- package/dist/unstable/http/Headers.js.map +1 -1
- package/dist/unstable/http/HttpBody.d.ts +41 -10
- package/dist/unstable/http/HttpBody.d.ts.map +1 -1
- package/dist/unstable/http/HttpBody.js +64 -10
- package/dist/unstable/http/HttpBody.js.map +1 -1
- package/dist/unstable/http/HttpClient.d.ts +1 -1
- package/dist/unstable/http/HttpClient.d.ts.map +1 -1
- package/dist/unstable/http/HttpClient.js +28 -18
- package/dist/unstable/http/HttpClient.js.map +1 -1
- package/dist/unstable/http/HttpClientRequest.d.ts +28 -6
- package/dist/unstable/http/HttpClientRequest.d.ts.map +1 -1
- package/dist/unstable/http/HttpClientRequest.js +8 -19
- package/dist/unstable/http/HttpClientRequest.js.map +1 -1
- package/dist/unstable/http/HttpClientResponse.d.ts +6 -1
- package/dist/unstable/http/HttpClientResponse.d.ts.map +1 -1
- package/dist/unstable/http/HttpClientResponse.js +10 -6
- package/dist/unstable/http/HttpClientResponse.js.map +1 -1
- package/dist/unstable/http/HttpEffect.d.ts +16 -0
- package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
- package/dist/unstable/http/HttpEffect.js +121 -65
- package/dist/unstable/http/HttpEffect.js.map +1 -1
- package/dist/unstable/http/HttpIncomingMessage.d.ts +3 -3
- package/dist/unstable/http/HttpIncomingMessage.d.ts.map +1 -1
- package/dist/unstable/http/HttpIncomingMessage.js +1 -13
- package/dist/unstable/http/HttpIncomingMessage.js.map +1 -1
- package/dist/unstable/http/HttpMiddleware.d.ts +1 -1
- package/dist/unstable/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/unstable/http/HttpMiddleware.js +29 -21
- package/dist/unstable/http/HttpMiddleware.js.map +1 -1
- package/dist/unstable/http/HttpPlatform.d.ts +12 -11
- package/dist/unstable/http/HttpPlatform.d.ts.map +1 -1
- package/dist/unstable/http/HttpPlatform.js +36 -10
- package/dist/unstable/http/HttpPlatform.js.map +1 -1
- package/dist/unstable/http/HttpRouter.d.ts +5 -1
- package/dist/unstable/http/HttpRouter.d.ts.map +1 -1
- package/dist/unstable/http/HttpRouter.js +22 -8
- package/dist/unstable/http/HttpRouter.js.map +1 -1
- package/dist/unstable/http/HttpServer.d.ts +11 -42
- package/dist/unstable/http/HttpServer.d.ts.map +1 -1
- package/dist/unstable/http/HttpServer.js +16 -20
- package/dist/unstable/http/HttpServer.js.map +1 -1
- package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/unstable/http/HttpServerRequest.js +11 -11
- package/dist/unstable/http/HttpServerRequest.js.map +1 -1
- package/dist/unstable/http/HttpServerRespondable.js +1 -1
- package/dist/unstable/http/HttpServerRespondable.js.map +1 -1
- package/dist/unstable/http/HttpServerResponse.d.ts +9 -13
- package/dist/unstable/http/HttpServerResponse.d.ts.map +1 -1
- package/dist/unstable/http/HttpServerResponse.js +34 -44
- package/dist/unstable/http/HttpServerResponse.js.map +1 -1
- package/dist/unstable/http/HttpStaticServer.d.ts +2 -2
- package/dist/unstable/http/HttpStaticServer.d.ts.map +1 -1
- package/dist/unstable/http/HttpStaticServer.js +19 -19
- package/dist/unstable/http/HttpStaticServer.js.map +1 -1
- package/dist/unstable/http/Mime.d.ts +28 -0
- package/dist/unstable/http/Mime.d.ts.map +1 -0
- package/dist/unstable/http/Mime.js +72 -0
- package/dist/unstable/http/Mime.js.map +1 -0
- package/dist/unstable/http/Multipart.d.ts +22 -9
- package/dist/unstable/http/Multipart.d.ts.map +1 -1
- package/dist/unstable/http/Multipart.js +26 -10
- package/dist/unstable/http/Multipart.js.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.d.ts +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.js +8 -3
- package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
- package/dist/unstable/http/MultipartParser.d.ts +4 -3
- package/dist/unstable/http/MultipartParser.d.ts.map +1 -1
- package/dist/unstable/http/MultipartParser.js.map +1 -1
- package/dist/unstable/http/UrlParams.d.ts +0 -97
- package/dist/unstable/http/UrlParams.d.ts.map +1 -1
- package/dist/unstable/http/UrlParams.js +3 -104
- package/dist/unstable/http/UrlParams.js.map +1 -1
- package/dist/unstable/http/index.d.ts +4 -0
- package/dist/unstable/http/index.d.ts.map +1 -1
- package/dist/unstable/http/index.js +4 -0
- package/dist/unstable/http/index.js.map +1 -1
- package/dist/unstable/http/internal/compression.js +8 -11
- package/dist/unstable/http/internal/compression.js.map +1 -1
- package/dist/unstable/http/internal/headers.d.ts +2 -0
- package/dist/unstable/http/internal/headers.d.ts.map +1 -0
- package/dist/unstable/http/internal/headers.js +5 -0
- package/dist/unstable/http/internal/headers.js.map +1 -0
- package/dist/unstable/http/internal/httpBody.js +8 -0
- package/dist/unstable/http/internal/httpBody.js.map +1 -1
- package/dist/unstable/http/internal/mimeTypes.d.ts +361 -0
- package/dist/unstable/http/internal/mimeTypes.d.ts.map +1 -0
- package/dist/unstable/http/internal/mimeTypes.js +363 -0
- package/dist/unstable/http/internal/mimeTypes.js.map +1 -0
- package/dist/unstable/httpapi/HttpApiBuilder.d.ts +13 -0
- package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiBuilder.js +23 -6
- package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiClient.d.ts +4 -3
- package/dist/unstable/httpapi/HttpApiClient.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiClient.js +25 -13
- package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +8 -3
- package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiEndpoint.js +9 -6
- package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiError.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiError.js +3 -1
- package/dist/unstable/httpapi/HttpApiError.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiMiddleware.js +7 -5
- package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiSchema.d.ts +12 -0
- package/dist/unstable/httpapi/HttpApiSchema.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiSchema.js +8 -10
- package/dist/unstable/httpapi/HttpApiSchema.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiTest.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiTest.js +8 -2
- package/dist/unstable/httpapi/HttpApiTest.js.map +1 -1
- package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
- package/dist/unstable/httpapi/OpenApi.js +31 -20
- package/dist/unstable/httpapi/OpenApi.js.map +1 -1
- package/dist/unstable/net/IpInterface.d.ts +140 -0
- package/dist/unstable/net/IpInterface.d.ts.map +1 -0
- package/dist/unstable/net/IpInterface.js +149 -0
- package/dist/unstable/net/IpInterface.js.map +1 -0
- package/dist/unstable/net/IpNetwork.d.ts +227 -0
- package/dist/unstable/net/IpNetwork.d.ts.map +1 -0
- package/dist/unstable/net/IpNetwork.js +239 -0
- package/dist/unstable/net/IpNetwork.js.map +1 -0
- package/dist/unstable/net/NetAddress.d.ts +705 -0
- package/dist/unstable/net/NetAddress.d.ts.map +1 -0
- package/dist/unstable/net/NetAddress.js +956 -0
- package/dist/unstable/net/NetAddress.js.map +1 -0
- package/dist/unstable/net/index.d.ts +16 -0
- package/dist/unstable/net/index.d.ts.map +1 -0
- package/dist/unstable/net/index.js +17 -0
- package/dist/unstable/net/index.js.map +1 -0
- package/dist/unstable/observability/OtlpExporter.d.ts +1 -0
- package/dist/unstable/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/unstable/observability/OtlpExporter.js +4 -6
- package/dist/unstable/observability/OtlpExporter.js.map +1 -1
- package/dist/unstable/observability/OtlpLogger.js +9 -9
- package/dist/unstable/observability/OtlpLogger.js.map +1 -1
- package/dist/unstable/observability/OtlpMetrics.d.ts.map +1 -1
- package/dist/unstable/observability/OtlpMetrics.js +9 -8
- package/dist/unstable/observability/OtlpMetrics.js.map +1 -1
- package/dist/unstable/observability/OtlpResource.d.ts.map +1 -1
- package/dist/unstable/observability/OtlpResource.js +2 -2
- package/dist/unstable/observability/OtlpResource.js.map +1 -1
- package/dist/unstable/observability/OtlpTracer.d.ts.map +1 -1
- package/dist/unstable/observability/OtlpTracer.js +82 -55
- package/dist/unstable/observability/OtlpTracer.js.map +1 -1
- package/dist/unstable/observability/internal/otlpEnv.d.ts +1 -1
- package/dist/unstable/observability/internal/otlpEnv.d.ts.map +1 -1
- package/dist/unstable/observability/internal/otlpEnv.js +5 -9
- package/dist/unstable/observability/internal/otlpEnv.js.map +1 -1
- package/dist/unstable/persistence/PersistedCache.js +1 -1
- package/dist/unstable/persistence/PersistedCache.js.map +1 -1
- package/dist/unstable/persistence/PersistedQueue.d.ts +114 -14
- package/dist/unstable/persistence/PersistedQueue.d.ts.map +1 -1
- package/dist/unstable/persistence/PersistedQueue.js +799 -234
- package/dist/unstable/persistence/PersistedQueue.js.map +1 -1
- package/dist/unstable/persistence/RateLimiter.d.ts +28 -15
- package/dist/unstable/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/unstable/persistence/RateLimiter.js +33 -41
- package/dist/unstable/persistence/RateLimiter.js.map +1 -1
- package/dist/unstable/process/ChildProcess.d.ts +8 -2
- package/dist/unstable/process/ChildProcess.d.ts.map +1 -1
- package/dist/unstable/process/ChildProcess.js +7 -2
- package/dist/unstable/process/ChildProcess.js.map +1 -1
- package/dist/unstable/process/ChildProcessSpawner.d.ts +1 -1
- package/dist/unstable/process/ChildProcessSpawner.d.ts.map +1 -1
- package/dist/unstable/process/ChildProcessSpawner.js +1 -1
- package/dist/unstable/process/ChildProcessSpawner.js.map +1 -1
- package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
- package/dist/unstable/reactivity/Atom.js +6 -3
- package/dist/unstable/reactivity/Atom.js.map +1 -1
- package/dist/unstable/reactivity/AtomHttpApi.d.ts +3 -6
- package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomHttpApi.js +6 -3
- package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
- package/dist/unstable/reactivity/AtomRegistry.js +2 -3
- package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
- package/dist/unstable/reactivity/AtomRpc.d.ts +2 -2
- package/dist/unstable/reactivity/AtomRpc.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomRpc.js +1 -1
- package/dist/unstable/reactivity/AtomRpc.js.map +1 -1
- package/dist/unstable/reactivity/Hydration.d.ts +6 -4
- package/dist/unstable/reactivity/Hydration.d.ts.map +1 -1
- package/dist/unstable/reactivity/Hydration.js +20 -6
- package/dist/unstable/reactivity/Hydration.js.map +1 -1
- package/dist/unstable/reactivity/Reactivity.d.ts +26 -26
- package/dist/unstable/reactivity/Reactivity.d.ts.map +1 -1
- package/dist/unstable/reactivity/Reactivity.js +10 -12
- package/dist/unstable/reactivity/Reactivity.js.map +1 -1
- package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcClient.js +64 -64
- package/dist/unstable/rpc/RpcClient.js.map +1 -1
- package/dist/unstable/rpc/RpcClientError.d.ts +1 -1
- package/dist/unstable/rpc/RpcMessage.d.ts +3 -1
- package/dist/unstable/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcMessage.js +1 -1
- package/dist/unstable/rpc/RpcMessage.js.map +1 -1
- package/dist/unstable/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcMiddleware.js +7 -5
- package/dist/unstable/rpc/RpcMiddleware.js.map +1 -1
- package/dist/unstable/rpc/RpcSerialization.d.ts +5 -39
- package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcSerialization.js +22 -115
- package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
- package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcServer.js +104 -57
- package/dist/unstable/rpc/RpcServer.js.map +1 -1
- package/dist/unstable/schema/Model.d.ts.map +1 -1
- package/dist/unstable/schema/Model.js +7 -6
- package/dist/unstable/schema/Model.js.map +1 -1
- package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
- package/dist/unstable/schema/VariantSchema.js +3 -0
- package/dist/unstable/schema/VariantSchema.js.map +1 -1
- package/dist/unstable/socket/Socket.d.ts +287 -102
- package/dist/unstable/socket/Socket.d.ts.map +1 -1
- package/dist/unstable/socket/Socket.js +408 -215
- package/dist/unstable/socket/Socket.js.map +1 -1
- package/dist/unstable/socket/SocketServer.d.ts +5 -32
- package/dist/unstable/socket/SocketServer.d.ts.map +1 -1
- package/dist/unstable/socket/SocketServer.js +3 -3
- package/dist/unstable/socket/SocketServer.js.map +1 -1
- package/dist/unstable/sql/SqlClient.d.ts +20 -0
- package/dist/unstable/sql/SqlClient.d.ts.map +1 -1
- package/dist/unstable/sql/SqlClient.js +14 -7
- package/dist/unstable/sql/SqlClient.js.map +1 -1
- package/dist/unstable/sql/SqlConnection.d.ts +15 -0
- package/dist/unstable/sql/SqlConnection.d.ts.map +1 -1
- package/dist/unstable/sql/SqlConnection.js.map +1 -1
- package/dist/unstable/sql/SqlModel.d.ts +1 -1
- package/dist/unstable/sql/SqlModel.d.ts.map +1 -1
- package/dist/unstable/sql/Statement.d.ts +13 -5
- package/dist/unstable/sql/Statement.d.ts.map +1 -1
- package/dist/unstable/sql/Statement.js +29 -15
- package/dist/unstable/sql/Statement.js.map +1 -1
- package/dist/unstable/workers/Transferable.js +1 -1
- package/dist/unstable/workers/Worker.d.ts.map +1 -1
- package/dist/unstable/workers/Worker.js +3 -1
- package/dist/unstable/workers/Worker.js.map +1 -1
- package/dist/unstable/workflow/Activity.d.ts +7 -0
- package/dist/unstable/workflow/Activity.d.ts.map +1 -1
- package/dist/unstable/workflow/Activity.js +14 -4
- package/dist/unstable/workflow/Activity.js.map +1 -1
- package/dist/unstable/workflow/DurableClock.js +1 -1
- package/dist/unstable/workflow/DurableClock.js.map +1 -1
- package/dist/unstable/workflow/DurableDeferred.d.ts +2 -2
- package/dist/unstable/workflow/DurableDeferred.d.ts.map +1 -1
- package/dist/unstable/workflow/DurableDeferred.js.map +1 -1
- package/dist/unstable/workflow/DurableQueue.d.ts +8 -0
- package/dist/unstable/workflow/DurableQueue.d.ts.map +1 -1
- package/dist/unstable/workflow/DurableQueue.js +8 -0
- package/dist/unstable/workflow/DurableQueue.js.map +1 -1
- package/dist/unstable/workflow/Workflow.d.ts +5 -0
- package/dist/unstable/workflow/Workflow.d.ts.map +1 -1
- package/dist/unstable/workflow/Workflow.js +70 -12
- package/dist/unstable/workflow/Workflow.js.map +1 -1
- package/dist/unstable/workflow/WorkflowEngine.d.ts +6 -0
- package/dist/unstable/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/unstable/workflow/WorkflowEngine.js +11 -0
- package/dist/unstable/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +7 -9
- package/src/Array.ts +118 -67
- package/src/BigDecimal.ts +50 -28
- package/src/ByteSize.ts +838 -0
- package/src/Cache.ts +17 -3
- package/src/Cause.ts +1 -1
- package/src/Channel.ts +183 -48
- package/src/Chunk.ts +65 -36
- package/src/Config.ts +237 -248
- package/src/ConfigProvider.ts +7 -8
- package/src/Context.ts +31 -0
- package/src/Cron.ts +2 -2
- package/src/Crypto.ts +3 -2
- package/src/Deferred.ts +7 -6
- package/src/Duration.ts +1 -1
- package/src/Effect.ts +226 -122
- package/src/Effectable.ts +78 -9
- package/src/Encoding.ts +101 -11
- package/src/Equal.ts +1 -1
- package/src/ExecutionPlan.ts +3 -0
- package/src/Fiber.ts +33 -8
- package/src/FiberHandle.ts +3 -3
- package/src/FiberMap.ts +19 -10
- package/src/FileSystem.ts +55 -230
- package/src/Formatter.ts +21 -3
- package/src/Graph.ts +55 -25
- package/src/Hash.ts +5 -16
- package/src/HashRing.ts +9 -2
- package/src/HashSet.ts +56 -0
- package/src/Iterable.ts +66 -41
- package/src/JsonPatch.ts +4 -4
- package/src/JsonPointer.ts +97 -4
- package/src/JsonSchema.ts +8 -26
- package/src/Layer.ts +59 -18
- package/src/LayerMap.ts +8 -6
- package/src/LayerRef.ts +7 -5
- package/src/Logger.ts +125 -18
- package/src/ManagedRuntime.ts +1 -1
- package/src/Match.ts +33 -14
- package/src/Metric.ts +24 -21
- package/src/MutableHashMap.ts +1 -1
- package/src/MutableHashSet.ts +1 -1
- package/src/MutableList.ts +18 -2
- package/src/Number.ts +7 -1
- package/src/Optic.ts +8 -4
- package/src/Option.ts +1 -1
- package/src/Order.ts +3 -1
- package/src/PartitionedSemaphore.ts +1 -2
- package/src/Path.ts +1 -1
- package/src/PlatformError.ts +1 -1
- package/src/Pool.ts +125 -8
- package/src/PrimaryKey.ts +1 -1
- package/src/PubSub.ts +32 -20
- package/src/Queue.ts +119 -28
- package/src/Random.ts +20 -6
- package/src/RcMap.ts +20 -13
- package/src/Redacted.ts +1 -1
- package/src/RequestResolver.ts +49 -26
- package/src/Result.ts +1 -1
- package/src/Scheduler.ts +21 -11
- package/src/Schema.ts +4809 -6368
- package/src/SchemaAST.ts +1031 -284
- package/src/SchemaGetter.ts +75 -21
- package/src/SchemaIssue.ts +259 -15
- package/src/SchemaParser.ts +2 -12
- package/src/SchemaRepresentation.ts +1593 -29
- package/src/SchemaTransformation.ts +145 -22
- package/src/ScopedCache.ts +10 -3
- package/src/Sink.ts +31 -15
- package/src/Stream.ts +294 -76
- package/src/String.ts +20 -9
- package/src/Struct.ts +8 -4
- package/src/SynchronizedRef.ts +3 -5
- package/src/Terminal.ts +3 -3
- package/src/Tracer.ts +43 -14
- package/src/Tuple.ts +3 -1
- package/src/TxChunk.ts +1 -1
- package/src/TxDeferred.ts +1 -1
- package/src/TxHashMap.ts +1 -1
- package/src/TxHashSet.ts +92 -11
- package/src/TxPriorityQueue.ts +1 -1
- package/src/TxPubSub.ts +1 -1
- package/src/TxQueue.ts +22 -17
- package/src/TxReentrantLock.ts +29 -1
- package/src/TxRef.ts +1 -1
- package/src/TxSemaphore.ts +1 -1
- package/src/TxSubscriptionRef.ts +1 -1
- package/src/Types.ts +40 -17
- package/src/Utils.ts +3 -1
- package/src/index.ts +5 -0
- package/src/internal/arbitrary/model.ts +701 -0
- package/src/internal/arbitrary/regexp.ts +855 -0
- package/src/internal/arbitrary/runner.ts +608 -0
- package/src/internal/arbitrary/schema.ts +1800 -0
- package/src/internal/core.ts +48 -22
- package/src/internal/count.ts +14 -0
- package/src/internal/dateTime.ts +8 -10
- package/src/internal/effect.ts +475 -295
- package/src/internal/graph.ts +6 -1
- package/src/internal/hashMap.ts +5 -3
- package/src/internal/hashSet.ts +1 -1
- package/src/internal/matcher.ts +34 -19
- package/src/internal/metric.ts +1 -1
- package/src/internal/option.ts +8 -6
- package/src/internal/random.ts +20 -7
- package/src/internal/rcRef.ts +10 -7
- package/src/internal/references.ts +1 -0
- package/src/internal/request.ts +12 -7
- package/src/internal/result.ts +15 -11
- package/src/internal/schema/annotations.ts +3 -3
- package/src/internal/schema/fromJsonSchemaDocument.ts +14 -29
- package/src/internal/schema/fromRepresentation.ts +5 -3
- package/src/internal/schema/{schema.ts → make.ts} +0 -40
- package/src/internal/schema/parser.ts +8 -0
- package/src/internal/schema/standardSchema.ts +125 -0
- package/src/internal/schema/toCodeDocument.ts +58 -44
- package/src/internal/schema/toCodec.ts +312 -0
- package/src/internal/schema/toDifferJsonPatch.ts +24 -0
- package/src/internal/schema/toEncoderXml.ts +110 -0
- package/src/internal/schema/toEquivalence.ts +76 -0
- package/src/internal/schema/toFormatter.ts +230 -0
- package/src/internal/schema/toIso.ts +20 -0
- package/src/internal/schema/toJsonSchemaDocument.ts +125 -67
- package/src/internal/schema/toRepresentation.ts +1 -1
- package/src/internal/schemaError.ts +8 -0
- package/src/internal/stream.ts +14 -6
- package/src/internal/tracer.ts +4 -0
- package/src/internal/trie.ts +5 -2
- package/src/internal/uuid.ts +4 -0
- package/src/testing/TestSchema.ts +36 -30
- package/src/testing/index.ts +0 -5
- package/src/unstable/ai/AiError.ts +69 -5
- package/src/unstable/ai/AnthropicStructuredOutput.ts +4 -1
- package/src/unstable/ai/Chat.ts +35 -36
- package/src/unstable/ai/EmbeddingModel.ts +27 -15
- package/src/unstable/ai/LanguageModel.ts +283 -100
- package/src/unstable/ai/McpServer.ts +36 -20
- package/src/unstable/ai/OpenAiStructuredOutput.ts +4 -1
- package/src/unstable/ai/Prompt.ts +19 -4
- package/src/unstable/ai/Response.ts +107 -189
- package/src/unstable/ai/Tokenizer.ts +4 -5
- package/src/unstable/ai/Tool.ts +58 -35
- package/src/unstable/ai/Toolkit.ts +43 -37
- package/src/unstable/ai/internal/structured-output.ts +5 -5
- package/src/unstable/arbitrary/Arbitrary.ts +715 -0
- package/src/unstable/arbitrary/index.ts +10 -0
- package/src/unstable/cli/Argument.ts +111 -115
- package/src/unstable/cli/CliError.ts +2 -0
- package/src/unstable/cli/CliOutput.ts +40 -3
- package/src/unstable/cli/Command.ts +74 -74
- package/src/unstable/cli/Completions.ts +4 -4
- package/src/unstable/cli/Flag.ts +158 -169
- package/src/unstable/cli/GlobalFlag.ts +13 -13
- package/src/unstable/cli/Param.ts +150 -159
- package/src/unstable/cli/Primitive.ts +75 -76
- package/src/unstable/cli/Prompt.ts +88 -87
- package/src/unstable/cli/internal/completions/descriptor.ts +23 -11
- package/src/unstable/cli/internal/completions/zsh.ts +2 -2
- package/src/unstable/cli/internal/config.ts +4 -4
- package/src/unstable/cli/internal/parser.ts +1 -1
- package/src/unstable/cli/internal/wizard.ts +18 -13
- package/src/unstable/cluster/ClusterSchema.ts +19 -1
- package/src/unstable/cluster/ClusterWorkflowEngine.ts +66 -16
- package/src/unstable/cluster/Entity.ts +3 -0
- package/src/unstable/cluster/EntityProxyServer.ts +6 -2
- package/src/unstable/cluster/Envelope.ts +3 -3
- package/src/unstable/cluster/HttpRunner.ts +7 -2
- package/src/unstable/cluster/MessageStorage.ts +62 -21
- package/src/unstable/cluster/Reply.ts +2 -2
- package/src/unstable/cluster/Sharding.ts +44 -19
- package/src/unstable/cluster/ShardingConfig.ts +23 -23
- package/src/unstable/cluster/SocketRunner.ts +5 -4
- package/src/unstable/cluster/SqlMessageStorage.ts +12 -8
- package/src/unstable/cluster/SqlRunnerStorage.ts +18 -10
- package/src/unstable/cluster/internal/clusterAbandon.ts +14 -0
- package/src/unstable/cluster/internal/entityManager.ts +12 -4
- package/src/unstable/cluster/internal/interruptors.ts +58 -1
- package/src/unstable/cluster/internal/resourceRef.ts +15 -7
- package/src/unstable/devtools/DevToolsClient.ts +13 -3
- package/src/unstable/encoding/Ini.ts +2 -0
- package/src/unstable/encoding/SchemaBinary.ts +150 -44
- package/src/unstable/encoding/Sse.ts +1 -1
- package/src/unstable/encoding/Toml.ts +7 -5
- package/src/unstable/encoding/Yaml.ts +18 -5
- package/src/unstable/encoding/index.ts +0 -5
- package/src/unstable/eventlog/Event.ts +7 -7
- package/src/unstable/eventlog/EventJournal.ts +9 -9
- package/src/unstable/eventlog/EventLog.ts +5 -5
- package/src/unstable/eventlog/EventLogMessage.ts +25 -15
- package/src/unstable/eventlog/EventLogRemote.ts +15 -4
- package/src/unstable/eventlog/EventLogServerUnencrypted.ts +2 -2
- package/src/unstable/eventlog/SqlEventJournal.ts +32 -9
- package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +11 -1
- package/src/unstable/http/Cookies.ts +2 -102
- package/src/unstable/http/FetchHttpClient.ts +1 -1
- package/src/unstable/http/FindMyWay/internal/router.ts +139 -33
- package/src/unstable/http/FindMyWay.ts +15 -5
- package/src/unstable/http/Headers.ts +3 -48
- package/src/unstable/http/HttpBody.ts +107 -36
- package/src/unstable/http/HttpClient.ts +41 -30
- package/src/unstable/http/HttpClientRequest.ts +32 -18
- package/src/unstable/http/HttpClientResponse.ts +16 -3
- package/src/unstable/http/HttpEffect.ts +145 -73
- package/src/unstable/http/HttpIncomingMessage.ts +4 -4
- package/src/unstable/http/HttpMiddleware.ts +43 -24
- package/src/unstable/http/HttpPlatform.ts +62 -20
- package/src/unstable/http/HttpRouter.ts +27 -16
- package/src/unstable/http/HttpServer.ts +18 -57
- package/src/unstable/http/HttpServerRequest.ts +17 -15
- package/src/unstable/http/HttpServerRespondable.ts +1 -1
- package/src/unstable/http/HttpServerResponse.ts +49 -40
- package/src/unstable/http/HttpStaticServer.ts +30 -28
- package/src/unstable/http/Mime.ts +76 -0
- package/src/unstable/http/Multipart.ts +35 -18
- package/src/unstable/http/MultipartParser/internal/multipart.ts +10 -3
- package/src/unstable/http/MultipartParser.ts +4 -3
- package/src/unstable/http/UrlParams.ts +3 -161
- package/src/unstable/http/index.ts +5 -0
- package/src/unstable/http/internal/compression.ts +11 -12
- package/src/unstable/http/internal/headers.ts +7 -0
- package/src/unstable/http/internal/httpBody.ts +9 -0
- package/src/unstable/http/internal/mimeTypes.ts +395 -0
- package/src/unstable/httpapi/HttpApiBuilder.ts +52 -6
- package/src/unstable/httpapi/HttpApiClient.ts +44 -24
- package/src/unstable/httpapi/HttpApiEndpoint.ts +24 -11
- package/src/unstable/httpapi/HttpApiError.ts +3 -1
- package/src/unstable/httpapi/HttpApiMiddleware.ts +7 -5
- package/src/unstable/httpapi/HttpApiSchema.ts +14 -7
- package/src/unstable/httpapi/HttpApiTest.ts +6 -1
- package/src/unstable/httpapi/OpenApi.ts +32 -22
- package/src/unstable/net/IpInterface.ts +242 -0
- package/src/unstable/net/IpNetwork.ts +357 -0
- package/src/unstable/net/NetAddress.ts +1271 -0
- package/src/unstable/net/index.ts +20 -0
- package/src/unstable/observability/OtlpExporter.ts +5 -6
- package/src/unstable/observability/OtlpLogger.ts +9 -9
- package/src/unstable/observability/OtlpMetrics.ts +9 -8
- package/src/unstable/observability/OtlpResource.ts +5 -4
- package/src/unstable/observability/OtlpTracer.ts +103 -81
- package/src/unstable/observability/internal/otlpEnv.ts +10 -13
- package/src/unstable/persistence/PersistedCache.ts +1 -1
- package/src/unstable/persistence/PersistedQueue.ts +1100 -291
- package/src/unstable/persistence/RateLimiter.ts +64 -58
- package/src/unstable/process/ChildProcess.ts +15 -4
- package/src/unstable/process/ChildProcessSpawner.ts +1 -1
- package/src/unstable/reactivity/Atom.ts +6 -3
- package/src/unstable/reactivity/AtomHttpApi.ts +21 -10
- package/src/unstable/reactivity/AtomRegistry.ts +2 -4
- package/src/unstable/reactivity/AtomRpc.ts +6 -5
- package/src/unstable/reactivity/Hydration.ts +26 -7
- package/src/unstable/reactivity/Reactivity.ts +49 -36
- package/src/unstable/rpc/RpcClient.ts +62 -59
- package/src/unstable/rpc/RpcMessage.ts +3 -1
- package/src/unstable/rpc/RpcMiddleware.ts +7 -5
- package/src/unstable/rpc/RpcSerialization.ts +27 -117
- package/src/unstable/rpc/RpcServer.ts +143 -80
- package/src/unstable/schema/Model.ts +8 -7
- package/src/unstable/schema/VariantSchema.ts +3 -0
- package/src/unstable/socket/Socket.ts +714 -383
- package/src/unstable/socket/SocketServer.ts +5 -35
- package/src/unstable/sql/SqlClient.ts +42 -7
- package/src/unstable/sql/SqlConnection.ts +18 -0
- package/src/unstable/sql/SqlModel.ts +1 -1
- package/src/unstable/sql/Statement.ts +46 -20
- package/src/unstable/workers/Transferable.ts +1 -1
- package/src/unstable/workers/Worker.ts +3 -1
- package/src/unstable/workflow/Activity.ts +20 -9
- package/src/unstable/workflow/DurableClock.ts +1 -1
- package/src/unstable/workflow/DurableDeferred.ts +6 -0
- package/src/unstable/workflow/DurableQueue.ts +8 -0
- package/src/unstable/workflow/Workflow.ts +105 -28
- package/src/unstable/workflow/WorkflowEngine.ts +22 -0
- package/dist/internal/schema/schema.d.ts.map +0 -1
- package/dist/internal/schema/schema.js.map +0 -1
- package/dist/internal/schema/toArbitrary.d.ts +0 -2
- package/dist/internal/schema/toArbitrary.d.ts.map +0 -1
- package/dist/internal/schema/toArbitrary.js +0 -705
- package/dist/internal/schema/toArbitrary.js.map +0 -1
- package/dist/testing/FastCheck.d.ts +0 -90
- package/dist/testing/FastCheck.d.ts.map +0 -1
- package/dist/testing/FastCheck.js +0 -90
- package/dist/testing/FastCheck.js.map +0 -1
- package/dist/unstable/encoding/Msgpack.d.ts +0 -185
- package/dist/unstable/encoding/Msgpack.d.ts.map +0 -1
- package/dist/unstable/encoding/Msgpack.js +0 -227
- package/dist/unstable/encoding/Msgpack.js.map +0 -1
- package/src/internal/schema/toArbitrary.ts +0 -843
- package/src/testing/FastCheck.ts +0 -90
- package/src/unstable/encoding/Msgpack.ts +0 -432
- /package/dist/internal/{schema → arbitrary}/schema.d.ts +0 -0
|
@@ -8,20 +8,23 @@
|
|
|
8
8
|
* This module includes a queue factory, store service, id-based de-duplication,
|
|
9
9
|
* retry handling, and in-memory, Redis, and SQL-backed store layers.
|
|
10
10
|
*
|
|
11
|
+
* Delivery is at-least-once: a crash between handler success and the
|
|
12
|
+
* acknowledgement redelivers the element, so handlers must be idempotent.
|
|
13
|
+
*
|
|
11
14
|
* @since 4.0.0
|
|
12
15
|
*/
|
|
13
|
-
import type * as Arr from "../../Array.ts"
|
|
14
16
|
import * as Cause from "../../Cause.ts"
|
|
17
|
+
import * as Clock from "../../Clock.ts"
|
|
15
18
|
import * as Context from "../../Context.ts"
|
|
16
|
-
import * as Data from "../../Data.ts"
|
|
17
19
|
import * as Duration from "../../Duration.ts"
|
|
18
20
|
import * as Effect from "../../Effect.ts"
|
|
19
|
-
import * as Exit from "../../Exit.ts"
|
|
20
21
|
import { flow } from "../../Function.ts"
|
|
21
|
-
import
|
|
22
|
+
import { sqlCleanupBatchSize } from "../../internal/persistence.ts"
|
|
22
23
|
import * as Latch from "../../Latch.ts"
|
|
23
24
|
import * as Layer from "../../Layer.ts"
|
|
24
25
|
import * as MutableRef from "../../MutableRef.ts"
|
|
26
|
+
import * as Predicate from "../../Predicate.ts"
|
|
27
|
+
import * as Pull from "../../Pull.ts"
|
|
25
28
|
import * as Queue from "../../Queue.ts"
|
|
26
29
|
import * as RcMap from "../../RcMap.ts"
|
|
27
30
|
import * as Schedule from "../../Schedule.ts"
|
|
@@ -54,8 +57,12 @@ export type TypeId = "~effect/persistence/PersistedQueue"
|
|
|
54
57
|
* **Details**
|
|
55
58
|
*
|
|
56
59
|
* `offer` enqueues values by id, and `take` processes one value at a time,
|
|
57
|
-
* marking it complete on success or retrying it
|
|
58
|
-
* reached
|
|
60
|
+
* marking it complete on success or retrying it with the queue's retry
|
|
61
|
+
* schedule until the maximum attempts is reached, after which it is marked as
|
|
62
|
+
* failed.
|
|
63
|
+
*
|
|
64
|
+
* Delivery is at-least-once: a crash between handler success and the
|
|
65
|
+
* acknowledgement redelivers the element, so handlers must be idempotent.
|
|
59
66
|
*
|
|
60
67
|
* @category models
|
|
61
68
|
* @since 4.0.0
|
|
@@ -69,7 +76,8 @@ export interface PersistedQueue<in out A, out R = never> {
|
|
|
69
76
|
* **Details**
|
|
70
77
|
*
|
|
71
78
|
* If an element with the same id already exists in the queue, it will not be
|
|
72
|
-
* added again.
|
|
79
|
+
* added again. De-duplication survives completion until the id is removed by
|
|
80
|
+
* `layerCleanup`.
|
|
73
81
|
*/
|
|
74
82
|
readonly offer: (value: A, options?: {
|
|
75
83
|
readonly id: string | undefined
|
|
@@ -82,18 +90,23 @@ export interface PersistedQueue<in out A, out R = never> {
|
|
|
82
90
|
* **Details**
|
|
83
91
|
*
|
|
84
92
|
* If the returned effect succeeds, the element is marked as processed;
|
|
85
|
-
* otherwise it will be retried
|
|
86
|
-
*
|
|
93
|
+
* otherwise it will be retried with the queue's retry schedule until the
|
|
94
|
+
* maximum attempts is reached, after which it is marked as failed.
|
|
95
|
+
*
|
|
96
|
+
* An attempt is counted when the element is claimed, so `attempts` in the
|
|
97
|
+
* handler metadata is 1-based ("this is attempt 3"). A handler crash that
|
|
98
|
+
* takes down the process still consumes an attempt.
|
|
99
|
+
*
|
|
100
|
+
* Elements that fail to decode with the queue's schema are marked as failed
|
|
101
|
+
* immediately and the next element is taken instead, so schema decode errors
|
|
102
|
+
* never surface from `take`.
|
|
87
103
|
*/
|
|
88
104
|
readonly take: <XA, XE, XR>(
|
|
89
105
|
f: (value: A, metadata: {
|
|
90
106
|
readonly id: string
|
|
91
107
|
readonly attempts: number
|
|
92
|
-
}) => Effect.Effect<XA, XE, XR
|
|
93
|
-
|
|
94
|
-
readonly maxAttempts?: number | undefined
|
|
95
|
-
}
|
|
96
|
-
) => Effect.Effect<XA, XE | PersistedQueueError | Schema.SchemaError, R | XR>
|
|
108
|
+
}) => Effect.Effect<XA, XE, XR>
|
|
109
|
+
) => Effect.Effect<XA, XE | PersistedQueueError, R | XR>
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
/**
|
|
@@ -108,6 +121,8 @@ export class PersistedQueueFactory extends Context.Service<
|
|
|
108
121
|
readonly make: <S extends Schema.Constraint>(options: {
|
|
109
122
|
readonly name: string
|
|
110
123
|
readonly schema: S
|
|
124
|
+
readonly maxAttempts?: number | undefined
|
|
125
|
+
readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
|
|
111
126
|
}) => Effect.Effect<PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>>
|
|
112
127
|
}
|
|
113
128
|
>()("effect/persistence/PersistedQueue/PersistedQueueFactory") {}
|
|
@@ -116,18 +131,65 @@ export class PersistedQueueFactory extends Context.Service<
|
|
|
116
131
|
* Accesses `PersistedQueueFactory` to create a named persisted queue for a
|
|
117
132
|
* schema.
|
|
118
133
|
*
|
|
134
|
+
* **Details**
|
|
135
|
+
*
|
|
136
|
+
* `maxAttempts` defaults to 10. `retrySchedule` controls the delay before a
|
|
137
|
+
* failed element becomes visible again, and defaults to an exponential delay
|
|
138
|
+
* starting at 1 second and capped at 5 minutes.
|
|
139
|
+
*
|
|
140
|
+
* The schedule's state is the element's persisted attempt count. On each
|
|
141
|
+
* failure the schedule is replayed up to the current attempt, so delays keep
|
|
142
|
+
* progressing even when consecutive retries run in different processes. The
|
|
143
|
+
* schedule input is the attempt number.
|
|
144
|
+
*
|
|
145
|
+
* Replay simulates elapsed time from the sum of the computed delays.
|
|
146
|
+
* Attempt-driven schedules are therefore exact, while wall-clock-anchored
|
|
147
|
+
* schedules observe this idealized time. In particular,
|
|
148
|
+
* `Schedule.upTo({ duration })` caps the summed delays rather than real time
|
|
149
|
+
* since the original failure.
|
|
150
|
+
*
|
|
119
151
|
* @category accessors
|
|
120
152
|
* @since 4.0.0
|
|
121
153
|
*/
|
|
122
154
|
export const make = <S extends Schema.Constraint>(options: {
|
|
123
155
|
readonly name: string
|
|
124
156
|
readonly schema: S
|
|
157
|
+
readonly maxAttempts?: number | undefined
|
|
158
|
+
readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
|
|
125
159
|
}): Effect.Effect<
|
|
126
160
|
PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>,
|
|
127
161
|
never,
|
|
128
162
|
PersistedQueueFactory
|
|
129
163
|
> => PersistedQueueFactory.use((factory) => factory.make(options))
|
|
130
164
|
|
|
165
|
+
const defaultRetrySchedule = Schedule.min([
|
|
166
|
+
Schedule.exponential("1 second"),
|
|
167
|
+
Schedule.spaced("5 minutes")
|
|
168
|
+
])
|
|
169
|
+
|
|
170
|
+
// The persisted attempt count is the schedule state: consecutive retries of an
|
|
171
|
+
// element can run in different processes, so a fresh schedule step is replayed
|
|
172
|
+
// up to the given attempt on every call. Delays therefore depend only on the
|
|
173
|
+
// attempt count, with elapsed time simulated from the summed delays.
|
|
174
|
+
const retryDelay = Effect.fnUntraced(function*(
|
|
175
|
+
schedule: Schedule.Schedule<any, number>,
|
|
176
|
+
attempts: number
|
|
177
|
+
): Effect.fn.Return<Duration.Duration> {
|
|
178
|
+
const step = yield* Schedule.toStep(schedule)
|
|
179
|
+
let now = 0
|
|
180
|
+
let delay = Duration.zero
|
|
181
|
+
for (let i = 0; i < attempts; i++) {
|
|
182
|
+
const result = yield* Pull.catchDone(step(now, i + 1), () => Effect.undefined)
|
|
183
|
+
if (result === undefined) {
|
|
184
|
+
// the schedule is done, keep using its final delay
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
delay = result[1]
|
|
188
|
+
now += Duration.toMillis(delay)
|
|
189
|
+
}
|
|
190
|
+
return delay
|
|
191
|
+
})
|
|
192
|
+
|
|
131
193
|
/**
|
|
132
194
|
* Creates a `PersistedQueueFactory` from the current `PersistedQueueStore`.
|
|
133
195
|
*
|
|
@@ -147,10 +209,18 @@ export const makeFactory = Effect.gen(function*() {
|
|
|
147
209
|
make<S extends Schema.Constraint>(options: {
|
|
148
210
|
readonly name: string
|
|
149
211
|
readonly schema: S
|
|
212
|
+
readonly maxAttempts?: number | undefined
|
|
213
|
+
readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
|
|
150
214
|
}) {
|
|
151
215
|
const jsonSchema = Schema.toCodecJson(options.schema)
|
|
152
216
|
const encodeUnknown = Schema.encodeUnknownEffect(jsonSchema)
|
|
153
217
|
const decodeUnknown = Schema.decodeUnknownEffect(jsonSchema)
|
|
218
|
+
const retrySchedule = options.retrySchedule ?? defaultRetrySchedule
|
|
219
|
+
const takeOptions = {
|
|
220
|
+
name: options.name,
|
|
221
|
+
maxAttempts: options.maxAttempts ?? 10,
|
|
222
|
+
retryDelay: (attempts: number) => retryDelay(retrySchedule, attempts)
|
|
223
|
+
}
|
|
154
224
|
|
|
155
225
|
return Effect.succeed<PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>>({
|
|
156
226
|
[TypeId]: TypeId,
|
|
@@ -170,15 +240,38 @@ export const makeFactory = Effect.gen(function*() {
|
|
|
170
240
|
)
|
|
171
241
|
}
|
|
172
242
|
),
|
|
173
|
-
take:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
243
|
+
take: <XA, XE, XR>(
|
|
244
|
+
f: (value: S["Type"], metadata: {
|
|
245
|
+
readonly id: string
|
|
246
|
+
readonly attempts: number
|
|
247
|
+
}) => Effect.Effect<XA, XE, XR>
|
|
248
|
+
) => {
|
|
249
|
+
const loop: Effect.Effect<XA, any, any> = Effect.scopedWith((scope) =>
|
|
250
|
+
store.take(takeOptions).pipe(
|
|
251
|
+
Scope.provide(scope),
|
|
252
|
+
Effect.flatMap((item) =>
|
|
253
|
+
decodeUnknown(item.element).pipe(
|
|
254
|
+
Effect.catchCause((cause): Effect.Effect<never, DeadLetter> =>
|
|
255
|
+
Cause.hasInterruptsOnly(cause)
|
|
256
|
+
? Effect.failCause(cause as Cause.Cause<never>)
|
|
257
|
+
: Effect.fail(new DeadLetter(cause))
|
|
258
|
+
),
|
|
259
|
+
Effect.flatMap((value) => f(value as S["Type"], { id: item.id, attempts: item.attempts }))
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
).pipe(
|
|
264
|
+
Effect.catchIf(
|
|
265
|
+
isDeadLetter,
|
|
266
|
+
(): Effect.Effect<XA, any, any> => loop
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
return loop as Effect.Effect<
|
|
270
|
+
XA,
|
|
271
|
+
XE | PersistedQueueError,
|
|
272
|
+
XR | S["EncodingServices"] | S["DecodingServices"]
|
|
273
|
+
>
|
|
274
|
+
}
|
|
182
275
|
})
|
|
183
276
|
}
|
|
184
277
|
})
|
|
@@ -196,13 +289,55 @@ export const layer: Layer.Layer<
|
|
|
196
289
|
PersistedQueueStore
|
|
197
290
|
> = Layer.effect(PersistedQueueFactory, makeFactory)
|
|
198
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Runs `PersistedQueueStore.cleanup` on a schedule.
|
|
294
|
+
*
|
|
295
|
+
* **Details**
|
|
296
|
+
*
|
|
297
|
+
* Completed elements are retained for `timeToLive` (default 30 days) so
|
|
298
|
+
* offer de-duplication keeps working across replays, then removed. Failed
|
|
299
|
+
* elements are the dead-letter record and are kept forever unless
|
|
300
|
+
* `failedTimeToLive` is set.
|
|
301
|
+
*
|
|
302
|
+
* Run this layer in one instance of a deployment rather than on every worker;
|
|
303
|
+
* racing instances are harmless since deletes are idempotent, but the work is
|
|
304
|
+
* redundant.
|
|
305
|
+
*
|
|
306
|
+
* @category layers
|
|
307
|
+
* @since 4.0.0
|
|
308
|
+
*/
|
|
309
|
+
export const layerCleanup = (options?: {
|
|
310
|
+
readonly interval?: Duration.Input | undefined
|
|
311
|
+
readonly timeToLive?: Duration.Input | undefined
|
|
312
|
+
readonly failedTimeToLive?: Duration.Input | undefined
|
|
313
|
+
}): Layer.Layer<never, never, PersistedQueueStore> =>
|
|
314
|
+
Layer.effectDiscard(Effect.gen(function*() {
|
|
315
|
+
const store = yield* PersistedQueueStore
|
|
316
|
+
const cleanupOptions = {
|
|
317
|
+
timeToLive: Duration.fromInputUnsafe(options?.timeToLive ?? Duration.days(30)),
|
|
318
|
+
failedTimeToLive: options?.failedTimeToLive === undefined
|
|
319
|
+
? undefined
|
|
320
|
+
: Duration.fromInputUnsafe(options.failedTimeToLive)
|
|
321
|
+
}
|
|
322
|
+
yield* store.cleanup(cleanupOptions).pipe(
|
|
323
|
+
Effect.catchCause((cause) => Effect.logWarning("Failed to clean up persisted queue", cause)),
|
|
324
|
+
Effect.repeat(Schedule.spaced(options?.interval ?? Duration.hours(1))),
|
|
325
|
+
Effect.interruptible,
|
|
326
|
+
Effect.forkScoped,
|
|
327
|
+
Effect.annotateLogs({
|
|
328
|
+
module: "effect/persistence/PersistedQueue",
|
|
329
|
+
fiber: "cleanup"
|
|
330
|
+
})
|
|
331
|
+
)
|
|
332
|
+
}))
|
|
333
|
+
|
|
199
334
|
/**
|
|
200
335
|
* Runtime type identifier for `PersistedQueueError`.
|
|
201
336
|
*
|
|
202
337
|
* @category type IDs
|
|
203
338
|
* @since 4.0.0
|
|
204
339
|
*/
|
|
205
|
-
export const ErrorTypeId: ErrorTypeId = "
|
|
340
|
+
export const ErrorTypeId: ErrorTypeId = "~effect/persistence/PersistedQueue/PersistedQueueError"
|
|
206
341
|
|
|
207
342
|
/**
|
|
208
343
|
* Type-level identifier used to brand `PersistedQueueError` values.
|
|
@@ -210,7 +345,7 @@ export const ErrorTypeId: ErrorTypeId = "~@effect/experimental/PersistedQueue/Pe
|
|
|
210
345
|
* @category type IDs
|
|
211
346
|
* @since 4.0.0
|
|
212
347
|
*/
|
|
213
|
-
export type ErrorTypeId = "
|
|
348
|
+
export type ErrorTypeId = "~effect/persistence/PersistedQueue/PersistedQueueError"
|
|
214
349
|
|
|
215
350
|
/**
|
|
216
351
|
* Error raised by persisted queue store operations.
|
|
@@ -233,6 +368,64 @@ export class PersistedQueueError extends Schema.Error<PersistedQueueError>(
|
|
|
233
368
|
readonly [ErrorTypeId]: ErrorTypeId = ErrorTypeId
|
|
234
369
|
}
|
|
235
370
|
|
|
371
|
+
// Local per-queue state shared between offer, take, and the mailbox lookup in
|
|
372
|
+
// the Redis and SQL stores: take records maxAttempts here before the mailbox
|
|
373
|
+
// is created, and offer opens the nudge latch so local pollers skip the poll
|
|
374
|
+
// interval.
|
|
375
|
+
type QueueState = {
|
|
376
|
+
maxAttempts: number
|
|
377
|
+
readonly nudge: Latch.Latch
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const makeQueueStates = (): {
|
|
381
|
+
readonly peek: (name: string) => QueueState | undefined
|
|
382
|
+
readonly get: (name: string) => QueueState
|
|
383
|
+
} => {
|
|
384
|
+
const states = new Map<string, QueueState>()
|
|
385
|
+
return {
|
|
386
|
+
peek: (name) => states.get(name),
|
|
387
|
+
get: (name) => {
|
|
388
|
+
let state = states.get(name)
|
|
389
|
+
if (state === undefined) {
|
|
390
|
+
// the placeholder is overwritten by take before any mailbox reads it,
|
|
391
|
+
// and is kept int32-safe for SQL parameters
|
|
392
|
+
state = { maxAttempts: 2147483647, nudge: Latch.makeUnsafe(false) }
|
|
393
|
+
states.set(name, state)
|
|
394
|
+
}
|
|
395
|
+
return state
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const makeAckRetrySchedule = (lockExpiration: Duration.Input) =>
|
|
401
|
+
Schedule.min([
|
|
402
|
+
Schedule.exponential(200, 1.5),
|
|
403
|
+
Schedule.spaced(5000)
|
|
404
|
+
]).pipe(Schedule.upTo({ duration: lockExpiration }))
|
|
405
|
+
|
|
406
|
+
// Internal signal used by the factory to tell a store that a taken element
|
|
407
|
+
// cannot ever be processed (its stored payload fails to decode) and must be
|
|
408
|
+
// dead-lettered instead of retried.
|
|
409
|
+
class DeadLetter {
|
|
410
|
+
readonly _tag = "~effect/persistence/PersistedQueue/DeadLetter"
|
|
411
|
+
readonly cause: Cause.Cause<unknown>
|
|
412
|
+
constructor(cause: Cause.Cause<unknown>) {
|
|
413
|
+
this.cause = cause
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const isDeadLetter = (u: unknown): u is DeadLetter =>
|
|
418
|
+
Predicate.isTagged(u, "~effect/persistence/PersistedQueue/DeadLetter")
|
|
419
|
+
|
|
420
|
+
const deadLetterFromCause = (cause: Cause.Cause<unknown>): DeadLetter | undefined => {
|
|
421
|
+
for (const reason of cause.reasons) {
|
|
422
|
+
if (Cause.isFailReason(reason) && isDeadLetter(reason.error)) {
|
|
423
|
+
return reason.error
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return undefined
|
|
427
|
+
}
|
|
428
|
+
|
|
236
429
|
/**
|
|
237
430
|
* Defines the low-level backing store service used by `PersistedQueue`.
|
|
238
431
|
*
|
|
@@ -246,6 +439,12 @@ export class PersistedQueueError extends Schema.Error<PersistedQueueError>(
|
|
|
246
439
|
* The store persists offered elements and returns taken elements in a scope so
|
|
247
440
|
* the finalizer can complete or retry them based on the processing exit.
|
|
248
441
|
*
|
|
442
|
+
* Claiming an element counts an attempt, so the `attempts` returned by `take`
|
|
443
|
+
* is 1-based. When the take scope closes with a success the element is marked
|
|
444
|
+
* completed; a failure retries it according to `retryDelay` or marks it failed
|
|
445
|
+
* once `maxAttempts` is exhausted; an interruption releases it without
|
|
446
|
+
* counting the attempt.
|
|
447
|
+
*
|
|
249
448
|
* @category services
|
|
250
449
|
* @since 4.0.0
|
|
251
450
|
*/
|
|
@@ -264,6 +463,7 @@ export class PersistedQueueStore extends Context.Service<
|
|
|
264
463
|
readonly take: (options: {
|
|
265
464
|
readonly name: string
|
|
266
465
|
readonly maxAttempts: number
|
|
466
|
+
readonly retryDelay: (attempts: number) => Effect.Effect<Duration.Duration>
|
|
267
467
|
}) => Effect.Effect<
|
|
268
468
|
{
|
|
269
469
|
readonly id: string
|
|
@@ -273,6 +473,16 @@ export class PersistedQueueStore extends Context.Service<
|
|
|
273
473
|
PersistedQueueError,
|
|
274
474
|
Scope.Scope
|
|
275
475
|
>
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Removes completed elements older than `timeToLive`, together with their
|
|
479
|
+
* de-duplication records. Failed elements are removed only when
|
|
480
|
+
* `failedTimeToLive` is provided.
|
|
481
|
+
*/
|
|
482
|
+
readonly cleanup: (options: {
|
|
483
|
+
readonly timeToLive: Duration.Duration
|
|
484
|
+
readonly failedTimeToLive: Duration.Duration | undefined
|
|
485
|
+
}) => Effect.Effect<void, PersistedQueueError>
|
|
276
486
|
}
|
|
277
487
|
>()("effect/persistence/PersistedQueue/PersistedQueueStore") {}
|
|
278
488
|
|
|
@@ -281,83 +491,156 @@ export class PersistedQueueStore extends Context.Service<
|
|
|
281
491
|
*
|
|
282
492
|
* **Details**
|
|
283
493
|
*
|
|
284
|
-
* The store is process-local and volatile; failed takes are requeued
|
|
285
|
-
* configured maximum attempts is reached
|
|
494
|
+
* The store is process-local and volatile; failed takes are requeued with the
|
|
495
|
+
* queue's retry schedule until the configured maximum attempts is reached,
|
|
496
|
+
* after which the element is marked as failed.
|
|
286
497
|
*
|
|
287
498
|
* @category layers
|
|
288
499
|
* @since 4.0.0
|
|
289
500
|
*/
|
|
290
501
|
export const layerStoreMemory: Layer.Layer<
|
|
291
502
|
PersistedQueueStore
|
|
292
|
-
> = Layer.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
503
|
+
> = Layer.effect(
|
|
504
|
+
PersistedQueueStore,
|
|
505
|
+
Effect.gen(function*() {
|
|
506
|
+
const clock = yield* Clock.Clock
|
|
507
|
+
type Entry = {
|
|
508
|
+
readonly id: string
|
|
509
|
+
readonly element: unknown
|
|
510
|
+
attempts: number
|
|
511
|
+
state: "pending" | "processing" | "completed" | "failed"
|
|
512
|
+
visibleAt: number
|
|
513
|
+
stateChangedAt: number
|
|
514
|
+
}
|
|
515
|
+
const queues = new Map<string, {
|
|
516
|
+
latch: Latch.Latch
|
|
517
|
+
// all entries ever offered, for de-duplication; `pending` holds the
|
|
518
|
+
// deliverable subset so take does not scan the completed backlog
|
|
519
|
+
entries: Map<string, Entry>
|
|
520
|
+
pending: Set<Entry>
|
|
521
|
+
}>()
|
|
522
|
+
const getOrCreateQueue = (name: string) => {
|
|
523
|
+
let queue = queues.get(name)
|
|
524
|
+
if (!queue) {
|
|
525
|
+
queue = {
|
|
526
|
+
latch: Latch.makeUnsafe(false),
|
|
527
|
+
entries: new Map(),
|
|
528
|
+
pending: new Set()
|
|
529
|
+
}
|
|
530
|
+
queues.set(name, queue)
|
|
310
531
|
}
|
|
311
|
-
|
|
532
|
+
return queue
|
|
312
533
|
}
|
|
313
|
-
return queue
|
|
314
|
-
}
|
|
315
534
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
535
|
+
return PersistedQueueStore.of({
|
|
536
|
+
offer: (options) =>
|
|
537
|
+
Effect.sync(() => {
|
|
538
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
539
|
+
const queue = getOrCreateQueue(options.name)
|
|
540
|
+
if (queue.entries.has(options.id)) return
|
|
541
|
+
const entry: Entry = {
|
|
542
|
+
id: options.id,
|
|
543
|
+
element: options.element,
|
|
544
|
+
attempts: 0,
|
|
545
|
+
state: "pending",
|
|
546
|
+
visibleAt: now,
|
|
547
|
+
stateChangedAt: now
|
|
548
|
+
}
|
|
549
|
+
queue.entries.set(options.id, entry)
|
|
550
|
+
queue.pending.add(entry)
|
|
551
|
+
queue.latch.openUnsafe()
|
|
552
|
+
}),
|
|
553
|
+
take: Effect.fnUntraced(function*(options) {
|
|
319
554
|
const queue = getOrCreateQueue(options.name)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
queue.items.add({ id: options.id, attempts: 0, element: options.element })
|
|
323
|
-
queue.latch.openUnsafe()
|
|
324
|
-
}),
|
|
325
|
-
take: Effect.fnUntraced(function*(options) {
|
|
326
|
-
const queue = getOrCreateQueue(options.name)
|
|
327
|
-
while (true) {
|
|
328
|
-
yield* queue.latch.await
|
|
329
|
-
const item = Iterable.headUnsafe(queue.items)
|
|
330
|
-
queue.items.delete(item)
|
|
331
|
-
if (queue.items.size === 0) {
|
|
555
|
+
while (true) {
|
|
556
|
+
// close before scanning so a mutation after the scan reopens the latch
|
|
332
557
|
queue.latch.closeUnsafe()
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
558
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
559
|
+
let item: Entry | undefined
|
|
560
|
+
let nextVisibleAt = Infinity
|
|
561
|
+
for (const entry of queue.pending) {
|
|
562
|
+
if (entry.attempts >= options.maxAttempts) continue
|
|
563
|
+
if (entry.visibleAt <= now) {
|
|
564
|
+
item = entry
|
|
565
|
+
break
|
|
566
|
+
}
|
|
567
|
+
nextVisibleAt = Math.min(nextVisibleAt, entry.visibleAt)
|
|
339
568
|
}
|
|
340
|
-
if (item
|
|
341
|
-
|
|
569
|
+
if (item === undefined) {
|
|
570
|
+
yield* nextVisibleAt === Infinity
|
|
571
|
+
? queue.latch.await
|
|
572
|
+
: Effect.race(queue.latch.await, Effect.sleep(Duration.millis(nextVisibleAt - now)))
|
|
573
|
+
continue
|
|
342
574
|
}
|
|
343
|
-
|
|
575
|
+
const entry = item
|
|
576
|
+
entry.state = "processing"
|
|
577
|
+
entry.attempts += 1
|
|
578
|
+
queue.pending.delete(entry)
|
|
344
579
|
queue.latch.openUnsafe()
|
|
345
|
-
|
|
580
|
+
yield* Effect.addFinalizer(
|
|
581
|
+
Effect.fnUntraced(function*(exit) {
|
|
582
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
583
|
+
if (exit._tag === "Success") {
|
|
584
|
+
entry.state = "completed"
|
|
585
|
+
entry.stateChangedAt = now
|
|
586
|
+
return
|
|
587
|
+
}
|
|
588
|
+
const deadLetter = deadLetterFromCause(exit.cause)
|
|
589
|
+
if (deadLetter !== undefined) {
|
|
590
|
+
entry.state = "failed"
|
|
591
|
+
entry.stateChangedAt = now
|
|
592
|
+
return
|
|
593
|
+
}
|
|
594
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
595
|
+
entry.attempts -= 1
|
|
596
|
+
} else if (entry.attempts >= options.maxAttempts) {
|
|
597
|
+
entry.state = "failed"
|
|
598
|
+
entry.stateChangedAt = now
|
|
599
|
+
return
|
|
600
|
+
} else {
|
|
601
|
+
entry.visibleAt = now + Duration.toMillis(yield* options.retryDelay(entry.attempts))
|
|
602
|
+
}
|
|
603
|
+
entry.state = "pending"
|
|
604
|
+
queue.pending.add(entry)
|
|
605
|
+
queue.latch.openUnsafe()
|
|
606
|
+
})
|
|
607
|
+
)
|
|
608
|
+
return { id: entry.id, attempts: entry.attempts, element: entry.element }
|
|
609
|
+
}
|
|
610
|
+
}),
|
|
611
|
+
cleanup: (options) =>
|
|
612
|
+
Effect.sync(() => {
|
|
613
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
614
|
+
const completedCutoff = now - Duration.toMillis(options.timeToLive)
|
|
615
|
+
const failedCutoff = options.failedTimeToLive === undefined
|
|
616
|
+
? undefined
|
|
617
|
+
: now - Duration.toMillis(options.failedTimeToLive)
|
|
618
|
+
for (const queue of queues.values()) {
|
|
619
|
+
for (const [id, entry] of queue.entries) {
|
|
620
|
+
const cutoff = entry.state === "completed"
|
|
621
|
+
? completedCutoff
|
|
622
|
+
: entry.state === "failed"
|
|
623
|
+
? failedCutoff
|
|
624
|
+
: undefined
|
|
625
|
+
if (cutoff !== undefined && entry.stateChangedAt <= cutoff) {
|
|
626
|
+
queue.entries.delete(id)
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
346
630
|
})
|
|
347
|
-
return item
|
|
348
|
-
}
|
|
349
631
|
})
|
|
350
632
|
})
|
|
351
|
-
|
|
633
|
+
)
|
|
352
634
|
|
|
353
635
|
/**
|
|
354
636
|
* Creates a Redis-backed `PersistedQueueStore`.
|
|
355
637
|
*
|
|
356
638
|
* **Details**
|
|
357
639
|
*
|
|
358
|
-
* The store uses Redis lists and
|
|
359
|
-
* refreshes locks while items are being processed,
|
|
360
|
-
* to a failed
|
|
640
|
+
* The store uses Redis lists, hashes, and sorted sets with worker locks,
|
|
641
|
+
* periodically refreshes locks while items are being processed, delays retried
|
|
642
|
+
* items with the queue's retry schedule, and moves exhausted items to a failed
|
|
643
|
+
* queue.
|
|
361
644
|
*
|
|
362
645
|
* @category constructors
|
|
363
646
|
* @since 4.0.0
|
|
@@ -371,6 +654,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
371
654
|
}
|
|
372
655
|
) {
|
|
373
656
|
const redis = yield* Redis.Redis
|
|
657
|
+
const clock = yield* Clock.Clock
|
|
374
658
|
|
|
375
659
|
const pollInterval = Duration.max(
|
|
376
660
|
options?.pollInterval ? Duration.fromInputUnsafe(options.pollInterval) : Duration.seconds(1),
|
|
@@ -389,35 +673,47 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
389
673
|
1
|
|
390
674
|
)
|
|
391
675
|
const prefix = options?.prefix ?? "effectq:"
|
|
392
|
-
const keyQueue = (name: string) => `${prefix}${name}`
|
|
393
676
|
const keyLock = (id: string) => `${prefix}${id}:lock`
|
|
394
|
-
const
|
|
395
|
-
|
|
677
|
+
const keysFor = (name: string) => ({
|
|
678
|
+
queue: `${prefix}${name}`,
|
|
679
|
+
pending: `${prefix}${name}:pending`,
|
|
680
|
+
failed: `${prefix}${name}:failed`,
|
|
681
|
+
delayed: `${prefix}${name}:delayed`,
|
|
682
|
+
attempts: `${prefix}${name}:attempts`,
|
|
683
|
+
ids: `${prefix}${name}:ids`
|
|
684
|
+
})
|
|
396
685
|
const workerId = crypto.randomUUID()
|
|
397
686
|
|
|
687
|
+
const ackRetrySchedule = makeAckRetrySchedule(lockExpirationMillis)
|
|
688
|
+
|
|
398
689
|
type Element = {
|
|
399
690
|
readonly id: string
|
|
400
691
|
readonly element: unknown
|
|
401
|
-
attempts: number
|
|
402
|
-
|
|
692
|
+
readonly attempts: number
|
|
693
|
+
// the raw wire payload, so requeue and retry do not re-stringify it
|
|
694
|
+
readonly payload: string
|
|
403
695
|
}
|
|
404
696
|
|
|
405
697
|
const requeue = redis.eval(requeueRedis)
|
|
406
698
|
const complete = redis.eval(completeRedis)
|
|
407
699
|
const failed = redis.eval(failedRedis)
|
|
700
|
+
const retry = redis.eval(retryRedis)
|
|
408
701
|
const resetQueue = redis.eval(resetQueueRedis)
|
|
409
702
|
const offer = redis.eval(offerRedis)
|
|
410
703
|
const take = redis.eval(takeRedis)
|
|
411
704
|
const expireAll = redis.eval(expireAllRedis)
|
|
705
|
+
const trimFailed = redis.eval(trimFailedRedis)
|
|
706
|
+
|
|
707
|
+
const queueStates = makeQueueStates()
|
|
412
708
|
|
|
413
709
|
const queues = yield* RcMap.make({
|
|
414
710
|
lookup: Effect.fnUntraced(function*(name: string) {
|
|
415
|
-
const
|
|
416
|
-
const pendingKey = keyPending(name)
|
|
711
|
+
const keys = keysFor(name)
|
|
417
712
|
const queue = yield* Queue.make<Element>()
|
|
418
713
|
const takers = MutableRef.make(0)
|
|
419
714
|
const pollLatch = Latch.makeUnsafe()
|
|
420
715
|
const takenLatch = Latch.makeUnsafe()
|
|
716
|
+
const state = queueStates.get(name)
|
|
421
717
|
|
|
422
718
|
yield* Effect.addFinalizer(() =>
|
|
423
719
|
Effect.orDie(
|
|
@@ -426,47 +722,77 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
426
722
|
(elements) =>
|
|
427
723
|
Effect.forEach(elements, (element) =>
|
|
428
724
|
requeue(
|
|
429
|
-
|
|
430
|
-
|
|
725
|
+
keys.queue,
|
|
726
|
+
keys.pending,
|
|
431
727
|
keyLock(element.id),
|
|
728
|
+
keys.attempts,
|
|
432
729
|
element.id,
|
|
433
|
-
|
|
730
|
+
element.payload
|
|
434
731
|
), { concurrency: "unbounded", discard: true })
|
|
435
732
|
)
|
|
436
733
|
)
|
|
437
734
|
)
|
|
438
735
|
|
|
439
|
-
yield*
|
|
736
|
+
yield* Effect.suspend(() =>
|
|
737
|
+
resetQueue(
|
|
738
|
+
keys.queue,
|
|
739
|
+
keys.pending,
|
|
740
|
+
keys.attempts,
|
|
741
|
+
keys.failed,
|
|
742
|
+
keys.ids,
|
|
743
|
+
prefix,
|
|
744
|
+
state.maxAttempts,
|
|
745
|
+
clock.currentTimeMillisUnsafe()
|
|
746
|
+
)
|
|
747
|
+
).pipe(
|
|
440
748
|
Effect.andThen(Effect.sleep(lockRefreshMillis)),
|
|
441
749
|
Effect.forever,
|
|
442
750
|
Effect.forkScoped
|
|
443
751
|
)
|
|
444
752
|
|
|
445
753
|
const poll = (size: number) =>
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
754
|
+
Effect.suspend(() =>
|
|
755
|
+
take(
|
|
756
|
+
keys.queue,
|
|
757
|
+
keys.pending,
|
|
758
|
+
keys.delayed,
|
|
759
|
+
keys.attempts,
|
|
760
|
+
prefix,
|
|
761
|
+
workerId,
|
|
762
|
+
size,
|
|
763
|
+
lockExpirationMillis,
|
|
764
|
+
clock.currentTimeMillisUnsafe()
|
|
765
|
+
)
|
|
453
766
|
)
|
|
454
767
|
|
|
455
768
|
yield* Effect.gen(function*() {
|
|
456
769
|
while (true) {
|
|
457
770
|
yield* pollLatch.await
|
|
458
771
|
yield* Effect.yieldNow
|
|
772
|
+
state.nudge.closeUnsafe()
|
|
459
773
|
const results = takers.current === 0 ? null : yield* poll(takers.current)
|
|
460
|
-
if (results === null) {
|
|
461
|
-
yield* Effect.sleep(pollInterval)
|
|
774
|
+
if (results === null || results.length === 0) {
|
|
775
|
+
yield* Effect.race(Effect.sleep(pollInterval), state.nudge.await)
|
|
462
776
|
continue
|
|
463
777
|
}
|
|
464
778
|
takenLatch.closeUnsafe()
|
|
465
|
-
|
|
779
|
+
const elements: Array<Element> = []
|
|
780
|
+
for (let i = 0; i < results.length; i += 2) {
|
|
781
|
+
const payload = results[i] as string
|
|
782
|
+
const parsed = JSON.parse(payload)
|
|
783
|
+
elements.push({
|
|
784
|
+
id: parsed.id,
|
|
785
|
+
element: parsed.element,
|
|
786
|
+
attempts: Number(results[i + 1]),
|
|
787
|
+
payload
|
|
788
|
+
})
|
|
789
|
+
}
|
|
790
|
+
yield* Queue.offerAll(queue, elements)
|
|
466
791
|
yield* takenLatch.await
|
|
467
792
|
yield* Effect.yieldNow
|
|
468
793
|
}
|
|
469
794
|
}).pipe(
|
|
795
|
+
Effect.tapCause(Effect.logWarning),
|
|
470
796
|
Effect.sandbox,
|
|
471
797
|
Effect.retry(Schedule.spaced(500)),
|
|
472
798
|
Effect.forkScoped,
|
|
@@ -494,32 +820,58 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
494
820
|
})
|
|
495
821
|
)
|
|
496
822
|
|
|
823
|
+
const scanKeys = (pattern: string) =>
|
|
824
|
+
Effect.gen(function*() {
|
|
825
|
+
const keys: Array<string> = []
|
|
826
|
+
let cursor = "0"
|
|
827
|
+
do {
|
|
828
|
+
const [next, batch] = yield* redis.send<[string, Array<string>]>(
|
|
829
|
+
"SCAN",
|
|
830
|
+
cursor,
|
|
831
|
+
"MATCH",
|
|
832
|
+
pattern,
|
|
833
|
+
"COUNT",
|
|
834
|
+
"100"
|
|
835
|
+
)
|
|
836
|
+
cursor = next
|
|
837
|
+
for (const key of batch) keys.push(key)
|
|
838
|
+
} while (cursor !== "0")
|
|
839
|
+
return keys
|
|
840
|
+
})
|
|
841
|
+
|
|
497
842
|
return PersistedQueueStore.of({
|
|
498
|
-
offer: ({ element, id, isCustomId, name }) =>
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
843
|
+
offer: ({ element, id, isCustomId, name }) => {
|
|
844
|
+
const keys = keysFor(name)
|
|
845
|
+
const payload = JSON.stringify({ id, element })
|
|
846
|
+
return (isCustomId
|
|
847
|
+
? offer(keys.queue, keys.ids, id, payload)
|
|
848
|
+
: redis.send("RPUSH", keys.queue, payload)).pipe(
|
|
849
|
+
Effect.mapError(({ cause }) =>
|
|
850
|
+
new PersistedQueueError({
|
|
851
|
+
message: "Failed to offer element to persisted queue",
|
|
852
|
+
cause
|
|
853
|
+
})
|
|
854
|
+
),
|
|
855
|
+
Effect.tap(() =>
|
|
856
|
+
Effect.sync(() => {
|
|
857
|
+
queueStates.peek(name)?.nudge.openUnsafe()
|
|
858
|
+
})
|
|
506
859
|
)
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
new PersistedQueueError({
|
|
510
|
-
message: "Failed to offer element to persisted queue",
|
|
511
|
-
cause
|
|
512
|
-
})
|
|
513
|
-
),
|
|
860
|
+
)
|
|
861
|
+
},
|
|
514
862
|
take: (options) =>
|
|
515
|
-
Effect.uninterruptibleMask((restore) =>
|
|
516
|
-
|
|
863
|
+
Effect.uninterruptibleMask((restore) => {
|
|
864
|
+
queueStates.get(options.name).maxAttempts = options.maxAttempts
|
|
865
|
+
return RcMap.get(queues, options.name).pipe(
|
|
517
866
|
Effect.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
|
|
518
867
|
takers.current++
|
|
519
868
|
if (takers.current === 1) {
|
|
520
869
|
pollLatch.openUnsafe()
|
|
521
870
|
}
|
|
522
|
-
|
|
871
|
+
// onExit so the decrement also runs when a waiting take is
|
|
872
|
+
// interrupted, otherwise the poller keeps fetching for a phantom
|
|
873
|
+
// taker
|
|
874
|
+
return Effect.onExit(restore(Queue.take(queue)), () =>
|
|
523
875
|
Effect.sync(() => {
|
|
524
876
|
takers.current--
|
|
525
877
|
if (takers.current === 0) {
|
|
@@ -532,50 +884,89 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
532
884
|
}),
|
|
533
885
|
Effect.scoped,
|
|
534
886
|
Effect.tap((element) => {
|
|
887
|
+
const keys = keysFor(options.name)
|
|
535
888
|
const lock = keyLock(element.id)
|
|
536
889
|
activeLockKeys.add(lock)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
890
|
+
const ack = (effect: Effect.Effect<unknown, Redis.RedisError>) =>
|
|
891
|
+
effect.pipe(
|
|
892
|
+
Effect.retry(ackRetrySchedule),
|
|
893
|
+
Effect.orDie,
|
|
894
|
+
Effect.ensuring(Effect.sync(() => activeLockKeys.delete(lock)))
|
|
895
|
+
)
|
|
896
|
+
return Effect.addFinalizer((exit) =>
|
|
897
|
+
Effect.suspend(() => {
|
|
898
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
899
|
+
if (exit._tag === "Success") {
|
|
900
|
+
return ack(complete(keys.pending, lock, keys.attempts, keys.ids, element.id, now))
|
|
901
|
+
}
|
|
902
|
+
const dead = deadLetterFromCause(exit.cause)
|
|
903
|
+
const failElement = (cause: Cause.Cause<unknown>) =>
|
|
904
|
+
ack(failed(
|
|
905
|
+
keys.pending,
|
|
544
906
|
lock,
|
|
545
|
-
|
|
907
|
+
keys.failed,
|
|
908
|
+
keys.attempts,
|
|
909
|
+
keys.ids,
|
|
546
910
|
element.id,
|
|
547
911
|
JSON.stringify({
|
|
548
|
-
|
|
912
|
+
id: element.id,
|
|
913
|
+
element: element.element,
|
|
914
|
+
attempts: element.attempts,
|
|
549
915
|
lastFailure: Cause.pretty(cause),
|
|
550
|
-
|
|
916
|
+
failedAt: now
|
|
551
917
|
})
|
|
552
918
|
))
|
|
919
|
+
if (dead !== undefined) {
|
|
920
|
+
return failElement(dead.cause)
|
|
553
921
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
922
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
923
|
+
return ack(requeue(keys.queue, keys.pending, lock, keys.attempts, element.id, element.payload))
|
|
924
|
+
}
|
|
925
|
+
if (element.attempts >= options.maxAttempts) {
|
|
926
|
+
return failElement(exit.cause)
|
|
927
|
+
}
|
|
928
|
+
return Effect.flatMap(options.retryDelay(element.attempts), (delay) =>
|
|
929
|
+
ack(retry(
|
|
930
|
+
keys.pending,
|
|
931
|
+
lock,
|
|
932
|
+
keys.delayed,
|
|
933
|
+
element.id,
|
|
934
|
+
element.payload,
|
|
935
|
+
now + Duration.toMillis(delay)
|
|
936
|
+
)))
|
|
937
|
+
})
|
|
938
|
+
)
|
|
939
|
+
})
|
|
940
|
+
)
|
|
941
|
+
}),
|
|
942
|
+
cleanup: ({ failedTimeToLive, timeToLive }) =>
|
|
943
|
+
Effect.gen(function*() {
|
|
944
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
945
|
+
const idsKeys = yield* scanKeys(`${prefix}*:ids`)
|
|
946
|
+
const cutoff = now - Duration.toMillis(timeToLive)
|
|
947
|
+
yield* Effect.forEach(
|
|
948
|
+
idsKeys,
|
|
949
|
+
(key) => redis.send("ZREMRANGEBYSCORE", key, "-inf", `(${cutoff}`),
|
|
950
|
+
{ concurrency: 16, discard: true }
|
|
951
|
+
)
|
|
952
|
+
if (failedTimeToLive !== undefined) {
|
|
953
|
+
const failedCutoff = now - Duration.toMillis(failedTimeToLive)
|
|
954
|
+
const failedKeys = yield* scanKeys(`${prefix}*:failed`)
|
|
955
|
+
yield* Effect.forEach(
|
|
956
|
+
failedKeys,
|
|
957
|
+
(key) =>
|
|
958
|
+
trimFailed(key, `${key.slice(0, -":failed".length)}:ids`, failedCutoff).pipe(
|
|
959
|
+
// each call trims at most one batch, so drain until done
|
|
960
|
+
Effect.repeat({ while: (removed) => removed >= trimFailedBatchSize })
|
|
961
|
+
),
|
|
962
|
+
{ concurrency: 16, discard: true }
|
|
963
|
+
)
|
|
964
|
+
}
|
|
965
|
+
}).pipe(
|
|
966
|
+
Effect.mapError(({ cause }) =>
|
|
967
|
+
new PersistedQueueError({
|
|
968
|
+
message: "Failed to clean up persisted queue",
|
|
969
|
+
cause
|
|
579
970
|
})
|
|
580
971
|
)
|
|
581
972
|
)
|
|
@@ -591,7 +982,9 @@ local key_ids = KEYS[2]
|
|
|
591
982
|
local id = ARGV[1]
|
|
592
983
|
local payload = ARGV[2]
|
|
593
984
|
|
|
594
|
-
|
|
985
|
+
-- park the dedupe entry outside the timeToLive trim range until the element
|
|
986
|
+
-- completes, so unprocessed elements never lose dedupe protection
|
|
987
|
+
local result = redis.call("ZADD", key_ids, "NX", "+inf", id)
|
|
595
988
|
if result == 1 then
|
|
596
989
|
redis.call("RPUSH", key_queue, payload)
|
|
597
990
|
end
|
|
@@ -601,12 +994,28 @@ end
|
|
|
601
994
|
)
|
|
602
995
|
|
|
603
996
|
const resetQueueRedis = Redis.script(
|
|
604
|
-
(
|
|
997
|
+
(
|
|
998
|
+
...args: [
|
|
999
|
+
keyQueue: string,
|
|
1000
|
+
keyPending: string,
|
|
1001
|
+
keyAttempts: string,
|
|
1002
|
+
keyFailed: string,
|
|
1003
|
+
keyIds: string,
|
|
1004
|
+
prefix: string,
|
|
1005
|
+
maxAttempts: number,
|
|
1006
|
+
now: number
|
|
1007
|
+
]
|
|
1008
|
+
) => args,
|
|
605
1009
|
{
|
|
606
1010
|
lua: `
|
|
607
1011
|
local key_queue = KEYS[1]
|
|
608
1012
|
local key_pending = KEYS[2]
|
|
1013
|
+
local key_attempts = KEYS[3]
|
|
1014
|
+
local key_failed = KEYS[4]
|
|
1015
|
+
local key_ids = KEYS[5]
|
|
609
1016
|
local prefix = ARGV[1]
|
|
1017
|
+
local max_attempts = tonumber(ARGV[2])
|
|
1018
|
+
local now = ARGV[3]
|
|
610
1019
|
|
|
611
1020
|
local entries = redis.call("HGETALL", key_pending)
|
|
612
1021
|
for i = 1, #entries, 2 do
|
|
@@ -615,96 +1024,184 @@ for i = 1, #entries, 2 do
|
|
|
615
1024
|
local lock_key = prefix .. id .. ":lock"
|
|
616
1025
|
local exists = redis.call("EXISTS", lock_key)
|
|
617
1026
|
if exists == 0 then
|
|
618
|
-
redis.call("
|
|
1027
|
+
local attempts = tonumber(redis.call("HGET", key_attempts, id) or "0")
|
|
1028
|
+
if attempts >= max_attempts then
|
|
1029
|
+
-- compose the failed record by hand so the element payload does not go
|
|
1030
|
+
-- through a lossy cjson round-trip
|
|
1031
|
+
local record = string.sub(payload, 1, -2) .. ',"attempts":' .. attempts ..
|
|
1032
|
+
',"lastFailure":"Lock expired after final attempt","failedAt":' .. now .. '}'
|
|
1033
|
+
redis.call("RPUSH", key_failed, record)
|
|
1034
|
+
redis.call("HDEL", key_attempts, id)
|
|
1035
|
+
-- failed ids keep their dedupe entry until failedTimeToLive removes the
|
|
1036
|
+
-- dead-letter record, so park them outside the timeToLive trim range
|
|
1037
|
+
redis.call("ZADD", key_ids, "XX", "+inf", id)
|
|
1038
|
+
else
|
|
1039
|
+
redis.call("RPUSH", key_queue, payload)
|
|
1040
|
+
end
|
|
619
1041
|
redis.call("HDEL", key_pending, id)
|
|
620
1042
|
end
|
|
621
1043
|
end
|
|
622
1044
|
`,
|
|
623
|
-
numberOfKeys:
|
|
1045
|
+
numberOfKeys: 5
|
|
624
1046
|
}
|
|
625
1047
|
)
|
|
626
1048
|
|
|
627
1049
|
const requeueRedis = Redis.script(
|
|
628
|
-
(
|
|
1050
|
+
(
|
|
1051
|
+
...args: [keyQueue: string, keyPending: string, keyLock: string, keyAttempts: string, id: string, payload: string]
|
|
1052
|
+
) => args,
|
|
629
1053
|
{
|
|
630
1054
|
lua: `
|
|
631
1055
|
local key_queue = KEYS[1]
|
|
632
1056
|
local key_pending = KEYS[2]
|
|
633
1057
|
local key_lock = KEYS[3]
|
|
1058
|
+
local key_attempts = KEYS[4]
|
|
634
1059
|
local id = ARGV[1]
|
|
635
1060
|
local payload = ARGV[2]
|
|
636
1061
|
|
|
637
1062
|
redis.call("DEL", key_lock)
|
|
638
1063
|
redis.call("HDEL", key_pending, id)
|
|
1064
|
+
local attempts = redis.call("HINCRBY", key_attempts, id, -1)
|
|
1065
|
+
if attempts <= 0 then
|
|
1066
|
+
redis.call("HDEL", key_attempts, id)
|
|
1067
|
+
end
|
|
639
1068
|
redis.call("RPUSH", key_queue, payload)
|
|
640
1069
|
`,
|
|
641
|
-
numberOfKeys:
|
|
1070
|
+
numberOfKeys: 4
|
|
642
1071
|
}
|
|
643
1072
|
)
|
|
644
1073
|
|
|
645
1074
|
const completeRedis = Redis.script(
|
|
646
|
-
(...args: [keyPending: string, keyLock: string, id: string]) =>
|
|
1075
|
+
(...args: [keyPending: string, keyLock: string, keyAttempts: string, keyIds: string, id: string, now: number]) =>
|
|
1076
|
+
args,
|
|
647
1077
|
{
|
|
648
1078
|
lua: `
|
|
649
1079
|
local key_pending = KEYS[1]
|
|
650
1080
|
local key_lock = KEYS[2]
|
|
1081
|
+
local key_attempts = KEYS[3]
|
|
1082
|
+
local key_ids = KEYS[4]
|
|
651
1083
|
local id = ARGV[1]
|
|
1084
|
+
local now = ARGV[2]
|
|
652
1085
|
|
|
653
1086
|
redis.call("DEL", key_lock)
|
|
654
1087
|
redis.call("HDEL", key_pending, id)
|
|
1088
|
+
redis.call("HDEL", key_attempts, id)
|
|
1089
|
+
redis.call("ZADD", key_ids, "XX", now, id)
|
|
655
1090
|
`,
|
|
656
|
-
numberOfKeys:
|
|
1091
|
+
numberOfKeys: 4
|
|
1092
|
+
}
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
const retryRedis = Redis.script(
|
|
1096
|
+
(
|
|
1097
|
+
...args: [keyPending: string, keyLock: string, keyDelayed: string, id: string, payload: string, visibleAt: number]
|
|
1098
|
+
) => args,
|
|
1099
|
+
{
|
|
1100
|
+
lua: `
|
|
1101
|
+
local key_pending = KEYS[1]
|
|
1102
|
+
local key_lock = KEYS[2]
|
|
1103
|
+
local key_delayed = KEYS[3]
|
|
1104
|
+
local id = ARGV[1]
|
|
1105
|
+
local payload = ARGV[2]
|
|
1106
|
+
local visible_at = ARGV[3]
|
|
1107
|
+
|
|
1108
|
+
redis.call("DEL", key_lock)
|
|
1109
|
+
redis.call("HDEL", key_pending, id)
|
|
1110
|
+
redis.call("ZADD", key_delayed, visible_at, payload)
|
|
1111
|
+
`,
|
|
1112
|
+
numberOfKeys: 3
|
|
657
1113
|
}
|
|
658
1114
|
)
|
|
659
1115
|
|
|
660
1116
|
const failedRedis = Redis.script(
|
|
661
|
-
(
|
|
1117
|
+
(
|
|
1118
|
+
...args: [
|
|
1119
|
+
keyPending: string,
|
|
1120
|
+
keyLock: string,
|
|
1121
|
+
keyFailed: string,
|
|
1122
|
+
keyAttempts: string,
|
|
1123
|
+
keyIds: string,
|
|
1124
|
+
id: string,
|
|
1125
|
+
payload: string
|
|
1126
|
+
]
|
|
1127
|
+
) => args,
|
|
662
1128
|
{
|
|
663
1129
|
lua: `
|
|
664
1130
|
local key_pending = KEYS[1]
|
|
665
1131
|
local key_lock = KEYS[2]
|
|
666
1132
|
local key_failed = KEYS[3]
|
|
1133
|
+
local key_attempts = KEYS[4]
|
|
1134
|
+
local key_ids = KEYS[5]
|
|
667
1135
|
local id = ARGV[1]
|
|
668
1136
|
local payload = ARGV[2]
|
|
669
1137
|
|
|
670
1138
|
redis.call("DEL", key_lock)
|
|
671
1139
|
redis.call("HDEL", key_pending, id)
|
|
1140
|
+
redis.call("HDEL", key_attempts, id)
|
|
672
1141
|
redis.call("RPUSH", key_failed, payload)
|
|
1142
|
+
-- failed ids keep their dedupe entry until failedTimeToLive removes the
|
|
1143
|
+
-- dead-letter record, so park them outside the timeToLive trim range
|
|
1144
|
+
redis.call("ZADD", key_ids, "XX", "+inf", id)
|
|
673
1145
|
`,
|
|
674
|
-
numberOfKeys:
|
|
1146
|
+
numberOfKeys: 5
|
|
675
1147
|
}
|
|
676
1148
|
)
|
|
677
1149
|
|
|
678
1150
|
const takeRedis = Redis.script(
|
|
679
1151
|
(
|
|
680
|
-
...args: [
|
|
1152
|
+
...args: [
|
|
1153
|
+
keyQueue: string,
|
|
1154
|
+
keyPending: string,
|
|
1155
|
+
keyDelayed: string,
|
|
1156
|
+
keyAttempts: string,
|
|
1157
|
+
prefix: string,
|
|
1158
|
+
workerId: string,
|
|
1159
|
+
batchSize: number,
|
|
1160
|
+
pttl: number,
|
|
1161
|
+
now: number
|
|
1162
|
+
]
|
|
681
1163
|
) => args,
|
|
682
1164
|
{
|
|
683
1165
|
lua: `
|
|
684
1166
|
local key_queue = KEYS[1]
|
|
685
1167
|
local key_pending = KEYS[2]
|
|
1168
|
+
local key_delayed = KEYS[3]
|
|
1169
|
+
local key_attempts = KEYS[4]
|
|
686
1170
|
local prefix = ARGV[1]
|
|
687
1171
|
local worker_id = ARGV[2]
|
|
688
1172
|
local batch_size = tonumber(ARGV[3])
|
|
689
1173
|
local pttl = ARGV[4]
|
|
1174
|
+
local now = ARGV[5]
|
|
1175
|
+
|
|
1176
|
+
local due = redis.call("ZRANGEBYSCORE", key_delayed, "-inf", now, "LIMIT", 0, 100)
|
|
1177
|
+
if #due > 0 then
|
|
1178
|
+
for i, payload in ipairs(due) do
|
|
1179
|
+
redis.call("RPUSH", key_queue, payload)
|
|
1180
|
+
end
|
|
1181
|
+
redis.call("ZREM", key_delayed, unpack(due))
|
|
1182
|
+
end
|
|
690
1183
|
|
|
691
1184
|
local payloads = redis.call("LPOP", key_queue, batch_size)
|
|
692
1185
|
if not payloads then
|
|
693
1186
|
return nil
|
|
694
1187
|
end
|
|
695
1188
|
|
|
1189
|
+
local result = {}
|
|
696
1190
|
for i, payload in ipairs(payloads) do
|
|
697
1191
|
local id = cjson.decode(payload).id
|
|
698
1192
|
local key_lock = prefix .. id .. ":lock"
|
|
699
1193
|
redis.call("SET", key_lock, worker_id, "PX", pttl)
|
|
700
1194
|
redis.call("HSET", key_pending, id, payload)
|
|
1195
|
+
local attempts = redis.call("HINCRBY", key_attempts, id, 1)
|
|
1196
|
+
result[i * 2 - 1] = payload
|
|
1197
|
+
result[i * 2] = attempts
|
|
701
1198
|
end
|
|
702
1199
|
|
|
703
|
-
return
|
|
1200
|
+
return result
|
|
704
1201
|
`,
|
|
705
|
-
numberOfKeys:
|
|
1202
|
+
numberOfKeys: 4
|
|
706
1203
|
}
|
|
707
|
-
).withReturnType<
|
|
1204
|
+
).withReturnType<ReadonlyArray<string | number> | null>()
|
|
708
1205
|
|
|
709
1206
|
const expireAllRedis = Redis.script(
|
|
710
1207
|
(keys: ReadonlyArray<string>, ttl: number) => [...keys, ttl],
|
|
@@ -719,6 +1216,41 @@ end
|
|
|
719
1216
|
}
|
|
720
1217
|
)
|
|
721
1218
|
|
|
1219
|
+
const trimFailedBatchSize = 1000
|
|
1220
|
+
|
|
1221
|
+
const trimFailedRedis = Redis.script(
|
|
1222
|
+
(...args: [keyFailed: string, keyIds: string, cutoff: number]) => args,
|
|
1223
|
+
{
|
|
1224
|
+
lua: `
|
|
1225
|
+
local key_failed = KEYS[1]
|
|
1226
|
+
local key_ids = KEYS[2]
|
|
1227
|
+
local cutoff = tonumber(ARGV[1])
|
|
1228
|
+
local removed = 0
|
|
1229
|
+
|
|
1230
|
+
while removed < ${trimFailedBatchSize} do
|
|
1231
|
+
local head = redis.call("LINDEX", key_failed, 0)
|
|
1232
|
+
if not head then break end
|
|
1233
|
+
local ok, decoded = pcall(cjson.decode, head)
|
|
1234
|
+
if ok then
|
|
1235
|
+
local failed_at = tonumber(decoded.failedAt)
|
|
1236
|
+
if failed_at ~= nil and failed_at >= cutoff then break end
|
|
1237
|
+
redis.call("LPOP", key_failed)
|
|
1238
|
+
if decoded.id ~= nil then
|
|
1239
|
+
redis.call("ZREM", key_ids, decoded.id)
|
|
1240
|
+
end
|
|
1241
|
+
else
|
|
1242
|
+
-- a corrupt head would otherwise block trimming of the whole list
|
|
1243
|
+
redis.call("LPOP", key_failed)
|
|
1244
|
+
end
|
|
1245
|
+
removed = removed + 1
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1248
|
+
return removed
|
|
1249
|
+
`,
|
|
1250
|
+
numberOfKeys: 2
|
|
1251
|
+
}
|
|
1252
|
+
).withReturnType<number>()
|
|
1253
|
+
|
|
722
1254
|
/**
|
|
723
1255
|
* Provides a Redis-backed `PersistedQueueStore` using `makeStoreRedis`.
|
|
724
1256
|
*
|
|
@@ -777,9 +1309,10 @@ export const makeStoreSql: (
|
|
|
777
1309
|
options?.lockExpiration ? Duration.fromInputUnsafe(options.lockExpiration) : Duration.minutes(2),
|
|
778
1310
|
Duration.millis(1)
|
|
779
1311
|
)
|
|
780
|
-
const lockExpirationSql = sql.literal(Math.ceil(Duration.toSeconds(lockExpiration)).toString())
|
|
781
1312
|
const workerId = crypto.randomUUID()
|
|
782
1313
|
|
|
1314
|
+
const ackRetrySchedule = makeAckRetrySchedule(lockExpiration)
|
|
1315
|
+
|
|
783
1316
|
yield* Effect.orDie(
|
|
784
1317
|
Migrator.make({})({
|
|
785
1318
|
loader: sqlMigrations(tableName),
|
|
@@ -788,45 +1321,55 @@ export const makeStoreSql: (
|
|
|
788
1321
|
)
|
|
789
1322
|
|
|
790
1323
|
const sqlNow = sql.onDialectOrElse({
|
|
791
|
-
|
|
1324
|
+
// GETDATE() rounds to 1/300s and can land in the future, hiding freshly
|
|
1325
|
+
// written visible_at values from the poll query
|
|
1326
|
+
mssql: () => sql.literal("SYSDATETIME()"),
|
|
792
1327
|
mysql: () => sql.literal("NOW()"),
|
|
793
1328
|
pg: () => sql.literal("NOW()"),
|
|
794
1329
|
// sqlite
|
|
795
1330
|
orElse: () => sql.literal("CURRENT_TIMESTAMP")
|
|
796
1331
|
})
|
|
797
1332
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1333
|
+
// `seconds` is a whole number, possibly negative
|
|
1334
|
+
const secondsOffset = (seconds: number) => {
|
|
1335
|
+
const s = sql.literal(seconds.toString())
|
|
1336
|
+
return sql.onDialectOrElse({
|
|
1337
|
+
pg: () => sql`${sqlNow} + INTERVAL '${s} seconds'`,
|
|
1338
|
+
mysql: () => sql`DATE_ADD(${sqlNow}, INTERVAL ${s} SECOND)`,
|
|
1339
|
+
mssql: () => sql`DATEADD(SECOND, ${s}, ${sqlNow})`,
|
|
1340
|
+
orElse: () => sql`datetime(${sqlNow}, '${s} seconds')`
|
|
1341
|
+
})
|
|
1342
|
+
}
|
|
1343
|
+
const secondsAgo = (seconds: number) => secondsOffset(-Math.max(Math.ceil(seconds), 0))
|
|
1344
|
+
const secondsFromNow = (seconds: number) => secondsOffset(Math.max(Math.ceil(seconds), 0))
|
|
1345
|
+
const expiresAt = secondsAgo(Duration.toSeconds(lockExpiration))
|
|
804
1346
|
|
|
805
1347
|
const offer = sql.onDialectOrElse({
|
|
806
1348
|
pg: () => (id: string, name: string, element: string) =>
|
|
807
1349
|
sql`
|
|
808
|
-
INSERT INTO ${tableNameSql} (id, queue_name, element,
|
|
809
|
-
VALUES (${id}, ${name}, ${element},
|
|
1350
|
+
INSERT INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
|
|
1351
|
+
VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
|
|
810
1352
|
ON CONFLICT (id, queue_name) DO NOTHING
|
|
811
1353
|
`,
|
|
812
1354
|
mysql: () => (id: string, name: string, element: string) =>
|
|
813
1355
|
sql`
|
|
814
|
-
INSERT IGNORE INTO ${tableNameSql} (id, queue_name, element,
|
|
815
|
-
VALUES (${id}, ${name}, ${element},
|
|
1356
|
+
INSERT IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
|
|
1357
|
+
VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
|
|
816
1358
|
`,
|
|
817
1359
|
mssql: () => (id: string, name: string, element: string) =>
|
|
818
1360
|
sql`
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1361
|
+
MERGE ${tableNameSql} WITH (HOLDLOCK) AS target
|
|
1362
|
+
USING (SELECT ${id} AS id, ${name} AS queue_name) AS source
|
|
1363
|
+
ON target.id = source.id AND target.queue_name = source.queue_name
|
|
1364
|
+
WHEN NOT MATCHED THEN
|
|
1365
|
+
INSERT (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
|
|
1366
|
+
VALUES (source.id, source.queue_name, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow});
|
|
824
1367
|
`,
|
|
825
1368
|
// sqlite
|
|
826
1369
|
orElse: () => (id: string, name: string, element: string) =>
|
|
827
1370
|
sql`
|
|
828
|
-
INSERT OR IGNORE INTO ${tableNameSql} (id, queue_name, element,
|
|
829
|
-
VALUES (${id}, ${name}, ${element},
|
|
1371
|
+
INSERT OR IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
|
|
1372
|
+
VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
|
|
830
1373
|
`
|
|
831
1374
|
})
|
|
832
1375
|
|
|
@@ -836,13 +1379,8 @@ export const makeStoreSql: (
|
|
|
836
1379
|
})
|
|
837
1380
|
const stringLiteral = (s: string) => sql.literal(wrapString(s))
|
|
838
1381
|
|
|
839
|
-
const sqlTrue = sql.onDialectOrElse({
|
|
840
|
-
sqlite: () => sql.literal("1"),
|
|
841
|
-
orElse: () => sql.literal("TRUE")
|
|
842
|
-
})
|
|
843
|
-
|
|
844
1382
|
const workerIdSql = stringLiteral(workerId)
|
|
845
|
-
const elementIds = new Set<number>()
|
|
1383
|
+
const elementIds = new Set<number | string>()
|
|
846
1384
|
const refreshLocks: Effect.Effect<void, SqlError> = Effect.suspend((): Effect.Effect<void, SqlError> => {
|
|
847
1385
|
if (elementIds.size === 0) return Effect.void
|
|
848
1386
|
const ids = Array.from(elementIds)
|
|
@@ -853,55 +1391,64 @@ export const makeStoreSql: (
|
|
|
853
1391
|
AND acquired_by = ${workerIdSql}
|
|
854
1392
|
`
|
|
855
1393
|
})
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}),
|
|
868
|
-
Effect.
|
|
1394
|
+
const ack = (
|
|
1395
|
+
statement: Effect.Effect<unknown, SqlError>,
|
|
1396
|
+
sequences: ReadonlyArray<number | string>
|
|
1397
|
+
): Effect.Effect<void> =>
|
|
1398
|
+
statement.pipe(
|
|
1399
|
+
Effect.retry(ackRetrySchedule),
|
|
1400
|
+
Effect.orDie,
|
|
1401
|
+
Effect.ensuring(Effect.sync(() => {
|
|
1402
|
+
for (const sequence of sequences) {
|
|
1403
|
+
elementIds.delete(sequence)
|
|
1404
|
+
}
|
|
1405
|
+
})),
|
|
1406
|
+
Effect.asVoid
|
|
869
1407
|
)
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
AND acquired_by = ${workerIdSql}
|
|
880
|
-
`.pipe(
|
|
881
|
-
Effect.retry({
|
|
882
|
-
times: 5,
|
|
883
|
-
schedule: Schedule.exponential(100, 1.5)
|
|
884
|
-
}),
|
|
885
|
-
Effect.orDie
|
|
1408
|
+
const complete = (sequence: number | string) =>
|
|
1409
|
+
ack(
|
|
1410
|
+
sql`
|
|
1411
|
+
UPDATE ${tableNameSql}
|
|
1412
|
+
SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, state = 'completed'
|
|
1413
|
+
WHERE sequence = ${sequence}
|
|
1414
|
+
AND acquired_by = ${workerIdSql}
|
|
1415
|
+
`,
|
|
1416
|
+
[sequence]
|
|
886
1417
|
)
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1418
|
+
const fail = (sequence: number | string, cause: Cause.Cause<any>) =>
|
|
1419
|
+
ack(
|
|
1420
|
+
sql`
|
|
1421
|
+
UPDATE ${tableNameSql}
|
|
1422
|
+
SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, state = 'failed', last_failure = ${
|
|
1423
|
+
Cause.pretty(cause)
|
|
1424
|
+
}
|
|
1425
|
+
WHERE sequence = ${sequence}
|
|
1426
|
+
AND acquired_by = ${workerIdSql}
|
|
1427
|
+
`,
|
|
1428
|
+
[sequence]
|
|
1429
|
+
)
|
|
1430
|
+
const retry = (sequence: number | string, delay: Duration.Duration, cause: Cause.Cause<any>) =>
|
|
1431
|
+
ack(
|
|
1432
|
+
sql`
|
|
1433
|
+
UPDATE ${tableNameSql}
|
|
1434
|
+
SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, visible_at = ${
|
|
1435
|
+
secondsFromNow(Duration.toSeconds(delay))
|
|
1436
|
+
}, last_failure = ${Cause.pretty(cause)}
|
|
1437
|
+
WHERE sequence = ${sequence}
|
|
1438
|
+
AND acquired_by = ${workerIdSql}
|
|
1439
|
+
`,
|
|
1440
|
+
[sequence]
|
|
1441
|
+
)
|
|
1442
|
+
const interrupt = (ids: Array<number | string>) =>
|
|
1443
|
+
ack(
|
|
1444
|
+
sql`
|
|
1445
|
+
UPDATE ${tableNameSql}
|
|
1446
|
+
SET acquired_at = NULL, acquired_by = NULL, attempts = attempts - 1
|
|
1447
|
+
WHERE sequence IN (${sql.literal(ids.join(","))})
|
|
1448
|
+
AND acquired_by = ${workerIdSql}
|
|
1449
|
+
`,
|
|
1450
|
+
ids
|
|
903
1451
|
)
|
|
904
|
-
}
|
|
905
1452
|
|
|
906
1453
|
yield* refreshLocks.pipe(
|
|
907
1454
|
Effect.tapCause(Effect.logWarning),
|
|
@@ -917,17 +1464,20 @@ export const makeStoreSql: (
|
|
|
917
1464
|
|
|
918
1465
|
type Element = {
|
|
919
1466
|
readonly id: string
|
|
920
|
-
sequence: number
|
|
921
|
-
readonly
|
|
922
|
-
|
|
923
|
-
readonly attempts: number
|
|
1467
|
+
readonly sequence: number | string
|
|
1468
|
+
readonly element: string
|
|
1469
|
+
attempts: number
|
|
924
1470
|
}
|
|
1471
|
+
const queueStates = makeQueueStates()
|
|
925
1472
|
const mailboxes = yield* RcMap.make({
|
|
926
|
-
lookup: Effect.fnUntraced(function*(
|
|
1473
|
+
lookup: Effect.fnUntraced(function*(name: string) {
|
|
1474
|
+
const state = queueStates.get(name)
|
|
1475
|
+
const maxAttempts = state.maxAttempts
|
|
927
1476
|
const queue = yield* Queue.make<Element>()
|
|
928
1477
|
const takers = MutableRef.make(0)
|
|
929
1478
|
const pollLatch = Latch.makeUnsafe()
|
|
930
1479
|
const takenLatch = Latch.makeUnsafe()
|
|
1480
|
+
const nudge = state.nudge
|
|
931
1481
|
|
|
932
1482
|
yield* Effect.addFinalizer(() =>
|
|
933
1483
|
Effect.flatMap(Queue.clear(queue), (elements) => {
|
|
@@ -936,35 +1486,55 @@ export const makeStoreSql: (
|
|
|
936
1486
|
})
|
|
937
1487
|
)
|
|
938
1488
|
|
|
1489
|
+
// flip exhausted rows whose lock expired (worker crashed on the final
|
|
1490
|
+
// attempt) to failed, since no finalizer will ever run for them
|
|
1491
|
+
yield* sql`
|
|
1492
|
+
UPDATE ${tableNameSql}
|
|
1493
|
+
SET state = 'failed', acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow},
|
|
1494
|
+
last_failure = COALESCE(last_failure, 'Lock expired after final attempt')
|
|
1495
|
+
WHERE queue_name = ${name}
|
|
1496
|
+
AND state = 'pending'
|
|
1497
|
+
AND attempts >= ${maxAttempts}
|
|
1498
|
+
AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
|
|
1499
|
+
`.pipe(
|
|
1500
|
+
Effect.tapCause(Effect.logWarning),
|
|
1501
|
+
Effect.ignore,
|
|
1502
|
+
Effect.schedule(Schedule.spaced(lockRefreshInterval)),
|
|
1503
|
+
Effect.forkScoped,
|
|
1504
|
+
Effect.interruptible
|
|
1505
|
+
)
|
|
1506
|
+
|
|
939
1507
|
const poll = sql.onDialectOrElse({
|
|
940
1508
|
pg: () => (size: number) =>
|
|
941
1509
|
sql<Element>`
|
|
942
1510
|
WITH cte AS (
|
|
943
1511
|
UPDATE ${tableNameSql}
|
|
944
|
-
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
|
|
1512
|
+
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
|
|
945
1513
|
WHERE sequence IN (
|
|
946
1514
|
SELECT sequence FROM ${tableNameSql}
|
|
947
1515
|
WHERE queue_name = ${name}
|
|
948
|
-
AND
|
|
1516
|
+
AND state = 'pending'
|
|
949
1517
|
AND attempts < ${maxAttempts}
|
|
1518
|
+
AND visible_at <= ${sqlNow}
|
|
950
1519
|
AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
|
|
951
|
-
ORDER BY
|
|
1520
|
+
ORDER BY visible_at ASC, sequence ASC
|
|
952
1521
|
FOR UPDATE SKIP LOCKED
|
|
953
1522
|
LIMIT ${sql.literal(size.toString())}
|
|
954
1523
|
)
|
|
955
|
-
RETURNING sequence, id,
|
|
1524
|
+
RETURNING sequence, id, element, attempts, visible_at
|
|
956
1525
|
)
|
|
957
|
-
SELECT sequence, id,
|
|
958
|
-
ORDER BY
|
|
1526
|
+
SELECT sequence, id, element, attempts FROM cte
|
|
1527
|
+
ORDER BY visible_at ASC, sequence ASC
|
|
959
1528
|
`,
|
|
960
1529
|
mysql: () => (size: number) =>
|
|
961
1530
|
sql<Element>`
|
|
962
|
-
SELECT sequence, id,
|
|
1531
|
+
SELECT sequence, id, element, attempts FROM ${tableNameSql} q
|
|
963
1532
|
WHERE queue_name = ${name}
|
|
964
|
-
AND
|
|
1533
|
+
AND state = 'pending'
|
|
965
1534
|
AND attempts < ${maxAttempts}
|
|
1535
|
+
AND visible_at <= ${sqlNow}
|
|
966
1536
|
AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
|
|
967
|
-
ORDER BY
|
|
1537
|
+
ORDER BY visible_at ASC, sequence ASC
|
|
968
1538
|
LIMIT ${sql.literal(size.toString())}
|
|
969
1539
|
FOR UPDATE SKIP LOCKED
|
|
970
1540
|
`.pipe(
|
|
@@ -972,10 +1542,16 @@ export const makeStoreSql: (
|
|
|
972
1542
|
if (rows.length === 0) return Effect.void
|
|
973
1543
|
return sql`
|
|
974
1544
|
UPDATE ${tableNameSql}
|
|
975
|
-
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
|
|
1545
|
+
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
|
|
976
1546
|
WHERE sequence IN (${sql.literal(rows.map((r) => r.sequence).join(","))})
|
|
977
1547
|
`.unprepared
|
|
978
1548
|
}),
|
|
1549
|
+
Effect.map((rows) => {
|
|
1550
|
+
for (const row of rows) {
|
|
1551
|
+
row.attempts = Number(row.attempts) + 1
|
|
1552
|
+
}
|
|
1553
|
+
return rows
|
|
1554
|
+
}),
|
|
979
1555
|
sql.withTransaction
|
|
980
1556
|
),
|
|
981
1557
|
mssql: () => (size: number) =>
|
|
@@ -983,14 +1559,15 @@ export const makeStoreSql: (
|
|
|
983
1559
|
WITH cte AS (
|
|
984
1560
|
SELECT TOP ${sql.literal(size.toString())} sequence FROM ${tableNameSql}
|
|
985
1561
|
WHERE queue_name = ${name}
|
|
986
|
-
AND
|
|
1562
|
+
AND state = 'pending'
|
|
987
1563
|
AND attempts < ${maxAttempts}
|
|
1564
|
+
AND visible_at <= ${sqlNow}
|
|
988
1565
|
AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
|
|
989
|
-
ORDER BY
|
|
1566
|
+
ORDER BY visible_at ASC, sequence ASC
|
|
990
1567
|
)
|
|
991
1568
|
UPDATE q
|
|
992
|
-
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
|
|
993
|
-
OUTPUT inserted.sequence, inserted.id, inserted.
|
|
1569
|
+
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = q.attempts + 1
|
|
1570
|
+
OUTPUT inserted.sequence, inserted.id, inserted.element, inserted.attempts
|
|
994
1571
|
FROM ${tableNameSql} AS q
|
|
995
1572
|
INNER JOIN cte ON q.sequence = cte.sequence
|
|
996
1573
|
`,
|
|
@@ -998,17 +1575,18 @@ export const makeStoreSql: (
|
|
|
998
1575
|
orElse: () => (size: number) =>
|
|
999
1576
|
sql<Element>`
|
|
1000
1577
|
UPDATE ${tableNameSql}
|
|
1001
|
-
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
|
|
1578
|
+
SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
|
|
1002
1579
|
WHERE sequence IN (
|
|
1003
1580
|
SELECT sequence FROM ${tableNameSql}
|
|
1004
1581
|
WHERE queue_name = ${name}
|
|
1005
|
-
AND
|
|
1582
|
+
AND state = 'pending'
|
|
1006
1583
|
AND attempts < ${maxAttempts}
|
|
1584
|
+
AND visible_at <= ${sqlNow}
|
|
1007
1585
|
AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
|
|
1008
|
-
ORDER BY
|
|
1586
|
+
ORDER BY visible_at ASC, sequence ASC
|
|
1009
1587
|
LIMIT ${sql.literal(size.toString())}
|
|
1010
1588
|
)
|
|
1011
|
-
RETURNING sequence, id,
|
|
1589
|
+
RETURNING sequence, id, element, attempts
|
|
1012
1590
|
`
|
|
1013
1591
|
})
|
|
1014
1592
|
|
|
@@ -1016,15 +1594,16 @@ export const makeStoreSql: (
|
|
|
1016
1594
|
while (true) {
|
|
1017
1595
|
yield* pollLatch.await
|
|
1018
1596
|
yield* Effect.yieldNow
|
|
1597
|
+
nudge.closeUnsafe()
|
|
1019
1598
|
const results = takers.current === 0 ? [] : yield* poll(takers.current)
|
|
1020
1599
|
if (results.length === 0) {
|
|
1021
|
-
yield* Effect.sleep(pollInterval)
|
|
1600
|
+
yield* Effect.race(Effect.sleep(pollInterval), nudge.await)
|
|
1022
1601
|
continue
|
|
1023
1602
|
}
|
|
1024
1603
|
takenLatch.closeUnsafe()
|
|
1025
|
-
for (
|
|
1026
|
-
|
|
1027
|
-
elementIds.add(
|
|
1604
|
+
for (const row of results) {
|
|
1605
|
+
row.attempts = Number(row.attempts)
|
|
1606
|
+
elementIds.add(row.sequence)
|
|
1028
1607
|
}
|
|
1029
1608
|
yield* Queue.offerAll(queue, results)
|
|
1030
1609
|
yield* takenLatch.await
|
|
@@ -1042,6 +1621,62 @@ export const makeStoreSql: (
|
|
|
1042
1621
|
idleTimeToLive: Duration.seconds(30)
|
|
1043
1622
|
})
|
|
1044
1623
|
|
|
1624
|
+
const cleanupBatch = sql.onDialectOrElse({
|
|
1625
|
+
pg: () => (state: string, seconds: number) =>
|
|
1626
|
+
sql<{ readonly count: number }>`
|
|
1627
|
+
WITH deleted_entries AS (
|
|
1628
|
+
DELETE FROM ${tableNameSql}
|
|
1629
|
+
WHERE sequence IN (
|
|
1630
|
+
SELECT sequence FROM ${tableNameSql}
|
|
1631
|
+
WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
|
|
1632
|
+
LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
|
|
1633
|
+
)
|
|
1634
|
+
RETURNING 1
|
|
1635
|
+
)
|
|
1636
|
+
SELECT COUNT(*)::INT AS count FROM deleted_entries
|
|
1637
|
+
`.pipe(Effect.map((rows) => rows[0].count)),
|
|
1638
|
+
mysql: () =>
|
|
1639
|
+
Effect.fnUntraced(
|
|
1640
|
+
function*(state: string, seconds: number) {
|
|
1641
|
+
const connection = yield* sql.reserve
|
|
1642
|
+
const [statement, parameters] = sql`
|
|
1643
|
+
DELETE FROM ${tableNameSql}
|
|
1644
|
+
WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
|
|
1645
|
+
LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
|
|
1646
|
+
`.compile()
|
|
1647
|
+
yield* connection.execute(statement, parameters, undefined)
|
|
1648
|
+
const rows = yield* connection.executeValues("SELECT ROW_COUNT()", [])
|
|
1649
|
+
return Number(rows[0][0])
|
|
1650
|
+
},
|
|
1651
|
+
Effect.scoped
|
|
1652
|
+
),
|
|
1653
|
+
mssql: () => (state: string, seconds: number) =>
|
|
1654
|
+
sql<{ readonly sequence: number }>`
|
|
1655
|
+
DELETE TOP (${sql.literal(String(sqlCleanupBatchSize))}) FROM ${tableNameSql}
|
|
1656
|
+
OUTPUT DELETED.sequence
|
|
1657
|
+
WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
|
|
1658
|
+
`.pipe(Effect.map((rows) => rows.length)),
|
|
1659
|
+
// sqlite
|
|
1660
|
+
orElse: () => (state: string, seconds: number) =>
|
|
1661
|
+
sql<{ readonly deleted: number }>`
|
|
1662
|
+
DELETE FROM ${tableNameSql}
|
|
1663
|
+
WHERE sequence IN (
|
|
1664
|
+
SELECT sequence FROM ${tableNameSql}
|
|
1665
|
+
WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
|
|
1666
|
+
LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
|
|
1667
|
+
)
|
|
1668
|
+
RETURNING 1 AS deleted
|
|
1669
|
+
`.pipe(Effect.map((rows) => rows.length))
|
|
1670
|
+
})
|
|
1671
|
+
|
|
1672
|
+
const cleanupState = (state: string, timeToLive: Duration.Duration) =>
|
|
1673
|
+
cleanupBatch(state, Duration.toSeconds(timeToLive)).pipe(
|
|
1674
|
+
Effect.repeat({
|
|
1675
|
+
while: (deletedCount) => deletedCount === sqlCleanupBatchSize,
|
|
1676
|
+
schedule: Schedule.spaced(Duration.millis(10))
|
|
1677
|
+
})
|
|
1678
|
+
)
|
|
1679
|
+
|
|
1045
1680
|
return PersistedQueueStore.of({
|
|
1046
1681
|
offer: ({ element, id, name }) =>
|
|
1047
1682
|
Effect.catchCause(Effect.suspend(() => offer(id, name, JSON.stringify(element))), (cause) =>
|
|
@@ -1050,16 +1685,34 @@ export const makeStoreSql: (
|
|
|
1050
1685
|
message: "Failed to offer element to persisted queue",
|
|
1051
1686
|
cause
|
|
1052
1687
|
})
|
|
1053
|
-
))
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1688
|
+
)).pipe(
|
|
1689
|
+
Effect.tap(() =>
|
|
1690
|
+
Effect.sync(() => {
|
|
1691
|
+
queueStates.peek(name)?.nudge.openUnsafe()
|
|
1692
|
+
})
|
|
1693
|
+
)
|
|
1694
|
+
),
|
|
1695
|
+
take: (options) => {
|
|
1696
|
+
queueStates.get(options.name).maxAttempts = options.maxAttempts
|
|
1697
|
+
const loop: Effect.Effect<
|
|
1698
|
+
{
|
|
1699
|
+
readonly id: string
|
|
1700
|
+
readonly attempts: number
|
|
1701
|
+
readonly element: unknown
|
|
1702
|
+
},
|
|
1703
|
+
PersistedQueueError,
|
|
1704
|
+
Scope.Scope
|
|
1705
|
+
> = Effect.uninterruptibleMask((restore) =>
|
|
1706
|
+
RcMap.get(mailboxes, options.name).pipe(
|
|
1057
1707
|
Effect.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
|
|
1058
1708
|
takers.current++
|
|
1059
1709
|
if (takers.current === 1) {
|
|
1060
1710
|
pollLatch.openUnsafe()
|
|
1061
1711
|
}
|
|
1062
|
-
|
|
1712
|
+
// onExit so the decrement also runs when a waiting take is
|
|
1713
|
+
// interrupted, otherwise the poller keeps fetching for a phantom
|
|
1714
|
+
// taker
|
|
1715
|
+
return Effect.onExit(restore(Queue.take(queue)), () =>
|
|
1063
1716
|
Effect.sync(() => {
|
|
1064
1717
|
takers.current--
|
|
1065
1718
|
if (takers.current === 0) {
|
|
@@ -1072,19 +1725,56 @@ export const makeStoreSql: (
|
|
|
1072
1725
|
}),
|
|
1073
1726
|
Effect.scoped,
|
|
1074
1727
|
restore,
|
|
1075
|
-
Effect.
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1728
|
+
Effect.flatMap((element) => {
|
|
1729
|
+
let parsed: unknown
|
|
1730
|
+
try {
|
|
1731
|
+
parsed = JSON.parse(element.element)
|
|
1732
|
+
} catch (defect) {
|
|
1733
|
+
// a row that cannot be parsed will never succeed, so dead-letter
|
|
1734
|
+
// it and take the next element
|
|
1735
|
+
return Effect.andThen(fail(element.sequence, Cause.die(defect)), loop)
|
|
1736
|
+
}
|
|
1737
|
+
return Effect.as(
|
|
1738
|
+
Effect.addFinalizer((exit) => {
|
|
1739
|
+
if (exit._tag === "Success") {
|
|
1740
|
+
return complete(element.sequence)
|
|
1741
|
+
}
|
|
1742
|
+
const dead = deadLetterFromCause(exit.cause)
|
|
1743
|
+
if (dead !== undefined) {
|
|
1744
|
+
return fail(element.sequence, dead.cause)
|
|
1745
|
+
}
|
|
1746
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
1747
|
+
return interrupt([element.sequence])
|
|
1748
|
+
}
|
|
1749
|
+
if (element.attempts >= options.maxAttempts) {
|
|
1750
|
+
return fail(element.sequence, exit.cause)
|
|
1751
|
+
}
|
|
1752
|
+
return Effect.flatMap(
|
|
1753
|
+
options.retryDelay(element.attempts),
|
|
1754
|
+
(delay) => retry(element.sequence, delay, exit.cause)
|
|
1755
|
+
)
|
|
1756
|
+
}),
|
|
1757
|
+
{ id: element.id, attempts: element.attempts, element: parsed }
|
|
1758
|
+
)
|
|
1759
|
+
})
|
|
1760
|
+
)
|
|
1761
|
+
)
|
|
1762
|
+
return loop
|
|
1763
|
+
},
|
|
1764
|
+
cleanup: ({ failedTimeToLive, timeToLive }) =>
|
|
1765
|
+
Effect.gen(function*() {
|
|
1766
|
+
yield* cleanupState("completed", timeToLive)
|
|
1767
|
+
if (failedTimeToLive !== undefined) {
|
|
1768
|
+
yield* cleanupState("failed", failedTimeToLive)
|
|
1769
|
+
}
|
|
1770
|
+
}).pipe(
|
|
1771
|
+
Effect.catchCause((cause) =>
|
|
1772
|
+
Effect.fail(
|
|
1773
|
+
new PersistedQueueError({
|
|
1774
|
+
message: "Failed to clean up persisted queue",
|
|
1775
|
+
cause
|
|
1776
|
+
})
|
|
1777
|
+
)
|
|
1088
1778
|
)
|
|
1089
1779
|
)
|
|
1090
1780
|
})
|
|
@@ -1126,7 +1816,7 @@ const sqlMigrations = (tableName: string) =>
|
|
|
1126
1816
|
updated_at TIMESTAMP NOT NULL
|
|
1127
1817
|
)`,
|
|
1128
1818
|
mssql: () =>
|
|
1129
|
-
sql`IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${
|
|
1819
|
+
sql`IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${tableName} AND xtype='U')
|
|
1130
1820
|
CREATE TABLE ${tableNameSql} (
|
|
1131
1821
|
sequence INT IDENTITY(1,1) PRIMARY KEY,
|
|
1132
1822
|
id NVARCHAR(36) NOT NULL,
|
|
@@ -1160,7 +1850,7 @@ const sqlMigrations = (tableName: string) =>
|
|
|
1160
1850
|
yield* sql.onDialectOrElse({
|
|
1161
1851
|
mssql: () =>
|
|
1162
1852
|
sql`IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = N'idx_${tableName}_id')
|
|
1163
|
-
CREATE UNIQUE INDEX idx_${
|
|
1853
|
+
CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`,
|
|
1164
1854
|
mysql: () =>
|
|
1165
1855
|
sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`.pipe(
|
|
1166
1856
|
Effect.ignore
|
|
@@ -1172,7 +1862,9 @@ const sqlMigrations = (tableName: string) =>
|
|
|
1172
1862
|
yield* sql.onDialectOrElse({
|
|
1173
1863
|
mssql: () =>
|
|
1174
1864
|
sql`IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = N'idx_${tableName}_take')
|
|
1175
|
-
CREATE INDEX
|
|
1865
|
+
CREATE INDEX ${
|
|
1866
|
+
sql(`idx_${tableName}_take`)
|
|
1867
|
+
} ON ${tableNameSql} (queue_name, completed, attempts, acquired_at)`,
|
|
1176
1868
|
mysql: () =>
|
|
1177
1869
|
sql`CREATE INDEX ${
|
|
1178
1870
|
sql(`idx_${tableName}_take`)
|
|
@@ -1195,14 +1887,131 @@ const sqlMigrations = (tableName: string) =>
|
|
|
1195
1887
|
orElse: () =>
|
|
1196
1888
|
sql`CREATE INDEX IF NOT EXISTS ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
|
|
1197
1889
|
})
|
|
1890
|
+
}),
|
|
1891
|
+
"0002_upgrade_schema": Effect.gen(function*() {
|
|
1892
|
+
const sql = (yield* SqlClient.SqlClient).withoutTransforms()
|
|
1893
|
+
const tableNameSql = sql(tableName)
|
|
1894
|
+
const takeIndex = sql(`idx_${tableName}_take`)
|
|
1895
|
+
|
|
1896
|
+
yield* sql.onDialectOrElse({
|
|
1897
|
+
pg: () =>
|
|
1898
|
+
Effect.gen(function*() {
|
|
1899
|
+
yield* sql`DROP INDEX IF EXISTS ${takeIndex}`
|
|
1900
|
+
yield* sql`ALTER TABLE ${tableNameSql}
|
|
1901
|
+
ADD COLUMN state VARCHAR(10),
|
|
1902
|
+
ADD COLUMN visible_at TIMESTAMP`
|
|
1903
|
+
yield* sql`UPDATE ${tableNameSql}
|
|
1904
|
+
SET state = CASE WHEN completed THEN 'completed' ELSE 'pending' END,
|
|
1905
|
+
visible_at = updated_at`
|
|
1906
|
+
yield* sql`ALTER TABLE ${tableNameSql}
|
|
1907
|
+
ALTER COLUMN sequence TYPE BIGINT,
|
|
1908
|
+
ALTER COLUMN id TYPE VARCHAR(255),
|
|
1909
|
+
ALTER COLUMN queue_name TYPE VARCHAR(255),
|
|
1910
|
+
ALTER COLUMN state SET NOT NULL,
|
|
1911
|
+
ALTER COLUMN visible_at SET NOT NULL,
|
|
1912
|
+
DROP COLUMN completed`
|
|
1913
|
+
}),
|
|
1914
|
+
mysql: () =>
|
|
1915
|
+
Effect.gen(function*() {
|
|
1916
|
+
yield* sql`DROP INDEX ${takeIndex} ON ${tableNameSql}`
|
|
1917
|
+
yield* sql`ALTER TABLE ${tableNameSql}
|
|
1918
|
+
MODIFY COLUMN id VARCHAR(255) NOT NULL,
|
|
1919
|
+
MODIFY COLUMN queue_name VARCHAR(255) NOT NULL,
|
|
1920
|
+
MODIFY COLUMN element MEDIUMTEXT NOT NULL,
|
|
1921
|
+
MODIFY COLUMN last_failure MEDIUMTEXT NULL,
|
|
1922
|
+
ADD COLUMN state VARCHAR(10) NULL,
|
|
1923
|
+
ADD COLUMN visible_at DATETIME NULL`
|
|
1924
|
+
yield* sql`UPDATE ${tableNameSql}
|
|
1925
|
+
SET state = CASE WHEN completed THEN 'completed' ELSE 'pending' END,
|
|
1926
|
+
visible_at = updated_at`
|
|
1927
|
+
yield* sql`ALTER TABLE ${tableNameSql}
|
|
1928
|
+
MODIFY COLUMN state VARCHAR(10) NOT NULL,
|
|
1929
|
+
MODIFY COLUMN visible_at DATETIME NOT NULL,
|
|
1930
|
+
DROP COLUMN completed`
|
|
1931
|
+
}),
|
|
1932
|
+
mssql: () =>
|
|
1933
|
+
Effect.gen(function*() {
|
|
1934
|
+
const upgradedTableName = `${tableName}_upgrade`
|
|
1935
|
+
const upgradedTable = sql(upgradedTableName)
|
|
1936
|
+
yield* sql`CREATE TABLE ${upgradedTable} (
|
|
1937
|
+
sequence BIGINT IDENTITY(1,1) PRIMARY KEY,
|
|
1938
|
+
id NVARCHAR(255) NOT NULL,
|
|
1939
|
+
queue_name NVARCHAR(255) NOT NULL,
|
|
1940
|
+
element NVARCHAR(MAX) NOT NULL,
|
|
1941
|
+
state NVARCHAR(10) NOT NULL,
|
|
1942
|
+
attempts INT NOT NULL DEFAULT 0,
|
|
1943
|
+
last_failure NVARCHAR(MAX) NULL,
|
|
1944
|
+
visible_at DATETIME2 NOT NULL,
|
|
1945
|
+
acquired_at DATETIME2 NULL,
|
|
1946
|
+
acquired_by UNIQUEIDENTIFIER NULL,
|
|
1947
|
+
created_at DATETIME2 NOT NULL,
|
|
1948
|
+
updated_at DATETIME2 NOT NULL
|
|
1949
|
+
)`
|
|
1950
|
+
yield* sql`SET IDENTITY_INSERT ${upgradedTable} ON;
|
|
1951
|
+
INSERT INTO ${upgradedTable}
|
|
1952
|
+
(sequence, id, queue_name, element, state, attempts, last_failure, visible_at,
|
|
1953
|
+
acquired_at, acquired_by, created_at, updated_at)
|
|
1954
|
+
SELECT sequence, id, queue_name, element,
|
|
1955
|
+
CASE WHEN completed = 1 THEN 'completed' ELSE 'pending' END,
|
|
1956
|
+
attempts, last_failure, updated_at, acquired_at, acquired_by, created_at, updated_at
|
|
1957
|
+
FROM ${tableNameSql};
|
|
1958
|
+
SET IDENTITY_INSERT ${upgradedTable} OFF`
|
|
1959
|
+
yield* sql`DROP TABLE ${tableNameSql}`
|
|
1960
|
+
yield* sql`EXEC sp_rename ${upgradedTableName}, ${tableName}`
|
|
1961
|
+
yield* sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`
|
|
1962
|
+
yield* sql`CREATE INDEX ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
|
|
1963
|
+
}),
|
|
1964
|
+
// sqlite rebuilds the table because altering or dropping constrained
|
|
1965
|
+
// columns is not portable across supported SQLite versions.
|
|
1966
|
+
orElse: () =>
|
|
1967
|
+
Effect.gen(function*() {
|
|
1968
|
+
const upgradedTableName = `${tableName}_upgrade`
|
|
1969
|
+
const upgradedTable = sql(upgradedTableName)
|
|
1970
|
+
yield* sql`DROP INDEX IF EXISTS ${takeIndex}`
|
|
1971
|
+
yield* sql`CREATE TABLE ${upgradedTable} (
|
|
1972
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1973
|
+
id TEXT NOT NULL,
|
|
1974
|
+
queue_name TEXT NOT NULL,
|
|
1975
|
+
element TEXT NOT NULL,
|
|
1976
|
+
state TEXT NOT NULL,
|
|
1977
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
1978
|
+
last_failure TEXT NULL,
|
|
1979
|
+
visible_at DATETIME NOT NULL,
|
|
1980
|
+
acquired_at DATETIME NULL,
|
|
1981
|
+
acquired_by TEXT NULL,
|
|
1982
|
+
created_at DATETIME NOT NULL,
|
|
1983
|
+
updated_at DATETIME NOT NULL
|
|
1984
|
+
)`
|
|
1985
|
+
yield* sql`INSERT INTO ${upgradedTable}
|
|
1986
|
+
(sequence, id, queue_name, element, state, attempts, last_failure, visible_at,
|
|
1987
|
+
acquired_at, acquired_by, created_at, updated_at)
|
|
1988
|
+
SELECT sequence, id, queue_name, element,
|
|
1989
|
+
CASE WHEN completed THEN 'completed' ELSE 'pending' END,
|
|
1990
|
+
attempts, last_failure, updated_at, acquired_at, acquired_by, created_at, updated_at
|
|
1991
|
+
FROM ${tableNameSql}`
|
|
1992
|
+
yield* sql`DROP TABLE ${tableNameSql}`
|
|
1993
|
+
yield* sql`ALTER TABLE ${upgradedTable} RENAME TO ${tableNameSql}`
|
|
1994
|
+
yield* sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`
|
|
1995
|
+
yield* sql`CREATE INDEX ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
|
|
1996
|
+
})
|
|
1997
|
+
})
|
|
1998
|
+
|
|
1999
|
+
// partial index where supported, so pollers never scan completed rows
|
|
2000
|
+
yield* sql.onDialectOrElse({
|
|
2001
|
+
mssql: () =>
|
|
2002
|
+
sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
|
|
2003
|
+
WHERE state = 'pending'`,
|
|
2004
|
+
mysql: () => sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, state, visible_at)`,
|
|
2005
|
+
pg: () =>
|
|
2006
|
+
sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
|
|
2007
|
+
WHERE state = 'pending'`,
|
|
2008
|
+
orElse: () =>
|
|
2009
|
+
sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
|
|
2010
|
+
WHERE state = 'pending'`
|
|
2011
|
+
})
|
|
1198
2012
|
})
|
|
1199
2013
|
})
|
|
1200
2014
|
|
|
1201
|
-
class QueueKey extends Data.Class<{
|
|
1202
|
-
readonly name: string
|
|
1203
|
-
readonly maxAttempts: number
|
|
1204
|
-
}> {}
|
|
1205
|
-
|
|
1206
2015
|
/**
|
|
1207
2016
|
* Provides a SQL-backed `PersistedQueueStore` using `makeStoreSql`.
|
|
1208
2017
|
*
|