@fragments-sdk/cli 0.7.1 → 0.7.3

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 (68) hide show
  1. package/LICENSE +77 -14
  2. package/dist/bin.js +22 -18
  3. package/dist/bin.js.map +1 -1
  4. package/dist/chunk-D34Q6A7S.js +266 -0
  5. package/dist/chunk-D34Q6A7S.js.map +1 -0
  6. package/dist/chunk-EKLMXTWU.js +80 -0
  7. package/dist/chunk-EKLMXTWU.js.map +1 -0
  8. package/dist/{chunk-GHYYFAQN.js → chunk-P33AKQJW.js} +1 -76
  9. package/dist/chunk-P33AKQJW.js.map +1 -0
  10. package/dist/{chunk-U6VTHBNI.js → chunk-QPY4DUFB.js} +177 -46
  11. package/dist/chunk-QPY4DUFB.js.map +1 -0
  12. package/dist/{chunk-32VIEOQY.js → chunk-R2YH7NLN.js} +9 -7
  13. package/dist/{chunk-32VIEOQY.js.map → chunk-R2YH7NLN.js.map} +1 -1
  14. package/dist/{chunk-5ITIP3ES.js → chunk-R6IZZSE7.js} +44 -278
  15. package/dist/chunk-R6IZZSE7.js.map +1 -0
  16. package/dist/{chunk-DQHWLAUV.js → chunk-TOIE7VXF.js} +2 -2
  17. package/dist/{chunk-GCZMFLDI.js → chunk-UXLGIGSX.js} +60 -3
  18. package/dist/chunk-UXLGIGSX.js.map +1 -0
  19. package/dist/{chunk-GKX2HPZ6.js → chunk-YMPGYEWK.js} +9 -3
  20. package/dist/chunk-YMPGYEWK.js.map +1 -0
  21. package/dist/chunk-Z7EY4VHE.js +50 -0
  22. package/dist/{core-SFHPYR5H.js → core-3NMNCLFW.js} +8 -5
  23. package/dist/discovery-AKGA6CJD.js +28 -0
  24. package/dist/{generate-54GJAWUY.js → generate-JAUEHKK7.js} +7 -4
  25. package/dist/{generate-54GJAWUY.js.map → generate-JAUEHKK7.js.map} +1 -1
  26. package/dist/index.js +15 -11
  27. package/dist/index.js.map +1 -1
  28. package/dist/{init-EIM5WNMP.js → init-DZQOT54X.js} +6 -4
  29. package/dist/{init-EIM5WNMP.js.map → init-DZQOT54X.js.map} +1 -1
  30. package/dist/mcp-bin.js +5 -3
  31. package/dist/mcp-bin.js.map +1 -1
  32. package/dist/sass.node-4XJK6YBF.js +130708 -0
  33. package/dist/sass.node-4XJK6YBF.js.map +1 -0
  34. package/dist/scan-OJRCVKK2.js +15 -0
  35. package/dist/{service-ED2LNCTU.js → service-CFFBHW4X.js} +6 -4
  36. package/dist/service-CFFBHW4X.js.map +1 -0
  37. package/dist/{static-viewer-Q4F4QP5M.js → static-viewer-VA2JXSCX.js} +6 -4
  38. package/dist/static-viewer-VA2JXSCX.js.map +1 -0
  39. package/dist/{test-6VN2DA3S.js → test-O7DZNKDC.js} +8 -4
  40. package/dist/{test-6VN2DA3S.js.map → test-O7DZNKDC.js.map} +1 -1
  41. package/dist/{tokens-P2B7ZAM3.js → tokens-N7THFD6J.js} +10 -7
  42. package/dist/{tokens-P2B7ZAM3.js.map → tokens-N7THFD6J.js.map} +1 -1
  43. package/dist/{viewer-GM7IQPPB.js → viewer-QTR7QJMM.js} +390 -25
  44. package/dist/viewer-QTR7QJMM.js.map +1 -0
  45. package/package.json +13 -2
  46. package/src/build.ts +60 -6
  47. package/src/commands/graph.ts +2 -2
  48. package/src/core/__tests__/token-resolver.test.ts +82 -0
  49. package/src/core/loader.ts +0 -3
  50. package/src/core/parser.ts +41 -1
  51. package/src/core/token-parser.ts +111 -1
  52. package/src/core/token-resolver.ts +155 -0
  53. package/src/service/__tests__/patch-generator.test.ts +2 -2
  54. package/src/service/patch-generator.ts +8 -1
  55. package/src/viewer/render-utils.ts +141 -0
  56. package/src/viewer/vite-plugin.ts +381 -23
  57. package/dist/chunk-5ITIP3ES.js.map +0 -1
  58. package/dist/chunk-GCZMFLDI.js.map +0 -1
  59. package/dist/chunk-GHYYFAQN.js.map +0 -1
  60. package/dist/chunk-GKX2HPZ6.js.map +0 -1
  61. package/dist/chunk-U6VTHBNI.js.map +0 -1
  62. package/dist/scan-KQBKUS64.js +0 -12
  63. package/dist/viewer-GM7IQPPB.js.map +0 -1
  64. /package/dist/{chunk-DQHWLAUV.js.map → chunk-TOIE7VXF.js.map} +0 -0
  65. /package/dist/{core-SFHPYR5H.js.map → chunk-Z7EY4VHE.js.map} +0 -0
  66. /package/dist/{scan-KQBKUS64.js.map → core-3NMNCLFW.js.map} +0 -0
  67. /package/dist/{service-ED2LNCTU.js.map → discovery-AKGA6CJD.js.map} +0 -0
  68. /package/dist/{static-viewer-Q4F4QP5M.js.map → scan-OJRCVKK2.js.map} +0 -0
