@burdenoff/fluidgrids-fe-sdk 2026.1022.1 → 2026.1022.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 (34) hide show
  1. package/dist/{ExecutionDetailPage-6UKPMPL2.js → ExecutionDetailPage-NFZVCUCA.js} +4 -4
  2. package/dist/{ExecutionsListPage-3IFRZU6A.js → ExecutionsListPage-KSOHJU4T.js} +5 -5
  3. package/dist/{WorkflowBuilder-PYJ5T23Q.js → WorkflowBuilder-FR3TUJ33.js} +7 -7
  4. package/dist/{WorkflowsList-3ZDCAB4D.js → WorkflowsList-BAYEP7TU.js} +5 -5
  5. package/dist/{chunk-2CDLVDSU.js → chunk-2HUSQTUK.js} +182 -18
  6. package/dist/{chunk-GU233T6U.js → chunk-3XEIMQNT.js} +51 -41
  7. package/dist/chunk-4UAWCWS7.js +162 -0
  8. package/dist/{chunk-XGFADY55.js → chunk-5TW5JP5R.js} +1 -1
  9. package/dist/{chunk-QL7CDZ7B.js → chunk-6LMWVUD7.js} +3 -3
  10. package/dist/{chunk-OJL6UH5K.js → chunk-CU23L6GP.js} +2 -2
  11. package/dist/{chunk-RFG3PJX3.js → chunk-EU5ANGO4.js} +2 -2
  12. package/dist/{chunk-3RIZZY32.js → chunk-FJHERSHE.js} +5 -5
  13. package/dist/{chunk-O6E247J6.js → chunk-HP3A6BPK.js} +29 -23
  14. package/dist/{chunk-V2UCWUBQ.js → chunk-JG65547T.js} +2 -2
  15. package/dist/{chunk-OM5X65LG.js → chunk-KNFGMDDU.js} +48 -39
  16. package/dist/{chunk-EPYVZPGU.js → chunk-LHOW7QKK.js} +1 -1
  17. package/dist/{chunk-IKVHPXHC.js → chunk-PBIN6ERJ.js} +9 -9
  18. package/dist/{chunk-L2UWPFJM.js → chunk-SAZVBFMO.js} +2 -1
  19. package/dist/{chunk-SQFFJUAT.js → chunk-SYVLFHWV.js} +2 -2
  20. package/dist/{chunk-6B6XOZ4V.js → chunk-TWIORNPU.js} +2 -2
  21. package/dist/{chunk-CGRYQMWL.js → chunk-XQVQKEZK.js} +1 -1
  22. package/dist/{chunk-YB6WGQTY.js → chunk-YPF7LVUZ.js} +2 -2
  23. package/dist/executions.js +6 -6
  24. package/dist/history.js +4 -4
  25. package/dist/index.d.ts +171 -2
  26. package/dist/index.js +26 -26
  27. package/dist/nodes.js +3 -3
  28. package/dist/observability.js +3 -3
  29. package/dist/settings.js +4 -4
  30. package/dist/templates.js +7 -7
  31. package/dist/workflow-builder.js +8 -8
  32. package/dist/workflows-list.js +5 -5
  33. package/package.json +1 -1
  34. package/dist/chunk-CFCUM4CN.js +0 -41
@@ -1,12 +1,12 @@
1
- export { ExecutionDetailPage } from './chunk-QL7CDZ7B.js';
2
- import './chunk-GU233T6U.js';
1
+ export { ExecutionDetailPage } from './chunk-6LMWVUD7.js';
2
+ import './chunk-3XEIMQNT.js';
3
3
  import './chunk-QBFAJNUC.js';
4
4
  import './chunk-ONVGFVNH.js';
5
5
  import './chunk-JL7CASN5.js';
6
6
  import './chunk-D3UWK7GY.js';
7
7
  import './chunk-UZG3ZIQM.js';
8
8
  import './chunk-PJAWNFJV.js';
9
- import './chunk-L2UWPFJM.js';
10
- import './chunk-CFCUM4CN.js';
9
+ import './chunk-SAZVBFMO.js';
10
+ import './chunk-4UAWCWS7.js';
11
11
  import './chunk-UULVIT25.js';
12
12
  import './chunk-NTWG3CJX.js';
@@ -1,11 +1,11 @@
1
- export { ExecutionsListPage } from './chunk-YB6WGQTY.js';
2
- import './chunk-GU233T6U.js';
1
+ export { ExecutionsListPage } from './chunk-YPF7LVUZ.js';
2
+ import './chunk-3XEIMQNT.js';
3
3
  import './chunk-QBFAJNUC.js';
4
- import './chunk-O6E247J6.js';
4
+ import './chunk-HP3A6BPK.js';
5
5
  import './chunk-D3UWK7GY.js';
6
6
  import './chunk-UZG3ZIQM.js';
7
7
  import './chunk-PJAWNFJV.js';
8
- import './chunk-L2UWPFJM.js';
9
- import './chunk-CFCUM4CN.js';
8
+ import './chunk-SAZVBFMO.js';
9
+ import './chunk-4UAWCWS7.js';
10
10
  import './chunk-UULVIT25.js';
