@vetta-org/plugin-vite 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/build-warning-filter.d.ts +8 -0
  2. package/dist/build-warning-filter.d.ts.map +1 -0
  3. package/dist/build-warning-filter.js +26 -0
  4. package/dist/build-warning-filter.js.map +1 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +83 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/dev-events.d.ts +23 -0
  10. package/dist/dev-events.d.ts.map +1 -0
  11. package/dist/dev-events.js +9 -0
  12. package/dist/dev-events.js.map +1 -0
  13. package/dist/dev-server.d.ts +9 -0
  14. package/dist/dev-server.d.ts.map +1 -0
  15. package/dist/dev-server.js +239 -0
  16. package/dist/dev-server.js.map +1 -0
  17. package/dist/dev-vite-plugins.d.ts +5 -0
  18. package/dist/dev-vite-plugins.d.ts.map +1 -0
  19. package/dist/dev-vite-plugins.js +144 -0
  20. package/dist/dev-vite-plugins.js.map +1 -0
  21. package/dist/host-theme.d.ts +5 -0
  22. package/dist/host-theme.d.ts.map +1 -0
  23. package/dist/host-theme.js +42 -0
  24. package/dist/host-theme.js.map +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +89 -4
  28. package/dist/index.js.map +1 -1
  29. package/dist/pack.d.ts +4 -0
  30. package/dist/pack.d.ts.map +1 -1
  31. package/dist/pack.js +45 -89
  32. package/dist/pack.js.map +1 -1
  33. package/dist/permission-contract.d.ts +13 -0
  34. package/dist/permission-contract.d.ts.map +1 -0
  35. package/dist/permission-contract.js +94 -0
  36. package/dist/permission-contract.js.map +1 -0
  37. package/dist/request-query.d.ts +8 -0
  38. package/dist/request-query.d.ts.map +1 -0
  39. package/dist/request-query.js +19 -0
  40. package/dist/request-query.js.map +1 -0
  41. package/dist/style-scope.d.ts +8 -0
  42. package/dist/style-scope.d.ts.map +1 -1
  43. package/dist/style-scope.js +102 -3
  44. package/dist/style-scope.js.map +1 -1
  45. package/package.json +13 -3
