@fluid-app/portal-sdk 0.1.370 → 0.1.371

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.
Files changed (104) hide show
  1. package/dist/{CardWidget-CqdJHxX0.cjs → CardWidget-BWwI1G0Z.cjs} +2 -2
  2. package/dist/{CardWidget-CqdJHxX0.cjs.map → CardWidget-BWwI1G0Z.cjs.map} +1 -1
  3. package/dist/{CardWidget-DCrXorb0.cjs → CardWidget-aJYbzWG9.cjs} +2 -2
  4. package/dist/{CardWidget-DdV1HDy0.mjs → CardWidget-cIc9cOG9.mjs} +2 -2
  5. package/dist/{CardWidget-DdV1HDy0.mjs.map → CardWidget-cIc9cOG9.mjs.map} +1 -1
  6. package/dist/{ContainerWidget-CYIOGpgy.cjs → ContainerWidget-C2LMzXVj.cjs} +3 -3
  7. package/dist/{ContainerWidget-CZTRim0A.cjs → ContainerWidget-bMMdFhLg.cjs} +2 -2
  8. package/dist/{ContainerWidget-CZTRim0A.cjs.map → ContainerWidget-bMMdFhLg.cjs.map} +1 -1
  9. package/dist/{ContainerWidget-BKaiiPlW.mjs → ContainerWidget-mSWihekF.mjs} +2 -2
  10. package/dist/{ContainerWidget-BKaiiPlW.mjs.map → ContainerWidget-mSWihekF.mjs.map} +1 -1
  11. package/dist/{FluidProvider-DG7duKf4.cjs → FluidProvider-C6d1TUVm.cjs} +8 -10
  12. package/dist/{FluidProvider-DG7duKf4.cjs.map → FluidProvider-C6d1TUVm.cjs.map} +1 -1
  13. package/dist/{FluidProvider-Bmmdax4d.mjs → FluidProvider-C80gc4Yi.mjs} +8 -10
  14. package/dist/{FluidProvider-Bmmdax4d.mjs.map → FluidProvider-C80gc4Yi.mjs.map} +1 -1
  15. package/dist/{LayoutWidget-xw1zY6cc.cjs → LayoutWidget-BqUbneWw.cjs} +2 -2
  16. package/dist/{LayoutWidget-xw1zY6cc.cjs.map → LayoutWidget-BqUbneWw.cjs.map} +1 -1
  17. package/dist/{LayoutWidget-VU6uFW3-.cjs → LayoutWidget-C1BzDeQI.cjs} +2 -2
  18. package/dist/{LayoutWidget-C0TbnxQW.mjs → LayoutWidget-D3qYKt9F.mjs} +2 -2
  19. package/dist/{LayoutWidget-C0TbnxQW.mjs.map → LayoutWidget-D3qYKt9F.mjs.map} +1 -1
  20. package/dist/{MessagingScreen-BY8IowR0.mjs → MessagingScreen-D3si8TPx.mjs} +6 -6
  21. package/dist/{MessagingScreen-CyxUDdcb.mjs → MessagingScreen-DKzYThAY.mjs} +2 -2
  22. package/dist/{MessagingScreen-CyxUDdcb.mjs.map → MessagingScreen-DKzYThAY.mjs.map} +1 -1
  23. package/dist/{MessagingScreen-ylXd2Um7.cjs → MessagingScreen-DqxRu2x3.cjs} +2 -2
  24. package/dist/{MessagingScreen-ylXd2Um7.cjs.map → MessagingScreen-DqxRu2x3.cjs.map} +1 -1
  25. package/dist/{MessagingScreen-BIJU7dWT.cjs → MessagingScreen-PZyO11S1.cjs} +6 -6
  26. package/dist/{OrdersScreen-DsTMrxlo.mjs → OrdersScreen-B2htbQ2Q.mjs} +2 -2
  27. package/dist/{OrdersScreen-DsTMrxlo.mjs.map → OrdersScreen-B2htbQ2Q.mjs.map} +1 -1
  28. package/dist/{OrdersScreen-1b2ZzMEg.cjs → OrdersScreen-BuX5aWgx.cjs} +6 -6
  29. package/dist/{OrdersScreen-BVRpsxMF.mjs → OrdersScreen-Cgjfo2Fs.mjs} +6 -6
  30. package/dist/{OrdersScreen-B-biRUNQ.cjs → OrdersScreen-CpNvxRDC.cjs} +2 -2
  31. package/dist/{OrdersScreen-B-biRUNQ.cjs.map → OrdersScreen-CpNvxRDC.cjs.map} +1 -1
  32. package/dist/{ProfileScreen-DvnWtXlF.mjs → ProfileScreen-BCwdwO_p.mjs} +3 -3
  33. package/dist/{ProfileScreen-DvnWtXlF.mjs.map → ProfileScreen-BCwdwO_p.mjs.map} +1 -1
  34. package/dist/{ProfileScreen-DzgGP3lr.mjs → ProfileScreen-D7UA604p.mjs} +7 -7
  35. package/dist/{ProfileScreen-DYe6Msda.cjs → ProfileScreen-DlIXDIeu.cjs} +3 -3
  36. package/dist/{ProfileScreen-DYe6Msda.cjs.map → ProfileScreen-DlIXDIeu.cjs.map} +1 -1
  37. package/dist/{ProfileScreen-Dsbr8wip.cjs → ProfileScreen-MypXufrO.cjs} +7 -7
  38. package/dist/{ScreenRenderer-CKu-Jc1I.mjs → ScreenRenderer-CcT_j-WQ.mjs} +1 -4
  39. package/dist/{ScreenRenderer-CKu-Jc1I.mjs.map → ScreenRenderer-CcT_j-WQ.mjs.map} +1 -1
  40. package/dist/{ScreenRenderer-CZTdJd2G.cjs → ScreenRenderer-Ce8MJPnR.cjs} +1 -4
  41. package/dist/{ScreenRenderer-CZTdJd2G.cjs.map → ScreenRenderer-Ce8MJPnR.cjs.map} +1 -1
  42. package/dist/{ShopScreen-DcgyX3Wk.cjs → ShopScreen-29SZwMWF.cjs} +2 -2
  43. package/dist/{ShopScreen-DcgyX3Wk.cjs.map → ShopScreen-29SZwMWF.cjs.map} +1 -1
  44. package/dist/{ShopScreen-nUFdeac6.cjs → ShopScreen-BC9NDysQ.cjs} +6 -6
  45. package/dist/{ShopScreen-Cy5cf6tn.mjs → ShopScreen-BKVNW6ya.mjs} +6 -6
  46. package/dist/{ShopScreen-DVYzIHJA.mjs → ShopScreen-CgN-6gic.mjs} +2 -2
  47. package/dist/{ShopScreen-DVYzIHJA.mjs.map → ShopScreen-CgN-6gic.mjs.map} +1 -1
  48. package/dist/{SubscriptionsScreen-BnRWR4Df.mjs → SubscriptionsScreen-CPiLzWhm.mjs} +7 -7
  49. package/dist/{SubscriptionsScreen-Dw68LWDh.cjs → SubscriptionsScreen-Dz6Y_1oD.cjs} +3 -3
  50. package/dist/{SubscriptionsScreen-Dw68LWDh.cjs.map → SubscriptionsScreen-Dz6Y_1oD.cjs.map} +1 -1
  51. package/dist/{SubscriptionsScreen-DqG_qHS_.mjs → SubscriptionsScreen-M2WaVHH7.mjs} +3 -3
  52. package/dist/{SubscriptionsScreen-DqG_qHS_.mjs.map → SubscriptionsScreen-M2WaVHH7.mjs.map} +1 -1
  53. package/dist/{SubscriptionsScreen-Day2TSrY.cjs → SubscriptionsScreen-dN42J-MR.cjs} +7 -7
  54. package/dist/{authoring-BlXhd4Sj.d.cts → authoring--rXPyZca.d.cts} +2 -2
  55. package/dist/{authoring-BlXhd4Sj.d.cts.map → authoring--rXPyZca.d.cts.map} +1 -1
  56. package/dist/{authoring-BMuHXNFy.d.mts → authoring-V0sXg_j4.d.mts} +2 -2
  57. package/dist/{authoring-BMuHXNFy.d.mts.map → authoring-V0sXg_j4.d.mts.map} +1 -1
  58. package/dist/index.cjs +22 -24
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +3 -8
  61. package/dist/index.d.cts.map +1 -1
  62. package/dist/index.d.mts +3 -9
  63. package/dist/index.d.mts.map +1 -1
  64. package/dist/index.mjs +22 -24
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/preview/preview-entry.cjs +6 -6
  67. package/dist/preview/preview-entry.d.cts +1 -1
  68. package/dist/preview/preview-entry.d.mts +1 -1
  69. package/dist/preview/preview-entry.mjs +6 -6
  70. package/dist/{use-portal-card-add-3ds-flow-hGLvDBZT.cjs → use-portal-card-add-3ds-flow-C0YUg213.cjs} +2 -2
  71. package/dist/{use-portal-card-add-3ds-flow-hGLvDBZT.cjs.map → use-portal-card-add-3ds-flow-C0YUg213.cjs.map} +1 -1
  72. package/dist/{use-portal-card-add-3ds-flow-FSWDlXtP.mjs → use-portal-card-add-3ds-flow-DKXraYw-.mjs} +2 -2
  73. package/dist/{use-portal-card-add-3ds-flow-FSWDlXtP.mjs.map → use-portal-card-add-3ds-flow-DKXraYw-.mjs.map} +1 -1
  74. package/dist/vite/builder-preview-entry.cjs +573 -0
  75. package/dist/vite/builder-preview-entry.cjs.map +1 -0
  76. package/dist/vite/builder-preview-entry.d.cts +11 -0
  77. package/dist/vite/builder-preview-entry.d.cts.map +1 -0
  78. package/dist/vite/builder-preview-entry.d.mts +11 -0
  79. package/dist/vite/builder-preview-entry.d.mts.map +1 -0
  80. package/dist/vite/builder-preview-entry.mjs +571 -0
  81. package/dist/vite/builder-preview-entry.mjs.map +1 -0
  82. package/dist/vite/index.cjs +16 -28
  83. package/dist/vite/index.cjs.map +1 -1
  84. package/dist/vite/index.d.cts.map +1 -1
  85. package/dist/vite/index.d.mts.map +1 -1
  86. package/dist/vite/index.mjs +16 -28
  87. package/dist/vite/index.mjs.map +1 -1
  88. package/dist/{widget-package-descriptor-BnVCE5Fz.d.cts → widget-package-descriptor-ByCz_FNh.d.cts} +177 -9
  89. package/dist/widget-package-descriptor-ByCz_FNh.d.cts.map +1 -0
  90. package/dist/{widget-package-descriptor-Dt8BkF85.d.mts → widget-package-descriptor-CPIPAdUO.d.mts} +177 -9
  91. package/dist/widget-package-descriptor-CPIPAdUO.d.mts.map +1 -0
  92. package/dist/widget-package-utils-BWn56ZFw.cjs +1553 -0
  93. package/dist/widget-package-utils-BWn56ZFw.cjs.map +1 -0
  94. package/dist/widget-package-utils-BdS0B0pI.mjs +1486 -0
  95. package/dist/widget-package-utils-BdS0B0pI.mjs.map +1 -0
  96. package/dist/widgets/authoring.d.cts +1 -1
  97. package/dist/widgets/authoring.d.mts +1 -1
  98. package/package.json +17 -21
  99. package/dist/widget-package-descriptor-BnVCE5Fz.d.cts.map +0 -1
  100. package/dist/widget-package-descriptor-Dt8BkF85.d.mts.map +0 -1
  101. package/dist/widget-package-utils-BGu5fWAU.cjs +0 -650
  102. package/dist/widget-package-utils-BGu5fWAU.cjs.map +0 -1
  103. package/dist/widget-package-utils-DzdFCK04.mjs +0 -582
  104. package/dist/widget-package-utils-DzdFCK04.mjs.map +0 -1
