@fluid-app/fluid-cli-portal 0.1.72 → 0.1.74

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.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A as toFluidOsDynamicObject, C as resolveIdToSlug, D as atomicWriteFile, E as writeMappings, M as ApiError, O as createFluidOsClient, S as removeMapping, T as updateMapping, _ as diffAgainstSnapshot, a as buildNavigationIdToSlugMap, b as deriveSlug, c as transformNavigation, d as transformScreen, f as transformTheme, g as computeFileHash, h as collectSnapshotNetworkWidgetGrants, i as buildIdToSlugMap, j as createFluidOsHeyApiClient, k as fluidOsApi, l as transformNavigationItems, m as collectNetworkWidgetGrants, o as buildThemeIdToSlugMap, p as buildSnapshot, r as validatePortalResource, s as deriveScreenSlug, t as pullCommand, u as transformProfile, v as readSnapshot, w as resolveSlugToId, x as readMappings, y as writeSnapshot } from "./pull-BWnby_pY.mjs";
2
2
  import { l as fluidOsV0DefinitionGitCredentialCreate } from "./sdk.gen-VRGYiLVJ.mjs";
3
- import { a as resolvePublishVersion, d as isRemoteDomWidgetPackageDescriptor, i as readCompanyWidgetPackages, n as preflightCompanyPackage, o as loadAndValidateWidgetPackage, r as readCompanyWidgetPackage, s as publishWidgetPackage, t as formatPackageKeyMismatch } from "./src-CQ24GVe6.mjs";
3
+ import { a as resolvePublishVersion, d as isRemoteDomWidgetPackageDescriptor, i as readCompanyWidgetPackages, n as preflightCompanyPackage, o as loadAndValidateWidgetPackage, r as readCompanyWidgetPackage, s as publishWidgetPackage, t as formatPackageKeyMismatch } from "./src-D1cU2gmw.mjs";
4
4
  import { Command } from "commander";
5
5
  import chalk from "chalk";
6
6
  import ora from "ora";
