@w6w/embed 0.2.11 → 0.2.20

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 (133) 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/connections.d.ts +1 -0
  83. package/dist/repos/constants.d.ts +12 -0
  84. package/dist/repos/data-stores.d.ts +84 -0
  85. package/dist/repos/history.d.ts +9 -0
  86. package/dist/repos/index.d.ts +2 -0
  87. package/dist/repos/spec-export.d.ts +3 -3
  88. package/dist/repos/spec-import.d.ts +30 -1
  89. package/dist/repos/step-tests.d.ts +2 -0
  90. package/dist/repos/webhook-listen.d.ts +35 -0
  91. package/dist/repos/workflows.d.ts +105 -2
  92. package/dist/repos.mjs +370 -328
  93. package/dist/repos.mjs.map +1 -1
  94. package/dist/setup-incomplete-BHs3Lpfy.js +199 -0
  95. package/dist/setup-incomplete-BHs3Lpfy.js.map +1 -0
  96. package/dist/{spec-export-CBbloO_n.js → spec-export-DGy5ijps.js} +72 -59
  97. package/dist/spec-export-DGy5ijps.js.map +1 -0
  98. package/dist/spec-import-SWOonsNr.js +116 -0
  99. package/dist/spec-import-SWOonsNr.js.map +1 -0
  100. package/dist/{step-tests-CivICmTq.js → step-tests-B4E4W3ly.js} +8 -7
  101. package/dist/step-tests-B4E4W3ly.js.map +1 -0
  102. package/dist/{use-callable-draft-DsALnSfF.js → use-callable-draft-DCauHt5o.js} +2 -2
  103. package/dist/{use-callable-draft-DsALnSfF.js.map → use-callable-draft-DCauHt5o.js.map} +1 -1
  104. package/dist/use-workflow-draft-l3P5KZzw.js +338 -0
  105. package/dist/use-workflow-draft-l3P5KZzw.js.map +1 -0
  106. package/dist/workflows-BZN4Hyz1.js +623 -0
  107. package/dist/workflows-BZN4Hyz1.js.map +1 -0
  108. package/package.json +4 -3
  109. package/src/styles.css +153 -1
  110. package/dist/ConfirmModal-BE-SdX-O.js +0 -185
  111. package/dist/ConfirmModal-BE-SdX-O.js.map +0 -1
  112. package/dist/ImportSpecModal-B7qd1Km2.js +0 -136
  113. package/dist/ImportSpecModal-B7qd1Km2.js.map +0 -1
  114. package/dist/WorkflowEditor-CwvmAGp2.js +0 -925
  115. package/dist/WorkflowEditor-CwvmAGp2.js.map +0 -1
  116. package/dist/apps-CboASBKr.js +0 -77
  117. package/dist/apps-CboASBKr.js.map +0 -1
  118. package/dist/coerce-input-DttlcSdO.js.map +0 -1
  119. package/dist/document-sources-UCwe8x77.js +0 -91
  120. package/dist/document-sources-UCwe8x77.js.map +0 -1
  121. package/dist/editor-apps-BUZWld-m.js +0 -95
  122. package/dist/editor-apps-BUZWld-m.js.map +0 -1
  123. package/dist/editor-tools-B_Lfx3kw.js +0 -1279
  124. package/dist/editor-tools-B_Lfx3kw.js.map +0 -1
  125. package/dist/rail-settings-XYGToh00.js.map +0 -1
  126. package/dist/spec-export-CBbloO_n.js.map +0 -1
  127. package/dist/spec-import-CW6R_Wk1.js +0 -469
  128. package/dist/spec-import-CW6R_Wk1.js.map +0 -1
  129. package/dist/step-tests-CivICmTq.js.map +0 -1
  130. package/dist/trigger-preview-DKxfjzUY.js +0 -145
  131. package/dist/trigger-preview-DKxfjzUY.js.map +0 -1
  132. package/dist/use-workflow-draft-DzAyUeA6.js +0 -316
  133. package/dist/use-workflow-draft-DzAyUeA6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/embed-status.ts","../src/W6WEmbedProvider.tsx"],"sourcesContent":["/**\n * The embed's public session status, as a pure total function over the `@w6w/react` session\n * store's status plus whether a terminal partner-token 401 has been seen. No React here, so\n * `node:test` covers it exhaustively.\n */\nimport type { W6WSessionStatus } from \"@w6w/react\";\n\nexport type EmbedStatusValue = \"connecting\" | \"ready\" | \"error\";\n\nexport interface EmbedStatus {\n status: EmbedStatusValue;\n /** `EmbedAuthError` for a terminal 401, else the session store's own error; `null` otherwise. */\n error: unknown;\n}\n\n/**\n * The terminal partner-token 401: the host's `getSession` minted a token the API refused, and a\n * renewal did not fix it. `cause` is the SDK `ApiError`.\n */\nexport class EmbedAuthError extends Error {\n constructor(cause: unknown) {\n super(\"The w6w API rejected the embed session token (401).\", { cause });\n this.name = \"EmbedAuthError\";\n }\n}\n\n/**\n * `loading -> connecting`, `ready|renewing -> ready`, `expired -> connecting` (the provider\n * re-acquires), `error -> error`. A seen 401 wins over everything: it is `error` with the\n * `EmbedAuthError`, whatever the store currently says.\n */\nexport function deriveEmbedStatus(\n sessionStatus: W6WSessionStatus,\n sessionError: unknown,\n authError: EmbedAuthError | null,\n): EmbedStatus {\n if (authError) return { status: \"error\", error: authError };\n switch (sessionStatus) {\n case \"ready\":\n case \"renewing\":\n return { status: \"ready\", error: null };\n case \"error\":\n return { status: \"error\", error: sessionError };\n case \"loading\":\n case \"expired\":\n return { status: \"connecting\", error: null };\n }\n}\n","import { QueryClient, QueryClientProvider, useQuery } from \"@tanstack/react-query\";\n/**\n * `<W6WEmbedProvider>` — everything an embedded w6w surface needs from a host, behind one\n * element: an identity-scoped `QueryClient`, a `@w6w/react` `<W6WProvider>` (client, session\n * renewal, per-request token) and `@w6w/ui`'s `<W6WUIProvider>` fed by `createW6WUiAdapter`.\n * It composes the three public pieces exactly as `@w6w/react`'s README bridge does: it never\n * spreads or patches the adapter, never reaches into `client.console`, and keeps no session\n * store of its own (`getSession` goes straight to `<W6WProvider>`).\n *\n * **Identity cache.** The `QueryClient` is owned here, not borrowed from the host, and it is a\n * NEW instance whenever `identityKey` changes — so a query cached while signed in as account A\n * is unreachable once the host switches to B. A new client alone is NOT enough: a mounted\n * `useQuery` keeps the `QueryObserver` it was born with, and that observer is bound to the client\n * it was created against, so a child that survives the swap would keep rendering A's cache. The\n * subtree is therefore also re-mounted (`key`), which drops every observer and every piece of\n * child state from the previous identity. The client, the `key` and the tuple handed to\n * `<W6WProvider>` all change in ONE state update: there is no render in which B's tree reads A's\n * cache. The old client is cleared once it is no longer mounted.\n *\n * **Identity switch (flush first).** A draft with a pending save must be written into the account\n * that produced the edit. When `identityKey` changes this provider therefore HOLDS the applied\n * tuple — `identityKey`, `apiOrigin`, `project`, `getSession`, the client — and keeps handing the\n * OLD one to `<W6WProvider>`, awaits `registry.flushAll()` (bounded, never rejects), and only then\n * applies the new tuple in a single update. `<W6WProvider>` swaps its client synchronously on a\n * new `identityKey` and detaches the old session store, so the flush must finish first; and the\n * new `getSession` is NOT forwarded early: `<W6WProvider>` reaches it through the stable wrapper\n * below, whose target is swapped in the same update as the tuple (forwarded early, the new\n * account's token would be minted on the old client). A flush that fails or times out still lets\n * the switch proceed (Q6) and is never retried with the new identity. A token rotation under an\n * unchanged `identityKey` is not a switch: it is forwarded at once, no hold.\n *\n * **No identity.** `identityKey={null}` mounts nothing but `fallback`: no `<W6WProvider>`, no\n * `QueryClientProvider`, no children — so no `getSession` call and no request. `null -> \"A\"`\n * applies at once (nothing can be pending); `\"A\" -> null` takes the SAME flush-first path as any\n * other switch before the fallback renders.\n *\n * **Default project.** `project={{ use: \"default\" }}` resolves ONCE per applied identity, through\n * one `client.projects.list()` query inside the identity-scoped `QueryClient`, to the row with\n * `isDefault === true` — never `list[0]`, never a fallback. `<W6WProvider>` gets no project until\n * it resolves; the client rebuild that follows reuses the cached answer (the query is keyed\n * without the client and lives in a component that stays mounted, so it is not re-issued).\n *\n * **Status.** `useEmbedStatus()` is `deriveEmbedStatus` over the `@w6w/react` session store plus a\n * terminal 401 seen through the forwarded `onUnauthorized`.\n */\nimport {\n W6WProvider,\n type W6WSession,\n createW6WUiAdapter,\n useW6WClient,\n useW6WSession,\n} from \"@w6w/react\";\nimport type { ApiError, FetchLike } from \"@w6w/sdk\";\nimport { type ThemeMode, type W6WApi, W6WUIProvider } from \"@w6w/ui\";\nimport {\n type ReactNode,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { DraftRegistryProvider, EmbedClientProvider, createDraftRegistry } from \"./draft/index.ts\";\nimport { EmbedAuthError, type EmbedStatus, deriveEmbedStatus } from \"./embed-status.ts\";\nimport {\n EmbedProjectContext,\n type EmbedProjectState,\n useProvidedProject,\n} from \"./project-context.ts\";\nimport { updateConnection } from \"./repos/connections.ts\";\nimport { withCredentialMethods } from \"./repos/credentials.ts\";\nimport { resolveActionFields } from \"./repos/fields.ts\";\nimport { listModelRoutes } from \"./repos/models.ts\";\nimport { EmbedThemeContext } from \"./theme-context.ts\";\n\nexport interface W6WEmbedProviderProps {\n /** The w6w API origin, e.g. `\"https://api.w6w.io\"`. */\n apiOrigin: string;\n /**\n * Mints a session for the CURRENT host user (`{token, expiresAt, expiresIn?}`). Always the\n * latest callback; the provider caches and renews the result.\n */\n getSession: () => Promise<W6WSession>;\n /**\n * Which signed-in identity this provider speaks for (e.g. `${userId}:${accountId}`). Changing it\n * rebuilds the API client AND replaces the `QueryClient`, so nothing cached for the previous\n * identity is ever shown under the new one.\n */\n identityKey: string | null;\n /**\n * Rendered instead of the embed while `identityKey` is `null` (no signed-in user). Nothing of\n * the embed mounts then: no `getSession` call, no request. Default `null`.\n */\n fallback?: ReactNode;\n /**\n * Start acquiring a session on mount. Without it the session is acquired lazily, by the first\n * request a mounted surface makes, and `useEmbedStatus()` stays `\"connecting\"` until then.\n */\n eager?: boolean;\n /** Force the `@w6w/ui` theme to follow the host's. Omit to resolve it the library's default way. */\n theme?: ThemeMode;\n /** Gates every read hook while `false` (default `true`) — e.g. until the host has a user. */\n ready?: boolean;\n /**\n * Default project for project-scoped operations: a project id, or `{ use: \"default\" }` to use\n * the account's default project (resolved once per identity; see `useEmbedProject()`).\n */\n project?: string | { use: \"default\" };\n /** Transport override (tests, request recording). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n children: ReactNode;\n}\n\nfunction makeQueryClient(): QueryClient {\n // Same defaults as Studio's own client (`studio/src/main.tsx`).\n return new QueryClient({\n defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },\n });\n}\n\n/** The part of the tuple that changes only when a switch has been APPLIED (after the flush). */\ninterface Applied {\n identityKey: string | null;\n queryClient: QueryClient;\n}\n\n/** What `<W6WProvider>` is fed while no switch is in progress; frozen at the last such render. */\ninterface Forwarded {\n apiOrigin: string;\n project: string | { use: \"default\" } | undefined;\n getSession: () => Promise<W6WSession>;\n}\n\n/** Hands the SDK client of the nearest `<W6WProvider>` to `useWorkflowDraft`. */\nfunction ClientBridge({ children }: { children: ReactNode }) {\n return <EmbedClientProvider client={useW6WClient()}>{children}</EmbedClientProvider>;\n}\n\n/** The README bridge: `useW6WClient` -> `createW6WUiAdapter` -> `<W6WUIProvider>`. */\nfunction UiBridge({ theme, children }: { theme?: ThemeMode; children: ReactNode }) {\n const client = useW6WClient();\n const provided = useProvidedProject();\n // `createW6WUiAdapter` has no `updateConnection` or `listModelRoutes`; keep them if it ever\n // gains them, else supply the embed repo's own so the ui Edit modal can save and the\n // `@w6w/model` node's Route field offers the project's routes (not free text) in any host.\n const api = useMemo(() => {\n const adapter: W6WApi = createW6WUiAdapter(client);\n if (typeof adapter.updateConnection !== \"function\") {\n adapter.updateConnection = (id, body) => updateConnection(client, id, body);\n }\n withCredentialMethods(adapter, client);\n if (typeof adapter.listModelRoutes !== \"function\") {\n adapter.listModelRoutes = async (editorProject) => {\n const project = editorProject || provided;\n if (!project) return [];\n const routes = await listModelRoutes(client, project);\n return routes.map((r) => ({ name: r.name, model: r.modelId, status: r.status }));\n };\n }\n if (typeof adapter.resolveActionFields !== \"function\") {\n adapter.resolveActionFields = (appId, actionKey, body, opts) =>\n resolveActionFields(client, appId, actionKey, body, opts);\n }\n return adapter;\n }, [client, provided]);\n return (\n <W6WUIProvider api={api} theme={theme}>\n <EmbedThemeContext.Provider value={theme}>{children}</EmbedThemeContext.Provider>\n </W6WUIProvider>\n );\n}\n\nconst EmbedStatusContext = createContext<EmbedStatus>({ status: \"connecting\", error: null });\n\n/** The session status of the nearest `<W6WEmbedProvider>`. See `deriveEmbedStatus`. */\nexport function useEmbedStatus(): EmbedStatus {\n return useContext(EmbedStatusContext);\n}\n\n/** Publishes `useEmbedStatus()`, and starts the session on mount when `eager`. */\nfunction StatusBridge({\n eager,\n authError,\n onAcquire,\n children,\n}: {\n eager: boolean;\n authError: EmbedAuthError | null;\n onAcquire: () => void;\n children: ReactNode;\n}) {\n const { status, error, refresh } = useW6WSession();\n // `refresh` is per session store, so a rebuilt client (project resolved) is started again.\n useEffect(() => {\n if (eager) void refresh();\n }, [eager, refresh]);\n useEffect(() => {\n if (status === \"loading\" || status === \"renewing\") onAcquire();\n }, [status, onAcquire]);\n const value = useMemo(\n () => deriveEmbedStatus(status, error, authError),\n [status, error, authError],\n );\n return <EmbedStatusContext.Provider value={value}>{children}</EmbedStatusContext.Provider>;\n}\n\n/**\n * Resolves `{use:\"default\"}`. Lives INSIDE `<W6WProvider>` (it needs the client) but at a fixed\n * slot, so it survives the client rebuild that follows resolution; the query key carries no\n * client, so nothing refetches.\n */\nfunction DefaultProjectResolver({\n identityKey,\n enabled,\n onState,\n}: {\n identityKey: string;\n enabled: boolean;\n onState: (key: string, state: EmbedProjectState) => void;\n}) {\n const client = useW6WClient();\n const q = useQuery({\n queryKey: [\"w6w-embed\", \"default-project\"],\n enabled,\n queryFn: async () => {\n const rows = await client.projects.list();\n const home = rows.find((p) => p.isDefault === true);\n if (!home) throw new Error(\"The account has no default project.\");\n return home.id;\n },\n });\n const { isError, error, data } = q;\n const state = useMemo<EmbedProjectState>(\n () =>\n isError\n ? { project: null, status: \"error\", error }\n : data !== undefined\n ? { project: data, status: \"ready\" }\n : { project: null, status: \"resolving\" },\n [isError, error, data],\n );\n useEffect(() => {\n onState(identityKey, state);\n }, [identityKey, state, onState]);\n return null;\n}\n\nexport function W6WEmbedProvider({\n apiOrigin,\n getSession,\n identityKey,\n fallback = null,\n eager = false,\n theme,\n ready,\n project,\n fetch,\n children,\n}: W6WEmbedProviderProps): ReactNode {\n const [registry] = useState(createDraftRegistry);\n const [applied, setApplied] = useState<Applied>(() => ({\n identityKey,\n queryClient: makeQueryClient(),\n }));\n\n // While `identityKey` differs from the applied one a switch is pending: everything below keeps\n // using the values frozen at the last render that was not pending. Otherwise props flow straight\n // through (project / origin changes, token rotation). Idempotent latest-value writes, the same\n // pattern `<W6WProvider>` uses for its own refs.\n const switching = identityKey !== applied.identityKey;\n const forwardedRef = useRef<Forwarded>({ apiOrigin, project, getSession });\n if (!switching) forwardedRef.current = { apiOrigin, project, getSession };\n const forwarded = forwardedRef.current;\n\n // The stable `getSession` handed to `<W6WProvider>`: reads whatever is applied RIGHT NOW. Its\n // target changes in the same render that applies the new tuple, never before the flush settles.\n const sessionRef = useRef(forwarded.getSession);\n sessionRef.current = forwarded.getSession;\n const [stableGetSession] = useState(() => () => sessionRef.current());\n\n useEffect(() => {\n if (identityKey === applied.identityKey) return;\n let cancelled = false;\n const apply = () => {\n if (cancelled) return;\n setApplied({ identityKey, queryClient: makeQueryClient() });\n };\n // Nothing is mounted under a null identity, so nothing can be pending: apply at once.\n if (applied.identityKey === null) {\n apply();\n return () => {\n cancelled = true;\n };\n }\n // `flushAll` never rejects and is bounded; the switch proceeds either way (Q6).\n registry.flushAll().then(apply, apply);\n return () => {\n cancelled = true;\n };\n }, [identityKey, applied.identityKey, registry]);\n\n // The replaced client is dropped once nothing mounted reads it.\n const queryClient = applied.queryClient;\n useEffect(() => () => queryClient.clear(), [queryClient]);\n\n // Default-project resolution, tagged with the identity it was resolved for: a result for A is\n // never read once B is applied.\n const [resolved, setResolved] = useState<{ key: string; state: EmbedProjectState } | null>(null);\n const onResolved = useCallback((key: string, state: EmbedProjectState) => {\n setResolved((prev) =>\n prev &&\n prev.key === key &&\n prev.state.project === state.project &&\n prev.state.status === state.status &&\n prev.state.error === state.error\n ? prev\n : { key, state },\n );\n }, []);\n\n // The terminal partner-token 401, tagged with its identity; cleared when the next acquisition\n // starts (StatusBridge) or the identity changes (the tag no longer matches).\n const [authFailure, setAuthFailure] = useState<{\n key: QueryClient;\n error: EmbedAuthError;\n } | null>(null);\n // The handler is bound to the per-mount client it was created for: a late 401 from a previous\n // `<W6WProvider>` is tagged with ITS client, never the one applied by the time it fires — even\n // when the identity key came back (A -> B -> A mints a new client for the same key).\n const onUnauthorized = useMemo(() => {\n const key = applied.queryClient;\n return (error: ApiError) => {\n setAuthFailure({ key, error: new EmbedAuthError(error) });\n };\n }, [applied.queryClient]);\n const clearAuthFailure = useCallback(() => setAuthFailure(null), []);\n\n const appliedKey = applied.identityKey;\n const spec = forwarded.project;\n // Only exactly `{ use: \"default\" }` asks for the account default; null, `{}`, `[]` or any other\n // object (JS hosts, casts) is \"no project\" and makes no lookup.\n const wantsDefault =\n typeof spec === \"object\" && spec !== null && !Array.isArray(spec) && spec.use === \"default\";\n const projectState: EmbedProjectState = useMemo(() => {\n if (typeof spec === \"string\") return { project: spec, status: \"ready\" };\n if (!wantsDefault) return { project: null, status: \"ready\" };\n return resolved && resolved.key === appliedKey\n ? resolved.state\n : { project: null, status: \"resolving\" };\n }, [spec, wantsDefault, resolved, appliedKey]);\n\n if (appliedKey === null) return fallback;\n\n return (\n <DraftRegistryProvider registry={registry}>\n <QueryClientProvider key={appliedKey} client={queryClient}>\n <W6WProvider\n baseUrl={forwarded.apiOrigin}\n getSession={stableGetSession}\n identityKey={appliedKey}\n ready={ready}\n project={projectState.project ?? undefined}\n fetch={fetch}\n onUnauthorized={onUnauthorized}\n >\n {wantsDefault ? (\n <DefaultProjectResolver\n identityKey={appliedKey}\n enabled={ready !== false}\n onState={onResolved}\n />\n ) : null}\n <EmbedProjectContext.Provider value={projectState}>\n <StatusBridge\n eager={eager}\n authError={authFailure && authFailure.key === queryClient ? authFailure.error : null}\n onAcquire={clearAuthFailure}\n >\n <ClientBridge>\n <UiBridge theme={theme}>{children}</UiBridge>\n </ClientBridge>\n </StatusBridge>\n </EmbedProjectContext.Provider>\n </W6WProvider>\n </QueryClientProvider>\n </DraftRegistryProvider>\n );\n}\n"],"names":["EmbedAuthError","cause","deriveEmbedStatus","sessionStatus","sessionError","authError","makeQueryClient","QueryClient","ClientBridge","children","jsx","EmbedClientProvider","useW6WClient","UiBridge","theme","client","provided","useProvidedProject","api","useMemo","adapter","createW6WUiAdapter","id","body","updateConnection","withCredentialMethods","editorProject","project","listModelRoutes","r","appId","actionKey","opts","resolveActionFields","W6WUIProvider","EmbedThemeContext","EmbedStatusContext","createContext","useEmbedStatus","useContext","StatusBridge","eager","onAcquire","status","error","refresh","useW6WSession","useEffect","value","DefaultProjectResolver","identityKey","enabled","onState","q","useQuery","home","p","isError","data","state","W6WEmbedProvider","apiOrigin","getSession","fallback","ready","fetch","registry","useState","createDraftRegistry","applied","setApplied","switching","forwardedRef","useRef","forwarded","sessionRef","stableGetSession","cancelled","apply","queryClient","resolved","setResolved","onResolved","useCallback","key","prev","authFailure","setAuthFailure","onUnauthorized","clearAuthFailure","appliedKey","spec","wantsDefault","projectState","DraftRegistryProvider","QueryClientProvider","jsxs","W6WProvider","EmbedProjectContext"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/embed-status.ts","../src/repos/webhook-listen.ts","../src/W6WEmbedProvider.tsx"],"sourcesContent":["/**\n * The embed's public session status, as a pure total function over the `@w6w/react` session\n * store's status plus whether a terminal partner-token 401 has been seen. No React here, so\n * `node:test` covers it exhaustively.\n */\nimport type { W6WSessionStatus } from \"@w6w/react\";\n\nexport type EmbedStatusValue = \"connecting\" | \"ready\" | \"error\";\n\nexport interface EmbedStatus {\n status: EmbedStatusValue;\n /** `EmbedAuthError` for a terminal 401, else the session store's own error; `null` otherwise. */\n error: unknown;\n}\n\n/**\n * The terminal partner-token 401: the host's `getSession` minted a token the API refused, and a\n * renewal did not fix it. `cause` is the SDK `ApiError`.\n */\nexport class EmbedAuthError extends Error {\n constructor(cause: unknown) {\n super(\"The w6w API rejected the embed session token (401).\", { cause });\n this.name = \"EmbedAuthError\";\n }\n}\n\n/**\n * `loading -> connecting`, `ready|renewing -> ready`, `expired -> connecting` (the provider\n * re-acquires), `error -> error`. A seen 401 wins over everything: it is `error` with the\n * `EmbedAuthError`, whatever the store currently says.\n */\nexport function deriveEmbedStatus(\n sessionStatus: W6WSessionStatus,\n sessionError: unknown,\n authError: EmbedAuthError | null,\n): EmbedStatus {\n if (authError) return { status: \"error\", error: authError };\n switch (sessionStatus) {\n case \"ready\":\n case \"renewing\":\n return { status: \"ready\", error: null };\n case \"error\":\n return { status: \"error\", error: sessionError };\n case \"loading\":\n case \"expired\":\n return { status: \"connecting\", error: null };\n }\n}\n","import type { W6WClient } from \"@w6w/sdk\";\nimport { path } from \"@w6w/sdk\";\n/**\n * \"Listen for test event\" on a `@w6w/webhook` subscription — `/subscriptions/:id/listen` over\n * `client.request()`. Not in the SDK's stable contract (Studio reaches it the same way, its\n * `repos/subscriptions.ts`), and `createW6WUiAdapter` carries none of it, so without these the\n * embedded editor's webhook Test tab fell back to the paste-a-payload form.\n */\n\n/**\n * Structural mirror of ui's `WebhookListenSession` (this barrel may not import `@w6w/ui`); the\n * UiBridge assignment typechecks against W6WApi structurally.\n */\nexport interface WebhookListenSession {\n id: string;\n subscriptionId: string;\n nodeId: string | null;\n expiresAt: string;\n status: \"waiting\" | \"captured\" | \"expired\";\n request?: {\n method: string;\n path: string;\n query: Record<string, string>;\n headers: Record<string, string>;\n body: unknown;\n };\n receivedAt?: string;\n}\n\nexport async function startWebhookListen(\n client: W6WClient,\n subscriptionId: string,\n): Promise<{ session: WebhookListenSession; webhookUrl: string }> {\n const res = await client.request<{ session: WebhookListenSession; webhookUrl: string }>({\n method: \"POST\",\n path: path`/subscriptions/${subscriptionId}/listen`,\n body: {},\n });\n return res.body;\n}\n\nexport async function pollWebhookListen(\n client: W6WClient,\n subscriptionId: string,\n sessionId: string,\n opts?: { waitSeconds?: number; signal?: AbortSignal },\n): Promise<WebhookListenSession> {\n const res = await client.request<{ session: WebhookListenSession }>({\n method: \"GET\",\n path: path`/subscriptions/${subscriptionId}/listen/${sessionId}`,\n query: { wait: String(opts?.waitSeconds ?? 0) },\n signal: opts?.signal,\n });\n return res.body.session;\n}\n\nexport async function stopWebhookListen(\n client: W6WClient,\n subscriptionId: string,\n sessionId: string,\n): Promise<void> {\n await client.request<unknown>({\n method: \"DELETE\",\n path: path`/subscriptions/${subscriptionId}/listen/${sessionId}`,\n });\n}\n","import { QueryClient, QueryClientProvider, useQuery } from \"@tanstack/react-query\";\n/**\n * `<W6WEmbedProvider>` — everything an embedded w6w surface needs from a host, behind one\n * element: an identity-scoped `QueryClient`, a `@w6w/react` `<W6WProvider>` (client, session\n * renewal, per-request token) and `@w6w/ui`'s `<W6WUIProvider>` fed by `createW6WUiAdapter`.\n * It composes the three public pieces exactly as `@w6w/react`'s README bridge does: it never\n * spreads or patches the adapter, never reaches into `client.console`, and keeps no session\n * store of its own (`getSession` goes straight to `<W6WProvider>`).\n *\n * **Identity cache.** The `QueryClient` is owned here, not borrowed from the host, and it is a\n * NEW instance whenever `identityKey` changes — so a query cached while signed in as account A\n * is unreachable once the host switches to B. A new client alone is NOT enough: a mounted\n * `useQuery` keeps the `QueryObserver` it was born with, and that observer is bound to the client\n * it was created against, so a child that survives the swap would keep rendering A's cache. The\n * subtree is therefore also re-mounted (`key`), which drops every observer and every piece of\n * child state from the previous identity. The client, the `key` and the tuple handed to\n * `<W6WProvider>` all change in ONE state update: there is no render in which B's tree reads A's\n * cache. The old client is cleared once it is no longer mounted.\n *\n * **Identity switch (flush first).** A draft with a pending save must be written into the account\n * that produced the edit. When `identityKey` changes this provider therefore HOLDS the applied\n * tuple — `identityKey`, `apiOrigin`, `project`, `getSession`, the client — and keeps handing the\n * OLD one to `<W6WProvider>`, awaits `registry.flushAll()` (bounded, never rejects), and only then\n * applies the new tuple in a single update. `<W6WProvider>` swaps its client synchronously on a\n * new `identityKey` and detaches the old session store, so the flush must finish first; and the\n * new `getSession` is NOT forwarded early: `<W6WProvider>` reaches it through the stable wrapper\n * below, whose target is swapped in the same update as the tuple (forwarded early, the new\n * account's token would be minted on the old client). A flush that fails or times out still lets\n * the switch proceed (Q6) and is never retried with the new identity. A token rotation under an\n * unchanged `identityKey` is not a switch: it is forwarded at once, no hold.\n *\n * **No identity.** `identityKey={null}` mounts nothing but `fallback`: no `<W6WProvider>`, no\n * `QueryClientProvider`, no children — so no `getSession` call and no request. `null -> \"A\"`\n * applies at once (nothing can be pending); `\"A\" -> null` takes the SAME flush-first path as any\n * other switch before the fallback renders.\n *\n * **Default project.** `project={{ use: \"default\" }}` resolves ONCE per applied identity, through\n * one `client.projects.list()` query inside the identity-scoped `QueryClient`, to the row with\n * `isDefault === true` — never `list[0]`, never a fallback. `<W6WProvider>` gets no project until\n * it resolves; the client rebuild that follows reuses the cached answer (the query is keyed\n * without the client and lives in a component that stays mounted, so it is not re-issued).\n *\n * **Status.** `useEmbedStatus()` is `deriveEmbedStatus` over the `@w6w/react` session store plus a\n * terminal 401 seen through the forwarded `onUnauthorized`.\n */\nimport {\n W6WProvider,\n type W6WSession,\n createW6WUiAdapter,\n useW6WClient,\n useW6WSession,\n} from \"@w6w/react\";\nimport type { ApiError, FetchLike } from \"@w6w/sdk\";\nimport { type ThemeMode, type W6WApi, W6WUIProvider } from \"@w6w/ui\";\nimport {\n type ReactNode,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { DraftRegistryProvider, EmbedClientProvider, createDraftRegistry } from \"./draft/index.ts\";\nimport { EmbedAuthError, type EmbedStatus, deriveEmbedStatus } from \"./embed-status.ts\";\nimport {\n EmbedProjectContext,\n type EmbedProjectState,\n useProvidedProject,\n} from \"./project-context.ts\";\nimport { updateConnection } from \"./repos/connections.ts\";\nimport { withCredentialMethods } from \"./repos/credentials.ts\";\nimport { listDataStoreEntries, listDataStores } from \"./repos/data-stores.ts\";\nimport { resolveActionFields } from \"./repos/fields.ts\";\nimport { listModelRoutes } from \"./repos/models.ts\";\nimport {\n pollWebhookListen,\n startWebhookListen,\n stopWebhookListen,\n} from \"./repos/webhook-listen.ts\";\nimport { listNodeSamples, listTestInputs, saveTestInput } from \"./repos/workflows.ts\";\nimport { EmbedThemeContext } from \"./theme-context.ts\";\n\nexport interface W6WEmbedProviderProps {\n /** The w6w API origin, e.g. `\"https://api.w6w.io\"`. */\n apiOrigin: string;\n /**\n * Mints a session for the CURRENT host user (`{token, expiresAt, expiresIn?}`). Always the\n * latest callback; the provider caches and renews the result.\n */\n getSession: () => Promise<W6WSession>;\n /**\n * Which signed-in identity this provider speaks for (e.g. `${userId}:${accountId}`). Changing it\n * rebuilds the API client AND replaces the `QueryClient`, so nothing cached for the previous\n * identity is ever shown under the new one.\n */\n identityKey: string | null;\n /**\n * Rendered instead of the embed while `identityKey` is `null` (no signed-in user). Nothing of\n * the embed mounts then: no `getSession` call, no request. Default `null`.\n */\n fallback?: ReactNode;\n /**\n * Start acquiring a session on mount. Without it the session is acquired lazily, by the first\n * request a mounted surface makes, and `useEmbedStatus()` stays `\"connecting\"` until then.\n */\n eager?: boolean;\n /** Force the `@w6w/ui` theme to follow the host's. Omit to resolve it the library's default way. */\n theme?: ThemeMode;\n /** Gates every read hook while `false` (default `true`) — e.g. until the host has a user. */\n ready?: boolean;\n /**\n * Default project for project-scoped operations: a project id, or `{ use: \"default\" }` to use\n * the account's default project (resolved once per identity; see `useEmbedProject()`).\n */\n project?: string | { use: \"default\" };\n /** Transport override (tests, request recording). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n children: ReactNode;\n}\n\nfunction makeQueryClient(): QueryClient {\n // Same defaults as Studio's own client (`studio/src/main.tsx`).\n return new QueryClient({\n defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },\n });\n}\n\n/** The part of the tuple that changes only when a switch has been APPLIED (after the flush). */\ninterface Applied {\n identityKey: string | null;\n queryClient: QueryClient;\n}\n\n/** What `<W6WProvider>` is fed while no switch is in progress; frozen at the last such render. */\ninterface Forwarded {\n apiOrigin: string;\n project: string | { use: \"default\" } | undefined;\n getSession: () => Promise<W6WSession>;\n}\n\n/** Hands the SDK client of the nearest `<W6WProvider>` to `useWorkflowDraft`. */\nfunction ClientBridge({ children }: { children: ReactNode }) {\n return <EmbedClientProvider client={useW6WClient()}>{children}</EmbedClientProvider>;\n}\n\n/** The README bridge: `useW6WClient` -> `createW6WUiAdapter` -> `<W6WUIProvider>`. */\nfunction UiBridge({ theme, children }: { theme?: ThemeMode; children: ReactNode }) {\n const client = useW6WClient();\n const provided = useProvidedProject();\n // `createW6WUiAdapter` has no `updateConnection` or `listModelRoutes`; keep them if it ever\n // gains them, else supply the embed repo's own so the ui Edit modal can save and the\n // `@w6w/model` node's Route field offers the project's routes (not free text) in any host.\n const api = useMemo(() => {\n const adapter: W6WApi = createW6WUiAdapter(client);\n if (typeof adapter.updateConnection !== \"function\") {\n adapter.updateConnection = (id, body) => updateConnection(client, id, body);\n }\n withCredentialMethods(adapter, client);\n if (typeof adapter.listModelRoutes !== \"function\") {\n adapter.listModelRoutes = async (editorProject) => {\n const project = editorProject || provided;\n if (!project) return [];\n const routes = await listModelRoutes(client, project);\n return routes.map((r) => ({ name: r.name, model: r.modelId, status: r.status }));\n };\n }\n // Same for the `@w6w/store` node's store picker: the project's store names, not free text.\n if (typeof adapter.listDataStores !== \"function\") {\n adapter.listDataStores = async (editorProject) => {\n const project = editorProject || provided;\n if (!project) return [];\n return listDataStores(client, project);\n };\n }\n // ...and its key picker: one page of the named store's entries, searched by key prefix.\n if (typeof adapter.listDataStoreEntries !== \"function\") {\n adapter.listDataStoreEntries = async (store, opts) => {\n const project = opts.project || provided;\n if (!project) return { items: [] };\n return listDataStoreEntries(client, project, store, opts);\n };\n }\n // Persisted node samples, so the editor's picker keeps upstream keys across a reload.\n if (typeof adapter.loadNodeSamples !== \"function\") {\n adapter.loadNodeSamples = (workflowId) => listNodeSamples(client, workflowId);\n }\n // Remembered test inputs, so every test form reopens prefilled with the last values used.\n if (typeof adapter.loadTestInputs !== \"function\") {\n adapter.loadTestInputs = (workflowId) => listTestInputs(client, workflowId);\n }\n if (typeof adapter.saveTestInput !== \"function\") {\n adapter.saveTestInput = (workflowId, nodeId, input) =>\n saveTestInput(client, workflowId, nodeId, input);\n }\n if (typeof adapter.resolveActionFields !== \"function\") {\n adapter.resolveActionFields = (appId, actionKey, body, opts) =>\n resolveActionFields(client, appId, actionKey, body, opts);\n }\n // The webhook node's Test tab listens for a real request only when all three exist; without\n // them it falls back to the paste-a-payload form.\n if (typeof adapter.startWebhookListen !== \"function\") {\n adapter.startWebhookListen = (subscriptionId) => startWebhookListen(client, subscriptionId);\n adapter.pollWebhookListen = (subscriptionId, sessionId, opts) =>\n pollWebhookListen(client, subscriptionId, sessionId, opts);\n adapter.stopWebhookListen = (subscriptionId, sessionId) =>\n stopWebhookListen(client, subscriptionId, sessionId);\n }\n return adapter;\n }, [client, provided]);\n return (\n <W6WUIProvider api={api} theme={theme}>\n <EmbedThemeContext.Provider value={theme}>{children}</EmbedThemeContext.Provider>\n </W6WUIProvider>\n );\n}\n\nconst EmbedStatusContext = createContext<EmbedStatus>({ status: \"connecting\", error: null });\n\n/** The session status of the nearest `<W6WEmbedProvider>`. See `deriveEmbedStatus`. */\nexport function useEmbedStatus(): EmbedStatus {\n return useContext(EmbedStatusContext);\n}\n\n/** Publishes `useEmbedStatus()`, and starts the session on mount when `eager`. */\nfunction StatusBridge({\n eager,\n authError,\n onAcquire,\n children,\n}: {\n eager: boolean;\n authError: EmbedAuthError | null;\n onAcquire: () => void;\n children: ReactNode;\n}) {\n const { status, error, refresh } = useW6WSession();\n // `refresh` is per session store, so a rebuilt client (project resolved) is started again.\n useEffect(() => {\n if (eager) void refresh();\n }, [eager, refresh]);\n useEffect(() => {\n if (status === \"loading\" || status === \"renewing\") onAcquire();\n }, [status, onAcquire]);\n const value = useMemo(\n () => deriveEmbedStatus(status, error, authError),\n [status, error, authError],\n );\n return <EmbedStatusContext.Provider value={value}>{children}</EmbedStatusContext.Provider>;\n}\n\n/**\n * Resolves `{use:\"default\"}`. Lives INSIDE `<W6WProvider>` (it needs the client) but at a fixed\n * slot, so it survives the client rebuild that follows resolution; the query key carries no\n * client, so nothing refetches.\n */\nfunction DefaultProjectResolver({\n identityKey,\n enabled,\n onState,\n}: {\n identityKey: string;\n enabled: boolean;\n onState: (key: string, state: EmbedProjectState) => void;\n}) {\n const client = useW6WClient();\n const q = useQuery({\n queryKey: [\"w6w-embed\", \"default-project\"],\n enabled,\n queryFn: async () => {\n const rows = await client.projects.list();\n const home = rows.find((p) => p.isDefault === true);\n if (!home) throw new Error(\"The account has no default project.\");\n return home.id;\n },\n });\n const { isError, error, data } = q;\n const state = useMemo<EmbedProjectState>(\n () =>\n isError\n ? { project: null, status: \"error\", error }\n : data !== undefined\n ? { project: data, status: \"ready\" }\n : { project: null, status: \"resolving\" },\n [isError, error, data],\n );\n useEffect(() => {\n onState(identityKey, state);\n }, [identityKey, state, onState]);\n return null;\n}\n\nexport function W6WEmbedProvider({\n apiOrigin,\n getSession,\n identityKey,\n fallback = null,\n eager = false,\n theme,\n ready,\n project,\n fetch,\n children,\n}: W6WEmbedProviderProps): ReactNode {\n const [registry] = useState(createDraftRegistry);\n const [applied, setApplied] = useState<Applied>(() => ({\n identityKey,\n queryClient: makeQueryClient(),\n }));\n\n // While `identityKey` differs from the applied one a switch is pending: everything below keeps\n // using the values frozen at the last render that was not pending. Otherwise props flow straight\n // through (project / origin changes, token rotation). Idempotent latest-value writes, the same\n // pattern `<W6WProvider>` uses for its own refs.\n const switching = identityKey !== applied.identityKey;\n const forwardedRef = useRef<Forwarded>({ apiOrigin, project, getSession });\n if (!switching) forwardedRef.current = { apiOrigin, project, getSession };\n const forwarded = forwardedRef.current;\n\n // The stable `getSession` handed to `<W6WProvider>`: reads whatever is applied RIGHT NOW. Its\n // target changes in the same render that applies the new tuple, never before the flush settles.\n const sessionRef = useRef(forwarded.getSession);\n sessionRef.current = forwarded.getSession;\n const [stableGetSession] = useState(() => () => sessionRef.current());\n\n useEffect(() => {\n if (identityKey === applied.identityKey) return;\n let cancelled = false;\n const apply = () => {\n if (cancelled) return;\n setApplied({ identityKey, queryClient: makeQueryClient() });\n };\n // Nothing is mounted under a null identity, so nothing can be pending: apply at once.\n if (applied.identityKey === null) {\n apply();\n return () => {\n cancelled = true;\n };\n }\n // `flushAll` never rejects and is bounded; the switch proceeds either way (Q6).\n registry.flushAll().then(apply, apply);\n return () => {\n cancelled = true;\n };\n }, [identityKey, applied.identityKey, registry]);\n\n // The replaced client is dropped once nothing mounted reads it.\n const queryClient = applied.queryClient;\n useEffect(() => () => queryClient.clear(), [queryClient]);\n\n // Default-project resolution, tagged with the identity it was resolved for: a result for A is\n // never read once B is applied.\n const [resolved, setResolved] = useState<{ key: string; state: EmbedProjectState } | null>(null);\n const onResolved = useCallback((key: string, state: EmbedProjectState) => {\n setResolved((prev) =>\n prev &&\n prev.key === key &&\n prev.state.project === state.project &&\n prev.state.status === state.status &&\n prev.state.error === state.error\n ? prev\n : { key, state },\n );\n }, []);\n\n // The terminal partner-token 401, tagged with its identity; cleared when the next acquisition\n // starts (StatusBridge) or the identity changes (the tag no longer matches).\n const [authFailure, setAuthFailure] = useState<{\n key: QueryClient;\n error: EmbedAuthError;\n } | null>(null);\n // The handler is bound to the per-mount client it was created for: a late 401 from a previous\n // `<W6WProvider>` is tagged with ITS client, never the one applied by the time it fires — even\n // when the identity key came back (A -> B -> A mints a new client for the same key).\n const onUnauthorized = useMemo(() => {\n const key = applied.queryClient;\n return (error: ApiError) => {\n setAuthFailure({ key, error: new EmbedAuthError(error) });\n };\n }, [applied.queryClient]);\n const clearAuthFailure = useCallback(() => setAuthFailure(null), []);\n\n const appliedKey = applied.identityKey;\n const spec = forwarded.project;\n // Only exactly `{ use: \"default\" }` asks for the account default; null, `{}`, `[]` or any other\n // object (JS hosts, casts) is \"no project\" and makes no lookup.\n const wantsDefault =\n typeof spec === \"object\" && spec !== null && !Array.isArray(spec) && spec.use === \"default\";\n const projectState: EmbedProjectState = useMemo(() => {\n if (typeof spec === \"string\") return { project: spec, status: \"ready\" };\n if (!wantsDefault) return { project: null, status: \"ready\" };\n return resolved && resolved.key === appliedKey\n ? resolved.state\n : { project: null, status: \"resolving\" };\n }, [spec, wantsDefault, resolved, appliedKey]);\n\n if (appliedKey === null) return fallback;\n\n return (\n <DraftRegistryProvider registry={registry}>\n <QueryClientProvider key={appliedKey} client={queryClient}>\n <W6WProvider\n baseUrl={forwarded.apiOrigin}\n getSession={stableGetSession}\n identityKey={appliedKey}\n ready={ready}\n project={projectState.project ?? undefined}\n fetch={fetch}\n onUnauthorized={onUnauthorized}\n >\n {wantsDefault ? (\n <DefaultProjectResolver\n identityKey={appliedKey}\n enabled={ready !== false}\n onState={onResolved}\n />\n ) : null}\n <EmbedProjectContext.Provider value={projectState}>\n <StatusBridge\n eager={eager}\n authError={authFailure && authFailure.key === queryClient ? authFailure.error : null}\n onAcquire={clearAuthFailure}\n >\n <ClientBridge>\n <UiBridge theme={theme}>{children}</UiBridge>\n </ClientBridge>\n </StatusBridge>\n </EmbedProjectContext.Provider>\n </W6WProvider>\n </QueryClientProvider>\n </DraftRegistryProvider>\n );\n}\n"],"names":["EmbedAuthError","cause","deriveEmbedStatus","sessionStatus","sessionError","authError","startWebhookListen","client","subscriptionId","path","pollWebhookListen","sessionId","opts","stopWebhookListen","makeQueryClient","QueryClient","ClientBridge","children","jsx","EmbedClientProvider","useW6WClient","UiBridge","theme","provided","useProvidedProject","api","useMemo","adapter","createW6WUiAdapter","id","body","updateConnection","withCredentialMethods","editorProject","project","listModelRoutes","r","listDataStores","store","listDataStoreEntries","workflowId","listNodeSamples","listTestInputs","nodeId","input","saveTestInput","appId","actionKey","resolveActionFields","W6WUIProvider","EmbedThemeContext","EmbedStatusContext","createContext","useEmbedStatus","useContext","StatusBridge","eager","onAcquire","status","error","refresh","useW6WSession","useEffect","value","DefaultProjectResolver","identityKey","enabled","onState","q","useQuery","home","p","isError","data","state","W6WEmbedProvider","apiOrigin","getSession","fallback","ready","fetch","registry","useState","createDraftRegistry","applied","setApplied","switching","forwardedRef","useRef","forwarded","sessionRef","stableGetSession","cancelled","apply","queryClient","resolved","setResolved","onResolved","useCallback","key","prev","authFailure","setAuthFailure","onUnauthorized","clearAuthFailure","appliedKey","spec","wantsDefault","projectState","DraftRegistryProvider","QueryClientProvider","jsxs","W6WProvider","EmbedProjectContext"],"mappings":"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"}
