basalt-ui 1.21.0 → 1.23.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.
Files changed (85) hide show
  1. package/AGENTS.md +11 -1
  2. package/CHANGELOG.md +33 -0
  3. package/MIGRATING.md +205 -9
  4. package/README.md +63 -16
  5. package/agent/rules/basalt-app.md +39 -9
  6. package/agent/rules/basalt-charts.md +41 -6
  7. package/agent/rules/basalt-router.md +1 -1
  8. package/agent/rules/basalt-state.md +4 -4
  9. package/agent/rules/basalt-tokens.md +25 -4
  10. package/agent/skills/basalt-app/SKILL.md +4 -3
  11. package/agent/skills/basalt-charts/SKILL.md +23 -8
  12. package/agent/templates/DESIGN.md.tpl +3 -1
  13. package/configs/oxlint-plugin.js +86 -9
  14. package/configs/oxlint-plugin.test.ts +173 -0
  15. package/dist/charts/hooks/useBandPlot.d.ts +138 -0
  16. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -0
  17. package/dist/charts/hooks/useBandPlot.js +127 -0
  18. package/dist/charts/index.d.ts +4 -0
  19. package/dist/charts/index.d.ts.map +1 -1
  20. package/dist/charts/index.js +14 -0
  21. package/dist/charts/kinds/BandStrip.d.ts +113 -0
  22. package/dist/charts/kinds/BandStrip.d.ts.map +1 -0
  23. package/dist/charts/kinds/BandStrip.js +218 -0
  24. package/dist/charts/kinds/BandStrip.test.d.ts +2 -0
  25. package/dist/charts/kinds/BandStrip.test.d.ts.map +1 -0
  26. package/dist/charts/kinds/Bars.d.ts +5 -1
  27. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  28. package/dist/charts/kinds/Bars.js +2 -0
  29. package/dist/charts/kinds/MirroredBars.d.ts +93 -0
  30. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -0
  31. package/dist/charts/kinds/MirroredBars.js +315 -0
  32. package/dist/charts/kinds/MirroredBars.test.d.ts +2 -0
  33. package/dist/charts/kinds/MirroredBars.test.d.ts.map +1 -0
  34. package/dist/charts/kinds/MultiLine.d.ts +5 -1
  35. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  36. package/dist/charts/kinds/MultiLine.js +2 -0
  37. package/dist/charts/kinds/StackedArea.d.ts +5 -1
  38. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  39. package/dist/charts/kinds/StackedArea.js +2 -0
  40. package/dist/charts/kinds/ZonedLine.d.ts +5 -1
  41. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  42. package/dist/charts/kinds/ZonedLine.js +2 -0
  43. package/dist/charts/primitives/CartesianChart.d.ts +18 -2
  44. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  45. package/dist/charts/primitives/CartesianChart.js +5 -2
  46. package/dist/charts/primitives/HatchPattern.d.ts +31 -0
  47. package/dist/charts/primitives/HatchPattern.d.ts.map +1 -0
  48. package/dist/charts/primitives/HatchPattern.js +31 -0
  49. package/dist/cli/index.d.ts +9 -1
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +275 -48
  52. package/dist/cli/shipped-versions.test.d.ts +2 -0
  53. package/dist/cli/shipped-versions.test.d.ts.map +1 -0
  54. package/dist/guard/index.d.ts +67 -0
  55. package/dist/guard/index.d.ts.map +1 -1
  56. package/dist/guard/index.js +70 -11
  57. package/dist/vite.d.ts +44 -2
  58. package/dist/vite.d.ts.map +1 -1
  59. package/dist/vite.js +71 -26
  60. package/llms.txt +2 -2
  61. package/package.json +1 -1
  62. package/src/charts/hooks/useBandPlot.ts +302 -0
  63. package/src/charts/index.ts +14 -0
  64. package/src/charts/kinds/BandStrip.test.tsx +200 -0
  65. package/src/charts/kinds/BandStrip.tsx +364 -0
  66. package/src/charts/kinds/Bars.tsx +7 -1
  67. package/src/charts/kinds/DualPanel.test.tsx +10 -4
  68. package/src/charts/kinds/MirroredBars.test.tsx +193 -0
  69. package/src/charts/kinds/MirroredBars.tsx +455 -0
  70. package/src/charts/kinds/MultiLine.tsx +7 -1
  71. package/src/charts/kinds/StackedArea.tsx +7 -1
  72. package/src/charts/kinds/ZonedLine.tsx +7 -1
  73. package/src/charts/primitives/CartesianChart.test.tsx +86 -4
  74. package/src/charts/primitives/CartesianChart.tsx +28 -3
  75. package/src/charts/primitives/HatchPattern.tsx +55 -0
  76. package/src/cli/check-theme.test.ts +35 -0
  77. package/src/cli/help.test.ts +5 -1
  78. package/src/cli/index.ts +552 -79
  79. package/src/cli/shipped-versions.test.ts +83 -0
  80. package/src/cli/toolchain-wiring.test.ts +362 -6
  81. package/src/cli/waiver-audit.test.ts +88 -4
  82. package/src/guard/check-source.test.ts +293 -0
  83. package/src/guard/index.ts +209 -20
  84. package/src/vite.test.ts +112 -0
  85. package/src/vite.ts +147 -31
