@smthrs/flow 1.0.0-rc.7 → 1.0.0-rc.9

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.
Files changed (61) hide show
  1. package/README.md +3 -1
  2. package/dist/cjs/Deadline.d.ts +5 -0
  3. package/dist/cjs/Deadline.d.ts.map +1 -1
  4. package/dist/cjs/Deadline.js +9 -1
  5. package/dist/cjs/Deadline.js.map +2 -2
  6. package/dist/cjs/DurableClock.d.ts +9 -10
  7. package/dist/cjs/DurableClock.d.ts.map +1 -1
  8. package/dist/cjs/DurableClock.js +19 -2
  9. package/dist/cjs/DurableClock.js.map +2 -2
  10. package/dist/cjs/Flow/make.d.ts +2 -1
  11. package/dist/cjs/Flow/make.d.ts.map +1 -1
  12. package/dist/cjs/Flow/make.js +3 -0
  13. package/dist/cjs/Flow/make.js.map +2 -2
  14. package/dist/cjs/HumanTask.d.ts.map +1 -1
  15. package/dist/cjs/HumanTask.js +2 -2
  16. package/dist/cjs/HumanTask.js.map +2 -2
  17. package/dist/cjs/Poll.d.ts +3 -1
  18. package/dist/cjs/Poll.d.ts.map +1 -1
  19. package/dist/cjs/Poll.js +6 -2
  20. package/dist/cjs/Poll.js.map +2 -2
  21. package/dist/cjs/Sleep.d.ts.map +1 -1
  22. package/dist/cjs/Sleep.js +15 -7
  23. package/dist/cjs/Sleep.js.map +2 -2
  24. package/dist/esm/Deadline.d.ts +5 -0
  25. package/dist/esm/Deadline.d.ts.map +1 -1
  26. package/dist/esm/Deadline.js +13 -1
  27. package/dist/esm/Deadline.js.map +1 -1
  28. package/dist/esm/DurableClock.d.ts +9 -10
  29. package/dist/esm/DurableClock.d.ts.map +1 -1
  30. package/dist/esm/DurableClock.js +49 -2
  31. package/dist/esm/DurableClock.js.map +1 -1
  32. package/dist/esm/Flow/make.d.ts +2 -1
  33. package/dist/esm/Flow/make.d.ts.map +1 -1
  34. package/dist/esm/Flow/make.js +5 -0
  35. package/dist/esm/Flow/make.js.map +1 -1
  36. package/dist/esm/HumanTask.d.ts.map +1 -1
  37. package/dist/esm/HumanTask.js +5 -3
  38. package/dist/esm/HumanTask.js.map +1 -1
  39. package/dist/esm/Poll.d.ts +3 -1
  40. package/dist/esm/Poll.d.ts.map +1 -1
  41. package/dist/esm/Poll.js +10 -3
  42. package/dist/esm/Poll.js.map +1 -1
  43. package/dist/esm/Sleep.d.ts.map +1 -1
  44. package/dist/esm/Sleep.js +30 -4
  45. package/dist/esm/Sleep.js.map +1 -1
  46. package/docs/README.md +3 -1
  47. package/docs/api.md +21 -17
  48. package/docs/guides/ask-a-person.md +5 -5
  49. package/docs/guides/inspect-the-plan.md +1 -1
  50. package/docs/guides/poll-until-ready.md +3 -1
  51. package/docs/guides/wait-for-a-deadline.md +5 -5
  52. package/docs/installation.md +14 -9
  53. package/docs/quickstart.md +5 -1
  54. package/docs/reference/flow.md +9 -5
  55. package/package.json +7 -7
  56. package/src/Deadline.ts +17 -1
  57. package/src/DurableClock.ts +45 -2
  58. package/src/Flow/make.ts +7 -1
  59. package/src/HumanTask.ts +5 -3
  60. package/src/Poll.ts +10 -4
  61. package/src/Sleep.ts +33 -4
