@fluid-app/fluid-cli-widget 0.1.28 → 0.1.30
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/index-JKSTx5pZ.d.mts +199 -0
- package/dist/index-JKSTx5pZ.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +767 -239
- package/dist/index.mjs.map +1 -1
- package/dist/{src-CVadzcYM.mjs → src-_wfDoBLp.mjs} +357 -30
- package/dist/src-_wfDoBLp.mjs.map +1 -0
- package/dist/vite/plugin.d.mts +2 -86
- package/dist/vite/plugin.mjs +1 -191
- package/package.json +1 -1
- package/templates/default/AGENTS.md +15 -0
- package/templates/default/styles.css +47 -15
- package/dist/index-BYRGkNyt.d.mts +0 -91
- package/dist/index-BYRGkNyt.d.mts.map +0 -1
- package/dist/src-CVadzcYM.mjs.map +0 -1
- package/dist/vite/plugin.d.mts.map +0 -1
- package/dist/vite/plugin.mjs.map +0 -1
|
@@ -586,12 +586,265 @@ function toDevelopmentCssUrl(url, origin) {
|
|
|
586
586
|
return href.includes("?") ? `${href}&direct` : `${href}?direct`;
|
|
587
587
|
}
|
|
588
588
|
//#endregion
|
|
589
|
+
//#region ../widget-tooling/src/dev-routes.ts
|
|
590
|
+
function fluidWidgetDevPlugin(options = {}) {
|
|
591
|
+
const entryModulePath = options.entryModulePath ?? WIDGET_ENTRY_MODULE_PATH;
|
|
592
|
+
const configModulePath = options.configModulePath ?? WIDGET_CONFIG_MODULE_PATH;
|
|
593
|
+
const owner = options.owner ?? "droplet";
|
|
594
|
+
return {
|
|
595
|
+
name: "fluid-widget-dev-routes",
|
|
596
|
+
apply: "serve",
|
|
597
|
+
config: createWidgetDevServerConfig,
|
|
598
|
+
configureServer(server) {
|
|
599
|
+
server.middlewares.use(async (request, response, next) => {
|
|
600
|
+
const pathname = (request.url ?? "").split("?")[0] ?? "";
|
|
601
|
+
if (pathname === "/__runtime-entry__" || pathname === "/__runtime-entry__/") {
|
|
602
|
+
sendJavaScript(request, response, buildRuntimeEntryBootstrapScript(entryModulePath, getRequestOrigin(request)));
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (pathname === "/__widget-packages__" || pathname === "/__widget-packages__/") {
|
|
606
|
+
await sendWidgetPackages(server, request, response, {
|
|
607
|
+
configModulePath,
|
|
608
|
+
owner,
|
|
609
|
+
allowNoPackages: options.allowNoPackages === true
|
|
610
|
+
});
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
next();
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Widget projects intentionally have no HTML entrypoint, so Vite cannot
|
|
620
|
+
* discover the worker's dependency graph during its normal startup scan.
|
|
621
|
+
* Without an explicit entry, the first live-preview request discovers and
|
|
622
|
+
* optimizes the portal worker after the server reports ready. Vite reloads
|
|
623
|
+
* the worker mid-mount, closing its message thread and stranding the preview
|
|
624
|
+
* in a permanent error state.
|
|
625
|
+
*
|
|
626
|
+
* Pre-optimizing the worker entry makes the initial graph stable before a
|
|
627
|
+
* preview can connect. Vite follows this entry's imports, so React and the
|
|
628
|
+
* Remote DOM worker runtime are optimized in the same startup pass.
|
|
629
|
+
*/
|
|
630
|
+
function createWidgetDevServerConfig() {
|
|
631
|
+
return {
|
|
632
|
+
optimizeDeps: { include: ["@fluid-app/portal-sdk/widgets/worker"] },
|
|
633
|
+
ssr: { noExternal: ["@fluid-app/portal-sdk"] }
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
/** Source-relative path of a widget project's entry module. The dev
|
|
637
|
+
* scaffold always places it here (see the widget template) — this is
|
|
638
|
+
* the one thing the bootstrap script below needs from the plugin
|
|
639
|
+
* that isn't itself part of the race/preamble fix. */
|
|
640
|
+
const WIDGET_ENTRY_MODULE_PATH = "/src/index.ts";
|
|
641
|
+
/** Source-relative path of a standalone widget project's package source. */
|
|
642
|
+
const WIDGET_CONFIG_MODULE_PATH = "/fluid.widget.config.ts";
|
|
643
|
+
/**
|
|
644
|
+
* Builds the `/__runtime-entry__` worker bootstrap. Runtime-verified
|
|
645
|
+
* against a real widget dev server + `@fluid-app/widget-runtime` host
|
|
646
|
+
* in a Playwright harness (styled Leaderboard widget rendering,
|
|
647
|
+
* mutation records flowing, `mounted.ready` resolving) — every piece
|
|
648
|
+
* below exists to fix something that reproduced in that harness:
|
|
649
|
+
*
|
|
650
|
+
* 1. **Message-buffer race (the silent-mount killer).** The host
|
|
651
|
+
* (widget-runtime) posts the "mount" message immediately after
|
|
652
|
+
* `new Worker(...)`. A production-built worker bundle evaluates
|
|
653
|
+
* synchronously and never races that message. In dev, though, the
|
|
654
|
+
* worker's module graph loads asynchronously over many HTTP
|
|
655
|
+
* requests (this bootstrap script, then the entry module, then
|
|
656
|
+
* everything it imports, each a separate fetch) — so the "mount"
|
|
657
|
+
* message can be dispatched and consumed by the Worker's default
|
|
658
|
+
* (listener-less) handling before the widget entry's own
|
|
659
|
+
* `message` listener, wired up deep inside the awaited import
|
|
660
|
+
* below, is ever attached. The message is dropped silently, with
|
|
661
|
+
* no error anywhere — the old `void import("/src/index.ts")` also
|
|
662
|
+
* swallowed the import's own rejections, compounding the silence.
|
|
663
|
+
* Traced live: host→worker "mount" posts, zero worker→host replies
|
|
664
|
+
* until this buffer-and-replay was added; after it, mutation
|
|
665
|
+
* records flow and `mounted.ready` resolves. Fix: buffer every
|
|
666
|
+
* message from the instant the worker boots (before anything else
|
|
667
|
+
* runs), then replay the buffer, in order, once the entry module
|
|
668
|
+
* has finished importing and has had the chance to attach its
|
|
669
|
+
* real listener.
|
|
670
|
+
* 2. **React-refresh preamble.** `@vitejs/plugin-react` injects a
|
|
671
|
+
* preamble into every dev-transformed `.tsx` module referencing
|
|
672
|
+
* `window.$RefreshReg$` / `$RefreshSig$`. A Worker global scope has
|
|
673
|
+
* no `window` and no preamble, so importing a react-based widget
|
|
674
|
+
* entry throws `window is not defined`, and — once `window` is
|
|
675
|
+
* aliased to `globalThis` — throws again on the bare
|
|
676
|
+
* `$RefreshSig$` alias. Installing the canonical preamble
|
|
677
|
+
* (`RefreshRuntime.injectIntoGlobalHook` + no-op `$RefreshReg$` /
|
|
678
|
+
* `$RefreshSig$`) before the entry import fixes both. The
|
|
679
|
+
* `window = globalThis` alias was checked against widget-runtime's
|
|
680
|
+
* worker lockdown / scope detection (which is
|
|
681
|
+
* `globalThis.postMessage`-based) — no conflict. `/@react-refresh`
|
|
682
|
+
* is `@vitejs/plugin-react`'s stable virtual module id, but a
|
|
683
|
+
* widget project isn't guaranteed to use the react plugin, so the
|
|
684
|
+
* whole block is try/catch-guarded — a project with no
|
|
685
|
+
* `/@react-refresh` module (no react plugin) boots straight
|
|
686
|
+
* through to the entry import unaffected.
|
|
687
|
+
* 3. **`await import`, not `void import`.** A genuine failure to
|
|
688
|
+
* import the entry module (syntax error, missing dependency, …)
|
|
689
|
+
* must reject the worker's module evaluation loudly, not vanish
|
|
690
|
+
* the way a fire-and-forget `void import` did.
|
|
691
|
+
* 4. **Absolute dev-server imports.** Mist and portal previews load the
|
|
692
|
+
* worker from another origin, so the host wraps this entry in a blob URL.
|
|
693
|
+
* Root-relative specifiers inside a blob module resolve against the host
|
|
694
|
+
* page (or fail to resolve at all), not this Vite server. Anchoring both
|
|
695
|
+
* imports to the request origin keeps the entire dev module graph on the
|
|
696
|
+
* widget server.
|
|
697
|
+
*
|
|
698
|
+
* Dev-only: this only ever serves from `configureServer` (the
|
|
699
|
+
* plugin's `apply: "serve"` gate), never a built/published package.
|
|
700
|
+
*/
|
|
701
|
+
function buildRuntimeEntryBootstrapScript(entryPath, origin) {
|
|
702
|
+
const refreshRuntimeUrl = new URL("/@react-refresh", origin).toString();
|
|
703
|
+
const entryUrl = new URL(entryPath, origin).toString();
|
|
704
|
+
return [
|
|
705
|
+
"// Dev-only worker bootstrap — see buildRuntimeEntryBootstrapScript",
|
|
706
|
+
"// in packages/cli/widget-tooling/src/dev-routes.ts for why each piece",
|
|
707
|
+
"// here exists (message-buffer race, react-refresh preamble,",
|
|
708
|
+
"// await-not-void import).",
|
|
709
|
+
"const __q = [];",
|
|
710
|
+
"const __buf = (e) => { __q.push(e.data); };",
|
|
711
|
+
"self.addEventListener(\"message\", __buf);",
|
|
712
|
+
"if (typeof window === \"undefined\") { globalThis.window = globalThis; }",
|
|
713
|
+
"try {",
|
|
714
|
+
` const { default: RefreshRuntime } = await import(${JSON.stringify(refreshRuntimeUrl)});`,
|
|
715
|
+
" RefreshRuntime.injectIntoGlobalHook(window);",
|
|
716
|
+
" window.$RefreshReg$ = () => {};",
|
|
717
|
+
" window.$RefreshSig$ = () => (type) => type;",
|
|
718
|
+
" window.__vite_plugin_react_preamble_installed__ = true;",
|
|
719
|
+
"} catch {",
|
|
720
|
+
" // No @vitejs/plugin-react in this widget project (no",
|
|
721
|
+
" // /@react-refresh virtual module) — nothing to preinstall.",
|
|
722
|
+
"}",
|
|
723
|
+
`await import(${JSON.stringify(entryUrl)});`,
|
|
724
|
+
"self.removeEventListener(\"message\", __buf);",
|
|
725
|
+
"for (const data of __q) {",
|
|
726
|
+
" self.dispatchEvent(new MessageEvent(\"message\", { data }));",
|
|
727
|
+
"}",
|
|
728
|
+
""
|
|
729
|
+
].join("\n");
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* The shared development descriptor workflow appends Vite's `?direct` query
|
|
733
|
+
* to CSS URLs so preview hosts receive real CSS instead of JS style modules.
|
|
734
|
+
* Published descriptors use a separate build path and never receive it.
|
|
735
|
+
*/
|
|
736
|
+
function createDevWidgetPackageDescriptors(configModule, origin, owner = "droplet") {
|
|
737
|
+
return [createWidgetPackageDevelopmentDescriptor({
|
|
738
|
+
sourceModule: configModule,
|
|
739
|
+
owner,
|
|
740
|
+
origin,
|
|
741
|
+
workerEntryPath: "/__runtime-entry__",
|
|
742
|
+
defaultCssPath: "/styles.css"
|
|
743
|
+
})];
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* True when validation failed *only* because the project declares no widget
|
|
747
|
+
* package. Checked against the typed diagnostics rather than message text, and
|
|
748
|
+
* deliberately narrow: a config that declares a package and gets it wrong must
|
|
749
|
+
* still be reported.
|
|
750
|
+
*/
|
|
751
|
+
function isNoSourcePackageError(error) {
|
|
752
|
+
if (!(error instanceof WidgetToolingError)) return false;
|
|
753
|
+
const diagnostics = error.diagnostics ?? [];
|
|
754
|
+
return diagnostics.length > 0 && diagnostics.every((diagnostic) => diagnostic.code === "NO_SOURCE_PACKAGE");
|
|
755
|
+
}
|
|
756
|
+
async function sendWidgetPackages(server, request, response, options) {
|
|
757
|
+
if (handleOptions(request, response)) return;
|
|
758
|
+
try {
|
|
759
|
+
sendJson(response, createDevWidgetPackageDescriptors(await server.ssrLoadModule(options.configModulePath), getRequestOrigin(request), options.owner));
|
|
760
|
+
} catch (error) {
|
|
761
|
+
if (options.allowNoPackages && isNoSourcePackageError(error)) {
|
|
762
|
+
sendJson(response, []);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
server.config.logger.error(`[fluid] Failed to load widget packages: ${String(error)}`);
|
|
766
|
+
sendJson(response, { error: String(error) }, 500);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function sendJavaScript(request, response, source) {
|
|
770
|
+
if (handleOptions(request, response)) return;
|
|
771
|
+
setCorsHeaders(response);
|
|
772
|
+
response.setHeader("Content-Type", "text/javascript");
|
|
773
|
+
response.statusCode = 200;
|
|
774
|
+
response.end(source);
|
|
775
|
+
}
|
|
776
|
+
function sendJson(response, body, statusCode = 200) {
|
|
777
|
+
setCorsHeaders(response);
|
|
778
|
+
response.setHeader("Content-Type", "application/json");
|
|
779
|
+
response.statusCode = statusCode;
|
|
780
|
+
response.end(JSON.stringify(body, null, 2));
|
|
781
|
+
}
|
|
782
|
+
function handleOptions(request, response) {
|
|
783
|
+
if (request.method !== "OPTIONS") return false;
|
|
784
|
+
setCorsHeaders(response);
|
|
785
|
+
response.statusCode = 204;
|
|
786
|
+
response.end();
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
function setCorsHeaders(response) {
|
|
790
|
+
response.setHeader("Access-Control-Allow-Origin", "*");
|
|
791
|
+
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
792
|
+
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
793
|
+
}
|
|
794
|
+
function getRequestOrigin(request) {
|
|
795
|
+
const host = readHeader(request.headers.host);
|
|
796
|
+
if (!host) throw new Error("Widget dev request is missing the Host header.");
|
|
797
|
+
return `${readHeader(request.headers["x-forwarded-proto"]) === "https" ? "https" : "http"}://${host}`;
|
|
798
|
+
}
|
|
799
|
+
function readHeader(value) {
|
|
800
|
+
const candidate = Array.isArray(value) ? value[0] : value;
|
|
801
|
+
return typeof candidate === "string" && candidate.trim().length > 0 ? candidate.trim() : void 0;
|
|
802
|
+
}
|
|
803
|
+
//#endregion
|
|
804
|
+
//#region ../widget-tooling/src/node-runtime.ts
|
|
805
|
+
/**
|
|
806
|
+
* How this package runs the helper scripts it generates.
|
|
807
|
+
*
|
|
808
|
+
* Building a widget package writes two scripts to disk and runs them: a config
|
|
809
|
+
* extractor (under `tsx`) and a Vite build. Both used to assume the same thing —
|
|
810
|
+
* that `process.execPath` is Node and that `tsx` is resolvable from this
|
|
811
|
+
* module. Both assumptions hold for a CLI and neither holds in an Electron main
|
|
812
|
+
* process, where `process.execPath` is the Electron binary. Spawning it without
|
|
813
|
+
* `ELECTRON_RUN_AS_NODE` starts a second copy of the app instead of running the
|
|
814
|
+
* script, and `tsx` is not in a packaged app's dependency closure at all.
|
|
815
|
+
*
|
|
816
|
+
* So the host says how to run a script rather than this package guessing. The
|
|
817
|
+
* default reproduces the old behaviour exactly, which is why both CLIs are
|
|
818
|
+
* unchanged by this.
|
|
819
|
+
*/
|
|
820
|
+
/** The executable to spawn, and the env to spawn it with. */
|
|
821
|
+
function resolveSpawnTarget(runtime) {
|
|
822
|
+
return {
|
|
823
|
+
execPath: runtime?.execPath ?? process.execPath,
|
|
824
|
+
env: runtime?.env ?? {}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Resolves `tsx`'s CLI, preferring an injected path.
|
|
829
|
+
*
|
|
830
|
+
* Deliberately a function rather than a module-scope constant. It used to be
|
|
831
|
+
* `const TSX_CLI_PATH = require.resolve("tsx/cli")`, which runs on IMPORT — so a
|
|
832
|
+
* host that merely imported this package, and never published anything, crashed
|
|
833
|
+
* at startup if `tsx` was absent. Resolving on use means a missing `tsx` is a
|
|
834
|
+
* publish-time error the caller can report, not a boot failure.
|
|
835
|
+
*/
|
|
836
|
+
function resolveTsxCliPath(runtime) {
|
|
837
|
+
if (runtime?.tsxCliPath) return runtime.tsxCliPath;
|
|
838
|
+
return createRequire(import.meta.url).resolve("tsx/cli");
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
589
841
|
//#region ../widget-tooling/src/internal/widget-package-config.ts
|
|
590
842
|
const WIDGET_CONFIG_PATH = "src/widgets.config.ts";
|
|
843
|
+
/** Long enough for a cold tsx start on a slow machine, short enough to fail. */
|
|
844
|
+
const CONFIG_EVAL_TIMEOUT_MS = 6e4;
|
|
591
845
|
const SOURCE_PACKAGE_EXTRACT_FILENAME = "extract-widget-packages.ts";
|
|
592
846
|
const SOURCE_PACKAGE_OUTPUT_FILENAME = "source-widget-packages.json";
|
|
593
847
|
const SOURCE_PACKAGE_OUTPUT_SENTINEL = "fluid-widget-source-packages:v1";
|
|
594
|
-
const TSX_CLI_PATH = createRequire(import.meta.url).resolve("tsx/cli");
|
|
595
848
|
async function resolvePortalWidgetSourceConfig(projectDir) {
|
|
596
849
|
const candidate = path.join(projectDir, WIDGET_CONFIG_PATH);
|
|
597
850
|
return await fs.pathExists(candidate) ? {
|
|
@@ -599,7 +852,7 @@ async function resolvePortalWidgetSourceConfig(projectDir) {
|
|
|
599
852
|
relativePath: WIDGET_CONFIG_PATH
|
|
600
853
|
} : void 0;
|
|
601
854
|
}
|
|
602
|
-
async function loadSourceWidgetPackages(projectDir) {
|
|
855
|
+
async function loadSourceWidgetPackages(projectDir, nodeRuntime) {
|
|
603
856
|
const config = await resolvePortalWidgetSourceConfig(projectDir);
|
|
604
857
|
if (!config) return success([]);
|
|
605
858
|
let tempDir;
|
|
@@ -614,8 +867,9 @@ async function loadSourceWidgetPackages(projectDir) {
|
|
|
614
867
|
encoding: "utf-8",
|
|
615
868
|
flag: "wx"
|
|
616
869
|
});
|
|
617
|
-
|
|
618
|
-
|
|
870
|
+
const spawn = resolveSpawnTarget(nodeRuntime);
|
|
871
|
+
await execa(spawn.execPath, [
|
|
872
|
+
resolveTsxCliPath(nodeRuntime),
|
|
619
873
|
extractFile,
|
|
620
874
|
outputFile
|
|
621
875
|
], {
|
|
@@ -623,8 +877,11 @@ async function loadSourceWidgetPackages(projectDir) {
|
|
|
623
877
|
stdio: "pipe",
|
|
624
878
|
env: {
|
|
625
879
|
...process.env,
|
|
880
|
+
...spawn.env,
|
|
626
881
|
NODE_ENV: "production"
|
|
627
|
-
}
|
|
882
|
+
},
|
|
883
|
+
timeout: CONFIG_EVAL_TIMEOUT_MS,
|
|
884
|
+
killSignal: "SIGKILL"
|
|
628
885
|
});
|
|
629
886
|
const outputResult = await readSourcePackageExtractorOutput(outputFile);
|
|
630
887
|
if (!outputResult.success) return outputResult;
|
|
@@ -812,6 +1069,21 @@ function validateSharedWidgetPackagePublishDir(projectDir, publishDir = DEFAULT_
|
|
|
812
1069
|
const relative = path.relative(resolvedProjectDir, resolvedPublishDir);
|
|
813
1070
|
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`${label} must stay inside the project directory.`);
|
|
814
1071
|
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Restamps each loaded source package with `version`.
|
|
1074
|
+
*
|
|
1075
|
+
* Object spread carries own enumerable SYMBOL keys as well as string ones,
|
|
1076
|
+
* which matters here: `defineWidgetPackage` marks its result with a symbol that
|
|
1077
|
+
* validation uses to recognise a real source package. A copy that dropped it
|
|
1078
|
+
* would fail validation with a message about a missing package rather than
|
|
1079
|
+
* about the version.
|
|
1080
|
+
*/
|
|
1081
|
+
function withVersionOverride(sourcePackages, version) {
|
|
1082
|
+
return sourcePackages.map((sourcePackage) => typeof sourcePackage === "object" && sourcePackage !== null ? {
|
|
1083
|
+
...sourcePackage,
|
|
1084
|
+
version
|
|
1085
|
+
} : sourcePackage);
|
|
1086
|
+
}
|
|
815
1087
|
async function buildSharedWidgetPackage(options) {
|
|
816
1088
|
const publishDirInput = options.publishDir ?? DEFAULT_PUBLISH_DIR;
|
|
817
1089
|
try {
|
|
@@ -823,13 +1095,13 @@ async function buildSharedWidgetPackage(options) {
|
|
|
823
1095
|
details: (err instanceof Error ? err : new Error(String(err))).message
|
|
824
1096
|
});
|
|
825
1097
|
}
|
|
826
|
-
const sourcePackageResult = await loadSourceWidgetPackages(options.projectDir);
|
|
1098
|
+
const sourcePackageResult = await loadSourceWidgetPackages(options.projectDir, options.nodeRuntime);
|
|
827
1099
|
if (!sourcePackageResult.success) return failure({
|
|
828
1100
|
code: "CONFIG_LOAD_FAILED",
|
|
829
1101
|
message: sourcePackageResult.error.message,
|
|
830
1102
|
details: sourcePackageResult.error.details
|
|
831
1103
|
});
|
|
832
|
-
const validation = validateSingleSourceWidgetPackage(sourcePackageResult.value, { owner: options.owner });
|
|
1104
|
+
const validation = validateSingleSourceWidgetPackage(options.versionOverride ? withVersionOverride(sourcePackageResult.value, options.versionOverride) : sourcePackageResult.value, { owner: options.owner });
|
|
833
1105
|
if (!validation.success || !validation.value) return failure({
|
|
834
1106
|
code: "VALIDATION_FAILED",
|
|
835
1107
|
message: validation.errors.map((error) => error.message).join("\n")
|
|
@@ -847,7 +1119,8 @@ async function buildSharedWidgetPackage(options) {
|
|
|
847
1119
|
projectDir: options.projectDir,
|
|
848
1120
|
tempDir,
|
|
849
1121
|
publishDir,
|
|
850
|
-
validated: validation.value
|
|
1122
|
+
validated: validation.value,
|
|
1123
|
+
...options.nodeRuntime ? { nodeRuntime: options.nodeRuntime } : {}
|
|
851
1124
|
});
|
|
852
1125
|
} catch (error) {
|
|
853
1126
|
buildFailure = { error };
|
|
@@ -959,7 +1232,8 @@ async function buildWidgetScriptBundle(options) {
|
|
|
959
1232
|
projectConfigPath: await resolveProjectViteConfigPath(options.projectDir)
|
|
960
1233
|
}), "utf-8");
|
|
961
1234
|
const viteCliPath = resolveViteCliPath(options.projectDir);
|
|
962
|
-
|
|
1235
|
+
const spawn = resolveSpawnTarget(options.nodeRuntime);
|
|
1236
|
+
await execa(spawn.execPath, [
|
|
963
1237
|
viteCliPath,
|
|
964
1238
|
"build",
|
|
965
1239
|
"--config",
|
|
@@ -969,6 +1243,7 @@ async function buildWidgetScriptBundle(options) {
|
|
|
969
1243
|
stdio: "pipe",
|
|
970
1244
|
env: {
|
|
971
1245
|
...process.env,
|
|
1246
|
+
...spawn.env,
|
|
972
1247
|
NODE_ENV: "production"
|
|
973
1248
|
}
|
|
974
1249
|
});
|
|
@@ -1355,28 +1630,63 @@ function isRecord$1(value) {
|
|
|
1355
1630
|
return typeof value === "object" && value !== null;
|
|
1356
1631
|
}
|
|
1357
1632
|
//#endregion
|
|
1358
|
-
//#region ../../api-clients/fluidos/src/
|
|
1633
|
+
//#region ../../api-clients/fluidos/src/hey-api/sdk.gen.ts
|
|
1359
1634
|
/**
|
|
1360
1635
|
* Create a Fluid OS widget package version
|
|
1361
|
-
* Start a company-owned Fluid OS widget package publish flow and return signed upload URLs for each artifact.
|
|
1362
1636
|
*
|
|
1363
|
-
*
|
|
1364
|
-
* @param body - body
|
|
1637
|
+
* Start a company-owned Fluid OS widget package publish flow and return signed upload URLs for each artifact.
|
|
1365
1638
|
*/
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1639
|
+
const fluidOsV0CreateWidgetPackageVersion = (options) => options.client.post({
|
|
1640
|
+
responseStyle: "data",
|
|
1641
|
+
security: [{
|
|
1642
|
+
scheme: "bearer",
|
|
1643
|
+
type: "http"
|
|
1644
|
+
}],
|
|
1645
|
+
url: "/api/company/fluid_os/widget_package_versions",
|
|
1646
|
+
...options,
|
|
1647
|
+
headers: {
|
|
1648
|
+
"Content-Type": "application/json",
|
|
1649
|
+
...options.headers
|
|
1650
|
+
}
|
|
1651
|
+
});
|
|
1369
1652
|
/**
|
|
1370
1653
|
* Complete a Fluid OS widget package upload
|
|
1654
|
+
*
|
|
1371
1655
|
* Verify uploaded artifacts for a company-owned Fluid OS widget package version and approve the version when verification succeeds. Uploaded manifest.json is verified for matching packageId, matching version, and valid company-owned `company.<package_key>.` widget type prefixes.
|
|
1656
|
+
*/
|
|
1657
|
+
const fluidOsV0CompleteWidgetPackageVersionUpload = (options) => options.client.post({
|
|
1658
|
+
responseStyle: "data",
|
|
1659
|
+
security: [{
|
|
1660
|
+
scheme: "bearer",
|
|
1661
|
+
type: "http"
|
|
1662
|
+
}],
|
|
1663
|
+
url: "/api/company/fluid_os/widget_package_versions/{version}/complete_upload",
|
|
1664
|
+
...options,
|
|
1665
|
+
headers: {
|
|
1666
|
+
"Content-Type": "application/json",
|
|
1667
|
+
...options.headers
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
/**
|
|
1671
|
+
* Mint a short-lived git credential for the company widget repo
|
|
1672
|
+
*
|
|
1673
|
+
* Returns an HTTPS clone URL with a fresh installation token from the
|
|
1674
|
+
* Fluid GitHub App embedded as the password. The CLI uses it
|
|
1675
|
+
* transparently — no `gh`, no PAT, no SSH key.
|
|
1676
|
+
*
|
|
1677
|
+
* Valid for ~1 hour (GitHub installation token TTL); re-mint on each
|
|
1678
|
+
* operation.
|
|
1372
1679
|
*
|
|
1373
|
-
* @param client - Fetch client instance
|
|
1374
|
-
* @param version - version
|
|
1375
|
-
* @param [body] - body
|
|
1376
1680
|
*/
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1681
|
+
const fluidOsV0WidgetPackageGitCredentialCreate = (options) => options.client.post({
|
|
1682
|
+
responseStyle: "data",
|
|
1683
|
+
security: [{
|
|
1684
|
+
scheme: "bearer",
|
|
1685
|
+
type: "http"
|
|
1686
|
+
}],
|
|
1687
|
+
url: "/api/company/fluid_os/widget_package/git_credential",
|
|
1688
|
+
...options
|
|
1689
|
+
});
|
|
1380
1690
|
//#endregion
|
|
1381
1691
|
//#region ../widget-tooling/src/internal/widget-package-upload.ts
|
|
1382
1692
|
var WidgetPackageUploadError = class extends Error {
|
|
@@ -1428,7 +1738,14 @@ function getWidgetPackageCompleteUploadRoute(owner, version) {
|
|
|
1428
1738
|
async function requestWidgetPackageUploadSession(client, owner, request) {
|
|
1429
1739
|
const payload = buildWidgetPackageUploadSessionPayload(request);
|
|
1430
1740
|
try {
|
|
1431
|
-
return normalizeWidgetPackageUploadSession(owner.kind === "company" ? await
|
|
1741
|
+
return normalizeWidgetPackageUploadSession(owner.kind === "company" ? await fluidOsV0CreateWidgetPackageVersion({
|
|
1742
|
+
body: payload,
|
|
1743
|
+
client
|
|
1744
|
+
}) : await client.post({
|
|
1745
|
+
body: payload,
|
|
1746
|
+
responseStyle: "data",
|
|
1747
|
+
url: getWidgetPackageUploadSessionRoute(owner)
|
|
1748
|
+
}), request.version, request.artifacts);
|
|
1432
1749
|
} catch (err) {
|
|
1433
1750
|
if (err instanceof WidgetPackageUploadError) throw err;
|
|
1434
1751
|
throw new WidgetPackageUploadError("SESSION_REQUEST_FAILED", `Failed to create widget package upload session: ${formatUnknownError(err)}`);
|
|
@@ -1467,7 +1784,15 @@ async function uploadWidgetPackageArtifacts(session, artifacts, fetchImpl = fetc
|
|
|
1467
1784
|
async function completeWidgetPackageUpload(client, owner, version, packageKey, artifacts) {
|
|
1468
1785
|
const payload = buildWidgetPackageCompleteUploadPayload(packageKey, artifacts);
|
|
1469
1786
|
try {
|
|
1470
|
-
return normalizeWidgetPackageCompleteResponse(owner.kind === "company" ? await
|
|
1787
|
+
return normalizeWidgetPackageCompleteResponse(owner.kind === "company" ? await fluidOsV0CompleteWidgetPackageVersionUpload({
|
|
1788
|
+
body: payload,
|
|
1789
|
+
client,
|
|
1790
|
+
path: { version }
|
|
1791
|
+
}) : await client.post({
|
|
1792
|
+
body: payload,
|
|
1793
|
+
responseStyle: "data",
|
|
1794
|
+
url: getWidgetPackageCompleteUploadRoute(owner, version)
|
|
1795
|
+
}), version);
|
|
1471
1796
|
} catch (err) {
|
|
1472
1797
|
if (err instanceof WidgetPackageUploadError) throw err;
|
|
1473
1798
|
throw new WidgetPackageUploadError("COMPLETE_UPLOAD_FAILED", `Failed to complete widget package upload: ${formatUnknownError(err)}`);
|
|
@@ -1479,7 +1804,7 @@ async function publishWidgetPackageVersion(options) {
|
|
|
1479
1804
|
dryRun: true,
|
|
1480
1805
|
sessionPayload
|
|
1481
1806
|
};
|
|
1482
|
-
if (!options.client) throw new Error("A
|
|
1807
|
+
if (!options.client) throw new Error("A FluidOS API client is required when dryRun is false.");
|
|
1483
1808
|
const session = await requestWidgetPackageUploadSession(options.client, options.owner, options.request);
|
|
1484
1809
|
return {
|
|
1485
1810
|
dryRun: false,
|
|
@@ -1594,7 +1919,7 @@ function safeStringify(value) {
|
|
|
1594
1919
|
//#region ../widget-tooling/src/workflows.ts
|
|
1595
1920
|
async function loadAndValidateWidgetPackage(options) {
|
|
1596
1921
|
const sourceConfig = await resolvePortalWidgetSourceConfig(options.projectDir);
|
|
1597
|
-
const loadResult = await loadSourceWidgetPackages(options.projectDir);
|
|
1922
|
+
const loadResult = await loadSourceWidgetPackages(options.projectDir, options.nodeRuntime);
|
|
1598
1923
|
if (!loadResult.success) throw new WidgetToolingError("CONFIG_LOAD_FAILED", loadResult.error.message, { details: loadResult.error.details });
|
|
1599
1924
|
const validation = validateSingleSourceWidgetPackage(loadResult.value, { owner: options.owner });
|
|
1600
1925
|
if (!validation.success || !validation.value) throw new WidgetToolingError("VALIDATION_FAILED", validation.errors.map((error) => error.message).join("\n") || "Widget package validation failed.", { diagnostics: validation.errors });
|
|
@@ -1609,14 +1934,16 @@ async function buildWidgetPackage(options) {
|
|
|
1609
1934
|
projectDir: options.projectDir,
|
|
1610
1935
|
publishDir: options.outDir,
|
|
1611
1936
|
owner: options.owner,
|
|
1612
|
-
cliVersion: options.cliVersion
|
|
1937
|
+
cliVersion: options.cliVersion,
|
|
1938
|
+
...options.version ? { versionOverride: options.version } : {},
|
|
1939
|
+
...options.nodeRuntime ? { nodeRuntime: options.nodeRuntime } : {}
|
|
1613
1940
|
});
|
|
1614
1941
|
if (result.success) return result.value;
|
|
1615
1942
|
throw new WidgetToolingError(result.error.code === "CONFIG_LOAD_FAILED" ? "CONFIG_LOAD_FAILED" : result.error.code === "VALIDATION_FAILED" ? "VALIDATION_FAILED" : result.error.code === "INVALID_PUBLISH_DIR" ? "INVALID_OUTPUT_DIR" : "BUILD_FAILED", result.error.message, { details: result.error.details });
|
|
1616
1943
|
}
|
|
1617
1944
|
async function publishWidgetPackage(options) {
|
|
1618
1945
|
if (options.owner !== options.uploadOwner.kind) throw new WidgetToolingError("OWNER_MISMATCH", `Widget package build owner "${options.owner}" must match upload owner kind "${options.uploadOwner.kind}".`);
|
|
1619
|
-
if (!options.dryRun && !options.client) throw new WidgetToolingError("CLIENT_REQUIRED", "A
|
|
1946
|
+
if (!options.dryRun && !options.client) throw new WidgetToolingError("CLIENT_REQUIRED", "A FluidOS API client is required when dryRun is false.");
|
|
1620
1947
|
const build = await buildWidgetPackage(options);
|
|
1621
1948
|
let request;
|
|
1622
1949
|
try {
|
|
@@ -1665,6 +1992,6 @@ function toPublicUploadErrorCode(code) {
|
|
|
1665
1992
|
}
|
|
1666
1993
|
}
|
|
1667
1994
|
//#endregion
|
|
1668
|
-
export {
|
|
1995
|
+
export { buildRuntimeEntryBootstrapScript as a, fluidWidgetDevPlugin as c, fluidOsV0WidgetPackageGitCredentialCreate as i, WidgetToolingError as l, loadAndValidateWidgetPackage as n, createDevWidgetPackageDescriptors as o, publishWidgetPackage as r, createWidgetDevServerConfig as s, buildWidgetPackage as t };
|
|
1669
1996
|
|
|
1670
|
-
//# sourceMappingURL=src-
|
|
1997
|
+
//# sourceMappingURL=src-_wfDoBLp.mjs.map
|