@w6w/embed 0.2.11 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +22 -5
  2. package/dist/ConfirmModal-Pv2-A_3U.js +186 -0
  3. package/dist/ConfirmModal-Pv2-A_3U.js.map +1 -0
  4. package/dist/{CreateFunctionModal-D6CQLpa_.js → CreateFunctionModal-DScxp_LN.js} +2 -2
  5. package/dist/{CreateFunctionModal-D6CQLpa_.js.map → CreateFunctionModal-DScxp_LN.js.map} +1 -1
  6. package/dist/{DocumentFormModal-YauVdolr.js → DocumentFormModal-Br4s8wmi.js} +3 -3
  7. package/dist/{DocumentFormModal-YauVdolr.js.map → DocumentFormModal-Br4s8wmi.js.map} +1 -1
  8. package/dist/ImportSpecModal-Cpzp8ndt.js +251 -0
  9. package/dist/ImportSpecModal-Cpzp8ndt.js.map +1 -0
  10. package/dist/{ModelRoutesManager-BGESQahw.js → ModelRoutesManager-1Ac5wcas.js} +3 -3
  11. package/dist/{ModelRoutesManager-BGESQahw.js.map → ModelRoutesManager-1Ac5wcas.js.map} +1 -1
  12. package/dist/WorkflowEditor-CmhXHRxi.js +1109 -0
  13. package/dist/WorkflowEditor-CmhXHRxi.js.map +1 -0
  14. package/dist/apps-ItBvnWX3.js +92 -0
  15. package/dist/apps-ItBvnWX3.js.map +1 -0
  16. package/dist/chrome/DangerZone.d.ts +4 -3
  17. package/dist/chrome/ExecutionProfileHeader.d.ts +3 -3
  18. package/dist/chrome/JsonToggle.d.ts +29 -20
  19. package/dist/chrome/Modal.d.ts +3 -1
  20. package/dist/chrome/RightRail.d.ts +6 -1
  21. package/dist/chrome/VersionHistoryModal.d.ts +2 -0
  22. package/dist/chrome/index.d.ts +1 -0
  23. package/dist/chrome/workspace.d.ts +32 -1
  24. package/dist/{coerce-input-DttlcSdO.js → coerce-input-DFJ4n_wc.js} +68 -69
  25. package/dist/coerce-input-DFJ4n_wc.js.map +1 -0
  26. package/dist/{connections-D7cgXhDS.js → connections-CzsCyIxY.js} +10 -10
  27. package/dist/{connections-D7cgXhDS.js.map → connections-CzsCyIxY.js.map} +1 -1
  28. package/dist/{credentials-DaQU3Ca_.js → credentials-Cn92LGhJ.js} +4 -4
  29. package/dist/{credentials-DaQU3Ca_.js.map → credentials-Cn92LGhJ.js.map} +1 -1
  30. package/dist/data-stores-UeQvwVCa.js +170 -0
  31. package/dist/data-stores-UeQvwVCa.js.map +1 -0
  32. package/dist/document-sources-DNmBojx6.js +169 -0
  33. package/dist/document-sources-DNmBojx6.js.map +1 -0
  34. package/dist/draft/use-workflow-draft.d.ts +2 -0
  35. package/dist/draft.mjs +2 -2
  36. package/dist/editor/PublishTooltip.d.ts +7 -0
  37. package/dist/editor/SetupIncompleteBanner.d.ts +12 -0
  38. package/dist/editor/StartingStateModal.d.ts +14 -0
  39. package/dist/editor/WorkflowSettingsModal.d.ts +6 -1
  40. package/dist/editor/WorkflowVariablesEditor.d.ts +6 -0
  41. package/dist/editor/definition.d.ts +4 -0
  42. package/dist/editor/json-apply.d.ts +29 -0
  43. package/dist/editor/step-problems-feed.d.ts +5 -0
  44. package/dist/editor/types.d.ts +2 -2
  45. package/dist/editor-apps-DkkC7wOQ.js +152 -0
  46. package/dist/editor-apps-DkkC7wOQ.js.map +1 -0
  47. package/dist/editor-tools-BR27RGto.js +1683 -0
  48. package/dist/editor-tools-BR27RGto.js.map +1 -0
  49. package/dist/editor.mjs +1 -1
  50. package/dist/editors.mjs +536 -478
  51. package/dist/editors.mjs.map +1 -1
  52. package/dist/forms.mjs +3 -3
  53. package/dist/{rail-settings-XYGToh00.js → import-bindings-EUtxZ9Ti.js} +90 -24
  54. package/dist/import-bindings-EUtxZ9Ti.js.map +1 -0
  55. package/dist/index.mjs +138 -108
  56. package/dist/index.mjs.map +1 -1
  57. package/dist/lib/binding-problems.d.ts +5 -1
  58. package/dist/lib/history-query.d.ts +45 -0
  59. package/dist/lib/import-bindings.d.ts +89 -0
  60. package/dist/lib/index.d.ts +3 -0
  61. package/dist/lib/rail-settings.d.ts +1 -1
  62. package/dist/lib/setup-incomplete.d.ts +22 -0
  63. package/dist/lib/starting-data.d.ts +31 -0
  64. package/dist/lib/state-panel.d.ts +29 -0
  65. package/dist/lib/target-fields.d.ts +3 -15
  66. package/dist/lib/workflow-live-run.d.ts +84 -6
  67. package/dist/lib/workflow-validity.d.ts +64 -16
  68. package/dist/lib/workflow-variables.d.ts +1 -1
  69. package/dist/lib/workflow-version.d.ts +2 -0
  70. package/dist/lib.mjs +87 -72
  71. package/dist/lists/DataStoreList.d.ts +17 -0
  72. package/dist/lists/DataStoreView.d.ts +19 -0
  73. package/dist/lists/Logs.d.ts +19 -0
  74. package/dist/lists.d.ts +6 -0
  75. package/dist/lists.mjs +1255 -816
  76. package/dist/lists.mjs.map +1 -1
  77. package/dist/rail/ConnectionsTool.d.ts +2 -0
  78. package/dist/rail/HistoryTool.d.ts +2 -0
  79. package/dist/rail/StartingStateTool.d.ts +2 -0
  80. package/dist/rail/settings-sections.d.ts +4 -1
  81. package/dist/rail.mjs +1 -1
  82. package/dist/repos/constants.d.ts +12 -0
  83. package/dist/repos/data-stores.d.ts +84 -0
  84. package/dist/repos/history.d.ts +9 -0
  85. package/dist/repos/index.d.ts +2 -0
  86. package/dist/repos/spec-export.d.ts +3 -3
  87. package/dist/repos/spec-import.d.ts +30 -1
  88. package/dist/repos/step-tests.d.ts +2 -0
  89. package/dist/repos/webhook-listen.d.ts +35 -0
  90. package/dist/repos/workflows.d.ts +105 -2
  91. package/dist/repos.mjs +370 -328
  92. package/dist/repos.mjs.map +1 -1
  93. package/dist/setup-incomplete-BHs3Lpfy.js +199 -0
  94. package/dist/setup-incomplete-BHs3Lpfy.js.map +1 -0
  95. package/dist/{spec-export-CBbloO_n.js → spec-export-DGy5ijps.js} +72 -59
  96. package/dist/spec-export-DGy5ijps.js.map +1 -0
  97. package/dist/spec-import-SWOonsNr.js +116 -0
  98. package/dist/spec-import-SWOonsNr.js.map +1 -0
  99. package/dist/{step-tests-CivICmTq.js → step-tests-B4E4W3ly.js} +8 -7
  100. package/dist/step-tests-B4E4W3ly.js.map +1 -0
  101. package/dist/{use-callable-draft-DsALnSfF.js → use-callable-draft-DCauHt5o.js} +2 -2
  102. package/dist/{use-callable-draft-DsALnSfF.js.map → use-callable-draft-DCauHt5o.js.map} +1 -1
  103. package/dist/use-workflow-draft-l3P5KZzw.js +338 -0
  104. package/dist/use-workflow-draft-l3P5KZzw.js.map +1 -0
  105. package/dist/workflows-BZN4Hyz1.js +623 -0
  106. package/dist/workflows-BZN4Hyz1.js.map +1 -0
  107. package/package.json +3 -2
  108. package/src/styles.css +153 -1
  109. package/dist/ConfirmModal-BE-SdX-O.js +0 -185
  110. package/dist/ConfirmModal-BE-SdX-O.js.map +0 -1
  111. package/dist/ImportSpecModal-B7qd1Km2.js +0 -136
  112. package/dist/ImportSpecModal-B7qd1Km2.js.map +0 -1
  113. package/dist/WorkflowEditor-CwvmAGp2.js +0 -925
  114. package/dist/WorkflowEditor-CwvmAGp2.js.map +0 -1
  115. package/dist/apps-CboASBKr.js +0 -77
  116. package/dist/apps-CboASBKr.js.map +0 -1
  117. package/dist/coerce-input-DttlcSdO.js.map +0 -1
  118. package/dist/document-sources-UCwe8x77.js +0 -91
  119. package/dist/document-sources-UCwe8x77.js.map +0 -1
  120. package/dist/editor-apps-BUZWld-m.js +0 -95
  121. package/dist/editor-apps-BUZWld-m.js.map +0 -1
  122. package/dist/editor-tools-B_Lfx3kw.js +0 -1279
  123. package/dist/editor-tools-B_Lfx3kw.js.map +0 -1
  124. package/dist/rail-settings-XYGToh00.js.map +0 -1
  125. package/dist/spec-export-CBbloO_n.js.map +0 -1
  126. package/dist/spec-import-CW6R_Wk1.js +0 -469
  127. package/dist/spec-import-CW6R_Wk1.js.map +0 -1
  128. package/dist/step-tests-CivICmTq.js.map +0 -1
  129. package/dist/trigger-preview-DKxfjzUY.js +0 -145
  130. package/dist/trigger-preview-DKxfjzUY.js.map +0 -1
  131. package/dist/use-workflow-draft-DzAyUeA6.js +0 -316
  132. package/dist/use-workflow-draft-DzAyUeA6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-callable-draft-DsALnSfF.js","sources":["../src/draft/use-callable-draft.ts"],"sourcesContent":["/**\n * `useCallableDraft` — the headless draft behind a Function / Endpoint editor: the editable\n * definition as FIELD state (an object, not text), seeded from the ONE react-query entry every\n * other writer invalidates, saved with the stale-write token, and never silently overwriting — or\n * overwritten by — another writer. `useFunctionDraft` / `useEndpointDraft` bind the kind.\n * Mirrors `use-workflow-draft.ts` (same saver, registry, flush-on-hide, no queue, no retry); that\n * file is not edited and the two do not share a runtime beyond `createAutoSaver` & co.\n *\n * ## One cache\n *\n * The buffer is seeded from `useCallableQuery(kind, id)` — the same key and queryFn as\n * `useFunctionQuery` / `useEndpointQuery`. There is no second `useQuery` and no direct\n * `console.*.get` here, so a rail save, a revert, or this draft's own save all land in the entry\n * the draft reads. `reload()` is that query's `refetch()`, then the buffer and token are replaced.\n * The record split (definition vs. `updatedAt` token) is `lib/callable-record.ts`'s `callableRecord`.\n *\n * ## External change (another writer)\n *\n * An external change is a cache update whose `callableRecord(...).updatedAt` differs from the\n * row's CURRENT token:\n *\n * - CLEAN buffer ⇒ the buffer and token are replaced from the cache (no `reload()`, no extra GET).\n * - DIRTY buffer ⇒ the buffer AND the old token are KEPT and `changedElsewhere` becomes true.\n * Adopting the new token without replacing the buffer would be the silent overwrite, so the\n * token only moves with the buffer. The next save therefore still carries the old token and the\n * server answers `409 *_stale`; `reload()` is the way out. Nothing is ever merged — a field-level\n * merge is deliberately not offered (ruling BC-stale).\n * - EQUAL ⇒ no-op. The row's token is advanced from the save response (`nextSaveToken`) BEFORE the\n * save invalidates the cache, so this draft's own save never reads as external — including an\n * edit typed while that save was in flight.\n *\n * ## The trust boundary (why the state is per ROW, not per hook)\n *\n * As the workflow draft: a pending save must be sent by the client that AUTHORED the buffer, even\n * if the host has swapped its client or moved to another resource. Each row holds `auth` (the\n * client the buffer was loaded as) and EVERY save path (debounce, `flush`, `flushThen`,\n * unmount, registry, hide) sends with it, never with the current render's. When the render's\n * client stops matching, the row hands over: one save of a dirty buffer on the authoring client,\n * then the buffer is discarded and re-seeded (the query's `refetch()`) under the new client;\n * `canSave` is false and pushes are ignored meanwhile. One `Row` per `kind:id`; a late completion\n * of an old row's save updates that old row only; `gen` is bumped whenever the buffer is replaced,\n * and a save completing from an older generation never touches the new buffer's token or clean\n * marker. No project is involved (a Function / Endpoint save carries none).\n *\n * `autoSave` defaults to FALSE (Studio saves these explicitly): `flushAll` on the draft registry\n * saves regardless of `autoSave`; hide / unmount honour it. Deliberately absent: retry, a save\n * queue — a 409 is surfaced (`stale`, `error.code`) and never retried.\n *\n * `onPolicyChange(next)` is referentially stable (it reads the buffer through a ref, because\n * `useWorkspace` keys on `JSON.stringify`, which drops functions) and saves\n * `definitionWithPolicy(<current buffer>, next)` immediately — `commit` semantics: any pending\n * editor edits ride along in that same save.\n */\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport type { W6WClient } from \"@w6w/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { callableKey } from \"../lib/callable-kind.ts\";\nimport { callableRecord } from \"../lib/callable-record.ts\";\nimport { invalidateSavedCallable } from \"../lib/save-invalidation.ts\";\nimport {\n type CallableDefinition,\n type CallablePolicy,\n definitionWithPolicy,\n policyFromDefinition,\n} from \"../lib/settings-rail.ts\";\nimport { callableListKey, useCallableQuery } from \"../repos/callables.ts\";\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 type { FlushOutcome } from \"./use-workflow-draft.ts\";\nimport { isStaleWrite, nextSaveToken } from \"./workflow-save.ts\";\n\nexport type CallableDraftKind = \"function\" | \"endpoint\";\ntype Definition = Record<string, unknown>;\n\nexport interface UseCallableDraftOptions {\n /** `null` ⇒ idle (the hook stays mounted above an early return). */\n id: string | null;\n /** Default FALSE: nothing is sent until `saveNow()` / `flush()` / `commit()`. */\n autoSave?: boolean;\n /** Injected busy probe. Gates AUTOMATIC saves only. */\n canAutoSave?: () => boolean;\n /** Injected edit guard: a reason blocks saving. */\n saveGuard?: (definition: Definition) => string | null;\n /**\n * Called once when a handover or an identity switch (`flushAll`) drops a dirty buffer it could\n * not save. `blocked`: refused before a request; `failed`: the request failed or the row was\n * stale. No UI here.\n */\n onDiscard?: (info: {\n id: string;\n definition: Definition;\n reason: \"blocked\" | \"failed\";\n }) => void;\n}\n\nexport interface CallableDraft {\n /** The editable definition (no `valid` / `updatedAt`); `null` until loaded. */\n definition: Definition | null;\n setDefinition(next: Definition | ((prev: Definition) => Definition)): void;\n /** `{retry, onError, reroute}` read off the buffer. */\n policy: CallablePolicy;\n /** Referentially stable. Saves `definitionWithPolicy(buffer, next)` now. */\n onPolicyChange(policy: CallablePolicy): void;\n loading: boolean;\n loadError: unknown | null;\n dirty: boolean;\n saving: boolean;\n canSave: boolean;\n saveBlockedReason: string | null;\n /** The last save was refused as stale (`function_stale` / `endpoint_stale`). */\n stale: boolean;\n /** Another writer changed the resource while this buffer is dirty. Never merged. */\n changedElsewhere: boolean;\n error: unknown | null;\n /** Push + fire now. */\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 /** `setDefinition(def)` + an immediate save. */\n commit(def: Definition): Promise<FlushOutcome>;\n /** Refetch the shared query, replace the buffer and token, clear `stale` / `changedElsewhere`. */\n reload(): Promise<void>;\n}\n\ninterface AuthCtx {\n client: W6WClient;\n}\n\ninterface SaveCtx {\n client: W6WClient;\n kind: CallableDraftKind;\n id: string;\n token: string | null;\n explicit: boolean;\n}\n\ninterface Row {\n id: string;\n kind: CallableDraftKind;\n def: Definition | null;\n /** `JSON.stringify(def)` — the saver's dirty marker compares on text. */\n text: string;\n token: string | null;\n loaded: boolean;\n stale: boolean;\n changedElsewhere: 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 auth: AuthCtx | null;\n handover: boolean;\n /** Bumped whenever the buffer is replaced by a fresh read. */\n gen: number;\n discardReported: number;\n}\n\nfunction evaluate(\n def: Definition | null,\n id: string,\n saveGuard: UseCallableDraftOptions[\"saveGuard\"],\n): string | null {\n if (!def) return null;\n // `upsert(def)` writes under `def.id`; a different id would save somewhere the author did not mean.\n if (typeof def.id === \"string\" && def.id !== id) return \"The id cannot be changed here.\";\n return saveGuard ? (saveGuard(def) ?? null) : null;\n}\n\nexport function useCallableDraft(kind: CallableDraftKind, o: UseCallableDraftOptions) {\n const client = useEmbedClient();\n const registry = useDraftRegistry();\n const qc = useQueryClient();\n const { id, saveGuard } = o;\n const autoSave = o.autoSave ?? false;\n\n // The ONE cache entry (see the docblock): the same hook `useFunctionQuery` / `useEndpointQuery`\n // are built on, so there is no second fetch path to drift.\n const query = useCallableQuery(kind, id ?? \"\", id !== null);\n\n const [def, setDefState] = useState<Definition | null>(null);\n const [loadedId, setLoadedId] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [stale, setStale] = useState(false);\n const [changedElsewhere, setChangedElsewhere] = useState(false);\n const [error, setError] = useState<unknown | null>(null);\n const [handingOver, setHandingOver] = useState(false);\n\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 refetchRef = useRef(query.refetch);\n refetchRef.current = query.refetch;\n const kindRef = useRef(kind);\n kindRef.current = kind;\n const rowRef = useRef<Row | null>(null);\n const guardRef = useRef(saveGuard);\n\n if (id === null) rowRef.current = null;\n else if (rowRef.current?.id !== id || rowRef.current.kind !== kind) {\n const row: Row = {\n id,\n kind,\n def: null,\n text: \"\",\n token: null,\n loaded: false,\n stale: false,\n changedElsewhere: 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, sent by the client captured at push time. */\n function saveRow(row: Row, payload: string, ctx: SaveCtx): Promise<void> {\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 const definition = JSON.parse(payload) as Definition;\n if (isCurrent()) {\n setSaving(true);\n setError(null);\n }\n try {\n const options = { ifUnmodifiedSince: ctx.token };\n const res =\n ctx.kind === \"function\"\n ? await ctx.client.console.functions.upsert(definition as never, options)\n : await ctx.client.console.endpoints.upsert(definition as never, options);\n // Advance the row's token BEFORE invalidating the cache, so the refetch this save causes\n // reads as \"equal\" and never as another writer. A buffer replaced meanwhile owns its token.\n if (gen === row.gen) row.token = nextSaveToken(ctx.token, res as { updatedAt?: unknown });\n invalidateSavedCallable(\n qcRef.current,\n callableListKey(ctx.kind),\n callableKey(ctx.kind, ctx.id),\n );\n if (gen !== row.gen) return;\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 const saveRowRef = useRef(saveRow);\n saveRowRef.current = saveRow;\n\n /** Replace the row's buffer and token with a fresh read (the seed, a clean external change, reload). */\n const install = useCallback(\n (row: Row, rec: { definition: Definition; updatedAt: string | null }, auth: AuthCtx) => {\n row.gen++;\n row.auth = auth;\n row.def = rec.definition;\n row.text = JSON.stringify(rec.definition);\n row.token = rec.updatedAt;\n row.saver.cancel();\n // What we just read IS the server's copy: the editor opens clean.\n row.saver.markSaved(row.text);\n row.refused = null;\n row.stale = false;\n row.lastError = null;\n row.changedElsewhere = false;\n row.loaded = true;\n setDefState(rec.definition);\n setLoadedId(row.id);\n setStale(false);\n setChangedElsewhere(false);\n setError(null);\n },\n [],\n );\n\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 id: row.id,\n definition: row.def as Definition,\n reason: outcome.status,\n });\n } catch {\n // A host callback must not break the switch.\n }\n }, []);\n\n /** Settle a row's buffer; see `use-workflow-draft.ts`'s `flushRow` (same gates). Never rejects. */\n const flushRow = useCallback(\n async (row: Row, explicit: boolean, ignoreGates = false): 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 definition has not loaded.\" };\n const text = row.text;\n if (!row.saver.isDirty(text)) return { status: \"clean\" };\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 blocked = evaluate(row.def, row.id, guardRef.current);\n if (blocked !== null) return { status: \"blocked\", reason: blocked };\n if (!explicit) {\n if (!ignoreGates && !(cur.autoSave ?? false)) {\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 auth = row.auth as AuthCtx;\n const ctx: SaveCtx = {\n client: auth.client,\n kind: row.kind,\n id: row.id,\n token: row.token,\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 // ── row switch ────────────────────────────────────────────────────────────\n // Keyed on the row identity ONLY: a rebuilt client neither reloads nor cancels the pending entry.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `id`/`kind` are the trigger; the row is read from a ref\n useEffect(() => {\n const row = rowRef.current;\n // Idempotent for a PERSISTED row: StrictMode replays this effect against a row the seed has\n // already installed (and which the seed will not install again, its token being equal), so\n // state is derived from the row instead of nulled. A genuinely new row is unloaded ⇒ null.\n if (row?.loaded) {\n setDefState(row.def);\n setLoadedId(row.id);\n setError(row.refused !== null ? row.lastError : null);\n setStale(row.stale);\n setChangedElsewhere(row.changedElsewhere);\n setSaving(row.inflight !== null);\n } else {\n setDefState(null);\n setLoadedId(null);\n setError(null);\n setStale(false);\n setChangedElsewhere(false);\n setSaving(false);\n }\n if (!row) return;\n return () => {\n // The pending entry belongs to the row that produced it: flush it there, never drop it.\n void flushRow(row, false);\n row.saver.cancel();\n };\n }, [id, kind, flushRow]);\n\n // ── seed / external change ────────────────────────────────────────────────\n const record = useMemo(\n () => (id === null ? null : callableRecord(kind, query.data)),\n [id, kind, query.data],\n );\n // biome-ignore lint/correctness/useExhaustiveDependencies: `record` is derived from `query.data`; the row is read from a ref\n useEffect(() => {\n const row = rowRef.current;\n if (!row || !record || row.handover) return;\n if (!row.loaded) {\n install(row, record, { client: clientRef.current });\n return;\n }\n // Equal ⇒ no-op: our own save (token already advanced) or an unrelated refetch.\n if (record.updatedAt === row.token) return;\n if (!row.saver.isDirty(row.text)) {\n // CLEAN: replace buffer and token together.\n install(row, record, row.auth as AuthCtx);\n } else if (!row.changedElsewhere) {\n // DIRTY: keep buffer AND the old token (the next save must 409, never overwrite).\n row.changedElsewhere = true;\n setChangedElsewhere(true);\n }\n }, [query.data, record, id, kind, install]);\n\n // ── derived ───────────────────────────────────────────────────────────────\n const row = rowRef.current;\n const ctxDiffers = (a: AuthCtx | null) => a !== null && a.client !== client;\n const loaded = row?.loaded === true && loadedId === id;\n const buffer = loaded ? def : null;\n const text = useMemo(() => (buffer ? JSON.stringify(buffer) : \"\"), [buffer]);\n const saveBlockedReason = evaluate(buffer, id ?? \"\", saveGuard);\n if (!(loaded && ctxDiffers(row?.auth ?? null))) guardRef.current = saveGuard;\n const switching = handingOver || (loaded && ctxDiffers(row?.auth ?? null));\n const canSave = loaded && saveBlockedReason === null && !saving && !switching;\n const dirty = loaded && row?.saver.isDirty(text) === true;\n const policy = useMemo(\n () => (buffer ? policyFromDefinition(buffer as CallableDefinition) : {}),\n [buffer],\n );\n const loadError =\n !loaded && id !== null\n ? (query.error ??\n (query.isSuccess && record === null\n ? new Error(\"The definition could not be read.\")\n : null))\n : null;\n\n // ── auto-save push ────────────────────────────────────────────────────────\n // biome-ignore lint/correctness/useExhaustiveDependencies: `text` is the trigger; the row is read from a ref\n useEffect(() => {\n const r = rowRef.current;\n if (!r || !loaded || r.id !== id) return;\n // While a 409 or a \"changed elsewhere\" notice is on screen a push would only retry into it.\n if (stale || changedElsewhere) return;\n if (text === r.refused) return;\n if (r.handover || !r.auth || ctxDiffers(r.auth)) return;\n const ctx: SaveCtx = {\n client: r.auth.client,\n kind: r.kind,\n id: r.id,\n token: r.token,\n explicit: false,\n };\n r.saver.push(text, autoSave, canSave, ctx);\n }, [loaded, id, text, stale, changedElsewhere, autoSave, canSave, client]);\n\n // ── context handover ──────────────────────────────────────────────────────\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 !== id || r.handover) return;\n if (!ctxDiffers(r.auth)) return;\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 res = await refetchRef.current();\n if (rowRef.current !== r) return;\n const rec = res.isError ? null : callableRecord(r.kind, res.data);\n if (!rec) throw res.error ?? new Error(\"The definition could not be read.\");\n install(r, rec, { client: clientRef.current });\n } catch (e) {\n if (rowRef.current === r) {\n r.loaded = false;\n setLoadedId(null);\n setError(e);\n }\n } finally {\n r.handover = false;\n if (rowRef.current === r) setHandingOver(false);\n }\n })();\n }, [loaded, id, client, handingOver, flushRow, reportDiscard, install]);\n\n // ── flush on hide / registry ──────────────────────────────────────────────\n // biome-ignore lint/correctness/useExhaustiveDependencies: `id`/`kind` are 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` saves regardless of `autoSave` and the busy probe, and reports what it dropped.\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 }, [id, kind, registry, flushRow, reportDiscard]);\n\n // ── stable actions ────────────────────────────────────────────────────────\n const setDefinition = useCallback((next: Definition | ((prev: Definition) => Definition)) => {\n const r = rowRef.current;\n if (!r || !r.loaded || !r.def) return;\n const value = typeof next === \"function\" ? next(r.def) : next;\n r.def = value;\n r.text = JSON.stringify(value);\n setDefState(value);\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: Definition): Promise<FlushOutcome> => {\n setDefinition(next);\n return flush();\n },\n [setDefinition, flush],\n );\n // Reads the CURRENT buffer through the row (a ref), never a render's closure: pending editor\n // edits ride along. Stable, because `useWorkspace` drops functions from its publish key.\n const onPolicyChange = useCallback(\n (next: CallablePolicy) => {\n const r = rowRef.current;\n if (!r || !r.loaded || !r.def) return;\n void commit(definitionWithPolicy(r.def as CallableDefinition, next) as Definition);\n },\n [commit],\n );\n const reload = useCallback(async () => {\n const r = rowRef.current;\n if (!r) return;\n try {\n const res = await refetchRef.current();\n if (rowRef.current !== r) return;\n const rec = res.isError ? null : callableRecord(r.kind, res.data);\n if (!rec) {\n setError(res.error ?? new Error(\"The definition could not be read.\"));\n return;\n }\n install(r, rec, { client: clientRef.current });\n } catch (e) {\n if (rowRef.current === r) setError(e);\n }\n }, [install]);\n\n const draft: CallableDraft = {\n definition: buffer,\n setDefinition,\n policy,\n onPolicyChange,\n loading: id !== null && !loaded && loadError === null,\n loadError,\n dirty,\n saving,\n canSave,\n saveBlockedReason,\n stale,\n changedElsewhere,\n error,\n saveNow,\n flush,\n flushThen,\n commit,\n reload,\n };\n return draft;\n}\n\n/** {@link useCallableDraft} bound to a Function. */\nexport function useFunctionDraft(o: UseCallableDraftOptions): CallableDraft {\n return useCallableDraft(\"function\", o);\n}\n\n/** {@link useCallableDraft} bound to an Endpoint. */\nexport function useEndpointDraft(o: UseCallableDraftOptions): CallableDraft {\n return useCallableDraft(\"endpoint\", o);\n}\n"],"names":["evaluate","def","id","saveGuard","useCallableDraft","kind","o","client","useEmbedClient","registry","useDraftRegistry","qc","useQueryClient","autoSave","query","useCallableQuery","setDefState","useState","loadedId","setLoadedId","saving","setSaving","stale","setStale","changedElsewhere","setChangedElsewhere","error","setError","handingOver","setHandingOver","optsRef","useRef","clientRef","qcRef","refetchRef","kindRef","rowRef","guardRef","_a","row","createAutoSaver","payload","ctx","saveRow","cur","isCurrent","gen","p","definition","options","res","nextSaveToken","invalidateSavedCallable","callableListKey","callableKey","e","isStaleWrite","clear","saveRowRef","install","useCallback","rec","auth","reportDiscard","outcome","_b","flushRow","explicit","ignoreGates","drained","text","blocked","useEffect","record","useMemo","callableRecord","ctxDiffers","a","loaded","buffer","saveBlockedReason","switching","canSave","dirty","policy","policyFromDefinition","loadError","r","w","d","offHide","registerFlushOnHide","unregister","setDefinition","next","value","flush","saveNow","flushThen","action","commit","onPolicyChange","definitionWithPolicy","reload","useFunctionDraft","useEndpointDraft"],"mappings":"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"}