@@ -96,9 +96,9 @@ async function promptProjectConfig(projectName, options) {
96
96
  //#endregion
97
97
  //#region src/utils/file-system.ts
98
98
  const _currentDir$1 = dirname(fileURLToPath(import.meta.url));
99
- const EMBEDDED_PORTAL_SDK_VERSION = "0.1.480";
100
- const EMBEDDED_CLI_CORE_VERSION = "0.1.17";
101
- const EMBEDDED_CLI_PORTAL_VERSION = "0.1.72";
99
+ const EMBEDDED_PORTAL_SDK_VERSION = "0.1.482";
100
+ const EMBEDDED_CLI_CORE_VERSION = "0.1.19";
101
+ const EMBEDDED_CLI_PORTAL_VERSION = "0.1.74";
102
102
  /**
103
103
  * Find the package root by walking up from the current directory to the nearest package.json.
104
104
  * Works whether running from dist/ (bundled) or src/utils/ (tsx dev mode).
@@ -1695,7 +1695,7 @@ const devCommand = new Command("dev").description("Start the development server
1695
1695
  const { createServer } = await import("vite");
1696
1696
  const { portalDevPlugin } = await import("./portal-dev-plugin-2jZODDsX.mjs").then((n) => n.n);
1697
1697
  const { backendDevPlugin } = await import("./backend-dev-plugin-z9IZCHaY.mjs").then((n) => n.n);
1698
- const { portalWidgetDevPlugin } = await import("./portal-widget-dev-plugin-Dyp2bVy7.mjs").then((n) => n.n);
1698
+ const { portalWidgetDevPlugin } = await import("./portal-widget-dev-plugin-CO-im-5P.mjs").then((n) => n.n);
1699
1699
  const server = await createServer({
1700
1700
  configFile: viteConfigPath,
1701
1701
  envPrefix: ["VITE_", "NEXT_PUBLIC_"],
@@ -1,5 +1,5 @@
1
1
  import { N as __exportAll } from "./pull-BWnby_pY.mjs";
2
- import { c as fluidWidgetDevPlugin, l as handleOptions, u as setCorsHeaders } from "./src-CQ24GVe6.mjs";
2
+ import { c as fluidWidgetDevPlugin, l as handleOptions, u as setCorsHeaders } from "./src-D1cU2gmw.mjs";
3
3
  import path from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
  //#region src/vite-plugin/portal-widget-dev-plugin.ts
@@ -143,4 +143,4 @@ if (widgetPackage) {
143
143
  //#endregion
144
144
  export { portal_widget_dev_plugin_exports as n, portalWidgetDevPlugin as t };
145
145
 
146
- //# sourceMappingURL=portal-widget-dev-plugin-Dyp2bVy7.mjs.map
146
+ //# sourceMappingURL=portal-widget-dev-plugin-CO-im-5P.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"portal-widget-dev-plugin-Dyp2bVy7.mjs","names":[],"sources":["../src/vite-plugin/portal-widget-dev-plugin.ts"],"sourcesContent":["/**\n * Dev routes that let a portal preview render the project's own widgets before\n * they are published.\n *\n * A portal keeps company-owned widget sources in `src/widgets.config.ts` and\n * publishes them with `fluid portal deploy`. Until that deploy runs, the widget\n * exists only on disk — the catalog has never heard of it, so a preview could\n * not show it and the only way to see your work was to publish it. Publishing\n * is not a safe iteration loop: a deploy is live immediately, there is no\n * rollback, and a duplicate `packageId@version` is ignored so every attempt\n * needs a version bump.\n *\n * These routes close that gap by serving the same two endpoints a standalone\n * widget project serves — `/__widget-packages__` (a canonical package\n * descriptor) and `/__runtime-entry__` (the worker bootstrap). A host that can\n * already render published packages needs no new code to render a local one:\n * the descriptor shape is identical, so it merges straight into the catalog\n * array.\n *\n * The route implementations are reused wholesale from\n * `@fluid-app/fluid-cli-widget-tooling`. Its bootstrap script in particular is\n * load-bearing — it exists to fix a message-buffer race that silently strands\n * worker mounts — and must not be reimplemented here.\n *\n * Imported from widget-tooling rather than from the widget CLI deliberately:\n * this package lists widget-tooling in its tsdown `alwaysBundle`, so the routes\n * compile into the portal dist. An import across published CLIs stays external\n * and resolves to un-built TypeScript at runtime.\n *\n * Two things differ from a standalone project, and both are passed as options:\n * the package source lives at `src/widgets.config.ts` rather than\n * `fluid.widget.config.ts`, and it is owned by `company` rather than `droplet`.\n * A portal also has no `startWidgetPackage()` module of its own, so this plugin\n * generates one.\n */\n\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n fluidWidgetDevPlugin,\n handleOptions,\n setCorsHeaders,\n} from \"@fluid-app/fluid-cli-widget-tooling\";\nimport type { Plugin } from \"vite\";\n\n/** Package source, relative to the Vite root. */\nconst WIDGET_CONFIG_MODULE_PATH = \"/src/widgets.config.ts\";\n\n/**\n * Generated worker entry. Requestable (no `\\0` prefix) because the bootstrap\n * script imports it by URL from the dev server rather than through Vite's\n * module graph.\n */\nconst VIRTUAL_ENTRY_PATH = \"/@fluid-portal-widget-entry.js\";\n\nconst WIDGET_PACKAGES_ROUTES = new Set([\n \"/__widget-packages__\",\n \"/__widget-packages__/\",\n]);\nconst RUNTIME_ENTRY_ROUTES = new Set([\n \"/__runtime-entry__\",\n \"/__runtime-entry__/\",\n]);\n\nexport function portalWidgetDevPlugin(): Plugin[] {\n return [\n missingConfigGuardPlugin(),\n virtualWorkerEntryPlugin(),\n fluidWidgetDevPlugin({\n entryModulePath: VIRTUAL_ENTRY_PATH,\n configModulePath: WIDGET_CONFIG_MODULE_PATH,\n owner: \"company\",\n // A portal with no custom widget yet is the normal case, not a fault.\n allowNoPackages: true,\n }),\n ];\n}\n\n/**\n * Short-circuits both routes when the project has no `src/widgets.config.ts`.\n *\n * Most portals never gain one. Without this the delegated routes would try to\n * load a module that does not exist and log a resolution failure on every poll\n * — the preview polls continuously — so the expected case would be the noisiest\n * thing in the log. Registered before the delegated plugin so its middleware\n * runs first.\n */\nfunction missingConfigGuardPlugin(): Plugin {\n let configFilePath = \"\";\n\n return {\n name: \"fluid-portal-widget-dev-missing-config-guard\",\n apply: \"serve\",\n configResolved(config) {\n configFilePath = path.join(\n config.root,\n WIDGET_CONFIG_MODULE_PATH.replace(/^\\//, \"\"),\n );\n },\n configureServer(server) {\n server.middlewares.use((request, response, next): void => {\n const pathname = (request.url ?? \"\").split(\"?\")[0] ?? \"\";\n const isWidgetRoute =\n WIDGET_PACKAGES_ROUTES.has(pathname) ||\n RUNTIME_ENTRY_ROUTES.has(pathname);\n if (!isWidgetRoute) {\n next();\n return;\n }\n // Checked per request, not at startup: `fluid portal widget create`\n // can add the file while the dev server is already running.\n if (existsSync(configFilePath)) {\n next();\n return;\n }\n\n // Answer a preflight exactly as the delegated routes do. Two\n // implementations of the same route disagreeing is bad enough; the\n // guard is the one that runs in the common case, since most portals\n // never gain a widget config, so the divergence would have been the\n // DEFAULT path rather than the edge.\n if (handleOptions(request, response)) return;\n setCorsHeaders(response);\n if (WIDGET_PACKAGES_ROUTES.has(pathname)) {\n response.setHeader(\"Content-Type\", \"application/json\");\n response.statusCode = 200;\n response.end(\"[]\");\n return;\n }\n response.setHeader(\"Content-Type\", \"text/javascript\");\n response.statusCode = 200;\n response.end(\"// No portal widget package in this project.\\n\");\n });\n },\n };\n}\n\n/**\n * Serves the worker entry a portal project does not have.\n *\n * A standalone widget project ships `src/index.ts` calling\n * `startWidgetPackage()`. A portal's `src/widgets.config.ts` only declares the\n * package, so this module supplies the missing call. Export precedence matches\n * the shared descriptor builder's, so the worker starts the same package the\n * descriptor advertises.\n */\nfunction virtualWorkerEntryPlugin(): Plugin {\n return {\n name: \"fluid-portal-widget-dev-entry\",\n apply: \"serve\",\n resolveId(id) {\n return id === VIRTUAL_ENTRY_PATH ? id : null;\n },\n load(id) {\n if (id !== VIRTUAL_ENTRY_PATH) return null;\n return `import { prepareRemoteDomWidgetWorker, startWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n\nprepareRemoteDomWidgetWorker();\nconst source = await import(\"${WIDGET_CONFIG_MODULE_PATH}\");\n\nconst packages = source.widgetPackages;\nconst widgetPackage =\n (Array.isArray(packages) && packages.length > 0 ? packages[0] : undefined) ??\n source.widgetPackage ??\n source.default;\n\nif (widgetPackage) {\n startWidgetPackage(widgetPackage);\n}\n`;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAM,4BAA4B;;;;;;AAOlC,MAAM,qBAAqB;AAE3B,MAAM,yBAAyB,IAAI,IAAI,CACrC,wBACA,wBACD,CAAC;AACF,MAAM,uBAAuB,IAAI,IAAI,CACnC,sBACA,sBACD,CAAC;AAEF,SAAgB,wBAAkC;AAChD,QAAO;EACL,0BAA0B;EAC1B,0BAA0B;EAC1B,qBAAqB;GACnB,iBAAiB;GACjB,kBAAkB;GAClB,OAAO;GAEP,iBAAiB;GAClB,CAAC;EACH;;;;;;;;;;;AAYH,SAAS,2BAAmC;CAC1C,IAAI,iBAAiB;AAErB,QAAO;EACL,MAAM;EACN,OAAO;EACP,eAAe,QAAQ;AACrB,oBAAiB,KAAK,KACpB,OAAO,MACP,0BAA0B,QAAQ,OAAO,GAAG,CAC7C;;EAEH,gBAAgB,QAAQ;AACtB,UAAO,YAAY,KAAK,SAAS,UAAU,SAAe;IACxD,MAAM,YAAY,QAAQ,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM;AAItD,QAAI,EAFF,uBAAuB,IAAI,SAAS,IACpC,qBAAqB,IAAI,SAAS,GAChB;AAClB,WAAM;AACN;;AAIF,QAAI,WAAW,eAAe,EAAE;AAC9B,WAAM;AACN;;AAQF,QAAI,cAAc,SAAS,SAAS,CAAE;AACtC,mBAAe,SAAS;AACxB,QAAI,uBAAuB,IAAI,SAAS,EAAE;AACxC,cAAS,UAAU,gBAAgB,mBAAmB;AACtD,cAAS,aAAa;AACtB,cAAS,IAAI,KAAK;AAClB;;AAEF,aAAS,UAAU,gBAAgB,kBAAkB;AACrD,aAAS,aAAa;AACtB,aAAS,IAAI,iDAAiD;KAC9D;;EAEL;;;;;;;;;;;AAYH,SAAS,2BAAmC;AAC1C,QAAO;EACL,MAAM;EACN,OAAO;EACP,UAAU,IAAI;AACZ,UAAO,OAAO,qBAAqB,KAAK;;EAE1C,KAAK,IAAI;AACP,OAAI,OAAO,mBAAoB,QAAO;AACtC,UAAO;;;+BAGkB,0BAA0B;;;;;;;;;;;;;EAatD"}
1
+ {"version":3,"file":"portal-widget-dev-plugin-CO-im-5P.mjs","names":[],"sources":["../src/vite-plugin/portal-widget-dev-plugin.ts"],"sourcesContent":["/**\n * Dev routes that let a portal preview render the project's own widgets before\n * they are published.\n *\n * A portal keeps company-owned widget sources in `src/widgets.config.ts` and\n * publishes them with `fluid portal deploy`. Until that deploy runs, the widget\n * exists only on disk — the catalog has never heard of it, so a preview could\n * not show it and the only way to see your work was to publish it. Publishing\n * is not a safe iteration loop: a deploy is live immediately, there is no\n * rollback, and a duplicate `packageId@version` is ignored so every attempt\n * needs a version bump.\n *\n * These routes close that gap by serving the same two endpoints a standalone\n * widget project serves — `/__widget-packages__` (a canonical package\n * descriptor) and `/__runtime-entry__` (the worker bootstrap). A host that can\n * already render published packages needs no new code to render a local one:\n * the descriptor shape is identical, so it merges straight into the catalog\n * array.\n *\n * The route implementations are reused wholesale from\n * `@fluid-app/fluid-cli-widget-tooling`. Its bootstrap script in particular is\n * load-bearing — it exists to fix a message-buffer race that silently strands\n * worker mounts — and must not be reimplemented here.\n *\n * Imported from widget-tooling rather than from the widget CLI deliberately:\n * this package lists widget-tooling in its tsdown `alwaysBundle`, so the routes\n * compile into the portal dist. An import across published CLIs stays external\n * and resolves to un-built TypeScript at runtime.\n *\n * Two things differ from a standalone project, and both are passed as options:\n * the package source lives at `src/widgets.config.ts` rather than\n * `fluid.widget.config.ts`, and it is owned by `company` rather than `droplet`.\n * A portal also has no `startWidgetPackage()` module of its own, so this plugin\n * generates one.\n */\n\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n fluidWidgetDevPlugin,\n handleOptions,\n setCorsHeaders,\n} from \"@fluid-app/fluid-cli-widget-tooling\";\nimport type { Plugin } from \"vite\";\n\n/** Package source, relative to the Vite root. */\nconst WIDGET_CONFIG_MODULE_PATH = \"/src/widgets.config.ts\";\n\n/**\n * Generated worker entry. Requestable (no `\\0` prefix) because the bootstrap\n * script imports it by URL from the dev server rather than through Vite's\n * module graph.\n */\nconst VIRTUAL_ENTRY_PATH = \"/@fluid-portal-widget-entry.js\";\n\nconst WIDGET_PACKAGES_ROUTES = new Set([\n \"/__widget-packages__\",\n \"/__widget-packages__/\",\n]);\nconst RUNTIME_ENTRY_ROUTES = new Set([\n \"/__runtime-entry__\",\n \"/__runtime-entry__/\",\n]);\n\nexport function portalWidgetDevPlugin(): Plugin[] {\n return [\n missingConfigGuardPlugin(),\n virtualWorkerEntryPlugin(),\n fluidWidgetDevPlugin({\n entryModulePath: VIRTUAL_ENTRY_PATH,\n configModulePath: WIDGET_CONFIG_MODULE_PATH,\n owner: \"company\",\n // A portal with no custom widget yet is the normal case, not a fault.\n allowNoPackages: true,\n }),\n ];\n}\n\n/**\n * Short-circuits both routes when the project has no `src/widgets.config.ts`.\n *\n * Most portals never gain one. Without this the delegated routes would try to\n * load a module that does not exist and log a resolution failure on every poll\n * — the preview polls continuously — so the expected case would be the noisiest\n * thing in the log. Registered before the delegated plugin so its middleware\n * runs first.\n */\nfunction missingConfigGuardPlugin(): Plugin {\n let configFilePath = \"\";\n\n return {\n name: \"fluid-portal-widget-dev-missing-config-guard\",\n apply: \"serve\",\n configResolved(config) {\n configFilePath = path.join(\n config.root,\n WIDGET_CONFIG_MODULE_PATH.replace(/^\\//, \"\"),\n );\n },\n configureServer(server) {\n server.middlewares.use((request, response, next): void => {\n const pathname = (request.url ?? \"\").split(\"?\")[0] ?? \"\";\n const isWidgetRoute =\n WIDGET_PACKAGES_ROUTES.has(pathname) ||\n RUNTIME_ENTRY_ROUTES.has(pathname);\n if (!isWidgetRoute) {\n next();\n return;\n }\n // Checked per request, not at startup: `fluid portal widget create`\n // can add the file while the dev server is already running.\n if (existsSync(configFilePath)) {\n next();\n return;\n }\n\n // Answer a preflight exactly as the delegated routes do. Two\n // implementations of the same route disagreeing is bad enough; the\n // guard is the one that runs in the common case, since most portals\n // never gain a widget config, so the divergence would have been the\n // DEFAULT path rather than the edge.\n if (handleOptions(request, response)) return;\n setCorsHeaders(response);\n if (WIDGET_PACKAGES_ROUTES.has(pathname)) {\n response.setHeader(\"Content-Type\", \"application/json\");\n response.statusCode = 200;\n response.end(\"[]\");\n return;\n }\n response.setHeader(\"Content-Type\", \"text/javascript\");\n response.statusCode = 200;\n response.end(\"// No portal widget package in this project.\\n\");\n });\n },\n };\n}\n\n/**\n * Serves the worker entry a portal project does not have.\n *\n * A standalone widget project ships `src/index.ts` calling\n * `startWidgetPackage()`. A portal's `src/widgets.config.ts` only declares the\n * package, so this module supplies the missing call. Export precedence matches\n * the shared descriptor builder's, so the worker starts the same package the\n * descriptor advertises.\n */\nfunction virtualWorkerEntryPlugin(): Plugin {\n return {\n name: \"fluid-portal-widget-dev-entry\",\n apply: \"serve\",\n resolveId(id) {\n return id === VIRTUAL_ENTRY_PATH ? id : null;\n },\n load(id) {\n if (id !== VIRTUAL_ENTRY_PATH) return null;\n return `import { prepareRemoteDomWidgetWorker, startWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n\nprepareRemoteDomWidgetWorker();\nconst source = await import(\"${WIDGET_CONFIG_MODULE_PATH}\");\n\nconst packages = source.widgetPackages;\nconst widgetPackage =\n (Array.isArray(packages) && packages.length > 0 ? packages[0] : undefined) ??\n source.widgetPackage ??\n source.default;\n\nif (widgetPackage) {\n startWidgetPackage(widgetPackage);\n}\n`;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAM,4BAA4B;;;;;;AAOlC,MAAM,qBAAqB;AAE3B,MAAM,yBAAyB,IAAI,IAAI,CACrC,wBACA,wBACD,CAAC;AACF,MAAM,uBAAuB,IAAI,IAAI,CACnC,sBACA,sBACD,CAAC;AAEF,SAAgB,wBAAkC;AAChD,QAAO;EACL,0BAA0B;EAC1B,0BAA0B;EAC1B,qBAAqB;GACnB,iBAAiB;GACjB,kBAAkB;GAClB,OAAO;GAEP,iBAAiB;GAClB,CAAC;EACH;;;;;;;;;;;AAYH,SAAS,2BAAmC;CAC1C,IAAI,iBAAiB;AAErB,QAAO;EACL,MAAM;EACN,OAAO;EACP,eAAe,QAAQ;AACrB,oBAAiB,KAAK,KACpB,OAAO,MACP,0BAA0B,QAAQ,OAAO,GAAG,CAC7C;;EAEH,gBAAgB,QAAQ;AACtB,UAAO,YAAY,KAAK,SAAS,UAAU,SAAe;IACxD,MAAM,YAAY,QAAQ,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM;AAItD,QAAI,EAFF,uBAAuB,IAAI,SAAS,IACpC,qBAAqB,IAAI,SAAS,GAChB;AAClB,WAAM;AACN;;AAIF,QAAI,WAAW,eAAe,EAAE;AAC9B,WAAM;AACN;;AAQF,QAAI,cAAc,SAAS,SAAS,CAAE;AACtC,mBAAe,SAAS;AACxB,QAAI,uBAAuB,IAAI,SAAS,EAAE;AACxC,cAAS,UAAU,gBAAgB,mBAAmB;AACtD,cAAS,aAAa;AACtB,cAAS,IAAI,KAAK;AAClB;;AAEF,aAAS,UAAU,gBAAgB,kBAAkB;AACrD,aAAS,aAAa;AACtB,aAAS,IAAI,iDAAiD;KAC9D;;EAEL;;;;;;;;;;;AAYH,SAAS,2BAAmC;AAC1C,QAAO;EACL,MAAM;EACN,OAAO;EACP,UAAU,IAAI;AACZ,UAAO,OAAO,qBAAqB,KAAK;;EAE1C,KAAK,IAAI;AACP,OAAI,OAAO,mBAAoB,QAAO;AACtC,UAAO;;;+BAGkB,0BAA0B;;;;;;;;;;;;;EAatD"}
@@ -727,7 +727,9 @@ function fluidWidgetDevPlugin(options = {}) {
727
727
  server.middlewares.use(async (request, response, next) => {
728
728
  const pathname = (request.url ?? "").split("?")[0] ?? "";
729
729
  if (pathname === "/__runtime-entry__" || pathname === "/__runtime-entry__/") {
730
- sendJavaScript(request, response, buildRuntimeEntryBootstrapScript(entryModulePath, getRequestOrigin(request)));
730
+ if (handleOptions(request, response)) return;
731
+ const origin = getRequestOrigin(request);
732
+ sendJavaScript(request, response, buildRuntimeEntryBootstrapScript(entryModulePath, origin, await resolveWorkerSdkUrl(server, origin)));
731
733
  return;
732
734
  }
733
735
  if (pathname === "/__widget-packages__" || pathname === "/__widget-packages__/") {
@@ -758,6 +760,11 @@ function fluidWidgetDevPlugin(options = {}) {
758
760
  function createWidgetDevServerConfig() {
759
761
  return {
760
762
  optimizeDeps: { include: ["@fluid-app/portal-sdk/widgets/worker"] },
763
+ resolve: { dedupe: [
764
+ "@fluid-app/portal-sdk",
765
+ "react",
766
+ "react-dom"
767
+ ] },
761
768
  ssr: { noExternal: ["@fluid-app/portal-sdk"] }
762
769
  };
763
770
  }
@@ -768,6 +775,48 @@ function createWidgetDevServerConfig() {
768
775
  const WIDGET_ENTRY_MODULE_PATH = "/src/index.ts";
769
776
  /** Source-relative path of a standalone widget project's package source. */
770
777
  const WIDGET_CONFIG_MODULE_PATH = "/fluid.widget.config.ts";
778
+ /** Bare specifier of the SDK worker module the bootstrap prepares. */
779
+ const WORKER_SDK_SPECIFIER = "@fluid-app/portal-sdk/widgets/worker";
780
+ /**
781
+ * Resolves the bootstrap's SDK worker import to the same module URL the
782
+ * widget entry's rewritten bare import uses.
783
+ *
784
+ * The bootstrap used to import the hardcoded alias
785
+ * `/@id/@fluid-app/portal-sdk/widgets/worker`. Vite serves the optimized
786
+ * module body inline at that URL while the entry's rewritten import uses the
787
+ * canonical `/node_modules/.vite/deps/...?v=<hash>` URL — same bytes, two
788
+ * resolved URLs, two records in the worker's ES module map. The network
789
+ * guard's re-entry check is module-instance-local, so the second evaluation
790
+ * threw on every dev preview load (MIST-PW-01). Resolving server-side at
791
+ * request time keeps both imports on one module record.
792
+ *
793
+ * Falls back to the `/@id/` alias when resolution fails — no worse than the
794
+ * previous behavior, and the widget-runtime duplicate-install tripwire names
795
+ * the condition if the split ever regresses.
796
+ */
797
+ async function resolveWorkerSdkUrl(server, origin) {
798
+ const fallback = new URL(`/@id/${WORKER_SDK_SPECIFIER}`, origin).toString();
799
+ try {
800
+ const resolved = await server.pluginContainer.resolveId(WORKER_SDK_SPECIFIER, void 0, { ssr: false });
801
+ if (!resolved?.id) return fallback;
802
+ const url = moduleIdToUrl(resolved.id, server.config.root);
803
+ return url ? new URL(url, origin).toString() : fallback;
804
+ } catch {
805
+ return fallback;
806
+ }
807
+ }
808
+ /**
809
+ * Mirrors Vite import-analysis URL normalization for the id shapes the
810
+ * resolver produces here: a file under the served root (the optimized-deps
811
+ * dir lives at `<root>/node_modules/.vite`) becomes a root-relative URL; any
812
+ * other absolute file path is served through `/@fs/`. Ids and root arrive
813
+ * posix-normalized from Vite, Windows drive letters included.
814
+ */
815
+ function moduleIdToUrl(id, root) {
816
+ const rootPrefix = root.endsWith("/") ? root : `${root}/`;
817
+ if (id.startsWith(rootPrefix)) return `/${id.slice(rootPrefix.length)}`;
818
+ if (id.startsWith("/") || /^[A-Za-z]:\//.test(id)) return `/@fs/${id.replace(/^\//, "")}`;
819
+ }
771
820
  /**
772
821
  * Builds the `/__runtime-entry__` worker bootstrap. Runtime-verified
773
822
  * against a real widget dev server + `@fluid-app/widget-runtime` host
@@ -823,12 +872,16 @@ const WIDGET_CONFIG_MODULE_PATH = "/fluid.widget.config.ts";
823
872
  * imports to the request origin keeps the entire dev module graph on the
824
873
  * widget server.
825
874
  *
875
+ * Callers obtain `workerSdkUrl` from `resolveWorkerSdkUrl`, which owns the
876
+ * `/@id/` fallback. The parameter is deliberately required: defaulting to the
877
+ * alias here would let a caller silently reintroduce the two-module-record
878
+ * split of MIST-PW-01.
879
+ *
826
880
  * Dev-only: this only ever serves from `configureServer` (the
827
881
  * plugin's `apply: "serve"` gate), never a built/published package.
828
882
  */
829
- function buildRuntimeEntryBootstrapScript(entryPath, origin) {
883
+ function buildRuntimeEntryBootstrapScript(entryPath, origin, workerSdkUrl) {
830
884
  const refreshRuntimeUrl = new URL("/@react-refresh", origin).toString();
831
- const workerSdkUrl = new URL("/@id/@fluid-app/portal-sdk/widgets/worker", origin).toString();
832
885
  const entryUrl = new URL(entryPath, origin).toString();
833
886
  return [
834
887
  "// Dev-only worker bootstrap — see buildRuntimeEntryBootstrapScript",
@@ -2285,4 +2338,4 @@ function isRecord(value) {
2285
2338
  //#endregion
2286
2339
  export { resolvePublishVersion as a, fluidWidgetDevPlugin as c, isRemoteDomWidgetPackageDescriptor as d, readCompanyWidgetPackages as i, handleOptions as l, preflightCompanyPackage as n, loadAndValidateWidgetPackage as o, readCompanyWidgetPackage as r, publishWidgetPackage as s, formatPackageKeyMismatch as t, setCorsHeaders as u };
2287
2340
 
2288
- //# sourceMappingURL=src-CQ24GVe6.mjs.map
2341
+ //# sourceMappingURL=src-D1cU2gmw.mjs.map