sunpeak 0.15.4 → 0.16.1

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 (98) hide show
  1. package/README.md +51 -48
  2. package/bin/commands/build.mjs +13 -4
  3. package/bin/commands/dev.mjs +64 -19
  4. package/bin/commands/new.mjs +13 -3
  5. package/bin/lib/extract-resource.mjs +1 -1
  6. package/bin/lib/extract-tool.mjs +78 -0
  7. package/bin/lib/patterns.mjs +2 -26
  8. package/dist/chatgpt/index.cjs +3 -6
  9. package/dist/chatgpt/index.cjs.map +1 -1
  10. package/dist/chatgpt/index.d.ts +1 -1
  11. package/dist/chatgpt/index.js +6 -9
  12. package/dist/claude/index.cjs +1 -1
  13. package/dist/claude/index.js +1 -1
  14. package/dist/discovery-CH80W5l9.js +217 -0
  15. package/dist/discovery-CH80W5l9.js.map +1 -0
  16. package/dist/discovery-DmB8_4QL.cjs +216 -0
  17. package/dist/discovery-DmB8_4QL.cjs.map +1 -0
  18. package/dist/{index-CutQgPzR.js → index-BjnAsaqp.js} +3 -6
  19. package/dist/index-BjnAsaqp.js.map +1 -0
  20. package/dist/{index-Cngntkp2.cjs → index-BvQ_ZuOO.cjs} +3 -6
  21. package/dist/{index-Cngntkp2.cjs.map → index-BvQ_ZuOO.cjs.map} +1 -1
  22. package/dist/{index-B0dxRJvS.cjs → index-C9CVbGFt.cjs} +3 -6
  23. package/dist/index-C9CVbGFt.cjs.map +1 -0
  24. package/dist/{index-Ce_5ZIdJ.js → index-CTGEqlgk.js} +3 -6
  25. package/dist/{index-Ce_5ZIdJ.js.map → index-CTGEqlgk.js.map} +1 -1
  26. package/dist/index.cjs +48 -5
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.js +3404 -3361
  30. package/dist/index.js.map +1 -1
  31. package/dist/lib/discovery-cli.cjs +58 -5
  32. package/dist/lib/discovery-cli.cjs.map +1 -1
  33. package/dist/lib/discovery-cli.d.ts +3 -2
  34. package/dist/lib/discovery-cli.js +61 -8
  35. package/dist/lib/discovery-cli.js.map +1 -1
  36. package/dist/lib/discovery.d.ts +42 -43
  37. package/dist/lib/extract-tool.d.ts +12 -0
  38. package/dist/mcp/favicon.d.ts +1 -1
  39. package/dist/mcp/index.cjs +3 -2
  40. package/dist/mcp/index.cjs.map +1 -1
  41. package/dist/mcp/index.d.ts +1 -1
  42. package/dist/mcp/index.js +3 -2
  43. package/dist/mcp/index.js.map +1 -1
  44. package/dist/mcp/types.d.ts +24 -1
  45. package/dist/platform/chatgpt/index.cjs +1 -1
  46. package/dist/platform/chatgpt/index.js +1 -1
  47. package/dist/simulator/index.cjs +2 -5
  48. package/dist/simulator/index.cjs.map +1 -1
  49. package/dist/simulator/index.d.ts +1 -1
  50. package/dist/simulator/index.js +5 -8
  51. package/dist/simulator/simulator-url.d.ts +9 -9
  52. package/dist/{simulator-DcfQBRXE.cjs → simulator-B56j5P8W.cjs} +8 -2
  53. package/dist/{simulator-DcfQBRXE.cjs.map → simulator-B56j5P8W.cjs.map} +1 -1
  54. package/dist/{simulator-CxrtnguM.js → simulator-C0H_k092.js} +8 -2
  55. package/dist/{simulator-CxrtnguM.js.map → simulator-C0H_k092.js.map} +1 -1
  56. package/dist/simulator-url-CuLqtnSS.js.map +1 -1
  57. package/dist/simulator-url-rgg_KYOg.cjs.map +1 -1
  58. package/dist/types/resource-config.d.ts +7 -5
  59. package/dist/{use-app-D_TeaMFG.js → use-app-BThbgFFT.js} +51 -22
  60. package/dist/{use-app-D_TeaMFG.js.map → use-app-BThbgFFT.js.map} +1 -1
  61. package/dist/{use-app-BnoSPiUT.cjs → use-app-BuufpXTQ.cjs} +49 -20
  62. package/dist/{use-app-BnoSPiUT.cjs.map → use-app-BuufpXTQ.cjs.map} +1 -1
  63. package/package.json +1 -1
  64. package/template/.sunpeak/dev.tsx +8 -4
  65. package/template/.sunpeak/resource-loader.tsx +2 -1
  66. package/template/README.md +14 -10
  67. package/template/package.json +2 -1
  68. package/template/src/resources/albums/{albums-resource.test.tsx → albums.test.tsx} +1 -1
  69. package/template/src/resources/albums/{albums-resource.tsx → albums.tsx} +0 -1
  70. package/template/src/resources/carousel/{carousel-resource.test.tsx → carousel.test.tsx} +1 -1
  71. package/template/src/resources/carousel/{carousel-resource.tsx → carousel.tsx} +0 -1
  72. package/template/src/resources/index.ts +4 -4
  73. package/template/src/resources/map/{map-resource.test.tsx → map.test.tsx} +1 -1
  74. package/template/src/resources/map/{map-resource.tsx → map.tsx} +0 -1
  75. package/template/src/resources/review/{review-resource.test.tsx → review.test.tsx} +1 -1
  76. package/template/src/resources/review/{review-resource.tsx → review.tsx} +1 -2
  77. package/template/src/server.ts +15 -0
  78. package/template/src/tools/review-diff.ts +24 -0
  79. package/template/src/tools/review-post.ts +26 -0
  80. package/template/src/tools/review-purchase.ts +31 -0
  81. package/template/src/tools/show-albums.ts +22 -0
  82. package/template/src/tools/show-carousel.ts +25 -0
  83. package/template/src/tools/show-map.ts +29 -0
  84. package/template/tests/e2e/albums.spec.ts +6 -6
  85. package/template/tests/e2e/carousel.spec.ts +6 -6
  86. package/template/tests/e2e/map.spec.ts +11 -11
  87. package/template/tests/simulations/{review/review-diff-simulation.json → review-diff.json} +1 -31
  88. package/template/tests/simulations/{review/review-post-simulation.json → review-post.json} +1 -37
  89. package/template/tests/simulations/{review/review-purchase-simulation.json → review-purchase.json} +1 -38
  90. package/template/tests/simulations/{albums/albums-show-simulation.json → show-albums.json} +1 -24
  91. package/template/tests/simulations/{carousel/carousel-show-simulation.json → show-carousel.json} +1 -24
  92. package/template/tests/simulations/{map/map-show-simulation.json → show-map.json} +1 -35
  93. package/dist/discovery-CRR3SlyI.cjs +0 -156
  94. package/dist/discovery-CRR3SlyI.cjs.map +0 -1
  95. package/dist/discovery-DzV3HLXs.js +0 -157
  96. package/dist/discovery-DzV3HLXs.js.map +0 -1
  97. package/dist/index-B0dxRJvS.cjs.map +0 -1
  98. package/dist/index-CutQgPzR.js.map +0 -1
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
- const discovery = require("../discovery-CRR3SlyI.cjs");
25
+ const discovery = require("../discovery-DmB8_4QL.cjs");
26
26
  const path = require("path");
