@burdenoff/fluidgrids-fe-sdk 2026.1113.1 → 2026.1113.2

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 (127) hide show
  1. package/dist/{AiStudioPage-D3MAk-bs.js → AiStudioPage-5KYZ8r8-.js} +464 -7
  2. package/dist/{ApprovalInboxPage-Bb8H9ruw.js → ApprovalInboxPage-BsJX4osU.js} +4 -3
  3. package/dist/{ConnectionFormDialog-Bg9_OdHX.js → ConnectionFormDialog-Dr1-CTan.js} +2 -1
  4. package/dist/CreateFluidgridsTagDocument-Dtp9KPLJ.js +1055 -0
  5. package/dist/{DataSinkDashboardPage-Dc2QuJbL.js → DataSinkDashboardPage-8ZDn85e2.js} +1 -1
  6. package/dist/{DataTablesPage-Cv4veaKK.js → DataTablesPage-BBvABuxE.js} +5 -4
  7. package/dist/{EntityTagPicker-D8ws706D.js → EntityTagPicker-BppBSEJk.js} +2 -2
  8. package/dist/{ExecutionDetailPage-CTVGE-d-.js → ExecutionDetailPage-BXDF2eoT.js} +1444 -18
  9. package/dist/ExecutionsListDocument-CzdLPpj5.js +1259 -0
  10. package/dist/{ExecutionsListPage-J0sJPuoM.js → ExecutionsListPage--4BthM8L.js} +285 -11
  11. package/dist/FailureTriagePanel-B5WCSRFo.js +1252 -0
  12. package/dist/{FluidGridsProvider-C4FVNz0n.js → FluidGridsProvider-Dc_Jhtch.js} +8666 -8734
  13. package/dist/{HistoryPage-B9XcTPrO.js → HistoryPage-BvFk2m1m.js} +4 -3
  14. package/dist/{InsightsPage-DFOltjyj.js → InsightsPage-Dwmb02F6.js} +2682 -5
  15. package/dist/{MissingNodesInstallModal-D8H-YNCJ.js → MissingNodesInstallModal-Cze674hp.js} +2 -2
  16. package/dist/{NodesPage-YQ-hP60x.js → NodesPage-C5Dq-ru-.js} +3 -2
  17. package/dist/{OpenTemplateAsCodeButton-hH26HQCk.js → OpenTemplateAsCodeButton-8MZHih6I.js} +2 -2
  18. package/dist/{PaidTemplateConfirm-DoKWMBtR.js → PaidTemplateConfirm-0G-gv29n.js} +1 -1
  19. package/dist/{PublishCustomNodeAction-5XXnSA3H.js → PublishCustomNodeAction-OOY35dUv.js} +1 -1
  20. package/dist/QuotaExhaustedDialog-wvu_-WJ-.js +55 -0
  21. package/dist/{RunnerStatusPill-Ctg4TJcP.js → RunnerStatusPill-Bh7mEsPh.js} +2 -1
  22. package/dist/{RunnersPage-CAt4YDhB.js → RunnersPage-DHeuUDil.js} +2 -2
  23. package/dist/{SettingsPage-DEKPCVVP.js → SettingsPage-CfjXiGpb.js} +694 -14
  24. package/dist/{TagFilterSelect-CwM2-DU6.js → TagFilterSelect-CAShgvbS.js} +1 -1
  25. package/dist/{TaskQueuesPage-Dhi6SIT4.js → TaskQueuesPage--Mmn36b-.js} +3 -3
  26. package/dist/{TemplateCard-DVSd_4zJ.js → TemplateCard-BA_9em22.js} +644 -352
  27. package/dist/{TemplateConfigurationModal-BKi1z7z7.js → TemplateConfigurationModal-BT3PaR0O.js} +4 -3
  28. package/dist/{TemplateDetailPanel-DK1gF7P5.js → TemplateDetailPanel-BxFNnlXm.js} +2 -1
  29. package/dist/{TemplatePreview-DuSX2Q7H.js → TemplatePreview-Bagja6WN.js} +7 -7
  30. package/dist/{TemplatesPage-CAWHh61l.js → TemplatesPage-CAajpJ5w.js} +711 -6
  31. package/dist/{VariablesPage-D7CoatWf.js → VariablesPage-CgUBKhAe.js} +3 -3
  32. package/dist/{VersionDiffDialog-DGwysiyE.js → VersionDiffDialog-D9W5Fm56.js} +233 -129
  33. package/dist/VersionLifecycleBar-DuphLOT0.js +131 -0
  34. package/dist/{WorkflowBuilder-DAeoQi5E.js → WorkflowBuilder-DMpSPElu.js} +16 -13
  35. package/dist/{WorkflowBuilder-BJgqDboZ.js → WorkflowBuilder-v8h8skwG.js} +5 -5
  36. package/dist/WorkflowDefinitionDocument-DZLbTPUZ.js +321 -0
  37. package/dist/WorkflowEditLeaseStatusDocument-Dp62med3.js +627 -0
  38. package/dist/WorkflowProposalCard-zCdUpCk8.js +249 -0
  39. package/dist/WorkflowProposalDocument-BdUbsrd0.js +620 -0
  40. package/dist/{WorkflowSearchRedirect-CMEvd_AR.js → WorkflowSearchRedirect-G78lINAE.js} +1 -1
  41. package/dist/{WorkflowShareDialog-BgHRsDP0.js → WorkflowShareDialog-B7VOgr4-.js} +304 -3
  42. package/dist/WorkflowVersionsDocument-CfPMloRW.js +3195 -0
  43. package/dist/{WorkflowsList-CcQPxWKL.js → WorkflowsList-CPYzfE4K.js} +11 -9
  44. package/dist/agent-form-D0uLvzx4.js +718 -0
  45. package/dist/{agentTools-DKpNKztf.js → agentTools-DTXUqtfu.js} +4 -4
  46. package/dist/api-BZVyX5d2.js +2286 -0
  47. package/dist/api-BqA-f-AD.js +8064 -0
  48. package/dist/app-BJUvugPM.js +3813 -0
  49. package/dist/assert-rules-CPNrO44K.js +318 -0
  50. package/dist/attach-node-helpers-CNsYtLOR.js +32 -0
  51. package/dist/badge-CXfPhoMH.js +26 -0
  52. package/dist/{canvasNoteTools-C_AN79m0.js → canvasNoteTools-DmfZwtnL.js} +3 -3
  53. package/dist/{channel-CZ70oI81.js → channel-DtF1n_ab.js} +1 -1
  54. package/dist/client-Ctl4jmY3.js +127 -0
  55. package/dist/code-editor-diagnostics-Zr0cIcx1.js +46 -0
  56. package/dist/{code-editor-field-Dt6KNWwN.js → code-editor-field-Cv907s3W.js} +2 -2
  57. package/dist/{codec-client-CJ95Rmqo.js → codec-client-CAYl6j46.js} +2 -1
  58. package/dist/conditionBuilder-D8aLGL1w.js +1134 -0
  59. package/dist/config-X97zrIvn.js +600 -0
  60. package/dist/{definition-canvas-Bshn_dSP.js → definition-canvas-DL25_6NH.js} +1 -1
  61. package/dist/{dialog-Cjb4lB4D.js → dialog-BVd152GS.js} +3 -14
  62. package/dist/durable-nodes-DXCCQYWD.js +81 -0
  63. package/dist/errors-xPZ-IbQd.js +142 -0
  64. package/dist/executions.js +2 -2
  65. package/dist/{flow-builder-D3Hl2AFk.js → flow-builder-Dv5fWJ6F.js} +9301 -8189
  66. package/dist/{node-registry-refresh-Dy0uRg9f.js → global-client-DP_Wxe5t.js} +2 -27
  67. package/dist/global-operations-7pvXw_j9.js +145 -0
  68. package/dist/history.js +1 -1
  69. package/dist/{index-CWcP-tG1.d.ts → index-DtMUtXNq.d.ts} +247 -247
  70. package/dist/index.d.ts +1 -1
  71. package/dist/index.js +4403 -46
  72. package/dist/input-ChT6Q-Pr.js +17 -0
  73. package/dist/logs-panel-DtG93ocy.js +342 -0
  74. package/dist/mapping-colors-BIX_YeCQ.js +48 -0
  75. package/dist/{node-detail-view-bVLeYarT.js → node-detail-view-BKI_2kM6.js} +12 -11
  76. package/dist/{node-editor-tab-body-JHHVtQgR.js → node-editor-tab-body-Cc1MWEFR.js} +3 -3
  77. package/dist/{node-operation-selection-DAbS6jSY.js → node-operation-selection-Czeeb2D4.js} +2 -139
  78. package/dist/node-registry-refresh-s3okf83X.js +27 -0
  79. package/dist/nodes.js +1 -1
  80. package/dist/notesKey-5EaREBGA.js +12 -0
  81. package/dist/observability.d.ts +1 -1
  82. package/dist/observability.js +2 -2
  83. package/dist/{popover-Xx3gN7vg.js → popover-DDv7J1Kl.js} +717 -6
  84. package/dist/{quick-config-panel-BvVQXuIL.js → quick-config-panel-BPCRfqWI.js} +4 -4
  85. package/dist/read-only-context-Dt9N9SIG.js +18 -0
  86. package/dist/{schedule-panel-DCb40TPN.js → schedule-panel-C1KwfLCE.js} +14 -12
  87. package/dist/settings.js +1 -1
  88. package/dist/{shared-CMfn30xT.js → shared-Ctl9ZHo1.js} +2 -1
  89. package/dist/{sidebar-node-editor-DxCWFZG2.js → sidebar-node-editor-d9Rlq5pU.js} +4 -4
  90. package/dist/sticky-notes-23j-wL6Q.js +96 -0
  91. package/dist/template-code-dqRunGOv.js +184 -0
  92. package/dist/template-preview-C01KV277.js +29 -0
  93. package/dist/templates.js +2 -2
  94. package/dist/token-refresh-link-BjN7H4qA.js +291 -0
  95. package/dist/useAgentDefinitions-ZKX2N9aP.js +3101 -0
  96. package/dist/useConnections-BlB0gbOB.js +931 -0
  97. package/dist/useDurableParity-BgLH_SCF.js +2088 -0
  98. package/dist/{useEntityTags-Db4Pc8_M.js → useEntityTags-4iW6UTly.js} +227 -3
  99. package/dist/{useExecutions-DOpG1pUQ.js → useExecutions-Bm7q_UwN.js} +6 -3
  100. package/dist/useGlobalStoreClient-DMTrywdX.js +24 -0
  101. package/dist/{useHumanRequests-ZiR0sSzS.js → useHumanRequests-DwG2tQ3f.js} +2 -1
  102. package/dist/{useObservability-rQmwhoZZ.js → useObservability-DPATdbw7.js} +4 -2
  103. package/dist/{useRunHistory-COn6dl7l.js → useRunHistory-CGfjCsJ3.js} +1122 -4
  104. package/dist/{useVariables-2ddyJX4a.js → useVariables-DFdZofLQ.js} +2 -1
  105. package/dist/{useWorkflowCodeExport-ZoUJKRSK.js → useWorkflowCodeExport-D_mCuhKP.js} +289 -4
  106. package/dist/useWorkflowOrganization-r5FWNBs6.js +1690 -0
  107. package/dist/useWorkflowProposal-Bh2_qjmM.js +7478 -0
  108. package/dist/{useWorkflows-jOW60YmD.js → useWorkflows-aqVD7kGa.js} +694 -4
  109. package/dist/workflow-builder.js +5 -5
  110. package/dist/{workflow-code-transfer-l9wO34S0.js → workflow-code-transfer-5C0HUzae.js} +1 -53
  111. package/dist/workflow-host.js +4 -2
  112. package/dist/workflow-pages.d.ts +1 -1
  113. package/dist/workflow-pages.js +19 -19
  114. package/dist/workflow-preview.js +3 -2
  115. package/dist/workflows-list.js +1 -1
  116. package/package.json +1 -1
  117. package/dist/FailureTriagePanel-BbYFYeV_.js +0 -311
  118. package/dist/WorkflowProposalCard-DGMdZC4R.js +0 -1432
  119. package/dist/agent-form-Bfvm32j1.js +0 -83
  120. package/dist/api-DQtHXbIg.js +0 -224
  121. package/dist/api-MeAm6g7E.js +0 -55
  122. package/dist/client-C9XSzSXr.js +0 -1147
  123. package/dist/graphql-operation-types-C9qbvRa8.js +0 -53122
  124. package/dist/useAgentDefinitions-_hgNCK1A.js +0 -550
  125. package/dist/useConnections-Cs7LCWEM.js +0 -401
  126. package/dist/useDurableParity-CcLeCDfv.js +0 -504
  127. package/dist/useWorkflowOrganization-QxVePSkk.js +0 -282
