sunpeak 0.16.29 → 0.17.2

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 (140) hide show
  1. package/bin/commands/dev.mjs +169 -342
  2. package/bin/commands/inspect.mjs +763 -0
  3. package/bin/commands/new.mjs +2 -2
  4. package/bin/lib/inspect/inspect-config.d.mts +20 -0
  5. package/bin/lib/inspect/inspect-config.mjs +76 -0
  6. package/bin/lib/live/global-setup.mjs +6 -1
  7. package/bin/sunpeak.js +11 -1
  8. package/dist/chatgpt/globals.css +8 -0
  9. package/dist/chatgpt/index.cjs +3 -11
  10. package/dist/chatgpt/index.cjs.map +1 -1
  11. package/dist/chatgpt/index.d.ts +2 -2
  12. package/dist/chatgpt/index.js +4 -8
  13. package/dist/chatgpt/index.js.map +1 -1
  14. package/dist/claude/index.cjs +1 -1
  15. package/dist/claude/index.js +1 -1
  16. package/dist/discovery-Cgoegt62.js +114 -0
  17. package/dist/discovery-Cgoegt62.js.map +1 -0
  18. package/dist/discovery-Clu4uHp1.cjs +161 -0
  19. package/dist/discovery-Clu4uHp1.cjs.map +1 -0
  20. package/dist/index.cjs +1 -4
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.js +2 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/lib/discovery-cli.cjs +1 -1
  25. package/dist/lib/discovery-cli.js +1 -1
  26. package/dist/lib/discovery.d.ts +7 -67
  27. package/dist/lib/index.d.ts +0 -1
  28. package/dist/mcp/index.cjs +34 -23
  29. package/dist/mcp/index.cjs.map +1 -1
  30. package/dist/mcp/index.js +34 -23
  31. package/dist/mcp/index.js.map +1 -1
  32. package/dist/mcp/types.d.ts +5 -0
  33. package/dist/simulator/index.cjs +5 -11
  34. package/dist/simulator/index.cjs.map +1 -1
  35. package/dist/simulator/index.d.ts +4 -2
  36. package/dist/simulator/index.js +5 -8
  37. package/dist/simulator/index.js.map +1 -1
  38. package/dist/simulator/simple-sidebar.d.ts +7 -4
  39. package/dist/simulator/simulator-url.d.ts +8 -0
  40. package/dist/simulator/simulator.d.ts +15 -2
  41. package/dist/simulator/use-mcp-connection.d.ts +19 -0
  42. package/dist/{simulator-DIVvI69i.cjs → simulator-CH9hs0N6.cjs} +129 -21
  43. package/dist/simulator-CH9hs0N6.cjs.map +1 -0
  44. package/dist/{simulator-C7mkK7Sz.js → simulator-Dl8B-Ljb.js} +124 -22
  45. package/dist/simulator-Dl8B-Ljb.js.map +1 -0
  46. package/dist/{simulator-url-BDGD4vZD.cjs → simulator-url-CozKF1jf.cjs} +3 -1
  47. package/dist/simulator-url-CozKF1jf.cjs.map +1 -0
  48. package/dist/{simulator-url-Bkxj43yT.js → simulator-url-KoS_ToP6.js} +3 -1
  49. package/dist/simulator-url-KoS_ToP6.js.map +1 -0
  50. package/dist/style.css +8 -0
  51. package/package.json +11 -3
  52. package/template/dist/albums/albums.html +105 -0
  53. package/template/dist/albums/albums.json +16 -0
  54. package/template/dist/carousel/carousel.html +105 -0
  55. package/template/dist/carousel/carousel.json +16 -0
  56. package/template/dist/map/map.html +3060 -0
  57. package/template/dist/map/map.json +22 -0
  58. package/template/dist/review/review.html +105 -0
  59. package/template/dist/review/review.json +16 -0
  60. package/template/dist/server.js +15 -0
  61. package/template/dist/tools/review-diff.js +50 -0
  62. package/template/dist/tools/review-post.js +50 -0
  63. package/template/dist/tools/review-purchase.js +61 -0
  64. package/template/dist/tools/review.js +31 -0
  65. package/template/dist/tools/show-albums.js +56 -0
  66. package/template/dist/tools/show-carousel.js +41 -0
  67. package/template/dist/tools/show-map.js +47 -0
  68. package/template/node_modules/.vite/deps/_metadata.json +8 -0
  69. package/template/node_modules/.vite/deps/package.json +3 -0
  70. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +500 -0
  71. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -0
  72. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +563 -0
  73. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -0
  74. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +575 -0
  75. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -0
  76. package/template/node_modules/.vite-mcp/deps/@testing-library_react.js +11363 -0
  77. package/template/node_modules/.vite-mcp/deps/@testing-library_react.js.map +1 -0
  78. package/template/node_modules/.vite-mcp/deps/_metadata.json +130 -0
  79. package/template/node_modules/.vite-mcp/deps/chunk-BoAXSpZd.js +33 -0
  80. package/template/node_modules/.vite-mcp/deps/client-CU1wWud4.js +14385 -0
  81. package/template/node_modules/.vite-mcp/deps/client-CU1wWud4.js.map +1 -0
  82. package/template/node_modules/.vite-mcp/deps/clsx.js +18 -0
  83. package/template/node_modules/.vite-mcp/deps/clsx.js.map +1 -0
  84. package/template/node_modules/.vite-mcp/deps/dist-uWX8WbjY.js +505 -0
  85. package/template/node_modules/.vite-mcp/deps/dist-uWX8WbjY.js.map +1 -0
  86. package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js +1461 -0
  87. package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js.map +1 -0
  88. package/template/node_modules/.vite-mcp/deps/embla-carousel-wheel-gestures.js +536 -0
  89. package/template/node_modules/.vite-mcp/deps/embla-carousel-wheel-gestures.js.map +1 -0
  90. package/template/node_modules/.vite-mcp/deps/magic-string.es-Cklsmr-5.js +1013 -0
  91. package/template/node_modules/.vite-mcp/deps/magic-string.es-Cklsmr-5.js.map +1 -0
  92. package/template/node_modules/.vite-mcp/deps/mapbox-gl.js +46311 -0
  93. package/template/node_modules/.vite-mcp/deps/mapbox-gl.js.map +1 -0
  94. package/template/node_modules/.vite-mcp/deps/package.json +3 -0
  95. package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js +2090 -0
  96. package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js.map +1 -0
  97. package/template/node_modules/.vite-mcp/deps/react-dom.js +186 -0
  98. package/template/node_modules/.vite-mcp/deps/react-dom.js.map +1 -0
  99. package/template/node_modules/.vite-mcp/deps/react-dom_client.js +2 -0
  100. package/template/node_modules/.vite-mcp/deps/react.js +769 -0
  101. package/template/node_modules/.vite-mcp/deps/react.js.map +1 -0
  102. package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js +205 -0
  103. package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js.map +1 -0
  104. package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js +209 -0
  105. package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js.map +1 -0
  106. package/template/node_modules/.vite-mcp/deps/schemas-NsgmY9QV.js +12157 -0
  107. package/template/node_modules/.vite-mcp/deps/schemas-NsgmY9QV.js.map +1 -0
  108. package/template/node_modules/.vite-mcp/deps/tailwind-merge.js +2025 -0
  109. package/template/node_modules/.vite-mcp/deps/tailwind-merge.js.map +1 -0
  110. package/template/node_modules/.vite-mcp/deps/vitest.js +14021 -0
  111. package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -0
  112. package/template/node_modules/.vite-mcp/deps/zod.js +624 -0
  113. package/template/node_modules/.vite-mcp/deps/zod.js.map +1 -0
  114. package/template/src/tools/review-diff.test.ts +5 -1
  115. package/template/src/tools/review-diff.ts +1 -1
  116. package/template/src/tools/review-post.test.ts +5 -1
  117. package/template/src/tools/review-post.ts +1 -1
  118. package/template/src/tools/review-purchase.test.ts +5 -1
  119. package/template/src/tools/review-purchase.ts +1 -1
  120. package/template/src/tools/review.test.ts +5 -1
  121. package/template/src/tools/review.ts +1 -1
  122. package/template/src/tools/show-albums.test.ts +5 -1
  123. package/template/src/tools/show-albums.ts +1 -1
  124. package/template/src/tools/show-carousel.test.ts +5 -1
  125. package/template/src/tools/show-carousel.ts +1 -1
  126. package/template/src/tools/show-map.test.ts +5 -1
  127. package/template/src/tools/show-map.ts +1 -1
  128. package/dist/discovery-BxKCIgG5.cjs +0 -332
  129. package/dist/discovery-BxKCIgG5.cjs.map +0 -1
  130. package/dist/discovery-Du4LHrih.js +0 -261
  131. package/dist/discovery-Du4LHrih.js.map +0 -1
  132. package/dist/simulator-C7mkK7Sz.js.map +0 -1
  133. package/dist/simulator-DIVvI69i.cjs.map +0 -1
  134. package/dist/simulator-url-BDGD4vZD.cjs.map +0 -1
  135. package/dist/simulator-url-Bkxj43yT.js.map +0 -1
  136. package/template/.sunpeak/dev.tsx +0 -79
  137. package/template/.sunpeak/resource-loader.html +0 -20
  138. package/template/.sunpeak/resource-loader.tsx +0 -57
  139. package/template/index.html +0 -14
  140. package/template/src/resources/index.ts +0 -17