@@ -3,6 +3,7 @@ require("../chunk-9hOWP6kD.cjs");
3
3
  const require_authoring = require("../authoring-BBlBGYC5.cjs");
4
4
  const require_protocol = require("../protocol-DIE_XooL.cjs");
5
5
  let node_path = require("node:path");
6
+ let node_url = require("node:url");
6
7
  let node_fs_promises = require("node:fs/promises");
7
8
  let node_fs = require("node:fs");
8
9
  //#region src/vite/validate-manifest.ts
@@ -131,6 +132,7 @@ const RAW_HTML = `<!doctype html>
131
132
  function fluidBuilderPreviewPlugin() {
132
133
  let configPath;
133
134
  let cssPath;
135
+ let builderPreviewEntryUrl;
134
136
  return {
135
137
  name: "fluid-builder-preview",
136
138
  apply: "serve",
@@ -142,6 +144,7 @@ function fluidBuilderPreviewPlugin() {
142
144
  "src/styles/index.css",
143
145
  "index.css"
144
146
  ].find((c) => (0, node_fs.existsSync)((0, node_path.join)(root, c))) ?? "src/index.css";
147
+ builderPreviewEntryUrl = toViteFsPath(resolveBuilderPreviewEntryPath());
145
148
  },
146
149
  resolveId(id) {
147
150
  if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;
@@ -150,35 +153,9 @@ function fluidBuilderPreviewPlugin() {
150
153
  if (id === RESOLVED_VIRTUAL_ID) return `
151
154
  import "/${cssPath}";
152
155
  import * as portalConfig from "/${configPath}";
153
- import { createRoot } from "react-dom/client";
154
- import { createElement } from "react";
155
- import { BuilderPreviewApp } from "@fluid-app/portal-preview";
156
- import {
157
- isSourceWidgetPackage,
158
- sourceWidgetPackagesToManifests,
159
- } from "@fluid-app/portal-sdk";
156
+ import { renderBuilderPreview } from "${builderPreviewEntryUrl}";
160
157
 
