@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.
- package/dist/{MySiteScreen-BgyCPMby.mjs → MySiteScreen-Dbvaw1nu.mjs} +0 -1
- package/dist/{OrdersScreen-BB7vBz1g.mjs → OrdersScreen-QFndb0Oi.mjs} +0 -1
- package/dist/{ProductsScreen-BDCW_OdU.mjs → ProductsScreen-HaYLhqt1.mjs} +0 -1
- package/dist/{ShareablesScreen-BuR6Mei7.mjs → ShareablesScreen-xqMDNJ6m.mjs} +0 -1
- package/dist/{ShopScreen-B3I6PRkp.mjs → ShopScreen-D2cMeKSk.mjs} +0 -1
- package/dist/{SubscriptionsScreen-V3mnPdFM.mjs → SubscriptionsScreen-DxVV0sZx.mjs} +0 -1
- package/dist/index.mjs +12 -12
- package/dist/vite/index.cjs +90 -2
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.mjs +89 -1
- package/dist/vite/index.mjs.map +1 -1
- package/package.json +16 -14
- package/styles/globals.css +4 -13
- package/styles/packages.css +20 -0
|
@@ -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-
|
|
2406
|
-
const SubscriptionsScreen$1 = lazy(() => import("./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-
|
|
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-
|
|
2412
|
-
const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-
|
|
2413
|
-
const MySiteScreen$1 = lazy(() => import("./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-
|
|
3819
|
-
SubscriptionsScreen: () => import("./SubscriptionsScreen-
|
|
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-
|
|
3822
|
-
MySiteScreen: () => import("./MySiteScreen-
|
|
3823
|
-
ShareablesScreen: () => import("./ShareablesScreen-
|
|
3824
|
-
ShopScreen: () => import("./ShopScreen-
|
|
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
|
};
|
package/dist/vite/index.cjs
CHANGED
|
@@ -1,6 +1,94 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../chunk-DAgNkxik.cjs");
|
|
3
|
-
|
|
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 =
|
|
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"));
|
package/dist/vite/index.cjs.map
CHANGED
|
@@ -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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"}
|
package/dist/vite/index.mjs
CHANGED
|
@@ -1,4 +1,92 @@
|
|
|
1
|
-
|
|
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.
|
package/dist/vite/index.mjs.map
CHANGED
|
@@ -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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/portal-sdk",
|
|
3
|
-
"version": "0.1.
|
|
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/
|
|
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": {
|
package/styles/globals.css
CHANGED
|
@@ -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 "
|
|
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";
|