27
27
  async function extractResourceExport(tsxPath) {
28
28
  const esbuild = await import("esbuild");
@@ -67,19 +67,72 @@ async function extractResourceExport(tsxPath) {
67
67
  const resource = mod.exports.resource;
68
68
  if (!resource) {
69
69
  throw new Error(
70
- `No "resource" export found in ${tsxPath}. Add: export const resource: ResourceConfig = { name: '...', ... };`
70
+ `No "resource" export found in ${tsxPath}. Add: export const resource: ResourceConfig = { title: '...', ... };`
71
71
  );
72
72
  }
73
73
  return resource;
74
74
  }
75
+ async function extractToolExport(tsPath) {
76
+ const esbuild = await import("esbuild");
77
+ const absolutePath = path.resolve(tsPath);
78
+ const dir = path.dirname(absolutePath);
79
+ const base = path.basename(absolutePath);
80
+ const result = await esbuild.build({
81
+ stdin: {
82
+ contents: `export { tool } from './${base}';`,
83
+ resolveDir: dir,
84
+ loader: "ts"
85
+ },
86
+ bundle: true,
87
+ write: false,
88
+ format: "esm",
89
+ treeShaking: true,
90
+ loader: { ".tsx": "tsx", ".ts": "ts", ".jsx": "jsx" },
91
+ logLevel: "silent",
92
+ plugins: [
93
+ {
94
+ name: "externalize-node-modules",
95
+ setup(build) {
96
+ build.onResolve({ filter: /.*/ }, (args) => {
97
+ if (args.kind !== "import-statement") return;
98
+ if (!args.path.startsWith(".") && !args.path.startsWith("/")) {
99
+ return { external: true };
100
+ }
101
+ return void 0;
102
+ });
103
+ }
104
+ }
105
+ ]
106
+ });
107
+ if (!result.outputFiles?.length) {
108
+ throw new Error(`Failed to extract tool from ${tsPath}`);
109
+ }
110
+ const code = result.outputFiles[0].text.replace(/^import\s+.*$/gm, "").replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, "");
111
+ let tool;
112
+ try {
113
+ const fn = new Function(code + "\nreturn tool;");
114
+ tool = fn();
115
+ } catch {
116
+ const toolMatch = result.outputFiles[0].text.match(/var tool\s*=\s*(\{[\s\S]*?\n\});/);
117
+ if (toolMatch) {
118
+ tool = new Function("return " + toolMatch[1])();
119
+ }
120
+ }
121
+ if (!tool) {
122
+ throw new Error(
123
+ `No "tool" export found in ${tsPath}. Add: export const tool: AppToolConfig = { resource, ... };`
124
+ );
125
+ }
126
+ return { tool };
127
+ }
75
128
  exports.extractResourceKey = discovery.extractResourceKey;
76
129
  exports.extractSimulationKey = discovery.extractSimulationKey;
77
- exports.extractSimulationName = discovery.extractSimulationName;
78
130
  exports.findResourceDirs = discovery.findResourceDirs;
79
131
  exports.findResourceKey = discovery.findResourceKey;
80
- exports.findSimulationFiles = discovery.findSimulationFiles;
132
+ exports.findSimulationFilesFlat = discovery.findSimulationFilesFlat;
133
+ exports.findToolFiles = discovery.findToolFiles;
81
134
  exports.getComponentName = discovery.getComponentName;
82
- exports.isSimulationFile = discovery.isSimulationFile;
83
135
  exports.toPascalCase = discovery.toPascalCase;
84
136
  exports.extractResourceExport = extractResourceExport;