@@ -1 +1 @@
1
- {"version":3,"file":"HumanTask.js","sourceRoot":"","sources":["../../src/HumanTask.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Asking a person something, as an ordinary declared action.\n *\n * {@link module:WaitFor.action} is the general rendezvous: it parks until\n * something outside the run resolves it, and settles with whatever arrived.\n * That is the right primitive and the wrong affordance for a human, because a\n * human answers in prose, in a hurry, and sometimes wrongly. Smithers 0.x had\n * the affordance — a `HumanTask` with a kind, a prompt, an output schema, an\n * attempt budget, and a deadline — and this module is that affordance rebuilt\n * on the primitives this package already has.\n *\n * Four things are added to a bare wait:\n *\n * - a **kind**, so the four shapes a person is asked for (`ask`, `confirm`,\n * `select`, `json`) are stated rather than reconstructed from a schema;\n * - **validation**, so an answer that does not fit is refused where it arrives\n * instead of failing three steps later as a decode error;\n * - **re-asking**, so a refused answer parks again on a NEW wait point rather\n * than ending the run, until the attempt budget is spent;\n * - a **deadline**, so a question nobody answers settles instead of parking\n * forever.\n *\n * All four are built from what is already durable. Each attempt is its own wait\n * point in {@link module:WaitFor}'s namespace — `WaitFor/<name>#<attempt>` — so\n * an answer is recorded exactly as any other durable deferred completion is, a\n * refused answer stays recorded under the attempt that refused it, and a\n * re-driven round replays every answer it already has before parking on the\n * first attempt that has none. The reason an answer was refused is recorded\n * too, as a sealed step named `HumanTask/<name>#<attempt>/rejected`, so the\n * judgment the run made sits in the journal beside the answer it judged rather\n * than in a log stream nobody replays. The park is declared through the ordinary\n * waiting vocabulary under `approval`, carrying the current attempt's token, so\n * whoever collects answers always sees the one wait point that is open. The\n * deadline is a {@link module:DurableClock} armed once per task and raced\n * against every attempt, which makes it a deadline for the QUESTION rather than\n * for one attempt at answering it.\n *\n * @since 0.1.0\n */\n\nimport * as Node from \"@smthrs/plan/Node\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as DurableClock from \"./DurableClock.ts\"\nimport * as DurableDeferred from \"./DurableDeferred.ts\"\nimport { FlowInstance } from \"./FlowRuntime/FlowInstance.ts\"\nimport { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport { annotateWaiting } from \"./FlowRuntime/WaitingAnnotation.ts\"\nimport * as BoundedJson from \"./internal/BoundedJson.ts\"\nimport * as JsonSchemaSubset from \"./internal/JsonSchemaSubset.ts\"\nimport * as WaitFor from \"./WaitFor.ts\"\n\n/**\n * The shape a person is asked for.\n *\n * `ask` wants prose, `confirm` wants yes or no, `select` wants one of the\n * options the task names, and `json` wants a value the task's JSON Schema\n * accepts. The kind is what an interface renders and what {@link validate}\n * checks; it is stated rather than inferred, because a text box and a two-\n * button confirmation are different questions even when both answers happen to\n * be strings.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Kind = \"ask\" | \"confirm\" | \"select\" | \"json\"\n\n/**\n * The schema the `kind` payload field is declared with.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Kind: Schema.Literals<readonly [\"ask\", \"confirm\", \"select\", \"json\"]> = Schema.Literals([\n \"ask\",\n \"confirm\",\n \"select\",\n \"json\"\n])\n\n/**\n * A question that ended without an answer the task could accept.\n *\n * `request_invalid` is the author's mistake — a `select` with no options, an\n * attempt budget below one, a deadline that is not a length of time, a JSON\n * Schema outside the supported subset — and is refused before anyone is asked\n * anything. `rejected` means every attempt was\n * spent on answers the task refused, and `timeout` means the deadline passed\n * with the question still open. All three are typed failures rather than\n * defects, so a body recovers from them with `Node.catch` like any other\n * declared failure — which is the point, because \"the reviewer never answered\"\n * is an ordinary outcome of asking a reviewer.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class HumanTaskFailed extends Schema.TaggedError<HumanTaskFailed>()(\n \"@smthrs/flow/HumanTaskFailed\",\n {\n code: Schema.Literals([\"request_invalid\", \"rejected\", \"timeout\"]),\n task: Schema.String,\n attempts: Schema.Number,\n rejections: Schema.Array(Schema.String),\n message: Schema.String\n }\n) {}\n\n/**\n * An answer refused before it could consume durable storage.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class HumanAnswerInvalid extends Schema.TaggedError<HumanAnswerInvalid>()(\n \"@smthrs/flow/HumanAnswerInvalid\",\n {\n code: Schema.Literals([\"answer_invalid\", \"answer_not_open\"]).pipe(\n Schema.withConstructorDefault(Effect.succeed(\"answer_invalid\"))\n ),\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the human-task declaration is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const tag = \"system/human-task\"\n\n/**\n * The attempt budget a task that names none is asked under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const defaultMaxAttempts = 10\n\n/**\n * The deepest supported JSON Schema path, counting the root as depth zero.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxSchemaDepth = JsonSchemaSubset.maxSchemaDepth\n\n/**\n * The most schema objects one human-task request may contain.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxSchemaNodes = JsonSchemaSubset.maxSchemaNodes\n\n/**\n * The most JSON values embedded across schema keywords such as `enum`.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaValueNodes = JsonSchemaSubset.maxSchemaValueNodes\n\n/**\n * The deepest JSON value embedded in a schema, including enum members.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaValueDepth = JsonSchemaSubset.maxSchemaValueDepth\n\n/**\n * The most JSON values one answer validation may visit.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxAnswerNodes = JsonSchemaSubset.maxAnswerNodes\n\n/**\n * The largest encoded JSON answer that can enter the durable store.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxAnswerBytes = JsonSchemaSubset.maxAnswerBytes\n\n/**\n * The largest encoded JSON Schema carried by one question.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaBytes = JsonSchemaSubset.maxSchemaBytes\n\n/**\n * The deepest admitted answer tree.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxAnswerDepth = JsonSchemaSubset.maxAnswerDepth\n\n/**\n * The largest encoded string value admitted in a request or answer.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonStringBytes = JsonSchemaSubset.maxJsonStringBytes\n\n/**\n * The largest encoded object key admitted in a request or answer.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonKeyBytes = JsonSchemaSubset.maxJsonKeyBytes\n\n/**\n * The most members admitted in one JSON array or object.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonMembers = JsonSchemaSubset.maxJsonMembers\n\n/**\n * The largest encoded task name.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxTaskNameBytes = 1_024\n\n/**\n * The largest encoded prompt.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxPromptBytes = 64 * 1024\n\n/**\n * The largest option list on one select question.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxOptions = 256\n\n/**\n * The largest encoded select option.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxOptionBytes = 4 * 1024\n\n/**\n * The largest attempt budget one human task may declare.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxAttemptBudget = 1_000\n\n/**\n * The most caller-supplied characters retained in one rendered diagnostic.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxDiagnosticChars = JsonSchemaSubset.maxDiagnosticChars\n\n/**\n * The most characters retained across a terminal failure's rejection list.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxRetainedRejectionChars = 8_192\n\n/**\n * The durable deferred one attempt at answering resolves through.\n *\n * Attempts are separate wait points on purpose. A durable deferred records the\n * FIRST completion and replays it forever, so re-asking on the same wait point\n * would read the refused answer again on every round; a new point per attempt\n * makes \"answer again\" expressible without a second completion mechanism, and\n * leaves the refused answers in place as the record of what was asked and what\n * came back.\n *\n * The name lives in {@link module:WaitFor}'s namespace because an answer is\n * completed through exactly the {@link module:WaitFor} path: a token, and\n * `DurableDeferred.succeed`. {@link answer} is that call with the token parsed\n * for you.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const deferred = (\n name: string,\n attempt: number\n): DurableDeferred.DurableDeferred<typeof Schema.Json> => WaitFor.deferred(`${name}#${attempt}`)\n\n/** The namespace {@link module:WaitFor.deferred} prepends to every wait point. */\nconst waitForNamespace = /^WaitFor\\//\n\n/**\n * The only spelling of an attempt {@link deferred} writes: a canonical decimal\n * with no sign, no leading zero, no exponent, and no fraction.\n *\n * `Number()` alone is not this check. `NaN`, `Infinity`, `-Infinity`, and\n * `1e+21` all survive a `Number()`/`String()` round trip character for\n * character, so a gate that rebuilt the name through `Number()` would accept\n * them as attempts the action never parks on.\n */\nconst attemptSuffix = /^(0|[1-9][0-9]*)$/\n\n/**\n * The deferred a token addresses, when {@link deferred} could have named it.\n *\n * {@link answer} builds its deferred FROM the caller's token, so\n * `DurableDeferred.done`'s own name check compares a name against itself and\n * can never refuse. This is the check that is not vacuous: the name is taken\n * apart at its attempt separator, the suffix is required to be a canonical\n * decimal naming an attempt the action can park on (a safe integer of one or\n * more), the name is rebuilt through {@link deferred}, and it is accepted only\n * when the rebuilt name is character-for-character the one the token carried.\n * A name {@link deferred} cannot produce -- a `DurableQueue` item address, a\n * plain {@link module:WaitFor} gate, another flow's deferred, a suffix spelled\n * any way other than the decimal {@link deferred} writes -- fails, so a\n * foreign token cannot be completed under `Schema.Json` through the\n * human-answer path.\n *\n * This bounds the confusion to human-task wait points. It does NOT distinguish\n * two human tasks from each other, nor a wait point an author deliberately\n * named to collide with one; separating those needs the deferred's schema and\n * purpose carried in the address itself, which is a wire-format change.\n *\n * @private\n */\nconst answerableDeferred = (\n deferredName: string\n): DurableDeferred.DurableDeferred<typeof Schema.Json> | undefined => {\n const separator = deferredName.lastIndexOf(\"#\")\n if (separator === -1) return undefined\n const suffix = deferredName.slice(separator + 1)\n if (!attemptSuffix.test(suffix)) return undefined\n const attempt = Number(suffix)\n if (!Number.isSafeInteger(attempt) || attempt < 1) return undefined\n const rebuilt = deferred(deferredName.slice(0, separator).replace(waitForNamespace, \"\"), attempt)\n return rebuilt.name === deferredName ? rebuilt : undefined\n}\n\n/**\n * The clock a task's deadline is armed on.\n *\n * One clock per task, not one per attempt: the deadline bounds how long the\n * QUESTION stays open, so re-asking does not extend it. The name is the task's,\n * so every attempt's race arms and awaits the same durable timer — a clock row\n * keeps the deadline it was first armed with, which is what makes the bound\n * survive a restart.\n *\n * The deadline settles on both hosts. `@smthrs/engine`'s in-process engine and\n * the SQLite engine store each re-drive a run parked on\n * {@link module:DurableDeferred.raceAll}, so the durable timer wakes the\n * question wherever it was asked\n * (`packages/smithers/flows/engine-store/test/RacedParkResume.test.ts`).\n *\n * @private\n */\nconst timeoutClockName = (name: string): string => `HumanTask/${name}/timeout`\n\n/**\n * What one attempt settled with: the answer that arrived, or the deadline.\n *\n * @private\n */\nconst Settled = Schema.Union([\n Schema.Struct({ _tag: Schema.Literal(\"answered\"), value: Schema.Json }),\n Schema.Struct({ _tag: Schema.Literal(\"timeout\") })\n])\n\ntype Settled = typeof Settled.Type\n\n/**\n * What {@link validate} checks an answer against.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Request {\n readonly kind: Kind\n /** Ignored for `json`: an option list alone constrains no JSON value. */\n readonly options?: ReadonlyArray<string> | undefined\n readonly schema?: unknown\n}\n\n/**\n * Checks that a JSON Schema stays inside the bounded subset, at every depth.\n *\n * This is a different question from {@link validate}, which asks whether an\n * ANSWER fits a schema. A question is worth refusing before anyone is asked it:\n * a constraint the subset cannot enforce would otherwise be silently dropped,\n * and the person would be told their answer was fine when half of what was\n * asked for went unchecked.\n *\n * The subset is `type` (`object`, `array`, `string`, `number`, `integer`,\n * `boolean`, `null`), `enum`, `properties`, `required`, `items`, and\n * `nullable`; it lives in `internal/JsonSchemaSubset`.\n *\n * Returns the first reason the schema is out of bounds, or `undefined` when the\n * whole tree is inside it.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const validateSchema = (\n schema: unknown,\n path: ReadonlyArray<string> = []\n): string | undefined => JsonSchemaSubset.validateSchema(schema, path)\n\n/**\n * Checks one answer against the question that was asked.\n *\n * Returns the reason the answer was refused, or `undefined` when it fits. It is\n * exported because it is the same check an interface should run BEFORE it\n * records an answer: refusing a typo in the text box the person is looking at\n * is worth more than refusing it one durable round later.\n *\n * ```ts\n * const rejection = HumanTask.validate(value, { kind: \"select\", options })\n * if (rejection !== undefined) return showTheReviewer(rejection)\n * ```\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const validate = (value: unknown, request: Request): string | undefined => {\n switch (request.kind) {\n case \"ask\":\n if (typeof value !== \"string\") return \"The answer must be a string.\"\n return BoundedJson.textFits(value, maxJsonStringBytes)\n ? undefined\n : `The answer must fit within ${maxJsonStringBytes} encoded bytes of well-formed text.`\n case \"confirm\":\n return typeof value === \"boolean\" ? undefined : \"The answer must be a boolean.\"\n case \"select\": {\n if (request.options === undefined || request.options.length === 0) {\n return \"The question declares no options to choose from.\"\n }\n if (\n request.options.length > maxOptions ||\n request.options.some((option) => typeof option !== \"string\" || !BoundedJson.textFits(option, maxOptionBytes))\n ) {\n return `The question's options exceed the ${maxOptions}-member or ${maxOptionBytes}-byte limits.`\n }\n return request.options.some((option) => option === value)\n ? undefined\n : `The answer ${JsonSchemaSubset.renderDiagnostic(value)} is not one of ${\n JsonSchemaSubset.truncateDiagnostic(request.options.join(\", \"))\n }.`\n }\n default: {\n if (request.schema !== undefined) return JsonSchemaSubset.check(value, request.schema)\n const admitted = BoundedJson.admit(value, JsonSchemaSubset.answerLimits)\n return admitted.ok ? undefined : admitted.complaint\n }\n }\n}\n\n/**\n * The fields a question is asked with.\n *\n * @private\n */\nconst boundedText = (label: string, maximumBytes: number) =>\n Schema.String.check(\n Schema.makeFilter(\n (value) =>\n BoundedJson.textFits(value, maximumBytes) ||\n `${label} must be well-formed text within ${maximumBytes} encoded bytes`,\n { title: `bounded${label.replaceAll(\" \", \"\")}` }\n )\n )\n\nconst boundedSchema = Schema.Json.check(\n Schema.makeFilter(\n (value) => {\n const admitted = BoundedJson.admit(value, JsonSchemaSubset.schemaLimits)\n return admitted.ok || admitted.complaint\n },\n { title: \"boundedHumanTaskSchema\" }\n )\n)\n\nconst option = boundedText(\"option\", maxOptionBytes)\n\nconst payloadFields = {\n name: boundedText(\"task name\", maxTaskNameBytes),\n kind: Kind,\n prompt: boundedText(\"prompt\", maxPromptBytes),\n options: Schema.optional(\n Schema.Array(option).check(Schema.isMaxLength(maxOptions))\n ),\n schema: Schema.optional(boundedSchema),\n timeoutMs: Schema.optional(Schema.Number),\n maxAttempts: Schema.optional(Schema.Number)\n}\n\n/**\n * The declared human-task action.\n *\n * **When to use**\n *\n * Use it in a body wherever the next step depends on a person: a release that\n * needs sign-off, a plan that needs a choice, a field the run cannot compute.\n * The node settles with the answer, so downstream steps read it the way they\n * read any other step result, and {@link decode} gives that answer the caller's\n * own type.\n *\n * ```ts\n * HumanTask.action.call({\n * name: \"release\",\n * kind: \"select\",\n * prompt: \"Which build should ship?\",\n * options: [\"canary\", \"stable\"],\n * timeoutMs: 6 * 60 * 60 * 1000\n * })\n * ```\n *\n * `name` addresses the question, so two calls naming one question in one\n * execution await one answer — the same reading {@link module:WaitFor} takes,\n * for the same reason: a person has to be able to name what they are answering.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const action: Action.Declared<\n typeof tag,\n Schema.Struct<typeof payloadFields>,\n typeof Schema.Json,\n typeof HumanTaskFailed,\n never\n> = Action.makeSystem(tag, {\n payload: payloadFields,\n success: Schema.Json,\n error: HumanTaskFailed,\n tier: \"sealed\"\n})\n\n/** The payload the action is called with, as its implementation reads it. */\ntype Payload = typeof action.payloadSchema.Type\n\n/**\n * The question a park publishes about itself, as the JSON text the durable\n * waiting row stores.\n *\n * It is the payload's own fields, narrowed to what an interface needs to\n * render an answer box and refuse a bad answer before a person sends it: the\n * kind picks the control, the prompt is the question, `options` fills a\n * select, `schema` validates a JSON answer, and the two attempt numbers say\n * how much of the budget is left. The answer VALUE never appears here — this\n * describes the question, not what anybody said.\n *\n * @private\n */\nconst declaredQuestion = (\n payload: Payload,\n attempt: number,\n maxAttempts: number\n): string =>\n JSON.stringify({\n task: \"human\",\n name: payload.name,\n kind: payload.kind,\n prompt: payload.prompt,\n attempt,\n maxAttempts,\n ...(payload.options === undefined ? {} : { options: [...payload.options] }),\n ...(payload.schema === undefined ? {} : { schema: payload.schema })\n })\n\n/** Builds the failure a task settles with, with the reasons it refused. */\nconst failed = (\n payload: Payload,\n code: typeof HumanTaskFailed.Type[\"code\"],\n attempts: number,\n rejections: ReadonlyArray<string>,\n message: string\n): HumanTaskFailed => new HumanTaskFailed({ code, task: payload.name, attempts, rejections, message })\n\ninterface RetainedRejections {\n readonly entries: Array<string>\n chars: number\n omitted: number\n}\n\n/** Describes the tail of a rejection history that could not be retained. */\nconst omissionMarker = (count: number): string => `${count} further rejections were omitted.`\n\n/** Adds one rejection while keeping the terminal rejection history bounded. */\nconst retainRejection = (state: RetainedRejections, rejection: string): void => {\n const alreadyOmitting = state.omitted > 0\n if (alreadyOmitting) {\n const previousMarker = state.entries.pop()!\n state.chars -= previousMarker.length\n }\n if (!alreadyOmitting && state.chars + rejection.length <= maxRetainedRejectionChars) {\n state.entries.push(rejection)\n state.chars += rejection.length\n return\n }\n state.omitted++\n let marker = omissionMarker(state.omitted)\n while (state.chars + marker.length > maxRetainedRejectionChars) {\n const removed = state.entries.pop()!\n state.chars -= removed.length\n state.omitted++\n marker = omissionMarker(state.omitted)\n }\n state.entries.push(marker)\n state.chars += marker.length\n}\n\n/**\n * The attempt budget a payload asks for, refused when it is not a whole number\n * of attempts.\n *\n * @private\n */\nconst budgetOf = (payload: Payload): Effect.Effect<number, HumanTaskFailed> => {\n const maxAttempts = payload.maxAttempts ?? defaultMaxAttempts\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n return Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with maxAttempts ${maxAttempts}. A question is asked at least once.`\n )\n )\n }\n return maxAttempts <= maxAttemptBudget\n ? Effect.succeed(maxAttempts)\n : Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with maxAttempts ${maxAttempts}. A question asked more than ` +\n `${maxAttemptBudget} times is a stuck question.`\n )\n )\n}\n\n/**\n * The deadline a payload asks for, refused when it is not a length of time.\n *\n * `Duration.millis` takes any number, so an unchecked deadline does not fail —\n * it changes what the question means. `NaN` becomes zero and a negative stays\n * negative, so both are deadlines that have ALREADY passed: the task times out\n * on its first park and nobody is ever really asked. `Infinity` becomes the\n * infinite duration, so the race has a branch that can never win and a question\n * asked with a deadline has none. All three are the same authoring mistake as a\n * `select` with no options, and they are refused in the same place, before the\n * first park, rather than surfacing as a `timeout` nobody waited for.\n *\n * @private\n */\nconst deadlineOf = (payload: Payload): Effect.Effect<Duration.Duration | undefined, HumanTaskFailed> => {\n const timeoutMs = payload.timeoutMs\n if (timeoutMs === undefined) return Effect.succeed(undefined)\n return Number.isFinite(timeoutMs) && timeoutMs >= 0\n ? Effect.succeed(Duration.millis(timeoutMs))\n : Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with timeoutMs ${timeoutMs}. A deadline is a finite number of ` +\n \"milliseconds that is not negative; the question would be over before it was asked, or \" +\n \"never over at all.\"\n )\n )\n}\n\n/**\n * Refuses a question nobody could answer correctly, before anyone is asked.\n *\n * A `select` with no options and a schema outside the supported subset are\n * authoring mistakes whose only symptom at answer time would be a person being\n * refused for something they did not do, so {@link validateSchema} walks the\n * whole schema tree before the first park rather than waiting for an answer to\n * reach the keyword nobody can enforce.\n *\n * An `options` list on a `json` question is ignored because it constrains\n * nothing; JSON constraints come only from `schema`.\n *\n * @private\n */\nconst requestOf = (payload: Payload): Effect.Effect<Request, HumanTaskFailed> => {\n const request: Request = {\n kind: payload.kind,\n options: payload.options,\n ...(payload.schema === undefined ? {} : { schema: payload.schema })\n }\n if (payload.schema !== undefined && payload.kind !== \"json\") {\n return Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `A schema describes a \"json\" answer, and this question is an \"${payload.kind}\".`\n )\n )\n }\n if (payload.kind === \"select\" && (payload.options === undefined || payload.options.length === 0)) {\n return Effect.fail(\n failed(payload, \"request_invalid\", 0, [], `${tag} was called as a select with no options to choose from.`)\n )\n }\n if (payload.kind === \"json\" && payload.schema !== undefined) {\n const complaint = validateSchema(payload.schema)\n if (complaint !== undefined) return Effect.fail(failed(payload, \"request_invalid\", 0, [], complaint))\n }\n return Effect.succeed(request)\n}\n\n/**\n * Awaits one attempt, racing the answer against the task's deadline.\n *\n * The race is named per attempt because a durable race records its winner: one\n * name across attempts would replay the first attempt's answer forever. The\n * clock is NOT named per attempt, for the mirrored reason — the deadline is one\n * fact about the question.\n *\n * @private\n */\nconst attemptOnce = (\n payload: Payload,\n attempt: number,\n deadline: Duration.Duration | undefined\n): Effect.Effect<Settled, never, Crypto.Crypto | FlowRuntime | FlowInstance> => {\n const answered: Effect.Effect<Settled, never, FlowRuntime | FlowInstance> = Effect.map(\n DurableDeferred.await(deferred(payload.name, attempt)),\n (value): Settled => ({ _tag: \"answered\", value })\n )\n if (deadline === undefined) return answered\n return DurableDeferred.raceAll({\n name: `HumanTask/${payload.name}#${attempt}`,\n success: Settled,\n error: Schema.Never,\n effects: [\n answered,\n Effect.as(\n DurableClock.sleep({\n name: timeoutClockName(payload.name),\n duration: deadline,\n // The wait is the point: the question parks the execution rather than\n // holding a fiber open for however long a person takes.\n inMemoryThreshold: Duration.zero\n }),\n { _tag: \"timeout\" } as const\n )\n ]\n })\n}\n\n/**\n * What a refusal records: which question, which attempt, and why the answer\n * could not be accepted.\n *\n * @private\n */\nconst RejectionRecord = Schema.Struct({\n task: Schema.String,\n attempt: Schema.Number,\n reason: Schema.String\n})\n\n/**\n * Records that an answer was refused, as a step rather than as a log line.\n *\n * A refusal is a decision the run made about something a person did, and it is\n * the only part of the exchange that is not already durable: the answer is\n * recorded under its own wait point and the re-ask is recorded as the next\n * one, but WHY the answer was refused would otherwise live in a log stream\n * nobody replays. `Action.make` is the recorded boundary the rest of this\n * package already writes through — {@link module:DurableClock.sleep} takes it\n * for an in-memory wait — so the refusal is dispatched, journaled, and\n * replayed like any other sealed step, under a name that carries the task and\n * the attempt it refused.\n *\n * The payload names no time. A record whose contents depend on the wall clock\n * would differ between the first run and its replay, which is the one thing a\n * journaled decision may never do.\n *\n * @private\n */\nconst recordRejection = (task: string, attempt: number, reason: string) =>\n Action.make({\n name: `HumanTask/${task}#${attempt}/rejected`,\n tier: \"sealed\",\n success: RejectionRecord,\n execute: Effect.succeed({ task, attempt, reason })\n })\n\n/**\n * The human-task implementation: park, validate, re-ask, settle.\n *\n * Provide it beside the other action implementation layers a body calls, over\n * the {@link module:Implementations.layerImplementations} table they file\n * themselves in:\n *\n * ```ts\n * Layer.mergeAll(HumanTask.layer, Interpreter.layer(Release)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = action.toLayer((payload) =>\n Effect.gen(function*() {\n const instance = yield* FlowInstance\n const maxAttempts = yield* budgetOf(payload)\n const deadline = yield* deadlineOf(payload)\n const request = yield* requestOf(payload)\n const retained: RetainedRejections = { entries: [], chars: 0, omitted: 0 }\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const token = DurableDeferred.tokenFromExecutionId(deferred(payload.name, attempt), {\n flow: instance.flow,\n executionId: instance.executionId\n })\n // Declared rather than left to a driver's derivation, so the run parks\n // under `approval` carrying the ONE wait point that is currently open.\n // The question travels with the park for a different reason than the\n // token does: the token addresses the wait, and the question is what an\n // inbox has to render for a person to answer it at all. Without it a\n // parked run carried nothing but a wait name, so run-3's\n // `coding-clarification` could be listed and never asked.\n yield* annotateWaiting({ reason: \"approval\", token, request: declaredQuestion(payload, attempt, maxAttempts) })\n const settled = yield* attemptOnce(payload, attempt, deadline)\n // The attempt is over either way, so the declared park is over too. A\n // raced attempt awaits on a copied instance, so the branch that resolved\n // could not clear the annotation itself.\n yield* annotateWaiting(undefined)\n if (settled._tag === \"timeout\") {\n return yield* Effect.fail(\n failed(\n payload,\n \"timeout\",\n attempt,\n retained.entries,\n `Nobody answered \"${payload.name}\" within ${payload.timeoutMs} ms.`\n )\n )\n }\n const rejection = validate(settled.value, request)\n if (rejection === undefined) return settled.value\n retainRejection(retained, `attempt ${attempt}: ${rejection}`)\n yield* recordRejection(payload.name, attempt, rejection)\n }\n return yield* Effect.fail(\n failed(\n payload,\n \"rejected\",\n maxAttempts,\n retained.entries,\n `\"${payload.name}\" was asked ${maxAttempts} times without an answer it could accept.`\n )\n )\n })\n)\n\n/**\n * Records one answer to a human task.\n *\n * `token` is the value the run parked with — the `token` of the waiting\n * annotation, which is the wait point the current attempt is open on. The\n * runtime checks that exact approval wait and records the completion as one\n * mutation, so guessed, unopened, or stale attempt tokens cannot pre-answer a\n * run. An admitted answer then resumes the run through the ordinary durable\n * deferred path.\n *\n * The token is not a credential: anyone who knows or derives the address of\n * the open attempt can answer it. The host decides who may answer, and must\n * authorize the caller before calling `answer`.\n *\n * ```ts\n * yield* HumanTask.answer({ token: waiting.token, value: { decision: \"ship\" } })\n * ```\n *\n * The durable JSON boundary is validated here: the value must be inert,\n * detached, and within the exported depth, node, member, string, key, and byte\n * limits. Whether that JSON fits the question is the task's own judgment, made\n * where its kind and schema are known; a semantically refused answer stays\n * recorded so the record shows what was actually said. Call {@link validate}\n * first when the interface can refuse it while the person is still looking at\n * it.\n *\n * The TOKEN is checked. This entry point completes an arbitrary caller-supplied\n * address under `Schema.Json`, so it accepts only a token whose deferred name\n * {@link deferred} could have written. A token for a `DurableQueue` item, a\n * plain {@link module:WaitFor} gate, or an unrelated flow's deferred fails with\n * `TokenInvalid` carrying `deferred_mismatch` rather than writing a JSON exit\n * into a row another schema will decode.\n *\n * A syntactically valid HumanTask token can still fail with\n * `HumanAnswerInvalid` carrying `answer_not_open` when the addressed run is not\n * currently parked on that exact approval token.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const answer = (options: {\n readonly token: DurableDeferred.Token\n readonly value: typeof Schema.Json.Type\n}): Effect.Effect<void, DurableDeferred.TokenInvalid | HumanAnswerInvalid, FlowRuntime> =>\n Effect.gen(function*() {\n const admitted = BoundedJson.admit(options.value, JsonSchemaSubset.answerLimits)\n if (!admitted.ok) {\n return yield* Effect.fail(\n new HumanAnswerInvalid({\n message: `The human-task answer was not recorded: ${JsonSchemaSubset.truncateDiagnostic(admitted.complaint)}`\n })\n )\n }\n const parsed = yield* DurableDeferred.TokenParsed.parse(options.token)\n const target = answerableDeferred(parsed.deferredName)\n if (target === undefined) {\n return yield* Effect.fail(\n new DurableDeferred.TokenInvalid({\n code: \"deferred_mismatch\",\n message:\n `The token addresses deferred \"${\n JsonSchemaSubset.truncateDiagnostic(parsed.deferredName)\n }\", which is not a ` +\n \"human task wait point. Answer it through the surface that owns it.\"\n })\n )\n }\n const runtime = yield* FlowRuntime\n const outcome = yield* runtime.deferredDoneIfWaiting(target, {\n flowName: parsed.flowName,\n executionId: parsed.executionId,\n deferredName: parsed.deferredName,\n reason: \"approval\",\n token: options.token,\n exit: Exit.succeed(admitted.value)\n })\n if (outcome === \"NotWaiting\") {\n return yield* Effect.fail(\n new HumanAnswerInvalid({\n code: \"answer_not_open\",\n message: \"The human-task answer was not recorded because the run is not parked on this approval token.\"\n })\n )\n }\n })\n\n/**\n * Gives an answer the caller's own type.\n *\n * The action's success schema is `Schema.Json`, because what a person may\n * answer is described by the task's payload rather than by a TypeScript type.\n * `decode` is the other half: the author states the schema they were really\n * asking for, and the node downstream of the question carries that type.\n *\n * ```ts\n * const Decision = Schema.Struct({ decision: Schema.Literals([\"ship\", \"hold\"]) })\n *\n * HumanTask.action.call({ name: \"release\", kind: \"json\", prompt, schema }).pipe(\n * HumanTask.decode(Decision)\n * )\n * ```\n *\n * The two descriptions must agree. The answer already passed the task's own\n * JSON Schema before it reached here, so a value this schema rejects means the\n * payload's schema and the author's schema describe different answers — an\n * authoring mistake, not a human one — and it surfaces as a defect naming the\n * offending path rather than as a failure a body could sensibly catch.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const decode =\n <S extends Schema.ConstraintDecoder<unknown>>(schema: S) =>\n <E, R>(self: Node.Node<typeof Schema.Json.Type, E, R>): Node.Node<S[\"Type\"], E, R> =>\n Node.map(self, Schema.decodeUnknownSync(schema))\n"]}
1
+ {"version":3,"file":"HumanTask.js","sourceRoot":"","sources":["../../src/HumanTask.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Asking a person something, as an ordinary declared action.\n *\n * {@link module:WaitFor.action} is the general rendezvous: it parks until\n * something outside the run resolves it, and settles with whatever arrived.\n * That is the right primitive and the wrong affordance for a human, because a\n * human answers in prose, in a hurry, and sometimes wrongly. Smithers 0.x had\n * the affordance — a `HumanTask` with a kind, a prompt, an output schema, an\n * attempt budget, and a deadline — and this module is that affordance rebuilt\n * on the primitives this package already has.\n *\n * Four things are added to a bare wait:\n *\n * - a **kind**, so the four shapes a person is asked for (`ask`, `confirm`,\n * `select`, `json`) are stated rather than reconstructed from a schema;\n * - **validation**, so an answer that does not fit is refused where it arrives\n * instead of failing three steps later as a decode error;\n * - **re-asking**, so a refused answer parks again on a NEW wait point rather\n * than ending the run, until the attempt budget is spent;\n * - a **deadline**, so a question nobody answers settles instead of parking\n * forever.\n *\n * All four are built from what is already durable. Each attempt is its own wait\n * point in {@link module:WaitFor}'s namespace — `WaitFor/<name>#<attempt>` — so\n * an answer is recorded exactly as any other durable deferred completion is, a\n * refused answer stays recorded under the attempt that refused it, and a\n * re-driven round replays every answer it already has before parking on the\n * first attempt that has none. The reason an answer was refused is recorded\n * too, as a sealed step named `HumanTask/<name>#<attempt>/rejected`, so the\n * judgment the run made sits in the journal beside the answer it judged rather\n * than in a log stream nobody replays. The park is declared through the ordinary\n * waiting vocabulary under `approval`, carrying the current attempt's token, so\n * whoever collects answers always sees the one wait point that is open. The\n * deadline is a {@link module:DurableClock} armed once per task and raced\n * against every attempt, which makes it a deadline for the QUESTION rather than\n * for one attempt at answering it.\n *\n * @since 0.1.0\n */\n\nimport * as Node from \"@smthrs/plan/Node\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as DurableClock from \"./DurableClock.ts\"\nimport * as DurableDeferred from \"./DurableDeferred.ts\"\nimport { FlowInstance } from \"./FlowRuntime/FlowInstance.ts\"\nimport { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport { annotateWaiting } from \"./FlowRuntime/WaitingAnnotation.ts\"\nimport * as BoundedJson from \"./internal/BoundedJson.ts\"\nimport * as JsonSchemaSubset from \"./internal/JsonSchemaSubset.ts\"\nimport * as WaitFor from \"./WaitFor.ts\"\n\n/**\n * The shape a person is asked for.\n *\n * `ask` wants prose, `confirm` wants yes or no, `select` wants one of the\n * options the task names, and `json` wants a value the task's JSON Schema\n * accepts. The kind is what an interface renders and what {@link validate}\n * checks; it is stated rather than inferred, because a text box and a two-\n * button confirmation are different questions even when both answers happen to\n * be strings.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Kind = \"ask\" | \"confirm\" | \"select\" | \"json\"\n\n/**\n * The schema the `kind` payload field is declared with.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Kind: Schema.Literals<readonly [\"ask\", \"confirm\", \"select\", \"json\"]> = Schema.Literals([\n \"ask\",\n \"confirm\",\n \"select\",\n \"json\"\n])\n\n/**\n * A question that ended without an answer the task could accept.\n *\n * `request_invalid` is the author's mistake — a `select` with no options, an\n * attempt budget below one, a deadline that is not a length of time, a JSON\n * Schema outside the supported subset — and is refused before anyone is asked\n * anything. `rejected` means every attempt was\n * spent on answers the task refused, and `timeout` means the deadline passed\n * with the question still open. All three are typed failures rather than\n * defects, so a body recovers from them with `Node.catch` like any other\n * declared failure — which is the point, because \"the reviewer never answered\"\n * is an ordinary outcome of asking a reviewer.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class HumanTaskFailed extends Schema.TaggedError<HumanTaskFailed>()(\n \"@smthrs/flow/HumanTaskFailed\",\n {\n code: Schema.Literals([\"request_invalid\", \"rejected\", \"timeout\"]),\n task: Schema.String,\n attempts: Schema.Number,\n rejections: Schema.Array(Schema.String),\n message: Schema.String\n }\n) {}\n\n/**\n * An answer refused before it could consume durable storage.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class HumanAnswerInvalid extends Schema.TaggedError<HumanAnswerInvalid>()(\n \"@smthrs/flow/HumanAnswerInvalid\",\n {\n code: Schema.Literals([\"answer_invalid\", \"answer_not_open\"]).pipe(\n Schema.withConstructorDefault(Effect.succeed(\"answer_invalid\"))\n ),\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the human-task declaration is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const tag = \"system/human-task\"\n\n/**\n * The attempt budget a task that names none is asked under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const defaultMaxAttempts = 10\n\n/**\n * The deepest supported JSON Schema path, counting the root as depth zero.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxSchemaDepth = JsonSchemaSubset.maxSchemaDepth\n\n/**\n * The most schema objects one human-task request may contain.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxSchemaNodes = JsonSchemaSubset.maxSchemaNodes\n\n/**\n * The most JSON values embedded across schema keywords such as `enum`.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaValueNodes = JsonSchemaSubset.maxSchemaValueNodes\n\n/**\n * The deepest JSON value embedded in a schema, including enum members.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaValueDepth = JsonSchemaSubset.maxSchemaValueDepth\n\n/**\n * The most JSON values one answer validation may visit.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxAnswerNodes = JsonSchemaSubset.maxAnswerNodes\n\n/**\n * The largest encoded JSON answer that can enter the durable store.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxAnswerBytes = JsonSchemaSubset.maxAnswerBytes\n\n/**\n * The largest encoded JSON Schema carried by one question.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxSchemaBytes = JsonSchemaSubset.maxSchemaBytes\n\n/**\n * The deepest admitted answer tree.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxAnswerDepth = JsonSchemaSubset.maxAnswerDepth\n\n/**\n * The largest encoded string value admitted in a request or answer.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonStringBytes = JsonSchemaSubset.maxJsonStringBytes\n\n/**\n * The largest encoded object key admitted in a request or answer.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonKeyBytes = JsonSchemaSubset.maxJsonKeyBytes\n\n/**\n * The most members admitted in one JSON array or object.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxJsonMembers = JsonSchemaSubset.maxJsonMembers\n\n/**\n * The largest encoded task name.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxTaskNameBytes = 1_024\n\n/**\n * The largest encoded prompt.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxPromptBytes = 64 * 1024\n\n/**\n * The largest option list on one select question.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxOptions = 256\n\n/**\n * The largest encoded select option.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const maxOptionBytes = 4 * 1024\n\n/**\n * The largest attempt budget one human task may declare.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxAttemptBudget = 1_000\n\n/**\n * The most caller-supplied characters retained in one rendered diagnostic.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxDiagnosticChars = JsonSchemaSubset.maxDiagnosticChars\n\n/**\n * The most characters retained across a terminal failure's rejection list.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const maxRetainedRejectionChars = 8_192\n\n/**\n * The durable deferred one attempt at answering resolves through.\n *\n * Attempts are separate wait points on purpose. A durable deferred records the\n * FIRST completion and replays it forever, so re-asking on the same wait point\n * would read the refused answer again on every round; a new point per attempt\n * makes \"answer again\" expressible without a second completion mechanism, and\n * leaves the refused answers in place as the record of what was asked and what\n * came back.\n *\n * The name lives in {@link module:WaitFor}'s namespace because an answer is\n * completed through exactly the {@link module:WaitFor} path: a token, and\n * `DurableDeferred.succeed`. {@link answer} is that call with the token parsed\n * for you.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const deferred = (\n name: string,\n attempt: number\n): DurableDeferred.DurableDeferred<typeof Schema.Json> => WaitFor.deferred(`${name}#${attempt}`)\n\n/** The namespace {@link module:WaitFor.deferred} prepends to every wait point. */\nconst waitForNamespace = /^WaitFor\\//\n\n/**\n * The only spelling of an attempt {@link deferred} writes: a canonical decimal\n * with no sign, no leading zero, no exponent, and no fraction.\n *\n * `Number()` alone is not this check. `NaN`, `Infinity`, `-Infinity`, and\n * `1e+21` all survive a `Number()`/`String()` round trip character for\n * character, so a gate that rebuilt the name through `Number()` would accept\n * them as attempts the action never parks on.\n */\nconst attemptSuffix = /^(0|[1-9][0-9]*)$/\n\n/**\n * The deferred a token addresses, when {@link deferred} could have named it.\n *\n * {@link answer} builds its deferred FROM the caller's token, so\n * `DurableDeferred.done`'s own name check compares a name against itself and\n * can never refuse. This is the check that is not vacuous: the name is taken\n * apart at its attempt separator, the suffix is required to be a canonical\n * decimal naming an attempt the action can park on (a safe integer of one or\n * more), the name is rebuilt through {@link deferred}, and it is accepted only\n * when the rebuilt name is character-for-character the one the token carried.\n * A name {@link deferred} cannot produce -- a `DurableQueue` item address, a\n * plain {@link module:WaitFor} gate, another flow's deferred, a suffix spelled\n * any way other than the decimal {@link deferred} writes -- fails, so a\n * foreign token cannot be completed under `Schema.Json` through the\n * human-answer path.\n *\n * This bounds the confusion to human-task wait points. It does NOT distinguish\n * two human tasks from each other, nor a wait point an author deliberately\n * named to collide with one; separating those needs the deferred's schema and\n * purpose carried in the address itself, which is a wire-format change.\n *\n * @private\n */\nconst answerableDeferred = (\n deferredName: string\n): DurableDeferred.DurableDeferred<typeof Schema.Json> | undefined => {\n const separator = deferredName.lastIndexOf(\"#\")\n if (separator === -1) return undefined\n const suffix = deferredName.slice(separator + 1)\n if (!attemptSuffix.test(suffix)) return undefined\n const attempt = Number(suffix)\n if (!Number.isSafeInteger(attempt) || attempt < 1) return undefined\n const rebuilt = deferred(deferredName.slice(0, separator).replace(waitForNamespace, \"\"), attempt)\n return rebuilt.name === deferredName ? rebuilt : undefined\n}\n\n/**\n * The clock a task's deadline is armed on.\n *\n * One clock per task, not one per attempt: the deadline bounds how long the\n * QUESTION stays open, so re-asking does not extend it. The name is the task's,\n * so every attempt's race arms and awaits the same durable timer — a clock row\n * keeps the deadline it was first armed with, which is what makes the bound\n * survive a restart.\n *\n * The deadline settles on both hosts. `@smthrs/engine`'s in-process engine and\n * the SQLite engine store each re-drive a run parked on\n * {@link module:DurableDeferred.raceAll}, so the durable timer wakes the\n * question wherever it was asked\n * (`packages/smithers/flows/engine-store/test/RacedParkResume.test.ts`).\n *\n * @private\n */\nconst timeoutClockName = (name: string): string => `HumanTask/${name}/timeout`\n\n/**\n * What one attempt settled with: the answer that arrived, or the deadline.\n *\n * @private\n */\nconst Settled = Schema.Union([\n Schema.Struct({ _tag: Schema.Literal(\"answered\"), value: Schema.Json }),\n Schema.Struct({ _tag: Schema.Literal(\"timeout\") })\n])\n\ntype Settled = typeof Settled.Type\n\n/**\n * What {@link validate} checks an answer against.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Request {\n readonly kind: Kind\n /** Ignored for `json`: an option list alone constrains no JSON value. */\n readonly options?: ReadonlyArray<string> | undefined\n readonly schema?: unknown\n}\n\n/**\n * Checks that a JSON Schema stays inside the bounded subset, at every depth.\n *\n * This is a different question from {@link validate}, which asks whether an\n * ANSWER fits a schema. A question is worth refusing before anyone is asked it:\n * a constraint the subset cannot enforce would otherwise be silently dropped,\n * and the person would be told their answer was fine when half of what was\n * asked for went unchecked.\n *\n * The subset is `type` (`object`, `array`, `string`, `number`, `integer`,\n * `boolean`, `null`), `enum`, `properties`, `required`, `items`, and\n * `nullable`; it lives in `internal/JsonSchemaSubset`.\n *\n * Returns the first reason the schema is out of bounds, or `undefined` when the\n * whole tree is inside it.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const validateSchema = (\n schema: unknown,\n path: ReadonlyArray<string> = []\n): string | undefined => JsonSchemaSubset.validateSchema(schema, path)\n\n/**\n * Checks one answer against the question that was asked.\n *\n * Returns the reason the answer was refused, or `undefined` when it fits. It is\n * exported because it is the same check an interface should run BEFORE it\n * records an answer: refusing a typo in the text box the person is looking at\n * is worth more than refusing it one durable round later.\n *\n * ```ts\n * const rejection = HumanTask.validate(value, { kind: \"select\", options })\n * if (rejection !== undefined) return showTheReviewer(rejection)\n * ```\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const validate = (value: unknown, request: Request): string | undefined => {\n switch (request.kind) {\n case \"ask\":\n if (typeof value !== \"string\") return \"The answer must be a string.\"\n return BoundedJson.textFits(value, maxJsonStringBytes)\n ? undefined\n : `The answer must fit within ${maxJsonStringBytes} encoded bytes of well-formed text.`\n case \"confirm\":\n return typeof value === \"boolean\" ? undefined : \"The answer must be a boolean.\"\n case \"select\": {\n if (request.options === undefined || request.options.length === 0) {\n return \"The question declares no options to choose from.\"\n }\n if (\n request.options.length > maxOptions ||\n request.options.some((option) => typeof option !== \"string\" || !BoundedJson.textFits(option, maxOptionBytes))\n ) {\n return `The question's options exceed the ${maxOptions}-member or ${maxOptionBytes}-byte limits.`\n }\n return request.options.some((option) => option === value)\n ? undefined\n : `The answer ${JsonSchemaSubset.renderDiagnostic(value)} is not one of ${\n JsonSchemaSubset.truncateDiagnostic(request.options.join(\", \"))\n }.`\n }\n default: {\n if (request.schema !== undefined) return JsonSchemaSubset.check(value, request.schema)\n const admitted = BoundedJson.admit(value, JsonSchemaSubset.answerLimits)\n return admitted.ok ? undefined : admitted.complaint\n }\n }\n}\n\n/**\n * The fields a question is asked with.\n *\n * @private\n */\nconst boundedText = (label: string, maximumBytes: number) =>\n Schema.String.check(\n Schema.makeFilter(\n (value) =>\n BoundedJson.textFits(value, maximumBytes) ||\n `${label} must be well-formed text within ${maximumBytes} encoded bytes`,\n { title: `bounded${label.replaceAll(\" \", \"\")}` }\n )\n )\n\nconst boundedSchema = Schema.Json.check(\n Schema.makeFilter(\n (value) => {\n const admitted = BoundedJson.admit(value, JsonSchemaSubset.schemaLimits)\n return admitted.ok || admitted.complaint\n },\n { title: \"boundedHumanTaskSchema\" }\n )\n)\n\nconst option = boundedText(\"option\", maxOptionBytes)\n\nconst payloadFields = {\n name: boundedText(\"task name\", maxTaskNameBytes),\n kind: Kind,\n prompt: boundedText(\"prompt\", maxPromptBytes),\n options: Schema.optional(\n Schema.Array(option).check(Schema.isMaxLength(maxOptions))\n ),\n schema: Schema.optional(boundedSchema),\n timeoutMs: Schema.optional(Schema.Number),\n maxAttempts: Schema.optional(Schema.Number)\n}\n\n/**\n * The declared human-task action.\n *\n * **When to use**\n *\n * Use it in a body wherever the next step depends on a person: a release that\n * needs sign-off, a plan that needs a choice, a field the run cannot compute.\n * The node settles with the answer, so downstream steps read it the way they\n * read any other step result, and {@link decode} gives that answer the caller's\n * own type.\n *\n * ```ts\n * HumanTask.action.call({\n * name: \"release\",\n * kind: \"select\",\n * prompt: \"Which build should ship?\",\n * options: [\"canary\", \"stable\"],\n * timeoutMs: 6 * 60 * 60 * 1000\n * })\n * ```\n *\n * `name` addresses the question, so two calls naming one question in one\n * execution await one answer — the same reading {@link module:WaitFor} takes,\n * for the same reason: a person has to be able to name what they are answering.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const action: Action.Declared<\n typeof tag,\n Schema.Struct<typeof payloadFields>,\n typeof Schema.Json,\n typeof HumanTaskFailed,\n never\n> = Action.makeSystem(tag, {\n payload: payloadFields,\n success: Schema.Json,\n error: HumanTaskFailed,\n tier: \"sealed\"\n})\n\n/** The payload the action is called with, as its implementation reads it. */\ntype Payload = typeof action.payloadSchema.Type\n\n/**\n * The question a park publishes about itself, as the JSON text the durable\n * waiting row stores.\n *\n * It is the payload's own fields, narrowed to what an interface needs to\n * render an answer box and refuse a bad answer before a person sends it: the\n * kind picks the control, the prompt is the question, `options` fills a\n * select, `schema` validates a JSON answer, and the two attempt numbers say\n * how much of the budget is left. The answer VALUE never appears here — this\n * describes the question, not what anybody said.\n *\n * @private\n */\nconst declaredQuestion = (\n payload: Payload,\n attempt: number,\n maxAttempts: number\n): string =>\n JSON.stringify({\n task: \"human\",\n name: payload.name,\n kind: payload.kind,\n prompt: payload.prompt,\n attempt,\n maxAttempts,\n ...(payload.options === undefined ? {} : { options: [...payload.options] }),\n ...(payload.schema === undefined ? {} : { schema: payload.schema })\n })\n\n/** Builds the failure a task settles with, with the reasons it refused. */\nconst failed = (\n payload: Payload,\n code: typeof HumanTaskFailed.Type[\"code\"],\n attempts: number,\n rejections: ReadonlyArray<string>,\n message: string\n): HumanTaskFailed => new HumanTaskFailed({ code, task: payload.name, attempts, rejections, message })\n\ninterface RetainedRejections {\n readonly entries: Array<string>\n chars: number\n omitted: number\n}\n\n/** Describes the tail of a rejection history that could not be retained. */\nconst omissionMarker = (count: number): string => `${count} further rejections were omitted.`\n\n/** Adds one rejection while keeping the terminal rejection history bounded. */\nconst retainRejection = (state: RetainedRejections, rejection: string): void => {\n const alreadyOmitting = state.omitted > 0\n if (alreadyOmitting) {\n const previousMarker = state.entries.pop()!\n state.chars -= previousMarker.length\n }\n if (!alreadyOmitting && state.chars + rejection.length <= maxRetainedRejectionChars) {\n state.entries.push(rejection)\n state.chars += rejection.length\n return\n }\n state.omitted++\n let marker = omissionMarker(state.omitted)\n while (state.chars + marker.length > maxRetainedRejectionChars) {\n const removed = state.entries.pop()!\n state.chars -= removed.length\n state.omitted++\n marker = omissionMarker(state.omitted)\n }\n state.entries.push(marker)\n state.chars += marker.length\n}\n\n/**\n * The attempt budget a payload asks for, refused when it is not a whole number\n * of attempts.\n *\n * @private\n */\nconst budgetOf = (payload: Payload): Effect.Effect<number, HumanTaskFailed> => {\n const maxAttempts = payload.maxAttempts ?? defaultMaxAttempts\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n return Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with maxAttempts ${maxAttempts}. A question is asked at least once.`\n )\n )\n }\n return maxAttempts <= maxAttemptBudget\n ? Effect.succeed(maxAttempts)\n : Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with maxAttempts ${maxAttempts}. A question asked more than ` +\n `${maxAttemptBudget} times is a stuck question.`\n )\n )\n}\n\n/**\n * The deadline a payload asks for, refused when it is not a length of time.\n *\n * `Duration.millis` takes any number, so an unchecked deadline does not fail —\n * it changes what the question means. `NaN` becomes zero and a negative stays\n * negative, so both are deadlines that have ALREADY passed: the task times out\n * on its first park and nobody is ever really asked. `Infinity` becomes the\n * infinite duration, so the race has a branch that can never win and a question\n * asked with a deadline has none. All three are the same authoring mistake as a\n * `select` with no options, and they are refused in the same place, before the\n * first park, rather than surfacing as a `timeout` nobody waited for.\n *\n * @private\n */\nconst deadlineOf = (payload: Payload): Effect.Effect<Duration.Duration | undefined, HumanTaskFailed> => {\n const timeoutMs = payload.timeoutMs\n if (timeoutMs === undefined) return Effect.succeed(undefined)\n // The timeout clock is due at its start plus the deadline, and a durable\n // store records that due time as a safe integer.\n return Number.isFinite(timeoutMs) && timeoutMs >= 0 && timeoutMs <= Number.MAX_SAFE_INTEGER\n ? Effect.succeed(Duration.millis(timeoutMs))\n : Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `${tag} was called with timeoutMs ${timeoutMs}. A deadline is a number of milliseconds ` +\n `from 0 to ${Number.MAX_SAFE_INTEGER}; the question would be over before it was asked, or ` +\n \"never over at all.\"\n )\n )\n}\n\n/**\n * Refuses a question nobody could answer correctly, before anyone is asked.\n *\n * A `select` with no options and a schema outside the supported subset are\n * authoring mistakes whose only symptom at answer time would be a person being\n * refused for something they did not do, so {@link validateSchema} walks the\n * whole schema tree before the first park rather than waiting for an answer to\n * reach the keyword nobody can enforce.\n *\n * An `options` list on a `json` question is ignored because it constrains\n * nothing; JSON constraints come only from `schema`.\n *\n * @private\n */\nconst requestOf = (payload: Payload): Effect.Effect<Request, HumanTaskFailed> => {\n const request: Request = {\n kind: payload.kind,\n options: payload.options,\n ...(payload.schema === undefined ? {} : { schema: payload.schema })\n }\n if (payload.schema !== undefined && payload.kind !== \"json\") {\n return Effect.fail(\n failed(\n payload,\n \"request_invalid\",\n 0,\n [],\n `A schema describes a \"json\" answer, and this question is an \"${payload.kind}\".`\n )\n )\n }\n if (payload.kind === \"select\" && (payload.options === undefined || payload.options.length === 0)) {\n return Effect.fail(\n failed(payload, \"request_invalid\", 0, [], `${tag} was called as a select with no options to choose from.`)\n )\n }\n if (payload.kind === \"json\" && payload.schema !== undefined) {\n const complaint = validateSchema(payload.schema)\n if (complaint !== undefined) return Effect.fail(failed(payload, \"request_invalid\", 0, [], complaint))\n }\n return Effect.succeed(request)\n}\n\n/**\n * Awaits one attempt, racing the answer against the task's deadline.\n *\n * The race is named per attempt because a durable race records its winner: one\n * name across attempts would replay the first attempt's answer forever. The\n * clock is NOT named per attempt, for the mirrored reason — the deadline is one\n * fact about the question.\n *\n * @private\n */\nconst attemptOnce = (\n payload: Payload,\n attempt: number,\n deadline: Duration.Duration | undefined\n): Effect.Effect<Settled, never, Crypto.Crypto | FlowRuntime | FlowInstance> => {\n const answered: Effect.Effect<Settled, never, FlowRuntime | FlowInstance> = Effect.map(\n DurableDeferred.await(deferred(payload.name, attempt)),\n (value): Settled => ({ _tag: \"answered\", value })\n )\n if (deadline === undefined) return answered\n return DurableDeferred.raceAll({\n name: `HumanTask/${payload.name}#${attempt}`,\n success: Settled,\n error: Schema.Never,\n effects: [\n answered,\n Effect.as(\n DurableClock.sleep({\n name: timeoutClockName(payload.name),\n duration: deadline,\n // The wait is the point: the question parks the execution rather than\n // holding a fiber open for however long a person takes.\n inMemoryThreshold: Duration.zero\n }),\n { _tag: \"timeout\" } as const\n )\n ]\n })\n}\n\n/**\n * What a refusal records: which question, which attempt, and why the answer\n * could not be accepted.\n *\n * @private\n */\nconst RejectionRecord = Schema.Struct({\n task: Schema.String,\n attempt: Schema.Number,\n reason: Schema.String\n})\n\n/**\n * Records that an answer was refused, as a step rather than as a log line.\n *\n * A refusal is a decision the run made about something a person did, and it is\n * the only part of the exchange that is not already durable: the answer is\n * recorded under its own wait point and the re-ask is recorded as the next\n * one, but WHY the answer was refused would otherwise live in a log stream\n * nobody replays. `Action.make` is the recorded boundary the rest of this\n * package already writes through — {@link module:DurableClock.sleep} takes it\n * for an in-memory wait — so the refusal is dispatched, journaled, and\n * replayed like any other sealed step, under a name that carries the task and\n * the attempt it refused.\n *\n * The payload names no time. A record whose contents depend on the wall clock\n * would differ between the first run and its replay, which is the one thing a\n * journaled decision may never do.\n *\n * @private\n */\nconst recordRejection = (task: string, attempt: number, reason: string) =>\n Action.make({\n name: `HumanTask/${task}#${attempt}/rejected`,\n tier: \"sealed\",\n success: RejectionRecord,\n execute: Effect.succeed({ task, attempt, reason })\n })\n\n/**\n * The human-task implementation: park, validate, re-ask, settle.\n *\n * Provide it beside the other action implementation layers a body calls, over\n * the {@link module:Implementations.layerImplementations} table they file\n * themselves in:\n *\n * ```ts\n * Layer.mergeAll(HumanTask.layer, Interpreter.layer(Release)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = action.toLayer((payload) =>\n Effect.gen(function*() {\n const instance = yield* FlowInstance\n const maxAttempts = yield* budgetOf(payload)\n const deadline = yield* deadlineOf(payload)\n const request = yield* requestOf(payload)\n const retained: RetainedRejections = { entries: [], chars: 0, omitted: 0 }\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const token = DurableDeferred.tokenFromExecutionId(deferred(payload.name, attempt), {\n flow: instance.flow,\n executionId: instance.executionId\n })\n // Declared rather than left to a driver's derivation, so the run parks\n // under `approval` carrying the ONE wait point that is currently open.\n // The question travels with the park for a different reason than the\n // token does: the token addresses the wait, and the question is what an\n // inbox has to render for a person to answer it at all. Without it a\n // parked run carried nothing but a wait name, so run-3's\n // `coding-clarification` could be listed and never asked.\n yield* annotateWaiting({ reason: \"approval\", token, request: declaredQuestion(payload, attempt, maxAttempts) })\n const settled = yield* attemptOnce(payload, attempt, deadline)\n // The attempt is over either way, so the declared park is over too. A\n // raced attempt awaits on a copied instance, so the branch that resolved\n // could not clear the annotation itself.\n yield* annotateWaiting(undefined)\n if (settled._tag === \"timeout\") {\n return yield* Effect.fail(\n failed(\n payload,\n \"timeout\",\n attempt,\n retained.entries,\n `Nobody answered \"${payload.name}\" within ${payload.timeoutMs} ms.`\n )\n )\n }\n const rejection = validate(settled.value, request)\n if (rejection === undefined) return settled.value\n retainRejection(retained, `attempt ${attempt}: ${rejection}`)\n yield* recordRejection(payload.name, attempt, rejection)\n }\n return yield* Effect.fail(\n failed(\n payload,\n \"rejected\",\n maxAttempts,\n retained.entries,\n `\"${payload.name}\" was asked ${maxAttempts} times without an answer it could accept.`\n )\n )\n })\n)\n\n/**\n * Records one answer to a human task.\n *\n * `token` is the value the run parked with — the `token` of the waiting\n * annotation, which is the wait point the current attempt is open on. The\n * runtime checks that exact approval wait and records the completion as one\n * mutation, so guessed, unopened, or stale attempt tokens cannot pre-answer a\n * run. An admitted answer then resumes the run through the ordinary durable\n * deferred path.\n *\n * The token is not a credential: anyone who knows or derives the address of\n * the open attempt can answer it. The host decides who may answer, and must\n * authorize the caller before calling `answer`.\n *\n * ```ts\n * yield* HumanTask.answer({ token: waiting.token, value: { decision: \"ship\" } })\n * ```\n *\n * The durable JSON boundary is validated here: the value must be inert,\n * detached, and within the exported depth, node, member, string, key, and byte\n * limits. Whether that JSON fits the question is the task's own judgment, made\n * where its kind and schema are known; a semantically refused answer stays\n * recorded so the record shows what was actually said. Call {@link validate}\n * first when the interface can refuse it while the person is still looking at\n * it.\n *\n * The TOKEN is checked. This entry point completes an arbitrary caller-supplied\n * address under `Schema.Json`, so it accepts only a token whose deferred name\n * {@link deferred} could have written. A token for a `DurableQueue` item, a\n * plain {@link module:WaitFor} gate, or an unrelated flow's deferred fails with\n * `TokenInvalid` carrying `deferred_mismatch` rather than writing a JSON exit\n * into a row another schema will decode.\n *\n * A syntactically valid HumanTask token can still fail with\n * `HumanAnswerInvalid` carrying `answer_not_open` when the addressed run is not\n * currently parked on that exact approval token.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const answer = (options: {\n readonly token: DurableDeferred.Token\n readonly value: typeof Schema.Json.Type\n}): Effect.Effect<void, DurableDeferred.TokenInvalid | HumanAnswerInvalid, FlowRuntime> =>\n Effect.gen(function*() {\n const admitted = BoundedJson.admit(options.value, JsonSchemaSubset.answerLimits)\n if (!admitted.ok) {\n return yield* Effect.fail(\n new HumanAnswerInvalid({\n message: `The human-task answer was not recorded: ${JsonSchemaSubset.truncateDiagnostic(admitted.complaint)}`\n })\n )\n }\n const parsed = yield* DurableDeferred.TokenParsed.parse(options.token)\n const target = answerableDeferred(parsed.deferredName)\n if (target === undefined) {\n return yield* Effect.fail(\n new DurableDeferred.TokenInvalid({\n code: \"deferred_mismatch\",\n message:\n `The token addresses deferred \"${\n JsonSchemaSubset.truncateDiagnostic(parsed.deferredName)\n }\", which is not a ` +\n \"human task wait point. Answer it through the surface that owns it.\"\n })\n )\n }\n const runtime = yield* FlowRuntime\n const outcome = yield* runtime.deferredDoneIfWaiting(target, {\n flowName: parsed.flowName,\n executionId: parsed.executionId,\n deferredName: parsed.deferredName,\n reason: \"approval\",\n token: options.token,\n exit: Exit.succeed(admitted.value)\n })\n if (outcome === \"NotWaiting\") {\n return yield* Effect.fail(\n new HumanAnswerInvalid({\n code: \"answer_not_open\",\n message: \"The human-task answer was not recorded because the run is not parked on this approval token.\"\n })\n )\n }\n })\n\n/**\n * Gives an answer the caller's own type.\n *\n * The action's success schema is `Schema.Json`, because what a person may\n * answer is described by the task's payload rather than by a TypeScript type.\n * `decode` is the other half: the author states the schema they were really\n * asking for, and the node downstream of the question carries that type.\n *\n * ```ts\n * const Decision = Schema.Struct({ decision: Schema.Literals([\"ship\", \"hold\"]) })\n *\n * HumanTask.action.call({ name: \"release\", kind: \"json\", prompt, schema }).pipe(\n * HumanTask.decode(Decision)\n * )\n * ```\n *\n * The two descriptions must agree. The answer already passed the task's own\n * JSON Schema before it reached here, so a value this schema rejects means the\n * payload's schema and the author's schema describe different answers — an\n * authoring mistake, not a human one — and it surfaces as a defect naming the\n * offending path rather than as a failure a body could sensibly catch.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const decode =\n <S extends Schema.ConstraintDecoder<unknown>>(schema: S) =>\n <E, R>(self: Node.Node<typeof Schema.Json.Type, E, R>): Node.Node<S[\"Type\"], E, R> =>\n Node.map(self, Schema.decodeUnknownSync(schema))\n"]}
@@ -198,7 +198,9 @@ export type PayloadSchema<Input extends Schema.Struct.Fields> = Schema.Struct<In
198
198
  * with a plan that looks correct. The check is on the schedule, not only on the