@@ -1,1147 +0,0 @@
1
- "use client";
2
- import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { ApolloClient, ApolloLink, InMemoryCache, Observable, createHttpLink, split } from "@apollo/client/core";
4
- import { setContext } from "@apollo/client/link/context";
5
- import { getMainDefinition } from "@apollo/client/utilities";
6
- import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
7
- import { createClient } from "graphql-ws";
8
- import { create } from "zustand";
9
- import { CombinedGraphQLErrors, ServerError } from "@apollo/client/errors";
10
-
11
- // src/modules/executions/store/executionsStore.ts
12
- const DEFAULT_EXECUTIONS_FILTER = {
13
- status: "all",
14
- workflowKey: "",
15
- dateRange: {
16
- start: null,
17
- end: null,
18
- preset: "all"
19
- },
20
- search: "",
21
- triggerType: "all",
22
- durationMinSeconds: null,
23
- durationMaxSeconds: null,
24
- errorType: "all",
25
- nodeId: "",
26
- failedNodeType: "",
27
- tagId: "",
28
- workflowId: ""
29
- };
30
- const defaultPagination = {
31
- page: 1,
32
- pageSize: 25,
33
- total: 0
34
- };
35
- const useExecutionsStore = create((set) => ({
36
- viewMode: "table",
37
- selectedExecution: null,
38
- expandedRows: /* @__PURE__ */ new Set(),
39
- selection: {
40
- runIds: [],
41
- allSelected: false
42
- },
43
- filter: DEFAULT_EXECUTIONS_FILTER,
44
- pagination: defaultPagination,
45
- isFilterPanelOpen: false,
46
- isDetailsPanelOpen: false,
47
- autoRefresh: true,
48
- autoRefreshInterval: 5e3,
49
- liveRunIds: /* @__PURE__ */ new Set(),
50
- setViewMode: (mode) => set({ viewMode: mode }),
51
- setSelectedExecution: (execution) => set({
52
- selectedExecution: execution,
53
- isDetailsPanelOpen: execution !== null
54
- }),
55
- toggleRowExpanded: (runId) => set((state) => {
56
- const newExpanded = new Set(state.expandedRows);
57
- if (newExpanded.has(runId)) newExpanded.delete(runId);
58
- else newExpanded.add(runId);
59
- return { expandedRows: newExpanded };
60
- }),
61
- setSelection: (selection) => set({ selection }),
62
- toggleSelectRun: (runId) => set((state) => {
63
- return { selection: {
64
- runIds: state.selection.runIds.includes(runId) ? state.selection.runIds.filter((id) => id !== runId) : [...state.selection.runIds, runId],
65
- allSelected: false
66
- } };
67
- }),
68
- selectAll: (runIds) => set((state) => ({ selection: {
69
- runIds: state.selection.allSelected ? [] : runIds,
70
- allSelected: !state.selection.allSelected
71
- } })),
72
- clearSelection: () => set({ selection: {
73
- runIds: [],
74
- allSelected: false
75
- } }),
76
- updateFilter: (filterUpdate) => set((state) => ({
77
- filter: {
78
- ...state.filter,
79
- ...filterUpdate
80
- },
81
- pagination: {
82
- ...state.pagination,
83
- page: 1
84
- }
85
- })),
86
- setStatusFilter: (status) => set((state) => ({
87
- filter: {
88
- ...state.filter,
89
- status
90
- },
91
- pagination: {
92
- ...state.pagination,
93
- page: 1
94
- }
95
- })),
96
- setWorkflowFilter: (workflowKey) => set((state) => ({
97
- filter: {
98
- ...state.filter,
99
- workflowKey
100
- },
101
- pagination: {
102
- ...state.pagination,
103
- page: 1
104
- }
105
- })),
106
- setDateRange: (dateRange) => set((state) => ({
107
- filter: {
108
- ...state.filter,
109
- dateRange
110
- },
111
- pagination: {
112
- ...state.pagination,
113
- page: 1
114
- }
115
- })),
116
- setSearchQuery: (search) => set((state) => ({
117
- filter: {
118
- ...state.filter,
119
- search
120
- },
121
- pagination: {
122
- ...state.pagination,
123
- page: 1
124
- }
125
- })),
126
- setTriggerTypeFilter: (triggerType) => set((state) => ({
127
- filter: {
128
- ...state.filter,
129
- triggerType
130
- },
131
- pagination: {
132
- ...state.pagination,
133
- page: 1
134
- }
135
- })),
136
- setDurationFilter: (durationMinSeconds, durationMaxSeconds) => set((state) => ({
137
- filter: {
138
- ...state.filter,
139
- durationMinSeconds,
140
- durationMaxSeconds
141
- },
142
- pagination: {
143
- ...state.pagination,
144
- page: 1
145
- }
146
- })),
147
- setErrorTypeFilter: (errorType) => set((state) => ({
148
- filter: {
149
- ...state.filter,
150
- errorType
151
- },
152
- pagination: {
153
- ...state.pagination,
154
- page: 1
155
- }
156
- })),
157
- setNodeFilter: (nodeId) => set((state) => ({
158
- filter: {
159
- ...state.filter,
160
- nodeId
161
- },
162
- pagination: {
163
- ...state.pagination,
164
- page: 1
165
- }
166
- })),
167
- setFailedNodeTypeFilter: (failedNodeType) => set((state) => ({
168
- filter: {
169
- ...state.filter,
170
- failedNodeType
171
- },
172
- pagination: {
173
- ...state.pagination,
174
- page: 1
175
- }
176
- })),
177
- setTagFilter: (tagId) => set((state) => ({
178
- filter: {
179
- ...state.filter,
180
- tagId
181
- },
182
- pagination: {
183
- ...state.pagination,
184
- page: 1
185
- }
186
- })),
187
- setWorkflowIdFilter: (workflowId) => set((state) => ({
188
- filter: {
189
- ...state.filter,
190
- workflowId
191
- },
192
- pagination: {
193
- ...state.pagination,
194
- page: 1
195
- }
196
- })),
197
- resetFilters: () => set({
198
- filter: DEFAULT_EXECUTIONS_FILTER,
199
- pagination: { ...defaultPagination }
200
- }),
201
- setPage: (page) => set((state) => ({ pagination: {
202
- ...state.pagination,
203
- page
204
- } })),
205
- setPageSize: (pageSize) => set((state) => ({ pagination: {
206
- ...state.pagination,
207
- pageSize,
208
- page: 1
209
- } })),
210
- setTotal: (total) => set((state) => ({ pagination: {
211
- ...state.pagination,
212
- total
213
- } })),
214
- toggleFilterPanel: () => set((state) => ({ isFilterPanelOpen: !state.isFilterPanelOpen })),
215
- toggleDetailsPanel: () => set((state) => ({ isDetailsPanelOpen: !state.isDetailsPanelOpen })),
216
- setAutoRefresh: (enabled) => set({ autoRefresh: enabled }),
217
- setAutoRefreshInterval: (interval) => set({ autoRefreshInterval: interval }),
218
- addLiveRun: (runId) => set((state) => {
219
- const newLiveRunIds = new Set(state.liveRunIds);
220
- newLiveRunIds.add(runId);
221
- return { liveRunIds: newLiveRunIds };
222
- }),
223
- removeLiveRun: (runId) => set((state) => {
224
- const newLiveRunIds = new Set(state.liveRunIds);
225
- newLiveRunIds.delete(runId);
226
- return { liveRunIds: newLiveRunIds };
227
- }),
228
- clearLiveRuns: () => set({ liveRunIds: /* @__PURE__ */ new Set() })
229
- }));
230
-
231
- // src/theme/fluidgridsTheme.ts
232
- const color = (...cssVariables) => ({
233
- kind: "color",
234
- cssVariables
235
- });
236
- const length = (...cssVariables) => ({
237
- kind: "length",
238
- cssVariables
239
- });
240
- const font = (...cssVariables) => ({
241
- kind: "font",
242
- cssVariables
243
- });
244
- /**
245
- * Theme token → the fe-libs semantic CSS variables it sets. This table is the
246
- * public contract: a token keeps its variables across minor releases.
247
- */
248
- const FLUIDGRIDS_THEME_TOKEN_MAP = {
249
- "colors.bgCanvas": color("--color-bg-canvas"),
250
- "colors.bgSurface": color("--color-bg-surface"),
251
- "colors.bgElevated": color("--color-bg-elevated"),
252
- "colors.bgSunken": color("--color-bg-sunken"),
253
- "colors.textPrimary": color("--color-text-primary"),
254
- "colors.textSecondary": color("--color-text-secondary"),
255
- "colors.textMuted": color("--color-text-muted"),
256
- "colors.borderDefault": color("--color-border-default"),
257
- "colors.borderSubtle": color("--color-border-subtle"),
258
- "colors.borderStrong": color("--color-border-strong"),
259
- "colors.primary": color("--color-action-primary-bg", "--color-action-primary-border"),
260
- "colors.primaryHover": color("--color-action-primary-bgHover"),
261
- "colors.primaryActive": color("--color-action-primary-bgActive"),
262
- "colors.primaryText": color("--color-action-primary-text"),
263
- "colors.success": color("--color-status-success-bg"),
264
- "colors.warning": color("--color-status-warning-bg"),
265
- "colors.error": color("--color-status-error-bg"),
266
- "colors.info": color("--color-status-info-bg"),
267
- "radius.base": length("--radius"),
268
- "radius.card": length("--radius-card"),
269
- "radius.button": length("--radius-button"),
270
- "radius.input": length("--radius-input"),
271
- "font.body": font("--font-family-body", "--font-sans"),
272
- "font.heading": font("--font-family-heading"),
273
- "font.mono": font("--font-family-mono", "--font-mono")
274
- };
275
- const MAX_VALUE_LENGTH = 200;
276
- const FORBIDDEN_VALUE = /[;{}<>\\!@"`]|\/\*|\*\/|url\s*\(|expression\s*\(|image\s*\(|image-set\s*\(|javascript:|@import/i;
277
- const HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
278
- const NAMED_COLOR = /^[a-z]{3,30}$/i;
279
- const COLOR_FUNCTION = /^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix)\([a-z0-9#%.,/\s()+-]*\)$/i;
280
- const CSS_VAR = /^var\(--[a-z0-9-]+\)$/i;
281
- const LENGTH = /^(?:0|\d*\.?\d+(?:px|rem|em|%|vh|vw|vmin|vmax|ch|ex|pt))$/i;
282
- const CALC = /^calc\([a-z0-9%.\s()*/+-]*\)$/i;
283
- const FONT_FAMILY_ITEM = /^(?:[a-z0-9][a-z0-9 _-]*|'[a-z0-9 _-]+')$/i;
284
- function balancedParentheses(value) {
285
- let depth = 0;
286
- for (const char of value) {
287
- if (char === "(") depth += 1;
288
- if (char === ")") depth -= 1;
289
- if (depth < 0) return false;
290
- }
291
- return depth === 0;
292
- }
293
- /**
294
- * Validate one token value. Returns the trimmed value, or null when it is not
295
- * a plain CSS value of the expected kind.
296
- */
297
- function sanitizeThemeValue(kind, value) {
298
- if (typeof value !== "string") return null;
299
- const trimmed = value.trim();
300
- if (!trimmed || trimmed.length > MAX_VALUE_LENGTH) return null;
301
- if (FORBIDDEN_VALUE.test(trimmed) || !balancedParentheses(trimmed)) return null;
302
- if (CSS_VAR.test(trimmed)) return trimmed;
303
- switch (kind) {
304
- case "color": return HEX_COLOR.test(trimmed) || NAMED_COLOR.test(trimmed) || COLOR_FUNCTION.test(trimmed) ? trimmed : null;
305
- case "length": return LENGTH.test(trimmed) || CALC.test(trimmed) ? trimmed : null;
306
- case "font": {
307
- const items = trimmed.split(",").map((item) => item.trim());
308
- return items.every((item) => FONT_FAMILY_ITEM.test(item)) ? items.join(", ") : null;
309
- }
310
- }
311
- }
312
- function warnDropped(key, value) {
313
- console.warn(`[FluidGrids SDK] theme token "${key}" ignored: ${JSON.stringify(value)} is not a plain CSS value.`);
314
- }
315
- /** Relative luminance of a hex colour, or null for any other notation. */
316
- function hexLuminance(value) {
317
- if (!HEX_COLOR.test(value)) return null;
318
- let hex = value.slice(1);
319
- if (hex.length <= 4) hex = hex.split("").map((digit) => digit + digit).join("");
320
- const [red = 0, green = 0, blue = 0] = [
321
- 0,
322
- 2,
323
- 4
324
- ].map((offset) => {
325
- const channel = parseInt(hex.slice(offset, offset + 2), 16) / 255;
326
- return channel <= .03928 ? channel / 12.92 : Math.pow((channel + .055) / 1.055, 2.4);
327
- });
328
- return .2126 * red + .7152 * green + .0722 * blue;
329
- }
330
- /**
331
- * The resolved mode of a theme: the shortcut itself, `theme.mode`, or
332
- * undefined when the theme leaves the mode to the host.
333
- */
334
- function themeMode(theme) {
335
- if (theme === "light" || theme === "dark") return theme;
336
- if (theme && typeof theme === "object") return theme.mode === "light" || theme.mode === "dark" ? theme.mode : void 0;
337
- }
338
- function groupEntries(theme, group) {
339
- const values = theme[group];
340
- if (!values || typeof values !== "object") return [];
341
- return Object.entries(values);
342
- }
343
- function isTokenKey(key) {
344
- return Object.prototype.hasOwnProperty.call(FLUIDGRIDS_THEME_TOKEN_MAP, key);
345
- }
346
- /**
347
- * Turn a theme into the CSS custom properties it sets. Pure: no DOM access.
348
- *
349
- * Values that are not plain CSS values of the token's kind (anything with
350
- * `;`, `{`, `}`, `url(`, `expression(`, quotes other than single-quoted font
351
- * names, …) are dropped with a console warning. Unknown keys are dropped the
352
- * same way. When `colors.primary` is set, the hover / pressed shades and the
353
- * text colour are derived from it unless they are set explicitly; the text
354
- * colour is picked for contrast when `primary` is a hex colour.
355
- */
356
- function themeToCssVariables(theme) {
357
- const variables = {};
358
- if (!theme || typeof theme !== "object") return variables;
359
- const colors = {};
360
- for (const group of [
361
- "colors",
362
- "radius",
363
- "font"
364
- ]) for (const [name, raw] of groupEntries(theme, group)) {
365
- if (raw === void 0) continue;
366
- const key = `${group}.${name}`;
367
- if (!isTokenKey(key)) {
368
- warnDropped(key, raw);
369
- continue;
370
- }
371
- const mapping = FLUIDGRIDS_THEME_TOKEN_MAP[key];
372
- const value = sanitizeThemeValue(mapping.kind, raw);
373
- if (value === null) {
374
- warnDropped(key, raw);
375
- continue;
376
- }
377
- if (group === "colors") colors[name] = value;
378
- for (const cssVariable of mapping.cssVariables) variables[cssVariable] = value;
379
- }
380
- const primary = colors.primary;
381
- if (primary) {
382
- if (!colors.primaryHover) variables["--color-action-primary-bgHover"] = `color-mix(in oklab, ${primary} 84%, var(--palette-black))`;
383
- if (!colors.primaryActive) variables["--color-action-primary-bgActive"] = `color-mix(in oklab, ${primary} 70%, var(--palette-black))`;
384
- if (!colors.primaryText) {
385
- const luminance = hexLuminance(primary);
386
- if (luminance !== null) variables["--color-action-primary-text"] = luminance > .4 ? "var(--palette-black)" : "var(--palette-white)";
387
- }
388
- }
389
- return variables;
390
- }
391
- const stack = [];
392
- let nextId = 1;
393
- let observer = null;
394
- const hostVariables = /* @__PURE__ */ new Map();
395
- const appliedVariables = /* @__PURE__ */ new Map();
396
- let hostMode = null;
397
- function readHostMode(root) {
398
- return {
399
- dataTheme: root.getAttribute("data-theme"),
400
- dark: root.classList.contains("dark")
401
- };
402
- }
403
- function writeMode(root, mode) {
404
- if (mode.dataTheme === null) root.removeAttribute("data-theme");
405
- else if (root.getAttribute("data-theme") !== mode.dataTheme) root.setAttribute("data-theme", mode.dataTheme);
406
- if (root.classList.contains("dark") !== mode.dark) root.classList.toggle("dark", mode.dark);
407
- }
408
- function modeDrifted(root, mode) {
409
- return root.getAttribute("data-theme") !== mode || root.classList.contains("dark") !== (mode === "dark");
410
- }
411
- function restoreVariable(root, name) {
412
- const previous = hostVariables.get(name) ?? "";
413
- if (previous) root.style.setProperty(name, previous);
414
- else root.style.removeProperty(name);
415
- hostVariables.delete(name);
416
- appliedVariables.delete(name);
417
- }
418
- function forceVariable(root, name, value) {
419
- root.style.setProperty(name, value);
420
- appliedVariables.set(name, root.style.getPropertyValue(name));
421
- }
422
- /** Re-force the winning theme where something else changed it. */
423
- function reconcileDrift() {
424
- const top = stack.at(-1);
425
- if (!top || typeof document === "undefined") return;
426
- const root = document.documentElement;
427
- for (const [name, value] of Object.entries(top.variables)) {
428
- const current = root.style.getPropertyValue(name);
429
- if (current !== appliedVariables.get(name)) {
430
- hostVariables.set(name, current);
431
- forceVariable(root, name, value);
432
- }
433
- }
434
- if (top.mode && modeDrifted(root, top.mode)) {
435
- hostMode = readHostMode(root);
436
- writeMode(root, {
437
- dataTheme: top.mode,
438
- dark: top.mode === "dark"
439
- });
440
- }
441
- }
442
- /** Make the document match the top of the stack. */
443
- function sync() {
444
- if (typeof document === "undefined") return;
445
- const root = document.documentElement;
446
- const top = stack.at(-1);
447
- const wanted = top?.variables ?? {};
448
- for (const name of [...appliedVariables.keys()]) if (!(name in wanted)) restoreVariable(root, name);
449
- for (const [name, value] of Object.entries(wanted)) {
450
- if (!hostVariables.has(name)) hostVariables.set(name, root.style.getPropertyValue(name));
451
- if (appliedVariables.get(name) !== value) forceVariable(root, name, value);
452
- }
453
- if (top?.mode) {
454
- if (!hostMode) hostMode = readHostMode(root);
455
- writeMode(root, {
456
- dataTheme: top.mode,
457
- dark: top.mode === "dark"
458
- });
459
- } else if (hostMode) {
460
- writeMode(root, hostMode);
461
- hostMode = null;
462
- }
463
- if (!top) {
464
- observer?.disconnect();
465
- observer = null;
466
- return;
467
- }
468
- if (!observer && typeof MutationObserver !== "undefined") {
469
- observer = new MutationObserver(reconcileDrift);
470
- observer.observe(root, {
471
- attributes: true,
472
- attributeFilter: [
473
- "style",
474
- "data-theme",
475
- "class"
476
- ]
477
- });
478
- }
479
- }
480
- /**
481
- * Apply a theme to the document and return a function that removes it again.
482
- *
483
- * Sets each variable from {@link themeToCssVariables} as an inline custom
484
- * property on `document.documentElement`, and for a mode sets `data-theme`
485
- * and the `.dark` class. While applied, the values are re-forced if something
486
- * else (a host ThemeProvider) rewrites them. Disposing restores the inline
487
- * values and mode the document had (including any change the host made in
488
- * between). When several themes are applied, the most recent one wins.
489
- * Calling the returned function more than once is safe. Without a DOM
490
- * (server rendering) it does nothing.
491
- */
492
- function applyFluidGridsTheme(theme) {
493
- if (typeof document === "undefined") return () => void 0;
494
- const entry = {
495
- id: nextId++,
496
- variables: themeToCssVariables(theme),
497
- mode: themeMode(theme)
498
- };
499
- stack.push(entry);
500
- sync();
501
- let disposed = false;
502
- return () => {
503
- if (disposed) return;
504
- disposed = true;
505
- const index = stack.findIndex((active) => active.id === entry.id);
506
- if (index >= 0) stack.splice(index, 1);
507
- sync();
508
- };
509
- }
510
- /**
511
- * Stable key for a theme value, so callers can tell two theme objects with the
512
- * same content apart from a real change (hosts often pass inline objects).
513
- */
514
- function themeKey(theme) {
515
- if (!theme) return "";
516
- if (typeof theme === "string") return theme;
517
- const sortedGroup = (group) => groupEntries(theme, group).filter(([, value]) => value !== void 0).sort(([a], [b]) => a.localeCompare(b));
518
- return JSON.stringify([
519
- theme.mode ?? null,
520
- sortedGroup("colors"),
521
- sortedGroup("radius"),
522
- sortedGroup("font")
523
- ]);
524
- }
525
-
526
- // src/config/index.ts
527
- var config_exports = /* @__PURE__ */ __exportAll({ getFluidGridsConfig: () => getFluidGridsConfig });
528
- let runtimeConfig = {
529
- apiGatewayUrl: "",
530
- workspaceId: ""
531
- };
532
- /**
533
- * Get current configuration via proxy for live access
534
- */
535
- const config = new Proxy({}, { get(_, prop) {
536
- return runtimeConfig[prop];
537
- } });
538
- /**
539
- * Get the full configuration object
540
- */
541
- function getFluidGridsConfig() {
542
- return { ...runtimeConfig };
543
- }
544
- /**
545
- * Set runtime configuration
546
- */
547
- function setFluidGridsConfig(newConfig) {
548
- const previous = runtimeConfig;
549
- runtimeConfig = {
550
- ...runtimeConfig,
551
- ...newConfig
552
- };
553
- if (previous.authToken !== runtimeConfig.authToken || previous.workspaceToken !== runtimeConfig.workspaceToken) tokenRevision += 1;
554
- if ("theme" in newConfig) syncConfigTheme(newConfig.theme);
555
- }
556
- /**
557
- * Reset configuration to defaults
558
- */
559
- function resetFluidGridsConfig() {
560
- tokenRevision += 1;
561
- runtimeConfig = {
562
- apiGatewayUrl: "",
563
- workspaceId: ""
564
- };
565
- syncConfigTheme(void 0);
566
- }
567
- let tokenRevision = 0;
568
- /** Current token revision. Internal: used by the SDK's Apollo clients. */
569
- function getFluidGridsTokenRevision() {
570
- return tokenRevision;
571
- }
572
- let configTheme = null;
573
- function syncConfigTheme(theme) {
574
- const key = themeKey(theme);
575
- if (configTheme?.key === key) return;
576
- const previous = configTheme;
577
- configTheme = key ? {
578
- key,
579
- dispose: applyFluidGridsTheme(theme)
580
- } : null;
581
- previous?.dispose();
582
- }
583
- function normalizeOrigin(origin) {
584
- return origin.replace(/\/$/, "");
585
- }
586
- function validateTrustedOrigin(trustedOrigins) {
587
- if (typeof window === "undefined" || !trustedOrigins?.length) return;
588
- const currentOrigin = normalizeOrigin(window.location.origin);
589
- if (!trustedOrigins.map(normalizeOrigin).includes(currentOrigin)) throw new Error(`FluidGrids SDK blocked untrusted origin: ${currentOrigin}`);
590
- }
591
- /**
592
- * Get the GraphQL endpoint URL
593
- */
594
- function getGraphQLEndpoint(baseUrl) {
595
- const url = baseUrl || runtimeConfig.apiGatewayUrl;
596
- if (!url) throw new Error("FluidGrids SDK requires apiGatewayUrl to be configured");
597
- const normalizedBase = url.endsWith("/") ? url.slice(0, -1) : url;
598
- if (normalizedBase.endsWith("/graphql")) return normalizedBase;
599
- return `${normalizedBase}/workspaces/graphql`;
600
- }
601
- /** Clear user execution state only when a host session ends or changes identity. */
602
- function resetFluidGridsExecutionState() {
603
- useExecutionsStore.setState(useExecutionsStore.getInitialState(), true);
604
- }
605
-
606
- // src/graphql/live-tokens.ts
607
- /**
608
- * Token reader for an SDK-created Apollo client (BOFF-9961).
609
- *
610
- * The client starts with the tokens it was created with. Once a token is set
611
- * in the SDK config after that — a refresh through `auth.onTokenExpired`, or
612
- * a host calling `setFluidGridsConfig({ authToken })` — every later request
613
- * reads the configured value instead. Reading per request (never capturing
614
- * the token in a closure) is what lets a refreshed token reach in-flight
615
- * retries and websocket reconnects.
616
- */
617
- function createLiveTokenReader(initial) {
618
- const createdAt = getFluidGridsTokenRevision();
619
- return () => {
620
- if (getFluidGridsTokenRevision() === createdAt) return initial;
621
- return {
622
- authToken: config.authToken ?? initial.authToken,
623
- workspaceToken: config.workspaceToken ?? initial.workspaceToken
624
- };
625
- };
626
- }
627
-
628
- // src/graphql/errors.ts
629
- /**
630
- * Operation-error helpers.
631
- *
632
- * The SDK usually runs against a host-injected Apollo client (see
633
- * `setFluidGridsClient`). App shells configure their shared client with
634
- * `errorPolicy: 'all'`, so `client.mutate` / `client.query` RESOLVE on
635
- * GraphQL errors — `result.data` is empty and the real failure sits in
636
- * `result.error`. Callers that only check `result.data` silently swallow
637
- * server errors (e.g. QUOTA_EXHAUSTED) and surface generic messages.
638
- *
639
- * Use `throwIfResultError(result)` right after a mutate/query so failures
640
- * flow through the existing catch paths, and `describeOperationError(err)`
641
- * to turn whatever was thrown into a user-presentable message.
642
- */
643
- /** Well-known gateway error codes mapped to user-friendly messages. */
644
- function describeGraphQLError(error) {
645
- switch (typeof error.extensions?.code === "string" ? error.extensions.code : void 0) {
646
- case "QUOTA_EXHAUSTED": {
647
- const quota = /Quota exhausted for (\S+)/.exec(error.message)?.[1];
648
- return `Quota exhausted — your workspace has used up its ${quota ? `"${quota}" quota` : "usage quota"}. Upgrade your plan or wait for the quota to reset.`;
649
- }
650
- case "QUOTA_NOT_PROVISIONED": return "This workspace's plan does not include this usage allowance yet. Contact your workspace admin or support to have it provisioned.";
651
- case "UNAUTHENTICATED": return "Your session has expired. Please sign in again.";
652
- case "FORBIDDEN": return error.message || "You don't have permission to perform this action.";
653
- default: return error.message || "Unknown error";
654
- }
655
- }
656
- /** The first GraphQL error of a raw JSON response body, if it carries one. */
657
- function firstGraphQLErrorInBody(bodyText) {
658
- try {
659
- const parsed = JSON.parse(bodyText);
660
- if (typeof parsed !== "object" || parsed === null) return null;
661
- const errors = parsed.errors;
662
- if (!Array.isArray(errors) || errors.length === 0) return null;
663
- const first = errors[0];
664
- if (typeof first !== "object" || first === null) return null;
665
- if (typeof first.message !== "string") return null;
666
- return first;
667
- } catch {
668
- return null;
669
- }
670
- }
671
- /**
672
- * Turn anything thrown (or found in `result.error`) into a message fit for
673
- * a toast / run log. Unwraps Apollo `CombinedGraphQLErrors` so well-known
674
- * gateway codes (quota, auth) read properly instead of a concatenated dump.
675
- */
676
- function describeOperationError(error, fallback = "Unknown error") {
677
- if (CombinedGraphQLErrors.is(error)) {
678
- const first = error.errors[0];
679
- return first ? describeGraphQLError(first) : error.message || fallback;
680
- }
681
- if (ServerError.is(error)) {
682
- const first = firstGraphQLErrorInBody(error.bodyText);
683
- if (first) return describeGraphQLError(first);
684
- }
685
- if (error instanceof Error) return error.message || fallback;
686
- if (typeof error === "string" && error.length > 0) return error;
687
- return fallback;
688
- }
689
- /**
690
- * Throw `result.error` when the injected client resolved an errored
691
- * operation (errorPolicy 'all'). Returns the result unchanged otherwise, so
692
- * it can be chained inline:
693
- *
694
- * ```ts
695
- * const result = throwIfResultError(await client.mutate({ ... }));
696
- * ```
697
- */
698
- function throwIfResultError(result) {
699
- if (result.error) throw new Error(describeOperationError(result.error), { cause: result.error });
700
- return result;
701
- }
702
- /** True when a GraphQL error list carries a FORBIDDEN code. */
703
- function hasForbiddenCode(errors) {
704
- return (errors ?? []).some((error) => error.extensions?.code === "FORBIDDEN");
705
- }
706
- /**
707
- * True when an operation failed because the caller lacks the permission
708
- * (gateway `@rbac` denial). Accepts the raw Apollo error, a `result.error`,
709
- * or the Error `throwIfResultError` throws (the original sits in `cause`).
710
- * Pages use it to render a no-access state instead of a load failure.
711
- */
712
- function isForbiddenError(error) {
713
- if (CombinedGraphQLErrors.is(error)) return hasForbiddenCode(error.errors);
714
- if (ServerError.is(error)) {
715
- if (error.statusCode === 403) return true;
716
- const first = firstGraphQLErrorInBody(error.bodyText);
717
- return first ? hasForbiddenCode([first]) : false;
718
- }
719
- if (error instanceof Error && error.cause !== void 0) return isForbiddenError(error.cause);
720
- return false;
721
- }
722
- const REFRESHABLE_AUTH_FAILURES = /* @__PURE__ */ new Set(["CREDENTIAL_EXPIRED", "CREDENTIAL_REVOKED"]);
723
- const EXPIRED_TOKEN_MESSAGE = /\b(?:jwt|token)\b[^.]{0,40}\bexpired\b|\bexpired\b[^.]{0,20}\b(?:jwt|token)\b/i;
724
- function isUnauthenticatedGraphQLError(error) {
725
- if (error.extensions?.code === "UNAUTHENTICATED") return true;
726
- const authFailure = error.extensions?.authFailure;
727
- if (typeof authFailure === "string" && REFRESHABLE_AUTH_FAILURES.has(authFailure)) return true;
728
- return typeof error.message === "string" && EXPIRED_TOKEN_MESSAGE.test(error.message);
729
- }
730
- /**
731
- * True when a GraphQL error list says the caller's credential expired or is
732
- * not accepted: an `UNAUTHENTICATED` code, an expired / revoked
733
- * `authFailure` category, or an expired-token message.
734
- */
735
- function hasUnauthenticatedGraphQLError(errors) {
736
- return (errors ?? []).some(isUnauthenticatedGraphQLError);
737
- }
738
- /**
739
- * True when an operation failed because the session expired: HTTP 401, a
740
- * GraphQL `UNAUTHENTICATED` error, or an expired / revoked credential.
741
- * Accepts the raw Apollo error, a `result.error`, or the Error
742
- * `throwIfResultError` throws (the original sits in `cause`). A 503
743
- * `AUTH_SERVICE_UNAVAILABLE` is not an expired session and returns false.
744
- */
745
- function isUnauthenticatedError(error) {
746
- if (CombinedGraphQLErrors.is(error)) return hasUnauthenticatedGraphQLError(error.errors);
747
- if (ServerError.is(error)) {
748
- if (error.statusCode === 401) return true;
749
- const first = firstGraphQLErrorInBody(error.bodyText);
750
- return first ? isUnauthenticatedGraphQLError(first) : false;
751
- }
752
- if (error instanceof Error && error.cause !== void 0) return isUnauthenticatedError(error.cause);
753
- return false;
754
- }
755
- /** The gateway error code of an operation failure, when it carries one. */
756
- function operationErrorCode(error) {
757
- const codeOf = (graphQLError) => {
758
- const code = graphQLError?.extensions?.code;
759
- return typeof code === "string" ? code : void 0;
760
- };
761
- if (CombinedGraphQLErrors.is(error)) return codeOf(error.errors[0]);
762
- if (ServerError.is(error)) return codeOf(firstGraphQLErrorInBody(error.bodyText) ?? void 0);
763
- if (error instanceof Error && error.cause !== void 0) return operationErrorCode(error.cause);
764
- }
765
-
766
- // src/events/eventBus.ts
767
- /**
768
- * Host event bus for embedders (BOFF-9961).
769
- *
770
- * The SDK reports what happens inside the embedded builder to the host page
771
- * through one typed callback, `onEvent`, set on `FluidGridsConfig` (or the
772
- * `onEvent` prop of `FluidGridsProvider`). Hosts use it to sync their own UI,
773
- * analytics or backend: a workflow was saved or activated, a run started or
774
- * finished, the user selected a node, something failed.
775
- *
776
- * This bus is separate from the product-analytics emitters in
777
- * `analytics/fluidgridsEvents.ts`, which publish `fluidgrids.*` facts on the
778
- * fe-libs shell bus for Rybbit/OTel. Those stay as they are; this one is the
779
- * documented, versioned contract for third-party hosts.
780
- *
781
- * Versioning: {@link FLUIDGRIDS_EVENTS_VERSION} follows semver.
782
- * - A new event name, or a new optional payload field, is a minor bump.
783
- * - Removing or renaming an event, removing a payload field, or changing a
784
- * field's type or meaning is a major bump (and a breaking SDK release).
785
- *
786
- * Delivery: events are synchronous and best effort. A handler that throws is
787
- * caught and logged with a warning; it never breaks the SDK action that
788
- * emitted the event.
789
- *
790
- * @module events/eventBus
791
- */
792
- /** Version of the host event contract. See the module note for the rules. */
793
- const FLUIDGRIDS_EVENTS_VERSION = "1.1.0";
794
- /** Every host event name, in documentation order. */
795
- const FLUIDGRIDS_EVENT_NAMES = [
796
- "workflow:saved",
797
- "workflow:activated",
798
- "run:started",
799
- "run:completed",
800
- "node:selected",
801
- "error",
802
- "auth:failed",
803
- "builder:fullscreen"
804
- ];
805
- /** Drop undefined keys so hosts see only the fields that are known. */
806
- function withoutUndefined(payload) {
807
- return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== void 0));
808
- }
809
- /**
810
- * Deliver one host event. Never throws: a missing handler is a no-op and a
811
- * handler that throws is logged, so SDK code can call this anywhere.
812
- */
813
- function emitFluidGridsEvent(name, payload) {
814
- let handler;
815
- try {
816
- handler = config.onEvent;
817
- } catch {
818
- return;
819
- }
820
- if (typeof handler !== "function") return;
821
- try {
822
- const args = [name, withoutUndefined(payload)];
823
- handler(...args);
824
- } catch (error) {
825
- console.warn(`[FluidGrids SDK] onEvent handler failed for ${name}`, error);
826
- }
827
- }
828
- /**
829
- * Emit an `error` event for a failed SDK operation: the user-presentable
830
- * message and, when the gateway sent one, its error code.
831
- */
832
- function emitOperationErrorEvent(source, error) {
833
- emitFluidGridsEvent("error", {
834
- source,
835
- message: describeOperationError(error),
836
- code: operationErrorCode(error)
837
- });
838
- }
839
- const MAX_COMPLETED_RUNS = 500;
840
- const completedRuns = /* @__PURE__ */ new Set();
841
- /**
842
- * Emit `run:completed` once per run id. Later calls for the same run (a
843
- * second poll that sees the terminal status, a remounted builder following
844
- * the same run) are ignored.
845
- */
846
- function emitRunCompletedOnce(payload) {
847
- if (!payload.runId || completedRuns.has(payload.runId)) return;
848
- completedRuns.add(payload.runId);
849
- if (completedRuns.size > MAX_COMPLETED_RUNS) {
850
- const oldest = completedRuns.values().next().value;
851
- if (oldest !== void 0) completedRuns.delete(oldest);
852
- }
853
- emitFluidGridsEvent("run:completed", payload);
854
- }
855
-
856
- // src/graphql/token-refresh-link.ts
857
- /**
858
- * Token refresh on expiry (BOFF-9961).
859
- *
860
- * Workspace tokens are short-lived, so a builder left open outlives the token
861
- * it was mounted with. The gateway then answers 401 with a GraphQL
862
- * `UNAUTHENTICATED` error (category `CREDENTIAL_EXPIRED` / `_REVOKED`). This
863
- * link catches that, asks the host for a fresh token through
864
- * `auth.onTokenExpired`, stores it in the SDK config, and retries the failed
865
- * operation once with the new headers.
866
- *
867
- * Rules:
868
- * - one refresh at a time: operations that fail while a refresh is running
869
- * wait for the same promise instead of calling the host again, and an
870
- * operation that was sent before a refresh finished retries with the
871
- * token that refresh produced;
872
- * - each operation is retried at most once, so a token the gateway keeps
873
- * refusing can never loop;
874
- * - when the host cannot refresh (the callback rejects or returns no token),
875
- * `auth:failed` is emitted and the operation fails with its ORIGINAL error;
876
- * - an operation sent before a refresh attempt that has since FAILED does
877
- * not start a new one: its 401 is the same expired session that attempt
878
- * already reported, so it fails with its original error and no second
879
- * `auth:failed`. Only an operation sent after the failure (the host may
880
- * have recovered, e.g. the user signed in again) may try a fresh refresh.
881
- *
882
- * @module graphql/token-refresh-link
883
- */
884
- let inFlight = null;
885
- let refreshGeneration = 0;
886
- let lastRefreshed = null;
887
- let settledAttempts = 0;
888
- let lastFailedAttempt = 0;
889
- function normalizeTokens(value) {
890
- if (typeof value === "string") return value.trim() ? { authToken: value } : null;
891
- if (!value || typeof value !== "object") return null;
892
- const tokens = {};
893
- if (typeof value.authToken === "string" && value.authToken.trim()) tokens.authToken = value.authToken;
894
- if (typeof value.workspaceToken === "string" && value.workspaceToken.trim()) tokens.workspaceToken = value.workspaceToken;
895
- return tokens.authToken || tokens.workspaceToken ? tokens : null;
896
- }
897
- function storeInConfig(tokens) {
898
- setFluidGridsConfig(tokens);
899
- }
900
- /**
901
- * Run the host refresh callback, sharing one call between concurrent
902
- * failures. Resolves with the fresh tokens, or null when the host could not
903
- * provide any (after emitting `auth:failed`).
904
- */
905
- function refreshTokens(callback, store) {
906
- if (inFlight) return inFlight;
907
- const fail = (message) => {
908
- settledAttempts += 1;
909
- lastFailedAttempt = settledAttempts;
910
- emitFluidGridsEvent("auth:failed", { message });
911
- return null;
912
- };
913
- const run = (async () => {
914
- try {
915
- const tokens = normalizeTokens(await callback());
916
- if (!tokens) return fail("onTokenExpired returned no token.");
917
- store(tokens);
918
- settledAttempts += 1;
919
- refreshGeneration += 1;
920
- lastRefreshed = tokens;
921
- return tokens;
922
- } catch (error) {
923
- return fail(describeOperationError(error, "Token refresh failed."));
924
- }
925
- })();
926
- inFlight = run;
927
- run.finally(() => {
928
- if (inFlight === run) inFlight = null;
929
- });
930
- return run;
931
- }
932
- function withTokenHeaders(headers, tokens) {
933
- return {
934
- ...headers,
935
- ...tokens.authToken && { authorization: `Bearer ${tokens.authToken}` },
936
- ...tokens.workspaceToken && { "x-workspace-authorization": `Bearer ${tokens.workspaceToken}` }
937
- };
938
- }
939
- function resultIsUnauthenticated(result) {
940
- return hasUnauthenticatedGraphQLError(result.errors);
941
- }
942
- /**
943
- * Apollo link that refreshes an expired token and retries once.
944
- *
945
- * The SDK adds it to the clients it creates (`createFluidGridsClient`,
946
- * `createGlobalStoreClient`, `createFluidGridsClients`). Hosts that inject
947
- * their own Apollo client add it to their chain, BEFORE the link that sets the
948
- * `authorization` header, and either read the token from the SDK config in
949
- * that link or pass `onTokenRefreshed`:
950
- *
951
- * ```ts
952
- * const link = ApolloLink.from([
953
- * createFluidGridsTokenRefreshLink(),
954
- * authLink, // reads getFluidGridsConfig().authToken per request
955
- * httpLink,
956
- * ]);
957
- * ```
958
- *
959
- * The retried operation also carries the new token in its own headers, so it
960
- * works when the host's auth link runs first, too — as long as that link
961
- * does not overwrite an `authorization` header it finds.
962
- */
963
- function createFluidGridsTokenRefreshLink(options = {}) {
964
- return new ApolloLink((operation, forward) => {
965
- const store = options.onTokenRefreshed ?? storeInConfig;
966
- return new Observable((observer) => {
967
- const sentInGeneration = refreshGeneration;
968
- const sentAfterAttempts = settledAttempts;
969
- let retried = false;
970
- let closed = false;
971
- let current = null;
972
- const pickRefresh = (callback) => {
973
- if (refreshGeneration !== sentInGeneration && lastRefreshed) return Promise.resolve(lastRefreshed);
974
- if (lastFailedAttempt > sentAfterAttempts) return Promise.resolve(null);
975
- return refreshTokens(callback, store);
976
- };
977
- const retryOrFail = (fail) => {
978
- const callback = options.onTokenExpired ?? config.auth?.onTokenExpired;
979
- if (!callback) return false;
980
- retried = true;
981
- current?.unsubscribe();
982
- current = null;
983
- pickRefresh(callback).then((tokens) => {
984
- if (closed) return;
985
- if (!tokens) {
986
- fail();
987
- return;
988
- }
989
- const headers = operation.getContext().headers;
990
- operation.setContext({ headers: withTokenHeaders(headers && typeof headers === "object" ? headers : void 0, tokens) });
991
- subscribe();
992
- });
993
- return true;
994
- };
995
- const subscribe = () => {
996
- let refreshing = false;
997
- current = forward(operation).subscribe({
998
- next: (result) => {
999
- if (!retried && resultIsUnauthenticated(result) && retryOrFail(() => {
1000
- observer.next(result);
1001
- observer.complete();
1002
- })) {
1003
- refreshing = true;
1004
- return;
1005
- }
1006
- observer.next(result);
1007
- },
1008
- error: (error) => {
1009
- if (!retried && isUnauthenticatedError(error) && retryOrFail(() => observer.error(error))) {
1010
- refreshing = true;
1011
- return;
1012
- }
1013
- observer.error(error);
1014
- },
1015
- complete: () => {
1016
- if (!refreshing) observer.complete();
1017
- }
1018
- });
1019
- };
1020
- subscribe();
1021
- return () => {
1022
- closed = true;
1023
- current?.unsubscribe();
1024
- };
1025
- });
1026
- });
1027
- }
1028
-
1029
- // src/graphql/client.ts
1030
- let clientInstance = null;
1031
- /**
1032
- * Create a new Apollo client for FluidGrids SDK.
1033
- *
1034
- * Tokens are read per request: the client starts with `config.authToken` /
1035
- * `config.workspaceToken` and follows any token set later in the SDK config
1036
- * (see `auth.onTokenExpired`). Expired-token responses are refreshed and
1037
- * retried once by `createFluidGridsTokenRefreshLink`; the websocket reads the
1038
- * current token whenever it (re)connects.
1039
- */
1040
- function createFluidGridsClient(config) {
1041
- const graphqlUrl = getGraphQLUrl(config.apiGatewayUrl);
1042
- const wsUrl = graphqlUrl.replace(/^http/, "ws");
1043
- const readTokens = createLiveTokenReader({
1044
- authToken: config.authToken,
1045
- workspaceToken: config.workspaceToken
1046
- });
1047
- const httpLink = createHttpLink({ uri: graphqlUrl });
1048
- const authLink = setContext((_, { headers }) => {
1049
- const { authToken, workspaceToken } = readTokens();
1050
- return { headers: {
1051
- ...headers,
1052
- ...authToken && { authorization: `Bearer ${authToken}` },
1053
- ...workspaceToken && { "x-workspace-authorization": `Bearer ${workspaceToken}` },
1054
- "x-workspace-id": config.workspaceId,
1055
- ...config.tenantId && { "x-tenant-id": config.tenantId },
1056
- ...config.productId && { "x-product-id": config.productId }
1057
- } };
1058
- });
1059
- const wsLink = new GraphQLWsLink(createClient({
1060
- url: wsUrl,
1061
- connectionParams: () => {
1062
- const { authToken, workspaceToken } = readTokens();
1063
- return {
1064
- authorization: authToken ? `Bearer ${authToken}` : "",
1065
- ...workspaceToken && { "x-workspace-authorization": `Bearer ${workspaceToken}` },
1066
- "x-workspace-id": config.workspaceId,
1067
- ...config.tenantId && { "x-tenant-id": config.tenantId },
1068
- ...config.productId && { "x-product-id": config.productId }
1069
- };
1070
- }
1071
- }));
1072
- const splitLink = split(({ query }) => {
1073
- const definition = getMainDefinition(query);
1074
- return definition.kind === "OperationDefinition" && definition.operation === "subscription";
1075
- }, wsLink, createFluidGridsTokenRefreshLink().concat(authLink).concat(httpLink));
1076
- return new ApolloClient({
1077
- link: splitLink,
1078
- cache: new InMemoryCache(),
1079
- defaultOptions: {
1080
- watchQuery: { fetchPolicy: "cache-and-network" },
1081
- query: { fetchPolicy: "network-only" }
1082
- }
1083
- });
1084
- }
1085
- /**
1086
- * Get the GraphQL URL from the API gateway URL
1087
- */
1088
- function getGraphQLUrl(apiGatewayUrl) {
1089
- const normalizedBase = apiGatewayUrl.endsWith("/") ? apiGatewayUrl.slice(0, -1) : apiGatewayUrl;
1090
- if (normalizedBase.endsWith("/graphql")) return normalizedBase;
1091
- return `${normalizedBase}/workspaces/graphql`;
1092
- }
1093
- /**
1094
- * Set the module-level client (for SDK-managed mode or wrapper injection)
1095
- */
1096
- function setFluidGridsClient(client) {
1097
- clientInstance = client;
1098
- }
1099
- /**
1100
- * Get the module-level client
1101
- * @throws Error if client is not initialized
1102
- */
1103
- function getFluidGridsClient() {
1104
- if (!clientInstance) throw new Error("FluidGrids client not initialized. Use FluidGridsProvider or call setFluidGridsClient().");
1105
- return clientInstance;
1106
- }
1107
- /**
1108
- * Get the client if available, or null if not initialized
1109
- */
1110
- function getFluidGridsClientSafe() {
1111
- return clientInstance;
1112
- }
1113
- /**
1114
- * Check if the client is ready
1115
- */
1116
- function isFluidGridsClientReady() {
1117
- return clientInstance !== null;
1118
- }
1119
- /**
1120
- * Wait for the client to be initialized
1121
- */
1122
- function waitForFluidGridsClient(timeoutMs = 1e4, intervalMs = 100) {
1123
- return new Promise((resolve) => {
1124
- if (clientInstance) {
1125
- resolve(clientInstance);
1126
- return;
1127
- }
1128
- const start = Date.now();
1129
- const timer = setInterval(() => {
1130
- if (clientInstance) {
1131
- clearInterval(timer);
1132
- resolve(clientInstance);
1133
- } else if (Date.now() - start >= timeoutMs) {
1134
- clearInterval(timer);
1135
- resolve(null);
1136
- }
1137
- }, intervalMs);
1138
- });
1139
- }
1140
- /**
1141
- * Reset the client reference (for cleanup on unmount)
1142
- */
1143
- function resetFluidGridsClient() {
1144
- clientInstance = null;
1145
- }
1146
-
1147
- export { themeKey as A, resetFluidGridsConfig as C, FLUIDGRIDS_THEME_TOKEN_MAP as D, validateTrustedOrigin as E, DEFAULT_EXECUTIONS_FILTER as M, useExecutionsStore as N, applyFluidGridsTheme as O, getGraphQLEndpoint as S, setFluidGridsConfig as T, throwIfResultError as _, resetFluidGridsClient as a, config_exports as b, createFluidGridsTokenRefreshLink as c, emitFluidGridsEvent as d, emitOperationErrorEvent as f, isUnauthenticatedError as g, isForbiddenError as h, isFluidGridsClientReady as i, themeToCssVariables as j, sanitizeThemeValue as k, FLUIDGRIDS_EVENTS_VERSION as l, describeOperationError as m, getFluidGridsClient as n, setFluidGridsClient as o, emitRunCompletedOnce as p, getFluidGridsClientSafe as r, waitForFluidGridsClient as s, createFluidGridsClient as t, FLUIDGRIDS_EVENT_NAMES as u, createLiveTokenReader as v, resetFluidGridsExecutionState as w, getFluidGridsConfig as x, config as y };