@@ -170,8 +170,8 @@ export async function init(projectName, resourcesArg, deps = defaultDeps) {
170
170
  filter: (src) => {
171
171
  const name = basename(src);
172
172
 
173
- // Skip node_modules and lock file
174
- if (name === 'node_modules' || name === 'pnpm-lock.yaml') {
173
+ // Skip node_modules, lock file, and legacy dev bootstrap files
174
+ if (name === 'node_modules' || name === 'pnpm-lock.yaml' || name === '.sunpeak' || name === 'index.html') {
175
175
  return false;
176
176
  }
177
177
 
@@ -0,0 +1,20 @@
1
+ export interface InspectConfigOptions {
2
+ /** MCP server URL or stdio command string (required) */
3
+ server: string;
4
+ /** Test directory (default: 'tests/e2e') */
5
+ testDir?: string;
6
+ /** Simulation JSON directory (opt-in, fixtures loaded only when specified) */
7
+ simulationsDir?: string;
8
+ /** Host shells to test (default: ['chatgpt', 'claude']) */
9
+ hosts?: ('chatgpt' | 'claude')[];
10
+ /** App name in simulator chrome */
11
+ name?: string;
12
+ /** Additional Playwright `use` options */
13
+ use?: Record<string, unknown>;
14
+ }
15
+
16
+ /**
17
+ * Create a complete Playwright config for testing an external MCP server
18
+ * using the sunpeak simulator.
19
+ */
20
+ export function defineInspectConfig(options: InspectConfigOptions): Record<string, unknown>;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Playwright config factory for inspect mode (BYOS — Bring Your Own Server).
3
+ *
4
+ * Generates a complete Playwright config that starts `sunpeak inspect` as the
5
+ * webServer and runs e2e tests against the simulator. Follows the same pattern
6
+ * as `defineLiveConfig` for live tests.
7
+ *
8
+ * Usage in playwright.config.ts:
9
+ * import { defineInspectConfig } from 'sunpeak/test/inspect/config';
10
+ * export default defineInspectConfig({
11
+ * server: 'http://localhost:8000/mcp',
12
+ * });
13
+ */
14
+ import { getPortSync } from '../get-port.mjs';
15
+
16
+ /**
17
+ * Create a complete Playwright config for testing an external MCP server.
18
+ *
19
+ * @param {Object} options
20
+ * @param {string} options.server - MCP server URL or stdio command (required)
21
+ * @param {string} [options.testDir='tests/e2e'] - Test directory
22
+ * @param {string} [options.simulationsDir='tests/simulations'] - Simulation JSON directory
23
+ * @param {string[]} [options.hosts=['chatgpt', 'claude']] - Host shells to test
24
+ * @param {string} [options.name] - App name in simulator chrome
25
+ * @param {Object} [options.use] - Additional Playwright `use` options
26
+ * @returns {import('@playwright/test').PlaywrightTestConfig}
27
+ */
28
+ export function defineInspectConfig(options) {
29
+ const {
30
+ server,
31
+ testDir = 'tests/e2e',
32
+ simulationsDir,
33
+ hosts = ['chatgpt', 'claude'],
34
+ name,
35
+ use: userUse,
36
+ } = options;
37
+
38
+ if (!server) {
39
+ throw new Error('defineInspectConfig: `server` option is required');
40
+ }
41
+
42
+ const port = Number(process.env.SUNPEAK_TEST_PORT) || getPortSync(6776);
43
+ const sandboxPort = Number(process.env.SUNPEAK_SANDBOX_PORT) || getPortSync(24680);
44
+
45
+ // Build the sunpeak inspect command
46
+ const serverArg = server.includes(' ') ? `"${server}"` : server;
47
+ const command = [
48
+ 'npx sunpeak inspect',
49
+ `--server ${serverArg}`,
50
+ ...(simulationsDir ? [`--simulations ${simulationsDir}`] : []),
51
+ `--port ${port}`,
52
+ ...(name ? [`--name "${name}"`] : []),
53
+ ].join(' ');
54
+
55
+ return {
56
+ testDir,
57
+ fullyParallel: true,
58
+ forbidOnly: !!process.env.CI,
59
+ retries: process.env.CI ? 2 : 1,
60
+ // Limit workers to avoid overwhelming the double-iframe sandbox proxy.
61
+ workers: process.env.CI ? 1 : 2,
62
+ reporter: 'list',
63
+ use: {
64
+ baseURL: `http://localhost:${port}`,
65
+ trace: 'on-first-retry',
66
+ ...userUse,
67
+ },
68
+ projects: hosts.map((host) => ({ name: host })),
69
+ webServer: {
70
+ command: `SUNPEAK_SANDBOX_PORT=${sandboxPort} ${command}`,
71
+ url: `http://localhost:${port}/health`,
72
+ reuseExistingServer: !process.env.CI,
73
+ timeout: 60_000,
74
+ },
75
+ };
76
+ }
@@ -23,7 +23,12 @@ import { dirname } from 'path';
23
23
  import { ANTI_BOT_ARGS, CHROME_USER_AGENT, resolvePlaywright, getAppName } from './utils.mjs';
24
24
  import { ChatGPTPage, CHATGPT_SELECTORS, CHATGPT_URLS } from './chatgpt-page.mjs';
25
25
 
26
- /** Auth state expires after 24 hours — ChatGPT session cookies are short-lived. */
26
+ /**
27
+ * Auth file retention window. The file is kept for 24 hours, but sessions
28
+ * typically expire much sooner (a few hours) because Cloudflare's HttpOnly
29
+ * `cf_clearance` cookie cannot be captured by storageState(). When it
30
+ * expires, the next run will need re-authentication even if the file is fresh.
31
+ */
27
32
  const MAX_AGE_MS = 24 * 60 * 60 * 1000;
28
33
 
29
34
  const CHATGPT_URL = CHATGPT_URLS.base;
package/bin/sunpeak.js CHANGED
@@ -37,7 +37,7 @@ function getVersion() {
37
37
  }
38
38
 
39
39
  // Commands that don't require a package.json
40
- const standaloneCommands = ['new', 'upgrade', 'help', undefined];
40
+ const standaloneCommands = ['new', 'upgrade', 'inspect', 'help', undefined];
41
41
 
42
42
  if (command && !standaloneCommands.includes(command)) {
43
43
  checkPackageJson();
@@ -72,6 +72,13 @@ function getVersion() {
72
72
  }
73
73
  break;
74
74
 
75
+ case 'inspect':
76
+ {
77
+ const { inspect } = await import(join(COMMANDS_DIR, 'inspect.mjs'));
78
+ await inspect(args);
79
+ }
80
+ break;
81
+
75
82
  case 'upgrade':
76
83
  {
77
84
  const { upgrade } = await import(join(COMMANDS_DIR, 'upgrade.mjs'));
@@ -100,6 +107,9 @@ Usage:
100
107
  sunpeak build Build resources + tools for production
101
108
  sunpeak start Start production MCP server
102
109
  --port, -p Server port (default: 8000, or PORT env)
110
+ sunpeak inspect Test an external MCP server in the simulator
111
+ --server, -s <url|cmd> MCP server URL or stdio command (required)
112
+ --simulations <dir> Simulation JSON directory
103
113
  sunpeak upgrade Upgrade sunpeak to latest version
104
114
  sunpeak --version Show version number
105
115
 
@@ -806,6 +806,10 @@
806
806
  height: calc(var(--spacing) * 6);
807
807
  }
808
808
 
809
+ .h-2 {
810
+ height: calc(var(--spacing) * 2);
811
+ }
812
+
809
813
  .h-3 {
810
814
  height: calc(var(--spacing) * 3);
811
815
  }
@@ -898,6 +902,10 @@
898
902
  width: calc(var(--spacing) * 1);
899
903
  }
900
904
 
905
+ .w-2 {
906
+ width: calc(var(--spacing) * 2);
907
+ }
908
+
901
909
  .w-3 {
902
910
  width: calc(var(--spacing) * 3);
903
911
  }
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../chunk-9hOWP6kD.cjs");
3
3
  require("../protocol-jbxhzcnS.cjs");
4
- const require_simulator = require("../simulator-DIVvI69i.cjs");
5
- const require_discovery = require("../discovery-BxKCIgG5.cjs");
6
- const require_simulator_url = require("../simulator-url-BDGD4vZD.cjs");
4
+ const require_simulator = require("../simulator-CH9hs0N6.cjs");
5
+ const require_simulator_url = require("../simulator-url-CozKF1jf.cjs");
6
+ const require_discovery = require("../discovery-Clu4uHp1.cjs");
7
7
  //#region src/chatgpt/index.ts
8
8
  var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
9
9
  IframeResource: () => require_simulator.IframeResource,
@@ -11,10 +11,6 @@ var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
11
11
  SCREEN_WIDTHS: () => require_simulator.SCREEN_WIDTHS,
12
12
  Simulator: () => require_simulator.Simulator,
13
13
  ThemeProvider: () => require_simulator.ThemeProvider,
14
- buildDevSimulations: () => require_discovery.buildDevSimulations,
15
- buildResourceMap: () => require_discovery.buildResourceMap,
16
- buildSimulations: () => require_discovery.buildSimulations,
17
- createResourceExports: () => require_discovery.createResourceExports,
18
14
  createSimulatorUrl: () => require_simulator_url.createSimulatorUrl,
19
15
  extractResourceCSP: () => require_simulator.extractResourceCSP,
20
16
  extractResourceKey: () => require_discovery.extractResourceKey,
@@ -32,16 +28,12 @@ exports.McpAppHost = require_simulator.McpAppHost;
32
28
  exports.SCREEN_WIDTHS = require_simulator.SCREEN_WIDTHS;
33
29
  exports.Simulator = require_simulator.Simulator;
34
30
  exports.ThemeProvider = require_simulator.ThemeProvider;
35
- exports.buildDevSimulations = require_discovery.buildDevSimulations;
36
- exports.buildResourceMap = require_discovery.buildResourceMap;
37
- exports.buildSimulations = require_discovery.buildSimulations;
38
31
  Object.defineProperty(exports, "chatgpt_exports", {
39
32
  enumerable: true,
40
33
  get: function() {
41
34
  return chatgpt_exports;
42
35
  }
43
36
  });
44
- exports.createResourceExports = require_discovery.createResourceExports;
45
37
  exports.createSimulatorUrl = require_simulator_url.createSimulatorUrl;
46
38
  exports.extractResourceCSP = require_simulator.extractResourceCSP;
47
39
  exports.extractResourceKey = require_discovery.extractResourceKey;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities for building simulations\nexport {\n buildDevSimulations,\n buildSimulations,\n buildResourceMap,\n createResourceExports,\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type {\n BuildSimulationsOptions,\n BuildDevSimulationsOptions,\n ResourceMetadata,\n ResourceDirInfo,\n FsOps,\n} from '../lib/discovery';\n"],"mappings":""}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities\nexport {\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type { ResourceDirInfo, FsOps } from '../lib/discovery';\n"],"mappings":""}
@@ -10,5 +10,5 @@ export type { ResourceCSP } from '../simulator/iframe-resource';
10
10
  export * from '../simulator/theme-provider';
11
11
  export { createSimulatorUrl } from '../simulator/simulator-url';
12
12
  export type { SimulatorUrlParams } from '../simulator/simulator-url';
13
- export { buildDevSimulations, buildSimulations, buildResourceMap, createResourceExports, toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs, } from '../lib/discovery';
14
- export type { BuildSimulationsOptions, BuildDevSimulationsOptions, ResourceMetadata, ResourceDirInfo, FsOps, } from '../lib/discovery';
13
+ export { toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs, } from '../lib/discovery';
14
+ export type { ResourceDirInfo, FsOps } from '../lib/discovery';
@@ -1,8 +1,8 @@
1
1
  import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
2
2
  import "../protocol-DJmRaBzO.js";
3
- import { _ as SCREEN_WIDTHS, d as ThemeProvider, f as useThemeContext, g as McpAppHost, h as extractResourceCSP, m as IframeResource, n as resolveServerToolResult, t as Simulator } from "../simulator-C7mkK7Sz.js";
4
- import { a as extractResourceKey, c as findResourceKey, d as getComponentName, f as toPascalCase, i as createResourceExports, n as buildResourceMap, o as extractSimulationKey, r as buildSimulations, s as findResourceDirs, t as buildDevSimulations } from "../discovery-Du4LHrih.js";
5
- import { t as createSimulatorUrl } from "../simulator-url-Bkxj43yT.js";
3
+ import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Simulator, v as SCREEN_WIDTHS } from "../simulator-Dl8B-Ljb.js";
4
+ import { t as createSimulatorUrl } from "../simulator-url-KoS_ToP6.js";
5
+ import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
6
6
  //#region src/chatgpt/index.ts
7
7
  var chatgpt_exports = /* @__PURE__ */ __exportAll({
8
8
  IframeResource: () => IframeResource,
@@ -10,10 +10,6 @@ var chatgpt_exports = /* @__PURE__ */ __exportAll({
10
10
  SCREEN_WIDTHS: () => SCREEN_WIDTHS,
11
11
  Simulator: () => Simulator,
12
12
  ThemeProvider: () => ThemeProvider,
13
- buildDevSimulations: () => buildDevSimulations,
14
- buildResourceMap: () => buildResourceMap,
15
- buildSimulations: () => buildSimulations,
16
- createResourceExports: () => createResourceExports,
17
13
  createSimulatorUrl: () => createSimulatorUrl,
18
14
  extractResourceCSP: () => extractResourceCSP,
19
15
  extractResourceKey: () => extractResourceKey,
@@ -26,6 +22,6 @@ var chatgpt_exports = /* @__PURE__ */ __exportAll({
26
22
  useThemeContext: () => useThemeContext
27
23
  });
28
24
  //#endregion
29
- export { IframeResource, McpAppHost, SCREEN_WIDTHS, Simulator, ThemeProvider, buildDevSimulations, buildResourceMap, buildSimulations, createResourceExports, createSimulatorUrl, extractResourceCSP, extractResourceKey, extractSimulationKey, findResourceDirs, findResourceKey, getComponentName, resolveServerToolResult, chatgpt_exports as t, toPascalCase, useThemeContext };
25
+ export { IframeResource, McpAppHost, SCREEN_WIDTHS, Simulator, ThemeProvider, createSimulatorUrl, extractResourceCSP, extractResourceKey, extractSimulationKey, findResourceDirs, findResourceKey, getComponentName, resolveServerToolResult, chatgpt_exports as t, toPascalCase, useThemeContext };
30
26
 
31
27
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities for building simulations\nexport {\n buildDevSimulations,\n buildSimulations,\n buildResourceMap,\n createResourceExports,\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type {\n BuildSimulationsOptions,\n BuildDevSimulationsOptions,\n ResourceMetadata,\n ResourceDirInfo,\n FsOps,\n} from '../lib/discovery';\n"],"mappings":""}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities\nexport {\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type { ResourceDirInfo, FsOps } from '../lib/discovery';\n"],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../chunk-9hOWP6kD.cjs");
3
3
  require("../protocol-jbxhzcnS.cjs");
4
- const require_simulator = require("../simulator-DIVvI69i.cjs");
4
+ const require_simulator = require("../simulator-CH9hs0N6.cjs");
5
5
  exports.Simulator = require_simulator.Simulator;
@@ -1,3 +1,3 @@
1
1
  import "../protocol-DJmRaBzO.js";
2
- import { t as Simulator } from "../simulator-C7mkK7Sz.js";
2
+ import { t as Simulator } from "../simulator-Dl8B-Ljb.js";
3
3
  export { Simulator };
@@ -0,0 +1,114 @@
1
+ //#region src/lib/discovery.ts
2
+ /**
3
+ * Discovery utilities for resources, tools, and simulations.
4
+ *
5
+ * String helpers (toPascalCase, extractResourceKey, etc.) are used by both
6
+ * browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,
7
+ * etc.) are used by CLI commands for build-time and runtime discovery.
8
+ */
9
+ /**
10
+ * Convert a kebab-case string to PascalCase
11
+ * @example toPascalCase('review') // 'Review'
12
+ * @example toPascalCase('album-art') // 'AlbumArt'
13
+ */
14
+ function toPascalCase(str) {
15
+ return str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
16
+ }
17
+ /**
18
+ * Extract the resource key from a resource file path.
19
+ * Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')
20
+ */
21
+ function extractResourceKey(path) {
22
+ const match = path.match(/([^/]+)\.tsx$/);
23
+ return match ? match[1] : void 0;
24
+ }
25
+ /**
26
+ * Extract the simulation key from a simulation file path.
27
+ * Matches any *.json file (e.g., './show-albums.json' → 'show-albums')
28
+ */
29
+ function extractSimulationKey(path) {
30
+ const match = path.match(/([^/]+)\.json$/);
31
+ return match ? match[1] : void 0;
32
+ }
33
+ /**
34
+ * Find the best matching resource key for a simulation key.
35
+ * Matches the longest resource name that is a prefix of the simulation key.
36
+ * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'
37
+ * @example findResourceKey('albums', ['albums', 'review']) // 'albums'
38
+ */
39
+ function findResourceKey(simulationKey, resourceKeys) {
40
+ const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);
41
+ for (const resourceKey of sorted) if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + "-")) return resourceKey;
42
+ }
43
+ /**
44
+ * Get the expected component export name for a resource
45
+ * @example getComponentName('review') // 'ReviewResource'
46
+ * @example getComponentName('album-art') // 'AlbumArtResource'
47
+ */
48
+ function getComponentName(resourceKey) {
49
+ return `${toPascalCase(resourceKey)}Resource`;
50
+ }
51
+ /**
52
+ * Find all resource directories in a base directory.
53
+ * Each valid resource directory contains a file matching the expected pattern.
54
+ *
55
+ * @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')
56
+ * @param filePattern - Function to generate expected filename from resource key
57
+ * @param fs - File system operations (for testing)
58
+ *
59
+ * @example
60
+ * // Find source resources (tsx files)
61
+ * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);
62
+ *
63
+ * @example
64
+ * // Find built resources (js files)
65
+ * const resources = findResourceDirs('dist', key => `${key}.js`);
66
+ */
67
+ function findResourceDirs(baseDir, filePattern, fs) {
68
+ if (!fs.existsSync(baseDir)) return [];
69
+ return fs.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
70
+ const key = entry.name;
71
+ const dir = `${baseDir}/${key}`;
72
+ const resourcePath = `${dir}/${filePattern(key)}`;
73
+ if (!fs.existsSync(resourcePath)) return null;
74
+ return {
75
+ key,
76
+ dir,
77
+ resourcePath
78
+ };
79
+ }).filter((info) => info !== null);
80
+ }
81
+ /**
82
+ * Find all tool files in a tools directory.
83
+ * Matches *.ts files directly in the directory (not recursive).
84
+ *
85
+ * @example
86
+ * findToolFiles('src/tools', fs)
87
+ * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]
88
+ */
89
+ function findToolFiles(toolsDir, fs) {
90
+ if (!fs.existsSync(toolsDir)) return [];
91
+ return fs.readdirSync(toolsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts")).map((entry) => ({
92
+ name: entry.name.replace(/\.ts$/, ""),
93
+ path: `${toolsDir}/${entry.name}`
94
+ }));
95
+ }
96
+ /**
97
+ * Find all simulation JSON files in a flat simulations directory.
98
+ * Matches any *.json file directly in the directory.
99
+ *
100
+ * @example
101
+ * findSimulationFilesFlat('tests/simulations', fs)
102
+ * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]
103
+ */
104
+ function findSimulationFilesFlat(simulationsDir, fs) {
105
+ if (!fs.existsSync(simulationsDir)) return [];
106
+ return fs.readdirSync(simulationsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".json")).map((entry) => ({
107
+ name: entry.name.replace(/\.json$/, ""),
108
+ path: `${simulationsDir}/${entry.name}`
109
+ }));
110
+ }
111
+ //#endregion
112
+ export { findSimulationFilesFlat as a, toPascalCase as c, findResourceKey as i, extractSimulationKey as n, findToolFiles as o, findResourceDirs as r, getComponentName as s, extractResourceKey as t };
113
+
114
+ //# sourceMappingURL=discovery-Cgoegt62.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-Cgoegt62.js","names":[],"sources":["../src/lib/discovery.ts"],"sourcesContent":["/**\n * Discovery utilities for resources, tools, and simulations.\n *\n * String helpers (toPascalCase, extractResourceKey, etc.) are used by both\n * browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,\n * etc.) are used by CLI commands for build-time and runtime discovery.\n */\n\n/**\n * Convert a kebab-case string to PascalCase\n * @example toPascalCase('review') // 'Review'\n * @example toPascalCase('album-art') // 'AlbumArt'\n */\nexport function toPascalCase(str: string): string {\n return str\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\n/**\n * Extract the resource key from a resource file path.\n * Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')\n */\nexport function extractResourceKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.tsx$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Extract the simulation key from a simulation file path.\n * Matches any *.json file (e.g., './show-albums.json' → 'show-albums')\n */\nexport function extractSimulationKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.json$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'\n * @example findResourceKey('albums', ['albums', 'review']) // 'albums'\n */\nexport function findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\n/**\n * Get the expected component export name for a resource\n * @example getComponentName('review') // 'ReviewResource'\n * @example getComponentName('album-art') // 'AlbumArtResource'\n */\nexport function getComponentName(resourceKey: string): string {\n return `${toPascalCase(resourceKey)}Resource`;\n}\n\n// --- Node.js utilities for CLI commands ---\n// These utilities use standard Node.js APIs and can be imported by build/push/mcp commands.\n\n/**\n * Information about a discovered resource directory\n */\nexport interface ResourceDirInfo {\n /** Resource key (directory name), e.g., 'albums', 'carousel' */\n key: string;\n /** Full path to the resource directory */\n dir: string;\n /** Full path to the main resource file (tsx or json depending on context) */\n resourcePath: string;\n}\n\n/**\n * File system operations interface for dependency injection in tests\n */\nexport interface FsOps {\n readdirSync: (\n path: string,\n options: { withFileTypes: true }\n ) => Array<{ name: string; isDirectory: () => boolean }>;\n existsSync: (path: string) => boolean;\n}\n\n/**\n * Find all resource directories in a base directory.\n * Each valid resource directory contains a file matching the expected pattern.\n *\n * @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')\n * @param filePattern - Function to generate expected filename from resource key\n * @param fs - File system operations (for testing)\n *\n * @example\n * // Find source resources (tsx files)\n * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);\n *\n * @example\n * // Find built resources (js files)\n * const resources = findResourceDirs('dist', key => `${key}.js`);\n */\nexport function findResourceDirs(\n baseDir: string,\n filePattern: (key: string) => string,\n fs: FsOps\n): ResourceDirInfo[] {\n if (!fs.existsSync(baseDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(baseDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => {\n const key = entry.name;\n const dir = `${baseDir}/${key}`;\n const resourcePath = `${dir}/${filePattern(key)}`;\n\n if (!fs.existsSync(resourcePath)) {\n return null;\n }\n\n return { key, dir, resourcePath };\n })\n .filter((info): info is ResourceDirInfo => info !== null);\n}\n\n// --- Tool files + flat simulations discovery ---\n\n/**\n * Information about a discovered tool file\n */\nexport interface ToolFileInfo {\n /** Tool name derived from filename (e.g., 'show-albums') */\n name: string;\n /** Full path to the tool file */\n path: string;\n}\n\n/**\n * Find all tool files in a tools directory.\n * Matches *.ts files directly in the directory (not recursive).\n *\n * @example\n * findToolFiles('src/tools', fs)\n * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]\n */\nexport function findToolFiles(\n toolsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): ToolFileInfo[] {\n if (!fs.existsSync(toolsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(toolsDir, { withFileTypes: true });\n\n return entries\n .filter(\n (entry) =>\n !entry.isDirectory() && entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')\n )\n .map((entry) => ({\n name: entry.name.replace(/\\.ts$/, ''),\n path: `${toolsDir}/${entry.name}`,\n }));\n}\n\n/**\n * Information about a discovered simulation file (flat convention)\n */\nexport interface SimulationFileInfo {\n /** Filename without extension (e.g., 'show-albums') */\n name: string;\n /** Full path to the simulation file */\n path: string;\n}\n\n/**\n * Find all simulation JSON files in a flat simulations directory.\n * Matches any *.json file directly in the directory.\n *\n * @example\n * findSimulationFilesFlat('tests/simulations', fs)\n * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]\n */\nexport function findSimulationFilesFlat(\n simulationsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): SimulationFileInfo[] {\n if (!fs.existsSync(simulationsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(simulationsDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => !entry.isDirectory() && entry.name.endsWith('.json'))\n .map((entry) => ({\n name: entry.name.replace(/\\.json$/, ''),\n path: `${simulationsDir}/${entry.name}`,\n }));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,aAAa,KAAqB;AAChD,QAAO,IACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;;;;;AAOb,SAAgB,mBAAmB,MAAkC;CACnE,MAAM,QAAQ,KAAK,MAAM,gBAAgB;AACzC,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;AAO5B,SAAgB,qBAAqB,MAAkC;CACrE,MAAM,QAAQ,KAAK,MAAM,iBAAiB;AAC1C,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;;;AAS5B,SAAgB,gBAAgB,eAAuB,cAA4C;CAEjG,MAAM,SAAS,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;AACpE,MAAK,MAAM,eAAe,OACxB,KAAI,kBAAkB,eAAe,cAAc,WAAW,cAAc,IAAI,CAC9E,QAAO;;;;;;;AAWb,SAAgB,iBAAiB,aAA6B;AAC5D,QAAO,GAAG,aAAa,YAAY,CAAC;;;;;;;;;;;;;;;;;;AA6CtC,SAAgB,iBACd,SACA,aACA,IACmB;AACnB,KAAI,CAAC,GAAG,WAAW,QAAQ,CACzB,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,SAAS,EAAE,eAAe,MAAM,CAAC,CAG7D,QAAQ,UAAU,MAAM,aAAa,CAAC,CACtC,KAAK,UAAU;EACd,MAAM,MAAM,MAAM;EAClB,MAAM,MAAM,GAAG,QAAQ,GAAG;EAC1B,MAAM,eAAe,GAAG,IAAI,GAAG,YAAY,IAAI;AAE/C,MAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO;AAGT,SAAO;GAAE;GAAK;GAAK;GAAc;GACjC,CACD,QAAQ,SAAkC,SAAS,KAAK;;;;;;;;;;AAuB7D,SAAgB,cACd,UACA,IACgB;AAChB,KAAI,CAAC,GAAG,WAAW,SAAS,CAC1B,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,UAAU,EAAE,eAAe,MAAM,CAAC,CAG9D,QACE,UACC,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,MAAM,IAAI,CAAC,MAAM,KAAK,SAAS,WAAW,CACzF,CACA,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,SAAS,GAAG;EACrC,MAAM,GAAG,SAAS,GAAG,MAAM;EAC5B,EAAE;;;;;;;;;;AAqBP,SAAgB,wBACd,gBACA,IACsB;AACtB,KAAI,CAAC,GAAG,WAAW,eAAe,CAChC,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,gBAAgB,EAAE,eAAe,MAAM,CAAC,CAGpE,QAAQ,UAAU,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,QAAQ,CAAC,CACvE,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG;EACvC,MAAM,GAAG,eAAe,GAAG,MAAM;EAClC,EAAE"}
@@ -0,0 +1,161 @@
1
+ //#region src/lib/discovery.ts
2
+ /**
3
+ * Discovery utilities for resources, tools, and simulations.
4
+ *
5
+ * String helpers (toPascalCase, extractResourceKey, etc.) are used by both
6
+ * browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,
7
+ * etc.) are used by CLI commands for build-time and runtime discovery.
8
+ */
9
+ /**
10
+ * Convert a kebab-case string to PascalCase
11
+ * @example toPascalCase('review') // 'Review'
12
+ * @example toPascalCase('album-art') // 'AlbumArt'
13
+ */
14
+ function toPascalCase(str) {
15
+ return str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
16
+ }
17
+ /**
18
+ * Extract the resource key from a resource file path.
19
+ * Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')
20
+ */
21
+ function extractResourceKey(path) {
22
+ const match = path.match(/([^/]+)\.tsx$/);
23
+ return match ? match[1] : void 0;
24
+ }
25
+ /**
26
+ * Extract the simulation key from a simulation file path.
27
+ * Matches any *.json file (e.g., './show-albums.json' → 'show-albums')
28
+ */
29
+ function extractSimulationKey(path) {
30
+ const match = path.match(/([^/]+)\.json$/);
31
+ return match ? match[1] : void 0;
32
+ }
33
+ /**
34
+ * Find the best matching resource key for a simulation key.
35
+ * Matches the longest resource name that is a prefix of the simulation key.
36
+ * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'
37
+ * @example findResourceKey('albums', ['albums', 'review']) // 'albums'
38
+ */
39
+ function findResourceKey(simulationKey, resourceKeys) {
40
+ const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);
41
+ for (const resourceKey of sorted) if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + "-")) return resourceKey;
42
+ }
43
+ /**
44
+ * Get the expected component export name for a resource
45
+ * @example getComponentName('review') // 'ReviewResource'
46
+ * @example getComponentName('album-art') // 'AlbumArtResource'
47
+ */
48
+ function getComponentName(resourceKey) {
49
+ return `${toPascalCase(resourceKey)}Resource`;
50
+ }
51
+ /**
52
+ * Find all resource directories in a base directory.
53
+ * Each valid resource directory contains a file matching the expected pattern.
54
+ *
55
+ * @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')
56
+ * @param filePattern - Function to generate expected filename from resource key
57
+ * @param fs - File system operations (for testing)
58
+ *
59
+ * @example
60
+ * // Find source resources (tsx files)
61
+ * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);
62
+ *
63
+ * @example
64
+ * // Find built resources (js files)
65
+ * const resources = findResourceDirs('dist', key => `${key}.js`);
66
+ */
67
+ function findResourceDirs(baseDir, filePattern, fs) {
68
+ if (!fs.existsSync(baseDir)) return [];
69
+ return fs.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
70
+ const key = entry.name;
71
+ const dir = `${baseDir}/${key}`;
72
+ const resourcePath = `${dir}/${filePattern(key)}`;
73
+ if (!fs.existsSync(resourcePath)) return null;
74
+ return {
75
+ key,
76
+ dir,
77
+ resourcePath
78
+ };
79
+ }).filter((info) => info !== null);
80
+ }
81
+ /**
82
+ * Find all tool files in a tools directory.
83
+ * Matches *.ts files directly in the directory (not recursive).
84
+ *
85
+ * @example
86
+ * findToolFiles('src/tools', fs)
87
+ * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]
88
+ */
89
+ function findToolFiles(toolsDir, fs) {
90
+ if (!fs.existsSync(toolsDir)) return [];
91
+ return fs.readdirSync(toolsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts")).map((entry) => ({
92
+ name: entry.name.replace(/\.ts$/, ""),
93
+ path: `${toolsDir}/${entry.name}`
94
+ }));
95
+ }
96
+ /**
97
+ * Find all simulation JSON files in a flat simulations directory.
98
+ * Matches any *.json file directly in the directory.
99
+ *
100
+ * @example
101
+ * findSimulationFilesFlat('tests/simulations', fs)
102
+ * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]
103
+ */
104
+ function findSimulationFilesFlat(simulationsDir, fs) {
105
+ if (!fs.existsSync(simulationsDir)) return [];
106
+ return fs.readdirSync(simulationsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".json")).map((entry) => ({
107
+ name: entry.name.replace(/\.json$/, ""),
108
+ path: `${simulationsDir}/${entry.name}`
109
+ }));
110
+ }
111
+ //#endregion
112
+ Object.defineProperty(exports, "extractResourceKey", {
113
+ enumerable: true,
114
+ get: function() {
115
+ return extractResourceKey;
116
+ }
117
+ });
118
+ Object.defineProperty(exports, "extractSimulationKey", {
119
+ enumerable: true,
120
+ get: function() {
121
+ return extractSimulationKey;
122
+ }
123
+ });
124
+ Object.defineProperty(exports, "findResourceDirs", {
125
+ enumerable: true,
126
+ get: function() {
127
+ return findResourceDirs;
128
+ }
129
+ });
130
+ Object.defineProperty(exports, "findResourceKey", {
131
+ enumerable: true,
132
+ get: function() {
133
+ return findResourceKey;
134
+ }
135
+ });
136
+ Object.defineProperty(exports, "findSimulationFilesFlat", {
137
+ enumerable: true,
138
+ get: function() {
139
+ return findSimulationFilesFlat;
140
+ }
141
+ });
142
+ Object.defineProperty(exports, "findToolFiles", {
143
+ enumerable: true,
144
+ get: function() {
145
+ return findToolFiles;
146
+ }
147
+ });
148
+ Object.defineProperty(exports, "getComponentName", {
149
+ enumerable: true,
150
+ get: function() {
151
+ return getComponentName;
152
+ }
153
+ });
154
+ Object.defineProperty(exports, "toPascalCase", {
155
+ enumerable: true,
156
+ get: function() {
157
+ return toPascalCase;
158
+ }
159
+ });
160
+
161
+ //# sourceMappingURL=discovery-Clu4uHp1.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-Clu4uHp1.cjs","names":[],"sources":["../src/lib/discovery.ts"],"sourcesContent":["/**\n * Discovery utilities for resources, tools, and simulations.\n *\n * String helpers (toPascalCase, extractResourceKey, etc.) are used by both\n * browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,\n * etc.) are used by CLI commands for build-time and runtime discovery.\n */\n\n/**\n * Convert a kebab-case string to PascalCase\n * @example toPascalCase('review') // 'Review'\n * @example toPascalCase('album-art') // 'AlbumArt'\n */\nexport function toPascalCase(str: string): string {\n return str\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\n/**\n * Extract the resource key from a resource file path.\n * Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')\n */\nexport function extractResourceKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.tsx$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Extract the simulation key from a simulation file path.\n * Matches any *.json file (e.g., './show-albums.json' → 'show-albums')\n */\nexport function extractSimulationKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.json$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'\n * @example findResourceKey('albums', ['albums', 'review']) // 'albums'\n */\nexport function findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\n/**\n * Get the expected component export name for a resource\n * @example getComponentName('review') // 'ReviewResource'\n * @example getComponentName('album-art') // 'AlbumArtResource'\n */\nexport function getComponentName(resourceKey: string): string {\n return `${toPascalCase(resourceKey)}Resource`;\n}\n\n// --- Node.js utilities for CLI commands ---\n// These utilities use standard Node.js APIs and can be imported by build/push/mcp commands.\n\n/**\n * Information about a discovered resource directory\n */\nexport interface ResourceDirInfo {\n /** Resource key (directory name), e.g., 'albums', 'carousel' */\n key: string;\n /** Full path to the resource directory */\n dir: string;\n /** Full path to the main resource file (tsx or json depending on context) */\n resourcePath: string;\n}\n\n/**\n * File system operations interface for dependency injection in tests\n */\nexport interface FsOps {\n readdirSync: (\n path: string,\n options: { withFileTypes: true }\n ) => Array<{ name: string; isDirectory: () => boolean }>;\n existsSync: (path: string) => boolean;\n}\n\n/**\n * Find all resource directories in a base directory.\n * Each valid resource directory contains a file matching the expected pattern.\n *\n * @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')\n * @param filePattern - Function to generate expected filename from resource key\n * @param fs - File system operations (for testing)\n *\n * @example\n * // Find source resources (tsx files)\n * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);\n *\n * @example\n * // Find built resources (js files)\n * const resources = findResourceDirs('dist', key => `${key}.js`);\n */\nexport function findResourceDirs(\n baseDir: string,\n filePattern: (key: string) => string,\n fs: FsOps\n): ResourceDirInfo[] {\n if (!fs.existsSync(baseDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(baseDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => {\n const key = entry.name;\n const dir = `${baseDir}/${key}`;\n const resourcePath = `${dir}/${filePattern(key)}`;\n\n if (!fs.existsSync(resourcePath)) {\n return null;\n }\n\n return { key, dir, resourcePath };\n })\n .filter((info): info is ResourceDirInfo => info !== null);\n}\n\n// --- Tool files + flat simulations discovery ---\n\n/**\n * Information about a discovered tool file\n */\nexport interface ToolFileInfo {\n /** Tool name derived from filename (e.g., 'show-albums') */\n name: string;\n /** Full path to the tool file */\n path: string;\n}\n\n/**\n * Find all tool files in a tools directory.\n * Matches *.ts files directly in the directory (not recursive).\n *\n * @example\n * findToolFiles('src/tools', fs)\n * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]\n */\nexport function findToolFiles(\n toolsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): ToolFileInfo[] {\n if (!fs.existsSync(toolsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(toolsDir, { withFileTypes: true });\n\n return entries\n .filter(\n (entry) =>\n !entry.isDirectory() && entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')\n )\n .map((entry) => ({\n name: entry.name.replace(/\\.ts$/, ''),\n path: `${toolsDir}/${entry.name}`,\n }));\n}\n\n/**\n * Information about a discovered simulation file (flat convention)\n */\nexport interface SimulationFileInfo {\n /** Filename without extension (e.g., 'show-albums') */\n name: string;\n /** Full path to the simulation file */\n path: string;\n}\n\n/**\n * Find all simulation JSON files in a flat simulations directory.\n * Matches any *.json file directly in the directory.\n *\n * @example\n * findSimulationFilesFlat('tests/simulations', fs)\n * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]\n */\nexport function findSimulationFilesFlat(\n simulationsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): SimulationFileInfo[] {\n if (!fs.existsSync(simulationsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(simulationsDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => !entry.isDirectory() && entry.name.endsWith('.json'))\n .map((entry) => ({\n name: entry.name.replace(/\\.json$/, ''),\n path: `${simulationsDir}/${entry.name}`,\n }));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,aAAa,KAAqB;AAChD,QAAO,IACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;;;;;AAOb,SAAgB,mBAAmB,MAAkC;CACnE,MAAM,QAAQ,KAAK,MAAM,gBAAgB;AACzC,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;AAO5B,SAAgB,qBAAqB,MAAkC;CACrE,MAAM,QAAQ,KAAK,MAAM,iBAAiB;AAC1C,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;;;AAS5B,SAAgB,gBAAgB,eAAuB,cAA4C;CAEjG,MAAM,SAAS,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;AACpE,MAAK,MAAM,eAAe,OACxB,KAAI,kBAAkB,eAAe,cAAc,WAAW,cAAc,IAAI,CAC9E,QAAO;;;;;;;AAWb,SAAgB,iBAAiB,aAA6B;AAC5D,QAAO,GAAG,aAAa,YAAY,CAAC;;;;;;;;;;;;;;;;;;AA6CtC,SAAgB,iBACd,SACA,aACA,IACmB;AACnB,KAAI,CAAC,GAAG,WAAW,QAAQ,CACzB,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,SAAS,EAAE,eAAe,MAAM,CAAC,CAG7D,QAAQ,UAAU,MAAM,aAAa,CAAC,CACtC,KAAK,UAAU;EACd,MAAM,MAAM,MAAM;EAClB,MAAM,MAAM,GAAG,QAAQ,GAAG;EAC1B,MAAM,eAAe,GAAG,IAAI,GAAG,YAAY,IAAI;AAE/C,MAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO;AAGT,SAAO;GAAE;GAAK;GAAK;GAAc;GACjC,CACD,QAAQ,SAAkC,SAAS,KAAK;;;;;;;;;;AAuB7D,SAAgB,cACd,UACA,IACgB;AAChB,KAAI,CAAC,GAAG,WAAW,SAAS,CAC1B,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,UAAU,EAAE,eAAe,MAAM,CAAC,CAG9D,QACE,UACC,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,MAAM,IAAI,CAAC,MAAM,KAAK,SAAS,WAAW,CACzF,CACA,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,SAAS,GAAG;EACrC,MAAM,GAAG,SAAS,GAAG,MAAM;EAC5B,EAAE;;;;;;;;;;AAqBP,SAAgB,wBACd,gBACA,IACsB;AACtB,KAAI,CAAC,GAAG,WAAW,eAAe,CAChC,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,gBAAgB,EAAE,eAAe,MAAM,CAAC,CAGpE,QAAQ,UAAU,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,QAAQ,CAAC,CACvE,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG;EACvC,MAAM,GAAG,eAAe,GAAG,MAAM;EAClC,EAAE"}
package/dist/index.cjs CHANGED
@@ -2,8 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-9hOWP6kD.cjs");
3
3
  const require_protocol = require("./protocol-jbxhzcnS.cjs");
