@fluid-app/portal-sdk 0.1.60 → 0.1.62

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.
@@ -32,7 +32,6 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import "./sortable.esm-DreCqRxJ.mjs";
37
36
  import { n as mySiteScreenPropertySchema, t as MySiteScreen } from "./MySiteScreen-Ddxdk1Pe.mjs";
38
37
  export { MySiteScreen, mySiteScreenPropertySchema };
@@ -32,7 +32,6 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import "./order-detail-DkMYJvzl.mjs";
37
36
  import { n as ordersScreenPropertySchema, t as OrdersScreen } from "./OrdersScreen-CwTIkQNI.mjs";
38
37
  export { OrdersScreen, ordersScreenPropertySchema };
@@ -32,7 +32,6 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import "./dist-CMGXkSgZ.mjs";
37
36
  import "./es-CrIkZTQ3.mjs";
38
37
  import "./dist-Cl4FsM3V.mjs";
@@ -32,7 +32,6 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import "./dist-CMGXkSgZ.mjs";
37
36
  import "./es-CrIkZTQ3.mjs";
38
37
  import "./dist-Cl4FsM3V.mjs";
@@ -32,6 +32,5 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import { n as shopScreenPropertySchema, t as ShopScreen } from "./ShopScreen-Dc3lQLGI.mjs";
37
36
  export { ShopScreen, shopScreenPropertySchema };
@@ -32,7 +32,6 @@ import "./TableWidget-CK5jQxjz.mjs";
32
32
  import "./ToDoWidget-Cmvf7I6c.mjs";
33
33
  import "./VideoWidget-ho9fGQ3V.mjs";
34
34
  import "./ScreenHeaderContext-CrdfLGKk.mjs";
35
- import "./AppNavigationContext-DJeNcP4Y.mjs";
36
35
  import "./order-detail-DkMYJvzl.mjs";
37
36
  import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-749p5heX.mjs";
38
37
  import "./src-BakNjVTk.mjs";
