@w6w/embed 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -4
- package/dist/ConfirmModal-RLbZT5_k.js +181 -0
- package/dist/ConfirmModal-RLbZT5_k.js.map +1 -0
- package/dist/CreateFunctionModal-DRsFx-FB.js +221 -0
- package/dist/CreateFunctionModal-DRsFx-FB.js.map +1 -0
- package/dist/DocumentFormModal-BExK1V-B.js +89 -0
- package/dist/DocumentFormModal-BExK1V-B.js.map +1 -0
- package/dist/ImportSpecModal-BDQIf6rg.js +136 -0
- package/dist/ImportSpecModal-BDQIf6rg.js.map +1 -0
- package/dist/W6WEmbedProvider.d.ts +22 -4
- package/dist/WorkflowEditor-DlbVv6Fs.js +854 -0
- package/dist/WorkflowEditor-DlbVv6Fs.js.map +1 -0
- package/dist/api-calls--JHyif6a.js +107 -0
- package/dist/api-calls--JHyif6a.js.map +1 -0
- package/dist/apps-1FNUwbGv.js +77 -0
- package/dist/apps-1FNUwbGv.js.map +1 -0
- package/dist/callables-3mFrMVG_.js +151 -0
- package/dist/callables-3mFrMVG_.js.map +1 -0
- package/dist/chrome/index.d.ts +6 -0
- package/dist/chrome/workspace.d.ts +29 -1
- package/dist/coerce-input-BicYr7nc.js +139 -0
- package/dist/coerce-input-BicYr7nc.js.map +1 -0
- package/dist/connections-CjjYeRqb.js +78 -0
- package/dist/connections-CjjYeRqb.js.map +1 -0
- package/dist/document-sources-C0grrtdq.js +86 -0
- package/dist/document-sources-C0grrtdq.js.map +1 -0
- package/dist/draft/index.d.ts +3 -1
- package/dist/draft/use-callable-draft.d.ts +65 -0
- package/dist/draft/workflow-save.d.ts +3 -0
- package/dist/draft.mjs +9 -5
- package/dist/draft.mjs.map +1 -1
- package/dist/editor/CallableEditorFrame.d.ts +33 -0
- package/dist/editor/EditorShell.d.ts +16 -0
- package/dist/editor/EndpointCards.d.ts +56 -0
- package/dist/editor/EndpointEditor.d.ts +26 -0
- package/dist/editor/FunctionEditor.d.ts +21 -0
- package/dist/editor/InputListEditor.d.ts +29 -0
- package/dist/editor/TargetSelector.d.ts +30 -0
- package/dist/editor/TargetSelector.types.d.ts +62 -0
- package/dist/editor/editor-apps.d.ts +8 -2
- package/dist/editor/types.d.ts +57 -0
- package/dist/editor-apps-BdenSV1o.js +95 -0
- package/dist/editor-apps-BdenSV1o.js.map +1 -0
- package/dist/editor-tools-RxmY5iyi.js +1279 -0
- package/dist/editor-tools-RxmY5iyi.js.map +1 -0
- package/dist/editor.mjs +2 -2166
- package/dist/editor.mjs.map +1 -1
- package/dist/editors/DocumentEditor.d.ts +20 -0
- package/dist/editors/doc-format.d.ts +3 -0
- package/dist/editors.d.ts +12 -0
- package/dist/editors.mjs +1313 -0
- package/dist/editors.mjs.map +1 -0
- package/dist/embed-status.d.ts +20 -0
- package/dist/endpoints-B2ynbZo-.js +227 -0
- package/dist/endpoints-B2ynbZo-.js.map +1 -0
- package/dist/forms/CreateEndpointModal.d.ts +54 -0
- package/dist/forms/CreateFunctionModal.d.ts +55 -0
- package/dist/forms/DocumentFormModal.d.ts +8 -0
- package/dist/forms/SecretsManager.d.ts +12 -0
- package/dist/forms/VarsManager.d.ts +29 -0
- package/dist/forms.d.ts +6 -0
- package/dist/forms.mjs +407 -0
- package/dist/forms.mjs.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +153 -50
- package/dist/index.mjs.map +1 -1
- package/dist/lib/callable-record.d.ts +21 -0
- package/dist/lib/coerce-input.d.ts +6 -0
- package/dist/lib/endpoint-editor.d.ts +23 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/input-coverage.d.ts +28 -0
- package/dist/lib/input-seeding.d.ts +67 -0
- package/dist/lib/rail-settings.d.ts +3 -3
- package/dist/lib/recent-activity.d.ts +128 -0
- package/dist/lib/target-fields.d.ts +168 -0
- package/dist/lib/workflow-blank.d.ts +2 -0
- package/dist/lib.mjs +70 -48
- package/dist/lib.mjs.map +1 -1
- package/dist/lists/ActivitySummary.d.ts +29 -0
- package/dist/lists/CallableKindList.d.ts +34 -0
- package/dist/lists/ConnectionDetail.d.ts +17 -0
- package/dist/lists/ConnectionList.d.ts +16 -0
- package/dist/lists/DocumentList.d.ts +29 -0
- package/dist/lists/EndpointList.d.ts +19 -0
- package/dist/lists/FunctionList.d.ts +18 -0
- package/dist/lists/ReliabilityBoard.d.ts +23 -0
- package/dist/lists/RunHistory.d.ts +11 -0
- package/dist/lists/ServiceDetail.d.ts +20 -0
- package/dist/lists/WorkflowList.d.ts +19 -0
- package/dist/lists/vendor-history.d.ts +40 -0
- package/dist/lists.d.ts +20 -0
- package/dist/lists.mjs +1185 -0
- package/dist/lists.mjs.map +1 -0
- package/dist/project-context-BiCjv_E3.js +25 -0
- package/dist/project-context-BiCjv_E3.js.map +1 -0
- package/dist/project-context.d.ts +15 -1
- package/dist/rail/CallableRail.d.ts +12 -0
- package/dist/rail/CallableWorkspace.d.ts +16 -0
- package/dist/rail/SettingsTool.d.ts +7 -1
- package/dist/rail/settings-sections.d.ts +2 -2
- package/dist/rail-settings-XYGToh00.js +111 -0
- package/dist/rail-settings-XYGToh00.js.map +1 -0
- package/dist/rail.d.ts +11 -0
- package/dist/rail.mjs +35 -0
- package/dist/rail.mjs.map +1 -0
- package/dist/repos/api-calls.d.ts +4 -0
- package/dist/repos/apps.d.ts +14 -0
- package/dist/repos/callables.d.ts +91 -0
- package/dist/repos/connections.d.ts +33 -0
- package/dist/repos/dashboard.d.ts +4 -0
- package/dist/repos/endpoints.d.ts +125 -0
- package/dist/repos/functions.d.ts +79 -0
- package/dist/repos/index.d.ts +10 -0
- package/dist/repos/reliability.d.ts +42 -0
- package/dist/repos/saved-tests.d.ts +46 -0
- package/dist/repos/spec-export.d.ts +23 -0
- package/dist/repos/workflows.d.ts +138 -0
- package/dist/repos.mjs +174 -88
- package/dist/repos.mjs.map +1 -1
- package/dist/settings-rail--GdRMusb.js +33 -0
- package/dist/settings-rail--GdRMusb.js.map +1 -0
- package/dist/slug-C4ktDPdy.js +315 -0
- package/dist/slug-C4ktDPdy.js.map +1 -0
- package/dist/spec-export-Cf3VQSQS.js +176 -0
- package/dist/spec-export-Cf3VQSQS.js.map +1 -0
- package/dist/spec-import-CjmxyZDR.js +465 -0
- package/dist/spec-import-CjmxyZDR.js.map +1 -0
- package/dist/step-tests-CivICmTq.js +18 -0
- package/dist/step-tests-CivICmTq.js.map +1 -0
- package/dist/trigger-preview-VPkrryDl.js +120 -0
- package/dist/trigger-preview-VPkrryDl.js.map +1 -0
- package/dist/use-callable-draft-zHLpcr-b.js +271 -0
- package/dist/use-callable-draft-zHLpcr-b.js.map +1 -0
- package/dist/{use-workflow-draft-oI02dsP-.js → use-workflow-draft-DzAyUeA6.js} +48 -46
- package/dist/use-workflow-draft-DzAyUeA6.js.map +1 -0
- package/dist/vault-BSEWhMYC.js +117 -0
- package/dist/vault-BSEWhMYC.js.map +1 -0
- package/dist/workflow-blank-DyL3_RUA.js +14 -0
- package/dist/workflow-blank-DyL3_RUA.js.map +1 -0
- package/package.json +21 -5
- package/src/styles.css +408 -1
- package/dist/project-context-Dbk0Gr1K.js +0 -16
- package/dist/project-context-Dbk0Gr1K.js.map +0 -1
- package/dist/slug-ByKQgxrf.js +0 -642
- package/dist/slug-ByKQgxrf.js.map +0 -1
- package/dist/spec-import-HgO7ZkLZ.js +0 -898
- package/dist/spec-import-HgO7ZkLZ.js.map +0 -1
- package/dist/use-workflow-draft-oI02dsP-.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spec-import-HgO7ZkLZ.js","sources":["../src/lib/workflow-live-run.ts","../src/lib/history-query.ts","../src/lib/workflow-poll.ts","../src/repos/workflows.ts","../src/lib/callable-kind.ts","../src/lib/save-invalidation.ts","../src/repos/versions.ts","../src/repos/step-tests.ts","../src/repos/history.ts","../src/repos/vars.ts","../src/repos/vault.ts","../src/repos/documents.ts","../src/lib/new-id.ts","../src/lib/callable-descriptors.ts","../src/repos/spec-import.ts"],"sourcesContent":["/**\n * Pure decision layer for the visual editor's live Run/Stop feature (T2.1.1,\n * A7). Every judgment call this feature makes that is NOT layout — is a run\n * in flight? should polling continue? is the canvas editable right now? what\n * should the Run button say and is it disabled? how does a cancel failure\n * get handled? how does a `RunState`'s steps map to the execution log\n * panel's rows, and what error does each row and the run show? what `input`\n * does the header Run send? — lives here as plain functions of (run state) -> result,\n * with no `.tsx` import and no React import, so `node --test` can load this\n * module directly. Same mechanical reason `workflow-poll.ts` and\n * `repos/reliability-uptime.ts` exist as separate `.ts` modules — read\n * `workflow-poll.ts`'s own header. `WorkflowsPage.tsx` calls these functions\n * rather than restating the conditions inline.\n */\nimport { type RunStatus, isTerminalRunStatus } from \"@w6w/sdk\";\n\nexport type { RunStatus };\n\n/**\n * The five states a workflow step's execution can be in — a thin LOCAL copy\n * of `@w6w/ui`'s `StepStatus` (itself a thin local copy of the engine's\n * `StepStatus` — see `run-visuals.ts`'s own header in `packages/ui`, the\n * same house idiom). `@w6w/sdk/console`'s `RunState.steps` is deliberately\n * typed `Record<string, unknown>` — opaque pass-through, by that module's\n * own documented design — so the real per-step shape has to be re-stated\n * here rather than imported.\n */\nexport type StepStatus = \"pending\" | \"running\" | \"succeeded\" | \"failed\" | \"skipped\";\n\n/** The per-step metadata (which app/version/action/connection ran) — one\n * local structural copy of the pinned `StepMeta`. A `type` alias, not an\n * `interface`, so it stays assignable to index-signature types. */\nexport type StepMetaLike = {\n app: string;\n version: string;\n action: string;\n connectionId: string | null;\n};\n\n/** The slice of a `StepExecution` this module reads, to build one\n * `ExecutionLogPanel` row. */\nexport interface StepExecutionLike {\n status: StepStatus;\n startedAt?: string;\n finishedAt?: string;\n input?: unknown;\n output?: unknown;\n /** `StepExecution.error` — a `StepError` on `failed` per rfcs/workflow.md\n * §StepExecution, but read as `unknown` because `RunState.steps` is opaque\n * on the wire; {@link toLogError} normalises whatever actually arrives. */\n error?: unknown;\n /** Recorded metadata; absent (or null) for steps that recorded none. */\n meta?: StepMetaLike | null;\n}\n\n/** A step or run error, as the run log panel renders it — field-for-field\n * `@w6w/ui`'s `ExecutionLogError` (rfcs/workflow.md §StepError's\n * `code`/`message`/`phase`/`retryable`), restated locally for the same reason\n * as {@link ExecutionLogStepLike} below. `message` is never empty. */\nexport interface LogErrorLike {\n code?: string;\n message: string;\n phase?: string;\n retryable?: boolean;\n}\n\n/** One `RunState.stepErrors` entry the panel shows at run level — field-for-field\n * `@w6w/ui`'s `ExecutionLogRecordedError`, restated locally (see above). */\nexport interface RecordedLogErrorLike {\n stepId: string;\n label?: string;\n error: LogErrorLike;\n}\n\n/** The slice of a `RunState` {@link runLevelErrors} reads. */\nexport interface RunErrorSourceLike {\n status?: string;\n error?: unknown;\n stepErrors?: ReadonlyArray<{ stepId: string; error: unknown }>;\n}\n\n/** The slice of a saved step-test fixture (`StepTest`) {@link latestFixtureInput}\n * reads — `with` is what the trigger's Test tab invokes it with. */\nexport interface StepTestFixtureLike {\n with?: unknown;\n}\n\n/** The row-level fallback for a `failed` step that carries no `error` at all. */\nexport const STEP_FAILED_NO_REASON = \"Step failed without reporting a reason.\";\n\n/** The run-level fallback for a `failed` run where nothing carries a reason. */\nexport const RUN_FAILED_NO_REASON = \"Run failed without reporting a reason.\";\n\n/** One `ExecutionLogPanel` row — field-for-field `@w6w/ui`'s\n * `ExecutionLogStep`, restated locally for the same reason as\n * {@link StepExecutionLike} above (no runtime dependency on `@w6w/ui`,\n * which this module must not import — see the file header). */\nexport interface ExecutionLogStepLike {\n id: string;\n status: StepStatus;\n startedAt?: string;\n finishedAt?: string;\n input?: unknown;\n output?: unknown;\n error?: LogErrorLike;\n meta?: StepMetaLike;\n}\n\n/** How a cancel-request failure should be handled (A6). */\nexport type CancelErrorOutcome = \"reconcile\" | \"surface\";\n\n/**\n * A2/M1 — the `refetchInterval` decision: keep polling (`true`) until the\n * run's status is terminal (`false`), including while no run has answered\n * yet (`status === undefined` — the very first poll hasn't landed, or no run\n * is active at all).\n */\nexport function shouldContinuePolling(status: RunStatus | undefined): boolean {\n return status === undefined || !isTerminalRunStatus(status);\n}\n\n/**\n * A4/M2/M3 — is a run currently \"in flight\" from this page's point of view?\n * `true` only while the poll has a live, non-terminal status AND the poll\n * itself hasn't given up/errored — a failed poll unlocks the canvas exactly\n * like reaching a real terminal status does (A4's own named failure case: \"a\n * canvas that stays locked after a cancel\"). Drives the canvas's `readOnly`\n * prop, the Stop button's visibility, and the Run button's disabled state —\n * one function, three call sites, so they can never drift out of sync with\n * each other.\n */\nexport function isRunInFlight(status: RunStatus | undefined, pollFailed: boolean): boolean {\n if (pollFailed) return false;\n if (status === undefined) return false;\n return !isTerminalRunStatus(status);\n}\n\n/** A1/A6 — what the Run button should read right now. */\nexport function runButtonLabel(status: RunStatus | undefined, pollFailed: boolean): string {\n if (!isRunInFlight(status, pollFailed)) return \"Run\";\n return status === \"queued\" ? \"Queued…\" : \"Running…\";\n}\n\n/**\n * A6/M6 — classify a `cancel()` failure. `run_not_cancelable` (409) means\n * the run already reached a terminal state before the cancel request landed\n * — not a user-facing error, just reconcile to whatever the next poll\n * reports. Everything else (`unknown_run`, a network failure, …) is a real\n * failure the page's existing error affordance must show.\n */\nexport function classifyCancelError(code: string): CancelErrorOutcome {\n return code === \"run_not_cancelable\" ? \"reconcile\" : \"surface\";\n}\n\n/**\n * G5 — map a `RunState`'s opaque `steps` record onto `ExecutionLogPanel`'s\n * row shape, in the record's own key order (the order the server checkpoints\n * steps in, so it already reads as execution order). `ExecutionLogPanel`'s\n * own contract is \"never re-sorted\" (`ExecutionLogPanel.tsx`'s header), so\n * this function must not reorder either.\n */\nexport function mapRunStateToLogSteps(\n steps: Record<string, StepExecutionLike> | undefined,\n): ExecutionLogStepLike[] {\n if (!steps) return [];\n return Object.entries(steps).map(([id, step]) => ({\n id,\n status: step.status,\n startedAt: step.startedAt,\n finishedAt: step.finishedAt,\n input: step.input,\n output: step.output,\n ...(step.meta !== undefined && step.meta !== null ? { meta: step.meta } : {}),\n ...rowError(step),\n }));\n}\n\n/** The `error` key for one row — present ONLY when the step reports an error\n * or is `failed` (then with {@link STEP_FAILED_NO_REASON}), so a healthy row\n * keeps exactly the shape it had before errors were carried. */\nfunction rowError(step: StepExecutionLike): { error?: LogErrorLike } {\n const error = toLogError(step.error);\n if (error) return { error };\n if (step.status === \"failed\") return { error: { message: STEP_FAILED_NO_REASON } };\n return {};\n}\n\n/**\n * Normalise whatever a step or run reported as its error into the panel's\n * {@link LogErrorLike}. `null`/`undefined` mean \"no error\". A non-empty string\n * is the message. An object contributes `code` (string), `message` (its own\n * non-empty `message`, else a non-empty `code`, else its JSON), `phase`\n * (string) and `retryable` (boolean). Anything else is stringified. The\n * returned `message` is never empty.\n */\nexport function toLogError(raw: unknown): LogErrorLike | undefined {\n if (raw === null || raw === undefined) return undefined;\n if (typeof raw === \"string\" && raw !== \"\") return { message: raw };\n if (typeof raw === \"object\") {\n const o = raw as Record<string, unknown>;\n const code = typeof o.code === \"string\" ? o.code : undefined;\n const message =\n typeof o.message === \"string\" && o.message !== \"\" ? o.message : code || safeJson(raw);\n return {\n ...(code !== undefined ? { code } : {}),\n message: message || \"Unknown error.\",\n ...(typeof o.phase === \"string\" ? { phase: o.phase } : {}),\n ...(typeof o.retryable === \"boolean\" ? { retryable: o.retryable } : {}),\n };\n }\n return { message: String(raw) || \"Unknown error.\" };\n}\n\n/** `JSON.stringify` that answers `\"\"` instead of throwing (a cycle, a BigInt)\n * or returning `undefined` (a `toJSON` that yields nothing). */\nfunction safeJson(raw: unknown): string {\n try {\n return JSON.stringify(raw) ?? \"\";\n } catch {\n return \"\";\n }\n}\n\n/**\n * The run-level half of the run log: `RunState.error` as `runError`, plus the\n * `RunState.stepErrors` entries whose step has NO row already carrying an\n * error (a `continue-record` failure is usually also on its own row — showing\n * it twice would read as two failures), in their given order. A row showing\n * only {@link STEP_FAILED_NO_REASON} does not count as carrying an error here,\n * so the reason recorded for that step is still surfaced. A `failed` run where\n * nothing — run, rows or surfaced step errors — carries a reason gets\n * {@link RUN_FAILED_NO_REASON}. `runError` is omitted when it duplicates a\n * row's error (equal `message` and equal `code`), since the step failure that\n * stopped the run is already shown on its row. It is likewise omitted when it\n * duplicates a surfaced `stepErrors` entry's error, by the same equality.\n * Keys are omitted when empty.\n */\nexport function runLevelErrors(\n run: RunErrorSourceLike | undefined,\n rows: ReadonlyArray<ExecutionLogStepLike>,\n): { runError?: LogErrorLike; stepErrors?: RecordedLogErrorLike[] } {\n if (!run) return {};\n const carried = new Set(rows.filter((r) => r.error !== undefined).map((r) => r.id));\n const reasoned = new Set(\n rows\n .filter((r) => r.error !== undefined && r.error.message !== STEP_FAILED_NO_REASON)\n .map((r) => r.id),\n );\n const stepErrors = (run.stepErrors ?? [])\n .filter((e) => !reasoned.has(e.stepId))\n .map((e) => ({\n stepId: e.stepId,\n error: toLogError(e.error) ?? { message: STEP_FAILED_NO_REASON },\n }));\n const own = toLogError(run.error);\n const sameAsOwn = (e: LogErrorLike | undefined) =>\n own !== undefined && e?.message === own.message && e?.code === own.code;\n const shown = rows.some((r) => sameAsOwn(r.error)) || stepErrors.some((e) => sameAsOwn(e.error));\n let runError = shown ? undefined : own;\n if (!runError && run.status === \"failed\" && carried.size === 0 && stepErrors.length === 0) {\n runError = { message: RUN_FAILED_NO_REASON };\n }\n return {\n ...(runError ? { runError } : {}),\n ...(stepErrors.length > 0 ? { stepErrors } : {}),\n };\n}\n\n/**\n * The header Run's `input`: the LAST saved fixture's `with` (`listStepTests`\n * answers oldest-first — the same \"last is latest\" reading\n * `@w6w/ui`'s `TriggerFillForm` seeds from), when it is a plain object.\n * Sent verbatim — `{type:\"expr\",…}` values included.\n */\nexport function latestFixtureInput(\n tests: ReadonlyArray<StepTestFixtureLike> | undefined,\n): Record<string, unknown> | undefined {\n const last = tests?.[tests.length - 1]?.with;\n if (last === null || typeof last !== \"object\" || Array.isArray(last)) return undefined;\n return last as Record<string, unknown>;\n}\n\n/**\n * Resolve the header Run's `input` for the manual trigger `triggerStepId`.\n * No trigger id → `undefined` without fetching. A fetch that throws or\n * rejects → `undefined`: a missing fixture never blocks the run. Fetched at\n * click time on purpose — a cached `stepTestsKey` query can be stale, since\n * saving a fixture does not invalidate it.\n */\nexport async function resolveHeaderRunInput(\n triggerStepId: string | undefined,\n list: (stepId: string) => Promise<ReadonlyArray<StepTestFixtureLike> | undefined>,\n): Promise<Record<string, unknown> | undefined> {\n if (!triggerStepId) return undefined;\n try {\n return latestFixtureInput(await list(triggerStepId));\n } catch {\n return undefined;\n }\n}\n","// Run: node --test src/lib/__tests__/history-query.test.ts (Node 24, type-stripped)\n//\n// T2.3.1 — the pure data layer behind the project-wide and per-callable\n// History page (T2.4.1) and the Function/Endpoint/Workflow header's History\n// button (T2.4.2). Everything that is a judgment call rather than React\n// plumbing lives here: how a URL's search string becomes a `HistoryFilters`\n// (and back), how a filter set becomes the wire's own query params, the\n// query keys every hook in `repos/history.ts` shares, and how a summary row\n// is expanded into a full detail view for one of the three callable kinds.\n//\n// Zero runtime imports other than `./workflow-live-run.ts` (A1) — every other\n// import here is `import type`, erased at build time, so `node --test` can\n// load this module directly with no React, no SDK runtime, no JSX. The one\n// runtime import reuses `mapRunStateToLogSteps`, the same step mapper the\n// visual editor's live-run overlay already uses, so a workflow's historical\n// detail view renders steps identically to its live one.\n//\n// The wire shape (`ExecutionKind`, `ExecutionStatus`, `ExecutionSummary`,\n// `ExecutionStats`) is pinned verbatim against T2.1.1's own contract — do not\n// drift it; T2.4.1/T2.4.2 are written against this file's exports exactly.\n\nimport type { W6WClient } from \"@w6w/sdk\";\nimport { type StepExecutionLike, mapRunStateToLogSteps } from \"./workflow-live-run.ts\";\n\n// ── wire types (pinned, identical to T2.1.1 §Wire) ──────────────────────\n\nexport type ExecutionKind = \"function\" | \"endpoint\" | \"workflow\";\nexport type ExecutionStatus = \"queued\" | \"running\" | \"succeeded\" | \"failed\" | \"canceled\";\n\nexport interface ExecutionSummary {\n id: string;\n kind: ExecutionKind;\n callableId: string;\n callableName: string;\n status: ExecutionStatus;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n workflowRunId: string | null;\n}\n\nexport interface ExecutionStats {\n total: number;\n succeeded: number;\n failed: number;\n canceled: number;\n inFlight: number;\n successRate: number | null;\n avgDurationMs: number | null;\n}\n\n// ── scope + filters ───────────────────────────────────────────────────────\n\n/** Either the project-wide view or one callable's own history — mutually\n * exclusive, never both, mirroring the wire's own `project`|`callable`\n * params (exactly one). */\nexport type HistoryScope = { project: string } | { callable: string };\n\n/** Everything the History page's filter chrome can set, already parsed out\n * of the URL. `status`/`kind` are `\"\"` for \"any\"; `from`/`to` are the\n * `YYYY-MM-DD` the date pickers hand back, or `\"\"` for unset; `offset` is\n * always a non-negative integer (0 for \"first page\"). */\nexport interface HistoryFilters {\n status: ExecutionStatus | \"\";\n kind: ExecutionKind | \"\";\n from: string;\n to: string;\n q: string;\n offset: number;\n}\n\nexport const EMPTY_HISTORY_FILTERS: HistoryFilters = {\n status: \"\",\n kind: \"\",\n from: \"\",\n to: \"\",\n q: \"\",\n offset: 0,\n};\n\nexport const HISTORY_PAGE_SIZE = 25;\n\nconst VALID_STATUSES: ReadonlySet<string> = new Set([\n \"queued\",\n \"running\",\n \"succeeded\",\n \"failed\",\n \"canceled\",\n]);\nconst VALID_KINDS: ReadonlySet<string> = new Set([\"function\", \"endpoint\", \"workflow\"]);\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** True when `YYYY-MM-DD` names a real calendar day (`2026-02-30` rolls into\n * March through `new Date`, so a round-trip comparison rejects it). */\nfunction isRealDate(s: string): boolean {\n if (!DATE_RE.test(s)) return false;\n const { y, m, d } = dateParts(s);\n const dt = new Date(y, m - 1, d);\n return dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d;\n}\n\n/** Parse a validated `YYYY-MM-DD` string into its three numeric parts. */\nfunction dateParts(value: string): { y: number; m: number; d: number } {\n const [y, m, d] = value.split(\"-\").map(Number);\n return { y, m, d };\n}\n\n/**\n * URL search string → `HistoryFilters`. An unrecognised `status`/`kind`, a\n * `from`/`to` not matching `YYYY-MM-DD`, or a non-integer/negative `offset`\n * all fall back to their empty/zero default rather than carrying a bogus\n * value forward into a request — a stale or hand-edited URL degrades to \"no\n * filter\" instead of erroring.\n */\nexport function filtersFromSearch(search: string): HistoryFilters {\n const params = new URLSearchParams(search);\n const status = params.get(\"status\") ?? \"\";\n const kind = params.get(\"kind\") ?? \"\";\n const from = params.get(\"from\") ?? \"\";\n const to = params.get(\"to\") ?? \"\";\n const q = params.get(\"q\") ?? \"\";\n const offsetRaw = params.get(\"offset\");\n let offset = 0;\n if (offsetRaw !== null) {\n const n = Number(offsetRaw);\n if (Number.isInteger(n) && n >= 0) offset = n;\n }\n return {\n status: VALID_STATUSES.has(status) ? (status as ExecutionStatus) : \"\",\n kind: VALID_KINDS.has(kind) ? (kind as ExecutionKind) : \"\",\n from: isRealDate(from) ? from : \"\",\n to: isRealDate(to) ? to : \"\",\n q,\n offset,\n };\n}\n\n/**\n * `HistoryFilters` → URL search string. `\"\"` when every field is at its\n * default, else a leading-`?` string with only the non-default fields, in\n * the pinned key order `status,kind,from,to,q,offset` (so two renders of the\n * same filters always produce byte-identical URLs — stable for bookmarking\n * and for `filtersFromSearch` round-tripping).\n */\nexport function filtersToSearch(f: HistoryFilters): string {\n const params = new URLSearchParams();\n if (f.status !== \"\") params.set(\"status\", f.status);\n if (f.kind !== \"\") params.set(\"kind\", f.kind);\n if (f.from !== \"\") params.set(\"from\", f.from);\n if (f.to !== \"\") params.set(\"to\", f.to);\n if (f.q !== \"\") params.set(\"q\", f.q);\n if (f.offset > 0) params.set(\"offset\", String(f.offset));\n const s = params.toString();\n return s === \"\" ? \"\" : `?${s}`;\n}\n\n/**\n * `HistoryFilters` + a scope → the wire's own query params (D3/pinned rules).\n * `kind` is sent only for a project scope with a non-empty kind — a\n * per-callable view already knows its one kind, and the server's `kind`\n * param is project-mode-only. `from`/`to` are expanded from the picked\n * calendar day into the half-open `[from, to)` ISO instant range the wire\n * wants — `to` is the NEXT day's midnight so the picked day itself is fully\n * inclusive. `q` is trimmed and omitted when blank (server-side id/name\n * search, D3 — never a payload scan). Paging params are added only when\n * `opts.paged`: `limit` always (defaulting to `HISTORY_PAGE_SIZE`), `offset`\n * only when it would change anything (`> 0`).\n */\nexport function historyRequestQuery(\n scope: HistoryScope,\n f: HistoryFilters,\n opts: { paged: boolean; limit?: number },\n): Record<string, string> {\n const query: Record<string, string> = {};\n\n if (\"project\" in scope) {\n query.project = scope.project;\n if (f.kind !== \"\") query.kind = f.kind;\n } else {\n query.callable = scope.callable;\n }\n\n if (f.status !== \"\") query.status = f.status;\n\n if (f.from !== \"\" && isRealDate(f.from)) {\n const { y, m, d } = dateParts(f.from);\n query.from = new Date(y, m - 1, d).toISOString();\n }\n if (f.to !== \"\" && isRealDate(f.to)) {\n const { y, m, d } = dateParts(f.to);\n query.to = new Date(y, m - 1, d + 1).toISOString();\n }\n\n const q = f.q.trim();\n if (q !== \"\") query.q = q;\n\n if (opts.paged) {\n query.limit = String(opts.limit ?? HISTORY_PAGE_SIZE);\n if (f.offset > 0) query.offset = String(f.offset);\n }\n\n return query;\n}\n\n// ── cache keys (ship from here, never from a caller — mirrors `repos/dashboard.ts`) ──\n\n/**\n * Every key this domain reads or invalidates. `list`/`stats` carry the full\n * scope and filters object so two distinct filter sets never collide on the\n * same cache entry (and the identical pair always do) — react-query's own\n * structural key hashing does the rest.\n */\nexport const historyKeys = {\n root: [\"history\"] as const,\n list(scope: HistoryScope, f: HistoryFilters, limit: number): readonly unknown[] {\n return [\"history\", \"list\", scope, f, limit];\n },\n stats(scope: HistoryScope, f: HistoryFilters): readonly unknown[] {\n return [\"history\", \"stats\", scope, f];\n },\n exists(callableId: string): readonly [\"history\", \"exists\", string] {\n return [\"history\", \"exists\", callableId];\n },\n detail(kind: ExecutionKind, id: string): readonly [\"history\", \"detail\", ExecutionKind, string] {\n return [\"history\", \"detail\", kind, id];\n },\n};\n\n// ── fetchers ─────────────────────────────────────────────────────────────\n\n/** `GET /history` — paged. */\nexport function getHistory(\n client: W6WClient,\n scope: HistoryScope,\n f: HistoryFilters,\n limit: number = HISTORY_PAGE_SIZE,\n): Promise<{ executions: ExecutionSummary[]; hasMore: boolean }> {\n return client\n .request<{ executions: ExecutionSummary[]; hasMore: boolean }>({\n method: \"GET\",\n path: \"/history\",\n query: historyRequestQuery(scope, f, { paged: true, limit }),\n })\n .then((response) => response.body);\n}\n\n/** `GET /history/stats` — unpaged (no `limit`/`offset`). */\nexport function getHistoryStats(\n client: W6WClient,\n scope: HistoryScope,\n f: HistoryFilters,\n): Promise<ExecutionStats> {\n return client\n .request<{ stats: ExecutionStats }>({\n method: \"GET\",\n path: \"/history/stats\",\n query: historyRequestQuery(scope, f, { paged: false }),\n })\n .then((response) => response.body.stats);\n}\n\n/** One execution's full detail — the shape `ExecutionDetail` (`@w6w/ui`,\n * T2.2.2) renders. `steps` is present only for a workflow run; a\n * function/endpoint invocation has none. */\nexport interface ExecutionDetailData {\n id: string;\n kind: ExecutionKind;\n callableName: string;\n status: ExecutionStatus;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n steps?: Array<{\n id: string;\n status: \"pending\" | \"running\" | \"succeeded\" | \"failed\" | \"skipped\";\n startedAt?: string;\n finishedAt?: string;\n input?: unknown;\n output?: unknown;\n }>;\n}\n\n/** The `GET /invocations/:id` response body's shape this module reads\n * (context notes: server `db/repos/invocations.ts` `InvocationRow`). */\ninterface InvocationDetailBody {\n status: ExecutionStatus;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n durationMs: number | null;\n startedAt: string;\n finishedAt: string | null;\n}\n\n/**\n * Expand a list row into its full detail. Function/Endpoint invocations are\n * synchronous calls recorded one row per call (`.claude/docs/invocations.md`)\n * — `GET /invocations/:id` by the escape-hatch `client.request` (no typed SDK\n * method exists for this internal route, same reasoning as\n * `repos/callables.ts`'s `archiveCallable`/`deleteCallable` split; A1: no\n * other runtime import needed). A workflow run instead reuses\n * `client.console.workflows.getRun` (`repos/workflows.ts`'s own\n * `getRunState`) — its `variables` become the detail's `input`, and its\n * opaque `steps` map through the SAME `mapRunStateToLogSteps` the live-run\n * overlay uses, so a historical run's step list renders identically to a\n * live one. `callableName` always comes from the summary — neither read\n * carries a human-readable callable name of its own.\n */\nexport async function getExecutionDetail(\n client: W6WClient,\n summary: ExecutionSummary,\n): Promise<ExecutionDetailData> {\n if (summary.kind === \"workflow\") {\n const run = await client.console.workflows.getRun(summary.id);\n return {\n id: summary.id,\n kind: \"workflow\",\n callableName: summary.callableName,\n status: run.status,\n startedAt: summary.startedAt,\n finishedAt: summary.finishedAt,\n durationMs: summary.durationMs,\n input: run.variables,\n output: run.output,\n error: run.error,\n steps: mapRunStateToLogSteps(run.steps as Record<string, StepExecutionLike> | undefined),\n };\n }\n\n const response = await client.request<{ invocation: InvocationDetailBody }>({\n method: \"GET\",\n path: `/invocations/${encodeURIComponent(summary.id)}`,\n });\n const invocation = response.body.invocation;\n return {\n id: summary.id,\n kind: summary.kind,\n callableName: summary.callableName,\n status: invocation.status,\n startedAt: invocation.startedAt,\n finishedAt: invocation.finishedAt,\n durationMs: invocation.durationMs,\n input: invocation.input,\n output: invocation.output,\n error: invocation.error,\n };\n}\n","/**\n * The run-polling loop, split out of `api/client.ts`'s original `runWorkflow`\n * into its own PURE module — for the SAME mechanical reason\n * `repos/reliability-uptime.ts` exists (read that file's own header comment in\n * full): a `.ts` module that would otherwise transitively import\n * `../api/apiClient.tsx` (a `.tsx` file Node's type-stripping cannot load at\n * all) must be split out to stay reachable by `node --test`.\n *\n * Ported field-for-field from `api/client.ts`'s original `runWorkflow` (same\n * 500 ms interval, same 600-iteration cap, same \"one more fetch after the\n * loop\" shape) — the ONLY change is the injected `getRun`/`sleep` parameters,\n * which is what makes this loop testable on a fake clock instead of a real\n * 5-minute wait. See `../repos/workflows.ts` for the caller.\n */\nimport type { RunState } from \"@w6w/sdk/console\";\n\nconst TERMINAL = new Set([\"succeeded\", \"failed\", \"canceled\"]);\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Poll `getRun` until the run reaches a terminal status, or 600 polls (~5\n * minutes at the default 500ms interval) elapse — whichever comes first.\n * Ported verbatim from `api/client.ts`'s `runWorkflow`; see\n * `../repos/workflows.ts` for the caller.\n */\nexport async function pollRunToTerminal(\n getRun: () => Promise<RunState>,\n sleep: (ms: number) => Promise<void> = realSleep,\n): Promise<RunState> {\n for (let i = 0; i < 600; i++) {\n const run = await getRun();\n if (TERMINAL.has(run.status)) return run;\n await sleep(500);\n }\n return getRun();\n}\n","import { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { W6WClient, WorkflowRunResult } from \"@w6w/sdk\";\nimport type { RunState } from \"@w6w/sdk/console\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\nimport { historyKeys } from \"../lib/history-query.ts\";\nimport { shouldContinuePolling } from \"../lib/workflow-live-run.ts\";\nimport { pollRunToTerminal } from \"../lib/workflow-poll.ts\";\n\nexport type { RunState, RunSummary } from \"@w6w/sdk/console\";\nexport type { WorkflowStatus, WorkflowSummary } from \"@w6w/sdk\";\n\n// `project` distinguishes a project-scoped entry (WorkflowsPage.tsx) from the\n// unscoped one (TargetSelector.tsx, SubscriptionsPage.tsx) — genuinely DIFFERENT\n// queries (not a spelling split to unify like T2.2.2's appActions fix). Keep them\n// as distinct cache entries.\nexport const workflowsKey = (project?: string) => [\"workflows\", project] as const;\n\nexport function listWorkflows(client: W6WClient, project?: string) {\n return client.workflows.list({ project });\n}\n\nexport function useWorkflowsQuery(project?: string, enabled = true) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: workflowsKey(project),\n queryFn: () => listWorkflows(apiClient, project),\n enabled,\n });\n}\n\nexport const workflowKey = (id: string) => [\"workflow\", id] as const;\n\nexport function getWorkflow(client: W6WClient, id: string) {\n return client.workflows.get(id);\n}\n\nexport function useWorkflowQuery(id: string, enabled = true) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: workflowKey(id),\n queryFn: () => getWorkflow(apiClient, id),\n enabled,\n });\n}\n\n/**\n * A missing `project` here is not \"unscoped\" the way a read can legitimately\n * be — the write would silently land in the account's default project\n * instead of the one the caller is looking at. Both mutations refuse rather\n * than risk that; `WorkflowsPage.tsx`'s own `projectId != null` gate on\n * `useWorkflowsQuery` should mean `project` is always set by the time either\n * of these can actually be invoked from the UI, but the guard costs nothing.\n */\nexport function useCreateWorkflowMutation(project?: string) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (definition: unknown) => {\n if (!project) return Promise.reject(new Error(\"No project selected yet.\"));\n return apiClient.console.workflows.upsert(definition, { project });\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"workflows\"] }),\n });\n}\n\nexport function useSaveWorkflowMutation(project?: string) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { definition: unknown; ifUnmodifiedSince: string | null }) => {\n if (!project) return Promise.reject(new Error(\"No project selected yet.\"));\n return apiClient.console.workflows.upsert(vars.definition, {\n project,\n ifUnmodifiedSince: vars.ifUnmodifiedSince,\n });\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"workflows\"] }),\n });\n}\n\nexport function useArchiveWorkflowMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) => apiClient.workflows.archive(id),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"workflows\"] }),\n });\n}\n\nexport function useDeleteWorkflowMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) => apiClient.workflows.delete(id),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"workflows\"] }),\n });\n}\n\nexport const runsKey = (workflowId: string) => [\"runs\", workflowId] as const;\n\nexport function useRunsQuery(workflowId: string) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: runsKey(workflowId),\n queryFn: () => apiClient.console.workflows.listRuns(workflowId),\n });\n}\n\n/**\n * Start a run and poll it to a terminal state.\n *\n * `input` is the run's START PAYLOAD, and it is a different thing from\n * `variables`: the server hands it to the entry `@w6w/trigger` node as that\n * node's own output, which is what makes `{{ steps.<trigger>.output.<field> }}`\n * resolve in a real run. Omitted, that node outputs `{}` and every reference to\n * a trigger field resolves to the empty string — the failure mode filed as\n * `.ai/projects/backlog/26-07-29-01-trigger-run-payload.md`, where a step tests\n * green and then does nothing in production. `variables` seeds `vars.*` and\n * reaches the trigger node not at all.\n *\n * Goes through the typed `client.workflows.run()` with\n * `allowUnpublished: true`: this call site always runs the STORED draft, and\n * the server exempts a caller that sets the flag from the publish gate\n * (`packages/api/run-workflow.ts`'s `enqueueRun`). The SDK's result is the flat\n * run body, so the `runId` used to poll comes off `res.runId`.\n */\nexport function runWorkflow(\n client: W6WClient,\n id: string,\n variables: Record<string, unknown>,\n input?: Record<string, unknown>,\n): Promise<RunState> {\n return client.workflows\n .run(id, { variables, input, allowUnpublished: true })\n .then((res) => pollRunToTerminal(() => client.console.workflows.getRun(res.runId)));\n}\n\nexport function useRunWorkflowMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: {\n id: string;\n variables: Record<string, unknown>;\n input?: Record<string, unknown>;\n }) => runWorkflow(apiClient, vars.id, vars.variables, vars.input),\n onSuccess: (_res, vars) => qc.invalidateQueries({ queryKey: runsKey(vars.id) }),\n onSettled: () => qc.invalidateQueries({ queryKey: historyKeys.root }),\n });\n}\n\n/**\n * Run a Workflow SYNCHRONOUSLY — the `W6WApi.runWorkflow` facade member's\n * backing function (the Workflows tab's Test stage, D-11/D-15(c)). A sibling\n * of {@link runWorkflow}, NOT a replacement: that function's enqueue-then-poll\n * shape is the studio-wide \"Run this workflow\" action and stays exactly as it\n * is. This one instead sends `wait: true` on the SAME base `client.workflows\n * .run` call already published (`?wait=true` — the server polls internally,\n * up to its own timeout) — zero new server code, and no second poll loop:\n * there is no `pollRunToTerminal` here at all. `terminal`/`httpStatus` on the\n * result distinguish \"finished in time\" (`200`) from \"still going when the\n * server's own wait timed out\" (`202`, a legitimate outcome, not an error).\n */\nexport function runWorkflowSync(\n client: W6WClient,\n id: string,\n variables: Record<string, unknown>,\n input?: Record<string, unknown>,\n): Promise<WorkflowRunResult> {\n return client.workflows.run(id, { variables, input, wait: true });\n}\n\n/**\n * T2.1.1 — the ENQUEUE half only: starts a run and returns its handle\n * immediately, without polling it to terminal. A genuine sibling of\n * {@link runWorkflow}, NOT a replacement — that function's \"enqueue, then\n * block to a terminal result\" shape is what `WorkflowDetailPage.tsx`'s \"Run\n * now\" panel is built on and stays exactly as it is. This one is for a host\n * that wants the `runId` right away so it can poll it live itself (the\n * visual editor's canvas + execution log panel) — see {@link useLiveRunQuery}.\n *\n * Like {@link runWorkflow}, this calls `client.workflows.run()` with\n * `allowUnpublished: true` unconditionally. Only `{ runId }` is returned, so\n * callers keep reading `runId` off the resolved value exactly as before.\n */\nexport function startWorkflowRun(\n client: W6WClient,\n id: string,\n variables: Record<string, unknown>,\n input?: Record<string, unknown>,\n) {\n return client.workflows\n .run(id, { variables, input, allowUnpublished: true })\n .then((res) => ({ runId: res.runId }));\n}\n\nexport function useStartRunMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: {\n id: string;\n variables: Record<string, unknown>;\n input?: Record<string, unknown>;\n }) => startWorkflowRun(apiClient, vars.id, vars.variables, vars.input),\n onSuccess: (_res, vars) => qc.invalidateQueries({ queryKey: runsKey(vars.id) }),\n onSettled: () => qc.invalidateQueries({ queryKey: historyKeys.root }),\n });\n}\n\n/**\n * T2.1.1/A6 — cancel a queued or running run. `client.workflows.cancel`\n * itself never claims the run has stopped (its own doc comment: a `202`\n * means the request was RECORDED, not that the run finished) — the caller\n * keeps polling {@link useLiveRunQuery} until the run actually reaches a\n * terminal status. See `../lib/workflow-live-run.ts`'s `classifyCancelError`\n * for how a `409 run_not_cancelable` (the run beat the cancel to the finish\n * line) is told apart from a real failure.\n */\nexport function cancelWorkflowRun(client: W6WClient, runId: string) {\n return client.workflows.cancel(runId);\n}\n\nexport function useCancelRunMutation() {\n const apiClient = useEmbedClient();\n return useMutation({\n mutationFn: (runId: string) => cancelWorkflowRun(apiClient, runId),\n });\n}\n\n/** The single-shot half of {@link useLiveRunQuery} — a thin, directly\n * testable wrapper around `client.console.workflows.getRun`, so the polling\n * config below can be pinned without needing to render a hook. */\nexport function getRunState(client: W6WClient, runId: string): Promise<RunState> {\n return client.console.workflows.getRun(runId);\n}\n\n/** Query key for one run's live state — distinct from {@link runsKey}, which\n * is the workflow's run LIST. */\nexport const runStateKey = (runId: string) => [\"run\", runId] as const;\n\n/**\n * T2.1.1/A2/A3 — poll one run's live state at a fixed 500ms interval,\n * stopping once it reaches a terminal status ({@link shouldContinuePolling},\n * `../lib/workflow-live-run.ts`). A genuine sibling of `pollRunToTerminal`\n * (`../lib/workflow-poll.ts`) using the SAME underlying SDK call\n * (`client.console.workflows.getRun`), but publishing every intermediate\n * `RunState` to its caller via TanStack Query's own `data` — unlike\n * `pollRunToTerminal`, which discards every non-terminal result and hands\n * back only the last one. `runId === null` disables the query entirely (no\n * run started yet, or the page navigated to a different workflow).\n */\nexport function useLiveRunQuery(runId: string | null) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: runStateKey(runId ?? \"\"),\n queryFn: () => getRunState(apiClient, runId as string),\n enabled: runId != null,\n refetchInterval: (query) => (shouldContinuePolling(query.state.data?.status) ? 500 : false),\n });\n}\n","/**\n * The three callable kinds and the single-resource query key per kind.\n *\n * Moved verbatim out of Studio's `repos/callables.ts` (`CallableKind`, `callableKey` and the one\n * pure table `callableKey` closes over, `RESOURCE_KEY`) so `repos/versions.ts` — which ships in\n * this package for all three kind arms — can invalidate a definition by key without importing a\n * Studio module. `repos/callables.ts` imports these from `@w6w/embed/repos` and does NOT re-export\n * them. No React, no client: loadable by `node --test`.\n */\n\n/** The three kinds the callable repos are total over. */\nexport type CallableKind = \"function\" | \"endpoint\" | \"workflow\";\n\n/** The single-resource key's own prefix — byte-identical to `functionKey`/`endpointKey`/\n * `workflowKey`. */\nconst RESOURCE_KEY: Record<CallableKind, (id: string) => readonly unknown[]> = {\n function: (id) => [\"function\", id],\n endpoint: (id) => [\"endpoint\", id],\n workflow: (id) => [\"workflow\", id],\n};\n\n/** `GET /<coll>/:id`'s own query key — the literal `lib/save-invalidation.ts` invalidates. */\nexport function callableKey(kind: CallableKind, id: string): readonly unknown[] {\n return RESOURCE_KEY[kind](id);\n}\n","/**\n * Shared invalidation for a whole-definition save (T3.1.1). `useSaveFunctionMutation` and\n * `useSaveEndpointMutation` (`src/repos/functions.ts`, `src/repos/endpoints.ts`) both route their\n * `onSuccess` through this one helper rather than inlining the two `invalidateQueries` calls at\n * each site — so a save invalidates the LIST key (both list pages and the editor depend on it)\n * *and* the saved resource's own single-resource key (`functionKey(id)` / `endpointKey(id)`),\n * never just the list.\n *\n * No value imports on purpose: `node --test` throws `ERR_UNKNOWN_FILE_EXTENSION` on anything that\n * reaches the `@w6w/ui` / `Layout.tsx` import graph, and `src/repos/*.ts` reaches it through\n * `../api/apiClient.tsx` (JSX). This module has none of that, so it is loadable by `node --test`\n * directly — see `src/lib/alias-rail.ts`'s docstring for the same reasoning.\n */\n\nexport interface QueryInvalidator {\n invalidateQueries(filters: { queryKey: readonly unknown[] }): unknown;\n}\n\n/**\n * Invalidate the list key, then the saved resource's own key. List first: both keys are\n * invalidated regardless of order, but the order is asserted (see the unit test) so a future edit\n * that drops one call is a diff a reviewer can spot in a single line.\n */\nexport function invalidateSavedCallable(\n qc: QueryInvalidator,\n listKey: readonly unknown[],\n callableKey: readonly unknown[],\n): void {\n qc.invalidateQueries({ queryKey: listKey });\n qc.invalidateQueries({ queryKey: callableKey });\n}\n","/**\n * The `/<collection>/:id/{publish,versions,enable,disable}` repo (T3.1.1) — the Settings rail's\n * read-only version-history browser plus Publish and Enable/Disable. A NEW file, mirroring\n * `src/repos/spec-export.ts` in full: kept out of `repos/functions.ts` / `repos/workflows.ts` /\n * `repos/endpoints.ts` so this node never collides with any of them on the same file, exactly the\n * reason `spec-export.ts`'s own docblock gives for the sibling `/spec` route.\n *\n * **Workflow and Function arms go through the public `@w6w/sdk` methods** (`listVersions`,\n * `getVersion`, `publish`, `revert`, `enable`, `disable`, and `workflows.runVersion`), never\n * `apiClient.console.*`. Each hook dispatches with an explicit per-kind `switch` over LITERAL\n * receivers (`apiClient.workflows.publish(id)`, `apiClient.functions.publish(id)`) and no `default:`\n * — the same reasoning `COLLECTION_PATH` and `useRevertMutation` give against derived dispatch.\n *\n * **Two arms deliberately stay on `W6WClient.request<T>()`:** the ENDPOINT arm of every hook\n * (R-5: endpoint versions have no SDK method — intentionally excluded from the public surface),\n * and the FUNCTION arm of {@link useInvokeVersionMutation} (F-P4: `VersionHistoryModal` renders the\n * raw invocation frame, but `functions.runVersion` unwraps the output like `functions.run`, so the\n * SDK method would drop the frame; follow-up F-6).\n *\n * Every query below is gated on the CALLER'S OWN `enabled` flag, ANDed with `kind !== null` and\n * `id !== \"\"` — never on mount alone (`packages/studio/.ai/PLANNER.md` fact 11): a hook that fires\n * unconditionally against an unresolved `null`/`\"\"` argument 404s once and, because `main.tsx`\n * sets `retry: false` and an unchanged query key never re-fetches, never self-corrects. Opening\n * the Settings panel must issue ZERO `/versions` requests; only opening its History section (or\n * `WorkflowsPage.tsx`'s own editor, a separate call site with its own gate) flips `enabled`.\n *\n * T5.3.1 (project.md Phase 2, Amendments #5/#6): a version can now be reverted to and invoked\n * directly, superseding the earlier \"read-only history, restore out of scope\" pin. `useRevertMutation`\n * overwrites the LIVE resource's `definition` with the archived version's content — never its\n * `key`/address (HITL-4's pinned default) — and does post a `definition` back, by design.\n * `useInvokeVersionMutation` runs that ARCHIVED version's own definition, not the live one, which is\n * exactly why it exists as a separate call from the live invoke/run routes `functions.ts`/\n * `endpoints.ts`/`workflows.ts` already expose.\n */\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { path } from \"@w6w/sdk\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\nimport { callableKey } from \"../lib/callable-kind.ts\";\nimport { historyKeys } from \"../lib/history-query.ts\";\nimport { invalidateSavedCallable } from \"../lib/save-invalidation.ts\";\nimport { workflowKey } from \"./workflows.ts\";\n\n/** The Endpoint / Function single-resource keys the revert arm invalidates: the same\n * `callableKey(\"<kind>\", id)` Studio's `repos/endpoints.ts` / `repos/functions.ts` export as\n * `endpointKey` / `functionKey` (byte-identical `[\"endpoint\", id]` / `[\"function\", id]`). */\nconst endpointKey = (id: string) => callableKey(\"endpoint\", id);\nconst functionKey = (id: string) => callableKey(\"function\", id);\n\n/** The three kinds the wire contract's version-history routes exist for — `connection` has none\n * (see `hasVersionHistory`, `../lib/rail-settings.ts`). */\nexport type VersionsKind = \"function\" | \"workflow\" | \"endpoint\";\n\n/** `<coll>` — the wire contract's own path segment for each kind, every one of the three arms\n * named explicitly (never derived by string pluralization), mirroring `specCollectionForKind`'s\n * own reasoning (`../lib/rail-settings.ts:69`): a typo'd suffix rule would silently send one\n * kind's requests at another kind's collection. */\nconst COLLECTION_PATH: Record<VersionsKind, \"functions\" | \"workflows\" | \"endpoints\"> = {\n function: \"functions\",\n workflow: \"workflows\",\n endpoint: \"endpoints\",\n};\n\n/** One row of `GET /<coll>/:id/versions`'s `versions` array — newest first, per the wire contract. */\nexport interface VersionSummary {\n versionNumber: number;\n createdAt: string;\n createdBy: string | null;\n isCurrentPublished: boolean;\n}\n\n/** `GET /<coll>/:id/versions`'s full 200 body. */\nexport interface VersionsEnvelope {\n enabled: boolean;\n currentPublishedVersion: number | null;\n versions: VersionSummary[];\n}\n\n/** `GET /<coll>/:id/versions/:n`'s `version` field — `definition` is the redacted stored\n * definition for THIS query; {@link useRevertMutation} is what posts a version's definition\n * back onto the live resource, and {@link useInvokeVersionMutation} is what runs it. */\nexport interface VersionDetail {\n versionNumber: number;\n createdAt: string;\n createdBy: string | null;\n isCurrentPublished: boolean;\n definition: Record<string, unknown>;\n}\n\n/** `POST /<coll>/:id/publish`'s 200 body. */\nexport interface PublishResult {\n version: {\n kind: string;\n callableId: string;\n versionNumber: number;\n createdAt: string;\n createdBy: string | null;\n };\n currentPublishedVersion: number;\n}\n\n/** `POST /<coll>/:id/versions/:n/revert`'s 200 body. */\nexport interface RevertResult {\n reverted: {\n kind: string;\n callableId: string;\n versionNumber: number;\n };\n}\n\n/** `<coll>` per kind ends its live invoke/run route in `/invoke` (Function, Endpoint) or `/run`\n * (Workflow, matching that kind's existing unversioned route) — every arm named explicitly\n * (never derived by suffix rule), mirroring `COLLECTION_PATH`'s own reasoning above: a derived\n * rule would silently send one kind's requests at the wrong verb. */\nconst VERSION_INVOKE_VERB: Record<VersionsKind, \"invoke\" | \"run\"> = {\n function: \"invoke\",\n endpoint: \"invoke\",\n workflow: \"run\",\n};\n\nexport function versionsKey(kind: VersionsKind | null, id: string) {\n return [\"versions\", kind, id] as const;\n}\n\nexport function versionKey(kind: VersionsKind | null, id: string, versionNumber: number | null) {\n return [\"version\", kind, id, versionNumber] as const;\n}\n\n/**\n * `GET /<collection>/:id/versions`. Never fires on mount — `enabled` is the caller's own flag\n * (the Settings panel's History-open state, or `WorkflowsPage.tsx`'s own editor-open gate), ANDed\n * with `kind !== null` and `id !== \"\"`, exactly `useSpecQuery`'s shape (`spec-export.ts:45-57`).\n */\nexport function useVersionsQuery(kind: VersionsKind | null, id: string, enabled: boolean) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: versionsKey(kind, id),\n queryFn: async (): Promise<VersionsEnvelope> => {\n switch (kind as VersionsKind) {\n case \"workflow\":\n return await apiClient.workflows.listVersions(id);\n case \"function\":\n return await apiClient.functions.listVersions(id);\n case \"endpoint\": {\n // R-5: no SDK method for endpoint versions.\n const res = await apiClient.request<VersionsEnvelope>({\n method: \"GET\",\n path: path`/${COLLECTION_PATH.endpoint}/${id}/versions`,\n });\n return res.body;\n }\n }\n },\n enabled: enabled && kind !== null && id !== \"\",\n });\n}\n\n/**\n * `GET /<collection>/:id/versions/:n`. Same gating shape as {@link useVersionsQuery}, plus\n * `versionNumber !== null` — the modal opens this only once a row has been clicked.\n */\nexport function useVersionQuery(\n kind: VersionsKind | null,\n id: string,\n versionNumber: number | null,\n enabled: boolean,\n) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: versionKey(kind, id, versionNumber),\n queryFn: async (): Promise<VersionDetail> => {\n // The SDK's `getVersion` already unwraps the `{ version }` envelope.\n const n = versionNumber as number;\n switch (kind as VersionsKind) {\n case \"workflow\":\n return (await apiClient.workflows.getVersion(id, n)) as VersionDetail;\n case \"function\":\n return (await apiClient.functions.getVersion(id, n)) as VersionDetail;\n case \"endpoint\": {\n // R-5: no SDK method for endpoint versions.\n const res = await apiClient.request<{ version: VersionDetail }>({\n method: \"GET\",\n path: path`/${COLLECTION_PATH.endpoint}/${id}/versions/${String(n)}`,\n });\n return res.body.version;\n }\n }\n },\n enabled: enabled && kind !== null && id !== \"\" && versionNumber !== null,\n });\n}\n\n/**\n * `POST /<collection>/:id/publish`. `kind` is fixed per hook instance — the caller already knows\n * which kind it is publishing (the rail's `ctx.kind`, or `WorkflowsPage.tsx`'s hardcoded\n * `\"workflow\"`) — and `mutate(id)` supplies the resource. `onSuccess` invalidates the SAME\n * `[\"versions\", kind, id]` key {@link useVersionsQuery} reads, so the panel's published pointer\n * and version list are fresh without a reload (A3), mirroring `useSaveFunctionMutation`'s\n * `onSuccess` shape (`./functions.ts`).\n */\nexport function usePublishMutation(kind: VersionsKind) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string): Promise<PublishResult> => {\n switch (kind) {\n case \"workflow\":\n return await apiClient.workflows.publish(id);\n case \"function\":\n return await apiClient.functions.publish(id);\n case \"endpoint\": {\n // R-5: no SDK method for endpoint versions.\n const res = await apiClient.request<PublishResult>({\n method: \"POST\",\n path: path`/${COLLECTION_PATH.endpoint}/${id}/publish`,\n });\n return res.body;\n }\n }\n },\n onSuccess: (_result, id) => {\n qc.invalidateQueries({ queryKey: versionsKey(kind, id) });\n },\n });\n}\n\n/**\n * `POST /<collection>/:id/{enable,disable}`. One mutation for both — the wire contract is two\n * routes distinguished only by which one is called, so `vars.enabled` picks the path segment\n * rather than this module carrying two near-identical mutations. `onSuccess` invalidates the same\n * key {@link usePublishMutation} does, for the same reason.\n */\nexport function useSetEnabledMutation(kind: VersionsKind) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; enabled: boolean }): Promise<{ enabled: boolean }> => {\n // The SDK's `enable`/`disable` return void; the hook's result stays `{ enabled }`.\n switch (kind) {\n case \"workflow\":\n if (vars.enabled) await apiClient.workflows.enable(vars.id);\n else await apiClient.workflows.disable(vars.id);\n return { enabled: vars.enabled };\n case \"function\":\n if (vars.enabled) await apiClient.functions.enable(vars.id);\n else await apiClient.functions.disable(vars.id);\n return { enabled: vars.enabled };\n case \"endpoint\": {\n // R-5: no SDK method for endpoint versions.\n const res = await apiClient.request<{ enabled: boolean }>({\n method: \"POST\",\n path: path`/${COLLECTION_PATH.endpoint}/${vars.id}/${vars.enabled ? \"enable\" : \"disable\"}`,\n });\n return res.body;\n }\n }\n },\n onSuccess: (_result, vars) => {\n qc.invalidateQueries({ queryKey: versionsKey(kind, vars.id) });\n },\n });\n}\n\n/**\n * `POST /<collection>/:id/versions/:n/revert`, no body — overwrites the LIVE resource's\n * `definition` with the archived version's content, preserving its `key`/address (HITL-4's\n * pinned default; see the module docblock). This is the first mutation in this family that\n * changes `definition` rather than merely appending to or flipping a flag on the version history\n * (`usePublishMutation`/`useSetEnabledMutation` above do neither), so `onSuccess` invalidates\n * BOTH `versionsKey(kind, id)` (the same key those two invalidate) AND the kind's own\n * definition-edit key — via the shared `invalidateSavedCallable` helper\n * `useSaveFunctionMutation`/`useSaveEndpointMutation` already route through\n * (`../lib/save-invalidation.ts`) — or the page a user reverted FROM keeps showing the\n * pre-revert draft until a manual reload. The definition key is picked by a `switch` naming all\n * three arms explicitly, with no `default:` — the same reasoning `COLLECTION_PATH`'s own docblock\n * gives: a derived rule would silently send one kind at another kind's key.\n */\nexport function useRevertMutation(kind: VersionsKind) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; versionNumber: number }): Promise<RevertResult> => {\n // The SDK's `revert` unwraps `{ reverted }`; the hook re-wraps it to keep its result shape.\n switch (kind) {\n case \"workflow\":\n return { reverted: await apiClient.workflows.revert(vars.id, vars.versionNumber) };\n case \"function\":\n return { reverted: await apiClient.functions.revert(vars.id, vars.versionNumber) };\n case \"endpoint\": {\n // R-5: no SDK method for endpoint versions.\n const res = await apiClient.request<RevertResult>({\n method: \"POST\",\n path: path`/${COLLECTION_PATH.endpoint}/${vars.id}/versions/${String(vars.versionNumber)}/revert`,\n });\n return res.body;\n }\n }\n },\n onSuccess: (_result, vars) => {\n let definitionKey: readonly unknown[];\n switch (kind) {\n case \"function\":\n definitionKey = functionKey(vars.id);\n break;\n case \"workflow\":\n definitionKey = workflowKey(vars.id);\n break;\n case \"endpoint\":\n definitionKey = endpointKey(vars.id);\n break;\n }\n invalidateSavedCallable(qc, versionsKey(kind, vars.id), definitionKey);\n },\n });\n}\n\n/**\n * `POST /<collection>/:id/versions/:n/{invoke,run}`, an EMPTY JSON body — identical to that\n * kind's unversioned invoke/run route's success body, except the requested version's OWN\n * `impl`/definition runs instead of the live one (T5.3.1's wire contract table). Unlike\n * {@link useRevertMutation}, invoking changes no resource state, so it invalidates History ONLY\n * (`onSettled`, `historyKeys.root` — a version run is a run, failed ones included, like every other\n * run path) and no resource query — the caller (`VersionHistoryModal`) renders the mutation's own `data`/`error` state\n * directly, and that state is naturally cleared whenever the modal remounts for a different\n * version (`SettingsTool.tsx`'s `openVersion` gate fully unmounts/remounts it, never leaves it\n * mounted across a prop change).\n */\nexport function useInvokeVersionMutation(kind: VersionsKind) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: {\n id: string;\n versionNumber: number;\n }): Promise<Record<string, unknown>> => {\n // The workflow arm moves to `workflows.runVersion`. The FUNCTION arm (F-P4 / F-6) and the\n // ENDPOINT arm (R-5) stay on `request()`: this hook's caller renders the raw invocation\n // frame, and `functions.runVersion` unwraps the output like `functions.run` — it would drop\n // the frame. The workflow arm returns only `{ runId, status }`, as the raw body did.\n if (kind === \"workflow\") {\n const run = await apiClient.workflows.runVersion(vars.id, vars.versionNumber, {});\n // Project back to the server's own run-start body: the SDK adds `steps`/`terminal`/\n // `httpStatus`, and `VersionHistoryModal` prints this value verbatim as JSON.\n return { runId: run.runId, status: run.status };\n }\n const res = await apiClient.request<Record<string, unknown>>({\n method: \"POST\",\n path: path`/${COLLECTION_PATH[kind]}/${vars.id}/versions/${String(vars.versionNumber)}/${VERSION_INVOKE_VERB[kind]}`,\n body: {},\n });\n return res.body;\n },\n onSettled: () => qc.invalidateQueries({ queryKey: historyKeys.root }),\n });\n}\n","/**\n * The `step tests` domain repo — wraps `@w6w/sdk`'s `client.console.stepTests.*`\n * (T2.6.1). Unlike every other domain migrated so far, its one read\n * (`listStepTests`) exports ONLY a query-key factory + a plain, explicit-client\n * fetcher — no hook. `WorkflowsPage.tsx`'s `stepTestsQueries` (`useQueries`\n * looping over `testGatedSteps`) is the sole read call site and cannot call a\n * hook inside `.map(...)` (Rules of Hooks); no other studio call site needs a\n * hook form. Mirrors `repos/apps.ts`'s \"build what's needed\" discipline,\n * applied to a query for the first time (every prior no-hook export in this\n * project was a mutation).\n *\n * None of these methods take a `project` scoping parameter — step tests are\n * scoped by workflow/tenant server-side, mirroring `console.connections`'s\n * own host shape.\n */\nimport type { W6WClient } from \"@w6w/sdk\";\nimport type { StepTest } from \"@w6w/sdk/console\";\n\nexport type { StepTest } from \"@w6w/sdk/console\";\n\n// ── step tests (list) — key + plain fetcher ONLY, no hook. See header. ────\n\nexport const stepTestsKey = (workflowId: string, stepId: string) =>\n [\"stepTests\", workflowId, stepId] as const;\n\nexport function listStepTests(\n client: W6WClient,\n workflowId: string,\n stepId: string,\n): Promise<StepTest[]> {\n return client.console.stepTests.list(workflowId, stepId);\n}\n\n// ── mutations (facade-only) ─────────────────────────────────────────────\n\nexport function saveStepTest(\n client: W6WClient,\n workflowId: string,\n stepId: string,\n body: { name?: string; input: Record<string, unknown>; with: Record<string, unknown> },\n): Promise<StepTest> {\n return client.console.stepTests.save(workflowId, stepId, body);\n}\n\n/** Same void-discard reasoning as `recordTestRun` above — see that function's doc comment. */\nexport function recordStepTestRun(\n client: W6WClient,\n workflowId: string,\n stepId: string,\n body: {\n stepTestId?: string | null;\n status: string;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n meta?: unknown;\n },\n): Promise<void> {\n return client.console.stepTests.recordRun(workflowId, stepId, body).then(() => undefined);\n}\n","/**\n * The `history` domain repo (T2.3.1) — thin react-query hooks over\n * `lib/history-query.ts`'s pure fetchers, mirroring `repos/dashboard.ts`'s\n * shape exactly: the query keys ship from the pure module, not from here or\n * from a caller; every hook is `useEmbedClient()` + one `useQuery`.\n *\n * `lib/history-query.ts` carries every judgment call (URL⇄filter mapping,\n * request params, cache keys, fetchers, detail mapping) so it stays loadable\n * by `node --test`; this file adds nothing but React wiring on top of it.\n */\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\nimport {\n EMPTY_HISTORY_FILTERS,\n type ExecutionSummary,\n HISTORY_PAGE_SIZE,\n type HistoryFilters,\n type HistoryScope,\n getExecutionDetail,\n getHistory,\n getHistoryStats,\n historyKeys,\n} from \"../lib/history-query.ts\";\n\n/** A placeholder scope to key a disabled query by — never fetched, since\n * `enabled` gates on the real (possibly `null`) scope, matching\n * `repos/workflows.ts`'s `useLiveRunQuery(runId ?? \"\")` idiom. */\nconst NO_SCOPE: HistoryScope = { project: \"\" };\n\n/** The project-wide or per-callable execution list, one page at a time.\n * Disabled while `scope` is `null` (no project/callable selected yet).\n * `placeholderData: keepPreviousData` keeps the previous page's rows on\n * screen while a filter/page change is in flight, instead of flashing to\n * an empty list. */\nexport function useHistoryQuery(\n scope: HistoryScope | null,\n f: HistoryFilters,\n limit: number = HISTORY_PAGE_SIZE,\n) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: historyKeys.list(scope ?? NO_SCOPE, f, limit),\n queryFn: () => getHistory(apiClient, scope as HistoryScope, f, limit),\n enabled: scope !== null,\n placeholderData: keepPreviousData,\n });\n}\n\n/** The aggregate stats card above the list — same scope/filters, no paging.\n * Disabled while `scope` is `null`. */\nexport function useHistoryStatsQuery(scope: HistoryScope | null, f: HistoryFilters) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: historyKeys.stats(scope ?? NO_SCOPE, f),\n queryFn: () => getHistoryStats(apiClient, scope as HistoryScope, f),\n enabled: scope !== null,\n });\n}\n\n/** \"Has this callable ever been executed?\" — the cheap existence probe the\n * header's History button uses to decide its disabled state (one row,\n * every filter at its default). `hasExecuted` reads `false` while loading\n * or on error — never a false \"yes\". */\nexport function useHasExecutedQuery(callableId: string): {\n hasExecuted: boolean;\n isLoading: boolean;\n} {\n const apiClient = useEmbedClient();\n const query = useQuery({\n queryKey: historyKeys.exists(callableId),\n queryFn: () => getHistory(apiClient, { callable: callableId }, EMPTY_HISTORY_FILTERS, 1),\n });\n return {\n hasExecuted: (query.data?.executions?.length ?? 0) > 0,\n isLoading: query.isLoading,\n };\n}\n\n/** One execution's full detail, opened from a list row. Disabled while\n * `summary` is `null` (no row selected). */\nexport function useExecutionDetailQuery(summary: ExecutionSummary | null) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: historyKeys.detail(summary?.kind ?? \"function\", summary?.id ?? \"\"),\n queryFn: () => getExecutionDetail(apiClient, summary as ExecutionSummary),\n enabled: summary !== null,\n });\n}\n","/**\n * The `vars` domain repo — wraps `@w6w/sdk`'s vars operations in react-query\n * (HITL-6). `QueryClientProvider` (`main.tsx`) stays the actual cache; this\n * module only centralizes the query-key factory and the `queryFn`/\n * `mutationFn` pairs so every page reads vars the same way.\n *\n * Vars ARE project-scoped: `server/packages/api/admin/vars.ts`'s\n * `resolveProject()` reads `c.req.query(\"project\")` on all six routes and\n * otherwise resolves the account's default project, and the live `vars`\n * table has a `NOT NULL` `project` column with both unique indexes keyed on\n * it (`vars_private_name_idx (tenant, project, subject, name)`,\n * `vars_shared_name_idx (tenant, project, name)`). {@linkcode varsKey}\n * therefore takes a project parameter, mirroring `documents.ts`'s shape.\n *\n * **Every operation here goes through `W6WClient.request<T>()` rather than\n * `apiClient.vars.*`.** `@w6w/sdk`'s `VarsApi` takes no options argument on\n * any of its six methods and its `VarsHost` deliberately exposes only the\n * transport, so it structurally cannot send `?project=` — that asymmetry is\n * intentional on the SDK's side (see `wrappers/node/src/vars.ts`'s own\n * docblock) and is not this module's to fix. `request()` is the client's\n * public escape hatch for exactly this case (`client.ts`: \"public so a host\n * can also reach an endpoint this version does not model yet\").\n */\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { Var, VarCreateInput, VarPatch } from \"@w6w/sdk\";\nimport { path } from \"@w6w/sdk\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\n\n/** Query key for the vars list, optionally scoped to a project. */\nexport function varsKey(projectId?: string) {\n return [\"vars\", projectId] as const;\n}\n\n/**\n * The current project's variables (or the tenant default when unscoped).\n * `enabled` defaults `true` to preserve that unscoped fallback for a caller\n * that genuinely wants it — a project-scoped page should instead pass\n * `enabled: projectId != null` so it never queries (and never renders)\n * against the wrong project while `ProjectContext`'s own project list is\n * still resolving. Same fix as `documents.ts`'s `useDocumentsQuery`.\n */\nexport function useVarsQuery(projectId?: string, enabled = true) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: varsKey(projectId),\n enabled,\n queryFn: async () => {\n const res = await apiClient.request<{ vars: Var[] }>({\n method: \"GET\",\n path: \"/vars\",\n query: { project: projectId },\n });\n return res.body.vars;\n },\n });\n}\n\nexport function useCreateVarMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { input: VarCreateInput; project?: string }) => {\n const res = await apiClient.request<{ var: Var }>({\n method: \"POST\",\n path: \"/vars\",\n query: { project: vars.project },\n body: {\n name: vars.input.name,\n type: vars.input.type,\n value: vars.input.value,\n description: vars.input.description,\n },\n });\n return res.body.var;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"vars\"] }),\n });\n}\n\nexport function useUpdateVarMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; patch: VarPatch; project?: string }) => {\n const res = await apiClient.request<{ var: Var }>({\n method: \"PATCH\",\n path: path`/vars/${vars.id}`,\n query: { project: vars.project },\n body: {\n type: vars.patch.type,\n value: vars.patch.value,\n description: vars.patch.description,\n },\n });\n return res.body.var;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"vars\"] }),\n });\n}\n\nexport function useDeleteVarMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; project?: string }) => {\n await apiClient.request<{ ok: true }>({\n method: \"DELETE\",\n path: path`/vars/${vars.id}`,\n query: { project: vars.project },\n });\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"vars\"] }),\n });\n}\n","/**\n * The `vault` domain repo — wraps `@w6w/sdk`'s `client.console.vault.*`\n * (T2.7.1). `sealSecret` is a THIRD shape beyond every prior repo's\n * key+plain-fetcher+hook: `useSealSecret()` returns a bound PLAIN FUNCTION,\n * not a `useQuery`/`useMutation` result — it is consumed as a raw prop by\n * `@w6w/ui`'s `ExpressionOptionsProvider` (`ExpressionOptions.sealSecret`),\n * not through the `W6WApi` facade.\n */\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { W6WClient } from \"@w6w/sdk\";\nimport type { SecretValue, VaultSecretSummary } from \"@w6w/sdk/console\";\nimport { useCallback } from \"react\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\n\nexport type { SecretValue, VaultSecretSummary } from \"@w6w/sdk/console\";\n\n// getSecret (console.vault.get) is DELIBERATELY not wrapped here — zero\n// studio callers anywhere (HITL-4). Do not add a hook/plain-function for it.\n\nexport const secretsKey = [\"secrets\"] as const;\n\nexport function listSecrets(client: W6WClient): Promise<VaultSecretSummary[]> {\n return client.console.vault.list();\n}\n\nexport function useSecretsQuery() {\n const apiClient = useEmbedClient();\n return useQuery({ queryKey: secretsKey, queryFn: () => listSecrets(apiClient) });\n}\n\n// ── seal — a raw prop into @w6w/ui's ExpressionOptions.sealSecret, not a\n// facade member and not react-query-backed (nothing to cache: sealing is\n// side-effecting, one value in, one envelope out). ─────────────────────\n\nexport function sealSecret(client: W6WClient, value: string): Promise<SecretValue> {\n return client.console.vault.seal(value);\n}\n\n/**\n * A stable, bound `(value: string) => Promise<SecretValue>` closing over\n * the ambient apiClient — drop straight into `ExpressionOptions.sealSecret`.\n * Rebuilds only when `apiClient` itself changes identity (HITL-2's own\n * token-rotation `useMemo`, one layer up).\n */\nexport function useSealSecret(): (value: string) => Promise<SecretValue> {\n const apiClient = useEmbedClient();\n return useCallback((value: string) => sealSecret(apiClient, value), [apiClient]);\n}\n\n// ── mutations (VaultPage.tsx only) ──────────────────────────────────\n\nexport function useCreateSecretMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (body: { name: string; value: string; description?: string }) =>\n apiClient.console.vault.create(body),\n onSuccess: () => qc.invalidateQueries({ queryKey: secretsKey }),\n });\n}\n\nexport function useUpdateSecretMutation(id: string) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (body: { value?: string; description?: string }) =>\n apiClient.console.vault.update(id, body),\n onSuccess: () => qc.invalidateQueries({ queryKey: secretsKey }),\n });\n}\n\nexport function useDeleteSecretMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) => apiClient.console.vault.delete(id),\n onSuccess: () => qc.invalidateQueries({ queryKey: secretsKey }),\n });\n}\n","/**\n * The `documents` domain repo — wraps `@w6w/sdk`'s `client.documents.*` in\n * react-query (HITL-6). `QueryClientProvider` (`main.tsx`) stays the actual\n * cache; this module only centralizes the query-key factory and the\n * `queryFn`/`mutationFn` pairs so every page reads documents the same way.\n *\n * Documents are project-scoped (`documents.ts`'s `DocumentOptions.project`);\n * every mutation here invalidates the unscoped `[\"documents\"]` key, which\n * prefix-matches every project-scoped query key produced by\n * {@linkcode documentsKey} — the same invalidation shape `DocumentsPage.tsx`\n * used before this migration.\n */\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { path, type Doc, type DocumentCreateInput, type DocumentPatch } from \"@w6w/sdk\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\n\n/**\n * T1.1.1's wire shape for one document, pinned here ahead of that node\n * merging (this node is dispatched speculatively against it — see this\n * task's contract). `shared` and the six `source*`/`syncedAt` fields are all\n * additive over the SDK's own `Doc`, so a locally-created document simply\n * carries them as `null` (or `false` for `shared`) rather than omitting\n * them. Do not rename a field — the server produces these names verbatim.\n */\nexport interface DocWithProvenance extends Doc {\n shared: boolean;\n /** \"owner/name\" of the bound repository this document was pulled from. */\n sourceRepo: string | null;\n /** Repo-relative file path the document was pulled from. */\n sourcePath: string | null;\n /** The pulled file's content hash — NOT a commit SHA. */\n sourceSha: string | null;\n /** The repository's HEAD commit SHA at pull time — NOT a content hash. */\n sourceRefSha: string | null;\n /** Web link to the file; may be absent even when the rest of provenance is set. */\n sourceUrl: string | null;\n /** ISO-8601. Stamped at the document's last actual change, not at every sync. */\n syncedAt: string | null;\n}\n\n/**\n * Which provenance state `DocumentDetailPage.tsx`'s \"Source\" card should\n * render for `doc`. `shared` (not `sourceRepo`) is the gate: a shared\n * document whose provenance columns haven't been stamped yet (any document\n * synced before this feature shipped, until its next real-content sync —\n * tier-1 \"unchanged\" syncs never restamp provenance) is still shared, and\n * must say so, distinct from a genuinely private document.\n *\n * - `\"private\"` — `doc.shared` is false. No card.\n * - `\"pending\"` — shared, but `sourceRepo` hasn't been stamped yet.\n * - `\"synced\"` — shared, with full provenance available.\n */\nexport function documentProvenanceState(doc: DocWithProvenance): \"private\" | \"pending\" | \"synced\" {\n if (!doc.shared) return \"private\";\n return doc.sourceRepo == null ? \"pending\" : \"synced\";\n}\n\n/** Query key for a documents list, optionally scoped to a project. */\nexport function documentsKey(projectId?: string) {\n return [\"documents\", projectId] as const;\n}\n\n/**\n * Query key for a single document, scoped to a project (mirrors\n * {@linkcode useDocumentsQuery}'s fix at :72-79/:80-87 below — `projectId`\n * belongs in the key itself, not just the `queryFn`, so a render where the\n * key was cached under `projectId === undefined` genuinely changes key once\n * `ProjectContext` resolves the real one, instead of reusing the same cache\n * entry — and its `retry: false` wrong-project 404 — forever). Still sits\n * under the bare `[\"documents\"]` prefix every mutation below invalidates, so\n * an edit/delete refreshes this page too — `\"detail\"` keeps it from\n * colliding with {@linkcode documentsKey}'s own `[\"documents\", projectId]`\n * shape.\n */\nexport function documentKey(id: string, projectId?: string) {\n return [\"documents\", \"detail\", id, projectId] as const;\n}\n\n/**\n * A single document by id, cast to {@link DocWithProvenance} (D-5): the SDK's\n * `Doc` doesn't model T1.1.1's provenance fields yet, and this repo is not\n * the place to fork or extend `@w6w/sdk` (D-4). The `project` option is\n * passed exactly as {@linkcode useDocumentsQuery} passes it — a document in a\n * non-default project 404s without it. `enabled` defaults `true` to preserve\n * that unscoped fallback for a caller that genuinely wants it — same as\n * {@linkcode useDocumentsQuery}'s own `enabled` doc comment below — a\n * project-scoped page should instead pass `enabled: projectId != null` so it\n * never queries (and never renders) against the wrong project while\n * `ProjectContext`'s own project list is still resolving.\n */\nexport function useDocumentQuery(id: string, projectId?: string, enabled = true) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: documentKey(id, projectId),\n queryFn: async () =>\n (await apiClient.documents.get(id, { project: projectId })) as DocWithProvenance,\n enabled,\n });\n}\n\n/**\n * The current project's documents (or the tenant default when unscoped).\n * `enabled` defaults `true` to preserve that unscoped fallback for a caller\n * that genuinely wants it — a project-scoped page should instead pass\n * `enabled: projectId != null` so it never queries (and never renders)\n * against the wrong project while `ProjectContext`'s own project list is\n * still resolving.\n */\nexport function useDocumentsQuery(projectId?: string, enabled = true) {\n const apiClient = useEmbedClient();\n return useQuery({\n queryKey: documentsKey(projectId),\n // Cast to DocWithProvenance[], same as useDocumentQuery above: the wire\n // response already carries `shared`/`source*`/`syncedAt` (repo.list()\n // serializes every document the same way repo.get() does), but the SDK's\n // own `Doc` type doesn't declare them yet (D-5). Without this cast,\n // DocumentsPage.tsx's list rows have no type-visible way to branch on\n // `d.shared` the way DocumentDetailPage.tsx already does.\n queryFn: async () =>\n (await apiClient.documents.list({ project: projectId })) as DocWithProvenance[],\n enabled,\n });\n}\n\nexport function useCreateDocumentMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { input: DocumentCreateInput; project?: string }) =>\n apiClient.documents.create(vars.input, { project: vars.project }),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"documents\"] }),\n });\n}\n\nexport function useUpdateDocumentMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; patch: DocumentPatch; project?: string }) =>\n apiClient.documents.update(vars.id, vars.patch, { project: vars.project }),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"documents\"] }),\n });\n}\n\nexport function useDeleteDocumentMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; project?: string }) =>\n apiClient.documents.delete(vars.id, { project: vars.project }),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"documents\"] }),\n });\n}\n\n/**\n * Re-pulls a shared document from its bound repository (\"Sync\" button, T2.1.4)\n * — `POST /documents/:id/sync` (T3.1.1). No SDK method exists for this route\n * yet (D-4/D-5), so this goes through `apiClient.request()` directly, same\n * escape hatch `repo-sync.ts`'s `useSyncRepoNowMutation` uses. The document\n * routes are project-scoped by QUERY param, not a path segment (unlike\n * `repo-binding`'s `/projects/:id/...` shape), so `project` travels in\n * `query`, mirroring {@linkcode useDocumentQuery}'s own `{ project: … }` use.\n */\nexport function useSyncDocumentMutation() {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; project?: string }) =>\n apiClient.request<{ document: DocWithProvenance }>({\n method: \"POST\",\n path: path`/documents/${vars.id}/sync`,\n query: { project: vars.project },\n }),\n onSuccess: () => qc.invalidateQueries({ queryKey: [\"documents\"] }),\n });\n}\n","/**\n * Platform id minter (studio copy). Algorithm is byte-identical to the server's\n * `packages/ids/ids.ts`: alphabet base62, drop byte b >= 248 (largest multiple of 62\n * that fits a byte), keep ALPHABET[b % 62]. No modulo bias.\n */\n\nexport const ID_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n/** Largest multiple of 62 <= 256; bytes at or above it are rejected. */\nconst REJECT_AT = 248;\n\n/** Byte source seam; defaults to `crypto.getRandomValues`. */\nexport type ByteSource = (n: number) => Uint8Array;\n\nconst defaultBytes: ByteSource = (n) => crypto.getRandomValues(new Uint8Array(n));\n\n/** `<prefix>_<len chars of base62>`. */\nexport function newId(prefix: string, len = 16, bytes: ByteSource = defaultBytes): string {\n let out = \"\";\n while (out.length < len) {\n for (const b of bytes(Math.max(len - out.length, 8))) {\n if (b >= REJECT_AT) continue;\n out += ID_ALPHABET[b % 62];\n if (out.length === len) break;\n }\n }\n return `${prefix}_${out}`;\n}\n","/**\n * `CALLABLE_DESCRIPTORS` — the ONE place the three resource kinds' chrome copy,\n * their Settings-modal behaviour and their id minting are named (T4.2.1,\n * `building-blocks.F3.md` §F-3).\n *\n * Before this file, every \"is this a workflow?\" branch lived inline at its call\n * site: `CallableSettingsModal.tsx` carried seven `kind ===` conditionals for\n * three different questions (may the key be edited? does this kind have tags?\n * which placeholder does the name field use?), and each list page repeated the\n * same title/description/empty-state/`data-testid` literals. A fourth kind — or\n * a rename — meant finding them all by hand.\n *\n * **Data only, never JSX.** A `Descriptor` holds strings, booleans and the one\n * id prefix; the kind-specific *editor body* stays React code in its own page\n * (`src/pages/FunctionsPage.tsx`), because this file is `.ts` and is loaded by\n * `node --test` (`.tsx` cannot be — `package.json`'s `test:unit` glob), exactly\n * the constraint `src/lib/save-invalidation.ts` and `src/lib/alias-rail.ts`\n * document for themselves. So `kind ===` moves OUT of the modal and the pages\n * and INTO a total `Record`, never into a render function.\n *\n * The wire plumbing these descriptors describe — collection segment, query keys,\n * the SDK call per kind — lives one layer down, in `src/repos/callables.ts`,\n * which owns its own total `Record` per kind (mirroring `src/repos/versions.ts`'s\n * `COLLECTION_PATH`). `callable-descriptors.test.ts` asserts the two tables name\n * the same three kinds and the same three collections, so they cannot drift.\n */\nimport type { CallableKind } from \"./callable-kind.ts\";\nimport { newId } from \"./new-id.ts\";\n\nexport type { CallableKind };\n\n/** The Settings modal's own per-kind behaviour (T4.2.1's formalization of the\n * seven `kind ===` conditionals `CallableSettingsModal.tsx` used to carry). */\nexport interface CallableSettingsDescriptor {\n /** May the `key` be typed here at all? Function/Workflow yes, Endpoint no\n * (`rfcs/endpoint.md` — an Endpoint's address is its identity). */\n canEditKey: boolean;\n /** Does this kind store tags? Workflow only. */\n hasTags: boolean;\n /** Placeholder for the Name field. */\n namePlaceholder: string;\n /** Placeholder for the Key field (read only when {@link canEditKey}). */\n keyPlaceholder: string;\n /** Appended to the shared \"Lowercase, kebab-case, 3–39 characters.\" hint —\n * `\"\"` for a kind that cannot unset its key, the \"Leave empty to unset.\"\n * sentence for the one that can. */\n keyHintSuffix: string;\n}\n\n/** The editor chrome's own per-kind copy. */\nexport interface CallableEditorDescriptor {\n /** The back control's label, e.g. `\"← Functions\"`. */\n backLabel: string;\n /** Heading fallback while the definition is still hydrating. */\n newTitle: string;\n /** Rendered in place of the form when the hydration GET 404s. */\n notFoundTitle: string;\n /** The Save button's idle label. */\n saveLabel: string;\n}\n\nexport interface CallableDescriptor {\n kind: CallableKind;\n /** The list page's `<h2>`, and the editor document title's second segment. */\n listTitle: string;\n /** The list page's explanatory paragraph. */\n listDescription: string;\n /** The empty-state paragraph (verbatim, including its typographic quotes). */\n emptyCopy: string;\n /** The create button's label, e.g. `\"+ New function\"`. */\n newLabel: string;\n /** The synthetic id's own prefix — `fn_…`/`ep_…`/`wf_…`, never user-edited. */\n newIdPrefix: string;\n /** `data-testid` of the list's Import affordance. */\n importOpenTestId: string;\n /** `data-testid` of the list's \"Show archived\" checkbox. */\n showArchivedTestId: string;\n /** `data-testid` of a row's \"archived\" badge. */\n archivedBadgeTestId: string;\n /** The row button's `title` tooltip (T4.2.3 — replaces the shared template literal\n * `` `Open this ${kind} in the editor.` ``, since Workflow's own row opens the visual editor,\n * not \"the editor\"). */\n openRowTitle: string;\n editor: CallableEditorDescriptor;\n settings: CallableSettingsDescriptor;\n}\n\n/**\n * The three kinds, every arm named explicitly — a total `Record<CallableKind,\n * …>`, so a fourth kind fails to compile here rather than silently rendering\n * another kind's copy (the same reasoning `COLLECTION_PATH` gives in\n * `src/repos/versions.ts`).\n *\n * Only the `function` arm is consumed today: Endpoints and Workflows keep their\n * own pages until T4.2.2/T4.2.3. Their arms are filled from those pages'\n * current literals so the later nodes are a deletion, not a re-derivation.\n */\nexport const CALLABLE_DESCRIPTORS: Record<CallableKind, CallableDescriptor> = {\n function: {\n kind: \"function\",\n listTitle: \"Functions\",\n listDescription:\n 'A Function is a reusable, vendor-abstracted operation — \"Send Email\", \"Create Contact\" — ' +\n \"with a stable interface bound to one swappable app action. Switch provider by \" +\n \"re-binding; callers never change.\",\n emptyCopy:\n \"None yet — click “New function” to create one, or “Import” to bring in a spec document.\",\n newLabel: \"+ New function\",\n newIdPrefix: \"fn\",\n importOpenTestId: \"functions-import-open\",\n showArchivedTestId: \"functions-show-archived\",\n archivedBadgeTestId: \"function-archived\",\n openRowTitle: \"Open this function in the editor.\",\n editor: {\n backLabel: \"← Functions\",\n newTitle: \"New function\",\n notFoundTitle: \"Function not found\",\n saveLabel: \"Save\",\n },\n settings: {\n canEditKey: true,\n hasTags: false,\n namePlaceholder: \"Send Email\",\n keyPlaceholder: \"send-email\",\n keyHintSuffix: \"\",\n },\n },\n endpoint: {\n kind: \"endpoint\",\n listTitle: \"Endpoints\",\n listDescription:\n \"An Endpoint is a named, stable entry point that dispatches to one app action, Function, \" +\n \"or Workflow. Callers bind to the Endpoint; the target behind it can change — promote an \" +\n \"action to a Function or a Workflow, or re-bind it — without breaking a single caller.\",\n emptyCopy: \"None yet — click “New endpoint” to create one.\",\n newLabel: \"+ New endpoint\",\n newIdPrefix: \"ep\",\n importOpenTestId: \"endpoints-import-open\",\n showArchivedTestId: \"endpoints-show-archived\",\n archivedBadgeTestId: \"endpoint-archived\",\n openRowTitle: \"Open this endpoint in the editor.\",\n editor: {\n backLabel: \"← Endpoints\",\n newTitle: \"New endpoint\",\n notFoundTitle: \"Endpoint not found\",\n saveLabel: \"Save\",\n },\n settings: {\n canEditKey: false,\n hasTags: false,\n namePlaceholder: \"My endpoint\",\n keyPlaceholder: \"send-email\",\n keyHintSuffix: \"\",\n },\n },\n workflow: {\n kind: \"workflow\",\n listTitle: \"Workflows\",\n listDescription:\n \"A Workflow is a durable, multi-step run: each step calls an app action, a Function, or \" +\n \"another Workflow, and a run keeps its own state, history and error handling.\",\n emptyCopy: \"None yet — click “New workflow” to create one.\",\n newLabel: \"+ New workflow\",\n newIdPrefix: \"wf\",\n importOpenTestId: \"workflows-import-open\",\n showArchivedTestId: \"workflows-show-archived\",\n archivedBadgeTestId: \"workflow-archived\",\n openRowTitle: \"Open this workflow in the visual editor.\",\n editor: {\n backLabel: \"← Workflows\",\n newTitle: \"New workflow\",\n notFoundTitle: \"Workflow not found\",\n saveLabel: \"Save\",\n },\n settings: {\n canEditKey: true,\n hasTags: true,\n namePlaceholder: \"My workflow\",\n keyPlaceholder: \"unset\",\n keyHintSuffix: \" Leave empty to unset.\",\n },\n },\n};\n\n/** Look a descriptor up by kind. A plain index, never a `switch`: the `Record`\n * above already made every arm total. */\nexport function descriptorFor(kind: CallableKind): CallableDescriptor {\n return CALLABLE_DESCRIPTORS[kind];\n}\n\n/**\n * A fresh synthetic id for a create-modal's first POST — `fn_<uuid>` for a\n * Function (verbatim the `newFunctionId` `FunctionsPage.tsx` used to hold),\n * `ep_`/`wf_` for the other two, whose pages mint the same shapes today. The\n * prefix comes from the descriptor, so \"which kind is this?\" is answered by the\n * same table that answers every other kind question.\n */\nexport function newCallableId(kind: CallableKind): string {\n return newId(descriptorFor(kind).newIdPrefix);\n}\n","/**\n * The `/spec/import` repo (T3.2.1). A NEW file, deliberately kept out of\n * `repos/functions.ts` / `repos/endpoints.ts` (T3.1.1 owns both) so the two\n * nodes never collide on the same file.\n *\n * **Every operation here goes through `W6WClient.request<T>()`, never\n * `apiClient.console.*`.** `T2.1.2`'s `inputs.touch` has no\n * `packages/wrappers` entry — there is no SDK method for this route — so\n * `request()` is the client's public escape hatch, exactly as\n * `repos/vars.ts`'s own docblock explains for the same reason.\n *\n * The posted `document` is forwarded to the server VERBATIM: this module\n * shapes nothing, validates nothing, and — this is the mechanism pin note 1\n * of the contract states from the side a wrong implementation must break —\n * never falls back to the sibling whole-definition save mutation\n * `FunctionIdentityModal.tsx` uses, on any response. `/spec/import` is where\n * the server-side sealed-secret walker lives (T2.1.2 §B runs\n * `assertNoSecretEnvelopes` over the WHOLE supplied body before any write);\n * a \"helpful\" fallback to that other write path would route a pasted\n * document straight around that check.\n *\n * T1.2.1 generalizes this over all three kinds: `onSuccess` invalidates the\n * ONE list key the imported document's own kind reads (`result.kind`, which\n * the server always echoes), never an unconditional `functionsKey`. The\n * decision of which key that is lives in `../lib/spec-import-invalidation.ts`\n * (a pure, `node --test`-loadable module). The three list keys are INJECTED by the host\n * (`listKeys`): Studio's `functionsKey`/`endpointsKey` live in its own `repos/callables`.\n */\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useEmbedClient } from \"../draft/client-context.ts\";\nimport {\n type SpecImportListKeys,\n invalidateImportedCallable,\n} from \"../lib/spec-import-invalidation.ts\";\n\n/**\n * Mirrors `T2.1.1`'s `SpecDocument` (`T2.1.1.contract.md:134-141`): the\n * portable document a `GET /<kind>/:id/spec` export produces and this\n * route consumes. Not a bare `FunctionDef` — connection/var references ride\n * as re-bindable `rebind` entries, never live ids.\n */\nexport interface SpecImportDocument {\n specVersion: string;\n kind: \"function\" | \"endpoint\" | \"workflow\";\n callable: Record<string, unknown>;\n rebind: unknown[];\n}\n\n/** One reference site's outcome (T2.1.2 §E's `SpecImportReferenceReport`). */\nexport interface SpecImportReferenceReport {\n path: string;\n kind: string;\n resolved: boolean;\n /** Present only when `resolved` is `true`. */\n boundTo?: string;\n /** Present only when `resolved` is `false`. */\n reason?: string;\n}\n\n/** The `201` body `POST /spec/import` returns (T2.1.2 §E). */\nexport interface SpecImportResult {\n kind: \"function\" | \"endpoint\" | \"workflow\";\n callable: { id: string; key?: string; name?: string };\n references: SpecImportReferenceReport[];\n}\n\n/**\n * `POST /spec/import`. Success is always `201` — including a partially\n * resolved import (T2.1.2 §E: \"report, never fail silently\"). This mutation\n * resolves on ANY `201`; branching on `references.every(r => r.resolved)` is\n * the caller's job (A5), never this repo's.\n *\n * `project` is the caller's current project — only the workflow arm reads it\n * (`listKeys.workflowsKey(project)`), but every caller passes it uniformly rather than branching on kind itself; see\n * `../lib/spec-import-invalidation.ts` for why the key differs by kind.\n */\nexport function useImportSpecMutation(project: string | undefined, listKeys: SpecImportListKeys) {\n const apiClient = useEmbedClient();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (document: Record<string, unknown>) => {\n const res = await apiClient.request<SpecImportResult>({\n method: \"POST\",\n path: \"/spec/import\",\n body: document,\n });\n return res.body;\n },\n // A row exists in every `201` outcome (T2.1.2 §E creates it even when\n // some references stayed unresolved), so the list is stale either way —\n // but ONLY the list the imported kind actually belongs to (AC-3): the\n // unconditional `functionsKey` this used to invalidate left an imported\n // Endpoint or Workflow's own list stale while pointlessly invalidating\n // Functions.\n onSuccess: (result) => invalidateImportedCallable(qc, result.kind, listKeys, project),\n });\n}\n"],"names":["STEP_FAILED_NO_REASON","RUN_FAILED_NO_REASON","shouldContinuePolling","status","isTerminalRunStatus","isRunInFlight","pollFailed","runButtonLabel","classifyCancelError","code","mapRunStateToLogSteps","steps","id","step","rowError","error","toLogError","raw","o","message","safeJson","runLevelErrors","run","rows","carried","r","reasoned","stepErrors","e","own","sameAsOwn","runError","latestFixtureInput","tests","last","_a","resolveHeaderRunInput","triggerStepId","list","EMPTY_HISTORY_FILTERS","HISTORY_PAGE_SIZE","VALID_STATUSES","VALID_KINDS","DATE_RE","isRealDate","s","y","m","d","dateParts","dt","value","filtersFromSearch","search","params","kind","from","to","q","offsetRaw","offset","n","filtersToSearch","f","historyRequestQuery","scope","opts","query","historyKeys","limit","callableId","getHistory","client","response","getHistoryStats","getExecutionDetail","summary","invocation","TERMINAL","realSleep","ms","resolve","pollRunToTerminal","getRun","sleep","i","workflowsKey","project","listWorkflows","useWorkflowsQuery","enabled","apiClient","useEmbedClient","useQuery","workflowKey","getWorkflow","useWorkflowQuery","useCreateWorkflowMutation","qc","useQueryClient","useMutation","definition","useSaveWorkflowMutation","vars","useArchiveWorkflowMutation","useDeleteWorkflowMutation","runsKey","workflowId","useRunsQuery","runWorkflow","variables","input","res","useRunWorkflowMutation","_res","runWorkflowSync","startWorkflowRun","useStartRunMutation","cancelWorkflowRun","runId","useCancelRunMutation","getRunState","runStateKey","useLiveRunQuery","RESOURCE_KEY","callableKey","invalidateSavedCallable","listKey","endpointKey","functionKey","COLLECTION_PATH","VERSION_INVOKE_VERB","versionsKey","versionKey","versionNumber","useVersionsQuery","path","useVersionQuery","usePublishMutation","_result","useSetEnabledMutation","useRevertMutation","definitionKey","useInvokeVersionMutation","stepTestsKey","stepId","listStepTests","saveStepTest","body","recordStepTestRun","NO_SCOPE","useHistoryQuery","keepPreviousData","useHistoryStatsQuery","useHasExecutedQuery","_b","useExecutionDetailQuery","varsKey","projectId","useVarsQuery","useCreateVarMutation","useUpdateVarMutation","useDeleteVarMutation","secretsKey","listSecrets","useSecretsQuery","sealSecret","useSealSecret","useCallback","useCreateSecretMutation","useUpdateSecretMutation","useDeleteSecretMutation","documentProvenanceState","doc","documentsKey","documentKey","useDocumentQuery","useDocumentsQuery","useCreateDocumentMutation","useUpdateDocumentMutation","useDeleteDocumentMutation","useSyncDocumentMutation","ID_ALPHABET","REJECT_AT","defaultBytes","newId","prefix","len","bytes","out","b","CALLABLE_DESCRIPTORS","descriptorFor","newCallableId","useImportSpecMutation","listKeys","document","result","invalidateImportedCallable"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-workflow-draft-oI02dsP-.js","sources":["../src/draft/autosave.ts","../src/draft/autosave-flush.ts","../src/draft/workflow-save.ts","../src/draft/use-workflow-draft.ts"],"sourcesContent":["/**\n * The debouncing, coalescing, payload-deduplicated saver behind auto-save.\n *\n * ## Why this exists at all\n *\n * Every edit on the canvas replaces the whole definition: a single keystroke in a\n * step's param form travels `ParamsForm onChange → commit → StepEditModal onChange\n * → updateStep → emitChange → onChange(flowToWorkflow(…))` and lands in\n * `WorkflowsPage` as `setJson(JSON.stringify(next, null, 2))`. So a naive\n * `useEffect(() => save(), [json])` is **one `POST /workflows` per character\n * typed**. This module is the thing that stands between those two facts.\n *\n * ## The rule\n *\n * **Trailing debounce, single pending entry, decided at fire time.**\n *\n * - `push` never writes. It replaces the one pending entry and restarts a\n * `delayMs` window. N pushes inside one window therefore produce exactly ONE\n * `save`, carrying the LAST payload.\n * - The decision to save is taken when the timer fires, not when the payload was\n * pushed. That is what makes a transiently-invalid edit harmless: while the JSON\n * modal round-trips text through `JsonEditor`, `canSave` flickers false\n * mid-typing, and a push-time decision would discard the save the author is\n * waiting for. An invalid push still re-arms the window; the next valid one wins.\n * - A payload equal to the last saved one is dropped without a request. The\n * comparison is on the serialised JSON **text**, the same structural-equality\n * dirty signal the house already uses (`packages/ui/src/ActionTestForm.tsx`,\n * *\"Compared by structural JSON so a load→edit→revert cycle clears it\"*), which\n * is why an in-place mutation of some nested object can never fool it: nothing\n * here holds a reference to a live object across ticks.\n *\n * Deliberately absent: retry/backoff. On a failed save the host stops and shows\n * the existing error surface; a background retry against a server that just\n * refused is worse than a visible failure (and, with a stale-write conflict on\n * screen, it would retry into the dialog the author is reading).\n *\n * It lives here rather than in `@w6w/ui` because `@w6w/ui` owns no workflow write\n * at all — the canvas emits `onChange`, the host decides when to persist — and in\n * a plain `.ts` module rather than in `WorkflowsPage.tsx` so it is executable:\n * that file is JSX, which Node 24's type-stripping cannot parse, so nothing in it\n * can be loaded by `node --test`. Same shape as `workflow-save.ts`.\n */\n\n/** The debounce window. One quiet beat after the last keystroke, not per keystroke. */\nexport const AUTOSAVE_DEBOUNCE_MS = 1200;\n\nexport interface AutoSaverOpts {\n /** May return a promise (awaited by `flush`); any other return value is ignored. */\n save: (payload: string, ctx?: unknown) => unknown;\n delayMs?: number;\n /** Injectable for tests; defaults to the globals. */\n setTimer?: (fn: () => void, ms: number) => number;\n clearTimer?: (handle: number) => void;\n}\n\nexport interface AutoSaver {\n /** Consider a payload. (Re)starts the debounce window. */\n push(payload: string, enabled: boolean, canSave: boolean, ctx?: unknown): void;\n /**\n * Fire the pending save now (the Save button). Resolves after the fired save settles\n * (`Promise.resolve()` when nothing fired); rejects if the fired save rejects.\n */\n flush(): Promise<void>;\n /** Drop any pending save without firing (unmount / workflow switch / stale conflict). */\n cancel(): void;\n /** This payload is now on the server. */\n markSaved(payload: string): void;\n /** Does `payload` diverge from the last-saved one? */\n isDirty(payload: string): boolean;\n}\n\n/** The single pending entry — a later `push` overwrites it rather than queueing. */\ninterface Pending {\n payload: string;\n enabled: boolean;\n canSave: boolean;\n /** Caller context captured at push time and handed to `save` at fire time. */\n ctx?: unknown;\n}\n\nexport function createAutoSaver(opts: AutoSaverOpts): AutoSaver {\n const delayMs = opts.delayMs ?? AUTOSAVE_DEBOUNCE_MS;\n const setTimer =\n opts.setTimer ?? ((fn: () => void, ms: number) => setTimeout(fn, ms) as unknown as number);\n const clearTimer = opts.clearTimer ?? ((handle: number) => clearTimeout(handle));\n\n let pending: Pending | null = null;\n let handle: number | null = null;\n let lastSaved: string | null = null;\n\n /** Disarm the window without touching the pending entry. */\n function disarm(): void {\n if (handle !== null) {\n clearTimer(handle);\n handle = null;\n }\n }\n\n /**\n * The window elapsed (or was flushed). Re-read the pending entry and decide\n * NOW — see the fire-time rule above. Either way the entry is consumed, so a\n * dropped save never lingers to fire later on its own.\n */\n function fire(): Promise<void> {\n handle = null;\n const entry = pending;\n pending = null;\n if (!entry) return Promise.resolve();\n if (!entry.enabled) return Promise.resolve();\n if (!entry.canSave) return Promise.resolve();\n if (entry.payload === lastSaved) return Promise.resolve();\n // A synchronous throw from `save` stays synchronous, as before; a returned promise is\n // awaited by `flush` only. The timer path ignores it (the host owns its own error surface).\n const result = opts.save(entry.payload, entry.ctx) as void | Promise<void>;\n return Promise.resolve(result);\n }\n\n return {\n push(payload: string, enabled: boolean, canSave: boolean, ctx?: unknown): void {\n pending = { payload, enabled, canSave, ctx };\n disarm();\n handle = setTimer(() => {\n // The timer path has no caller to receive a rejection; `save` owns its error surface.\n void fire().catch(() => {});\n }, delayMs);\n },\n flush(): Promise<void> {\n disarm();\n return fire();\n },\n cancel(): void {\n disarm();\n pending = null;\n },\n markSaved(payload: string): void {\n lastSaved = payload;\n },\n isDirty(payload: string): boolean {\n return payload !== lastSaved;\n },\n };\n}\n","/**\n * Flush a pending debounced write before the page actually goes away.\n *\n * ## Why this exists at all\n *\n * `autosave.ts`'s `createAutoSaver` coalesces edits behind an `AUTOSAVE_DEBOUNCE_MS`\n * (1200ms) window timer (`autosave.ts:45`) — a `push()` inside that window never writes,\n * it only re-arms the timer. Nothing in `autosave.ts` itself ever\n * flushes on navigation, so a canvas pan (or any other edit) that lands less than the\n * window's width before a refresh is lost: the tab is gone before the timer fires (I-3).\n * A zoom gesture \"works\" only by accident — it takes longer than the window to finish.\n *\n * This module does not touch the debounce, the `AutoSaver` interface, or its save\n * decision (`fire()`'s `enabled`/`canSave`/dedup checks in `autosave.ts:98-107` already\n * apply to a flush exactly as they do to a natural fire) — it only decides WHEN to call\n * the `flush()` the caller already owns.\n *\n * ## The rule\n *\n * Two signals, both wired here, neither replacing the other:\n *\n * - `visibilitychange` with `document.visibilityState === \"hidden\"` is the PRIMARY path.\n * It fires on tab close, tab switch, and navigation alike, and — unlike `beforeunload`\n * — a listener registered on it is not itself a signal to the browser that the page is\n * unusual, so it carries no risk of suppressing the back/forward cache.\n * - `beforeunload` is the BELT, wired unconditionally (it does not check\n * `visibilityState`): some navigations do not reliably fire `visibilitychange` first in\n * every browser, so this is a second chance at the same flush.\n *\n * **Known, accepted limit — do not claim this race is provably closed.** A `fetch`\n * kicked off from either handler is not guaranteed to complete before the browser\n * actually tears the page down (`beforeunload` in particular has no such guarantee in\n * every browser). `visibilitychange`+`hidden` fires earlier in the common\n * switch-tab/navigate-away cases, which is why it is the primary path and `beforeunload`\n * only the belt — this module narrows the loss window, it does not eliminate it.\n *\n * ## Why the parameters are typed the way they are\n *\n * `WindowLike`/`DocumentLike` below name only the handful of members this module\n * actually calls. That is what lets `node --test` — which cannot load `.tsx`, the same\n * reason `autosave.ts` itself lives in a plain `.ts` module (`autosave.ts:37-41`) — pass\n * a plain fake object in tests, while `tsc -b --noEmit` still proves the real\n * `window`/`document` satisfy the same shape at the call site in `WorkflowsPage.tsx`.\n */\n\n/** The subset of `Window` this module needs: register/unregister `beforeunload`. */\nexport interface WindowLike {\n addEventListener(type: \"beforeunload\", listener: () => void): void;\n removeEventListener(type: \"beforeunload\", listener: () => void): void;\n}\n\n/**\n * The subset of `Document` this module needs: register/unregister `visibilitychange`,\n * and read the visibility state the moment the event fires.\n */\nexport interface DocumentLike {\n readonly visibilityState: string;\n addEventListener(type: \"visibilitychange\", listener: () => void): void;\n removeEventListener(type: \"visibilitychange\", listener: () => void): void;\n}\n\n/**\n * Register both listeners; call `flush` when the page is going away. Returns a cleanup\n * that removes both — callers wire this straight into a `useEffect`'s return.\n */\nexport function registerFlushOnHide(\n windowTarget: WindowLike,\n documentTarget: DocumentLike,\n flush: () => void,\n): () => void {\n const onBeforeUnload = () => {\n flush();\n };\n const onVisibilityChange = () => {\n if (documentTarget.visibilityState === \"hidden\") {\n flush();\n }\n };\n\n windowTarget.addEventListener(\"beforeunload\", onBeforeUnload);\n documentTarget.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n return () => {\n windowTarget.removeEventListener(\"beforeunload\", onBeforeUnload);\n documentTarget.removeEventListener(\"visibilitychange\", onVisibilityChange);\n };\n}\n","/**\n * The two decisions behind the workflow save precondition (optimistic\n * concurrency; server half in `packages/server` `packages/api/workflows.ts`).\n *\n * `POST /workflows` accepts an optional `x-w6w-if-unmodified-since` header\n * carrying the exact ISO-8601 `updatedAt` the client last saw. If the stored row\n * has moved on, the save is refused with **409 `workflow_stale`** instead of\n * silently overwriting whoever saved in between.\n *\n * Both functions here are pure functions of their inputs, and they live in a\n * `.ts` module rather than in `WorkflowsPage.tsx` so they are executable:\n * `WorkflowsPage.tsx` is JSX, which Node 24's type-stripping cannot parse, so\n * nothing in it can be loaded by `node --test`. Same shape as\n * `src/lib/sdk-snippets.ts` + its `.test.ts`.\n */\n/**\n * Is this the one save failure an author can fix by reloading?\n *\n * Keyed on the error **code**, never on the HTTP status. The server answers 409\n * for two completely different situations and only one of them is recoverable:\n *\n * - `workflow_stale` — your copy is out of date. Reload and re-save. ← this one\n * - `workflow_conflict` — another `(tenant, subject)` owns that workflow id.\n * Reloading changes nothing; offering a reload here would be a dead end.\n *\n * Anything that carries no `code` (a thrown `TypeError`, a string, `null`) is\n * not a server verdict at all, so it is not a stale write either.\n *\n * Duck-typed on the `code` property rather than `instanceof ApiError`: this module is\n * headless and must not value-import the SDK, and two SDK copies would defeat `instanceof`.\n */\nexport function isStaleWrite(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { code?: unknown }).code === \"workflow_stale\";\n}\n\n/**\n * The precondition token to send with the NEXT save, given the token we sent\n * with this one and the response that came back.\n *\n * The server rides `updatedAt` on both the `GET /workflows/:id` body and the\n * `POST /workflows` 201, precisely so a client can advance without a re-GET.\n *\n * When the response carries no usable `updatedAt`, the answer is **`null`** (no\n * precondition on the next save), never `prev`: `prev` is by then a value the\n * server may already have moved past, so resending it would 409 forever with no\n * way out. Dropping the guard for one save is the weaker failure — it is exactly\n * the pre-existing last-write-wins behaviour, which the header is optional for.\n */\nexport function nextSaveToken(prev: string | null, res: { updatedAt?: unknown }): string | null {\n if (typeof res.updatedAt === \"string\") return res.updatedAt;\n // `prev` is deliberately dropped here — see above.\n void prev;\n return null;\n}\n","/**\n * `useWorkflowDraft` — the headless draft lifecycle behind the workflow editor: load, a JSON text\n * buffer, syntax validity plus injected guards, SERIALIZED conditional saves behind a debounced\n * auto-save, flush on hide / unmount / workflow switch, an awaited flush before an action, and a\n * 409 that is surfaced and never retried. Lifted from Studio's `WorkflowsPage` (load effect,\n * `saveCallbacks`, `reloadFromServer`, `onSave`, `autoSaveNow` + push effect), minus the UI.\n *\n * ## The trust boundary (why the state is per ROW, not per hook)\n *\n * A pending save must write into the account / project / workflow that PRODUCED the edit, even if\n * the host has since swapped its client (token rotation, account switch) or moved to another\n * workflow. Two mechanisms hold that:\n *\n * - The AUTHORING context belongs to the buffer. Each row holds `{client, project}` — set when the\n * buffer is loaded — and EVERY save path (debounce, re-push after a guard flip, `flush`,\n * `flushThen`, unmount, registry, hide) sends with it, never with the current render's values.\n * When the render's client or project stops matching it, the row hands over: one save of a dirty\n * buffer with the authoring context, then (saved or not) the buffer is discarded and reloaded\n * under the new context. `canSave` is false and pushes are ignored meanwhile, so no save is ever\n * issued with the new context for text authored under the old one.\n * - One `Row` (its own saver, buffer, token, in-flight promise) per `workflowId`. Switching rows\n * flushes the old row's pending entry into the old row, and a late completion of that save\n * updates the OLD row object — it can never move the new row's token or clean marker.\n *\n * `project` is required to save: with none, the save is refused (`saveBlockedReason`), the buffer\n * is kept and no request is sent. It is always passed explicitly to the SDK — never the client's\n * `config.project` fallback, and never the account default (a projectless update would silently\n * MOVE the row).\n *\n * Deliberately absent: retry, a save queue. A save in flight disables the next one; the edit made\n * meanwhile is pushed again when the first settles, carrying the token that save returned. The one\n * exception is the context handover above: it sends a dirty buffer once more into the OLD account\n * (even if the save that just failed carried the same bytes), and never into the new one.\n */\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport type { W6WClient, WorkflowDefinition } from \"@w6w/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { registerFlushOnHide } from \"./autosave-flush.ts\";\nimport { type AutoSaver, createAutoSaver } from \"./autosave.ts\";\nimport { useEmbedClient } from \"./client-context.ts\";\nimport { useDraftRegistry } from \"./draft-registry.ts\";\nimport { isStaleWrite, nextSaveToken } from \"./workflow-save.ts\";\n\nexport type FlushOutcome =\n | { status: \"clean\" }\n | { status: \"saved\" }\n | { status: \"blocked\"; reason: string }\n | { status: \"failed\"; error: unknown };\n\nexport interface UseWorkflowDraftOptions {\n /** `null` ⇒ idle (the hook stays mounted above an early return). */\n workflowId: string | null;\n /** REQUIRED to save; empty/absent ⇒ the save is refused and no request is sent. */\n project: string | null | undefined;\n /** Default true; a debounced save follows the last edit. */\n autoSave?: boolean;\n /** Injected busy probe (Studio: expression modal open). Gates AUTOMATIC saves only. */\n canAutoSave?: () => boolean;\n /** Injected key-edit guard: a reason blocks saving. */\n saveGuard?: (parsed: unknown) => string | null;\n /** Injected (B/D-3): reported as `publishBlockedReason`, not enforced by save. */\n publishBlockedReason?: (parsed: unknown) => string | null;\n /**\n * Called exactly once when a handover (client/project change) or an identity switch\n * (`flushAll`) drops a dirty buffer it could not save. `blocked`: the save was refused before a\n * request (invalid text, no project, a guard, busy); `failed`: the request was sent or the row\n * was stale and the save did not succeed. No UI here: the host decides what to show.\n */\n onDiscard?: (info: { workflowId: string; text: string; reason: \"blocked\" | \"failed\" }) => void;\n}\n\nexport interface WorkflowDraft {\n json: string;\n setJson(next: string): void;\n parsed: unknown | null;\n syntaxError: string | null;\n loading: boolean;\n loadError: unknown | null;\n dirty: boolean;\n saving: boolean;\n canSave: boolean;\n saveBlockedReason: string | null;\n stale: boolean;\n error: unknown | null;\n publishBlockedReason: string | null;\n /** Push + fire now (Ctrl/Cmd+S, the Save button). */\n saveNow(): void;\n flush(): Promise<FlushOutcome>;\n /** Flush, then run `action` only when the buffer is clean or just saved. */\n flushThen<T>(\n action: () => Promise<T>,\n ): Promise<{ ok: true; value: T } | { ok: false; outcome: FlushOutcome }>;\n /** `setJson(next)` + an immediate save (the saveMeta / onPolicyChange paths). */\n commit(next: string): Promise<FlushOutcome>;\n /** 409 recovery: GET, replace the buffer and token, clear `stale`. */\n reload(): Promise<void>;\n}\n\n/** What a pending entry carries from push time to fire time. */\ninterface SaveCtx {\n client: W6WClient;\n project: string | null | undefined;\n id: string;\n token: string | null;\n /** Explicit (button / flushThen) saves ignore the busy probe; automatic ones honour it. */\n explicit: boolean;\n}\n\n/** Everything that belongs to ONE workflow id's draft. */\ninterface Row {\n id: string;\n /** The buffer text; the row keeps its final text after the hook has moved on. */\n text: string;\n token: string | null;\n loaded: boolean;\n stale: boolean;\n /** Bytes the server refused — auto-save must not re-push them. */\n refused: string | null;\n lastError: unknown;\n inflight: Promise<void> | null;\n saver: AutoSaver;\n /** Who this buffer was loaded as (the context every save of it is sent with). */\n auth: AuthCtx | null;\n /** A context change is being settled (save with `auth`, then reload); pushes are ignored. */\n handover: boolean;\n /**\n * Bumped whenever the buffer is replaced by a fresh read (the handover, `reload()`). A save\n * remembers the generation it started in; a completion from an older one describes a buffer that\n * is gone and must not touch the new one's token, clean marker or error.\n */\n gen: number;\n /** The `gen` whose dirty buffer was already reported through `onDiscard` (at most once each). */\n discardReported: number;\n}\n\ninterface AuthCtx {\n client: W6WClient;\n project: string | null | undefined;\n}\n\n/**\n * Does the render context differ from the one the buffer was authored in? A buffer loaded before\n * any project existed (so it could never be saved) is not in conflict with the same client gaining\n * one: nothing was authored under a project, and no account changes.\n */\nfunction ctxDiffers(a: AuthCtx | null, client: W6WClient, project: string | null | undefined) {\n if (!a) return false;\n if (a.client !== client) return true;\n return (a.project || null) !== (project || null) && !!a.project;\n}\n\ninterface Evaluation {\n parsed: unknown | null;\n syntaxError: string | null;\n /** Why this text cannot be saved right now, or `null`. */\n blocked: string | null;\n}\n\nconst NO_PROJECT = \"No project selected: a workflow can only be saved into a project.\";\n\nfunction evaluate(\n text: string,\n id: string,\n project: string | null | undefined,\n saveGuard: UseWorkflowDraftOptions[\"saveGuard\"],\n): Evaluation {\n let parsed: unknown | null = null;\n let syntaxError: string | null = null;\n try {\n parsed = JSON.parse(text);\n } catch (e) {\n syntaxError = e instanceof Error ? e.message : String(e);\n }\n let blocked: string | null = null;\n if (!project) blocked = NO_PROJECT;\n else if (syntaxError !== null) blocked = `Invalid JSON: ${syntaxError}`;\n else if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n blocked = \"A workflow definition must be a JSON object.\";\n } else if (\n typeof (parsed as { id?: unknown }).id === \"string\" &&\n (parsed as { id: string }).id !== id\n ) {\n // `update(id, def)` writes `def` under `id`; a different id in the text would be saved under\n // the original id yet read as saved. Refuse rather than write somewhere the author did not mean.\n blocked = \"The workflow id cannot be changed here.\";\n } else if (saveGuard) blocked = saveGuard(parsed) ?? null;\n return { parsed, syntaxError, blocked };\n}\n\nexport function useWorkflowDraft(o: UseWorkflowDraftOptions): WorkflowDraft {\n const client = useEmbedClient();\n const registry = useDraftRegistry();\n const qc = useQueryClient();\n const { workflowId } = o;\n const { project, saveGuard } = o;\n const autoSave = o.autoSave ?? true;\n\n const [json, setJsonState] = useState(\"\");\n const [loadedId, setLoadedId] = useState<string | null>(null);\n const [loadError, setLoadError] = useState<unknown | null>(null);\n const [saving, setSaving] = useState(false);\n const [stale, setStale] = useState(false);\n const [error, setError] = useState<unknown | null>(null);\n const [handingOver, setHandingOver] = useState(false);\n\n // Latest-value refs: stable callbacks read these, never a captured render.\n const optsRef = useRef(o);\n optsRef.current = o;\n const clientRef = useRef(client);\n clientRef.current = client;\n const qcRef = useRef(qc);\n qcRef.current = qc;\n const rowRef = useRef<Row | null>(null);\n // The key-edit guard of the context that authored the buffer: a handover save is judged by the\n // guard in force when the text was written, not by one that arrived with the new context.\n const guardRef = useRef(saveGuard);\n\n // One row per workflow id, created during render so `dirty` is answerable on the first paint.\n // Nothing in a fresh row starts a timer or a request, so a discarded render leaks nothing.\n if (workflowId === null) rowRef.current = null;\n else if (rowRef.current?.id !== workflowId) {\n const row: Row = {\n id: workflowId,\n text: \"\",\n token: null, // a token from another row would 409 against this one\n loaded: false,\n stale: false,\n refused: null,\n lastError: null,\n inflight: null,\n saver: undefined as unknown as AutoSaver,\n auth: null,\n handover: false,\n gen: 0,\n discardReported: -1,\n };\n row.saver = createAutoSaver({\n save: (payload, ctx) => saveRow(row, payload, ctx as SaveCtx),\n });\n rowRef.current = row;\n }\n\n /** The coalescer's sink: ONE request, written into the ctx captured at push time. */\n const saveRowRef = useRef(saveRow);\n saveRowRef.current = saveRow;\n function saveRow(row: Row, payload: string, ctx: SaveCtx): Promise<void> {\n // Serialization: no queue. The edit made meanwhile is re-pushed when this one settles.\n if (row.inflight) return Promise.resolve();\n const cur = optsRef.current;\n if (!ctx.explicit && cur.canAutoSave && !cur.canAutoSave()) return Promise.resolve();\n const isCurrent = () => rowRef.current === row;\n const gen = row.gen;\n const run = async (): Promise<void> => {\n if (!ctx.project) throw new Error(NO_PROJECT);\n const definition = JSON.parse(payload) as WorkflowDefinition;\n if (isCurrent()) {\n setSaving(true);\n setError(null);\n }\n try {\n const res = await ctx.client.workflows.update(ctx.id, definition, {\n project: ctx.project,\n ifUnmodifiedSince: ctx.token,\n });\n void qcRef.current.invalidateQueries({ queryKey: [\"workflows\"] });\n // The buffer was replaced while this save was in flight: its token and clean marker belong\n // to the old buffer.\n if (gen !== row.gen) return;\n row.token = nextSaveToken(ctx.token, res as { updatedAt?: unknown });\n row.saver.markSaved(payload);\n row.refused = null;\n } catch (e) {\n if (gen === row.gen) {\n row.refused = payload;\n row.lastError = e;\n row.saver.cancel();\n if (isStaleWrite(e)) row.stale = true;\n if (isCurrent()) {\n if (row.stale) setStale(true);\n setError(e);\n }\n }\n throw e;\n }\n };\n const p = run();\n row.inflight = p;\n const clear = () => {\n if (row.inflight === p) row.inflight = null;\n if (isCurrent()) setSaving(false);\n };\n p.then(clear, clear);\n return p;\n }\n\n /** A dirty buffer that could not be saved is about to be dropped: tell the host, once. */\n const reportDiscard = useCallback((row: Row, outcome: FlushOutcome) => {\n if (outcome.status !== \"blocked\" && outcome.status !== \"failed\") return;\n if (row.discardReported === row.gen) return;\n row.discardReported = row.gen;\n try {\n optsRef.current.onDiscard?.({\n workflowId: row.id,\n text: row.text,\n reason: outcome.status,\n });\n } catch {\n // A host callback must not break the switch.\n }\n }, []);\n\n /**\n * Settle a row's buffer. `explicit` (Save button, `flushThen`) saves any dirty saveable text;\n * the automatic callers (hide, unmount, the registry) additionally honour `autoSave` and the busy\n * probe, and re-use the ctx of the entry they are flushing. Never rejects.\n */\n const flushRow = useCallback(\n async (\n row: Row,\n explicit: boolean,\n /**\n * Identity switch / logout / client-project handover: the buffer is about to be dropped, so\n * save a dirty saveable one even when `autoSave` is off AND the busy probe reports busy.\n * Validity (syntax, project, guard) still blocks.\n */\n ignoreGates = false,\n ): Promise<FlushOutcome> => {\n try {\n let drained: { error: unknown } | null = null;\n while (row.inflight) {\n try {\n await row.inflight;\n } catch (e) {\n drained = { error: e };\n }\n }\n if (!row.loaded) return { status: \"blocked\", reason: \"The workflow has not loaded.\" };\n const text = row.text;\n if (!row.saver.isDirty(text)) return { status: \"clean\" };\n // The save we just waited for refused these very bytes: report it, do not hammer the server.\n if (drained && row.refused === text) return { status: \"failed\", error: drained.error };\n if (row.stale) return { status: \"failed\", error: row.lastError };\n const cur = optsRef.current;\n const auth = row.auth as AuthCtx;\n if (!auth.project && auth.client === clientRef.current) auth.project = cur.project;\n const { blocked } = evaluate(text, row.id, auth.project, guardRef.current);\n if (blocked !== null) return { status: \"blocked\", reason: blocked };\n if (!explicit) {\n if (!ignoreGates && !(cur.autoSave ?? true)) {\n return { status: \"blocked\", reason: \"Auto-save is off.\" };\n }\n if (!ignoreGates && cur.canAutoSave && !cur.canAutoSave()) {\n return { status: \"blocked\", reason: \"The editor is busy.\" };\n }\n }\n const ctx: SaveCtx = {\n client: auth.client,\n project: auth.project,\n id: row.id,\n token: row.token,\n // `saveRow` re-checks the busy probe for automatic saves; a gate-ignoring flush is explicit.\n explicit: explicit || ignoreGates,\n };\n row.saver.push(text, true, true, ctx);\n await row.saver.flush();\n return { status: \"saved\" };\n } catch (error) {\n return { status: \"failed\", error };\n }\n },\n [],\n );\n\n // ── load ──────────────────────────────────────────────────────────────────\n // Keyed on `workflowId` ONLY: a rebuilt client (token rotation) neither reloads nor cancels the\n // pending entry.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `workflowId` is the trigger; the row is read from a ref\n useEffect(() => {\n const row = rowRef.current;\n if (!row) return;\n let canceled = false;\n setJsonState(\"\");\n setLoadedId(null);\n setLoadError(null);\n setError(null);\n setStale(false);\n setSaving(false);\n const authored: AuthCtx = { client: clientRef.current, project: optsRef.current.project };\n authored.client.workflows.get(row.id).then(\n (r) => {\n if (canceled) return;\n row.auth = authored;\n const text = JSON.stringify(r.workflow, null, 2);\n row.text = text;\n row.token = nextSaveToken(null, r);\n // What we just loaded IS the server's copy: the editor opens clean.\n row.saver.markSaved(text);\n row.loaded = true;\n setJsonState(text);\n setLoadedId(row.id);\n },\n (e) => {\n if (!canceled) setLoadError(e);\n },\n );\n return () => {\n canceled = true;\n // The pending entry belongs to the row that produced it: flush it there, never drop it.\n void flushRow(row, false);\n // Disarm the window; a flush still waiting on an in-flight save pushes afresh.\n row.saver.cancel();\n };\n }, [workflowId, flushRow]);\n\n // ── derived ───────────────────────────────────────────────────────────────\n const row = rowRef.current;\n // `row.loaded` too: A→B→A leaves `loadedId === A` for one render while the NEW A row loads.\n const loaded = row?.loaded === true && loadedId === workflowId;\n const text = loaded ? json : \"\";\n const ev = useMemo(\n () => (loaded && workflowId !== null ? evaluate(text, workflowId, project, saveGuard) : null),\n [loaded, text, workflowId, project, saveGuard],\n );\n const saveBlockedReason = ev ? ev.blocked : !project && workflowId !== null ? NO_PROJECT : null;\n if (!(loaded && ctxDiffers(row?.auth ?? null, client, project))) guardRef.current = saveGuard;\n const switching = handingOver || (loaded && ctxDiffers(row?.auth ?? null, client, project));\n const canSave = loaded && saveBlockedReason === null && !saving && !switching;\n const dirty = loaded && row?.saver.isDirty(text) === true;\n const publishBlocked =\n ev && ev.parsed !== null && o.publishBlockedReason ? o.publishBlockedReason(ev.parsed) : null;\n\n // ── auto-save push ──────────────────────────────────────────────────────────\n // Never writes: hands the payload to the coalescer. `project`/`client` are read here (push time)\n // and ride the entry as its ctx.\n useEffect(() => {\n const r = rowRef.current;\n if (!r || !loaded || r.id !== workflowId) return;\n // While a 409 is on screen every save is refused the same way; pushing would retry into it.\n if (stale) return;\n if (json === r.refused) return;\n // A context change is being handed over: nothing is pushed under the new context.\n if (r.handover || !r.auth || ctxDiffers(r.auth, clientRef.current, optsRef.current.project)) {\n return;\n }\n if (!r.auth.project) r.auth.project = optsRef.current.project;\n const ctx: SaveCtx = {\n client: r.auth.client,\n project: r.auth.project,\n id: r.id,\n token: r.token,\n explicit: false,\n };\n r.saver.push(json, autoSave, canSave, ctx);\n }, [loaded, workflowId, json, stale, autoSave, canSave]);\n\n // ── context handover ──────────────────────────────────────────────────────\n // The host swapped client or project under a loaded buffer: settle the buffer into the context\n // that authored it (once), then discard it and read the row afresh under the new one.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the row is read from a ref\n useEffect(() => {\n const r = rowRef.current;\n if (!r || !loaded || r.id !== workflowId || r.handover) return;\n if (!ctxDiffers(r.auth, client, project)) {\n if (r.auth && !r.auth.project) r.auth.project = project;\n return;\n }\n r.handover = true;\n setHandingOver(true);\n void (async () => {\n try {\n // Same path as the registry's flushAll (so also regardless of `autoSave`); saved or\n // refused, the buffer is then dropped.\n reportDiscard(r, await flushRow(r, false, true));\n if (rowRef.current !== r) return;\n r.saver.cancel();\n const authored: AuthCtx = { client: clientRef.current, project: optsRef.current.project };\n const res = await authored.client.workflows.get(r.id);\n if (rowRef.current !== r) return;\n const next = JSON.stringify(res.workflow, null, 2);\n r.gen++;\n r.auth = authored;\n r.text = next;\n r.token = nextSaveToken(null, res);\n r.saver.markSaved(next);\n r.refused = null;\n r.stale = false;\n r.lastError = null;\n setJsonState(next);\n setStale(false);\n setError(null);\n } catch (e) {\n if (rowRef.current === r) {\n r.loaded = false;\n setLoadedId(null);\n setLoadError(e);\n }\n } finally {\n r.handover = false;\n if (rowRef.current === r) setHandingOver(false);\n }\n })();\n }, [loaded, workflowId, client, project, handingOver, flushRow, reportDiscard]);\n\n // ── flush on hide / registry ──────────────────────────────────────────────\n // biome-ignore lint/correctness/useExhaustiveDependencies: `workflowId` is the trigger; the row is read from a ref\n useEffect(() => {\n const r = rowRef.current;\n if (!r) return;\n const w = (globalThis as { window?: Window }).window;\n const d = (globalThis as { document?: Document }).document;\n const offHide =\n w && d\n ? registerFlushOnHide(w, d, () => {\n void flushRow(r, false);\n })\n : undefined;\n // `flushAll` exists for identity switches and logout, so it saves regardless of `autoSave`\n // and of the busy probe, and reports what it had to drop. Hide / unmount keep their `autoSave` gate.\n const unregister = registry?.register(async () => {\n const outcome = await flushRow(r, false, true);\n reportDiscard(r, outcome);\n return outcome;\n });\n return () => {\n offHide?.();\n unregister?.();\n };\n }, [workflowId, registry, flushRow, reportDiscard]);\n\n // ── stable actions ────────────────────────────────────────────────────────\n const setJson = useCallback((next: string) => {\n const r = rowRef.current;\n if (!r || !r.loaded) return;\n r.text = next;\n setJsonState(next);\n }, []);\n const flush = useCallback((): Promise<FlushOutcome> => {\n const r = rowRef.current;\n return r ? flushRow(r, true) : Promise.resolve({ status: \"clean\" });\n }, [flushRow]);\n const saveNow = useCallback(() => {\n void flush();\n }, [flush]);\n const flushThen = useCallback(\n async <T>(action: () => Promise<T>) => {\n const outcome = await flush();\n if (outcome.status === \"clean\" || outcome.status === \"saved\") {\n return { ok: true as const, value: await action() };\n }\n return { ok: false as const, outcome };\n },\n [flush],\n );\n const commit = useCallback(\n (next: string): Promise<FlushOutcome> => {\n setJson(next);\n return flush();\n },\n [setJson, flush],\n );\n const reload = useCallback(async () => {\n const r = rowRef.current;\n if (!r) return;\n try {\n const authored: AuthCtx = { client: clientRef.current, project: optsRef.current.project };\n const res = await authored.client.workflows.get(r.id);\n if (rowRef.current !== r) return;\n r.auth = authored;\n const next = JSON.stringify(res.workflow, null, 2);\n // The canvas we are replacing is gone, and so is anything it had pending.\n r.saver.cancel();\n r.gen++;\n r.text = next;\n r.token = nextSaveToken(null, res);\n r.saver.markSaved(next);\n r.refused = null;\n r.stale = false;\n r.lastError = null;\n r.loaded = true;\n setJsonState(next);\n setLoadedId(r.id);\n setStale(false);\n setError(null);\n } catch (e) {\n if (rowRef.current === r) setError(e);\n }\n }, []);\n\n return {\n json: text,\n setJson,\n parsed: ev ? ev.parsed : null,\n syntaxError: ev ? ev.syntaxError : null,\n loading: workflowId !== null && !loaded && loadError === null,\n loadError,\n dirty,\n saving,\n canSave,\n saveBlockedReason,\n stale,\n error,\n publishBlockedReason: publishBlocked,\n saveNow,\n flush,\n flushThen,\n commit,\n reload,\n };\n}\n"],"names":["AUTOSAVE_DEBOUNCE_MS","createAutoSaver","opts","delayMs","setTimer","fn","ms","clearTimer","handle","pending","lastSaved","disarm","fire","entry","result","payload","enabled","canSave","ctx","registerFlushOnHide","windowTarget","documentTarget","flush","onBeforeUnload","onVisibilityChange","isStaleWrite","e","nextSaveToken","prev","res","ctxDiffers","a","client","project","NO_PROJECT","evaluate","text","id","saveGuard","parsed","syntaxError","blocked","useWorkflowDraft","o","useEmbedClient","registry","useDraftRegistry","qc","useQueryClient","workflowId","autoSave","json","setJsonState","useState","loadedId","setLoadedId","loadError","setLoadError","saving","setSaving","stale","setStale","error","setError","handingOver","setHandingOver","optsRef","useRef","clientRef","qcRef","rowRef","guardRef","_a","row","saveRow","saveRowRef","cur","isCurrent","gen","p","definition","clear","reportDiscard","useCallback","outcome","_b","flushRow","explicit","ignoreGates","drained","auth","useEffect","canceled","authored","r","loaded","ev","useMemo","saveBlockedReason","switching","dirty","publishBlocked","next","w","d","offHide","unregister","setJson","saveNow","flushThen","action","commit","reload"],"mappings":";;;;AA4CO,MAAMA,KAAuB;AAoC7B,SAASC,GAAgBC,GAAgC;AAC9D,QAAMC,IAAUD,EAAK,WAAWF,IAC1BI,IACJF,EAAK,aAAa,CAACG,GAAgBC,MAAe,WAAWD,GAAIC,CAAE,IAC/DC,IAAaL,EAAK,eAAe,CAACM,MAAmB,aAAaA,CAAM;AAE9E,MAAIC,IAA0B,MAC1BD,IAAwB,MACxBE,IAA2B;AAG/B,WAASC,IAAe;AACtB,IAAIH,MAAW,SACbD,EAAWC,CAAM,GACjBA,IAAS;AAAA,EAEb;AAOA,WAASI,IAAsB;AAC7B,IAAAJ,IAAS;AACT,UAAMK,IAAQJ;AAKd,QAJAA,IAAU,MACN,CAACI,KACD,CAACA,EAAM,WACP,CAACA,EAAM,WACPA,EAAM,YAAYH,EAAW,QAAO,QAAQ,QAAA;AAGhD,UAAMI,IAASZ,EAAK,KAAKW,EAAM,SAASA,EAAM,GAAG;AACjD,WAAO,QAAQ,QAAQC,CAAM;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,KAAKC,GAAiBC,GAAkBC,GAAkBC,GAAqB;AAC7E,MAAAT,IAAU,EAAE,SAAAM,GAAS,SAAAC,GAAS,SAAAC,GAAS,KAAAC,EAAA,GACvCP,EAAA,GACAH,IAASJ,EAAS,MAAM;AAEtB,QAAKQ,EAAA,EAAO,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC5B,GAAGT,CAAO;AAAA,IACZ;AAAA,IACA,QAAuB;AACrB,aAAAQ,EAAA,GACOC,EAAA;AAAA,IACT;AAAA,IACA,SAAe;AACb,MAAAD,EAAA,GACAF,IAAU;AAAA,IACZ;AAAA,IACA,UAAUM,GAAuB;AAC/B,MAAAL,IAAYK;AAAA,IACd;AAAA,IACA,QAAQA,GAA0B;AAChC,aAAOA,MAAYL;AAAA,IACrB;AAAA,EAAA;AAEJ;AC5EO,SAASS,GACdC,GACAC,GACAC,GACY;AACZ,QAAMC,IAAiB,MAAM;AAC3B,IAAAD,EAAA;AAAA,EACF,GACME,IAAqB,MAAM;AAC/B,IAAIH,EAAe,oBAAoB,YACrCC,EAAA;AAAA,EAEJ;AAEA,SAAAF,EAAa,iBAAiB,gBAAgBG,CAAc,GAC5DF,EAAe,iBAAiB,oBAAoBG,CAAkB,GAE/D,MAAM;AACX,IAAAJ,EAAa,oBAAoB,gBAAgBG,CAAc,GAC/DF,EAAe,oBAAoB,oBAAoBG,CAAkB;AAAA,EAC3E;AACF;ACvDO,SAASC,GAAaC,GAAqB;AAChD,SAAO,OAAOA,KAAM,YAAYA,MAAM,QAASA,EAAyB,SAAS;AACnF;AAeO,SAASC,EAAcC,GAAqBC,GAA6C;AAC9F,SAAI,OAAOA,EAAI,aAAc,WAAiBA,EAAI,YAG3C;AACT;AC4FA,SAASC,EAAWC,GAAmBC,GAAmBC,GAAoC;AAC5F,SAAKF,IACDA,EAAE,WAAWC,IAAe,MACxBD,EAAE,WAAW,WAAWE,KAAW,SAAS,CAAC,CAACF,EAAE,UAFzC;AAGjB;AASA,MAAMG,IAAa;AAEnB,SAASC,GACPC,GACAC,GACAJ,GACAK,GACY;AACZ,MAAIC,IAAyB,MACzBC,IAA6B;AACjC,MAAI;AACF,IAAAD,IAAS,KAAK,MAAMH,CAAI;AAAA,EAC1B,SAASV,GAAG;AACV,IAAAc,IAAcd,aAAa,QAAQA,EAAE,UAAU,OAAOA,CAAC;AAAA,EACzD;AACA,MAAIe,IAAyB;AAC7B,SAAKR,IACIO,MAAgB,OAAMC,IAAU,iBAAiBD,CAAW,KAC5D,OAAOD,KAAW,YAAYA,MAAW,QAAQ,MAAM,QAAQA,CAAM,IAC5EE,IAAU,iDAEV,OAAQF,EAA4B,MAAO,YAC1CA,EAA0B,OAAOF,IAIlCI,IAAU,4CACDH,MAAWG,IAAUH,EAAUC,CAAM,KAAK,QAXvCE,IAAUP,GAYjB,EAAE,QAAAK,GAAQ,aAAAC,GAAa,SAAAC,EAAA;AAChC;AAEO,SAASC,GAAiBC,GAA2C;;AAC1E,QAAMX,IAASY,GAAA,GACTC,IAAWC,GAAA,GACXC,IAAKC,GAAA,GACL,EAAE,YAAAC,MAAeN,GACjB,EAAE,SAAAV,GAAS,WAAAK,EAAA,IAAcK,GACzBO,IAAWP,EAAE,YAAY,IAEzB,CAACQ,GAAMC,CAAY,IAAIC,EAAS,EAAE,GAClC,CAACC,GAAUC,CAAW,IAAIF,EAAwB,IAAI,GACtD,CAACG,GAAWC,CAAY,IAAIJ,EAAyB,IAAI,GACzD,CAACK,GAAQC,CAAS,IAAIN,EAAS,EAAK,GACpC,CAACO,GAAOC,CAAQ,IAAIR,EAAS,EAAK,GAClC,CAACS,IAAOC,CAAQ,IAAIV,EAAyB,IAAI,GACjD,CAACW,GAAaC,CAAc,IAAIZ,EAAS,EAAK,GAG9Ca,IAAUC,EAAOxB,CAAC;AACxB,EAAAuB,EAAQ,UAAUvB;AAClB,QAAMyB,IAAYD,EAAOnC,CAAM;AAC/B,EAAAoC,EAAU,UAAUpC;AACpB,QAAMqC,IAAQF,EAAOpB,CAAE;AACvB,EAAAsB,EAAM,UAAUtB;AAChB,QAAMuB,IAASH,EAAmB,IAAI,GAGhCI,IAAWJ,EAAO7B,CAAS;AAIjC,MAAIW,MAAe,KAAM,CAAAqB,EAAO,UAAU;AAAA,aACjCE,KAAAF,EAAO,YAAP,gBAAAE,GAAgB,QAAOvB,GAAY;AAC1C,UAAMwB,IAAW;AAAA,MACf,IAAIxB;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,UAAU;AAAA,MACV,OAAO;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,MACV,KAAK;AAAA,MACL,iBAAiB;AAAA,IAAA;AAEnBwB,IAAAA,EAAI,QAAQxE,GAAgB;AAAA,MAC1B,MAAM,CAACc,GAASG,MAAQwD,EAAQD,GAAK1D,GAASG,CAAc;AAAA,IAAA,CAC7D,GACDoD,EAAO,UAAUG;AAAAA,EACnB;AAGA,QAAME,KAAaR,EAAOO,CAAO;AACjC,EAAAC,GAAW,UAAUD;AACrB,WAASA,EAAQD,GAAU1D,GAAiBG,GAA6B;AAEvE,QAAIuD,EAAI,SAAU,QAAO,QAAQ,QAAA;AACjC,UAAMG,IAAMV,EAAQ;AACpB,QAAI,CAAChD,EAAI,YAAY0D,EAAI,eAAe,CAACA,EAAI,YAAA,EAAe,QAAO,QAAQ,QAAA;AAC3E,UAAMC,IAAY,MAAMP,EAAO,YAAYG,GACrCK,IAAML,EAAI,KAkCVM,KAjCM,YAA2B;AACrC,UAAI,CAAC7D,EAAI,QAAS,OAAM,IAAI,MAAMgB,CAAU;AAC5C,YAAM8C,IAAa,KAAK,MAAMjE,CAAO;AACrC,MAAI8D,QACFlB,EAAU,EAAI,GACdI,EAAS,IAAI;AAEf,UAAI;AACF,cAAMlC,IAAM,MAAMX,EAAI,OAAO,UAAU,OAAOA,EAAI,IAAI8D,GAAY;AAAA,UAChE,SAAS9D,EAAI;AAAA,UACb,mBAAmBA,EAAI;AAAA,QAAA,CACxB;AAID,YAHKmD,EAAM,QAAQ,kBAAkB,EAAE,UAAU,CAAC,WAAW,GAAG,GAG5DS,MAAQL,EAAI,IAAK;AACrBA,QAAAA,EAAI,QAAQ9C,EAAcT,EAAI,OAAOW,CAA8B,GACnE4C,EAAI,MAAM,UAAU1D,CAAO,GAC3B0D,EAAI,UAAU;AAAA,MAChB,SAAS/C,GAAG;AACV,cAAIoD,MAAQL,EAAI,QACdA,EAAI,UAAU1D,GACd0D,EAAI,YAAY/C,GAChB+C,EAAI,MAAM,OAAA,GACNhD,GAAaC,CAAC,MAAG+C,EAAI,QAAQ,KAC7BI,QACEJ,EAAI,SAAOZ,EAAS,EAAI,GAC5BE,EAASrC,CAAC,KAGRA;AAAA,MACR;AAAA,IACF,GACU;AACV+C,IAAAA,EAAI,WAAWM;AACf,UAAME,IAAQ,MAAM;AAClB,MAAIR,EAAI,aAAaM,MAAGN,EAAI,WAAW,OACnCI,EAAA,KAAalB,EAAU,EAAK;AAAA,IAClC;AACA,WAAAoB,EAAE,KAAKE,GAAOA,CAAK,GACZF;AAAA,EACT;AAGA,QAAMG,IAAgBC,EAAY,CAACV,GAAUW,MAA0B;;AACrE,QAAI,EAAAA,EAAQ,WAAW,aAAaA,EAAQ,WAAW,aACnDX,EAAI,oBAAoBA,EAAI,KAChCA;AAAAA,MAAAA,EAAI,kBAAkBA,EAAI;AAC1B,UAAI;AACF,SAAAY,KAAAb,IAAAN,EAAQ,SAAQ,cAAhB,QAAAmB,EAAA,KAAAb,GAA4B;AAAA,UAC1B,YAAYC,EAAI;AAAA,UAChB,MAAMA,EAAI;AAAA,UACV,QAAQW,EAAQ;AAAA,QAAA;AAAA,MAEpB,QAAQ;AAAA,MAER;AAAA;AAAA,EACF,GAAG,CAAA,CAAE,GAOCE,IAAWH;AAAA,IACf,OACEV,GACAc,GAMAC,IAAc,OACY;AAC1B,UAAI;AACF,YAAIC,IAAqC;AACzC,eAAOhB,EAAI;AACT,cAAI;AACF,kBAAMA,EAAI;AAAA,UACZ,SAAS/C,GAAG;AACV,YAAA+D,IAAU,EAAE,OAAO/D,EAAA;AAAA,UACrB;AAEF,YAAI,CAAC+C,EAAI,OAAQ,QAAO,EAAE,QAAQ,WAAW,QAAQ,+BAAA;AACrD,cAAMrC,IAAOqC,EAAI;AACjB,YAAI,CAACA,EAAI,MAAM,QAAQrC,CAAI,EAAG,QAAO,EAAE,QAAQ,QAAA;AAE/C,YAAIqD,KAAWhB,EAAI,YAAYrC,EAAM,QAAO,EAAE,QAAQ,UAAU,OAAOqD,EAAQ,MAAA;AAC/E,YAAIhB,EAAI,MAAO,QAAO,EAAE,QAAQ,UAAU,OAAOA,EAAI,UAAA;AACrD,cAAMG,IAAMV,EAAQ,SACdwB,IAAOjB,EAAI;AACjB,QAAI,CAACiB,EAAK,WAAWA,EAAK,WAAWtB,EAAU,YAASsB,EAAK,UAAUd,EAAI;AAC3E,cAAM,EAAE,SAAAnC,EAAA,IAAYN,GAASC,GAAMqC,EAAI,IAAIiB,EAAK,SAASnB,EAAS,OAAO;AACzE,YAAI9B,MAAY,KAAM,QAAO,EAAE,QAAQ,WAAW,QAAQA,EAAA;AAC1D,YAAI,CAAC8C,GAAU;AACb,cAAI,CAACC,KAAe,EAAEZ,EAAI,YAAY;AACpC,mBAAO,EAAE,QAAQ,WAAW,QAAQ,oBAAA;AAEtC,cAAI,CAACY,KAAeZ,EAAI,eAAe,CAACA,EAAI;AAC1C,mBAAO,EAAE,QAAQ,WAAW,QAAQ,sBAAA;AAAA,QAExC;AACA,cAAM1D,IAAe;AAAA,UACnB,QAAQwE,EAAK;AAAA,UACb,SAASA,EAAK;AAAA,UACd,IAAIjB,EAAI;AAAA,UACR,OAAOA,EAAI;AAAA;AAAA,UAEX,UAAUc,KAAYC;AAAA,QAAA;AAExBf,eAAAA,EAAI,MAAM,KAAKrC,GAAM,IAAM,IAAMlB,CAAG,GACpC,MAAMuD,EAAI,MAAM,MAAA,GACT,EAAE,QAAQ,QAAA;AAAA,MACnB,SAASX,GAAO;AACd,eAAO,EAAE,QAAQ,UAAU,OAAAA,EAAAA;AAAAA,MAC7B;AAAA,IACF;AAAA,IACA,CAAA;AAAA,EAAC;AAOH,EAAA6B,EAAU,MAAM;AACd,UAAMlB,IAAMH,EAAO;AACnB,QAAI,CAACG,EAAK;AACV,QAAImB,IAAW;AACf,IAAAxC,EAAa,EAAE,GACfG,EAAY,IAAI,GAChBE,EAAa,IAAI,GACjBM,EAAS,IAAI,GACbF,EAAS,EAAK,GACdF,EAAU,EAAK;AACf,UAAMkC,IAAoB,EAAE,QAAQzB,EAAU,SAAS,SAASF,EAAQ,QAAQ,QAAA;AAChF,WAAA2B,EAAS,OAAO,UAAU,IAAIpB,EAAI,EAAE,EAAE;AAAA,MACpC,CAACqB,MAAM;AACL,YAAIF,EAAU;AACdnB,QAAAA,EAAI,OAAOoB;AACX,cAAMzD,IAAO,KAAK,UAAU0D,EAAE,UAAU,MAAM,CAAC;AAC/CrB,QAAAA,EAAI,OAAOrC,GACXqC,EAAI,QAAQ9C,EAAc,MAAMmE,CAAC,GAEjCrB,EAAI,MAAM,UAAUrC,CAAI,GACxBqC,EAAI,SAAS,IACbrB,EAAahB,CAAI,GACjBmB,EAAYkB,EAAI,EAAE;AAAA,MACpB;AAAA,MACA,CAAC/C,MAAM;AACL,QAAKkE,KAAUnC,EAAa/B,CAAC;AAAA,MAC/B;AAAA,IAAA,GAEK,MAAM;AACX,MAAAkE,IAAW,IAENN,EAASb,GAAK,EAAK,GAExBA,EAAI,MAAM,OAAA;AAAA,IACZ;AAAA,EACF,GAAG,CAACxB,GAAYqC,CAAQ,CAAC;AAGzB,QAAMb,IAAMH,EAAO,SAEbyB,KAAStB,KAAA,gBAAAA,EAAK,YAAW,MAAQnB,MAAaL,GAC9Cb,IAAO2D,IAAS5C,IAAO,IACvB6C,IAAKC;AAAA,IACT,MAAOF,KAAU9C,MAAe,OAAOd,GAASC,GAAMa,GAAYhB,GAASK,CAAS,IAAI;AAAA,IACxF,CAACyD,GAAQ3D,GAAMa,GAAYhB,GAASK,CAAS;AAAA,EAAA,GAEzC4D,KAAoBF,IAAKA,EAAG,UAAU,CAAC/D,KAAWgB,MAAe,OAAOf,IAAa;AAC3F,EAAM6D,KAAUjE,GAAW2C,KAAA,gBAAAA,EAAK,SAAQ,MAAMzC,GAAQC,CAAO,MAAIsC,EAAS,UAAUjC;AACpF,QAAM6D,KAAYnC,KAAgB+B,KAAUjE,GAAW2C,KAAA,gBAAAA,EAAK,SAAQ,MAAMzC,GAAQC,CAAO,GACnFhB,IAAU8E,KAAUG,OAAsB,QAAQ,CAACxC,KAAU,CAACyC,IAC9DC,KAAQL,MAAUtB,KAAA,gBAAAA,EAAK,MAAM,QAAQrC,QAAU,IAC/CiE,KACJL,KAAMA,EAAG,WAAW,QAAQrD,EAAE,uBAAuBA,EAAE,qBAAqBqD,EAAG,MAAM,IAAI;AAK3F,EAAAL,EAAU,MAAM;AACd,UAAMG,IAAIxB,EAAO;AAMjB,QALI,CAACwB,KAAK,CAACC,KAAUD,EAAE,OAAO7C,KAE1BW,KACAT,MAAS2C,EAAE,WAEXA,EAAE,YAAY,CAACA,EAAE,QAAQhE,EAAWgE,EAAE,MAAM1B,EAAU,SAASF,EAAQ,QAAQ,OAAO;AACxF;AAEF,IAAK4B,EAAE,KAAK,cAAW,KAAK,UAAU5B,EAAQ,QAAQ;AACtD,UAAMhD,IAAe;AAAA,MACnB,QAAQ4E,EAAE,KAAK;AAAA,MACf,SAASA,EAAE,KAAK;AAAA,MAChB,IAAIA,EAAE;AAAA,MACN,OAAOA,EAAE;AAAA,MACT,UAAU;AAAA,IAAA;AAEZ,IAAAA,EAAE,MAAM,KAAK3C,GAAMD,GAAUjC,GAASC,CAAG;AAAA,EAC3C,GAAG,CAAC6E,GAAQ9C,GAAYE,GAAMS,GAAOV,GAAUjC,CAAO,CAAC,GAMvD0E,EAAU,MAAM;AACd,UAAMG,IAAIxB,EAAO;AACjB,QAAI,GAACwB,KAAK,CAACC,KAAUD,EAAE,OAAO7C,KAAc6C,EAAE,WAC9C;AAAA,UAAI,CAAChE,EAAWgE,EAAE,MAAM9D,GAAQC,CAAO,GAAG;AACxC,QAAI6D,EAAE,QAAQ,CAACA,EAAE,KAAK,YAASA,EAAE,KAAK,UAAU7D;AAChD;AAAA,MACF;AACA,MAAA6D,EAAE,WAAW,IACb7B,EAAe,EAAI,IACb,YAAY;AAChB,YAAI;AAIF,cADAiB,EAAcY,GAAG,MAAMR,EAASQ,GAAG,IAAO,EAAI,CAAC,GAC3CxB,EAAO,YAAYwB,EAAG;AAC1B,UAAAA,EAAE,MAAM,OAAA;AACR,gBAAMD,IAAoB,EAAE,QAAQzB,EAAU,SAAS,SAASF,EAAQ,QAAQ,QAAA,GAC1ErC,IAAM,MAAMgE,EAAS,OAAO,UAAU,IAAIC,EAAE,EAAE;AACpD,cAAIxB,EAAO,YAAYwB,EAAG;AAC1B,gBAAMQ,IAAO,KAAK,UAAUzE,EAAI,UAAU,MAAM,CAAC;AACjD,UAAAiE,EAAE,OACFA,EAAE,OAAOD,GACTC,EAAE,OAAOQ,GACTR,EAAE,QAAQnE,EAAc,MAAME,CAAG,GACjCiE,EAAE,MAAM,UAAUQ,CAAI,GACtBR,EAAE,UAAU,MACZA,EAAE,QAAQ,IACVA,EAAE,YAAY,MACd1C,EAAakD,CAAI,GACjBzC,EAAS,EAAK,GACdE,EAAS,IAAI;AAAA,QACf,SAASrC,GAAG;AACV,UAAI4C,EAAO,YAAYwB,MACrBA,EAAE,SAAS,IACXvC,EAAY,IAAI,GAChBE,EAAa/B,CAAC;AAAA,QAElB,UAAA;AACE,UAAAoE,EAAE,WAAW,IACTxB,EAAO,YAAYwB,KAAG7B,EAAe,EAAK;AAAA,QAChD;AAAA,MACF,GAAA;AAAA;AAAA,EACF,GAAG,CAAC8B,GAAQ9C,GAAYjB,GAAQC,GAAS+B,GAAasB,GAAUJ,CAAa,CAAC,GAI9ES,EAAU,MAAM;AACd,UAAMG,IAAIxB,EAAO;AACjB,QAAI,CAACwB,EAAG;AACR,UAAMS,IAAK,WAAmC,QACxCC,IAAK,WAAuC,UAC5CC,IACJF,KAAKC,IACDrF,GAAoBoF,GAAGC,GAAG,MAAM;AAC9B,MAAKlB,EAASQ,GAAG,EAAK;AAAA,IACxB,CAAC,IACD,QAGAY,IAAa7D,KAAA,gBAAAA,EAAU,SAAS,YAAY;AAChD,YAAMuC,IAAU,MAAME,EAASQ,GAAG,IAAO,EAAI;AAC7C,aAAAZ,EAAcY,GAAGV,CAAO,GACjBA;AAAA,IACT;AACA,WAAO,MAAM;AACX,MAAAqB,KAAA,QAAAA,KACAC,KAAA,QAAAA;AAAA,IACF;AAAA,EACF,GAAG,CAACzD,GAAYJ,GAAUyC,GAAUJ,CAAa,CAAC;AAGlD,QAAMyB,IAAUxB,EAAY,CAACmB,MAAiB;AAC5C,UAAMR,IAAIxB,EAAO;AACjB,IAAI,CAACwB,KAAK,CAACA,EAAE,WACbA,EAAE,OAAOQ,GACTlD,EAAakD,CAAI;AAAA,EACnB,GAAG,CAAA,CAAE,GACChF,IAAQ6D,EAAY,MAA6B;AACrD,UAAMW,IAAIxB,EAAO;AACjB,WAAOwB,IAAIR,EAASQ,GAAG,EAAI,IAAI,QAAQ,QAAQ,EAAE,QAAQ,SAAS;AAAA,EACpE,GAAG,CAACR,CAAQ,CAAC,GACPsB,KAAUzB,EAAY,MAAM;AAChC,IAAK7D,EAAA;AAAA,EACP,GAAG,CAACA,CAAK,CAAC,GACJuF,KAAY1B;AAAA,IAChB,OAAU2B,MAA6B;AACrC,YAAM1B,IAAU,MAAM9D,EAAA;AACtB,aAAI8D,EAAQ,WAAW,WAAWA,EAAQ,WAAW,UAC5C,EAAE,IAAI,IAAe,OAAO,MAAM0B,IAAO,IAE3C,EAAE,IAAI,IAAgB,SAAA1B,EAAA;AAAA,IAC/B;AAAA,IACA,CAAC9D,CAAK;AAAA,EAAA,GAEFyF,KAAS5B;AAAA,IACb,CAACmB,OACCK,EAAQL,CAAI,GACLhF,EAAA;AAAA,IAET,CAACqF,GAASrF,CAAK;AAAA,EAAA,GAEX0F,KAAS7B,EAAY,YAAY;AACrC,UAAMW,IAAIxB,EAAO;AACjB,QAAKwB;AACL,UAAI;AACF,cAAMD,IAAoB,EAAE,QAAQzB,EAAU,SAAS,SAASF,EAAQ,QAAQ,QAAA,GAC1ErC,IAAM,MAAMgE,EAAS,OAAO,UAAU,IAAIC,EAAE,EAAE;AACpD,YAAIxB,EAAO,YAAYwB,EAAG;AAC1B,QAAAA,EAAE,OAAOD;AACT,cAAMS,IAAO,KAAK,UAAUzE,EAAI,UAAU,MAAM,CAAC;AAEjD,QAAAiE,EAAE,MAAM,OAAA,GACRA,EAAE,OACFA,EAAE,OAAOQ,GACTR,EAAE,QAAQnE,EAAc,MAAME,CAAG,GACjCiE,EAAE,MAAM,UAAUQ,CAAI,GACtBR,EAAE,UAAU,MACZA,EAAE,QAAQ,IACVA,EAAE,YAAY,MACdA,EAAE,SAAS,IACX1C,EAAakD,CAAI,GACjB/C,EAAYuC,EAAE,EAAE,GAChBjC,EAAS,EAAK,GACdE,EAAS,IAAI;AAAA,MACf,SAASrC,GAAG;AACV,QAAI4C,EAAO,YAAYwB,KAAG/B,EAASrC,CAAC;AAAA,MACtC;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,MAAMU;AAAA,IACN,SAAAuE;AAAA,IACA,QAAQX,IAAKA,EAAG,SAAS;AAAA,IACzB,aAAaA,IAAKA,EAAG,cAAc;AAAA,IACnC,SAAS/C,MAAe,QAAQ,CAAC8C,KAAUvC,MAAc;AAAA,IACzD,WAAAA;AAAA,IACA,OAAA4C;AAAA,IACA,QAAA1C;AAAA,IACA,SAAAzC;AAAA,IACA,mBAAAiF;AAAA,IACA,OAAAtC;AAAA,IACA,OAAAE;AAAA,IACA,sBAAsBuC;AAAA,IACtB,SAAAO;AAAA,IACA,OAAAtF;AAAA,IACA,WAAAuF;AAAA,IACA,QAAAE;AAAA,IACA,QAAAC;AAAA,EAAA;AAEJ;"}
|