11
11
  import './chunk-NTWG3CJX.js';
@@ -1,20 +1,20 @@
1
- export { WorkflowBuilder } from './chunk-RFG3PJX3.js';
1
+ export { WorkflowBuilder } from './chunk-EU5ANGO4.js';
2
2
  import './chunk-6N27YGMB.js';
3
- import './chunk-2CDLVDSU.js';
3
+ import './chunk-2HUSQTUK.js';
4
4
  import './chunk-GEMNVFPK.js';
5
5
  import './chunk-IEVSFCFS.js';
6
6
  import './chunk-7LSGGJ7D.js';
7
7
  import './chunk-GDAFKRQO.js';
8
- import './chunk-SQFFJUAT.js';
8
+ import './chunk-SYVLFHWV.js';
9
9
  import './chunk-PXIFUMLA.js';
10
10
  import './chunk-WBH2XEWO.js';
11
11
  import './chunk-ONVGFVNH.js';
12
12
  import './chunk-JL7CASN5.js';
13
- import './chunk-O6E247J6.js';
13
+ import './chunk-HP3A6BPK.js';
14
14
  import './chunk-UZG3ZIQM.js';
15
- import './chunk-XGFADY55.js';
15
+ import './chunk-5TW5JP5R.js';
16
16
  import './chunk-EGYJKHAL.js';
17
- import './chunk-L2UWPFJM.js';
18
- import './chunk-CFCUM4CN.js';
17
+ import './chunk-SAZVBFMO.js';
18
+ import './chunk-4UAWCWS7.js';
19
19
  import './chunk-UULVIT25.js';
20
20
  import './chunk-NTWG3CJX.js';
@@ -1,14 +1,14 @@
1
- export { WorkflowsList } from './chunk-3RIZZY32.js';
1
+ export { WorkflowsList } from './chunk-FJHERSHE.js';
2
2
  import './chunk-6N27YGMB.js';
3
- import './chunk-SQFFJUAT.js';
3
+ import './chunk-SYVLFHWV.js';
4
4
  import './chunk-ONVGFVNH.js';
5
5
  import './chunk-JL7CASN5.js';
6
- import './chunk-O6E247J6.js';
6
+ import './chunk-HP3A6BPK.js';
7
7
  import './chunk-D3UWK7GY.js';
8
8
  import './chunk-UZG3ZIQM.js';
9
9
  import './chunk-PJAWNFJV.js';
10
10
  import './chunk-EGYJKHAL.js';
11
- import './chunk-L2UWPFJM.js';
12
- import './chunk-CFCUM4CN.js';
11
+ import './chunk-SAZVBFMO.js';
12
+ import './chunk-4UAWCWS7.js';
13
13
  import './chunk-UULVIT25.js';
14
14
  import './chunk-NTWG3CJX.js';
@@ -2,17 +2,17 @@ import { deepClone, applyTemplateInputs } from './chunk-GEMNVFPK.js';
2
2
  import { Input as Input$1 } from './chunk-IEVSFCFS.js';
3
3
  import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter } from './chunk-7LSGGJ7D.js';
4
4
  import { Badge as Badge$1, Button as Button$1 } from './chunk-GDAFKRQO.js';
5
- import { QuotaExhaustedDialog } from './chunk-SQFFJUAT.js';
5
+ import { QuotaExhaustedDialog } from './chunk-SYVLFHWV.js';
6
6
  import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-PXIFUMLA.js';
7
7
  import { GetMyNodeInstallationsDocument, BrowseWorkflowTemplatesDocument, InstallWorkflowNodeDocument, InstallPurchasedNodeDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument, GetNodeInstallableVersionsDocument } from './chunk-WBH2XEWO.js';
8
8
  import { SdkPermissionGate, FLUIDGRIDS_PERMISSIONS, SdkPermissionButton } from './chunk-ONVGFVNH.js';
9
9
  import { cn as cn$1 } from './chunk-JL7CASN5.js';
10
- import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-O6E247J6.js';
10
+ import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-HP3A6BPK.js';
11
11
  import { config, getFluidGridsConfig, validateTrustedOrigin, resetFluidGridsExecutionState, setFluidGridsConfig, resetFluidGridsConfig } from './chunk-UZG3ZIQM.js';
12
- import { fetchAllNodeEndpointConfigs } from './chunk-XGFADY55.js';
12
+ import { fetchAllNodeEndpointConfigs } from './chunk-5TW5JP5R.js';
13
13
  import { useFlowStore, isNodeTrigger, useIsValidConnection, isTriggerOperation } from './chunk-EGYJKHAL.js';