137
+ exports.extractToolExport = extractToolExport;
85
138
  //# sourceMappingURL=discovery-cli.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery-cli.cjs","sources":["../../src/lib/extract-resource.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Extract the `resource` named export from a resource .tsx file.\n *\n * Uses esbuild to bundle only the `resource` export, stubbing all of the\n * resource file's own imports (React, components, etc.) so only the static\n * config object is evaluated.\n */\nexport async function extractResourceExport(tsxPath: string): Promise<Record<string, unknown>> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsxPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { resource } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'cjs',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-deps',\n setup(build) {\n let entryResolved = false;\n // Let stdin's import of the resource file resolve normally,\n // but externalize everything the resource file itself imports.\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n if (!entryResolved) {\n entryResolved = true;\n return;\n }\n return { external: true };\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract resource from ${tsxPath}`);\n }\n\n const code = result.outputFiles[0].text;\n const mod: { exports: Record<string, unknown> } = { exports: {} };\n new Function('module', 'exports', 'require', code)(mod, mod.exports, () => ({}));\n\n const resource = mod.exports.resource as Record<string, unknown> | undefined;\n if (!resource) {\n throw new Error(\n `No \"resource\" export found in ${tsxPath}. ` +\n `Add: export const resource: ResourceConfig = { name: '...', ... };`\n );\n }\n\n return resource;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AASA,eAAsB,sBAAsB,SAAmD;AAC7F,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,eAAe,KAAK,QAAQ,OAAO;AACzC,QAAM,MAAM,KAAK,QAAQ,YAAY;AACrC,QAAM,OAAO,KAAK,SAAS,YAAY;AAEvC,QAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,IACjC,OAAO;AAAA,MACL,UAAU,+BAA+B,IAAI;AAAA,MAC7C,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA;AAAA,IAEV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ,EAAE,QAAQ,OAAO,OAAO,MAAM,QAAQ,MAAA;AAAA,IAC9C,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,MAAM,OAAO;AACX,cAAI,gBAAgB;AAGpB,gBAAM,UAAU,EAAE,QAAQ,KAAA,GAAQ,CAAC,SAAS;AAC1C,gBAAI,KAAK,SAAS,mBAAoB;AACtC,gBAAI,CAAC,eAAe;AAClB,8BAAgB;AAChB;AAAA,YACF;AACA,mBAAO,EAAE,UAAU,KAAA;AAAA,UACrB,CAAC;AAAA,QACH;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAED,MAAI,CAAC,OAAO,aAAa,QAAQ;AAC/B,UAAM,IAAI,MAAM,mCAAmC,OAAO,EAAE;AAAA,EAC9D;AAEA,QAAM,OAAO,OAAO,YAAY,CAAC,EAAE;AACnC,QAAM,MAA4C,EAAE,SAAS,GAAC;AAC9D,MAAI,SAAS,UAAU,WAAW,WAAW,IAAI,EAAE,KAAK,IAAI,SAAS,OAAO,CAAA,EAAG;AAE/E,QAAM,WAAW,IAAI,QAAQ;AAC7B,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,iCAAiC,OAAO;AAAA,IAAA;AAAA,EAG5C;AAEA,SAAO;AACT;;;;;;;;;;;"}
