@_davideast/stitch-mcp 0.5.5 → 0.7.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 (168) hide show
  1. package/dist/chunk-0xbbvsve.js +370 -0
  2. package/dist/chunk-0xbbvsve.js.map +16 -0
  3. package/dist/chunk-11ab03kg.js +137 -0
  4. package/dist/chunk-11ab03kg.js.map +10 -0
  5. package/dist/chunk-17cs61t4.js +270 -0
  6. package/dist/chunk-17cs61t4.js.map +10 -0
  7. package/dist/chunk-1mz7188j.js +20 -0
  8. package/dist/chunk-1mz7188j.js.map +9 -0
  9. package/dist/chunk-1txb8kjb.js +17 -0
  10. package/dist/chunk-1txb8kjb.js.map +9 -0
  11. package/dist/chunk-1v5q6d3n.js +17 -0
  12. package/dist/chunk-1v5q6d3n.js.map +9 -0
  13. package/dist/chunk-1wcg0pjg.js +94 -0
  14. package/dist/chunk-1wcg0pjg.js.map +10 -0
  15. package/dist/chunk-2zp03jky.js +19 -0
  16. package/dist/chunk-2zp03jky.js.map +9 -0
  17. package/dist/chunk-441ntz7a.js +137 -0
  18. package/dist/chunk-441ntz7a.js.map +10 -0
  19. package/dist/chunk-543135qd.js +43679 -0
  20. package/dist/chunk-543135qd.js.map +234 -0
  21. package/dist/chunk-564wpgj7.js +246 -0
  22. package/dist/chunk-564wpgj7.js.map +14 -0
  23. package/dist/chunk-5jbenaez.js +19185 -0
  24. package/dist/chunk-5jbenaez.js.map +115 -0
  25. package/dist/chunk-633ma50k.js +371 -0
  26. package/dist/chunk-633ma50k.js.map +16 -0
  27. package/dist/chunk-7ryqstaa.js +7065 -0
  28. package/dist/chunk-7ryqstaa.js.map +115 -0
  29. package/dist/chunk-7z5z40ar.js +165 -0
  30. package/dist/chunk-7z5z40ar.js.map +10 -0
  31. package/dist/chunk-86gwwcyr.js +46325 -0
  32. package/dist/chunk-86gwwcyr.js.map +261 -0
  33. package/dist/chunk-8gegrmmt.js +256 -0
  34. package/dist/chunk-8gegrmmt.js.map +11 -0
  35. package/dist/chunk-8hgrrc49.js +17 -0
  36. package/dist/chunk-8hgrrc49.js.map +9 -0
  37. package/dist/chunk-8w97w2wa.js +50 -0
  38. package/dist/chunk-8w97w2wa.js.map +9 -0
  39. package/dist/chunk-8yfetpqq.js +19132 -0
  40. package/dist/chunk-8yfetpqq.js.map +115 -0
  41. package/dist/chunk-94knm2sw.js +1495 -0
  42. package/dist/chunk-94knm2sw.js.map +23 -0
  43. package/dist/chunk-9ckyz47q.js +518 -0
  44. package/dist/chunk-9ckyz47q.js.map +12 -0
  45. package/dist/chunk-appd0sxm.js +5217 -0
  46. package/dist/chunk-appd0sxm.js.map +67 -0
  47. package/dist/chunk-b9atzag0.js +67 -0
  48. package/dist/chunk-b9atzag0.js.map +10 -0
  49. package/dist/chunk-c08qy4ty.js +67 -0
  50. package/dist/chunk-c08qy4ty.js.map +10 -0
  51. package/dist/chunk-c5dtqvff.js +11 -0
  52. package/dist/chunk-c5dtqvff.js.map +9 -0
  53. package/dist/chunk-d1ea3tmp.js +17 -0
  54. package/dist/chunk-d1ea3tmp.js.map +9 -0
  55. package/dist/chunk-ecn1ca83.js +34253 -0
  56. package/dist/chunk-ecn1ca83.js.map +261 -0
  57. package/dist/chunk-ef1c6gq5.js +947 -0
  58. package/dist/chunk-ef1c6gq5.js.map +28 -0
  59. package/dist/chunk-ef355a3f.js +20 -0
  60. package/dist/chunk-ef355a3f.js.map +9 -0
  61. package/dist/chunk-f0dt9hv8.js +680 -0
  62. package/dist/chunk-f0dt9hv8.js.map +17 -0
  63. package/dist/chunk-f2aj8ff8.js +11 -0
  64. package/dist/chunk-f2aj8ff8.js.map +9 -0
  65. package/dist/chunk-f3wp07zw.js +24 -0
  66. package/dist/chunk-f3wp07zw.js.map +9 -0
  67. package/dist/chunk-f5wqd3z3.js +10 -0
  68. package/dist/chunk-f5wqd3z3.js.map +9 -0
  69. package/dist/chunk-g8hwy0wx.js +504 -0
  70. package/dist/chunk-g8hwy0wx.js.map +21 -0
  71. package/dist/chunk-gcx3c3yc.js +680 -0
  72. package/dist/chunk-gcx3c3yc.js.map +17 -0
  73. package/dist/chunk-gq6vxp70.js +202 -0
  74. package/dist/chunk-gq6vxp70.js.map +13 -0
  75. package/dist/chunk-gw64p5pg.js +164 -0
  76. package/dist/chunk-gw64p5pg.js.map +10 -0
  77. package/dist/chunk-hgv5frj1.js +256 -0
  78. package/dist/chunk-hgv5frj1.js.map +11 -0
  79. package/dist/chunk-hst78da7.js +87 -0
  80. package/dist/chunk-hst78da7.js.map +13 -0
  81. package/dist/chunk-hsxpgjyd.js +256 -0
  82. package/dist/chunk-hsxpgjyd.js.map +11 -0
  83. package/dist/chunk-j1v44zzm.js +109 -0
  84. package/dist/chunk-j1v44zzm.js.map +10 -0
  85. package/dist/chunk-jfd5md63.js +736 -0
  86. package/dist/chunk-jfd5md63.js.map +16 -0
  87. package/dist/chunk-jn5pcnz9.js +270 -0
  88. package/dist/chunk-jn5pcnz9.js.map +10 -0
  89. package/dist/chunk-jvhzgyhy.js +62 -0
  90. package/dist/chunk-jvhzgyhy.js.map +10 -0
  91. package/dist/chunk-k86st2r8.js +7 -0
  92. package/dist/chunk-k86st2r8.js.map +9 -0
  93. package/dist/chunk-kztccppz.js +606 -0
  94. package/dist/chunk-kztccppz.js.map +15 -0
  95. package/dist/chunk-m2vk15q9.js +503 -0
  96. package/dist/chunk-m2vk15q9.js.map +21 -0
  97. package/dist/chunk-mk40f3ka.js +31529 -0
  98. package/dist/chunk-mk40f3ka.js.map +245 -0
  99. package/dist/chunk-mp1sf8x6.js +264 -0
  100. package/dist/chunk-mp1sf8x6.js.map +12 -0
  101. package/dist/chunk-mzyqavzd.js +736 -0
  102. package/dist/chunk-mzyqavzd.js.map +16 -0
  103. package/dist/chunk-n9fs543g.js +94 -0
  104. package/dist/chunk-n9fs543g.js.map +10 -0
  105. package/dist/chunk-nbbwjw90.js +165 -0
  106. package/dist/chunk-nbbwjw90.js.map +10 -0
  107. package/dist/chunk-nh14pn95.js +137 -0
  108. package/dist/chunk-nh14pn95.js.map +10 -0
  109. package/dist/chunk-qnd877d5.js +947 -0
  110. package/dist/chunk-qnd877d5.js.map +28 -0
  111. package/dist/chunk-rng2ypf7.js +538 -0
  112. package/dist/chunk-rng2ypf7.js.map +15 -0
  113. package/dist/chunk-sjq10wbw.js +39 -0
  114. package/dist/chunk-sjq10wbw.js.map +9 -0
  115. package/dist/chunk-snv6a65k.js +759 -0
  116. package/dist/chunk-snv6a65k.js.map +19 -0
  117. package/dist/chunk-sqhdg0mf.js +2138 -0
  118. package/dist/chunk-sqhdg0mf.js.map +44 -0
  119. package/dist/chunk-tebher8z.js +514 -0
  120. package/dist/chunk-tebher8z.js.map +12 -0
  121. package/dist/chunk-v0wtyr4k.js +66 -0
  122. package/dist/chunk-v0wtyr4k.js.map +10 -0
  123. package/dist/chunk-v20274k8.js +246 -0
  124. package/dist/chunk-v20274k8.js.map +14 -0
  125. package/dist/chunk-vcp9fp2w.js +839 -0
  126. package/dist/chunk-vcp9fp2w.js.map +11 -0
  127. package/dist/chunk-vz737k5f.js +269 -0
  128. package/dist/chunk-vz737k5f.js.map +10 -0
  129. package/dist/chunk-x6bsgeqa.js +736 -0
  130. package/dist/chunk-x6bsgeqa.js.map +16 -0
  131. package/dist/chunk-xg9kcbp1.js +371 -0
  132. package/dist/chunk-xg9kcbp1.js.map +16 -0
  133. package/dist/chunk-xhad5b8x.js +110 -0
  134. package/dist/chunk-xhad5b8x.js.map +10 -0
  135. package/dist/chunk-xkwa1mn5.js +203 -0
  136. package/dist/chunk-xkwa1mn5.js.map +13 -0
  137. package/dist/chunk-xtcg74kf.js +50 -0
  138. package/dist/chunk-xtcg74kf.js.map +9 -0
  139. package/dist/chunk-xzjkaqe9.js +759 -0
  140. package/dist/chunk-xzjkaqe9.js.map +19 -0
  141. package/dist/chunk-y65xgj69.js +1495 -0
  142. package/dist/chunk-y65xgj69.js.map +23 -0
  143. package/dist/commands/doctor/command.js +1 -1
  144. package/dist/commands/init/command.js +1 -1
  145. package/dist/commands/logout/command.js +1 -1
  146. package/dist/commands/proxy/LoggingCallToolHandler.d.ts +11 -0
  147. package/dist/commands/proxy/command.js +1 -1
  148. package/dist/commands/screens/command.js +4 -4
  149. package/dist/commands/serve/command.js +5 -5
  150. package/dist/commands/site/command.js +1 -1
  151. package/dist/commands/snapshot/command.js +1 -1
  152. package/dist/commands/tool/command.js +1 -1
  153. package/dist/commands/tool/steps/LogExecuteToolStep.d.ts +19 -0
  154. package/dist/commands/upload/command.d.ts +2 -0
  155. package/dist/commands/upload/command.js +77 -0
  156. package/dist/commands/upload/command.js.map +11 -0
  157. package/dist/commands/upload/handler.d.ts +20 -0
  158. package/dist/commands/upload/spec.d.ts +38 -0
  159. package/dist/commands/view/command.js +1 -1
  160. package/dist/index.js +9 -9
  161. package/dist/index.js.map +1 -1
  162. package/dist/lib/log/append.d.ts +12 -0
  163. package/dist/lib/log/blob-store/handler.d.ts +10 -0
  164. package/dist/lib/log/blob-store/spec.d.ts +251 -0
  165. package/dist/lib/log/capture/handler.d.ts +10 -0
  166. package/dist/lib/log/capture/spec.d.ts +1504 -0
  167. package/dist/lib/log/factory.d.ts +4 -0
  168. package/package.json +2 -2