199
199
  * interval: `{ intervalMs: 1000, maxAttempts: 2000, backoff: "exponential" }`
200
200
  * states three finite options and asks for an infinite wait, so the LAST wait
201
- * the poll can arm has to be a length too. `maxAttempts` is the lineage's round
201
+ * the poll can arm has to be a length too, and no longer than
202
+ * `Number.MAX_SAFE_INTEGER` milliseconds, the latest deadline a durable timer
203
+ * records. `maxAttempts` is the lineage's round
202
204
  * budget, and a budget below one attempt reaches `Flow.make` as a `maxRounds`
203
205
  * complaint about an option the author never wrote.
204
206
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Poll.d.ts","sourceRoot":"","sources":["../../src/Poll.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Poll.d.ts","sourceRoot":"","sources":["../../src/Poll.ts"],"names":[],"mappings":"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"}
package/dist/esm/Poll.js CHANGED
@@ -90,7 +90,10 @@ export const exhausted = Action.makeSystem(exhaustedTag, {
90
90
  * @category combinators
91
91
  * @since 0.1.0
92
92
  */
93
- export const delayMillis = (options) => options.intervalMs *
93
+ export const delayMillis = (options) =>
94
+ // A zero interval is zero under every backoff. Multiplying it by a factor
95
+ // that has overflowed to `Infinity` would make it `NaN`.
96
+ options.intervalMs === 0 ? 0 : options.intervalMs *
94
97
  (options.backoff === "fixed" ? 1 : options.backoff === "linear" ? options.attempt : 2 ** (options.attempt - 1));
95
98
  /**
96
99
  * What a poll round can fail with.
@@ -184,7 +187,9 @@ const withAttempt = (payload, attempt) => ({ ...payload, attempt });
184
187
  * with a plan that looks correct. The check is on the schedule, not only on the
185
188
  * interval: `{ intervalMs: 1000, maxAttempts: 2000, backoff: "exponential" }`
186
189
  * states three finite options and asks for an infinite wait, so the LAST wait
187
- * the poll can arm has to be a length too. `maxAttempts` is the lineage's round
190
+ * the poll can arm has to be a length too, and no longer than
191
+ * `Number.MAX_SAFE_INTEGER` milliseconds, the latest deadline a durable timer
192
+ * records. `maxAttempts` is the lineage's round
188
193
  * budget, and a budget below one attempt reaches `Flow.make` as a `maxRounds`
189
194
  * complaint about an option the author never wrote.
190
195
  *
@@ -225,7 +230,9 @@ export const make = (tag, options) => {
225
230
  const longestWaitMs = maxAttempts > 1
226
231
  ? delayMillis({ intervalMs, backoff, attempt: maxAttempts - 1 })
227
232
  : 0;
228
- if (!Number.isFinite(longestWaitMs)) {
233
+ // A durable timer records its deadline as a safe integer of milliseconds,
234
+ // so `Sleep` refuses a longer wait on the round that reaches it.
235
+ if (!Number.isFinite(longestWaitMs) || longestWaitMs > Number.MAX_SAFE_INTEGER) {
229
236
  throw new RangeError(`Poll.make: "${tag}" asks for a wait of ${longestWaitMs} ms before its last attempt. An ` +
230
237
  `intervalMs of ${intervalMs} under the "${backoff}" backoff reaches a length no clock ` +
231
238
  `can be armed with by attempt ${maxAttempts - 1} of ${maxAttempts}. Lower ` +
@@ -1 +1 @@
1
- {"version":3,"file":"Poll.js","sourceRoot":"","sources":["../../src/Poll.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Polling as a durable lineage: one attempt per round, a durable wait between\n * attempts, and a bound the lineage settles on.\n *\n * Smithers 0.x had a `Poller` component — a check, an interval, a backoff, an\n * attempt bound, and what to do at that bound. The pieces it was built from\n * already exist here: {@link module:Sleep.action} is the durable wait, and\n * `Flow.to` opens the next round. What was missing is the shape that puts them\n * together, and that is this module.\n *\n * A poll is a flow whose body is ONE attempt. It runs the check, and branches:\n * a satisfied check settles the lineage with the check's own output, an\n * unsatisfied one sleeps for this attempt's delay and hands off to the next\n * round with the attempt counter raised. The last attempt has no sleep and no\n * handoff — it either answers with the output it just read (`return-last`) or\n * fails {@link PollExhausted} (`fail`).\n *\n * Everything durable about it is durable for the ordinary reason. Each attempt\n * is a round with its own keyed plan nodes, so a check that already ran replays\n * from its recorded outcome instead of running again; the wait between attempts\n * is a durable timer, so a process that dies during it resumes on the round it\n * left rather than starting the poll over; and the attempt counter travels in\n * the payload, so nothing about \"which attempt is this\" lives in memory.\n *\n * The bound is stated twice on purpose. The body ends the lineage on the last\n * attempt, and `maxAttempts` is also the flow's `maxRounds` budget, so a\n * lineage that somehow opened another round is refused by the engine rather\n * than polling forever.\n *\n * @since 0.1.0\n */\n\nimport * as Node from \"@smthrs/plan/Node\"\nimport type * as Repetition from \"@smthrs/plan/Repetition\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as Flow from \"./Flow/index.ts\"\nimport type { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport * as Sleep from \"./Sleep.ts\"\n\n/**\n * How the wait between attempts grows.\n *\n * `fixed` waits the interval every time, `linear` waits the interval times the\n * attempt, and `exponential` doubles the interval on every attempt.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Backoff = \"fixed\" | \"linear\" | \"exponential\"\n\n/**\n * What a check reports: whether the poll is over, and the value this attempt\n * read.\n *\n * The output travels on every attempt, satisfied or not, because it is also\n * the answer a `return-last` poll settles with when it runs out of attempts.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Check<out Result> {\n readonly satisfied: boolean\n readonly output: Result\n}\n\n/**\n * The success schema a check action declares.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const CheckResult = <Result extends Schema.Top>(\n result: Result\n): Schema.Struct<{\n readonly satisfied: typeof Schema.Boolean\n readonly output: Result\n}> => Schema.Struct({ satisfied: Schema.Boolean, output: result })\n\n/**\n * A poll that used its last attempt without a satisfied check.\n *\n * It is a typed failure rather than a defect: running out of attempts is an\n * expected outcome of polling something that may never become ready, so a\n * caller recovers from it through `Node.catch` like any other declared failure.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class PollExhausted extends Schema.TaggedError<PollExhausted>()(\n \"@smthrs/flow/PollExhausted\",\n {\n // This wire shape freezes at 1.0.0-rc.0.\n code: Schema.Literal(\"poll_exhausted\").pipe(\n Schema.withConstructorDefault(Effect.succeed(\"poll_exhausted\"))\n ),\n poll: Schema.String,\n attempts: Schema.Number,\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the exhaustion step is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const exhaustedTag = \"system/poll-exhausted\"\n\n/**\n * The declared step a poll takes when its last attempt was not satisfied.\n *\n * Running out of attempts is a step rather than a bare `Effect.fail` so the\n * exhaustion is in the plan and in the journal: the round that gave up shows\n * which poll gave up and after how many attempts, exactly like every other node\n * a body drove.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const exhausted: Action.Declared<\n typeof exhaustedTag,\n Schema.Struct<{\n readonly poll: typeof Schema.String\n readonly attempts: typeof Schema.Number\n }>,\n typeof Schema.Never,\n typeof PollExhausted,\n never\n> = Action.makeSystem(exhaustedTag, {\n payload: { poll: Schema.String, attempts: Schema.Number },\n success: Schema.Never,\n error: PollExhausted,\n tier: \"sealed\"\n})\n\n/**\n * The wait before the attempt after this one, in milliseconds.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const delayMillis = (options: {\n readonly intervalMs: number\n readonly backoff: Backoff\n readonly attempt: number\n}): number =>\n options.intervalMs *\n (options.backoff === \"fixed\" ? 1 : options.backoff === \"linear\" ? options.attempt : 2 ** (options.attempt - 1))\n\n/**\n * What a poll round can fail with.\n *\n * {@link PollExhausted} is the poll's own failure. `SleepRequestInvalid` is the\n * wait node's, declared because the wait between attempts is an ordinary\n * {@link module:Sleep.action} node and its typed refusal is part of the\n * topology a poll carries. Its codes cover a payload that names no deadline,\n * names two deadlines, or names a value that is not a deadline. {@link make}\n * refuses invalid author schedules at construction. A caller-visible round\n * payload can still carry an invalid attempt that produces an invalid derived\n * wait, so `Sleep` refuses that wait before the round parks.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Failure: Schema.Union<\n readonly [typeof PollExhausted, typeof Sleep.SleepRequestInvalid]\n> = Schema.Union([PollExhausted, Sleep.SleepRequestInvalid])\n\n/**\n * The payload schema {@link make} derives from an author's input fields: the\n * author's own fields, plus the attempt this round is.\n *\n * `attempt` is optional so a caller starts a poll with its own fields alone;\n * the first round reads it as one.\n *\n * @category models\n * @since 0.1.0\n */\nexport type PayloadSchema<Input extends Schema.Struct.Fields> = Schema.Struct<\n Input & { readonly attempt: Schema.optional<typeof Schema.Number> }\n>\n\n/**\n * The payload schema one round is opened with: the author's fields, plus the\n * attempt counter the lineage carries between them.\n *\n * @private\n */\nconst payloadSchema = <Input extends Schema.Struct.Fields>(\n input: Input,\n maxAttempts: number\n): PayloadSchema<Input> =>\n Schema.Struct({\n ...input,\n attempt: Schema.optional(\n Schema.Int.check(\n Schema.isGreaterThanOrEqualTo(1),\n Schema.isLessThanOrEqualTo(maxAttempts)\n )\n )\n })\n\n/**\n * The attempt a round payload carries, defaulting to the first.\n *\n * @private\n */\nconst attemptOf = (payload: object): number => {\n const attempt = (payload as { readonly attempt?: unknown }).attempt\n return typeof attempt === \"number\" ? attempt : 1\n}\n\n/**\n * Rebuilds a round payload with an explicit attempt.\n *\n * The two views of one round payload — what the check reads, and what the next\n * round is opened with — are the same fields with `attempt` resolved. TypeScript\n * cannot reduce a mapped type over an unresolved generic field set, so the\n * shape is stated once here instead of at each use.\n *\n * @private\n */\nconst withAttempt = <Payload>(payload: object, attempt: number): Payload =>\n ({ ...payload, attempt }) as unknown as Payload\n\n/**\n * Declares a durable poller.\n *\n * **When to use**\n *\n * Use it wherever a run has to keep asking until something is ready — a build\n * that finishes, a deployment that goes live, a review that lands — and the\n * waiting has to survive a restart. `check` is the one thing an author writes:\n * a body fragment that reads whatever it polls and reports a {@link Check}.\n * Everything else is schedule. Under stable callback admission (the default of\n * `Interpreter.layerWithImplementations`), wrap `check` in `Node.capture` with\n * every semantic capture, including a version for imported behavior. An\n * uncaptured check keeps the poll process-local. The helper captures its own\n * callbacks using the schedule and the check's identity.\n *\n * ```ts\n * const Deployment = Poll.make(\"deploy/wait\", {\n * input: { id: Schema.String },\n * result: Schema.String,\n * intervalMs: 5_000,\n * backoff: \"exponential\",\n * maxAttempts: 8,\n * onTimeout: \"fail\",\n * check: Node.capture(\n * { action: Status.name, implementationVersion: \"deploy/status-check/v1\" },\n * ({ attempt, id }) => Status.call({ attempt, id })\n * )\n * })\n * ```\n *\n * The check may not fail. A check that can fail states what a failure means for\n * the poll — give up, or treat it as \"not ready yet\" — with `Node.catch` inside\n * the fragment, rather than leaving the poll to guess.\n *\n * A per-attempt time limit on the check itself is NOT a poll option. A plan\n * node's duration is not something the body around it can bound; that bound\n * belongs in the check's own implementation, where `DurableDeferred.raceAll`\n * races the work against a durable clock and stays replayable.\n *\n * A schedule no clock can be armed with is refused here, with a `RangeError`\n * naming the option that is wrong. `intervalMs` becomes the `millis` of a\n * {@link module:Sleep.action} node, and a wait of `Infinity`, `NaN`, or a\n * negative length arms a timer that never fires — a poll that hangs forever\n * with a plan that looks correct. The check is on the schedule, not only on the\n * interval: `{ intervalMs: 1000, maxAttempts: 2000, backoff: \"exponential\" }`\n * states three finite options and asks for an infinite wait, so the LAST wait\n * the poll can arm has to be a length too. `maxAttempts` is the lineage's round\n * budget, and a budget below one attempt reaches `Flow.make` as a `maxRounds`\n * complaint about an option the author never wrote.\n *\n * @throws A `RangeError` when `intervalMs` is not a duration a clock accepts,\n * when `maxAttempts` is not a whole number of attempts of at least one, or when\n * the interval under the declared backoff reaches a wait no clock can be armed\n * with before the budget is spent.\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <\n const Tag extends string,\n Input extends Schema.Struct.Fields,\n Result extends Schema.Top,\n R = never\n>(\n tag: Tag,\n options: {\n readonly input: Input\n readonly result: Result\n readonly check: (\n payload: Schema.Struct.Type<Input> & { readonly attempt: number }\n ) => Node.Node<Check<Result[\"Type\"]>, never, R>\n readonly intervalMs: number\n readonly maxAttempts: number\n readonly backoff?: Backoff | undefined\n readonly onTimeout?: Repetition.AtCeiling | undefined\n }\n): Flow.Flow<Tag, PayloadSchema<Input>, Result, typeof Failure, R> => {\n type Round = Flow.Flow<Tag, PayloadSchema<Input>, Result, typeof Failure, R>\n type RoundPayload = PayloadSchema<Input>[\"Type\"]\n if (Object.prototype.hasOwnProperty.call(options.input, \"attempt\")) {\n throw new TypeError(\n `Poll.make: \"${tag}\" input cannot declare the reserved \"attempt\" field. ` +\n \"Poll owns that counter across durable rounds.\"\n )\n }\n const input = Object.freeze({ ...options.input }) as Input\n const result = options.result\n const check = options.check\n const intervalMs = options.intervalMs\n const maxAttempts = options.maxAttempts\n if (!Number.isFinite(intervalMs) || intervalMs < 0) {\n throw new RangeError(\n `Poll.make: \"${tag}\" intervalMs must be a finite number of milliseconds that is not negative, ` +\n `and was ${intervalMs}. The interval is the length of a durable wait, and a wait of ` +\n \"that length never ends.\"\n )\n }\n if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {\n throw new RangeError(\n `Poll.make: \"${tag}\" maxAttempts must be a whole number of attempts of at least one, and was ` +\n `${maxAttempts}. A poll checks at least once.`\n )\n }\n const backoff = options.backoff ?? \"fixed\"\n // The wait a poll arms is not `intervalMs`. {@link delayMillis} multiplies it\n // by the backoff, so a schedule whose options are each finite can still ask\n // for a wait that is not a length — and that wait arms the same timer that\n // never fires the interval guard above exists to refuse. The last wait is the\n // one before the final attempt, because the attempt at the budget gives up\n // rather than sleeps, and the schedule never shrinks, so the last wait is\n // also the longest: checking it checks all of them.\n const longestWaitMs = maxAttempts > 1\n ? delayMillis({ intervalMs, backoff, attempt: maxAttempts - 1 })\n : 0\n if (!Number.isFinite(longestWaitMs)) {\n throw new RangeError(\n `Poll.make: \"${tag}\" asks for a wait of ${longestWaitMs} ms before its last attempt. An ` +\n `intervalMs of ${intervalMs} under the \"${backoff}\" backoff reaches a length no clock ` +\n `can be armed with by attempt ${maxAttempts - 1} of ${maxAttempts}. Lower ` +\n \"maxAttempts, shorten the interval, or slow the backoff.\"\n )\n }\n const onTimeout = options.onTimeout ?? \"fail\"\n const captures = { tag, intervalMs, backoff, maxAttempts, onTimeout, check: Node.functionIdentity(check) }\n const body = (\n payload: RoundPayload\n ): Node.Node<Flow.BodySuccess<Result[\"Type\"]>, typeof Failure.Type, R> => {\n const attempt = attemptOf(payload)\n const last = attempt >= maxAttempts\n return check(withAttempt(payload, attempt)).pipe(\n Node.branch({\n if: Node.capture(captures, (checked) => checked.satisfied),\n then: (checked) => Flow.done(checked.output),\n else: (checked): Node.Node<Flow.BodySuccess<Result[\"Type\"]>, typeof Failure.Type> =>\n last\n ? onTimeout === \"return-last\"\n ? Flow.done(checked.output)\n : exhausted.call({ poll: tag, attempts: attempt })\n : Sleep.action.call({\n millis: delayMillis({ intervalMs, backoff, attempt })\n }).pipe(\n Node.andThen(\n self.to(withAttempt<Parameters<Round[\"to\"]>[0]>(payload, attempt + 1))\n )\n )\n })\n )\n }\n // The body names the flow it is the body of, which is what makes one round\n // open the next. It is read when a round is planned, long after `Flow.make`\n // has returned, so the binding is initialized by then.\n const self: Round = Flow.make(tag, {\n payload: payloadSchema(input, maxAttempts),\n success: result,\n error: Failure,\n maxRounds: maxAttempts,\n // A captured wrapper must not admit a process-local check as stable.\n body: captures.check.algorithm === \"sha256-source-ephemeral/v4\" ? body : Node.capture(captures, body)\n })\n return self\n}\n\n/**\n * The exhaustion implementation, provided beside the other action layers a\n * poll's rounds call.\n *\n * ```ts\n * Layer.mergeAll(Poll.layer, Sleep.layer, Interpreter.layer(Deployment)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = exhausted.toLayer(\n ({ attempts, poll }) =>\n Effect.fail(\n new PollExhausted({\n poll,\n attempts,\n message: `Poll \"${poll}\" used all ${attempts} attempts without a satisfied check.`\n })\n )\n)\n"]}
1
+ {"version":3,"file":"Poll.js","sourceRoot":"","sources":["../../src/Poll.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Polling as a durable lineage: one attempt per round, a durable wait between\n * attempts, and a bound the lineage settles on.\n *\n * Smithers 0.x had a `Poller` component — a check, an interval, a backoff, an\n * attempt bound, and what to do at that bound. The pieces it was built from\n * already exist here: {@link module:Sleep.action} is the durable wait, and\n * `Flow.to` opens the next round. What was missing is the shape that puts them\n * together, and that is this module.\n *\n * A poll is a flow whose body is ONE attempt. It runs the check, and branches:\n * a satisfied check settles the lineage with the check's own output, an\n * unsatisfied one sleeps for this attempt's delay and hands off to the next\n * round with the attempt counter raised. The last attempt has no sleep and no\n * handoff — it either answers with the output it just read (`return-last`) or\n * fails {@link PollExhausted} (`fail`).\n *\n * Everything durable about it is durable for the ordinary reason. Each attempt\n * is a round with its own keyed plan nodes, so a check that already ran replays\n * from its recorded outcome instead of running again; the wait between attempts\n * is a durable timer, so a process that dies during it resumes on the round it\n * left rather than starting the poll over; and the attempt counter travels in\n * the payload, so nothing about \"which attempt is this\" lives in memory.\n *\n * The bound is stated twice on purpose. The body ends the lineage on the last\n * attempt, and `maxAttempts` is also the flow's `maxRounds` budget, so a\n * lineage that somehow opened another round is refused by the engine rather\n * than polling forever.\n *\n * @since 0.1.0\n */\n\nimport * as Node from \"@smthrs/plan/Node\"\nimport type * as Repetition from \"@smthrs/plan/Repetition\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as Flow from \"./Flow/index.ts\"\nimport type { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport * as Sleep from \"./Sleep.ts\"\n\n/**\n * How the wait between attempts grows.\n *\n * `fixed` waits the interval every time, `linear` waits the interval times the\n * attempt, and `exponential` doubles the interval on every attempt.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Backoff = \"fixed\" | \"linear\" | \"exponential\"\n\n/**\n * What a check reports: whether the poll is over, and the value this attempt\n * read.\n *\n * The output travels on every attempt, satisfied or not, because it is also\n * the answer a `return-last` poll settles with when it runs out of attempts.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Check<out Result> {\n readonly satisfied: boolean\n readonly output: Result\n}\n\n/**\n * The success schema a check action declares.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const CheckResult = <Result extends Schema.Top>(\n result: Result\n): Schema.Struct<{\n readonly satisfied: typeof Schema.Boolean\n readonly output: Result\n}> => Schema.Struct({ satisfied: Schema.Boolean, output: result })\n\n/**\n * A poll that used its last attempt without a satisfied check.\n *\n * It is a typed failure rather than a defect: running out of attempts is an\n * expected outcome of polling something that may never become ready, so a\n * caller recovers from it through `Node.catch` like any other declared failure.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class PollExhausted extends Schema.TaggedError<PollExhausted>()(\n \"@smthrs/flow/PollExhausted\",\n {\n // This wire shape freezes at 1.0.0-rc.0.\n code: Schema.Literal(\"poll_exhausted\").pipe(\n Schema.withConstructorDefault(Effect.succeed(\"poll_exhausted\"))\n ),\n poll: Schema.String,\n attempts: Schema.Number,\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the exhaustion step is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const exhaustedTag = \"system/poll-exhausted\"\n\n/**\n * The declared step a poll takes when its last attempt was not satisfied.\n *\n * Running out of attempts is a step rather than a bare `Effect.fail` so the\n * exhaustion is in the plan and in the journal: the round that gave up shows\n * which poll gave up and after how many attempts, exactly like every other node\n * a body drove.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const exhausted: Action.Declared<\n typeof exhaustedTag,\n Schema.Struct<{\n readonly poll: typeof Schema.String\n readonly attempts: typeof Schema.Number\n }>,\n typeof Schema.Never,\n typeof PollExhausted,\n never\n> = Action.makeSystem(exhaustedTag, {\n payload: { poll: Schema.String, attempts: Schema.Number },\n success: Schema.Never,\n error: PollExhausted,\n tier: \"sealed\"\n})\n\n/**\n * The wait before the attempt after this one, in milliseconds.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const delayMillis = (options: {\n readonly intervalMs: number\n readonly backoff: Backoff\n readonly attempt: number\n}): number =>\n // A zero interval is zero under every backoff. Multiplying it by a factor\n // that has overflowed to `Infinity` would make it `NaN`.\n options.intervalMs === 0 ? 0 : options.intervalMs *\n (options.backoff === \"fixed\" ? 1 : options.backoff === \"linear\" ? options.attempt : 2 ** (options.attempt - 1))\n\n/**\n * What a poll round can fail with.\n *\n * {@link PollExhausted} is the poll's own failure. `SleepRequestInvalid` is the\n * wait node's, declared because the wait between attempts is an ordinary\n * {@link module:Sleep.action} node and its typed refusal is part of the\n * topology a poll carries. Its codes cover a payload that names no deadline,\n * names two deadlines, or names a value that is not a deadline. {@link make}\n * refuses invalid author schedules at construction. A caller-visible round\n * payload can still carry an invalid attempt that produces an invalid derived\n * wait, so `Sleep` refuses that wait before the round parks.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Failure: Schema.Union<\n readonly [typeof PollExhausted, typeof Sleep.SleepRequestInvalid]\n> = Schema.Union([PollExhausted, Sleep.SleepRequestInvalid])\n\n/**\n * The payload schema {@link make} derives from an author's input fields: the\n * author's own fields, plus the attempt this round is.\n *\n * `attempt` is optional so a caller starts a poll with its own fields alone;\n * the first round reads it as one.\n *\n * @category models\n * @since 0.1.0\n */\nexport type PayloadSchema<Input extends Schema.Struct.Fields> = Schema.Struct<\n Input & { readonly attempt: Schema.optional<typeof Schema.Number> }\n>\n\n/**\n * The payload schema one round is opened with: the author's fields, plus the\n * attempt counter the lineage carries between them.\n *\n * @private\n */\nconst payloadSchema = <Input extends Schema.Struct.Fields>(\n input: Input,\n maxAttempts: number\n): PayloadSchema<Input> =>\n Schema.Struct({\n ...input,\n attempt: Schema.optional(\n Schema.Int.check(\n Schema.isGreaterThanOrEqualTo(1),\n Schema.isLessThanOrEqualTo(maxAttempts)\n )\n )\n })\n\n/**\n * The attempt a round payload carries, defaulting to the first.\n *\n * @private\n */\nconst attemptOf = (payload: object): number => {\n const attempt = (payload as { readonly attempt?: unknown }).attempt\n return typeof attempt === \"number\" ? attempt : 1\n}\n\n/**\n * Rebuilds a round payload with an explicit attempt.\n *\n * The two views of one round payload — what the check reads, and what the next\n * round is opened with — are the same fields with `attempt` resolved. TypeScript\n * cannot reduce a mapped type over an unresolved generic field set, so the\n * shape is stated once here instead of at each use.\n *\n * @private\n */\nconst withAttempt = <Payload>(payload: object, attempt: number): Payload =>\n ({ ...payload, attempt }) as unknown as Payload\n\n/**\n * Declares a durable poller.\n *\n * **When to use**\n *\n * Use it wherever a run has to keep asking until something is ready — a build\n * that finishes, a deployment that goes live, a review that lands — and the\n * waiting has to survive a restart. `check` is the one thing an author writes:\n * a body fragment that reads whatever it polls and reports a {@link Check}.\n * Everything else is schedule. Under stable callback admission (the default of\n * `Interpreter.layerWithImplementations`), wrap `check` in `Node.capture` with\n * every semantic capture, including a version for imported behavior. An\n * uncaptured check keeps the poll process-local. The helper captures its own\n * callbacks using the schedule and the check's identity.\n *\n * ```ts\n * const Deployment = Poll.make(\"deploy/wait\", {\n * input: { id: Schema.String },\n * result: Schema.String,\n * intervalMs: 5_000,\n * backoff: \"exponential\",\n * maxAttempts: 8,\n * onTimeout: \"fail\",\n * check: Node.capture(\n * { action: Status.name, implementationVersion: \"deploy/status-check/v1\" },\n * ({ attempt, id }) => Status.call({ attempt, id })\n * )\n * })\n * ```\n *\n * The check may not fail. A check that can fail states what a failure means for\n * the poll — give up, or treat it as \"not ready yet\" — with `Node.catch` inside\n * the fragment, rather than leaving the poll to guess.\n *\n * A per-attempt time limit on the check itself is NOT a poll option. A plan\n * node's duration is not something the body around it can bound; that bound\n * belongs in the check's own implementation, where `DurableDeferred.raceAll`\n * races the work against a durable clock and stays replayable.\n *\n * A schedule no clock can be armed with is refused here, with a `RangeError`\n * naming the option that is wrong. `intervalMs` becomes the `millis` of a\n * {@link module:Sleep.action} node, and a wait of `Infinity`, `NaN`, or a\n * negative length arms a timer that never fires — a poll that hangs forever\n * with a plan that looks correct. The check is on the schedule, not only on the\n * interval: `{ intervalMs: 1000, maxAttempts: 2000, backoff: \"exponential\" }`\n * states three finite options and asks for an infinite wait, so the LAST wait\n * the poll can arm has to be a length too, and no longer than\n * `Number.MAX_SAFE_INTEGER` milliseconds, the latest deadline a durable timer\n * records. `maxAttempts` is the lineage's round\n * budget, and a budget below one attempt reaches `Flow.make` as a `maxRounds`\n * complaint about an option the author never wrote.\n *\n * @throws A `RangeError` when `intervalMs` is not a duration a clock accepts,\n * when `maxAttempts` is not a whole number of attempts of at least one, or when\n * the interval under the declared backoff reaches a wait no clock can be armed\n * with before the budget is spent.\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <\n const Tag extends string,\n Input extends Schema.Struct.Fields,\n Result extends Schema.Top,\n R = never\n>(\n tag: Tag,\n options: {\n readonly input: Input\n readonly result: Result\n readonly check: (\n payload: Schema.Struct.Type<Input> & { readonly attempt: number }\n ) => Node.Node<Check<Result[\"Type\"]>, never, R>\n readonly intervalMs: number\n readonly maxAttempts: number\n readonly backoff?: Backoff | undefined\n readonly onTimeout?: Repetition.AtCeiling | undefined\n }\n): Flow.Flow<Tag, PayloadSchema<Input>, Result, typeof Failure, R> => {\n type Round = Flow.Flow<Tag, PayloadSchema<Input>, Result, typeof Failure, R>\n type RoundPayload = PayloadSchema<Input>[\"Type\"]\n if (Object.prototype.hasOwnProperty.call(options.input, \"attempt\")) {\n throw new TypeError(\n `Poll.make: \"${tag}\" input cannot declare the reserved \"attempt\" field. ` +\n \"Poll owns that counter across durable rounds.\"\n )\n }\n const input = Object.freeze({ ...options.input }) as Input\n const result = options.result\n const check = options.check\n const intervalMs = options.intervalMs\n const maxAttempts = options.maxAttempts\n if (!Number.isFinite(intervalMs) || intervalMs < 0) {\n throw new RangeError(\n `Poll.make: \"${tag}\" intervalMs must be a finite number of milliseconds that is not negative, ` +\n `and was ${intervalMs}. The interval is the length of a durable wait, and a wait of ` +\n \"that length never ends.\"\n )\n }\n if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {\n throw new RangeError(\n `Poll.make: \"${tag}\" maxAttempts must be a whole number of attempts of at least one, and was ` +\n `${maxAttempts}. A poll checks at least once.`\n )\n }\n const backoff = options.backoff ?? \"fixed\"\n // The wait a poll arms is not `intervalMs`. {@link delayMillis} multiplies it\n // by the backoff, so a schedule whose options are each finite can still ask\n // for a wait that is not a length — and that wait arms the same timer that\n // never fires the interval guard above exists to refuse. The last wait is the\n // one before the final attempt, because the attempt at the budget gives up\n // rather than sleeps, and the schedule never shrinks, so the last wait is\n // also the longest: checking it checks all of them.\n const longestWaitMs = maxAttempts > 1\n ? delayMillis({ intervalMs, backoff, attempt: maxAttempts - 1 })\n : 0\n // A durable timer records its deadline as a safe integer of milliseconds,\n // so `Sleep` refuses a longer wait on the round that reaches it.\n if (!Number.isFinite(longestWaitMs) || longestWaitMs > Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n `Poll.make: \"${tag}\" asks for a wait of ${longestWaitMs} ms before its last attempt. An ` +\n `intervalMs of ${intervalMs} under the \"${backoff}\" backoff reaches a length no clock ` +\n `can be armed with by attempt ${maxAttempts - 1} of ${maxAttempts}. Lower ` +\n \"maxAttempts, shorten the interval, or slow the backoff.\"\n )\n }\n const onTimeout = options.onTimeout ?? \"fail\"\n const captures = { tag, intervalMs, backoff, maxAttempts, onTimeout, check: Node.functionIdentity(check) }\n const body = (\n payload: RoundPayload\n ): Node.Node<Flow.BodySuccess<Result[\"Type\"]>, typeof Failure.Type, R> => {\n const attempt = attemptOf(payload)\n const last = attempt >= maxAttempts\n return check(withAttempt(payload, attempt)).pipe(\n Node.branch({\n if: Node.capture(captures, (checked) => checked.satisfied),\n then: (checked) => Flow.done(checked.output),\n else: (checked): Node.Node<Flow.BodySuccess<Result[\"Type\"]>, typeof Failure.Type> =>\n last\n ? onTimeout === \"return-last\"\n ? Flow.done(checked.output)\n : exhausted.call({ poll: tag, attempts: attempt })\n : Sleep.action.call({\n millis: delayMillis({ intervalMs, backoff, attempt })\n }).pipe(\n Node.andThen(\n self.to(withAttempt<Parameters<Round[\"to\"]>[0]>(payload, attempt + 1))\n )\n )\n })\n )\n }\n // The body names the flow it is the body of, which is what makes one round\n // open the next. It is read when a round is planned, long after `Flow.make`\n // has returned, so the binding is initialized by then.\n const self: Round = Flow.make(tag, {\n payload: payloadSchema(input, maxAttempts),\n success: result,\n error: Failure,\n maxRounds: maxAttempts,\n // A captured wrapper must not admit a process-local check as stable.\n body: captures.check.algorithm === \"sha256-source-ephemeral/v4\" ? body : Node.capture(captures, body)\n })\n return self\n}\n\n/**\n * The exhaustion implementation, provided beside the other action layers a\n * poll's rounds call.\n *\n * ```ts\n * Layer.mergeAll(Poll.layer, Sleep.layer, Interpreter.layer(Deployment)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = exhausted.toLayer(\n ({ attempts, poll }) =>\n Effect.fail(\n new PollExhausted({\n poll,\n attempts,\n message: `Poll \"${poll}\" used all ${attempts} attempts without a satisfied check.`\n })\n )\n)\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Sleep.d.ts","sourceRoot":"","sources":["../../src/Sleep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAG5C,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;;;;;AAG/D;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,SAAQ,wBAMxC;CAAG;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,GAAG,iBAAiB,CAAA;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,QAAQ,CAClC,OAAO,GAAG,EACV,MAAM,CAAC,MAAM,CAAC;IACZ,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;CAC/C,CAAC,EACF,OAAO,MAAM,CAAC,IAAI,EAClB,OAAO,mBAAmB,EAC1B,KAAK,CASL,CAAA;AA6GF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,WAAW,CAuCxE,CAAA"}
1
+ {"version":3,"file":"Sleep.d.ts","sourceRoot":"","sources":["../../src/Sleep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAG5C,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;;;;;AAG/D;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,SAAQ,wBAMxC;CAAG;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,GAAG,iBAAiB,CAAA;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,QAAQ,CAClC,OAAO,GAAG,EACV,MAAM,CAAC,MAAM,CAAC;IACZ,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;CAC/C,CAAC,EACF,OAAO,MAAM,CAAC,IAAI,EAClB,OAAO,mBAAmB,EAC1B,KAAK,CASL,CAAA;AA0IF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,WAAW,CAuCxE,CAAA"}
package/dist/esm/Sleep.js CHANGED
@@ -83,6 +83,27 @@ export const action = Action.makeSystem(tag, {
83
83
  error: SleepRequestInvalid,
84
84
  tier: "sealed"
85
85
  });
86
+ /**
87
+ * The latest deadline a sleep can arm, in epoch milliseconds.
88
+ *
89
+ * A durable store records `wakeAt` and a clock's due time as a non-negative
90
+ * safe integer. A finite deadline past this one passed every other check and
91
+ * then failed the park write, so it is refused with the payload's other
92
+ * invalid deadlines instead.
93
+ *
94
+ * @private
95
+ */
96
+ const latestDeadline = Number.MAX_SAFE_INTEGER;
97
+ /**
98
+ * The refusal for a deadline later than {@link latestDeadline}.
99
+ *
100
+ * @private
101
+ */
102
+ const tooLate = (stated) => new SleepRequestInvalid({
103
+ code: "invalid_deadline",
104
+ message: `${tag} was called with ${stated}, which ends after ${latestDeadline}, the latest epoch ` +
105
+ "millisecond a durable timer records."
106
+ });
86
107
  /**
87
108
  * The absolute deadline a payload names, in epoch milliseconds.
88
109
  *
@@ -125,16 +146,21 @@ const deadlineOf = (payload, now) => {
125
146
  `${deadline}. A deadline is a finite epoch time in milliseconds.`
126
147
  }));
127
148
  }
128
- return Effect.succeed(deadline);
149
+ return Math.ceil(deadline) > latestDeadline
150
+ ? Effect.fail(tooLate(`millis ${payload.millis} at current time ${now}`))
151
+ : Effect.succeed(deadline);
129
152
  }
130
153
  if (payload.until !== undefined) {
131
- return Number.isFinite(payload.until)
132
- ? Effect.succeed(payload.until)
133
- : Effect.fail(new SleepRequestInvalid({
154
+ if (!Number.isFinite(payload.until)) {
155
+ return Effect.fail(new SleepRequestInvalid({
134
156
  code: "invalid_deadline",
135
157
  message: `${tag} was called with until ${payload.until}. A deadline is a finite epoch time in milliseconds; ` +
136
158
  "the sleep must end at a real point in time."
137
159
  }));
160
+ }
161
+ return Math.ceil(payload.until) > latestDeadline
162
+ ? Effect.fail(tooLate(`until ${payload.until}`))
163
+ : Effect.succeed(payload.until);
138
164
  }
139
165
  return Effect.fail(new SleepRequestInvalid({
140
166
  code: "missing_deadline",
@@ -1 +1 @@
1
- {"version":3,"file":"Sleep.js","sourceRoot":"","sources":["../../src/Sleep.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The system timer, as an ordinary declared action.\n *\n * `docs/concepts/flows-and-actions.md` names the timer primitive a\n * gap and says how to close it: ship it over the existing `DurableClock`, so a\n * wait is a visible, keyed plan node rather than a second execution mechanism.\n * That is this module. {@link action} is a plain `Action.make` declaration —\n * it appears in a catalog, `.call()` records a node, and `Graph.build` puts that\n * node in the plan beside every other step — and {@link layer} is the\n * implementation the hosts that can run a timer provide.\n *\n * The wait itself is `DurableClock`'s, not a new one. The implementation arms\n * the durable clock and awaits its deferred, which is what parks the execution;\n * the park is declared through the ordinary waiting vocabulary\n * ({@link module:WaitingAnnotation.annotateWaiting}) under `timer`, with the\n * deadline as `wakeAt`, so a durable driver records the same waiting row it\n * records for a hand-written `DurableClock.sleep`. Replay is the deferred's:\n * once the clock has fired, its result is persisted, so a re-driven round reads\n * it and runs straight through instead of parking a second time.\n *\n * Which clock a wait arms is the dispatch's own, read from\n * {@link module:Context.CurrentInvocationKey}: a keyed node already owns an\n * identity that survives replay and separates two identical calls, so the timer\n * inherits it rather than being addressed by the payload it was called with.\n *\n * @since 0.1.0\n */\n\nimport * as Clock from \"effect/Clock\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as DurableClock from \"./DurableClock.ts\"\nimport type { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport { annotateWaiting } from \"./FlowRuntime/WaitingAnnotation.ts\"\n\n/**\n * A sleep whose payload does not name exactly one deadline.\n *\n * A payload carries a relative `millis` or an absolute `until`, and naming\n * both, or neither, leaves no deadline to arm a clock with. It is a typed\n * failure rather than a defect because the payload of a driven node is\n * assembled from plan values, so an author's mistake is recoverable through\n * `Node.catch` like any other declared failure.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class SleepRequestInvalid extends Schema.TaggedError<SleepRequestInvalid>()(\n \"@smthrs/flow/SleepRequestInvalid\",\n {\n code: Schema.Literals([\"missing_deadline\", \"ambiguous_deadline\", \"invalid_deadline\"]),\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the sleep declaration is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const tag = \"system/sleep\"\n\n/**\n * The declared sleep action: `millis` from now, or an absolute `until`.\n *\n * **When to use**\n *\n * Use it in a body wherever a round has to wait for time to pass —\n * `Sleep.action.call({ millis: 60_000 })` — rather than reaching for\n * `DurableClock.sleep`, which is only callable from a handler and leaves\n * nothing in the plan. A sleep node is keyed, inspectable, and skippable by a\n * branch exactly like an action that does work.\n *\n * Two waits of the same length are two waits: `sleep(1h)` followed by\n * `sleep(1h)` sleeps for two hours, because each call is its own node with its\n * own identity, exactly as two calls of any other declaration are.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const action: Action.Declared<\n typeof tag,\n Schema.Struct<{\n readonly millis: Schema.optional<Schema.Number>\n readonly until: Schema.optional<Schema.Number>\n }>,\n typeof Schema.Void,\n typeof SleepRequestInvalid,\n never\n> = Action.makeSystem(tag, {\n payload: {\n millis: Schema.optional(Schema.Number),\n until: Schema.optional(Schema.Number)\n },\n success: Schema.Void,\n error: SleepRequestInvalid,\n tier: \"sealed\"\n})\n\n/**\n * The absolute deadline a payload names, in epoch milliseconds.\n *\n * DECIDED: a payload names EXACTLY one of `millis`\n * and `until`, and both other shapes are refused rather than resolved by\n * precedence. A precedence rule would silently ignore half of a payload an\n * author believed in, and the two fields are the same fact stated two ways, so\n * there is no shape where carrying both is meaningful.\n *\n * `Duration.millis` accepts every number, so an unchecked deadline changes the\n * sleep instead of refusing it. `NaN` becomes zero, a negative relative delay\n * has already passed, and `Infinity` never ends. The implementation rejects\n * those values before it annotates the instance or arms a clock. A finite\n * absolute `until` remains legal when it is in the past because that sleep can\n * settle immediately.\n *\n * @private\n */\nconst deadlineOf = (\n payload: { readonly millis?: number | undefined; readonly until?: number | undefined },\n now: number\n): Effect.Effect<number, SleepRequestInvalid> => {\n if (payload.millis !== undefined && payload.until !== undefined) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"ambiguous_deadline\",\n message: `${tag} was called with both \"millis\" and \"until\". A sleep names one deadline: ` +\n \"a relative duration, or an absolute epoch time.\"\n })\n )\n }\n if (payload.millis !== undefined) {\n if (!Number.isFinite(payload.millis) || payload.millis < 0) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message: `${tag} was called with millis ${payload.millis}. A deadline is a finite number of ` +\n \"milliseconds that is not negative; the sleep would be over before it started, or never over at all.\"\n })\n )\n }\n const deadline = now + payload.millis\n if (!Number.isFinite(deadline)) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message: `${tag} was called with millis ${payload.millis} at current time ${now}, which produces ` +\n `${deadline}. A deadline is a finite epoch time in milliseconds.`\n })\n )\n }\n return Effect.succeed(deadline)\n }\n if (payload.until !== undefined) {\n return Number.isFinite(payload.until)\n ? Effect.succeed(payload.until)\n : Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message:\n `${tag} was called with until ${payload.until}. A deadline is a finite epoch time in milliseconds; ` +\n \"the sleep must end at a real point in time.\"\n })\n )\n }\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"missing_deadline\",\n message: `${tag} was called with neither \"millis\" nor \"until\", so there is no deadline to wait for.`\n })\n )\n}\n\n/**\n * The durable clock the dispatch currently executing arms.\n *\n * DECIDED: the name is derived from the ENGINE's\n * per-dispatch identity — the invocation key it already allocated — rather than\n * from the payload. A durable clock's deferred is addressed by (execution,\n * name), so the name has to be stable across replays of one node and distinct\n * between two identical nodes, and the invocation key is exactly that: it is\n * allocated on every dispatch including a replayed one, because the driver\n * addresses the recorded outcome by it. A payload-derived name has only the\n * first property — `sleep(1h); work(); sleep(1h)` would address ONE wake and\n * sleep once — and closing that with a discriminator in the payload would make\n * an author carry identity that a keyed plan node already has.\n *\n * DECIDED: a runtime that supplies no dispatch\n * identity is refused as a defect rather than falling back to the payload. The\n * fallback is unobservable in the one-wait case and silently collapses two\n * waits in every other, so it would trade a wiring error a host fixes once for\n * a run that quietly sleeps for half as long as it declared.\n *\n * @private\n */\nconst clockName: Effect.Effect<string> = Effect.gen(function*() {\n const invocation = yield* Action.CurrentInvocationKey\n if (invocation === undefined) {\n return yield* Effect.die(\n `${tag} ran under a runtime that supplies no Action.CurrentInvocationKey. ` +\n \"A durable timer is named by the dispatch that armed it, so a runtime that does not \" +\n \"identify the dispatch cannot arm one.\"\n )\n }\n return `Sleep/${invocation}`\n})\n\n/**\n * The sleep implementation: arm the durable clock, park under `timer`, wake.\n *\n * Provide it beside the other action implementation layers a body calls, over\n * the {@link module:Implementations.layerImplementations} table they file\n * themselves in:\n *\n * ```ts\n * Layer.mergeAll(Sleep.layer, Interpreter.layer(Pipeline)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = action.toLayer((payload) =>\n Effect.gen(function*() {\n // Whole milliseconds, both of them. A durable deadline lands in an INTEGER\n // column whose CHECK constraint refuses anything else\n // (`flows_runs.waiting_wake_at_ms`, `flows_clock_deadlines.due_at_ms`), and\n // `Clock.currentTimeMillis` is not integral under every clock: a test clock\n // advanced by a fractional-millisecond sleep reports a fractional now, and\n // a fractional `millis` or `until` in the payload does the same. The\n // unrounded deadline reached the park write as a raw SQL constraint\n // failure, which killed the drive and left the run `running` with nothing\n // left to wake it. `now` floors, and the deadline ceils so a rounded sleep\n // never ends before the instant it was asked to wait for.\n const now = Math.floor(yield* Clock.currentTimeMillis)\n const wakeAt = Math.ceil(yield* deadlineOf(payload, now))\n const remaining = wakeAt - now\n // DECIDED: a deadline that has already passed\n // settles the node instead of parking it. A durable wait exists to survive\n // the gap until a deadline, and a run that resumes after its own deadline\n // has to make progress rather than park again on a timer that can only fire\n // in the past.\n if (remaining <= 0) return\n const name = yield* clockName\n // DECIDED: the park is declared rather than\n // left to a driver's derivation, so a host with no clock table still parks\n // under `timer`. A `millis` deadline is recomputed from the current clock,\n // so a round re-driven before its timer fires re-declares a `wakeAt` later\n // than the armed one; the wake itself is unaffected, because the durable\n // clock row is keyed by name and keeps the deadline it was first armed\n // with, and `wakeAt` is a sweeper hint rather than the wake.\n yield* annotateWaiting({ reason: \"timer\", wakeAt })\n yield* DurableClock.sleep({\n name,\n duration: Duration.millis(remaining),\n // The wait is the point: a sleep called as a plan node parks the\n // execution rather than holding a fiber open, so the in-memory shortcut\n // is disabled instead of being left at its 60-second default.\n inMemoryThreshold: Duration.zero\n })\n })\n)\n"]}
1
+ {"version":3,"file":"Sleep.js","sourceRoot":"","sources":["../../src/Sleep.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The system timer, as an ordinary declared action.\n *\n * `docs/concepts/flows-and-actions.md` names the timer primitive a\n * gap and says how to close it: ship it over the existing `DurableClock`, so a\n * wait is a visible, keyed plan node rather than a second execution mechanism.\n * That is this module. {@link action} is a plain `Action.make` declaration —\n * it appears in a catalog, `.call()` records a node, and `Graph.build` puts that\n * node in the plan beside every other step — and {@link layer} is the\n * implementation the hosts that can run a timer provide.\n *\n * The wait itself is `DurableClock`'s, not a new one. The implementation arms\n * the durable clock and awaits its deferred, which is what parks the execution;\n * the park is declared through the ordinary waiting vocabulary\n * ({@link module:WaitingAnnotation.annotateWaiting}) under `timer`, with the\n * deadline as `wakeAt`, so a durable driver records the same waiting row it\n * records for a hand-written `DurableClock.sleep`. Replay is the deferred's:\n * once the clock has fired, its result is persisted, so a re-driven round reads\n * it and runs straight through instead of parking a second time.\n *\n * Which clock a wait arms is the dispatch's own, read from\n * {@link module:Context.CurrentInvocationKey}: a keyed node already owns an\n * identity that survives replay and separates two identical calls, so the timer\n * inherits it rather than being addressed by the payload it was called with.\n *\n * @since 0.1.0\n */\n\nimport * as Clock from \"effect/Clock\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Action from \"./Action/index.ts\"\nimport * as DurableClock from \"./DurableClock.ts\"\nimport type { FlowRuntime } from \"./FlowRuntime/FlowRuntime.ts\"\nimport { annotateWaiting } from \"./FlowRuntime/WaitingAnnotation.ts\"\n\n/**\n * A sleep whose payload does not name exactly one deadline.\n *\n * A payload carries a relative `millis` or an absolute `until`, and naming\n * both, or neither, leaves no deadline to arm a clock with. It is a typed\n * failure rather than a defect because the payload of a driven node is\n * assembled from plan values, so an author's mistake is recoverable through\n * `Node.catch` like any other declared failure.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class SleepRequestInvalid extends Schema.TaggedError<SleepRequestInvalid>()(\n \"@smthrs/flow/SleepRequestInvalid\",\n {\n code: Schema.Literals([\"missing_deadline\", \"ambiguous_deadline\", \"invalid_deadline\"]),\n message: Schema.String\n }\n) {}\n\n/**\n * The tag the sleep declaration is catalogued and resolved under.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const tag = \"system/sleep\"\n\n/**\n * The declared sleep action: `millis` from now, or an absolute `until`.\n *\n * **When to use**\n *\n * Use it in a body wherever a round has to wait for time to pass —\n * `Sleep.action.call({ millis: 60_000 })` — rather than reaching for\n * `DurableClock.sleep`, which is only callable from a handler and leaves\n * nothing in the plan. A sleep node is keyed, inspectable, and skippable by a\n * branch exactly like an action that does work.\n *\n * Two waits of the same length are two waits: `sleep(1h)` followed by\n * `sleep(1h)` sleeps for two hours, because each call is its own node with its\n * own identity, exactly as two calls of any other declaration are.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const action: Action.Declared<\n typeof tag,\n Schema.Struct<{\n readonly millis: Schema.optional<Schema.Number>\n readonly until: Schema.optional<Schema.Number>\n }>,\n typeof Schema.Void,\n typeof SleepRequestInvalid,\n never\n> = Action.makeSystem(tag, {\n payload: {\n millis: Schema.optional(Schema.Number),\n until: Schema.optional(Schema.Number)\n },\n success: Schema.Void,\n error: SleepRequestInvalid,\n tier: \"sealed\"\n})\n\n/**\n * The latest deadline a sleep can arm, in epoch milliseconds.\n *\n * A durable store records `wakeAt` and a clock's due time as a non-negative\n * safe integer. A finite deadline past this one passed every other check and\n * then failed the park write, so it is refused with the payload's other\n * invalid deadlines instead.\n *\n * @private\n */\nconst latestDeadline = Number.MAX_SAFE_INTEGER\n\n/**\n * The refusal for a deadline later than {@link latestDeadline}.\n *\n * @private\n */\nconst tooLate = (stated: string): SleepRequestInvalid =>\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message: `${tag} was called with ${stated}, which ends after ${latestDeadline}, the latest epoch ` +\n \"millisecond a durable timer records.\"\n })\n\n/**\n * The absolute deadline a payload names, in epoch milliseconds.\n *\n * DECIDED: a payload names EXACTLY one of `millis`\n * and `until`, and both other shapes are refused rather than resolved by\n * precedence. A precedence rule would silently ignore half of a payload an\n * author believed in, and the two fields are the same fact stated two ways, so\n * there is no shape where carrying both is meaningful.\n *\n * `Duration.millis` accepts every number, so an unchecked deadline changes the\n * sleep instead of refusing it. `NaN` becomes zero, a negative relative delay\n * has already passed, and `Infinity` never ends. The implementation rejects\n * those values before it annotates the instance or arms a clock. A finite\n * absolute `until` remains legal when it is in the past because that sleep can\n * settle immediately.\n *\n * @private\n */\nconst deadlineOf = (\n payload: { readonly millis?: number | undefined; readonly until?: number | undefined },\n now: number\n): Effect.Effect<number, SleepRequestInvalid> => {\n if (payload.millis !== undefined && payload.until !== undefined) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"ambiguous_deadline\",\n message: `${tag} was called with both \"millis\" and \"until\". A sleep names one deadline: ` +\n \"a relative duration, or an absolute epoch time.\"\n })\n )\n }\n if (payload.millis !== undefined) {\n if (!Number.isFinite(payload.millis) || payload.millis < 0) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message: `${tag} was called with millis ${payload.millis}. A deadline is a finite number of ` +\n \"milliseconds that is not negative; the sleep would be over before it started, or never over at all.\"\n })\n )\n }\n const deadline = now + payload.millis\n if (!Number.isFinite(deadline)) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message: `${tag} was called with millis ${payload.millis} at current time ${now}, which produces ` +\n `${deadline}. A deadline is a finite epoch time in milliseconds.`\n })\n )\n }\n return Math.ceil(deadline) > latestDeadline\n ? Effect.fail(tooLate(`millis ${payload.millis} at current time ${now}`))\n : Effect.succeed(deadline)\n }\n if (payload.until !== undefined) {\n if (!Number.isFinite(payload.until)) {\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"invalid_deadline\",\n message:\n `${tag} was called with until ${payload.until}. A deadline is a finite epoch time in milliseconds; ` +\n \"the sleep must end at a real point in time.\"\n })\n )\n }\n return Math.ceil(payload.until) > latestDeadline\n ? Effect.fail(tooLate(`until ${payload.until}`))\n : Effect.succeed(payload.until)\n }\n return Effect.fail(\n new SleepRequestInvalid({\n code: \"missing_deadline\",\n message: `${tag} was called with neither \"millis\" nor \"until\", so there is no deadline to wait for.`\n })\n )\n}\n\n/**\n * The durable clock the dispatch currently executing arms.\n *\n * DECIDED: the name is derived from the ENGINE's\n * per-dispatch identity — the invocation key it already allocated — rather than\n * from the payload. A durable clock's deferred is addressed by (execution,\n * name), so the name has to be stable across replays of one node and distinct\n * between two identical nodes, and the invocation key is exactly that: it is\n * allocated on every dispatch including a replayed one, because the driver\n * addresses the recorded outcome by it. A payload-derived name has only the\n * first property — `sleep(1h); work(); sleep(1h)` would address ONE wake and\n * sleep once — and closing that with a discriminator in the payload would make\n * an author carry identity that a keyed plan node already has.\n *\n * DECIDED: a runtime that supplies no dispatch\n * identity is refused as a defect rather than falling back to the payload. The\n * fallback is unobservable in the one-wait case and silently collapses two\n * waits in every other, so it would trade a wiring error a host fixes once for\n * a run that quietly sleeps for half as long as it declared.\n *\n * @private\n */\nconst clockName: Effect.Effect<string> = Effect.gen(function*() {\n const invocation = yield* Action.CurrentInvocationKey\n if (invocation === undefined) {\n return yield* Effect.die(\n `${tag} ran under a runtime that supplies no Action.CurrentInvocationKey. ` +\n \"A durable timer is named by the dispatch that armed it, so a runtime that does not \" +\n \"identify the dispatch cannot arm one.\"\n )\n }\n return `Sleep/${invocation}`\n})\n\n/**\n * The sleep implementation: arm the durable clock, park under `timer`, wake.\n *\n * Provide it beside the other action implementation layers a body calls, over\n * the {@link module:Implementations.layerImplementations} table they file\n * themselves in:\n *\n * ```ts\n * Layer.mergeAll(Sleep.layer, Interpreter.layer(Pipeline)).pipe(\n * Layer.provideMerge(Action.layerImplementations)\n * )\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<never, never, Crypto.Crypto | FlowRuntime> = action.toLayer((payload) =>\n Effect.gen(function*() {\n // Whole milliseconds, both of them. A durable deadline lands in an INTEGER\n // column whose CHECK constraint refuses anything else\n // (`flows_runs.waiting_wake_at_ms`, `flows_clock_deadlines.due_at_ms`), and\n // `Clock.currentTimeMillis` is not integral under every clock: a test clock\n // advanced by a fractional-millisecond sleep reports a fractional now, and\n // a fractional `millis` or `until` in the payload does the same. The\n // unrounded deadline reached the park write as a raw SQL constraint\n // failure, which killed the drive and left the run `running` with nothing\n // left to wake it. `now` floors, and the deadline ceils so a rounded sleep\n // never ends before the instant it was asked to wait for.\n const now = Math.floor(yield* Clock.currentTimeMillis)\n const wakeAt = Math.ceil(yield* deadlineOf(payload, now))\n const remaining = wakeAt - now\n // DECIDED: a deadline that has already passed\n // settles the node instead of parking it. A durable wait exists to survive\n // the gap until a deadline, and a run that resumes after its own deadline\n // has to make progress rather than park again on a timer that can only fire\n // in the past.\n if (remaining <= 0) return\n const name = yield* clockName\n // DECIDED: the park is declared rather than\n // left to a driver's derivation, so a host with no clock table still parks\n // under `timer`. A `millis` deadline is recomputed from the current clock,\n // so a round re-driven before its timer fires re-declares a `wakeAt` later\n // than the armed one; the wake itself is unaffected, because the durable\n // clock row is keyed by name and keeps the deadline it was first armed\n // with, and `wakeAt` is a sweeper hint rather than the wake.\n yield* annotateWaiting({ reason: \"timer\", wakeAt })\n yield* DurableClock.sleep({\n name,\n duration: Duration.millis(remaining),\n // The wait is the point: a sleep called as a plan node parks the\n // execution rather than holding a fiber open, so the in-memory shortcut\n // is disabled instead of being left at its 60-second default.\n inMemoryThreshold: Duration.zero\n })\n })\n)\n"]}
package/docs/README.md CHANGED
@@ -36,7 +36,9 @@ restarted. Skip it when a plain `Effect` retry loop covers the whole problem.
36
36
 
37
37
  ## Install
38
38
 
39
- Not on npm yet; see [Installation](./installation.md#use-the-libraries).
39
+ ```bash
40
+ npm install @smthrs/flow@next effect
41
+ ```
40
42
 
41
43
  Node.js 26.4.0 or later. [Installation](./installation.md) covers the import
42
44
  forms, and what each companion package supplies.