@burdenoff/fluidgrids-fe-sdk 2026.1112.7 → 2026.1112.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiStudioPage-QGTFaVpx.js → AiStudioPage-DfoV1Olc.js} +5 -5
- package/dist/ApprovalInboxPage-Bb8H9ruw.js +1317 -0
- package/dist/{DataSinkDashboardPage-kyzcjpzL.js → DataSinkDashboardPage-Dt3xTBb8.js} +1 -1
- package/dist/{DataTablesPage-0U89KNBt.js → DataTablesPage-Zz23QZQp.js} +6 -4
- package/dist/Disclosure-DQiUKrqd.js +41 -0
- package/dist/{EntityTagPicker-dxfzv8aO.js → EntityTagPicker-CuZAMby_.js} +1 -1
- package/dist/{ExecutionDetailPage-D4Mdbzb2.js → ExecutionDetailPage-CcUxFexZ.js} +15 -14
- package/dist/{ExecutionsListPage-g_SPxAGX.js → ExecutionsListPage-JpByw6nA.js} +11 -9
- package/dist/{FailureTriagePanel-D4lJ4zqQ.js → FailureTriagePanel-BjatYyAa.js} +1 -1
- package/dist/{FluidGridsProvider-B1hgCbxG.js → FluidGridsProvider-TQ-aDPtn.js} +3 -3
- package/dist/{HistoryPage-C7NwkVNC.js → HistoryPage-s9pl5gyl.js} +2 -2
- package/dist/{InsightsPage-D9B_qW8e.js → InsightsPage-CtjJCky-.js} +4 -4
- package/dist/{MissingNodesInstallModal-BkkgeFeN.js → MissingNodesInstallModal-olEc52p2.js} +1 -1
- package/dist/{NodesPage-BEeUbIts.js → NodesPage-BSNpOUMX.js} +2 -2
- package/dist/OpenTemplateAsCodeButton-Dh3ee2OR.js +94 -0
- package/dist/PaidTemplateConfirm-DjmVi_m-.js +28 -0
- package/dist/{RunActionsMenu-DmO14hEE.js → RunActionsMenu-BE_1r3bF.js} +1 -50
- package/dist/{RunnersPage-CvAPHdfU.js → RunnersPage-CAt4YDhB.js} +2 -1
- package/dist/{SettingsPage-k21044-Z.js → SettingsPage-ywyjIAL8.js} +6 -6
- package/dist/{TagFilterSelect-CqhxcDjh.js → TagFilterSelect-DcusTt1p.js} +1 -1
- package/dist/{TaskQueuesPage-B86p51gH.js → TaskQueuesPage-C2dkenVF.js} +2 -2
- package/dist/TechnicalDetails-D4xGy201.js +19 -0
- package/dist/{TemplateCard-DanOe65C.js → TemplateCard--WYJZlA_.js} +293 -36
- package/dist/{TemplateConfigurationModal-CeMpPEhT.js → TemplateConfigurationModal-cd0-QVMZ.js} +20 -11
- package/dist/{TemplateDetailPanel-DnoA6mC3.js → TemplateDetailPanel-C-2JuXg6.js} +1 -1
- package/dist/TemplatePreview-BCnGjMuN.js +646 -0
- package/dist/{TemplatesPage-C7f2xq12.js → TemplatesPage-u7bXJYfv.js} +377 -138
- package/dist/{VariablesPage-BtoLjmUA.js → VariablesPage-CPL34WBc.js} +2 -358
- package/dist/{VersionDiffDialog-pLpG_I1r.js → VersionDiffDialog-c27yet_-.js} +1 -1
- package/dist/{WorkflowBuilder-Aslfa2vj.js → WorkflowBuilder-BWxBmBoP.js} +3 -3
- package/dist/{WorkflowBuilder-DY1_KJiV.js → WorkflowBuilder-DxAAPHsn.js} +8 -8
- package/dist/{WorkflowProposalCard-BjYAZH3q.js → WorkflowProposalCard-BvmJ5ea8.js} +1 -1
- package/dist/{WorkflowSearchRedirect-iPQBL6WO.js → WorkflowSearchRedirect-BGKduhfZ.js} +1 -1
- package/dist/{WorkflowShareDialog-DB-qJLB2.js → WorkflowShareDialog-BRwTF9QB.js} +1 -1
- package/dist/{WorkflowsList-rOxlI49U.js → WorkflowsList-Bocag4TX.js} +9 -8
- package/dist/{agentTools-B8gnHFVM.js → agentTools-JHtYGgd7.js} +3 -3
- package/dist/{api-JpNuowX4.js → api-Dw8nfVRD.js} +1 -1
- package/dist/{api-Dvitj1aT.js → api-m8hX_l2k.js} +1 -1
- package/dist/{canvasNoteTools-Cj7MvVDH.js → canvasNoteTools-B2ouyIbj.js} +2 -2
- package/dist/{HumanRequestCard-lbWDpWd7.js → channel-CZ70oI81.js} +5 -228
- package/dist/{code-editor-field-yJZEDJBi.js → code-editor-field-CWGZyNGM.js} +1 -1
- package/dist/executions.js +2 -2
- package/dist/{feature-flags-DM5myWaD.js → feature-flags-B_b8PJ6Z.js} +63 -3
- package/dist/flags.d.ts +1 -1
- package/dist/flags.js +1 -1
- package/dist/{flow-builder-dvqpKs8d.js → flow-builder-DzKPrW2r.js} +535 -313
- package/dist/{graphql-operation-types-z2oXfQvM.js → graphql-operation-types-DZVhCBEi.js} +250 -1
- package/dist/history.js +1 -1
- package/dist/{index-BcdtKBM-.d.ts → index-B4g88tCa.d.ts} +15 -1
- package/dist/{index-DNo9aFUT.d.ts → index-CGCAcRKo.d.ts} +32 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +38 -38
- package/dist/{node-detail-view-ZnJ3Hvvo.js → node-detail-view-DNilUGcx.js} +9 -9
- package/dist/{node-editor-tab-body-BuiIr7VD.js → node-editor-tab-body-XBedxkpN.js} +3 -3
- package/dist/{node-endpoint-configs-typvqw2c.js → node-endpoint-configs-BI7zc8eM.js} +1 -1
- package/dist/nodes.js +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/{popover-mexhIFP3.js → popover-BC4r84K0.js} +3 -3
- package/dist/{quick-config-panel-EV66lJOM.js → quick-config-panel-BPmrhIOb.js} +3 -3
- package/dist/{schedule-panel-BeLfDNFh.js → schedule-panel-DJN1TDQH.js} +8 -8
- package/dist/settings.js +1 -1
- package/dist/{sidebar-node-editor-D85Moj5d.js → sidebar-node-editor-DKdfcd2r.js} +3 -3
- package/dist/templates.js +2 -2
- package/dist/{useAgentDefinitions-Uy6nhpKN.js → useAgentDefinitions-B4sw7bLw.js} +1 -1
- package/dist/{useDurableParity-04-5PTk2.js → useDurableParity-B3h8jqP0.js} +2 -2
- package/dist/{useEntityTags-D5C0tdLs.js → useEntityTags-BpDeLhzV.js} +1 -1
- package/dist/{useExecutions-DJXtbA15.js → useExecutions-BbkBSYpS.js} +2 -2
- package/dist/{useObservability-Dfgh4yjv.js → useObservability-BASo2sA3.js} +1 -1
- package/dist/{useRunHistory-BZQ3t68Q.js → useRunHistory-C3Pv_SJW.js} +2 -2
- package/dist/useVariables-cI8uLwye.js +361 -0
- package/dist/useWorkflowCodeExport-k5Emm4ZS.js +226 -0
- package/dist/{useWorkflows-CP-d3ygY.js → useWorkflows-CRUlQBd3.js} +2 -2
- package/dist/workflow-builder.js +4 -4
- package/dist/workflow-code-transfer-Cshsgf2H.js +375 -0
- package/dist/workflow-host.d.ts +1 -1
- package/dist/workflow-host.js +1 -1
- package/dist/workflow-pages.d.ts +1 -1
- package/dist/workflow-pages.js +19 -19
- package/dist/workflow-transfer-CQnQC-ke.js +104 -0
- package/dist/workflows-list.js +1 -1
- package/package.json +1 -1
- package/dist/ApprovalInboxPage-QAJim81n.js +0 -559
- package/dist/QuotaExhaustedDialog-Bj7AG_0y.js +0 -55
- package/dist/TemplatePreview-DY4GxnfC.js +0 -231
- package/dist/useWorkflowCodeExport-D0zapXXm.js +0 -643
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { _ as throwIfResultError, n as getFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
|
|
3
3
|
import { i as getGlobalStoreClient } from "./node-registry-refresh-Dy0uRg9f.js";
|
|
4
|
-
import { A as StoreNodeProductsByTypeDocument, C as GetMyNodeInstallationsDocument, D as InstallWorkflowNodeDocument, c as ensureInstalledNodeTypesLoaded, d as ensureRegistryNodeTypesLoaded, i as AVAILABLE_NODES, o as NODE_CONFIGS, t as FluidGridsContext, vn as useFluidGridsAnalytics, x as BrowseWorkflowTemplatesDocument } from "./FluidGridsProvider-
|
|
4
|
+
import { A as StoreNodeProductsByTypeDocument, C as GetMyNodeInstallationsDocument, D as InstallWorkflowNodeDocument, c as ensureInstalledNodeTypesLoaded, d as ensureRegistryNodeTypesLoaded, i as AVAILABLE_NODES, o as NODE_CONFIGS, t as FluidGridsContext, vn as useFluidGridsAnalytics, x as BrowseWorkflowTemplatesDocument } from "./FluidGridsProvider-TQ-aDPtn.js";
|
|
5
5
|
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
6
|
+
import { h as usePhaseDFlags } from "./feature-flags-B_b8PJ6Z.js";
|
|
6
7
|
import { c as isQuotaExhaustedFailure } from "./quota-Cdt8yz7N.js";
|
|
7
8
|
import { a as emitTemplateUsed, s as emitWorkflowCreated, t as emitNodeInstalled } from "./fluidgridsEvents-Dm5CaHtr.js";
|
|
8
9
|
import { t as cn } from "./cn-QqidFIgr.js";
|
|
9
10
|
import { t as Button } from "./button-DG7y_FVJ.js";
|
|
11
|
+
import { b as utf8Length } from "./workflow-code-transfer-Cshsgf2H.js";
|
|
10
12
|
import { i as SdkPermissionButton, t as FLUIDGRIDS_PERMISSIONS } from "./constants-dcxg6zG6.js";
|
|
13
|
+
import { n as useNavigationV2 } from "./useNavigationV2-C2WeqN7d.js";
|
|
11
14
|
import React, { Suspense, forwardRef, lazy, useCallback, useContext, useMemo, useState } from "react";
|
|
12
15
|
import { useQuery } from "@apollo/client/react";
|
|
13
16
|
import { gql } from "@apollo/client/core";
|
|
@@ -17,6 +20,7 @@ import { toast } from "sonner";
|
|
|
17
20
|
import { useEventBus } from "@burdenoff/fe-libs/shared/events";
|
|
18
21
|
import { cva } from "class-variance-authority";
|
|
19
22
|
import { Download, Eye, Sparkles, Star } from "lucide-react";
|
|
23
|
+
import JSZip from "jszip";
|
|
20
24
|
|
|
21
25
|
// src/feature-flags/durable-nodes.ts
|
|
22
26
|
/**
|
|
@@ -567,7 +571,7 @@ function replaceVariables(str, inputs) {
|
|
|
567
571
|
}
|
|
568
572
|
|
|
569
573
|
// src/utils/template-import-flow.ts
|
|
570
|
-
function isRecord(value) {
|
|
574
|
+
function isRecord$1(value) {
|
|
571
575
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
572
576
|
}
|
|
573
577
|
function graphOf(value) {
|
|
@@ -585,13 +589,13 @@ function graphOf(value) {
|
|
|
585
589
|
* manifest holds neither.
|
|
586
590
|
*/
|
|
587
591
|
function manifestTemplateGraph(manifest) {
|
|
588
|
-
if (!isRecord(manifest)) return null;
|
|
592
|
+
if (!isRecord$1(manifest)) return null;
|
|
589
593
|
const direct = graphOf(manifest);
|
|
590
594
|
if (direct) return direct;
|
|
591
595
|
if (typeof manifest.jsonContent !== "string") return null;
|
|
592
596
|
try {
|
|
593
597
|
const parsed = JSON.parse(manifest.jsonContent);
|
|
594
|
-
return isRecord(parsed) ? graphOf(parsed) : null;
|
|
598
|
+
return isRecord$1(parsed) ? graphOf(parsed) : null;
|
|
595
599
|
} catch {
|
|
596
600
|
return null;
|
|
597
601
|
}
|
|
@@ -630,9 +634,9 @@ async function resolveTemplateContent(product, requiredNodeTypesOf) {
|
|
|
630
634
|
*/
|
|
631
635
|
function unwrapTemplateDefinition(definitionJson) {
|
|
632
636
|
const data = definitionJson.data;
|
|
633
|
-
if (!isRecord(data)) return definitionJson;
|
|
634
|
-
const nested = isRecord(data.workflowDefinition) ? data.workflowDefinition.definitionJson : void 0;
|
|
635
|
-
return isRecord(nested) ? nested : definitionJson;
|
|
637
|
+
if (!isRecord$1(data)) return definitionJson;
|
|
638
|
+
const nested = isRecord$1(data.workflowDefinition) ? data.workflowDefinition.definitionJson : void 0;
|
|
639
|
+
return isRecord$1(nested) ? nested : definitionJson;
|
|
636
640
|
}
|
|
637
641
|
/**
|
|
638
642
|
* Whether "Use this template" must stop at the configuration dialog before
|
|
@@ -673,6 +677,201 @@ async function installNodesInOrder(nodes, install, onProgress, onFailure) {
|
|
|
673
677
|
return installed;
|
|
674
678
|
}
|
|
675
679
|
|
|
680
|
+
// src/utils/template-code.ts
|
|
681
|
+
/**
|
|
682
|
+
* Templates as code (BOFF-9907, PLAN-D D2 and C-TEMPLATE).
|
|
683
|
+
*
|
|
684
|
+
* A store WORKFLOW template may carry its project files in `content.sources`
|
|
685
|
+
* next to the `definitionJson` every client installs. These helpers:
|
|
686
|
+
*
|
|
687
|
+
* - read a template's code defensively (store content is free JSON);
|
|
688
|
+
* - package it as a project zip for *Open as code*;
|
|
689
|
+
* - shape it as the `source` of `importWorkflowTemplate` for a tier-2
|
|
690
|
+
* install;
|
|
691
|
+
* - check code before it is published, with the same rules global-store-svc
|
|
692
|
+
* applies on every write (DD3 size guard, path rules, DD7), so a refusal is
|
|
693
|
+
* explained before anything is sent.
|
|
694
|
+
*
|
|
695
|
+
* Nothing here lifts or lowers code: the service does that.
|
|
696
|
+
*/
|
|
697
|
+
/** DD3: the largest `content.sources` the store accepts, as UTF-8 JSON. */
|
|
698
|
+
const TEMPLATE_SOURCES_MAX_BYTES = 1048576;
|
|
699
|
+
/** DD3: the most files `content.sources` may hold. */
|
|
700
|
+
const TEMPLATE_SOURCES_MAX_FILES = 64;
|
|
701
|
+
/** The only package a tier-2 template's sources may import (C-TEMPLATE). */
|
|
702
|
+
const TEMPLATE_RUNTIME_PACKAGE = "@fluidgrids/workflows";
|
|
703
|
+
var TemplateCodeError = class extends Error {
|
|
704
|
+
problem;
|
|
705
|
+
constructor(problem) {
|
|
706
|
+
super(`Template code refused: ${problem.code}`);
|
|
707
|
+
this.problem = problem;
|
|
708
|
+
this.name = "TemplateCodeError";
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
function isRecord(value) {
|
|
712
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Whether a source path is a plain relative POSIX path, as global-store-svc
|
|
716
|
+
* requires: not empty, not absolute, no drive letter, no backslash, no `.`,
|
|
717
|
+
* `..` or empty segment, and no control character.
|
|
718
|
+
*/
|
|
719
|
+
function isSafeTemplateSourcePath(path) {
|
|
720
|
+
if (!path || path.startsWith("/") || path.includes("\\")) return false;
|
|
721
|
+
if (/^[A-Za-z]:/.test(path)) return false;
|
|
722
|
+
for (const character of path) {
|
|
723
|
+
const code = character.charCodeAt(0);
|
|
724
|
+
if (code < 32 || code === 127) return false;
|
|
725
|
+
}
|
|
726
|
+
return path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
|
|
727
|
+
}
|
|
728
|
+
/** Bytes the store counts for a sources list: its UTF-8 JSON. */
|
|
729
|
+
function templateSourcesBytes(sources) {
|
|
730
|
+
return utf8Length(JSON.stringify(sources));
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Module specifiers a source imports: static and dynamic `import`, `export
|
|
734
|
+
* … from` and `require`. Comments are not stripped, so a commented-out import
|
|
735
|
+
* is counted too; that only makes the check stricter.
|
|
736
|
+
*/
|
|
737
|
+
function importedSpecifiers(source) {
|
|
738
|
+
const found = [];
|
|
739
|
+
for (const pattern of [
|
|
740
|
+
/\b(?:import|export)\s[^'"`;]*?\bfrom\s*['"]([^'"]+)['"]/g,
|
|
741
|
+
/\bimport\s*['"]([^'"]+)['"]/g,
|
|
742
|
+
/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
743
|
+
/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
744
|
+
]) for (const match of source.matchAll(pattern)) if (match[1] !== void 0) found.push(match[1]);
|
|
745
|
+
return found;
|
|
746
|
+
}
|
|
747
|
+
/** A specifier a tier-2 source may use: a project file or the SDK itself. */
|
|
748
|
+
function isAllowedSpecifier(specifier) {
|
|
749
|
+
return specifier.startsWith("./") || specifier.startsWith("../") || specifier === "@fluidgrids/workflows";
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Checks sources against the store's rules (and, for tier 2, the import
|
|
753
|
+
* rule). Throws {@link TemplateCodeError} with the first problem found.
|
|
754
|
+
*/
|
|
755
|
+
function assertTemplateSources(sources, tier) {
|
|
756
|
+
if (sources.length === 0) throw new TemplateCodeError({ code: "no-files" });
|
|
757
|
+
if (sources.length > 64) throw new TemplateCodeError({
|
|
758
|
+
code: "too-many-files",
|
|
759
|
+
limit: 64
|
|
760
|
+
});
|
|
761
|
+
const seen = /* @__PURE__ */ new Set();
|
|
762
|
+
for (const file of sources) {
|
|
763
|
+
if (!isSafeTemplateSourcePath(file.path)) throw new TemplateCodeError({
|
|
764
|
+
code: "unsafe-path",
|
|
765
|
+
path: file.path
|
|
766
|
+
});
|
|
767
|
+
const folded = file.path.toLowerCase();
|
|
768
|
+
if (seen.has(folded)) throw new TemplateCodeError({
|
|
769
|
+
code: "duplicate-path",
|
|
770
|
+
path: file.path
|
|
771
|
+
});
|
|
772
|
+
seen.add(folded);
|
|
773
|
+
if (tier === "tier2" && /\.[cm]?[jt]s$/.test(file.path)) {
|
|
774
|
+
const foreign = importedSpecifiers(file.content).find((specifier) => !isAllowedSpecifier(specifier));
|
|
775
|
+
if (foreign !== void 0) throw new TemplateCodeError({
|
|
776
|
+
code: "foreign-import",
|
|
777
|
+
path: file.path,
|
|
778
|
+
specifier: foreign
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (templateSourcesBytes(sources) > 1048576) throw new TemplateCodeError({
|
|
783
|
+
code: "too-large",
|
|
784
|
+
limitBytes: TEMPLATE_SOURCES_MAX_BYTES
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* A template's code, or null when it has none or what it has is not usable
|
|
789
|
+
* (not a list of files, unsafe paths, an unknown format or tier). The store
|
|
790
|
+
* refuses such content on write; reading defensively keeps a template written
|
|
791
|
+
* before the guard, or by another client, from breaking the page.
|
|
792
|
+
*/
|
|
793
|
+
function readTemplateCode(content) {
|
|
794
|
+
if (!content || !Array.isArray(content.sources)) return null;
|
|
795
|
+
const files = [];
|
|
796
|
+
for (const entry of content.sources) {
|
|
797
|
+
if (!isRecord(entry) || typeof entry.path !== "string" || typeof entry.content !== "string") return null;
|
|
798
|
+
files.push({
|
|
799
|
+
path: entry.path,
|
|
800
|
+
contentType: typeof entry.contentType === "string" ? entry.contentType : contentTypeOf(entry.path),
|
|
801
|
+
content: entry.content
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
const format = content.sourceFormat ?? "TYPESCRIPT";
|
|
805
|
+
if (format !== "TYPESCRIPT" && format !== "PYTHON") return null;
|
|
806
|
+
const tier = content.codeTier ?? "tier1";
|
|
807
|
+
if (tier !== "tier1" && tier !== "tier2") return null;
|
|
808
|
+
try {
|
|
809
|
+
assertTemplateSources(files, "tier1");
|
|
810
|
+
} catch {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
files,
|
|
815
|
+
format,
|
|
816
|
+
tier,
|
|
817
|
+
irHash: typeof content.sourceIrHash === "string" ? content.sourceIrHash : null
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
/** The content type a project file is stored with, by its extension. */
|
|
821
|
+
function contentTypeOf(path) {
|
|
822
|
+
if (/\.[cm]?ts$/.test(path)) return "text/typescript";
|
|
823
|
+
if (/\.[cm]?js$/.test(path)) return "text/javascript";
|
|
824
|
+
if (path.endsWith(".py")) return "text/x-python";
|
|
825
|
+
if (path.endsWith(".json")) return "application/json";
|
|
826
|
+
return "text/plain";
|
|
827
|
+
}
|
|
828
|
+
/** A file name segment that is safe on every OS. */
|
|
829
|
+
function safeSegment(value) {
|
|
830
|
+
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
831
|
+
}
|
|
832
|
+
/** The zip *Open as code* downloads for a template. */
|
|
833
|
+
function templateArchiveName(templateName) {
|
|
834
|
+
return `${safeSegment(templateName) || "template"}.code.zip`;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* *Open as code*: the template's project files as one zip that keeps their
|
|
838
|
+
* paths, so unpacking it gives a FluidGrids project (`fluidgrids push`
|
|
839
|
+
* reads it as it is).
|
|
840
|
+
*/
|
|
841
|
+
async function buildTemplateCodeDownload(templateName, code) {
|
|
842
|
+
const zip = new JSZip();
|
|
843
|
+
for (const file of code.files) zip.file(file.path, file.content);
|
|
844
|
+
return {
|
|
845
|
+
filename: templateArchiveName(templateName),
|
|
846
|
+
blob: await zip.generateAsync({
|
|
847
|
+
type: "blob",
|
|
848
|
+
mimeType: "application/zip"
|
|
849
|
+
})
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
/** The `source` of an `importWorkflowTemplate` for a tier-2 install. */
|
|
853
|
+
function templateImportSource(code) {
|
|
854
|
+
return {
|
|
855
|
+
format: code.format,
|
|
856
|
+
files: code.files.map((file) => ({
|
|
857
|
+
path: file.path,
|
|
858
|
+
content: file.content
|
|
859
|
+
}))
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* What `importWorkflowTemplate` installs a template from (PLAN-D D2): its
|
|
864
|
+
* sources for a tier-2 TypeScript template while code-first, durable-functions and
|
|
865
|
+
* code-templates are on (`codeTemplatesDurable`), else null, and the caller
|
|
866
|
+
* sends `definitionJson` exactly as before (tier 1, Python, no code, or a
|
|
867
|
+
* gate off).
|
|
868
|
+
*/
|
|
869
|
+
function templateInstallSource(content, codeTemplatesDurable) {
|
|
870
|
+
if (!codeTemplatesDurable) return null;
|
|
871
|
+
const code = readTemplateCode(content);
|
|
872
|
+
return code?.tier === "tier2" && code.format === "TYPESCRIPT" ? templateImportSource(code) : null;
|
|
873
|
+
}
|
|
874
|
+
|
|
676
875
|
// src/hooks/store/useImportWorkflowTemplate.ts
|
|
677
876
|
const IMPORT_WORKFLOW_TEMPLATE = gql`
|
|
678
877
|
mutation ImportWorkflowTemplate($input: ImportWorkflowTemplateInput!) {
|
|
@@ -739,6 +938,7 @@ function useImportWorkflowTemplate() {
|
|
|
739
938
|
const context = useContext(FluidGridsContext);
|
|
740
939
|
const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
|
|
741
940
|
const bus = useEventBus();
|
|
941
|
+
const { codeTemplatesDurable } = usePhaseDFlags();
|
|
742
942
|
const getTemplateDetail = useCallback(async (templateId) => {
|
|
743
943
|
return fetchTemplateDetail(templateId);
|
|
744
944
|
}, [fetchTemplateDetail]);
|
|
@@ -771,8 +971,9 @@ function useImportWorkflowTemplate() {
|
|
|
771
971
|
try {
|
|
772
972
|
const content = options?.content ?? parseTemplateContent(template);
|
|
773
973
|
if (!content) throw new Error("Template does not contain valid workflow definition data");
|
|
974
|
+
const installSource = templateInstallSource(content, codeTemplatesDurable);
|
|
774
975
|
let definitionJson = unwrapTemplateDefinition(deepClone(content.definitionJson));
|
|
775
|
-
if (options?.templateInputs) definitionJson = applyTemplateInputs(definitionJson, options.templateInputs);
|
|
976
|
+
if (options?.templateInputs && !installSource) definitionJson = applyTemplateInputs(definitionJson, options.templateInputs);
|
|
776
977
|
const config = getFluidGridsConfig();
|
|
777
978
|
const importContext = {
|
|
778
979
|
...options?.context || {},
|
|
@@ -783,7 +984,7 @@ function useImportWorkflowTemplate() {
|
|
|
783
984
|
const importResult = throwIfResultError(await client.mutate({
|
|
784
985
|
mutation: IMPORT_WORKFLOW_TEMPLATE,
|
|
785
986
|
variables: { input: {
|
|
786
|
-
definitionJson,
|
|
987
|
+
...installSource ? { source: installSource } : { definitionJson },
|
|
787
988
|
name: options?.name || content.defaultWorkflowName || template.name,
|
|
788
989
|
description: options?.description || template.description,
|
|
789
990
|
sourceTemplateId: template.id,
|
|
@@ -830,7 +1031,7 @@ function useImportWorkflowTemplate() {
|
|
|
830
1031
|
workspaceId,
|
|
831
1032
|
workflowId: importResult.workflow.id,
|
|
832
1033
|
source: "template",
|
|
833
|
-
definition: definitionJson
|
|
1034
|
+
definition: installSource ? importResult.workflow.definitionJson : definitionJson
|
|
834
1035
|
});
|
|
835
1036
|
emitTemplateUsed(bus, {
|
|
836
1037
|
workspaceId,
|
|
@@ -864,7 +1065,8 @@ function useImportWorkflowTemplate() {
|
|
|
864
1065
|
globalClient,
|
|
865
1066
|
workspaceId,
|
|
866
1067
|
context?.tenantId,
|
|
867
|
-
bus
|
|
1068
|
+
bus,
|
|
1069
|
+
codeTemplatesDurable
|
|
868
1070
|
]),
|
|
869
1071
|
/** Whether we're fetching template detail */
|
|
870
1072
|
fetchingDetail,
|
|
@@ -1080,7 +1282,7 @@ async function dismissToastWhenShown(id) {
|
|
|
1080
1282
|
}
|
|
1081
1283
|
|
|
1082
1284
|
// src/hooks/store/useTemplateImportFlow.tsx
|
|
1083
|
-
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-
|
|
1285
|
+
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-cd0-QVMZ.js").then((m) => ({ default: m.TemplateConfigurationModal })));
|
|
1084
1286
|
/**
|
|
1085
1287
|
* "Use this template", the same everywhere it is offered: the builder's
|
|
1086
1288
|
* template panel, the Templates page and the template preview page
|
|
@@ -1139,10 +1341,10 @@ function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
|
|
|
1139
1341
|
if (result.success && result.workflow) {
|
|
1140
1342
|
toast.success(tr("fluidgrids.builder.desktopSidebar.workflowCreatedToast", "Workflow created from template!"), {
|
|
1141
1343
|
id: "template-import",
|
|
1142
|
-
description:
|
|
1344
|
+
description: tr("fluidgrids.pages.templatePreview.importSuccessDescription", "\"{{name}}\" is ready to edit", { name: result.workflow.name })
|
|
1143
1345
|
});
|
|
1144
1346
|
if ((content.requiredCredentialTypes?.length ?? 0) > 0) toast.info(tr("fluidgrids.builder.desktopSidebar.credentialsNeededToast", "Credentials needed"), {
|
|
1145
|
-
description:
|
|
1347
|
+
description: tr("fluidgrids.builder.desktopSidebar.credentialsNeededDescription", "Configure {{types}} in the Credentials page", { types: (content.requiredCredentialTypes ?? []).join(", ") }),
|
|
1146
1348
|
duration: 6e3
|
|
1147
1349
|
});
|
|
1148
1350
|
onImported(result.workflow);
|
|
@@ -1286,12 +1488,78 @@ function templatePreview(template) {
|
|
|
1286
1488
|
};
|
|
1287
1489
|
}
|
|
1288
1490
|
|
|
1491
|
+
// src/utils/template-price.ts
|
|
1492
|
+
/**
|
|
1493
|
+
* The price shown on a store template card and in the preview.
|
|
1494
|
+
*
|
|
1495
|
+
* USD (and a template with no currency) keeps the plain "$12" form the cards
|
|
1496
|
+
* have always shown; any other currency is written with its own symbol, so a
|
|
1497
|
+
* template priced in rupees or euros is not shown with a dollar sign.
|
|
1498
|
+
*/
|
|
1499
|
+
const formatters = /* @__PURE__ */ new Map();
|
|
1500
|
+
function formatterFor(currency) {
|
|
1501
|
+
const cached = formatters.get(currency);
|
|
1502
|
+
if (cached) return cached;
|
|
1503
|
+
try {
|
|
1504
|
+
const created = new Intl.NumberFormat(void 0, {
|
|
1505
|
+
style: "currency",
|
|
1506
|
+
currency,
|
|
1507
|
+
maximumFractionDigits: 2
|
|
1508
|
+
});
|
|
1509
|
+
formatters.set(currency, created);
|
|
1510
|
+
return created;
|
|
1511
|
+
} catch {
|
|
1512
|
+
return null;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
function formatTemplatePrice(price, currency) {
|
|
1516
|
+
const code = currency?.trim().toUpperCase() || "USD";
|
|
1517
|
+
if (code === "USD") return `$${price?.toString() || "0"}`;
|
|
1518
|
+
const formatter = formatterFor(code);
|
|
1519
|
+
return formatter ? formatter.format(price ?? 0) : `${price?.toString() || "0"} ${code}`;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* A template that costs money and has not been bought yet: "Use this
|
|
1523
|
+
* template" asks first, naming the price (Navigation v2, BOFF-11848).
|
|
1524
|
+
*/
|
|
1525
|
+
function isPaidTemplate(template) {
|
|
1526
|
+
return !!template.pricingModel && template.pricingModel !== "FREE" && (template.price ?? 0) > 0 && template.isPurchased !== true;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1289
1529
|
// src/builder/components/flow-builder/components/templates/TemplateCard.tsx
|
|
1290
1530
|
function formatNumber(count) {
|
|
1291
1531
|
if (count >= 1e6) return `${(count / 1e6).toFixed(1)}M`;
|
|
1292
1532
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
1293
1533
|
return count.toString();
|
|
1294
1534
|
}
|
|
1535
|
+
function PriceBadge({ template }) {
|
|
1536
|
+
const tr = useTr();
|
|
1537
|
+
return template.pricingModel === "FREE" ? /* @__PURE__ */ jsx(Badge, {
|
|
1538
|
+
variant: "secondary",
|
|
1539
|
+
className: "bg-status-success-bg text-action-primary-text border-0",
|
|
1540
|
+
children: tr("fluidgrids.builder.templateCard.freeBadge", "Free")
|
|
1541
|
+
}) : /* @__PURE__ */ jsx(Badge, {
|
|
1542
|
+
variant: "default",
|
|
1543
|
+
className: "bg-status-info-bg text-action-primary-text border-0",
|
|
1544
|
+
children: formatTemplatePrice(template.price, template.currency)
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
/** Downloads and views (today's card; Navigation v2 shows them on the preview). */
|
|
1548
|
+
function UsageCounts({ template }) {
|
|
1549
|
+
const tr = useTr();
|
|
1550
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1551
|
+
className: "flex items-center gap-3",
|
|
1552
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1553
|
+
className: "flex items-center gap-1",
|
|
1554
|
+
title: tr("fluidgrids.builder.templateCard.downloadsTooltip", "Downloads"),
|
|
1555
|
+
children: [/* @__PURE__ */ jsx(Download, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.downloads || 0) })]
|
|
1556
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1557
|
+
className: "flex items-center gap-1",
|
|
1558
|
+
title: tr("fluidgrids.builder.templateCard.viewsTooltip", "Views"),
|
|
1559
|
+
children: [/* @__PURE__ */ jsx(Eye, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.viewCount || 0) })]
|
|
1560
|
+
})]
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1295
1563
|
/**
|
|
1296
1564
|
* Template card component inspired by ComfyUI's template showcase
|
|
1297
1565
|
* Features:
|
|
@@ -1301,9 +1569,14 @@ function formatNumber(count) {
|
|
|
1301
1569
|
* - Stats (downloads, views, rating)
|
|
1302
1570
|
* - Featured badge
|
|
1303
1571
|
* - Category/tags
|
|
1572
|
+
*
|
|
1573
|
+
* Navigation v2 (BOFF-11848, the Templates gallery only): the download and
|
|
1574
|
+
* view counts move to the template preview. The builder's template picker
|
|
1575
|
+
* turns the switch off for its cards (see TemplatesGrid), so it stays as is.
|
|
1304
1576
|
*/
|
|
1305
1577
|
function TemplateCard({ template, onClick, onUseTemplate }) {
|
|
1306
1578
|
const tr = useTr();
|
|
1579
|
+
const v2 = useNavigationV2();
|
|
1307
1580
|
const [imageError, setImageError] = React.useState(false);
|
|
1308
1581
|
const preview = templatePreview(template);
|
|
1309
1582
|
const previewImage = preview.kind === "image" ? preview.src : void 0;
|
|
@@ -1346,15 +1619,7 @@ function TemplateCard({ template, onClick, onUseTemplate }) {
|
|
|
1346
1619
|
}),
|
|
1347
1620
|
/* @__PURE__ */ jsx("div", {
|
|
1348
1621
|
className: "absolute top-2 left-2",
|
|
1349
|
-
children:
|
|
1350
|
-
variant: "secondary",
|
|
1351
|
-
className: "bg-status-success-bg text-action-primary-text border-0",
|
|
1352
|
-
children: tr("fluidgrids.builder.templateCard.freeBadge", "Free")
|
|
1353
|
-
}) : /* @__PURE__ */ jsxs(Badge, {
|
|
1354
|
-
variant: "default",
|
|
1355
|
-
className: "bg-status-info-bg text-action-primary-text border-0",
|
|
1356
|
-
children: ["$", template.price?.toString() || "0"]
|
|
1357
|
-
})
|
|
1622
|
+
children: /* @__PURE__ */ jsx(PriceBadge, { template })
|
|
1358
1623
|
}),
|
|
1359
1624
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-bg-overlay via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" })
|
|
1360
1625
|
]
|
|
@@ -1377,6 +1642,9 @@ function TemplateCard({ template, onClick, onUseTemplate }) {
|
|
|
1377
1642
|
}), template.publisher?.isVerified && /* @__PURE__ */ jsx(Badge, {
|
|
1378
1643
|
variant: "secondary",
|
|
1379
1644
|
className: "text-xs px-1.5 py-0",
|
|
1645
|
+
role: "img",
|
|
1646
|
+
"aria-label": tr("fluidgrids.builder.templateCard.verifiedLabel", "Verified publisher"),
|
|
1647
|
+
title: tr("fluidgrids.builder.templateCard.verifiedLabel", "Verified publisher"),
|
|
1380
1648
|
children: "✓"
|
|
1381
1649
|
})]
|
|
1382
1650
|
}),
|
|
@@ -1402,18 +1670,7 @@ function TemplateCard({ template, onClick, onUseTemplate }) {
|
|
|
1402
1670
|
})
|
|
1403
1671
|
]
|
|
1404
1672
|
})
|
|
1405
|
-
}), /* @__PURE__ */
|
|
1406
|
-
className: "flex items-center gap-3",
|
|
1407
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1408
|
-
className: "flex items-center gap-1",
|
|
1409
|
-
title: tr("fluidgrids.builder.templateCard.downloadsTooltip", "Downloads"),
|
|
1410
|
-
children: [/* @__PURE__ */ jsx(Download, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.downloads || 0) })]
|
|
1411
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1412
|
-
className: "flex items-center gap-1",
|
|
1413
|
-
title: tr("fluidgrids.builder.templateCard.viewsTooltip", "Views"),
|
|
1414
|
-
children: [/* @__PURE__ */ jsx(Eye, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.viewCount || 0) })]
|
|
1415
|
-
})]
|
|
1416
|
-
})]
|
|
1673
|
+
}), !v2 && /* @__PURE__ */ jsx(UsageCounts, { template })]
|
|
1417
1674
|
}),
|
|
1418
1675
|
(template.category || template.tags?.length > 0) && /* @__PURE__ */ jsxs("div", {
|
|
1419
1676
|
className: "flex flex-wrap gap-1",
|
|
@@ -1450,4 +1707,4 @@ function TemplateCard({ template, onClick, onUseTemplate }) {
|
|
|
1450
1707
|
});
|
|
1451
1708
|
}
|
|
1452
1709
|
|
|
1453
|
-
export {
|
|
1710
|
+
export { DURABLE_FUNCTION_NODE_TYPES as A, useGlobalStoreClient as C, CardFooter as D, CardDescription as E, isNodeTypeHiddenFromPalette as F, isRunControlNodeType as I, WORKFLOW_TEST_NODE_TYPES as M, filterPaletteNodes as N, CardHeader as O, isDurableFunctionNodeType as P, useInstalledNodes as S, CardContent as T, getStoreNodeIcon as _, Badge as a, storeNodeDisplayVersion as b, buildNodeCheck as c, useImportWorkflowTemplate as d, buildTemplateCodeDownload as f, getStoreNodeAvailability as g, filterStoreNodesForPalette as h, templatePreview as i, RUN_CONTROL_NODE_TYPES as j, CardTitle as k, buildRequiredNodeTypes as l, useWorkflowTemplates as m, formatTemplatePrice as n, badgeVariants as o, readTemplateCode as p, isPaidTemplate as r, useTemplateImportFlow as s, TemplateCard as t, getWorkflowNodeTypes as u, getStoreNodeType as v, Card as w, storeNodeInstallVersion as x, isStoreNodeUpdateAvailable as y };
|
package/dist/{TemplateConfigurationModal-CeMpPEhT.js → TemplateConfigurationModal-cd0-QVMZ.js}
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { a as DialogFooter, d as Input, i as DialogDescription, l as DialogTitle, o as DialogHeader, r as DialogContent, t as Dialog } from "./dialog-Cjb4lB4D.js";
|
|
3
3
|
import { t as Button } from "./button-DG7y_FVJ.js";
|
|
4
|
-
import {
|
|
4
|
+
import { a as Badge } from "./TemplateCard--WYJZlA_.js";
|
|
5
5
|
import { t as Label } from "./label-CDy86rIZ.js";
|
|
6
6
|
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
7
|
+
import { t as OpenTemplateAsCodeButton } from "./OpenTemplateAsCodeButton-Dh3ee2OR.js";
|
|
7
8
|
import { useEffect, useMemo, useState } from "react";
|
|
8
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
import { AlertTriangle, CheckCircle2, Download, ExternalLink, KeyRound, Loader2, ShoppingCart, Sparkles } from "lucide-react";
|
|
@@ -246,16 +247,24 @@ function TemplateConfigurationModal({ open, onOpenChange, template, content, nod
|
|
|
246
247
|
})]
|
|
247
248
|
}), /* @__PURE__ */ jsxs("div", {
|
|
248
249
|
className: "flex w-full justify-end gap-2",
|
|
249
|
-
children: [
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
250
|
+
children: [
|
|
251
|
+
/* @__PURE__ */ jsx(OpenTemplateAsCodeButton, {
|
|
252
|
+
templateName: template.name,
|
|
253
|
+
content,
|
|
254
|
+
className: "mr-auto"
|
|
255
|
+
}),
|
|
256
|
+
/* @__PURE__ */ jsx(Button, {
|
|
257
|
+
variant: "outline",
|
|
258
|
+
onClick: () => onOpenChange(false),
|
|
259
|
+
disabled: importing,
|
|
260
|
+
children: tr("fluidgrids.builder.templateConfigModal.cancelButton", "Cancel")
|
|
261
|
+
}),
|
|
262
|
+
/* @__PURE__ */ jsx(Button, {
|
|
263
|
+
onClick: handleConfirm,
|
|
264
|
+
disabled: !isValid || importing || hasPaidBlockers,
|
|
265
|
+
children: importing ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Loader2, { className: "size-4 mr-2 animate-spin" }), buttonLabel] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-4 mr-2" }), buttonLabel] })
|
|
266
|
+
})
|
|
267
|
+
]
|
|
259
268
|
})]
|
|
260
269
|
})
|
|
261
270
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { t as Button } from "./button-DG7y_FVJ.js";
|
|
3
|
-
import {
|
|
3
|
+
import { a as Badge, i as templatePreview } from "./TemplateCard--WYJZlA_.js";
|
|
4
4
|
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
5
5
|
import { i as SdkPermissionButton, t as FLUIDGRIDS_PERMISSIONS } from "./constants-dcxg6zG6.js";
|
|
6
6
|
import { Icon } from "@iconify/react";
|