canicode 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,7 +41,7 @@ var SEVERITY_LABELS = {
41
41
  suggestion: "Suggestion"
42
42
  };
43
43
 
44
- // src/contracts/rule.ts
44
+ // src/core/contracts/rule.ts
45
45
  z.object({
46
46
  id: z.string(),
47
47
  name: z.string(),
@@ -62,7 +62,7 @@ function supportsDepthWeight(category) {
62
62
  return DEPTH_WEIGHT_CATEGORIES.includes(category);
63
63
  }
64
64
 
65
- // src/rules/rule-config.ts
65
+ // src/core/rules/rule-config.ts
66
66
  var RULE_CONFIGS = {
67
67
  // ============================================
68
68
  // Layout (11 rules)
@@ -357,7 +357,7 @@ function getRuleOption(ruleId, optionKey, defaultValue) {
357
357
  return value ?? defaultValue;
358
358
  }
359
359
 
360
- // src/rules/rule-registry.ts
360
+ // src/core/rules/rule-registry.ts
361
361
  var RuleRegistry = class {
362
362
  rules = /* @__PURE__ */ new Map();
363
363
  /**
@@ -418,7 +418,7 @@ function defineRule(rule) {
418
418
  return rule;
419
419
  }
420
420
 
421
- // src/core/rule-engine.ts
421
+ // src/core/engine/rule-engine.ts
422
422
  function calculateMaxDepth(node, currentDepth = 0) {
423
423
  if (!node.children || node.children.length === 0) {
424
424
  return currentDepth;
@@ -591,7 +591,7 @@ function analyzeFile(file, options) {
591
591
  return engine.analyze(file);
592
592
  }
593
593
 
594
- // src/adapters/figma-client.ts
594
+ // src/core/adapters/figma-client.ts
595
595
  var FIGMA_API_BASE = "https://api.figma.com/v1";
596
596
  var FigmaClient = class _FigmaClient {
597
597
  token;
@@ -699,7 +699,7 @@ var FigmaClientError = class extends Error {
699
699
  }
700
700
  };
701
701
 
702
- // src/adapters/figma-transformer.ts
702
+ // src/core/adapters/figma-transformer.ts
703
703
  function transformFigmaResponse(fileKey, response) {
704
704
  return {
705
705
  fileKey,
@@ -818,7 +818,7 @@ function transformStyles(styles) {
818
818
  return result;
819
819
  }
820
820
 
821
- // src/adapters/figma-file-loader.ts
821
+ // src/core/adapters/figma-file-loader.ts
822
822
  async function loadFigmaFileFromJson(filePath) {
823
823
  const content = await readFile(filePath, "utf-8");
824
824
  const data = JSON.parse(content);
@@ -916,7 +916,7 @@ function getDeviceId() {
916
916
  return id;
917
917
  }
918
918
 
919
- // src/core/loader.ts
919
+ // src/core/engine/loader.ts
920
920
  function isFigmaUrl(input) {
921
921
  return input.includes("figma.com/");
922
922
  }
@@ -959,7 +959,7 @@ async function loadFromApi(fileKey, nodeId, token) {
959
959
  };
960
960
  }
961
961
 
962
- // src/adapters/figma-mcp-adapter.ts
962
+ // src/core/adapters/figma-mcp-adapter.ts
963
963
  var TAG_TYPE_MAP = {
964
964
  canvas: "CANVAS",
965
965
  frame: "FRAME",
@@ -1090,7 +1090,7 @@ function parseMcpMetadataXml(xml, fileKey, fileName) {
1090
1090
  };
1091
1091
  }
1092
1092
 
1093
- // src/core/design-data-parser.ts
1093
+ // src/core/engine/design-data-parser.ts
1094
1094
  function parseDesignData(data, fileKey, fileName) {
1095
1095
  const trimmed = data.trim();
1096
1096
  if (trimmed.startsWith("<")) {
@@ -1108,7 +1108,7 @@ function parseDesignData(data, fileKey, fileName) {
1108
1108
  );
1109
1109
  }
1110
1110
 
1111
- // src/core/scoring.ts
1111
+ // src/core/engine/scoring.ts
1112
1112
  var SEVERITY_DENSITY_WEIGHT = {
1113
1113
  blocking: 3,
1114
1114
  risk: 2,
@@ -1271,7 +1271,7 @@ function formatScoreSummary(report) {
1271
1271
  return lines.join("\n");
1272
1272
  }
1273
1273
 
1274
- // src/report-html/index.ts
1274
+ // src/core/ui-constants.ts
1275
1275
  var GAUGE_R = 54;
1276
1276
  var GAUGE_C = Math.round(2 * Math.PI * GAUGE_R);
1277
1277
  var CATEGORY_DESCRIPTIONS = {
@@ -1282,12 +1282,31 @@ var CATEGORY_DESCRIPTIONS = {
1282
1282
  "ai-readability": "Structure clarity for AI code generation, z-index, empty frames",
1283
1283
  "handoff-risk": "Hardcoded values, text truncation, image placeholders, dev status"
1284
1284
  };
1285
- var SEVERITY_ORDER = ["blocking", "risk", "missing-info", "suggestion"];
1285
+ var SEVERITY_ORDER = [
1286
+ "blocking",
1287
+ "risk",
1288
+ "missing-info",
1289
+ "suggestion"
1290
+ ];
1291
+
1292
+ // src/core/ui-helpers.ts
1286
1293
  function gaugeColor(pct) {
1287
1294
  if (pct >= 75) return "#22c55e";
1288
1295
  if (pct >= 50) return "#f59e0b";
1289
1296
  return "#ef4444";
1290
1297
  }
1298
+ function escapeHtml(text) {
1299
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1300
+ }
1301
+ function severityDot(sev) {
1302
+ const map = {
1303
+ blocking: "bg-red-500",
1304
+ risk: "bg-amber-500",
1305
+ "missing-info": "bg-zinc-400",
1306
+ suggestion: "bg-green-500"
1307
+ };
1308
+ return map[sev];
1309
+ }
1291
1310
  function severityBadge(sev) {
1292
1311
  const map = {
1293
1312
  blocking: "bg-red-500/10 text-red-600 border-red-500/20",
@@ -1302,15 +1321,24 @@ function scoreBadgeStyle(pct) {
1302
1321
  if (pct >= 50) return "bg-amber-500/10 text-amber-700 border-amber-500/20";
1303
1322
  return "bg-red-500/10 text-red-700 border-red-500/20";
1304
1323
  }
1305
- function severityDot(sev) {
1306
- const map = {
1307
- blocking: "bg-red-500",
1308
- risk: "bg-amber-500",
1309
- "missing-info": "bg-zinc-400",
1310
- suggestion: "bg-green-500"
1311
- };
1312
- return map[sev];
1324
+ function renderGaugeSvg(pct, size, strokeW, grade) {
1325
+ const offset = GAUGE_C * (1 - pct / 100);
1326
+ const color = gaugeColor(pct);
1327
+ if (grade) {
1328
+ return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="gauge-svg block">
1329
+ <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" stroke="#e4e4e7" class="stroke-border" />
1330
+ <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
1331
+ <text x="60" y="60" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="48" font-weight="700" font-family="Inter,-apple-system,sans-serif" class="font-sans">${escapeHtml(grade)}</text>
1332
+ </svg>`;
1333
+ }
1334
+ return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="gauge-svg block">
1335
+ <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" stroke="#e4e4e7" class="stroke-border" />
1336
+ <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
1337
+ <text x="60" y="62" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="28" font-weight="700" font-family="Inter,-apple-system,sans-serif" class="font-sans">${pct}</text>
1338
+ </svg>`;
1313
1339
  }
1340
+
1341
+ // src/core/report-html/index.ts
1314
1342
  function generateHtmlReport(file, result, scores, options) {
1315
1343
  const screenshotMap = new Map(
1316
1344
  (options?.nodeScreenshots ?? []).map((ns) => [ns.nodeId, ns])
@@ -1467,23 +1495,6 @@ ${figmaToken ? ` <script>
1467
1495
  </body>
1468
1496
  </html>`;
1469
1497
  }
1470
- function renderGaugeSvg(pct, size, strokeW, grade) {
1471
- const offset = GAUGE_C * (1 - pct / 100);
1472
- const color = gaugeColor(pct);
1473
- if (grade) {
1474
- return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="block">
1475
- <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" class="stroke-border" />
1476
- <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
1477
- <text x="60" y="60" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="52" font-weight="700" class="font-sans">${esc(grade)}</text>
1478
- </svg>`;
1479
- }
1480
- const fontSize = 32;
1481
- return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="block">
1482
- <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" class="stroke-border" />
1483
- <circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
1484
- <text x="60" y="62" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="${fontSize}" font-weight="700" class="font-sans">${pct}</text>
1485
- </svg>`;
1486
- }
1487
1498
  function renderSummaryDot(dotClass, count, label) {
1488
1499
  return `<div class="flex items-center gap-2">
1489
1500
  <span class="w-2.5 h-2.5 rounded-full ${dotClass}"></span>
@@ -1595,9 +1606,7 @@ function groupIssuesByCategory(issues) {
1595
1606
  for (const issue of issues) grouped.get(issue.rule.definition.category).push(issue);
1596
1607
  return grouped;
1597
1608
  }
1598
- function esc(text) {
1599
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1600
- }
1609
+ var esc = escapeHtml;
1601
1610
  var RuleOverrideSchema = z.object({
1602
1611
  score: z.number().int().max(0).optional(),
1603
1612
  severity: SeveritySchema.optional(),
@@ -1700,7 +1709,7 @@ var CustomRuleSchema = z.object({
1700
1709
  });
1701
1710
  var CustomRulesFileSchema = z.array(CustomRuleSchema);
1702
1711
 
1703
- // src/rules/custom/custom-rule-loader.ts
1712
+ // src/core/rules/custom/custom-rule-loader.ts
1704
1713
  async function loadCustomRules(filePath) {
1705
1714
  const absPath = resolve(filePath);
1706
1715
  const raw = await readFile(absPath, "utf-8");
@@ -1778,7 +1787,7 @@ function createPatternCheck(cr) {
1778
1787
  };
1779
1788
  }
1780
1789
 
1781
- // src/monitoring/events.ts
1790
+ // src/core/monitoring/events.ts
1782
1791
  var EVENT_PREFIX = "cic_";
1783
1792
  var EVENTS = {
1784
1793
  // Analysis
@@ -1796,11 +1805,13 @@ var EVENTS = {
1796
1805
  CLI_INIT: `${EVENT_PREFIX}cli_init`
1797
1806
  };
1798
1807
 
1799
- // src/monitoring/capture.ts
1808
+ // src/core/monitoring/capture.ts
1800
1809
  var monitoringEnabled = false;
1801
1810
  var posthogApiKey;
1802
1811
  var sentryDsn;
1803
1812
  var distinctId = "anonymous";
1813
+ var environment = "unknown";
1814
+ var version = "unknown";
1804
1815
  var commonProps = {};
1805
1816
  function uuid4() {
1806
1817
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -1828,10 +1839,12 @@ function initCapture(config2) {
1828
1839
  posthogApiKey = config2.posthogApiKey;
1829
1840
  sentryDsn = config2.sentryDsn;
1830
1841
  distinctId = config2.distinctId ?? "anonymous";
1842
+ environment = config2.environment ?? "unknown";
1843
+ version = config2.version ?? "unknown";
1831
1844
  commonProps = {
1832
1845
  _sdk: "canicode",
1833
- _sdk_version: config2.version ?? "unknown",
1834
- _env: config2.environment ?? "unknown"
1846
+ _sdk_version: version,
1847
+ _env: environment
1835
1848
  };
1836
1849
  }
1837
1850
  function captureEvent(event, properties) {
@@ -1866,6 +1879,8 @@ function captureError(error, context) {
1866
1879
  event_id: eventId,
1867
1880
  exception: { values: [{ type: error.name, value: error.message }] },
1868
1881
  platform: "node",
1882
+ environment,
1883
+ release: `canicode@${version}`,
1869
1884
  timestamp: Date.now() / 1e3,
1870
1885
  extra: context
1871
1886
  })
@@ -1890,10 +1905,12 @@ function shutdownCapture() {
1890
1905
  posthogApiKey = void 0;
1891
1906
  sentryDsn = void 0;
1892
1907
  distinctId = "anonymous";
1908
+ environment = "unknown";
1909
+ version = "unknown";
1893
1910
  commonProps = {};
1894
1911
  }
1895
1912
 
1896
- // src/monitoring/index.ts
1913
+ // src/core/monitoring/index.ts
1897
1914
  function initMonitoring(config2) {
1898
1915
  initCapture(config2);
1899
1916
  }
@@ -1916,17 +1933,17 @@ function shutdownMonitoring() {
1916
1933
  }
1917
1934
  }
1918
1935
 
1919
- // src/monitoring/keys.ts
1936
+ // src/core/monitoring/keys.ts
1920
1937
  var POSTHOG_API_KEY = "phc_rBFeG140KqJLpUnlpYDEFgdMM6JozZeqQsf9twXf5Dq" ;
1921
1938
  var SENTRY_DSN = "https://80a836a8300b25f17ef5bbf23afb5b3a@o4511080656207872.ingest.us.sentry.io/4511080661319680" ;
1922
1939
 
1923
- // src/rules/excluded-names.ts
1940
+ // src/core/rules/excluded-names.ts
1924
1941
  var EXCLUDED_NAME_PATTERN = /(badge|close|dismiss|overlay|float|fab|dot|indicator|corner|decoration|tag|status|notification|icon|ico|image|asset|filter|dim|dimmed|bg|background|logo|avatar|divider|separator|nav|navigation|gnb|header|footer|sidebar|toolbar|modal|dialog|popup|toast|tooltip|dropdown|menu|sticky|spinner|loader|cursor|cta|chatbot|thumb|thumbnail|tabbar|tab-bar|statusbar|status-bar)/i;
1925
1942
  function isExcludedName(name) {
1926
1943
  return EXCLUDED_NAME_PATTERN.test(name);
1927
1944
  }
1928
1945
 
1929
- // src/rules/layout/index.ts
1946
+ // src/core/rules/layout/index.ts
1930
1947
  function isContainerNode(node) {
1931
1948
  return node.type === "FRAME" || node.type === "GROUP" || node.type === "COMPONENT";
1932
1949
  }
@@ -2213,7 +2230,7 @@ defineRule({
2213
2230
  check: inconsistentSiblingLayoutDirectionCheck
2214
2231
  });
2215
2232
 
2216
- // src/rules/token/index.ts
2233
+ // src/core/rules/token/index.ts
2217
2234
  function hasStyleReference(node, styleType) {
2218
2235
  return node.styles !== void 0 && styleType in node.styles;
2219
2236
  }
@@ -2417,7 +2434,7 @@ defineRule({
2417
2434
  check: multipleFillColorsCheck
2418
2435
  });
2419
2436
 
2420
- // src/rules/component/index.ts
2437
+ // src/core/rules/component/index.ts
2421
2438
  function isComponentInstance(node) {
2422
2439
  return node.type === "INSTANCE";
2423
2440
  }
@@ -2591,7 +2608,7 @@ defineRule({
2591
2608
  check: singleUseComponentCheck
2592
2609
  });
2593
2610
 
2594
- // src/rules/naming/index.ts
2611
+ // src/core/rules/naming/index.ts
2595
2612
  var DEFAULT_NAME_PATTERNS = [
2596
2613
  /^Frame\s*\d*$/i,
2597
2614
  /^Group\s*\d*$/i,
@@ -2776,7 +2793,7 @@ defineRule({
2776
2793
  check: tooLongNameCheck
2777
2794
  });
2778
2795
 
2779
- // src/rules/ai-readability/index.ts
2796
+ // src/core/rules/ai-readability/index.ts
2780
2797
  function hasAutoLayout2(node) {
2781
2798
  return node.layoutMode !== void 0 && node.layoutMode !== "NONE";
2782
2799
  }
@@ -2953,7 +2970,7 @@ defineRule({
2953
2970
  check: emptyFrameCheck
2954
2971
  });
2955
2972
 
2956
- // src/rules/handoff-risk/index.ts
2973
+ // src/core/rules/handoff-risk/index.ts
2957
2974
  function hasAutoLayout3(node) {
2958
2975
  return node.layoutMode !== void 0 && node.layoutMode !== "NONE";
2959
2976
  }