@@ -0,0 +1,514 @@
1
+ import {
2
+ require_lib
3
+ } from "./chunk-sqhdg0mf.js";
4
+ import {
5
+ load
6
+ } from "./chunk-543135qd.js";
7
+ import {
8
+ __require,
9
+ __toESM
10
+ } from "./chunk-9wyra8hs.js";
11
+
12
+ // src/lib/server/AssetGateway.ts
13
+ var import_fs_extra = __toESM(require_lib(), 1);
14
+ import path from "path";
15
+ import crypto from "crypto";
16
+ import { parse } from "@astrojs/compiler";
17
+ import { is, serialize } from "@astrojs/compiler/utils";
18
+
19
+ class AssetGateway {
20
+ cacheDir;
21
+ static ALLOWED_HOST_PATTERNS = [
22
+ /\.googleapis\.com$/,
23
+ /\.googleusercontent\.com$/,
24
+ /\.gstatic\.com$/,
25
+ /^cdnjs\.cloudflare\.com$/
26
+ ];
27
+ static validateAssetUrl(url) {
28
+ let parsed;
29
+ try {
30
+ parsed = new URL(url);
31
+ } catch {
32
+ return false;
33
+ }
34
+ if (parsed.protocol !== "https:") {
35
+ return false;
36
+ }
37
+ return AssetGateway.ALLOWED_HOST_PATTERNS.some((pattern) => pattern.test(parsed.hostname));
38
+ }
39
+ constructor(projectRoot = process.cwd()) {
40
+ this.cacheDir = path.join(projectRoot, ".stitch-mcp", "cache");
41
+ }
42
+ async init() {
43
+ await import_fs_extra.default.ensureDir(this.cacheDir);
44
+ }
45
+ getHash(url) {
46
+ return crypto.createHash("md5").update(url).digest("hex");
47
+ }
48
+ async fetchAsset(url) {
49
+ await this.init();
50
+ if (!AssetGateway.validateAssetUrl(url)) {
51
+ console.warn(`Blocked asset fetch for disallowed URL: ${url}`);
52
+ return null;
53
+ }
54
+ const hash = this.getHash(url);
55
+ const cachePath = path.join(this.cacheDir, hash);
56
+ const metadataPath = cachePath + ".meta.json";
57
+ if (await import_fs_extra.default.pathExists(cachePath)) {
58
+ let contentType;
59
+ if (await import_fs_extra.default.pathExists(metadataPath)) {
60
+ try {
61
+ const meta = await import_fs_extra.default.readJson(metadataPath);
62
+ contentType = meta.contentType;
63
+ } catch (e) {}
64
+ }
65
+ try {
66
+ const stream = import_fs_extra.default.createReadStream(cachePath);
67
+ stream.on("error", () => {});
68
+ return { stream, contentType };
69
+ } catch (e) {
70
+ console.warn(`Failed to open cached asset: ${url}`, e);
71
+ }
72
+ }
73
+ try {
74
+ const response = await fetch(url, {
75
+ headers: {
76
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
77
+ }
78
+ });
79
+ if (!response.ok) {
80
+ console.warn(`Failed to fetch asset: ${url} (${response.status})`);
81
+ return null;
82
+ }
83
+ const contentType = response.headers.get("content-type") || undefined;
84
+ const buffer = await response.arrayBuffer();
85
+ await import_fs_extra.default.writeFile(cachePath, Buffer.from(buffer));
86
+ if (contentType) {
87
+ await import_fs_extra.default.writeJson(metadataPath, { contentType });
88
+ }
89
+ const stream = import_fs_extra.default.createReadStream(cachePath);
90
+ stream.on("error", () => {});
91
+ return { stream, contentType };
92
+ } catch (e) {
93
+ console.warn(`Failed to fetch asset: ${url}`, e);
94
+ return null;
95
+ }
96
+ }
97
+ rewriteCssUrls(css, baseUrl) {
98
+ const discovered = [];
99
+ const importRewritten = css.replace(/@import\s+(['"])([^'"]+)\1\s*;/g, (match, quote, rawUrl) => {
100
+ const trimmed = rawUrl.trim();
101
+ if (!trimmed || trimmed.startsWith("data:") || trimmed.startsWith("/_stitch/")) {
102
+ return match;
103
+ }
104
+ let resolved;
105
+ try {
106
+ if (trimmed.startsWith("//")) {
107
+ resolved = new URL(trimmed, baseUrl).href;
108
+ } else if (/^https?:\/\//.test(trimmed)) {
109
+ resolved = trimmed;
110
+ } else {
111
+ resolved = new URL(trimmed, baseUrl).href;
112
+ }
113
+ } catch {
114
+ return match;
115
+ }
116
+ discovered.push(resolved);
117
+ return `@import ${quote}/_stitch/asset?url=${encodeURIComponent(resolved)}${quote};`;
118
+ });
119
+ const rewritten = importRewritten.replace(/url\(\s*(['"]?)([^)]*?)\1\s*\)/g, (match, quote, rawUrl) => {
120
+ const trimmed = rawUrl.trim();
121
+ if (!trimmed || trimmed.startsWith("data:") || trimmed.startsWith("#") && !trimmed.startsWith("#/") || trimmed.startsWith("/_stitch/")) {
122
+ return match;
123
+ }
124
+ let resolved;
125
+ try {
126
+ if (trimmed.startsWith("//")) {
127
+ resolved = new URL(trimmed, baseUrl).href;
128
+ } else if (/^https?:\/\//.test(trimmed)) {
129
+ resolved = trimmed;
130
+ } else {
131
+ resolved = new URL(trimmed, baseUrl).href;
132
+ }
133
+ } catch {
134
+ return match;
135
+ }
136
+ discovered.push(resolved);
137
+ return `url(${quote}/_stitch/asset?url=${encodeURIComponent(resolved)}${quote})`;
138
+ });
139
+ Promise.all(discovered.map((url) => this.fetchAsset(url).catch(() => {})));
140
+ return rewritten;
141
+ }
142
+ async rewriteHtmlForPreview(html) {
143
+ const $ = load(html);
144
+ const assets = new Set;
145
+ const process2 = (el, attr) => {
146
+ const url = $(el).attr(attr);
147
+ if (url && url.startsWith("http")) {
148
+ assets.add(url);
149
+ $(el).attr(attr, `/_stitch/asset?url=${encodeURIComponent(url)}`);
150
+ }
151
+ };
152
+ $("img").each((_, el) => process2(el, "src"));
153
+ $('link[rel="stylesheet"]').each((_, el) => process2(el, "href"));
154
+ $("script").each((_, el) => process2(el, "src"));
155
+ await Promise.all(Array.from(assets).map((url) => this.fetchAsset(url).catch(console.error)));
156
+ return $.html();
157
+ }
158
+ getExtensionFromContentType(contentType) {
159
+ if (!contentType)
160
+ return "";
161
+ const mimeType = contentType.split(";")[0]?.trim().toLowerCase();
162
+ const mimeToExt = {
163
+ "text/css": ".css",
164
+ "text/javascript": ".js",
165
+ "application/javascript": ".js",
166
+ "application/x-javascript": ".js",
167
+ "image/png": ".png",
168
+ "image/jpeg": ".jpg",
169
+ "image/gif": ".gif",
170
+ "image/webp": ".webp",
171
+ "image/svg+xml": ".svg",
172
+ "image/x-icon": ".ico",
173
+ "image/vnd.microsoft.icon": ".ico",
174
+ "font/woff": ".woff",
175
+ "font/woff2": ".woff2",
176
+ "font/ttf": ".ttf",
177
+ "font/otf": ".otf",
178
+ "application/font-woff": ".woff",
179
+ "application/font-woff2": ".woff2",
180
+ "application/json": ".json",
181
+ "text/html": ".html",
182
+ "text/plain": ".txt"
183
+ };
184
+ return mimeToExt[mimeType || ""] || "";
185
+ }
186
+ async rewriteHtmlForBuild(html) {
187
+ const $ = load(html);
188
+ const assetUrls = [];
189
+ const collectUrl = (el, attr) => {
190
+ const url = $(el).attr(attr);
191
+ if (url && url.startsWith("http")) {
192
+ assetUrls.push(url);
193
+ }
194
+ };
195
+ $("img").each((_, el) => collectUrl(el, "src"));
196
+ $('link[rel="stylesheet"]').each((_, el) => collectUrl(el, "href"));
197
+ $("script").each((_, el) => collectUrl(el, "src"));
198
+ const urlToFilename = new Map;
199
+ await Promise.all(assetUrls.map(async (url) => {
200
+ try {
201
+ const result = await this.fetchAsset(url);
202
+ if (!result)
203
+ return;
204
+ const { stream, contentType } = result;
205
+ stream.destroy();
206
+ const hash = this.getHash(url);
207
+ const urlObj = new URL(url);
208
+ let ext = path.extname(urlObj.pathname);
209
+ if (!ext) {
210
+ ext = this.getExtensionFromContentType(contentType);
211
+ }
212
+ const filename = `${hash}${ext}`;
213
+ urlToFilename.set(url, filename);
214
+ } catch (e) {}
215
+ }));
216
+ const assets = [];
217
+ const rewriteUrl = (el, attr, defaultExt) => {
218
+ const url = $(el).attr(attr);
219
+ if (url && url.startsWith("http")) {
220
+ let filename = urlToFilename.get(url);
221
+ if (!filename) {
222
+ const hash = this.getHash(url);
223
+ filename = `${hash}${defaultExt}`;
224
+ }
225
+ $(el).attr(attr, `/assets/${filename}`);
226
+ assets.push({ url, filename });
227
+ }
228
+ };
229
+ $("img").each((_, el) => rewriteUrl(el, "src", ".png"));
230
+ $('link[rel="stylesheet"]').each((_, el) => rewriteUrl(el, "href", ".css"));
231
+ $("script").each((_, el) => {
232
+ rewriteUrl(el, "src", ".js");
233
+ if ($(el).attr("src")?.startsWith("/assets/")) {
234
+ $(el).attr("is:inline", "");
235
+ }
236
+ });
237
+ $.root().contents().filter((_, el) => el.type === "directive" && el.name === "!doctype").remove();
238
+ let outputHtml = $.html();
239
+ const astroContent = `---
240
+ ---
241
+ ${outputHtml}`;
242
+ const parseResult = await parse(astroContent, { position: false });
243
+ const skipElements = new Set(["script", "style"]);
244
+ const escapeExpressions = (node, insideSkipElement) => {
245
+ const isSkipElement = is.element(node) && skipElements.has(node.name.toLowerCase());
246
+ const shouldSkip = insideSkipElement || isSkipElement;
247
+ if (is.parent(node) && !shouldSkip) {
248
+ const newChildren = [];
249
+ for (const child of node.children) {
250
+ if (child.type === "expression") {
251
+ const exprContent = child.children?.filter((c) => is.text(c)).map((c) => c.value).join("") || "";
252
+ newChildren.push({
253
+ type: "text",
254
+ value: `{'{'}${exprContent}{'}'}`
255
+ });
256
+ } else {
257
+ newChildren.push(child);
258
+ escapeExpressions(child, shouldSkip);
259
+ }
260
+ }
261
+ node.children = newChildren;
262
+ } else if (is.parent(node)) {
263
+ for (const child of node.children) {
264
+ escapeExpressions(child, shouldSkip);
265
+ }
266
+ }
267
+ };
268
+ escapeExpressions(parseResult.ast, false);
269
+ const astroOutput = serialize(parseResult.ast);
270
+ return { html: astroOutput, assets };
271
+ }
272
+ async copyAssetTo(url, destPath) {
273
+ await this.init();
274
+ const hash = this.getHash(url);
275
+ const cachePath = path.join(this.cacheDir, hash);
276
+ if (await import_fs_extra.default.pathExists(cachePath)) {
277
+ await import_fs_extra.default.copy(cachePath, destPath);
278
+ return true;
279
+ } else {
280
+ const result = await this.fetchAsset(url);
281
+ if (!result) {
282
+ console.warn(`Skipping asset copy, fetch failed: ${url}`);
283
+ return false;
284
+ }
285
+ await import_fs_extra.default.copy(cachePath, destPath);
286
+ return true;
287
+ }
288
+ }
289
+ }
290
+
291
+ // src/lib/server/vite/plugins/virtualContent.ts
292
+ var VIRTUAL_NAV_ID = "virtual:stitch-nav";
293
+ var RESOLVED_VIRTUAL_NAV_ID = "\x00" + VIRTUAL_NAV_ID;
294
+ function virtualContent({ assetGateway, htmlMap }) {
295
+ return {
296
+ name: "stitch-virtual-content",
297
+ resolveId(id) {
298
+ if (id === VIRTUAL_NAV_ID) {
299
+ return RESOLVED_VIRTUAL_NAV_ID;
300
+ }
301
+ },
302
+ load(id) {
303
+ if (id === RESOLVED_VIRTUAL_NAV_ID) {
304
+ return `
305
+ if (import.meta.hot) {
306
+ // Register navigation handler
307
+ import.meta.hot.on('stitch:navigate', ({ url }) => {
308
+ window.location.href = url;
309
+ });
310
+
311
+ // Log connection status for debugging
312
+ import.meta.hot.on('vite:ws:connect', () => {
313
+ console.log('[stitch] HMR connected');
314
+ });
315
+
316
+ console.log('[stitch] Navigation handler registered');
317
+ } else {
318
+ console.warn('[stitch] HMR not available');
319
+ }
320
+ `;
321
+ }
322
+ },
323
+ configureServer(server) {
324
+ server.middlewares.use(async (req, res, next) => {
325
+ if (!req.url)
326
+ return next();
327
+ const url = new URL(req.url, "http://localhost");
328
+ if (url.pathname === "/_stitch/asset") {
329
+ const assetUrl = url.searchParams.get("url");
330
+ if (!assetUrl) {
331
+ res.statusCode = 400;
332
+ res.end("Missing url parameter");
333
+ return;
334
+ }
335
+ try {
336
+ const result = await assetGateway.fetchAsset(assetUrl);
337
+ if (!result) {
338
+ res.statusCode = 404;
339
+ res.end("Asset not found");
340
+ return;
341
+ }
342
+ const { stream, contentType } = result;
343
+ let effectiveContentType = contentType;
344
+ if (!contentType || contentType.includes("application/octet-stream")) {
345
+ const fontExtMap = {
346
+ ".woff2": "font/woff2",
347
+ ".woff": "font/woff",
348
+ ".ttf": "font/ttf",
349
+ ".otf": "font/otf",
350
+ ".eot": "application/vnd.ms-fontobject"
351
+ };
352
+ try {
353
+ const ext = new URL(assetUrl).pathname.match(/\.[^.]+$/)?.[0]?.toLowerCase();
354
+ if (ext && fontExtMap[ext]) {
355
+ effectiveContentType = fontExtMap[ext];
356
+ }
357
+ } catch {}
358
+ }
359
+ if (effectiveContentType) {
360
+ res.setHeader("Content-Type", effectiveContentType);
361
+ }
362
+ const isCss = effectiveContentType?.includes("text/css");
363
+ res.setHeader("Cache-Control", isCss ? "no-cache" : "public, max-age=31536000");
364
+ if (isCss) {
365
+ const chunks = [];
366
+ stream.on("data", (chunk) => chunks.push(chunk));
367
+ stream.on("end", async () => {
368
+ const css = Buffer.concat(chunks).toString("utf-8");
369
+ const rewritten = await assetGateway.rewriteCssUrls(css, assetUrl);
370
+ res.end(rewritten);
371
+ });
372
+ stream.on("error", (err) => {
373
+ console.error("CSS stream error:", err);
374
+ res.statusCode = 500;
375
+ res.end("Internal Server Error");
376
+ });
377
+ } else {
378
+ stream.pipe(res);
379
+ }
380
+ } catch (error) {
381
+ console.error("Asset proxy error:", error);
382
+ res.statusCode = 500;
383
+ res.end("Internal Server Error");
384
+ }
385
+ return;
386
+ }
387
+ const content = htmlMap.get(url.pathname);
388
+ if (content) {
389
+ try {
390
+ const transformed = await server.transformIndexHtml(req.url, content);
391
+ res.setHeader("Content-Type", "text/html");
392
+ res.end(transformed);
393
+ } catch (e) {
394
+ console.error("Transform error:", e);
395
+ next();
396
+ }
397
+ return;
398
+ }
399
+ if (url.pathname.startsWith("/_preview/")) {
400
+ const loadingHtml = `
401
+ <!DOCTYPE html>
402
+ <html>
403
+ <head>
404
+ <meta charset="UTF-8">
405
+ <meta http-equiv="refresh" content="1">
406
+ <title>Loading...</title>
407
+ <style>
408
+ body {
409
+ font-family: system-ui, -apple-system, sans-serif;
410
+ display: flex;
411
+ align-items: center;
412
+ justify-content: center;
413
+ height: 100vh;
414
+ margin: 0;
415
+ background: #1a1a1a;
416
+ color: #fff;
417
+ }
418
+ .loader {
419
+ text-align: center;
420
+ }
421
+ .spinner {
422
+ width: 40px;
423
+ height: 40px;
424
+ border: 3px solid #333;
425
+ border-top-color: #3b82f6;
426
+ border-radius: 50%;
427
+ animation: spin 1s linear infinite;
428
+ margin: 0 auto 16px;
429
+ }
430
+ @keyframes spin {
431
+ to { transform: rotate(360deg); }
432
+ }
433
+ </style>
434
+ </head>
435
+ <body>
436
+ <div class="loader">
437
+ <div class="spinner"></div>
438
+ <p>Loading preview...</p>
439
+ </div>
440
+ </body>
441
+ </html>`;
442
+ res.setHeader("Content-Type", "text/html");
443
+ res.end(loadingHtml);
444
+ return;
445
+ }
446
+ next();
447
+ });
448
+ },
449
+ async transformIndexHtml(html) {
450
+ const rewritten = await assetGateway.rewriteHtmlForPreview(html);
451
+ const script = `<script type="module" src="/@id/${VIRTUAL_NAV_ID}"></script>`;
452
+ if (rewritten.includes("</head>")) {
453
+ return rewritten.replace("</head>", script + "</head>");
454
+ }
455
+ return rewritten + script;
456
+ }
457
+ };
458
+ }
459
+
460
+ // src/lib/server/vite/StitchViteServer.ts
461
+ class StitchViteServer {
462
+ server = null;
463
+ htmlMap = new Map;
464
+ assetGateway;
465
+ constructor(projectRoot = process.cwd(), assetGateway) {
466
+ this.assetGateway = assetGateway || new AssetGateway(projectRoot);
467
+ }
468
+ async start(port = 3000) {
469
+ const { createServer } = await import("vite");
470
+ this.server = await createServer({
471
+ configFile: false,
472
+ root: process.cwd(),
473
+ server: {
474
+ port,
475
+ middlewareMode: false
476
+ },
477
+ appType: "custom",
478
+ plugins: [
479
+ virtualContent({
480
+ assetGateway: this.assetGateway,
481
+ htmlMap: this.htmlMap
482
+ })
483
+ ],
484
+ logLevel: "silent"
485
+ });
486
+ await this.server.listen();
487
+ const address = this.server.httpServer?.address();
488
+ if (address && typeof address === "object") {
489
+ return `http://localhost:${address.port}`;
490
+ }
491
+ return `http://localhost:${port}`;
492
+ }
493
+ async stop() {
494
+ if (this.server) {
495
+ await this.server.close();
496
+ this.server = null;
497
+ }
498
+ }
499
+ mount(route, html) {
500
+ this.htmlMap.set(route, html);
501
+ if (this.server) {
502
+ this.server.ws.send({ type: "full-reload", path: route });
503
+ }
504
+ }
505
+ navigate(url) {
506
+ if (this.server) {
507
+ this.server.ws.send("stitch:navigate", { url });
508
+ }
509
+ }
510
+ }
511
+
512
+ export { AssetGateway, StitchViteServer };
513
+
514
+ //# debugId=D84DFB3FB35F744464756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/server/AssetGateway.ts", "../src/lib/server/vite/plugins/virtualContent.ts", "../src/lib/server/vite/StitchViteServer.ts"],
4
+ "sourcesContent": [
5
+ "import fs from 'fs-extra';\nimport path from 'path';\nimport crypto from 'crypto';\nimport * as cheerio from 'cheerio';\nimport { Readable } from 'stream';\nimport { parse } from '@astrojs/compiler';\nimport { is, serialize } from '@astrojs/compiler/utils';\n\nexport class AssetGateway {\n private cacheDir: string;\n\n /**\n * Allowlist of hostname patterns for asset fetching.\n * Only HTTPS URLs matching these patterns are permitted.\n * Expand as needed for additional CDNs used in Stitch designs.\n */\n private static ALLOWED_HOST_PATTERNS: RegExp[] = [\n /\\.googleapis\\.com$/,\n /\\.googleusercontent\\.com$/,\n /\\.gstatic\\.com$/,\n /^cdnjs\\.cloudflare\\.com$/,\n ];\n\n /**\n * Validates that a URL is safe to fetch:\n * - Must be HTTPS\n * - Hostname must match the allowlist\n */\n static validateAssetUrl(url: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n\n if (parsed.protocol !== 'https:') {\n return false;\n }\n\n return AssetGateway.ALLOWED_HOST_PATTERNS.some(pattern => pattern.test(parsed.hostname));\n }\n\n constructor(projectRoot: string = process.cwd()) {\n this.cacheDir = path.join(projectRoot, '.stitch-mcp', 'cache');\n }\n\n async init() {\n await fs.ensureDir(this.cacheDir);\n }\n\n private getHash(url: string): string {\n return crypto.createHash('md5').update(url).digest('hex');\n }\n\n async fetchAsset(url: string): Promise<{ stream: Readable; contentType?: string } | null> {\n await this.init();\n\n if (!AssetGateway.validateAssetUrl(url)) {\n console.warn(`Blocked asset fetch for disallowed URL: ${url}`);\n return null;\n }\n\n const hash = this.getHash(url);\n const cachePath = path.join(this.cacheDir, hash);\n const metadataPath = cachePath + '.meta.json';\n\n if (await fs.pathExists(cachePath)) {\n let contentType: string | undefined;\n if (await fs.pathExists(metadataPath)) {\n try {\n const meta = await fs.readJson(metadataPath);\n contentType = meta.contentType;\n } catch (e) { }\n }\n try {\n const stream = fs.createReadStream(cachePath);\n // Suppress unhandled errors (e.g. file deletion race conditions)\n stream.on('error', () => {});\n return { stream, contentType };\n } catch (e) {\n // Fallback to fetch if opening stream fails (e.g. race condition/deletion)\n console.warn(`Failed to open cached asset: ${url}`, e);\n }\n }\n\n // Miss - fetch with User-Agent for Google Fonts compatibility\n try {\n const response = await fetch(url, {\n headers: {\n 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'\n }\n });\n\n if (!response.ok) {\n console.warn(`Failed to fetch asset: ${url} (${response.status})`);\n return null;\n }\n\n const contentType = response.headers.get('content-type') || undefined;\n\n const buffer = await response.arrayBuffer();\n await fs.writeFile(cachePath, Buffer.from(buffer));\n\n if (contentType) {\n await fs.writeJson(metadataPath, { contentType });\n }\n\n const stream = fs.createReadStream(cachePath);\n // Suppress unhandled errors\n stream.on('error', () => {});\n return { stream, contentType };\n } catch (e) {\n console.warn(`Failed to fetch asset: ${url}`, e);\n return null;\n }\n }\n\n rewriteCssUrls(css: string, baseUrl: string): string {\n const discovered: string[] = [];\n\n // First pass: rewrite bare-string @import (not @import url() which is handled below)\n // Matches: @import '../reset.css'; and @import \"https://fonts.googleapis.com/...\";\n const importRewritten = css.replace(\n /@import\\s+(['\"])([^'\"]+)\\1\\s*;/g,\n (match, quote, rawUrl) => {\n const trimmed = rawUrl.trim();\n\n if (\n !trimmed ||\n trimmed.startsWith('data:') ||\n trimmed.startsWith('/_stitch/')\n ) {\n return match;\n }\n\n let resolved: string;\n try {\n if (trimmed.startsWith('//')) {\n resolved = new URL(trimmed, baseUrl).href;\n } else if (/^https?:\\/\\//.test(trimmed)) {\n resolved = trimmed;\n } else {\n resolved = new URL(trimmed, baseUrl).href;\n }\n } catch {\n return match;\n }\n\n discovered.push(resolved);\n return `@import ${quote}/_stitch/asset?url=${encodeURIComponent(resolved)}${quote};`;\n },\n );\n\n // Second pass: rewrite url() references (covers @import url(), font src, background, etc.)\n const rewritten = importRewritten.replace(\n /url\\(\\s*(['\"]?)([^)]*?)\\1\\s*\\)/g,\n (match, quote, rawUrl) => {\n const trimmed = rawUrl.trim();\n\n // Skip empty, data URIs, fragment-only refs, and already-proxied URLs\n if (\n !trimmed ||\n trimmed.startsWith('data:') ||\n (trimmed.startsWith('#') && !trimmed.startsWith('#/')) ||\n trimmed.startsWith('/_stitch/')\n ) {\n return match;\n }\n\n let resolved: string;\n try {\n if (trimmed.startsWith('//')) {\n // Protocol-relative URL — resolve against base to get full URL\n resolved = new URL(trimmed, baseUrl).href;\n } else if (/^https?:\\/\\//.test(trimmed)) {\n // Already absolute HTTP URL\n resolved = trimmed;\n } else {\n // Relative URL — resolve against baseUrl\n resolved = new URL(trimmed, baseUrl).href;\n }\n } catch {\n // Malformed URL — leave unchanged\n return match;\n }\n\n discovered.push(resolved);\n return `url(${quote}/_stitch/asset?url=${encodeURIComponent(resolved)}${quote})`;\n },\n );\n\n // Optimistic prefetch: fire-and-forget parallel cache warming.\n // rewriteCssUrls is synchronous; prefetching is a side-effect that does\n // not need to complete before the rewritten CSS is returned.\n Promise.all(discovered.map(url => this.fetchAsset(url).catch(() => {})));\n\n return rewritten;\n }\n\n async rewriteHtmlForPreview(html: string): Promise<string> {\n const $ = cheerio.load(html);\n const assets = new Set<string>();\n\n const process = (el: any, attr: string) => {\n const url = $(el).attr(attr);\n if (url && url.startsWith('http')) {\n assets.add(url);\n $(el).attr(attr, `/_stitch/asset?url=${encodeURIComponent(url)}`);\n }\n };\n\n $('img').each((_, el) => process(el, 'src'));\n $('link[rel=\"stylesheet\"]').each((_, el) => process(el, 'href'));\n $('script').each((_, el) => process(el, 'src'));\n\n // Optimistic fetch\n await Promise.all(Array.from(assets).map(url => this.fetchAsset(url).catch(console.error)));\n\n return $.html();\n }\n\n /**\n * Maps common MIME types to file extensions.\n */\n private getExtensionFromContentType(contentType: string | undefined): string {\n if (!contentType) return '';\n\n // Extract the base MIME type (ignore charset and other params)\n const mimeType = contentType.split(';')[0]?.trim().toLowerCase();\n\n const mimeToExt: Record<string, string> = {\n // Stylesheets\n 'text/css': '.css',\n // JavaScript\n 'text/javascript': '.js',\n 'application/javascript': '.js',\n 'application/x-javascript': '.js',\n // Images\n 'image/png': '.png',\n 'image/jpeg': '.jpg',\n 'image/gif': '.gif',\n 'image/webp': '.webp',\n 'image/svg+xml': '.svg',\n 'image/x-icon': '.ico',\n 'image/vnd.microsoft.icon': '.ico',\n // Fonts\n 'font/woff': '.woff',\n 'font/woff2': '.woff2',\n 'font/ttf': '.ttf',\n 'font/otf': '.otf',\n 'application/font-woff': '.woff',\n 'application/font-woff2': '.woff2',\n // Other\n 'application/json': '.json',\n 'text/html': '.html',\n 'text/plain': '.txt',\n };\n\n return mimeToExt[mimeType || ''] || '';\n }\n\n async rewriteHtmlForBuild(html: string): Promise<{ html: string; assets: { url: string; filename: string }[] }> {\n const $ = cheerio.load(html);\n const assetUrls: string[] = [];\n\n // Collect all asset URLs\n const collectUrl = (el: any, attr: string) => {\n const url = $(el).attr(attr);\n if (url && url.startsWith('http')) {\n assetUrls.push(url);\n }\n };\n\n $('img').each((_, el) => collectUrl(el, 'src'));\n $('link[rel=\"stylesheet\"]').each((_, el) => collectUrl(el, 'href'));\n $('script').each((_, el) => collectUrl(el, 'src'));\n\n // Fetch all assets to get Content-Type headers\n const urlToFilename = new Map<string, string>();\n\n await Promise.all(assetUrls.map(async (url) => {\n try {\n const result = await this.fetchAsset(url);\n if (!result) return; // Skip failed assets\n\n const { stream, contentType } = result;\n stream.destroy(); // Close the stream as we only need the content type here\n\n const hash = this.getHash(url);\n\n // Try URL extension first, fall back to Content-Type\n const urlObj = new URL(url);\n let ext = path.extname(urlObj.pathname);\n\n if (!ext) {\n ext = this.getExtensionFromContentType(contentType);\n }\n\n // If still no extension, use a sensible default based on element type\n // (handled below when rewriting)\n const filename = `${hash}${ext}`;\n urlToFilename.set(url, filename);\n } catch (e) {\n // Skip failed assets\n }\n }));\n\n // Rewrite URLs in HTML\n const assets: { url: string; filename: string }[] = [];\n\n const rewriteUrl = (el: any, attr: string, defaultExt: string) => {\n const url = $(el).attr(attr);\n if (url && url.startsWith('http')) {\n let filename = urlToFilename.get(url);\n if (!filename) {\n // Fallback if fetch failed\n const hash = this.getHash(url);\n filename = `${hash}${defaultExt}`;\n }\n $(el).attr(attr, `/assets/${filename}`);\n assets.push({ url, filename });\n }\n };\n\n $('img').each((_, el) => rewriteUrl(el, 'src', '.png'));\n $('link[rel=\"stylesheet\"]').each((_, el) => rewriteUrl(el, 'href', '.css'));\n $('script').each((_, el) => {\n rewriteUrl(el, 'src', '.js');\n // Add is:inline for Astro compatibility - prevents bundling of public/ assets\n if ($(el).attr('src')?.startsWith('/assets/')) {\n $(el).attr('is:inline', '');\n }\n });\n\n // Use AST-based escaping for curly braces\n // This escapes {...} only in text nodes that are NOT inside <script> or <style> elements\n // Making the output compatible with Astro, React, and other JSX-like frameworks\n\n // Remove DOCTYPE declaration using Cheerio's DOM API (more robust than regex)\n // Astro adds DOCTYPE during build, and the Astro compiler's serialize function\n // incorrectly outputs just \"html\" for DOCTYPE nodes\n $.root().contents().filter((_, el) => el.type === 'directive' && el.name === '!doctype').remove();\n\n let outputHtml = $.html();\n\n // Add Astro frontmatter fences first to make it parseable\n const astroContent = `---\\n---\\n${outputHtml}`;\n\n // Parse with Astro compiler to get AST\n const parseResult = await parse(astroContent, { position: false });\n\n // Elements whose content should not be escaped\n const skipElements = new Set(['script', 'style']);\n\n // Recursive function to walk and escape expression nodes\n // The Astro parser converts {foo} into expression nodes, so we need to\n // convert them back to escaped text like {'{'}foo{'}'}\n const escapeExpressions = (node: any, insideSkipElement: boolean): void => {\n // Check if this is a skip element\n const isSkipElement = is.element(node) && skipElements.has(node.name.toLowerCase());\n const shouldSkip = insideSkipElement || isSkipElement;\n\n // Process children and convert expression nodes to escaped text\n if (is.parent(node) && !shouldSkip) {\n const newChildren: any[] = [];\n for (const child of node.children) {\n // Convert expression nodes to escaped text\n if (child.type === 'expression') {\n // Get the expression content\n const exprContent = child.children\n ?.filter((c: any) => is.text(c))\n .map((c: any) => c.value)\n .join('') || '';\n // Replace with escaped text: {'{'}content{'}'}\n newChildren.push({\n type: 'text',\n value: `{'{'}${exprContent}{'}'}`\n });\n } else {\n newChildren.push(child);\n escapeExpressions(child, shouldSkip);\n }\n }\n node.children = newChildren;\n } else if (is.parent(node)) {\n // Inside skip element, just recurse without escaping\n for (const child of node.children) {\n escapeExpressions(child, shouldSkip);\n }\n }\n };\n\n // Start walking from the root\n escapeExpressions(parseResult.ast, false);\n\n const astroOutput = serialize(parseResult.ast);\n\n return { html: astroOutput, assets };\n }\n\n async copyAssetTo(url: string, destPath: string): Promise<boolean> {\n await this.init();\n const hash = this.getHash(url);\n const cachePath = path.join(this.cacheDir, hash);\n\n if (await fs.pathExists(cachePath)) {\n await fs.copy(cachePath, destPath);\n return true;\n } else {\n // Try to fetch if not cached\n const result = await this.fetchAsset(url);\n if (!result) {\n console.warn(`Skipping asset copy, fetch failed: ${url}`);\n return false;\n }\n await fs.copy(cachePath, destPath);\n return true;\n }\n }\n}\n",
6
+ "import { type Plugin, type ViteDevServer } from 'vite';\nimport { AssetGateway } from '../../AssetGateway.js';\nimport { IncomingMessage, ServerResponse } from 'http';\n\nexport interface VirtualContentOptions {\n assetGateway: AssetGateway;\n htmlMap: Map<string, string>;\n}\n\n// Virtual module ID for the navigation client\nconst VIRTUAL_NAV_ID = 'virtual:stitch-nav';\nconst RESOLVED_VIRTUAL_NAV_ID = '\\0' + VIRTUAL_NAV_ID;\n\nexport function virtualContent({ assetGateway, htmlMap }: VirtualContentOptions): Plugin {\n return {\n name: 'stitch-virtual-content',\n\n // Resolve virtual module\n resolveId(id) {\n if (id === VIRTUAL_NAV_ID) {\n return RESOLVED_VIRTUAL_NAV_ID;\n }\n },\n\n // Load virtual module content\n load(id) {\n if (id === RESOLVED_VIRTUAL_NAV_ID) {\n return `\n if (import.meta.hot) {\n // Register navigation handler\n import.meta.hot.on('stitch:navigate', ({ url }) => {\n window.location.href = url;\n });\n \n // Log connection status for debugging\n import.meta.hot.on('vite:ws:connect', () => {\n console.log('[stitch] HMR connected');\n });\n \n console.log('[stitch] Navigation handler registered');\n } else {\n console.warn('[stitch] HMR not available');\n }\n `;\n }\n },\n\n configureServer(server: ViteDevServer) {\n server.middlewares.use(async (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n if (!req.url) return next();\n\n // TODO: Consider a better configuration to support hosted URLs\n // such as GitHub Codespaces and other cloud IDEs\n const url = new URL(req.url, 'http://localhost');\n\n // Asset Proxy\n if (url.pathname === '/_stitch/asset') {\n const assetUrl = url.searchParams.get('url');\n if (!assetUrl) {\n res.statusCode = 400;\n res.end('Missing url parameter');\n return;\n }\n\n try {\n const result = await assetGateway.fetchAsset(assetUrl);\n if (!result) {\n res.statusCode = 404;\n res.end('Asset not found');\n return;\n }\n\n const { stream, contentType } = result;\n\n // Infer correct Content-Type from URL extension when cached as octet-stream\n let effectiveContentType = contentType;\n if (!contentType || contentType.includes('application/octet-stream')) {\n const fontExtMap: Record<string, string> = {\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n };\n try {\n const ext = new URL(assetUrl).pathname.match(/\\.[^.]+$/)?.[0]?.toLowerCase();\n if (ext && fontExtMap[ext]) {\n effectiveContentType = fontExtMap[ext];\n }\n } catch { /* leave as-is */ }\n }\n\n if (effectiveContentType) {\n res.setHeader('Content-Type', effectiveContentType);\n }\n\n const isCss = effectiveContentType?.includes('text/css');\n // CSS is transformed (url rewriting), so use no-cache to force\n // revalidation. Other assets (fonts, images, JS) are static.\n res.setHeader('Cache-Control', isCss ? 'no-cache' : 'public, max-age=31536000');\n\n if (isCss) {\n // Buffer CSS to rewrite url() references for sub-resources (fonts, images)\n const chunks: Buffer[] = [];\n stream.on('data', (chunk: Buffer) => chunks.push(chunk));\n stream.on('end', async () => {\n const css = Buffer.concat(chunks).toString('utf-8');\n const rewritten = await assetGateway.rewriteCssUrls(css, assetUrl);\n res.end(rewritten);\n });\n stream.on('error', (err) => {\n console.error('CSS stream error:', err);\n res.statusCode = 500;\n res.end('Internal Server Error');\n });\n } else {\n stream.pipe(res);\n }\n } catch (error) {\n console.error('Asset proxy error:', error);\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n return;\n }\n\n // Virtual Routes\n const content = htmlMap.get(url.pathname);\n if (content) {\n try {\n // Transform HTML (injects Vite client, etc.)\n const transformed = await server.transformIndexHtml(req.url, content);\n res.setHeader('Content-Type', 'text/html');\n res.end(transformed);\n } catch (e) {\n console.error('Transform error:', e);\n next();\n }\n return;\n }\n\n // Handle preview URLs that aren't hydrated yet\n // Return a loading page that auto-refreshes\n if (url.pathname.startsWith('/_preview/')) {\n const loadingHtml = `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"refresh\" content=\"1\">\n <title>Loading...</title>\n <style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100vh;\n margin: 0;\n background: #1a1a1a;\n color: #fff;\n }\n .loader {\n text-align: center;\n }\n .spinner {\n width: 40px;\n height: 40px;\n border: 3px solid #333;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n </style>\n</head>\n<body>\n <div class=\"loader\">\n <div class=\"spinner\"></div>\n <p>Loading preview...</p>\n </div>\n</body>\n</html>`;\n res.setHeader('Content-Type', 'text/html');\n res.end(loadingHtml);\n return;\n }\n\n next();\n });\n },\n\n async transformIndexHtml(html) {\n // Rewrite assets for preview\n const rewritten = await assetGateway.rewriteHtmlForPreview(html);\n\n // Inject import for the virtual navigation module\n // Use /@id/ prefix which is how Vite exposes virtual modules to the browser\n const script = `<script type=\"module\" src=\"/@id/${VIRTUAL_NAV_ID}\"></script>`;\n\n // Insert script in head for proper loading order\n if (rewritten.includes('</head>')) {\n return rewritten.replace('</head>', script + '</head>');\n }\n\n // Fallback: append if no head tag\n return rewritten + script;\n }\n };\n}\n",
7
+ "import type { ViteDevServer } from 'vite';\nimport { AssetGateway } from '../AssetGateway.js';\nimport { virtualContent } from './plugins/virtualContent.js';\n\nexport class StitchViteServer {\n private server: ViteDevServer | null = null;\n private htmlMap = new Map<string, string>();\n public assetGateway: AssetGateway;\n\n constructor(projectRoot: string = process.cwd(), assetGateway?: AssetGateway) {\n this.assetGateway = assetGateway || new AssetGateway(projectRoot);\n }\n\n async start(port: number = 3000): Promise<string> {\n // Dynamic import to avoid loading vite at module evaluation time.\n // Vite's module initialization (testCaseInsensitiveFS) can fail in\n // CI environments and makes the class unmockable in tests.\n const { createServer } = await import('vite');\n\n this.server = await createServer({\n configFile: false,\n root: process.cwd(),\n server: {\n port,\n middlewareMode: false,\n },\n appType: 'custom',\n plugins: [\n virtualContent({\n assetGateway: this.assetGateway,\n htmlMap: this.htmlMap\n })\n ],\n logLevel: 'silent'\n });\n\n await this.server.listen();\n\n const address = this.server.httpServer?.address();\n if (address && typeof address === 'object') {\n return `http://localhost:${address.port}`;\n }\n return `http://localhost:${port}`;\n }\n\n async stop() {\n if (this.server) {\n await this.server.close();\n this.server = null;\n }\n }\n\n mount(route: string, html: string) {\n this.htmlMap.set(route, html);\n if (this.server) {\n this.server.ws.send({ type: 'full-reload', path: route });\n }\n }\n\n navigate(url: string) {\n if (this.server) {\n this.server.ws.send('stitch:navigate', { url });\n }\n }\n}\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "D84DFB3FB35F744464756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ StitchViteServer
3
+ } from "./chunk-86gwwcyr.js";
4
+ import"./chunk-7vdj1qwb.js";
5
+ import {
6
+ downloadText
7
+ } from "./chunk-fkzq5m59.js";
8
+ import {
9
+ pLimit
10
+ } from "./chunk-a5xra9jn.js";
11
+ import"./chunk-9wyra8hs.js";
12
+
13
+ // src/commands/serve/json-server/handler.ts
14
+ class JsonServerHandler {
15
+ client;
16
+ downloadHtml;
17
+ constructor(client, downloadHtml = downloadText) {
18
+ this.client = client;
19
+ this.downloadHtml = downloadHtml;
20
+ }
21
+ async execute(input) {
22
+ let sdkScreens;
23
+ try {
24
+ const project = this.client.project(input.projectId);
25
+ sdkScreens = await project.screens();
26
+ } catch (e) {
27
+ return {
28
+ success: false,
29
+ error: { code: "SCREENS_FETCH_FAILED", message: e.message, recoverable: false }
30
+ };
31
+ }
32
+ const server = new StitchViteServer;
33
+ let baseUrl;
34
+ try {
35
+ baseUrl = await server.start(0);
36
+ } catch (e) {
37
+ return {
38
+ success: false,
39
+ error: { code: "SERVER_START_FAILED", message: e.message, recoverable: false }
40
+ };
41
+ }
42
+ const limit = pLimit(3);
43
+ const screens = [];
44
+ await Promise.all(sdkScreens.map((s) => limit(async () => {
45
+ try {
46
+ const codeUrl = await s.getHtml();
47
+ if (codeUrl) {
48
+ const html = await this.downloadHtml(codeUrl);
49
+ server.mount(`/screens/${s.screenId}`, html);
50
+ }
51
+ screens.push({
52
+ screenId: s.screenId,
53
+ title: s.title ?? s.screenId,
54
+ url: `${baseUrl}/screens/${s.screenId}`
55
+ });
56
+ } catch {}
57
+ })));
58
+ screens.sort((a, b) => a.title.localeCompare(b.title));
59
+ return { success: true, url: baseUrl, screens };
60
+ }
61
+ }
62
+ export {
63
+ JsonServerHandler
64
+ };
65
+
66
+ //# debugId=685E77DF481CF8C964756E2164756E21