1
+ {"version":3,"file":"use-callable-draft-DCauHt5o.js","sources":["../src/draft/use-callable-draft.ts"],"sourcesContent":["/**\n * `useCallableDraft` — the headless draft behind a Function / Endpoint editor: the editable\n * definition as FIELD state (an object, not text), seeded from the ONE react-query entry every\n * other writer invalidates, saved with the stale-write token, and never silently overwriting — or\n * overwritten by — another writer. `useFunctionDraft` / `useEndpointDraft` bind the kind.\n * Mirrors `use-workflow-draft.ts` (same saver, registry, flush-on-hide, no queue, no retry); that\n * file is not edited and the two do not share a runtime beyond `createAutoSaver` & co.\n *\n * ## One cache\n *\n * The buffer is seeded from `useCallableQuery(kind, id)` — the same key and queryFn as\n * `useFunctionQuery` / `useEndpointQuery`. There is no second `useQuery` and no direct\n * `console.*.get` here, so a rail save, a revert, or this draft's own save all land in the entry\n * the draft reads. `reload()` is that query's `refetch()`, then the buffer and token are replaced.\n * The record split (definition vs. `updatedAt` token) is `lib/callable-record.ts`'s `callableRecord`.\n *\n * ## External change (another writer)\n *\n * An external change is a cache update whose `callableRecord(...).updatedAt` differs from the\n * row's CURRENT token:\n *\n * - CLEAN buffer ⇒ the buffer and token are replaced from the cache (no `reload()`, no extra GET).\n * - DIRTY buffer ⇒ the buffer AND the old token are KEPT and `changedElsewhere` becomes true.\n * Adopting the new token without replacing the buffer would be the silent overwrite, so the\n * token only moves with the buffer. The next save therefore still carries the old token and the\n * server answers `409 *_stale`; `reload()` is the way out. Nothing is ever merged — a field-level\n * merge is deliberately not offered (ruling BC-stale).\n * - EQUAL ⇒ no-op. The row's token is advanced from the save response (`nextSaveToken`) BEFORE the\n * save invalidates the cache, so this draft's own save never reads as external — including an\n * edit typed while that save was in flight.\n *\n * ## The trust boundary (why the state is per ROW, not per hook)\n *\n * As the workflow draft: a pending save must be sent by the client that AUTHORED the buffer, even\n * if the host has swapped its client or moved to another resource. Each row holds `auth` (the\n * client the buffer was loaded as) and EVERY save path (debounce, `flush`, `flushThen`,\n * unmount, registry, hide) sends with it, never with the current render's. When the render's\n * client stops matching, the row hands over: one save of a dirty buffer on the authoring client,\n * then the buffer is discarded and re-seeded (the query's `refetch()`) under the new client;\n * `canSave` is false and pushes are ignored meanwhile. One `Row` per `kind:id`; a late completion\n * of an old row's save updates that old row only; `gen` is bumped whenever the buffer is replaced,\n * and a save completing from an older generation never touches the new buffer's token or clean\n * marker. No project is involved (a Function / Endpoint save carries none).\n *\n * `autoSave` defaults to FALSE (Studio saves these explicitly): `flushAll` on the draft registry\n * saves regardless of `autoSave`; hide / unmount honour it. Deliberately absent: retry, a save\n * queue — a 409 is surfaced (`stale`, `error.code`) and never retried.\n *\n * `onPolicyChange(next)` is referentially stable (it reads the buffer through a ref, because\n * `useWorkspace` keys on `JSON.stringify`, which drops functions) and saves\n * `definitionWithPolicy(<current buffer>, next)` immediately — `commit` semantics: any pending\n * editor edits ride along in that same save.\n */\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport type { W6WClient } from \"@w6w/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { callableKey } from \"../lib/callable-kind.ts\";\nimport { callableRecord } from \"../lib/callable-record.ts\";\nimport { invalidateSavedCallable } from \"../lib/save-invalidation.ts\";\nimport {\n type CallableDefinition,\n type CallablePolicy,\n definitionWithPolicy,\n policyFromDefinition,\n} from \"../lib/settings-rail.ts\";\nimport { callableListKey, useCallableQuery } from \"../repos/callables.ts\";\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 type { FlushOutcome } from \"./use-workflow-draft.ts\";\nimport { isStaleWrite, nextSaveToken } from \"./workflow-save.ts\";\n\nexport type CallableDraftKind = \"function\" | \"endpoint\";\ntype Definition = Record<string, unknown>;\n\nexport interface UseCallableDraftOptions {\n /** `null` ⇒ idle (the hook stays mounted above an early return). */\n id: string | null;\n /** Default FALSE: nothing is sent until `saveNow()` / `flush()` / `commit()`. */\n autoSave?: boolean;\n /** Injected busy probe. Gates AUTOMATIC saves only. */\n canAutoSave?: () => boolean;\n /** Injected edit guard: a reason blocks saving. */\n saveGuard?: (definition: Definition) => string | null;\n /**\n * Called once when a handover or an identity switch (`flushAll`) drops a dirty buffer it could\n * not save. `blocked`: refused before a request; `failed`: the request failed or the row was\n * stale. No UI here.\n */\n onDiscard?: (info: {\n id: string;\n definition: Definition;\n reason: \"blocked\" | \"failed\";\n }) => void;\n}\n\nexport interface CallableDraft {\n /** The editable definition (no `valid` / `updatedAt`); `null` until loaded. */\n definition: Definition | null;\n setDefinition(next: Definition | ((prev: Definition) => Definition)): void;\n /** `{retry, onError, reroute}` read off the buffer. */\n policy: CallablePolicy;\n /** Referentially stable. Saves `definitionWithPolicy(buffer, next)` now. */\n onPolicyChange(policy: CallablePolicy): void;\n loading: boolean;\n loadError: unknown | null;\n dirty: boolean;\n saving: boolean;\n canSave: boolean;\n saveBlockedReason: string | null;\n /** The last save was refused as stale (`function_stale` / `endpoint_stale`). */\n stale: boolean;\n /** Another writer changed the resource while this buffer is dirty. Never merged. */\n changedElsewhere: boolean;\n error: unknown | null;\n /** Push + fire now. */\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 /** `setDefinition(def)` + an immediate save. */\n commit(def: Definition): Promise<FlushOutcome>;\n /** Refetch the shared query, replace the buffer and token, clear `stale` / `changedElsewhere`. */\n reload(): Promise<void>;\n}\n\ninterface AuthCtx {\n client: W6WClient;\n}\n\ninterface SaveCtx {\n client: W6WClient;\n kind: CallableDraftKind;\n id: string;\n token: string | null;\n explicit: boolean;\n}\n\ninterface Row {\n id: string;\n kind: CallableDraftKind;\n def: Definition | null;\n /** `JSON.stringify(def)` — the saver's dirty marker compares on text. */\n text: string;\n token: string | null;\n loaded: boolean;\n stale: boolean;\n changedElsewhere: 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 auth: AuthCtx | null;\n handover: boolean;\n /** Bumped whenever the buffer is replaced by a fresh read. */\n gen: number;\n discardReported: number;\n}\n\nfunction evaluate(\n def: Definition | null,\n id: string,\n saveGuard: UseCallableDraftOptions[\"saveGuard\"],\n): string | null {\n if (!def) return null;\n // `upsert(def)` writes under `def.id`; a different id would save somewhere the author did not mean.\n if (typeof def.id === \"string\" && def.id !== id) return \"The id cannot be changed here.\";\n return saveGuard ? (saveGuard(def) ?? null) : null;\n}\n\nexport function useCallableDraft(kind: CallableDraftKind, o: UseCallableDraftOptions) {\n const client = useEmbedClient();\n const registry = useDraftRegistry();\n const qc = useQueryClient();\n const { id, saveGuard } = o;\n const autoSave = o.autoSave ?? false;\n\n // The ONE cache entry (see the docblock): the same hook `useFunctionQuery` / `useEndpointQuery`\n // are built on, so there is no second fetch path to drift.\n const query = useCallableQuery(kind, id ?? \"\", id !== null);\n\n const [def, setDefState] = useState<Definition | null>(null);\n const [loadedId, setLoadedId] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [stale, setStale] = useState(false);\n const [changedElsewhere, setChangedElsewhere] = useState(false);\n const [error, setError] = useState<unknown | null>(null);\n const [handingOver, setHandingOver] = useState(false);\n\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 refetchRef = useRef(query.refetch);\n refetchRef.current = query.refetch;\n const kindRef = useRef(kind);\n kindRef.current = kind;\n const rowRef = useRef<Row | null>(null);\n const guardRef = useRef(saveGuard);\n\n if (id === null) rowRef.current = null;\n else if (rowRef.current?.id !== id || rowRef.current.kind !== kind) {\n const row: Row = {\n id,\n kind,\n def: null,\n text: \"\",\n token: null,\n loaded: false,\n stale: false,\n changedElsewhere: 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, sent by the client captured at push time. */\n function saveRow(row: Row, payload: string, ctx: SaveCtx): Promise<void> {\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 const definition = JSON.parse(payload) as Definition;\n if (isCurrent()) {\n setSaving(true);\n setError(null);\n }\n try {\n const options = { ifUnmodifiedSince: ctx.token };\n const res =\n ctx.kind === \"function\"\n ? await ctx.client.console.functions.upsert(definition as never, options)\n : await ctx.client.console.endpoints.upsert(definition as never, options);\n // Advance the row's token BEFORE invalidating the cache, so the refetch this save causes\n // reads as \"equal\" and never as another writer. A buffer replaced meanwhile owns its token.\n if (gen === row.gen) row.token = nextSaveToken(ctx.token, res as { updatedAt?: unknown });\n invalidateSavedCallable(\n qcRef.current,\n callableListKey(ctx.kind),\n callableKey(ctx.kind, ctx.id),\n );\n if (gen !== row.gen) return;\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 const saveRowRef = useRef(saveRow);\n saveRowRef.current = saveRow;\n\n /** Replace the row's buffer and token with a fresh read (the seed, a clean external change, reload). */\n const install = useCallback(\n (row: Row, rec: { definition: Definition; updatedAt: string | null }, auth: AuthCtx) => {\n row.gen++;\n row.auth = auth;\n row.def = rec.definition;\n row.text = JSON.stringify(rec.definition);\n row.token = rec.updatedAt;\n row.saver.cancel();\n // What we just read IS the server's copy: the editor opens clean.\n row.saver.markSaved(row.text);\n row.refused = null;\n row.stale = false;\n row.lastError = null;\n row.changedElsewhere = false;\n row.loaded = true;\n setDefState(rec.definition);\n setLoadedId(row.id);\n setStale(false);\n setChangedElsewhere(false);\n setError(null);\n },\n [],\n );\n\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 id: row.id,\n definition: row.def as Definition,\n reason: outcome.status,\n });\n } catch {\n // A host callback must not break the switch.\n }\n }, []);\n\n /** Settle a row's buffer; see `use-workflow-draft.ts`'s `flushRow` (same gates). Never rejects. */\n const flushRow = useCallback(\n async (row: Row, explicit: boolean, ignoreGates = false): 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 definition has not loaded.\" };\n const text = row.text;\n if (!row.saver.isDirty(text)) return { status: \"clean\" };\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 blocked = evaluate(row.def, row.id, guardRef.current);\n if (blocked !== null) return { status: \"blocked\", reason: blocked };\n if (!explicit) {\n if (!ignoreGates && !(cur.autoSave ?? false)) {\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 auth = row.auth as AuthCtx;\n const ctx: SaveCtx = {\n client: auth.client,\n kind: row.kind,\n id: row.id,\n token: row.token,\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 // ── row switch ────────────────────────────────────────────────────────────\n // Keyed on the row identity ONLY: a rebuilt client neither reloads nor cancels the pending entry.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `id`/`kind` are the trigger; the row is read from a ref\n useEffect(() => {\n const row = rowRef.current;\n // Idempotent for a PERSISTED row: StrictMode replays this effect against a row the seed has\n // already installed (and which the seed will not install again, its token being equal), so\n // state is derived from the row instead of nulled. A genuinely new row is unloaded ⇒ null.\n if (row?.loaded) {\n setDefState(row.def);\n setLoadedId(row.id);\n setError(row.refused !== null ? row.lastError : null);\n setStale(row.stale);\n setChangedElsewhere(row.changedElsewhere);\n setSaving(row.inflight !== null);\n } else {\n setDefState(null);\n setLoadedId(null);\n setError(null);\n setStale(false);\n setChangedElsewhere(false);\n setSaving(false);\n }\n if (!row) return;\n return () => {\n // The pending entry belongs to the row that produced it: flush it there, never drop it.\n void flushRow(row, false);\n row.saver.cancel();\n };\n }, [id, kind, flushRow]);\n\n // ── seed / external change ────────────────────────────────────────────────\n const record = useMemo(\n () => (id === null ? null : callableRecord(kind, query.data)),\n [id, kind, query.data],\n );\n // biome-ignore lint/correctness/useExhaustiveDependencies: `record` is derived from `query.data`; the row is read from a ref\n useEffect(() => {\n const row = rowRef.current;\n if (!row || !record || row.handover) return;\n if (!row.loaded) {\n install(row, record, { client: clientRef.current });\n return;\n }\n // Equal ⇒ no-op: our own save (token already advanced) or an unrelated refetch.\n if (record.updatedAt === row.token) return;\n if (!row.saver.isDirty(row.text)) {\n // CLEAN: replace buffer and token together.\n install(row, record, row.auth as AuthCtx);\n } else if (!row.changedElsewhere) {\n // DIRTY: keep buffer AND the old token (the next save must 409, never overwrite).\n row.changedElsewhere = true;\n setChangedElsewhere(true);\n }\n }, [query.data, record, id, kind, install]);\n\n // ── derived ───────────────────────────────────────────────────────────────\n const row = rowRef.current;\n const ctxDiffers = (a: AuthCtx | null) => a !== null && a.client !== client;\n const loaded = row?.loaded === true && loadedId === id;\n const buffer = loaded ? def : null;\n const text = useMemo(() => (buffer ? JSON.stringify(buffer) : \"\"), [buffer]);\n const saveBlockedReason = evaluate(buffer, id ?? \"\", saveGuard);\n if (!(loaded && ctxDiffers(row?.auth ?? null))) guardRef.current = saveGuard;\n const switching = handingOver || (loaded && ctxDiffers(row?.auth ?? null));\n const canSave = loaded && saveBlockedReason === null && !saving && !switching;\n const dirty = loaded && row?.saver.isDirty(text) === true;\n const policy = useMemo(\n () => (buffer ? policyFromDefinition(buffer as CallableDefinition) : {}),\n [buffer],\n );\n const loadError =\n !loaded && id !== null\n ? (query.error ??\n (query.isSuccess && record === null\n ? new Error(\"The definition could not be read.\")\n : null))\n : null;\n\n // ── auto-save push ────────────────────────────────────────────────────────\n // biome-ignore lint/correctness/useExhaustiveDependencies: `text` is the trigger; the row is read from a ref\n useEffect(() => {\n const r = rowRef.current;\n if (!r || !loaded || r.id !== id) return;\n // While a 409 or a \"changed elsewhere\" notice is on screen a push would only retry into it.\n if (stale || changedElsewhere) return;\n if (text === r.refused) return;\n if (r.handover || !r.auth || ctxDiffers(r.auth)) return;\n const ctx: SaveCtx = {\n client: r.auth.client,\n kind: r.kind,\n id: r.id,\n token: r.token,\n explicit: false,\n };\n r.saver.push(text, autoSave, canSave, ctx);\n }, [loaded, id, text, stale, changedElsewhere, autoSave, canSave, client]);\n\n // ── context handover ──────────────────────────────────────────────────────\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 !== id || r.handover) return;\n if (!ctxDiffers(r.auth)) return;\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 res = await refetchRef.current();\n if (rowRef.current !== r) return;\n const rec = res.isError ? null : callableRecord(r.kind, res.data);\n if (!rec) throw res.error ?? new Error(\"The definition could not be read.\");\n install(r, rec, { client: clientRef.current });\n } catch (e) {\n if (rowRef.current === r) {\n r.loaded = false;\n setLoadedId(null);\n setError(e);\n }\n } finally {\n r.handover = false;\n if (rowRef.current === r) setHandingOver(false);\n }\n })();\n }, [loaded, id, client, handingOver, flushRow, reportDiscard, install]);\n\n // ── flush on hide / registry ──────────────────────────────────────────────\n // biome-ignore lint/correctness/useExhaustiveDependencies: `id`/`kind` are 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` saves regardless of `autoSave` and the busy probe, and reports what it dropped.\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 }, [id, kind, registry, flushRow, reportDiscard]);\n\n // ── stable actions ────────────────────────────────────────────────────────\n const setDefinition = useCallback((next: Definition | ((prev: Definition) => Definition)) => {\n const r = rowRef.current;\n if (!r || !r.loaded || !r.def) return;\n const value = typeof next === \"function\" ? next(r.def) : next;\n r.def = value;\n r.text = JSON.stringify(value);\n setDefState(value);\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: Definition): Promise<FlushOutcome> => {\n setDefinition(next);\n return flush();\n },\n [setDefinition, flush],\n );\n // Reads the CURRENT buffer through the row (a ref), never a render's closure: pending editor\n // edits ride along. Stable, because `useWorkspace` drops functions from its publish key.\n const onPolicyChange = useCallback(\n (next: CallablePolicy) => {\n const r = rowRef.current;\n if (!r || !r.loaded || !r.def) return;\n void commit(definitionWithPolicy(r.def as CallableDefinition, next) as Definition);\n },\n [commit],\n );\n const reload = useCallback(async () => {\n const r = rowRef.current;\n if (!r) return;\n try {\n const res = await refetchRef.current();\n if (rowRef.current !== r) return;\n const rec = res.isError ? null : callableRecord(r.kind, res.data);\n if (!rec) {\n setError(res.error ?? new Error(\"The definition could not be read.\"));\n return;\n }\n install(r, rec, { client: clientRef.current });\n } catch (e) {\n if (rowRef.current === r) setError(e);\n }\n }, [install]);\n\n const draft: CallableDraft = {\n definition: buffer,\n setDefinition,\n policy,\n onPolicyChange,\n loading: id !== null && !loaded && loadError === null,\n loadError,\n dirty,\n saving,\n canSave,\n saveBlockedReason,\n stale,\n changedElsewhere,\n error,\n saveNow,\n flush,\n flushThen,\n commit,\n reload,\n };\n return draft;\n}\n\n/** {@link useCallableDraft} bound to a Function. */\nexport function useFunctionDraft(o: UseCallableDraftOptions): CallableDraft {\n return useCallableDraft(\"function\", o);\n}\n\n/** {@link useCallableDraft} bound to an Endpoint. */\nexport function useEndpointDraft(o: UseCallableDraftOptions): CallableDraft {\n return useCallableDraft(\"endpoint\", o);\n}\n"],"names":["evaluate","def","id","saveGuard","useCallableDraft","kind","o","client","useEmbedClient","registry","useDraftRegistry","qc","useQueryClient","autoSave","query","useCallableQuery","setDefState","useState","loadedId","setLoadedId","saving","setSaving","stale","setStale","changedElsewhere","setChangedElsewhere","error","setError","handingOver","setHandingOver","optsRef","useRef","clientRef","qcRef","refetchRef","kindRef","rowRef","guardRef","_a","row","createAutoSaver","payload","ctx","saveRow","cur","isCurrent","gen","p","definition","options","res","nextSaveToken","invalidateSavedCallable","callableListKey","callableKey","e","isStaleWrite","clear","saveRowRef","install","useCallback","rec","auth","reportDiscard","outcome","_b","flushRow","explicit","ignoreGates","drained","text","blocked","useEffect","record","useMemo","callableRecord","ctxDiffers","a","loaded","buffer","saveBlockedReason","switching","canSave","dirty","policy","policyFromDefinition","loadError","r","w","d","offHide","registerFlushOnHide","unregister","setDefinition","next","value","flush","saveNow","flushThen","action","commit","onPolicyChange","definitionWithPolicy","reload","useFunctionDraft","useEndpointDraft"],"mappings":"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"}
@@ -0,0 +1,338 @@
1
+ import { useQueryClient as ve } from "@tanstack/react-query";
2
+ import { useState as w, useRef as D, useCallback as S, useEffect as M, useMemo as he } from "react";
3
+ import { u as pe } from "./client-context-CX61Bgw-.js";
4
+ import { u as ke } from "./draft-registry-YF0tqNN0.js";
5
+ const ge = 1200;
6
+ function ye(l) {
7
+ const a = l.delayMs ?? ge, d = l.setTimer ?? ((u, E) => setTimeout(u, E)), p = l.clearTimer ?? ((u) => clearTimeout(u));
8
+ let r = null, c = null, f = null;
9
+ function k() {
10
+ c !== null && (p(c), c = null);
11
+ }
12
+ function j() {
13
+ c = null;
14
+ const u = r;
15
+ if (r = null, !u || !u.enabled || !u.canSave || u.payload === f) return Promise.resolve();
16
+ const E = l.save(u.payload, u.ctx);
17
+ return Promise.resolve(E);
18
+ }
19
+ return {
20
+ push(u, E, O, B) {
21
+ r = { payload: u, enabled: E, canSave: O, ctx: B }, k(), c = d(() => {
22
+ j().catch(() => {
23
+ });
24
+ }, a);
25
+ },
26
+ flush() {
27
+ return k(), j();
28
+ },
29
+ cancel() {
30
+ k(), r = null;
31
+ },
32
+ markSaved(u) {
33
+ f = u;
34
+ },
35
+ isDirty(u) {
36
+ return u !== f;
37
+ }
38
+ };
39
+ }
40
+ function me(l, a, d) {
41
+ const p = () => {
42
+ d();
43
+ }, r = () => {
44
+ a.visibilityState === "hidden" && d();
45
+ };
46
+ return l.addEventListener("beforeunload", p), a.addEventListener("visibilitychange", r), () => {
47
+ l.removeEventListener("beforeunload", p), a.removeEventListener("visibilitychange", r);
48
+ };
49
+ }
50
+ function be(l) {
51
+ if (typeof l != "object" || l === null) return !1;
52
+ const a = l.code;
53
+ return a === "workflow_stale" || a === "function_stale" || a === "endpoint_stale";
54
+ }
55
+ function U(l, a) {
56
+ return typeof a.updatedAt == "string" ? a.updatedAt : null;
57
+ }
58
+ function q(l, a, d) {
59
+ return l ? l.client !== a ? !0 : (l.project || null) !== (d || null) && !!l.project : !1;
60
+ }
61
+ const X = "No project selected: a workflow can only be saved into a project.";
62
+ function ne(l, a, d, p) {
63
+ let r = null, c = null;
64
+ try {
65
+ r = JSON.parse(l);
66
+ } catch (k) {
67
+ c = k instanceof Error ? k.message : String(k);
68
+ }
69
+ let f = null;
70
+ return d ? c !== null ? f = `Invalid JSON: ${c}` : typeof r != "object" || r === null || Array.isArray(r) ? f = "A workflow definition must be a JSON object." : typeof r.id == "string" && r.id !== a ? f = "The workflow id cannot be changed here." : p && (f = p(r) ?? null) : f = X, { parsed: r, syntaxError: c, blocked: f };
71
+ }
72
+ function xe(l) {
73
+ var re;
74
+ const a = pe(), d = ke(), p = ve(), { workflowId: r } = l, { project: c, saveGuard: f } = l, k = l.autoSave ?? !0, [j, u] = w(""), [E, O] = w(null), [B, Q] = w(null), [Y, V] = w(!1), [W, P] = w(!1), [se, T] = w(null), [Z, C] = w(!1), y = D(l);
75
+ y.current = l;
76
+ const N = D(a);
77
+ N.current = a;
78
+ const G = D(p);
79
+ G.current = p;
80
+ const o = D(null), ee = D(f);
81
+ if (r === null) o.current = null;
82
+ else if (((re = o.current) == null ? void 0 : re.id) !== r) {
83
+ const e = {
84
+ id: r,
85
+ text: "",
86
+ token: null,
87
+ // a token from another row would 409 against this one
88
+ loaded: !1,
89
+ stale: !1,
90
+ refused: null,
91
+ lastError: null,
92
+ inflight: null,
93
+ saver: void 0,
94
+ auth: null,
95
+ handover: !1,
96
+ replacing: !1,
97
+ gen: 0,
98
+ discardReported: -1
99
+ };
100
+ e.saver = ye({
101
+ save: (t, n) => F(e, t, n)
102
+ }), o.current = e;
103
+ }
104
+ const le = D(F);
105
+ le.current = F;
106
+ function F(e, t, n) {
107
+ if (e.inflight) return Promise.resolve();
108
+ const s = y.current;
109
+ if (!n.explicit && s.canAutoSave && !s.canAutoSave()) return Promise.resolve();
110
+ const i = () => o.current === e, g = e.gen, A = (async () => {
111
+ if (!n.project) throw new Error(X);
112
+ const z = JSON.parse(t);
113
+ i() && (V(!0), T(null));
114
+ try {
115
+ const J = await n.client.workflows.update(n.id, z, {
116
+ project: n.project,
117
+ ifUnmodifiedSince: n.token
118
+ });
119
+ if (G.current.invalidateQueries({ queryKey: ["workflows"] }), g !== e.gen) return;
120
+ e.token = U(n.token, J), e.saver.markSaved(t), e.refused = null;
121
+ } catch (J) {
122
+ throw g === e.gen && (e.refused = t, e.lastError = J, e.saver.cancel(), be(J) && (e.stale = !0), i() && (e.stale && P(!0), T(J))), J;
123
+ }
124
+ })();
125
+ e.inflight = A;
126
+ const I = () => {
127
+ e.inflight === A && (e.inflight = null), i() && V(!1);
128
+ };
129
+ return A.then(I, I), A;
130
+ }
131
+ const L = S((e, t) => {
132
+ var n, s;
133
+ if (!(t.status !== "blocked" && t.status !== "failed") && e.discardReported !== e.gen) {
134
+ e.discardReported = e.gen;
135
+ try {
136
+ (s = (n = y.current).onDiscard) == null || s.call(n, {
137
+ workflowId: e.id,
138
+ text: e.text,
139
+ reason: t.status
140
+ });
141
+ } catch {
142
+ }
143
+ }
144
+ }, []), m = S(
145
+ async (e, t, n = !1) => {
146
+ try {
147
+ if (e.replacing) return { status: "blocked", reason: "A version is being restored." };
148
+ let s = null;
149
+ for (; e.inflight; )
150
+ try {
151
+ await e.inflight;
152
+ } catch (z) {
153
+ s = { error: z };
154
+ }
155
+ if (e.replacing) return { status: "blocked", reason: "A version is being restored." };
156
+ if (!e.loaded) return { status: "blocked", reason: "The workflow has not loaded." };
157
+ const i = e.text;
158
+ if (!e.saver.isDirty(i)) return { status: "clean" };
159
+ if (s && e.refused === i) return { status: "failed", error: s.error };
160
+ if (e.stale) return { status: "failed", error: e.lastError };
161
+ const g = y.current, x = e.auth;
162
+ !x.project && x.client === N.current && (x.project = g.project);
163
+ const { blocked: A } = ne(i, e.id, x.project, ee.current);
164
+ if (A !== null) return { status: "blocked", reason: A };
165
+ if (!t) {
166
+ if (!n && !(g.autoSave ?? !0))
167
+ return { status: "blocked", reason: "Auto-save is off." };
168
+ if (!n && g.canAutoSave && !g.canAutoSave())
169
+ return { status: "blocked", reason: "The editor is busy." };
170
+ }
171
+ const I = {
172
+ client: x.client,
173
+ project: x.project,
174
+ id: e.id,
175
+ token: e.token,
176
+ // `saveRow` re-checks the busy probe for automatic saves; a gate-ignoring flush is explicit.
177
+ explicit: t || n
178
+ };
179
+ return e.saver.push(i, !0, !0, I), await e.saver.flush(), { status: "saved" };
180
+ } catch (s) {
181
+ return { status: "failed", error: s };
182
+ }
183
+ },
184
+ []
185
+ );
186
+ M(() => {
187
+ const e = o.current;
188
+ if (!e) return;
189
+ let t = !1;
190
+ u(""), O(null), Q(null), T(null), P(!1), V(!1);
191
+ const n = { client: N.current, project: y.current.project };
192
+ return n.client.workflows.get(e.id).then(
193
+ (s) => {
194
+ if (t) return;
195
+ e.auth = n;
196
+ const i = JSON.stringify(s.workflow, null, 2);
197
+ e.text = i, e.token = U(null, s), e.saver.markSaved(i), e.loaded = !0, u(i), O(e.id);
198
+ },
199
+ (s) => {
200
+ t || Q(s);
201
+ }
202
+ ), () => {
203
+ t = !0, m(e, !1), e.saver.cancel();
204
+ };
205
+ }, [r, m]);
206
+ const v = o.current, h = (v == null ? void 0 : v.loaded) === !0 && E === r, _ = h ? j : "", b = he(
207
+ () => h && r !== null ? ne(_, r, c, f) : null,
208
+ [h, _, r, c, f]
209
+ ), te = b ? b.blocked : !c && r !== null ? X : null;
210
+ h && q((v == null ? void 0 : v.auth) ?? null, a, c) || (ee.current = f);
211
+ const ae = Z || h && q((v == null ? void 0 : v.auth) ?? null, a, c), H = h && te === null && !Y && !ae, ue = h && (v == null ? void 0 : v.saver.isDirty(_)) === !0, oe = b && b.parsed !== null && l.publishBlockedReason ? l.publishBlockedReason(b.parsed) : null;
212
+ M(() => {
213
+ const e = o.current;
214
+ if (!e || !h || e.id !== r || W || j === e.refused || e.handover || !e.auth || q(e.auth, N.current, y.current.project))
215
+ return;
216
+ e.auth.project || (e.auth.project = y.current.project);
217
+ const t = {
218
+ client: e.auth.client,
219
+ project: e.auth.project,
220
+ id: e.id,
221
+ token: e.token,
222
+ explicit: !1
223
+ };
224
+ e.saver.push(j, k, H, t);
225
+ }, [h, r, j, W, k, H]), M(() => {
226
+ const e = o.current;
227
+ if (!(!e || !h || e.id !== r || e.handover)) {
228
+ if (!q(e.auth, a, c)) {
229
+ e.auth && !e.auth.project && (e.auth.project = c);
230
+ return;
231
+ }
232
+ e.handover = !0, C(!0), (async () => {
233
+ try {
234
+ if (L(e, await m(e, !1, !0)), o.current !== e) return;
235
+ e.saver.cancel();
236
+ const t = { client: N.current, project: y.current.project }, n = await t.client.workflows.get(e.id);
237
+ if (o.current !== e) return;
238
+ const s = JSON.stringify(n.workflow, null, 2);
239
+ e.gen++, e.auth = t, e.text = s, e.token = U(null, n), e.saver.markSaved(s), e.refused = null, e.stale = !1, e.lastError = null, u(s), P(!1), T(null);
240
+ } catch (t) {
241
+ o.current === e && (e.loaded = !1, O(null), Q(t));
242
+ } finally {
243
+ e.handover = !1, o.current === e && C(!1);
244
+ }
245
+ })();
246
+ }
247
+ }, [h, r, a, c, Z, m, L]), M(() => {
248
+ const e = o.current;
249
+ if (!e) return;
250
+ const t = globalThis.window, n = globalThis.document, s = t && n ? me(t, n, () => {
251
+ m(e, !1);
252
+ }) : void 0, i = d == null ? void 0 : d.register(async () => {
253
+ const g = await m(e, !1, !0);
254
+ return L(e, g), g;
255
+ });
256
+ return () => {
257
+ s == null || s(), i == null || i();
258
+ };
259
+ }, [r, d, m, L]);
260
+ const K = S((e) => {
261
+ const t = o.current;
262
+ !t || !t.loaded || t.replacing || (t.text = e, u(e));
263
+ }, []), R = S(() => {
264
+ const e = o.current;
265
+ return e ? m(e, !0) : Promise.resolve({ status: "clean" });
266
+ }, [m]), ce = S(() => {
267
+ R();
268
+ }, [R]), ie = S(
269
+ async (e) => {
270
+ const t = await R();
271
+ return t.status === "clean" || t.status === "saved" ? { ok: !0, value: await e() } : { ok: !1, outcome: t };
272
+ },
273
+ [R]
274
+ ), fe = S(
275
+ (e) => (K(e), R()),
276
+ [K, R]
277
+ ), $ = S(async () => {
278
+ const e = o.current;
279
+ if (e)
280
+ try {
281
+ const t = { client: N.current, project: y.current.project }, n = await t.client.workflows.get(e.id);
282
+ if (o.current !== e) return;
283
+ e.auth = t;
284
+ const s = JSON.stringify(n.workflow, null, 2);
285
+ e.saver.cancel(), e.gen++, e.text = s, e.token = U(null, n), e.saver.markSaved(s), e.refused = null, e.stale = !1, e.lastError = null, e.loaded = !0, u(s), O(e.id), P(!1), T(null);
286
+ } catch (t) {
287
+ o.current === e && T(t);
288
+ }
289
+ }, []), de = S(
290
+ async (e) => {
291
+ const t = o.current;
292
+ if (!(!t || t.replacing)) {
293
+ t.replacing = !0, t.handover = !0, t.saver.cancel(), C(!0);
294
+ try {
295
+ for (; t.inflight; )
296
+ try {
297
+ await t.inflight;
298
+ } catch {
299
+ }
300
+ await e(), o.current === t && (t.stale = !0, P(!0), await $());
301
+ } finally {
302
+ t.replacing = !1, t.handover = !1, o.current === t && C(!1);
303
+ }
304
+ }
305
+ },
306
+ [$]
307
+ );
308
+ return {
309
+ json: _,
310
+ setJson: K,
311
+ parsed: b ? b.parsed : null,
312
+ syntaxError: b ? b.syntaxError : null,
313
+ loading: r !== null && !h && B === null,
314
+ loadError: B,
315
+ dirty: ue,
316
+ saving: Y,
317
+ canSave: H,
318
+ saveBlockedReason: te,
319
+ stale: W,
320
+ error: se,
321
+ publishBlockedReason: oe,
322
+ saveNow: ce,
323
+ flush: R,
324
+ flushThen: ie,
325
+ commit: fe,
326
+ reload: $,
327
+ discardThen: de
328
+ };
329
+ }
330
+ export {
331
+ ge as A,
332
+ ye as c,
333
+ be as i,
334
+ U as n,
335
+ me as r,
336
+ xe as u
337
+ };
338
+ //# sourceMappingURL=use-workflow-draft-l3P5KZzw.js.map