@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.
@@ -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
- await execa(process.execPath, [
618
- TSX_CLI_PATH,
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
- await execa(process.execPath, [
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/namespaces/fluid_os_v0.ts
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
- * @param client - Fetch client instance
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
- async function fluid_os_v0_create_widget_package_version(client, body) {
1367
- return client.post(`/api/company/fluid_os/widget_package_versions`, body);
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
- async function fluid_os_v0_complete_widget_package_version_upload(client, version, body) {
1378
- return client.post(`/api/company/fluid_os/widget_package_versions/${version}/complete_upload`, body);
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 fluid_os_v0_create_widget_package_version(client, payload) : await client.post(getWidgetPackageUploadSessionRoute(owner), payload), request.version, request.artifacts);
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 fluid_os_v0_complete_widget_package_version_upload(client, version, payload) : await client.post(getWidgetPackageCompleteUploadRoute(owner, version), payload), version);
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 FetchClient is required when dryRun is false.");
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 FetchClient is required when dryRun is false.");
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 { WidgetToolingError as a, createWidgetPackageDevelopmentDescriptor as i, loadAndValidateWidgetPackage as n, publishWidgetPackage as r, buildWidgetPackage as t };
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-CVadzcYM.mjs.map
1997
+ //# sourceMappingURL=src-_wfDoBLp.mjs.map