@@ -0,0 +1,144 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { readFileSync } from "node:fs";
3
+ import { relative, resolve } from "node:path";
4
+ import { parsePluginManifest } from "@vetta-org/plugin-sdk/manifest";
5
+ import { emitVettaPluginDevEvent } from "./dev-events.js";
6
+ export const VETTA_PLUGIN_DEV_ENTRY_ID = "virtual:vetta-plugin-dev-entry";
7
+ const RESOLVED_DEV_ENTRY_ID = `\0${VETTA_PLUGIN_DEV_ENTRY_ID}`;
8
+ const DEV_PREAMBLE_PATH = "/@vetta-plugin-dev-preamble";
9
+ export function isVettaPluginDevServer() {
10
+ return process.env.VETTA_PLUGIN_DEV_SERVER === "1";
11
+ }
12
+ function readPluginId(config) {
13
+ const raw = JSON.parse(readFileSync(resolve(config.root, "plugin.json"), "utf8"));
14
+ return parsePluginManifest(raw).id;
15
+ }
16
+ function createDevEntryPlugin(entry) {
17
+ let pluginId = "";
18
+ let entryUrl = "";
19
+ return {
20
+ name: "vetta-plugin-dev-entry",
21
+ apply: "serve",
22
+ configResolved(config) {
23
+ pluginId = readPluginId(config);
24
+ entryUrl = `/${relative(config.root, resolve(config.root, entry)).replaceAll("\\", "/")}`;
25
+ },
26
+ resolveId(id) {
27
+ return id === VETTA_PLUGIN_DEV_ENTRY_ID ? RESOLVED_DEV_ENTRY_ID : undefined;
28
+ },
29
+ load(id) {
30
+ if (id !== RESOLVED_DEV_ENTRY_ID)
31
+ return;
32
+ return `
33
+ import * as pluginModule from ${JSON.stringify(entryUrl)};
34
+
35
+ const moduleStore = globalThis.__VETTA_PLUGIN_DEV_MODULES__ ??= new Map();
36
+ moduleStore.set(${JSON.stringify(pluginId)}, pluginModule);
37
+
38
+ export * from ${JSON.stringify(entryUrl)};
39
+ export default pluginModule.default;
40
+
41
+ if (import.meta.hot) {
42
+ import.meta.hot.accept(${JSON.stringify(entryUrl)}, (nextModule) => {
43
+ if (!nextModule) return;
44
+ moduleStore.set(${JSON.stringify(pluginId)}, nextModule);
45
+ import.meta.hot.send("vetta:plugin-lifecycle-reload", {
46
+ pluginId: ${JSON.stringify(pluginId)},
47
+ reason: "entry",
48
+ path: ${JSON.stringify(entryUrl)},
49
+ });
50
+ });
51
+ import.meta.hot.on("vetta:plugin-full-reload", async (reload) => {
52
+ const nextModule = await import(${JSON.stringify(`${entryUrl}?vetta-reload=`)} + Date.now());
53
+ moduleStore.set(${JSON.stringify(pluginId)}, nextModule);
54
+ import.meta.hot.send("vetta:plugin-lifecycle-reload", {
55
+ pluginId: ${JSON.stringify(pluginId)},
56
+ reason: "full-reload",
57
+ path: reload?.path,
58
+ triggeredBy: reload?.triggeredBy,
59
+ });
60
+ });
61
+ }
62
+ `;
63
+ },
64
+ };
65
+ }
66
+ function createDevRuntimePlugin() {
67
+ let pluginId = "";
68
+ return {
69
+ name: "vetta-plugin-dev-runtime",
70
+ apply: "serve",
71
+ configResolved(config) {
72
+ pluginId = readPluginId(config);
73
+ },
74
+ configureServer(server) {
75
+ server.middlewares.use((request, response, next) => {
76
+ if (request.url?.split("?", 1)[0] !== DEV_PREAMBLE_PATH) {
77
+ next();
78
+ return;
79
+ }
80
+ response.statusCode = 200;
81
+ response.setHeader("Content-Type", "text/javascript");
82
+ response.end(`
83
+ import RefreshRuntime from "/@react-refresh";
84
+ RefreshRuntime.injectIntoGlobalHook(window);
85
+ window.$RefreshReg$ = () => {};
86
+ window.$RefreshSig$ = () => (type) => type;
87
+ window.__vite_plugin_react_preamble_installed__ = true;
88
+ `);
89
+ });
90
+ server.ws.on("vetta:plugin-lifecycle-reload", (data) => {
91
+ const reason = typeof data === "object" && data !== null && "reason" in data && data.reason === "full-reload"
92
+ ? "full-reload"
93
+ : "entry";
94
+ const path = readOptionalString(data, "path");
95
+ const triggeredBy = readOptionalString(data, "triggeredBy");
96
+ emitVettaPluginDevEvent({
97
+ type: "update",
98
+ pluginId,
99
+ reason,
100
+ ...(path ? { path } : {}),
101
+ ...(triggeredBy ? { triggeredBy } : {}),
102
+ });
103
+ });
104
+ const send = server.ws.send.bind(server.ws);
105
+ server.ws.send = ((payloadOrEvent, data) => {
106
+ if (typeof payloadOrEvent === "object" && payloadOrEvent !== null && "type" in payloadOrEvent) {
107
+ if (payloadOrEvent.type === "full-reload") {
108
+ send({
109
+ type: "custom",
110
+ event: "vetta:plugin-full-reload",
111
+ data: {
112
+ path: readOptionalString(payloadOrEvent, "path"),
113
+ triggeredBy: readOptionalString(payloadOrEvent, "triggeredBy"),
114
+ },
115
+ });
116
+ return;
117
+ }
118
+ if (payloadOrEvent.type === "error" && "err" in payloadOrEvent) {
119
+ const error = payloadOrEvent.err;
120
+ const message = typeof error === "object" && error !== null && "message" in error
121
+ ? String(error.message)
122
+ : String(error);
123
+ emitVettaPluginDevEvent({ type: "error", pluginId, message });
124
+ }
125
+ }
126
+ if (typeof payloadOrEvent === "string") {
127
+ send(payloadOrEvent, data);
128
+ return;
129
+ }
130
+ send(payloadOrEvent);
131
+ });
132
+ },
133
+ };
134
+ }
135
+ function readOptionalString(value, key) {
136
+ if (typeof value !== "object" || value === null || !(key in value))
137
+ return undefined;
138
+ const candidate = value[key];
139
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : undefined;
140
+ }
141
+ export function createVettaPluginDevPlugins(entry) {
142
+ return [react(), createDevEntryPlugin(entry), createDevRuntimePlugin()];
143
+ }
144
+ //# sourceMappingURL=dev-vite-plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-vite-plugins.js","sourceRoot":"","sources":["../src/dev-vite-plugins.ts"],"names":[],"mappings":"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","sourcesContent":["import react from \"@vitejs/plugin-react\";\nimport { readFileSync } from \"node:fs\";\nimport { relative, resolve } from \"node:path\";\nimport { parsePluginManifest } from \"@vetta-org/plugin-sdk/manifest\";\nimport type { HMRPayload, Plugin, PluginOption, ResolvedConfig } from \"vite\";\nimport { emitVettaPluginDevEvent } from \"./dev-events.js\";\n\nexport const VETTA_PLUGIN_DEV_ENTRY_ID = \"virtual:vetta-plugin-dev-entry\";\n\nconst RESOLVED_DEV_ENTRY_ID = `\\0${VETTA_PLUGIN_DEV_ENTRY_ID}`;\nconst DEV_PREAMBLE_PATH = \"/@vetta-plugin-dev-preamble\";\n\nexport function isVettaPluginDevServer(): boolean {\n\treturn process.env.VETTA_PLUGIN_DEV_SERVER === \"1\";\n}\n\nfunction readPluginId(config: ResolvedConfig): string {\n\tconst raw: unknown = JSON.parse(readFileSync(resolve(config.root, \"plugin.json\"), \"utf8\"));\n\treturn parsePluginManifest(raw).id;\n}\n\nfunction createDevEntryPlugin(entry: string): Plugin {\n\tlet pluginId = \"\";\n\tlet entryUrl = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-dev-entry\",\n\t\tapply: \"serve\",\n\t\tconfigResolved(config) {\n\t\t\tpluginId = readPluginId(config);\n\t\t\tentryUrl = `/${relative(config.root, resolve(config.root, entry)).replaceAll(\"\\\\\", \"/\")}`;\n\t\t},\n\t\tresolveId(id) {\n\t\t\treturn id === VETTA_PLUGIN_DEV_ENTRY_ID ? RESOLVED_DEV_ENTRY_ID : undefined;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== RESOLVED_DEV_ENTRY_ID) return;\n\t\t\treturn `\nimport * as pluginModule from ${JSON.stringify(entryUrl)};\n\nconst moduleStore = globalThis.__VETTA_PLUGIN_DEV_MODULES__ ??= new Map();\nmoduleStore.set(${JSON.stringify(pluginId)}, pluginModule);\n\nexport * from ${JSON.stringify(entryUrl)};\nexport default pluginModule.default;\n\nif (import.meta.hot) {\n import.meta.hot.accept(${JSON.stringify(entryUrl)}, (nextModule) => {\n if (!nextModule) return;\n moduleStore.set(${JSON.stringify(pluginId)}, nextModule);\n import.meta.hot.send(\"vetta:plugin-lifecycle-reload\", {\n pluginId: ${JSON.stringify(pluginId)},\n reason: \"entry\",\n\t path: ${JSON.stringify(entryUrl)},\n });\n });\n import.meta.hot.on(\"vetta:plugin-full-reload\", async (reload) => {\n const nextModule = await import(${JSON.stringify(`${entryUrl}?vetta-reload=`)} + Date.now());\n moduleStore.set(${JSON.stringify(pluginId)}, nextModule);\n import.meta.hot.send(\"vetta:plugin-lifecycle-reload\", {\n pluginId: ${JSON.stringify(pluginId)},\n reason: \"full-reload\",\n\t path: reload?.path,\n\t triggeredBy: reload?.triggeredBy,\n });\n });\n}\n`;\n\t\t},\n\t};\n}\n\nfunction createDevRuntimePlugin(): Plugin {\n\tlet pluginId = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-dev-runtime\",\n\t\tapply: \"serve\",\n\t\tconfigResolved(config) {\n\t\t\tpluginId = readPluginId(config);\n\t\t},\n\t\tconfigureServer(server) {\n\t\t\tserver.middlewares.use((request, response, next) => {\n\t\t\t\tif (request.url?.split(\"?\", 1)[0] !== DEV_PREAMBLE_PATH) {\n\t\t\t\t\tnext();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tresponse.statusCode = 200;\n\t\t\t\tresponse.setHeader(\"Content-Type\", \"text/javascript\");\n\t\t\t\tresponse.end(`\nimport RefreshRuntime from \"/@react-refresh\";\nRefreshRuntime.injectIntoGlobalHook(window);\nwindow.$RefreshReg$ = () => {};\nwindow.$RefreshSig$ = () => (type) => type;\nwindow.__vite_plugin_react_preamble_installed__ = true;\n`);\n\t\t\t});\n\n\t\t\tserver.ws.on(\"vetta:plugin-lifecycle-reload\", (data) => {\n\t\t\t\tconst reason =\n\t\t\t\t\ttypeof data === \"object\" && data !== null && \"reason\" in data && data.reason === \"full-reload\"\n\t\t\t\t\t\t? \"full-reload\"\n\t\t\t\t\t\t: \"entry\";\n\t\t\t\tconst path = readOptionalString(data, \"path\");\n\t\t\t\tconst triggeredBy = readOptionalString(data, \"triggeredBy\");\n\t\t\t\temitVettaPluginDevEvent({\n\t\t\t\t\ttype: \"update\",\n\t\t\t\t\tpluginId,\n\t\t\t\t\treason,\n\t\t\t\t\t...(path ? { path } : {}),\n\t\t\t\t\t...(triggeredBy ? { triggeredBy } : {}),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tconst send = server.ws.send.bind(server.ws);\n\t\t\tserver.ws.send = ((payloadOrEvent: unknown, data?: unknown) => {\n\t\t\t\tif (typeof payloadOrEvent === \"object\" && payloadOrEvent !== null && \"type\" in payloadOrEvent) {\n\t\t\t\t\tif (payloadOrEvent.type === \"full-reload\") {\n\t\t\t\t\t\tsend({\n\t\t\t\t\t\t\ttype: \"custom\",\n\t\t\t\t\t\t\tevent: \"vetta:plugin-full-reload\",\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tpath: readOptionalString(payloadOrEvent, \"path\"),\n\t\t\t\t\t\t\t\ttriggeredBy: readOptionalString(payloadOrEvent, \"triggeredBy\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (payloadOrEvent.type === \"error\" && \"err\" in payloadOrEvent) {\n\t\t\t\t\t\tconst error = payloadOrEvent.err;\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\ttypeof error === \"object\" && error !== null && \"message\" in error\n\t\t\t\t\t\t\t\t? String(error.message)\n\t\t\t\t\t\t\t\t: String(error);\n\t\t\t\t\t\temitVettaPluginDevEvent({ type: \"error\", pluginId, message });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (typeof payloadOrEvent === \"string\") {\n\t\t\t\t\tsend(payloadOrEvent, data);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsend(payloadOrEvent as HMRPayload);\n\t\t\t}) as typeof server.ws.send;\n\t\t},\n\t};\n}\n\nfunction readOptionalString(value: unknown, key: string): string | undefined {\n\tif (typeof value !== \"object\" || value === null || !(key in value)) return undefined;\n\tconst candidate = (value as Record<string, unknown>)[key];\n\treturn typeof candidate === \"string\" && candidate.length > 0 ? candidate : undefined;\n}\n\nexport function createVettaPluginDevPlugins(entry: string): PluginOption[] {\n\treturn [react(), createDevEntryPlugin(entry), createDevRuntimePlugin()];\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from "vite";
2
+ export declare const HOST_THEME_STYLESHEET_ID = "@vetta-org/plugin-sdk/tailwind-theme.css";
3
+ export declare function injectHostThemeBridge(css: string): string | undefined;
4
+ export declare function createHostThemeBridgePlugin(): Plugin;
5
+ //# sourceMappingURL=host-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-theme.d.ts","sourceRoot":"","sources":["../src/host-theme.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,eAAO,MAAM,wBAAwB,6CAA6C,CAAC;AAMnF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrE;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAkBpD","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { isAbsolute, relative } from \"node:path\";\nimport type { Plugin } from \"vite\";\n\nexport const HOST_THEME_STYLESHEET_ID = \"@vetta-org/plugin-sdk/tailwind-theme.css\";\n\nconst HOST_THEME_IMPORT = `@import \"${HOST_THEME_STYLESHEET_ID}\";`;\nconst HOST_THEME_IMPORT_PATTERN = /@import\\s+[\"']@vetta-org\\/plugin-sdk\\/tailwind-theme\\.css[\"']\\s*;/u;\nconst TAILWIND_IMPORT_PATTERN = /@import\\s+[\"']tailwindcss(?:\\/(?:theme|utilities)\\.css)?[\"'](?:\\s+layer\\([^)]*\\))?\\s*;/u;\n\nexport function injectHostThemeBridge(css: string): string | undefined {\n\tif (HOST_THEME_IMPORT_PATTERN.test(css)) return;\n\tconst tailwindImport = TAILWIND_IMPORT_PATTERN.exec(css);\n\tif (!tailwindImport || tailwindImport.index === undefined) return;\n\tconst insertionIndex = tailwindImport.index + tailwindImport[0].length;\n\treturn `${css.slice(0, insertionIndex)}\\n${HOST_THEME_IMPORT}${css.slice(insertionIndex)}`;\n}\n\nexport function createHostThemeBridgePlugin(): Plugin {\n\tlet rootDir = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-host-theme\",\n\t\tenforce: \"pre\",\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id.includes(\"?\") || id.includes(\"\\0\") || !id.endsWith(\".css\") || !isInsideRoot(id, rootDir)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst css = await readFile(id, \"utf8\").catch(() => null);\n\t\t\tif (css === null) return;\n\t\t\tconst injected = injectHostThemeBridge(css);\n\t\t\treturn injected ? { code: injected, map: null } : undefined;\n\t\t},\n\t};\n}\n\nfunction isInsideRoot(filePath: string, rootDir: string): boolean {\n\tif (!rootDir || !isAbsolute(filePath)) return false;\n\tconst relativePath = relative(rootDir, filePath);\n\treturn relativePath !== \"\" && !relativePath.startsWith(\"..\") && !isAbsolute(relativePath);\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { isAbsolute, relative } from "node:path";
3
+ export const HOST_THEME_STYLESHEET_ID = "@vetta-org/plugin-sdk/tailwind-theme.css";
4
+ const HOST_THEME_IMPORT = `@import "${HOST_THEME_STYLESHEET_ID}";`;
5
+ const HOST_THEME_IMPORT_PATTERN = /@import\s+["']@vetta-org\/plugin-sdk\/tailwind-theme\.css["']\s*;/u;
6
+ const TAILWIND_IMPORT_PATTERN = /@import\s+["']tailwindcss(?:\/(?:theme|utilities)\.css)?["'](?:\s+layer\([^)]*\))?\s*;/u;
7
+ export function injectHostThemeBridge(css) {
8
+ if (HOST_THEME_IMPORT_PATTERN.test(css))
9
+ return;
10
+ const tailwindImport = TAILWIND_IMPORT_PATTERN.exec(css);
11
+ if (!tailwindImport || tailwindImport.index === undefined)
12
+ return;
13
+ const insertionIndex = tailwindImport.index + tailwindImport[0].length;
14
+ return `${css.slice(0, insertionIndex)}\n${HOST_THEME_IMPORT}${css.slice(insertionIndex)}`;
15
+ }
16
+ export function createHostThemeBridgePlugin() {
17
+ let rootDir = "";
18
+ return {
19
+ name: "vetta-plugin-host-theme",
20
+ enforce: "pre",
21
+ configResolved(config) {
22
+ rootDir = config.root;
23
+ },
24
+ async load(id) {
25
+ if (id.includes("?") || id.includes("\0") || !id.endsWith(".css") || !isInsideRoot(id, rootDir)) {
26
+ return;
27
+ }
28
+ const css = await readFile(id, "utf8").catch(() => null);
29
+ if (css === null)
30
+ return;
31
+ const injected = injectHostThemeBridge(css);
32
+ return injected ? { code: injected, map: null } : undefined;
33
+ },
34
+ };
35
+ }
36
+ function isInsideRoot(filePath, rootDir) {
37
+ if (!rootDir || !isAbsolute(filePath))
38
+ return false;
39
+ const relativePath = relative(rootDir, filePath);
40
+ return relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute(relativePath);
41
+ }
42
+ //# sourceMappingURL=host-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-theme.js","sourceRoot":"","sources":["../src/host-theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,0CAA0C,CAAC;AAEnF,MAAM,iBAAiB,GAAG,YAAY,wBAAwB,IAAI,CAAC;AACnE,MAAM,yBAAyB,GAAG,oEAAoE,CAAC;AACvG,MAAM,uBAAuB,GAAG,yFAAyF,CAAC;AAE1H,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAsB;IACtE,IAAI,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO;IAChD,MAAM,cAAc,GAAG,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO;IAClE,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACvE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,iBAAiB,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;AAAA,CAC3F;AAED,MAAM,UAAU,2BAA2B,GAAW;IACrD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,OAAO;QACN,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,KAAK;QACd,cAAc,CAAC,MAAM,EAAE;YACtB,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;QAAA,CACtB;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;gBACjG,OAAO;YACR,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACzD,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO;YACzB,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAAA,CAC5D;KACD,CAAC;AAAA,CACF;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,OAAe,EAAW;IACjE,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjD,OAAO,YAAY,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAAA,CAC1F","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { isAbsolute, relative } from \"node:path\";\nimport type { Plugin } from \"vite\";\n\nexport const HOST_THEME_STYLESHEET_ID = \"@vetta-org/plugin-sdk/tailwind-theme.css\";\n\nconst HOST_THEME_IMPORT = `@import \"${HOST_THEME_STYLESHEET_ID}\";`;\nconst HOST_THEME_IMPORT_PATTERN = /@import\\s+[\"']@vetta-org\\/plugin-sdk\\/tailwind-theme\\.css[\"']\\s*;/u;\nconst TAILWIND_IMPORT_PATTERN = /@import\\s+[\"']tailwindcss(?:\\/(?:theme|utilities)\\.css)?[\"'](?:\\s+layer\\([^)]*\\))?\\s*;/u;\n\nexport function injectHostThemeBridge(css: string): string | undefined {\n\tif (HOST_THEME_IMPORT_PATTERN.test(css)) return;\n\tconst tailwindImport = TAILWIND_IMPORT_PATTERN.exec(css);\n\tif (!tailwindImport || tailwindImport.index === undefined) return;\n\tconst insertionIndex = tailwindImport.index + tailwindImport[0].length;\n\treturn `${css.slice(0, insertionIndex)}\\n${HOST_THEME_IMPORT}${css.slice(insertionIndex)}`;\n}\n\nexport function createHostThemeBridgePlugin(): Plugin {\n\tlet rootDir = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-host-theme\",\n\t\tenforce: \"pre\",\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id.includes(\"?\") || id.includes(\"\\0\") || !id.endsWith(\".css\") || !isInsideRoot(id, rootDir)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst css = await readFile(id, \"utf8\").catch(() => null);\n\t\t\tif (css === null) return;\n\t\t\tconst injected = injectHostThemeBridge(css);\n\t\t\treturn injected ? { code: injected, map: null } : undefined;\n\t\t},\n\t};\n}\n\nfunction isInsideRoot(filePath: string, rootDir: string): boolean {\n\tif (!rootDir || !isAbsolute(filePath)) return false;\n\tconst relativePath = relative(rootDir, filePath);\n\treturn relativePath !== \"\" && !relativePath.startsWith(\"..\") && !isAbsolute(relativePath);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface VettaPluginFederationOptions {
10
10
  entry?: string;
11
11
  manifestFileName?: string;
12
12
  remoteEntryFileName?: string;
13
+ /** Share the narrow host UI contract exposed by `@vetta/theme-ui/plugin-ui`. */
14
+ hostThemeUi?: boolean;
13
15
  shared?: ModuleFederationOptions["shared"];
14
16
  package?: boolean | VettaPluginPackageOptions;
15
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,KAAK,+BAA+B,EAA4B,MAAM,WAAW,CAAC;AAG3F,MAAM,WAAW,yBAA0B,SAAQ,IAAI,CAAC,+BAA+B,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9G,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;CAC9C;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,4BAA4B,GAAG,uBAAuB,CAuChH;AA+DD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,YAAY,EAAE,CAc3F","sourcesContent":["import { federation, type ModuleFederationOptions } from \"@module-federation/vite\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport { type CreateVettaPluginPackageOptions, createVettaPluginPackage } from \"./pack.js\";\nimport { createPluginStyleScopePlugin } from \"./style-scope.js\";\n\nexport interface VettaPluginPackageOptions extends Omit<CreateVettaPluginPackageOptions, \"rootDir\" | \"distDir\"> {\n\tenabled?: boolean;\n}\n\nexport interface VettaPluginFederationOptions {\n\tname: string;\n\texpose?: string;\n\tentry?: string;\n\tmanifestFileName?: string;\n\tremoteEntryFileName?: string;\n\tshared?: ModuleFederationOptions[\"shared\"];\n\tpackage?: boolean | VettaPluginPackageOptions;\n}\n\nexport function createVettaPluginFederationConfig(options: VettaPluginFederationOptions): ModuleFederationOptions {\n\tconst expose = options.expose ?? \"./plugin\";\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\treturn {\n\t\tname: options.name,\n\t\tfilename: options.remoteEntryFileName ?? \"remoteEntry.js\",\n\t\texposes: {\n\t\t\t[expose]: entry,\n\t\t},\n\t\tmanifest: {\n\t\t\tfileName: options.manifestFileName ?? \"mf-manifest.json\",\n\t\t},\n\t\tdts: false,\n\t\tshared: {\n\t\t\treact: {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t\"react-dom\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Match host plugin-shared-modules (tldraw remotes may require this subpath).\n\t\t\t\"react-dom/client\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Host design-system primitives; runtime provided by desktop-app share scope.\n\t\t\t\"@vetta/ui\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t...options.shared,\n\t\t},\n\t};\n}\n\nfunction createBuildDefaultsPlugin(entry: string): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-build-defaults\",\n\t\tapply: \"build\",\n\t\tconfig() {\n\t\t\treturn {\n\t\t\t\tbuild: {\n\t\t\t\t\trollupOptions: {\n\t\t\t\t\t\tinput: entry,\n\t\t\t\t\t\t// Host-provided singletons (see desktop-app plugin-shared-modules + vetta-host protocol).\n\t\t\t\t\t\texternal: [\"@vetta-org/plugin-sdk\", \"@vetta/ui\"],\n\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\tassetFileNames(assetInfo) {\n\t\t\t\t\t\t\t\treturn assetInfo.names.some((name) => name.endsWith(\".css\"))\n\t\t\t\t\t\t\t\t\t? \"style.css\"\n\t\t\t\t\t\t\t\t\t: \"assets/[name]-[hash][extname]\";\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpaths: {\n\t\t\t\t\t\t\t\t\"@vetta-org/plugin-sdk\": \"vetta-host://plugin-sdk\",\n\t\t\t\t\t\t\t\t\"@vetta/ui\": \"vetta-host://ui\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction createPackagePlugin(options: VettaPluginPackageOptions): Plugin {\n\tlet rootDir = \"\";\n\tlet distDir = \"\";\n\tlet buildFailed = false;\n\n\treturn {\n\t\tname: \"vetta-plugin-package\",\n\t\tapply: \"build\",\n\t\tbuildStart() {\n\t\t\tbuildFailed = false;\n\t\t},\n\t\tbuildEnd(error) {\n\t\t\tbuildFailed = error !== undefined;\n\t\t},\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t\tdistDir = config.build.outDir;\n\t\t},\n\t\tasync closeBundle() {\n\t\t\tif (options.enabled === false || buildFailed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await createVettaPluginPackage({\n\t\t\t\t...options,\n\t\t\t\trootDir,\n\t\t\t\tdistDir,\n\t\t\t});\n\t\t\tconsole.log(`[vetta-plugin-vite] Wrote ${result.outputPath} with ${result.files.length} runtime files`);\n\t\t},\n\t};\n}\n\nexport function vettaPluginFederation(options: VettaPluginFederationOptions): PluginOption[] {\n\tconst packageOptions = typeof options.package === \"object\" ? options.package : {};\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\tconst plugins: PluginOption[] = [\n\t\tcreateBuildDefaultsPlugin(entry),\n\t\t...federation(createVettaPluginFederationConfig(options)),\n\t\tcreatePluginStyleScopePlugin(),\n\t];\n\t// VETTA_PLUGIN_DEV_WATCH=1:宿主 dev 热更新的 `vite build --watch` 只需要 dist,\n\t// 跳过每次增量重建都重打 zip(closeBundle 在 watch 模式每轮都会触发)。\n\tif (options.package !== false && process.env.VETTA_PLUGIN_DEV_WATCH !== \"1\") {\n\t\tplugins.push(createPackagePlugin(packageOptions));\n\t}\n\treturn plugins;\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAInF,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,MAAM,CAAC;AAQjD,OAAO,EAAE,KAAK,+BAA+B,EAA4B,MAAM,WAAW,CAAC;AAQ3F,MAAM,WAAW,yBAA0B,SAAQ,IAAI,CAAC,+BAA+B,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9G,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;CAC9C;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,4BAA4B,GAAG,uBAAuB,CAsDhH;AAyHD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,YAAY,EAAE,CAwB3F","sourcesContent":["import { federation, type ModuleFederationOptions } from \"@module-federation/vite\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parsePluginManifest } from \"@vetta-org/plugin-sdk/manifest\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport {\n\tcreateVettaPluginDevPlugins,\n\tisVettaPluginDevServer,\n\tVETTA_PLUGIN_DEV_ENTRY_ID,\n} from \"./dev-vite-plugins.js\";\nimport { createPluginBuildWarningFilter } from \"./build-warning-filter.js\";\nimport { createHostThemeBridgePlugin } from \"./host-theme.js\";\nimport { type CreateVettaPluginPackageOptions, createVettaPluginPackage } from \"./pack.js\";\nimport { assertPluginPermissionContract } from \"./permission-contract.js\";\nimport { createPluginStyleScopePlugin } from \"./style-scope.js\";\n\nconst SHARED_REACT_COMMONJS_BRIDGE_ID = \"virtual:vetta-plugin-shared-react-commonjs\";\nconst RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID = `\\0${SHARED_REACT_COMMONJS_BRIDGE_ID}`;\nconst STATIC_REACT_REQUIRE_PATTERN = /\\brequire\\s*\\(\\s*([\"'])react\\1\\s*\\)/gu;\n\nexport interface VettaPluginPackageOptions extends Omit<CreateVettaPluginPackageOptions, \"rootDir\" | \"distDir\"> {\n\tenabled?: boolean;\n}\n\nexport interface VettaPluginFederationOptions {\n\tname: string;\n\texpose?: string;\n\tentry?: string;\n\tmanifestFileName?: string;\n\tremoteEntryFileName?: string;\n\t/** Share the narrow host UI contract exposed by `@vetta/theme-ui/plugin-ui`. */\n\thostThemeUi?: boolean;\n\tshared?: ModuleFederationOptions[\"shared\"];\n\tpackage?: boolean | VettaPluginPackageOptions;\n}\n\nexport function createVettaPluginFederationConfig(options: VettaPluginFederationOptions): ModuleFederationOptions {\n\tconst expose = options.expose ?? \"./plugin\";\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\treturn {\n\t\tname: options.name,\n\t\tfilename: options.remoteEntryFileName ?? \"remoteEntry.js\",\n\t\texposes: {\n\t\t\t[expose]: entry,\n\t\t},\n\t\tmanifest: {\n\t\t\tfileName: options.manifestFileName ?? \"mf-manifest.json\",\n\t\t},\n\t\tdts: false,\n\t\tshared: {\n\t\t\t\"@vetta-org/plugin-sdk\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\treact: {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t\"react-dom\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Match host plugin-shared-modules (tldraw remotes may require this subpath).\n\t\t\t\"react-dom/client\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Host design-system primitives; runtime provided by desktop-app share scope.\n\t\t\t\"@vetta/ui\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t...(options.hostThemeUi\n\t\t\t\t? {\n\t\t\t\t\t\t// Host-built UI components (model selector, …); opt in to keep unrelated plugins decoupled.\n\t\t\t\t\t\t\"@vetta/theme-ui/plugin-ui\": {\n\t\t\t\t\t\t\tsingleton: true,\n\t\t\t\t\t\t\timport: false,\n\t\t\t\t\t\t\trequiredVersion: \"*\",\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...options.shared,\n\t\t},\n\t};\n}\n\nfunction createBuildDefaultsPlugin(entry: string): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-build-defaults\",\n\t\tapply: \"build\",\n\t\tconfig() {\n\t\t\treturn {\n\t\t\t\t// Plugin remotes run inside the host page. Absolute asset URLs like\n\t\t\t\t// `/icon.png` resolve against the host origin (desktop-app public/), not\n\t\t\t\t// the remote. Prefer inlining small assets; large ones still go under\n\t\t\t\t// assets/ and rely on MF publicPath, but never land on host public/.\n\t\t\t\tbuild: {\n\t\t\t\t\tassetsInlineLimit: 32 * 1024,\n\t\t\t\t\trollupOptions: {\n\t\t\t\t\t\tinput: entry,\n\t\t\t\t\t\t// Host-provided singletons (see desktop-app plugin-shared-modules + vetta-host protocol).\n\t\t\t\t\t\texternal: [\"@vetta-org/plugin-sdk\", \"@vetta/ui\", \"@vetta/theme-ui/plugin-ui\"],\n\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\tassetFileNames(assetInfo) {\n\t\t\t\t\t\t\t\treturn assetInfo.names.some((name) => name.endsWith(\".css\"))\n\t\t\t\t\t\t\t\t\t? \"style.css\"\n\t\t\t\t\t\t\t\t\t: \"assets/[name]-[hash][extname]\";\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpaths: {\n\t\t\t\t\t\t\t\t\"@vetta-org/plugin-sdk\": \"vetta-host://plugin-sdk\",\n\t\t\t\t\t\t\t\t\"@vetta/ui\": \"vetta-host://ui\",\n\t\t\t\t\t\t\t\t\"@vetta/theme-ui/plugin-ui\": \"vetta-host://theme-ui-plugin\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\n// Module Federation exposes shared React through a virtual ESM module. Routing\n// static CommonJS requires through this namespace keeps Rollup's generated\n// bindings stable when dependencies such as use-sync-external-store are bundled.\nfunction createSharedReactCommonJsBridgePlugin(): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-shared-react-commonjs-bridge\",\n\t\tapply: \"build\",\n\t\tenforce: \"pre\",\n\t\ttransform(code) {\n\t\t\tif (!code.includes(\"require\") || !code.includes(\"react\")) return;\n\t\t\tconst transformed = code.replace(\n\t\t\t\tSTATIC_REACT_REQUIRE_PATTERN,\n\t\t\t\t`require(${JSON.stringify(SHARED_REACT_COMMONJS_BRIDGE_ID)})`,\n\t\t\t);\n\t\t\tif (transformed === code) return;\n\t\t\treturn { code: transformed, map: null };\n\t\t},\n\t\tresolveId(id) {\n\t\t\tif (id === SHARED_REACT_COMMONJS_BRIDGE_ID) return RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID) return;\n\t\t\treturn `import * as React from \"react\";\nexport * from \"react\";\nexport default React;\n`;\n\t\t},\n\t};\n}\n\nfunction createPackagePlugin(options: VettaPluginPackageOptions): Plugin {\n\tlet rootDir = \"\";\n\tlet distDir = \"\";\n\tlet buildFailed = false;\n\n\treturn {\n\t\tname: \"vetta-plugin-package\",\n\t\tapply: \"build\",\n\t\tbuildStart() {\n\t\t\tbuildFailed = false;\n\t\t},\n\t\tbuildEnd(error) {\n\t\t\tbuildFailed = error !== undefined;\n\t\t},\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t\tdistDir = config.build.outDir;\n\t\t},\n\t\tasync closeBundle() {\n\t\t\tif (options.enabled === false || buildFailed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await createVettaPluginPackage({\n\t\t\t\t...options,\n\t\t\t\trootDir,\n\t\t\t\tdistDir,\n\t\t\t});\n\t\t\tconsole.log(`[vetta-plugin-vite] Wrote ${result.outputPath} with ${result.files.length} runtime files`);\n\t\t},\n\t};\n}\n\nfunction createPermissionContractPlugin(): Plugin {\n\tlet rootDir = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-permission-contract\",\n\t\tapply: \"build\",\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t},\n\t\tasync generateBundle(_outputOptions, bundle) {\n\t\t\tconst manifest = parsePluginManifest(\n\t\t\t\tJSON.parse(await readFile(resolve(rootDir, \"plugin.json\"), \"utf8\")) as unknown,\n\t\t\t);\n\t\t\tassertPluginPermissionContract(\n\t\t\t\tmanifest,\n\t\t\t\tObject.values(bundle).flatMap((output) =>\n\t\t\t\t\toutput.type === \"chunk\" ? [{ fileName: output.fileName, code: output.code }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\t};\n}\n\nexport function vettaPluginFederation(options: VettaPluginFederationOptions): PluginOption[] {\n\tconst packageOptions = typeof options.package === \"object\" ? options.package : {};\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\tconst devServer = isVettaPluginDevServer();\n\tconst plugins: PluginOption[] = [\n\t\tcreatePluginBuildWarningFilter(),\n\t\tcreateHostThemeBridgePlugin(),\n\t\t...(devServer ? createVettaPluginDevPlugins(entry) : []),\n\t\tcreateBuildDefaultsPlugin(entry),\n\t\tcreateSharedReactCommonJsBridgePlugin(),\n\t\t...federation({\n\t\t\t...createVettaPluginFederationConfig(options),\n\t\t\texposes: {\n\t\t\t\t[options.expose ?? \"./plugin\"]: devServer ? VETTA_PLUGIN_DEV_ENTRY_ID : entry,\n\t\t\t},\n\t\t}),\n\t\tcreatePluginStyleScopePlugin(),\n\t\tcreatePermissionContractPlugin(),\n\t];\n\t// 兼容旧宿主的 build-watch 流程:增量构建时不重复打 zip。\n\tif (options.package !== false && process.env.VETTA_PLUGIN_DEV_WATCH !== \"1\") {\n\t\tplugins.push(createPackagePlugin(packageOptions));\n\t}\n\treturn plugins;\n}\n"]}
package/dist/index.js CHANGED
@@ -1,6 +1,16 @@
1
1
  import { federation } from "@module-federation/vite";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { parsePluginManifest } from "@vetta-org/plugin-sdk/manifest";
5
+ import { createVettaPluginDevPlugins, isVettaPluginDevServer, VETTA_PLUGIN_DEV_ENTRY_ID, } from "./dev-vite-plugins.js";
6
+ import { createPluginBuildWarningFilter } from "./build-warning-filter.js";
7
+ import { createHostThemeBridgePlugin } from "./host-theme.js";
2
8
  import { createVettaPluginPackage } from "./pack.js";
9
+ import { assertPluginPermissionContract } from "./permission-contract.js";
3
10
  import { createPluginStyleScopePlugin } from "./style-scope.js";
11
+ const SHARED_REACT_COMMONJS_BRIDGE_ID = "virtual:vetta-plugin-shared-react-commonjs";
12
+ const RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID = `\0${SHARED_REACT_COMMONJS_BRIDGE_ID}`;
13
+ const STATIC_REACT_REQUIRE_PATTERN = /\brequire\s*\(\s*(["'])react\1\s*\)/gu;
4
14
  export function createVettaPluginFederationConfig(options) {
5
15
  const expose = options.expose ?? "./plugin";
6
16
  const entry = options.entry ?? "./src/index.tsx";
@@ -15,6 +25,11 @@ export function createVettaPluginFederationConfig(options) {
15
25
  },
16
26
  dts: false,
17
27
  shared: {
28
+ "@vetta-org/plugin-sdk": {
29
+ singleton: true,
30
+ import: false,
31
+ requiredVersion: "*",
32
+ },
18
33
  react: {
19
34
  singleton: true,
20
35
  import: false,
@@ -37,6 +52,16 @@ export function createVettaPluginFederationConfig(options) {
37
52
  import: false,
38
53
  requiredVersion: "*",
39
54
  },
55
+ ...(options.hostThemeUi
56
+ ? {
57
+ // Host-built UI components (model selector, …); opt in to keep unrelated plugins decoupled.
58
+ "@vetta/theme-ui/plugin-ui": {
59
+ singleton: true,
60
+ import: false,
61
+ requiredVersion: "*",
62
+ },
63
+ }
64
+ : {}),
40
65
  ...options.shared,
41
66
  },
42
67
  };
@@ -47,11 +72,16 @@ function createBuildDefaultsPlugin(entry) {
47
72
  apply: "build",
48
73
  config() {
49
74
  return {
75
+ // Plugin remotes run inside the host page. Absolute asset URLs like
76
+ // `/icon.png` resolve against the host origin (desktop-app public/), not
77
+ // the remote. Prefer inlining small assets; large ones still go under
78
+ // assets/ and rely on MF publicPath, but never land on host public/.
50
79
  build: {
80
+ assetsInlineLimit: 32 * 1024,
51
81
  rollupOptions: {
52
82
  input: entry,
53
83
  // Host-provided singletons (see desktop-app plugin-shared-modules + vetta-host protocol).
54
- external: ["@vetta-org/plugin-sdk", "@vetta/ui"],
84
+ external: ["@vetta-org/plugin-sdk", "@vetta/ui", "@vetta/theme-ui/plugin-ui"],
55
85
  output: {
56
86
  assetFileNames(assetInfo) {
57
87
  return assetInfo.names.some((name) => name.endsWith(".css"))
@@ -61,6 +91,7 @@ function createBuildDefaultsPlugin(entry) {
61
91
  paths: {
62
92
  "@vetta-org/plugin-sdk": "vetta-host://plugin-sdk",
63
93
  "@vetta/ui": "vetta-host://ui",
94
+ "@vetta/theme-ui/plugin-ui": "vetta-host://theme-ui-plugin",
64
95
  },
65
96
  },
66
97
  },
@@ -69,6 +100,36 @@ function createBuildDefaultsPlugin(entry) {
69
100
  },
70
101
  };
71
102
  }
103
+ // Module Federation exposes shared React through a virtual ESM module. Routing
104
+ // static CommonJS requires through this namespace keeps Rollup's generated
105
+ // bindings stable when dependencies such as use-sync-external-store are bundled.
106
+ function createSharedReactCommonJsBridgePlugin() {
107
+ return {
108
+ name: "vetta-plugin-shared-react-commonjs-bridge",
109
+ apply: "build",
110
+ enforce: "pre",
111
+ transform(code) {
112
+ if (!code.includes("require") || !code.includes("react"))
113
+ return;
114
+ const transformed = code.replace(STATIC_REACT_REQUIRE_PATTERN, `require(${JSON.stringify(SHARED_REACT_COMMONJS_BRIDGE_ID)})`);
115
+ if (transformed === code)
116
+ return;
117
+ return { code: transformed, map: null };
118
+ },
119
+ resolveId(id) {
120
+ if (id === SHARED_REACT_COMMONJS_BRIDGE_ID)
121
+ return RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID;
122
+ },
123
+ load(id) {
124
+ if (id !== RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID)
125
+ return;
126
+ return `import * as React from "react";
127
+ export * from "react";
128
+ export default React;
129
+ `;
130
+ },
131
+ };
132
+ }
72
133
  function createPackagePlugin(options) {
73
134
  let rootDir = "";
74
135
  let distDir = "";
@@ -99,16 +160,40 @@ function createPackagePlugin(options) {
99
160
  },
100
161
  };
101
162
  }
163
+ function createPermissionContractPlugin() {
164
+ let rootDir = "";
165
+ return {
166
+ name: "vetta-plugin-permission-contract",
167
+ apply: "build",
168
+ configResolved(config) {
169
+ rootDir = config.root;
170
+ },
171
+ async generateBundle(_outputOptions, bundle) {
172
+ const manifest = parsePluginManifest(JSON.parse(await readFile(resolve(rootDir, "plugin.json"), "utf8")));
173
+ assertPluginPermissionContract(manifest, Object.values(bundle).flatMap((output) => output.type === "chunk" ? [{ fileName: output.fileName, code: output.code }] : []));
174
+ },
175
+ };
176
+ }
102
177
  export function vettaPluginFederation(options) {
103
178
  const packageOptions = typeof options.package === "object" ? options.package : {};
104
179
  const entry = options.entry ?? "./src/index.tsx";
180
+ const devServer = isVettaPluginDevServer();
105
181
  const plugins = [
182
+ createPluginBuildWarningFilter(),
183
+ createHostThemeBridgePlugin(),
184
+ ...(devServer ? createVettaPluginDevPlugins(entry) : []),
106
185
  createBuildDefaultsPlugin(entry),
107
- ...federation(createVettaPluginFederationConfig(options)),
186
+ createSharedReactCommonJsBridgePlugin(),
187
+ ...federation({
188
+ ...createVettaPluginFederationConfig(options),
189
+ exposes: {
190
+ [options.expose ?? "./plugin"]: devServer ? VETTA_PLUGIN_DEV_ENTRY_ID : entry,
191
+ },
192
+ }),
108
193
  createPluginStyleScopePlugin(),
194
+ createPermissionContractPlugin(),
109
195
  ];
110
- // VETTA_PLUGIN_DEV_WATCH=1:宿主 dev 热更新的 `vite build --watch` 只需要 dist,
111
- // 跳过每次增量重建都重打 zip(closeBundle 在 watch 模式每轮都会触发)。
196
+ // 兼容旧宿主的 build-watch 流程:增量构建时不重复打 zip。
112
197
  if (options.package !== false && process.env.VETTA_PLUGIN_DEV_WATCH !== "1") {
113
198
  plugins.push(createPackagePlugin(packageOptions));
114
199
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { federation, type ModuleFederationOptions } from \"@module-federation/vite\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport { type CreateVettaPluginPackageOptions, createVettaPluginPackage } from \"./pack.js\";\nimport { createPluginStyleScopePlugin } from \"./style-scope.js\";\n\nexport interface VettaPluginPackageOptions extends Omit<CreateVettaPluginPackageOptions, \"rootDir\" | \"distDir\"> {\n\tenabled?: boolean;\n}\n\nexport interface VettaPluginFederationOptions {\n\tname: string;\n\texpose?: string;\n\tentry?: string;\n\tmanifestFileName?: string;\n\tremoteEntryFileName?: string;\n\tshared?: ModuleFederationOptions[\"shared\"];\n\tpackage?: boolean | VettaPluginPackageOptions;\n}\n\nexport function createVettaPluginFederationConfig(options: VettaPluginFederationOptions): ModuleFederationOptions {\n\tconst expose = options.expose ?? \"./plugin\";\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\treturn {\n\t\tname: options.name,\n\t\tfilename: options.remoteEntryFileName ?? \"remoteEntry.js\",\n\t\texposes: {\n\t\t\t[expose]: entry,\n\t\t},\n\t\tmanifest: {\n\t\t\tfileName: options.manifestFileName ?? \"mf-manifest.json\",\n\t\t},\n\t\tdts: false,\n\t\tshared: {\n\t\t\treact: {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t\"react-dom\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Match host plugin-shared-modules (tldraw remotes may require this subpath).\n\t\t\t\"react-dom/client\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Host design-system primitives; runtime provided by desktop-app share scope.\n\t\t\t\"@vetta/ui\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t...options.shared,\n\t\t},\n\t};\n}\n\nfunction createBuildDefaultsPlugin(entry: string): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-build-defaults\",\n\t\tapply: \"build\",\n\t\tconfig() {\n\t\t\treturn {\n\t\t\t\tbuild: {\n\t\t\t\t\trollupOptions: {\n\t\t\t\t\t\tinput: entry,\n\t\t\t\t\t\t// Host-provided singletons (see desktop-app plugin-shared-modules + vetta-host protocol).\n\t\t\t\t\t\texternal: [\"@vetta-org/plugin-sdk\", \"@vetta/ui\"],\n\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\tassetFileNames(assetInfo) {\n\t\t\t\t\t\t\t\treturn assetInfo.names.some((name) => name.endsWith(\".css\"))\n\t\t\t\t\t\t\t\t\t? \"style.css\"\n\t\t\t\t\t\t\t\t\t: \"assets/[name]-[hash][extname]\";\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpaths: {\n\t\t\t\t\t\t\t\t\"@vetta-org/plugin-sdk\": \"vetta-host://plugin-sdk\",\n\t\t\t\t\t\t\t\t\"@vetta/ui\": \"vetta-host://ui\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction createPackagePlugin(options: VettaPluginPackageOptions): Plugin {\n\tlet rootDir = \"\";\n\tlet distDir = \"\";\n\tlet buildFailed = false;\n\n\treturn {\n\t\tname: \"vetta-plugin-package\",\n\t\tapply: \"build\",\n\t\tbuildStart() {\n\t\t\tbuildFailed = false;\n\t\t},\n\t\tbuildEnd(error) {\n\t\t\tbuildFailed = error !== undefined;\n\t\t},\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t\tdistDir = config.build.outDir;\n\t\t},\n\t\tasync closeBundle() {\n\t\t\tif (options.enabled === false || buildFailed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await createVettaPluginPackage({\n\t\t\t\t...options,\n\t\t\t\trootDir,\n\t\t\t\tdistDir,\n\t\t\t});\n\t\t\tconsole.log(`[vetta-plugin-vite] Wrote ${result.outputPath} with ${result.files.length} runtime files`);\n\t\t},\n\t};\n}\n\nexport function vettaPluginFederation(options: VettaPluginFederationOptions): PluginOption[] {\n\tconst packageOptions = typeof options.package === \"object\" ? options.package : {};\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\tconst plugins: PluginOption[] = [\n\t\tcreateBuildDefaultsPlugin(entry),\n\t\t...federation(createVettaPluginFederationConfig(options)),\n\t\tcreatePluginStyleScopePlugin(),\n\t];\n\t// VETTA_PLUGIN_DEV_WATCH=1:宿主 dev 热更新的 `vite build --watch` 只需要 dist,\n\t// 跳过每次增量重建都重打 zip(closeBundle 在 watch 模式每轮都会触发)。\n\tif (options.package !== false && process.env.VETTA_PLUGIN_DEV_WATCH !== \"1\") {\n\t\tplugins.push(createPackagePlugin(packageOptions));\n\t}\n\treturn plugins;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { federation, type ModuleFederationOptions } from \"@module-federation/vite\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parsePluginManifest } from \"@vetta-org/plugin-sdk/manifest\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport {\n\tcreateVettaPluginDevPlugins,\n\tisVettaPluginDevServer,\n\tVETTA_PLUGIN_DEV_ENTRY_ID,\n} from \"./dev-vite-plugins.js\";\nimport { createPluginBuildWarningFilter } from \"./build-warning-filter.js\";\nimport { createHostThemeBridgePlugin } from \"./host-theme.js\";\nimport { type CreateVettaPluginPackageOptions, createVettaPluginPackage } from \"./pack.js\";\nimport { assertPluginPermissionContract } from \"./permission-contract.js\";\nimport { createPluginStyleScopePlugin } from \"./style-scope.js\";\n\nconst SHARED_REACT_COMMONJS_BRIDGE_ID = \"virtual:vetta-plugin-shared-react-commonjs\";\nconst RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID = `\\0${SHARED_REACT_COMMONJS_BRIDGE_ID}`;\nconst STATIC_REACT_REQUIRE_PATTERN = /\\brequire\\s*\\(\\s*([\"'])react\\1\\s*\\)/gu;\n\nexport interface VettaPluginPackageOptions extends Omit<CreateVettaPluginPackageOptions, \"rootDir\" | \"distDir\"> {\n\tenabled?: boolean;\n}\n\nexport interface VettaPluginFederationOptions {\n\tname: string;\n\texpose?: string;\n\tentry?: string;\n\tmanifestFileName?: string;\n\tremoteEntryFileName?: string;\n\t/** Share the narrow host UI contract exposed by `@vetta/theme-ui/plugin-ui`. */\n\thostThemeUi?: boolean;\n\tshared?: ModuleFederationOptions[\"shared\"];\n\tpackage?: boolean | VettaPluginPackageOptions;\n}\n\nexport function createVettaPluginFederationConfig(options: VettaPluginFederationOptions): ModuleFederationOptions {\n\tconst expose = options.expose ?? \"./plugin\";\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\treturn {\n\t\tname: options.name,\n\t\tfilename: options.remoteEntryFileName ?? \"remoteEntry.js\",\n\t\texposes: {\n\t\t\t[expose]: entry,\n\t\t},\n\t\tmanifest: {\n\t\t\tfileName: options.manifestFileName ?? \"mf-manifest.json\",\n\t\t},\n\t\tdts: false,\n\t\tshared: {\n\t\t\t\"@vetta-org/plugin-sdk\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\treact: {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t\"react-dom\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Match host plugin-shared-modules (tldraw remotes may require this subpath).\n\t\t\t\"react-dom/client\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t// Host design-system primitives; runtime provided by desktop-app share scope.\n\t\t\t\"@vetta/ui\": {\n\t\t\t\tsingleton: true,\n\t\t\t\timport: false,\n\t\t\t\trequiredVersion: \"*\",\n\t\t\t},\n\t\t\t...(options.hostThemeUi\n\t\t\t\t? {\n\t\t\t\t\t\t// Host-built UI components (model selector, …); opt in to keep unrelated plugins decoupled.\n\t\t\t\t\t\t\"@vetta/theme-ui/plugin-ui\": {\n\t\t\t\t\t\t\tsingleton: true,\n\t\t\t\t\t\t\timport: false,\n\t\t\t\t\t\t\trequiredVersion: \"*\",\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...options.shared,\n\t\t},\n\t};\n}\n\nfunction createBuildDefaultsPlugin(entry: string): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-build-defaults\",\n\t\tapply: \"build\",\n\t\tconfig() {\n\t\t\treturn {\n\t\t\t\t// Plugin remotes run inside the host page. Absolute asset URLs like\n\t\t\t\t// `/icon.png` resolve against the host origin (desktop-app public/), not\n\t\t\t\t// the remote. Prefer inlining small assets; large ones still go under\n\t\t\t\t// assets/ and rely on MF publicPath, but never land on host public/.\n\t\t\t\tbuild: {\n\t\t\t\t\tassetsInlineLimit: 32 * 1024,\n\t\t\t\t\trollupOptions: {\n\t\t\t\t\t\tinput: entry,\n\t\t\t\t\t\t// Host-provided singletons (see desktop-app plugin-shared-modules + vetta-host protocol).\n\t\t\t\t\t\texternal: [\"@vetta-org/plugin-sdk\", \"@vetta/ui\", \"@vetta/theme-ui/plugin-ui\"],\n\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\tassetFileNames(assetInfo) {\n\t\t\t\t\t\t\t\treturn assetInfo.names.some((name) => name.endsWith(\".css\"))\n\t\t\t\t\t\t\t\t\t? \"style.css\"\n\t\t\t\t\t\t\t\t\t: \"assets/[name]-[hash][extname]\";\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpaths: {\n\t\t\t\t\t\t\t\t\"@vetta-org/plugin-sdk\": \"vetta-host://plugin-sdk\",\n\t\t\t\t\t\t\t\t\"@vetta/ui\": \"vetta-host://ui\",\n\t\t\t\t\t\t\t\t\"@vetta/theme-ui/plugin-ui\": \"vetta-host://theme-ui-plugin\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\n// Module Federation exposes shared React through a virtual ESM module. Routing\n// static CommonJS requires through this namespace keeps Rollup's generated\n// bindings stable when dependencies such as use-sync-external-store are bundled.\nfunction createSharedReactCommonJsBridgePlugin(): Plugin {\n\treturn {\n\t\tname: \"vetta-plugin-shared-react-commonjs-bridge\",\n\t\tapply: \"build\",\n\t\tenforce: \"pre\",\n\t\ttransform(code) {\n\t\t\tif (!code.includes(\"require\") || !code.includes(\"react\")) return;\n\t\t\tconst transformed = code.replace(\n\t\t\t\tSTATIC_REACT_REQUIRE_PATTERN,\n\t\t\t\t`require(${JSON.stringify(SHARED_REACT_COMMONJS_BRIDGE_ID)})`,\n\t\t\t);\n\t\t\tif (transformed === code) return;\n\t\t\treturn { code: transformed, map: null };\n\t\t},\n\t\tresolveId(id) {\n\t\t\tif (id === SHARED_REACT_COMMONJS_BRIDGE_ID) return RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== RESOLVED_SHARED_REACT_COMMONJS_BRIDGE_ID) return;\n\t\t\treturn `import * as React from \"react\";\nexport * from \"react\";\nexport default React;\n`;\n\t\t},\n\t};\n}\n\nfunction createPackagePlugin(options: VettaPluginPackageOptions): Plugin {\n\tlet rootDir = \"\";\n\tlet distDir = \"\";\n\tlet buildFailed = false;\n\n\treturn {\n\t\tname: \"vetta-plugin-package\",\n\t\tapply: \"build\",\n\t\tbuildStart() {\n\t\t\tbuildFailed = false;\n\t\t},\n\t\tbuildEnd(error) {\n\t\t\tbuildFailed = error !== undefined;\n\t\t},\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t\tdistDir = config.build.outDir;\n\t\t},\n\t\tasync closeBundle() {\n\t\t\tif (options.enabled === false || buildFailed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await createVettaPluginPackage({\n\t\t\t\t...options,\n\t\t\t\trootDir,\n\t\t\t\tdistDir,\n\t\t\t});\n\t\t\tconsole.log(`[vetta-plugin-vite] Wrote ${result.outputPath} with ${result.files.length} runtime files`);\n\t\t},\n\t};\n}\n\nfunction createPermissionContractPlugin(): Plugin {\n\tlet rootDir = \"\";\n\treturn {\n\t\tname: \"vetta-plugin-permission-contract\",\n\t\tapply: \"build\",\n\t\tconfigResolved(config) {\n\t\t\trootDir = config.root;\n\t\t},\n\t\tasync generateBundle(_outputOptions, bundle) {\n\t\t\tconst manifest = parsePluginManifest(\n\t\t\t\tJSON.parse(await readFile(resolve(rootDir, \"plugin.json\"), \"utf8\")) as unknown,\n\t\t\t);\n\t\t\tassertPluginPermissionContract(\n\t\t\t\tmanifest,\n\t\t\t\tObject.values(bundle).flatMap((output) =>\n\t\t\t\t\toutput.type === \"chunk\" ? [{ fileName: output.fileName, code: output.code }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\t};\n}\n\nexport function vettaPluginFederation(options: VettaPluginFederationOptions): PluginOption[] {\n\tconst packageOptions = typeof options.package === \"object\" ? options.package : {};\n\tconst entry = options.entry ?? \"./src/index.tsx\";\n\tconst devServer = isVettaPluginDevServer();\n\tconst plugins: PluginOption[] = [\n\t\tcreatePluginBuildWarningFilter(),\n\t\tcreateHostThemeBridgePlugin(),\n\t\t...(devServer ? createVettaPluginDevPlugins(entry) : []),\n\t\tcreateBuildDefaultsPlugin(entry),\n\t\tcreateSharedReactCommonJsBridgePlugin(),\n\t\t...federation({\n\t\t\t...createVettaPluginFederationConfig(options),\n\t\t\texposes: {\n\t\t\t\t[options.expose ?? \"./plugin\"]: devServer ? VETTA_PLUGIN_DEV_ENTRY_ID : entry,\n\t\t\t},\n\t\t}),\n\t\tcreatePluginStyleScopePlugin(),\n\t\tcreatePermissionContractPlugin(),\n\t];\n\t// 兼容旧宿主的 build-watch 流程:增量构建时不重复打 zip。\n\tif (options.package !== false && process.env.VETTA_PLUGIN_DEV_WATCH !== \"1\") {\n\t\tplugins.push(createPackagePlugin(packageOptions));\n\t}\n\treturn plugins;\n}\n"]}
package/dist/pack.d.ts CHANGED
@@ -4,6 +4,7 @@ export interface VettaPluginPackageFile {
4
4
  }
5
5
  export interface VettaPluginPackageResult {
6
6
  outputPath: string;
7
+ npmOutputPath?: string;
7
8
  files: VettaPluginPackageFile[];
8
9
  }
9
10
  export interface CreateVettaPluginPackageOptions {
@@ -11,6 +12,9 @@ export interface CreateVettaPluginPackageOptions {
11
12
  manifestPath?: string;
12
13
  releaseDir?: string;
13
14
  distDir?: string;
15
+ /** Also write the stable npm distribution artifact `release/vetta-plugin.zip`. */
16
+ npmArchive?: boolean;
14
17
  }
18
+ export declare const VETTA_NPM_PLUGIN_ARCHIVE_PATH = "release/vetta-plugin.zip";
15
19
  export declare function createVettaPluginPackage(options?: CreateVettaPluginPackageOptions): Promise<VettaPluginPackageResult>;
16
20
  //# sourceMappingURL=pack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,sBAAsB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,+BAA+B;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AA2YD,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,GAC3C,OAAO,CAAC,wBAAwB,CAAC,CAcnC","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\n\nexport interface VettaPluginPackageFile {\n\tfullPath: string;\n\tarchivePath: string;\n}\n\nexport interface VettaPluginPackageResult {\n\toutputPath: string;\n\tfiles: VettaPluginPackageFile[];\n}\n\nexport interface CreateVettaPluginPackageOptions {\n\trootDir?: string;\n\tmanifestPath?: string;\n\treleaseDir?: string;\n\tdistDir?: string;\n}\n\ninterface PluginManifest {\n\tid: string;\n\tversion: string;\n\tentry: string;\n\tstyles?: string[];\n\t/** 三态:Iconify 名 / `http(s)://` 外链 / 包内相对路径(只有后者需要打包)。 */\n\ticon?: string;\n\tagent?: {\n\t\tsystemPrompt?: {\n\t\t\tpromptPaths?: string[];\n\t\t};\n\t\tskillPaths?: string[];\n\t\t/** Relative path to `.mcp.json` or present as object (inline). */\n\t\tmcpServers?: string | Record<string, unknown>;\n\t};\n}\n\nconst crcTable = new Uint32Array(256);\nfor (let i = 0; i < 256; i += 1) {\n\tlet value = i;\n\tfor (let bit = 0; bit < 8; bit += 1) {\n\t\tvalue = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;\n\t}\n\tcrcTable[i] = value >>> 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readString(record: Record<string, unknown>, key: string): string | undefined {\n\tconst value = record[key];\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction readStringArray(record: Record<string, unknown>, key: string): string[] | undefined {\n\tconst value = record[key];\n\tif (!Array.isArray(value)) {\n\t\treturn undefined;\n\t}\n\tconst strings = value.filter((entry): entry is string => typeof entry === \"string\");\n\treturn strings.length === value.length ? strings : undefined;\n}\n\nfunction readAgentManifest(record: Record<string, unknown>): PluginManifest[\"agent\"] {\n\tconst agent = record.agent;\n\tif (!isRecord(agent)) {\n\t\treturn undefined;\n\t}\n\tconst systemPrompt = isRecord(agent.systemPrompt)\n\t\t? {\n\t\t\t\tpromptPaths: readStringArray(agent.systemPrompt, \"promptPaths\"),\n\t\t\t}\n\t\t: undefined;\n\tconst mcpServersRaw = agent.mcpServers;\n\tconst mcpServers =\n\t\ttypeof mcpServersRaw === \"string\"\n\t\t\t? mcpServersRaw\n\t\t\t: isRecord(mcpServersRaw)\n\t\t\t\t? mcpServersRaw\n\t\t\t\t: undefined;\n\treturn {\n\t\tsystemPrompt,\n\t\tskillPaths: readStringArray(agent, \"skillPaths\"),\n\t\tmcpServers,\n\t};\n}\n\nfunction parsePluginManifest(value: unknown): PluginManifest {\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"plugin.json must contain an object.\");\n\t}\n\n\tconst id = readString(value, \"id\");\n\tconst version = readString(value, \"version\");\n\tconst entry = readString(value, \"entry\");\n\tif (!id || !version || !entry) {\n\t\tthrow new Error(\"plugin.json must define string id, version, and entry fields.\");\n\t}\n\n\treturn {\n\t\tid,\n\t\tversion,\n\t\tentry,\n\t\ticon: readString(value, \"icon\"),\n\t\tagent: readAgentManifest(value),\n\t\tstyles: readStringArray(value, \"styles\"),\n\t};\n}\n\nfunction validateAbilityDescriptor(value: unknown, pluginManifest: PluginManifest): void {\n\tif (!isRecord(value)) throw new Error(\"ability.json must contain an object.\");\n\tif (\n\t\tvalue.schemaVersion !== 1 ||\n\t\tvalue.type !== \"plugin\" ||\n\t\tvalue.slug !== pluginManifest.id ||\n\t\tvalue.version !== pluginManifest.version\n\t) {\n\t\tthrow new Error(\"ability.json identity must match plugin.json id and version.\");\n\t}\n}\n\n/** Iconify 图标名:`solar:magic-stick-3-bold` 这种 `<集合>:<图标>` 形态。 */\nconst ICONIFY_ICON_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * 与宿主 `isPassthroughIconRef`、服务端 `isPackagedIconPath` 保持同一判定:\n * Iconify 名与 http(s) 外链不落包,其余按包内相对路径打进 zip。\n */\nfunction isPackagedIconPath(icon: string): boolean {\n\treturn (\n\t\ticon !== \"\" &&\n\t\t!icon.startsWith(\"http://\") &&\n\t\t!icon.startsWith(\"https://\") &&\n\t\t!ICONIFY_ICON_PATTERN.test(icon)\n\t);\n}\n\nfunction parseJsonObject(buffer: Buffer, fileName: string): Record<string, unknown> {\n\tconst parsed = JSON.parse(buffer.toString(\"utf-8\")) as unknown;\n\tif (!isRecord(parsed)) {\n\t\tthrow new Error(`${fileName} must contain an object.`);\n\t}\n\treturn parsed;\n}\n\nfunction crc32(buffer: Buffer): number {\n\tlet value = 0xffffffff;\n\tfor (const byte of buffer) {\n\t\tvalue = crcTable[(value ^ byte) & 0xff] ^ (value >>> 8);\n\t}\n\treturn (value ^ 0xffffffff) >>> 0;\n}\n\nfunction writeUInt16(value: number): Buffer {\n\tconst buffer = Buffer.allocUnsafe(2);\n\tbuffer.writeUInt16LE(value, 0);\n\treturn buffer;\n}\n\nfunction writeUInt32(value: number): Buffer {\n\tconst buffer = Buffer.allocUnsafe(4);\n\tbuffer.writeUInt32LE(value >>> 0, 0);\n\treturn buffer;\n}\n\nfunction archivePathFromRoot(rootDir: string, fullPath: string): string {\n\tconst archivePath = relative(rootDir, fullPath).replace(/\\\\/g, \"/\");\n\tif (!archivePath || archivePath.startsWith(\"..\") || archivePath.includes(`${sep}..${sep}`)) {\n\t\tthrow new Error(`File is outside plugin root: ${fullPath}`);\n\t}\n\treturn archivePath;\n}\n\nasync function collectFiles(dir: string): Promise<VettaPluginPackageFile[]> {\n\tconst entries = await readdir(dir, { withFileTypes: true });\n\tconst files: VettaPluginPackageFile[] = [];\n\tfor (const entry of entries) {\n\t\tconst fullPath = join(dir, entry.name);\n\t\tif (entry.isDirectory()) {\n\t\t\tfiles.push(...(await collectFiles(fullPath)));\n\t\t} else if (entry.isFile()) {\n\t\t\tfiles.push({ fullPath, archivePath: \"\" });\n\t\t}\n\t}\n\treturn files;\n}\n\nasync function collectPath(path: string): Promise<VettaPluginPackageFile[]> {\n\tconst info = await stat(path);\n\tif (info.isDirectory()) {\n\t\treturn collectFiles(path);\n\t}\n\tif (info.isFile()) {\n\t\treturn [{ fullPath: path, archivePath: \"\" }];\n\t}\n\treturn [];\n}\n\nasync function createZip(files: VettaPluginPackageFile[]): Promise<Buffer> {\n\tconst localParts: Buffer[] = [];\n\tconst centralParts: Buffer[] = [];\n\tlet offset = 0;\n\n\tfor (const file of files) {\n\t\tconst data = await readFile(file.fullPath);\n\t\tconst name = Buffer.from(file.archivePath.replace(/\\\\/g, \"/\"));\n\t\tconst checksum = crc32(data);\n\t\tconst localHeader = Buffer.concat([\n\t\t\twriteUInt32(0x04034b50),\n\t\t\twriteUInt16(20),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt32(checksum),\n\t\t\twriteUInt32(data.length),\n\t\t\twriteUInt32(data.length),\n\t\t\twriteUInt16(name.length),\n\t\t\twriteUInt16(0),\n\t\t\tname,\n\t\t]);\n\t\tlocalParts.push(localHeader, data);\n\n\t\tcentralParts.push(\n\t\t\tBuffer.concat([\n\t\t\t\twriteUInt32(0x02014b50),\n\t\t\t\twriteUInt16(20),\n\t\t\t\twriteUInt16(20),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt32(checksum),\n\t\t\t\twriteUInt32(data.length),\n\t\t\t\twriteUInt32(data.length),\n\t\t\t\twriteUInt16(name.length),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt32(0),\n\t\t\t\twriteUInt32(offset),\n\t\t\t\tname,\n\t\t\t]),\n\t\t);\n\t\toffset += localHeader.length + data.length;\n\t}\n\n\tconst centralDirectory = Buffer.concat(centralParts);\n\tconst endOfCentralDirectory = Buffer.concat([\n\t\twriteUInt32(0x06054b50),\n\t\twriteUInt16(0),\n\t\twriteUInt16(0),\n\t\twriteUInt16(files.length),\n\t\twriteUInt16(files.length),\n\t\twriteUInt32(centralDirectory.length),\n\t\twriteUInt32(offset),\n\t\twriteUInt16(0),\n\t]);\n\n\treturn Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);\n}\n\nfunction readManifestRemoteEntry(manifest: Record<string, unknown>): string | undefined {\n\tconst metaData = manifest.metaData;\n\tif (!isRecord(metaData)) {\n\t\treturn undefined;\n\t}\n\tconst remoteEntry = metaData.remoteEntry;\n\tif (!isRecord(remoteEntry)) {\n\t\treturn undefined;\n\t}\n\tconst name = readString(remoteEntry, \"name\");\n\tif (!name) {\n\t\treturn undefined;\n\t}\n\tconst path = readString(remoteEntry, \"path\") ?? \"\";\n\treturn path ? `${path.replace(/\\/$/, \"\")}/${name}` : name;\n}\n\nasync function collectRuntimeFiles(\n\trootDir: string,\n\tmanifestPath: string,\n\tdistDir: string,\n): Promise<VettaPluginPackageFile[]> {\n\tconst pluginManifest = parsePluginManifest(parseJsonObject(await readFile(manifestPath), basename(manifestPath)));\n\tconst packageFiles = new Map<string, VettaPluginPackageFile>();\n\tconst addFile = (fullPath: string) => {\n\t\tconst resolved = resolve(fullPath);\n\t\tconst archivePath = archivePathFromRoot(rootDir, resolved);\n\t\tpackageFiles.set(archivePath, { fullPath: resolved, archivePath });\n\t};\n\n\taddFile(manifestPath);\n\n\t// 插件详情跟随插件包发布;ability.json 缺省兼容,存在时必须与 plugin.json 身份一致。\n\tconst abilityDescriptorPath = resolve(rootDir, \"ability.json\");\n\tif (existsSync(abilityDescriptorPath)) {\n\t\tvalidateAbilityDescriptor(\n\t\t\tparseJsonObject(await readFile(abilityDescriptorPath), basename(abilityDescriptorPath)),\n\t\t\tpluginManifest,\n\t\t);\n\t\taddFile(abilityDescriptorPath);\n\t\t// 约定的展示资源目录;内联 blocks 可引用其中的图片,随包整体带上。\n\t\ttry {\n\t\t\tfor (const file of await collectFiles(resolve(rootDir, \"presentation\"))) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// optional presentation/ directory\n\t\t}\n\t}\n\n\tconst federationManifestPath = resolve(rootDir, pluginManifest.entry);\n\taddFile(federationManifestPath);\n\n\tconst federationManifest = parseJsonObject(await readFile(federationManifestPath), basename(federationManifestPath));\n\tconst remoteEntry = readManifestRemoteEntry(federationManifest);\n\tif (remoteEntry) {\n\t\taddFile(resolve(dirname(federationManifestPath), remoteEntry));\n\t}\n\n\tconst assetsDir = join(distDir, \"assets\");\n\tfor (const file of await collectFiles(assetsDir)) {\n\t\taddFile(file.fullPath);\n\t}\n\n\t// Vite cssCodeSplit emits extra files like dist/style2.css for async chunks;\n\t// include all dist-root CSS so preload-helper can fetch them (not only styles[]).\n\ttry {\n\t\tfor (const file of await collectFiles(distDir)) {\n\t\t\tconst rel = relative(distDir, file.fullPath).replace(/\\\\/g, \"/\");\n\t\t\tif (rel.endsWith(\".css\") && !rel.includes(\"/\")) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// dist missing\n\t}\n\n\tfor (const style of pluginManifest.styles ?? []) {\n\t\taddFile(resolve(rootDir, style));\n\t}\n\n\t// 包内相对路径图标:不打进 zip 的话,宿主 vetta-plugin:// 会 404、市场上传会被服务端拒绝。\n\tconst icon = pluginManifest.icon?.trim();\n\tif (icon && isPackagedIconPath(icon)) {\n\t\tconst iconPath = resolve(rootDir, icon);\n\t\tif (!existsSync(iconPath)) {\n\t\t\tthrow new Error(`plugin.json icon file not found: ${icon}`);\n\t\t}\n\t\taddFile(iconPath);\n\t}\n\n\tfor (const promptPath of pluginManifest.agent?.systemPrompt?.promptPaths ?? []) {\n\t\tfor (const file of await collectPath(resolve(rootDir, promptPath))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t}\n\n\tfor (const skillPath of pluginManifest.agent?.skillPaths ?? []) {\n\t\tfor (const file of await collectPath(resolve(rootDir, skillPath))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t}\n\n\t// Plugin-scoped MCP: include config file and conventional companion dirs when declared.\n\tconst mcpServers = pluginManifest.agent?.mcpServers;\n\tif (mcpServers !== undefined) {\n\t\tif (typeof mcpServers === \"string\") {\n\t\t\taddFile(resolve(rootDir, mcpServers));\n\t\t}\n\t\ttry {\n\t\t\tfor (const file of await collectFiles(resolve(rootDir, \"mcp\"))) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// optional mcp/ directory\n\t\t}\n\t}\n\n\t// Helper scripts (plugin workbench etc.): always pack scripts/ when present.\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"scripts\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// optional scripts/ directory\n\t}\n\n\t// Handbook / agent docs beside skills (not always listed in skillPaths).\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"agent/docs\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// optional agent/docs\n\t}\n\n\t// Plugin i18n catalogs (ADR-0033): bundle locales/<lang>.json so the host can\n\t// load them alongside the manifest. Optional — absent for single-language plugins.\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"locales\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// no locales/ directory — nothing to bundle\n\t}\n\n\treturn [...packageFiles.values()].sort((a, b) => a.archivePath.localeCompare(b.archivePath));\n}\n\nexport async function createVettaPluginPackage(\n\toptions: CreateVettaPluginPackageOptions = {},\n): Promise<VettaPluginPackageResult> {\n\tconst rootDir = resolve(options.rootDir ?? process.cwd());\n\tconst manifestPath = resolve(rootDir, options.manifestPath ?? \"plugin.json\");\n\tconst releaseDir = resolve(rootDir, options.releaseDir ?? \"release\");\n\tconst distDir = resolve(rootDir, options.distDir ?? \"dist\");\n\tconst pluginManifest = parsePluginManifest(parseJsonObject(await readFile(manifestPath), basename(manifestPath)));\n\tconst outputPath = join(releaseDir, `${pluginManifest.id}-${pluginManifest.version}.zip`);\n\tconst files = await collectRuntimeFiles(rootDir, manifestPath, distDir);\n\n\tawait rm(releaseDir, { recursive: true, force: true });\n\tawait mkdir(releaseDir, { recursive: true });\n\tawait writeFile(outputPath, await createZip(files));\n\n\treturn { outputPath, files };\n}\n"]}
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,sBAAsB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,+BAA+B;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,6BAA6B,6BAA6B,CAAC;AAqSxE,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,GAC3C,OAAO,CAAC,wBAAwB,CAAC,CAgDnC","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { listPluginManifestResources, parsePluginManifest, type PluginManifest } from \"@vetta-org/plugin-sdk/manifest\";\nimport { parseVettaNpmPluginPackage } from \"@vetta-org/plugin-sdk/npm-package\";\nimport { assertPluginPermissionContract } from \"./permission-contract.js\";\n\nexport interface VettaPluginPackageFile {\n\tfullPath: string;\n\tarchivePath: string;\n}\n\nexport interface VettaPluginPackageResult {\n\toutputPath: string;\n\tnpmOutputPath?: string;\n\tfiles: VettaPluginPackageFile[];\n}\n\nexport interface CreateVettaPluginPackageOptions {\n\trootDir?: string;\n\tmanifestPath?: string;\n\treleaseDir?: string;\n\tdistDir?: string;\n\t/** Also write the stable npm distribution artifact `release/vetta-plugin.zip`. */\n\tnpmArchive?: boolean;\n}\n\nexport const VETTA_NPM_PLUGIN_ARCHIVE_PATH = \"release/vetta-plugin.zip\";\n\nconst crcTable = new Uint32Array(256);\nfor (let i = 0; i < 256; i += 1) {\n\tlet value = i;\n\tfor (let bit = 0; bit < 8; bit += 1) {\n\t\tvalue = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;\n\t}\n\tcrcTable[i] = value >>> 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readString(record: Record<string, unknown>, key: string): string | undefined {\n\tconst value = record[key];\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction validateAbilityDescriptor(value: unknown, pluginManifest: PluginManifest): void {\n\tif (!isRecord(value)) throw new Error(\"ability.json must contain an object.\");\n\tif (\n\t\tvalue.schemaVersion !== 1 ||\n\t\tvalue.type !== \"plugin\" ||\n\t\tvalue.slug !== pluginManifest.id ||\n\t\tvalue.version !== pluginManifest.version\n\t) {\n\t\tthrow new Error(\"ability.json identity must match plugin.json id and version.\");\n\t}\n}\n\nfunction parseJsonObject(buffer: Buffer, fileName: string): Record<string, unknown> {\n\tconst parsed = JSON.parse(buffer.toString(\"utf-8\")) as unknown;\n\tif (!isRecord(parsed)) {\n\t\tthrow new Error(`${fileName} must contain an object.`);\n\t}\n\treturn parsed;\n}\n\nfunction crc32(buffer: Buffer): number {\n\tlet value = 0xffffffff;\n\tfor (const byte of buffer) {\n\t\tvalue = crcTable[(value ^ byte) & 0xff] ^ (value >>> 8);\n\t}\n\treturn (value ^ 0xffffffff) >>> 0;\n}\n\nfunction writeUInt16(value: number): Buffer {\n\tconst buffer = Buffer.allocUnsafe(2);\n\tbuffer.writeUInt16LE(value, 0);\n\treturn buffer;\n}\n\nfunction writeUInt32(value: number): Buffer {\n\tconst buffer = Buffer.allocUnsafe(4);\n\tbuffer.writeUInt32LE(value >>> 0, 0);\n\treturn buffer;\n}\n\nfunction archivePathFromRoot(rootDir: string, fullPath: string): string {\n\tconst archivePath = relative(rootDir, fullPath).replace(/\\\\/g, \"/\");\n\tif (!archivePath || archivePath.startsWith(\"..\") || archivePath.includes(`${sep}..${sep}`)) {\n\t\tthrow new Error(`File is outside plugin root: ${fullPath}`);\n\t}\n\treturn archivePath;\n}\n\nasync function collectFiles(dir: string): Promise<VettaPluginPackageFile[]> {\n\tconst entries = await readdir(dir, { withFileTypes: true });\n\tconst files: VettaPluginPackageFile[] = [];\n\tfor (const entry of entries) {\n\t\tconst fullPath = join(dir, entry.name);\n\t\tif (entry.isDirectory()) {\n\t\t\tfiles.push(...(await collectFiles(fullPath)));\n\t\t} else if (entry.isFile()) {\n\t\t\tfiles.push({ fullPath, archivePath: \"\" });\n\t\t}\n\t}\n\treturn files;\n}\n\nasync function collectPath(path: string): Promise<VettaPluginPackageFile[]> {\n\tconst info = await stat(path);\n\tif (info.isDirectory()) {\n\t\treturn collectFiles(path);\n\t}\n\tif (info.isFile()) {\n\t\treturn [{ fullPath: path, archivePath: \"\" }];\n\t}\n\treturn [];\n}\n\nasync function createZip(files: VettaPluginPackageFile[]): Promise<Buffer> {\n\tconst localParts: Buffer[] = [];\n\tconst centralParts: Buffer[] = [];\n\tlet offset = 0;\n\n\tfor (const file of files) {\n\t\tconst data = await readFile(file.fullPath);\n\t\tconst name = Buffer.from(file.archivePath.replace(/\\\\/g, \"/\"));\n\t\tconst checksum = crc32(data);\n\t\tconst localHeader = Buffer.concat([\n\t\t\twriteUInt32(0x04034b50),\n\t\t\twriteUInt16(20),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt16(0),\n\t\t\twriteUInt32(checksum),\n\t\t\twriteUInt32(data.length),\n\t\t\twriteUInt32(data.length),\n\t\t\twriteUInt16(name.length),\n\t\t\twriteUInt16(0),\n\t\t\tname,\n\t\t]);\n\t\tlocalParts.push(localHeader, data);\n\n\t\tcentralParts.push(\n\t\t\tBuffer.concat([\n\t\t\t\twriteUInt32(0x02014b50),\n\t\t\t\twriteUInt16(20),\n\t\t\t\twriteUInt16(20),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt32(checksum),\n\t\t\t\twriteUInt32(data.length),\n\t\t\t\twriteUInt32(data.length),\n\t\t\t\twriteUInt16(name.length),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt16(0),\n\t\t\t\twriteUInt32(0),\n\t\t\t\twriteUInt32(offset),\n\t\t\t\tname,\n\t\t\t]),\n\t\t);\n\t\toffset += localHeader.length + data.length;\n\t}\n\n\tconst centralDirectory = Buffer.concat(centralParts);\n\tconst endOfCentralDirectory = Buffer.concat([\n\t\twriteUInt32(0x06054b50),\n\t\twriteUInt16(0),\n\t\twriteUInt16(0),\n\t\twriteUInt16(files.length),\n\t\twriteUInt16(files.length),\n\t\twriteUInt32(centralDirectory.length),\n\t\twriteUInt32(offset),\n\t\twriteUInt16(0),\n\t]);\n\n\treturn Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);\n}\n\nfunction readManifestRemoteEntry(manifest: Record<string, unknown>): string | undefined {\n\tconst metaData = manifest.metaData;\n\tif (!isRecord(metaData)) {\n\t\treturn undefined;\n\t}\n\tconst remoteEntry = metaData.remoteEntry;\n\tif (!isRecord(remoteEntry)) {\n\t\treturn undefined;\n\t}\n\tconst name = readString(remoteEntry, \"name\");\n\tif (!name) {\n\t\treturn undefined;\n\t}\n\tconst path = readString(remoteEntry, \"path\") ?? \"\";\n\treturn path ? `${path.replace(/\\/$/, \"\")}/${name}` : name;\n}\n\nasync function collectRuntimeFiles(\n\trootDir: string,\n\tmanifestPath: string,\n\tdistDir: string,\n): Promise<VettaPluginPackageFile[]> {\n\tconst pluginManifest = parsePluginManifest(parseJsonObject(await readFile(manifestPath), basename(manifestPath)));\n\tconst packageFiles = new Map<string, VettaPluginPackageFile>();\n\tconst addFile = (fullPath: string) => {\n\t\tconst resolved = resolve(fullPath);\n\t\tconst archivePath = archivePathFromRoot(rootDir, resolved);\n\t\tpackageFiles.set(archivePath, { fullPath: resolved, archivePath });\n\t};\n\n\taddFile(manifestPath);\n\n\t// 插件详情跟随插件包发布;ability.json 缺省兼容,存在时必须与 plugin.json 身份一致。\n\tconst abilityDescriptorPath = resolve(rootDir, \"ability.json\");\n\tif (existsSync(abilityDescriptorPath)) {\n\t\tvalidateAbilityDescriptor(\n\t\t\tparseJsonObject(await readFile(abilityDescriptorPath), basename(abilityDescriptorPath)),\n\t\t\tpluginManifest,\n\t\t);\n\t\taddFile(abilityDescriptorPath);\n\t\t// 约定的展示资源目录;内联 blocks 可引用其中的图片,随包整体带上。\n\t\ttry {\n\t\t\tfor (const file of await collectFiles(resolve(rootDir, \"presentation\"))) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// optional presentation/ directory\n\t\t}\n\t}\n\n\tconst runtimeEntryPath = resolve(rootDir, pluginManifest.entry);\n\taddFile(runtimeEntryPath);\n\n\tconst federationManifest = parseJsonObject(await readFile(runtimeEntryPath), basename(runtimeEntryPath));\n\tconst remoteEntry = readManifestRemoteEntry(federationManifest);\n\tif (remoteEntry) {\n\t\taddFile(resolve(dirname(runtimeEntryPath), remoteEntry));\n\t}\n\n\tconst assetsDir = join(distDir, \"assets\");\n\tfor (const file of await collectFiles(assetsDir)) {\n\t\taddFile(file.fullPath);\n\t}\n\n\t// Vite cssCodeSplit emits extra files like dist/style2.css for async chunks;\n\t// include all dist-root CSS so preload-helper can fetch them (not only styles[]).\n\ttry {\n\t\tfor (const file of await collectFiles(distDir)) {\n\t\t\tconst rel = relative(distDir, file.fullPath).replace(/\\\\/g, \"/\");\n\t\t\tif (rel.endsWith(\".css\") && !rel.includes(\"/\")) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// dist missing\n\t}\n\n\tfor (const resource of listPluginManifestResources(pluginManifest)) {\n\t\tif (resource.field === \"entry\") continue;\n\t\tconst resourcePath = resolve(rootDir, resource.path);\n\t\tif (resource.kind === \"file\") {\n\t\t\tif (!existsSync(resourcePath)) {\n\t\t\t\tthrow new Error(`plugin.json resource not found: ${resource.field} (${resource.path})`);\n\t\t\t}\n\t\t\taddFile(resourcePath);\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const file of await collectPath(resourcePath)) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t}\n\n\t// Plugin-scoped MCP: include config file and conventional companion dirs when declared.\n\tconst mcpServers = pluginManifest.agent?.mcpServers;\n\tif (mcpServers !== undefined) {\n\t\ttry {\n\t\t\tfor (const file of await collectFiles(resolve(rootDir, \"mcp\"))) {\n\t\t\t\taddFile(file.fullPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// optional mcp/ directory\n\t\t}\n\t}\n\n\t// Helper scripts (plugin workbench etc.): always pack scripts/ when present.\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"scripts\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// optional scripts/ directory\n\t}\n\n\t// Handbook / agent docs beside skills (not always listed in skillPaths).\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"agent/docs\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// optional agent/docs\n\t}\n\n\t// Plugin i18n catalogs (ADR-0033): bundle locales/<lang>.json so the host can\n\t// load them alongside the manifest. Optional — absent for single-language plugins.\n\ttry {\n\t\tfor (const file of await collectFiles(resolve(rootDir, \"locales\"))) {\n\t\t\taddFile(file.fullPath);\n\t\t}\n\t} catch {\n\t\t// no locales/ directory — nothing to bundle\n\t}\n\n\treturn [...packageFiles.values()].sort((a, b) => a.archivePath.localeCompare(b.archivePath));\n}\n\nexport async function createVettaPluginPackage(\n\toptions: CreateVettaPluginPackageOptions = {},\n): Promise<VettaPluginPackageResult> {\n\tconst rootDir = resolve(options.rootDir ?? process.cwd());\n\tconst manifestPath = resolve(rootDir, options.manifestPath ?? \"plugin.json\");\n\tconst releaseDir = resolve(rootDir, options.releaseDir ?? \"release\");\n\tconst distDir = resolve(rootDir, options.distDir ?? \"dist\");\n\tconst pluginManifest = parsePluginManifest(parseJsonObject(await readFile(manifestPath), basename(manifestPath)));\n\tconst outputPath = join(releaseDir, `${pluginManifest.id}-${pluginManifest.version}.zip`);\n\tconst files = await collectRuntimeFiles(rootDir, manifestPath, distDir);\n\tassertPluginPermissionContract(\n\t\tpluginManifest,\n\t\tawait Promise.all(\n\t\t\tfiles\n\t\t\t\t.filter((file) => /\\.(?:c|m)?js$/u.test(file.archivePath))\n\t\t\t\t.map(async (file) => ({ fileName: file.archivePath, code: await readFile(file.fullPath, \"utf8\") })),\n\t\t),\n\t);\n\tlet npmOutputPath: string | undefined;\n\tif (options.npmArchive === true) {\n\t\tconst packageManifest = parseVettaNpmPluginPackage(\n\t\t\tparseJsonObject(await readFile(resolve(rootDir, \"package.json\")), \"package.json\"),\n\t\t);\n\t\tif (packageManifest.version !== pluginManifest.version) {\n\t\t\tthrow new Error(\n\t\t\t\t`npm package version ${packageManifest.version} must match plugin version ${pluginManifest.version}.`,\n\t\t\t);\n\t\t}\n\t\tif (packageManifest.vetta.pluginId !== pluginManifest.id) {\n\t\t\tthrow new Error(\n\t\t\t\t`npm package plugin id ${packageManifest.vetta.pluginId} must match plugin id ${pluginManifest.id}.`,\n\t\t\t);\n\t\t}\n\t\tif (packageManifest.vetta.archive !== VETTA_NPM_PLUGIN_ARCHIVE_PATH) {\n\t\t\tthrow new Error(`npm package archive must be ${VETTA_NPM_PLUGIN_ARCHIVE_PATH}.`);\n\t\t}\n\t\tnpmOutputPath = resolve(rootDir, VETTA_NPM_PLUGIN_ARCHIVE_PATH);\n\t}\n\n\tawait mkdir(releaseDir, { recursive: true });\n\tawait rm(outputPath, { force: true });\n\tconst archive = await createZip(files);\n\tawait writeFile(outputPath, archive);\n\tif (npmOutputPath && npmOutputPath !== outputPath) {\n\t\tawait mkdir(dirname(npmOutputPath), { recursive: true });\n\t\tawait rm(npmOutputPath, { force: true });\n\t\tawait writeFile(npmOutputPath, archive);\n\t}\n\n\treturn { outputPath, npmOutputPath, files };\n}\n"]}