@fluid-app/fluid-cli-portal 0.1.78 → 0.1.79

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.
@@ -1,4 +1,4 @@
1
- import { N as __exportAll } from "./pull-BWnby_pY.mjs";
1
+ import { N as __exportAll } from "./pull-Kx1ncTLF.mjs";
2
2
  import { buildManifestFromLocalFiles } from "./build-manifest.mjs";
3
3
  import { join, resolve, sep } from "node:path";
4
4
  import { existsSync, readFileSync } from "node:fs";
@@ -158,28 +158,27 @@ function portalDevPlugin(options) {
158
158
  if (server.__fluidPortalDevWired) return;
159
159
  server.__fluidPortalDevWired = true;
160
160
  const watchDir = portalDir;
161
- if (existsSync(watchDir)) {
162
- server.watcher.add(watchDir);
163
- let reloadTimer = null;
164
- const onFileChange = (filePath) => {
165
- if (!filePath.startsWith(watchDir)) return;
166
- cachedManifest = null;
167
- buildInFlight = null;
168
- manifestGeneration += 1;
169
- console.log(`[fluid-portal-dev] File changed: ${filePath.replace(watchDir + sep, "")}`);
170
- if (reloadTimer) clearTimeout(reloadTimer);
171
- reloadTimer = setTimeout(() => {
172
- reloadTimer = null;
173
- server.ws.send({
174
- type: "full-reload",
175
- path: "*"
176
- });
177
- }, DEBOUNCE_MS);
178
- };
179
- server.watcher.on("change", onFileChange);
180
- server.watcher.on("add", onFileChange);
181
- server.watcher.on("unlink", onFileChange);
182
- }
161
+ server.watcher.add(watchDir);
162
+ let reloadTimer = null;
163
+ const watchDirPrefix = watchDir.endsWith(sep) ? watchDir : watchDir + sep;
164
+ const onFileChange = (filePath) => {
165
+ if (filePath !== watchDir && !filePath.startsWith(watchDirPrefix)) return;
166
+ cachedManifest = null;
167
+ buildInFlight = null;
168
+ manifestGeneration += 1;
169
+ console.log(`[fluid-portal-dev] File changed: ${filePath.replace(watchDirPrefix, "")}`);
170
+ if (reloadTimer) clearTimeout(reloadTimer);
171
+ reloadTimer = setTimeout(() => {
172
+ reloadTimer = null;
173
+ server.ws.send({
174
+ type: "full-reload",
175
+ path: "*"
176
+ });
177
+ }, DEBOUNCE_MS);
178
+ };
179
+ server.watcher.on("change", onFileChange);
180
+ server.watcher.on("add", onFileChange);
181
+ server.watcher.on("unlink", onFileChange);
183
182
  server.middlewares.use((req, res, next) => {
184
183
  const url = req.url;
185
184
  if (!url || !MANIFEST_PATHS.some((path) => url.includes(path))) {
@@ -229,4 +228,4 @@ function portalDevPlugin(options) {
229
228
  //#endregion
230
229
  export { portal_dev_plugin_exports as n, portalDevPlugin as t };
231
230
 
232
- //# sourceMappingURL=portal-dev-plugin-2jZODDsX.mjs.map
231
+ //# sourceMappingURL=portal-dev-plugin-BFqDxRMa.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"portal-dev-plugin-2jZODDsX.mjs","names":[],"sources":["../src/vite-plugin/portal-dev-plugin.ts"],"sourcesContent":["/**\n * Vite plugin for local portal content development.\n *\n * Intercepts the manifest API request and serves content assembled from the\n * local `portal/` directory. Watches portal files for changes and triggers a\n * full page reload.\n *\n * The manifest is intercepted on BOTH paths the SDK has used across versions\n * (`/api/app/manifest` since portal-sdk 0.1.4xx, `/api/fluid_os/definitions/\n * active` before that) so a newer SDK against an older-generation app — or\n * vice versa — still gets the local draft.\n */\n\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getAuthToken, getFluidApiBase } from \"@fluid-app/fluid-cli\";\nimport { buildManifestFromLocalFiles } from \"./build-manifest.js\";\n\n/** Options for the portal dev plugin. */\nexport interface PortalDevPluginOptions {\n portalDir?: string;\n /**\n * When true (default), dev-server requests are forced same-origin: the\n * plugin blanks `import.meta.env.VITE_API_URL` in serve mode and proxies\n * `/api` to the configured Fluid host instead. Without this, a `.env`\n * pointing VITE_API_URL at a remote API makes the SPA fetch its manifest\n * cross-origin — silently bypassing this plugin and rendering the remote\n * app instead of the local `portal/` draft. Set false to opt out and let\n * the SPA call the remote API directly.\n */\n sameOriginDev?: boolean;\n /**\n * Override the `/api` dev-proxy target. Defaults to VITE_API_URL from the\n * process env / .env files, then the authenticated company's\n * `<subdomain>.portal.fluid.app` tenant BFF, then https://api.fluid.app.\n */\n apiProxyTarget?: string;\n /**\n * Override tenant-target discovery. Primarily useful to keep embedded plugin\n * integrations and tests deterministic.\n */\n resolveTenantApiTarget?: () => Promise<string | undefined>;\n}\n\nconst DEBOUNCE_MS = 100;\n\n/** Manifest endpoints across SDK generations — all serve the local draft. */\nconst MANIFEST_PATHS = [\n \"/api/app/manifest\",\n \"/api/fluid_os/definitions/active\",\n];\n\nconst DEFAULT_API_URL = \"https://api.fluid.app\";\nconst TENANT_LOOKUP_TIMEOUT_MS = 3_000;\nconst tenantTargetPromises = new WeakMap<\n () => Promise<string | undefined>,\n Promise<string | undefined>\n>();\n\ninterface CompanyMeResponse {\n data?: {\n company?: {\n subdomain?: unknown;\n };\n };\n}\n\nexport interface TenantApiTargetDependencies {\n token?: string | null;\n apiBase?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/**\n * Resolve the portal tenant BFF for the CLI's project-aware Fluid profile.\n *\n * Portal member APIs do not live on api.fluid.app or `<tenant>.fluid.app`.\n * Their OpenAPI server is `<tenant>.portal.fluid.app`, and using either of\n * the former hosts produces a wall of unrelated 404s in otherwise-valid\n * local portals.\n */\nexport async function resolvePortalTenantApiTarget(\n dependencies: TenantApiTargetDependencies = {},\n): Promise<string | undefined> {\n const token =\n dependencies.token !== undefined ? dependencies.token : getAuthToken();\n if (!token) return undefined;\n\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n dependencies.timeoutMs ?? TENANT_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n const response = await (dependencies.fetchImpl ?? fetch)(\n `${(dependencies.apiBase ?? getFluidApiBase()).replace(/\\/+$/, \"\")}/api/company/v1/companies/me`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n signal: controller.signal,\n },\n );\n if (!response.ok) return undefined;\n\n const body = (await response.json()) as CompanyMeResponse;\n const subdomain = body.data?.company?.subdomain;\n if (\n typeof subdomain !== \"string\" ||\n !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(subdomain)\n ) {\n return undefined;\n }\n\n return `https://${subdomain}.portal.fluid.app`;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Fallback .env reader for VITE_API_URL, used only when vite's `loadEnv`\n * can't be imported (it's a declared dependency, so this is defensive — but\n * a broken install must degrade to \"wrong host\" rather than \"dev server\n * won't boot\"). Mirrors the basic lookup order: live env > .env.local >\n * .env. Unlike loadEnv it ignores .env.[mode] files and variable expansion.\n */\nfunction readViteApiUrlFallback(root: string): string | undefined {\n if (process.env.VITE_API_URL) return process.env.VITE_API_URL;\n if (process.env.NEXT_PUBLIC_API_BASE_URL) {\n return process.env.NEXT_PUBLIC_API_BASE_URL;\n }\n for (const file of [\".env.local\", \".env\"]) {\n const path = join(root, file);\n if (!existsSync(path)) continue;\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n // Quoted values stop at the closing `\"`; unquoted values stop before\n // an inline ` # comment` tail (dotenv semantics — a trailing comment\n // must not leak into the proxy target URL).\n const match =\n /^\\s*VITE_API_URL\\s*=\\s*(?:\"([^\"]*)\"|(.*?))\\s*(?:#.*)?$/.exec(line);\n const value = match?.[1] ?? match?.[2];\n if (value) return value;\n }\n }\n return undefined;\n}\n\nfunction storefrontSubdomainFromTarget(target: string): string | undefined {\n try {\n const hostname = new URL(target).hostname;\n const match = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.fluid\\.app$/.exec(\n hostname,\n );\n const subdomain = match?.[1];\n if (\n !subdomain ||\n [\"admin\", \"api\", \"app\", \"docs\", \"portal\", \"upload\"].includes(subdomain)\n ) {\n return undefined;\n }\n return subdomain;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve VITE_API_URL the way vite itself would — `loadEnv` honors\n * .env.[mode], .env.[mode].local, envDir, and variable expansion, which the\n * fallback parser does not (a mode-specific staging API must not silently\n * fall back to production).\n */\nasync function resolveViteApiUrl(\n root: string,\n mode: string,\n envDir: string | undefined,\n): Promise<string | undefined> {\n try {\n const { loadEnv } = await import(\"vite\");\n const env = loadEnv(mode, envDir ?? root, [\"VITE_\", \"NEXT_PUBLIC_\"]);\n return (\n env.VITE_API_URL ||\n process.env.VITE_API_URL ||\n // Portals migrated from Next.js conventions configure the API host as\n // NEXT_PUBLIC_API_BASE_URL — honored by `fluid portal dev` since it\n // was introduced, so it stays a recognized source here.\n env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n undefined\n );\n } catch {\n return readViteApiUrlFallback(root);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function portalDevPlugin(options?: PortalDevPluginOptions): any {\n let portalDir: string;\n let cachedManifest: string | null = null;\n let buildInFlight: Promise<string> | null = null;\n /** Bumped on every watched file change. A build carries the value it\n * started under; if it no longer matches when the build resolves, the\n * build read pre-edit state and must not be cached. */\n let manifestGeneration = 0;\n const resolveTenantTarget =\n options?.resolveTenantApiTarget ?? resolvePortalTenantApiTarget;\n\n return {\n name: \"fluid-portal-dev\",\n enforce: \"pre\" as const,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async config(config: any, env: any) {\n if (env?.command !== \"serve\" || options?.sameOriginDev === false) {\n return {};\n }\n\n const root: string = config.root ?? process.cwd();\n const appDefinesProxy = config.server?.proxy?.[\"/api\"] !== undefined;\n const configuredTarget =\n options?.apiProxyTarget || appDefinesProxy\n ? undefined\n : await resolveViteApiUrl(\n root,\n env?.mode ?? \"development\",\n config.envDir,\n );\n const tenantTarget =\n options?.apiProxyTarget || appDefinesProxy || configuredTarget\n ? undefined\n : await (() => {\n const existing = tenantTargetPromises.get(resolveTenantTarget);\n if (existing) return existing;\n\n const pending = resolveTenantTarget();\n tenantTargetPromises.set(resolveTenantTarget, pending);\n return pending;\n })();\n const target =\n options?.apiProxyTarget ??\n configuredTarget ??\n tenantTarget ??\n DEFAULT_API_URL;\n const storefrontSubdomain = storefrontSubdomainFromTarget(target);\n\n if (tenantTarget) {\n console.log(`[fluid-portal-dev] Tenant API proxy: ${tenantTarget}`);\n console.log(\n \"[fluid-portal-dev] Member data still requires a portal handoff session; localhost requests may return 401.\",\n );\n } else if (\n !options?.apiProxyTarget &&\n !appDefinesProxy &&\n !configuredTarget\n ) {\n console.warn(\n `[fluid-portal-dev] Could not resolve the company tenant API; falling back to ${DEFAULT_API_URL}. ` +\n \"Built-in member screens may not load. Set VITE_API_URL explicitly or sign in with the project's Fluid profile.\",\n );\n } else if (storefrontSubdomain) {\n console.warn(\n `[fluid-portal-dev] ${target} is the storefront host, not the Portal tenant BFF. ` +\n `Built-in member APIs live at https://${storefrontSubdomain}.portal.fluid.app; remove the override to let the CLI resolve it.`,\n );\n }\n\n // Vite merges a plugin's returned config OVER the user config, so an\n // app's own explicit define / /api proxy is only preserved by checking\n // for it and yielding here. BOTH API-host env vars must be blanked:\n // dev.ts's envPrefix exposes NEXT_PUBLIC_API_BASE_URL to the SPA too,\n // so leaving it defined would recreate the exact cross-origin bypass\n // this hook exists to prevent, just for Next.js-migrated portals.\n const defines: Record<string, string> = {};\n for (const key of [\n \"import.meta.env.VITE_API_URL\",\n \"import.meta.env.NEXT_PUBLIC_API_BASE_URL\",\n ]) {\n if (config.define?.[key] === undefined) {\n defines[key] = JSON.stringify(\"\");\n }\n }\n const proxy = !appDefinesProxy\n ? { \"/api\": { target, changeOrigin: true } }\n : {};\n\n return {\n define: defines,\n server: { proxy },\n };\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configResolved(config: any) {\n const root: string = config.root ?? process.cwd();\n portalDir = options?.portalDir\n ? resolve(root, options.portalDir)\n : join(root, \"portal\");\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configureServer(server: any) {\n // Idempotency: `fluid portal dev` injects this plugin, and a portal's\n // own vite.config may also wire it. Only the first instance registers\n // the watcher + middleware so we don't double-reload or double-serve.\n if (server.__fluidPortalDevWired) return;\n server.__fluidPortalDevWired = true;\n\n const watchDir = portalDir;\n\n if (existsSync(watchDir)) {\n server.watcher.add(watchDir);\n\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n const onFileChange = (filePath: string) => {\n if (!filePath.startsWith(watchDir)) return;\n\n cachedManifest = null;\n buildInFlight = null;\n manifestGeneration += 1;\n\n console.log(\n `[fluid-portal-dev] File changed: ${filePath.replace(watchDir + sep, \"\")}`,\n );\n\n if (reloadTimer) clearTimeout(reloadTimer);\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n server.ws.send({ type: \"full-reload\", path: \"*\" });\n }, DEBOUNCE_MS);\n };\n\n server.watcher.on(\"change\", onFileChange);\n server.watcher.on(\"add\", onFileChange);\n server.watcher.on(\"unlink\", onFileChange);\n }\n\n // ── Middleware to intercept manifest requests ───────────────────\n // Every MANIFEST_PATHS route returns the SAME `{ manifest }` payload:\n // the portal-tenant BFF route the current SDK runtime fetches\n // (useFluidApp → app_manifest_show) and the legacy FluidOS route, so\n // the local portal/ draft feeds the running app regardless of which\n // runtime generation it uses (the SDK adapter reads\n // `response.manifest`, matching buildManifestFromLocalFiles' output).\n server.middlewares.use(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (req: any, res: any, next: () => void) => {\n const url: string | undefined = req.url;\n\n if (!url || !MANIFEST_PATHS.some((path) => url.includes(path))) {\n next();\n return;\n }\n\n (async () => {\n try {\n if (!existsSync(portalDir)) {\n // Fall through to the Vite proxy so requests reach the real\n // API when local portal content is unavailable (e.g. --skip-pull).\n next();\n return;\n }\n\n let manifestJson = cachedManifest;\n if (manifestJson == null) {\n if (!buildInFlight) {\n // Stamp the build with the generation it was started\n // under. onFileChange bumps that counter, so a build\n // whose inputs changed underneath it can be recognized\n // when it finally resolves.\n const generation = manifestGeneration;\n buildInFlight = buildManifestFromLocalFiles(portalDir)\n .then((m) => {\n const json = JSON.stringify(m);\n // Only a build still current may populate the\n // cache. Caching a superseded one pinned the\n // pre-edit snapshot permanently: a non-null cache\n // means no later request ever rebuilds, so the\n // edit stayed invisible until some unrelated file\n // happened to change outside a build window.\n if (generation === manifestGeneration) {\n cachedManifest = json;\n buildInFlight = null;\n }\n return json;\n })\n .catch((err) => {\n // Same guard: a superseded build must not clear a\n // newer one's handle.\n if (generation === manifestGeneration) {\n buildInFlight = null; // allow retry on next request\n }\n throw err;\n });\n }\n // This response serves what was built for it, which is\n // correct even when superseded — the request predates\n // the edit. The point is that the NEXT one rebuilds.\n manifestJson = await buildInFlight;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"X-Portal-Dev-Mode\": \"true\",\n \"Cache-Control\": \"no-store\",\n });\n res.end(manifestJson);\n } catch (err) {\n console.error(\"[fluid-portal-dev] Error building manifest:\", err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n err instanceof Error\n ? err.message\n : \"Failed to build manifest from local files\",\n }),\n );\n }\n })();\n },\n );\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAM,cAAc;;AAGpB,MAAM,iBAAiB,CACrB,qBACA,mCACD;AAED,MAAM,kBAAkB;AACxB,MAAM,2BAA2B;AACjC,MAAM,uCAAuB,IAAI,SAG9B;;;;;;;;;AAyBH,eAAsB,6BACpB,eAA4C,EAAE,EACjB;CAC7B,MAAM,QACJ,aAAa,UAAU,KAAA,IAAY,aAAa,QAAQ,cAAc;AACxE,KAAI,CAAC,MAAO,QAAO,KAAA;CAEnB,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBACN,WAAW,OAAO,EACxB,aAAa,aAAa,yBAC3B;AAED,KAAI;EACF,MAAM,WAAW,OAAO,aAAa,aAAa,OAChD,IAAI,aAAa,WAAW,iBAAiB,EAAE,QAAQ,QAAQ,GAAG,CAAC,+BACnE;GACE,SAAS;IACP,eAAe,UAAU;IACzB,QAAQ;IACT;GACD,QAAQ,WAAW;GACpB,CACF;AACD,MAAI,CAAC,SAAS,GAAI,QAAO,KAAA;EAGzB,MAAM,aADQ,MAAM,SAAS,MAAM,EACZ,MAAM,SAAS;AACtC,MACE,OAAO,cAAc,YACrB,CAAC,yCAAyC,KAAK,UAAU,CAEzD;AAGF,SAAO,WAAW,UAAU;SACtB;AACN;WACQ;AACR,eAAa,MAAM;;;;;;;;;;AAWvB,SAAS,uBAAuB,MAAkC;AAChE,KAAI,QAAQ,IAAI,aAAc,QAAO,QAAQ,IAAI;AACjD,KAAI,QAAQ,IAAI,yBACd,QAAO,QAAQ,IAAI;AAErB,MAAK,MAAM,QAAQ,CAAC,cAAc,OAAO,EAAE;EACzC,MAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,MAAI,CAAC,WAAW,KAAK,CAAE;AACvB,OAAK,MAAM,QAAQ,aAAa,MAAM,OAAO,CAAC,MAAM,KAAK,EAAE;GAIzD,MAAM,QACJ,yDAAyD,KAAK,KAAK;GACrE,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AACpC,OAAI,MAAO,QAAO;;;;AAMxB,SAAS,8BAA8B,QAAoC;AACzE,KAAI;EACF,MAAM,WAAW,IAAI,IAAI,OAAO,CAAC;EAIjC,MAAM,YAHQ,kDAAkD,KAC9D,SACD,GACyB;AAC1B,MACE,CAAC,aACD;GAAC;GAAS;GAAO;GAAO;GAAQ;GAAU;GAAS,CAAC,SAAS,UAAU,CAEvE;AAEF,SAAO;SACD;AACN;;;;;;;;;AAUJ,eAAe,kBACb,MACA,MACA,QAC6B;AAC7B,KAAI;EACF,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,MAAM,MAAM,QAAQ,MAAM,UAAU,MAAM,CAAC,SAAS,eAAe,CAAC;AACpE,SACE,IAAI,gBACJ,QAAQ,IAAI,gBAIZ,IAAI,4BACJ,QAAQ,IAAI,4BACZ,KAAA;SAEI;AACN,SAAO,uBAAuB,KAAK;;;AAKvC,SAAgB,gBAAgB,SAAuC;CACrE,IAAI;CACJ,IAAI,iBAAgC;CACpC,IAAI,gBAAwC;;;;CAI5C,IAAI,qBAAqB;CACzB,MAAM,sBACJ,SAAS,0BAA0B;AAErC,QAAO;EACL,MAAM;EACN,SAAS;EAGT,MAAM,OAAO,QAAa,KAAU;AAClC,OAAI,KAAK,YAAY,WAAW,SAAS,kBAAkB,MACzD,QAAO,EAAE;GAGX,MAAM,OAAe,OAAO,QAAQ,QAAQ,KAAK;GACjD,MAAM,kBAAkB,OAAO,QAAQ,QAAQ,YAAY,KAAA;GAC3D,MAAM,mBACJ,SAAS,kBAAkB,kBACvB,KAAA,IACA,MAAM,kBACJ,MACA,KAAK,QAAQ,eACb,OAAO,OACR;GACP,MAAM,eACJ,SAAS,kBAAkB,mBAAmB,mBAC1C,KAAA,IACA,aAAa;IACX,MAAM,WAAW,qBAAqB,IAAI,oBAAoB;AAC9D,QAAI,SAAU,QAAO;IAErB,MAAM,UAAU,qBAAqB;AACrC,yBAAqB,IAAI,qBAAqB,QAAQ;AACtD,WAAO;OACL;GACV,MAAM,SACJ,SAAS,kBACT,oBACA,gBACA;GACF,MAAM,sBAAsB,8BAA8B,OAAO;AAEjE,OAAI,cAAc;AAChB,YAAQ,IAAI,wCAAwC,eAAe;AACnE,YAAQ,IACN,6GACD;cAED,CAAC,SAAS,kBACV,CAAC,mBACD,CAAC,iBAED,SAAQ,KACN,gFAAgF,gBAAgB,kHAEjG;YACQ,oBACT,SAAQ,KACN,sBAAsB,OAAO,2FACa,oBAAoB,mEAC/D;GASH,MAAM,UAAkC,EAAE;AAC1C,QAAK,MAAM,OAAO,CAChB,gCACA,2CACD,CACC,KAAI,OAAO,SAAS,SAAS,KAAA,EAC3B,SAAQ,OAAO,KAAK,UAAU,GAAG;AAOrC,UAAO;IACL,QAAQ;IACR,QAAQ,EAAE,OANE,CAAC,kBACX,EAAE,QAAQ;KAAE;KAAQ,cAAc;KAAM,EAAE,GAC1C,EAAE,EAIa;IAClB;;EAIH,eAAe,QAAa;GAC1B,MAAM,OAAe,OAAO,QAAQ,QAAQ,KAAK;AACjD,eAAY,SAAS,YACjB,QAAQ,MAAM,QAAQ,UAAU,GAChC,KAAK,MAAM,SAAS;;EAI1B,gBAAgB,QAAa;AAI3B,OAAI,OAAO,sBAAuB;AAClC,UAAO,wBAAwB;GAE/B,MAAM,WAAW;AAEjB,OAAI,WAAW,SAAS,EAAE;AACxB,WAAO,QAAQ,IAAI,SAAS;IAE5B,IAAI,cAAoD;IAExD,MAAM,gBAAgB,aAAqB;AACzC,SAAI,CAAC,SAAS,WAAW,SAAS,CAAE;AAEpC,sBAAiB;AACjB,qBAAgB;AAChB,2BAAsB;AAEtB,aAAQ,IACN,oCAAoC,SAAS,QAAQ,WAAW,KAAK,GAAG,GACzE;AAED,SAAI,YAAa,cAAa,YAAY;AAC1C,mBAAc,iBAAiB;AAC7B,oBAAc;AACd,aAAO,GAAG,KAAK;OAAE,MAAM;OAAe,MAAM;OAAK,CAAC;QACjD,YAAY;;AAGjB,WAAO,QAAQ,GAAG,UAAU,aAAa;AACzC,WAAO,QAAQ,GAAG,OAAO,aAAa;AACtC,WAAO,QAAQ,GAAG,UAAU,aAAa;;AAU3C,UAAO,YAAY,KAEhB,KAAU,KAAU,SAAqB;IACxC,MAAM,MAA0B,IAAI;AAEpC,QAAI,CAAC,OAAO,CAAC,eAAe,MAAM,SAAS,IAAI,SAAS,KAAK,CAAC,EAAE;AAC9D,WAAM;AACN;;AAGF,KAAC,YAAY;AACX,SAAI;AACF,UAAI,CAAC,WAAW,UAAU,EAAE;AAG1B,aAAM;AACN;;MAGF,IAAI,eAAe;AACnB,UAAI,gBAAgB,MAAM;AACxB,WAAI,CAAC,eAAe;QAKlB,MAAM,aAAa;AACnB,wBAAgB,4BAA4B,UAAU,CACnD,MAAM,MAAM;SACX,MAAM,OAAO,KAAK,UAAU,EAAE;AAO9B,aAAI,eAAe,oBAAoB;AACrC,2BAAiB;AACjB,0BAAgB;;AAElB,gBAAO;UACP,CACD,OAAO,QAAQ;AAGd,aAAI,eAAe,mBACjB,iBAAgB;AAElB,eAAM;UACN;;AAKN,sBAAe,MAAM;;AAGvB,UAAI,UAAU,KAAK;OACjB,gBAAgB;OAChB,qBAAqB;OACrB,iBAAiB;OAClB,CAAC;AACF,UAAI,IAAI,aAAa;cACd,KAAK;AACZ,cAAQ,MAAM,+CAA+C,IAAI;AACjE,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IACF,KAAK,UAAU,EACb,OACE,eAAe,QACX,IAAI,UACJ,6CACP,CAAC,CACH;;QAED;KAEP;;EAEJ"}
1
+ {"version":3,"file":"portal-dev-plugin-BFqDxRMa.mjs","names":[],"sources":["../src/vite-plugin/portal-dev-plugin.ts"],"sourcesContent":["/**\n * Vite plugin for local portal content development.\n *\n * Intercepts the manifest API request and serves content assembled from the\n * local `portal/` directory. Watches portal files for changes and triggers a\n * full page reload.\n *\n * The manifest is intercepted on BOTH paths the SDK has used across versions\n * (`/api/app/manifest` since portal-sdk 0.1.4xx, `/api/fluid_os/definitions/\n * active` before that) so a newer SDK against an older-generation app — or\n * vice versa — still gets the local draft.\n */\n\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getAuthToken, getFluidApiBase } from \"@fluid-app/fluid-cli\";\nimport { buildManifestFromLocalFiles } from \"./build-manifest.js\";\n\n/** Options for the portal dev plugin. */\nexport interface PortalDevPluginOptions {\n portalDir?: string;\n /**\n * When true (default), dev-server requests are forced same-origin: the\n * plugin blanks `import.meta.env.VITE_API_URL` in serve mode and proxies\n * `/api` to the configured Fluid host instead. Without this, a `.env`\n * pointing VITE_API_URL at a remote API makes the SPA fetch its manifest\n * cross-origin — silently bypassing this plugin and rendering the remote\n * app instead of the local `portal/` draft. Set false to opt out and let\n * the SPA call the remote API directly.\n */\n sameOriginDev?: boolean;\n /**\n * Override the `/api` dev-proxy target. Defaults to VITE_API_URL from the\n * process env / .env files, then the authenticated company's\n * `<subdomain>.portal.fluid.app` tenant BFF, then https://api.fluid.app.\n */\n apiProxyTarget?: string;\n /**\n * Override tenant-target discovery. Primarily useful to keep embedded plugin\n * integrations and tests deterministic.\n */\n resolveTenantApiTarget?: () => Promise<string | undefined>;\n}\n\nconst DEBOUNCE_MS = 100;\n\n/** Manifest endpoints across SDK generations — all serve the local draft. */\nconst MANIFEST_PATHS = [\n \"/api/app/manifest\",\n \"/api/fluid_os/definitions/active\",\n];\n\nconst DEFAULT_API_URL = \"https://api.fluid.app\";\nconst TENANT_LOOKUP_TIMEOUT_MS = 3_000;\nconst tenantTargetPromises = new WeakMap<\n () => Promise<string | undefined>,\n Promise<string | undefined>\n>();\n\ninterface CompanyMeResponse {\n data?: {\n company?: {\n subdomain?: unknown;\n };\n };\n}\n\nexport interface TenantApiTargetDependencies {\n token?: string | null;\n apiBase?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/**\n * Resolve the portal tenant BFF for the CLI's project-aware Fluid profile.\n *\n * Portal member APIs do not live on api.fluid.app or `<tenant>.fluid.app`.\n * Their OpenAPI server is `<tenant>.portal.fluid.app`, and using either of\n * the former hosts produces a wall of unrelated 404s in otherwise-valid\n * local portals.\n */\nexport async function resolvePortalTenantApiTarget(\n dependencies: TenantApiTargetDependencies = {},\n): Promise<string | undefined> {\n const token =\n dependencies.token !== undefined ? dependencies.token : getAuthToken();\n if (!token) return undefined;\n\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n dependencies.timeoutMs ?? TENANT_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n const response = await (dependencies.fetchImpl ?? fetch)(\n `${(dependencies.apiBase ?? getFluidApiBase()).replace(/\\/+$/, \"\")}/api/company/v1/companies/me`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n signal: controller.signal,\n },\n );\n if (!response.ok) return undefined;\n\n const body = (await response.json()) as CompanyMeResponse;\n const subdomain = body.data?.company?.subdomain;\n if (\n typeof subdomain !== \"string\" ||\n !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(subdomain)\n ) {\n return undefined;\n }\n\n return `https://${subdomain}.portal.fluid.app`;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Fallback .env reader for VITE_API_URL, used only when vite's `loadEnv`\n * can't be imported (it's a declared dependency, so this is defensive — but\n * a broken install must degrade to \"wrong host\" rather than \"dev server\n * won't boot\"). Mirrors the basic lookup order: live env > .env.local >\n * .env. Unlike loadEnv it ignores .env.[mode] files and variable expansion.\n */\nfunction readViteApiUrlFallback(root: string): string | undefined {\n if (process.env.VITE_API_URL) return process.env.VITE_API_URL;\n if (process.env.NEXT_PUBLIC_API_BASE_URL) {\n return process.env.NEXT_PUBLIC_API_BASE_URL;\n }\n for (const file of [\".env.local\", \".env\"]) {\n const path = join(root, file);\n if (!existsSync(path)) continue;\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n // Quoted values stop at the closing `\"`; unquoted values stop before\n // an inline ` # comment` tail (dotenv semantics — a trailing comment\n // must not leak into the proxy target URL).\n const match =\n /^\\s*VITE_API_URL\\s*=\\s*(?:\"([^\"]*)\"|(.*?))\\s*(?:#.*)?$/.exec(line);\n const value = match?.[1] ?? match?.[2];\n if (value) return value;\n }\n }\n return undefined;\n}\n\nfunction storefrontSubdomainFromTarget(target: string): string | undefined {\n try {\n const hostname = new URL(target).hostname;\n const match = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.fluid\\.app$/.exec(\n hostname,\n );\n const subdomain = match?.[1];\n if (\n !subdomain ||\n [\"admin\", \"api\", \"app\", \"docs\", \"portal\", \"upload\"].includes(subdomain)\n ) {\n return undefined;\n }\n return subdomain;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve VITE_API_URL the way vite itself would — `loadEnv` honors\n * .env.[mode], .env.[mode].local, envDir, and variable expansion, which the\n * fallback parser does not (a mode-specific staging API must not silently\n * fall back to production).\n */\nasync function resolveViteApiUrl(\n root: string,\n mode: string,\n envDir: string | undefined,\n): Promise<string | undefined> {\n try {\n const { loadEnv } = await import(\"vite\");\n const env = loadEnv(mode, envDir ?? root, [\"VITE_\", \"NEXT_PUBLIC_\"]);\n return (\n env.VITE_API_URL ||\n process.env.VITE_API_URL ||\n // Portals migrated from Next.js conventions configure the API host as\n // NEXT_PUBLIC_API_BASE_URL — honored by `fluid portal dev` since it\n // was introduced, so it stays a recognized source here.\n env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n undefined\n );\n } catch {\n return readViteApiUrlFallback(root);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function portalDevPlugin(options?: PortalDevPluginOptions): any {\n let portalDir: string;\n let cachedManifest: string | null = null;\n let buildInFlight: Promise<string> | null = null;\n /** Bumped on every watched file change. A build carries the value it\n * started under; if it no longer matches when the build resolves, the\n * build read pre-edit state and must not be cached. */\n let manifestGeneration = 0;\n const resolveTenantTarget =\n options?.resolveTenantApiTarget ?? resolvePortalTenantApiTarget;\n\n return {\n name: \"fluid-portal-dev\",\n enforce: \"pre\" as const,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async config(config: any, env: any) {\n if (env?.command !== \"serve\" || options?.sameOriginDev === false) {\n return {};\n }\n\n const root: string = config.root ?? process.cwd();\n const appDefinesProxy = config.server?.proxy?.[\"/api\"] !== undefined;\n const configuredTarget =\n options?.apiProxyTarget || appDefinesProxy\n ? undefined\n : await resolveViteApiUrl(\n root,\n env?.mode ?? \"development\",\n config.envDir,\n );\n const tenantTarget =\n options?.apiProxyTarget || appDefinesProxy || configuredTarget\n ? undefined\n : await (() => {\n const existing = tenantTargetPromises.get(resolveTenantTarget);\n if (existing) return existing;\n\n const pending = resolveTenantTarget();\n tenantTargetPromises.set(resolveTenantTarget, pending);\n return pending;\n })();\n const target =\n options?.apiProxyTarget ??\n configuredTarget ??\n tenantTarget ??\n DEFAULT_API_URL;\n const storefrontSubdomain = storefrontSubdomainFromTarget(target);\n\n if (tenantTarget) {\n console.log(`[fluid-portal-dev] Tenant API proxy: ${tenantTarget}`);\n console.log(\n \"[fluid-portal-dev] Member data still requires a portal handoff session; localhost requests may return 401.\",\n );\n } else if (\n !options?.apiProxyTarget &&\n !appDefinesProxy &&\n !configuredTarget\n ) {\n console.warn(\n `[fluid-portal-dev] Could not resolve the company tenant API; falling back to ${DEFAULT_API_URL}. ` +\n \"Built-in member screens may not load. Set VITE_API_URL explicitly or sign in with the project's Fluid profile.\",\n );\n } else if (storefrontSubdomain) {\n console.warn(\n `[fluid-portal-dev] ${target} is the storefront host, not the Portal tenant BFF. ` +\n `Built-in member APIs live at https://${storefrontSubdomain}.portal.fluid.app; remove the override to let the CLI resolve it.`,\n );\n }\n\n // Vite merges a plugin's returned config OVER the user config, so an\n // app's own explicit define / /api proxy is only preserved by checking\n // for it and yielding here. BOTH API-host env vars must be blanked:\n // dev.ts's envPrefix exposes NEXT_PUBLIC_API_BASE_URL to the SPA too,\n // so leaving it defined would recreate the exact cross-origin bypass\n // this hook exists to prevent, just for Next.js-migrated portals.\n const defines: Record<string, string> = {};\n for (const key of [\n \"import.meta.env.VITE_API_URL\",\n \"import.meta.env.NEXT_PUBLIC_API_BASE_URL\",\n ]) {\n if (config.define?.[key] === undefined) {\n defines[key] = JSON.stringify(\"\");\n }\n }\n const proxy = !appDefinesProxy\n ? { \"/api\": { target, changeOrigin: true } }\n : {};\n\n return {\n define: defines,\n server: { proxy },\n };\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configResolved(config: any) {\n const root: string = config.root ?? process.cwd();\n portalDir = options?.portalDir\n ? resolve(root, options.portalDir)\n : join(root, \"portal\");\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configureServer(server: any) {\n // Idempotency: `fluid portal dev` injects this plugin, and a portal's\n // own vite.config may also wire it. Only the first instance registers\n // the watcher + middleware so we don't double-reload or double-serve.\n if (server.__fluidPortalDevWired) return;\n server.__fluidPortalDevWired = true;\n\n const watchDir = portalDir;\n\n // Registered unconditionally: `portal/` need not exist when the server\n // boots. `fluid portal dev --skip-pull` is exactly that case, and the\n // manifest middleware below re-checks existence per request, so it\n // starts serving local content the moment the directory appears.\n // Gating the watcher on startup existence left that flow serving the\n // very first build forever — no invalidation, no reload, no error.\n server.watcher.add(watchDir);\n\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n const watchDirPrefix = watchDir.endsWith(sep) ? watchDir : watchDir + sep;\n\n const onFileChange = (filePath: string) => {\n // A separator boundary is required. A bare prefix test also matches\n // siblings that merely start with the same characters, so editing\n // `<root>/portal-backup/x.json` or `<root>/portal.config.ts` used to\n // drop the manifest cache and force a full page reload.\n if (filePath !== watchDir && !filePath.startsWith(watchDirPrefix)) {\n return;\n }\n\n cachedManifest = null;\n buildInFlight = null;\n manifestGeneration += 1;\n\n console.log(\n `[fluid-portal-dev] File changed: ${filePath.replace(watchDirPrefix, \"\")}`,\n );\n\n if (reloadTimer) clearTimeout(reloadTimer);\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n server.ws.send({ type: \"full-reload\", path: \"*\" });\n }, DEBOUNCE_MS);\n };\n\n server.watcher.on(\"change\", onFileChange);\n server.watcher.on(\"add\", onFileChange);\n server.watcher.on(\"unlink\", onFileChange);\n\n // ── Middleware to intercept manifest requests ───────────────────\n // Every MANIFEST_PATHS route returns the SAME `{ manifest }` payload:\n // the portal-tenant BFF route the current SDK runtime fetches\n // (useFluidApp → app_manifest_show) and the legacy FluidOS route, so\n // the local portal/ draft feeds the running app regardless of which\n // runtime generation it uses (the SDK adapter reads\n // `response.manifest`, matching buildManifestFromLocalFiles' output).\n server.middlewares.use(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (req: any, res: any, next: () => void) => {\n const url: string | undefined = req.url;\n\n if (!url || !MANIFEST_PATHS.some((path) => url.includes(path))) {\n next();\n return;\n }\n\n (async () => {\n try {\n if (!existsSync(portalDir)) {\n // Fall through to the Vite proxy so requests reach the real\n // API when local portal content is unavailable (e.g. --skip-pull).\n next();\n return;\n }\n\n let manifestJson = cachedManifest;\n if (manifestJson == null) {\n if (!buildInFlight) {\n // Stamp the build with the generation it was started\n // under. onFileChange bumps that counter, so a build\n // whose inputs changed underneath it can be recognized\n // when it finally resolves.\n const generation = manifestGeneration;\n buildInFlight = buildManifestFromLocalFiles(portalDir)\n .then((m) => {\n const json = JSON.stringify(m);\n // Only a build still current may populate the\n // cache. Caching a superseded one pinned the\n // pre-edit snapshot permanently: a non-null cache\n // means no later request ever rebuilds, so the\n // edit stayed invisible until some unrelated file\n // happened to change outside a build window.\n if (generation === manifestGeneration) {\n cachedManifest = json;\n buildInFlight = null;\n }\n return json;\n })\n .catch((err) => {\n // Same guard: a superseded build must not clear a\n // newer one's handle.\n if (generation === manifestGeneration) {\n buildInFlight = null; // allow retry on next request\n }\n throw err;\n });\n }\n // This response serves what was built for it, which is\n // correct even when superseded — the request predates\n // the edit. The point is that the NEXT one rebuilds.\n manifestJson = await buildInFlight;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"X-Portal-Dev-Mode\": \"true\",\n \"Cache-Control\": \"no-store\",\n });\n res.end(manifestJson);\n } catch (err) {\n console.error(\"[fluid-portal-dev] Error building manifest:\", err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n err instanceof Error\n ? err.message\n : \"Failed to build manifest from local files\",\n }),\n );\n }\n })();\n },\n );\n },\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { N as __exportAll } from "./pull-BWnby_pY.mjs";
1
+ import { N as __exportAll } from "./pull-Kx1ncTLF.mjs";
2
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";
@@ -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-CO-im-5P.mjs.map
146
+ //# sourceMappingURL=portal-widget-dev-plugin-Dis7d954.mjs.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"portal-widget-dev-plugin-Dis7d954.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"}
@@ -14548,7 +14548,10 @@ const generatedSchemas = {
14548
14548
  }],
14549
14549
  title: "Background",
14550
14550
  description: "Background for the text container",
14551
- default: "background"
14551
+ default: {
14552
+ type: "solid",
14553
+ color: "background"
14554
+ }
14552
14555
  },
14553
14556
  padding: {
14554
14557
  enum: [
@@ -14747,7 +14750,10 @@ const generatedSchemas = {
14747
14750
  }],
14748
14751
  title: "Background",
14749
14752
  description: "Background for the list container",
14750
- default: "background"
14753
+ default: {
14754
+ type: "solid",
14755
+ color: "background"
14756
+ }
14751
14757
  },
14752
14758
  padding: {
14753
14759
  enum: [
@@ -14913,7 +14919,10 @@ const generatedSchemas = {
14913
14919
  }],
14914
14920
  title: "Background",
14915
14921
  description: "Background for the calendar container",
14916
- default: "background"
14922
+ default: {
14923
+ type: "solid",
14924
+ color: "background"
14925
+ }
14917
14926
  },
14918
14927
  textColor: {
14919
14928
  enum: [
@@ -15183,7 +15192,10 @@ const generatedSchemas = {
15183
15192
  }],
15184
15193
  title: "Background",
15185
15194
  description: "Background for the card",
15186
- default: "background"
15195
+ default: {
15196
+ type: "solid",
15197
+ color: "background"
15198
+ }
15187
15199
  },
15188
15200
  padding: {
15189
15201
  enum: [
@@ -15639,7 +15651,10 @@ const generatedSchemas = {
15639
15651
  }],
15640
15652
  title: "Background",
15641
15653
  description: "Background for the container",
15642
- default: "background"
15654
+ default: {
15655
+ type: "solid",
15656
+ color: "background"
15657
+ }
15643
15658
  },
15644
15659
  textColor: {
15645
15660
  enum: [
@@ -15910,7 +15925,10 @@ const generatedSchemas = {
15910
15925
  }],
15911
15926
  title: "Background",
15912
15927
  description: "Background for the chart container",
15913
- default: "background"
15928
+ default: {
15929
+ type: "solid",
15930
+ color: "background"
15931
+ }
15914
15932
  },
15915
15933
  padding: {
15916
15934
  enum: [
@@ -16069,7 +16087,10 @@ const generatedSchemas = {
16069
16087
  }],
16070
16088
  title: "Background",
16071
16089
  description: "Background for the container",
16072
- default: "background"
16090
+ default: {
16091
+ type: "solid",
16092
+ color: "background"
16093
+ }
16073
16094
  },
16074
16095
  children: {
16075
16096
  type: "array",
@@ -16529,7 +16550,10 @@ const generatedSchemas = {
16529
16550
  }],
16530
16551
  title: "Background",
16531
16552
  description: "Background for the layout container",
16532
- default: "background"
16553
+ default: {
16554
+ type: "solid",
16555
+ color: "background"
16556
+ }
16533
16557
  },
16534
16558
  padding: {
16535
16559
  enum: [
@@ -16990,7 +17014,10 @@ const generatedSchemas = {
16990
17014
  }],
16991
17015
  title: "Background",
16992
17016
  description: "Background color or image for the widget",
16993
- default: "background"
17017
+ default: {
17018
+ type: "solid",
17019
+ color: "background"
17020
+ }
16994
17021
  },
16995
17022
  listType: {
16996
17023
  enum: ["unordered", "ordered"],
@@ -17351,7 +17378,10 @@ const generatedSchemas = {
17351
17378
  }],
17352
17379
  title: "Background",
17353
17380
  description: "Background for the widget container",
17354
- default: "background"
17381
+ default: {
17382
+ type: "solid",
17383
+ color: "background"
17384
+ }
17355
17385
  },
17356
17386
  textColor: {
17357
17387
  enum: [
@@ -17640,7 +17670,10 @@ const generatedSchemas = {
17640
17670
  }],
17641
17671
  title: "Background",
17642
17672
  description: "Background color for nested media container",
17643
- default: "background"
17673
+ default: {
17674
+ type: "solid",
17675
+ color: "background"
17676
+ }
17644
17677
  },
17645
17678
  shareables: {
17646
17679
  $ref: "#/$defs/jsonValue",
@@ -17773,7 +17806,10 @@ const generatedSchemas = {
17773
17806
  }],
17774
17807
  title: "Background",
17775
17808
  description: "Background for the widget",
17776
- default: "background"
17809
+ default: {
17810
+ type: "solid",
17811
+ color: "background"
17812
+ }
17777
17813
  },
17778
17814
  textColor: {
17779
17815
  enum: [
@@ -18488,7 +18524,10 @@ const generatedSchemas = {
18488
18524
  }],
18489
18525
  title: "Background",
18490
18526
  description: "Background for the widget container",
18491
- default: "background"
18527
+ default: {
18528
+ type: "solid",
18529
+ color: "background"
18530
+ }
18492
18531
  },
18493
18532
  textColor: {
18494
18533
  enum: [
@@ -18897,7 +18936,10 @@ const generatedSchemas = {
18897
18936
  }],
18898
18937
  title: "Background",
18899
18938
  description: "Background for the widget container",
18900
- default: "background"
18939
+ default: {
18940
+ type: "solid",
18941
+ color: "background"
18942
+ }
18901
18943
  },
18902
18944
  textColor: {
18903
18945
  enum: [
@@ -19192,7 +19234,10 @@ const generatedSchemas = {
19192
19234
  }],
19193
19235
  title: "Background",
19194
19236
  description: "Container background",
19195
- default: "background"
19237
+ default: {
19238
+ type: "solid",
19239
+ color: "background"
19240
+ }
19196
19241
  },
19197
19242
  padding: {
19198
19243
  enum: [
@@ -19406,7 +19451,10 @@ const generatedSchemas = {
19406
19451
  }],
19407
19452
  title: "Background",
19408
19453
  description: "Background for the table container",
19409
- default: "background"
19454
+ default: {
19455
+ type: "solid",
19456
+ color: "background"
19457
+ }
19410
19458
  },
19411
19459
  alternatingColorEnabled: {
19412
19460
  type: "boolean",
@@ -19726,7 +19774,10 @@ const generatedSchemas = {
19726
19774
  }],
19727
19775
  title: "Background",
19728
19776
  description: "Background for the text container",
19729
- default: "background"
19777
+ default: {
19778
+ type: "solid",
19779
+ color: "background"
19780
+ }
19730
19781
  },
19731
19782
  padding: {
19732
19783
  enum: [
@@ -19879,7 +19930,10 @@ const generatedSchemas = {
19879
19930
  }],
19880
19931
  title: "Background",
19881
19932
  description: "Background for the widget container",
19882
- default: "background"
19933
+ default: {
19934
+ type: "solid",
19935
+ color: "background"
19936
+ }
19883
19937
  },
19884
19938
  textColor: {
19885
19939
  enum: [
@@ -21073,4 +21127,4 @@ const pullCommand = new Command("pull").description("Pull a Fluid OS definition'
21073
21127
  //#endregion
21074
21128
  export { toFluidOsDynamicObject as A, resolveIdToSlug as C, atomicWriteFile as D, writeMappings as E, ApiError as M, __exportAll as N, createFluidOsClient as O, removeMapping as S, updateMapping as T, diffAgainstSnapshot as _, buildNavigationIdToSlugMap as a, deriveSlug as b, transformNavigation as c, transformScreen as d, transformTheme as f, computeFileHash as g, collectSnapshotNetworkWidgetGrants as h, buildIdToSlugMap as i, createFluidOsHeyApiClient as j, fluidOsApi as k, transformNavigationItems as l, collectNetworkWidgetGrants as m, pull_exports as n, buildThemeIdToSlugMap as o, buildSnapshot as p, validatePortalResource as r, deriveScreenSlug as s, pullCommand as t, transformProfile as u, readSnapshot as v, resolveSlugToId as w, readMappings as x, writeSnapshot as y };
21075
21129
 
21076
- //# sourceMappingURL=pull-BWnby_pY.mjs.map
21130
+ //# sourceMappingURL=pull-Kx1ncTLF.mjs.map