@sayoriqwq/effect-harness 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HARNESS.md +34 -0
- package/README.md +113 -0
- package/dist/bin/effect-harness.d.ts +2 -0
- package/dist/bin/effect-harness.js +39 -0
- package/dist/bin/effect-harness.js.map +1 -0
- package/dist/src/cli/Main.d.ts +6 -0
- package/dist/src/cli/Main.js +141 -0
- package/dist/src/cli/Main.js.map +1 -0
- package/dist/src/harness/CraftSkills.d.ts +14 -0
- package/dist/src/harness/CraftSkills.js +164 -0
- package/dist/src/harness/CraftSkills.js.map +1 -0
- package/dist/src/harness/Errors.d.ts +24 -0
- package/dist/src/harness/Errors.js +26 -0
- package/dist/src/harness/Errors.js.map +1 -0
- package/dist/src/harness/GuardrailRules.d.ts +6 -0
- package/dist/src/harness/GuardrailRules.js +303 -0
- package/dist/src/harness/GuardrailRules.js.map +1 -0
- package/dist/src/harness/Guardrails.d.ts +10 -0
- package/dist/src/harness/Guardrails.js +69 -0
- package/dist/src/harness/Guardrails.js.map +1 -0
- package/dist/src/harness/Init.d.ts +9 -0
- package/dist/src/harness/Init.js +234 -0
- package/dist/src/harness/Init.js.map +1 -0
- package/dist/src/harness/Model.d.ts +44 -0
- package/dist/src/harness/Model.js +169 -0
- package/dist/src/harness/Model.js.map +1 -0
- package/dist/src/harness/ModuleSources.d.ts +5 -0
- package/dist/src/harness/ModuleSources.js +47 -0
- package/dist/src/harness/ModuleSources.js.map +1 -0
- package/dist/src/harness/PnpmWorkspace.d.ts +2 -0
- package/dist/src/harness/PnpmWorkspace.js +23 -0
- package/dist/src/harness/PnpmWorkspace.js.map +1 -0
- package/dist/src/harness/Publish.d.ts +53 -0
- package/dist/src/harness/Publish.js +282 -0
- package/dist/src/harness/Publish.js.map +1 -0
- package/dist/src/harness/SelfVerify.d.ts +4 -0
- package/dist/src/harness/SelfVerify.js +56 -0
- package/dist/src/harness/SelfVerify.js.map +1 -0
- package/dist/src/harness/SourcePin.d.ts +12 -0
- package/dist/src/harness/SourcePin.js +320 -0
- package/dist/src/harness/SourcePin.js.map +1 -0
- package/dist/src/harness/Status.d.ts +12 -0
- package/dist/src/harness/Status.js +88 -0
- package/dist/src/harness/Status.js.map +1 -0
- package/dist/src/harness/TargetVerify.d.ts +8 -0
- package/dist/src/harness/TargetVerify.js +432 -0
- package/dist/src/harness/TargetVerify.js.map +1 -0
- package/dist/src/harness/TestContract.d.ts +6 -0
- package/dist/src/harness/TestContract.js +45 -0
- package/dist/src/harness/TestContract.js.map +1 -0
- package/dist/src/platform/Json.d.ts +8 -0
- package/dist/src/platform/Json.js +35 -0
- package/dist/src/platform/Json.js.map +1 -0
- package/dist/src/platform/ManagedFiles.d.ts +8 -0
- package/dist/src/platform/ManagedFiles.js +59 -0
- package/dist/src/platform/ManagedFiles.js.map +1 -0
- package/dist/src/platform/Process.d.ts +8 -0
- package/dist/src/platform/Process.js +80 -0
- package/dist/src/platform/Process.js.map +1 -0
- package/guide/default-capabilities.md +38 -0
- package/guide/index.md +49 -0
- package/guide/setup.md +64 -0
- package/harness/default-capabilities.md +32 -0
- package/harness/exposure.md +46 -0
- package/harness/feedback/index.md +60 -0
- package/harness/index.md +22 -0
- package/harness/official-inventory.md +85 -0
- package/harness/runtime/codex/AGENTS.fragment.md +28 -0
- package/harness/runtime/codex/agents/effect-worker.md +26 -0
- package/harness/runtime/codex/skills/effect-code/SKILL.md +72 -0
- package/harness/runtime/codex/skills/effect-code/agents/openai.yaml +4 -0
- package/harness/runtime/codex/skills/effect-feedback/SKILL.md +66 -0
- package/harness/runtime/codex/skills/effect-feedback/agents/openai.yaml +4 -0
- package/harness/source.md +52 -0
- package/harness/target-agent-contract.md +205 -0
- package/package.json +68 -0
- package/repos/craft-skills.manifest.json +34 -0
- package/repos/effect/.agents/skills/grill-me/SKILL.md +43 -0
- package/repos/effect/.agents/skills/jsdocs/SKILL.md +203 -0
- package/repos/effect/.agents/skills/scratchpad/SKILL.md +44 -0
- package/repos/effect/.agents/skills/scratchpad/agents/openai.yaml +7 -0
- package/repos/effect/.agents/skills/scratchpad/scripts/extract-example.mjs +260 -0
- package/repos/effect/.changeset/add-bigdecimal-sumall-multiplyall.md +5 -0
- package/repos/effect/.changeset/add-chunk-schema.md +5 -0
- package/repos/effect/.changeset/add-command-hidden.md +19 -0
- package/repos/effect/.changeset/add-config-nested.md +5 -0
- package/repos/effect/.changeset/add-flag-hidden.md +15 -0
- package/repos/effect/.changeset/add-from-string-schemas.md +5 -0
- package/repos/effect/.changeset/add-headers-remove-many.md +5 -0
- package/repos/effect/.changeset/add-indexeddb-kvs-layer.md +5 -0
- package/repos/effect/.changeset/add-make-msgpack.md +5 -0
- package/repos/effect/.changeset/add-make-option.md +5 -0
- package/repos/effect/.changeset/add-missing-tx-modules.md +11 -0
- package/repos/effect/.changeset/add-newtype-module.md +5 -0
- package/repos/effect/.changeset/add-scalar-show-operation-id.md +5 -0
- package/repos/effect/.changeset/add-schedule-tap.md +5 -0
- package/repos/effect/.changeset/add-schema-annotate-encoded.md +5 -0
- package/repos/effect/.changeset/add-schema-array-ensure.md +5 -0
- package/repos/effect/.changeset/add-schema-bigdecimal.md +5 -0
- package/repos/effect/.changeset/add-schema-datetime.md +5 -0
- package/repos/effect/.changeset/add-schema-option-from-optional-nullor.md +5 -0
- package/repos/effect/.changeset/add-schema-option-from-undefined-nullish.md +5 -0
- package/repos/effect/.changeset/add-schema-string-encoding.md +5 -0
- package/repos/effect/.changeset/add-sql-pglite.md +5 -0
- package/repos/effect/.changeset/add-standard-jsdoc-rule.md +5 -0
- package/repos/effect/.changeset/add-stream-broadcastn.md +5 -0
- package/repos/effect/.changeset/add-unstable-encoding-export.md +5 -0
- package/repos/effect/.changeset/afraid-cobras-like.md +5 -0
- package/repos/effect/.changeset/ai-openai-config-field-leak.md +6 -0
- package/repos/effect/.changeset/ai-openai-file-nullable-fields.md +5 -0
- package/repos/effect/.changeset/anthropic-4-6-structured-output.md +5 -0
- package/repos/effect/.changeset/asyncresult-exhaustive.md +5 -0
- package/repos/effect/.changeset/atom-stream-error-type.md +5 -0
- package/repos/effect/.changeset/beige-paths-sort.md +5 -0
- package/repos/effect/.changeset/better-apples-nail.md +5 -0
- package/repos/effect/.changeset/better-rocks-arrive.md +5 -0
- package/repos/effect/.changeset/big-pans-look.md +5 -0
- package/repos/effect/.changeset/blue-dingos-greet.md +5 -0
- package/repos/effect/.changeset/blue-ligers-cheat.md +5 -0
- package/repos/effect/.changeset/blue-onions-smile.md +5 -0
- package/repos/effect/.changeset/blue-ravens-type.md +5 -0
- package/repos/effect/.changeset/blue-trams-kiss.md +7 -0
- package/repos/effect/.changeset/bold-chairs-yawn.md +5 -0
- package/repos/effect/.changeset/bold-planets-shout.md +30 -0
- package/repos/effect/.changeset/breezy-meals-see.md +5 -0
- package/repos/effect/.changeset/bright-bugs-bow.md +5 -0
- package/repos/effect/.changeset/bright-canyons-clean.md +5 -0
- package/repos/effect/.changeset/bright-dogs-fail.md +5 -0
- package/repos/effect/.changeset/bright-flags-stand.md +5 -0
- package/repos/effect/.changeset/bright-laws-teach.md +5 -0
- package/repos/effect/.changeset/bright-lemons-dance.md +5 -0
- package/repos/effect/.changeset/bright-planes-smash.md +5 -0
- package/repos/effect/.changeset/bright-rats-attend.md +5 -0
- package/repos/effect/.changeset/bright-toes-rush.md +5 -0
- package/repos/effect/.changeset/bumpy-boxes-teach.md +5 -0
- package/repos/effect/.changeset/busy-lions-sneeze.md +5 -0
- package/repos/effect/.changeset/busy-maps-attend.md +7 -0
- package/repos/effect/.changeset/calm-buckets-own.md +5 -0
- package/repos/effect/.changeset/calm-carrots-march.md +7 -0
- package/repos/effect/.changeset/calm-cars-rest.md +5 -0
- package/repos/effect/.changeset/calm-panthers-nail.md +5 -0
- package/repos/effect/.changeset/calm-seas-smile.md +5 -0
- package/repos/effect/.changeset/calm-squids-hug.md +5 -0
- package/repos/effect/.changeset/chatty-poets-type.md +5 -0
- package/repos/effect/.changeset/chilled-mice-wash.md +5 -0
- package/repos/effect/.changeset/chilly-pumas-rule.md +5 -0
- package/repos/effect/.changeset/chubby-buckets-feel.md +5 -0
- package/repos/effect/.changeset/chubby-parents-flow.md +5 -0
- package/repos/effect/.changeset/chubby-planets-fall.md +5 -0
- package/repos/effect/.changeset/clean-balloons-tan.md +5 -0
- package/repos/effect/.changeset/clean-bulldogs-care.md +5 -0
- package/repos/effect/.changeset/clean-dryers-sneeze.md +5 -0
- package/repos/effect/.changeset/clean-geese-work.md +5 -0
- package/repos/effect/.changeset/clean-goats-wave.md +5 -0
- package/repos/effect/.changeset/clean-needles-shake.md +23 -0
- package/repos/effect/.changeset/clean-tires-guess.md +5 -0
- package/repos/effect/.changeset/clear-spies-boil.md +5 -0
- package/repos/effect/.changeset/cli-help-choices.md +5 -0
- package/repos/effect/.changeset/cold-knives-lie.md +35 -0
- package/repos/effect/.changeset/cold-rooms-show.md +5 -0
- package/repos/effect/.changeset/cold-sloths-wave.md +5 -0
- package/repos/effect/.changeset/compact-json-schema-enum.md +25 -0
- package/repos/effect/.changeset/config-withdefault-eager.md +7 -0
- package/repos/effect/.changeset/config.json +24 -0
- package/repos/effect/.changeset/consolidate-encoding.md +5 -0
- package/repos/effect/.changeset/consolidate-sql-error.md +16 -0
- package/repos/effect/.changeset/crisp-seas-warn.md +5 -0
- package/repos/effect/.changeset/cuddly-rooms-bet.md +5 -0
- package/repos/effect/.changeset/curly-poems-talk.md +5 -0
- package/repos/effect/.changeset/curly-spies-relax.md +5 -0
- package/repos/effect/.changeset/curvy-apples-float.md +5 -0
- package/repos/effect/.changeset/custom-http-security-openapi-generator.md +5 -0
- package/repos/effect/.changeset/cute-heads-thank.md +5 -0
- package/repos/effect/.changeset/cyan-loops-grow.md +5 -0
- package/repos/effect/.changeset/cyan-radios-switch.md +5 -0
- package/repos/effect/.changeset/deep-rivers-spend.md +5 -0
- package/repos/effect/.changeset/dirty-lamps-trade.md +5 -0
- package/repos/effect/.changeset/dirty-laws-wear.md +5 -0
- package/repos/effect/.changeset/duration-temporal-object-input.md +7 -0
- package/repos/effect/.changeset/eager-coats-cheat.md +5 -0
- package/repos/effect/.changeset/early-birds-dream.md +5 -0
- package/repos/effect/.changeset/early-donuts-argue.md +5 -0
- package/repos/effect/.changeset/early-peaches-check.md +6 -0
- package/repos/effect/.changeset/eff-691-default-logger-ordering.md +5 -0
- package/repos/effect/.changeset/eff-693-rpcgroup-handler-deps.md +5 -0
- package/repos/effect/.changeset/eff-694-cli-completions-module.md +5 -0
- package/repos/effect/.changeset/eff-695-layer-mock-dual-api.md +5 -0
- package/repos/effect/.changeset/eff-697-rpcserialization-json-array-decode.md +5 -0
- package/repos/effect/.changeset/eff-698-rpcserialization-unreachable-branch.md +5 -0
- package/repos/effect/.changeset/eff-700-httpapi-middleware-errors.md +12 -0
- package/repos/effect/.changeset/eff-701-httpapierror-respondable.md +5 -0
- package/repos/effect/.changeset/eff-704-stream-merge-predicate.md +5 -0
- package/repos/effect/.changeset/eff-705-layer-tap-apis.md +5 -0
- package/repos/effect/.changeset/eff-706-servicemap-mutate.md +6 -0
- package/repos/effect/.changeset/eff-716-response-id-tracker-map.md +5 -0
- package/repos/effect/.changeset/eff-717-openai-socket-cancel.md +5 -0
- package/repos/effect/.changeset/eff-718-embedding-model-surface.md +13 -0
- package/repos/effect/.changeset/eff-725-fix-catch-jsdoc.md +5 -0
- package/repos/effect/.changeset/eff-726-model-dimensions.md +7 -0
- package/repos/effect/.changeset/eff-727-cli-help-alignment.md +5 -0
- package/repos/effect/.changeset/eff-730-language-model-incremental-fallback.md +5 -0
- package/repos/effect/.changeset/eff-736-cached-with-ttl.md +5 -0
- package/repos/effect/.changeset/eff-738-cron-prev.md +5 -0
- package/repos/effect/.changeset/eff-739-openai-function-call-done.md +5 -0
- package/repos/effect/.changeset/eff-740-missing-summary-parts.md +5 -0
- package/repos/effect/.changeset/eff-742-http-client-request-web.md +5 -0
- package/repos/effect/.changeset/eff-744-sqlite-migrator-lock.md +5 -0
- package/repos/effect/.changeset/eff-746-fixed-iteration-catchup.md +5 -0
- package/repos/effect/.changeset/eff-747-unify-effect.md +5 -0
- package/repos/effect/.changeset/eff-754-url-builder-any.md +6 -0
- package/repos/effect/.changeset/eff-755-references-core.md +5 -0
- package/repos/effect/.changeset/eff-769-select-text-highlight.md +5 -0
- package/repos/effect/.changeset/eff-774-mutable-list-append-all-empty-array.md +5 -0
- package/repos/effect/.changeset/eff-777-schema-make-effect.md +5 -0
- package/repos/effect/.changeset/eff-778-http-middleware-path-logger.md +5 -0
- package/repos/effect/.changeset/eff-779-keyvaluestore-layer-sql.md +5 -0
- package/repos/effect/.changeset/eff-780-layer-unify.md +5 -0
- package/repos/effect/.changeset/eff-781-fix-stream-toqueue-types.md +5 -0
- package/repos/effect/.changeset/eff-782-httpapi-status-literals.md +5 -0
- package/repos/effect/.changeset/eff-783-atom-http-api-errors.md +5 -0
- package/repos/effect/.changeset/eff-819-cluster-workflow-shard-groups.md +5 -0
- package/repos/effect/.changeset/eight-turkeys-own.md +7 -0
- package/repos/effect/.changeset/eighty-lies-deny.md +5 -0
- package/repos/effect/.changeset/eighty-poets-draw.md +9 -0
- package/repos/effect/.changeset/eighty-swans-scream.md +5 -0
- package/repos/effect/.changeset/eighty-teeth-sniff.md +5 -0
- package/repos/effect/.changeset/eleven-apes-share.md +5 -0
- package/repos/effect/.changeset/eleven-numbers-bake.md +5 -0
- package/repos/effect/.changeset/empty-gifts-beg.md +5 -0
- package/repos/effect/.changeset/empty-http-rpc-client.md +5 -0
- package/repos/effect/.changeset/eventlog-unencrypted.md +5 -0
- package/repos/effect/.changeset/expand-schema-filter-output.md +24 -0
- package/repos/effect/.changeset/export-schema-encode-keys-interface.md +7 -0
- package/repos/effect/.changeset/extract-semaphore-latch.md +10 -0
- package/repos/effect/.changeset/fair-bees-relax.md +5 -0
- package/repos/effect/.changeset/fair-buttons-share.md +7 -0
- package/repos/effect/.changeset/fair-cooks-stop.md +5 -0
- package/repos/effect/.changeset/fair-cups-train.md +5 -0
- package/repos/effect/.changeset/fair-dryers-speak.md +5 -0
- package/repos/effect/.changeset/fair-forks-shake.md +5 -0
- package/repos/effect/.changeset/fair-pandas-prove.md +5 -0
- package/repos/effect/.changeset/fair-pants-float.md +5 -0
- package/repos/effect/.changeset/fair-poems-visit.md +5 -0
- package/repos/effect/.changeset/famous-wolves-lead.md +5 -0
- package/repos/effect/.changeset/fancy-glasses-grow.md +5 -0
- package/repos/effect/.changeset/fast-times-camp.md +5 -0
- package/repos/effect/.changeset/few-birds-matter.md +5 -0
- package/repos/effect/.changeset/few-cougars-dig.md +5 -0
- package/repos/effect/.changeset/few-foxes-grin.md +5 -0
- package/repos/effect/.changeset/few-mirrors-pull.md +14 -0
- package/repos/effect/.changeset/few-socks-poke.md +5 -0
- package/repos/effect/.changeset/fiber-runtime-start-metrics.md +5 -0
- package/repos/effect/.changeset/fiery-jokes-care.md +5 -0
- package/repos/effect/.changeset/fiery-mammals-call.md +5 -0
- package/repos/effect/.changeset/fine-walls-decide.md +5 -0
- package/repos/effect/.changeset/first-success-of.md +5 -0
- package/repos/effect/.changeset/five-parents-relax.md +5 -0
- package/repos/effect/.changeset/five-worms-rhyme.md +5 -0
- package/repos/effect/.changeset/fix-1332.md +5 -0
- package/repos/effect/.changeset/fix-1917.md +11 -0
- package/repos/effect/.changeset/fix-1927.md +5 -0
- package/repos/effect/.changeset/fix-1940.md +7 -0
- package/repos/effect/.changeset/fix-1947.md +7 -0
- package/repos/effect/.changeset/fix-2002.md +5 -0
- package/repos/effect/.changeset/fix-2012.md +5 -0
- package/repos/effect/.changeset/fix-2015.md +5 -0
- package/repos/effect/.changeset/fix-2260.md +5 -0
- package/repos/effect/.changeset/fix-2268.md +5 -0
- package/repos/effect/.changeset/fix-2271.md +5 -0
- package/repos/effect/.changeset/fix-2384.md +7 -0
- package/repos/effect/.changeset/fix-ai-empty-params-structured-output.md +5 -0
- package/repos/effect/.changeset/fix-ai-text-toolkit-typing.md +5 -0
- package/repos/effect/.changeset/fix-catch-orelse-error-erasure.md +5 -0
- package/repos/effect/.changeset/fix-class-constructor-defaults.md +5 -0
- package/repos/effect/.changeset/fix-cli-mixed-global-flag-context.md +7 -0
- package/repos/effect/.changeset/fix-config-withDefault.md +5 -0
- package/repos/effect/.changeset/fix-config-withdefault-filter.md +7 -0
- package/repos/effect/.changeset/fix-datetime-gmt.md +5 -0
- package/repos/effect/.changeset/fix-devtools-flush-on-teardown.md +10 -0
- package/repos/effect/.changeset/fix-duration-symmetric-rounding.md +5 -0
- package/repos/effect/.changeset/fix-entity-manager-defect-replay.md +5 -0
- package/repos/effect/.changeset/fix-entity-proxy-rpc-handler-context.md +5 -0
- package/repos/effect/.changeset/fix-entity-proxy-server-path-params.md +5 -0
- package/repos/effect/.changeset/fix-from-readable-stream-cancel-defect.md +5 -0
- package/repos/effect/.changeset/fix-graph-undirected-traversal.md +5 -0
- package/repos/effect/.changeset/fix-has-interrupts-only-empty.md +5 -0
- package/repos/effect/.changeset/fix-hashmap-bit31-ordering.md +5 -0
- package/repos/effect/.changeset/fix-headers-proto-enumerability.md +5 -0
- package/repos/effect/.changeset/fix-http-incoming-message-parse-options.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-endpoint-error-inference.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-schema-types.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-security-middleware-cache.md +5 -0
- package/repos/effect/.changeset/fix-is-json-dag.md +7 -0
- package/repos/effect/.changeset/fix-json-schema-anyof-oneof-siblings.md +5 -0
- package/repos/effect/.changeset/fix-json-schema-import-json.md +5 -0
- package/repos/effect/.changeset/fix-keepalive-blocked-timers.md +5 -0
- package/repos/effect/.changeset/fix-mcp-param-name-resolution.md +5 -0
- package/repos/effect/.changeset/fix-mermaid-escape-special-chars.md +5 -0
- package/repos/effect/.changeset/fix-mutable-list-filter-length.md +5 -0
- package/repos/effect/.changeset/fix-object-keyword-json-schema.md +5 -0
- package/repos/effect/.changeset/fix-openai-mcp-tool-names.md +5 -0
- package/repos/effect/.changeset/fix-openapi-generator-form-urlencoded.md +5 -0
- package/repos/effect/.changeset/fix-openapi-generator-swagger2openapi.md +5 -0
- package/repos/effect/.changeset/fix-openapi-preserve-multiple-response-content-types.md +5 -0
- package/repos/effect/.changeset/fix-openrouter-sparse-array.md +5 -0
- package/repos/effect/.changeset/fix-otel-logger-clock-skew.md +7 -0
- package/repos/effect/.changeset/fix-otel-logger-severity-number.md +20 -0
- package/repos/effect/.changeset/fix-queue-collect-duplication.md +5 -0
- package/repos/effect/.changeset/fix-random-string-seeds.md +5 -0
- package/repos/effect/.changeset/fix-ratelimiter-tokenbucket-redis-ttl.md +5 -0
- package/repos/effect/.changeset/fix-remainder-scientific-notation.md +5 -0
- package/repos/effect/.changeset/fix-request-resolver-pending-batches-leak.md +5 -0
- package/repos/effect/.changeset/fix-retry-transient-autocomplete.md +5 -0
- package/repos/effect/.changeset/fix-rpc-http-requestids-finalizer.md +5 -0
- package/repos/effect/.changeset/fix-rpc-json-id-edges.md +5 -0
- package/repos/effect/.changeset/fix-schedule-fixed-double-exec.md +9 -0
- package/repos/effect/.changeset/fix-schedule-reduce-sync-state.md +5 -0
- package/repos/effect/.changeset/fix-schema-arbitrary-exclusive-bounds.md +5 -0
- package/repos/effect/.changeset/fix-schema-defect-message.md +5 -0
- package/repos/effect/.changeset/fix-schema-encode-keys-property-keys.md +5 -0
- package/repos/effect/.changeset/fix-schema-encodekeys-class.md +5 -0
- package/repos/effect/.changeset/fix-schema-encodekeys-struct.md +5 -0
- package/repos/effect/.changeset/fix-schema-identifier-expected-message.md +5 -0
- package/repos/effect/.changeset/fix-schema-is-uuid.md +5 -0
- package/repos/effect/.changeset/fix-schema-option-non-schema-failures.md +17 -0
- package/repos/effect/.changeset/fix-schema-parser-checks.md +9 -0
- package/repos/effect/.changeset/fix-searchparam-initial-decode.md +5 -0
- package/repos/effect/.changeset/fix-serializable-wire-transfer.md +7 -0
- package/repos/effect/.changeset/fix-stream-grouped-within-flush.md +5 -0
- package/repos/effect/.changeset/fix-stream-run-for-each-while.md +5 -0
- package/repos/effect/.changeset/fix-stream-scan-effect.md +5 -0
- package/repos/effect/.changeset/fix-stream-scoped-scope.md +5 -0
- package/repos/effect/.changeset/fix-strip-approval-artifacts-multi-round.md +13 -0
- package/repos/effect/.changeset/fix-struct-utility-types-simplify.md +5 -0
- package/repos/effect/.changeset/fix-structwithrest-index-signatures.md +6 -0
- package/repos/effect/.changeset/fix-tagged-union-class-sentinels.md +5 -0
- package/repos/effect/.changeset/fix-tagged-union-match-unify.md +6 -0
- package/repos/effect/.changeset/fix-to-tagged-union-isanyof-custom-tags.md +10 -0
- package/repos/effect/.changeset/fix-tuple-with-rest-post-rest-index-drift.md +5 -0
- package/repos/effect/.changeset/fix-tuple-with-rest-post-rest-validation.md +5 -0
- package/repos/effect/.changeset/fix-types-voidifempty.md +5 -0
- package/repos/effect/.changeset/fix-void-response-encoding.md +5 -0
- package/repos/effect/.changeset/fix-workflow-proxy-rpc-handler-context.md +5 -0
- package/repos/effect/.changeset/flat-chicken-remain.md +5 -0
- package/repos/effect/.changeset/floppy-cows-spend.md +5 -0
- package/repos/effect/.changeset/floppy-items-admire.md +5 -0
- package/repos/effect/.changeset/floppy-pigs-kiss.md +5 -0
- package/repos/effect/.changeset/fluffy-meals-matter.md +5 -0
- package/repos/effect/.changeset/fluffy-pumas-push.md +5 -0
- package/repos/effect/.changeset/forked-memo-maps.md +6 -0
- package/repos/effect/.changeset/forty-hounds-cheer.md +5 -0
- package/repos/effect/.changeset/forty-otters-cry.md +5 -0
- package/repos/effect/.changeset/forty-rings-film.md +7 -0
- package/repos/effect/.changeset/forty-signs-stay.md +5 -0
- package/repos/effect/.changeset/forty-swans-divide.md +5 -0
- package/repos/effect/.changeset/forty-trees-pay.md +5 -0
- package/repos/effect/.changeset/four-papayas-bow.md +5 -0
- package/repos/effect/.changeset/four-points-repeat.md +5 -0
- package/repos/effect/.changeset/fresh-cats-smash.md +5 -0
- package/repos/effect/.changeset/fresh-emus-cheat.md +5 -0
- package/repos/effect/.changeset/fresh-monkeys-smoke.md +9 -0
- package/repos/effect/.changeset/fruity-houses-learn.md +6 -0
- package/repos/effect/.changeset/full-adults-double.md +5 -0
- package/repos/effect/.changeset/funny-crabs-hang.md +6 -0
- package/repos/effect/.changeset/funny-forks-move.md +5 -0
- package/repos/effect/.changeset/fuzzy-camels-hunt.md +5 -0
- package/repos/effect/.changeset/fuzzy-dodos-help.md +5 -0
- package/repos/effect/.changeset/fuzzy-lions-perform.md +5 -0
- package/repos/effect/.changeset/fuzzy-planets-sneeze.md +5 -0
- package/repos/effect/.changeset/gold-meteors-move.md +5 -0
- package/repos/effect/.changeset/gold-readers-hug.md +5 -0
- package/repos/effect/.changeset/gold-rings-start.md +5 -0
- package/repos/effect/.changeset/good-tools-work.md +7 -0
- package/repos/effect/.changeset/graph-algorithm-fixes.md +5 -0
- package/repos/effect/.changeset/great-trains-mate.md +5 -0
- package/repos/effect/.changeset/great-trams-report.md +5 -0
- package/repos/effect/.changeset/green-beds-unref.md +7 -0
- package/repos/effect/.changeset/green-chips-wash.md +5 -0
- package/repos/effect/.changeset/green-moons-smile.md +5 -0
- package/repos/effect/.changeset/green-pugs-play.md +5 -0
- package/repos/effect/.changeset/green-rings-prove.md +5 -0
- package/repos/effect/.changeset/happy-mirrors-dream.md +5 -0
- package/repos/effect/.changeset/heavy-loops-cut.md +5 -0
- package/repos/effect/.changeset/heavy-trams-fix.md +5 -0
- package/repos/effect/.changeset/hip-socks-travel.md +5 -0
- package/repos/effect/.changeset/honest-pens-thank.md +5 -0
- package/repos/effect/.changeset/honest-rivers-notice.md +5 -0
- package/repos/effect/.changeset/hot-taxis-fry.md +5 -0
- package/repos/effect/.changeset/httpapi-endpoint-relax-constraints.md +5 -0
- package/repos/effect/.changeset/httpapi-schema-service-types.md +6 -0
- package/repos/effect/.changeset/huge-moons-rhyme.md +5 -0
- package/repos/effect/.changeset/humble-pigs-dig.md +5 -0
- package/repos/effect/.changeset/icy-flies-cross.md +5 -0
- package/repos/effect/.changeset/itchy-radios-poke.md +5 -0
- package/repos/effect/.changeset/itchy-results-bet.md +5 -0
- package/repos/effect/.changeset/itchy-shrimps-deny.md +5 -0
- package/repos/effect/.changeset/itchy-toes-promise.md +5 -0
- package/repos/effect/.changeset/k8s-last-transition-null.md +5 -0
- package/repos/effect/.changeset/khaki-cats-learn.md +5 -0
- package/repos/effect/.changeset/khaki-melons-appear.md +5 -0
- package/repos/effect/.changeset/kind-hounds-float.md +5 -0
- package/repos/effect/.changeset/kind-windows-fall.md +5 -0
- package/repos/effect/.changeset/late-hotels-rule.md +5 -0
- package/repos/effect/.changeset/late-lamps-care.md +5 -0
- package/repos/effect/.changeset/late-rivers-applaud.md +5 -0
- package/repos/effect/.changeset/layer-map-dynamic-idle-ttl.md +5 -0
- package/repos/effect/.changeset/lazy-queens-rush.md +5 -0
- package/repos/effect/.changeset/lazy-recursive-forward-refs.md +5 -0
- package/repos/effect/.changeset/lazy-timers-exist.md +5 -0
- package/repos/effect/.changeset/legal-pants-drop.md +5 -0
- package/repos/effect/.changeset/lemon-taxis-sin.md +5 -0
- package/repos/effect/.changeset/light-kids-sneeze.md +5 -0
- package/repos/effect/.changeset/little-dryers-allow.md +7 -0
- package/repos/effect/.changeset/long-cameras-think.md +5 -0
- package/repos/effect/.changeset/lovely-cobras-change.md +5 -0
- package/repos/effect/.changeset/lovely-frogs-rescue.md +5 -0
- package/repos/effect/.changeset/lucky-buttons-jump.md +5 -0
- package/repos/effect/.changeset/lucky-phones-listen.md +5 -0
- package/repos/effect/.changeset/lucky-worms-type.md +5 -0
- package/repos/effect/.changeset/mean-dingos-share.md +5 -0
- package/repos/effect/.changeset/mean-trains-smash.md +5 -0
- package/repos/effect/.changeset/metal-parts-yell.md +5 -0
- package/repos/effect/.changeset/mighty-games-matter.md +5 -0
- package/repos/effect/.changeset/modern-uuid-guid-filter.md +5 -0
- package/repos/effect/.changeset/nasty-geese-grow.md +5 -0
- package/repos/effect/.changeset/neat-goats-wave.md +5 -0
- package/repos/effect/.changeset/neat-kings-chew.md +5 -0
- package/repos/effect/.changeset/neat-lions-rest.md +5 -0
- package/repos/effect/.changeset/neat-snails-wash.md +5 -0
- package/repos/effect/.changeset/neat-taxis-notice.md +5 -0
- package/repos/effect/.changeset/new-dogs-swim.md +5 -0
- package/repos/effect/.changeset/new-toes-stop.md +5 -0
- package/repos/effect/.changeset/ninety-geese-exist.md +5 -0
- package/repos/effect/.changeset/odd-boats-think.md +5 -0
- package/repos/effect/.changeset/odd-bulldogs-sleep.md +5 -0
- package/repos/effect/.changeset/odd-fans-glow.md +5 -0
- package/repos/effect/.changeset/odd-forks-talk.md +5 -0
- package/repos/effect/.changeset/odd-laws-draw.md +5 -0
- package/repos/effect/.changeset/odd-owls-smoke.md +5 -0
- package/repos/effect/.changeset/odd-socks-boil.md +5 -0
- package/repos/effect/.changeset/odd-suns-dance.md +5 -0
- package/repos/effect/.changeset/old-brooms-cry.md +5 -0
- package/repos/effect/.changeset/old-facts-stand.md +5 -0
- package/repos/effect/.changeset/old-mirrors-float.md +5 -0
- package/repos/effect/.changeset/olive-poems-visit.md +5 -0
- package/repos/effect/.changeset/open-hotels-remain.md +5 -0
- package/repos/effect/.changeset/openai-compat-nullable-tool-name.md +7 -0
- package/repos/effect/.changeset/openai-compat-reasoning.md +5 -0
- package/repos/effect/.changeset/otel-resource-env-precedence.md +5 -0
- package/repos/effect/.changeset/perfect-buckets-tickle.md +5 -0
- package/repos/effect/.changeset/petite-months-allow.md +6 -0
- package/repos/effect/.changeset/platform-crypto-service.md +9 -0
- package/repos/effect/.changeset/platform-node-shared-barrel.md +5 -0
- package/repos/effect/.changeset/plenty-moons-pull.md +5 -0
- package/repos/effect/.changeset/polite-brooms-tickle.md +5 -0
- package/repos/effect/.changeset/polite-pigs-speak.md +5 -0
- package/repos/effect/.changeset/polite-tables-kneel.md +5 -0
- package/repos/effect/.changeset/port-react-hydration.md +8 -0
- package/repos/effect/.changeset/pre.json +588 -0
- package/repos/effect/.changeset/public-deer-ring.md +5 -0
- package/repos/effect/.changeset/public-jeans-stop.md +5 -0
- package/repos/effect/.changeset/pubsub-publish-false.md +5 -0
- package/repos/effect/.changeset/puny-pens-clap.md +6 -0
- package/repos/effect/.changeset/purple-bars-prove.md +5 -0
- package/repos/effect/.changeset/purple-schools-float.md +5 -0
- package/repos/effect/.changeset/purple-turtles-draw.md +5 -0
- package/repos/effect/.changeset/quick-dragons-fix.md +5 -0
- package/repos/effect/.changeset/quick-falcons-travel.md +5 -0
- package/repos/effect/.changeset/quick-geese-relax.md +5 -0
- package/repos/effect/.changeset/quick-lamps-dig.md +5 -0
- package/repos/effect/.changeset/quick-lizards-fall.md +5 -0
- package/repos/effect/.changeset/quick-trees-join.md +5 -0
- package/repos/effect/.changeset/quiet-carpets-grin.md +5 -0
- package/repos/effect/.changeset/quiet-files-hunt.md +5 -0
- package/repos/effect/.changeset/quiet-lamps-jam.md +5 -0
- package/repos/effect/.changeset/quiet-radios-wave.md +5 -0
- package/repos/effect/.changeset/quiet-redis-scripts.md +5 -0
- package/repos/effect/.changeset/ready-olives-divide.md +5 -0
- package/repos/effect/.changeset/real-trains-ring.md +5 -0
- package/repos/effect/.changeset/red-pigs-repair.md +5 -0
- package/repos/effect/.changeset/redacted-representation-options.md +7 -0
- package/repos/effect/.changeset/refactor-cli-global-flags.md +27 -0
- package/repos/effect/.changeset/refactor-config-provider.md +23 -0
- package/repos/effect/.changeset/refactor-representation-references.md +5 -0
- package/repos/effect/.changeset/remove-effect-transactionwith.md +5 -0
- package/repos/effect/.changeset/remove-http-span-counter.md +5 -0
- package/repos/effect/.changeset/remove-nullor.md +5 -0
- package/repos/effect/.changeset/remove-openapi-fromapi-options.md +7 -0
- package/repos/effect/.changeset/remove-types-mergerecord.md +5 -0
- package/repos/effect/.changeset/remove-unused-utils-apis.md +5 -0
- package/repos/effect/.changeset/rename-rebuild-out.md +5 -0
- package/repos/effect/.changeset/restore-schema-parse-options.md +5 -0
- package/repos/effect/.changeset/rich-dots-push.md +5 -0
- package/repos/effect/.changeset/rich-hoops-nail.md +5 -0
- package/repos/effect/.changeset/rich-sloths-draw.md +5 -0
- package/repos/effect/.changeset/ripe-lies-battle.md +5 -0
- package/repos/effect/.changeset/rpc-middleware-provides-fix.md +5 -0
- package/repos/effect/.changeset/scalar-custom-fetch.md +5 -0
- package/repos/effect/.changeset/schema-as-class.md +17 -0
- package/repos/effect/.changeset/schema-asserts-signature.md +5 -0
- package/repos/effect/.changeset/schema-clean-up-additionalProperties.md +7 -0
- package/repos/effect/.changeset/schema-datetime-utc-from-string.md +5 -0
- package/repos/effect/.changeset/schema-decoding-defaults-services.md +7 -0
- package/repos/effect/.changeset/schema-defaults-issue-channel.md +7 -0
- package/repos/effect/.changeset/schema-dollar-prefix.md +5 -0
- package/repos/effect/.changeset/schema-missing-self-generic.md +5 -0
- package/repos/effect/.changeset/schema-ordered-arbitrary-constraints.md +12 -0
- package/repos/effect/.changeset/schema-parser-adapter-errors.md +5 -0
- package/repos/effect/.changeset/schema-refactor-toCodecJson.md +5 -0
- package/repos/effect/.changeset/schema-remove-annotate-in.md +5 -0
- package/repos/effect/.changeset/schema-rename-makeUnsafe-to-make.md +7 -0
- package/repos/effect/.changeset/schema-rename-parser-makeUnsafe.md +5 -0
- package/repos/effect/.changeset/schema-result-combinators.md +5 -0
- package/repos/effect/.changeset/schema-struct-simplify.md +5 -0
- package/repos/effect/.changeset/seven-mugs-marry.md +5 -0
- package/repos/effect/.changeset/shaggy-birds-stay.md +5 -0
- package/repos/effect/.changeset/shaggy-cities-push.md +5 -0
- package/repos/effect/.changeset/shaggy-numbers-accept.md +5 -0
- package/repos/effect/.changeset/sharp-emus-applaud.md +5 -0
- package/repos/effect/.changeset/sharp-goats-wink.md +5 -0
- package/repos/effect/.changeset/sharp-pandas-care.md +5 -0
- package/repos/effect/.changeset/sharp-peas-march.md +5 -0
- package/repos/effect/.changeset/sharp-rules-draw.md +5 -0
- package/repos/effect/.changeset/sharp-singers-sort.md +5 -0
- package/repos/effect/.changeset/shiny-trains-hug.md +5 -0
- package/repos/effect/.changeset/short-cows-relate.md +5 -0
- package/repos/effect/.changeset/short-foxes-admire.md +5 -0
- package/repos/effect/.changeset/shy-cycles-flow.md +5 -0
- package/repos/effect/.changeset/shy-geckos-sniff.md +30 -0
- package/repos/effect/.changeset/silent-geckos-matter.md +5 -0
- package/repos/effect/.changeset/silent-needles-design.md +6 -0
- package/repos/effect/.changeset/silent-plants-matter.md +8 -0
- package/repos/effect/.changeset/silent-spoons-stare.md +5 -0
- package/repos/effect/.changeset/silly-loops-tickle.md +5 -0
- package/repos/effect/.changeset/silver-bulk-indexeddb.md +5 -0
- package/repos/effect/.changeset/silver-emus-smoke.md +5 -0
- package/repos/effect/.changeset/silver-kings-poke.md +5 -0
- package/repos/effect/.changeset/silver-snails-sqlite.md +17 -0
- package/repos/effect/.changeset/silver-wings-watch.md +5 -0
- package/repos/effect/.changeset/six-cups-taste.md +5 -0
- package/repos/effect/.changeset/sixty-mails-shout.md +5 -0
- package/repos/effect/.changeset/sixty-socks-yell.md +5 -0
- package/repos/effect/.changeset/slimy-planets-divide.md +5 -0
- package/repos/effect/.changeset/slimy-turtles-juggle.md +5 -0
- package/repos/effect/.changeset/slow-beans-battle.md +28 -0
- package/repos/effect/.changeset/slow-berries-enjoy.md +5 -0
- package/repos/effect/.changeset/small-bugs-hunt.md +5 -0
- package/repos/effect/.changeset/small-crabs-care.md +9 -0
- package/repos/effect/.changeset/small-pets-sit.md +6 -0
- package/repos/effect/.changeset/smart-ducks-jump.md +6 -0
- package/repos/effect/.changeset/smart-pillows-buy.md +6 -0
- package/repos/effect/.changeset/smart-timers-fly.md +5 -0
- package/repos/effect/.changeset/smart-tips-sort.md +5 -0
- package/repos/effect/.changeset/social-pumas-prove.md +5 -0
- package/repos/effect/.changeset/soft-comics-wink.md +5 -0
- package/repos/effect/.changeset/soft-delete-sqlmodel.md +5 -0
- package/repos/effect/.changeset/soft-seals-allow.md +5 -0
- package/repos/effect/.changeset/solid-cougars-attack.md +5 -0
- package/repos/effect/.changeset/solid-doors-ring.md +5 -0
- package/repos/effect/.changeset/solid-items-tease.md +5 -0
- package/repos/effect/.changeset/solid-towns-smoke.md +5 -0
- package/repos/effect/.changeset/sour-canyons-rescue.md +5 -0
- package/repos/effect/.changeset/sparkly-bears-act.md +5 -0
- package/repos/effect/.changeset/sparkly-coins-sit.md +5 -0
- package/repos/effect/.changeset/spotty-comics-fry.md +5 -0
- package/repos/effect/.changeset/sql-migrator-mjs-mts.md +5 -0
- package/repos/effect/.changeset/stale-dots-tell.md +5 -0
- package/repos/effect/.changeset/stale-snakes-know.md +5 -0
- package/repos/effect/.changeset/strict-areas-end.md +5 -0
- package/repos/effect/.changeset/strict-buckets-hug.md +8 -0
- package/repos/effect/.changeset/strip-resolved-approvals.md +5 -0
- package/repos/effect/.changeset/strong-balloons-tickle.md +5 -0
- package/repos/effect/.changeset/strong-bees-queue.md +5 -0
- package/repos/effect/.changeset/struct-record.md +5 -0
- package/repos/effect/.changeset/sunny-ads-hang.md +5 -0
- package/repos/effect/.changeset/sunny-bikes-sleep.md +5 -0
- package/repos/effect/.changeset/sunny-rooms-invent.md +5 -0
- package/repos/effect/.changeset/sweet-donuts-bet.md +5 -0
- package/repos/effect/.changeset/sweet-hotels-give.md +5 -0
- package/repos/effect/.changeset/sweet-views-learn.md +5 -0
- package/repos/effect/.changeset/swift-spiders-unpack.md +5 -0
- package/repos/effect/.changeset/swift-symbols-stand.md +5 -0
- package/repos/effect/.changeset/tagged-error-class-optional-empty-props.md +5 -0
- package/repos/effect/.changeset/tall-hairs-return.md +5 -0
- package/repos/effect/.changeset/tall-mails-listen.md +5 -0
- package/repos/effect/.changeset/tall-queens-cheer.md +5 -0
- package/repos/effect/.changeset/tall-wombats-wave.md +5 -0
- package/repos/effect/.changeset/tangy-colts-lose.md +5 -0
- package/repos/effect/.changeset/tasty-comics-send.md +5 -0
- package/repos/effect/.changeset/ten-kings-fry.md +5 -0
- package/repos/effect/.changeset/thick-pandas-wait.md +5 -0
- package/repos/effect/.changeset/thin-ducks-wonder.md +5 -0
- package/repos/effect/.changeset/thirty-ducks-go.md +5 -0
- package/repos/effect/.changeset/thirty-pans-love.md +5 -0
- package/repos/effect/.changeset/three-corners-sort.md +5 -0
- package/repos/effect/.changeset/three-ravens-jam.md +5 -0
- package/repos/effect/.changeset/three-tomatoes-wave.md +5 -0
- package/repos/effect/.changeset/tidy-foxes-own.md +5 -0
- package/repos/effect/.changeset/tidy-icons-glow.md +5 -0
- package/repos/effect/.changeset/tidy-stacks-encode.md +5 -0
- package/repos/effect/.changeset/tidy-stars-drive.md +5 -0
- package/repos/effect/.changeset/tiny-buckets-wave.md +5 -0
- package/repos/effect/.changeset/tiny-lilies-flash.md +5 -0
- package/repos/effect/.changeset/tiny-rabbits-smile.md +5 -0
- package/repos/effect/.changeset/tocodecjson-return-json-type.md +10 -0
- package/repos/effect/.changeset/tool-get-json-schema-tests.md +5 -0
- package/repos/effect/.changeset/true-actors-battle.md +5 -0
- package/repos/effect/.changeset/twenty-buttons-cheer.md +5 -0
- package/repos/effect/.changeset/two-roses-double.md +5 -0
- package/repos/effect/.changeset/unify-error-defect-stack-options.md +23 -0
- package/repos/effect/.changeset/update-schema-arbitrary-report.md +22 -0
- package/repos/effect/.changeset/upset-colts-stick.md +5 -0
- package/repos/effect/.changeset/vast-bananas-send.md +5 -0
- package/repos/effect/.changeset/vast-deserts-travel.md +5 -0
- package/repos/effect/.changeset/violet-peaches-feel.md +5 -0
- package/repos/effect/.changeset/vitest-layer-top-level-options.md +5 -0
- package/repos/effect/.changeset/wacky-grapes-poke.md +5 -0
- package/repos/effect/.changeset/wacky-rice-add.md +5 -0
- package/repos/effect/.changeset/warm-dolls-brake.md +5 -0
- package/repos/effect/.changeset/warm-friends-tie.md +5 -0
- package/repos/effect/.changeset/warm-snails-shop.md +7 -0
- package/repos/effect/.changeset/wet-news-invent.md +5 -0
- package/repos/effect/.changeset/wild-readers-clean.md +5 -0
- package/repos/effect/.changeset/wild-suns-bearer-space.md +5 -0
- package/repos/effect/.changeset/wise-ants-wave.md +6 -0
- package/repos/effect/.changeset/wise-flags-shift.md +22 -0
- package/repos/effect/.changeset/wise-oranges-stay.md +5 -0
- package/repos/effect/.changeset/witty-lobsters-share.md +5 -0
- package/repos/effect/.changeset/yellow-adults-study.md +5 -0
- package/repos/effect/.changeset/yellow-clocks-dance.md +5 -0
- package/repos/effect/.changeset/yellow-dingos-jump.md +5 -0
- package/repos/effect/.changeset/young-doors-change.md +5 -0
- package/repos/effect/.envrc +1 -0
- package/repos/effect/.github/actions/setup/action.yaml +33 -0
- package/repos/effect/.github/workflows/ai-codegen.yml +66 -0
- package/repos/effect/.github/workflows/bundle-comment.yml +72 -0
- package/repos/effect/.github/workflows/check.yml +195 -0
- package/repos/effect/.github/workflows/release.yml +47 -0
- package/repos/effect/.github/workflows/snapshot.yml +34 -0
- package/repos/effect/.oxlintrc.json +25 -0
- package/repos/effect/.patterns/effect.md +102 -0
- package/repos/effect/.patterns/jsdoc.md +53 -0
- package/repos/effect/.patterns/testing.md +44 -0
- package/repos/effect/.specs/README.md +2 -0
- package/repos/effect/.vscode/extensions.json +6 -0
- package/repos/effect/.vscode/settings.json +44 -0
- package/repos/effect/AGENTS.md +139 -0
- package/repos/effect/LICENSE +21 -0
- package/repos/effect/LLMS.md +343 -0
- package/repos/effect/MIGRATION.md +83 -0
- package/repos/effect/README.md +3 -0
- package/repos/effect/TODOS.md +28 -0
- package/repos/effect/ai-docs/README.md +44 -0
- package/repos/effect/ai-docs/package.json +36 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +39 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/index.md +5 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/01_service.ts +45 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/10_reference.ts +10 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/20_layer-composition.ts +70 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/20_layer-unwrap.ts +66 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/index.md +5 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/01_error-handling.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/10_catch-tags.ts +24 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/20_reason-errors.ts +64 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/index.md +1 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/10_acquire-release.ts +105 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/20_layer-side-effects.ts +31 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/30_layer-map.ts +86 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/index.md +3 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/10_run-main.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/20_layer-launch.ts +27 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/index.md +1 -0
- package/repos/effect/ai-docs/src/01_effect/06_pubsub/10_pubsub.ts +56 -0
- package/repos/effect/ai-docs/src/01_effect/06_pubsub/index.md +3 -0
- package/repos/effect/ai-docs/src/02_stream/10_creating-streams.ts +103 -0
- package/repos/effect/ai-docs/src/02_stream/20_consuming-streams.ts +137 -0
- package/repos/effect/ai-docs/src/02_stream/30_encoding.ts +165 -0
- package/repos/effect/ai-docs/src/02_stream/index.md +4 -0
- package/repos/effect/ai-docs/src/03_integration/10_managed-runtime.ts +129 -0
- package/repos/effect/ai-docs/src/03_integration/index.md +5 -0
- package/repos/effect/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
- package/repos/effect/ai-docs/src/05_batching/index.md +3 -0
- package/repos/effect/ai-docs/src/06_schedule/10_schedules.ts +102 -0
- package/repos/effect/ai-docs/src/06_schedule/index.md +3 -0
- package/repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
- package/repos/effect/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
- package/repos/effect/ai-docs/src/07_datetime/index.md +5 -0
- package/repos/effect/ai-docs/src/08_observability/10_logging.ts +66 -0
- package/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
- package/repos/effect/ai-docs/src/08_observability/index.md +7 -0
- package/repos/effect/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
- package/repos/effect/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
- package/repos/effect/ai-docs/src/09_testing/index.md +1 -0
- package/repos/effect/ai-docs/src/50_http-client/10_basics.ts +102 -0
- package/repos/effect/ai-docs/src/50_http-client/index.md +3 -0
- package/repos/effect/ai-docs/src/51_http-server/10_basics.ts +116 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts +91 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts +12 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +36 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +71 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts +62 -0
- package/repos/effect/ai-docs/src/51_http-server/index.md +3 -0
- package/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
- package/repos/effect/ai-docs/src/60_child-process/index.md +3 -0
- package/repos/effect/ai-docs/src/70_cli/10_basics.ts +136 -0
- package/repos/effect/ai-docs/src/70_cli/index.md +5 -0
- package/repos/effect/ai-docs/src/71_ai/10_language-model.ts +156 -0
- package/repos/effect/ai-docs/src/71_ai/20_tools.ts +226 -0
- package/repos/effect/ai-docs/src/71_ai/30_chat.ts +158 -0
- package/repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
- package/repos/effect/ai-docs/src/71_ai/index.md +5 -0
- package/repos/effect/ai-docs/src/80_cluster/10_entities.ts +97 -0
- package/repos/effect/ai-docs/src/80_cluster/index.md +4 -0
- package/repos/effect/ai-docs/src/index.md +12 -0
- package/repos/effect/ai-docs/tsconfig.json +24 -0
- package/repos/effect/cookbooks/schedule.md +3507 -0
- package/repos/effect/deno.json +33 -0
- package/repos/effect/docker-compose.yaml +18 -0
- package/repos/effect/dprint.json +32 -0
- package/repos/effect/flake.lock +27 -0
- package/repos/effect/flake.nix +24 -0
- package/repos/effect/jsdocs.config.json +16 -0
- package/repos/effect/migration/cause.md +163 -0
- package/repos/effect/migration/equality.md +72 -0
- package/repos/effect/migration/error-handling.md +106 -0
- package/repos/effect/migration/fiber-keep-alive.md +74 -0
- package/repos/effect/migration/fiberref.md +109 -0
- package/repos/effect/migration/forking.md +94 -0
- package/repos/effect/migration/generators.md +32 -0
- package/repos/effect/migration/layer-memoization.md +98 -0
- package/repos/effect/migration/runtime.md +88 -0
- package/repos/effect/migration/schema.md +1032 -0
- package/repos/effect/migration/scope.md +50 -0
- package/repos/effect/migration/services.md +235 -0
- package/repos/effect/migration/v3-to-v4.md +421 -0
- package/repos/effect/migration/yieldable.md +173 -0
- package/repos/effect/package.json +104 -0
- package/repos/effect/packages/ai/anthropic/CHANGELOG.md +611 -0
- package/repos/effect/packages/ai/anthropic/codegen.yml +63 -0
- package/repos/effect/packages/ai/anthropic/docgen.json +26 -0
- package/repos/effect/packages/ai/anthropic/package.json +67 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts +432 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts +84 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicError.ts +212 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts +3098 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts +132 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicTool.ts +2512 -0
- package/repos/effect/packages/ai/anthropic/src/Generated.ts +12600 -0
- package/repos/effect/packages/ai/anthropic/src/index.ts +40 -0
- package/repos/effect/packages/ai/anthropic/src/internal/errors.ts +370 -0
- package/repos/effect/packages/ai/anthropic/src/internal/utilities.ts +26 -0
- package/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +300 -0
- package/repos/effect/packages/ai/anthropic/tsconfig.json +8 -0
- package/repos/effect/packages/ai/anthropic/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openai/CHANGELOG.md +668 -0
- package/repos/effect/packages/ai/openai/codegen.yaml +41 -0
- package/repos/effect/packages/ai/openai/docgen.json +24 -0
- package/repos/effect/packages/ai/openai/package.json +67 -0
- package/repos/effect/packages/ai/openai/src/Generated.ts +35774 -0
- package/repos/effect/packages/ai/openai/src/OpenAiClient.ts +730 -0
- package/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts +202 -0
- package/repos/effect/packages/ai/openai/src/OpenAiConfig.ts +93 -0
- package/repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts +258 -0
- package/repos/effect/packages/ai/openai/src/OpenAiError.ts +242 -0
- package/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts +3133 -0
- package/repos/effect/packages/ai/openai/src/OpenAiSchema.ts +1286 -0
- package/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts +162 -0
- package/repos/effect/packages/ai/openai/src/OpenAiTool.ts +342 -0
- package/repos/effect/packages/ai/openai/src/index.ts +55 -0
- package/repos/effect/packages/ai/openai/src/internal/errors.ts +349 -0
- package/repos/effect/packages/ai/openai/src/internal/utilities.ts +33 -0
- package/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts +798 -0
- package/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts +289 -0
- package/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts +1528 -0
- package/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts +273 -0
- package/repos/effect/packages/ai/openai/tsconfig.json +8 -0
- package/repos/effect/packages/ai/openai/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openai-compat/CHANGELOG.md +626 -0
- package/repos/effect/packages/ai/openai-compat/docgen.json +24 -0
- package/repos/effect/packages/ai/openai-compat/package.json +69 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts +1221 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts +87 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts +254 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +178 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +1951 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts +164 -0
- package/repos/effect/packages/ai/openai-compat/src/index.ts +35 -0
- package/repos/effect/packages/ai/openai-compat/src/internal/errors.ts +327 -0
- package/repos/effect/packages/ai/openai-compat/src/internal/utilities.ts +33 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts +474 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts +216 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts +1505 -0
- package/repos/effect/packages/ai/openai-compat/tsconfig.json +8 -0
- package/repos/effect/packages/ai/openai-compat/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openrouter/CHANGELOG.md +607 -0
- package/repos/effect/packages/ai/openrouter/codegen.yml +35 -0
- package/repos/effect/packages/ai/openrouter/docgen.json +24 -0
- package/repos/effect/packages/ai/openrouter/package.json +67 -0
- package/repos/effect/packages/ai/openrouter/src/Generated.ts +10028 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts +357 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts +99 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts +225 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts +1796 -0
- package/repos/effect/packages/ai/openrouter/src/index.ts +30 -0
- package/repos/effect/packages/ai/openrouter/src/internal/errors.ts +375 -0
- package/repos/effect/packages/ai/openrouter/src/internal/utilities.ts +92 -0
- package/repos/effect/packages/ai/openrouter/tsconfig.json +10 -0
- package/repos/effect/packages/ai/openrouter/vitest.config.ts +6 -0
- package/repos/effect/packages/atom/react/CHANGELOG.md +599 -0
- package/repos/effect/packages/atom/react/LICENSE +21 -0
- package/repos/effect/packages/atom/react/README.md +7 -0
- package/repos/effect/packages/atom/react/docgen.json +25 -0
- package/repos/effect/packages/atom/react/package.json +79 -0
- package/repos/effect/packages/atom/react/src/Hooks.ts +476 -0
- package/repos/effect/packages/atom/react/src/ReactHydration.ts +109 -0
- package/repos/effect/packages/atom/react/src/RegistryContext.ts +108 -0
- package/repos/effect/packages/atom/react/src/ScopedAtom.ts +151 -0
- package/repos/effect/packages/atom/react/src/index.ts +23 -0
- package/repos/effect/packages/atom/react/test/index.test.tsx +650 -0
- package/repos/effect/packages/atom/react/tsconfig.json +8 -0
- package/repos/effect/packages/atom/react/vitest.config.ts +9 -0
- package/repos/effect/packages/atom/react/vitest.setup.ts +8 -0
- package/repos/effect/packages/atom/solid/CHANGELOG.md +595 -0
- package/repos/effect/packages/atom/solid/LICENSE +21 -0
- package/repos/effect/packages/atom/solid/README.md +7 -0
- package/repos/effect/packages/atom/solid/docgen.json +26 -0
- package/repos/effect/packages/atom/solid/package.json +72 -0
- package/repos/effect/packages/atom/solid/src/Hooks.ts +359 -0
- package/repos/effect/packages/atom/solid/src/RegistryContext.ts +80 -0
- package/repos/effect/packages/atom/solid/src/index.ts +13 -0
- package/repos/effect/packages/atom/solid/test/index.test.tsx +296 -0
- package/repos/effect/packages/atom/solid/tsconfig.json +8 -0
- package/repos/effect/packages/atom/solid/vitest.config.ts +15 -0
- package/repos/effect/packages/atom/solid/vitest.setup.ts +8 -0
- package/repos/effect/packages/atom/vue/CHANGELOG.md +593 -0
- package/repos/effect/packages/atom/vue/LICENSE +21 -0
- package/repos/effect/packages/atom/vue/README.md +7 -0
- package/repos/effect/packages/atom/vue/docgen.json +24 -0
- package/repos/effect/packages/atom/vue/package.json +68 -0
- package/repos/effect/packages/atom/vue/src/index.ts +211 -0
- package/repos/effect/packages/atom/vue/test/index.test.ts +5 -0
- package/repos/effect/packages/atom/vue/tsconfig.json +14 -0
- package/repos/effect/packages/atom/vue/vitest.config.ts +8 -0
- package/repos/effect/packages/effect/CHANGELOG.md +1607 -0
- package/repos/effect/packages/effect/CONFIG.md +690 -0
- package/repos/effect/packages/effect/HTTPAPI.md +3213 -0
- package/repos/effect/packages/effect/LICENSE +21 -0
- package/repos/effect/packages/effect/MCP.md +369 -0
- package/repos/effect/packages/effect/OPTIC.md +620 -0
- package/repos/effect/packages/effect/README.md +53 -0
- package/repos/effect/packages/effect/SCHEMA.md +7284 -0
- package/repos/effect/packages/effect/benchmark/schema/Enums.ts +40 -0
- package/repos/effect/packages/effect/benchmark/schema/Optic.ts +79 -0
- package/repos/effect/packages/effect/benchmark/schema/array.ts +74 -0
- package/repos/effect/packages/effect/benchmark/schema/filter.ts +74 -0
- package/repos/effect/packages/effect/benchmark/schema/object.ts +82 -0
- package/repos/effect/packages/effect/benchmark/schema/tagged-union.ts +69 -0
- package/repos/effect/packages/effect/benchmark/schema/transformation.ts +91 -0
- package/repos/effect/packages/effect/benchmark/stream/splitLines.ts +46 -0
- package/repos/effect/packages/effect/docgen.json +38 -0
- package/repos/effect/packages/effect/package.json +127 -0
- package/repos/effect/packages/effect/src/Array.ts +4908 -0
- package/repos/effect/packages/effect/src/BigDecimal.ts +1943 -0
- package/repos/effect/packages/effect/src/BigInt.ts +1029 -0
- package/repos/effect/packages/effect/src/Boolean.ts +481 -0
- package/repos/effect/packages/effect/src/Brand.ts +318 -0
- package/repos/effect/packages/effect/src/Cache.ts +1295 -0
- package/repos/effect/packages/effect/src/Cause.ts +1939 -0
- package/repos/effect/packages/effect/src/Channel.ts +8615 -0
- package/repos/effect/packages/effect/src/ChannelSchema.ts +308 -0
- package/repos/effect/packages/effect/src/Chunk.ts +2976 -0
- package/repos/effect/packages/effect/src/Clock.ts +194 -0
- package/repos/effect/packages/effect/src/Combiner.ts +316 -0
- package/repos/effect/packages/effect/src/Config.ts +1416 -0
- package/repos/effect/packages/effect/src/ConfigProvider.ts +1159 -0
- package/repos/effect/packages/effect/src/Console.ts +653 -0
- package/repos/effect/packages/effect/src/Context.ts +1339 -0
- package/repos/effect/packages/effect/src/Cron.ts +1212 -0
- package/repos/effect/packages/effect/src/Crypto.ts +318 -0
- package/repos/effect/packages/effect/src/Data.ts +774 -0
- package/repos/effect/packages/effect/src/DateTime.ts +2922 -0
- package/repos/effect/packages/effect/src/Deferred.ts +917 -0
- package/repos/effect/packages/effect/src/Differ.ts +32 -0
- package/repos/effect/packages/effect/src/Duration.ts +1827 -0
- package/repos/effect/packages/effect/src/Effect.ts +15347 -0
- package/repos/effect/packages/effect/src/Effectable.ts +68 -0
- package/repos/effect/packages/effect/src/Encoding.ts +1046 -0
- package/repos/effect/packages/effect/src/Equal.ts +550 -0
- package/repos/effect/packages/effect/src/Equivalence.ts +870 -0
- package/repos/effect/packages/effect/src/ErrorReporter.ts +543 -0
- package/repos/effect/packages/effect/src/ExecutionPlan.ts +416 -0
- package/repos/effect/packages/effect/src/Exit.ts +1085 -0
- package/repos/effect/packages/effect/src/Fiber.ts +632 -0
- package/repos/effect/packages/effect/src/FiberHandle.ts +815 -0
- package/repos/effect/packages/effect/src/FiberMap.ts +1030 -0
- package/repos/effect/packages/effect/src/FiberSet.ts +713 -0
- package/repos/effect/packages/effect/src/FileSystem.ts +1394 -0
- package/repos/effect/packages/effect/src/Filter.ts +827 -0
- package/repos/effect/packages/effect/src/Formatter.ts +319 -0
- package/repos/effect/packages/effect/src/Function.ts +1415 -0
- package/repos/effect/packages/effect/src/Graph.ts +4892 -0
- package/repos/effect/packages/effect/src/HKT.ts +213 -0
- package/repos/effect/packages/effect/src/Hash.ts +539 -0
- package/repos/effect/packages/effect/src/HashMap.ts +1236 -0
- package/repos/effect/packages/effect/src/HashRing.ts +426 -0
- package/repos/effect/packages/effect/src/HashSet.ts +605 -0
- package/repos/effect/packages/effect/src/Inspectable.ts +329 -0
- package/repos/effect/packages/effect/src/Iterable.ts +2483 -0
- package/repos/effect/packages/effect/src/JsonPatch.ts +469 -0
- package/repos/effect/packages/effect/src/JsonPointer.ts +80 -0
- package/repos/effect/packages/effect/src/JsonSchema.ts +948 -0
- package/repos/effect/packages/effect/src/Latch.ts +373 -0
- package/repos/effect/packages/effect/src/Layer.ts +2773 -0
- package/repos/effect/packages/effect/src/LayerMap.ts +468 -0
- package/repos/effect/packages/effect/src/LogLevel.ts +412 -0
- package/repos/effect/packages/effect/src/Logger.ts +1284 -0
- package/repos/effect/packages/effect/src/ManagedRuntime.ts +381 -0
- package/repos/effect/packages/effect/src/Match.ts +2606 -0
- package/repos/effect/packages/effect/src/Metric.ts +4058 -0
- package/repos/effect/packages/effect/src/MutableHashMap.ts +845 -0
- package/repos/effect/packages/effect/src/MutableHashSet.ts +422 -0
- package/repos/effect/packages/effect/src/MutableList.ts +967 -0
- package/repos/effect/packages/effect/src/MutableRef.ts +822 -0
- package/repos/effect/packages/effect/src/Newtype.ts +319 -0
- package/repos/effect/packages/effect/src/NonEmptyIterable.ts +221 -0
- package/repos/effect/packages/effect/src/Number.ts +860 -0
- package/repos/effect/packages/effect/src/Optic.ts +1701 -0
- package/repos/effect/packages/effect/src/Option.ts +2664 -0
- package/repos/effect/packages/effect/src/Order.ts +982 -0
- package/repos/effect/packages/effect/src/Ordering.ts +182 -0
- package/repos/effect/packages/effect/src/PartitionedSemaphore.ts +561 -0
- package/repos/effect/packages/effect/src/Path.ts +870 -0
- package/repos/effect/packages/effect/src/Pipeable.ts +674 -0
- package/repos/effect/packages/effect/src/PlatformError.ts +221 -0
- package/repos/effect/packages/effect/src/Pool.ts +699 -0
- package/repos/effect/packages/effect/src/Predicate.ts +1872 -0
- package/repos/effect/packages/effect/src/PrimaryKey.ts +123 -0
- package/repos/effect/packages/effect/src/PubSub.ts +2797 -0
- package/repos/effect/packages/effect/src/Pull.ts +376 -0
- package/repos/effect/packages/effect/src/Queue.ts +1962 -0
- package/repos/effect/packages/effect/src/Random.ts +549 -0
- package/repos/effect/packages/effect/src/RcMap.ts +612 -0
- package/repos/effect/packages/effect/src/RcRef.ts +225 -0
- package/repos/effect/packages/effect/src/Record.ts +1563 -0
- package/repos/effect/packages/effect/src/Redactable.ts +168 -0
- package/repos/effect/packages/effect/src/Redacted.ts +314 -0
- package/repos/effect/packages/effect/src/Reducer.ts +178 -0
- package/repos/effect/packages/effect/src/Ref.ts +801 -0
- package/repos/effect/packages/effect/src/References.ts +786 -0
- package/repos/effect/packages/effect/src/RegExp.ts +82 -0
- package/repos/effect/packages/effect/src/Request.ts +598 -0
- package/repos/effect/packages/effect/src/RequestResolver.ts +1249 -0
- package/repos/effect/packages/effect/src/Resource.ts +184 -0
- package/repos/effect/packages/effect/src/Result.ts +1939 -0
- package/repos/effect/packages/effect/src/Runtime.ts +432 -0
- package/repos/effect/packages/effect/src/Schedule.ts +3564 -0
- package/repos/effect/packages/effect/src/Scheduler.ts +285 -0
- package/repos/effect/packages/effect/src/Schema.ts +14653 -0
- package/repos/effect/packages/effect/src/SchemaAST.ts +3903 -0
- package/repos/effect/packages/effect/src/SchemaGetter.ts +1870 -0
- package/repos/effect/packages/effect/src/SchemaIssue.ts +1176 -0
- package/repos/effect/packages/effect/src/SchemaParser.ts +1105 -0
- package/repos/effect/packages/effect/src/SchemaRepresentation.ts +3944 -0
- package/repos/effect/packages/effect/src/SchemaTransformation.ts +1818 -0
- package/repos/effect/packages/effect/src/SchemaUtils.ts +51 -0
- package/repos/effect/packages/effect/src/Scope.ts +572 -0
- package/repos/effect/packages/effect/src/ScopedCache.ts +820 -0
- package/repos/effect/packages/effect/src/ScopedRef.ts +192 -0
- package/repos/effect/packages/effect/src/Semaphore.ts +517 -0
- package/repos/effect/packages/effect/src/Sink.ts +2174 -0
- package/repos/effect/packages/effect/src/Stdio.ts +143 -0
- package/repos/effect/packages/effect/src/Stream.ts +11722 -0
- package/repos/effect/packages/effect/src/String.ts +1427 -0
- package/repos/effect/packages/effect/src/Struct.ts +979 -0
- package/repos/effect/packages/effect/src/SubscriptionRef.ts +1021 -0
- package/repos/effect/packages/effect/src/Symbol.ts +31 -0
- package/repos/effect/packages/effect/src/SynchronizedRef.ts +668 -0
- package/repos/effect/packages/effect/src/Take.ts +46 -0
- package/repos/effect/packages/effect/src/Terminal.ts +188 -0
- package/repos/effect/packages/effect/src/Tracer.ts +683 -0
- package/repos/effect/packages/effect/src/Trie.ts +953 -0
- package/repos/effect/packages/effect/src/Tuple.ts +737 -0
- package/repos/effect/packages/effect/src/TxChunk.ts +822 -0
- package/repos/effect/packages/effect/src/TxDeferred.ts +319 -0
- package/repos/effect/packages/effect/src/TxHashMap.ts +2165 -0
- package/repos/effect/packages/effect/src/TxHashSet.ts +908 -0
- package/repos/effect/packages/effect/src/TxPriorityQueue.ts +567 -0
- package/repos/effect/packages/effect/src/TxPubSub.ts +684 -0
- package/repos/effect/packages/effect/src/TxQueue.ts +1488 -0
- package/repos/effect/packages/effect/src/TxReentrantLock.ts +655 -0
- package/repos/effect/packages/effect/src/TxRef.ts +282 -0
- package/repos/effect/packages/effect/src/TxSemaphore.ts +707 -0
- package/repos/effect/packages/effect/src/TxSubscriptionRef.ts +518 -0
- package/repos/effect/packages/effect/src/Types.ts +1121 -0
- package/repos/effect/packages/effect/src/UndefinedOr.ts +239 -0
- package/repos/effect/packages/effect/src/Unify.ts +314 -0
- package/repos/effect/packages/effect/src/Utils.ts +221 -0
- package/repos/effect/packages/effect/src/index.ts +712 -0
- package/repos/effect/packages/effect/src/internal/array.ts +8 -0
- package/repos/effect/packages/effect/src/internal/concurrency.ts +52 -0
- package/repos/effect/packages/effect/src/internal/core.ts +666 -0
- package/repos/effect/packages/effect/src/internal/dateTime.ts +1264 -0
- package/repos/effect/packages/effect/src/internal/doNotation.ts +117 -0
- package/repos/effect/packages/effect/src/internal/effect.ts +6427 -0
- package/repos/effect/packages/effect/src/internal/equal.ts +28 -0
- package/repos/effect/packages/effect/src/internal/errors.ts +9 -0
- package/repos/effect/packages/effect/src/internal/executionPlan.ts +120 -0
- package/repos/effect/packages/effect/src/internal/hashMap.ts +1309 -0
- package/repos/effect/packages/effect/src/internal/hashSet.ts +190 -0
- package/repos/effect/packages/effect/src/internal/layer.ts +88 -0
- package/repos/effect/packages/effect/src/internal/matcher.ts +637 -0
- package/repos/effect/packages/effect/src/internal/metric.ts +2 -0
- package/repos/effect/packages/effect/src/internal/option.ts +98 -0
- package/repos/effect/packages/effect/src/internal/random.ts +20 -0
- package/repos/effect/packages/effect/src/internal/rcRef.ts +189 -0
- package/repos/effect/packages/effect/src/internal/record.ts +18 -0
- package/repos/effect/packages/effect/src/internal/redacted.ts +19 -0
- package/repos/effect/packages/effect/src/internal/references.ts +72 -0
- package/repos/effect/packages/effect/src/internal/request.ts +205 -0
- package/repos/effect/packages/effect/src/internal/result.ts +112 -0
- package/repos/effect/packages/effect/src/internal/schedule.ts +248 -0
- package/repos/effect/packages/effect/src/internal/schema/annotations.ts +37 -0
- package/repos/effect/packages/effect/src/internal/schema/arbitrary.ts +894 -0
- package/repos/effect/packages/effect/src/internal/schema/cause.ts +26 -0
- package/repos/effect/packages/effect/src/internal/schema/equivalence.ts +154 -0
- package/repos/effect/packages/effect/src/internal/schema/representation.ts +799 -0
- package/repos/effect/packages/effect/src/internal/schema/schema.ts +128 -0
- package/repos/effect/packages/effect/src/internal/stream.ts +28 -0
- package/repos/effect/packages/effect/src/internal/tracer.ts +41 -0
- package/repos/effect/packages/effect/src/internal/trie.ts +733 -0
- package/repos/effect/packages/effect/src/internal/version.ts +2 -0
- package/repos/effect/packages/effect/src/testing/FastCheck.ts +89 -0
- package/repos/effect/packages/effect/src/testing/TestClock.ts +547 -0
- package/repos/effect/packages/effect/src/testing/TestConsole.ts +369 -0
- package/repos/effect/packages/effect/src/testing/TestSchema.ts +567 -0
- package/repos/effect/packages/effect/src/testing/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/ai/AiError.ts +1629 -0
- package/repos/effect/packages/effect/src/unstable/ai/AnthropicStructuredOutput.ts +407 -0
- package/repos/effect/packages/effect/src/unstable/ai/Chat.ts +931 -0
- package/repos/effect/packages/effect/src/unstable/ai/EmbeddingModel.ts +269 -0
- package/repos/effect/packages/effect/src/unstable/ai/IdGenerator.ts +300 -0
- package/repos/effect/packages/effect/src/unstable/ai/LanguageModel.ts +2221 -0
- package/repos/effect/packages/effect/src/unstable/ai/McpSchema.ts +2527 -0
- package/repos/effect/packages/effect/src/unstable/ai/McpServer.ts +1468 -0
- package/repos/effect/packages/effect/src/unstable/ai/Model.ts +166 -0
- package/repos/effect/packages/effect/src/unstable/ai/OpenAiStructuredOutput.ts +476 -0
- package/repos/effect/packages/effect/src/unstable/ai/Prompt.ts +2234 -0
- package/repos/effect/packages/effect/src/unstable/ai/Response.ts +2582 -0
- package/repos/effect/packages/effect/src/unstable/ai/ResponseIdTracker.ts +139 -0
- package/repos/effect/packages/effect/src/unstable/ai/Telemetry.ts +515 -0
- package/repos/effect/packages/effect/src/unstable/ai/Tokenizer.ts +176 -0
- package/repos/effect/packages/effect/src/unstable/ai/Tool.ts +2003 -0
- package/repos/effect/packages/effect/src/unstable/ai/Toolkit.ts +554 -0
- package/repos/effect/packages/effect/src/unstable/ai/index.ts +90 -0
- package/repos/effect/packages/effect/src/unstable/ai/internal/codec-transformer.ts +13 -0
- package/repos/effect/packages/effect/src/unstable/cli/Argument.ts +749 -0
- package/repos/effect/packages/effect/src/unstable/cli/CliError.ts +549 -0
- package/repos/effect/packages/effect/src/unstable/cli/CliOutput.ts +607 -0
- package/repos/effect/packages/effect/src/unstable/cli/Command.ts +1610 -0
- package/repos/effect/packages/effect/src/unstable/cli/Completions.ts +123 -0
- package/repos/effect/packages/effect/src/unstable/cli/Flag.ts +986 -0
- package/repos/effect/packages/effect/src/unstable/cli/GlobalFlag.ts +295 -0
- package/repos/effect/packages/effect/src/unstable/cli/HelpDoc.ts +323 -0
- package/repos/effect/packages/effect/src/unstable/cli/Param.ts +2193 -0
- package/repos/effect/packages/effect/src/unstable/cli/Primitive.ts +766 -0
- package/repos/effect/packages/effect/src/unstable/cli/Prompt.ts +3852 -0
- package/repos/effect/packages/effect/src/unstable/cli/SEMANTICS.md +157 -0
- package/repos/effect/packages/effect/src/unstable/cli/index.ts +60 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/ansi.ts +152 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/auto-suggest.ts +41 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/command.ts +340 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/bash.ts +222 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/descriptor.ts +133 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/fish.ts +253 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/zsh.ts +220 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/config.ts +272 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/help.ts +181 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/lexer.ts +70 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/parser.ts +932 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterCron.ts +163 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterError.ts +238 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterMetrics.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterSchema.ts +170 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterWorkflowEngine.ts +778 -0
- package/repos/effect/packages/effect/src/unstable/cluster/DeliverAt.ts +58 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Entity.ts +779 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityAddress.ts +93 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityId.ts +50 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityProxy.ts +263 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityProxyServer.ts +149 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityResource.ts +184 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityType.ts +52 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Envelope.ts +433 -0
- package/repos/effect/packages/effect/src/unstable/cluster/HttpRunner.ts +345 -0
- package/repos/effect/packages/effect/src/unstable/cluster/K8sHttpClient.ts +297 -0
- package/repos/effect/packages/effect/src/unstable/cluster/MachineId.ts +54 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Message.ts +314 -0
- package/repos/effect/packages/effect/src/unstable/cluster/MessageStorage.ts +1072 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Reply.ts +455 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Runner.ts +129 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerAddress.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerHealth.ts +145 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerServer.ts +220 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerStorage.ts +243 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Runners.ts +690 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardId.ts +178 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Sharding.ts +1488 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardingConfig.ts +367 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardingRegistrationEvent.ts +76 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SingleRunner.ts +75 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Singleton.ts +56 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SingletonAddress.ts +49 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Snowflake.ts +282 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SocketRunner.ts +96 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SqlMessageStorage.ts +1045 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SqlRunnerStorage.ts +700 -0
- package/repos/effect/packages/effect/src/unstable/cluster/TestRunner.ts +36 -0
- package/repos/effect/packages/effect/src/unstable/cluster/index.ts +195 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/entityManager.ts +678 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/entityReaper.ts +58 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/hash.ts +23 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/interruptors.ts +2 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/resourceMap.ts +100 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/resourceRef.ts +99 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevTools.ts +68 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsClient.ts +236 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsSchema.ts +558 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsServer.ts +94 -0
- package/repos/effect/packages/effect/src/unstable/devtools/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Msgpack.ts +383 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Ndjson.ts +602 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Sse.ts +593 -0
- package/repos/effect/packages/effect/src/unstable/encoding/index.ts +20 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/Event.ts +447 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventGroup.ts +193 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventJournal.ts +795 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLog.ts +1030 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogEncryption.ts +169 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogMessage.ts +354 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogRemote.ts +377 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServer.ts +242 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerEncrypted.ts +268 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerUnencrypted.ts +898 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogSessionAuth.ts +524 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventJournal.ts +353 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventLogServerEncrypted.ts +343 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +527 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/index.ts +75 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/internal/identityRootSecretDerivation.ts +153 -0
- package/repos/effect/packages/effect/src/unstable/http/Cookies.ts +952 -0
- package/repos/effect/packages/effect/src/unstable/http/Etag.ts +141 -0
- package/repos/effect/packages/effect/src/unstable/http/FetchHttpClient.ts +123 -0
- package/repos/effect/packages/effect/src/unstable/http/FindMyWay.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/http/Headers.ts +438 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpBody.ts +545 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClient.ts +1630 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientError.ts +328 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientRequest.ts +968 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientResponse.ts +408 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpEffect.ts +400 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpIncomingMessage.ts +153 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpMethod.ts +124 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpMiddleware.ts +394 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpPlatform.ts +165 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpRouter.ts +1341 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServer.ts +329 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerError.ts +374 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerRequest.ts +1081 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerRespondable.ts +115 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerResponse.ts +1381 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpStaticServer.ts +464 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpTraceContext.ts +160 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipart.ts +830 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/HeadersParser.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Node.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Search.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Web.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/http/Template.ts +262 -0
- package/repos/effect/packages/effect/src/unstable/http/Url.ts +330 -0
- package/repos/effect/packages/effect/src/unstable/http/UrlParams.ts +678 -0
- package/repos/effect/packages/effect/src/unstable/http/index.ts +145 -0
- package/repos/effect/packages/effect/src/unstable/http/internal/preResponseHandler.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApi.ts +316 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts +1015 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiClient.ts +1005 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiEndpoint.ts +1545 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiError.ts +443 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiGroup.ts +381 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts +478 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiScalar.ts +267 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSchema.ts +733 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSecurity.ts +244 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSwagger.ts +68 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiTest.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/OpenApi.ts +1098 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/index.ts +70 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/html.ts +18 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiScalar.ts +5 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiSwagger.ts +9 -0
- package/repos/effect/packages/effect/src/unstable/observability/Otlp.ts +172 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpExporter.ts +149 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpLogger.ts +340 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpMetrics.ts +826 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpResource.ts +288 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpSerialization.ts +66 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpTracer.ts +442 -0
- package/repos/effect/packages/effect/src/unstable/observability/PrometheusMetrics.ts +468 -0
- package/repos/effect/packages/effect/src/unstable/observability/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/otlpEnv.ts +39 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/otlpProtobuf.ts +779 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/protobuf.ts +262 -0
- package/repos/effect/packages/effect/src/unstable/persistence/KeyValueStore.ts +864 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Persistable.ts +252 -0
- package/repos/effect/packages/effect/src/unstable/persistence/PersistedCache.ts +111 -0
- package/repos/effect/packages/effect/src/unstable/persistence/PersistedQueue.ts +1196 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Persistence.ts +1071 -0
- package/repos/effect/packages/effect/src/unstable/persistence/RateLimiter.ts +748 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Redis.ts +188 -0
- package/repos/effect/packages/effect/src/unstable/persistence/index.ts +40 -0
- package/repos/effect/packages/effect/src/unstable/process/ChildProcess.ts +1036 -0
- package/repos/effect/packages/effect/src/unstable/process/ChildProcessSpawner.ts +285 -0
- package/repos/effect/packages/effect/src/unstable/process/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AsyncResult.ts +1081 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Atom.ts +2520 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomHttpApi.ts +352 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRef.ts +352 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRegistry.ts +1127 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRpc.ts +305 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Hydration.ts +155 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Reactivity.ts +348 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/rpc/Rpc.ts +1255 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcClient.ts +1370 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcClientError.ts +57 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcGroup.ts +409 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcMessage.ts +402 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcMiddleware.ts +344 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcSchema.ts +97 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcSerialization.ts +546 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcServer.ts +1492 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcTest.ts +56 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcWorker.ts +118 -0
- package/repos/effect/packages/effect/src/unstable/rpc/Utils.ts +120 -0
- package/repos/effect/packages/effect/src/unstable/rpc/index.ts +65 -0
- package/repos/effect/packages/effect/src/unstable/schema/Model.ts +862 -0
- package/repos/effect/packages/effect/src/unstable/schema/VariantSchema.ts +604 -0
- package/repos/effect/packages/effect/src/unstable/schema/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/socket/Socket.ts +911 -0
- package/repos/effect/packages/effect/src/unstable/socket/SocketServer.ts +147 -0
- package/repos/effect/packages/effect/src/unstable/socket/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/sql/Migrator.ts +440 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlClient.ts +342 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlConnection.ts +83 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlError.ts +590 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlModel.ts +359 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlResolver.ts +380 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlSchema.ts +172 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlStream.ts +81 -0
- package/repos/effect/packages/effect/src/unstable/sql/Statement.ts +1500 -0
- package/repos/effect/packages/effect/src/unstable/sql/index.ts +50 -0
- package/repos/effect/packages/effect/src/unstable/workers/Transferable.ts +191 -0
- package/repos/effect/packages/effect/src/unstable/workers/Worker.ts +245 -0
- package/repos/effect/packages/effect/src/unstable/workers/WorkerError.ts +148 -0
- package/repos/effect/packages/effect/src/unstable/workers/WorkerRunner.ts +57 -0
- package/repos/effect/packages/effect/src/unstable/workers/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/workflow/Activity.ts +322 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableClock.ts +117 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableDeferred.ts +653 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableQueue.ts +360 -0
- package/repos/effect/packages/effect/src/unstable/workflow/Workflow.ts +898 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowEngine.ts +759 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowProxy.ts +212 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowProxyServer.ts +151 -0
- package/repos/effect/packages/effect/src/unstable/workflow/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/workflow/internal/crypto.ts +15 -0
- package/repos/effect/packages/effect/test/Array.test.ts +1579 -0
- package/repos/effect/packages/effect/test/AtomRef.test.ts +27 -0
- package/repos/effect/packages/effect/test/BigDecimal.test.ts +525 -0
- package/repos/effect/packages/effect/test/BigInt.test.ts +57 -0
- package/repos/effect/packages/effect/test/Boolean.test.ts +289 -0
- package/repos/effect/packages/effect/test/Brand.test.ts +160 -0
- package/repos/effect/packages/effect/test/Cache.test.ts +1423 -0
- package/repos/effect/packages/effect/test/Cause.test.ts +834 -0
- package/repos/effect/packages/effect/test/Channel.test.ts +556 -0
- package/repos/effect/packages/effect/test/Chunk.test.ts +874 -0
- package/repos/effect/packages/effect/test/Combiner.test.ts +42 -0
- package/repos/effect/packages/effect/test/Config.test.ts +1416 -0
- package/repos/effect/packages/effect/test/ConfigProvider.test.ts +726 -0
- package/repos/effect/packages/effect/test/Cron.test.ts +401 -0
- package/repos/effect/packages/effect/test/Crypto.test.ts +86 -0
- package/repos/effect/packages/effect/test/Data.test.ts +269 -0
- package/repos/effect/packages/effect/test/DateTime.test.ts +953 -0
- package/repos/effect/packages/effect/test/Deferred.test.ts +165 -0
- package/repos/effect/packages/effect/test/Duration.test.ts +679 -0
- package/repos/effect/packages/effect/test/Effect.test.ts +2658 -0
- package/repos/effect/packages/effect/test/EffectEager.test.ts +678 -0
- package/repos/effect/packages/effect/test/EffectKeepAlive.test.ts +71 -0
- package/repos/effect/packages/effect/test/Equal.test.ts +1278 -0
- package/repos/effect/packages/effect/test/Equivalence.test.ts +170 -0
- package/repos/effect/packages/effect/test/ExecutionPlan.test.ts +117 -0
- package/repos/effect/packages/effect/test/Exit.test.ts +12 -0
- package/repos/effect/packages/effect/test/Fiber.test.ts +9 -0
- package/repos/effect/packages/effect/test/FiberHandle.test.ts +146 -0
- package/repos/effect/packages/effect/test/FiberMap.test.ts +171 -0
- package/repos/effect/packages/effect/test/FiberSet.test.ts +119 -0
- package/repos/effect/packages/effect/test/Formatter.test.ts +247 -0
- package/repos/effect/packages/effect/test/Function.test.ts +333 -0
- package/repos/effect/packages/effect/test/Graph.test.ts +3505 -0
- package/repos/effect/packages/effect/test/HashMap.test.ts +618 -0
- package/repos/effect/packages/effect/test/HashSet.test.ts +298 -0
- package/repos/effect/packages/effect/test/HttpClient.test.ts +131 -0
- package/repos/effect/packages/effect/test/Iterable.test.ts +486 -0
- package/repos/effect/packages/effect/test/JsonPatch.test.ts +948 -0
- package/repos/effect/packages/effect/test/JsonPointer.test.ts +353 -0
- package/repos/effect/packages/effect/test/JsonSchema.test.ts +1253 -0
- package/repos/effect/packages/effect/test/Latch.test.ts +21 -0
- package/repos/effect/packages/effect/test/Layer.test.ts +648 -0
- package/repos/effect/packages/effect/test/LayerMap.test.ts +98 -0
- package/repos/effect/packages/effect/test/LogLevel.test.ts +28 -0
- package/repos/effect/packages/effect/test/Logger.test.ts +154 -0
- package/repos/effect/packages/effect/test/ManagedRuntime.test.ts +58 -0
- package/repos/effect/packages/effect/test/Match.test.ts +27 -0
- package/repos/effect/packages/effect/test/Metric.test.ts +682 -0
- package/repos/effect/packages/effect/test/Migrator.test.ts +69 -0
- package/repos/effect/packages/effect/test/MutableHashMap.test.ts +309 -0
- package/repos/effect/packages/effect/test/MutableHashSet.test.ts +10 -0
- package/repos/effect/packages/effect/test/MutableList.test.ts +58 -0
- package/repos/effect/packages/effect/test/Newtype.test.ts +90 -0
- package/repos/effect/packages/effect/test/Number.test.ts +422 -0
- package/repos/effect/packages/effect/test/Optic.test.ts +452 -0
- package/repos/effect/packages/effect/test/Option.test.ts +564 -0
- package/repos/effect/packages/effect/test/Order.test.ts +163 -0
- package/repos/effect/packages/effect/test/Ordering.test.ts +18 -0
- package/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts +74 -0
- package/repos/effect/packages/effect/test/Pathfinding.test.ts +426 -0
- package/repos/effect/packages/effect/test/Pool.test.ts +422 -0
- package/repos/effect/packages/effect/test/Predicate.test.ts +555 -0
- package/repos/effect/packages/effect/test/PubSub.test.ts +538 -0
- package/repos/effect/packages/effect/test/Queue.test.ts +317 -0
- package/repos/effect/packages/effect/test/Random.test.ts +238 -0
- package/repos/effect/packages/effect/test/RcMap.test.ts +237 -0
- package/repos/effect/packages/effect/test/RcRef.test.ts +97 -0
- package/repos/effect/packages/effect/test/Record.test.ts +395 -0
- package/repos/effect/packages/effect/test/Redacted.test.ts +59 -0
- package/repos/effect/packages/effect/test/Reducer.test.ts +12 -0
- package/repos/effect/packages/effect/test/Ref.test.ts +187 -0
- package/repos/effect/packages/effect/test/Request.test.ts +305 -0
- package/repos/effect/packages/effect/test/Resource.test.ts +59 -0
- package/repos/effect/packages/effect/test/Result.test.ts +517 -0
- package/repos/effect/packages/effect/test/Schedule.test.ts +395 -0
- package/repos/effect/packages/effect/test/Scheduler.test.ts +70 -0
- package/repos/effect/packages/effect/test/Scope.test.ts +19 -0
- package/repos/effect/packages/effect/test/ScopedCache.test.ts +2316 -0
- package/repos/effect/packages/effect/test/ScopedRef.test.ts +80 -0
- package/repos/effect/packages/effect/test/Semaphore.test.ts +469 -0
- package/repos/effect/packages/effect/test/Sink.test.ts +230 -0
- package/repos/effect/packages/effect/test/Stream.test.ts +4712 -0
- package/repos/effect/packages/effect/test/String.test.ts +690 -0
- package/repos/effect/packages/effect/test/Struct.test.ts +216 -0
- package/repos/effect/packages/effect/test/SubscriptionRef.test.ts +90 -0
- package/repos/effect/packages/effect/test/Symbol.test.ts +50 -0
- package/repos/effect/packages/effect/test/SynchronizedRef.test.ts +105 -0
- package/repos/effect/packages/effect/test/TestClock.test.ts +71 -0
- package/repos/effect/packages/effect/test/Tracer.test.ts +410 -0
- package/repos/effect/packages/effect/test/Trie.test.ts +531 -0
- package/repos/effect/packages/effect/test/Tuple.test.ts +127 -0
- package/repos/effect/packages/effect/test/TxChunk.test.ts +342 -0
- package/repos/effect/packages/effect/test/TxDeferred.test.ts +197 -0
- package/repos/effect/packages/effect/test/TxHashMap.test.ts +963 -0
- package/repos/effect/packages/effect/test/TxHashSet.test.ts +442 -0
- package/repos/effect/packages/effect/test/TxPriorityQueue.test.ts +187 -0
- package/repos/effect/packages/effect/test/TxPubSub.test.ts +391 -0
- package/repos/effect/packages/effect/test/TxQueue.test.ts +1260 -0
- package/repos/effect/packages/effect/test/TxReentrantLock.test.ts +413 -0
- package/repos/effect/packages/effect/test/TxSemaphore.test.ts +283 -0
- package/repos/effect/packages/effect/test/TxSubscriptionRef.test.ts +197 -0
- package/repos/effect/packages/effect/test/UndefinedOr.test.ts +65 -0
- package/repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts +652 -0
- package/repos/effect/packages/effect/test/cluster/Entity.test.ts +81 -0
- package/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts +34 -0
- package/repos/effect/packages/effect/test/cluster/MessageStorage.test.ts +196 -0
- package/repos/effect/packages/effect/test/cluster/Sharding.test.ts +578 -0
- package/repos/effect/packages/effect/test/cluster/TestEntity.ts +132 -0
- package/repos/effect/packages/effect/test/reactivity/AsyncResult.test.ts +41 -0
- package/repos/effect/packages/effect/test/reactivity/Atom.test.ts +2551 -0
- package/repos/effect/packages/effect/test/reactivity/AtomHttpApi.test.ts +86 -0
- package/repos/effect/packages/effect/test/reactivity/AtomRpc.test.ts +70 -0
- package/repos/effect/packages/effect/test/rpc/Rpc.test.ts +73 -0
- package/repos/effect/packages/effect/test/rpc/RpcClient.test.ts +58 -0
- package/repos/effect/packages/effect/test/rpc/RpcSerialization.test.ts +171 -0
- package/repos/effect/packages/effect/test/schema/HMR.test.ts +72 -0
- package/repos/effect/packages/effect/test/schema/Schema.test.ts +9133 -0
- package/repos/effect/packages/effect/test/schema/SchemaAST.test.ts +280 -0
- package/repos/effect/packages/effect/test/schema/SchemaGetter.test.ts +383 -0
- package/repos/effect/packages/effect/test/schema/SchemaIssue.test.ts +9 -0
- package/repos/effect/packages/effect/test/schema/SchemaParser.test.ts +323 -0
- package/repos/effect/packages/effect/test/schema/fixtures/HMR-Class.ts +5 -0
- package/repos/effect/packages/effect/test/schema/fixtures/HMR-sanity-check.ts +3 -0
- package/repos/effect/packages/effect/test/schema/representation/fromASTs.test.ts +820 -0
- package/repos/effect/packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts +2137 -0
- package/repos/effect/packages/effect/test/schema/representation/toCodeDocument.test.ts +2089 -0
- package/repos/effect/packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts +39 -0
- package/repos/effect/packages/effect/test/schema/representation/toSchema.test.ts +506 -0
- package/repos/effect/packages/effect/test/schema/toArbitrary.test.ts +1693 -0
- package/repos/effect/packages/effect/test/schema/toCodec.test.ts +3149 -0
- package/repos/effect/packages/effect/test/schema/toDifferJsonPatch.test.ts +239 -0
- package/repos/effect/packages/effect/test/schema/toEquivalence.test.ts +519 -0
- package/repos/effect/packages/effect/test/schema/toFormatter.test.ts +565 -0
- package/repos/effect/packages/effect/test/schema/toIso.test.ts +394 -0
- package/repos/effect/packages/effect/test/schema/toJsonSchemaDocument.test.ts +3630 -0
- package/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts +126 -0
- package/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts +495 -0
- package/repos/effect/packages/effect/test/schema/v3-v4.test.ts +250 -0
- package/repos/effect/packages/effect/test/testing/TestSchema.test.ts +78 -0
- package/repos/effect/packages/effect/test/unstable/ai/AiError.test.ts +823 -0
- package/repos/effect/packages/effect/test/unstable/ai/AnthropicStructuredOutput.test.ts +593 -0
- package/repos/effect/packages/effect/test/unstable/ai/Chat.test.ts +147 -0
- package/repos/effect/packages/effect/test/unstable/ai/EmbeddingModel.test.ts +185 -0
- package/repos/effect/packages/effect/test/unstable/ai/LanguageModel.test.ts +1714 -0
- package/repos/effect/packages/effect/test/unstable/ai/LanguageModelTrackerLifecycle.test.ts +413 -0
- package/repos/effect/packages/effect/test/unstable/ai/McpServer.test.ts +82 -0
- package/repos/effect/packages/effect/test/unstable/ai/Model.test.ts +16 -0
- package/repos/effect/packages/effect/test/unstable/ai/OpenAiStructuredOutput.test.ts +723 -0
- package/repos/effect/packages/effect/test/unstable/ai/Prompt.test.ts +346 -0
- package/repos/effect/packages/effect/test/unstable/ai/ResponseIdTracker.test.ts +424 -0
- package/repos/effect/packages/effect/test/unstable/ai/Tool.test.ts +1264 -0
- package/repos/effect/packages/effect/test/unstable/ai/utils.ts +68 -0
- package/repos/effect/packages/effect/test/unstable/cli/Arguments.test.ts +390 -0
- package/repos/effect/packages/effect/test/unstable/cli/Command.test.ts +1801 -0
- package/repos/effect/packages/effect/test/unstable/cli/Errors.test.ts +150 -0
- package/repos/effect/packages/effect/test/unstable/cli/Help.test.ts +572 -0
- package/repos/effect/packages/effect/test/unstable/cli/LogLevel.test.ts +202 -0
- package/repos/effect/packages/effect/test/unstable/cli/Param.test.ts +391 -0
- package/repos/effect/packages/effect/test/unstable/cli/Primitive.test.ts +321 -0
- package/repos/effect/packages/effect/test/unstable/cli/Prompt.test.ts +597 -0
- package/repos/effect/packages/effect/test/unstable/cli/completions/completions.test.ts +656 -0
- package/repos/effect/packages/effect/test/unstable/cli/completions/descriptor.test.ts +245 -0
- package/repos/effect/packages/effect/test/unstable/cli/fixtures/ComprehensiveCli.ts +422 -0
- package/repos/effect/packages/effect/test/unstable/cli/services/MockTerminal.ts +135 -0
- package/repos/effect/packages/effect/test/unstable/cli/services/TestActions.ts +36 -0
- package/repos/effect/packages/effect/test/unstable/encoding/Ndjson.test.ts +18 -0
- package/repos/effect/packages/effect/test/unstable/encoding/Sse.test.ts +62 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventJournal.test.ts +25 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLog.test.ts +104 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogIdentityDerivation.test.ts +85 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogRemote.test.ts +176 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogSessionAuth.test.ts +130 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/SqlEventLogServerUnencryptedStorageTest.ts +169 -0
- package/repos/effect/packages/effect/test/unstable/http/Cookies.test.ts +80 -0
- package/repos/effect/packages/effect/test/unstable/http/Headers.test.ts +73 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpClient.test.ts +306 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpClientRequest.test.ts +164 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpEffect.test.ts +254 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpMiddleware.test.ts +57 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerError.test.ts +121 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerRequest.test.ts +157 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerResponse.test.ts +76 -0
- package/repos/effect/packages/effect/test/unstable/http/Multipart.test.ts +69 -0
- package/repos/effect/packages/effect/test/unstable/http/Url.test.ts +98 -0
- package/repos/effect/packages/effect/test/unstable/http/UrlParams.test.ts +30 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiBuilder.test.ts +186 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiClient.test.ts +446 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiEndpoint.test.ts +160 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiSchema.test.ts +108 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiSecurity.test.ts +41 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/OpenApi.test.ts +39 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpConfig.test.ts +151 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpExporter.test.ts +108 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpMetrics.test.ts +518 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpResource.test.ts +84 -0
- package/repos/effect/packages/effect/test/unstable/observability/PrometheusMetrics.test.ts +417 -0
- package/repos/effect/packages/effect/test/unstable/persistence/KeyValueStore.test.ts +164 -0
- package/repos/effect/packages/effect/test/unstable/persistence/KeyValueStoreTest.ts +85 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedCache.test.ts +4 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedCacheTest.ts +111 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedQueue.test.ts +4 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedQueueTest.ts +106 -0
- package/repos/effect/packages/effect/test/unstable/persistence/RateLimiter.test.ts +145 -0
- package/repos/effect/packages/effect/test/unstable/persistence/Redis.test.ts +43 -0
- package/repos/effect/packages/effect/test/unstable/process/ChildProcess.test.ts +352 -0
- package/repos/effect/packages/effect/test/unstable/schema/VariantSchema.test.ts +72 -0
- package/repos/effect/packages/effect/test/unstable/sql/SqlError.test.ts +228 -0
- package/repos/effect/packages/effect/test/unstable/sql/SqlSchema.test.ts +168 -0
- package/repos/effect/packages/effect/test/unstable/workers/WorkerError.test.ts +90 -0
- package/repos/effect/packages/effect/test/unstable/workflow/DurableQueue.test.ts +105 -0
- package/repos/effect/packages/effect/test/unstable/workflow/WorkflowEngine.test.ts +60 -0
- package/repos/effect/packages/effect/test/utils/assert.ts +182 -0
- package/repos/effect/packages/effect/test/utils/chunkCoordination.ts +36 -0
- package/repos/effect/packages/effect/test/utils/counter.ts +54 -0
- package/repos/effect/packages/effect/tsconfig.json +8 -0
- package/repos/effect/packages/effect/typetest/AiError.tst.ts +49 -0
- package/repos/effect/packages/effect/typetest/Array.tst.ts +1289 -0
- package/repos/effect/packages/effect/typetest/Brand.tst.ts +51 -0
- package/repos/effect/packages/effect/typetest/Channel.tst.ts +112 -0
- package/repos/effect/packages/effect/typetest/Config.tst.ts +31 -0
- package/repos/effect/packages/effect/typetest/Data.tst.ts +121 -0
- package/repos/effect/packages/effect/typetest/Effect.tst.ts +976 -0
- package/repos/effect/packages/effect/typetest/Equivalence.tst.ts +19 -0
- package/repos/effect/packages/effect/typetest/Newtype.tst.ts +72 -0
- package/repos/effect/packages/effect/typetest/Optic.tst.ts +130 -0
- package/repos/effect/packages/effect/typetest/Option.tst.ts +72 -0
- package/repos/effect/packages/effect/typetest/Order.tst.ts +19 -0
- package/repos/effect/packages/effect/typetest/Predicate.tst.ts +251 -0
- package/repos/effect/packages/effect/typetest/Result.tst.ts +353 -0
- package/repos/effect/packages/effect/typetest/Schedule.tst.ts +21 -0
- package/repos/effect/packages/effect/typetest/Stream.tst.ts +197 -0
- package/repos/effect/packages/effect/typetest/Struct.tst.ts +422 -0
- package/repos/effect/packages/effect/typetest/Tuple.tst.ts +177 -0
- package/repos/effect/packages/effect/typetest/Types.tst.ts +364 -0
- package/repos/effect/packages/effect/typetest/VariantSchema.tst.ts +76 -0
- package/repos/effect/packages/effect/typetest/schema/Array.tst.ts +46 -0
- package/repos/effect/packages/effect/typetest/schema/Literals.tst.ts +58 -0
- package/repos/effect/packages/effect/typetest/schema/Record.tst.ts +145 -0
- package/repos/effect/packages/effect/typetest/schema/Schema.tst.ts +1854 -0
- package/repos/effect/packages/effect/typetest/schema/Struct.tst.ts +745 -0
- package/repos/effect/packages/effect/typetest/schema/Tuple.tst.ts +199 -0
- package/repos/effect/packages/effect/typetest/schema/Union.tst.ts +198 -0
- package/repos/effect/packages/effect/typetest/schema/api.tst.ts +276 -0
- package/repos/effect/packages/effect/typetest/schema/toArbitrary.tst.ts +65 -0
- package/repos/effect/packages/effect/typetest/schema/toIso.tst.ts +282 -0
- package/repos/effect/packages/effect/typetest/unstable/ai/LanguageModel.tst.ts +191 -0
- package/repos/effect/packages/effect/typetest/unstable/ai/Tool.tst.ts +33 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Command.tst.ts +124 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Param.tst.ts +20 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Prompt.tst.ts +97 -0
- package/repos/effect/packages/effect/typetest/unstable/encoding/Ndjson.tst.ts +24 -0
- package/repos/effect/packages/effect/typetest/unstable/http/HttpClient.tst.ts +143 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiBuilder.tst.ts +98 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiClient.tst.ts +677 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiEndpoint.tst.ts +466 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiMiddleware.tst.ts +90 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiSchema.tst.ts +135 -0
- package/repos/effect/packages/effect/typetest/unstable/reactivity/AsyncResult.tst.ts +65 -0
- package/repos/effect/packages/effect/typetest/unstable/reactivity/AtomHttpApi.tst.ts +63 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/Rpc.tst.ts +74 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcGroup.tst.ts +61 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcMiddleware.tst.ts +41 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcServer.tst.ts +26 -0
- package/repos/effect/packages/effect/typetest/unstable/sql/SqlSchema.tst.ts +75 -0
- package/repos/effect/packages/effect/typetest/unstable/workflow/Workflow.tst.ts +16 -0
- package/repos/effect/packages/effect/vitest.config.ts +11 -0
- package/repos/effect/packages/opentelemetry/CHANGELOG.md +620 -0
- package/repos/effect/packages/opentelemetry/LICENSE +21 -0
- package/repos/effect/packages/opentelemetry/README.md +5 -0
- package/repos/effect/packages/opentelemetry/docgen.json +30 -0
- package/repos/effect/packages/opentelemetry/package.json +126 -0
- package/repos/effect/packages/opentelemetry/src/Logger.ts +193 -0
- package/repos/effect/packages/opentelemetry/src/Metrics.ts +138 -0
- package/repos/effect/packages/opentelemetry/src/NodeSdk.ts +162 -0
- package/repos/effect/packages/opentelemetry/src/Resource.ts +147 -0
- package/repos/effect/packages/opentelemetry/src/Tracer.ts +594 -0
- package/repos/effect/packages/opentelemetry/src/WebSdk.ts +140 -0
- package/repos/effect/packages/opentelemetry/src/index.ts +35 -0
- package/repos/effect/packages/opentelemetry/src/internal/attributes.ts +28 -0
- package/repos/effect/packages/opentelemetry/src/internal/metrics.ts +463 -0
- package/repos/effect/packages/opentelemetry/src/internal/utilities.ts +5 -0
- package/repos/effect/packages/opentelemetry/test/Logger.test.ts +122 -0
- package/repos/effect/packages/opentelemetry/test/Metrics.test.ts +339 -0
- package/repos/effect/packages/opentelemetry/test/Tracer.test.ts +167 -0
- package/repos/effect/packages/opentelemetry/tsconfig.json +8 -0
- package/repos/effect/packages/opentelemetry/vitest.config.ts +6 -0
- package/repos/effect/packages/platform-browser/CHANGELOG.md +637 -0
- package/repos/effect/packages/platform-browser/LICENSE +21 -0
- package/repos/effect/packages/platform-browser/README.md +1 -0
- package/repos/effect/packages/platform-browser/docgen.json +21 -0
- package/repos/effect/packages/platform-browser/package.json +74 -0
- package/repos/effect/packages/platform-browser/src/BrowserCrypto.ts +97 -0
- package/repos/effect/packages/platform-browser/src/BrowserHttpClient.ts +430 -0
- package/repos/effect/packages/platform-browser/src/BrowserKeyValueStore.ts +177 -0
- package/repos/effect/packages/platform-browser/src/BrowserPersistence.ts +334 -0
- package/repos/effect/packages/platform-browser/src/BrowserRuntime.ts +53 -0
- package/repos/effect/packages/platform-browser/src/BrowserSocket.ts +52 -0
- package/repos/effect/packages/platform-browser/src/BrowserStream.ts +56 -0
- package/repos/effect/packages/platform-browser/src/BrowserWorker.ts +98 -0
- package/repos/effect/packages/platform-browser/src/BrowserWorkerRunner.ts +199 -0
- package/repos/effect/packages/platform-browser/src/Clipboard.ts +143 -0
- package/repos/effect/packages/platform-browser/src/Geolocation.ts +233 -0
- package/repos/effect/packages/platform-browser/src/IndexedDb.ts +113 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbDatabase.ts +648 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbQueryBuilder.ts +2032 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbTable.ts +260 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbVersion.ts +138 -0
- package/repos/effect/packages/platform-browser/src/Permissions.ts +146 -0
- package/repos/effect/packages/platform-browser/src/index.ts +90 -0
- package/repos/effect/packages/platform-browser/test/BrowserCrypto.test.ts +74 -0
- package/repos/effect/packages/platform-browser/test/BrowserHttpClient.test.ts +91 -0
- package/repos/effect/packages/platform-browser/test/BrowserKeyValueStore.test.ts +23 -0
- package/repos/effect/packages/platform-browser/test/BrowserPersistence.test.ts +209 -0
- package/repos/effect/packages/platform-browser/test/BrowserPersistencePersistedCache.test.ts +29 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbDatabase.test.ts +253 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbQueryBuilder.test.ts +1459 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbTable.test.ts +101 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbVersion.test.ts +50 -0
- package/repos/effect/packages/platform-browser/test/Permissions.test.ts +12 -0
- package/repos/effect/packages/platform-browser/test/RpcWorker.test.ts +19 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-e2e.ts +141 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-schemas.ts +157 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-worker.ts +12 -0
- package/repos/effect/packages/platform-browser/tsconfig.json +8 -0
- package/repos/effect/packages/platform-browser/vitest.config.ts +17 -0
- package/repos/effect/packages/platform-browser/vitest.setup.ts +5 -0
- package/repos/effect/packages/platform-bun/CHANGELOG.md +691 -0
- package/repos/effect/packages/platform-bun/LICENSE +21 -0
- package/repos/effect/packages/platform-bun/README.md +7 -0
- package/repos/effect/packages/platform-bun/docgen.json +20 -0
- package/repos/effect/packages/platform-bun/package.json +73 -0
- package/repos/effect/packages/platform-bun/src/BunChildProcessSpawner.ts +6 -0
- package/repos/effect/packages/platform-bun/src/BunClusterHttp.ts +174 -0
- package/repos/effect/packages/platform-bun/src/BunClusterSocket.ts +153 -0
- package/repos/effect/packages/platform-bun/src/BunCrypto.ts +24 -0
- package/repos/effect/packages/platform-bun/src/BunFileSystem.ts +20 -0
- package/repos/effect/packages/platform-bun/src/BunHttpClient.ts +9 -0
- package/repos/effect/packages/platform-bun/src/BunHttpPlatform.ts +50 -0
- package/repos/effect/packages/platform-bun/src/BunHttpServer.ts +600 -0
- package/repos/effect/packages/platform-bun/src/BunHttpServerRequest.ts +19 -0
- package/repos/effect/packages/platform-bun/src/BunMultipart.ts +54 -0
- package/repos/effect/packages/platform-bun/src/BunPath.ts +37 -0
- package/repos/effect/packages/platform-bun/src/BunRedis.ts +88 -0
- package/repos/effect/packages/platform-bun/src/BunRuntime.ts +52 -0
- package/repos/effect/packages/platform-bun/src/BunServices.ts +49 -0
- package/repos/effect/packages/platform-bun/src/BunSink.ts +8 -0
- package/repos/effect/packages/platform-bun/src/BunSocket.ts +54 -0
- package/repos/effect/packages/platform-bun/src/BunSocketServer.ts +8 -0
- package/repos/effect/packages/platform-bun/src/BunStdio.ts +22 -0
- package/repos/effect/packages/platform-bun/src/BunStream.ts +68 -0
- package/repos/effect/packages/platform-bun/src/BunTerminal.ts +32 -0
- package/repos/effect/packages/platform-bun/src/BunWorker.ts +92 -0
- package/repos/effect/packages/platform-bun/src/BunWorkerRunner.ts +113 -0
- package/repos/effect/packages/platform-bun/src/index.ts +115 -0
- package/repos/effect/packages/platform-bun/tsconfig.json +12 -0
- package/repos/effect/packages/platform-node/CHANGELOG.md +695 -0
- package/repos/effect/packages/platform-node/LICENSE +21 -0
- package/repos/effect/packages/platform-node/README.md +7 -0
- package/repos/effect/packages/platform-node/docgen.json +24 -0
- package/repos/effect/packages/platform-node/package.json +82 -0
- package/repos/effect/packages/platform-node/src/Mime.ts +25 -0
- package/repos/effect/packages/platform-node/src/NodeChildProcessSpawner.ts +6 -0
- package/repos/effect/packages/platform-node/src/NodeClusterHttp.ts +155 -0
- package/repos/effect/packages/platform-node/src/NodeClusterSocket.ts +176 -0
- package/repos/effect/packages/platform-node/src/NodeCrypto.ts +24 -0
- package/repos/effect/packages/platform-node/src/NodeFileSystem.ts +21 -0
- package/repos/effect/packages/platform-node/src/NodeHttpClient.ts +664 -0
- package/repos/effect/packages/platform-node/src/NodeHttpIncomingMessage.ts +141 -0
- package/repos/effect/packages/platform-node/src/NodeHttpPlatform.ts +70 -0
- package/repos/effect/packages/platform-node/src/NodeHttpServer.ts +646 -0
- package/repos/effect/packages/platform-node/src/NodeHttpServerRequest.ts +33 -0
- package/repos/effect/packages/platform-node/src/NodeMultipart.ts +183 -0
- package/repos/effect/packages/platform-node/src/NodePath.ts +40 -0
- package/repos/effect/packages/platform-node/src/NodeRedis.ts +92 -0
- package/repos/effect/packages/platform-node/src/NodeRuntime.ts +53 -0
- package/repos/effect/packages/platform-node/src/NodeServices.ts +50 -0
- package/repos/effect/packages/platform-node/src/NodeSink.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeSocket.ts +72 -0
- package/repos/effect/packages/platform-node/src/NodeSocketServer.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeStdio.ts +22 -0
- package/repos/effect/packages/platform-node/src/NodeStream.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeTerminal.ts +32 -0
- package/repos/effect/packages/platform-node/src/NodeWorker.ts +121 -0
- package/repos/effect/packages/platform-node/src/NodeWorkerRunner.ts +110 -0
- package/repos/effect/packages/platform-node/src/Undici.ts +29 -0
- package/repos/effect/packages/platform-node/src/index.ts +130 -0
- package/repos/effect/packages/platform-node/test/HttpApi.test.ts +1831 -0
- package/repos/effect/packages/platform-node/test/HttpStaticServer.test.ts +253 -0
- package/repos/effect/packages/platform-node/test/HttpStaticServerConditional.test.ts +296 -0
- package/repos/effect/packages/platform-node/test/NodeCrypto.test.ts +59 -0
- package/repos/effect/packages/platform-node/test/NodeHttpClient.test.ts +157 -0
- package/repos/effect/packages/platform-node/test/NodeHttpPlatform.test.ts +51 -0
- package/repos/effect/packages/platform-node/test/NodeHttpServer.test.ts +598 -0
- package/repos/effect/packages/platform-node/test/NodeRedis.test.ts +31 -0
- package/repos/effect/packages/platform-node/test/NodeSocket.test.ts +153 -0
- package/repos/effect/packages/platform-node/test/OpenApi.test.ts +1573 -0
- package/repos/effect/packages/platform-node/test/RpcServer.test.ts +213 -0
- package/repos/effect/packages/platform-node/test/__snapshots__/HttpApi.test.ts.snap +715 -0
- package/repos/effect/packages/platform-node/test/cluster/MessageStorageTest.ts +196 -0
- package/repos/effect/packages/platform-node/test/cluster/SocketRunner.test.ts +120 -0
- package/repos/effect/packages/platform-node/test/cluster/SqlMessageStorage.test.ts +222 -0
- package/repos/effect/packages/platform-node/test/cluster/SqlRunnerStorage.test.ts +101 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/conditional.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/custom/home.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/file.binx +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/guide/index.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/hello.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/index.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/range.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server-outside.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/mysql2-utils.ts +64 -0
- package/repos/effect/packages/platform-node/test/fixtures/pg-utils.ts +39 -0
- package/repos/effect/packages/platform-node/test/fixtures/rpc-e2e.ts +149 -0
- package/repos/effect/packages/platform-node/test/fixtures/rpc-schemas.ts +175 -0
- package/repos/effect/packages/platform-node/test/fixtures/text.txt +1 -0
- package/repos/effect/packages/platform-node/tsconfig.json +12 -0
- package/repos/effect/packages/platform-node/vitest.config.ts +6 -0
- package/repos/effect/packages/platform-node-shared/CHANGELOG.md +617 -0
- package/repos/effect/packages/platform-node-shared/LICENSE +21 -0
- package/repos/effect/packages/platform-node-shared/README.md +7 -0
- package/repos/effect/packages/platform-node-shared/docgen.json +38 -0
- package/repos/effect/packages/platform-node-shared/package.json +76 -0
- package/repos/effect/packages/platform-node-shared/src/NodeChildProcessSpawner.ts +713 -0
- package/repos/effect/packages/platform-node-shared/src/NodeClusterSocket.ts +70 -0
- package/repos/effect/packages/platform-node-shared/src/NodeCrypto.ts +60 -0
- package/repos/effect/packages/platform-node-shared/src/NodeFileSystem.ts +652 -0
- package/repos/effect/packages/platform-node-shared/src/NodePath.ts +80 -0
- package/repos/effect/packages/platform-node-shared/src/NodeRuntime.ts +59 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSink.ts +116 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSocket.ts +269 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSocketServer.ts +301 -0
- package/repos/effect/packages/platform-node-shared/src/NodeStdio.ts +67 -0
- package/repos/effect/packages/platform-node-shared/src/NodeStream.ts +445 -0
- package/repos/effect/packages/platform-node-shared/src/NodeTerminal.ts +129 -0
- package/repos/effect/packages/platform-node-shared/src/index.ts +65 -0
- package/repos/effect/packages/platform-node-shared/src/internal/utils.ts +51 -0
- package/repos/effect/packages/platform-node-shared/test/NodeChildProcessSpawner.test.ts +1069 -0
- package/repos/effect/packages/platform-node-shared/test/NodeFileSystem.test.ts +214 -0
- package/repos/effect/packages/platform-node-shared/test/NodeSink.test.ts +153 -0
- package/repos/effect/packages/platform-node-shared/test/NodeStream.test.ts +173 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/no-permissions.sh +3 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/parent-exits-early.sh +31 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/spawn-children.sh +32 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/SHOUTING +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/integer +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/nested/config +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/secret +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/helloworld.tar.gz +0 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/text.txt +1 -0
- package/repos/effect/packages/platform-node-shared/tsconfig.json +11 -0
- package/repos/effect/packages/platform-node-shared/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/clickhouse/CHANGELOG.md +681 -0
- package/repos/effect/packages/sql/clickhouse/LICENSE +21 -0
- package/repos/effect/packages/sql/clickhouse/README.md +7 -0
- package/repos/effect/packages/sql/clickhouse/docgen.json +24 -0
- package/repos/effect/packages/sql/clickhouse/package.json +72 -0
- package/repos/effect/packages/sql/clickhouse/src/ClickhouseClient.ts +546 -0
- package/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts +51 -0
- package/repos/effect/packages/sql/clickhouse/src/index.ts +15 -0
- package/repos/effect/packages/sql/clickhouse/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/clickhouse/test/SqlErrorClassification.test.ts +75 -0
- package/repos/effect/packages/sql/clickhouse/tsconfig.json +12 -0
- package/repos/effect/packages/sql/clickhouse/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/d1/CHANGELOG.md +597 -0
- package/repos/effect/packages/sql/d1/LICENSE +21 -0
- package/repos/effect/packages/sql/d1/README.md +7 -0
- package/repos/effect/packages/sql/d1/docgen.json +24 -0
- package/repos/effect/packages/sql/d1/package.json +75 -0
- package/repos/effect/packages/sql/d1/src/D1Client.ts +249 -0
- package/repos/effect/packages/sql/d1/src/index.ts +10 -0
- package/repos/effect/packages/sql/d1/test/Client.test.ts +65 -0
- package/repos/effect/packages/sql/d1/test/Resolver.test.ts +176 -0
- package/repos/effect/packages/sql/d1/test/utils.ts +38 -0
- package/repos/effect/packages/sql/d1/tsconfig.json +8 -0
- package/repos/effect/packages/sql/d1/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/libsql/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/libsql/LICENSE +21 -0
- package/repos/effect/packages/sql/libsql/README.md +7 -0
- package/repos/effect/packages/sql/libsql/docgen.json +24 -0
- package/repos/effect/packages/sql/libsql/package.json +73 -0
- package/repos/effect/packages/sql/libsql/src/LibsqlClient.ts +381 -0
- package/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts +46 -0
- package/repos/effect/packages/sql/libsql/src/index.ts +15 -0
- package/repos/effect/packages/sql/libsql/test/Client.test.ts +106 -0
- package/repos/effect/packages/sql/libsql/test/Resolver.test.ts +177 -0
- package/repos/effect/packages/sql/libsql/test/util.ts +54 -0
- package/repos/effect/packages/sql/libsql/tsconfig.json +9 -0
- package/repos/effect/packages/sql/libsql/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/mssql/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/mssql/LICENSE +21 -0
- package/repos/effect/packages/sql/mssql/README.md +7 -0
- package/repos/effect/packages/sql/mssql/docgen.json +24 -0
- package/repos/effect/packages/sql/mssql/package.json +70 -0
- package/repos/effect/packages/sql/mssql/src/MssqlClient.ts +732 -0
- package/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts +45 -0
- package/repos/effect/packages/sql/mssql/src/Parameter.ts +64 -0
- package/repos/effect/packages/sql/mssql/src/Procedure.ts +213 -0
- package/repos/effect/packages/sql/mssql/src/index.ts +32 -0
- package/repos/effect/packages/sql/mssql/test/Client.test.ts +91 -0
- package/repos/effect/packages/sql/mssql/test/SqlErrorClassification.test.ts +206 -0
- package/repos/effect/packages/sql/mssql/tsconfig.json +8 -0
- package/repos/effect/packages/sql/mssql/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/mysql2/CHANGELOG.md +603 -0
- package/repos/effect/packages/sql/mysql2/LICENSE +21 -0
- package/repos/effect/packages/sql/mysql2/README.md +7 -0
- package/repos/effect/packages/sql/mysql2/docgen.json +24 -0
- package/repos/effect/packages/sql/mysql2/package.json +71 -0
- package/repos/effect/packages/sql/mysql2/src/MysqlClient.ts +517 -0
- package/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts +102 -0
- package/repos/effect/packages/sql/mysql2/src/index.ts +15 -0
- package/repos/effect/packages/sql/mysql2/test/Client.test.ts +34 -0
- package/repos/effect/packages/sql/mysql2/test/KeyValueStore.test.ts +16 -0
- package/repos/effect/packages/sql/mysql2/test/Model.test.ts +227 -0
- package/repos/effect/packages/sql/mysql2/test/MysqlClient.test.ts +35 -0
- package/repos/effect/packages/sql/mysql2/test/Persistence.test.ts +22 -0
- package/repos/effect/packages/sql/mysql2/test/SqlErrorClassification.test.ts +133 -0
- package/repos/effect/packages/sql/mysql2/test/SqlEventLogServerUnencrypted.test.ts +7 -0
- package/repos/effect/packages/sql/mysql2/test/utils.ts +64 -0
- package/repos/effect/packages/sql/mysql2/tsconfig.json +9 -0
- package/repos/effect/packages/sql/mysql2/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/pg/CHANGELOG.md +3432 -0
- package/repos/effect/packages/sql/pg/LICENSE +21 -0
- package/repos/effect/packages/sql/pg/README.md +7 -0
- package/repos/effect/packages/sql/pg/docgen.json +24 -0
- package/repos/effect/packages/sql/pg/package.json +77 -0
- package/repos/effect/packages/sql/pg/src/PgClient.ts +947 -0
- package/repos/effect/packages/sql/pg/src/PgMigrator.ts +120 -0
- package/repos/effect/packages/sql/pg/src/index.ts +15 -0
- package/repos/effect/packages/sql/pg/test/Client.test.ts +373 -0
- package/repos/effect/packages/sql/pg/test/KeyValueStore.test.ts +16 -0
- package/repos/effect/packages/sql/pg/test/Persistence.test.ts +20 -0
- package/repos/effect/packages/sql/pg/test/SqlErrorClassification.test.ts +81 -0
- package/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.test.ts +7 -0
- package/repos/effect/packages/sql/pg/test/TransactionAcquire.test.ts +52 -0
- package/repos/effect/packages/sql/pg/test/utils.ts +49 -0
- package/repos/effect/packages/sql/pg/tsconfig.json +9 -0
- package/repos/effect/packages/sql/pg/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/pglite/CHANGELOG.md +198 -0
- package/repos/effect/packages/sql/pglite/LICENSE +21 -0
- package/repos/effect/packages/sql/pglite/README.md +7 -0
- package/repos/effect/packages/sql/pglite/docgen.json +24 -0
- package/repos/effect/packages/sql/pglite/package.json +74 -0
- package/repos/effect/packages/sql/pglite/src/PgliteClient.ts +503 -0
- package/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts +44 -0
- package/repos/effect/packages/sql/pglite/src/index.ts +15 -0
- package/repos/effect/packages/sql/pglite/test/Client.test.ts +149 -0
- package/repos/effect/packages/sql/pglite/test/Migrator.test.ts +45 -0
- package/repos/effect/packages/sql/pglite/test/SqlErrorClassification.test.ts +56 -0
- package/repos/effect/packages/sql/pglite/test/Transaction.test.ts +62 -0
- package/repos/effect/packages/sql/pglite/tsconfig.json +8 -0
- package/repos/effect/packages/sql/pglite/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-bun/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-bun/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-bun/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-bun/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts +262 -0
- package/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts +90 -0
- package/repos/effect/packages/sql/sqlite-bun/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-bun/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-bun/tsconfig.json +11 -0
- package/repos/effect/packages/sql/sqlite-bun/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-do/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-do/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-do/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-do/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-do/package.json +68 -0
- package/repos/effect/packages/sql/sqlite-do/src/SqliteClient.ts +249 -0
- package/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts +45 -0
- package/repos/effect/packages/sql/sqlite-do/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-do/test/Client.test.ts +24 -0
- package/repos/effect/packages/sql/sqlite-do/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-do/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-node/CHANGELOG.md +3415 -0
- package/repos/effect/packages/sql/sqlite-node/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-node/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-node/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-node/package.json +72 -0
- package/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts +312 -0
- package/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts +90 -0
- package/repos/effect/packages/sql/sqlite-node/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-node/test/Client.test.ts +75 -0
- package/repos/effect/packages/sql/sqlite-node/test/KeyValueStore.test.ts +29 -0
- package/repos/effect/packages/sql/sqlite-node/test/Persistence.test.ts +109 -0
- package/repos/effect/packages/sql/sqlite-node/test/Resolver.test.ts +183 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventJournal.test.ts +84 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventLogServerEncrypted.test.ts +249 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventLogServerUnencrypted.test.ts +110 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqliteMigrator.test.ts +48 -0
- package/repos/effect/packages/sql/sqlite-node/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-node/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-react-native/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-react-native/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-react-native/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-react-native/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts +354 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts +55 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-react-native/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-react-native/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-react-native/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-wasm/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-wasm/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-wasm/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-wasm/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts +118 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts +551 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts +55 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/index.ts +14 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/internal/opfsWorker.ts +7 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts +47 -0
- package/repos/effect/packages/sql/sqlite-wasm/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-wasm/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-wasm/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/ai-codegen/codegen.schema.json +92 -0
- package/repos/effect/packages/tools/ai-codegen/docgen.json +4 -0
- package/repos/effect/packages/tools/ai-codegen/package.json +61 -0
- package/repos/effect/packages/tools/ai-codegen/src/Config.ts +292 -0
- package/repos/effect/packages/tools/ai-codegen/src/Discovery.ts +197 -0
- package/repos/effect/packages/tools/ai-codegen/src/Generator.ts +183 -0
- package/repos/effect/packages/tools/ai-codegen/src/Glob.ts +56 -0
- package/repos/effect/packages/tools/ai-codegen/src/PostProcess.ts +157 -0
- package/repos/effect/packages/tools/ai-codegen/src/SpecFetcher.ts +154 -0
- package/repos/effect/packages/tools/ai-codegen/src/bin.ts +10 -0
- package/repos/effect/packages/tools/ai-codegen/src/main.ts +237 -0
- package/repos/effect/packages/tools/ai-codegen/tsconfig.json +15 -0
- package/repos/effect/packages/tools/ai-docgen/docgen.json +4 -0
- package/repos/effect/packages/tools/ai-docgen/package.json +60 -0
- package/repos/effect/packages/tools/ai-docgen/src/Glob.ts +48 -0
- package/repos/effect/packages/tools/ai-docgen/src/main.ts +186 -0
- package/repos/effect/packages/tools/ai-docgen/tsconfig.json +14 -0
- package/repos/effect/packages/tools/bundle/docgen.json +4 -0
- package/repos/effect/packages/tools/bundle/fixtures/basic.ts +3 -0
- package/repos/effect/packages/tools/bundle/fixtures/batching.ts +25 -0
- package/repos/effect/packages/tools/bundle/fixtures/brand.ts +5 -0
- package/repos/effect/packages/tools/bundle/fixtures/cache.ts +10 -0
- package/repos/effect/packages/tools/bundle/fixtures/config.ts +13 -0
- package/repos/effect/packages/tools/bundle/fixtures/differ.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/http-client.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/logger.ts +5 -0
- package/repos/effect/packages/tools/bundle/fixtures/metric.ts +13 -0
- package/repos/effect/packages/tools/bundle/fixtures/optic.ts +6 -0
- package/repos/effect/packages/tools/bundle/fixtures/pubsub.ts +22 -0
- package/repos/effect/packages/tools/bundle/fixtures/queue.ts +18 -0
- package/repos/effect/packages/tools/bundle/fixtures/schedule.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-fromJsonSchemaDocument.ts +16 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-representation-roundtrip.ts +16 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-string-transformation.ts +20 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-string.ts +8 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-template-literal.ts +8 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toArbitraryLazy.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toCodeDocument.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toCodecJson.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toEquivalence.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toFormatter.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toJsonSchemaDocument.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toRepresentation.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/stm.ts +21 -0
- package/repos/effect/packages/tools/bundle/fixtures/stream.ts +7 -0
- package/repos/effect/packages/tools/bundle/package.json +52 -0
- package/repos/effect/packages/tools/bundle/src/Cli.ts +104 -0
- package/repos/effect/packages/tools/bundle/src/Fixtures.ts +51 -0
- package/repos/effect/packages/tools/bundle/src/Plugins.ts +131 -0
- package/repos/effect/packages/tools/bundle/src/Reporter.ts +200 -0
- package/repos/effect/packages/tools/bundle/src/Rollup.ts +179 -0
- package/repos/effect/packages/tools/bundle/src/bin.ts +23 -0
- package/repos/effect/packages/tools/bundle/tsconfig.fixtures.json +13 -0
- package/repos/effect/packages/tools/bundle/tsconfig.json +9 -0
- package/repos/effect/packages/tools/bundle/tsconfig.src.json +13 -0
- package/repos/effect/packages/tools/jsdocs/docgen.json +4 -0
- package/repos/effect/packages/tools/jsdocs/package.json +64 -0
- package/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts +3303 -0
- package/repos/effect/packages/tools/jsdocs/src/bin.ts +23 -0
- package/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts +979 -0
- package/repos/effect/packages/tools/jsdocs/tsconfig.json +8 -0
- package/repos/effect/packages/tools/jsdocs/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/openapi-generator/CHANGELOG.md +691 -0
- package/repos/effect/packages/tools/openapi-generator/LICENSE +21 -0
- package/repos/effect/packages/tools/openapi-generator/docgen.json +4 -0
- package/repos/effect/packages/tools/openapi-generator/package.json +78 -0
- package/repos/effect/packages/tools/openapi-generator/src/HttpApiTransformer.ts +552 -0
- package/repos/effect/packages/tools/openapi-generator/src/JsonSchemaGenerator.ts +319 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiGenerator.ts +1201 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiPatch.ts +539 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiTransformer.ts +1019 -0
- package/repos/effect/packages/tools/openapi-generator/src/ParsedOperation.ts +277 -0
- package/repos/effect/packages/tools/openapi-generator/src/Utils.ts +111 -0
- package/repos/effect/packages/tools/openapi-generator/src/bin.ts +10 -0
- package/repos/effect/packages/tools/openapi-generator/src/main.ts +116 -0
- package/repos/effect/packages/tools/openapi-generator/test/JsonSchemaGenerator.test.ts +173 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiGenerator.test.ts +2130 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiGeneratorCli.test.ts +147 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiPatch.test.ts +380 -0
- package/repos/effect/packages/tools/openapi-generator/test/Utils.test.ts +33 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/cli-basic-spec.json +35 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/cli-warning-spec.json +57 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/invalid-op.json +7 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/missing-path.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/missing-value.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-add.json +8 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-multiple.json +17 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-patch.yaml +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-remove.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-replace.json +7 -0
- package/repos/effect/packages/tools/openapi-generator/tsconfig.json +9 -0
- package/repos/effect/packages/tools/openapi-generator/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/oxc/docgen.json +4 -0
- package/repos/effect/packages/tools/oxc/oxlintrc.json +83 -0
- package/repos/effect/packages/tools/oxc/package.json +72 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/index.ts +18 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/jsdocs.ts +108 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-bigint-literals.ts +25 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-import-from-barrel-package.ts +144 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-js-extension-imports.ts +73 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-opaque-instance-fields.ts +92 -0
- package/repos/effect/packages/tools/oxc/test/jsdocs.test.ts +75 -0
- package/repos/effect/packages/tools/oxc/test/no-import-from-barrel-package.test.ts +182 -0
- package/repos/effect/packages/tools/oxc/test/no-js-extension-imports.test.ts +120 -0
- package/repos/effect/packages/tools/oxc/test/no-opaque-instance-fields.test.ts +276 -0
- package/repos/effect/packages/tools/oxc/test/utils.ts +61 -0
- package/repos/effect/packages/tools/oxc/tsconfig.json +8 -0
- package/repos/effect/packages/tools/oxc/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/utils/docgen.json +4 -0
- package/repos/effect/packages/tools/utils/package.json +59 -0
- package/repos/effect/packages/tools/utils/src/Codegen.ts +214 -0
- package/repos/effect/packages/tools/utils/src/Glob.ts +61 -0
- package/repos/effect/packages/tools/utils/src/bin.ts +16 -0
- package/repos/effect/packages/tools/utils/src/commands/codegen.ts +44 -0
- package/repos/effect/packages/tools/utils/tsconfig.json +9 -0
- package/repos/effect/packages/vitest/CHANGELOG.md +601 -0
- package/repos/effect/packages/vitest/LICENSE +21 -0
- package/repos/effect/packages/vitest/README.md +320 -0
- package/repos/effect/packages/vitest/docgen.json +24 -0
- package/repos/effect/packages/vitest/package.json +60 -0
- package/repos/effect/packages/vitest/src/index.ts +259 -0
- package/repos/effect/packages/vitest/src/internal/internal.ts +374 -0
- package/repos/effect/packages/vitest/src/utils.ts +321 -0
- package/repos/effect/packages/vitest/test/index.test.ts +222 -0
- package/repos/effect/packages/vitest/test/isolation.test.ts +105 -0
- package/repos/effect/packages/vitest/test/nested-isolation.test.ts +212 -0
- package/repos/effect/packages/vitest/tsconfig.json +11 -0
- package/repos/effect/packages/vitest/typetest/index.tst.ts +56 -0
- package/repos/effect/packages/vitest/vitest.config.ts +6 -0
- package/repos/effect/patches/@changesets__assemble-release-plan.patch +26 -0
- package/repos/effect/patches/@changesets__get-github-info.patch +44 -0
- package/repos/effect/pnpm-lock.yaml +13483 -0
- package/repos/effect/pnpm-workspace.yaml +10 -0
- package/repos/effect/scripts/bundle-compare.sh +42 -0
- package/repos/effect/scripts/check-cookbook-schedule.ts +390 -0
- package/repos/effect/scripts/circular.mjs +26 -0
- package/repos/effect/scripts/clean.mjs +19 -0
- package/repos/effect/scripts/codemod.mjs +22 -0
- package/repos/effect/scripts/codemods/jsdoc.ts +79 -0
- package/repos/effect/scripts/docs.mjs +64 -0
- package/repos/effect/scripts/package-scalar.mjs +14 -0
- package/repos/effect/scripts/package-swagger.mjs +23 -0
- package/repos/effect/scripts/package.json +22 -0
- package/repos/effect/scripts/tsconfig.json +18 -0
- package/repos/effect/scripts/version.mjs +9 -0
- package/repos/effect/scripts/version.template.txt +2 -0
- package/repos/effect/scripts/worktree-setup.sh +27 -0
- package/repos/effect/stats.txt +30 -0
- package/repos/effect/tsconfig.base.json +46 -0
- package/repos/effect/tsconfig.json +128 -0
- package/repos/effect/tsconfig.packages.json +39 -0
- package/repos/effect/tstyche.json +7 -0
- package/repos/effect/vitest.config.ts +31 -0
- package/repos/effect/vitest.setup.ts +4 -0
- package/repos/effect/vitest.shared.ts +61 -0
- package/repos/effect.subtree.json +16 -0
|
@@ -0,0 +1,3507 @@
|
|
|
1
|
+
# Schedule Cookbook
|
|
2
|
+
|
|
3
|
+
Use this cookbook by problem shape: retry failed work, poll successful status values, repeat background work, or observe a schedule.
|
|
4
|
+
|
|
5
|
+
## Before Choosing A Recipe
|
|
6
|
+
|
|
7
|
+
- `Effect.retry` observes typed failures and stops on the first success.
|
|
8
|
+
- `Effect.repeat` observes successful values and stops on the first failure.
|
|
9
|
+
- Recurrence counts are follow-up runs after the first execution, so `Schedule.recurs(3)` allows three more attempts or repeats.
|
|
10
|
+
- Schedule output is policy output. Use `Schedule.passthrough` when a repeated program should return the latest successful value.
|
|
11
|
+
- Backoff, jitter, and spacing control timing. They do not classify errors, make writes idempotent, or add a stop condition by themselves.
|
|
12
|
+
- Bound request-path recurrence with a count, elapsed budget, or both. Leave unbounded schedules to explicitly owned background fibers.
|
|
13
|
+
|
|
14
|
+
| Problem | Start with |
|
|
15
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| Retry a flaky operation | `Effect.retry` plus `Schedule.exponential`, usually with `Schedule.recurs` and `Schedule.jittered` |
|
|
17
|
+
| Poll until a status is terminal | `Effect.repeat` plus `Schedule.spaced`, `Schedule.passthrough`, and `Schedule.while` |
|
|
18
|
+
| Run periodic background work | `Effect.repeat` plus `Schedule.spaced`, `Schedule.fixed`, `Schedule.windowed`, or `Schedule.cron` |
|
|
19
|
+
| Avoid overload across many callers | Add jitter, backoff, count limits, elapsed limits, and idempotency where side effects are retried |
|
|
20
|
+
|
|
21
|
+
## Core Building Blocks
|
|
22
|
+
|
|
23
|
+
### Exponential Backoff
|
|
24
|
+
|
|
25
|
+
Create an Effect Schedule value named `retryBackoff` that uses exponential backoff starting at 100 milliseconds.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { Schedule } from "effect"
|
|
29
|
+
|
|
30
|
+
const retryBackoff = Schedule.exponential("100 millis")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Limit Recurrences
|
|
34
|
+
|
|
35
|
+
Create an Effect Schedule value named `threeRetries` that allows at most 3 recurrences.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Schedule } from "effect"
|
|
39
|
+
|
|
40
|
+
const threeRetries = Schedule.recurs(3)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
44
|
+
|
|
45
|
+
### Space Repetitions
|
|
46
|
+
|
|
47
|
+
Create an Effect Schedule value named `pollEverySecond` that recurs about once per second after each completed run.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Schedule } from "effect"
|
|
51
|
+
|
|
52
|
+
const pollEverySecond = Schedule.spaced("1 second")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Use Fixed Cadence
|
|
56
|
+
|
|
57
|
+
Create an Effect Schedule value named `heartbeatCadence` that runs on an aligned 30 second cadence instead of waiting 30 seconds after each run finishes.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { Schedule } from "effect"
|
|
61
|
+
|
|
62
|
+
const heartbeatCadence = Schedule.fixed("30 seconds")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Run One Follow-Up
|
|
66
|
+
|
|
67
|
+
Create an Effect Schedule value named `oneFollowUpRun` that performs exactly one additional recurrence after about 10 seconds.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Schedule } from "effect"
|
|
71
|
+
|
|
72
|
+
const oneFollowUpRun = Schedule.duration("10 seconds")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Stop After Elapsed Time
|
|
76
|
+
|
|
77
|
+
Create an Effect Schedule value named `thirtySecondBudget` that can be used to stop repetitions or retries after about 30 seconds have elapsed.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { Schedule } from "effect"
|
|
81
|
+
|
|
82
|
+
const thirtySecondBudget = Schedule.during("30 seconds")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Add Jitter
|
|
86
|
+
|
|
87
|
+
Create an Effect Schedule value named `jitteredBackoff` that starts with 100 ms exponential backoff and adds a little randomness to each delay.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Schedule } from "effect"
|
|
91
|
+
|
|
92
|
+
const jitteredBackoff = Schedule.exponential("100 millis").pipe(
|
|
93
|
+
Schedule.jittered
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Explanation: jitter randomly changes each computed delay a little. This helps when many callers fail at the same time, because they will not all retry again at the exact same moment.
|
|
98
|
+
|
|
99
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
100
|
+
|
|
101
|
+
### Combine Backoff With Count And Time Limits
|
|
102
|
+
|
|
103
|
+
Create an Effect Schedule value named `boundedBackoff` that uses exponential backoff starting at 100 ms, allows at most 5 recurrences, and also stops after about 10 seconds have elapsed.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { Schedule } from "effect"
|
|
107
|
+
|
|
108
|
+
const boundedBackoff = Schedule.exponential("100 millis").pipe(
|
|
109
|
+
Schedule.both(Schedule.recurs(5)),
|
|
110
|
+
Schedule.both(Schedule.during("10 seconds"))
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Explanation: `Schedule.both` runs both schedules against the same input. The combined schedule continues only while both schedules continue. Its output is a pair, which matters when another operator reads the schedule output.
|
|
115
|
+
|
|
116
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
117
|
+
|
|
118
|
+
### Keep The Left Schedule Output
|
|
119
|
+
|
|
120
|
+
Create an Effect Schedule value named `limitedBackoff` that combines exponential backoff starting at 100 ms with a maximum of 5 recurrences, while keeping the backoff delay as the schedule output.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { Schedule } from "effect"
|
|
124
|
+
|
|
125
|
+
const limitedBackoff = Schedule.exponential("100 millis").pipe(
|
|
126
|
+
Schedule.bothLeft(Schedule.recurs(5))
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
131
|
+
|
|
132
|
+
### Keep The Right Schedule Output
|
|
133
|
+
|
|
134
|
+
Create an Effect Schedule value named `retryAttemptCount` that combines exponential backoff starting at 100 ms with a maximum of 5 recurrences, while keeping the recurrence count as the schedule output.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { Schedule } from "effect"
|
|
138
|
+
|
|
139
|
+
const retryAttemptCount = Schedule.exponential("100 millis").pipe(
|
|
140
|
+
Schedule.bothRight(Schedule.recurs(5))
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
145
|
+
|
|
146
|
+
### Return The Latest Input
|
|
147
|
+
|
|
148
|
+
Create an Effect Schedule value named `keepLatestStatus` that checks about once per second and outputs the latest successful `Status` value instead of the schedule's internal counter.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { Schedule } from "effect"
|
|
152
|
+
|
|
153
|
+
type Status = "Pending" | "Done"
|
|
154
|
+
|
|
155
|
+
const keepLatestStatus = Schedule.spaced("1 second").pipe(
|
|
156
|
+
Schedule.setInputType<Status>(),
|
|
157
|
+
Schedule.passthrough
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Explanation: `Schedule.setInputType` tells TypeScript what kind of input the schedule receives. `Schedule.passthrough` changes the schedule output to the latest input, so `Effect.repeat` can return the latest successful value instead of the schedule's counter or delay.
|
|
162
|
+
|
|
163
|
+
### Continue While Input Matches
|
|
164
|
+
|
|
165
|
+
Create an Effect Schedule value named `pollWhilePending` that checks about once per second, keeps returning the latest successful `Status`, and stops when the latest status is no longer `Pending`.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { Schedule } from "effect"
|
|
169
|
+
|
|
170
|
+
type Status = "Pending" | "Done"
|
|
171
|
+
|
|
172
|
+
const pollWhilePending = Schedule.spaced("1 second").pipe(
|
|
173
|
+
Schedule.setInputType<Status>(),
|
|
174
|
+
Schedule.passthrough,
|
|
175
|
+
Schedule.while(({ input }) => input === "Pending")
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Run One Policy Then Another
|
|
180
|
+
|
|
181
|
+
Create an Effect Schedule value named `warmThenSteady` that recurs 3 times 100 ms apart, then switches to recurring every 5 seconds.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { Schedule } from "effect"
|
|
185
|
+
|
|
186
|
+
const warmThenSteady = Schedule.spaced("100 millis").pipe(
|
|
187
|
+
Schedule.bothLeft(Schedule.recurs(3)),
|
|
188
|
+
Schedule.andThen(Schedule.spaced("5 seconds"))
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
193
|
+
|
|
194
|
+
### Retry With A Schedule
|
|
195
|
+
|
|
196
|
+
Write the Effect code that retries `request` with exponential backoff starting at 100 ms and at most 3 retries.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { Effect, Schedule } from "effect"
|
|
200
|
+
|
|
201
|
+
type RequestError = unknown
|
|
202
|
+
|
|
203
|
+
declare const request: Effect.Effect<Response, RequestError>
|
|
204
|
+
|
|
205
|
+
const retryPolicy = Schedule.exponential("100 millis").pipe(
|
|
206
|
+
Schedule.both(Schedule.recurs(3))
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
const program = request.pipe(Effect.retry(retryPolicy))
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
213
|
+
|
|
214
|
+
### Repeat With A Schedule
|
|
215
|
+
|
|
216
|
+
Write the Effect code that repeats `refresh` after each successful run, waiting about 1 minute between runs.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { Effect, Schedule } from "effect"
|
|
220
|
+
|
|
221
|
+
type RefreshError = unknown
|
|
222
|
+
|
|
223
|
+
declare const refresh: Effect.Effect<void, RefreshError>
|
|
224
|
+
|
|
225
|
+
const refreshCadence = Schedule.spaced("1 minute")
|
|
226
|
+
|
|
227
|
+
const program = refresh.pipe(Effect.repeat(refreshCadence))
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
231
|
+
|
|
232
|
+
### Retry Only Selected Failures
|
|
233
|
+
|
|
234
|
+
Write the Effect code that retries `callService` at most 3 times, but only when the failure is `Transient`.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { Effect, Schedule } from "effect"
|
|
238
|
+
|
|
239
|
+
type ServiceResponse = unknown
|
|
240
|
+
|
|
241
|
+
type ServiceError =
|
|
242
|
+
| { readonly _tag: "Transient" }
|
|
243
|
+
| { readonly _tag: "Permanent" }
|
|
244
|
+
|
|
245
|
+
declare const callService: Effect.Effect<ServiceResponse, ServiceError>
|
|
246
|
+
|
|
247
|
+
const program = callService.pipe(
|
|
248
|
+
Effect.retry({
|
|
249
|
+
schedule: Schedule.recurs(3),
|
|
250
|
+
while: (error) => error._tag === "Transient"
|
|
251
|
+
})
|
|
252
|
+
)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
256
|
+
|
|
257
|
+
### Repeat Until A Successful Value Matches
|
|
258
|
+
|
|
259
|
+
Write the Effect code that repeats `readStatus` about once per second until the successful status is `Complete`.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { Effect, Schedule } from "effect"
|
|
263
|
+
|
|
264
|
+
type StatusError = unknown
|
|
265
|
+
|
|
266
|
+
type Status = "Pending" | "Complete"
|
|
267
|
+
|
|
268
|
+
declare const readStatus: Effect.Effect<Status, StatusError>
|
|
269
|
+
|
|
270
|
+
const program = readStatus.pipe(
|
|
271
|
+
Effect.repeat({
|
|
272
|
+
schedule: Schedule.spaced("1 second"),
|
|
273
|
+
until: (status) => status === "Complete"
|
|
274
|
+
})
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
279
|
+
|
|
280
|
+
### Observe Schedule Input
|
|
281
|
+
|
|
282
|
+
Create an Effect Schedule value named `loggingRetryPolicy` that uses exponential backoff starting at 100 ms and logs each retry input error tag before the delay is selected.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { Console, Schedule } from "effect"
|
|
286
|
+
|
|
287
|
+
type RequestError = { readonly _tag: string }
|
|
288
|
+
|
|
289
|
+
const loggingRetryPolicy = Schedule.exponential("100 millis").pipe(
|
|
290
|
+
Schedule.tapInput((error: RequestError) => Console.log(error._tag))
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Observe Schedule Output
|
|
295
|
+
|
|
296
|
+
Create an Effect Schedule value named `measuredBackoff` that uses exponential backoff starting at 100 ms and records each selected retry delay.
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
300
|
+
|
|
301
|
+
declare const Metrics: {
|
|
302
|
+
readonly recordRetryDelay: (delay: Duration.Duration) => Effect.Effect<void>
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const measuredBackoff = Schedule.exponential("100 millis").pipe(
|
|
306
|
+
Schedule.tapOutput((delay) => Metrics.recordRetryDelay(delay))
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Run On Time Windows
|
|
311
|
+
|
|
312
|
+
Create an Effect Schedule value named `minuteWindow` that recurs on one-minute window boundaries.
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { Schedule } from "effect"
|
|
316
|
+
|
|
317
|
+
const minuteWindow = Schedule.windowed("1 minute")
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Run From A Cron Expression
|
|
321
|
+
|
|
322
|
+
Create an Effect Schedule value named `nightlyAtTwo` that recurs every night at 02:00 using a cron expression.
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { Schedule } from "effect"
|
|
326
|
+
|
|
327
|
+
const nightlyAtTwo = Schedule.cron("0 2 * * *")
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Retry And Recovery
|
|
331
|
+
|
|
332
|
+
### Retry HTTP GET Timeouts
|
|
333
|
+
|
|
334
|
+
Write the Effect code that retries `getUser("user-123")` only when it fails with `HttpTimeout`.
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { Effect, Schedule } from "effect"
|
|
338
|
+
|
|
339
|
+
type User = unknown
|
|
340
|
+
|
|
341
|
+
type HttpTimeout = { readonly _tag: "HttpTimeout" }
|
|
342
|
+
type HttpStatusError = { readonly _tag: "HttpStatusError"; readonly status: number }
|
|
343
|
+
type DecodeError = { readonly _tag: "DecodeError" }
|
|
344
|
+
|
|
345
|
+
declare const getUser: (
|
|
346
|
+
id: string
|
|
347
|
+
) => Effect.Effect<User, HttpTimeout | HttpStatusError | DecodeError>
|
|
348
|
+
|
|
349
|
+
const isHttpTimeout = (
|
|
350
|
+
error: HttpTimeout | HttpStatusError | DecodeError
|
|
351
|
+
): error is HttpTimeout => error._tag === "HttpTimeout"
|
|
352
|
+
|
|
353
|
+
const retryGetUserTimeouts = Schedule.exponential("100 millis").pipe(
|
|
354
|
+
Schedule.jittered,
|
|
355
|
+
Schedule.both(Schedule.recurs(3)),
|
|
356
|
+
Schedule.both(Schedule.during("2 seconds"))
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
const program = getUser("user-123").pipe(
|
|
360
|
+
Effect.retry({
|
|
361
|
+
schedule: retryGetUserTimeouts,
|
|
362
|
+
while: isHttpTimeout
|
|
363
|
+
})
|
|
364
|
+
)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
368
|
+
|
|
369
|
+
### Retry An Idempotent Order Submission
|
|
370
|
+
|
|
371
|
+
Write the Effect code that retries a temporarily failed order submission.
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import { Effect, Schedule } from "effect"
|
|
375
|
+
|
|
376
|
+
type Order = unknown
|
|
377
|
+
|
|
378
|
+
type PostOrderError =
|
|
379
|
+
| { readonly _tag: "Timeout" }
|
|
380
|
+
| { readonly _tag: "ConnectionReset" }
|
|
381
|
+
| { readonly _tag: "BadGateway" }
|
|
382
|
+
| { readonly _tag: "ServiceUnavailable" }
|
|
383
|
+
| { readonly _tag: "RateLimited" }
|
|
384
|
+
| { readonly _tag: "InvalidRequest" }
|
|
385
|
+
| { readonly _tag: "Unauthorized" }
|
|
386
|
+
|
|
387
|
+
declare const postOrder: (
|
|
388
|
+
request: {
|
|
389
|
+
readonly customerId: string
|
|
390
|
+
readonly sku: string
|
|
391
|
+
readonly quantity: number
|
|
392
|
+
readonly idempotencyKey: string
|
|
393
|
+
}
|
|
394
|
+
) => Effect.Effect<Order, PostOrderError>
|
|
395
|
+
|
|
396
|
+
const idempotencyKey = "order-key-123"
|
|
397
|
+
|
|
398
|
+
const isRetryablePostOrderError = (error: PostOrderError): boolean => {
|
|
399
|
+
switch (error._tag) {
|
|
400
|
+
case "Timeout":
|
|
401
|
+
case "ConnectionReset":
|
|
402
|
+
case "BadGateway":
|
|
403
|
+
case "ServiceUnavailable":
|
|
404
|
+
case "RateLimited":
|
|
405
|
+
return true
|
|
406
|
+
case "InvalidRequest":
|
|
407
|
+
case "Unauthorized":
|
|
408
|
+
return false
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const retryOrderSubmission = Schedule.exponential("100 millis").pipe(
|
|
413
|
+
Schedule.jittered,
|
|
414
|
+
Schedule.both(Schedule.recurs(4)),
|
|
415
|
+
Schedule.both(Schedule.during("3 seconds"))
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
const program = postOrder({
|
|
419
|
+
customerId: "customer-1",
|
|
420
|
+
sku: "sku-1",
|
|
421
|
+
quantity: 2,
|
|
422
|
+
idempotencyKey
|
|
423
|
+
}).pipe(
|
|
424
|
+
Effect.retry({
|
|
425
|
+
schedule: retryOrderSubmission,
|
|
426
|
+
while: isRetryablePostOrderError
|
|
427
|
+
})
|
|
428
|
+
)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Caution: the schedule only times retries; the stable idempotency key is what makes duplicate attempts safe.
|
|
432
|
+
|
|
433
|
+
### Retry HTTP GET 503 Responses
|
|
434
|
+
|
|
435
|
+
Write the Effect code that retries `getCatalog("https://api.example.test/catalog")` only when the error value is an HTTP `GET` response with status `503`.
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
import { Effect, Schedule } from "effect"
|
|
439
|
+
|
|
440
|
+
type TransportError = { readonly _tag: "TransportError" }
|
|
441
|
+
type HttpResponseError = {
|
|
442
|
+
readonly _tag: "HttpResponseError"
|
|
443
|
+
readonly method: "GET" | "POST"
|
|
444
|
+
readonly status: number
|
|
445
|
+
}
|
|
446
|
+
type GetCatalogError = TransportError | HttpResponseError
|
|
447
|
+
|
|
448
|
+
declare const getCatalog: (
|
|
449
|
+
url: string
|
|
450
|
+
) => Effect.Effect<string, GetCatalogError>
|
|
451
|
+
|
|
452
|
+
const isServiceUnavailableGet = (error: GetCatalogError): boolean =>
|
|
453
|
+
error._tag === "HttpResponseError" &&
|
|
454
|
+
error.method === "GET" &&
|
|
455
|
+
error.status === 503
|
|
456
|
+
|
|
457
|
+
const retry503WithBackoff = Schedule.exponential("100 millis").pipe(
|
|
458
|
+
Schedule.jittered,
|
|
459
|
+
Schedule.both(Schedule.recurs(4)),
|
|
460
|
+
Schedule.both(Schedule.during("3 seconds"))
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
const program = getCatalog("https://api.example.test/catalog").pipe(
|
|
464
|
+
Effect.retry({
|
|
465
|
+
schedule: retry503WithBackoff,
|
|
466
|
+
while: isServiceUnavailableGet
|
|
467
|
+
})
|
|
468
|
+
)
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
472
|
+
|
|
473
|
+
### Respect Retry-After On Rate Limits
|
|
474
|
+
|
|
475
|
+
Write the Effect code that retries `callApi` only for `RateLimited` errors.
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
479
|
+
|
|
480
|
+
type HttpError =
|
|
481
|
+
| { readonly _tag: "RateLimited"; readonly retryAfter: Duration.Duration | undefined }
|
|
482
|
+
| { readonly _tag: "Unauthorized" | "Forbidden" | "BadRequest" | "Unavailable" }
|
|
483
|
+
|
|
484
|
+
declare const callApi: Effect.Effect<string, HttpError>
|
|
485
|
+
|
|
486
|
+
const rateLimitPolicy = Schedule.exponential("100 millis").pipe(
|
|
487
|
+
Schedule.jittered,
|
|
488
|
+
Schedule.both(Schedule.identity<HttpError>()),
|
|
489
|
+
Schedule.modifyDelay(([_, error], delay) =>
|
|
490
|
+
Effect.succeed(
|
|
491
|
+
error._tag === "RateLimited" && error.retryAfter !== undefined
|
|
492
|
+
? Duration.max(delay, error.retryAfter)
|
|
493
|
+
: delay
|
|
494
|
+
)
|
|
495
|
+
),
|
|
496
|
+
Schedule.both(Schedule.recurs(5)),
|
|
497
|
+
Schedule.while(({ input }) => input._tag === "RateLimited")
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
const program = Effect.retry(callApi, rateLimitPolicy)
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Explanation: `Schedule.identity<HttpError>()` keeps the retry error available as schedule output. `Schedule.modifyDelay` can then compare the normal backoff delay with `retryAfter` and choose the longer delay.
|
|
504
|
+
|
|
505
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
506
|
+
|
|
507
|
+
### Retry Startup Config Fetches
|
|
508
|
+
|
|
509
|
+
Write the Effect code that retries startup config loading only for temporary network/service failures. Use exponential backoff starting at about 100 ms. Try at most 3 more times.
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
import { Effect, Schedule } from "effect"
|
|
513
|
+
|
|
514
|
+
type ClientConfig = unknown
|
|
515
|
+
|
|
516
|
+
type ConfigFetchError =
|
|
517
|
+
| { readonly _tag: "NetworkUnavailable" }
|
|
518
|
+
| { readonly _tag: "ServiceUnavailable" }
|
|
519
|
+
| { readonly _tag: "MalformedConfig" }
|
|
520
|
+
|
|
521
|
+
declare const fetchStartupConfig: Effect.Effect<ClientConfig, ConfigFetchError>
|
|
522
|
+
|
|
523
|
+
const isTransientConfigFailure = (error: ConfigFetchError): boolean =>
|
|
524
|
+
error._tag === "NetworkUnavailable" || error._tag === "ServiceUnavailable"
|
|
525
|
+
|
|
526
|
+
const startupConfigRetryPolicy = Schedule.exponential("100 millis").pipe(
|
|
527
|
+
Schedule.both(Schedule.recurs(3))
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
const program = fetchStartupConfig.pipe(
|
|
531
|
+
Effect.retry({
|
|
532
|
+
schedule: startupConfigRetryPolicy,
|
|
533
|
+
while: isTransientConfigFailure
|
|
534
|
+
})
|
|
535
|
+
)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
539
|
+
|
|
540
|
+
### Retry Transient Profile Loads
|
|
541
|
+
|
|
542
|
+
Write the Effect code that retries `fetchProfile("user-123")` only for temporary profile-load failures. Use exponential backoff starting at about 100 ms, add a little randomness so callers do not retry together, and try at most 3 more times.
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
import { Effect, Schedule } from "effect"
|
|
546
|
+
|
|
547
|
+
type Profile = unknown
|
|
548
|
+
type ProfileLoadError = unknown
|
|
549
|
+
|
|
550
|
+
declare const fetchProfile: (
|
|
551
|
+
userId: string
|
|
552
|
+
) => Effect.Effect<Profile, ProfileLoadError>
|
|
553
|
+
|
|
554
|
+
declare const isTransientProfileLoadError: (
|
|
555
|
+
error: ProfileLoadError
|
|
556
|
+
) => boolean
|
|
557
|
+
|
|
558
|
+
const retryTransientProfileLoad = Schedule.exponential("100 millis").pipe(
|
|
559
|
+
Schedule.jittered,
|
|
560
|
+
Schedule.both(Schedule.recurs(3))
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
const program = fetchProfile("user-123").pipe(
|
|
564
|
+
Effect.retry({
|
|
565
|
+
schedule: retryTransientProfileLoad,
|
|
566
|
+
while: isTransientProfileLoadError
|
|
567
|
+
})
|
|
568
|
+
)
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
572
|
+
|
|
573
|
+
### Retry Token Refresh Briefly
|
|
574
|
+
|
|
575
|
+
Write the Effect code that retries token refresh only for temporary refresh failures. Use exponential backoff starting at about 100 ms. Add a little randomness, try at most 2 more times, stop after about 1 second, and do not retry rejected, revoked, or expired refresh tokens.
|
|
576
|
+
|
|
577
|
+
```ts
|
|
578
|
+
import { Effect, Schedule } from "effect"
|
|
579
|
+
|
|
580
|
+
type Tokens = unknown
|
|
581
|
+
|
|
582
|
+
type RefreshTimeout = { readonly _tag: "RefreshTimeout" }
|
|
583
|
+
type RefreshServiceUnavailable = { readonly _tag: "RefreshServiceUnavailable" }
|
|
584
|
+
type RefreshRejected = { readonly _tag: "RefreshRejected" }
|
|
585
|
+
type RefreshError = RefreshTimeout | RefreshServiceUnavailable | RefreshRejected
|
|
586
|
+
|
|
587
|
+
declare const postRefreshToken: (
|
|
588
|
+
refreshToken: string
|
|
589
|
+
) => Effect.Effect<Tokens, RefreshError>
|
|
590
|
+
|
|
591
|
+
const isTransientRefreshFailure = (
|
|
592
|
+
error: RefreshError
|
|
593
|
+
): error is RefreshTimeout | RefreshServiceUnavailable =>
|
|
594
|
+
error._tag === "RefreshTimeout" ||
|
|
595
|
+
error._tag === "RefreshServiceUnavailable"
|
|
596
|
+
|
|
597
|
+
const retryTokenRefreshBriefly = Schedule.exponential("100 millis").pipe(
|
|
598
|
+
Schedule.jittered,
|
|
599
|
+
Schedule.both(Schedule.recurs(2)),
|
|
600
|
+
Schedule.both(Schedule.during("1 second"))
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
const program = postRefreshToken("refresh-token-1").pipe(
|
|
604
|
+
Effect.retry({
|
|
605
|
+
schedule: retryTokenRefreshBriefly,
|
|
606
|
+
while: isTransientRefreshFailure
|
|
607
|
+
})
|
|
608
|
+
)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
612
|
+
|
|
613
|
+
### Reconnect A WebSocket With Capped Backoff
|
|
614
|
+
|
|
615
|
+
Write the Effect code that reconnects a WebSocket. Use exponential backoff starting at about 100 ms, never wait more than 5 seconds before the next attempt, and try at most 8 more times.
|
|
616
|
+
|
|
617
|
+
```ts
|
|
618
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
619
|
+
|
|
620
|
+
type LiveSocket = unknown
|
|
621
|
+
type WebSocketOpenError = unknown
|
|
622
|
+
|
|
623
|
+
declare const openLiveSocket: Effect.Effect<LiveSocket, WebSocketOpenError>
|
|
624
|
+
declare const isRetryableOpenError: (error: WebSocketOpenError) => boolean
|
|
625
|
+
|
|
626
|
+
const websocketReconnectPolicy = Schedule.exponential("100 millis").pipe(
|
|
627
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(5)))),
|
|
628
|
+
Schedule.both(Schedule.recurs(8))
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
const program = openLiveSocket.pipe(
|
|
632
|
+
Effect.retry({
|
|
633
|
+
schedule: websocketReconnectPolicy,
|
|
634
|
+
while: isRetryableOpenError
|
|
635
|
+
})
|
|
636
|
+
)
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
640
|
+
|
|
641
|
+
### Reconnect A WebSocket With Jitter
|
|
642
|
+
|
|
643
|
+
Write the Effect code that reconnects a WebSocket. Add a little randomness so clients do not reconnect together, use exponential backoff starting at about 100 ms, never wait more than 5 seconds before the next attempt, try at most 8 more times, and stop retrying non-retryable reconnect errors.
|
|
644
|
+
|
|
645
|
+
```ts
|
|
646
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
647
|
+
|
|
648
|
+
type WebSocketReconnectError = unknown
|
|
649
|
+
|
|
650
|
+
declare const reconnectWebSocket: Effect.Effect<string, WebSocketReconnectError>
|
|
651
|
+
declare const isRetryableReconnect: (error: WebSocketReconnectError) => boolean
|
|
652
|
+
|
|
653
|
+
const webSocketReconnectPolicy = Schedule.exponential("100 millis").pipe(
|
|
654
|
+
Schedule.setInputType<WebSocketReconnectError>(),
|
|
655
|
+
Schedule.jittered,
|
|
656
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(5)))),
|
|
657
|
+
Schedule.both(Schedule.recurs(8)),
|
|
658
|
+
Schedule.while(({ input }) => isRetryableReconnect(input))
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
const program = reconnectWebSocket.pipe(
|
|
662
|
+
Effect.retry(webSocketReconnectPolicy)
|
|
663
|
+
)
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
667
|
+
|
|
668
|
+
### Retry Startup Dependency Checks
|
|
669
|
+
|
|
670
|
+
Write the Effect code that retries startup dependency checks only for temporary startup failures. Use exponential backoff starting at about 100 ms, never wait more than 2 seconds before the next retry, try at most 5 more times, and do not keep startup waiting for more than about 20 seconds.
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
674
|
+
|
|
675
|
+
type DependencyCheckError =
|
|
676
|
+
| { readonly reason: "DnsLookup" | "ConnectionRefused" | "Timeout" }
|
|
677
|
+
| { readonly reason: "BadCredentials" | "SchemaMismatch" }
|
|
678
|
+
|
|
679
|
+
declare const checkDatabase: Effect.Effect<void, DependencyCheckError>
|
|
680
|
+
|
|
681
|
+
const isRetryableStartupFailure = (error: DependencyCheckError) =>
|
|
682
|
+
error.reason === "DnsLookup" ||
|
|
683
|
+
error.reason === "ConnectionRefused" ||
|
|
684
|
+
error.reason === "Timeout"
|
|
685
|
+
|
|
686
|
+
const startupDependencyPolicy = Schedule.exponential("100 millis").pipe(
|
|
687
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(2)))),
|
|
688
|
+
Schedule.both(Schedule.recurs(5)),
|
|
689
|
+
Schedule.both(Schedule.during("20 seconds"))
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
const program = checkDatabase.pipe(
|
|
693
|
+
Effect.retry({
|
|
694
|
+
schedule: startupDependencyPolicy,
|
|
695
|
+
while: isRetryableStartupFailure
|
|
696
|
+
})
|
|
697
|
+
)
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
701
|
+
|
|
702
|
+
### Retry A Duplicate-Safe Deployment Hook
|
|
703
|
+
|
|
704
|
+
Write the Effect code that retries a duplicate-safe deployment hook only for temporary hook failures.
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
708
|
+
|
|
709
|
+
type HookReceipt = unknown
|
|
710
|
+
|
|
711
|
+
type DeploymentHookError = {
|
|
712
|
+
readonly status: number
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
declare const invokeDeploymentHook: (
|
|
716
|
+
request: {
|
|
717
|
+
readonly deploymentId: string
|
|
718
|
+
readonly hookName: string
|
|
719
|
+
readonly idempotencyKey: string
|
|
720
|
+
}
|
|
721
|
+
) => Effect.Effect<HookReceipt, DeploymentHookError>
|
|
722
|
+
|
|
723
|
+
const isRetryableHookError = (error: DeploymentHookError) =>
|
|
724
|
+
error.status === 408 ||
|
|
725
|
+
error.status === 409 ||
|
|
726
|
+
error.status === 429 ||
|
|
727
|
+
error.status >= 500
|
|
728
|
+
|
|
729
|
+
const deploymentHookRetryPolicy = Schedule.exponential("100 millis").pipe(
|
|
730
|
+
Schedule.setInputType<DeploymentHookError>(),
|
|
731
|
+
Schedule.jittered,
|
|
732
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(2)))),
|
|
733
|
+
Schedule.both(Schedule.recurs(5)),
|
|
734
|
+
Schedule.while(({ input }) => isRetryableHookError(input))
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
const program = invokeDeploymentHook({
|
|
738
|
+
deploymentId: "deploy-1",
|
|
739
|
+
hookName: "post-deploy-smoke-test",
|
|
740
|
+
idempotencyKey: "deploy-1:post-deploy-smoke-test"
|
|
741
|
+
}).pipe(
|
|
742
|
+
Effect.retry(deploymentHookRetryPolicy)
|
|
743
|
+
)
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
Caution: the schedule only times retries; the stable idempotency key is what makes duplicate attempts safe.
|
|
747
|
+
|
|
748
|
+
### Retry Infrastructure API Calls
|
|
749
|
+
|
|
750
|
+
Write the Effect code that retries a safe infrastructure API call for timeout, temporary unavailability, or rate limit failures. Reuse the same `clientToken`, use exponential backoff starting at about 100 ms, add a little randomness, try at most 5 more times, stop after about 30 seconds, and do not retry invalid requests or authorization failures.
|
|
751
|
+
|
|
752
|
+
```ts
|
|
753
|
+
import { Effect, Schedule } from "effect"
|
|
754
|
+
|
|
755
|
+
type InfrastructureApiError =
|
|
756
|
+
| { readonly _tag: "ApiTimeout" }
|
|
757
|
+
| { readonly _tag: "ApiUnavailable" }
|
|
758
|
+
| { readonly _tag: "RateLimited" }
|
|
759
|
+
| { readonly _tag: "InvalidRequest" }
|
|
760
|
+
| { readonly _tag: "Unauthorized" }
|
|
761
|
+
| { readonly _tag: "Forbidden" }
|
|
762
|
+
|
|
763
|
+
declare const createSubnet: (
|
|
764
|
+
request: {
|
|
765
|
+
readonly vpcId: string
|
|
766
|
+
readonly cidrBlock: string
|
|
767
|
+
readonly clientToken: string
|
|
768
|
+
}
|
|
769
|
+
) => Effect.Effect<string, InfrastructureApiError>
|
|
770
|
+
|
|
771
|
+
const retryInfrastructureApi = Schedule.exponential("100 millis").pipe(
|
|
772
|
+
Schedule.jittered,
|
|
773
|
+
Schedule.both(Schedule.recurs(5)),
|
|
774
|
+
Schedule.both(Schedule.during("30 seconds"))
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
const program = createSubnet({
|
|
778
|
+
vpcId: "vpc-123",
|
|
779
|
+
cidrBlock: "10.0.8.0/24",
|
|
780
|
+
clientToken: "deploy-1-subnet-10-0-8"
|
|
781
|
+
}).pipe(
|
|
782
|
+
Effect.retry({
|
|
783
|
+
schedule: retryInfrastructureApi,
|
|
784
|
+
while: (error) =>
|
|
785
|
+
error._tag === "ApiTimeout" ||
|
|
786
|
+
error._tag === "ApiUnavailable" ||
|
|
787
|
+
error._tag === "RateLimited"
|
|
788
|
+
})
|
|
789
|
+
)
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
793
|
+
|
|
794
|
+
### Retry Export Generation
|
|
795
|
+
|
|
796
|
+
Write the Effect code that retries idempotent export generation only for temporary database, renderer, or object-storage failures. Use a stable `exportId`, use exponential backoff starting at about 100 ms, add a little randomness, try at most 4 more times, and do not retry invalid requests or permission failures.
|
|
797
|
+
|
|
798
|
+
```ts
|
|
799
|
+
import { Effect, Schedule } from "effect"
|
|
800
|
+
|
|
801
|
+
type ExportFile = unknown
|
|
802
|
+
|
|
803
|
+
type ExportRequest = {
|
|
804
|
+
readonly exportId: string
|
|
805
|
+
readonly accountId: string
|
|
806
|
+
readonly format: "csv" | "json"
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
type ExportError =
|
|
810
|
+
| { readonly _tag: "DatabaseUnavailable" }
|
|
811
|
+
| { readonly _tag: "RendererBusy" }
|
|
812
|
+
| { readonly _tag: "ObjectStorageUnavailable" }
|
|
813
|
+
| { readonly _tag: "InvalidExportRequest" }
|
|
814
|
+
| { readonly _tag: "PermissionDenied" }
|
|
815
|
+
|
|
816
|
+
declare const generateExport: (
|
|
817
|
+
request: ExportRequest
|
|
818
|
+
) => Effect.Effect<ExportFile, ExportError>
|
|
819
|
+
|
|
820
|
+
const isTransientExportError = (error: ExportError): boolean => {
|
|
821
|
+
switch (error._tag) {
|
|
822
|
+
case "DatabaseUnavailable":
|
|
823
|
+
case "RendererBusy":
|
|
824
|
+
case "ObjectStorageUnavailable":
|
|
825
|
+
return true
|
|
826
|
+
case "InvalidExportRequest":
|
|
827
|
+
case "PermissionDenied":
|
|
828
|
+
return false
|
|
829
|
+
default:
|
|
830
|
+
return false
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const retryTransientExportFailures = Schedule.exponential("100 millis").pipe(
|
|
835
|
+
Schedule.jittered,
|
|
836
|
+
Schedule.both(Schedule.recurs(4))
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
const request: ExportRequest = {
|
|
840
|
+
exportId: "export-2026-05-17",
|
|
841
|
+
accountId: "acct-123",
|
|
842
|
+
format: "csv"
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const program = generateExport(request).pipe(
|
|
846
|
+
Effect.retry({
|
|
847
|
+
schedule: retryTransientExportFailures,
|
|
848
|
+
while: isTransientExportError
|
|
849
|
+
})
|
|
850
|
+
)
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
854
|
+
|
|
855
|
+
### Retry Object Storage Uploads
|
|
856
|
+
|
|
857
|
+
Write the Effect code that retries an idempotent object storage upload only for temporary storage failures. Reuse deterministic object identity, add a little randomness so concurrent upload retries do not line up, use exponential backoff starting at about 250 ms, try at most 5 more times, and stop after about 30 seconds.
|
|
858
|
+
|
|
859
|
+
```ts
|
|
860
|
+
import { Effect, Schedule } from "effect"
|
|
861
|
+
|
|
862
|
+
type UploadRequest = {
|
|
863
|
+
readonly bucket: string
|
|
864
|
+
readonly key: string
|
|
865
|
+
readonly body: Uint8Array
|
|
866
|
+
readonly checksumSha256: string
|
|
867
|
+
readonly idempotencyKey: string
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
type UploadError =
|
|
871
|
+
| { readonly reason: "Timeout" }
|
|
872
|
+
| { readonly reason: "Throttled" }
|
|
873
|
+
| { readonly reason: "Unavailable" }
|
|
874
|
+
| { readonly reason: "ChecksumMismatch" }
|
|
875
|
+
| { readonly reason: "Forbidden" }
|
|
876
|
+
| { readonly reason: "BadRequest" }
|
|
877
|
+
|
|
878
|
+
declare const body: Uint8Array
|
|
879
|
+
|
|
880
|
+
declare const uploadObject: (
|
|
881
|
+
request: UploadRequest
|
|
882
|
+
) => Effect.Effect<void, UploadError>
|
|
883
|
+
|
|
884
|
+
const isTransientStorageError = (error: UploadError) =>
|
|
885
|
+
error.reason === "Timeout" ||
|
|
886
|
+
error.reason === "Throttled" ||
|
|
887
|
+
error.reason === "Unavailable"
|
|
888
|
+
|
|
889
|
+
const uploadRetryPolicy = Schedule.exponential("250 millis").pipe(
|
|
890
|
+
Schedule.jittered,
|
|
891
|
+
Schedule.both(Schedule.recurs(5)),
|
|
892
|
+
Schedule.both(Schedule.during("30 seconds"))
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
const program = uploadObject({
|
|
896
|
+
bucket: "reports",
|
|
897
|
+
key: "daily/sha256-demo.json",
|
|
898
|
+
body,
|
|
899
|
+
checksumSha256: "sha256-demo",
|
|
900
|
+
idempotencyKey: "sha256-demo"
|
|
901
|
+
}).pipe(
|
|
902
|
+
Effect.retry({
|
|
903
|
+
schedule: uploadRetryPolicy,
|
|
904
|
+
while: isTransientStorageError
|
|
905
|
+
})
|
|
906
|
+
)
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Caution: the schedule only times retries; the stable idempotency key is what makes duplicate attempts safe.
|
|
910
|
+
|
|
911
|
+
### Retry Import Processing
|
|
912
|
+
|
|
913
|
+
Write the Effect code that retries one idempotent import batch only for storage, database, or enrichment outages. Use exponential backoff starting at about 100 ms, add a little randomness, try at most 4 more times, stop after about 20 seconds, and do not retry malformed input or domain validation failures.
|
|
914
|
+
|
|
915
|
+
```ts
|
|
916
|
+
import { Effect, Schedule } from "effect"
|
|
917
|
+
|
|
918
|
+
type ImportBatch = unknown
|
|
919
|
+
type ImportSummary = unknown
|
|
920
|
+
|
|
921
|
+
type ImportError =
|
|
922
|
+
| { readonly _tag: "StorageTimeout" }
|
|
923
|
+
| { readonly _tag: "DatabaseUnavailable" }
|
|
924
|
+
| { readonly _tag: "EnrichmentUnavailable" }
|
|
925
|
+
| { readonly _tag: "MalformedInput" }
|
|
926
|
+
| { readonly _tag: "ValidationFailed" }
|
|
927
|
+
|
|
928
|
+
declare const processImportBatch: (
|
|
929
|
+
batch: ImportBatch
|
|
930
|
+
) => Effect.Effect<ImportSummary, ImportError>
|
|
931
|
+
|
|
932
|
+
declare const batch: ImportBatch
|
|
933
|
+
|
|
934
|
+
const isTransientImportError = (error: ImportError): boolean =>
|
|
935
|
+
error._tag === "StorageTimeout" ||
|
|
936
|
+
error._tag === "DatabaseUnavailable" ||
|
|
937
|
+
error._tag === "EnrichmentUnavailable"
|
|
938
|
+
|
|
939
|
+
const importRetryPolicy = Schedule.exponential("100 millis").pipe(
|
|
940
|
+
Schedule.jittered,
|
|
941
|
+
Schedule.both(Schedule.recurs(4)),
|
|
942
|
+
Schedule.both(Schedule.during("20 seconds"))
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
const program = processImportBatch(batch).pipe(
|
|
946
|
+
Effect.retry({
|
|
947
|
+
schedule: importRetryPolicy,
|
|
948
|
+
while: isTransientImportError
|
|
949
|
+
})
|
|
950
|
+
)
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
954
|
+
|
|
955
|
+
### Retry Payment Status Fetches
|
|
956
|
+
|
|
957
|
+
Write the Effect code that retries a safe payment status read only for temporary status-fetch failures such as 408, 429, or 5xx responses. Use exponential backoff starting at about 100 ms, add a little randomness, and try at most 5 more times.
|
|
958
|
+
|
|
959
|
+
```ts
|
|
960
|
+
import { Effect, Schedule } from "effect"
|
|
961
|
+
|
|
962
|
+
type PaymentStatus = unknown
|
|
963
|
+
|
|
964
|
+
type PaymentStatusFetchError = {
|
|
965
|
+
readonly status: number
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
declare const fetchPaymentStatus: Effect.Effect<PaymentStatus, PaymentStatusFetchError>
|
|
969
|
+
|
|
970
|
+
const isRetryableStatusFetch = (error: PaymentStatusFetchError) =>
|
|
971
|
+
error.status === 408 || error.status === 429 || error.status >= 500
|
|
972
|
+
|
|
973
|
+
const paymentStatusFetchRetry = Schedule.exponential("100 millis").pipe(
|
|
974
|
+
Schedule.setInputType<PaymentStatusFetchError>(),
|
|
975
|
+
Schedule.jittered,
|
|
976
|
+
Schedule.both(Schedule.recurs(5)),
|
|
977
|
+
Schedule.while(({ input }) => isRetryableStatusFetch(input))
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
const program = fetchPaymentStatus.pipe(
|
|
981
|
+
Effect.retry(paymentStatusFetchRetry)
|
|
982
|
+
)
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
986
|
+
|
|
987
|
+
### Retry Notification Delivery
|
|
988
|
+
|
|
989
|
+
Write the Effect code that retries notification delivery only for timeout or provider-unavailable failures while preserving a stable notification idempotency key. Use exponential backoff starting at about 100 ms, add a little randomness, try at most 5 more times, and do not retry invalid recipients, malformed messages, or unauthorized requests.
|
|
990
|
+
|
|
991
|
+
```ts
|
|
992
|
+
import { Effect, Schedule } from "effect"
|
|
993
|
+
|
|
994
|
+
type Notification = {
|
|
995
|
+
readonly idempotencyKey: string
|
|
996
|
+
readonly recipient: string
|
|
997
|
+
readonly body: string
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
type DeliveryError =
|
|
1001
|
+
| { readonly _tag: "Timeout" }
|
|
1002
|
+
| { readonly _tag: "ProviderUnavailable" }
|
|
1003
|
+
| { readonly _tag: "InvalidRecipient" }
|
|
1004
|
+
| { readonly _tag: "MalformedMessage" }
|
|
1005
|
+
| { readonly _tag: "Unauthorized" }
|
|
1006
|
+
|
|
1007
|
+
declare const sendWithIdempotency: (
|
|
1008
|
+
notification: Notification
|
|
1009
|
+
) => Effect.Effect<void, DeliveryError>
|
|
1010
|
+
|
|
1011
|
+
const isRetryableDeliveryError = (error: DeliveryError): boolean =>
|
|
1012
|
+
error._tag === "Timeout" || error._tag === "ProviderUnavailable"
|
|
1013
|
+
|
|
1014
|
+
const retryTransientDelivery = Schedule.exponential("100 millis").pipe(
|
|
1015
|
+
Schedule.setInputType<DeliveryError>(),
|
|
1016
|
+
Schedule.jittered,
|
|
1017
|
+
Schedule.both(Schedule.recurs(5))
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
const program = sendWithIdempotency({
|
|
1021
|
+
idempotencyKey: "notification-01HZYX8R7P0J9PAW4Q6V7N3QYB",
|
|
1022
|
+
recipient: "user@example.com",
|
|
1023
|
+
body: "Your export is ready."
|
|
1024
|
+
}).pipe(
|
|
1025
|
+
Effect.retry({
|
|
1026
|
+
schedule: retryTransientDelivery,
|
|
1027
|
+
while: isRetryableDeliveryError
|
|
1028
|
+
})
|
|
1029
|
+
)
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
Caution: the schedule only times retries; the stable idempotency key is what makes duplicate attempts safe.
|
|
1033
|
+
|
|
1034
|
+
### Retry An Unstable Remote API
|
|
1035
|
+
|
|
1036
|
+
Write the Effect code for a request-path call to `callRemoteApi`.
|
|
1037
|
+
|
|
1038
|
+
```ts
|
|
1039
|
+
import { Effect, Schedule } from "effect"
|
|
1040
|
+
|
|
1041
|
+
type RemoteApiError =
|
|
1042
|
+
| { readonly _tag: "Timeout" }
|
|
1043
|
+
| { readonly _tag: "ConnectionReset" }
|
|
1044
|
+
| { readonly _tag: "ServiceUnavailable" }
|
|
1045
|
+
| { readonly _tag: "BadRequest" }
|
|
1046
|
+
| { readonly _tag: "Unauthorized" }
|
|
1047
|
+
|
|
1048
|
+
declare const callRemoteApi: Effect.Effect<Response, RemoteApiError>
|
|
1049
|
+
|
|
1050
|
+
const isRetryableRemoteApiError = (error: RemoteApiError): boolean =>
|
|
1051
|
+
error._tag === "Timeout" ||
|
|
1052
|
+
error._tag === "ConnectionReset" ||
|
|
1053
|
+
error._tag === "ServiceUnavailable"
|
|
1054
|
+
|
|
1055
|
+
const remoteApiRetry = Schedule.exponential("100 millis").pipe(
|
|
1056
|
+
Schedule.jittered,
|
|
1057
|
+
Schedule.both(Schedule.recurs(5)),
|
|
1058
|
+
Schedule.both(Schedule.during("10 seconds"))
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
const program = callRemoteApi.pipe(
|
|
1062
|
+
Effect.retry({
|
|
1063
|
+
schedule: remoteApiRetry,
|
|
1064
|
+
while: isRetryableRemoteApiError
|
|
1065
|
+
})
|
|
1066
|
+
)
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
1070
|
+
|
|
1071
|
+
### Retry Queue Reconnection
|
|
1072
|
+
|
|
1073
|
+
Write the Effect code used by a worker during startup to connect to the queue.
|
|
1074
|
+
|
|
1075
|
+
```ts
|
|
1076
|
+
import { Effect, Schedule } from "effect"
|
|
1077
|
+
|
|
1078
|
+
type QueueConnection = unknown
|
|
1079
|
+
|
|
1080
|
+
type QueueConnectError =
|
|
1081
|
+
| { readonly _tag: "DnsFailure" }
|
|
1082
|
+
| { readonly _tag: "ConnectionRefused" }
|
|
1083
|
+
| { readonly _tag: "BrokerUnavailable" }
|
|
1084
|
+
| { readonly _tag: "BadCredentials" }
|
|
1085
|
+
| { readonly _tag: "UnsupportedProtocol" }
|
|
1086
|
+
|
|
1087
|
+
declare const connectQueue: Effect.Effect<QueueConnection, QueueConnectError>
|
|
1088
|
+
|
|
1089
|
+
const isRetryableQueueConnectError = (error: QueueConnectError): boolean =>
|
|
1090
|
+
error._tag === "DnsFailure" ||
|
|
1091
|
+
error._tag === "ConnectionRefused" ||
|
|
1092
|
+
error._tag === "BrokerUnavailable"
|
|
1093
|
+
|
|
1094
|
+
const queueReconnect = Schedule.exponential("100 millis").pipe(
|
|
1095
|
+
Schedule.jittered,
|
|
1096
|
+
Schedule.both(Schedule.recurs(8)),
|
|
1097
|
+
Schedule.both(Schedule.during("1 minute"))
|
|
1098
|
+
)
|
|
1099
|
+
|
|
1100
|
+
const program = connectQueue.pipe(
|
|
1101
|
+
Effect.retry({
|
|
1102
|
+
schedule: queueReconnect,
|
|
1103
|
+
while: isRetryableQueueConnectError
|
|
1104
|
+
})
|
|
1105
|
+
)
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
1109
|
+
|
|
1110
|
+
### Retry Cold-Start Dependencies
|
|
1111
|
+
|
|
1112
|
+
Write the Effect code for a service startup check.
|
|
1113
|
+
|
|
1114
|
+
```ts
|
|
1115
|
+
import { Effect, Schedule } from "effect"
|
|
1116
|
+
|
|
1117
|
+
type DependencyCheckError =
|
|
1118
|
+
| { readonly _tag: "DnsLookup" }
|
|
1119
|
+
| { readonly _tag: "ConnectionRefused" }
|
|
1120
|
+
| { readonly _tag: "Timeout" }
|
|
1121
|
+
| { readonly _tag: "BadCredentials" }
|
|
1122
|
+
| { readonly _tag: "SchemaMismatch" }
|
|
1123
|
+
|
|
1124
|
+
declare const checkDependency: Effect.Effect<void, DependencyCheckError>
|
|
1125
|
+
|
|
1126
|
+
const isRetryableStartupCheckError = (error: DependencyCheckError): boolean =>
|
|
1127
|
+
error._tag === "DnsLookup" ||
|
|
1128
|
+
error._tag === "ConnectionRefused" ||
|
|
1129
|
+
error._tag === "Timeout"
|
|
1130
|
+
|
|
1131
|
+
const dependencyStartup = Schedule.exponential("100 millis").pipe(
|
|
1132
|
+
Schedule.both(Schedule.recurs(6)),
|
|
1133
|
+
Schedule.both(Schedule.during("30 seconds"))
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
const program = checkDependency.pipe(
|
|
1137
|
+
Effect.retry({
|
|
1138
|
+
schedule: dependencyStartup,
|
|
1139
|
+
while: isRetryableStartupCheckError
|
|
1140
|
+
})
|
|
1141
|
+
)
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1145
|
+
|
|
1146
|
+
### Retry With Capped Backoff
|
|
1147
|
+
|
|
1148
|
+
Write the Effect code for calling the search backend.
|
|
1149
|
+
|
|
1150
|
+
```ts
|
|
1151
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
1152
|
+
|
|
1153
|
+
type SearchResult = unknown
|
|
1154
|
+
|
|
1155
|
+
type SearchBackendError =
|
|
1156
|
+
| { readonly _tag: "Timeout" }
|
|
1157
|
+
| { readonly _tag: "Overloaded" }
|
|
1158
|
+
| { readonly _tag: "RejectedQuery" }
|
|
1159
|
+
|
|
1160
|
+
declare const callSearchBackend: Effect.Effect<SearchResult, SearchBackendError>
|
|
1161
|
+
|
|
1162
|
+
const isRetryableSearchBackendError = (error: SearchBackendError): boolean =>
|
|
1163
|
+
error._tag === "Timeout" || error._tag === "Overloaded"
|
|
1164
|
+
|
|
1165
|
+
const cappedBackoff = Schedule.exponential("100 millis").pipe(
|
|
1166
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(5)))),
|
|
1167
|
+
Schedule.both(Schedule.recurs(5))
|
|
1168
|
+
)
|
|
1169
|
+
|
|
1170
|
+
const program = callSearchBackend.pipe(
|
|
1171
|
+
Effect.retry({
|
|
1172
|
+
schedule: cappedBackoff,
|
|
1173
|
+
while: isRetryableSearchBackendError
|
|
1174
|
+
})
|
|
1175
|
+
)
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1179
|
+
|
|
1180
|
+
### Retry HTTP Clients With Jitter
|
|
1181
|
+
|
|
1182
|
+
Write the Effect code for a client request that may be called by many app instances at once.
|
|
1183
|
+
|
|
1184
|
+
```ts
|
|
1185
|
+
import { Effect, Schedule } from "effect"
|
|
1186
|
+
|
|
1187
|
+
type HttpResponse = unknown
|
|
1188
|
+
|
|
1189
|
+
type HttpClientError =
|
|
1190
|
+
| { readonly _tag: "Timeout" }
|
|
1191
|
+
| { readonly _tag: "ConnectionReset" }
|
|
1192
|
+
| { readonly _tag: "ResponseError"; readonly status: number }
|
|
1193
|
+
| { readonly _tag: "InvalidRequest" }
|
|
1194
|
+
|
|
1195
|
+
declare const httpRequest: Effect.Effect<HttpResponse, HttpClientError>
|
|
1196
|
+
|
|
1197
|
+
const isRetryableHttpClientError = (error: HttpClientError): boolean =>
|
|
1198
|
+
error._tag === "Timeout" ||
|
|
1199
|
+
error._tag === "ConnectionReset" ||
|
|
1200
|
+
(error._tag === "ResponseError" && error.status >= 500)
|
|
1201
|
+
|
|
1202
|
+
const httpRetry = Schedule.exponential("100 millis").pipe(
|
|
1203
|
+
Schedule.jittered,
|
|
1204
|
+
Schedule.both(Schedule.recurs(5))
|
|
1205
|
+
)
|
|
1206
|
+
|
|
1207
|
+
const program = httpRequest.pipe(
|
|
1208
|
+
Effect.retry({
|
|
1209
|
+
schedule: httpRetry,
|
|
1210
|
+
while: isRetryableHttpClientError
|
|
1211
|
+
})
|
|
1212
|
+
)
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
1216
|
+
|
|
1217
|
+
### Do Not Retry Validation Errors
|
|
1218
|
+
|
|
1219
|
+
Write the Effect code for submitting the form.
|
|
1220
|
+
|
|
1221
|
+
```ts
|
|
1222
|
+
import { Effect, Schedule } from "effect"
|
|
1223
|
+
|
|
1224
|
+
type SubmitReceipt = unknown
|
|
1225
|
+
|
|
1226
|
+
type SubmitError =
|
|
1227
|
+
| { readonly _tag: "Timeout" }
|
|
1228
|
+
| { readonly _tag: "ServiceUnavailable" }
|
|
1229
|
+
| { readonly _tag: "ValidationError"; readonly field: string }
|
|
1230
|
+
|
|
1231
|
+
declare const submitForm: Effect.Effect<SubmitReceipt, SubmitError>
|
|
1232
|
+
|
|
1233
|
+
const isRetryableSubmitError = (error: SubmitError): boolean =>
|
|
1234
|
+
error._tag === "Timeout" || error._tag === "ServiceUnavailable"
|
|
1235
|
+
|
|
1236
|
+
const program = submitForm.pipe(
|
|
1237
|
+
Effect.retry({
|
|
1238
|
+
schedule: Schedule.exponential("100 millis").pipe(Schedule.both(Schedule.recurs(3))),
|
|
1239
|
+
while: isRetryableSubmitError
|
|
1240
|
+
})
|
|
1241
|
+
)
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
1245
|
+
|
|
1246
|
+
### Retry Only 5xx Responses
|
|
1247
|
+
|
|
1248
|
+
Write the Effect code for a safe resource read.
|
|
1249
|
+
|
|
1250
|
+
```ts
|
|
1251
|
+
import { Effect, Schedule } from "effect"
|
|
1252
|
+
|
|
1253
|
+
type Resource = unknown
|
|
1254
|
+
|
|
1255
|
+
type HttpError =
|
|
1256
|
+
| { readonly _tag: "HttpError"; readonly status: number }
|
|
1257
|
+
| { readonly _tag: "NetworkError" }
|
|
1258
|
+
|
|
1259
|
+
declare const fetchResource: Effect.Effect<Resource, HttpError>
|
|
1260
|
+
|
|
1261
|
+
const isServerError = (error: HttpError): boolean => error._tag === "HttpError" && error.status >= 500
|
|
1262
|
+
|
|
1263
|
+
const program = fetchResource.pipe(
|
|
1264
|
+
Effect.retry({
|
|
1265
|
+
schedule: Schedule.exponential("100 millis").pipe(Schedule.both(Schedule.recurs(3))),
|
|
1266
|
+
while: isServerError
|
|
1267
|
+
})
|
|
1268
|
+
)
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1272
|
+
|
|
1273
|
+
### Retry Safe GET Reads
|
|
1274
|
+
|
|
1275
|
+
Write the Effect code for a safe read path.
|
|
1276
|
+
|
|
1277
|
+
```ts
|
|
1278
|
+
import { Effect, Schedule } from "effect"
|
|
1279
|
+
|
|
1280
|
+
type Resource = unknown
|
|
1281
|
+
|
|
1282
|
+
type GetError =
|
|
1283
|
+
| { readonly _tag: "Timeout"; readonly method: "GET" | "POST" }
|
|
1284
|
+
| { readonly _tag: "ServiceUnavailable"; readonly method: "GET" | "POST" }
|
|
1285
|
+
| { readonly _tag: "BadRequest"; readonly method: "GET" | "POST" }
|
|
1286
|
+
|
|
1287
|
+
declare const getResource: Effect.Effect<Resource, GetError>
|
|
1288
|
+
|
|
1289
|
+
const isRetryableGetError = (error: GetError): boolean =>
|
|
1290
|
+
error.method === "GET" &&
|
|
1291
|
+
(error._tag === "Timeout" || error._tag === "ServiceUnavailable")
|
|
1292
|
+
|
|
1293
|
+
const program = getResource.pipe(
|
|
1294
|
+
Effect.retry({
|
|
1295
|
+
schedule: Schedule.exponential("100 millis").pipe(Schedule.both(Schedule.recurs(3))),
|
|
1296
|
+
while: isRetryableGetError
|
|
1297
|
+
})
|
|
1298
|
+
)
|
|
1299
|
+
```
|
|
1300
|
+
|
|
1301
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1302
|
+
|
|
1303
|
+
### Retry Short Pauses, Then Backoff
|
|
1304
|
+
|
|
1305
|
+
Write the Effect code for a search request where the first failures are often very short blips.
|
|
1306
|
+
|
|
1307
|
+
```ts
|
|
1308
|
+
import { Effect, Schedule } from "effect"
|
|
1309
|
+
|
|
1310
|
+
type SearchBackendError = unknown
|
|
1311
|
+
type SearchResult = unknown
|
|
1312
|
+
|
|
1313
|
+
declare const callSearchBackend: Effect.Effect<SearchResult, SearchBackendError>
|
|
1314
|
+
|
|
1315
|
+
const shortRetries = Schedule.spaced("100 millis").pipe(
|
|
1316
|
+
Schedule.bothLeft(Schedule.recurs(2))
|
|
1317
|
+
)
|
|
1318
|
+
const backoffRetries = Schedule.exponential("250 millis").pipe(
|
|
1319
|
+
Schedule.both(Schedule.recurs(4))
|
|
1320
|
+
)
|
|
1321
|
+
const policy = shortRetries.pipe(Schedule.andThen(backoffRetries))
|
|
1322
|
+
|
|
1323
|
+
const program = callSearchBackend.pipe(Effect.retry(policy))
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1327
|
+
|
|
1328
|
+
### Retry While An Auth Token Is Stale
|
|
1329
|
+
|
|
1330
|
+
Write the Effect code for refreshing an auth token.
|
|
1331
|
+
|
|
1332
|
+
```ts
|
|
1333
|
+
import { Effect, Schedule } from "effect"
|
|
1334
|
+
|
|
1335
|
+
type AuthToken = unknown
|
|
1336
|
+
|
|
1337
|
+
type AuthTokenError =
|
|
1338
|
+
| { readonly _tag: "StaleToken" }
|
|
1339
|
+
| { readonly _tag: "TokenRevoked" }
|
|
1340
|
+
| { readonly _tag: "MalformedToken" }
|
|
1341
|
+
|
|
1342
|
+
declare const refreshAuthToken: Effect.Effect<AuthToken, AuthTokenError>
|
|
1343
|
+
|
|
1344
|
+
const program = refreshAuthToken.pipe(
|
|
1345
|
+
Effect.retry({
|
|
1346
|
+
schedule: Schedule.spaced("500 millis").pipe(
|
|
1347
|
+
Schedule.both(Schedule.recurs(4))
|
|
1348
|
+
),
|
|
1349
|
+
while: (error) => error._tag === "StaleToken"
|
|
1350
|
+
})
|
|
1351
|
+
)
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1355
|
+
|
|
1356
|
+
### Retry While A Circuit Breaker Allows It
|
|
1357
|
+
|
|
1358
|
+
Write the Effect code for calling billing from a checkout flow.
|
|
1359
|
+
|
|
1360
|
+
```ts
|
|
1361
|
+
import { Effect, Schedule } from "effect"
|
|
1362
|
+
|
|
1363
|
+
type BillingReceipt = unknown
|
|
1364
|
+
|
|
1365
|
+
type BillingError =
|
|
1366
|
+
| { readonly _tag: "TransientBillingError" }
|
|
1367
|
+
| { readonly _tag: "RejectedPayment" }
|
|
1368
|
+
|
|
1369
|
+
declare const callBilling: Effect.Effect<BillingReceipt, BillingError>
|
|
1370
|
+
declare const circuitBreakerAllowsRetry: (error: BillingError) => Effect.Effect<boolean>
|
|
1371
|
+
|
|
1372
|
+
const program = callBilling.pipe(
|
|
1373
|
+
Effect.retry({
|
|
1374
|
+
schedule: Schedule.exponential("100 millis").pipe(Schedule.both(Schedule.recurs(5))),
|
|
1375
|
+
while: (error) =>
|
|
1376
|
+
error._tag === "TransientBillingError"
|
|
1377
|
+
? circuitBreakerAllowsRetry(error)
|
|
1378
|
+
: Effect.succeed(false)
|
|
1379
|
+
})
|
|
1380
|
+
)
|
|
1381
|
+
```
|
|
1382
|
+
|
|
1383
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1384
|
+
|
|
1385
|
+
## Polling And State Observation
|
|
1386
|
+
|
|
1387
|
+
In these recipes, non-terminal states are successful values, not failures. `Effect.repeat` runs the effect again after each success and stops when the schedule stops. A real failure from the effect still fails the whole program.
|
|
1388
|
+
|
|
1389
|
+
### Poll Job Status Until Terminal
|
|
1390
|
+
|
|
1391
|
+
Write the Effect code that polls `readJobStatus("job-1")` until the status is `succeeded` or `failed`, or until about 30 seconds pass.
|
|
1392
|
+
|
|
1393
|
+
```ts
|
|
1394
|
+
import { Effect, Schedule } from "effect"
|
|
1395
|
+
|
|
1396
|
+
type JobStatusError = unknown
|
|
1397
|
+
|
|
1398
|
+
type JobStatus =
|
|
1399
|
+
| { readonly state: "queued"; readonly jobId: string }
|
|
1400
|
+
| { readonly state: "running"; readonly jobId: string }
|
|
1401
|
+
| { readonly state: "succeeded"; readonly jobId: string; readonly artifactUrl: string }
|
|
1402
|
+
| { readonly state: "failed"; readonly jobId: string; readonly reason: string }
|
|
1403
|
+
|
|
1404
|
+
declare const readJobStatus: (
|
|
1405
|
+
jobId: string
|
|
1406
|
+
) => Effect.Effect<JobStatus, JobStatusError>
|
|
1407
|
+
|
|
1408
|
+
const isTerminal = (status: JobStatus): boolean => status.state === "succeeded" || status.state === "failed"
|
|
1409
|
+
|
|
1410
|
+
const pollJobStatus = Schedule.spaced("1 second").pipe(
|
|
1411
|
+
Schedule.setInputType<JobStatus>(),
|
|
1412
|
+
Schedule.passthrough,
|
|
1413
|
+
Schedule.while(({ input }) => !isTerminal(input)),
|
|
1414
|
+
Schedule.bothLeft(
|
|
1415
|
+
Schedule.during("30 seconds").pipe(
|
|
1416
|
+
Schedule.setInputType<JobStatus>()
|
|
1417
|
+
)
|
|
1418
|
+
)
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
const program = readJobStatus("job-1").pipe(
|
|
1422
|
+
Effect.repeat(pollJobStatus)
|
|
1423
|
+
)
|
|
1424
|
+
```
|
|
1425
|
+
|
|
1426
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1427
|
+
|
|
1428
|
+
### Poll Platform Readiness
|
|
1429
|
+
|
|
1430
|
+
Write the Effect code that polls readiness snapshots.
|
|
1431
|
+
|
|
1432
|
+
```ts
|
|
1433
|
+
import { Effect, Schedule } from "effect"
|
|
1434
|
+
|
|
1435
|
+
type ReadinessCheckError = unknown
|
|
1436
|
+
type ReadinessSnapshot = unknown
|
|
1437
|
+
|
|
1438
|
+
declare const readPlatformReadiness: Effect.Effect<ReadinessSnapshot, ReadinessCheckError>
|
|
1439
|
+
declare const isTerminalReadiness: (snapshot: ReadinessSnapshot) => boolean
|
|
1440
|
+
|
|
1441
|
+
const readinessPolling = Schedule.spaced("1 second").pipe(
|
|
1442
|
+
Schedule.setInputType<ReadinessSnapshot>(),
|
|
1443
|
+
Schedule.passthrough,
|
|
1444
|
+
Schedule.while(({ input }) => !isTerminalReadiness(input)),
|
|
1445
|
+
Schedule.bothLeft(Schedule.during("30 seconds")),
|
|
1446
|
+
Schedule.bothLeft(Schedule.recurs(30))
|
|
1447
|
+
)
|
|
1448
|
+
|
|
1449
|
+
const program = readPlatformReadiness.pipe(
|
|
1450
|
+
Effect.repeat(readinessPolling)
|
|
1451
|
+
)
|
|
1452
|
+
```
|
|
1453
|
+
|
|
1454
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1455
|
+
|
|
1456
|
+
### Poll Rollout Status With Jitter
|
|
1457
|
+
|
|
1458
|
+
Write the Effect code that polls a rollout about once per second while it is `running`. Add a little randomness so many status watchers do not poll at the same instant. Stop on `succeeded` or `failed`; if it is still running after about 2 minutes, return the latest status.
|
|
1459
|
+
|
|
1460
|
+
```ts
|
|
1461
|
+
import { Effect, Schedule } from "effect"
|
|
1462
|
+
|
|
1463
|
+
type StatusReadError = unknown
|
|
1464
|
+
|
|
1465
|
+
type RolloutStatus =
|
|
1466
|
+
| { readonly state: "running" }
|
|
1467
|
+
| { readonly state: "succeeded" }
|
|
1468
|
+
| { readonly state: "failed"; readonly reason: string }
|
|
1469
|
+
|
|
1470
|
+
declare const readRolloutStatus: (
|
|
1471
|
+
rolloutId: string
|
|
1472
|
+
) => Effect.Effect<RolloutStatus, StatusReadError>
|
|
1473
|
+
|
|
1474
|
+
const pollRolloutStatus = Schedule.spaced("1 second").pipe(
|
|
1475
|
+
Schedule.jittered,
|
|
1476
|
+
Schedule.setInputType<RolloutStatus>(),
|
|
1477
|
+
Schedule.passthrough,
|
|
1478
|
+
Schedule.while(({ input }) => input.state === "running"),
|
|
1479
|
+
Schedule.bothLeft(Schedule.during("2 minutes"))
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
const program = readRolloutStatus("rollout-42").pipe(
|
|
1483
|
+
Effect.repeat(pollRolloutStatus)
|
|
1484
|
+
)
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1488
|
+
|
|
1489
|
+
### Poll ETL Status
|
|
1490
|
+
|
|
1491
|
+
Write the Effect code that polls ETL status until `succeeded`, `failed`, or `canceled`, preserving the final observed status. Check about every 5 seconds and stop waiting after about 5 minutes.
|
|
1492
|
+
|
|
1493
|
+
```ts
|
|
1494
|
+
import { Effect, Schedule } from "effect"
|
|
1495
|
+
|
|
1496
|
+
type StatusReadError = unknown
|
|
1497
|
+
|
|
1498
|
+
type EtlStatus =
|
|
1499
|
+
| { readonly state: "running" }
|
|
1500
|
+
| { readonly state: "succeeded" }
|
|
1501
|
+
| { readonly state: "failed"; readonly reason: string }
|
|
1502
|
+
| { readonly state: "canceled" }
|
|
1503
|
+
|
|
1504
|
+
declare const readEtlStatus: (
|
|
1505
|
+
runId: string
|
|
1506
|
+
) => Effect.Effect<EtlStatus, StatusReadError>
|
|
1507
|
+
|
|
1508
|
+
const isTerminalEtlStatus = (status: EtlStatus): boolean =>
|
|
1509
|
+
status.state === "succeeded" ||
|
|
1510
|
+
status.state === "failed" ||
|
|
1511
|
+
status.state === "canceled"
|
|
1512
|
+
|
|
1513
|
+
const pollEtlStatusBudget = Schedule.spaced("5 seconds").pipe(
|
|
1514
|
+
Schedule.setInputType<EtlStatus>(),
|
|
1515
|
+
Schedule.passthrough,
|
|
1516
|
+
Schedule.while(({ input }) => !isTerminalEtlStatus(input)),
|
|
1517
|
+
Schedule.bothLeft(Schedule.during("5 minutes")),
|
|
1518
|
+
Schedule.bothLeft(Schedule.recurs(60))
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
const program = readEtlStatus("etl-run-7").pipe(
|
|
1522
|
+
Effect.repeat(pollEtlStatusBudget)
|
|
1523
|
+
)
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1527
|
+
|
|
1528
|
+
### Poll Payment Settlement
|
|
1529
|
+
|
|
1530
|
+
Write the Effect code that polls successful settlement statuses.
|
|
1531
|
+
|
|
1532
|
+
```ts
|
|
1533
|
+
import { Effect, Schedule } from "effect"
|
|
1534
|
+
|
|
1535
|
+
type SettlementError = unknown
|
|
1536
|
+
|
|
1537
|
+
type SettlementStatus =
|
|
1538
|
+
| { readonly _tag: "Pending" }
|
|
1539
|
+
| { readonly _tag: "Processing" }
|
|
1540
|
+
| { readonly _tag: "Settled" }
|
|
1541
|
+
| { readonly _tag: "Declined" }
|
|
1542
|
+
|
|
1543
|
+
declare const fetchSettlementStatus: Effect.Effect<SettlementStatus, SettlementError>
|
|
1544
|
+
|
|
1545
|
+
const isOpen = (status: SettlementStatus) => status._tag === "Pending" || status._tag === "Processing"
|
|
1546
|
+
|
|
1547
|
+
const pollOpenSettlements = Schedule.spaced("1 second").pipe(
|
|
1548
|
+
Schedule.setInputType<SettlementStatus>(),
|
|
1549
|
+
Schedule.passthrough,
|
|
1550
|
+
Schedule.while(({ input }) => isOpen(input)),
|
|
1551
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1552
|
+
)
|
|
1553
|
+
|
|
1554
|
+
const program = fetchSettlementStatus.pipe(
|
|
1555
|
+
Effect.repeat(pollOpenSettlements)
|
|
1556
|
+
)
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1560
|
+
|
|
1561
|
+
### Poll Order Fulfillment
|
|
1562
|
+
|
|
1563
|
+
Write the Effect code that polls fulfillment statuses.
|
|
1564
|
+
|
|
1565
|
+
```ts
|
|
1566
|
+
import { Effect, Schedule } from "effect"
|
|
1567
|
+
|
|
1568
|
+
type FulfillmentReadError = unknown
|
|
1569
|
+
|
|
1570
|
+
type FulfillmentStatus =
|
|
1571
|
+
| { readonly state: "preparing" }
|
|
1572
|
+
| { readonly state: "shipped" }
|
|
1573
|
+
| { readonly state: "delivered" }
|
|
1574
|
+
| { readonly state: "canceled"; readonly reason: string }
|
|
1575
|
+
|
|
1576
|
+
declare const readFulfillmentStatus: Effect.Effect<FulfillmentStatus, FulfillmentReadError>
|
|
1577
|
+
|
|
1578
|
+
const isTerminalFulfillment = (status: FulfillmentStatus) => status.state === "delivered" || status.state === "canceled"
|
|
1579
|
+
|
|
1580
|
+
const userFacingPolling = Schedule.spaced("1 second").pipe(
|
|
1581
|
+
Schedule.setInputType<FulfillmentStatus>(),
|
|
1582
|
+
Schedule.passthrough,
|
|
1583
|
+
Schedule.while(({ input }) => !isTerminalFulfillment(input)),
|
|
1584
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1585
|
+
)
|
|
1586
|
+
|
|
1587
|
+
const program = readFulfillmentStatus.pipe(
|
|
1588
|
+
Effect.repeat(userFacingPolling)
|
|
1589
|
+
)
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1593
|
+
|
|
1594
|
+
### Poll A Background Job
|
|
1595
|
+
|
|
1596
|
+
Write the Effect code used by an API endpoint after it starts a background job.
|
|
1597
|
+
|
|
1598
|
+
```ts
|
|
1599
|
+
import { Effect, Schedule } from "effect"
|
|
1600
|
+
|
|
1601
|
+
type JobStatusError = unknown
|
|
1602
|
+
|
|
1603
|
+
type JobStatus =
|
|
1604
|
+
| { readonly state: "queued"; readonly jobId: string }
|
|
1605
|
+
| { readonly state: "running"; readonly jobId: string }
|
|
1606
|
+
| { readonly state: "done"; readonly jobId: string }
|
|
1607
|
+
| { readonly state: "failed"; readonly jobId: string; readonly reason: string }
|
|
1608
|
+
|
|
1609
|
+
declare const jobId: string
|
|
1610
|
+
declare const readJobStatus: (jobId: string) => Effect.Effect<JobStatus, JobStatusError>
|
|
1611
|
+
|
|
1612
|
+
const pollJob = Schedule.spaced("1 second").pipe(
|
|
1613
|
+
Schedule.setInputType<JobStatus>(),
|
|
1614
|
+
Schedule.passthrough,
|
|
1615
|
+
Schedule.while(({ input }) => input.state === "queued" || input.state === "running"),
|
|
1616
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1617
|
+
)
|
|
1618
|
+
|
|
1619
|
+
const program = readJobStatus(jobId).pipe(Effect.repeat(pollJob))
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1623
|
+
|
|
1624
|
+
### Poll A Payment Until Settled
|
|
1625
|
+
|
|
1626
|
+
Write the Effect code used after submitting a payment.
|
|
1627
|
+
|
|
1628
|
+
```ts
|
|
1629
|
+
import { Effect, Schedule } from "effect"
|
|
1630
|
+
|
|
1631
|
+
type PaymentReadError = unknown
|
|
1632
|
+
|
|
1633
|
+
type PaymentStatus =
|
|
1634
|
+
| { readonly _tag: "Pending" }
|
|
1635
|
+
| { readonly _tag: "Processing" }
|
|
1636
|
+
| { readonly _tag: "Settled" }
|
|
1637
|
+
| { readonly _tag: "Declined" }
|
|
1638
|
+
|
|
1639
|
+
declare const paymentId: string
|
|
1640
|
+
declare const readPaymentStatus: (paymentId: string) => Effect.Effect<PaymentStatus, PaymentReadError>
|
|
1641
|
+
|
|
1642
|
+
const pollPayment = Schedule.spaced("1 second").pipe(
|
|
1643
|
+
Schedule.setInputType<PaymentStatus>(),
|
|
1644
|
+
Schedule.passthrough,
|
|
1645
|
+
Schedule.while(({ input }) => input._tag === "Pending" || input._tag === "Processing"),
|
|
1646
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1647
|
+
)
|
|
1648
|
+
|
|
1649
|
+
const program = readPaymentStatus(paymentId).pipe(Effect.repeat(pollPayment))
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1653
|
+
|
|
1654
|
+
### Poll An Export Until Ready
|
|
1655
|
+
|
|
1656
|
+
Write the Effect code used after requesting a report export.
|
|
1657
|
+
|
|
1658
|
+
```ts
|
|
1659
|
+
import { Effect, Schedule } from "effect"
|
|
1660
|
+
|
|
1661
|
+
type ExportStatusError = unknown
|
|
1662
|
+
|
|
1663
|
+
type ExportStatus =
|
|
1664
|
+
| { readonly state: "queued" }
|
|
1665
|
+
| { readonly state: "running" }
|
|
1666
|
+
| { readonly state: "ready"; readonly url: string }
|
|
1667
|
+
| { readonly state: "failed"; readonly reason: string }
|
|
1668
|
+
|
|
1669
|
+
declare const exportId: string
|
|
1670
|
+
declare const readExportStatus: (exportId: string) => Effect.Effect<ExportStatus, ExportStatusError>
|
|
1671
|
+
|
|
1672
|
+
const pollExport = Schedule.spaced("2 seconds").pipe(
|
|
1673
|
+
Schedule.setInputType<ExportStatus>(),
|
|
1674
|
+
Schedule.passthrough,
|
|
1675
|
+
Schedule.while(({ input }) => input.state === "queued" || input.state === "running"),
|
|
1676
|
+
Schedule.bothLeft(Schedule.during("2 minutes"))
|
|
1677
|
+
)
|
|
1678
|
+
|
|
1679
|
+
const program = readExportStatus(exportId).pipe(Effect.repeat(pollExport))
|
|
1680
|
+
```
|
|
1681
|
+
|
|
1682
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1683
|
+
|
|
1684
|
+
### Poll Cloud Provisioning
|
|
1685
|
+
|
|
1686
|
+
Write the Effect code used after creating cloud infrastructure.
|
|
1687
|
+
|
|
1688
|
+
```ts
|
|
1689
|
+
import { Effect, Schedule } from "effect"
|
|
1690
|
+
|
|
1691
|
+
type ProvisioningReadError = unknown
|
|
1692
|
+
|
|
1693
|
+
type ProvisioningStatus =
|
|
1694
|
+
| { readonly state: "creating" }
|
|
1695
|
+
| { readonly state: "updating" }
|
|
1696
|
+
| { readonly state: "ready" }
|
|
1697
|
+
| { readonly state: "failed"; readonly reason: string }
|
|
1698
|
+
|
|
1699
|
+
declare const resourceId: string
|
|
1700
|
+
declare const readProvisioningStatus: (resourceId: string) => Effect.Effect<ProvisioningStatus, ProvisioningReadError>
|
|
1701
|
+
|
|
1702
|
+
const pollProvisioning = Schedule.spaced("5 seconds").pipe(
|
|
1703
|
+
Schedule.setInputType<ProvisioningStatus>(),
|
|
1704
|
+
Schedule.passthrough,
|
|
1705
|
+
Schedule.while(({ input }) => input.state === "creating" || input.state === "updating"),
|
|
1706
|
+
Schedule.bothLeft(Schedule.during("5 minutes"))
|
|
1707
|
+
)
|
|
1708
|
+
|
|
1709
|
+
const program = readProvisioningStatus(resourceId).pipe(Effect.repeat(pollProvisioning))
|
|
1710
|
+
```
|
|
1711
|
+
|
|
1712
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1713
|
+
|
|
1714
|
+
### Poll Until Completed
|
|
1715
|
+
|
|
1716
|
+
Write the Effect code used by a client that waits briefly for completion.
|
|
1717
|
+
|
|
1718
|
+
```ts
|
|
1719
|
+
import { Effect, Schedule } from "effect"
|
|
1720
|
+
|
|
1721
|
+
type StatusReadError = unknown
|
|
1722
|
+
|
|
1723
|
+
type Status = "Queued" | "Running" | "Completed"
|
|
1724
|
+
|
|
1725
|
+
declare const id: string
|
|
1726
|
+
declare const readStatus: (id: string) => Effect.Effect<Status, StatusReadError>
|
|
1727
|
+
|
|
1728
|
+
const untilCompleted = Schedule.spaced("1 second").pipe(
|
|
1729
|
+
Schedule.setInputType<Status>(),
|
|
1730
|
+
Schedule.passthrough,
|
|
1731
|
+
Schedule.while(({ input }) => input !== "Completed"),
|
|
1732
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1733
|
+
)
|
|
1734
|
+
|
|
1735
|
+
const program = readStatus(id).pipe(Effect.repeat(untilCompleted))
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1739
|
+
|
|
1740
|
+
### Repeat Until A Condition Is True
|
|
1741
|
+
|
|
1742
|
+
Write the Effect code used by a command-line tool waiting for work to complete.
|
|
1743
|
+
|
|
1744
|
+
```ts
|
|
1745
|
+
import { Effect, Schedule } from "effect"
|
|
1746
|
+
|
|
1747
|
+
type StatusCheckError = unknown
|
|
1748
|
+
|
|
1749
|
+
type Status =
|
|
1750
|
+
| { readonly _tag: "Working" }
|
|
1751
|
+
| { readonly _tag: "Complete" }
|
|
1752
|
+
|
|
1753
|
+
declare const checkStatus: Effect.Effect<Status, StatusCheckError>
|
|
1754
|
+
declare const isComplete: (status: Status) => boolean
|
|
1755
|
+
|
|
1756
|
+
const untilComplete = Schedule.spaced("1 second").pipe(
|
|
1757
|
+
Schedule.setInputType<Status>(),
|
|
1758
|
+
Schedule.passthrough,
|
|
1759
|
+
Schedule.while(({ input }) => !isComplete(input))
|
|
1760
|
+
)
|
|
1761
|
+
|
|
1762
|
+
const program = checkStatus.pipe(Effect.repeat(untilComplete))
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1765
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1766
|
+
|
|
1767
|
+
### Repeat While Work Remains
|
|
1768
|
+
|
|
1769
|
+
Write the Effect code for draining batches from a backlog.
|
|
1770
|
+
|
|
1771
|
+
```ts
|
|
1772
|
+
import { Effect, Schedule } from "effect"
|
|
1773
|
+
|
|
1774
|
+
type BatchError = unknown
|
|
1775
|
+
|
|
1776
|
+
type BatchResult = {
|
|
1777
|
+
readonly processed: number
|
|
1778
|
+
readonly remaining: number
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
declare const processBatch: Effect.Effect<BatchResult, BatchError>
|
|
1782
|
+
|
|
1783
|
+
const whileWorkRemains = Schedule.spaced("1 second").pipe(
|
|
1784
|
+
Schedule.setInputType<BatchResult>(),
|
|
1785
|
+
Schedule.passthrough,
|
|
1786
|
+
Schedule.while(({ input }) => input.remaining > 0)
|
|
1787
|
+
)
|
|
1788
|
+
|
|
1789
|
+
const program = processBatch.pipe(Effect.repeat(whileWorkRemains))
|
|
1790
|
+
```
|
|
1791
|
+
|
|
1792
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1793
|
+
|
|
1794
|
+
### Poll Until A Resource Exists
|
|
1795
|
+
|
|
1796
|
+
Write the Effect code that waits for a just-created resource to become visible.
|
|
1797
|
+
|
|
1798
|
+
```ts
|
|
1799
|
+
import { Effect, Schedule } from "effect"
|
|
1800
|
+
|
|
1801
|
+
type LookupError = unknown
|
|
1802
|
+
type Resource = unknown
|
|
1803
|
+
|
|
1804
|
+
type ResourceLookup =
|
|
1805
|
+
| { readonly _tag: "Missing" }
|
|
1806
|
+
| { readonly _tag: "Found"; readonly resource: Resource }
|
|
1807
|
+
|
|
1808
|
+
declare const lookupResource: (id: string) => Effect.Effect<ResourceLookup, LookupError>
|
|
1809
|
+
declare const id: string
|
|
1810
|
+
|
|
1811
|
+
const untilResourceExists = Schedule.spaced("1 second").pipe(
|
|
1812
|
+
Schedule.setInputType<ResourceLookup>(),
|
|
1813
|
+
Schedule.passthrough,
|
|
1814
|
+
Schedule.while(({ input }) => input._tag === "Missing"),
|
|
1815
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1816
|
+
)
|
|
1817
|
+
|
|
1818
|
+
const program = lookupResource(id).pipe(Effect.repeat(untilResourceExists))
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1822
|
+
|
|
1823
|
+
### Poll Until A Cache Entry Appears
|
|
1824
|
+
|
|
1825
|
+
Write the Effect code that waits for a cache entry written by another process.
|
|
1826
|
+
|
|
1827
|
+
```ts
|
|
1828
|
+
import { Effect, Schedule } from "effect"
|
|
1829
|
+
|
|
1830
|
+
type CacheReadError = unknown
|
|
1831
|
+
type CachedValue = unknown
|
|
1832
|
+
|
|
1833
|
+
type CacheLookup =
|
|
1834
|
+
| { readonly _tag: "Miss" }
|
|
1835
|
+
| { readonly _tag: "Hit"; readonly value: CachedValue }
|
|
1836
|
+
|
|
1837
|
+
declare const readCacheEntry: (key: string) => Effect.Effect<CacheLookup, CacheReadError>
|
|
1838
|
+
declare const key: string
|
|
1839
|
+
|
|
1840
|
+
const untilCacheHit = Schedule.spaced("500 millis").pipe(
|
|
1841
|
+
Schedule.setInputType<CacheLookup>(),
|
|
1842
|
+
Schedule.passthrough,
|
|
1843
|
+
Schedule.while(({ input }) => input._tag === "Miss"),
|
|
1844
|
+
Schedule.bothLeft(Schedule.during("10 seconds"))
|
|
1845
|
+
)
|
|
1846
|
+
|
|
1847
|
+
const program = readCacheEntry(key).pipe(Effect.repeat(untilCacheHit))
|
|
1848
|
+
```
|
|
1849
|
+
|
|
1850
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1851
|
+
|
|
1852
|
+
### Poll Until Replication Catches Up
|
|
1853
|
+
|
|
1854
|
+
Write the Effect code used after writing to the primary database.
|
|
1855
|
+
|
|
1856
|
+
```ts
|
|
1857
|
+
import { Effect, Schedule } from "effect"
|
|
1858
|
+
|
|
1859
|
+
type ReplicationReadError = unknown
|
|
1860
|
+
|
|
1861
|
+
type ReplicationLag = {
|
|
1862
|
+
readonly behindBy: number
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
declare const readReplicationLag: Effect.Effect<ReplicationLag, ReplicationReadError>
|
|
1866
|
+
|
|
1867
|
+
const untilCaughtUp = Schedule.spaced("1 second").pipe(
|
|
1868
|
+
Schedule.setInputType<ReplicationLag>(),
|
|
1869
|
+
Schedule.passthrough,
|
|
1870
|
+
Schedule.while(({ input }) => input.behindBy > 0),
|
|
1871
|
+
Schedule.bothLeft(Schedule.during("1 minute"))
|
|
1872
|
+
)
|
|
1873
|
+
|
|
1874
|
+
const program = readReplicationLag.pipe(Effect.repeat(untilCaughtUp))
|
|
1875
|
+
```
|
|
1876
|
+
|
|
1877
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1878
|
+
|
|
1879
|
+
### Poll With An Interval And Deadline
|
|
1880
|
+
|
|
1881
|
+
Write the Effect code for a status reader that should keep checking briefly.
|
|
1882
|
+
|
|
1883
|
+
```ts
|
|
1884
|
+
import { Effect, Schedule } from "effect"
|
|
1885
|
+
|
|
1886
|
+
type Status = unknown
|
|
1887
|
+
type StatusReadError = unknown
|
|
1888
|
+
|
|
1889
|
+
declare const readStatus: Effect.Effect<Status, StatusReadError>
|
|
1890
|
+
|
|
1891
|
+
const polling = Schedule.spaced("1 second").pipe(
|
|
1892
|
+
Schedule.passthrough,
|
|
1893
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1894
|
+
)
|
|
1895
|
+
|
|
1896
|
+
const program = readStatus.pipe(Effect.repeat(polling))
|
|
1897
|
+
```
|
|
1898
|
+
|
|
1899
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1900
|
+
|
|
1901
|
+
### Return A Graceful Poll Timeout
|
|
1902
|
+
|
|
1903
|
+
Write the Effect code for an API endpoint that waits briefly for work to finish.
|
|
1904
|
+
|
|
1905
|
+
```ts
|
|
1906
|
+
import { Effect, Schedule } from "effect"
|
|
1907
|
+
|
|
1908
|
+
type Result = unknown
|
|
1909
|
+
type StatusReadError = unknown
|
|
1910
|
+
|
|
1911
|
+
type Status =
|
|
1912
|
+
| { readonly _tag: "Running" }
|
|
1913
|
+
| { readonly _tag: "Finished"; readonly value: Result }
|
|
1914
|
+
|
|
1915
|
+
declare const readStatus: Effect.Effect<Status, StatusReadError>
|
|
1916
|
+
|
|
1917
|
+
const polling = Schedule.spaced("1 second").pipe(
|
|
1918
|
+
Schedule.setInputType<Status>(),
|
|
1919
|
+
Schedule.passthrough,
|
|
1920
|
+
Schedule.while(({ input }) => input._tag === "Running"),
|
|
1921
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
1922
|
+
)
|
|
1923
|
+
|
|
1924
|
+
const program = readStatus.pipe(
|
|
1925
|
+
Effect.repeat(polling),
|
|
1926
|
+
Effect.flatMap((status) =>
|
|
1927
|
+
status._tag === "Running"
|
|
1928
|
+
? Effect.fail({ _tag: "TimedOut", latest: status } as const)
|
|
1929
|
+
: Effect.succeed(status)
|
|
1930
|
+
)
|
|
1931
|
+
)
|
|
1932
|
+
```
|
|
1933
|
+
|
|
1934
|
+
Explanation: exhausting the schedule is not a failure by itself. `Effect.repeat` returns the latest schedule output, so the final `flatMap` turns an unfinished latest status into a domain-specific timeout.
|
|
1935
|
+
|
|
1936
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1937
|
+
|
|
1938
|
+
### Poll Fast, Then Slow
|
|
1939
|
+
|
|
1940
|
+
Write the Effect code used immediately after starting a workflow.
|
|
1941
|
+
|
|
1942
|
+
```ts
|
|
1943
|
+
import { Effect, Schedule } from "effect"
|
|
1944
|
+
|
|
1945
|
+
type WorkflowStatus = unknown
|
|
1946
|
+
type WorkflowStatusError = unknown
|
|
1947
|
+
|
|
1948
|
+
declare const readWorkflowStatus: Effect.Effect<WorkflowStatus, WorkflowStatusError>
|
|
1949
|
+
|
|
1950
|
+
const fastThenSlow = Schedule.spaced("250 millis").pipe(
|
|
1951
|
+
Schedule.bothLeft(Schedule.recurs(8))
|
|
1952
|
+
)
|
|
1953
|
+
const slowPolling = Schedule.spaced("2 seconds")
|
|
1954
|
+
const polling = Schedule.andThen(fastThenSlow, slowPolling).pipe(
|
|
1955
|
+
Schedule.setInputType<WorkflowStatus>(),
|
|
1956
|
+
Schedule.passthrough
|
|
1957
|
+
)
|
|
1958
|
+
|
|
1959
|
+
const program = readWorkflowStatus.pipe(Effect.repeat(polling))
|
|
1960
|
+
```
|
|
1961
|
+
|
|
1962
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
1963
|
+
|
|
1964
|
+
### Repeat Until An Effectful Predicate Stops
|
|
1965
|
+
|
|
1966
|
+
Write the Effect code for a startup readiness probe.
|
|
1967
|
+
|
|
1968
|
+
```ts
|
|
1969
|
+
import { Effect, Schedule } from "effect"
|
|
1970
|
+
|
|
1971
|
+
type ProbeError = unknown
|
|
1972
|
+
|
|
1973
|
+
type ServiceProbeResult =
|
|
1974
|
+
| { readonly _tag: "Starting" }
|
|
1975
|
+
| { readonly _tag: "Ready" }
|
|
1976
|
+
| { readonly _tag: "Failed" }
|
|
1977
|
+
|
|
1978
|
+
declare const probeService: Effect.Effect<ServiceProbeResult, ProbeError>
|
|
1979
|
+
declare const recordReadinessProbe: (result: ServiceProbeResult) => Effect.Effect<void>
|
|
1980
|
+
|
|
1981
|
+
const program = probeService.pipe(
|
|
1982
|
+
Effect.repeat({
|
|
1983
|
+
schedule: Schedule.spaced("1 second").pipe(
|
|
1984
|
+
Schedule.both(Schedule.recurs(30))
|
|
1985
|
+
),
|
|
1986
|
+
until: (result) =>
|
|
1987
|
+
recordReadinessProbe(result).pipe(
|
|
1988
|
+
Effect.map(() => result._tag === "Ready" || result._tag === "Failed")
|
|
1989
|
+
)
|
|
1990
|
+
})
|
|
1991
|
+
)
|
|
1992
|
+
```
|
|
1993
|
+
|
|
1994
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
1995
|
+
|
|
1996
|
+
## Repeat And Background Work
|
|
1997
|
+
|
|
1998
|
+
### Repeat Failed-Record Reprocessing
|
|
1999
|
+
|
|
2000
|
+
Write the Effect code for a repair job that reprocesses failed records in passes. After the first successful pass, run at most 3 more passes, waiting about 30 seconds between passes.
|
|
2001
|
+
|
|
2002
|
+
```ts
|
|
2003
|
+
import { Effect, Schedule } from "effect"
|
|
2004
|
+
|
|
2005
|
+
type ReprocessBatchError = unknown
|
|
2006
|
+
|
|
2007
|
+
declare const reprocessFailedBatch: Effect.Effect<void, ReprocessBatchError>
|
|
2008
|
+
|
|
2009
|
+
const reprocessingCadence = Schedule.spaced("30 seconds").pipe(
|
|
2010
|
+
Schedule.take(3)
|
|
2011
|
+
)
|
|
2012
|
+
|
|
2013
|
+
const program = reprocessFailedBatch.pipe(
|
|
2014
|
+
Effect.repeat(reprocessingCadence)
|
|
2015
|
+
)
|
|
2016
|
+
```
|
|
2017
|
+
|
|
2018
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2019
|
+
|
|
2020
|
+
### Repeat CRM Sync
|
|
2021
|
+
|
|
2022
|
+
Write the Effect code that repeats successful CRM sync passes, waiting about 5 minutes between completed passes. Return the latest successful sync summary. Failed CRM requests should fail the program; do not turn this into retry logic.
|
|
2023
|
+
|
|
2024
|
+
```ts
|
|
2025
|
+
import { Effect, Schedule } from "effect"
|
|
2026
|
+
|
|
2027
|
+
type SyncError = unknown
|
|
2028
|
+
type SyncSummary = unknown
|
|
2029
|
+
|
|
2030
|
+
declare const syncCrmOnce: Effect.Effect<SyncSummary, SyncError>
|
|
2031
|
+
|
|
2032
|
+
const crmSyncCadence = Schedule.spaced("5 minutes").pipe(
|
|
2033
|
+
Schedule.setInputType<SyncSummary>(),
|
|
2034
|
+
Schedule.passthrough
|
|
2035
|
+
)
|
|
2036
|
+
|
|
2037
|
+
const program = syncCrmOnce.pipe(
|
|
2038
|
+
Effect.repeat(crmSyncCadence)
|
|
2039
|
+
)
|
|
2040
|
+
```
|
|
2041
|
+
|
|
2042
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
2043
|
+
|
|
2044
|
+
### Jitter Periodic Cache Refreshes
|
|
2045
|
+
|
|
2046
|
+
This service runs on many instances. Write the Effect code that refreshes the cache in the background after each successful refresh.
|
|
2047
|
+
|
|
2048
|
+
```ts
|
|
2049
|
+
import { Effect, Schedule } from "effect"
|
|
2050
|
+
|
|
2051
|
+
type CacheRefreshError = unknown
|
|
2052
|
+
|
|
2053
|
+
declare const refreshCache: Effect.Effect<void, CacheRefreshError>
|
|
2054
|
+
|
|
2055
|
+
const refreshCadence = Schedule.spaced("1 minute").pipe(
|
|
2056
|
+
Schedule.jittered
|
|
2057
|
+
)
|
|
2058
|
+
|
|
2059
|
+
const program = refreshCache.pipe(Effect.repeat(refreshCadence))
|
|
2060
|
+
```
|
|
2061
|
+
|
|
2062
|
+
Caution: jitter spreads delay across callers; it does not add a retry limit.
|
|
2063
|
+
|
|
2064
|
+
### Slow A Worker Loop
|
|
2065
|
+
|
|
2066
|
+
Write the Effect code for a background worker loop.
|
|
2067
|
+
|
|
2068
|
+
```ts
|
|
2069
|
+
import { Effect, Schedule } from "effect"
|
|
2070
|
+
|
|
2071
|
+
type WorkerError = unknown
|
|
2072
|
+
|
|
2073
|
+
type WorkerIteration =
|
|
2074
|
+
| { readonly _tag: "Processed"; readonly count: number }
|
|
2075
|
+
| { readonly _tag: "Idle" }
|
|
2076
|
+
|
|
2077
|
+
declare const workerIteration: Effect.Effect<WorkerIteration, WorkerError>
|
|
2078
|
+
|
|
2079
|
+
const workerCadence = Schedule.spaced("500 millis")
|
|
2080
|
+
|
|
2081
|
+
const program = workerIteration.pipe(
|
|
2082
|
+
Effect.repeat(workerCadence)
|
|
2083
|
+
)
|
|
2084
|
+
```
|
|
2085
|
+
|
|
2086
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2087
|
+
|
|
2088
|
+
### Drain A Queue Slowly
|
|
2089
|
+
|
|
2090
|
+
Write the Effect code for a maintenance process that drains queued work.
|
|
2091
|
+
|
|
2092
|
+
```ts
|
|
2093
|
+
import { Effect, Schedule } from "effect"
|
|
2094
|
+
|
|
2095
|
+
type DrainSummary = unknown
|
|
2096
|
+
type QueueDrainError = unknown
|
|
2097
|
+
|
|
2098
|
+
declare const drainQueueOnce: Effect.Effect<DrainSummary, QueueDrainError>
|
|
2099
|
+
|
|
2100
|
+
const drainCadence = Schedule.spaced("1 second")
|
|
2101
|
+
const program = drainQueueOnce.pipe(Effect.repeat(drainCadence))
|
|
2102
|
+
```
|
|
2103
|
+
|
|
2104
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2105
|
+
|
|
2106
|
+
### Observe Repeat Counts
|
|
2107
|
+
|
|
2108
|
+
Write the Effect code for a metrics flush loop that stops after a small number of follow-up flushes.
|
|
2109
|
+
|
|
2110
|
+
```ts
|
|
2111
|
+
import { Effect, Schedule } from "effect"
|
|
2112
|
+
|
|
2113
|
+
type MetricsFlushError = unknown
|
|
2114
|
+
|
|
2115
|
+
declare const flushMetricsBatch: Effect.Effect<void, MetricsFlushError>
|
|
2116
|
+
|
|
2117
|
+
declare const Metrics: {
|
|
2118
|
+
readonly recordFlushAttempt: (attempt: number) => Effect.Effect<void>
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
const policy = Schedule.spaced("10 seconds").pipe(
|
|
2122
|
+
Schedule.bothRight(Schedule.recurs(6)),
|
|
2123
|
+
Schedule.tapOutput((count) => Metrics.recordFlushAttempt(count))
|
|
2124
|
+
)
|
|
2125
|
+
|
|
2126
|
+
const program = flushMetricsBatch.pipe(Effect.repeat(policy))
|
|
2127
|
+
```
|
|
2128
|
+
|
|
2129
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
2130
|
+
|
|
2131
|
+
### Warm Up Polling, Then Steady Polling
|
|
2132
|
+
|
|
2133
|
+
Write the Effect code used right after enabling a new indexer.
|
|
2134
|
+
|
|
2135
|
+
```ts
|
|
2136
|
+
import { Effect, Schedule } from "effect"
|
|
2137
|
+
|
|
2138
|
+
type IndexerLag = unknown
|
|
2139
|
+
type IndexerLagError = unknown
|
|
2140
|
+
|
|
2141
|
+
declare const readIndexerLag: Effect.Effect<IndexerLag, IndexerLagError>
|
|
2142
|
+
|
|
2143
|
+
const warmup = Schedule.spaced("250 millis").pipe(
|
|
2144
|
+
Schedule.bothLeft(Schedule.recurs(8))
|
|
2145
|
+
)
|
|
2146
|
+
const steady = Schedule.spaced("5 seconds")
|
|
2147
|
+
const policy = warmup.pipe(
|
|
2148
|
+
Schedule.andThen(steady),
|
|
2149
|
+
Schedule.setInputType<IndexerLag>(),
|
|
2150
|
+
Schedule.passthrough
|
|
2151
|
+
)
|
|
2152
|
+
|
|
2153
|
+
const program = readIndexerLag.pipe(Effect.repeat(policy))
|
|
2154
|
+
```
|
|
2155
|
+
|
|
2156
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
2157
|
+
|
|
2158
|
+
### Burst Heartbeats, Then Fixed Heartbeats
|
|
2159
|
+
|
|
2160
|
+
Write the Effect code used when a process first joins a cluster.
|
|
2161
|
+
|
|
2162
|
+
```ts
|
|
2163
|
+
import { Effect, Schedule } from "effect"
|
|
2164
|
+
|
|
2165
|
+
type HeartbeatError = unknown
|
|
2166
|
+
|
|
2167
|
+
declare const sendHeartbeat: Effect.Effect<void, HeartbeatError>
|
|
2168
|
+
|
|
2169
|
+
const burst = Schedule.spaced("100 millis").pipe(
|
|
2170
|
+
Schedule.bothLeft(Schedule.recurs(3))
|
|
2171
|
+
)
|
|
2172
|
+
const steady = Schedule.fixed("30 seconds")
|
|
2173
|
+
const policy = burst.pipe(Schedule.andThen(steady))
|
|
2174
|
+
|
|
2175
|
+
const program = sendHeartbeat.pipe(Effect.repeat(policy))
|
|
2176
|
+
```
|
|
2177
|
+
|
|
2178
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
2179
|
+
|
|
2180
|
+
### Repeat A Fixed Heartbeat
|
|
2181
|
+
|
|
2182
|
+
Write the Effect code for a long-running process heartbeat.
|
|
2183
|
+
|
|
2184
|
+
```ts
|
|
2185
|
+
import { Effect, Schedule } from "effect"
|
|
2186
|
+
|
|
2187
|
+
type HeartbeatError = unknown
|
|
2188
|
+
|
|
2189
|
+
declare const sendHeartbeat: Effect.Effect<void, HeartbeatError>
|
|
2190
|
+
|
|
2191
|
+
const heartbeatCadence = Schedule.fixed("30 seconds")
|
|
2192
|
+
const program = sendHeartbeat.pipe(Effect.repeat(heartbeatCadence))
|
|
2193
|
+
```
|
|
2194
|
+
|
|
2195
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2196
|
+
|
|
2197
|
+
### Repeat On Metric Flush Windows
|
|
2198
|
+
|
|
2199
|
+
Write the Effect code for flushing metrics on wall-clock minute windows.
|
|
2200
|
+
|
|
2201
|
+
```ts
|
|
2202
|
+
import { Effect, Schedule } from "effect"
|
|
2203
|
+
|
|
2204
|
+
type MetricsFlushError = unknown
|
|
2205
|
+
|
|
2206
|
+
declare const flushMetrics: Effect.Effect<void, MetricsFlushError>
|
|
2207
|
+
|
|
2208
|
+
const flushWindow = Schedule.windowed("1 minute")
|
|
2209
|
+
const program = flushMetrics.pipe(Effect.repeat(flushWindow))
|
|
2210
|
+
```
|
|
2211
|
+
|
|
2212
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2213
|
+
|
|
2214
|
+
### Repeat A Nightly Cron Job
|
|
2215
|
+
|
|
2216
|
+
Write the Effect code for a database maintenance job.
|
|
2217
|
+
|
|
2218
|
+
```ts
|
|
2219
|
+
import { Effect, Schedule } from "effect"
|
|
2220
|
+
|
|
2221
|
+
type CompactionError = unknown
|
|
2222
|
+
|
|
2223
|
+
declare const compactDatabase: Effect.Effect<void, CompactionError>
|
|
2224
|
+
|
|
2225
|
+
const nightly = Schedule.cron("0 2 * * *")
|
|
2226
|
+
const program = compactDatabase.pipe(Effect.repeat(nightly))
|
|
2227
|
+
```
|
|
2228
|
+
|
|
2229
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2230
|
+
|
|
2231
|
+
### Run One Follow-Up Cache Warm
|
|
2232
|
+
|
|
2233
|
+
Write the Effect code for a deploy hook that warms the cache once, then performs one follow-up warm after 10 seconds.
|
|
2234
|
+
|
|
2235
|
+
```ts
|
|
2236
|
+
import { Effect, Schedule } from "effect"
|
|
2237
|
+
|
|
2238
|
+
type CacheWarmError = unknown
|
|
2239
|
+
|
|
2240
|
+
declare const warmCache: Effect.Effect<void, CacheWarmError>
|
|
2241
|
+
|
|
2242
|
+
const oneMoreRun = Schedule.duration("10 seconds")
|
|
2243
|
+
const program = warmCache.pipe(Effect.repeat(oneMoreRun))
|
|
2244
|
+
```
|
|
2245
|
+
|
|
2246
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2247
|
+
|
|
2248
|
+
### Repeat Until Terminal With Options
|
|
2249
|
+
|
|
2250
|
+
Write the Effect code used after starting a file transfer.
|
|
2251
|
+
|
|
2252
|
+
```ts
|
|
2253
|
+
import { Effect, Schedule } from "effect"
|
|
2254
|
+
|
|
2255
|
+
type TransferStatusError = unknown
|
|
2256
|
+
|
|
2257
|
+
type TransferStatus =
|
|
2258
|
+
| { readonly _tag: "Pending" }
|
|
2259
|
+
| { readonly _tag: "InProgress" }
|
|
2260
|
+
| { readonly _tag: "Completed" }
|
|
2261
|
+
| { readonly _tag: "Failed" }
|
|
2262
|
+
|
|
2263
|
+
declare const readTransferStatus: Effect.Effect<TransferStatus, TransferStatusError>
|
|
2264
|
+
|
|
2265
|
+
const program = readTransferStatus.pipe(
|
|
2266
|
+
Effect.repeat({
|
|
2267
|
+
schedule: Schedule.spaced("1 second"),
|
|
2268
|
+
until: (status) => status._tag === "Completed" || status._tag === "Failed"
|
|
2269
|
+
})
|
|
2270
|
+
)
|
|
2271
|
+
```
|
|
2272
|
+
|
|
2273
|
+
Caution: `Effect.repeat` observes successful values and stops on the first failure.
|
|
2274
|
+
|
|
2275
|
+
## Observability
|
|
2276
|
+
|
|
2277
|
+
### Log Each Retry Attempt
|
|
2278
|
+
|
|
2279
|
+
Write the Effect code for fetching inventory from a provider.
|
|
2280
|
+
|
|
2281
|
+
```ts
|
|
2282
|
+
import { Console, Effect, Schedule } from "effect"
|
|
2283
|
+
|
|
2284
|
+
type InventorySnapshot = unknown
|
|
2285
|
+
|
|
2286
|
+
type InventoryError =
|
|
2287
|
+
| { readonly _tag: "TransientInventoryError" }
|
|
2288
|
+
| { readonly _tag: "InvalidSku" }
|
|
2289
|
+
|
|
2290
|
+
declare const fetchInventory: Effect.Effect<InventorySnapshot, InventoryError>
|
|
2291
|
+
|
|
2292
|
+
const retryInventoryPolicy = Schedule.exponential("100 millis").pipe(
|
|
2293
|
+
Schedule.both(Schedule.recurs(5)),
|
|
2294
|
+
Schedule.tapInput((error: InventoryError) => Console.log(`retry input: ${error._tag}`)),
|
|
2295
|
+
Schedule.tapOutput((output) => Console.log(`retry scheduled: ${String(output)}`))
|
|
2296
|
+
)
|
|
2297
|
+
|
|
2298
|
+
const program = fetchInventory.pipe(
|
|
2299
|
+
Effect.retry({
|
|
2300
|
+
schedule: retryInventoryPolicy,
|
|
2301
|
+
while: (error) => error._tag === "TransientInventoryError"
|
|
2302
|
+
})
|
|
2303
|
+
)
|
|
2304
|
+
```
|
|
2305
|
+
|
|
2306
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
2307
|
+
|
|
2308
|
+
### Log Retry Counts With `bothRight`
|
|
2309
|
+
|
|
2310
|
+
Write the Effect code for uploading one chunk.
|
|
2311
|
+
|
|
2312
|
+
```ts
|
|
2313
|
+
import { Console, Effect, Schedule } from "effect"
|
|
2314
|
+
|
|
2315
|
+
type UploadError =
|
|
2316
|
+
| { readonly _tag: "TransientUploadError" }
|
|
2317
|
+
| { readonly _tag: "InvalidUpload" }
|
|
2318
|
+
|
|
2319
|
+
declare const uploadChunk: Effect.Effect<void, UploadError>
|
|
2320
|
+
|
|
2321
|
+
const policy = Schedule.exponential("100 millis").pipe(
|
|
2322
|
+
Schedule.bothRight(Schedule.recurs(4)),
|
|
2323
|
+
Schedule.tapOutput((count) => Console.log(`retry attempt ${count}`))
|
|
2324
|
+
)
|
|
2325
|
+
|
|
2326
|
+
const program = uploadChunk.pipe(
|
|
2327
|
+
Effect.retry({
|
|
2328
|
+
schedule: policy,
|
|
2329
|
+
while: (error) => error._tag === "TransientUploadError"
|
|
2330
|
+
})
|
|
2331
|
+
)
|
|
2332
|
+
```
|
|
2333
|
+
|
|
2334
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
2335
|
+
|
|
2336
|
+
### Observe Retry Input Errors
|
|
2337
|
+
|
|
2338
|
+
Write the Effect code for an inventory lookup that records retry diagnostics.
|
|
2339
|
+
|
|
2340
|
+
```ts
|
|
2341
|
+
import { Console, Effect, Schedule } from "effect"
|
|
2342
|
+
|
|
2343
|
+
type InventorySnapshot = unknown
|
|
2344
|
+
|
|
2345
|
+
type InventoryError =
|
|
2346
|
+
| { readonly _tag: "TransientInventoryError" }
|
|
2347
|
+
| { readonly _tag: "InvalidSku" }
|
|
2348
|
+
|
|
2349
|
+
declare const callInventory: Effect.Effect<InventorySnapshot, InventoryError>
|
|
2350
|
+
|
|
2351
|
+
const policy = Schedule.exponential("100 millis").pipe(
|
|
2352
|
+
Schedule.tapInput((error: InventoryError) => Console.log(`retrying after ${error._tag}`)),
|
|
2353
|
+
Schedule.both(Schedule.recurs(4))
|
|
2354
|
+
)
|
|
2355
|
+
|
|
2356
|
+
const program = callInventory.pipe(
|
|
2357
|
+
Effect.retry({
|
|
2358
|
+
schedule: policy,
|
|
2359
|
+
while: (error) => error._tag === "TransientInventoryError"
|
|
2360
|
+
})
|
|
2361
|
+
)
|
|
2362
|
+
```
|
|
2363
|
+
|
|
2364
|
+
Caution: classify permanent failures before applying retry; backoff does not make invalid input transient.
|
|
2365
|
+
|
|
2366
|
+
### Observe Retry Output Delays
|
|
2367
|
+
|
|
2368
|
+
Write the Effect code for webhook delivery.
|
|
2369
|
+
|
|
2370
|
+
```ts
|
|
2371
|
+
import { Duration, Effect, Schedule } from "effect"
|
|
2372
|
+
|
|
2373
|
+
type WebhookError = unknown
|
|
2374
|
+
|
|
2375
|
+
declare const sendWebhook: Effect.Effect<void, WebhookError>
|
|
2376
|
+
|
|
2377
|
+
declare const Metrics: {
|
|
2378
|
+
readonly recordRetryDelay: (delay: Duration.Duration) => Effect.Effect<void>
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
const policy = Schedule.exponential("100 millis").pipe(
|
|
2382
|
+
Schedule.tapOutput((delay) => Metrics.recordRetryDelay(delay)),
|
|
2383
|
+
Schedule.both(Schedule.recurs(5))
|
|
2384
|
+
)
|
|
2385
|
+
|
|
2386
|
+
const program = sendWebhook.pipe(Effect.retry(policy))
|
|
2387
|
+
```
|
|
2388
|
+
|
|
2389
|
+
Caution: `recurs(n)` counts follow-up recurrences after the first execution.
|
|
2390
|
+
|
|
2391
|
+
### Observe Poll Output Statuses
|
|
2392
|
+
|
|
2393
|
+
Write the Effect code for a deployment status watcher.
|
|
2394
|
+
|
|
2395
|
+
```ts
|
|
2396
|
+
import { Console, Effect, Schedule } from "effect"
|
|
2397
|
+
|
|
2398
|
+
type DeploymentStatusError = unknown
|
|
2399
|
+
|
|
2400
|
+
type DeploymentStatus =
|
|
2401
|
+
| { readonly _tag: "Queued" }
|
|
2402
|
+
| { readonly _tag: "Running" }
|
|
2403
|
+
| { readonly _tag: "Complete" }
|
|
2404
|
+
| { readonly _tag: "Failed" }
|
|
2405
|
+
|
|
2406
|
+
declare const readDeploymentStatus: Effect.Effect<DeploymentStatus, DeploymentStatusError>
|
|
2407
|
+
|
|
2408
|
+
const policy = Schedule.spaced("2 seconds").pipe(
|
|
2409
|
+
Schedule.setInputType<DeploymentStatus>(),
|
|
2410
|
+
Schedule.passthrough,
|
|
2411
|
+
Schedule.tapOutput((status) => Console.log(`deployment status: ${status._tag}`)),
|
|
2412
|
+
Schedule.while(({ input }) => input._tag === "Queued" || input._tag === "Running")
|
|
2413
|
+
)
|
|
2414
|
+
|
|
2415
|
+
const program = readDeploymentStatus.pipe(Effect.repeat(policy))
|
|
2416
|
+
```
|
|
2417
|
+
|
|
2418
|
+
Caution: raw `Effect.repeat(schedule)` returns the schedule output; `Schedule.passthrough` preserves the latest successful value.
|
|
2419
|
+
|
|
2420
|
+
## Runnable Scenario Checks
|
|
2421
|
+
|
|
2422
|
+
These examples keep the recipe shape, add small deterministic operations, and use `TestClock` where time must advance.
|
|
2423
|
+
|
|
2424
|
+
### Run A Timeout Retry
|
|
2425
|
+
|
|
2426
|
+
Use this to check that a retryable timeout is retried and the first success is returned.
|
|
2427
|
+
|
|
2428
|
+
```ts runnable deterministic
|
|
2429
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2430
|
+
import { TestClock } from "effect/testing"
|
|
2431
|
+
|
|
2432
|
+
type HttpTimeout = { readonly _tag: "HttpTimeout" }
|
|
2433
|
+
type HttpStatusError = { readonly _tag: "HttpStatusError"; readonly status: number }
|
|
2434
|
+
type DecodeError = { readonly _tag: "DecodeError" }
|
|
2435
|
+
type User = { readonly id: string }
|
|
2436
|
+
|
|
2437
|
+
const getUser = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
2438
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
2439
|
+
yield* Console.log(`getUser attempt ${attempt}`)
|
|
2440
|
+
|
|
2441
|
+
if (attempt < 3) {
|
|
2442
|
+
return yield* Effect.fail({ _tag: "HttpTimeout" } as HttpTimeout)
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
return { id: "user-123" } satisfies User
|
|
2446
|
+
})
|
|
2447
|
+
|
|
2448
|
+
const isHttpTimeout = (
|
|
2449
|
+
error: HttpTimeout | HttpStatusError | DecodeError
|
|
2450
|
+
): error is HttpTimeout => error._tag === "HttpTimeout"
|
|
2451
|
+
|
|
2452
|
+
const retryGetUserTimeouts = Schedule.exponential("10 millis").pipe(
|
|
2453
|
+
Schedule.both(Schedule.recurs(3))
|
|
2454
|
+
)
|
|
2455
|
+
|
|
2456
|
+
const program = Effect.gen(function*() {
|
|
2457
|
+
const attempts = yield* Ref.make(0)
|
|
2458
|
+
const fiber = yield* getUser(attempts).pipe(
|
|
2459
|
+
Effect.retry({
|
|
2460
|
+
schedule: retryGetUserTimeouts,
|
|
2461
|
+
while: isHttpTimeout
|
|
2462
|
+
}),
|
|
2463
|
+
Effect.forkScoped
|
|
2464
|
+
)
|
|
2465
|
+
|
|
2466
|
+
yield* TestClock.adjust("30 millis")
|
|
2467
|
+
const user = yield* Fiber.join(fiber)
|
|
2468
|
+
yield* Console.log(`loaded ${user.id}`)
|
|
2469
|
+
})
|
|
2470
|
+
|
|
2471
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2472
|
+
// Output:
|
|
2473
|
+
// getUser attempt 1
|
|
2474
|
+
// getUser attempt 2
|
|
2475
|
+
// getUser attempt 3
|
|
2476
|
+
// loaded user-123
|
|
2477
|
+
```
|
|
2478
|
+
|
|
2479
|
+
### Stop On Permanent Failure
|
|
2480
|
+
|
|
2481
|
+
Use this to check that permanent errors do not spend the retry budget.
|
|
2482
|
+
|
|
2483
|
+
```ts runnable deterministic
|
|
2484
|
+
import { Console, Effect, Schedule } from "effect"
|
|
2485
|
+
|
|
2486
|
+
type SubmitError =
|
|
2487
|
+
| { readonly _tag: "Timeout" }
|
|
2488
|
+
| { readonly _tag: "ServiceUnavailable" }
|
|
2489
|
+
| { readonly _tag: "ValidationError"; readonly field: string }
|
|
2490
|
+
|
|
2491
|
+
const submitForm: Effect.Effect<string, SubmitError> = Effect.gen(function*() {
|
|
2492
|
+
yield* Console.log("submit attempt")
|
|
2493
|
+
return yield* Effect.fail({ _tag: "ValidationError", field: "email" } as const)
|
|
2494
|
+
})
|
|
2495
|
+
|
|
2496
|
+
const isRetryableSubmitError = (error: SubmitError): boolean =>
|
|
2497
|
+
error._tag === "Timeout" || error._tag === "ServiceUnavailable"
|
|
2498
|
+
|
|
2499
|
+
const program = submitForm.pipe(
|
|
2500
|
+
Effect.retry({
|
|
2501
|
+
schedule: Schedule.recurs(3),
|
|
2502
|
+
while: isRetryableSubmitError
|
|
2503
|
+
}),
|
|
2504
|
+
Effect.exit,
|
|
2505
|
+
Effect.flatMap((exit) => Console.log(`failed immediately: ${exit._tag === "Failure"}`))
|
|
2506
|
+
)
|
|
2507
|
+
|
|
2508
|
+
Effect.runPromise(program)
|
|
2509
|
+
// Output:
|
|
2510
|
+
// submit attempt
|
|
2511
|
+
// failed immediately: true
|
|
2512
|
+
```
|
|
2513
|
+
|
|
2514
|
+
### Respect Retry-After
|
|
2515
|
+
|
|
2516
|
+
Use this to check that a rate-limit response can stretch the selected retry delay.
|
|
2517
|
+
|
|
2518
|
+
```ts runnable deterministic
|
|
2519
|
+
import { Console, Duration, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2520
|
+
import { TestClock } from "effect/testing"
|
|
2521
|
+
|
|
2522
|
+
type HttpError =
|
|
2523
|
+
| { readonly _tag: "RateLimited"; readonly retryAfter: Duration.Duration | undefined }
|
|
2524
|
+
| { readonly _tag: "Unauthorized" | "Forbidden" | "BadRequest" | "Unavailable" }
|
|
2525
|
+
|
|
2526
|
+
const callApi = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
2527
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
2528
|
+
yield* Console.log(`api attempt ${attempt}`)
|
|
2529
|
+
|
|
2530
|
+
if (attempt === 1) {
|
|
2531
|
+
return yield* Effect.fail({
|
|
2532
|
+
_tag: "RateLimited",
|
|
2533
|
+
retryAfter: Duration.seconds(1)
|
|
2534
|
+
} as HttpError)
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
return "ok"
|
|
2538
|
+
})
|
|
2539
|
+
|
|
2540
|
+
const rateLimitPolicy = Schedule.exponential("10 millis").pipe(
|
|
2541
|
+
Schedule.both(Schedule.identity<HttpError>()),
|
|
2542
|
+
Schedule.modifyDelay(([_, error], delay) =>
|
|
2543
|
+
Effect.succeed(
|
|
2544
|
+
error._tag === "RateLimited" && error.retryAfter !== undefined
|
|
2545
|
+
? Duration.max(delay, error.retryAfter)
|
|
2546
|
+
: delay
|
|
2547
|
+
)
|
|
2548
|
+
),
|
|
2549
|
+
Schedule.both(Schedule.recurs(2)),
|
|
2550
|
+
Schedule.while(({ input }) => input._tag === "RateLimited")
|
|
2551
|
+
)
|
|
2552
|
+
|
|
2553
|
+
const program = Effect.gen(function*() {
|
|
2554
|
+
const attempts = yield* Ref.make(0)
|
|
2555
|
+
const fiber = yield* callApi(attempts).pipe(
|
|
2556
|
+
Effect.retry(rateLimitPolicy),
|
|
2557
|
+
Effect.forkScoped
|
|
2558
|
+
)
|
|
2559
|
+
|
|
2560
|
+
yield* TestClock.adjust("999 millis")
|
|
2561
|
+
yield* Console.log(`before retry: ${yield* Ref.get(attempts)}`)
|
|
2562
|
+
yield* TestClock.adjust("1 millis")
|
|
2563
|
+
const result = yield* Fiber.join(fiber)
|
|
2564
|
+
yield* Console.log(`result: ${result}`)
|
|
2565
|
+
})
|
|
2566
|
+
|
|
2567
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2568
|
+
// Output:
|
|
2569
|
+
// api attempt 1
|
|
2570
|
+
// before retry: 1
|
|
2571
|
+
// api attempt 2
|
|
2572
|
+
// result: ok
|
|
2573
|
+
```
|
|
2574
|
+
|
|
2575
|
+
### Reuse An Idempotency Key
|
|
2576
|
+
|
|
2577
|
+
Use this to check that a retried write keeps the same logical operation identity.
|
|
2578
|
+
|
|
2579
|
+
```ts runnable deterministic
|
|
2580
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2581
|
+
import { TestClock } from "effect/testing"
|
|
2582
|
+
|
|
2583
|
+
type Order = { readonly id: string }
|
|
2584
|
+
type PostOrderError = { readonly _tag: "Timeout" | "InvalidRequest" }
|
|
2585
|
+
|
|
2586
|
+
const idempotencyKey = "order-key-123"
|
|
2587
|
+
|
|
2588
|
+
const postOrder = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
2589
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
2590
|
+
yield* Console.log(`order attempt ${attempt} key=${idempotencyKey}`)
|
|
2591
|
+
|
|
2592
|
+
if (attempt < 2) {
|
|
2593
|
+
return yield* Effect.fail({ _tag: "Timeout" } as PostOrderError)
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
return { id: "order-1" } satisfies Order
|
|
2597
|
+
})
|
|
2598
|
+
|
|
2599
|
+
const program = Effect.gen(function*() {
|
|
2600
|
+
const attempts = yield* Ref.make(0)
|
|
2601
|
+
const fiber = yield* postOrder(attempts).pipe(
|
|
2602
|
+
Effect.retry({
|
|
2603
|
+
schedule: Schedule.spaced("10 millis").pipe(Schedule.both(Schedule.recurs(4))),
|
|
2604
|
+
while: (error) => error._tag === "Timeout"
|
|
2605
|
+
}),
|
|
2606
|
+
Effect.forkScoped
|
|
2607
|
+
)
|
|
2608
|
+
|
|
2609
|
+
yield* TestClock.adjust("10 millis")
|
|
2610
|
+
const order = yield* Fiber.join(fiber)
|
|
2611
|
+
yield* Console.log(`created ${order.id}`)
|
|
2612
|
+
})
|
|
2613
|
+
|
|
2614
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2615
|
+
// Output:
|
|
2616
|
+
// order attempt 1 key=order-key-123
|
|
2617
|
+
// order attempt 2 key=order-key-123
|
|
2618
|
+
// created order-1
|
|
2619
|
+
```
|
|
2620
|
+
|
|
2621
|
+
### Cap Reconnect Backoff
|
|
2622
|
+
|
|
2623
|
+
Use this to check that a reconnect policy retries until the socket opens.
|
|
2624
|
+
|
|
2625
|
+
```ts runnable deterministic
|
|
2626
|
+
import { Console, Duration, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2627
|
+
import { TestClock } from "effect/testing"
|
|
2628
|
+
|
|
2629
|
+
type LiveSocket = { readonly connected: true }
|
|
2630
|
+
type WebSocketOpenError = { readonly _tag: "ConnectTimeout" | "BadHandshake" }
|
|
2631
|
+
|
|
2632
|
+
const openLiveSocket = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
2633
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
2634
|
+
yield* Console.log(`socket attempt ${attempt}`)
|
|
2635
|
+
|
|
2636
|
+
if (attempt < 3) {
|
|
2637
|
+
return yield* Effect.fail({ _tag: "ConnectTimeout" } as WebSocketOpenError)
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
return { connected: true } satisfies LiveSocket
|
|
2641
|
+
})
|
|
2642
|
+
|
|
2643
|
+
const websocketReconnectPolicy = Schedule.exponential("10 millis").pipe(
|
|
2644
|
+
Schedule.modifyDelay((_, delay) => Effect.succeed(Duration.min(delay, Duration.seconds(5)))),
|
|
2645
|
+
Schedule.both(Schedule.recurs(8))
|
|
2646
|
+
)
|
|
2647
|
+
|
|
2648
|
+
const program = Effect.gen(function*() {
|
|
2649
|
+
const attempts = yield* Ref.make(0)
|
|
2650
|
+
const fiber = yield* openLiveSocket(attempts).pipe(
|
|
2651
|
+
Effect.retry({
|
|
2652
|
+
schedule: websocketReconnectPolicy,
|
|
2653
|
+
while: (error) => error._tag === "ConnectTimeout"
|
|
2654
|
+
}),
|
|
2655
|
+
Effect.forkScoped
|
|
2656
|
+
)
|
|
2657
|
+
|
|
2658
|
+
yield* TestClock.adjust("30 millis")
|
|
2659
|
+
const socket = yield* Fiber.join(fiber)
|
|
2660
|
+
yield* Console.log(`connected: ${socket.connected}`)
|
|
2661
|
+
})
|
|
2662
|
+
|
|
2663
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2664
|
+
// Output:
|
|
2665
|
+
// socket attempt 1
|
|
2666
|
+
// socket attempt 2
|
|
2667
|
+
// socket attempt 3
|
|
2668
|
+
// connected: true
|
|
2669
|
+
```
|
|
2670
|
+
|
|
2671
|
+
### Ask A Circuit Breaker Before Retrying
|
|
2672
|
+
|
|
2673
|
+
Use this to check an effectful `while` predicate.
|
|
2674
|
+
|
|
2675
|
+
```ts runnable deterministic
|
|
2676
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2677
|
+
import { TestClock } from "effect/testing"
|
|
2678
|
+
|
|
2679
|
+
type BillingError =
|
|
2680
|
+
| { readonly _tag: "TransientBillingError" }
|
|
2681
|
+
| { readonly _tag: "RejectedPayment" }
|
|
2682
|
+
|
|
2683
|
+
const callBilling = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
2684
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
2685
|
+
yield* Console.log(`billing attempt ${attempt}`)
|
|
2686
|
+
|
|
2687
|
+
if (attempt < 2) {
|
|
2688
|
+
return yield* Effect.fail({ _tag: "TransientBillingError" } as BillingError)
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
return "receipt"
|
|
2692
|
+
})
|
|
2693
|
+
|
|
2694
|
+
const circuitBreakerAllowsRetry = (error: BillingError): Effect.Effect<boolean> =>
|
|
2695
|
+
Console.log(`breaker saw ${error._tag}`).pipe(Effect.as(true))
|
|
2696
|
+
|
|
2697
|
+
const program = Effect.gen(function*() {
|
|
2698
|
+
const attempts = yield* Ref.make(0)
|
|
2699
|
+
const fiber = yield* callBilling(attempts).pipe(
|
|
2700
|
+
Effect.retry({
|
|
2701
|
+
schedule: Schedule.spaced("10 millis").pipe(Schedule.both(Schedule.recurs(5))),
|
|
2702
|
+
while: (error) =>
|
|
2703
|
+
error._tag === "TransientBillingError"
|
|
2704
|
+
? circuitBreakerAllowsRetry(error)
|
|
2705
|
+
: Effect.succeed(false)
|
|
2706
|
+
}),
|
|
2707
|
+
Effect.forkScoped
|
|
2708
|
+
)
|
|
2709
|
+
|
|
2710
|
+
yield* TestClock.adjust("10 millis")
|
|
2711
|
+
const receipt = yield* Fiber.join(fiber)
|
|
2712
|
+
yield* Console.log(`result: ${receipt}`)
|
|
2713
|
+
})
|
|
2714
|
+
|
|
2715
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2716
|
+
// Output:
|
|
2717
|
+
// billing attempt 1
|
|
2718
|
+
// breaker saw TransientBillingError
|
|
2719
|
+
// billing attempt 2
|
|
2720
|
+
// result: receipt
|
|
2721
|
+
```
|
|
2722
|
+
|
|
2723
|
+
### Poll A Job Until Terminal
|
|
2724
|
+
|
|
2725
|
+
Use this to check that successful non-terminal states drive `Effect.repeat`.
|
|
2726
|
+
|
|
2727
|
+
```ts runnable deterministic
|
|
2728
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2729
|
+
import { TestClock } from "effect/testing"
|
|
2730
|
+
|
|
2731
|
+
type JobStatus =
|
|
2732
|
+
| { readonly state: "queued"; readonly jobId: string }
|
|
2733
|
+
| { readonly state: "running"; readonly jobId: string }
|
|
2734
|
+
| { readonly state: "succeeded"; readonly jobId: string; readonly artifactUrl: string }
|
|
2735
|
+
| { readonly state: "failed"; readonly jobId: string; readonly reason: string }
|
|
2736
|
+
|
|
2737
|
+
const readJobStatus = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
2738
|
+
const read = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
2739
|
+
const status: JobStatus = read < 3
|
|
2740
|
+
? { state: "running", jobId: "job-1" }
|
|
2741
|
+
: { state: "succeeded", jobId: "job-1", artifactUrl: "/artifact" }
|
|
2742
|
+
yield* Console.log(`job ${status.state}`)
|
|
2743
|
+
return status
|
|
2744
|
+
})
|
|
2745
|
+
|
|
2746
|
+
const isTerminal = (status: JobStatus): boolean => status.state === "succeeded" || status.state === "failed"
|
|
2747
|
+
|
|
2748
|
+
const pollJobStatus = Schedule.spaced("1 second").pipe(
|
|
2749
|
+
Schedule.setInputType<JobStatus>(),
|
|
2750
|
+
Schedule.passthrough,
|
|
2751
|
+
Schedule.while(({ input }) => !isTerminal(input)),
|
|
2752
|
+
Schedule.bothLeft(Schedule.during("30 seconds").pipe(Schedule.setInputType<JobStatus>()))
|
|
2753
|
+
)
|
|
2754
|
+
|
|
2755
|
+
const program = Effect.gen(function*() {
|
|
2756
|
+
const reads = yield* Ref.make(0)
|
|
2757
|
+
const fiber = yield* readJobStatus(reads).pipe(
|
|
2758
|
+
Effect.repeat(pollJobStatus),
|
|
2759
|
+
Effect.forkScoped
|
|
2760
|
+
)
|
|
2761
|
+
|
|
2762
|
+
yield* TestClock.adjust("2 seconds")
|
|
2763
|
+
const status = yield* Fiber.join(fiber)
|
|
2764
|
+
yield* Console.log(`final ${status.state}`)
|
|
2765
|
+
})
|
|
2766
|
+
|
|
2767
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2768
|
+
// Output:
|
|
2769
|
+
// job running
|
|
2770
|
+
// job running
|
|
2771
|
+
// job succeeded
|
|
2772
|
+
// final succeeded
|
|
2773
|
+
```
|
|
2774
|
+
|
|
2775
|
+
### Poll Payment Settlement
|
|
2776
|
+
|
|
2777
|
+
Use this to check that pending payment states are successes, not retry failures.
|
|
2778
|
+
|
|
2779
|
+
```ts runnable deterministic
|
|
2780
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2781
|
+
import { TestClock } from "effect/testing"
|
|
2782
|
+
|
|
2783
|
+
type SettlementStatus =
|
|
2784
|
+
| { readonly _tag: "Pending" }
|
|
2785
|
+
| { readonly _tag: "Processing" }
|
|
2786
|
+
| { readonly _tag: "Settled" }
|
|
2787
|
+
| { readonly _tag: "Declined" }
|
|
2788
|
+
|
|
2789
|
+
const fetchSettlementStatus = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
2790
|
+
const read = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
2791
|
+
const status: SettlementStatus = read === 1 ? { _tag: "Pending" } : { _tag: "Settled" }
|
|
2792
|
+
yield* Console.log(`settlement ${status._tag}`)
|
|
2793
|
+
return status
|
|
2794
|
+
})
|
|
2795
|
+
|
|
2796
|
+
const isOpen = (status: SettlementStatus) => status._tag === "Pending" || status._tag === "Processing"
|
|
2797
|
+
|
|
2798
|
+
const pollOpenSettlements = Schedule.spaced("1 second").pipe(
|
|
2799
|
+
Schedule.setInputType<SettlementStatus>(),
|
|
2800
|
+
Schedule.passthrough,
|
|
2801
|
+
Schedule.while(({ input }) => isOpen(input)),
|
|
2802
|
+
Schedule.bothLeft(Schedule.during("30 seconds"))
|
|
2803
|
+
)
|
|
2804
|
+
|
|
2805
|
+
const program = Effect.gen(function*() {
|
|
2806
|
+
const reads = yield* Ref.make(0)
|
|
2807
|
+
const fiber = yield* fetchSettlementStatus(reads).pipe(
|
|
2808
|
+
Effect.repeat(pollOpenSettlements),
|
|
2809
|
+
Effect.forkScoped
|
|
2810
|
+
)
|
|
2811
|
+
|
|
2812
|
+
yield* TestClock.adjust("1 second")
|
|
2813
|
+
const status = yield* Fiber.join(fiber)
|
|
2814
|
+
yield* Console.log(`final ${status._tag}`)
|
|
2815
|
+
})
|
|
2816
|
+
|
|
2817
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2818
|
+
// Output:
|
|
2819
|
+
// settlement Pending
|
|
2820
|
+
// settlement Settled
|
|
2821
|
+
// final Settled
|
|
2822
|
+
```
|
|
2823
|
+
|
|
2824
|
+
### Poll Until A Cache Entry Appears
|
|
2825
|
+
|
|
2826
|
+
Use this to check a successful miss-then-hit polling loop.
|
|
2827
|
+
|
|
2828
|
+
```ts runnable deterministic
|
|
2829
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2830
|
+
import { TestClock } from "effect/testing"
|
|
2831
|
+
|
|
2832
|
+
type CachedValue = string
|
|
2833
|
+
type CacheLookup =
|
|
2834
|
+
| { readonly _tag: "Miss" }
|
|
2835
|
+
| { readonly _tag: "Hit"; readonly value: CachedValue }
|
|
2836
|
+
|
|
2837
|
+
const readCacheEntry = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
2838
|
+
const read = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
2839
|
+
const lookup: CacheLookup = read < 3 ? { _tag: "Miss" } : { _tag: "Hit", value: "cached" }
|
|
2840
|
+
yield* Console.log(`cache ${lookup._tag}`)
|
|
2841
|
+
return lookup
|
|
2842
|
+
})
|
|
2843
|
+
|
|
2844
|
+
const untilCacheHit = Schedule.spaced("500 millis").pipe(
|
|
2845
|
+
Schedule.setInputType<CacheLookup>(),
|
|
2846
|
+
Schedule.passthrough,
|
|
2847
|
+
Schedule.while(({ input }) => input._tag === "Miss"),
|
|
2848
|
+
Schedule.bothLeft(Schedule.during("10 seconds"))
|
|
2849
|
+
)
|
|
2850
|
+
|
|
2851
|
+
const program = Effect.gen(function*() {
|
|
2852
|
+
const reads = yield* Ref.make(0)
|
|
2853
|
+
const fiber = yield* readCacheEntry(reads).pipe(
|
|
2854
|
+
Effect.repeat(untilCacheHit),
|
|
2855
|
+
Effect.forkScoped
|
|
2856
|
+
)
|
|
2857
|
+
|
|
2858
|
+
yield* TestClock.adjust("1 second")
|
|
2859
|
+
const lookup = yield* Fiber.join(fiber)
|
|
2860
|
+
yield* Console.log(`final ${lookup._tag}`)
|
|
2861
|
+
})
|
|
2862
|
+
|
|
2863
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2864
|
+
// Output:
|
|
2865
|
+
// cache Miss
|
|
2866
|
+
// cache Miss
|
|
2867
|
+
// cache Hit
|
|
2868
|
+
// final Hit
|
|
2869
|
+
```
|
|
2870
|
+
|
|
2871
|
+
### Return A Timeout Result From Polling
|
|
2872
|
+
|
|
2873
|
+
Use this to check that an exhausted polling budget is interpreted after `repeat`.
|
|
2874
|
+
|
|
2875
|
+
```ts runnable deterministic
|
|
2876
|
+
import { Console, Effect, Fiber, Schedule } from "effect"
|
|
2877
|
+
import { TestClock } from "effect/testing"
|
|
2878
|
+
|
|
2879
|
+
type Status =
|
|
2880
|
+
| { readonly _tag: "Running" }
|
|
2881
|
+
| { readonly _tag: "Finished"; readonly value: string }
|
|
2882
|
+
|
|
2883
|
+
const readStatus: Effect.Effect<Status> = Console.log("status Running").pipe(
|
|
2884
|
+
Effect.as({ _tag: "Running" } as Status)
|
|
2885
|
+
)
|
|
2886
|
+
|
|
2887
|
+
const polling = Schedule.spaced("1 second").pipe(
|
|
2888
|
+
Schedule.setInputType<Status>(),
|
|
2889
|
+
Schedule.passthrough,
|
|
2890
|
+
Schedule.while(({ input }) => input._tag === "Running"),
|
|
2891
|
+
Schedule.bothLeft(Schedule.recurs(1))
|
|
2892
|
+
)
|
|
2893
|
+
|
|
2894
|
+
const program = Effect.gen(function*() {
|
|
2895
|
+
const fiber = yield* readStatus.pipe(
|
|
2896
|
+
Effect.repeat(polling),
|
|
2897
|
+
Effect.flatMap((status) =>
|
|
2898
|
+
status._tag === "Running"
|
|
2899
|
+
? Effect.fail({ _tag: "TimedOut", latest: status } as const)
|
|
2900
|
+
: Effect.succeed(status)
|
|
2901
|
+
),
|
|
2902
|
+
Effect.exit,
|
|
2903
|
+
Effect.forkScoped
|
|
2904
|
+
)
|
|
2905
|
+
|
|
2906
|
+
yield* TestClock.adjust("1 second")
|
|
2907
|
+
const exit = yield* Fiber.join(fiber)
|
|
2908
|
+
yield* Console.log(`timed out: ${exit._tag === "Failure"}`)
|
|
2909
|
+
})
|
|
2910
|
+
|
|
2911
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2912
|
+
// Output:
|
|
2913
|
+
// status Running
|
|
2914
|
+
// status Running
|
|
2915
|
+
// timed out: true
|
|
2916
|
+
```
|
|
2917
|
+
|
|
2918
|
+
### Poll Fast, Then Slow
|
|
2919
|
+
|
|
2920
|
+
Use this to check phased polling with `andThen`.
|
|
2921
|
+
|
|
2922
|
+
```ts runnable deterministic
|
|
2923
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2924
|
+
import { TestClock } from "effect/testing"
|
|
2925
|
+
|
|
2926
|
+
type WorkflowStatus = { readonly step: number }
|
|
2927
|
+
|
|
2928
|
+
const readWorkflowStatus = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
2929
|
+
const step = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
2930
|
+
yield* Console.log(`workflow ${step}`)
|
|
2931
|
+
return { step } satisfies WorkflowStatus
|
|
2932
|
+
})
|
|
2933
|
+
|
|
2934
|
+
const fastThenSlow = Schedule.spaced("250 millis").pipe(
|
|
2935
|
+
Schedule.bothLeft(Schedule.recurs(2))
|
|
2936
|
+
)
|
|
2937
|
+
const slowPolling = Schedule.spaced("2 seconds")
|
|
2938
|
+
const polling = fastThenSlow.pipe(
|
|
2939
|
+
Schedule.andThen(slowPolling),
|
|
2940
|
+
Schedule.setInputType<WorkflowStatus>(),
|
|
2941
|
+
Schedule.passthrough,
|
|
2942
|
+
Schedule.bothLeft(Schedule.recurs(3).pipe(Schedule.setInputType<WorkflowStatus>()))
|
|
2943
|
+
)
|
|
2944
|
+
|
|
2945
|
+
const program = Effect.gen(function*() {
|
|
2946
|
+
const reads = yield* Ref.make(0)
|
|
2947
|
+
const fiber = yield* readWorkflowStatus(reads).pipe(
|
|
2948
|
+
Effect.repeat(polling),
|
|
2949
|
+
Effect.forkScoped
|
|
2950
|
+
)
|
|
2951
|
+
|
|
2952
|
+
yield* TestClock.adjust("3 seconds")
|
|
2953
|
+
const status = yield* Fiber.join(fiber)
|
|
2954
|
+
yield* Console.log(`final step ${status.step}`)
|
|
2955
|
+
})
|
|
2956
|
+
|
|
2957
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
2958
|
+
// Output:
|
|
2959
|
+
// workflow 1
|
|
2960
|
+
// workflow 2
|
|
2961
|
+
// workflow 3
|
|
2962
|
+
// workflow 4
|
|
2963
|
+
// final step 4
|
|
2964
|
+
```
|
|
2965
|
+
|
|
2966
|
+
### Repeat A Background Sync
|
|
2967
|
+
|
|
2968
|
+
Use this to check that `passthrough` returns the latest successful sync summary.
|
|
2969
|
+
|
|
2970
|
+
```ts runnable deterministic
|
|
2971
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
2972
|
+
import { TestClock } from "effect/testing"
|
|
2973
|
+
|
|
2974
|
+
type SyncSummary = { readonly pass: number }
|
|
2975
|
+
|
|
2976
|
+
const syncCrmOnce = Effect.fnUntraced(function*(passes: Ref.Ref<number>) {
|
|
2977
|
+
const pass = yield* Ref.updateAndGet(passes, (n) => n + 1)
|
|
2978
|
+
yield* Console.log(`sync ${pass}`)
|
|
2979
|
+
return { pass } satisfies SyncSummary
|
|
2980
|
+
})
|
|
2981
|
+
|
|
2982
|
+
const crmSyncCadence = Schedule.spaced("5 minutes").pipe(
|
|
2983
|
+
Schedule.setInputType<SyncSummary>(),
|
|
2984
|
+
Schedule.passthrough,
|
|
2985
|
+
Schedule.bothLeft(Schedule.recurs(2).pipe(Schedule.setInputType<SyncSummary>()))
|
|
2986
|
+
)
|
|
2987
|
+
|
|
2988
|
+
const program = Effect.gen(function*() {
|
|
2989
|
+
const passes = yield* Ref.make(0)
|
|
2990
|
+
const fiber = yield* syncCrmOnce(passes).pipe(
|
|
2991
|
+
Effect.repeat(crmSyncCadence),
|
|
2992
|
+
Effect.forkScoped
|
|
2993
|
+
)
|
|
2994
|
+
|
|
2995
|
+
yield* TestClock.adjust("10 minutes")
|
|
2996
|
+
const summary = yield* Fiber.join(fiber)
|
|
2997
|
+
yield* Console.log(`latest ${summary.pass}`)
|
|
2998
|
+
})
|
|
2999
|
+
|
|
3000
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3001
|
+
// Output:
|
|
3002
|
+
// sync 1
|
|
3003
|
+
// sync 2
|
|
3004
|
+
// sync 3
|
|
3005
|
+
// latest 3
|
|
3006
|
+
```
|
|
3007
|
+
|
|
3008
|
+
### Repeat A Repair Pass A Few Times
|
|
3009
|
+
|
|
3010
|
+
Use this to check `Schedule.take` on a spaced repeat cadence.
|
|
3011
|
+
|
|
3012
|
+
```ts runnable deterministic
|
|
3013
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3014
|
+
import { TestClock } from "effect/testing"
|
|
3015
|
+
|
|
3016
|
+
const reprocessFailedBatch = Effect.fnUntraced(function*(passes: Ref.Ref<number>) {
|
|
3017
|
+
const pass = yield* Ref.updateAndGet(passes, (n) => n + 1)
|
|
3018
|
+
yield* Console.log(`repair pass ${pass}`)
|
|
3019
|
+
})
|
|
3020
|
+
|
|
3021
|
+
const reprocessingCadence = Schedule.spaced("30 seconds").pipe(
|
|
3022
|
+
Schedule.take(3)
|
|
3023
|
+
)
|
|
3024
|
+
|
|
3025
|
+
const program = Effect.gen(function*() {
|
|
3026
|
+
const passes = yield* Ref.make(0)
|
|
3027
|
+
const fiber = yield* reprocessFailedBatch(passes).pipe(
|
|
3028
|
+
Effect.repeat(reprocessingCadence),
|
|
3029
|
+
Effect.forkScoped
|
|
3030
|
+
)
|
|
3031
|
+
|
|
3032
|
+
yield* TestClock.adjust("90 seconds")
|
|
3033
|
+
yield* Fiber.join(fiber)
|
|
3034
|
+
yield* Console.log(`passes ${yield* Ref.get(passes)}`)
|
|
3035
|
+
})
|
|
3036
|
+
|
|
3037
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3038
|
+
// Output:
|
|
3039
|
+
// repair pass 1
|
|
3040
|
+
// repair pass 2
|
|
3041
|
+
// repair pass 3
|
|
3042
|
+
// repair pass 4
|
|
3043
|
+
// passes 4
|
|
3044
|
+
```
|
|
3045
|
+
|
|
3046
|
+
### Slow A Worker Loop
|
|
3047
|
+
|
|
3048
|
+
Use this to check that successful worker iterations leave a gap.
|
|
3049
|
+
|
|
3050
|
+
```ts runnable deterministic
|
|
3051
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3052
|
+
import { TestClock } from "effect/testing"
|
|
3053
|
+
|
|
3054
|
+
type WorkerIteration =
|
|
3055
|
+
| { readonly _tag: "Processed"; readonly count: number }
|
|
3056
|
+
| { readonly _tag: "Idle" }
|
|
3057
|
+
|
|
3058
|
+
const workerIteration = Effect.fnUntraced(function*(iterations: Ref.Ref<number>) {
|
|
3059
|
+
const iteration = yield* Ref.updateAndGet(iterations, (n) => n + 1)
|
|
3060
|
+
const result: WorkerIteration = iteration === 1 ? { _tag: "Processed", count: 1 } : { _tag: "Idle" }
|
|
3061
|
+
yield* Console.log(`worker ${result._tag}`)
|
|
3062
|
+
return result
|
|
3063
|
+
})
|
|
3064
|
+
|
|
3065
|
+
const workerCadence = Schedule.spaced("500 millis").pipe(
|
|
3066
|
+
Schedule.take(2)
|
|
3067
|
+
)
|
|
3068
|
+
|
|
3069
|
+
const program = Effect.gen(function*() {
|
|
3070
|
+
const iterations = yield* Ref.make(0)
|
|
3071
|
+
const fiber = yield* workerIteration(iterations).pipe(
|
|
3072
|
+
Effect.repeat(workerCadence),
|
|
3073
|
+
Effect.forkScoped
|
|
3074
|
+
)
|
|
3075
|
+
|
|
3076
|
+
yield* TestClock.adjust("1 second")
|
|
3077
|
+
yield* Fiber.join(fiber)
|
|
3078
|
+
yield* Console.log(`iterations ${yield* Ref.get(iterations)}`)
|
|
3079
|
+
})
|
|
3080
|
+
|
|
3081
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3082
|
+
// Output:
|
|
3083
|
+
// worker Processed
|
|
3084
|
+
// worker Idle
|
|
3085
|
+
// worker Idle
|
|
3086
|
+
// iterations 3
|
|
3087
|
+
```
|
|
3088
|
+
|
|
3089
|
+
### Run One Follow-Up
|
|
3090
|
+
|
|
3091
|
+
Use this to check `Schedule.duration` for exactly one additional run.
|
|
3092
|
+
|
|
3093
|
+
```ts runnable deterministic
|
|
3094
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3095
|
+
import { TestClock } from "effect/testing"
|
|
3096
|
+
|
|
3097
|
+
const warmCache = Effect.fnUntraced(function*(runs: Ref.Ref<number>) {
|
|
3098
|
+
const run = yield* Ref.updateAndGet(runs, (n) => n + 1)
|
|
3099
|
+
yield* Console.log(`warm ${run}`)
|
|
3100
|
+
})
|
|
3101
|
+
|
|
3102
|
+
const oneMoreRun = Schedule.duration("10 seconds")
|
|
3103
|
+
|
|
3104
|
+
const program = Effect.gen(function*() {
|
|
3105
|
+
const runs = yield* Ref.make(0)
|
|
3106
|
+
const fiber = yield* warmCache(runs).pipe(
|
|
3107
|
+
Effect.repeat(oneMoreRun),
|
|
3108
|
+
Effect.forkScoped
|
|
3109
|
+
)
|
|
3110
|
+
|
|
3111
|
+
yield* TestClock.adjust("10 seconds")
|
|
3112
|
+
yield* Fiber.join(fiber)
|
|
3113
|
+
yield* Console.log(`runs ${yield* Ref.get(runs)}`)
|
|
3114
|
+
})
|
|
3115
|
+
|
|
3116
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3117
|
+
// Output:
|
|
3118
|
+
// warm 1
|
|
3119
|
+
// warm 2
|
|
3120
|
+
// runs 2
|
|
3121
|
+
```
|
|
3122
|
+
|
|
3123
|
+
### Repeat Until A Terminal Value With Options
|
|
3124
|
+
|
|
3125
|
+
Use this to check the options-form `until` predicate.
|
|
3126
|
+
|
|
3127
|
+
```ts runnable deterministic
|
|
3128
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3129
|
+
import { TestClock } from "effect/testing"
|
|
3130
|
+
|
|
3131
|
+
type TransferStatus =
|
|
3132
|
+
| { readonly _tag: "Pending" }
|
|
3133
|
+
| { readonly _tag: "InProgress" }
|
|
3134
|
+
| { readonly _tag: "Completed" }
|
|
3135
|
+
| { readonly _tag: "Failed" }
|
|
3136
|
+
|
|
3137
|
+
const readTransferStatus = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
3138
|
+
const read = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
3139
|
+
const status: TransferStatus = read < 2
|
|
3140
|
+
? { _tag: "InProgress" }
|
|
3141
|
+
: read === 2
|
|
3142
|
+
? { _tag: "Completed" }
|
|
3143
|
+
: { _tag: "Failed" }
|
|
3144
|
+
yield* Console.log(`transfer ${status._tag}`)
|
|
3145
|
+
return status
|
|
3146
|
+
})
|
|
3147
|
+
|
|
3148
|
+
const program = Effect.gen(function*() {
|
|
3149
|
+
const reads = yield* Ref.make(0)
|
|
3150
|
+
const fiber = yield* readTransferStatus(reads).pipe(
|
|
3151
|
+
Effect.repeat({
|
|
3152
|
+
schedule: Schedule.spaced("1 second"),
|
|
3153
|
+
until: (status) => status._tag === "Completed" || status._tag === "Failed"
|
|
3154
|
+
}),
|
|
3155
|
+
Effect.forkScoped
|
|
3156
|
+
)
|
|
3157
|
+
|
|
3158
|
+
yield* TestClock.adjust("1 second")
|
|
3159
|
+
const status = yield* Fiber.join(fiber)
|
|
3160
|
+
yield* Console.log(`final ${status._tag}`)
|
|
3161
|
+
})
|
|
3162
|
+
|
|
3163
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3164
|
+
// Output:
|
|
3165
|
+
// transfer InProgress
|
|
3166
|
+
// transfer Completed
|
|
3167
|
+
// final Completed
|
|
3168
|
+
```
|
|
3169
|
+
|
|
3170
|
+
### Repeat Until An Effectful Predicate Stops
|
|
3171
|
+
|
|
3172
|
+
Use this to check an effectful `until` predicate that records every successful result.
|
|
3173
|
+
|
|
3174
|
+
```ts runnable deterministic
|
|
3175
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3176
|
+
import { TestClock } from "effect/testing"
|
|
3177
|
+
|
|
3178
|
+
type ServiceProbeResult =
|
|
3179
|
+
| { readonly _tag: "Starting" }
|
|
3180
|
+
| { readonly _tag: "Ready" }
|
|
3181
|
+
| { readonly _tag: "Failed" }
|
|
3182
|
+
|
|
3183
|
+
const probeService = Effect.fnUntraced(function*(reads: Ref.Ref<number>) {
|
|
3184
|
+
const read = yield* Ref.updateAndGet(reads, (n) => n + 1)
|
|
3185
|
+
const result: ServiceProbeResult = read < 2
|
|
3186
|
+
? { _tag: "Starting" }
|
|
3187
|
+
: read === 2
|
|
3188
|
+
? { _tag: "Ready" }
|
|
3189
|
+
: { _tag: "Failed" }
|
|
3190
|
+
yield* Console.log(`probe ${result._tag}`)
|
|
3191
|
+
return result
|
|
3192
|
+
})
|
|
3193
|
+
|
|
3194
|
+
const recordReadinessProbe = (result: ServiceProbeResult): Effect.Effect<void> => Console.log(`recorded ${result._tag}`)
|
|
3195
|
+
|
|
3196
|
+
const program = Effect.gen(function*() {
|
|
3197
|
+
const reads = yield* Ref.make(0)
|
|
3198
|
+
const fiber = yield* probeService(reads).pipe(
|
|
3199
|
+
Effect.repeat({
|
|
3200
|
+
schedule: Schedule.spaced("1 second").pipe(
|
|
3201
|
+
Schedule.both(Schedule.recurs(30))
|
|
3202
|
+
),
|
|
3203
|
+
until: (result) =>
|
|
3204
|
+
recordReadinessProbe(result).pipe(
|
|
3205
|
+
Effect.map(() => result._tag === "Ready" || result._tag === "Failed")
|
|
3206
|
+
)
|
|
3207
|
+
}),
|
|
3208
|
+
Effect.forkScoped
|
|
3209
|
+
)
|
|
3210
|
+
|
|
3211
|
+
yield* TestClock.adjust("1 second")
|
|
3212
|
+
const result = yield* Fiber.join(fiber)
|
|
3213
|
+
yield* Console.log(`final ${result._tag}`)
|
|
3214
|
+
})
|
|
3215
|
+
|
|
3216
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3217
|
+
// Output:
|
|
3218
|
+
// probe Starting
|
|
3219
|
+
// recorded Starting
|
|
3220
|
+
// probe Ready
|
|
3221
|
+
// recorded Ready
|
|
3222
|
+
// final Ready
|
|
3223
|
+
```
|
|
3224
|
+
|
|
3225
|
+
### Observe Retry Input And Output
|
|
3226
|
+
|
|
3227
|
+
Use this to check schedule diagnostics without changing retry behavior.
|
|
3228
|
+
|
|
3229
|
+
```ts runnable deterministic
|
|
3230
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3231
|
+
import { TestClock } from "effect/testing"
|
|
3232
|
+
|
|
3233
|
+
type InventoryError =
|
|
3234
|
+
| { readonly _tag: "TransientInventoryError" }
|
|
3235
|
+
| { readonly _tag: "InvalidSku" }
|
|
3236
|
+
|
|
3237
|
+
const fetchInventory = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
3238
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
3239
|
+
yield* Console.log(`inventory attempt ${attempt}`)
|
|
3240
|
+
|
|
3241
|
+
if (attempt < 2) {
|
|
3242
|
+
return yield* Effect.fail({ _tag: "TransientInventoryError" } as InventoryError)
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
return ["sku-1"]
|
|
3246
|
+
})
|
|
3247
|
+
|
|
3248
|
+
const retryInventoryPolicy = Schedule.exponential("10 millis").pipe(
|
|
3249
|
+
Schedule.both(Schedule.recurs(5)),
|
|
3250
|
+
Schedule.tapInput((error: InventoryError) => Console.log(`retry input: ${error._tag}`)),
|
|
3251
|
+
Schedule.tapOutput(([_, count]) => Console.log(`retry scheduled: ${count}`))
|
|
3252
|
+
)
|
|
3253
|
+
|
|
3254
|
+
const program = Effect.gen(function*() {
|
|
3255
|
+
const attempts = yield* Ref.make(0)
|
|
3256
|
+
const fiber = yield* fetchInventory(attempts).pipe(
|
|
3257
|
+
Effect.retry({
|
|
3258
|
+
schedule: retryInventoryPolicy,
|
|
3259
|
+
while: (error) => error._tag === "TransientInventoryError"
|
|
3260
|
+
}),
|
|
3261
|
+
Effect.forkScoped
|
|
3262
|
+
)
|
|
3263
|
+
|
|
3264
|
+
yield* TestClock.adjust("10 millis")
|
|
3265
|
+
const items = yield* Fiber.join(fiber)
|
|
3266
|
+
yield* Console.log(`items ${items.length}`)
|
|
3267
|
+
})
|
|
3268
|
+
|
|
3269
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3270
|
+
// Output:
|
|
3271
|
+
// inventory attempt 1
|
|
3272
|
+
// retry input: TransientInventoryError
|
|
3273
|
+
// retry scheduled: 0
|
|
3274
|
+
// inventory attempt 2
|
|
3275
|
+
// items 1
|
|
3276
|
+
```
|
|
3277
|
+
|
|
3278
|
+
### Log Retry Counts With `bothRight`
|
|
3279
|
+
|
|
3280
|
+
Use this to check that `bothRight` keeps the recurrence count as output.
|
|
3281
|
+
|
|
3282
|
+
```ts runnable deterministic
|
|
3283
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3284
|
+
import { TestClock } from "effect/testing"
|
|
3285
|
+
|
|
3286
|
+
type UploadError =
|
|
3287
|
+
| { readonly _tag: "TransientUploadError" }
|
|
3288
|
+
| { readonly _tag: "InvalidUpload" }
|
|
3289
|
+
|
|
3290
|
+
const uploadChunk = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
3291
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
3292
|
+
yield* Console.log(`upload attempt ${attempt}`)
|
|
3293
|
+
|
|
3294
|
+
if (attempt < 3) {
|
|
3295
|
+
return yield* Effect.fail({ _tag: "TransientUploadError" } as UploadError)
|
|
3296
|
+
}
|
|
3297
|
+
})
|
|
3298
|
+
|
|
3299
|
+
const policy = Schedule.exponential("10 millis").pipe(
|
|
3300
|
+
Schedule.bothRight(Schedule.recurs(4)),
|
|
3301
|
+
Schedule.tapOutput((count) => Console.log(`retry count ${count}`))
|
|
3302
|
+
)
|
|
3303
|
+
|
|
3304
|
+
const program = Effect.gen(function*() {
|
|
3305
|
+
const attempts = yield* Ref.make(0)
|
|
3306
|
+
const fiber = yield* uploadChunk(attempts).pipe(
|
|
3307
|
+
Effect.retry({
|
|
3308
|
+
schedule: policy,
|
|
3309
|
+
while: (error) => error._tag === "TransientUploadError"
|
|
3310
|
+
}),
|
|
3311
|
+
Effect.forkScoped
|
|
3312
|
+
)
|
|
3313
|
+
|
|
3314
|
+
yield* TestClock.adjust("30 millis")
|
|
3315
|
+
yield* Fiber.join(fiber)
|
|
3316
|
+
yield* Console.log(`done after ${yield* Ref.get(attempts)}`)
|
|
3317
|
+
})
|
|
3318
|
+
|
|
3319
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3320
|
+
// Output:
|
|
3321
|
+
// upload attempt 1
|
|
3322
|
+
// retry count 0
|
|
3323
|
+
// upload attempt 2
|
|
3324
|
+
// retry count 1
|
|
3325
|
+
// upload attempt 3
|
|
3326
|
+
// done after 3
|
|
3327
|
+
```
|
|
3328
|
+
|
|
3329
|
+
## Timing Checks
|
|
3330
|
+
|
|
3331
|
+
Use `TestClock` when the timing behavior itself is the recipe contract. Fork the repeated or retried program,
|
|
3332
|
+
advance virtual time, then check the result.
|
|
3333
|
+
|
|
3334
|
+
The examples fork the program because the program is waiting for time to pass. While the fiber is suspended, `TestClock.adjust` moves virtual time forward. `Fiber.join` then waits for the forked program to finish after the expected retries or repeats have run.
|
|
3335
|
+
|
|
3336
|
+
### Assert Retry Delay
|
|
3337
|
+
|
|
3338
|
+
Use when a retry policy must not run before its configured delay.
|
|
3339
|
+
|
|
3340
|
+
```ts runnable deterministic
|
|
3341
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3342
|
+
import { TestClock } from "effect/testing"
|
|
3343
|
+
|
|
3344
|
+
const ensure = (condition: boolean, message: string) => condition ? Effect.void : Effect.fail(new Error(message))
|
|
3345
|
+
|
|
3346
|
+
const retryPolicy = Schedule.spaced("100 millis").pipe(
|
|
3347
|
+
Schedule.both(Schedule.recurs(2))
|
|
3348
|
+
)
|
|
3349
|
+
|
|
3350
|
+
const operation = Effect.fnUntraced(function*(attempts: Ref.Ref<number>) {
|
|
3351
|
+
const attempt = yield* Ref.updateAndGet(attempts, (n) => n + 1)
|
|
3352
|
+
yield* Console.log(`attempt ${attempt}`)
|
|
3353
|
+
|
|
3354
|
+
if (attempt < 3) {
|
|
3355
|
+
return yield* Effect.fail("transient" as const)
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
return "ok" as const
|
|
3359
|
+
})
|
|
3360
|
+
|
|
3361
|
+
const program = Effect.gen(function*() {
|
|
3362
|
+
const attempts = yield* Ref.make(0)
|
|
3363
|
+
const fiber = yield* operation(attempts).pipe(
|
|
3364
|
+
Effect.retry(retryPolicy),
|
|
3365
|
+
Effect.forkScoped
|
|
3366
|
+
)
|
|
3367
|
+
|
|
3368
|
+
yield* Effect.yieldNow
|
|
3369
|
+
yield* TestClock.adjust("99 millis")
|
|
3370
|
+
yield* ensure((yield* Ref.get(attempts)) === 1, "retry ran too early")
|
|
3371
|
+
|
|
3372
|
+
yield* TestClock.adjust("1 millis")
|
|
3373
|
+
yield* ensure((yield* Ref.get(attempts)) === 2, "first retry did not run")
|
|
3374
|
+
|
|
3375
|
+
yield* TestClock.adjust("100 millis")
|
|
3376
|
+
const result = yield* Fiber.join(fiber)
|
|
3377
|
+
yield* ensure(result === "ok", "retry did not succeed")
|
|
3378
|
+
yield* Console.log(`result: ${result}`)
|
|
3379
|
+
})
|
|
3380
|
+
|
|
3381
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3382
|
+
// Output:
|
|
3383
|
+
// attempt 1
|
|
3384
|
+
// attempt 2
|
|
3385
|
+
// attempt 3
|
|
3386
|
+
// result: ok
|
|
3387
|
+
```
|
|
3388
|
+
|
|
3389
|
+
### Assert Fixed Cadence
|
|
3390
|
+
|
|
3391
|
+
Use when the repeat policy should stay on aligned intervals instead of waiting after each run completes.
|
|
3392
|
+
|
|
3393
|
+
```ts runnable deterministic
|
|
3394
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3395
|
+
import { TestClock } from "effect/testing"
|
|
3396
|
+
|
|
3397
|
+
const ensure = (condition: boolean, message: string) => condition ? Effect.void : Effect.fail(new Error(message))
|
|
3398
|
+
|
|
3399
|
+
const heartbeatCadence = Schedule.fixed("1 second").pipe(
|
|
3400
|
+
Schedule.take(2)
|
|
3401
|
+
)
|
|
3402
|
+
|
|
3403
|
+
const heartbeat = Effect.fnUntraced(function*(ticks: Ref.Ref<number>) {
|
|
3404
|
+
const tick = yield* Ref.updateAndGet(ticks, (n) => n + 1)
|
|
3405
|
+
yield* Console.log(`heartbeat ${tick}`)
|
|
3406
|
+
})
|
|
3407
|
+
|
|
3408
|
+
const program = Effect.gen(function*() {
|
|
3409
|
+
const ticks = yield* Ref.make(0)
|
|
3410
|
+
const fiber = yield* heartbeat(ticks).pipe(
|
|
3411
|
+
Effect.repeat(heartbeatCadence),
|
|
3412
|
+
Effect.forkScoped
|
|
3413
|
+
)
|
|
3414
|
+
|
|
3415
|
+
yield* Effect.yieldNow
|
|
3416
|
+
yield* TestClock.adjust("1 second")
|
|
3417
|
+
yield* TestClock.adjust("1 second")
|
|
3418
|
+
yield* Fiber.join(fiber)
|
|
3419
|
+
yield* ensure((yield* Ref.get(ticks)) === 3, "expected one initial run and two fixed recurrences")
|
|
3420
|
+
})
|
|
3421
|
+
|
|
3422
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3423
|
+
// Output:
|
|
3424
|
+
// heartbeat 1
|
|
3425
|
+
// heartbeat 2
|
|
3426
|
+
// heartbeat 3
|
|
3427
|
+
```
|
|
3428
|
+
|
|
3429
|
+
### Assert Windowed Cadence
|
|
3430
|
+
|
|
3431
|
+
Use when the repeat policy should recur on wall-clock windows.
|
|
3432
|
+
|
|
3433
|
+
```ts runnable deterministic
|
|
3434
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3435
|
+
import { TestClock } from "effect/testing"
|
|
3436
|
+
|
|
3437
|
+
const ensure = (condition: boolean, message: string) => condition ? Effect.void : Effect.fail(new Error(message))
|
|
3438
|
+
|
|
3439
|
+
const flushWindow = Schedule.windowed("1 minute").pipe(
|
|
3440
|
+
Schedule.take(2)
|
|
3441
|
+
)
|
|
3442
|
+
|
|
3443
|
+
const flushMetrics = Effect.fnUntraced(function*(flushes: Ref.Ref<number>) {
|
|
3444
|
+
const flush = yield* Ref.updateAndGet(flushes, (n) => n + 1)
|
|
3445
|
+
yield* Console.log(`flush ${flush}`)
|
|
3446
|
+
})
|
|
3447
|
+
|
|
3448
|
+
const program = Effect.gen(function*() {
|
|
3449
|
+
yield* TestClock.setTime(0)
|
|
3450
|
+
const flushes = yield* Ref.make(0)
|
|
3451
|
+
const fiber = yield* flushMetrics(flushes).pipe(
|
|
3452
|
+
Effect.repeat(flushWindow),
|
|
3453
|
+
Effect.forkScoped
|
|
3454
|
+
)
|
|
3455
|
+
|
|
3456
|
+
yield* Effect.yieldNow
|
|
3457
|
+
yield* TestClock.adjust("1 minute")
|
|
3458
|
+
yield* TestClock.adjust("1 minute")
|
|
3459
|
+
yield* Fiber.join(fiber)
|
|
3460
|
+
yield* ensure((yield* Ref.get(flushes)) === 3, "expected one initial flush and two windowed recurrences")
|
|
3461
|
+
})
|
|
3462
|
+
|
|
3463
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3464
|
+
// Output:
|
|
3465
|
+
// flush 1
|
|
3466
|
+
// flush 2
|
|
3467
|
+
// flush 3
|
|
3468
|
+
```
|
|
3469
|
+
|
|
3470
|
+
### Assert Cron Cadence
|
|
3471
|
+
|
|
3472
|
+
Use when the repeat policy should recur at times selected by a cron expression.
|
|
3473
|
+
|
|
3474
|
+
```ts runnable deterministic
|
|
3475
|
+
import { Console, Effect, Fiber, Ref, Schedule } from "effect"
|
|
3476
|
+
import { TestClock } from "effect/testing"
|
|
3477
|
+
|
|
3478
|
+
const ensure = (condition: boolean, message: string) => condition ? Effect.void : Effect.fail(new Error(message))
|
|
3479
|
+
|
|
3480
|
+
const nightly = Schedule.cron("0 2 * * *").pipe(
|
|
3481
|
+
Schedule.take(1)
|
|
3482
|
+
)
|
|
3483
|
+
|
|
3484
|
+
const compactDatabase = Effect.fnUntraced(function*(runs: Ref.Ref<number>) {
|
|
3485
|
+
const run = yield* Ref.updateAndGet(runs, (n) => n + 1)
|
|
3486
|
+
yield* Console.log(`compaction ${run}`)
|
|
3487
|
+
})
|
|
3488
|
+
|
|
3489
|
+
const program = Effect.gen(function*() {
|
|
3490
|
+
yield* TestClock.setTime(new Date(2024, 0, 1, 1, 59, 59).getTime())
|
|
3491
|
+
const runs = yield* Ref.make(0)
|
|
3492
|
+
const fiber = yield* compactDatabase(runs).pipe(
|
|
3493
|
+
Effect.repeat(nightly),
|
|
3494
|
+
Effect.forkScoped
|
|
3495
|
+
)
|
|
3496
|
+
|
|
3497
|
+
yield* Effect.yieldNow
|
|
3498
|
+
yield* TestClock.adjust("1 second")
|
|
3499
|
+
yield* Fiber.join(fiber)
|
|
3500
|
+
yield* ensure((yield* Ref.get(runs)) === 2, "expected initial run and one cron recurrence")
|
|
3501
|
+
})
|
|
3502
|
+
|
|
3503
|
+
Effect.runPromise(program.pipe(Effect.provide(TestClock.layer()), Effect.scoped))
|
|
3504
|
+
// Output:
|
|
3505
|
+
// compaction 1
|
|
3506
|
+
// compaction 2
|
|
3507
|
+
```
|