@@ -11,6 +11,7 @@ export interface BindingSite {
11
11
  uses?: {
12
12
  app?: string;
13
13
  connection?: string | null;
14
+ connectionRef?: string;
14
15
  };
15
16
  }
16
17
  export interface BindingProblem {
@@ -21,7 +22,10 @@ export interface BindingProblem {
21
22
  }
22
23
  /** A problem iff app + connection are non-empty and the app's list is `ready` without the id.
23
24
  * `loading`, `error` and `undefined` (internal app / not fetched) never report — no false red. */
24
- export declare function findBindingProblems(sites: readonly BindingSite[], byApp: (app: string) => AppConnections | undefined): BindingProblem[];
25
+ export declare function findBindingProblems(sites: readonly BindingSite[], byApp: (app: string) => AppConnections | undefined, connections?: Record<string, {
26
+ app: string;
27
+ connection: string | null;
28
+ }>): BindingProblem[];
25
29
  export interface DisabledBannerInput {
26
30
  enabled: boolean | undefined;
27
31
  problemCount: number;
@@ -11,6 +11,15 @@ export interface ExecutionSummary {
11
11
  finishedAt: string | null;
12
12
  durationMs: number | null;
13
13
  workflowRunId: string | null;
14
+ /** The execution this one RE-RUNS (History's Re-run), or `null`. Absent from an older server. */
15
+ rerunOf?: string | null;
16
+ /** How a workflow run was started (manual, schedule, webhook, event, replay); `null` for a
17
+ * Function/Endpoint call. Absent from an older server. */
18
+ trigger?: string | null;
19
+ /** The trigger node a workflow run entered through. Absent from an older server. */
20
+ triggerNodeId?: string | null;
21
+ /** The start of the input as JSON text. Absent from an older server. */
22
+ inputPreview?: string | null;
14
23
  }
15
24
  export interface ExecutionStats {
16
25
  total: number;
@@ -109,15 +118,36 @@ export interface ExecutionDetailData {
109
118
  input?: unknown;
110
119
  output?: unknown;
111
120
  error?: unknown;
121
+ /** The execution this one re-runs, carried from its summary row. */
122
+ rerunOf?: string | null;
112
123
  steps?: Array<{
113
124
  id: string;
114
125
  status: "pending" | "running" | "succeeded" | "failed" | "skipped";
126
+ skipReason?: "not_on_path" | "condition" | "stopped" | "disabled";
115
127
  startedAt?: string;
116
128
  finishedAt?: string;
117
129
  input?: unknown;
118
130
  output?: unknown;
131
+ node?: StepNode;
119
132
  }>;
120
133
  }
134
+ /** What a workflow step IS (`@w6w/ui`'s `ExecutionLogNode`, restated locally — A1): the app and
135
+ * action it uses, whether it is a trigger, and the app's display names when it is a registered app.
136
+ * A built-in `@w6w/*` node carries ids only; the panel names those from its own catalog. */
137
+ export interface StepNode {
138
+ app: string;
139
+ action: string;
140
+ trigger?: boolean;
141
+ appName?: string;
142
+ actionName?: string;
143
+ }
144
+ /**
145
+ * Every node of the workflow's CURRENT definition, keyed by id, as a {@link StepNode} — with the
146
+ * display names of each registered app (`GET /apps/:id`'s `displayName` and action titles, plus
147
+ * `GET /apps/:id/triggers` for an app a trigger node uses). Best-effort throughout: a definition
148
+ * that cannot be read yields an empty map, and an app that cannot be read keeps its raw ids.
149
+ */
150
+ export declare function describeWorkflowNodes(client: W6WClient, workflowId: string): Promise<Map<string, StepNode>>;
121
151
  /**
122
152
  * Expand a list row into its full detail. Function/Endpoint invocations are
123
153
  * synchronous calls recorded one row per call (`.claude/docs/invocations.md`)
@@ -133,3 +163,18 @@ export interface ExecutionDetailData {
133
163
  * carries a human-readable callable name of its own.
134
164
  */
135
165
  export declare function getExecutionDetail(client: W6WClient, summary: ExecutionSummary): Promise<ExecutionDetailData>;
166
+ /**
167
+ * Run a past execution again with the input it had, against the callable's
168
+ * CURRENT definition (the stored draft — `allowUnpublished`, as the editor's
169
+ * own Run and Test do), and return the new execution's summary row so the
170
+ * host can open it. A workflow is replayed by the server from its stored seed
171
+ * (`POST /runs/:id/rerun`, trigger payload included); a function replays its
172
+ * `inputs`; an endpoint its `input`. `null` when the new row is not yet listed (the host then just
173
+ * shows the refreshed list).
174
+ */
175
+ export declare function rerunExecution(client: W6WClient, summary: ExecutionSummary, opts?: {
176
+ sleep?: (ms: number) => Promise<void>;
177
+ }): Promise<ExecutionSummary | null>;
178
+ /** One callable's execution by id, as its summary row (`GET /history`'s id search), or `null`
179
+ * when it is not listed — e.g. the original a re-run names, once retention removed it. */
180
+ export declare function findExecution(client: W6WClient, callableId: string, id: string): Promise<ExecutionSummary | null>;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Pure row logic behind `ImportSpecModal`'s configure step (T2.1.1, IF-1 / D-B8 / D-B9).
3
+ *
4
+ * Turns the server's `POST /spec/preview` dependencies into editable rows and back into the
5
+ * `selections` map the import body carries. The client NEVER ranks: a row's initial selection is
6
+ * exactly the server's `suggested`. Every dependency yields exactly one selection on the way out —
7
+ * `{mode:"unconfigured"}` included, never omitted and never upgraded to a suggestion the user
8
+ * cleared.
9
+ *
10
+ * `node --test` throws `ERR_UNKNOWN_FILE_EXTENSION` on anything reaching the `@w6w/ui` import graph
11
+ * (see `lib/import-paste.ts`), so this module takes no value import at all.
12
+ */
13
+ export type DependencyKind = "connection" | "var" | "secret" | "const" | "store";
14
+ export type Selection = {
15
+ mode: "bind";
16
+ target: string;
17
+ } | {
18
+ mode: "create";
19
+ name: string;
20
+ } | {
21
+ mode: "unconfigured";
22
+ };
23
+ export type SelectionMode = Selection["mode"];
24
+ export interface DependencyOption {
25
+ id: string;
26
+ label: string;
27
+ source: "connection" | "built-in" | "var" | "secret" | "const" | "store";
28
+ authKey?: string;
29
+ compatible?: boolean;
30
+ reason?: string;
31
+ }
32
+ export interface SpecDependency {
33
+ key: string;
34
+ kind: DependencyKind;
35
+ name: string;
36
+ app?: string;
37
+ authHint?: {
38
+ method: "built-in";
39
+ } | {
40
+ method: "authKey";
41
+ authKey: string;
42
+ };
43
+ usages: {
44
+ nodeId: string | null;
45
+ path: string;
46
+ access?: "read" | "write" | "delete";
47
+ }[];
48
+ store?: {
49
+ kind: "kv" | "doc" | "table" | null;
50
+ schema: unknown | null;
51
+ access: {
52
+ read: boolean;
53
+ write: boolean;
54
+ delete: boolean;
55
+ };
56
+ };
57
+ dynamic?: true;
58
+ }
59
+ export interface PreviewDependency extends SpecDependency {
60
+ options: DependencyOption[];
61
+ suggested: Selection;
62
+ canCreate: boolean;
63
+ }
64
+ export interface ImportRow {
65
+ key: string;
66
+ dep: PreviewDependency;
67
+ /** Options offered to the row (the server's, plus any connection created in-modal). */
68
+ options: DependencyOption[];
69
+ selection: Selection;
70
+ /** Remembered name for `create`, so toggling modes does not lose an edit. */
71
+ createName: string;
72
+ }
73
+ /** IF-1 "Allowed modes". */
74
+ export declare function allowedModes(dep: PreviewDependency): SelectionMode[];
75
+ /** One row per dependency; the initial selection is `suggested` verbatim (D-B9). */
76
+ export declare function buildRows(deps: readonly PreviewDependency[]): ImportRow[];
77
+ /** Bind a row to an option. Refused (row unchanged) for an unknown / incompatible option or a disallowed mode. */
78
+ export declare function setBind(rows: readonly ImportRow[], key: string, target: string): ImportRow[];
79
+ /** Switch a store row to `create` / edit its name. Refused unless `create` is allowed. */
80
+ export declare function setCreateName(rows: readonly ImportRow[], key: string, name: string): ImportRow[];
81
+ /** Leave the dependency unconfigured (always allowed). */
82
+ export declare function setUnconfigured(rows: readonly ImportRow[], key: string): ImportRow[];
83
+ /** A connection was just created via "Add connection": offer it and bind the row to it. */
84
+ export declare function withCreatedConnection(rows: readonly ImportRow[], key: string, id: string, label: string): ImportRow[];
85
+ /** Whether a row can be submitted: a `create` needs a non-blank name. */
86
+ export declare function rowReady(row: ImportRow): boolean;
87
+ export declare function rowsReady(rows: readonly ImportRow[]): boolean;
88
+ /** The import body's `selections`: ONE entry per dependency key, `unconfigured` included. */
89
+ export declare function toSelections(rows: readonly ImportRow[]): Record<string, Selection>;
@@ -1,5 +1,6 @@
1
1
  export * from './workflow-validity.ts';
2
2
  export * from './workflow-variables.ts';
3
+ export * from './starting-data.ts';
3
4
  export * from './workflow-settings.ts';
4
5
  export * from './workflow-route.ts';
5
6
  export type { RailCallable } from './code-rail.ts';
@@ -23,3 +24,5 @@ export * from './input-coverage.ts';
23
24
  export * from './coerce-input.ts';
24
25
  export * from './callable-record.ts';
25
26
  export * from './binding-problems.ts';
27
+ export * from './setup-incomplete.ts';
28
+ export * from './import-bindings.ts';
@@ -78,7 +78,7 @@ export declare function hasDelete(kind: RailSettingsKind): boolean;
78
78
  * `/endpoints/:id/spec` by a wrong `if`/`else` fallthrough — every one of the four
79
79
  * `RailSettingsKind` arms is named explicitly below.
80
80
  */
81
- export declare function specCollectionForKind(kind: RailSettingsKind): "functions" | "endpoints" | null;
81
+ export declare function specCollectionForKind(kind: RailSettingsKind): "functions" | "endpoints" | "workflows" | null;
82
82
  /** What A8's `evaluateEdit` needs to know about the row already on screen — deliberately
83
83
  * narrower than a whole `FunctionDef`/`EndpointDef`: the two fields a hand-edit must not
84
84
  * be allowed to change out from under the row it opened on. */
@@ -0,0 +1,22 @@
1
+ /** A name list for one category; `ready:false` (loading/errored) means that category is never flagged. */
2
+ export interface SetupNameList {
3
+ names: string[];
4
+ ready: boolean;
5
+ }
6
+ export interface SetupIncompleteInput {
7
+ /** The parsed saved workflow (nodes, connections, variables, …). */
8
+ workflow: unknown;
9
+ lists: {
10
+ vars: SetupNameList;
11
+ secrets: SetupNameList;
12
+ constants: SetupNameList;
13
+ stores: SetupNameList;
14
+ };
15
+ }
16
+ /** Node id -> human labels of what is missing at that node (e.g. `connection "gmail"`, `var.x`). */
17
+ export type SetupIncompleteResult = Record<string, string[]>;
18
+ /**
19
+ * Derive what is unconfigured in a saved workflow (plan IF-1 shape), keyed by node id.
20
+ * A category whose list is not `ready` is never flagged.
21
+ */
22
+ export declare function findSetupIncomplete(input: SetupIncompleteInput): SetupIncompleteResult;
@@ -0,0 +1,31 @@
1
+ import { WorkflowVariableRow } from './workflow-variables.ts';
2
+ /** Why a Starting data draft cannot be saved. */
3
+ export type StartingDataProblem = "invalid_json" | "not_object" | "too_large";
4
+ /** The outcome of reading a Starting data draft for save. `value: undefined` drops the key. */
5
+ export type StartingDataForSave = {
6
+ ok: true;
7
+ value: Record<string, unknown> | undefined;
8
+ } | {
9
+ ok: false;
10
+ reason: StartingDataProblem;
11
+ };
12
+ /**
13
+ * Read a definition's `initialState` field. Takes `unknown` because the source is
14
+ * `JSON.parse`d and the `</>` editor lets an author type anything: only a plain
15
+ * object counts; anything else reads as "no starting data".
16
+ */
17
+ export declare function readStartingData(raw: unknown): Record<string, unknown> | undefined;
18
+ /**
19
+ * Turn the tab's JSON text into the value `patchMeta({ initialState })` saves.
20
+ * Blank text and `{}` both mean "no starting data" (`value: undefined`, so the
21
+ * key is dropped from the definition). Anything but a plain object is refused,
22
+ * as is an object whose serialised form exceeds `STATE_LIMITS.initialChars`
23
+ * (measured exactly as the server measures it: `JSON.stringify(v).length`).
24
+ */
25
+ export declare function startingDataForSave(text: string): StartingDataForSave;
26
+ /**
27
+ * The declared variable keys that are also top-level keys of the starting data —
28
+ * on those keys the variable's value wins (R2'). Variable declaration order, no
29
+ * duplicates, no empty keys.
30
+ */
31
+ export declare function startingDataCollisions(initial: Record<string, unknown> | undefined, variables: readonly WorkflowVariableRow[]): string[];
@@ -0,0 +1,29 @@
1
+ import { StateLogRow } from '@w6w/expr';
2
+ /**
3
+ * The run's state tree: the snapshot `GET /runs/:id` serves when one was persisted, else the
4
+ * state-write log folded by `@w6w/expr`'s `foldStateLog` (the same fold the run log's "state
5
+ * after this step" uses). `undefined` when neither has anything.
6
+ */
7
+ export declare function runStateTree(snapshot: unknown, writes: readonly StateLogRow[] | undefined): Record<string, unknown> | undefined;
8
+ /**
9
+ * Who last wrote each top-level state key, from the run's state-write log: the node id of the
10
+ * last write under that key, or `null` for a key that came from the starting state (the seed row,
11
+ * `seq 0` with no node). A warning row wrote nothing; a delete (`new` absent or `null`) forgets the
12
+ * key; a whole-tree write (`path []`) hands every key it sets to its node. Read in `seq` order
13
+ * whatever order the rows arrive in.
14
+ */
15
+ export declare function stateKeyWriters(writes: readonly StateLogRow[]): Map<string, string | null>;
16
+ /** One line of the starting state preview: a leaf's dotted path and how it reads. */
17
+ export interface StartingStateRow {
18
+ path: string;
19
+ /** `reference`: a string carrying a `{{ }}` reference, resolved when a run starts. */
20
+ kind: "literal" | "reference";
21
+ /** The reference text verbatim, or the literal as JSON. */
22
+ text: string;
23
+ }
24
+ /**
25
+ * The starting state flattened to one row per leaf, in key order: nested objects descend
26
+ * (`a.b`), arrays and empty objects stay one literal row. A string with a `{{ }}` reference is a
27
+ * `reference` row shown as typed, never as a value: it only resolves when a run starts.
28
+ */
29
+ export declare function startingStateRows(initial: Record<string, unknown> | undefined): StartingStateRow[];
@@ -1,18 +1,4 @@
1
- /**
2
- * Pure helpers shared by `TargetSelector`'s three arms (rfcs/endpoint.md
3
- * :339-347 — `EndpointTarget = CallableRef | ActionTarget`) and by
4
- * `EndpointsPage`'s save gate.
5
- *
6
- * Pure and testable by design, mirroring `workflow-validity.ts`'s shape:
7
- * `@w6w/ui`'s `package.json` `exports["."]` resolves to raw `./src/index.ts`,
8
- * which re-exports `.tsx` files, so `node --test` throws
9
- * `ERR_UNKNOWN_FILE_EXTENSION` the moment anything here imports `@w6w/ui` or
10
- * `@w6w/ui/flow` — even type-only. So this module declares its own local
11
- * structural (`*Like`) types instead of importing the real ones from
12
- * `../api/types.ts`, and `isTriggerApp`/`asFieldDefs` (the workflow arm's only
13
- * dependency on `@w6w/ui/flow`) are **caller-injected** — `TargetSelector.tsx`
14
- * (unaffected by the JSX constraint) imports the real ones and passes them in.
15
- */
1
+ import { ParamWhen } from '@w6w/expr';
16
2
  /** The four arms `EndpointTarget` can take; a Function's `impl` takes the
17
3
  * first three (D-8) — `"endpoint"` is alias-only, never legal for `Fn.impl`. */
18
4
  export type TargetKind = "action" | "function" | "workflow" | "endpoint";
@@ -79,6 +65,7 @@ export interface ParamLike {
79
65
  required?: boolean;
80
66
  default?: unknown;
81
67
  showIf?: ShowIfPredicate;
68
+ when?: ParamWhen[];
82
69
  }
83
70
  /** Visibility predicate — mirroring ActionParam.showIf from @w6w/ui. */
84
71
  export interface ShowIfPredicate {
@@ -122,6 +109,7 @@ export interface TargetField {
122
109
  required?: boolean;
123
110
  default?: unknown;
124
111
  showIf?: ShowIfPredicate;
112
+ when?: ParamWhen[];
125
113
  }
126
114
  /** One row of an input-contract list (`FunctionInput`/`EndpointInput`'s common shape). */
127
115
  export interface InputRowLike {
@@ -1,3 +1,4 @@
1
+ import { StateLogRow } from '@w6w/expr';
1
2
  import { RunStatus } from '@w6w/sdk';
2
3
  export type { RunStatus };
3
4
  /**
@@ -23,16 +24,25 @@ export type StepMetaLike = {
23
24
  * `ExecutionLogPanel` row. */
24
25
  export interface StepExecutionLike {
25
26
  status: StepStatus;
27
+ /** Why a `skipped` step did not run (`NodeExecution.skipReason`); read as `unknown` (opaque
28
+ * wire) and carried only when it is one of the four known reasons. */
29
+ skipReason?: unknown;
26
30
  startedAt?: string;
27
31
  finishedAt?: string;
28
32
  input?: unknown;
29
33
  output?: unknown;
34
+ logs?: string[];
30
35
  /** `StepExecution.error` — a `StepError` on `failed` per rfcs/workflow.md
31
36
  * §StepExecution, but read as `unknown` because `RunState.steps` is opaque
32
37
  * on the wire; {@link toLogError} normalises whatever actually arrives. */
33
38
  error?: unknown;
34
39
  /** Recorded metadata; absent (or null) for steps that recorded none. */
35
40
  meta?: StepMetaLike | null;
41
+ /** For each only (I3): per-iteration body records, read as `unknown` (opaque wire). */
42
+ iterations?: unknown;
43
+ iterationsTotal?: unknown;
44
+ iterationsFailed?: unknown;
45
+ iterationsTruncated?: unknown;
36
46
  }
37
47
  /** A step or run error, as the run log panel renders it — field-for-field
38
48
  * `@w6w/ui`'s `ExecutionLogError` (rfcs/workflow.md §StepError's
@@ -83,12 +93,39 @@ export declare const RUN_FAILED_NO_REASON = "Run failed without reporting a reas
83
93
  export interface ExecutionLogStepLike {
84
94
  id: string;
85
95
  status: StepStatus;
96
+ skipReason?: SkipReasonLike;
86
97
  startedAt?: string;
87
98
  finishedAt?: string;
88
99
  input?: unknown;
89
100
  output?: unknown;
101
+ logs?: string[];
90
102
  error?: LogErrorLike;
91
103
  meta?: StepMetaLike;
104
+ /** For each only (I4) — field-for-field `@w6w/ui`'s `ExecutionLogStep`. */
105
+ iterations?: ExecutionLogIterationLike[];
106
+ iterationsTotal?: number;
107
+ iterationsFailed?: number;
108
+ iterationsTruncated?: boolean;
109
+ /** This node's state writes (`GET /runs/:id/state-writes` rows, in `seq` order) —
110
+ * field-for-field `@w6w/ui`'s `ExecutionLogStep.stateWrites` (plan §2.7). Absent → no State block. */
111
+ stateWrites?: StateWriteEntryLike[] | null;
112
+ /** The state tree right after this node's last write (`foldStateLog`). */
113
+ stateAfter?: Record<string, unknown> | null;
114
+ }
115
+ /** One state-write entry on a run-log row — `@w6w/ui`'s `ExecutionLogStep.stateWrites[number]`. */
116
+ export type StateWriteEntryLike = {
117
+ path: string[];
118
+ item?: number;
119
+ old?: unknown;
120
+ new?: unknown;
121
+ warning?: string;
122
+ };
123
+ /** One For each iteration row (I4) — mirrors `@w6w/ui`'s `ExecutionLogIteration`. */
124
+ export interface ExecutionLogIterationLike {
125
+ index: number;
126
+ status: "succeeded" | "failed" | "running";
127
+ failedNodeId?: string;
128
+ steps: ExecutionLogStepLike[];
92
129
  }
93
130
  /** How a cancel-request failure should be handled (A6). */
94
131
  export type CancelErrorOutcome = "reconcile" | "surface";
@@ -110,6 +147,21 @@ export declare function shouldContinuePolling(status: RunStatus | undefined): bo
110
147
  * each other.
111
148
  */
112
149
  export declare function isRunInFlight(status: RunStatus | undefined, pollFailed: boolean): boolean;
150
+ /**
151
+ * When a run turns terminal, how long after (ms) the editor re-reads the workflow's saved step
152
+ * tests. The server books every step that succeeded in an editor run as its passing saved test
153
+ * (`step-passes.ts`) — the record the publish gate reads — but only AFTER the engine has
154
+ * checkpointed the run's terminal state, so the poll can see `succeeded` a few milliseconds before
155
+ * the pass lands. The immediate read covers the usual case; the later one covers that window.
156
+ */
157
+ export declare const STEP_PASS_REFRESH_DELAYS_MS: readonly number[];
158
+ /**
159
+ * Should the editor re-read this workflow's saved step tests now? Only once the run it polls is
160
+ * terminal and belongs to the open workflow — a run's passes are booked as it finishes, and the
161
+ * step-tests cache never refetches on its own (`refetchOnWindowFocus: false`), so without this
162
+ * the publish gate keeps the pre-run list and a step that just passed still blocks Publish.
163
+ */
164
+ export declare function stepPassRefreshDue(status: RunStatus | undefined, runWorkflowId: string | undefined, workflowId: string): boolean;
113
165
  /** A1/A6 — what the Run button should read right now. */
114
166
  export declare function runButtonLabel(status: RunStatus | undefined, pollFailed: boolean): string;
115
167
  /**
@@ -122,12 +174,13 @@ export declare function runButtonLabel(status: RunStatus | undefined, pollFailed
122
174
  export declare function classifyCancelError(code: string): CancelErrorOutcome;
123
175
  /**
124
176
  * G5 — map a `RunState`'s opaque `steps` record onto `ExecutionLogPanel`'s
125
- * row shape, in the record's own key order (the order the server checkpoints
126
- * steps in, so it already reads as execution order). `ExecutionLogPanel`'s
127
- * own contract is "never re-sorted" (`ExecutionLogPanel.tsx`'s header), so
128
- * this function must not reorder either.
177
+ * row shape, preserving its keys and timestamps. Record key order is not
178
+ * execution order; `ExecutionLogPanel` sorts the rows chronologically for
179
+ * both live runs and saved history.
129
180
  */
130
- export declare function mapRunStateToLogSteps(steps: Record<string, StepExecutionLike> | undefined): ExecutionLogStepLike[];
181
+ export declare function mapRunStateToLogSteps(steps: Record<string, StepExecutionLike> | undefined, stateLog?: readonly StateLogRow[]): ExecutionLogStepLike[];
182
+ /** `@w6w/ui`'s `SkipReason`, restated locally (see {@link ExecutionLogStepLike}). */
183
+ export type SkipReasonLike = "not_on_path" | "condition" | "stopped" | "disabled";
131
184
  /**
132
185
  * Normalise whatever a step or run reported as its error into the panel's
133
186
  * {@link LogErrorLike}. `null`/`undefined` mean "no error". A non-empty string
@@ -169,4 +222,29 @@ export declare function latestFixtureInput(tests: ReadonlyArray<StepTestFixtureL
169
222
  * click time on purpose — a cached `stepTestsKey` query can be stale, since
170
223
  * saving a fixture does not invalidate it.
171
224
  */
172
- export declare function resolveHeaderRunInput(triggerStepId: string | undefined, list: (stepId: string) => Promise<ReadonlyArray<StepTestFixtureLike> | undefined>): Promise<Record<string, unknown> | undefined>;
225
+ export declare function resolveHeaderRunInput(triggerStepId: string | undefined, list: (stepId: string) => Promise<ReadonlyArray<StepTestFixtureLike> | undefined>, remembered?: () => Promise<ReadonlyArray<{
226
+ nodeId: string;
227
+ input: unknown;
228
+ }> | undefined>): Promise<Record<string, unknown> | undefined>;
229
+ /** The trigger the header Run starts at, and whether it is the Manual trigger. */
230
+ export interface HeaderRunEntry {
231
+ id: string;
232
+ manual: boolean;
233
+ }
234
+ /**
235
+ * The trigger the header Run starts at, and so whose remembered sample payload it runs with: the
236
+ * FIRST Manual trigger (a run's own default entry), else the first other entry trigger — a
237
+ * webhook, a schedule or an app trigger. Before, only a Manual trigger was looked up, so a
238
+ * webhook-started workflow's header Run sent no payload at all. `undefined` when nothing in
239
+ * `steps` is a trigger. The predicates are injected (`@w6w/ui/flow`), as in `trigger-preview.ts`.
240
+ */
241
+ export declare function headerRunEntry(steps: ReadonlyArray<{
242
+ id: string;
243
+ uses?: {
244
+ app?: string;
245
+ trigger?: unknown;
246
+ };
247
+ }>, isManualTrigger: (app: string) => boolean, isEntryTrigger: (uses: {
248
+ app: string;
249
+ trigger?: true;
250
+ }) => boolean): HeaderRunEntry | undefined;