14
- import { NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, FluidgridsEntityTagsDocument, AvailableFluidgridsTagsDocument, SearchFluidgridsTagsDocument, TagFluidgridsEntityDocument, UntagFluidgridsEntityDocument, ReplaceFluidgridsEntityTagsDocument, CreateFluidgridsTagDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-L2UWPFJM.js';
15
- import { throwIfResultError, describeOperationError } from './chunk-CFCUM4CN.js';
14
+ import { NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, FluidgridsEntityTagsDocument, AvailableFluidgridsTagsDocument, SearchFluidgridsTagsDocument, TagFluidgridsEntityDocument, UntagFluidgridsEntityDocument, ReplaceFluidgridsEntityTagsDocument, CreateFluidgridsTagDocument, WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-SAZVBFMO.js';
15
+ import { throwIfResultError, isQuotaExhaustedFailure, describeOperationError, openRunQuotaUpgradePrompt, RUNS_QUOTA_NAME, BILLING_PLANS_PATH } from './chunk-4UAWCWS7.js';
16
16
  import { useTr, FluidGridsTranslationProvider, ENGLISH_FALLBACK_TRANSLATOR } from './chunk-UULVIT25.js';
17
17
  import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-NTWG3CJX.js';
18
18
  import * as React2 from 'react';
@@ -30,9 +30,8 @@ import { Handle, Position, getBezierPath, MarkerType, useReactFlow, NodeResizer,
30
30
  import { nanoid } from 'nanoid';
31
31
  import ReactMarkdown from 'react-markdown';
32
32
  import { toast } from 'sonner';
33
- import { useParams, useNavigate } from 'react-router';
33
+ import { useParams, useNavigate, Link } from 'react-router';
34
34
  import { useShallow, shallow } from 'zustand/shallow';
35
- import { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';
36
35
  import { InfoCircledIcon } from '@radix-ui/react-icons';
37
36
  import { ApolloProvider, useQuery, useMutation } from '@apollo/client/react';
38
37
  import { TagBadge, TagsWidget } from '@burdenoff/fe-libs/tag';
@@ -4178,7 +4177,7 @@ function useSaveWorkflow() {
4178
4177
  const message = error instanceof Error ? error.message : "Unknown error";
4179
4178
  console.error("Error saving workflow:", message);
4180
4179
  const cause = error instanceof Error ? error.cause : void 0;
4181
- if (isQuotaExhaustedError(cause) || isQuotaExhaustedError(error)) {
4180
+ if (isQuotaExhaustedFailure(cause) || isQuotaExhaustedFailure(error)) {
4182
4181
  setQuotaExhausted(true);
4183
4182
  } else {
4184
4183
  toast.error("Failed to save workflow", {
@@ -5906,6 +5905,7 @@ function calculateItemCount(output) {
5906
5905
  return 1;
5907
5906
  }
5908
5907
  function useNodeExecution() {
5908
+ const tr = useTr();
5909
5909
  const pollTimerRef = useRef(null);
5910
5910
  const pollStartRef = useRef(0);
5911
5911
  const activeRunIdRef = useRef(null);
@@ -6253,6 +6253,7 @@ function useNodeExecution() {
6253
6253
  return { success: false, error: "No result from trigger" };
6254
6254
  } catch (error) {
6255
6255
  console.error("[useNodeExecution] Error triggering workflow:", error);
6256
+ openRunQuotaUpgradePrompt(error, tr);
6256
6257
  const errorMessage = describeOperationError(error);
6257
6258
  const errorRunId = nanoid$1();
6258
6259
  const errorLog = {
@@ -6285,7 +6286,8 @@ function useNodeExecution() {
6285
6286
  addLog,
6286
6287
  setNodeStatus,
6287
6288
  pollRunUpdates,
6288
- completeExecution
6289
+ completeExecution,
6290
+ tr
6289
6291
  ]
6290
6292
  );
6291
6293
  const followRun = useCallback(
@@ -6348,6 +6350,7 @@ function useNodeExecution() {
6348
6350
  return { success: false, error: "No result from runFromNode" };
6349
6351
  } catch (error) {
6350
6352
  console.error("[useNodeExecution] Error running from node:", error);
6353
+ openRunQuotaUpgradePrompt(error, tr);
6351
6354
  const errorMessage = describeOperationError(error);
6352
6355
  setNodeStatus(nodeId, "error");
6353
6356
  completeExecution();
@@ -6360,7 +6363,8 @@ function useNodeExecution() {
6360
6363
  setNodeStatus,
6361
6364
  startExecution,
6362
6365
  pollRunUpdates,
6363
- completeExecution
6366
+ completeExecution,
6367
+ tr
6364
6368
  ]
6365
6369
  );
6366
6370
  const loadExecution = useCallback(
@@ -7209,6 +7213,159 @@ function EntityTagPicker({
7209
7213
  }
7210
7214
  );
7211
7215
  }
7216
+
7217
+ // src/billing/runsQuota.ts
7218
+ var MAX_ASSIGNMENTS_PER_QUOTA = 100;
7219
+ var RUNS_QUOTA_WARNING_RATIO = 0.8;
7220
+ var createdAtMs = (row) => {
7221
+ const ms = Date.parse(row.createdAt);
7222
+ return Number.isNaN(ms) ? Number.POSITIVE_INFINITY : ms;
7223
+ };
7224
+ function summarizeRunsQuota(rows, quotaName = RUNS_QUOTA_NAME) {
7225
+ const assignments = rows.filter((row) => row.isActive && row.quotaName === quotaName).sort((a, b) => createdAtMs(a) - createdAtMs(b)).slice(0, MAX_ASSIGNMENTS_PER_QUOTA);
7226
+ if (assignments.length === 0) return null;
7227
+ const used = assignments.reduce((sum, row) => sum + row.used, 0);
7228
+ if (assignments.some((row) => row.noLimit || row.limit == null)) {
7229
+ return { used, limit: null, noLimit: true };
7230
+ }
7231
+ const limit = assignments.reduce((sum, row) => sum + (row.limit ?? 0), 0);
7232
+ return { used, limit, noLimit: false };
7233
+ }
7234
+ function runsQuotaLevel(quota) {
7235
+ if (quota.noLimit || quota.limit === null) return "ok";
7236
+ if (quota.limit <= 0 || quota.used >= quota.limit) return "exhausted";
7237
+ return quota.used / quota.limit >= RUNS_QUOTA_WARNING_RATIO ? "warning" : "ok";
7238
+ }
7239
+ async function readRunsQuota(client, workspaceId) {
7240
+ if (!workspaceId) return null;
7241
+ try {
7242
+ const result = await client.query({
7243
+ query: WorkspaceRunsQuotaDocument,
7244
+ variables: { workspaceId },
7245
+ fetchPolicy: "network-only",
7246
+ errorPolicy: "all"
7247
+ });
7248
+ if (result.error) return null;
7249
+ const rows = result.data?.workspaceQuotaAssignments;
7250
+ return rows ? summarizeRunsQuota(rows) : null;
7251
+ } catch {
7252
+ return null;
7253
+ }
7254
+ }
7255
+ function useRunsQuota() {
7256
+ const [quota, setQuota] = useState(null);
7257
+ const isExecuting = useFlowStore((s) => s.execution.isExecuting);
7258
+ const request = useRef(0);
7259
+ const refresh = useCallback(async () => {
7260
+ const client = getFluidGridsClientSafe();
7261
+ const { workspaceId } = getFluidGridsConfig();
7262
+ const id = ++request.current;
7263
+ const next = client ? await readRunsQuota(client, workspaceId) : null;
7264
+ if (id === request.current) setQuota(next);
7265
+ }, []);
7266
+ useEffect(() => {
7267
+ if (!isExecuting) void refresh();
7268
+ }, [isExecuting, refresh]);
7269
+ useEffect(
7270
+ () => () => {
7271
+ request.current += 1;
7272
+ },
7273
+ []
7274
+ );
7275
+ return quota;
7276
+ }
7277
+ var BAR_CLASS = {
7278
+ ok: "bg-primary",
7279
+ warning: "bg-status-warning-bg",
7280
+ exhausted: "bg-status-error-bg"
7281
+ };
7282
+ var TEXT_CLASS = {
7283
+ ok: "text-muted-foreground",
7284
+ warning: "text-status-warning-text",
7285
+ exhausted: "text-status-error-text"
7286
+ };
7287
+ function RunsUsageMeterView({
7288
+ quota,
7289
+ className
7290
+ }) {
7291
+ const tr = useTr();
7292
+ const level = runsQuotaLevel(quota);
7293
+ const used = quota.used.toLocaleString();
7294
+ if (quota.noLimit || quota.limit === null) {
7295
+ return /* @__PURE__ */ jsx(
7296
+ "span",
7297
+ {
7298
+ "data-testid": "fluidgrids-runs-meter",
7299
+ "data-level": level,
7300
+ className: cn("text-xs text-muted-foreground", className),
7301
+ children: tr(
7302
+ "fluidgrids.billing.runsMeter.unlimited",
7303
+ "{{used}} runs this month",
7304
+ { used }
7305
+ )
7306
+ }
7307
+ );
7308
+ }
7309
+ const limit = quota.limit.toLocaleString();
7310
+ const percent = quota.limit > 0 ? Math.min(100, Math.round(quota.used / quota.limit * 100)) : 100;
7311
+ const label = tr(
7312
+ "fluidgrids.billing.runsMeter.label",
7313
+ "{{used}} of {{limit}} runs used this month",
7314
+ { used, limit }
7315
+ );
7316
+ return /* @__PURE__ */ jsxs(
7317
+ "div",
7318
+ {
7319
+ "data-testid": "fluidgrids-runs-meter",
7320
+ "data-level": level,
7321
+ className: cn("flex items-center gap-2 text-xs", className),
7322
+ children: [
7323
+ /* @__PURE__ */ jsxs(
7324
+ "div",
7325
+ {
7326
+ className: "flex flex-col gap-1",
7327
+ title: label,
7328
+ role: "meter",
7329
+ "aria-label": label,
7330
+ "aria-valuemin": 0,
7331
+ "aria-valuemax": quota.limit,
7332
+ "aria-valuenow": Math.min(quota.used, quota.limit),
7333
+ children: [
7334
+ /* @__PURE__ */ jsx("span", { className: cn("whitespace-nowrap", TEXT_CLASS[level]), children: tr(
7335
+ "fluidgrids.billing.runsMeter.short",
7336
+ "Runs {{used}} / {{limit}}",
7337
+ {
7338
+ used,
7339
+ limit
7340
+ }
7341
+ ) }),
7342
+ /* @__PURE__ */ jsx("span", { className: "block h-1 w-24 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
7343
+ "span",
7344
+ {
7345
+ className: cn("block h-full rounded-full", BAR_CLASS[level]),
7346
+ style: { width: `${percent}%` }
7347
+ }
7348
+ ) })
7349
+ ]
7350
+ }
7351
+ ),
7352
+ level !== "ok" && /* @__PURE__ */ jsx(
7353
+ Link,
7354
+ {
7355
+ to: BILLING_PLANS_PATH,
7356
+ className: "whitespace-nowrap font-medium text-primary hover:underline",
7357
+ children: tr("fluidgrids.billing.runsMeter.upgrade", "Upgrade")
7358
+ }
7359
+ )
7360
+ ]
7361
+ }
7362
+ );
7363
+ }
7364
+ function RunsUsageMeter({ className }) {
7365
+ const quota = useRunsQuota();
7366
+ if (!quota) return null;
7367
+ return /* @__PURE__ */ jsx(RunsUsageMeterView, { quota, className });
7368
+ }
7212
7369
  function useGlobalStoreClient() {
7213
7370
  const context = useContext(FluidGridsContext);
7214
7371
  if (context?.globalClient) {
@@ -20089,7 +20246,7 @@ var SaveFlowButton = ({ className }) => {
20089
20246
  },
20090
20247
  onUpgrade: () => {
20091
20248
  dismissQuotaExhausted();
20092
- navigate("/billing");
20249
+ navigate(BILLING_PLANS_PATH);
20093
20250
  }
20094
20251
  }
20095
20252
  )
@@ -21748,6 +21905,7 @@ function useWebhookTestListener(target, onReceived) {
21748
21905
  );
21749
21906
  const [busy, setBusy] = useState(false);
21750
21907
  const [error, setError] = useState(null);
21908
+ const tr = useTr();
21751
21909
  const pollTimer = useRef(null);
21752
21910
  const openId = useRef(null);
21753
21911
  const generation = useRef(0);
@@ -21822,10 +21980,12 @@ function useWebhookTestListener(target, onReceived) {
21822
21980
  setBusy(true);
21823
21981
  setError(null);
21824
21982
  try {
21825
- const result = await client.mutate({
21826
- mutation: ListenForWebhookTestEventDocument,
21827
- variables: { input: { workflowKey, version, triggerNodeId } }
21828
- });
21983
+ const result = throwIfResultError(
21984
+ await client.mutate({
21985
+ mutation: ListenForWebhookTestEventDocument,
21986
+ variables: { input: { workflowKey, version, triggerNodeId } }
21987
+ })
21988
+ );
21829
21989
  const armed = result.data?.listenForWebhookTestEvent;
21830
21990
  if (!armed) throw new Error("The test listener could not be started");
21831
21991
  if (generation.current !== armedFor) {
@@ -21843,8 +22003,10 @@ function useWebhookTestListener(target, onReceived) {
21843
22003
  });
21844
22004
  poll(armed.id);
21845
22005
  } catch (err) {
21846
- if (generation.current === armedFor)
22006
+ if (generation.current === armedFor) {
22007
+ openRunQuotaUpgradePrompt(err, tr);
21847
22008
  setError(describeOperationError(err));
22009
+ }
21848
22010
  } finally {
21849
22011
  if (generation.current === armedFor) setBusy(false);
21850
22012
  }
@@ -21854,7 +22016,8 @@ function useWebhookTestListener(target, onReceived) {
21854
22016
  triggerNodeId,
21855
22017
  poll,
21856
22018
  stopOpenListener,
21857
- stopAbandoned
22019
+ stopAbandoned,
22020
+ tr
21858
22021
  ]);
21859
22022
  const stop = useCallback(async () => {
21860
22023
  const id = openId.current;
@@ -26722,6 +26885,7 @@ var FlowBuilder = ({ readOnly = false }) => {
26722
26885
  ] }),
26723
26886
  name !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
26724
26887
  /* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 lg:inline-flex items-center gap-2", children: [
26888
+ /* @__PURE__ */ jsx(RunsUsageMeter, { className: "mr-1" }),
26725
26889
  /* @__PURE__ */ jsx(VersionHistoryPanel, {}),
26726
26890
  /* @__PURE__ */ jsx(SaveFlowButton, {}),
26727
26891
  /* @__PURE__ */ jsx(ActivateWorkflowButton, {}),
@@ -26992,4 +27156,4 @@ var FlowBuilderPage = ({
26992
27156
  ] });
26993
27157
  };
26994
27158
 
26995
- export { Card2 as Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ConnectionFormDialog, ConnectionSelector, EntityTagPicker, FLUIDGRIDS_ENTITY_TYPES, FlowBuilderPage, FluidGridsMark, FluidGridsProvider, RUNNER_INSTALL_SCRIPT_URL, RunOnSection, RunnerStatusPill, TemplateCard, buildNodeCheck, buildRequiredNodeTypes, listCustomBlocks, mergeStickyNotes, refetchStoreNodes, registerCustomBlock, resolveUiSpec, runnerInstallCommand, stripSubmitActions, tagLabel, useAvailableTags, useConnection, useConnections, useConnectionsByProvider, useCreateConnection, useCreateTag, useDraftEntityTags, useEntityTags, useFluidGridsContext, useImportWorkflowTemplate, useInitiateOAuth, useInstalledNodes, useNodeExecution, useProviders, useReplaceEntityTags, useRunnerActions, useRunners, useSearchTags, useTagEntity, useUntagEntity, useWorkflowTemplates };
27159
+ export { Card2 as Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ConnectionFormDialog, ConnectionSelector, EntityTagPicker, FLUIDGRIDS_ENTITY_TYPES, FlowBuilderPage, FluidGridsMark, FluidGridsProvider, RUNNER_INSTALL_SCRIPT_URL, RunOnSection, RunnerStatusPill, RunsUsageMeter, RunsUsageMeterView, TemplateCard, buildNodeCheck, buildRequiredNodeTypes, listCustomBlocks, mergeStickyNotes, readRunsQuota, refetchStoreNodes, registerCustomBlock, resolveUiSpec, runnerInstallCommand, runsQuotaLevel, stripSubmitActions, summarizeRunsQuota, tagLabel, useAvailableTags, useConnection, useConnections, useConnectionsByProvider, useCreateConnection, useCreateTag, useDraftEntityTags, useEntityTags, useFluidGridsContext, useImportWorkflowTemplate, useInitiateOAuth, useInstalledNodes, useNodeExecution, useProviders, useReplaceEntityTags, useRunnerActions, useRunners, useRunsQuota, useSearchTags, useTagEntity, useUntagEntity, useWorkflowTemplates };
@@ -2,8 +2,8 @@ import { REPLAY_WORKFLOW_RUN, RETRY_WORKFLOW_RUNS } from './chunk-QBFAJNUC.js';
2
2
  import { executionsControls } from './chunk-D3UWK7GY.js';
3
3
  import { useExecutionsStore } from './chunk-UZG3ZIQM.js';
4
4
  import { useFluidGridsPageControls } from './chunk-PJAWNFJV.js';
5
- import { ExecutionDetailDocument, ExecutionStatsDocument, PauseExecutionDocument, ResumeExecutionDocument, CancelExecutionDocument, RetryNodeDocument, TriggerWorkflowDocument, TerminateExecutionDocument, ExecutionsListDocument } from './chunk-L2UWPFJM.js';
6
- import { throwIfResultError } from './chunk-CFCUM4CN.js';
5
+ import { ExecutionDetailDocument, ExecutionStatsDocument, PauseExecutionDocument, ResumeExecutionDocument, CancelExecutionDocument, RetryNodeDocument, TriggerWorkflowDocument, TerminateExecutionDocument, ExecutionsListDocument } from './chunk-SAZVBFMO.js';
6
+ import { throwIfResultError, openRunQuotaUpgradePrompt } from './chunk-4UAWCWS7.js';
7
7
  import { useTr } from './chunk-UULVIT25.js';
8
8
  import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
9
9
  import { Icon } from '@iconify/react';
@@ -2885,6 +2885,7 @@ function useExecutionStats(options = {}) {
2885
2885
  };
2886
2886
  }
2887
2887
  function useExecutionActions() {
2888
+ const tr = useTr();
2888
2889
  const [loading, setLoading] = useState(false);
2889
2890
  const [error, setError] = useState(null);
2890
2891
  const pauseExecution = useCallback(async (id) => {
@@ -2991,51 +2992,60 @@ function useExecutionActions() {
2991
2992
  } catch (err) {
2992
2993
  const error2 = err;
2993
2994
  setError(error2);
2995
+ openRunQuotaUpgradePrompt(err, tr);
2994
2996
  return { ok: false, message: error2.message };
2995
2997
  } finally {
2996
2998
  setLoading(false);
2997
2999
  }
2998
3000
  },
2999
- []
3001
+ [tr]
3002
+ );
3003
+ const replayExecution = useCallback(
3004
+ async (runId) => {
3005
+ setLoading(true);
3006
+ setError(null);
3007
+ try {
3008
+ const client = getFluidGridsClient();
3009
+ const result = throwIfResultError(
3010
+ await client.mutate({
3011
+ mutation: REPLAY_WORKFLOW_RUN,
3012
+ variables: { runId }
3013
+ })
3014
+ );
3015
+ return result.data?.replayWorkflowRun ?? null;
3016
+ } catch (err) {
3017
+ setError(err);
3018
+ openRunQuotaUpgradePrompt(err, tr);
3019
+ return null;
3020
+ } finally {
3021
+ setLoading(false);
3022
+ }
3023
+ },
3024
+ [tr]
3025
+ );
3026
+ const retryExecutions = useCallback(
3027
+ async (runIds) => {
3028
+ setLoading(true);
3029
+ setError(null);
3030
+ try {
3031
+ const client = getFluidGridsClient();
3032
+ const result = throwIfResultError(
3033
+ await client.mutate({
3034
+ mutation: RETRY_WORKFLOW_RUNS,
3035
+ variables: { runIds }
3036
+ })
3037
+ );
3038
+ return result.data?.retryWorkflowRuns ?? [];
3039
+ } catch (err) {
3040
+ setError(err);
3041
+ openRunQuotaUpgradePrompt(err, tr);
3042
+ return [];
3043
+ } finally {
3044
+ setLoading(false);
3045
+ }
3046
+ },
3047
+ [tr]
3000
3048
  );
3001
- const replayExecution = useCallback(async (runId) => {
3002
- setLoading(true);
3003
- setError(null);
3004
- try {
3005
- const client = getFluidGridsClient();
3006
- const result = throwIfResultError(
3007
- await client.mutate({
3008
- mutation: REPLAY_WORKFLOW_RUN,
3009
- variables: { runId }
3010
- })
3011
- );
3012
- return result.data?.replayWorkflowRun ?? null;
3013
- } catch (err) {
3014
- setError(err);
3015
- return null;
3016
- } finally {
3017
- setLoading(false);
3018
- }
3019
- }, []);
3020
- const retryExecutions = useCallback(async (runIds) => {
3021
- setLoading(true);
3022
- setError(null);
3023
- try {
3024
- const client = getFluidGridsClient();
3025
- const result = throwIfResultError(
3026
- await client.mutate({
3027
- mutation: RETRY_WORKFLOW_RUNS,
3028
- variables: { runIds }
3029
- })
3030
- );
3031
- return result.data?.retryWorkflowRuns ?? [];
3032
- } catch (err) {
3033
- setError(err);
3034
- return [];
3035
- } finally {
3036
- setLoading(false);
3037
- }
3038
- }, []);
3039
3049
  const terminateExecution = useCallback(
3040
3050
  async (runId, reason) => {
3041
3051
  setLoading(true);
@@ -0,0 +1,162 @@
1
+ import { ENGLISH_FALLBACK_TRANSLATOR } from './chunk-UULVIT25.js';
2
+ import { CombinedGraphQLErrors, ServerError } from '@apollo/client/errors';
3
+ import { dispatchUpgradePrompt } from '@burdenoff/fe-libs/shared/components';
4
+
5
+ function describeGraphQLError(error) {
6
+ const code = typeof error.extensions?.code === "string" ? error.extensions.code : void 0;
7
+ switch (code) {
8
+ case "QUOTA_EXHAUSTED": {
9
+ const quota = /Quota exhausted for (\S+)/.exec(error.message)?.[1];
10
+ const allowance = quota ? `"${quota}" quota` : "usage quota";
11
+ return `Quota exhausted \u2014 your workspace has used up its ${allowance}. Upgrade your plan or wait for the quota to reset.`;
12
+ }
13
+ case "QUOTA_NOT_PROVISIONED":
14
+ return "This workspace's plan does not include this usage allowance yet. Contact your workspace admin or support to have it provisioned.";
15
+ case "UNAUTHENTICATED":
16
+ return "Your session has expired. Please sign in again.";
17
+ case "FORBIDDEN":
18
+ return error.message || "You don't have permission to perform this action.";
19
+ default:
20
+ return error.message || "Unknown error";
21
+ }
22
+ }
23
+ function firstGraphQLErrorInBody(bodyText) {
24
+ try {
25
+ const parsed = JSON.parse(bodyText);
26
+ if (typeof parsed !== "object" || parsed === null) return null;
27
+ const errors = parsed.errors;
28
+ if (!Array.isArray(errors) || errors.length === 0) return null;
29
+ const first = errors[0];
30
+ if (typeof first !== "object" || first === null) return null;
31
+ const message = first.message;
32
+ if (typeof message !== "string") return null;
33
+ return first;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ function describeOperationError(error, fallback = "Unknown error") {
39
+ if (CombinedGraphQLErrors.is(error)) {
40
+ const first = error.errors[0];
41
+ return first ? describeGraphQLError(first) : error.message || fallback;
42
+ }
43
+ if (ServerError.is(error)) {
44
+ const first = firstGraphQLErrorInBody(error.bodyText);
45
+ if (first) return describeGraphQLError(first);
46
+ }
47
+ if (error instanceof Error) {
48
+ return error.message || fallback;
49
+ }
50
+ if (typeof error === "string" && error.length > 0) {
51
+ return error;
52
+ }
53
+ return fallback;
54
+ }
55
+ function throwIfResultError(result) {
56
+ if (result.error) {
57
+ throw new Error(describeOperationError(result.error), {
58
+ cause: result.error
59
+ });
60
+ }
61
+ return result;
62
+ }
63
+
64
+ // src/billing/quota.ts
65
+ var RUNS_QUOTA_NAME = "fluidgrids.runs.monthly";
66
+ var WORKFLOWS_QUOTA_NAME = "fluidgrids.workflows.count";
67
+ var BILLING_PLANS_PATH = "/billing/plans";
68
+ var QUOTA_EXHAUSTED_CODE = "QUOTA_EXHAUSTED";
69
+ var QUOTA_NOT_PROVISIONED_CODE = "QUOTA_NOT_PROVISIONED";
70
+ function asRecord(value) {
71
+ return typeof value === "object" && value !== null ? value : null;
72
+ }
73
+ function extensionsOfList(list) {
74
+ if (!Array.isArray(list)) return [];
75
+ const found = [];
76
+ for (const entry of list) {
77
+ const extensions = asRecord(asRecord(entry)?.extensions);
78
+ if (extensions) found.push(extensions);
79
+ }
80
+ return found;
81
+ }
82
+ function extensionsOfBody(bodyText) {
83
+ if (typeof bodyText !== "string" || !bodyText.includes("errors")) return [];
84
+ try {
85
+ const parsed = JSON.parse(bodyText);
86
+ return extensionsOfList(asRecord(parsed)?.errors);
87
+ } catch {
88
+ return [];
89
+ }
90
+ }
91
+ var MAX_CAUSE_DEPTH = 5;
92
+ function collectExtensions(error) {
93
+ const found = [];
94
+ const seen = /* @__PURE__ */ new Set();
95
+ let current = error;
96
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth += 1) {
97
+ const host = asRecord(current);
98
+ if (!host || seen.has(host)) break;
99
+ seen.add(host);
100
+ const direct = asRecord(host.extensions);
101
+ if (direct) found.push(direct);
102
+ found.push(...extensionsOfList(host.errors));
103
+ found.push(...extensionsOfList(host.graphQLErrors));
104
+ const networkError = asRecord(host.networkError);
105
+ found.push(...extensionsOfList(asRecord(networkError?.result)?.errors));
106
+ found.push(...extensionsOfBody(networkError?.bodyText));
107
+ found.push(...extensionsOfList(asRecord(host.result)?.errors));
108
+ found.push(...extensionsOfBody(host.bodyText));
109
+ current = host.cause;
110
+ }
111
+ return found;
112
+ }
113
+ function stringField(extensions, key) {
114
+ const value = extensions[key];
115
+ return typeof value === "string" && value.length > 0 ? value : null;
116
+ }
117
+ function findQuotaRefusal(error) {
118
+ const all = collectExtensions(error);
119
+ const notProvisioned = all.find(
120
+ (ext) => ext.code === QUOTA_NOT_PROVISIONED_CODE
121
+ );
122
+ if (notProvisioned) {
123
+ return {
124
+ kind: "not-provisioned",
125
+ quotaName: stringField(notProvisioned, "quotaName"),
126
+ limitLeft: null
127
+ };
128
+ }
129
+ const exhausted = all.find((ext) => ext.code === QUOTA_EXHAUSTED_CODE);
130
+ if (exhausted) {
131
+ const limitLeft = exhausted.limitLeft;
132
+ return {
133
+ kind: "exhausted",
134
+ quotaName: stringField(exhausted, "quotaName"),
135
+ limitLeft: typeof limitLeft === "number" ? limitLeft : null
136
+ };
137
+ }
138
+ return null;
139
+ }
140
+ function isQuotaExhaustedFailure(error) {
141
+ return findQuotaRefusal(error)?.kind === "exhausted";
142
+ }
143
+ function openRunQuotaUpgradePrompt(error, tr = ENGLISH_FALLBACK_TRANSLATOR) {
144
+ const refusal = findQuotaRefusal(error);
145
+ if (refusal?.kind !== "exhausted") return false;
146
+ dispatchUpgradePrompt({
147
+ extensions: {
148
+ code: QUOTA_EXHAUSTED_CODE,
149
+ quotaName: refusal.quotaName ?? RUNS_QUOTA_NAME,
150
+ resource: tr("fluidgrids.billing.runQuota.resource", "workflow runs"),
151
+ ...refusal.limitLeft !== null ? { limitLeft: refusal.limitLeft } : {}
152
+ },
153
+ title: tr("fluidgrids.billing.runQuota.title", "Monthly run limit reached"),
154
+ description: tr(
155
+ "fluidgrids.billing.runQuota.description",
156
+ "Your workspace has used all the workflow runs included in its plan this month. Upgrade your plan to keep running workflows, or wait for the monthly reset."
157
+ )
158
+ });
159
+ return true;
160
+ }
161
+
162
+ export { BILLING_PLANS_PATH, RUNS_QUOTA_NAME, WORKFLOWS_QUOTA_NAME, describeOperationError, findQuotaRefusal, isQuotaExhaustedFailure, openRunQuotaUpgradePrompt, throwIfResultError };