package/dist/cli/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import { createHash } from "node:crypto";
2
3
  import {
3
4
  existsSync,
@@ -14,9 +15,11 @@ import {
14
15
  checkSource,
15
16
  DEFAULT_GUARD_CONFIG,
16
17
  GENERATED_HEADER_LINE,
18
+ findAllowAnnotations,
17
19
  guardKindRemedy,
18
20
  GUARD_RULES,
19
21
  guardWaiverHint,
22
+ neutralizeAllowAnnotation,
20
23
  TOKENS_ONLY_DISABLED_KINDS,
21
24
  unmatchedExemptPatterns
22
25
  } from "../guard/index.js";
@@ -157,6 +160,16 @@ function hasBasaltProject(dir) {
157
160
  return false;
158
161
  }
159
162
  }
163
+ function descendantProjects(cwd, depth = 2) {
164
+ if (depth === 0) return [];
165
+ const out = [];
166
+ for (const name of subdirNames(cwd, { skipDotDirs: true })) {
167
+ const dir = resolve(cwd, name);
168
+ if (hasBasaltProject(dir)) out.push(dir);
169
+ else out.push(...descendantProjects(dir, depth - 1));
170
+ }
171
+ return out;
172
+ }
160
173
  function resolveProjectDir(cwd) {
161
174
  const override = process.env["BASALT_CWD"];
162
175
  if (override !== void 0 && override.length > 0) {
@@ -164,7 +177,8 @@ function resolveProjectDir(cwd) {
164
177
  return { dir, relocatedFrom: dir === cwd ? null : cwd, ambiguous: null };
165
178
  }
166
179
  if (hasBasaltProject(cwd)) return { dir: cwd, relocatedFrom: null, ambiguous: null };
167
- const candidates = collectWorkspacePackages(cwd).packages.filter((pkg) => pkg.dir !== cwd && hasBasaltProject(pkg.dir)).map((pkg) => pkg.dir);
180
+ const declared = collectWorkspacePackages(cwd).packages.filter((pkg) => pkg.dir !== cwd && hasBasaltProject(pkg.dir)).map((pkg) => pkg.dir);
181
+ const candidates = declared.length > 0 ? declared : descendantProjects(cwd);
168
182
  if (candidates.length === 1) {
169
183
  return { dir: candidates[0], relocatedFrom: cwd, ambiguous: null };
170
184
  }
@@ -219,6 +233,13 @@ function appShellFiles(rootAbs) {
219
233
  out.push(...walkSourceFiles(resolve(appDir, "public")));
220
234
  return out;
221
235
  }
236
+ const PLAIN_JSON_HINT_PATH = "manifest.json";
237
+ function waiverHintFor(relPath, profile) {
238
+ if (profile !== "tokens-only" || !relPath.endsWith(".webmanifest")) {
239
+ return guardWaiverHint(relPath);
240
+ }
241
+ return `${guardWaiverHint(PLAIN_JSON_HINT_PATH)} (basaltAppPlugin would emit the file instead, but it needs basalt's Vite/React layer, which a tokens-only consumer has opted out of.)`;
242
+ }
222
243
  function checkTheme(invocationCwd = process.cwd(), flags = []) {
223
244
  if (conflictingProfileFlags(flags)) {
224
245
  console.error(
@@ -229,7 +250,7 @@ function checkTheme(invocationCwd = process.cwd(), flags = []) {
229
250
  const project = resolveProjectDir(invocationCwd);
230
251
  if (project.ambiguous !== null) {
231
252
  console.error(
232
- `\u2716 basalt-ui check-theme: no basalt config at ${invocationCwd}, and ${project.ambiguous.length} workspace packages carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run it from one of them, or set BASALT_CWD to pick.`
253
+ `\u2716 basalt-ui check-theme: no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run it from one of them, or set BASALT_CWD to pick.`
233
254
  );
234
255
  return 1;
235
256
  }
@@ -329,7 +350,9 @@ function checkTheme(invocationCwd = process.cwd(), flags = []) {
329
350
  `);
330
351
  }
331
352
  const presentKinds = [...new Set(findings.map((f) => f.kind))].toSorted();
332
- const waiverHints = [...new Set(findings.map((f) => guardWaiverHint(f.relPath)))].toSorted();
353
+ const waiverHints = [
354
+ ...new Set(findings.map((f) => waiverHintFor(f.relPath, profile)))
355
+ ].toSorted();
333
356
  console.error(["Fix:", ...presentKinds.map(guardKindRemedy), ...waiverHints].join(" "));
334
357
  if (findings.some((f) => SERIES_MODULE_HINT_RE.test(f.relPath))) {
335
358
  console.error(
@@ -338,20 +361,89 @@ function checkTheme(invocationCwd = process.cwd(), flags = []) {
338
361
  }
339
362
  return errors.length > 0 ? 1 : 0;
340
363
  }
341
- const AUDIT_NEUTRALIZED_TOKEN = "basalt-audit-neutralized";
342
364
  function findingKey(f) {
343
365
  return `${f.relPath}:${f.line}:${f.kind}`;
344
366
  }
345
- const AUDIT_ANNOTATION_PREFIX = /(?:^|\/\/|\/\*|<!--|^\s*\*)\s*$/;
346
- function annotationRuleIds(rest) {
347
- const head = (rest.replace(/^-file\b/, "").split(/—|–|:|\s-\s/)[0] ?? "").trim();
348
- return head.split(",").map((word) => word.trim().replace(/^basalt\//, "")).filter((word) => /^[a-z][a-z0-9-]*$/.test(word));
349
- }
350
- function isAllowAnnotationLine(line, token) {
351
- const at = line.indexOf(token);
352
- if (at === -1) return false;
353
- if (line.includes(`"basalt:${token}`)) return true;
354
- return AUDIT_ANNOTATION_PREFIX.test(line.slice(0, at));
367
+ function resolveToolBin(dir, name) {
368
+ let current = dir;
369
+ for (; ; ) {
370
+ const candidate = resolve(current, "node_modules/.bin", name);
371
+ if (existsSync(candidate)) return candidate;
372
+ const parent = resolve(current, "..");
373
+ if (parent === current) break;
374
+ current = parent;
375
+ }
376
+ const probe = spawnSync(name, ["--version"], { stdio: "ignore", timeout: 1e4 });
377
+ return probe.error === void 0 && probe.status === 0 ? name : null;
378
+ }
379
+ function diagnosticCounts(diagnostics, filename) {
380
+ const counts = /* @__PURE__ */ new Map();
381
+ for (const d of diagnostics) {
382
+ if (d.filename !== filename) continue;
383
+ const code = typeof d.code === "string" ? d.code : "";
384
+ const rule = /^basalt\((.+)\)$/.exec(code)?.[1];
385
+ if (rule === void 0) continue;
386
+ const line = d.labels?.[0]?.span?.line;
387
+ const key = `${typeof line === "number" ? line : 0}:${rule}`;
388
+ counts.set(key, (counts.get(key) ?? 0) + 1);
389
+ }
390
+ return counts;
391
+ }
392
+ function probePluginRules(cwd, guardCfg, probes, sources) {
393
+ if (probes.length === 0) return /* @__PURE__ */ new Map();
394
+ const bin = resolveToolBin(cwd, "oxlint");
395
+ if (bin === null) return null;
396
+ const written = [];
397
+ const probePaths = /* @__PURE__ */ new Map();
398
+ try {
399
+ for (const probe of probes) {
400
+ const probed = neutralizeAllowAnnotation(sources.get(probe.rel) ?? "", probe.line, guardCfg);
401
+ const dot = probe.rel.lastIndexOf(".");
402
+ const rel = dot === -1 ? `${probe.rel}.basalt-audit-${probe.slot}` : `${probe.rel.slice(0, dot)}.basalt-audit-${probe.slot}${probe.rel.slice(dot)}`;
403
+ const abs = resolve(cwd, rel);
404
+ if (existsSync(abs)) continue;
405
+ writeFileSync(abs, probed, "utf8");
406
+ written.push(abs);
407
+ probePaths.set(probe.slot, rel);
408
+ }
409
+ if (probePaths.size === 0) return null;
410
+ const targets = [...new Set(probes.map((p) => p.rel)), ...probePaths.values()];
411
+ const run2 = spawnSync(bin, ["--format=json", "--no-error-on-unmatched-pattern", ...targets], {
412
+ cwd,
413
+ encoding: "utf8",
414
+ timeout: 12e4,
415
+ stdio: ["ignore", "pipe", "ignore"]
416
+ });
417
+ if (run2.error !== void 0 || typeof run2.stdout !== "string") return null;
418
+ let diagnostics;
419
+ try {
420
+ diagnostics = JSON.parse(run2.stdout).diagnostics ?? [];
421
+ } catch {
422
+ return null;
423
+ }
424
+ const verdicts = /* @__PURE__ */ new Map();
425
+ for (const probe of probes) {
426
+ const probeRel = probePaths.get(probe.slot);
427
+ if (probeRel === void 0) continue;
428
+ const before = diagnosticCounts(diagnostics, probe.rel);
429
+ const after = diagnosticCounts(diagnostics, probeRel);
430
+ const revealed = [];
431
+ for (const [key, count] of after) {
432
+ if (count <= (before.get(key) ?? 0)) continue;
433
+ const [line = "", rule = ""] = key.split(":");
434
+ revealed.push(`${rule}@${line}`);
435
+ }
436
+ verdicts.set(probe.slot, revealed.toSorted());
437
+ }
438
+ return verdicts;
439
+ } finally {
440
+ for (const abs of written) {
441
+ try {
442
+ unlinkSync(abs);
443
+ } catch {
444
+ }
445
+ }
446
+ }
355
447
  }
356
448
  function auditAllows(cwd, cfg, guardCfg, scanned) {
357
449
  const token = guardCfg.allowComment;
@@ -363,6 +455,7 @@ basalt-ui check-theme --audit-allows \u2014 ${cwd}
363
455
  let outOfReach = 0;
364
456
  let unaccountable = 0;
365
457
  const waiverLines = [];
458
+ const pluginProbes = [];
366
459
  const sources = /* @__PURE__ */ new Map();
367
460
  for (const rel of scanned) sources.set(rel, readFileSync(resolve(cwd, rel), "utf8"));
368
461
  for (const rel of scanned) {
@@ -375,31 +468,60 @@ basalt-ui check-theme --audit-allows \u2014 ${cwd}
375
468
  const unscopedAt = new Map(
376
469
  baseline.filter((f) => f.kind === "theme-allow-unscoped").map((f) => [f.line, f.token])
377
470
  );
378
- const fileLines = text.split("\n");
379
- for (const [index, line] of fileLines.entries()) {
380
- if (!isAllowAnnotationLine(line ?? "", token)) continue;
381
- const probe = [...fileLines];
382
- probe[index] = (line ?? "").replaceAll(token, AUDIT_NEUTRALIZED_TOKEN);
383
- const revealed = checkSource(probe.join("\n"), rel, guardCfg).filter((f) => f.kind !== "theme-allow-unscoped" && !baselineKeys.has(findingKey(f))).map((f) => `${f.kind}@${f.line}`);
384
- const note = unscopedAt.get(index + 1);
471
+ for (const site of findAllowAnnotations(text, rel, guardCfg)) {
472
+ const revealed = checkSource(
473
+ neutralizeAllowAnnotation(text, site.line, guardCfg),
474
+ rel,
475
+ guardCfg
476
+ ).filter((f) => f.kind !== "theme-allow-unscoped" && !baselineKeys.has(findingKey(f))).map((f) => `${f.kind}@${f.line}`);
477
+ const note = unscopedAt.get(site.line);
385
478
  if (note !== void 0) unaccountable++;
386
- const site = ` ${rel}:${index + 1}`.padEnd(52);
479
+ const label = ` ${rel}:${site.line}`.padEnd(52);
387
480
  const suffix = note === void 0 ? "" : ` [${note}]`;
388
- const ids = annotationRuleIds((line ?? "").slice((line ?? "").indexOf(token) + token.length));
389
481
  if (revealed.length > 0) {
390
482
  live++;
391
- waiverLines.push(`${site} suppresses ${[...new Set(revealed)].join(", ")}${suffix}`);
392
- } else if (ids.length > 0 && !ids.some((id) => Object.hasOwn(GUARD_RULES, id))) {
483
+ waiverLines.push(`${label} suppresses ${[...new Set(revealed)].join(", ")}${suffix}`);
484
+ continue;
485
+ }
486
+ if (site.pluginRules.length > 0 && site.guardKinds.length === 0) {
487
+ pluginProbes.push({
488
+ slot: waiverLines.length,
489
+ rel,
490
+ line: site.line,
491
+ ids: site.pluginRules,
492
+ site: label,
493
+ suffix
494
+ });
495
+ waiverLines.push("");
496
+ continue;
497
+ }
498
+ if (site.guardKinds.length === 0 && site.unknownRules.length > 0) {
393
499
  outOfReach++;
394
500
  waiverLines.push(
395
- `${site} scoped to ${ids.join(", ")} \u2014 not a check-theme kind, so this audit cannot judge it (an oxlint plugin rule, or a typo theme-allow-unscoped would report)${suffix}`
501
+ `${label} scoped to ${site.unknownRules.join(", ")} \u2014 names no guard kind and no plugin rule, so this audit cannot judge it (most likely a typo, which theme-allow-unscoped reports)${suffix}`
396
502
  );
397
- } else {
398
- dead++;
399
- waiverLines.push(`${site} SUPPRESSES NOTHING \u2014 dead, delete it${suffix}`);
503
+ continue;
400
504
  }
505
+ dead++;
506
+ waiverLines.push(`${label} SUPPRESSES NOTHING \u2014 dead, delete it${suffix}`);
401
507
  }
402
508
  }
509
+ const pluginVerdicts = probePluginRules(cwd, guardCfg, pluginProbes, sources);
510
+ for (const probe of pluginProbes) {
511
+ const revealed = pluginVerdicts?.get(probe.slot);
512
+ if (revealed === void 0) {
513
+ outOfReach++;
514
+ waiverLines[probe.slot] = `${probe.site} scoped to ${probe.ids.join(", ")} \u2014 an oxlint plugin rule, and oxlint could not be run here, so this audit cannot judge it${probe.suffix}`;
515
+ continue;
516
+ }
517
+ if (revealed.length > 0) {
518
+ live++;
519
+ waiverLines[probe.slot] = `${probe.site} suppresses ${revealed.join(", ")} (oxlint)${probe.suffix}`;
520
+ continue;
521
+ }
522
+ dead++;
523
+ waiverLines[probe.slot] = `${probe.site} SUPPRESSES NOTHING \u2014 dead, delete it (proved by re-running oxlint over the file with this annotation neutralized)${probe.suffix}`;
524
+ }
403
525
  lines.push(`${token} annotations (${waiverLines.length}):`);
404
526
  lines.push(...waiverLines.length === 0 ? [" (none)"] : waiverLines);
405
527
  const exemptLines = [];
@@ -452,7 +574,11 @@ basalt.exemptRules entries (${exemptCount}):`);
452
574
  lines.push(...exemptLines.length === 0 ? [" (none)"] : exemptLines);
453
575
  lines.push(
454
576
  `
455
- ${live} live, ${dead} dead, ${outOfReach} outside check-theme's reach, ${unaccountable} unaccountable (reported as theme-allow-unscoped by a normal run).`
577
+ ${live} live, ${dead} dead, ${outOfReach} unjudgeable, ${unaccountable} unaccountable (reported as theme-allow-unscoped by a normal run).`
578
+ );
579
+ const includes = cfg.include ?? [];
580
+ lines.push(
581
+ `Scope: the ${scanned.length} file(s) check-theme scans \u2014 everything under basalt.roots (${resolveRoots(cfg).join(", ")}), plus each root's sibling index.html and public/ tree, ` + (includes.length === 0 ? "plus anything named in basalt.include (none). " : `plus basalt.include (${includes.join(", ")}). `) + "A waiver in a file none of those reach is not audited \u2014 widen roots, or name it in basalt.include, or accept that nothing polices that file."
456
582
  );
457
583
  if (dead > 0) {
458
584
  lines.push(
@@ -746,6 +872,16 @@ function migrateLegacyOxfmt(cwd, pkgRoot, manifest) {
746
872
  const shipped = readSource(pkgRoot, "configs/oxfmt.json");
747
873
  if (shipped !== null && onDisk === shipped) unlinkSync(legacyAbs);
748
874
  }
875
+ const DESIGN_VERSION_LINE = /^(> Managed by basalt-ui) \(\d+\.\d+\.\d+\)\./m;
876
+ function migrateDesignVersionLine(cwd) {
877
+ const dest = resolve(cwd, "DESIGN.md");
878
+ const onDisk = readIfExists(dest);
879
+ if (onDisk === null || !DESIGN_VERSION_LINE.test(onDisk)) return;
880
+ writeFileEnsuringDir(
881
+ dest,
882
+ onDisk.replace(DESIGN_VERSION_LINE, "$1 \u2014 run `basalt-ui doctor` for the version.")
883
+ );
884
+ }
749
885
  function writeFileEnsuringDir(abs, content) {
750
886
  mkdirSync(dirname(abs), { recursive: true });
751
887
  writeFileSync(abs, content);
@@ -969,6 +1105,38 @@ function inspectLefthookSeam(repoRoot) {
969
1105
  if (entry === void 0) return { kind: "unwired", file };
970
1106
  return existsSync(resolve(repoRoot, entry)) ? { kind: "ok", file, entry } : { kind: "broken", file, entry };
971
1107
  }
1108
+ const LEFTHOOK_GUARD_RUN = /^\s*run:\s*.*\bcheck-theme\b/m;
1109
+ function lefthookDump(repoRoot) {
1110
+ const localBin = resolve(repoRoot, "node_modules/.bin/lefthook");
1111
+ const bin = existsSync(localBin) ? localBin : "lefthook";
1112
+ try {
1113
+ const result = spawnSync(bin, ["dump"], {
1114
+ cwd: repoRoot,
1115
+ encoding: "utf8",
1116
+ timeout: 1e4,
1117
+ stdio: ["ignore", "pipe", "ignore"]
1118
+ });
1119
+ if (result.error !== void 0 || result.status !== 0) return null;
1120
+ return result.stdout;
1121
+ } catch {
1122
+ return null;
1123
+ }
1124
+ }
1125
+ function inspectLefthookGate(repoRoot) {
1126
+ const seam = inspectLefthookSeam(repoRoot);
1127
+ if (seam.kind === "no-file") return { kind: "no-file" };
1128
+ if (seam.kind === "ok") return { kind: "extends-ok", file: seam.file, entry: seam.entry };
1129
+ if (seam.kind === "broken") {
1130
+ return { kind: "extends-broken", file: seam.file, entry: seam.entry };
1131
+ }
1132
+ const dump = lefthookDump(repoRoot);
1133
+ if (dump !== null) {
1134
+ return LEFTHOOK_GUARD_RUN.test(dump) ? { kind: "wired", file: seam.file, proof: "dump" } : { kind: "absent", file: seam.file };
1135
+ }
1136
+ const raw = readIfExists(resolve(repoRoot, seam.file)) ?? "";
1137
+ if (LEFTHOOK_GUARD_RUN.test(raw)) return { kind: "wired", file: seam.file, proof: "config" };
1138
+ return { kind: "advisory", file: seam.file };
1139
+ }
972
1140
  function mergeOxlintExtends(cwd, presetPath) {
973
1141
  const abs = resolve(cwd, ".oxlintrc.json");
974
1142
  const raw = readIfExists(abs);
@@ -1131,15 +1299,60 @@ function readOxlintPresetPlugins(pkgRoot) {
1131
1299
  return [];
1132
1300
  }
1133
1301
  }
1134
- function sync(opts = {}, cwd = process.cwd()) {
1302
+ function findManifestAbove(dir) {
1303
+ let current = resolve(dir, "..");
1304
+ for (; ; ) {
1305
+ if (existsSync(resolve(current, MANIFEST_PATH))) return current;
1306
+ const parent = resolve(current, "..");
1307
+ if (parent === current) return null;
1308
+ current = parent;
1309
+ }
1310
+ }
1311
+ function sync(opts = {}, invocationCwd = process.cwd()) {
1312
+ const syncFlags = opts.flags ?? [];
1313
+ if (conflictingProfileFlags(syncFlags)) {
1314
+ console.error("basalt-ui sync: --tokens-only and --framework are alternatives \u2014 pass one.");
1315
+ return 1;
1316
+ }
1317
+ const project = resolveProjectDir(invocationCwd);
1318
+ if (project.ambiguous !== null) {
1319
+ console.error(
1320
+ `\u2716 basalt-ui sync: no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run it from one of them, or set BASALT_CWD to pick.`
1321
+ );
1322
+ return 1;
1323
+ }
1324
+ const cwd = project.dir;
1325
+ if (project.relocatedFrom !== null) {
1326
+ console.log(
1327
+ `basalt-ui sync: no basalt config at ${project.relocatedFrom} \u2014 running in ${relativePosix(project.relocatedFrom, cwd)}, where it lives.`
1328
+ );
1329
+ }
1330
+ if (declaredProfile(readBasaltConfig(cwd), syncFlags) === "tokens-only") {
1331
+ console.log(
1332
+ `basalt-ui sync: ${MANIFEST_PATH}: n/a \u2014 a tokens-only consumer has no scaffold to reconcile. The token layer is refreshed by re-running \`basalt-ui tokens:css\`, and \`tokens:css --check\` is its drift gate. Pass --framework to force the full profile.`
1333
+ );
1334
+ return 0;
1335
+ }
1336
+ if (!existsSync(resolve(cwd, MANIFEST_PATH))) {
1337
+ const elsewhere = findManifestAbove(cwd);
1338
+ const wouldWrite = managedFiles(resolvePeerFlags(cwd, {}), resolvePlacement(cwd)).length;
1339
+ console.error(
1340
+ `\u2716 basalt-ui sync: no ${MANIFEST_PATH} at ${cwd} \u2014 refusing to scaffold. \`sync\` refreshes an EXISTING basalt install; creating one is \`basalt-ui init\`'s decision, and continuing here would write ${wouldWrite} managed file(s) into this directory.` + (elsewhere === null ? " No manifest exists at any ancestor either \u2014 run `basalt-ui init` here if this package really is meant to be a consumer." : ` This repo's install is at ${relativePosix(cwd, elsewhere)} (${elsewhere}) \u2014 run sync there, or set BASALT_CWD to it.`)
1341
+ );
1342
+ return 1;
1343
+ }
1135
1344
  const rootsState = reconcileRoots(cwd, { write: opts.check !== true });
1136
1345
  const ctx = renderContext(cwd);
1137
1346
  const manifest = readManifest(cwd);
1138
- if (!opts.check) migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
1347
+ if (!opts.check) {
1348
+ migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
1349
+ migrateDesignVersionLine(cwd);
1350
+ }
1139
1351
  const peers = resolvePeerFlags(cwd, {});
1140
1352
  const placement = resolvePlacement(cwd);
1141
1353
  const files = managedFiles(peers, placement);
1142
1354
  let updated = 0;
1355
+ let created = 0;
1143
1356
  let recreated = 0;
1144
1357
  let skippedDrift = 0;
1145
1358
  let staleForCheck = 0;
@@ -1151,10 +1364,12 @@ function sync(opts = {}, cwd = process.cwd()) {
1151
1364
  missingSources.push(file.source);
1152
1365
  continue;
1153
1366
  }
1367
+ const tracked = manifest.files[file.dest] !== void 0;
1154
1368
  if (file.mode === "seed") {
1155
1369
  if (state.current === null && !opts.check) {
1156
1370
  manifest.files[file.dest] = writeUnit(file, cwd, state.desired);
1157
- recreated++;
1371
+ if (tracked) recreated++;
1372
+ else created++;
1158
1373
  }
1159
1374
  continue;
1160
1375
  }
@@ -1172,8 +1387,9 @@ function sync(opts = {}, cwd = process.cwd()) {
1172
1387
  staleForCheck++;
1173
1388
  if (opts.check) continue;
1174
1389
  manifest.files[file.dest] = writeUnit(file, cwd, state.desired);
1175
- if (kind === "missing") recreated++;
1176
- else updated++;
1390
+ if (kind !== "missing") updated++;
1391
+ else if (tracked) recreated++;
1392
+ else created++;
1177
1393
  }
1178
1394
  if (!placement.isPackageRepoRoot) {
1179
1395
  console.log(
@@ -1222,7 +1438,7 @@ function sync(opts = {}, cwd = process.cwd()) {
1222
1438
  writeFileEnsuringDir(resolve(cwd, MANIFEST_PATH), `${JSON.stringify(manifest, null, 2)}
1223
1439
  `);
1224
1440
  console.log(
1225
- `basalt-ui sync: ${updated} updated, ${recreated} recreated, ${skippedDrift} skipped (drift).`
1441
+ `basalt-ui sync: ${updated} updated, ${created} created, ${recreated} recreated, ${skippedDrift} skipped (drift).`
1226
1442
  );
1227
1443
  if (driftLines.length > 0) {
1228
1444
  console.log("Locally-edited files were skipped (run with --force to overwrite):");
@@ -1454,7 +1670,7 @@ function doctor(invocationCwd = process.cwd(), flags = []) {
1454
1670
  `
1455
1671
  basalt-ui doctor \u2014 ${invocationCwd}
1456
1672
 
1457
- \u2716 no basalt config at ${invocationCwd}, and ${project.ambiguous.length} workspace packages carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run doctor from one of them, or set BASALT_CWD to pick.
1673
+ \u2716 no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run doctor from one of them, or set BASALT_CWD to pick.
1458
1674
 
1459
1675
  1 hard failure(s), 0 warning(s).
1460
1676
  `
@@ -1630,22 +1846,30 @@ basalt-ui doctor \u2014 ${cwd}
1630
1846
  }
1631
1847
  }
1632
1848
  const repoRoot = findRepoRoot(cwd);
1633
- const lefthookSeam = profile === "tokens-only" ? { kind: "n/a" } : inspectLefthookSeam(repoRoot);
1849
+ const lefthookGate = profile === "tokens-only" ? { kind: "n/a" } : inspectLefthookGate(repoRoot);
1634
1850
  const lefthookCorrect = shippedAssetPath(install, repoRoot, "configs/lefthook.yml");
1635
- if (lefthookSeam.kind === "n/a") {
1851
+ if (lefthookGate.kind === "n/a") {
1636
1852
  pass("lefthook-preset: n/a \u2014 a tokens-only consumer wires its own hooks");
1637
- } else if (lefthookSeam.kind === "no-file") {
1853
+ } else if (lefthookGate.kind === "no-file") {
1638
1854
  pass(`lefthook-preset: n/a \u2014 no lefthook config at ${repoRoot}`);
1639
- } else if (lefthookSeam.kind === "unwired") {
1640
- warn(
1641
- `${lefthookSeam.file} does not extend the shipped lefthook preset \u2014 the oxlint / oxfmt / check-theme pre-commit jobs are not wired. Add "extends: [${lefthookCorrect}]" to it.`
1855
+ } else if (lefthookGate.kind === "extends-ok") {
1856
+ pass(`${lefthookGate.file} extends the shipped lefthook preset (${lefthookGate.entry})`);
1857
+ } else if (lefthookGate.kind === "wired") {
1858
+ pass(
1859
+ `lefthook-preset: ${lefthookGate.file} does not extend the shipped preset, but a pre-commit command runs check-theme directly \u2014 the gate exists (read from ` + (lefthookGate.proof === "dump" ? "`lefthook dump`, so extends/include/root: are all resolved)." : "the config text; `lefthook dump` was not runnable here, so an `include:` or a remote config was not consulted).")
1642
1860
  );
1643
- } else if (lefthookSeam.kind === "broken") {
1861
+ } else if (lefthookGate.kind === "extends-broken") {
1644
1862
  fail(
1645
- `${lefthookSeam.file} extends "${lefthookSeam.entry}", which does not exist (${resolve(repoRoot, lefthookSeam.entry)}) \u2014 lefthook merges a missing extends target into ZERO commands and still exits 0, so this repo has no pre-commit gate and \`lefthook dump\` looks clean. Repoint it at "${lefthookCorrect}".`
1863
+ `${lefthookGate.file} extends "${lefthookGate.entry}", which does not exist (${resolve(repoRoot, lefthookGate.entry)}) \u2014 lefthook merges a missing extends target into ZERO commands and still exits 0, so this repo has no pre-commit gate and \`lefthook dump\` looks clean. Repoint it at "${lefthookCorrect}".`
1864
+ );
1865
+ } else if (lefthookGate.kind === "absent") {
1866
+ warn(
1867
+ `${lefthookGate.file}: \`lefthook dump\` resolves the merged config and NO pre-commit command runs check-theme \u2014 the theme guard is not gating commits. Either add "extends: [${lefthookCorrect}]", or add your own command running \`bunx basalt-ui check-theme\` (with \`root:\` if it must run in a subdirectory).`
1646
1868
  );
1647
1869
  } else {
1648
- pass(`${lefthookSeam.file} extends the shipped lefthook preset (${lefthookSeam.entry})`);
1870
+ warn(
1871
+ `${lefthookGate.file} names no basalt gate and \`lefthook dump\` could not be run here, so this check cannot tell a missing gate from one wired through an \`include:\` or a remote config it never read \u2014 treat it as advisory. If the guard really is unwired, add "extends: [${lefthookCorrect}]" or your own command running \`bunx basalt-ui check-theme\`.`
1872
+ );
1649
1873
  }
1650
1874
  const publicDir = resolve(cwd, "public");
1651
1875
  if (profile === "framework" && existsSync(publicDir)) {
@@ -2017,7 +2241,7 @@ async function guardHook(cwd = process.cwd()) {
2017
2241
  }
2018
2242
  return 0;
2019
2243
  }
2020
- const USAGE = "Usage: basalt-ui <init [--with-router] [--with-query] [--merge-lint] | sync [--force] [--check] |\n check-theme [--audit-allows] | check-coverage | info [--json] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Auto-detects a tokens-only consumer (no manifest + no @mantine/core) and checks only what\n applies. A check that cannot RUN is reported as SKIPPED and exits non-zero.\n\ncheck-theme / doctor honour BASALT_CWD, and relocate to the single workspace package carrying a\nbasalt config when invoked from a repo root that has none \u2014 several carrying one is ambiguous\nand exits 1 in BOTH. --tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.";
2244
+ const USAGE = "Usage: basalt-ui <init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--tokens-only|--framework] |\n check-theme [--audit-allows] | check-coverage | info [--json] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Auto-detects a tokens-only consumer (no manifest + no @mantine/core) and checks only what\n applies. A check that cannot RUN is reported as SKIPPED and exits non-zero.\n\nsync [--force] [--check] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n\ncheck-theme / doctor / sync all resolve their project the same way: they honour BASALT_CWD, and\nrelocate to the single package below the cwd carrying a basalt config when invoked from a repo\nroot that has none \u2014 several carrying one is ambiguous and exits 1 in ALL THREE.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.";
2021
2245
  function run(argv, cwd = process.cwd()) {
2022
2246
  const [cmd, ...flags] = argv;
2023
2247
  if (cmd === "help" || cmd === "--help" || cmd === "-h" || flags.includes("--help") || flags.includes("-h")) {
@@ -2032,7 +2256,10 @@ function run(argv, cwd = process.cwd()) {
2032
2256
  mergeLint: flags.includes("--merge-lint")
2033
2257
  });
2034
2258
  case "sync":
2035
- return sync({ force: flags.includes("--force"), check: flags.includes("--check") }, cwd);
2259
+ return sync(
2260
+ { force: flags.includes("--force"), check: flags.includes("--check"), flags },
2261
+ cwd
2262
+ );
2036
2263
  case "check-theme":
2037
2264
  return checkTheme(cwd, flags);
2038
2265
  case "check-coverage":
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shipped-versions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipped-versions.test.d.ts","sourceRoot":"","sources":["../../src/cli/shipped-versions.test.ts"],"names":[],"mappings":""}
@@ -28,6 +28,17 @@ export type UnmatchedExemptPattern = {
28
28
  * typo'd kind name is an exemption for a rule that does not exist.
29
29
  */
30
30
  export declare function unmatchedExemptPatterns(cfg: GuardConfig, scannedRelPaths: readonly string[]): UnmatchedExemptPattern[];
31
+ /**
32
+ * The oxlint plugin's rule ids. Not guard kinds — they can never suppress a `checkSource` finding —
33
+ * but they ARE valid ids in a `theme-allow`, so an annotation scoped to one of them must parse as
34
+ * accountable rather than be reported as unscoped. Kept as a literal list because the plugin is a
35
+ * standalone `.js` file this module must not import (see the plugin's own header).
36
+ *
37
+ * Exported for `check-theme --audit-allows`: an annotation naming one of these is outside the
38
+ * guard's reach, and judging it needs a `oxlint` run rather than a `checkSource` one. Keep in step
39
+ * with the plugin's own `KNOWN_RULE_IDS`; `configs/oxlint-plugin.test.ts` asserts the two agree.
40
+ */
41
+ export declare const PLUGIN_RULE_IDS: ReadonlySet<string>;
31
42
  /**
32
43
  * The exact first line of every stylesheet the CLI emits — the contract between `basalt-ui
33
44
  * tokens:css` / `fonts:css` and this scanner, and the single source of truth for it (`src/cli`
@@ -262,4 +273,60 @@ export declare function guardWaiverHint(relPath: string): string;
262
273
  * if (findings.some((f) => f.kind === 'raw-hex')) { ... }
263
274
  */
264
275
  export declare function checkSource(text: string, relPath: string, cfg: GuardConfig): Finding[];
276
+ /**
277
+ * One `theme-allow` annotation as WRITTEN, classified by whose reach its ids fall in.
278
+ *
279
+ * `guardKinds` is what `checkSource` can judge by re-running itself with the annotation
280
+ * neutralized. `pluginRules` is what only an `oxlint` run can judge. `unknownRules` names no rule
281
+ * at all. All three empty with `bare: true` is the legacy blanket form.
282
+ */
283
+ export type AllowAnnotationSite = {
284
+ /** 1-based line the annotation is written on. */
285
+ readonly line: number;
286
+ /** `'file'` for the `theme-allow-file` declaration form, `'line'` otherwise. */
287
+ readonly scope: 'line' | 'file';
288
+ /** Every id the annotation names that resolves to a real rule, `basalt/` prefix stripped. */
289
+ readonly rules: readonly string[];
290
+ /** The subset of `rules` `checkSource` owns. */
291
+ readonly guardKinds: readonly GuardKind[];
292
+ /** The subset of `rules` the oxlint plugin owns — outside `checkSource`'s reach. */
293
+ readonly pluginRules: readonly string[];
294
+ /** Words that occupied the id slot but name no rule. Their presence makes the parse fail closed. */
295
+ readonly unknownRules: readonly string[];
296
+ /** A written reason follows the ids. */
297
+ readonly hasReason: boolean;
298
+ /** The legacy blanket form — no ids at all, known or unknown. Only covers anything at line scope. */
299
+ readonly bare: boolean;
300
+ /** The source line, verbatim. */
301
+ readonly text: string;
302
+ };
303
+ /**
304
+ * Every allow annotation in one file, with the ids it names classified by reach.
305
+ *
306
+ * Pure, and it shares `collectAllowAnnotations` with {@link checkSource} — so a caller enumerating
307
+ * annotations sees exactly the set the scan honours, rather than a second regex that can drift from
308
+ * it. `--audit-allows` carried such a mirror and it was already one alternation behind.
309
+ *
310
+ * A file-scoped declaration appears once, at the line it is written on.
311
+ *
312
+ * @example
313
+ * for (const site of findAllowAnnotations(text, rel, cfg)) {
314
+ * if (site.pluginRules.length > 0) probeWithOxlint(neutralizeAllowAnnotation(text, site.line, cfg))
315
+ * }
316
+ */
317
+ export declare function findAllowAnnotations(text: string, relPath: string, cfg: GuardConfig): AllowAnnotationSite[];
318
+ /** The token a neutralized annotation is rewritten to. Shares no substring with `theme-allow`. */
319
+ export declare const NEUTRALIZED_ALLOW_TOKEN = "basalt-audit-neutralized";
320
+ /**
321
+ * `text` with the allow token on ONE (1-based) line rewritten to {@link NEUTRALIZED_ALLOW_TOKEN},
322
+ * leaving every other annotation intact.
323
+ *
324
+ * The probe half of an audit: re-scan the result and the findings that appear are exactly what that
325
+ * one annotation suppresses. Shipped rather than left to each caller so the guard probe and the
326
+ * oxlint probe neutralize identically — a `--audit-allows` verdict that differs between the two
327
+ * halves by a substitution detail is worse than no verdict.
328
+ *
329
+ * A line with no token comes back unchanged.
330
+ */
331
+ export declare function neutralizeAllowAnnotation(text: string, line: number, cfg: GuardConfig): string;
265
332
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAyb9D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAA;AAmJD,sGAAsG;AACtG,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAAA;CAC7C,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,WAAW,EAChB,eAAe,EAAE,SAAS,MAAM,EAAE,GACjC,sBAAsB,EAAE,CAc1B;AA8WD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,gGACwD,CAAA;AA8F1F;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,SAAS,CAqB5D,CAAA;AA2NF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;gCA0BL,WAAW;;;;;;;gCAcX,WAAW;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAQX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCASX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCAaX,WAAW;;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;;;;;;gCAgBX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCASX,WAAW;;;;;;;;;CAWmB,CAAA;AAEjD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEvD;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBvD;AAUD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,EAAE,CA0XtF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"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"}