161
- const sourcePackageExports = [];
162
- const seenSourcePackageIds = new Set();
163
- for (const candidate of [
164
- portalConfig.widgetPackage,
165
- ...(Array.isArray(portalConfig.widgetPackages) ? portalConfig.widgetPackages : []),
166
- portalConfig.default,
167
- ]) {
168
- if (!isSourceWidgetPackage(candidate) || seenSourcePackageIds.has(candidate.packageId)) continue;
169
- seenSourcePackageIds.add(candidate.packageId);
170
- sourcePackageExports.push(candidate);
171
- }
172
- const widgets = [
173
- ...(portalConfig.customWidgets || []),
174
- ...sourceWidgetPackagesToManifests(sourcePackageExports),
175
- ];
176
- const root = document.getElementById("builder-preview-root");
177
- if (root) {
178
- createRoot(root).render(
179
- createElement(BuilderPreviewApp, { widgets })
180
- );
181
- }
158
+ renderBuilderPreview(portalConfig);
182
159
  `;
183
160
  },
184
161
  configureServer(server) {
@@ -198,6 +175,17 @@ if (root) {
198
175
  }
199
176
  };
200
177
  }
178
+ function resolveBuilderPreviewEntryPath() {
179
+ const sourcePath = (0, node_url.fileURLToPath)(new URL("./builder-preview-entry.tsx", require("url").pathToFileURL(__filename).href));
180
+ if ((0, node_fs.existsSync)(sourcePath)) return sourcePath;
181
+ const builtPath = (0, node_url.fileURLToPath)(new URL("./builder-preview-entry.mjs", require("url").pathToFileURL(__filename).href));
182
+ if ((0, node_fs.existsSync)(builtPath)) return builtPath;
183
+ throw new Error(`[fluid-builder-preview] Could not locate builder-preview-entry. Expected source at ${sourcePath} or built output at ${builtPath}.`);
184
+ }
185
+ function toViteFsPath(path) {
186
+ const normalized = path.replace(/\\/g, "/");
187
+ return normalized.startsWith("/") ? `/@fs${normalized}` : `/@fs/${normalized}`;
188
+ }
201
189
  //#endregion
202
190
  //#region src/vite/manifest-plugin.ts
203
191
  const DEV_WIDGET_PACKAGE_ID = "company.local-portal-widgets";
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["sourceWidgetPackagesToDescriptors","sourceWidgetPackagesToManifests","isSourceWidgetPackage","readNonEmptyTrimmedString","isAllowedOrigin"],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/config-paths.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst WIDGET_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport function resolveWidgetConfigPath(root: string): string {\n return (\n WIDGET_CONFIG_CANDIDATES.find((candidate) =>\n existsSync(join(root, candidate)),\n ) ?? \"src/portal.config.ts\"\n );\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n configPath = resolveWidgetConfigPath(root);\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n} from \"@fluid-app/portal-sdk\";\n\nconst sourcePackageExports = [];\nconst seenSourcePackageIds = new Set();\nfor (const candidate of [\n portalConfig.widgetPackage,\n ...(Array.isArray(portalConfig.widgetPackages) ? portalConfig.widgetPackages : []),\n portalConfig.default,\n]) {\n if (!isSourceWidgetPackage(candidate) || seenSourcePackageIds.has(candidate.packageId)) continue;\n seenSourcePackageIds.add(candidate.packageId);\n sourcePackageExports.push(candidate);\n}\nconst widgets = [\n ...(portalConfig.customWidgets || []),\n ...sourceWidgetPackagesToManifests(sourcePackageExports),\n];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToDescriptors,\n sourceWidgetPackagesToManifests,\n type SourceWidgetPackage,\n} from \"../widgets/authoring\";\nimport { readNonEmptyTrimmedString } from \"../utils/read-non-empty-trimmed-string\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst DEV_WIDGET_PACKAGE_ID = \"company.local-portal-widgets\";\nconst DEV_WIDGET_PACKAGE_VERSION = \"0.0.0-dev\";\nconst DEV_WIDGET_PACKAGE_TYPE = \"company\";\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\n\ninterface LoadedWidgetConfig {\n manifests: Record<string, unknown>[];\n legacyManifests: Record<string, unknown>[];\n sourcePackages: SourceWidgetPackage[];\n cssUrls: string[];\n remoteEntryUrl: string | undefined;\n}\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n configPath = `/${resolveWidgetConfigPath(config.root)}`;\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n sendJson(res, serializable);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n\n server.middlewares.use(\"/__widget-packages__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const { legacyManifests, sourcePackages, cssUrls, remoteEntryUrl } =\n await loadWidgetConfig(server, server.config.logger, configPath, {\n includeWidgetPackageRemoteEntryUrl: true,\n });\n const descriptors = [\n ...manifestsToWidgetPackageDescriptors(legacyManifests, {\n cssUrls,\n remoteEntryUrl,\n }),\n ...sourceWidgetPackagesToDescriptors(sourcePackages, {\n cssUrls,\n remoteEntryUrl,\n }),\n ];\n\n sendJson(res, descriptors);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load widget package descriptors: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<Record<string, unknown>[]> {\n const { manifests } = await loadWidgetConfig(server, logger, configFilePath);\n return manifests;\n}\n\nasync function loadWidgetConfig(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n options: { includeWidgetPackageRemoteEntryUrl?: boolean } = {},\n): Promise<LoadedWidgetConfig> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets/widgetPackage exports.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return emptyLoadedWidgetConfig();\n }\n\n const sourcePackages = readSourceWidgetPackages(mod, logger);\n const legacyManifests = readLegacyCustomWidgets(\n mod,\n logger,\n configFilePath,\n sourcePackages.length > 0,\n );\n const sourceManifests = sourceWidgetPackagesToManifests(sourcePackages).map(\n widgetManifestToRecord,\n );\n const fullManifests = [...legacyManifests, ...sourceManifests];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of fullManifests) {\n const result = validateManifest(manifest);\n if (result.success === false) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return {\n manifests: stripComponents(fullManifests),\n legacyManifests: stripComponents(legacyManifests),\n sourcePackages,\n cssUrls: options.includeWidgetPackageRemoteEntryUrl\n ? readCssUrls(mod, logger)\n : [],\n remoteEntryUrl: options.includeWidgetPackageRemoteEntryUrl\n ? readWidgetPackageRemoteEntryUrl(mod, logger)\n : undefined,\n };\n}\n\nfunction emptyLoadedWidgetConfig(): LoadedWidgetConfig {\n return {\n manifests: [],\n legacyManifests: [],\n sourcePackages: [],\n cssUrls: [],\n remoteEntryUrl: undefined,\n };\n}\n\nfunction widgetManifestToRecord(\n manifest: WidgetManifest,\n): Record<string, unknown> {\n return {\n manifestVersion: manifest.manifestVersion,\n type: manifest.type,\n component: manifest.component,\n displayName: manifest.displayName,\n description: manifest.description,\n icon: manifest.icon,\n category: manifest.category,\n propertySchema: manifest.propertySchema,\n defaultProps: manifest.defaultProps,\n container: manifest.container,\n minSdkVersion: manifest.minSdkVersion,\n resizable: manifest.resizable,\n };\n}\n\nfunction readLegacyCustomWidgets(\n mod: Record<string, unknown>,\n logger: Logger | undefined,\n configFilePath: string | undefined,\n hasSourcePackages: boolean,\n): Record<string, unknown>[] {\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\") && !hasSourcePackages) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets or widgetPackage. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n return rawWidgets as Record<string, unknown>[];\n}\n\nexport function readSourceWidgetPackages(\n mod: Record<string, unknown>,\n logger?: Pick<Logger, \"warn\">,\n): SourceWidgetPackage[] {\n const packages: SourceWidgetPackage[] = [];\n\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackage,\n \"widgetPackage\",\n logger,\n );\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackages,\n \"widgetPackages\",\n logger,\n );\n collectSourceWidgetPackageExport(packages, mod.default, \"default\", logger, {\n warnOnInvalid: false,\n });\n\n const byPackageId = new Map<string, SourceWidgetPackage>();\n for (const sourcePackage of packages) {\n if (byPackageId.has(sourcePackage.packageId)) {\n logger?.warn(\n `[fluid] Duplicate widget package \"${sourcePackage.packageId}\" found. Keeping the first export.`,\n );\n continue;\n }\n byPackageId.set(sourcePackage.packageId, sourcePackage);\n }\n\n return Array.from(byPackageId.values());\n}\n\nfunction collectSourceWidgetPackageExport(\n packages: SourceWidgetPackage[],\n value: unknown,\n exportName: string,\n logger: Pick<Logger, \"warn\"> | undefined,\n options: { warnOnInvalid?: boolean } = {},\n): void {\n if (value === undefined) return;\n\n if (isSourceWidgetPackage(value)) {\n packages.push(value);\n return;\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (isSourceWidgetPackage(entry)) {\n packages.push(entry);\n continue;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} contains a non-source widget package entry. Skipping entry.`,\n );\n }\n }\n return;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} export is not a source widget package or array. Skipping source packages.`,\n );\n }\n}\n\nfunction stripComponents(\n manifests: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n\nexport function manifestsToWidgetPackageDescriptors(\n manifests: readonly Record<string, unknown>[],\n options: {\n cssUrls?: readonly string[];\n remoteEntryUrl?: string;\n } = {},\n): WidgetPackageDescriptor[] {\n const remoteEntryUrl = readNonEmptyTrimmedString(options.remoteEntryUrl);\n if (manifests.length === 0 || !remoteEntryUrl) return [];\n\n const descriptor = {\n manifestVersion: WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION,\n packageId: DEV_WIDGET_PACKAGE_ID,\n packageType: DEV_WIDGET_PACKAGE_TYPE,\n version: DEV_WIDGET_PACKAGE_VERSION,\n remoteEntryUrl,\n cssUrls: options.cssUrls ?? [],\n widgets: manifests.map(manifestToWidgetDescriptor),\n } satisfies WidgetPackageDescriptor;\n\n return [descriptor];\n}\n\nfunction manifestToWidgetDescriptor(\n manifest: Record<string, unknown>,\n index: number,\n): WidgetDescriptor {\n const type =\n readNonEmptyString(manifest.type) ?? `custom-widget-${index + 1}`;\n const displayName = readNonEmptyString(manifest.displayName) ?? type;\n\n return {\n type,\n name: type,\n displayName,\n description:\n readNonEmptyString(manifest.description) ??\n `Custom widget ${displayName}`,\n icon: readNonEmptyString(manifest.icon) ?? \"box\",\n category: readNonEmptyString(manifest.category) ?? \"components\",\n propertySchema: normalizePropertySchema(manifest.propertySchema, type),\n defaultProps: isObjectRecord(manifest.defaultProps)\n ? manifest.defaultProps\n : {},\n container: normalizeContainer(manifest.container),\n minSdkVersion: readNonEmptyString(manifest.minSdkVersion) ?? \"0.0.0\",\n resizable: normalizeResizable(manifest.resizable),\n };\n}\n\n/**\n * Runtime package descriptors must point to an actually served classic-script\n * remote entry. Local SDK legacy widgets continue to use the legacy\n * /__manifests__ + /__preview__ flow, so legacy /__widget-packages__ output\n * stays empty unless the widget config explicitly exports a non-empty\n * `widgetPackageRemoteEntryUrl` string. Source packages authored with\n * defineWidgetPackage still contribute local preview/manifests, but are emitted\n * as runtime descriptors only when the package or widget config explicitly\n * provides a remote entry URL.\n */\nfunction readWidgetPackageRemoteEntryUrl(\n mod: Record<string, unknown>,\n logger?: Logger,\n): string | undefined {\n const rawRemoteEntryUrl = mod.widgetPackageRemoteEntryUrl;\n if (rawRemoteEntryUrl === undefined) return undefined;\n\n const remoteEntryUrl = readNonEmptyTrimmedString(rawRemoteEntryUrl);\n if (!remoteEntryUrl) {\n logger?.warn(\n `[fluid] widgetPackageRemoteEntryUrl export must be a non-empty string. ` +\n `Legacy customWidgets will not be included in /__widget-packages__.`,\n );\n return undefined;\n }\n\n return remoteEntryUrl;\n}\n\nfunction readCssUrls(mod: Record<string, unknown>, logger?: Logger): string[] {\n const rawCssUrls = mod.widgetPackageCssUrls;\n if (rawCssUrls === undefined) return [];\n\n if (!Array.isArray(rawCssUrls)) {\n logger?.warn(\n `[fluid] widget package cssUrls export is not an array (got ${typeof rawCssUrls}). Skipping CSS URLs.`,\n );\n return [];\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of rawCssUrls) {\n const value = readNonEmptyString(cssUrl);\n if (value) cssUrls.push(value);\n }\n return cssUrls;\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isObjectRecord(value)) return { widgetType };\n return { ...value, widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return \"block\";\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isObjectRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction handleDevCors(\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n): boolean {\n setDevCorsHeaders(res);\n\n if (req.method !== \"OPTIONS\") return false;\n\n res.statusCode = 204;\n res.end();\n return true;\n}\n\nfunction setDevCorsHeaders(res: import(\"node:http\").ServerResponse): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n}\n\nfunction sendJson(\n res: import(\"node:http\").ServerResponse,\n body: unknown,\n statusCode = 200,\n): void {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = statusCode;\n res.end(JSON.stringify(body));\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: adds preview.html as a second Rollup input so `vite build`\n * produces it alongside index.html for production deployment.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n config(_config: UserConfig, { command }) {\n if (command !== \"build\") return;\n\n // config() runs before configResolved, so use process.cwd()\n // which is the project root when invoked via `vite build`.\n const projectRoot = process.cwd();\n const previewHtml = resolve(projectRoot, \"preview.html\");\n if (!existsSync(previewHtml)) return;\n\n // Add preview.html as a second Rollup entry point\n return {\n build: {\n rollupOptions: {\n input: {\n main: resolve(projectRoot, \"index.html\"),\n preview: previewHtml,\n },\n },\n },\n };\n },\n\n // vite preview: static file server with no rewrite rules.\n // Map /__preview__ → preview.html and /__manifests__ → __manifests__.json\n // so local production testing works the same as nginx in production.\n configurePreviewServer(server) {\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const html = await readFile(\n resolve(distDir, \"preview.html\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch {\n res.statusCode = 404;\n res.end(\"preview.html not found in build output\");\n }\n });\n\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const json = await readFile(\n resolve(distDir, \"__manifests__.json\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.statusCode = 200;\n res.end(json);\n } catch {\n res.statusCode = 404;\n res.end(\"__manifests__.json not found in build output\");\n }\n });\n },\n\n // vite dev: serve /__preview__ with Vite's HTML transform pipeline\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["sourceWidgetPackagesToDescriptors","sourceWidgetPackagesToManifests","isSourceWidgetPackage","readNonEmptyTrimmedString","isAllowedOrigin"],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/config-paths.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst WIDGET_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport function resolveWidgetConfigPath(root: string): string {\n return (\n WIDGET_CONFIG_CANDIDATES.find((candidate) =>\n existsSync(join(root, candidate)),\n ) ?? \"src/portal.config.ts\"\n );\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n let builderPreviewEntryUrl: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n configPath = resolveWidgetConfigPath(root);\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n builderPreviewEntryUrl = toViteFsPath(resolveBuilderPreviewEntryPath());\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { renderBuilderPreview } from \"${builderPreviewEntryUrl}\";\n\nrenderBuilderPreview(portalConfig);\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction resolveBuilderPreviewEntryPath(): string {\n const sourcePath = fileURLToPath(\n new URL(\"./builder-preview-entry.tsx\", import.meta.url),\n );\n\n if (existsSync(sourcePath)) return sourcePath;\n\n const builtPath = fileURLToPath(\n new URL(\"./builder-preview-entry.mjs\", import.meta.url),\n );\n if (existsSync(builtPath)) return builtPath;\n\n throw new Error(\n `[fluid-builder-preview] Could not locate builder-preview-entry. ` +\n `Expected source at ${sourcePath} or built output at ${builtPath}.`,\n );\n}\n\nfunction toViteFsPath(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n return normalized.startsWith(\"/\")\n ? `/@fs${normalized}`\n : `/@fs/${normalized}`;\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToDescriptors,\n sourceWidgetPackagesToManifests,\n type SourceWidgetPackage,\n} from \"../widgets/authoring\";\nimport { readNonEmptyTrimmedString } from \"../utils/read-non-empty-trimmed-string\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst DEV_WIDGET_PACKAGE_ID = \"company.local-portal-widgets\";\nconst DEV_WIDGET_PACKAGE_VERSION = \"0.0.0-dev\";\nconst DEV_WIDGET_PACKAGE_TYPE = \"company\";\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\n\ninterface LoadedWidgetConfig {\n manifests: Record<string, unknown>[];\n legacyManifests: Record<string, unknown>[];\n sourcePackages: SourceWidgetPackage[];\n cssUrls: string[];\n remoteEntryUrl: string | undefined;\n}\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n configPath = `/${resolveWidgetConfigPath(config.root)}`;\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n sendJson(res, serializable);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n\n server.middlewares.use(\"/__widget-packages__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const { legacyManifests, sourcePackages, cssUrls, remoteEntryUrl } =\n await loadWidgetConfig(server, server.config.logger, configPath, {\n includeWidgetPackageRemoteEntryUrl: true,\n });\n const descriptors = [\n ...manifestsToWidgetPackageDescriptors(legacyManifests, {\n cssUrls,\n remoteEntryUrl,\n }),\n ...sourceWidgetPackagesToDescriptors(sourcePackages, {\n cssUrls,\n remoteEntryUrl,\n }),\n ];\n\n sendJson(res, descriptors);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load widget package descriptors: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<Record<string, unknown>[]> {\n const { manifests } = await loadWidgetConfig(server, logger, configFilePath);\n return manifests;\n}\n\nasync function loadWidgetConfig(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n options: { includeWidgetPackageRemoteEntryUrl?: boolean } = {},\n): Promise<LoadedWidgetConfig> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets/widgetPackage exports.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return emptyLoadedWidgetConfig();\n }\n\n const sourcePackages = readSourceWidgetPackages(mod, logger);\n const legacyManifests = readLegacyCustomWidgets(\n mod,\n logger,\n configFilePath,\n sourcePackages.length > 0,\n );\n const sourceManifests = sourceWidgetPackagesToManifests(sourcePackages).map(\n widgetManifestToRecord,\n );\n const fullManifests = [...legacyManifests, ...sourceManifests];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of fullManifests) {\n const result = validateManifest(manifest);\n if (result.success === false) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return {\n manifests: stripComponents(fullManifests),\n legacyManifests: stripComponents(legacyManifests),\n sourcePackages,\n cssUrls: options.includeWidgetPackageRemoteEntryUrl\n ? readCssUrls(mod, logger)\n : [],\n remoteEntryUrl: options.includeWidgetPackageRemoteEntryUrl\n ? readWidgetPackageRemoteEntryUrl(mod, logger)\n : undefined,\n };\n}\n\nfunction emptyLoadedWidgetConfig(): LoadedWidgetConfig {\n return {\n manifests: [],\n legacyManifests: [],\n sourcePackages: [],\n cssUrls: [],\n remoteEntryUrl: undefined,\n };\n}\n\nfunction widgetManifestToRecord(\n manifest: WidgetManifest,\n): Record<string, unknown> {\n return {\n manifestVersion: manifest.manifestVersion,\n type: manifest.type,\n component: manifest.component,\n displayName: manifest.displayName,\n description: manifest.description,\n icon: manifest.icon,\n category: manifest.category,\n propertySchema: manifest.propertySchema,\n defaultProps: manifest.defaultProps,\n container: manifest.container,\n minSdkVersion: manifest.minSdkVersion,\n resizable: manifest.resizable,\n };\n}\n\nfunction readLegacyCustomWidgets(\n mod: Record<string, unknown>,\n logger: Logger | undefined,\n configFilePath: string | undefined,\n hasSourcePackages: boolean,\n): Record<string, unknown>[] {\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\") && !hasSourcePackages) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets or widgetPackage. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n return rawWidgets as Record<string, unknown>[];\n}\n\nexport function readSourceWidgetPackages(\n mod: Record<string, unknown>,\n logger?: Pick<Logger, \"warn\">,\n): SourceWidgetPackage[] {\n const packages: SourceWidgetPackage[] = [];\n\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackage,\n \"widgetPackage\",\n logger,\n );\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackages,\n \"widgetPackages\",\n logger,\n );\n collectSourceWidgetPackageExport(packages, mod.default, \"default\", logger, {\n warnOnInvalid: false,\n });\n\n const byPackageId = new Map<string, SourceWidgetPackage>();\n for (const sourcePackage of packages) {\n if (byPackageId.has(sourcePackage.packageId)) {\n logger?.warn(\n `[fluid] Duplicate widget package \"${sourcePackage.packageId}\" found. Keeping the first export.`,\n );\n continue;\n }\n byPackageId.set(sourcePackage.packageId, sourcePackage);\n }\n\n return Array.from(byPackageId.values());\n}\n\nfunction collectSourceWidgetPackageExport(\n packages: SourceWidgetPackage[],\n value: unknown,\n exportName: string,\n logger: Pick<Logger, \"warn\"> | undefined,\n options: { warnOnInvalid?: boolean } = {},\n): void {\n if (value === undefined) return;\n\n if (isSourceWidgetPackage(value)) {\n packages.push(value);\n return;\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (isSourceWidgetPackage(entry)) {\n packages.push(entry);\n continue;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} contains a non-source widget package entry. Skipping entry.`,\n );\n }\n }\n return;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} export is not a source widget package or array. Skipping source packages.`,\n );\n }\n}\n\nfunction stripComponents(\n manifests: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n\nexport function manifestsToWidgetPackageDescriptors(\n manifests: readonly Record<string, unknown>[],\n options: {\n cssUrls?: readonly string[];\n remoteEntryUrl?: string;\n } = {},\n): WidgetPackageDescriptor[] {\n const remoteEntryUrl = readNonEmptyTrimmedString(options.remoteEntryUrl);\n if (manifests.length === 0 || !remoteEntryUrl) return [];\n\n const descriptor = {\n manifestVersion: WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION,\n packageId: DEV_WIDGET_PACKAGE_ID,\n packageType: DEV_WIDGET_PACKAGE_TYPE,\n version: DEV_WIDGET_PACKAGE_VERSION,\n remoteEntryUrl,\n cssUrls: options.cssUrls ?? [],\n widgets: manifests.map(manifestToWidgetDescriptor),\n } satisfies WidgetPackageDescriptor;\n\n return [descriptor];\n}\n\nfunction manifestToWidgetDescriptor(\n manifest: Record<string, unknown>,\n index: number,\n): WidgetDescriptor {\n const type =\n readNonEmptyString(manifest.type) ?? `custom-widget-${index + 1}`;\n const displayName = readNonEmptyString(manifest.displayName) ?? type;\n\n return {\n type,\n name: type,\n displayName,\n description:\n readNonEmptyString(manifest.description) ??\n `Custom widget ${displayName}`,\n icon: readNonEmptyString(manifest.icon) ?? \"box\",\n category: readNonEmptyString(manifest.category) ?? \"components\",\n propertySchema: normalizePropertySchema(manifest.propertySchema, type),\n defaultProps: isObjectRecord(manifest.defaultProps)\n ? manifest.defaultProps\n : {},\n container: normalizeContainer(manifest.container),\n minSdkVersion: readNonEmptyString(manifest.minSdkVersion) ?? \"0.0.0\",\n resizable: normalizeResizable(manifest.resizable),\n };\n}\n\n/**\n * Runtime package descriptors must point to an actually served classic-script\n * remote entry. Local SDK legacy widgets continue to use the legacy\n * /__manifests__ + /__preview__ flow, so legacy /__widget-packages__ output\n * stays empty unless the widget config explicitly exports a non-empty\n * `widgetPackageRemoteEntryUrl` string. Source packages authored with\n * defineWidgetPackage still contribute local preview/manifests, but are emitted\n * as runtime descriptors only when the package or widget config explicitly\n * provides a remote entry URL.\n */\nfunction readWidgetPackageRemoteEntryUrl(\n mod: Record<string, unknown>,\n logger?: Logger,\n): string | undefined {\n const rawRemoteEntryUrl = mod.widgetPackageRemoteEntryUrl;\n if (rawRemoteEntryUrl === undefined) return undefined;\n\n const remoteEntryUrl = readNonEmptyTrimmedString(rawRemoteEntryUrl);\n if (!remoteEntryUrl) {\n logger?.warn(\n `[fluid] widgetPackageRemoteEntryUrl export must be a non-empty string. ` +\n `Legacy customWidgets will not be included in /__widget-packages__.`,\n );\n return undefined;\n }\n\n return remoteEntryUrl;\n}\n\nfunction readCssUrls(mod: Record<string, unknown>, logger?: Logger): string[] {\n const rawCssUrls = mod.widgetPackageCssUrls;\n if (rawCssUrls === undefined) return [];\n\n if (!Array.isArray(rawCssUrls)) {\n logger?.warn(\n `[fluid] widget package cssUrls export is not an array (got ${typeof rawCssUrls}). Skipping CSS URLs.`,\n );\n return [];\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of rawCssUrls) {\n const value = readNonEmptyString(cssUrl);\n if (value) cssUrls.push(value);\n }\n return cssUrls;\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isObjectRecord(value)) return { widgetType };\n return { ...value, widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return \"block\";\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isObjectRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction handleDevCors(\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n): boolean {\n setDevCorsHeaders(res);\n\n if (req.method !== \"OPTIONS\") return false;\n\n res.statusCode = 204;\n res.end();\n return true;\n}\n\nfunction setDevCorsHeaders(res: import(\"node:http\").ServerResponse): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n}\n\nfunction sendJson(\n res: import(\"node:http\").ServerResponse,\n body: unknown,\n statusCode = 200,\n): void {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = statusCode;\n res.end(JSON.stringify(body));\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: adds preview.html as a second Rollup input so `vite build`\n * produces it alongside index.html for production deployment.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n config(_config: UserConfig, { command }) {\n if (command !== \"build\") return;\n\n // config() runs before configResolved, so use process.cwd()\n // which is the project root when invoked via `vite build`.\n const projectRoot = process.cwd();\n const previewHtml = resolve(projectRoot, \"preview.html\");\n if (!existsSync(previewHtml)) return;\n\n // Add preview.html as a second Rollup entry point\n return {\n build: {\n rollupOptions: {\n input: {\n main: resolve(projectRoot, \"index.html\"),\n preview: previewHtml,\n },\n },\n },\n };\n },\n\n // vite preview: static file server with no rewrite rules.\n // Map /__preview__ → preview.html and /__manifests__ → __manifests__.json\n // so local production testing works the same as nginx in production.\n configurePreviewServer(server) {\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const html = await readFile(\n resolve(distDir, \"preview.html\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch {\n res.statusCode = 404;\n res.end(\"preview.html not found in build output\");\n }\n });\n\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const json = await readFile(\n resolve(distDir, \"__manifests__.json\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.statusCode = 200;\n res.end(json);\n } catch {\n res.statusCode = 404;\n res.end(\"__manifests__.json not found in build output\");\n }\n });\n },\n\n // vite dev: serve /__preview__ with Vite's HTML transform pipeline\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AA0CA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;;;AAAvC;;iBCZgB,yBAAA,CAAA,GAA6B,MAAA;;;;;;;ADY7C;;;;;iBE3BgB,kBAAA,CAAA,GAAsB,MAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AA0CA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;;;AAAvC;;iBCXgB,yBAAA,CAAA,GAA6B,MAAA;;;;;;;ADW7C;;;;;iBE3BgB,kBAAA,CAAA,GAAsB,MAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AA0CA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;;;AAAvC;;iBCZgB,yBAAA,CAAA,GAA6B,MAAA;;;;;;;ADY7C;;;;;iBE3BgB,kBAAA,CAAA,GAAsB,MAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AA0CA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;;;AAAvC;;iBCXgB,yBAAA,CAAA,GAA6B,MAAA;;;;;;;ADW7C;;;;;iBE3BgB,kBAAA,CAAA,GAAsB,MAAA"}
@@ -2,6 +2,7 @@ import { c as sourceWidgetPackagesToManifests, i as isSourceWidgetPackage, l as
2
2
  import { t as isAllowedOrigin } from "../protocol-D5N3pIHL.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { readFile } from "node:fs/promises";
6
7
  //#region src/vite/validate-manifest.ts
7
8
  /**
@@ -129,6 +130,7 @@ const RAW_HTML = `<!doctype html>
129
130
  function fluidBuilderPreviewPlugin() {
130
131
  let configPath;
131
132
  let cssPath;
133
+ let builderPreviewEntryUrl;
132
134
  return {
133
135
  name: "fluid-builder-preview",
134
136
  apply: "serve",
@@ -140,6 +142,7 @@ function fluidBuilderPreviewPlugin() {
140
142
  "src/styles/index.css",
141
143
  "index.css"
142
144
  ].find((c) => existsSync(join(root, c))) ?? "src/index.css";
145
+ builderPreviewEntryUrl = toViteFsPath(resolveBuilderPreviewEntryPath());
143
146
  },
144
147
  resolveId(id) {
145
148
  if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;
@@ -148,35 +151,9 @@ function fluidBuilderPreviewPlugin() {
148
151
  if (id === RESOLVED_VIRTUAL_ID) return `
149
152
  import "/${cssPath}";
150
153
  import * as portalConfig from "/${configPath}";
151
- import { createRoot } from "react-dom/client";
152
- import { createElement } from "react";
153
- import { BuilderPreviewApp } from "@fluid-app/portal-preview";
154
- import {
155
- isSourceWidgetPackage,
156
- sourceWidgetPackagesToManifests,
157
- } from "@fluid-app/portal-sdk";
154
+ import { renderBuilderPreview } from "${builderPreviewEntryUrl}";
158
155
 
159
- const sourcePackageExports = [];
160
- const seenSourcePackageIds = new Set();
161
- for (const candidate of [
162
- portalConfig.widgetPackage,
163
- ...(Array.isArray(portalConfig.widgetPackages) ? portalConfig.widgetPackages : []),
164
- portalConfig.default,
165
- ]) {
166
- if (!isSourceWidgetPackage(candidate) || seenSourcePackageIds.has(candidate.packageId)) continue;
167
- seenSourcePackageIds.add(candidate.packageId);
168
- sourcePackageExports.push(candidate);
169
- }
170
- const widgets = [
171
- ...(portalConfig.customWidgets || []),
172
- ...sourceWidgetPackagesToManifests(sourcePackageExports),
173
- ];
174
- const root = document.getElementById("builder-preview-root");
175
- if (root) {
176
- createRoot(root).render(
177
- createElement(BuilderPreviewApp, { widgets })
178
- );
179
- }
156
+ renderBuilderPreview(portalConfig);
180
157
  `;
181
158
  },
182
159
  configureServer(server) {
@@ -196,6 +173,17 @@ if (root) {
196
173
  }
197
174
  };
198
175
  }
176
+ function resolveBuilderPreviewEntryPath() {
177
+ const sourcePath = fileURLToPath(new URL("./builder-preview-entry.tsx", import.meta.url));
178
+ if (existsSync(sourcePath)) return sourcePath;
179
+ const builtPath = fileURLToPath(new URL("./builder-preview-entry.mjs", import.meta.url));
180
+ if (existsSync(builtPath)) return builtPath;
181
+ throw new Error(`[fluid-builder-preview] Could not locate builder-preview-entry. Expected source at ${sourcePath} or built output at ${builtPath}.`);
182
+ }
183
+ function toViteFsPath(path) {
184
+ const normalized = path.replace(/\\/g, "/");
185
+ return normalized.startsWith("/") ? `/@fs${normalized}` : `/@fs/${normalized}`;
186
+ }
199
187
  //#endregion
200
188
  //#region src/vite/manifest-plugin.ts
201
189
  const DEV_WIDGET_PACKAGE_ID = "company.local-portal-widgets";
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/config-paths.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst WIDGET_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport function resolveWidgetConfigPath(root: string): string {\n return (\n WIDGET_CONFIG_CANDIDATES.find((candidate) =>\n existsSync(join(root, candidate)),\n ) ?? \"src/portal.config.ts\"\n );\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n configPath = resolveWidgetConfigPath(root);\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n} from \"@fluid-app/portal-sdk\";\n\nconst sourcePackageExports = [];\nconst seenSourcePackageIds = new Set();\nfor (const candidate of [\n portalConfig.widgetPackage,\n ...(Array.isArray(portalConfig.widgetPackages) ? portalConfig.widgetPackages : []),\n portalConfig.default,\n]) {\n if (!isSourceWidgetPackage(candidate) || seenSourcePackageIds.has(candidate.packageId)) continue;\n seenSourcePackageIds.add(candidate.packageId);\n sourcePackageExports.push(candidate);\n}\nconst widgets = [\n ...(portalConfig.customWidgets || []),\n ...sourceWidgetPackagesToManifests(sourcePackageExports),\n];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToDescriptors,\n sourceWidgetPackagesToManifests,\n type SourceWidgetPackage,\n} from \"../widgets/authoring\";\nimport { readNonEmptyTrimmedString } from \"../utils/read-non-empty-trimmed-string\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst DEV_WIDGET_PACKAGE_ID = \"company.local-portal-widgets\";\nconst DEV_WIDGET_PACKAGE_VERSION = \"0.0.0-dev\";\nconst DEV_WIDGET_PACKAGE_TYPE = \"company\";\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\n\ninterface LoadedWidgetConfig {\n manifests: Record<string, unknown>[];\n legacyManifests: Record<string, unknown>[];\n sourcePackages: SourceWidgetPackage[];\n cssUrls: string[];\n remoteEntryUrl: string | undefined;\n}\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n configPath = `/${resolveWidgetConfigPath(config.root)}`;\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n sendJson(res, serializable);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n\n server.middlewares.use(\"/__widget-packages__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const { legacyManifests, sourcePackages, cssUrls, remoteEntryUrl } =\n await loadWidgetConfig(server, server.config.logger, configPath, {\n includeWidgetPackageRemoteEntryUrl: true,\n });\n const descriptors = [\n ...manifestsToWidgetPackageDescriptors(legacyManifests, {\n cssUrls,\n remoteEntryUrl,\n }),\n ...sourceWidgetPackagesToDescriptors(sourcePackages, {\n cssUrls,\n remoteEntryUrl,\n }),\n ];\n\n sendJson(res, descriptors);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load widget package descriptors: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<Record<string, unknown>[]> {\n const { manifests } = await loadWidgetConfig(server, logger, configFilePath);\n return manifests;\n}\n\nasync function loadWidgetConfig(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n options: { includeWidgetPackageRemoteEntryUrl?: boolean } = {},\n): Promise<LoadedWidgetConfig> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets/widgetPackage exports.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return emptyLoadedWidgetConfig();\n }\n\n const sourcePackages = readSourceWidgetPackages(mod, logger);\n const legacyManifests = readLegacyCustomWidgets(\n mod,\n logger,\n configFilePath,\n sourcePackages.length > 0,\n );\n const sourceManifests = sourceWidgetPackagesToManifests(sourcePackages).map(\n widgetManifestToRecord,\n );\n const fullManifests = [...legacyManifests, ...sourceManifests];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of fullManifests) {\n const result = validateManifest(manifest);\n if (result.success === false) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return {\n manifests: stripComponents(fullManifests),\n legacyManifests: stripComponents(legacyManifests),\n sourcePackages,\n cssUrls: options.includeWidgetPackageRemoteEntryUrl\n ? readCssUrls(mod, logger)\n : [],\n remoteEntryUrl: options.includeWidgetPackageRemoteEntryUrl\n ? readWidgetPackageRemoteEntryUrl(mod, logger)\n : undefined,\n };\n}\n\nfunction emptyLoadedWidgetConfig(): LoadedWidgetConfig {\n return {\n manifests: [],\n legacyManifests: [],\n sourcePackages: [],\n cssUrls: [],\n remoteEntryUrl: undefined,\n };\n}\n\nfunction widgetManifestToRecord(\n manifest: WidgetManifest,\n): Record<string, unknown> {\n return {\n manifestVersion: manifest.manifestVersion,\n type: manifest.type,\n component: manifest.component,\n displayName: manifest.displayName,\n description: manifest.description,\n icon: manifest.icon,\n category: manifest.category,\n propertySchema: manifest.propertySchema,\n defaultProps: manifest.defaultProps,\n container: manifest.container,\n minSdkVersion: manifest.minSdkVersion,\n resizable: manifest.resizable,\n };\n}\n\nfunction readLegacyCustomWidgets(\n mod: Record<string, unknown>,\n logger: Logger | undefined,\n configFilePath: string | undefined,\n hasSourcePackages: boolean,\n): Record<string, unknown>[] {\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\") && !hasSourcePackages) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets or widgetPackage. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n return rawWidgets as Record<string, unknown>[];\n}\n\nexport function readSourceWidgetPackages(\n mod: Record<string, unknown>,\n logger?: Pick<Logger, \"warn\">,\n): SourceWidgetPackage[] {\n const packages: SourceWidgetPackage[] = [];\n\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackage,\n \"widgetPackage\",\n logger,\n );\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackages,\n \"widgetPackages\",\n logger,\n );\n collectSourceWidgetPackageExport(packages, mod.default, \"default\", logger, {\n warnOnInvalid: false,\n });\n\n const byPackageId = new Map<string, SourceWidgetPackage>();\n for (const sourcePackage of packages) {\n if (byPackageId.has(sourcePackage.packageId)) {\n logger?.warn(\n `[fluid] Duplicate widget package \"${sourcePackage.packageId}\" found. Keeping the first export.`,\n );\n continue;\n }\n byPackageId.set(sourcePackage.packageId, sourcePackage);\n }\n\n return Array.from(byPackageId.values());\n}\n\nfunction collectSourceWidgetPackageExport(\n packages: SourceWidgetPackage[],\n value: unknown,\n exportName: string,\n logger: Pick<Logger, \"warn\"> | undefined,\n options: { warnOnInvalid?: boolean } = {},\n): void {\n if (value === undefined) return;\n\n if (isSourceWidgetPackage(value)) {\n packages.push(value);\n return;\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (isSourceWidgetPackage(entry)) {\n packages.push(entry);\n continue;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} contains a non-source widget package entry. Skipping entry.`,\n );\n }\n }\n return;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} export is not a source widget package or array. Skipping source packages.`,\n );\n }\n}\n\nfunction stripComponents(\n manifests: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n\nexport function manifestsToWidgetPackageDescriptors(\n manifests: readonly Record<string, unknown>[],\n options: {\n cssUrls?: readonly string[];\n remoteEntryUrl?: string;\n } = {},\n): WidgetPackageDescriptor[] {\n const remoteEntryUrl = readNonEmptyTrimmedString(options.remoteEntryUrl);\n if (manifests.length === 0 || !remoteEntryUrl) return [];\n\n const descriptor = {\n manifestVersion: WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION,\n packageId: DEV_WIDGET_PACKAGE_ID,\n packageType: DEV_WIDGET_PACKAGE_TYPE,\n version: DEV_WIDGET_PACKAGE_VERSION,\n remoteEntryUrl,\n cssUrls: options.cssUrls ?? [],\n widgets: manifests.map(manifestToWidgetDescriptor),\n } satisfies WidgetPackageDescriptor;\n\n return [descriptor];\n}\n\nfunction manifestToWidgetDescriptor(\n manifest: Record<string, unknown>,\n index: number,\n): WidgetDescriptor {\n const type =\n readNonEmptyString(manifest.type) ?? `custom-widget-${index + 1}`;\n const displayName = readNonEmptyString(manifest.displayName) ?? type;\n\n return {\n type,\n name: type,\n displayName,\n description:\n readNonEmptyString(manifest.description) ??\n `Custom widget ${displayName}`,\n icon: readNonEmptyString(manifest.icon) ?? \"box\",\n category: readNonEmptyString(manifest.category) ?? \"components\",\n propertySchema: normalizePropertySchema(manifest.propertySchema, type),\n defaultProps: isObjectRecord(manifest.defaultProps)\n ? manifest.defaultProps\n : {},\n container: normalizeContainer(manifest.container),\n minSdkVersion: readNonEmptyString(manifest.minSdkVersion) ?? \"0.0.0\",\n resizable: normalizeResizable(manifest.resizable),\n };\n}\n\n/**\n * Runtime package descriptors must point to an actually served classic-script\n * remote entry. Local SDK legacy widgets continue to use the legacy\n * /__manifests__ + /__preview__ flow, so legacy /__widget-packages__ output\n * stays empty unless the widget config explicitly exports a non-empty\n * `widgetPackageRemoteEntryUrl` string. Source packages authored with\n * defineWidgetPackage still contribute local preview/manifests, but are emitted\n * as runtime descriptors only when the package or widget config explicitly\n * provides a remote entry URL.\n */\nfunction readWidgetPackageRemoteEntryUrl(\n mod: Record<string, unknown>,\n logger?: Logger,\n): string | undefined {\n const rawRemoteEntryUrl = mod.widgetPackageRemoteEntryUrl;\n if (rawRemoteEntryUrl === undefined) return undefined;\n\n const remoteEntryUrl = readNonEmptyTrimmedString(rawRemoteEntryUrl);\n if (!remoteEntryUrl) {\n logger?.warn(\n `[fluid] widgetPackageRemoteEntryUrl export must be a non-empty string. ` +\n `Legacy customWidgets will not be included in /__widget-packages__.`,\n );\n return undefined;\n }\n\n return remoteEntryUrl;\n}\n\nfunction readCssUrls(mod: Record<string, unknown>, logger?: Logger): string[] {\n const rawCssUrls = mod.widgetPackageCssUrls;\n if (rawCssUrls === undefined) return [];\n\n if (!Array.isArray(rawCssUrls)) {\n logger?.warn(\n `[fluid] widget package cssUrls export is not an array (got ${typeof rawCssUrls}). Skipping CSS URLs.`,\n );\n return [];\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of rawCssUrls) {\n const value = readNonEmptyString(cssUrl);\n if (value) cssUrls.push(value);\n }\n return cssUrls;\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isObjectRecord(value)) return { widgetType };\n return { ...value, widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return \"block\";\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isObjectRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction handleDevCors(\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n): boolean {\n setDevCorsHeaders(res);\n\n if (req.method !== \"OPTIONS\") return false;\n\n res.statusCode = 204;\n res.end();\n return true;\n}\n\nfunction setDevCorsHeaders(res: import(\"node:http\").ServerResponse): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n}\n\nfunction sendJson(\n res: import(\"node:http\").ServerResponse,\n body: unknown,\n statusCode = 200,\n): void {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = statusCode;\n res.end(JSON.stringify(body));\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: adds preview.html as a second Rollup input so `vite build`\n * produces it alongside index.html for production deployment.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n config(_config: UserConfig, { command }) {\n if (command !== \"build\") return;\n\n // config() runs before configResolved, so use process.cwd()\n // which is the project root when invoked via `vite build`.\n const projectRoot = process.cwd();\n const previewHtml = resolve(projectRoot, \"preview.html\");\n if (!existsSync(previewHtml)) return;\n\n // Add preview.html as a second Rollup entry point\n return {\n build: {\n rollupOptions: {\n input: {\n main: resolve(projectRoot, \"index.html\"),\n preview: previewHtml,\n },\n },\n },\n };\n },\n\n // vite preview: static file server with no rewrite rules.\n // Map /__preview__ → preview.html and /__manifests__ → __manifests__.json\n // so local production testing works the same as nginx in production.\n configurePreviewServer(server) {\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const html = await readFile(\n resolve(distDir, \"preview.html\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch {\n res.statusCode = 404;\n res.end(\"preview.html not found in build output\");\n }\n });\n\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const json = await readFile(\n resolve(distDir, \"__manifests__.json\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.statusCode = 200;\n res.end(json);\n } catch {\n res.statusCode = 404;\n res.end(\"__manifests__.json not found in build output\");\n }\n });\n },\n\n // vite dev: serve /__preview__ with Vite's HTML transform pipeline\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/config-paths.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst WIDGET_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport function resolveWidgetConfigPath(root: string): string {\n return (\n WIDGET_CONFIG_CANDIDATES.find((candidate) =>\n existsSync(join(root, candidate)),\n ) ?? \"src/portal.config.ts\"\n );\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n let builderPreviewEntryUrl: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n configPath = resolveWidgetConfigPath(root);\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n builderPreviewEntryUrl = toViteFsPath(resolveBuilderPreviewEntryPath());\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { renderBuilderPreview } from \"${builderPreviewEntryUrl}\";\n\nrenderBuilderPreview(portalConfig);\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction resolveBuilderPreviewEntryPath(): string {\n const sourcePath = fileURLToPath(\n new URL(\"./builder-preview-entry.tsx\", import.meta.url),\n );\n\n if (existsSync(sourcePath)) return sourcePath;\n\n const builtPath = fileURLToPath(\n new URL(\"./builder-preview-entry.mjs\", import.meta.url),\n );\n if (existsSync(builtPath)) return builtPath;\n\n throw new Error(\n `[fluid-builder-preview] Could not locate builder-preview-entry. ` +\n `Expected source at ${sourcePath} or built output at ${builtPath}.`,\n );\n}\n\nfunction toViteFsPath(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n return normalized.startsWith(\"/\")\n ? `/@fs${normalized}`\n : `/@fs/${normalized}`;\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport {\n isSourceWidgetPackage,\n sourceWidgetPackagesToDescriptors,\n sourceWidgetPackagesToManifests,\n type SourceWidgetPackage,\n} from \"../widgets/authoring\";\nimport { readNonEmptyTrimmedString } from \"../utils/read-non-empty-trimmed-string\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\nimport { resolveWidgetConfigPath } from \"./config-paths\";\n\nconst DEV_WIDGET_PACKAGE_ID = \"company.local-portal-widgets\";\nconst DEV_WIDGET_PACKAGE_VERSION = \"0.0.0-dev\";\nconst DEV_WIDGET_PACKAGE_TYPE = \"company\";\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\n\ninterface LoadedWidgetConfig {\n manifests: Record<string, unknown>[];\n legacyManifests: Record<string, unknown>[];\n sourcePackages: SourceWidgetPackage[];\n cssUrls: string[];\n remoteEntryUrl: string | undefined;\n}\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n configPath = `/${resolveWidgetConfigPath(config.root)}`;\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n sendJson(res, serializable);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n\n server.middlewares.use(\"/__widget-packages__\", async (req, res) => {\n if (handleDevCors(req, res)) return;\n\n try {\n const { legacyManifests, sourcePackages, cssUrls, remoteEntryUrl } =\n await loadWidgetConfig(server, server.config.logger, configPath, {\n includeWidgetPackageRemoteEntryUrl: true,\n });\n const descriptors = [\n ...manifestsToWidgetPackageDescriptors(legacyManifests, {\n cssUrls,\n remoteEntryUrl,\n }),\n ...sourceWidgetPackagesToDescriptors(sourcePackages, {\n cssUrls,\n remoteEntryUrl,\n }),\n ];\n\n sendJson(res, descriptors);\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load widget package descriptors: ${err}`,\n );\n sendJson(res, { error: String(err) }, 500);\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<Record<string, unknown>[]> {\n const { manifests } = await loadWidgetConfig(server, logger, configFilePath);\n return manifests;\n}\n\nasync function loadWidgetConfig(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n options: { includeWidgetPackageRemoteEntryUrl?: boolean } = {},\n): Promise<LoadedWidgetConfig> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets/widgetPackage exports.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return emptyLoadedWidgetConfig();\n }\n\n const sourcePackages = readSourceWidgetPackages(mod, logger);\n const legacyManifests = readLegacyCustomWidgets(\n mod,\n logger,\n configFilePath,\n sourcePackages.length > 0,\n );\n const sourceManifests = sourceWidgetPackagesToManifests(sourcePackages).map(\n widgetManifestToRecord,\n );\n const fullManifests = [...legacyManifests, ...sourceManifests];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of fullManifests) {\n const result = validateManifest(manifest);\n if (result.success === false) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return {\n manifests: stripComponents(fullManifests),\n legacyManifests: stripComponents(legacyManifests),\n sourcePackages,\n cssUrls: options.includeWidgetPackageRemoteEntryUrl\n ? readCssUrls(mod, logger)\n : [],\n remoteEntryUrl: options.includeWidgetPackageRemoteEntryUrl\n ? readWidgetPackageRemoteEntryUrl(mod, logger)\n : undefined,\n };\n}\n\nfunction emptyLoadedWidgetConfig(): LoadedWidgetConfig {\n return {\n manifests: [],\n legacyManifests: [],\n sourcePackages: [],\n cssUrls: [],\n remoteEntryUrl: undefined,\n };\n}\n\nfunction widgetManifestToRecord(\n manifest: WidgetManifest,\n): Record<string, unknown> {\n return {\n manifestVersion: manifest.manifestVersion,\n type: manifest.type,\n component: manifest.component,\n displayName: manifest.displayName,\n description: manifest.description,\n icon: manifest.icon,\n category: manifest.category,\n propertySchema: manifest.propertySchema,\n defaultProps: manifest.defaultProps,\n container: manifest.container,\n minSdkVersion: manifest.minSdkVersion,\n resizable: manifest.resizable,\n };\n}\n\nfunction readLegacyCustomWidgets(\n mod: Record<string, unknown>,\n logger: Logger | undefined,\n configFilePath: string | undefined,\n hasSourcePackages: boolean,\n): Record<string, unknown>[] {\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\") && !hasSourcePackages) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets or widgetPackage. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n return rawWidgets as Record<string, unknown>[];\n}\n\nexport function readSourceWidgetPackages(\n mod: Record<string, unknown>,\n logger?: Pick<Logger, \"warn\">,\n): SourceWidgetPackage[] {\n const packages: SourceWidgetPackage[] = [];\n\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackage,\n \"widgetPackage\",\n logger,\n );\n collectSourceWidgetPackageExport(\n packages,\n mod.widgetPackages,\n \"widgetPackages\",\n logger,\n );\n collectSourceWidgetPackageExport(packages, mod.default, \"default\", logger, {\n warnOnInvalid: false,\n });\n\n const byPackageId = new Map<string, SourceWidgetPackage>();\n for (const sourcePackage of packages) {\n if (byPackageId.has(sourcePackage.packageId)) {\n logger?.warn(\n `[fluid] Duplicate widget package \"${sourcePackage.packageId}\" found. Keeping the first export.`,\n );\n continue;\n }\n byPackageId.set(sourcePackage.packageId, sourcePackage);\n }\n\n return Array.from(byPackageId.values());\n}\n\nfunction collectSourceWidgetPackageExport(\n packages: SourceWidgetPackage[],\n value: unknown,\n exportName: string,\n logger: Pick<Logger, \"warn\"> | undefined,\n options: { warnOnInvalid?: boolean } = {},\n): void {\n if (value === undefined) return;\n\n if (isSourceWidgetPackage(value)) {\n packages.push(value);\n return;\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (isSourceWidgetPackage(entry)) {\n packages.push(entry);\n continue;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} contains a non-source widget package entry. Skipping entry.`,\n );\n }\n }\n return;\n }\n\n if (options.warnOnInvalid !== false) {\n logger?.warn(\n `[fluid] ${exportName} export is not a source widget package or array. Skipping source packages.`,\n );\n }\n}\n\nfunction stripComponents(\n manifests: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n\nexport function manifestsToWidgetPackageDescriptors(\n manifests: readonly Record<string, unknown>[],\n options: {\n cssUrls?: readonly string[];\n remoteEntryUrl?: string;\n } = {},\n): WidgetPackageDescriptor[] {\n const remoteEntryUrl = readNonEmptyTrimmedString(options.remoteEntryUrl);\n if (manifests.length === 0 || !remoteEntryUrl) return [];\n\n const descriptor = {\n manifestVersion: WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION,\n packageId: DEV_WIDGET_PACKAGE_ID,\n packageType: DEV_WIDGET_PACKAGE_TYPE,\n version: DEV_WIDGET_PACKAGE_VERSION,\n remoteEntryUrl,\n cssUrls: options.cssUrls ?? [],\n widgets: manifests.map(manifestToWidgetDescriptor),\n } satisfies WidgetPackageDescriptor;\n\n return [descriptor];\n}\n\nfunction manifestToWidgetDescriptor(\n manifest: Record<string, unknown>,\n index: number,\n): WidgetDescriptor {\n const type =\n readNonEmptyString(manifest.type) ?? `custom-widget-${index + 1}`;\n const displayName = readNonEmptyString(manifest.displayName) ?? type;\n\n return {\n type,\n name: type,\n displayName,\n description:\n readNonEmptyString(manifest.description) ??\n `Custom widget ${displayName}`,\n icon: readNonEmptyString(manifest.icon) ?? \"box\",\n category: readNonEmptyString(manifest.category) ?? \"components\",\n propertySchema: normalizePropertySchema(manifest.propertySchema, type),\n defaultProps: isObjectRecord(manifest.defaultProps)\n ? manifest.defaultProps\n : {},\n container: normalizeContainer(manifest.container),\n minSdkVersion: readNonEmptyString(manifest.minSdkVersion) ?? \"0.0.0\",\n resizable: normalizeResizable(manifest.resizable),\n };\n}\n\n/**\n * Runtime package descriptors must point to an actually served classic-script\n * remote entry. Local SDK legacy widgets continue to use the legacy\n * /__manifests__ + /__preview__ flow, so legacy /__widget-packages__ output\n * stays empty unless the widget config explicitly exports a non-empty\n * `widgetPackageRemoteEntryUrl` string. Source packages authored with\n * defineWidgetPackage still contribute local preview/manifests, but are emitted\n * as runtime descriptors only when the package or widget config explicitly\n * provides a remote entry URL.\n */\nfunction readWidgetPackageRemoteEntryUrl(\n mod: Record<string, unknown>,\n logger?: Logger,\n): string | undefined {\n const rawRemoteEntryUrl = mod.widgetPackageRemoteEntryUrl;\n if (rawRemoteEntryUrl === undefined) return undefined;\n\n const remoteEntryUrl = readNonEmptyTrimmedString(rawRemoteEntryUrl);\n if (!remoteEntryUrl) {\n logger?.warn(\n `[fluid] widgetPackageRemoteEntryUrl export must be a non-empty string. ` +\n `Legacy customWidgets will not be included in /__widget-packages__.`,\n );\n return undefined;\n }\n\n return remoteEntryUrl;\n}\n\nfunction readCssUrls(mod: Record<string, unknown>, logger?: Logger): string[] {\n const rawCssUrls = mod.widgetPackageCssUrls;\n if (rawCssUrls === undefined) return [];\n\n if (!Array.isArray(rawCssUrls)) {\n logger?.warn(\n `[fluid] widget package cssUrls export is not an array (got ${typeof rawCssUrls}). Skipping CSS URLs.`,\n );\n return [];\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of rawCssUrls) {\n const value = readNonEmptyString(cssUrl);\n if (value) cssUrls.push(value);\n }\n return cssUrls;\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isObjectRecord(value)) return { widgetType };\n return { ...value, widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return \"block\";\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isObjectRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction handleDevCors(\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n): boolean {\n setDevCorsHeaders(res);\n\n if (req.method !== \"OPTIONS\") return false;\n\n res.statusCode = 204;\n res.end();\n return true;\n}\n\nfunction setDevCorsHeaders(res: import(\"node:http\").ServerResponse): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n}\n\nfunction sendJson(\n res: import(\"node:http\").ServerResponse,\n body: unknown,\n statusCode = 200,\n): void {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = statusCode;\n res.end(JSON.stringify(body));\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: adds preview.html as a second Rollup input so `vite build`\n * produces it alongside index.html for production deployment.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n config(_config: UserConfig, { command }) {\n if (command !== \"build\") return;\n\n // config() runs before configResolved, so use process.cwd()\n // which is the project root when invoked via `vite build`.\n const projectRoot = process.cwd();\n const previewHtml = resolve(projectRoot, \"preview.html\");\n if (!existsSync(previewHtml)) return;\n\n // Add preview.html as a second Rollup entry point\n return {\n build: {\n rollupOptions: {\n input: {\n main: resolve(projectRoot, \"index.html\"),\n preview: previewHtml,\n },\n },\n },\n };\n },\n\n // vite preview: static file server with no rewrite rules.\n // Map /__preview__ → preview.html and /__manifests__ → __manifests__.json\n // so local production testing works the same as nginx in production.\n configurePreviewServer(server) {\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const html = await readFile(\n resolve(distDir, \"preview.html\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch {\n res.statusCode = 404;\n res.end(\"preview.html not found in build output\");\n }\n });\n\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const json = await readFile(\n resolve(distDir, \"__manifests__.json\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.statusCode = 200;\n res.end(json);\n } catch {\n res.statusCode = 404;\n res.end(\"__manifests__.json not found in build output\");\n }\n });\n },\n\n // vite dev: serve /__preview__ with Vite's HTML transform pipeline\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}