@@ -1,18 +1,22 @@
1
1
  import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
2
  import {
3
- discoverFragmentFiles,
4
- discoverInstalledFragments,
5
3
  findPreviewConfigPath,
6
4
  findStorybookDir,
7
5
  generatePreviewModule,
8
6
  loadConfig
9
- } from "./chunk-5ITIP3ES.js";
7
+ } from "./chunk-R6IZZSE7.js";
8
+ import {
9
+ discoverFragmentFiles,
10
+ discoverInstalledFragments
11
+ } from "./chunk-D34Q6A7S.js";
10
12
  import {
11
13
  generateContext
12
- } from "./chunk-GCZMFLDI.js";
14
+ } from "./chunk-UXLGIGSX.js";
15
+ import "./chunk-P33AKQJW.js";
13
16
  import {
14
17
  BRAND
15
- } from "./chunk-GHYYFAQN.js";
18
+ } from "./chunk-EKLMXTWU.js";
19
+ import "./chunk-Z7EY4VHE.js";
16
20
 
17
21
  // src/viewer/server.ts
18
22
  import {
@@ -123,6 +127,122 @@ async function render() {
123
127
  }
124
128
  }
125
129
 
130
+ render();
131
+ `;
132
+ }
133
+ function generateVariantRenderScript(fragmentPath, componentName, variantName) {
134
+ const variantNameLower = JSON.stringify(variantName.toLowerCase());
135
+ return `
136
+ import React from "react";
137
+ import { createRoot } from "react-dom/client";
138
+
139
+ async function render() {
140
+ const root = document.getElementById("render-root");
141
+
142
+ try {
143
+ const fragmentModule = await import("${fragmentPath}");
144
+ const fragment = fragmentModule.default;
145
+
146
+ if (!fragment || !fragment.variants || fragment.variants.length === 0) {
147
+ throw new Error("Fragment has no variants");
148
+ }
149
+
150
+ const variant = fragment.variants.find(
151
+ v => v.name.toLowerCase() === ${variantNameLower}
152
+ );
153
+
154
+ if (!variant) {
155
+ const available = fragment.variants.map(v => v.name).join(", ");
156
+ throw new Error("Variant '" + ${JSON.stringify(variantName)} + "' not found. Available: " + available);
157
+ }
158
+
159
+ const element = variant.render();
160
+
161
+ const reactRoot = createRoot(root);
162
+ reactRoot.render(element);
163
+
164
+ requestAnimationFrame(() => {
165
+ requestAnimationFrame(() => {
166
+ root.classList.add("ready");
167
+ window.__RENDER_READY__ = true;
168
+ });
169
+ });
170
+ } catch (error) {
171
+ console.error("Render error:", error);
172
+ root.innerHTML = \`
173
+ <div class="render-error">
174
+ <strong>Render Error</strong>
175
+ <pre>\${error.message}</pre>
176
+ </div>
177
+ \`;
178
+ root.classList.add("ready");
179
+ window.__RENDER_READY__ = true;
180
+ window.__RENDER_ERROR__ = error.message;
181
+ }
182
+ }
183
+
184
+ render();
185
+ `;
186
+ }
187
+ function generateA11yRenderScript(fragmentPath, componentName, variantName) {
188
+ const variantLookup = variantName ? `
189
+ const variant = fragment.variants?.find(
190
+ v => v.name.toLowerCase() === ${JSON.stringify(variantName.toLowerCase())}
191
+ );
192
+ if (!variant) {
193
+ throw new Error("Variant '${variantName}' not found");
194
+ }
195
+ element = variant.render();` : `
196
+ element = React.createElement(fragment.component, {});`;
197
+ return `
198
+ import React from "react";
199
+ import { createRoot } from "react-dom/client";
200
+ import axe from "axe-core";
201
+
202
+ async function render() {
203
+ const root = document.getElementById("render-root");
204
+
205
+ try {
206
+ const fragmentModule = await import("${fragmentPath}");
207
+ const fragment = fragmentModule.default;
208
+
209
+ if (!fragment || !fragment.component) {
210
+ throw new Error("Fragment does not export a component");
211
+ }
212
+
213
+ let element;
214
+ ${variantLookup}
215
+
216
+ const reactRoot = createRoot(root);
217
+ reactRoot.render(element);
218
+
219
+ // Wait for React to flush rendering
220
+ await new Promise(resolve => {
221
+ requestAnimationFrame(() => {
222
+ requestAnimationFrame(resolve);
223
+ });
224
+ });
225
+
226
+ // Additional settle time for CSS/animations
227
+ await new Promise(resolve => setTimeout(resolve, 100));
228
+
229
+ // Run axe-core accessibility audit
230
+ const results = await axe.run('#render-root', {
231
+ runOnly: {
232
+ type: 'tag',
233
+ values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice'],
234
+ },
235
+ });
236
+
237
+ window.__AXE_RESULTS__ = results;
238
+ window.__RENDER_READY__ = true;
239
+ } catch (error) {
240
+ console.error("A11y audit error:", error);
241
+ window.__AXE_ERROR__ = error.message;
242
+ window.__RENDER_READY__ = true;
243
+ }
244
+ }
245
+
126
246
  render();
127
247
  `;
128
248
  }
@@ -240,7 +360,7 @@ var sharedRenderPool = null;
240
360
  var browserPoolModule = null;
241
361
  async function getSharedRenderPool() {
242
362
  if (!browserPoolModule) {
243
- browserPoolModule = await import("./service-ED2LNCTU.js");
363
+ browserPoolModule = await import("./service-CFFBHW4X.js");
244
364
  }
245
365
  if (!sharedRenderPool) {
246
366
  sharedRenderPool = new browserPoolModule.BrowserPool({
@@ -302,7 +422,7 @@ function fragmentsPlugin(options) {
302
422
  if (req.url === "/fragments/render" && req.method === "POST") {
303
423
  try {
304
424
  const body = await parseJsonBody(req);
305
- const { component, props = {}, viewport } = body;
425
+ const { component, props = {}, viewport, variant } = body;
306
426
  if (!component) {
307
427
  res.writeHead(400, { "Content-Type": "application/json" });
308
428
  res.end(
@@ -337,7 +457,11 @@ function fragmentsPlugin(options) {
337
457
  );
338
458
  return;
339
459
  }
340
- const renderScript = generateRenderScript(
460
+ const renderScript = variant ? generateVariantRenderScript(
461
+ fragmentFile.absolutePath,
462
+ fragmentInfo.name,
463
+ variant
464
+ ) : generateRenderScript(
341
465
  fragmentFile.absolutePath,
342
466
  fragmentInfo.name,
343
467
  props
@@ -469,7 +593,7 @@ function fragmentsPlugin(options) {
469
593
  const address = _server.httpServer?.address();
470
594
  const port = typeof address === "object" && address ? address.port : 6006;
471
595
  const renderViewport = viewport || { width: 800, height: 600 };
472
- const { FigmaClient, bufferToBase64Url } = await import("./service-ED2LNCTU.js");
596
+ const { FigmaClient, bufferToBase64Url } = await import("./service-CFFBHW4X.js");
473
597
  const figmaClient = new FigmaClient({
474
598
  accessToken: figmaToken
475
599
  });
@@ -560,7 +684,7 @@ function fragmentsPlugin(options) {
560
684
  );
561
685
  return;
562
686
  }
563
- const { FigmaClient } = await import("./service-ED2LNCTU.js");
687
+ const { FigmaClient } = await import("./service-CFFBHW4X.js");
564
688
  const figmaClient = new FigmaClient({ accessToken: figmaToken });
565
689
  const { fileKey, nodeId } = figmaClient.parseUrl(figmaUrl);
566
690
  const figmaDesignProps = await figmaClient.getNodeProperties(
@@ -601,7 +725,7 @@ function fragmentsPlugin(options) {
601
725
  }));
602
726
  return;
603
727
  }
604
- const { getSharedTokenRegistry } = await import("./service-ED2LNCTU.js");
728
+ const { getSharedTokenRegistry } = await import("./service-CFFBHW4X.js");
605
729
  const registry = getSharedTokenRegistry();
606
730
  if (!registry.isInitialized()) {
607
731
  await registry.initialize(config.tokens, projectRoot);
@@ -661,7 +785,7 @@ function fragmentsPlugin(options) {
661
785
  }));
662
786
  return;
663
787
  }
664
- const { getSharedTokenRegistry } = await import("./service-ED2LNCTU.js");
788
+ const { getSharedTokenRegistry } = await import("./service-CFFBHW4X.js");
665
789
  const registry = getSharedTokenRegistry();
666
790
  if (!registry.isInitialized()) {
667
791
  await registry.initialize(config.tokens, projectRoot);
@@ -723,7 +847,7 @@ function fragmentsPlugin(options) {
723
847
  res.end(JSON.stringify({ error: "Could not resolve fragment file path" }));
724
848
  return;
725
849
  }
726
- const { getSharedTokenRegistry } = await import("./service-ED2LNCTU.js");
850
+ const { getSharedTokenRegistry } = await import("./service-CFFBHW4X.js");
727
851
  const registry = getSharedTokenRegistry();
728
852
  if (!registry.isInitialized()) {
729
853
  await registry.initialize(config.tokens, projectRoot);
@@ -849,7 +973,7 @@ function fragmentsPlugin(options) {
849
973
  }
850
974
  const { writeFile, mkdir } = await import("fs/promises");
851
975
  const { join: join2 } = await import("path");
852
- const { BRAND: BRAND2 } = await import("./core-SFHPYR5H.js");
976
+ const { BRAND: BRAND2 } = await import("./core-3NMNCLFW.js");
853
977
  const fragmentsDir = join2(projectRoot, BRAND2.dataDir, BRAND2.componentsDir);
854
978
  await mkdir(fragmentsDir, { recursive: true });
855
979
  const fragmentPath = join2(
@@ -884,12 +1008,30 @@ function fragmentsPlugin(options) {
884
1008
  return;
885
1009
  }
886
1010
  if (!config.tokens || !config.tokens.include || config.tokens.include.length === 0) {
887
- res.writeHead(400, { "Content-Type": "application/json" });
888
- res.end(JSON.stringify({
889
- error: "No token configuration found",
890
- suggestion: "Add 'tokens' config to fragments.config.ts to enable fix generation"
891
- }));
892
- return;
1011
+ try {
1012
+ const { discoverTokenFiles } = await import("./discovery-AKGA6CJD.js");
1013
+ const discovered = await discoverTokenFiles(projectRoot);
1014
+ if (discovered.length > 0) {
1015
+ config.tokens = {
1016
+ ...config.tokens,
1017
+ include: discovered.map((f) => f.relativePath)
1018
+ };
1019
+ } else {
1020
+ res.writeHead(400, { "Content-Type": "application/json" });
1021
+ res.end(JSON.stringify({
1022
+ error: "No token files found",
1023
+ suggestion: "Add 'tokens' config to fragments.config.ts or add token files matching default patterns (_variables.scss, tokens.scss, etc.)"
1024
+ }));
1025
+ return;
1026
+ }
1027
+ } catch {
1028
+ res.writeHead(400, { "Content-Type": "application/json" });
1029
+ res.end(JSON.stringify({
1030
+ error: "No token configuration found and auto-discovery failed",
1031
+ suggestion: "Add 'tokens' config to fragments.config.ts to enable fix generation"
1032
+ }));
1033
+ return;
1034
+ }
893
1035
  }
894
1036
  const loadedFragments = await loadFragmentsForRender(fragmentFiles, projectRoot);
895
1037
  const fragmentInfo = findFragmentByName(component, loadedFragments);
@@ -904,7 +1046,7 @@ function fragmentsPlugin(options) {
904
1046
  const {
905
1047
  getSharedTokenRegistry,
906
1048
  generateTokenPatches
907
- } = await import("./service-ED2LNCTU.js");
1049
+ } = await import("./service-CFFBHW4X.js");
908
1050
  const registry = getSharedTokenRegistry();
909
1051
  if (!registry.isInitialized()) {
910
1052
  await registry.initialize(config.tokens, projectRoot);
@@ -913,10 +1055,56 @@ function fragmentsPlugin(options) {
913
1055
  (f) => f.relativePath === fragmentInfo.path
914
1056
  );
915
1057
  const sourceFile = fragmentFile?.relativePath || `${component}.tsx`;
1058
+ let styleDiffs = [];
1059
+ if (fragmentFile) {
1060
+ try {
1061
+ const renderScript = generateRenderScript(
1062
+ fragmentFile.absolutePath,
1063
+ fragmentInfo.name,
1064
+ {}
1065
+ );
1066
+ const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2);
1067
+ pendingRenders.set(requestId, {
1068
+ script: renderScript,
1069
+ viewport: { width: 800, height: 600 }
1070
+ });
1071
+ const address = _server.httpServer?.address();
1072
+ const port = typeof address === "object" && address ? address.port : 6006;
1073
+ const { computedStyles } = await captureRenderWithStyles(
1074
+ `http://localhost:${port}/fragments/__render__/${requestId}`,
1075
+ { width: 800, height: 600 },
1076
+ true
1077
+ );
1078
+ pendingRenders.delete(requestId);
1079
+ if (computedStyles) {
1080
+ const tokenValues = /* @__PURE__ */ new Map();
1081
+ const allTokens = registry.getAllTokens();
1082
+ for (const t of allTokens) {
1083
+ if (t.resolvedValue) {
1084
+ tokenValues.set(t.resolvedValue, t.name);
1085
+ }
1086
+ }
1087
+ for (const [prop, value] of Object.entries(computedStyles)) {
1088
+ if (!value || value === "transparent" || value === "rgba(0, 0, 0, 0)") continue;
1089
+ const matchesToken = tokenValues.has(value);
1090
+ if (!matchesToken) {
1091
+ styleDiffs.push({
1092
+ property: prop,
1093
+ figma: value,
1094
+ // Using rendered as "expected" since we have no Figma
1095
+ rendered: value,
1096
+ match: false
1097
+ });
1098
+ }
1099
+ }
1100
+ }
1101
+ } catch (renderErr) {
1102
+ console.warn("[Fragments] Could not render for style extraction:", renderErr);
1103
+ }
1104
+ }
916
1105
  const result = generateTokenPatches(
917
1106
  component,
918
- [],
919
- // Would be populated by actual style diffs
1107
+ styleDiffs,
920
1108
  registry,
921
1109
  { sourceFile }
922
1110
  );
@@ -936,6 +1124,148 @@ function fragmentsPlugin(options) {
936
1124
  }
937
1125
  return;
938
1126
  }
1127
+ if (req.url === "/fragments/a11y" && req.method === "POST") {
1128
+ try {
1129
+ const body = await parseJsonBody(req);
1130
+ const { component, variant: variantName, standard = "AA" } = body;
1131
+ if (!component) {
1132
+ res.writeHead(400, { "Content-Type": "application/json" });
1133
+ res.end(
1134
+ JSON.stringify({ error: "Missing required field: component" })
1135
+ );
1136
+ return;
1137
+ }
1138
+ const loadedFragments = await loadFragmentsForRender(
1139
+ fragmentFiles,
1140
+ projectRoot
1141
+ );
1142
+ const fragmentInfo = findFragmentByName(component, loadedFragments);
1143
+ if (!fragmentInfo) {
1144
+ const available = getAvailableComponents(loadedFragments);
1145
+ res.writeHead(400, { "Content-Type": "application/json" });
1146
+ res.end(
1147
+ JSON.stringify({
1148
+ error: `Component '${component}' not found. Available: ${available.join(", ")}`
1149
+ })
1150
+ );
1151
+ return;
1152
+ }
1153
+ const fragmentFile = fragmentFiles.find(
1154
+ (f) => f.relativePath === fragmentInfo.path
1155
+ );
1156
+ if (!fragmentFile) {
1157
+ res.writeHead(500, { "Content-Type": "application/json" });
1158
+ res.end(
1159
+ JSON.stringify({ error: "Could not resolve fragment file path" })
1160
+ );
1161
+ return;
1162
+ }
1163
+ const variantNames = [];
1164
+ if (variantName) {
1165
+ variantNames.push(variantName);
1166
+ } else {
1167
+ const fullData = await loadFullFragmentData(projectRoot);
1168
+ const fragmentData = fullData ? Object.values(fullData.fragments).find(
1169
+ (f) => f.meta.name.toLowerCase() === component.toLowerCase()
1170
+ ) : null;
1171
+ if (fragmentData && fragmentData.variants?.length > 0) {
1172
+ for (const v of fragmentData.variants) {
1173
+ variantNames.push(v.name);
1174
+ }
1175
+ } else {
1176
+ variantNames.push("Default");
1177
+ }
1178
+ }
1179
+ const address = _server.httpServer?.address();
1180
+ const port = typeof address === "object" && address ? address.port : 6006;
1181
+ const results = [];
1182
+ for (const vName of variantNames) {
1183
+ const a11yScript = generateA11yRenderScript(
1184
+ fragmentFile.absolutePath,
1185
+ fragmentInfo.name,
1186
+ vName === "Default" && !variantName ? void 0 : vName
1187
+ );
1188
+ const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2);
1189
+ pendingRenders.set(requestId, {
1190
+ script: a11yScript,
1191
+ viewport: { width: 800, height: 600 }
1192
+ });
1193
+ try {
1194
+ const auditResult = await captureA11yAudit(
1195
+ `http://localhost:${port}/fragments/__render__/${requestId}`,
1196
+ { width: 800, height: 600 }
1197
+ );
1198
+ let critical = 0;
1199
+ let serious = 0;
1200
+ let moderate = 0;
1201
+ let minor = 0;
1202
+ for (const violation of auditResult.violations ?? []) {
1203
+ switch (violation.impact) {
1204
+ case "critical":
1205
+ critical++;
1206
+ break;
1207
+ case "serious":
1208
+ serious++;
1209
+ break;
1210
+ case "moderate":
1211
+ moderate++;
1212
+ break;
1213
+ case "minor":
1214
+ minor++;
1215
+ break;
1216
+ }
1217
+ }
1218
+ results.push({
1219
+ variant: vName,
1220
+ violations: auditResult.violations?.length ?? 0,
1221
+ passes: auditResult.passes?.length ?? 0,
1222
+ incomplete: auditResult.incomplete?.length ?? 0,
1223
+ summary: {
1224
+ total: critical + serious + moderate + minor,
1225
+ critical,
1226
+ serious,
1227
+ moderate,
1228
+ minor
1229
+ },
1230
+ violationDetails: (auditResult.violations ?? []).map((v) => ({
1231
+ id: v.id,
1232
+ impact: v.impact,
1233
+ description: v.description,
1234
+ helpUrl: v.helpUrl,
1235
+ nodes: v.nodes.length
1236
+ }))
1237
+ });
1238
+ } catch (err) {
1239
+ results.push({
1240
+ variant: vName,
1241
+ violations: 0,
1242
+ passes: 0,
1243
+ incomplete: 0,
1244
+ summary: {
1245
+ total: 0,
1246
+ critical: 0,
1247
+ serious: 0,
1248
+ moderate: 0,
1249
+ minor: 0
1250
+ }
1251
+ });
1252
+ } finally {
1253
+ pendingRenders.delete(requestId);
1254
+ }
1255
+ }
1256
+ res.setHeader("Content-Type", "application/json");
1257
+ res.end(JSON.stringify({ results }));
1258
+ } catch (error) {
1259
+ console.error("[Fragments] Error running a11y audit:", error);
1260
+ res.writeHead(500, { "Content-Type": "application/json" });
1261
+ res.end(
1262
+ JSON.stringify({
1263
+ error: error instanceof Error ? error.message : "A11y audit failed"
1264
+ })
1265
+ );
1266
+ }
1267
+ return;
1268
+ }
939
1269
  if (req.url?.startsWith("/fragments/preview")) {
940
1270
  if (req.url === "/fragments/preview") {
941
1271
  res.writeHead(302, { Location: "/fragments/preview/" });
@@ -1356,6 +1686,41 @@ async function loadFragmentsForRender(fragmentFiles, configDir) {
1356
1686
  };
1357
1687
  });
1358
1688
  }
1689
+ async function loadFullFragmentData(configDir) {
1690
+ const { join: join2 } = await import("path");
1691
+ const fragmentsJsonPath = join2(configDir, BRAND.outFile);
1692
+ try {
1693
+ const content = await readFile(fragmentsJsonPath, "utf-8");
1694
+ return JSON.parse(content);
1695
+ } catch {
1696
+ return null;
1697
+ }
1698
+ }
1699
+ async function captureA11yAudit(url, viewport) {
1700
+ const { pool } = await getSharedRenderPool();
1701
+ const ctx = await pool.acquire();
1702
+ const page = await ctx.newPage();
1703
+ try {
1704
+ await page.setViewportSize(viewport);
1705
+ await page.goto(url, { waitUntil: "networkidle" });
1706
+ await page.waitForFunction(
1707
+ () => window.__RENDER_READY__ === true,
1708
+ { timeout: 15e3 }
1709
+ );
1710
+ const error = await page.evaluate(() => window.__AXE_ERROR__);
1711
+ if (error) {
1712
+ throw new Error(`A11y audit error: ${error}`);
1713
+ }
1714
+ const results = await page.evaluate(() => window.__AXE_RESULTS__);
1715
+ if (!results) {
1716
+ throw new Error("Axe results not available \u2014 axe-core may not be installed");
1717
+ }
1718
+ return results;
1719
+ } finally {
1720
+ await page.close();
1721
+ pool.release(ctx);
1722
+ }
1723
+ }
1359
1724
  async function serveRenderHTML(res, server, renderScript) {
1360
1725
  const viewerRoot2 = viewerAssetsRoot;
1361
1726
  try {
@@ -1566,7 +1931,7 @@ async function loadFullFragmentForCompare(_server, _fragmentFiles, componentName
1566
1931
  }
1567
1932
  }
1568
1933
  async function compareImages(image1Base64, image2Base64, threshold) {
1569
- const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-ED2LNCTU.js");
1934
+ const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-CFFBHW4X.js");
1570
1935
  const { PNG } = await import("pngjs");
1571
1936
  const buffer1 = base64UrlToBuffer(image1Base64);
1572
1937
  const buffer2 = base64UrlToBuffer(image2Base64);
@@ -1819,4 +2184,4 @@ export {
1819
2184
  createDevServer,
1820
2185
  fragmentsPlugin
1821
2186
  };
1822
- //# sourceMappingURL=viewer-GM7IQPPB.js.map
2187
+ //# sourceMappingURL=viewer-QTR7QJMM.js.map