4
4
  const require_use_app = require("./use-app-D2h-aiyr.cjs");
5
- const require_simulator = require("./simulator-DIVvI69i.cjs");
6
- const require_discovery = require("./discovery-BxKCIgG5.cjs");
5
+ const require_simulator = require("./simulator-CH9hs0N6.cjs");
7
6
  const require_host_index = require("./host/index.cjs");
8
7
  const require_simulator_index = require("./simulator/index.cjs");
9
8
  const require_chatgpt_index = require("./chatgpt/index.cjs");
@@ -3227,7 +3226,6 @@ exports.TOOL_RESULT_METHOD = require_use_app.l;
3227
3226
  exports.applyDocumentTheme = require_use_app.FQ;
3228
3227
  exports.applyHostFonts = require_use_app.TQ;
3229
3228
  exports.applyHostStyleVariables = require_use_app.PQ;
3230
- exports.buildResourceMap = require_discovery.buildResourceMap;
3231
3229
  Object.defineProperty(exports, "chatgpt", {
3232
3230
  enumerable: true,
3233
3231
  get: function() {
@@ -3235,7 +3233,6 @@ Object.defineProperty(exports, "chatgpt", {
3235
3233
  }
3236
3234
  });
3237
3235
  exports.cn = cn;
3238
- exports.createResourceExports = require_discovery.createResourceExports;
3239
3236
  exports.detectHost = require_host_index.detectHost;
3240
3237
  exports.detectPlatform = require_host_index.detectPlatform;
3241
3238
  exports.getDocumentTheme = require_use_app.AQ;