1
+ {"version":3,"file":"discovery-cli.cjs","sources":["../../src/lib/extract-resource.ts","../../src/lib/extract-tool.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Extract the `resource` named export from a resource .tsx file.\n *\n * Uses esbuild to bundle only the `resource` export, stubbing all of the\n * resource file's own imports (React, components, etc.) so only the static\n * config object is evaluated.\n */\nexport async function extractResourceExport(tsxPath: string): Promise<Record<string, unknown>> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsxPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { resource } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'cjs',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-deps',\n setup(build) {\n let entryResolved = false;\n // Let stdin's import of the resource file resolve normally,\n // but externalize everything the resource file itself imports.\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n if (!entryResolved) {\n entryResolved = true;\n return;\n }\n return { external: true };\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract resource from ${tsxPath}`);\n }\n\n const code = result.outputFiles[0].text;\n const mod: { exports: Record<string, unknown> } = { exports: {} };\n new Function('module', 'exports', 'require', code)(mod, mod.exports, () => ({}));\n\n const resource = mod.exports.resource as Record<string, unknown> | undefined;\n if (!resource) {\n throw new Error(\n `No \"resource\" export found in ${tsxPath}. ` +\n `Add: export const resource: ResourceConfig = { title: '...', ... };`\n );\n }\n\n return resource;\n}\n","import path from 'path';\n\n/**\n * Extract the `tool` named export from a tool .ts file.\n *\n * Uses esbuild in ESM mode to compile TypeScript and tree-shake to just the\n * `tool` export. ESM tree-shaking drops unused exports (schema, handler) so\n * their dependencies (zod, etc.) are never evaluated.\n *\n * `schema` and `default` handler are loaded at runtime via Vite SSR.\n */\nexport async function extractToolExport(\n tsPath: string\n): Promise<{ tool: Record<string, unknown> }> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { tool } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'esm',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-node-modules',\n setup(build) {\n // Resolve relative imports normally (resource files, local modules)\n // but externalize everything from node_modules\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n // Bare specifiers (not starting with . or /) are node_modules\n if (!args.path.startsWith('.') && !args.path.startsWith('/')) {\n return { external: true };\n }\n return undefined;\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract tool from ${tsPath}`);\n }\n\n // Strip import statements and export block so we can eval as plain JS.\n // `tool` is pure data (no dependencies), so stripping imports is safe.\n // Other top-level code (schema, etc.) may reference stripped imports but\n // we only need the `tool` variable — errors in other code are caught and ignored.\n const code = result.outputFiles[0].text\n .replace(/^import\\s+.*$/gm, '')\n .replace(/^export\\s*\\{[^}]*\\}\\s*;?\\s*$/m, '');\n let tool: Record<string, unknown> | undefined;\n try {\n const fn = new Function(code + '\\nreturn tool;');\n tool = fn() as Record<string, unknown> | undefined;\n } catch {\n // If other top-level code crashes (e.g. schema using stripped zod),\n // extract just the tool variable declaration and eval that alone.\n const toolMatch = result.outputFiles[0].text.match(/var tool\\s*=\\s*(\\{[\\s\\S]*?\\n\\});/);\n if (toolMatch) {\n tool = new Function('return ' + toolMatch[1])() as Record<string, unknown>;\n }\n }\n if (!tool) {\n throw new Error(\n `No \"tool\" export found in ${tsPath}. ` +\n `Add: export const tool: AppToolConfig = { resource, ... };`\n );\n }\n\n return { tool };\n}\n"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  * This module re-exports only the Node.js-compatible discovery utilities
5
5
  * without pulling in React components. Used by CLI commands like `sunpeak dev`.
6
6
  */
7
- export { findResourceDirs, findSimulationFiles, isSimulationFile, extractSimulationName, extractResourceKey, extractSimulationKey, toPascalCase, getComponentName, findResourceKey, } from './discovery';
8
- export type { ResourceDirInfo, FsOps } from './discovery';
7
+ export { findResourceDirs, findSimulationFilesFlat, findToolFiles, extractResourceKey, extractSimulationKey, toPascalCase, getComponentName, findResourceKey, } from './discovery';
8
+ export type { ResourceDirInfo, ToolFileInfo, SimulationFileInfo, FsOps } from './discovery';
9
9
  export { extractResourceExport } from './extract-resource';
10
+ export { extractToolExport } from './extract-tool';
@@ -1,4 +1,4 @@
1
- import { e, f, g, h, i, j, k, l, t } from "../discovery-DzV3HLXs.js";
1
+ import { e, f, g, h, j, k, i, t } from "../discovery-CH80W5l9.js";
2
2
  import path from "path";
3
3
  async function extractResourceExport(tsxPath) {
4
4
  const esbuild = await import("esbuild");
@@ -43,21 +43,74 @@ async function extractResourceExport(tsxPath) {
43
43
  const resource = mod.exports.resource;
44
44
  if (!resource) {
45
45
  throw new Error(
46
- `No "resource" export found in ${tsxPath}. Add: export const resource: ResourceConfig = { name: '...', ... };`
46
+ `No "resource" export found in ${tsxPath}. Add: export const resource: ResourceConfig = { title: '...', ... };`
47
47
  );
48
48
  }
49
49
  return resource;
50
50
  }
51
+ async function extractToolExport(tsPath) {
52
+ const esbuild = await import("esbuild");
53
+ const absolutePath = path.resolve(tsPath);
54
+ const dir = path.dirname(absolutePath);
55
+ const base = path.basename(absolutePath);
56
+ const result = await esbuild.build({
57
+ stdin: {
58
+ contents: `export { tool } from './${base}';`,
59
+ resolveDir: dir,
60
+ loader: "ts"
61
+ },
62
+ bundle: true,
63
+ write: false,
64
+ format: "esm",
65
+ treeShaking: true,
66
+ loader: { ".tsx": "tsx", ".ts": "ts", ".jsx": "jsx" },
67
+ logLevel: "silent",
68
+ plugins: [
69
+ {
70
+ name: "externalize-node-modules",
71
+ setup(build) {
72
+ build.onResolve({ filter: /.*/ }, (args) => {
73
+ if (args.kind !== "import-statement") return;
74
+ if (!args.path.startsWith(".") && !args.path.startsWith("/")) {
75
+ return { external: true };
76
+ }
77
+ return void 0;
78
+ });
79
+ }
80
+ }
81
+ ]
82
+ });
83
+ if (!result.outputFiles?.length) {
84
+ throw new Error(`Failed to extract tool from ${tsPath}`);
85
+ }
86
+ const code = result.outputFiles[0].text.replace(/^import\s+.*$/gm, "").replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, "");
87
+ let tool;
88
+ try {
89
+ const fn = new Function(code + "\nreturn tool;");
90
+ tool = fn();
91
+ } catch {
92
+ const toolMatch = result.outputFiles[0].text.match(/var tool\s*=\s*(\{[\s\S]*?\n\});/);
93
+ if (toolMatch) {
94
+ tool = new Function("return " + toolMatch[1])();
95
+ }
96
+ }
97
+ if (!tool) {
98
+ throw new Error(
99
+ `No "tool" export found in ${tsPath}. Add: export const tool: AppToolConfig = { resource, ... };`
100
+ );
101
+ }
102
+ return { tool };
103
+ }
51
104
  export {
52
105
  extractResourceExport,
53
106
  e as extractResourceKey,
54
107
  f as extractSimulationKey,
55
- g as extractSimulationName,
56
- h as findResourceDirs,
57
- i as findResourceKey,
58
- j as findSimulationFiles,
59
- k as getComponentName,
60
- l as isSimulationFile,
108
+ extractToolExport,
109
+ g as findResourceDirs,
110
+ h as findResourceKey,
111
+ j as findSimulationFilesFlat,
112
+ k as findToolFiles,
113
+ i as getComponentName,
61
114
  t as toPascalCase
62
115
  };
63
116
  //# sourceMappingURL=discovery-cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery-cli.js","sources":["../../src/lib/extract-resource.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Extract the `resource` named export from a resource .tsx file.\n *\n * Uses esbuild to bundle only the `resource` export, stubbing all of the\n * resource file's own imports (React, components, etc.) so only the static\n * config object is evaluated.\n */\nexport async function extractResourceExport(tsxPath: string): Promise<Record<string, unknown>> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsxPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { resource } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'cjs',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-deps',\n setup(build) {\n let entryResolved = false;\n // Let stdin's import of the resource file resolve normally,\n // but externalize everything the resource file itself imports.\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n if (!entryResolved) {\n entryResolved = true;\n return;\n }\n return { external: true };\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract resource from ${tsxPath}`);\n }\n\n const code = result.outputFiles[0].text;\n const mod: { exports: Record<string, unknown> } = { exports: {} };\n new Function('module', 'exports', 'require', code)(mod, mod.exports, () => ({}));\n\n const resource = mod.exports.resource as Record<string, unknown> | undefined;\n if (!resource) {\n throw new Error(\n `No \"resource\" export found in ${tsxPath}. ` +\n `Add: export const resource: ResourceConfig = { name: '...', ... };`\n );\n }\n\n return resource;\n}\n"],"names":[],"mappings":";;AASA,eAAsB,sBAAsB,SAAmD;AAC7F,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,eAAe,KAAK,QAAQ,OAAO;AACzC,QAAM,MAAM,KAAK,QAAQ,YAAY;AACrC,QAAM,OAAO,KAAK,SAAS,YAAY;AAEvC,QAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,IACjC,OAAO;AAAA,MACL,UAAU,+BAA+B,IAAI;AAAA,MAC7C,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA;AAAA,IAEV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ,EAAE,QAAQ,OAAO,OAAO,MAAM,QAAQ,MAAA;AAAA,IAC9C,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,MAAM,OAAO;AACX,cAAI,gBAAgB;AAGpB,gBAAM,UAAU,EAAE,QAAQ,KAAA,GAAQ,CAAC,SAAS;AAC1C,gBAAI,KAAK,SAAS,mBAAoB;AACtC,gBAAI,CAAC,eAAe;AAClB,8BAAgB;AAChB;AAAA,YACF;AACA,mBAAO,EAAE,UAAU,KAAA;AAAA,UACrB,CAAC;AAAA,QACH;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAED,MAAI,CAAC,OAAO,aAAa,QAAQ;AAC/B,UAAM,IAAI,MAAM,mCAAmC,OAAO,EAAE;AAAA,EAC9D;AAEA,QAAM,OAAO,OAAO,YAAY,CAAC,EAAE;AACnC,QAAM,MAA4C,EAAE,SAAS,GAAC;AAC9D,MAAI,SAAS,UAAU,WAAW,WAAW,IAAI,EAAE,KAAK,IAAI,SAAS,OAAO,CAAA,EAAG;AAE/E,QAAM,WAAW,IAAI,QAAQ;AAC7B,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,iCAAiC,OAAO;AAAA,IAAA;AAAA,EAG5C;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"discovery-cli.js","sources":["../../src/lib/extract-resource.ts","../../src/lib/extract-tool.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Extract the `resource` named export from a resource .tsx file.\n *\n * Uses esbuild to bundle only the `resource` export, stubbing all of the\n * resource file's own imports (React, components, etc.) so only the static\n * config object is evaluated.\n */\nexport async function extractResourceExport(tsxPath: string): Promise<Record<string, unknown>> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsxPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { resource } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'cjs',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-deps',\n setup(build) {\n let entryResolved = false;\n // Let stdin's import of the resource file resolve normally,\n // but externalize everything the resource file itself imports.\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n if (!entryResolved) {\n entryResolved = true;\n return;\n }\n return { external: true };\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract resource from ${tsxPath}`);\n }\n\n const code = result.outputFiles[0].text;\n const mod: { exports: Record<string, unknown> } = { exports: {} };\n new Function('module', 'exports', 'require', code)(mod, mod.exports, () => ({}));\n\n const resource = mod.exports.resource as Record<string, unknown> | undefined;\n if (!resource) {\n throw new Error(\n `No \"resource\" export found in ${tsxPath}. ` +\n `Add: export const resource: ResourceConfig = { title: '...', ... };`\n );\n }\n\n return resource;\n}\n","import path from 'path';\n\n/**\n * Extract the `tool` named export from a tool .ts file.\n *\n * Uses esbuild in ESM mode to compile TypeScript and tree-shake to just the\n * `tool` export. ESM tree-shaking drops unused exports (schema, handler) so\n * their dependencies (zod, etc.) are never evaluated.\n *\n * `schema` and `default` handler are loaded at runtime via Vite SSR.\n */\nexport async function extractToolExport(\n tsPath: string\n): Promise<{ tool: Record<string, unknown> }> {\n const esbuild = await import('esbuild');\n const absolutePath = path.resolve(tsPath);\n const dir = path.dirname(absolutePath);\n const base = path.basename(absolutePath);\n\n const result = await esbuild.build({\n stdin: {\n contents: `export { tool } from './${base}';`,\n resolveDir: dir,\n loader: 'ts',\n },\n bundle: true,\n write: false,\n format: 'esm',\n treeShaking: true,\n loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },\n logLevel: 'silent',\n plugins: [\n {\n name: 'externalize-node-modules',\n setup(build) {\n // Resolve relative imports normally (resource files, local modules)\n // but externalize everything from node_modules\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind !== 'import-statement') return;\n // Bare specifiers (not starting with . or /) are node_modules\n if (!args.path.startsWith('.') && !args.path.startsWith('/')) {\n return { external: true };\n }\n return undefined;\n });\n },\n },\n ],\n });\n\n if (!result.outputFiles?.length) {\n throw new Error(`Failed to extract tool from ${tsPath}`);\n }\n\n // Strip import statements and export block so we can eval as plain JS.\n // `tool` is pure data (no dependencies), so stripping imports is safe.\n // Other top-level code (schema, etc.) may reference stripped imports but\n // we only need the `tool` variable — errors in other code are caught and ignored.\n const code = result.outputFiles[0].text\n .replace(/^import\\s+.*$/gm, '')\n .replace(/^export\\s*\\{[^}]*\\}\\s*;?\\s*$/m, '');\n let tool: Record<string, unknown> | undefined;\n try {\n const fn = new Function(code + '\\nreturn tool;');\n tool = fn() as Record<string, unknown> | undefined;\n } catch {\n // If other top-level code crashes (e.g. schema using stripped zod),\n // extract just the tool variable declaration and eval that alone.\n const toolMatch = result.outputFiles[0].text.match(/var tool\\s*=\\s*(\\{[\\s\\S]*?\\n\\});/);\n if (toolMatch) {\n tool = new Function('return ' + toolMatch[1])() as Record<string, unknown>;\n }\n }\n if (!tool) {\n throw new Error(\n `No \"tool\" export found in ${tsPath}. ` +\n `Add: export const tool: AppToolConfig = { resource, ... };`\n );\n }\n\n return { tool };\n}\n"],"names":[],"mappings":";;AASA,eAAsB,sBAAsB,SAAmD;AAC7F,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,eAAe,KAAK,QAAQ,OAAO;AACzC,QAAM,MAAM,KAAK,QAAQ,YAAY;AACrC,QAAM,OAAO,KAAK,SAAS,YAAY;AAEvC,QAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,IACjC,OAAO;AAAA,MACL,UAAU,+BAA+B,IAAI;AAAA,MAC7C,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA;AAAA,IAEV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ,EAAE,QAAQ,OAAO,OAAO,MAAM,QAAQ,MAAA;AAAA,IAC9C,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,MAAM,OAAO;AACX,cAAI,gBAAgB;AAGpB,gBAAM,UAAU,EAAE,QAAQ,KAAA,GAAQ,CAAC,SAAS;AAC1C,gBAAI,KAAK,SAAS,mBAAoB;AACtC,gBAAI,CAAC,eAAe;AAClB,8BAAgB;AAChB;AAAA,YACF;AACA,mBAAO,EAAE,UAAU,KAAA;AAAA,UACrB,CAAC;AAAA,QACH;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAED,MAAI,CAAC,OAAO,aAAa,QAAQ;AAC/B,UAAM,IAAI,MAAM,mCAAmC,OAAO,EAAE;AAAA,EAC9D;AAEA,QAAM,OAAO,OAAO,YAAY,CAAC,EAAE;AACnC,QAAM,MAA4C,EAAE,SAAS,GAAC;AAC9D,MAAI,SAAS,UAAU,WAAW,WAAW,IAAI,EAAE,KAAK,IAAI,SAAS,OAAO,CAAA,EAAG;AAE/E,QAAM,WAAW,IAAI,QAAQ;AAC7B,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,iCAAiC,OAAO;AAAA,IAAA;AAAA,EAG5C;AAEA,SAAO;AACT;ACrDA,eAAsB,kBACpB,QAC4C;AAC5C,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,eAAe,KAAK,QAAQ,MAAM;AACxC,QAAM,MAAM,KAAK,QAAQ,YAAY;AACrC,QAAM,OAAO,KAAK,SAAS,YAAY;AAEvC,QAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,IACjC,OAAO;AAAA,MACL,UAAU,2BAA2B,IAAI;AAAA,MACzC,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA;AAAA,IAEV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ,EAAE,QAAQ,OAAO,OAAO,MAAM,QAAQ,MAAA;AAAA,IAC9C,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,MAAM,OAAO;AAGX,gBAAM,UAAU,EAAE,QAAQ,KAAA,GAAQ,CAAC,SAAS;AAC1C,gBAAI,KAAK,SAAS,mBAAoB;AAEtC,gBAAI,CAAC,KAAK,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,KAAK,WAAW,GAAG,GAAG;AAC5D,qBAAO,EAAE,UAAU,KAAA;AAAA,YACrB;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAED,MAAI,CAAC,OAAO,aAAa,QAAQ;AAC/B,UAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;AAAA,EACzD;AAMA,QAAM,OAAO,OAAO,YAAY,CAAC,EAAE,KAChC,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,iCAAiC,EAAE;AAC9C,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,IAAI,SAAS,OAAO,gBAAgB;AAC/C,WAAO,GAAA;AAAA,EACT,QAAQ;AAGN,UAAM,YAAY,OAAO,YAAY,CAAC,EAAE,KAAK,MAAM,kCAAkC;AACrF,QAAI,WAAW;AACb,aAAO,IAAI,SAAS,YAAY,UAAU,CAAC,CAAC,EAAA;AAAA,IAC9C;AAAA,EACF;AACA,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR,6BAA6B,MAAM;AAAA,IAAA;AAAA,EAGvC;AAEA,SAAO,EAAE,KAAA;AACX;"}
@@ -6,21 +6,20 @@ import { Simulation } from '../types/simulation.js';
6
6
  */
7
7
  export declare function toPascalCase(str: string): string;
8
8
  /**
9
- * Extract the resource key from a resource file path
10
- * @example extractResourceKey('./review-resource.tsx') // 'review'
11
- * @example extractResourceKey('../src/resources/albums-resource.tsx') // 'albums'
9
+ * Extract the resource key from a resource file path.
10
+ * Matches {name}.tsx (e.g., './albums/albums.tsx' 'albums')
12
11
  */
13
12
  export declare function extractResourceKey(path: string): string | undefined;
14
13
  /**
15
- * Extract the simulation key from a simulation file path
16
- * @example extractSimulationKey('./albums-show-simulation.json') // 'albums-show'
14
+ * Extract the simulation key from a simulation file path.
15
+ * Matches any *.json file (e.g., './show-albums.json' 'show-albums')
17
16
  */
18
17
  export declare function extractSimulationKey(path: string): string | undefined;
19
18
  /**
20
19
  * Find the best matching resource key for a simulation key.
21
20
  * Matches the longest resource name that is a prefix of the simulation key.
22
- * @example findResourceKey('albums-show', ['albums', 'album']) // 'albums'
23
21
  * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'
22
+ * @example findResourceKey('albums', ['albums', 'review']) // 'albums'
24
23
  */
25
24
  export declare function findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined;
26
25
  /**
@@ -35,7 +34,7 @@ type GlobModules = Record<string, unknown>;
35
34
  * Extracts components and exports them with PascalCase names.
36
35
  *
37
36
  * @example
38
- * const modules = import.meta.glob('./**\/*-resource.tsx', { eager: true });
37
+ * const modules = import.meta.glob('./*\/*.tsx', { eager: true });
39
38
  * export default createResourceExports(modules);
40
39
  */
41
40
  export declare function createResourceExports(modules: GlobModules): Record<string, React.ComponentType>;
@@ -44,7 +43,7 @@ export declare function createResourceExports(modules: GlobModules): Record<stri
44
43
  * Used for connecting simulations to their resource definitions.
45
44
  *
46
45
  * @example
47
- * const modules = import.meta.glob('../src/resources/**\/*-resource.tsx', { eager: true });
46
+ * const modules = import.meta.glob('../src/resources/*\/*.tsx', { eager: true });
48
47
  * const resourcesMap = buildResourceMap(modules);
49
48
  */
50
49
  export declare function buildResourceMap<T>(modules: GlobModules): Map<string, T>;
@@ -69,7 +68,7 @@ export interface BuildSimulationsOptions<TResource, TSimulation> {
69
68
  */
70
69
  export declare function buildSimulations<TResource, TSimulation>(options: BuildSimulationsOptions<TResource, TSimulation>): Record<string, TSimulation>;
71
70
  /**
72
- * Resource metadata from *-resource.tsx files
71
+ * Resource metadata from resource .tsx files
73
72
  */
74
73
  export interface ResourceMetadata {
75
74
  name: string;
@@ -79,23 +78,19 @@ export interface ResourceMetadata {
79
78
  * Options for building dev simulations
80
79
  */
81
80
  export interface BuildDevSimulationsOptions {
82
- /** Glob result of simulation JSON files: import.meta.glob('*-simulation.json', { eager: true }) */
81
+ /** Glob result of simulation JSON files */
83
82
  simulationModules: GlobModules;
84
- /** Glob result of resource JSON files: import.meta.glob('*-resource.tsx', { eager: true }) */
85
- resourceModules: GlobModules;
86
83
  /** Resource components map from src/resources/index.ts */
87
84
  resourceComponents: Record<string, React.ComponentType>;
85
+ /** Glob result of tool files: import.meta.glob('src/tools/*.ts', { eager: true }) */
86
+ toolModules: GlobModules;
87
+ /** Glob result of resource .tsx files from src/resources/ */
88
+ resourceModules: GlobModules;
88
89
  }
89
90
  /**
90
91
  * Build simulations for the dev server from glob results.
91
- * This is the main entry point for dev.tsx bootstrap.
92
- *
93
- * @example
94
- * const simulations = buildDevSimulations({
95
- * simulationModules: import.meta.glob('../src/resources/**\/*-simulation.json', { eager: true }),
96
- * resourceModules: import.meta.glob('../src/resources/**\/*-resource.tsx', { eager: true }),
97
- * resourceComponents: resourceComponents,
98
- * });
92
+ * Simulation JSON has `"tool": "tool-name"` string referencing a tool file.
93
+ * Tool files have `resource: 'name'` linking to a resource discovered from resourceModules.
99
94
  */
100
95
  export declare function buildDevSimulations(options: BuildDevSimulationsOptions): Record<string, Simulation>;
101
96
  /**
@@ -131,7 +126,7 @@ export interface FsOps {
131
126
  *
132
127
  * @example
133
128
  * // Find source resources (tsx files)
134
- * const resources = findResourceDirs('src/resources', key => `${key}-resource.tsx`);
129
+ * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);
135
130
  *
136
131
  * @example
137
132
  * // Find built resources (js files)
@@ -139,35 +134,39 @@ export interface FsOps {
139
134
  */
140
135
  export declare function findResourceDirs(baseDir: string, filePattern: (key: string) => string, fs: FsOps): ResourceDirInfo[];
141
136
  /**
142
- * Check if a filename is a simulation file for a given resource.
143
- * Matches pattern: {resourceKey}-*-simulation.json
144
- *
145
- * @example
146
- * isSimulationFile('albums-show-simulation.json', 'albums') // true
147
- * isSimulationFile('albums-show-simulation.json', 'carousel') // false
148
- * isSimulationFile('albums-resource.tsx', 'albums') // false
137
+ * Information about a discovered tool file
149
138
  */
150
- export declare function isSimulationFile(filename: string, resourceKey: string): boolean;
139
+ export interface ToolFileInfo {
140
+ /** Tool name derived from filename (e.g., 'show-albums') */
141
+ name: string;
142
+ /** Full path to the tool file */
143
+ path: string;
144
+ }
151
145
  /**
152
- * Extract the simulation name from a simulation filename.
153
- * Given "{resourceKey}-{name}-simulation.json", returns "{name}".
146
+ * Find all tool files in a tools directory.
147
+ * Matches *.ts files directly in the directory (not recursive).
154
148
  *
155
149
  * @example
156
- * extractSimulationName('albums-show-simulation.json', 'albums') // 'show'
157
- * extractSimulationName('carousel-hero-simulation.json', 'carousel') // 'hero'
150
+ * findToolFiles('src/tools', fs)
151
+ * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]
158
152
  */
159
- export declare function extractSimulationName(filename: string, resourceKey: string): string;
153
+ export declare function findToolFiles(toolsDir: string, fs: Pick<FsOps, 'readdirSync' | 'existsSync'>): ToolFileInfo[];
160
154
  /**
161
- * Find all simulation files in a resource directory.
162
- *
163
- * @param resourceDir - Path to the resource directory
164
- * @param resourceKey - Resource key (e.g., 'albums')
165
- * @param fs - File system operations (for testing)
166
- * @returns Array of { filename, name } objects
155
+ * Information about a discovered simulation file (flat convention)
167
156
  */
168
- export declare function findSimulationFiles(resourceDir: string, resourceKey: string, fs: Pick<FsOps, 'readdirSync' | 'existsSync'>): Array<{
169
- filename: string;
157
+ export interface SimulationFileInfo {
158
+ /** Filename without extension (e.g., 'show-albums') */
170
159
  name: string;
160
+ /** Full path to the simulation file */
171
161
  path: string;
172
- }>;
162
+ }
163
+ /**
164
+ * Find all simulation JSON files in a flat simulations directory.
165
+ * Matches any *.json file directly in the directory.
166
+ *
167
+ * @example
168
+ * findSimulationFilesFlat('tests/simulations', fs)
169
+ * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]
170
+ */
171
+ export declare function findSimulationFilesFlat(simulationsDir: string, fs: Pick<FsOps, 'readdirSync' | 'existsSync'>): SimulationFileInfo[];
173
172
  export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Extract the `tool` named export from a tool .ts file.
3
+ *
4
+ * Uses esbuild in ESM mode to compile TypeScript and tree-shake to just the
5
+ * `tool` export. ESM tree-shaking drops unused exports (schema, handler) so
6
+ * their dependencies (zod, etc.) are never evaluated.
7
+ *
8
+ * `schema` and `default` handler are loaded at runtime via Vite SSR.
9
+ */
10
+ export declare function extractToolExport(tsPath: string): Promise<{
11
+ tool: Record<string, unknown>;
12
+ }>;
@@ -1,2 +1,2 @@
1
- export declare const FAVICON_BASE64 = "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";
1
+ export declare const FAVICON_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAOdEVYdFNvZnR3YXJlAEZpZ21hnrGWYwAAB3dJREFUeAHtnU9MFFccx78L/gPULkkTrESzok1ND7BGSWkvLgeSNj0IRxNT8aTpocDFHpqIeOm/g3BqPBUSTr0s3pr0wHJRG0xcOdVUcDEpkVbjVhG0SOnvN7AR11mYmS0zb+b3+ySTnZmdjZH3+f3eezPvzYvBFak48Keddpr4gDY6RgJKgCzngViWdmiLjQHVGSCTd/rrmLPLWlLA0gn6xzrpJ3EopjMIVPYBN3IbXbiBAFbE99JON5QwMriRCOsIYEX9j9AUH3ZytHUA41m7Lyvsf9PcRYU/Ci38KJCg7RbwgW0Wt8kAXPjoRwSJ1ywhvvMlbUvWfjG52W3Iz21B/lkloklFD/Dra2VbJEBzEpYt4SVR9wLJhgUk9rxAU8M87c9bBc7n3ZCb3b4iBMlwe7IG2clqZKeqrXPhprKV2gSZwtEaAVoSYUz7XLDtH+VxvPEJUo1zFNkvsZmwGNmpKoxN7EZmYpclRsjIAYtHqNdodRXXCNDMDb5OhIBU41Oc+PCxVfBuI/v/hoVgEYZ+edv6DAlUDYz38M6qAFb034PBJA/OW4Xe3fHnpke5Vwoy9A3Xh6CqWKkKVgUwN/o52ntP/WF9holCVhikzVAGKAt0kwBJutmz9TEMo7PtIRX8TOApvlw4K/QN7zVRBGoDLB4gAY7Svf2KNAwhKgVfjKEinCEBmrlf2IWACWuqdwuL0HHpkCm9hwG641H/JQLs+vENmR++mEb/ufsU9f8g6vA9iXOf/mX9X29P1QR902kHC8AZYAcCoJ1a9aPf30HL4TlIg3s13I39mwTgG0wB8ZyrgGX4DEf9ZYp4ru8VWO2CoLqOvguQPLiA9IXfI9fIKxduG7SeP+y7BL4KsNLQm4FSmp4r+3xtIAZSBSjmUAFFNCqAcFQA4agAwlEBhKMCCEcFEI4KIBwVQDgqgHBUAOFsQRnwkz2eeKEEB48lyE5WwSueBeARLfpYN3h4RNGBz5o8jyzyXAXw+D0t/OApDK7xiicBeDiTjuYxBy4Lr4NpPQmQvnAXillwRvaCawHYNk395sEZwEtWdi2ADukyFy9l40oAjX6z4bJxmwVcCaDRbz5uy8ixABr94YDLyE2PwLEAp9seQQkHbnoEjgTgu36pxidQwgFnAL5X4wRHAnjtYyrBwfMuneDoWcD07HaT33Sh2PDWziVH1+nMIOHoeADhqADCUQGEowIIRwUQjgogHBVAOCqAcFQA4agAwlEBhFPyYVB3x6ztujpK+OBJI/3pOtvvSj4MWv55HEp0iH3cbHvetgqI79TIjxqlsrm9AIYuyaJ4h5fLs8NWAAmvbZdGqayuvQAhuKICFDmoAMJRAYRjK0B+LqqLJyvF2AsQ2dWz5VJqJZISApT17ijFQPJz9mWqVYAQSmX1kqHec2W/PgyKCOtV6TozSDjaDRSOCiAcFUA4KoBwVADhqADCUQGEowIIRwUQjgogHBVAOI6e+/afve/4tWOKGfDz/77h+g2vcyQAjw/o6piFEh6cvtfRURUwci0OJVz0De91dJ0jAXhpsszEbijhIDOxi6qA7Y6uddwIdGqUEjxDLl7r61gAN1YpwZFz+V5nV91AzQLm47aMXAnAZmkWMJech7e6u74RpFnAXLyUjWsB2DDtEZhHzuOaDp5uBWsWMI+OS4fgBU8CcI9gSFcQMQaO/OxkNbzg+WFQ95X9OofQADj1l5ORy5oYwitTJRsWoARHdqrKc/QzOjNIODoeQDgqgHBUAOGoAMJRAYSjAghHBRCOCiAcFUA4KoBwVADh+PpGyOTBBVw+Ow2lNDybhx+3+4XvD4MSdS8w+t0d61N5BT/W5UEd5TzZ84LvVQD/R1vPv6cDStbAf4sjn7/ve+EzlUB9J336OveL5xqOXK/FNMnA1YLURar473Dy6wZ889M7eL4YSHMsXxBgDwKAp5xdvR5HLQnAg0skwcO4PvnqXfob1CBAbnAboJ92uhAwyYZ5pHvvRr5twCOqeQiXnw29dRjgDMDR346AefB4KwZG6iJbLXDbh1/AzZs5k2ti31IGSFL9v/UefG4HbERn20P0npoJfUYoDNocNLLRu1gbW9kxoxqwg0U43fYIqcYnCBOGpXo7BoHxM6sCHEtROhiFwXAmuEgZ4XjjU2OzArfqB9J11MOJB9Klc0flAWoD5mKvThylLFBhZBYoJkUScGYwQQZO8Vevxa1urcHRXswgRz/vrBGA2wLbbgHLCYQI7j6yECwD9yQ2WwiOci7oMdr41Tnhmy0dy1Ggt3L0W0evf2l+VbARLAD3IliGJpKDl73hc27F4ILlhZZ44sVtul/Bx9nJqihMjz9C0Z8tHMTe/P5YN52+jAjCq6Jz99JucWye5saLZVlbZFdNW+4BbvavPROzvzC6EsjlzcJnYqV/0Jykr9NhaxMoxXCd/y81+G5m7L5dZ3rvzANg31USoJYOklDCyABQcxK49lupC2JwREsCWLpIO6ehmE6etiEK3JFSUb8WhwIUSFFX8WmKuhG0oQkrmUFfIxosOawU+hht1LqvoYLP5J3++D/8aGq5otzXywAAAABJRU5ErkJggg==";
2
2
  export declare const FAVICON_BUFFER: Buffer<ArrayBuffer>;
@@ -7,7 +7,7 @@ const path = require("node:path");
7
7
  const protocol = require("../protocol-CL4_Npj5.cjs");
8
8
  const zod = require("zod");
9
9
  const sse_js = require("@modelcontextprotocol/sdk/server/sse.js");
10
- const FAVICON_BASE64 = "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";
10
+ const FAVICON_BASE64 = "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";
11
11
  const FAVICON_BUFFER = Buffer.from(FAVICON_BASE64, "base64");
12
12
  function getDefaultExportFromCjs(x2) {
13
13
  return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
@@ -14225,7 +14225,8 @@ function readResourceHtmlProd(distPath) {
14225
14225
  return fs.readFileSync(htmlPath, "utf8");
14226
14226
  }
14227
14227
  function getViteResourceHtml(srcPath) {
14228
- const fileName = srcPath.split("/").pop()?.replace(/-resource\.tsx$/, "") ?? "";
14228
+ const rawFileName = srcPath.split("/").pop() ?? "";
14229
+ const fileName = rawFileName.replace(/\.tsx$/, "");
14229
14230
  const componentName = fileName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("") + "Resource";
14230
14231
  const devServerUrl = LOCAL_DEV_SERVER_URL;
14231
14232
  const entryParams = new URLSearchParams({ src: srcPath, component: componentName });