package/dist/index.mjs CHANGED
@@ -2402,15 +2402,15 @@ function AccountManageLayout({ children }) {
2402
2402
  //#endregion
2403
2403
  //#region src/shell/system-screen-map.ts
2404
2404
  const ProfileScreen$1 = lazy(() => import("./ProfileScreen-DSr0OwIA.mjs").then((m) => ({ default: m.ProfileScreen })));
2405
- const OrdersScreen$1 = lazy(() => import("./OrdersScreen-BB7vBz1g.mjs").then((m) => ({ default: m.OrdersScreen })));
2406
- const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-V3mnPdFM.mjs").then((m) => ({ default: m.SubscriptionsScreen })));
2405
+ const OrdersScreen$1 = lazy(() => import("./OrdersScreen-QFndb0Oi.mjs").then((m) => ({ default: m.OrdersScreen })));
2406
+ const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-DxVV0sZx.mjs").then((m) => ({ default: m.SubscriptionsScreen })));
2407
2407
  const MessagingScreen$1 = lazy(() => import("./MessagingScreen-RhCqymLN.mjs").then((m) => ({ default: m.MessagingScreen })));
2408
2408
  const ContactsScreen$1 = lazy(() => import("./ContactsScreen-C8Bp62F4.mjs").then((m) => ({ default: m.ContactsScreen })));
2409
- const ShopScreen$1 = lazy(() => import("./ShopScreen-B3I6PRkp.mjs").then((m) => ({ default: m.ShopScreen })));
2409
+ const ShopScreen$1 = lazy(() => import("./ShopScreen-D2cMeKSk.mjs").then((m) => ({ default: m.ShopScreen })));
2410
2410
  const CustomersScreen$1 = lazy(() => import("./CustomersScreen-D22G27ru.mjs").then((n) => n.n).then((m) => ({ default: m.CustomersScreen })));
2411
- const ProductsScreen$1 = lazy(() => import("./ProductsScreen-BDCW_OdU.mjs").then((m) => ({ default: m.ProductsScreen })));
2412
- const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-BuR6Mei7.mjs").then((m) => ({ default: m.ShareablesScreen })));
2413
- const MySiteScreen$1 = lazy(() => import("./MySiteScreen-BgyCPMby.mjs").then((m) => ({ default: m.MySiteScreen })));
2411
+ const ProductsScreen$1 = lazy(() => import("./ProductsScreen-HaYLhqt1.mjs").then((m) => ({ default: m.ProductsScreen })));
2412
+ const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-xqMDNJ6m.mjs").then((m) => ({ default: m.ShareablesScreen })));
2413
+ const MySiteScreen$1 = lazy(() => import("./MySiteScreen-Dbvaw1nu.mjs").then((m) => ({ default: m.MySiteScreen })));
2414
2414
  const UpgradeScreen = lazy(() => import("./UpgradeScreen-D7LfdVSJ.mjs").then((n) => n.t).then((m) => ({ default: m.UpgradeScreen })));
2415
2415
  const AppDownloadScreen = lazy(() => import("./AppDownloadScreen-CLWxnFsd.mjs").then((m) => ({ default: m.AppDownloadScreen })));
2416
2416
  const SYSTEM_SLUG_SCREEN_MAP = {
@@ -3815,13 +3815,13 @@ const screenPropertySchemas = {
3815
3815
  ProfileScreen: () => import("./ProfileScreen-DSr0OwIA.mjs").then((m) => m.profileScreenPropertySchema),
3816
3816
  MessagingScreen: () => import("./MessagingScreen-RhCqymLN.mjs").then((m) => m.messagingScreenPropertySchema),
3817
3817
  ContactsScreen: () => import("./ContactsScreen-C8Bp62F4.mjs").then((m) => m.contactsScreenPropertySchema),
3818
- OrdersScreen: () => import("./OrdersScreen-BB7vBz1g.mjs").then((m) => m.ordersScreenPropertySchema),
3819
- SubscriptionsScreen: () => import("./SubscriptionsScreen-V3mnPdFM.mjs").then((m) => m.subscriptionsScreenPropertySchema),
3818
+ OrdersScreen: () => import("./OrdersScreen-QFndb0Oi.mjs").then((m) => m.ordersScreenPropertySchema),
3819
+ SubscriptionsScreen: () => import("./SubscriptionsScreen-DxVV0sZx.mjs").then((m) => m.subscriptionsScreenPropertySchema),
3820
3820
  CustomersScreen: () => import("./CustomersScreen-D22G27ru.mjs").then((n) => n.n).then((m) => m.customersScreenPropertySchema),
3821
- ProductsScreen: () => import("./ProductsScreen-BDCW_OdU.mjs").then((m) => m.productsScreenPropertySchema),
3822
- MySiteScreen: () => import("./MySiteScreen-BgyCPMby.mjs").then((m) => m.mySiteScreenPropertySchema),
3823
- ShareablesScreen: () => import("./ShareablesScreen-BuR6Mei7.mjs").then((m) => m.shareablesScreenPropertySchema),
3824
- ShopScreen: () => import("./ShopScreen-B3I6PRkp.mjs").then((m) => m.shopScreenPropertySchema),
3821
+ ProductsScreen: () => import("./ProductsScreen-HaYLhqt1.mjs").then((m) => m.productsScreenPropertySchema),
3822
+ MySiteScreen: () => import("./MySiteScreen-Dbvaw1nu.mjs").then((m) => m.mySiteScreenPropertySchema),
3823
+ ShareablesScreen: () => import("./ShareablesScreen-xqMDNJ6m.mjs").then((m) => m.shareablesScreenPropertySchema),
3824
+ ShopScreen: () => import("./ShopScreen-D2cMeKSk.mjs").then((m) => m.shopScreenPropertySchema),
3825
3825
  UpgradeScreen: () => import("./UpgradeScreen-D7LfdVSJ.mjs").then((n) => n.t).then((m) => m.upgradeScreenPropertySchema),
3826
3826
  AppDownloadScreen: () => import("./AppDownloadScreen-CLWxnFsd.mjs").then((m) => m.appDownloadScreenPropertySchema)
3827
3827
  };
@@ -1,6 +1,94 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../chunk-DAgNkxik.cjs");
3
- let _fluid_app_portal_core_validation = require("@fluid-app/portal-core/validation");
3
+ //#region src/vite/validate-manifest.ts
4
+ /**
5
+ * Lightweight manifest validation for the SDK vite plugin.
6
+ *
7
+ * Inlined here (rather than imported from @fluid-app/portal-core) because
8
+ * portal-core is private and not published to npm. This avoids a runtime
9
+ * ERR_MODULE_NOT_FOUND for portals installed from npm.
10
+ */
11
+ const VALID_FIELD_TYPES = [
12
+ "text",
13
+ "textarea",
14
+ "number",
15
+ "boolean",
16
+ "select",
17
+ "color",
18
+ "range",
19
+ "dataSource",
20
+ "resource",
21
+ "image",
22
+ "alignment",
23
+ "slider",
24
+ "colorPicker",
25
+ "sectionHeader",
26
+ "separator",
27
+ "buttonGroup",
28
+ "colorSelect",
29
+ "sectionLayoutSelect",
30
+ "background",
31
+ "contentPosition",
32
+ "textSizeSelect",
33
+ "cssUnit",
34
+ "fontPicker",
35
+ "stringArray",
36
+ "borderRadius",
37
+ "screenPicker"
38
+ ];
39
+ function validateManifest(input) {
40
+ const errors = [];
41
+ const m = input;
42
+ if (!m || typeof m !== "object") return {
43
+ success: false,
44
+ errors: [{
45
+ path: "",
46
+ message: "Manifest must be an object"
47
+ }]
48
+ };
49
+ for (const key of [
50
+ "type",
51
+ "displayName",
52
+ "description",
53
+ "icon",
54
+ "category"
55
+ ]) if (typeof m[key] !== "string" || m[key].length === 0) errors.push({
56
+ path: key,
57
+ message: `${key} is required and must be a non-empty string`
58
+ });
59
+ if (typeof m.manifestVersion !== "number" || m.manifestVersion < 1) errors.push({
60
+ path: "manifestVersion",
61
+ message: "manifestVersion must be a positive integer"
62
+ });
63
+ if (typeof m.component !== "function") errors.push({
64
+ path: "component",
65
+ message: "component must be a React component (function)"
66
+ });
67
+ const schema = m.propertySchema;
68
+ if (schema && typeof schema === "object") {
69
+ if (typeof schema.widgetType !== "string" || !schema.widgetType) errors.push({
70
+ path: "propertySchema.widgetType",
71
+ message: "widgetType is required"
72
+ });
73
+ if (typeof m.type === "string" && schema.widgetType !== m.type) errors.push({
74
+ path: "propertySchema.widgetType",
75
+ message: "manifest.type must match manifest.propertySchema.widgetType"
76
+ });
77
+ if (Array.isArray(schema.fields)) for (let i = 0; i < schema.fields.length; i++) {
78
+ const field = schema.fields[i];
79
+ if (!field || typeof field.type !== "string") continue;
80
+ if (!VALID_FIELD_TYPES.includes(field.type)) errors.push({
81
+ path: `propertySchema.fields.${i}.type`,
82
+ message: `Invalid field type "${field.type}". Valid types: ${VALID_FIELD_TYPES.join(", ")}`
83
+ });
84
+ }
85
+ }
86
+ return errors.length === 0 ? { success: true } : {
87
+ success: false,
88
+ errors
89
+ };
90
+ }
91
+ //#endregion
4
92
  //#region src/vite/manifest-plugin.ts
5
93
  /**
6
94
  * Vite plugin that serves widget manifest metadata.
@@ -62,7 +150,7 @@ async function loadManifests(server, logger) {
62
150
  }
63
151
  const manifests = rawWidgets;
64
152
  if (logger) for (const manifest of manifests) {
65
- const result = (0, _fluid_app_portal_core_validation.validateManifest)(manifest);
153
+ const result = validateManifest(manifest);
66
154
  if (!result.success) {
67
155
  const type = manifest.type ?? "unknown";
68
156
  logger.warn(`[fluid] Invalid manifest for "${type}":\n` + result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n"));
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"@fluid-app/portal-core/validation\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\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 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): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\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 const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\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 manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\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=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</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 *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\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 * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\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"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,sBAA8B;AAC5C,QAAO;EACL,MAAM;EAEN,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,kBAAkB,OAAO,MAAM,QAAQ;AAC5D,QAAI;KACF,MAAM,eAAe,MAAM,cACzB,QACA,OAAO,OAAO,OACf;AAED,SAAI,UAAU,gBAAgB,mBAAmB;AACjD,SAAI,UAAU,+BAA+B,IAAI;AACjD,SAAI,IAAI,KAAK,UAAU,aAAa,CAAC;aAC9B,KAAK;AACZ,YAAO,OAAO,OAAO,MACnB,qCAAqC,MACtC;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;;KAEjD;;EAGJ,iBAAiB;AAGf,QAAK,KACH,yIAED;AACD,QAAK,SAAS;IACZ,MAAM;IACN,UAAU;IACV,QAAQ,KAAK,UAAU,EAAE,CAAC;IAC3B,CAAC;;EAEL;;;;;;;AAQH,eAAe,cACb,QACA,QACoB;CACpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,wBAAwB;UAClD,KAAK;AACZ,UAAQ,KACN,4CAA4C,eAAe,QAAQ,IAAI,UAAU,MAClF;AACD,SAAO,EAAE;;CAGX,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,WAAY,QAAO,EAAE;AAE1B,KAAI,CAAC,MAAM,QAAQ,WAAW,EAAE;AAC9B,UAAQ,KACN,qDAAqD,OAAO,WAAW,+BACxE;AACD,SAAO,EAAE;;CAGX,MAAM,YAAY;AAGlB,KAAI,OACF,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,UAAA,GAAA,kCAAA,kBAA0B,SAAS;AACzC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,OAAQ,SAA+B,QAAQ;AACrD,UAAO,KACL,iCAAiC,KAAK,QACpC,OAAO,OAAO,KAAK,MAAM,OAAO,EAAE,KAAK,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CACrE;;;AAKP,QAAO,UAAU,KACd,EAAE,WAAW,YAAY,GAAG,WAAoC,KAClE;;;;ACrGH,MAAM,mBAAmB;AACzB,MAAM,sBAAsB,OAAO;AAGnC,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;AAyBjB,SAAgB,4BAAoC;AAClD,QAAO;EACL,MAAM;EACN,OAAO;EAEP,UAAU,IAAI;AACZ,OAAI,OAAO,iBAAkB,QAAO;;EAGtC,KAAK,IAAI;AACP,OAAI,OAAO,oBACT,QAAO;;;;;;;;;;;;;;;EAiBX,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IAC/C,MAAM,YAAY,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC;AAC5C,QAAI,aAAa,sBAAsB,aAAa,oBAClD,QAAO,MAAM;AACf,QAAI;KACF,MAAM,cAAc,MAAM,OAAO,mBAC/B,oBACA,SACD;AACD,SAAI,UAAU,gBAAgB,YAAY;AAC1C,SAAI,IAAI,YAAY;aACb,GAAG;AACV,YAAO,OAAO,OAAO,MACnB,4CAA4C,IAC7C;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,iCAAiC;;KAE3C;;EAEL"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/manifest-plugin.ts","../../src/vite/builder-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 type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"./validate-manifest\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\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 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): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\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 const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\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 manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\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=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</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 *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\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 * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\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"],"mappings":"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"}
@@ -1,4 +1,92 @@
1
- import { validateManifest } from "@fluid-app/portal-core/validation";
1
+ //#region src/vite/validate-manifest.ts
2
+ /**
3
+ * Lightweight manifest validation for the SDK vite plugin.
4
+ *
5
+ * Inlined here (rather than imported from @fluid-app/portal-core) because
6
+ * portal-core is private and not published to npm. This avoids a runtime
7
+ * ERR_MODULE_NOT_FOUND for portals installed from npm.
8
+ */
9
+ const VALID_FIELD_TYPES = [
10
+ "text",
11
+ "textarea",
12
+ "number",
13
+ "boolean",
14
+ "select",
15
+ "color",
16
+ "range",
17
+ "dataSource",
18
+ "resource",
19
+ "image",
20
+ "alignment",
21
+ "slider",
22
+ "colorPicker",
23
+ "sectionHeader",
24
+ "separator",
25
+ "buttonGroup",
26
+ "colorSelect",
27
+ "sectionLayoutSelect",
28
+ "background",
29
+ "contentPosition",
30
+ "textSizeSelect",
31
+ "cssUnit",
32
+ "fontPicker",
33
+ "stringArray",
34
+ "borderRadius",
35
+ "screenPicker"
36
+ ];
37
+ function validateManifest(input) {
38
+ const errors = [];
39
+ const m = input;
40
+ if (!m || typeof m !== "object") return {
41
+ success: false,
42
+ errors: [{
43
+ path: "",
44
+ message: "Manifest must be an object"
45
+ }]
46
+ };
47
+ for (const key of [
48
+ "type",
49
+ "displayName",
50
+ "description",
51
+ "icon",
52
+ "category"
53
+ ]) if (typeof m[key] !== "string" || m[key].length === 0) errors.push({
54
+ path: key,
55
+ message: `${key} is required and must be a non-empty string`
56
+ });
57
+ if (typeof m.manifestVersion !== "number" || m.manifestVersion < 1) errors.push({
58
+ path: "manifestVersion",
59
+ message: "manifestVersion must be a positive integer"
60
+ });
61
+ if (typeof m.component !== "function") errors.push({
62
+ path: "component",
63
+ message: "component must be a React component (function)"
64
+ });
65
+ const schema = m.propertySchema;
66
+ if (schema && typeof schema === "object") {
67
+ if (typeof schema.widgetType !== "string" || !schema.widgetType) errors.push({
68
+ path: "propertySchema.widgetType",
69
+ message: "widgetType is required"
70
+ });
71
+ if (typeof m.type === "string" && schema.widgetType !== m.type) errors.push({
72
+ path: "propertySchema.widgetType",
73
+ message: "manifest.type must match manifest.propertySchema.widgetType"
74
+ });
75
+ if (Array.isArray(schema.fields)) for (let i = 0; i < schema.fields.length; i++) {
76
+ const field = schema.fields[i];
77
+ if (!field || typeof field.type !== "string") continue;
78
+ if (!VALID_FIELD_TYPES.includes(field.type)) errors.push({
79
+ path: `propertySchema.fields.${i}.type`,
80
+ message: `Invalid field type "${field.type}". Valid types: ${VALID_FIELD_TYPES.join(", ")}`
81
+ });
82
+ }
83
+ }
84
+ return errors.length === 0 ? { success: true } : {
85
+ success: false,
86
+ errors
87
+ };
88
+ }
89
+ //#endregion
2
90
  //#region src/vite/manifest-plugin.ts
3
91
  /**
4
92
  * Vite plugin that serves widget manifest metadata.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"@fluid-app/portal-core/validation\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\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 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): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\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 const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\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 manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\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=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</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 *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\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 * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\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"],"mappings":";;;;;;;;;;;;;;AAeA,SAAgB,sBAA8B;AAC5C,QAAO;EACL,MAAM;EAEN,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,kBAAkB,OAAO,MAAM,QAAQ;AAC5D,QAAI;KACF,MAAM,eAAe,MAAM,cACzB,QACA,OAAO,OAAO,OACf;AAED,SAAI,UAAU,gBAAgB,mBAAmB;AACjD,SAAI,UAAU,+BAA+B,IAAI;AACjD,SAAI,IAAI,KAAK,UAAU,aAAa,CAAC;aAC9B,KAAK;AACZ,YAAO,OAAO,OAAO,MACnB,qCAAqC,MACtC;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;;KAEjD;;EAGJ,iBAAiB;AAGf,QAAK,KACH,yIAED;AACD,QAAK,SAAS;IACZ,MAAM;IACN,UAAU;IACV,QAAQ,KAAK,UAAU,EAAE,CAAC;IAC3B,CAAC;;EAEL;;;;;;;AAQH,eAAe,cACb,QACA,QACoB;CACpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,wBAAwB;UAClD,KAAK;AACZ,UAAQ,KACN,4CAA4C,eAAe,QAAQ,IAAI,UAAU,MAClF;AACD,SAAO,EAAE;;CAGX,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,WAAY,QAAO,EAAE;AAE1B,KAAI,CAAC,MAAM,QAAQ,WAAW,EAAE;AAC9B,UAAQ,KACN,qDAAqD,OAAO,WAAW,+BACxE;AACD,SAAO,EAAE;;CAGX,MAAM,YAAY;AAGlB,KAAI,OACF,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,SAAS,iBAAiB,SAAS;AACzC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,OAAQ,SAA+B,QAAQ;AACrD,UAAO,KACL,iCAAiC,KAAK,QACpC,OAAO,OAAO,KAAK,MAAM,OAAO,EAAE,KAAK,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CACrE;;;AAKP,QAAO,UAAU,KACd,EAAE,WAAW,YAAY,GAAG,WAAoC,KAClE;;;;ACrGH,MAAM,mBAAmB;AACzB,MAAM,sBAAsB,OAAO;AAGnC,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;AAyBjB,SAAgB,4BAAoC;AAClD,QAAO;EACL,MAAM;EACN,OAAO;EAEP,UAAU,IAAI;AACZ,OAAI,OAAO,iBAAkB,QAAO;;EAGtC,KAAK,IAAI;AACP,OAAI,OAAO,oBACT,QAAO;;;;;;;;;;;;;;;EAiBX,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IAC/C,MAAM,YAAY,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC;AAC5C,QAAI,aAAa,sBAAsB,aAAa,oBAClD,QAAO,MAAM;AACf,QAAI;KACF,MAAM,cAAc,MAAM,OAAO,mBAC/B,oBACA,SACD;AACD,SAAI,UAAU,gBAAgB,YAAY;AAC1C,SAAI,IAAI,YAAY;aACb,GAAG;AACV,YAAO,OAAO,OAAO,MACnB,4CAA4C,IAC7C;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,iCAAiC;;KAE3C;;EAEL"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/manifest-plugin.ts","../../src/vite/builder-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 type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"./validate-manifest\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\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 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): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\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 const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\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 manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\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=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</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 *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\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 * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\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"],"mappings":";;;;;;;;AAQA,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAWD,SAAgB,iBAAiB,OAAkC;CACjE,MAAM,SAA4B,EAAE;CACpC,MAAM,IAAI;AAEV,KAAI,CAAC,KAAK,OAAO,MAAM,SACrB,QAAO;EACL,SAAS;EACT,QAAQ,CAAC;GAAE,MAAM;GAAI,SAAS;GAA8B,CAAC;EAC9D;AAIH,MAAK,MAAM,OAAO;EAChB;EACA;EACA;EACA;EACA;EACD,CACC,KAAI,OAAO,EAAE,SAAS,YAAa,EAAE,KAAgB,WAAW,EAC9D,QAAO,KAAK;EACV,MAAM;EACN,SAAS,GAAG,IAAI;EACjB,CAAC;AAIN,KAAI,OAAO,EAAE,oBAAoB,YAAY,EAAE,kBAAkB,EAC/D,QAAO,KAAK;EACV,MAAM;EACN,SAAS;EACV,CAAC;AAGJ,KAAI,OAAO,EAAE,cAAc,WACzB,QAAO,KAAK;EACV,MAAM;EACN,SAAS;EACV,CAAC;CAIJ,MAAM,SAAS,EAAE;AACjB,KAAI,UAAU,OAAO,WAAW,UAAU;AACxC,MAAI,OAAO,OAAO,eAAe,YAAY,CAAC,OAAO,WACnD,QAAO,KAAK;GACV,MAAM;GACN,SAAS;GACV,CAAC;AAEJ,MAAI,OAAO,EAAE,SAAS,YAAY,OAAO,eAAe,EAAE,KACxD,QAAO,KAAK;GACV,MAAM;GACN,SAAS;GACV,CAAC;AAEJ,MAAI,MAAM,QAAQ,OAAO,OAAO,CAC9B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KAAK;GAC7C,MAAM,QAAQ,OAAO,OAAO;AAC5B,OAAI,CAAC,SAAS,OAAO,MAAM,SAAS,SAAU;AAC9C,OACE,CAAC,kBAAkB,SACjB,MAAM,KACP,CAED,QAAO,KAAK;IACV,MAAM,yBAAyB,EAAE;IACjC,SAAS,uBAAuB,MAAM,KAAK,kBAAkB,kBAAkB,KAAK,KAAK;IAC1F,CAAC;;;AAMV,QAAO,OAAO,WAAW,IAAI,EAAE,SAAS,MAAM,GAAG;EAAE,SAAS;EAAO;EAAQ;;;;;;;;;;;;;;;;ACzG7E,SAAgB,sBAA8B;AAC5C,QAAO;EACL,MAAM;EAEN,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,kBAAkB,OAAO,MAAM,QAAQ;AAC5D,QAAI;KACF,MAAM,eAAe,MAAM,cACzB,QACA,OAAO,OAAO,OACf;AAED,SAAI,UAAU,gBAAgB,mBAAmB;AACjD,SAAI,UAAU,+BAA+B,IAAI;AACjD,SAAI,IAAI,KAAK,UAAU,aAAa,CAAC;aAC9B,KAAK;AACZ,YAAO,OAAO,OAAO,MACnB,qCAAqC,MACtC;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;;KAEjD;;EAGJ,iBAAiB;AAGf,QAAK,KACH,yIAED;AACD,QAAK,SAAS;IACZ,MAAM;IACN,UAAU;IACV,QAAQ,KAAK,UAAU,EAAE,CAAC;IAC3B,CAAC;;EAEL;;;;;;;AAQH,eAAe,cACb,QACA,QACoB;CACpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,wBAAwB;UAClD,KAAK;AACZ,UAAQ,KACN,4CAA4C,eAAe,QAAQ,IAAI,UAAU,MAClF;AACD,SAAO,EAAE;;CAGX,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,WAAY,QAAO,EAAE;AAE1B,KAAI,CAAC,MAAM,QAAQ,WAAW,EAAE;AAC9B,UAAQ,KACN,qDAAqD,OAAO,WAAW,+BACxE;AACD,SAAO,EAAE;;CAGX,MAAM,YAAY;AAGlB,KAAI,OACF,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,SAAS,iBAAiB,SAAS;AACzC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,OAAQ,SAA+B,QAAQ;AACrD,UAAO,KACL,iCAAiC,KAAK,QACpC,OAAO,OAAO,KAAK,MAAM,OAAO,EAAE,KAAK,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CACrE;;;AAKP,QAAO,UAAU,KACd,EAAE,WAAW,YAAY,GAAG,WAAoC,KAClE;;;;ACrGH,MAAM,mBAAmB;AACzB,MAAM,sBAAsB,OAAO;AAGnC,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;AAyBjB,SAAgB,4BAAoC;AAClD,QAAO;EACL,MAAM;EACN,OAAO;EAEP,UAAU,IAAI;AACZ,OAAI,OAAO,iBAAkB,QAAO;;EAGtC,KAAK,IAAI;AACP,OAAI,OAAO,oBACT,QAAO;;;;;;;;;;;;;;;EAiBX,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IAC/C,MAAM,YAAY,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC;AAC5C,QAAI,aAAa,sBAAsB,aAAa,oBAClD,QAAO,MAAM;AACf,QAAI;KACF,MAAM,cAAc,MAAM,OAAO,mBAC/B,oBACA,SACD;AACD,SAAI,UAAU,gBAAgB,YAAY;AAC1C,SAAI,IAAI,YAAY;aACb,GAAG;AACV,YAAO,OAAO,OAAO,MACnB,4CAA4C,IAC7C;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,iCAAiC;;KAE3C;;EAEL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-app/portal-sdk",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "SDK for building custom Fluid portals",
5
5
  "files": [
6
6
  "dist",
@@ -9,6 +9,7 @@
9
9
  "type": "module",
10
10
  "sideEffects": [
11
11
  "./styles/globals.css",
12
+ "./styles/packages.css",
12
13
  "./styles/theme.css"
13
14
  ],
14
15
  "main": "./dist/index.cjs",
@@ -21,6 +22,7 @@
21
22
  "types": "./dist/index.d.mts"
22
23
  },
23
24
  "./globals.css": "./styles/globals.css",
25
+ "./packages.css": "./styles/packages.css",
24
26
  "./theme.css": "./styles/theme.css",
25
27
  "./vite": {
26
28
  "import": {
@@ -62,44 +64,44 @@
62
64
  "tsdown": "^0.21.0",
63
65
  "typescript": "^5",
64
66
  "zod": "4.3.5",
65
- "@fluid-app/company-switcher-core": "0.1.0",
67
+ "@fluid-app/api-client-core": "0.1.0",
66
68
  "@fluid-app/company-switcher-ui": "0.1.0",
67
69
  "@fluid-app/contacts-ui": "0.1.0",
70
+ "@fluid-app/auth": "0.1.0",
68
71
  "@fluid-app/file-picker-api-client": "0.1.0",
69
72
  "@fluid-app/fluid-pay-api-client": "0.1.0",
70
- "@fluid-app/api-client-core": "0.1.0",
71
- "@fluid-app/auth": "0.1.0",
72
- "@fluid-app/messaging-api-client": "0.1.0",
73
73
  "@fluid-app/fluidos-api-client": "0.1.0",
74
+ "@fluid-app/messaging-api-client": "0.1.0",
74
75
  "@fluid-app/messaging-core": "0.1.0",
76
+ "@fluid-app/messaging-ui": "0.1.0",
75
77
  "@fluid-app/mysite-ui": "0.1.0",
78
+ "@fluid-app/company-switcher-core": "0.1.0",
76
79
  "@fluid-app/orders-api-client": "0.1.0",
77
- "@fluid-app/messaging-ui": "0.1.0",
78
80
  "@fluid-app/orders-core": "0.1.0",
81
+ "@fluid-app/orders-ui": "0.1.0",
79
82
  "@fluid-app/permissions": "0.1.0",
80
83
  "@fluid-app/portal-app-download-ui": "0.1.0",
81
- "@fluid-app/orders-ui": "0.1.0",
82
84
  "@fluid-app/portal-core": "0.1.23",
83
- "@fluid-app/portal-pro-upgrade-ui": "0.1.0",
84
85
  "@fluid-app/portal-preview": "0.1.0",
86
+ "@fluid-app/portal-pro-upgrade-ui": "0.1.0",
85
87
  "@fluid-app/portal-react": "0.1.0",
86
- "@fluid-app/profile-core": "0.1.0",
87
88
  "@fluid-app/portal-widgets": "0.1.22",
88
89
  "@fluid-app/products-core": "0.1.0",
89
90
  "@fluid-app/products-api-client": "0.1.0",
91
+ "@fluid-app/profile-core": "0.1.0",
90
92
  "@fluid-app/profile-ui": "0.1.0",
91
93
  "@fluid-app/query-persister": "0.1.0",
92
- "@fluid-app/shareables-core": "0.1.0",
93
94
  "@fluid-app/shareables-api-client": "0.1.0",
95
+ "@fluid-app/shareables-core": "0.1.0",
94
96
  "@fluid-app/shareables-ui": "0.1.0",
95
- "@fluid-app/subscriptions-api-client": "0.1.0",
96
97
  "@fluid-app/shop-ui": "0.1.0",
97
- "@fluid-app/subscriptions-ui": "0.1.0",
98
98
  "@fluid-app/subscriptions-core": "0.1.0",
99
+ "@fluid-app/subscriptions-ui": "0.1.0",
100
+ "@fluid-app/subscriptions-api-client": "0.1.0",
99
101
  "@fluid-app/typescript-config": "0.0.0",
100
- "@fluid-app/user-contacts-api-client": "0.1.0",
101
- "@fluid-app/user-notes-api-client": "0.1.0",
102
102
  "@fluid-app/ui-primitives": "0.1.13",
103
+ "@fluid-app/user-notes-api-client": "0.1.0",
104
+ "@fluid-app/user-contacts-api-client": "0.1.0",
103
105
  "@fluid-app/user-tasks-api-client": "0.1.0"
104
106
  },
105
107
  "peerDependencies": {
@@ -3,22 +3,13 @@
3
3
  *
4
4
  * Import this in your app's main CSS file:
5
5
  * @import "@fluid-app/portal-sdk/globals.css";
6
+ *
7
+ * For package styles without portal theme tokens (e.g. in fluid-admin):
8
+ * @import "@fluid-app/portal-sdk/packages.css";
6
9
  */
7
10
 
8
11
  /* ── Package CSS imports ─────────────────────────────────────────────────── */
9
- @import "@fluid-app/company-switcher-ui/styles/company-switcher.css";
10
- @import "@fluid-app/contacts-ui/styles/contacts.css";
11
- @import "@fluid-app/messaging-ui/styles/messaging.css";
12
- @import "@fluid-app/orders-ui/styles/orders.css";
13
- @import "@fluid-app/shop-ui/styles/shop.css";
14
- @import "@fluid-app/profile-ui/styles/profile.css";
15
- @import "@fluid-app/shareables-ui/styles/shareables.css";
16
- @import "@fluid-app/subscriptions-ui/styles/subscriptions.css";
17
- @import "@fluid-app/mysite-ui/styles/mysite.css";
18
- @import "@fluid-app/ui-primitives/styles/ui-primitives.css";
19
- @import "@fluid-app/portal-widgets/globals.css";
20
- @import "@fluid-app/portal-preview/styles/preview.css";
21
- @import "@fluid-app/portal-react/globals.css";
12
+ @import "./packages.css";
22
13
 
23
14
  /* ── Source ──────────────────────────────────────────────────────────────── */
24
15
  @source "../src/**/*.{ts,tsx}";
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Shared package CSS imports — safe to use in any app.
3
+ *
4
+ * Import this when you need the package styles without the portal theme tokens:
5
+ * @import "@fluid-app/portal-sdk/packages.css";
6
+ */
7
+
8
+ @import "@fluid-app/company-switcher-ui/styles/company-switcher.css";
9
+ @import "@fluid-app/contacts-ui/styles/contacts.css";
10
+ @import "@fluid-app/messaging-ui/styles/messaging.css";
11
+ @import "@fluid-app/orders-ui/styles/orders.css";
12
+ @import "@fluid-app/shop-ui/styles/shop.css";
13
+ @import "@fluid-app/profile-ui/styles/profile.css";
14
+ @import "@fluid-app/shareables-ui/styles/shareables.css";
15
+ @import "@fluid-app/subscriptions-ui/styles/subscriptions.css";
16
+ @import "@fluid-app/mysite-ui/styles/mysite.css";
17
+ @import "@fluid-app/ui-primitives/styles/ui-primitives.css";
18
+ @import "@fluid-app/portal-widgets/globals.css";
19
+ @import "@fluid-app/portal-preview/styles/preview.css";
20
+ @import "@fluid-app/portal-react/globals.css";