@usefragments/core 1.5.2 → 1.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.
Files changed (64) hide show
  1. package/dist/chunk-RANPUC6C.js +72 -0
  2. package/dist/chunk-RANPUC6C.js.map +1 -0
  3. package/dist/{chunk-BAHCOAVG.js → chunk-WNMWKUYG.js} +447 -201
  4. package/dist/chunk-WNMWKUYG.js.map +1 -0
  5. package/dist/codes/index.d.ts +2 -2
  6. package/dist/codes/index.js +1 -1
  7. package/dist/compiled-types/index.d.ts +1 -1
  8. package/dist/compiled-types/index.js +8 -0
  9. package/dist/generate/index.d.ts +1 -1
  10. package/dist/{governance-pKrfh517.d.ts → governance-D9KtH-vg.d.ts} +617 -22
  11. package/dist/index.d.ts +800 -179
  12. package/dist/index.js +1003 -237
  13. package/dist/index.js.map +1 -1
  14. package/dist/react-types.d.ts +1 -1
  15. package/dist/registry.d.ts +36 -36
  16. package/dist/schemas/index.d.ts +1 -1
  17. package/dist/test-utils.d.ts +1 -1
  18. package/package.json +2 -1
  19. package/src/__tests__/policy-exclude.test.ts +180 -0
  20. package/src/agent-format.test.ts +13 -0
  21. package/src/agent-format.ts +9 -3
  22. package/src/canonical-bridge.ts +69 -1
  23. package/src/canonical-direction.test.ts +118 -0
  24. package/src/canonical-direction.ts +43 -2
  25. package/src/codes/__tests__/codes.test.ts +14 -2
  26. package/src/codes/codes.ts +41 -2
  27. package/src/compiled-types/index.ts +81 -0
  28. package/src/compiled-types/parse.test.ts +47 -0
  29. package/src/facts/builders.ts +48 -0
  30. package/src/facts/compile.ts +148 -34
  31. package/src/facts/fact-index.ts +15 -2
  32. package/src/facts/facts.test.ts +44 -3
  33. package/src/facts/index.ts +11 -6
  34. package/src/facts/types.ts +60 -9
  35. package/src/governance-integrity.test.ts +272 -4
  36. package/src/governance-integrity.ts +345 -25
  37. package/src/governance.ts +87 -1
  38. package/src/index.ts +46 -1
  39. package/src/policy-exclude.ts +113 -0
  40. package/src/rules/a11y-required-accessible-name.ts +175 -28
  41. package/src/rules/a11y-standard.ts +102 -0
  42. package/src/rules/a11y-utils.ts +7 -0
  43. package/src/rules/components-prefer-library.test.ts +75 -28
  44. package/src/rules/components-prefer-library.ts +35 -15
  45. package/src/rules/components-shadow-component.test.ts +21 -9
  46. package/src/rules/emit-gate.test.ts +74 -4
  47. package/src/rules/emit-gate.ts +24 -9
  48. package/src/rules/families.test.ts +69 -0
  49. package/src/rules/families.ts +52 -0
  50. package/src/rules/fix-availability.ts +1 -0
  51. package/src/rules/index.ts +18 -2
  52. package/src/rules/jsx-preferred-import-path.ts +29 -11
  53. package/src/rules/rules.test.ts +188 -8
  54. package/src/rules/styles-no-raw-color.ts +13 -4
  55. package/src/rules/styles-no-raw-dimensions.ts +13 -4
  56. package/src/rules/styles-no-raw-spacing.ts +12 -4
  57. package/src/rules/styles-no-raw-typography.ts +13 -4
  58. package/src/rules/tiers.ts +1 -0
  59. package/src/rules/utils.ts +39 -0
  60. package/src/tokens/design-token-parser.test.ts +131 -0
  61. package/src/tokens/design-token-parser.ts +362 -49
  62. package/src/types.ts +2 -2
  63. package/dist/chunk-BAHCOAVG.js.map +0 -1
  64. package/dist/{index-DbkPE46t.d.ts → index-hZAlYCli.d.ts} +8 -8
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode, JSX } from 'react';
2
- export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-pKrfh517.js';
2
+ export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-D9KtH-vg.js';
3
3
  import 'zod';
4
4
  import './topology/index.js';
5
5
  import './types-xJ2xyp_G.js';
@@ -52,15 +52,15 @@ declare const registryFileContentSchema: z.ZodObject<{
52
52
  }, "strip", z.ZodTypeAny, {
53
53
  path: string;
54
54
  size: number;
55
+ content: string;
55
56
  contentType: string;
56
57
  sha256: string;
57
- content: string;
58
58
  }, {
59
59
  path: string;
60
60
  size: number;
61
+ content: string;
61
62
  contentType: string;
62
63
  sha256: string;
63
- content: string;
64
64
  }>;
65
65
  declare const registrySourceSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
66
66
  kind: z.ZodLiteral<"package">;
@@ -216,7 +216,7 @@ declare const registryComponentSchema: z.ZodObject<{
216
216
  componentId: string;
217
217
  name: string;
218
218
  publicRef: string;
219
- tier: "core" | "custom" | "utility" | "composition";
219
+ tier: "custom" | "utility" | "composition" | "core";
220
220
  examples: unknown[];
221
221
  files: {
222
222
  path: string;
@@ -264,7 +264,7 @@ declare const registryComponentSchema: z.ZodObject<{
264
264
  provenance?: Record<string, unknown> | undefined;
265
265
  category?: string | undefined;
266
266
  contract?: unknown;
267
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
267
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
268
268
  examples?: unknown[] | undefined;
269
269
  styleFiles?: string[] | undefined;
270
270
  tokenDependencies?: string[] | undefined;
@@ -498,7 +498,7 @@ declare const registryManifestDraftSchema: z.ZodObject<{
498
498
  componentId: string;
499
499
  name: string;
500
500
  publicRef: string;
501
- tier: "core" | "custom" | "utility" | "composition";
501
+ tier: "custom" | "utility" | "composition" | "core";
502
502
  examples: unknown[];
503
503
  files: {
504
504
  path: string;
@@ -546,7 +546,7 @@ declare const registryManifestDraftSchema: z.ZodObject<{
546
546
  provenance?: Record<string, unknown> | undefined;
547
547
  category?: string | undefined;
548
548
  contract?: unknown;
549
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
549
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
550
550
  examples?: unknown[] | undefined;
551
551
  styleFiles?: string[] | undefined;
552
552
  tokenDependencies?: string[] | undefined;
@@ -611,7 +611,7 @@ declare const registryManifestDraftSchema: z.ZodObject<{
611
611
  componentId: string;
612
612
  name: string;
613
613
  publicRef: string;
614
- tier: "core" | "custom" | "utility" | "composition";
614
+ tier: "custom" | "utility" | "composition" | "core";
615
615
  examples: unknown[];
616
616
  files: {
617
617
  path: string;
@@ -667,8 +667,8 @@ declare const registryManifestDraftSchema: z.ZodObject<{
667
667
  label: string;
668
668
  };
669
669
  version: string;
670
- channel: string;
671
670
  generatedAt: string;
671
+ channel: string;
672
672
  packageFiles: {
673
673
  path: string;
674
674
  size: number;
@@ -713,7 +713,7 @@ declare const registryManifestDraftSchema: z.ZodObject<{
713
713
  provenance?: Record<string, unknown> | undefined;
714
714
  category?: string | undefined;
715
715
  contract?: unknown;
716
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
716
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
717
717
  examples?: unknown[] | undefined;
718
718
  styleFiles?: string[] | undefined;
719
719
  tokenDependencies?: string[] | undefined;
@@ -759,8 +759,8 @@ declare const registryManifestDraftSchema: z.ZodObject<{
759
759
  label: string;
760
760
  };
761
761
  version: string;
762
- channel: string;
763
762
  generatedAt: string;
763
+ channel: string;
764
764
  fcid?: string | undefined;
765
765
  contractVersionId?: string | undefined;
766
766
  packageFiles?: {
@@ -971,7 +971,7 @@ declare const registryManifestSchema: z.ZodObject<{
971
971
  componentId: string;
972
972
  name: string;
973
973
  publicRef: string;
974
- tier: "core" | "custom" | "utility" | "composition";
974
+ tier: "custom" | "utility" | "composition" | "core";
975
975
  examples: unknown[];
976
976
  files: {
977
977
  path: string;
@@ -1019,7 +1019,7 @@ declare const registryManifestSchema: z.ZodObject<{
1019
1019
  provenance?: Record<string, unknown> | undefined;
1020
1020
  category?: string | undefined;
1021
1021
  contract?: unknown;
1022
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
1022
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
1023
1023
  examples?: unknown[] | undefined;
1024
1024
  styleFiles?: string[] | undefined;
1025
1025
  tokenDependencies?: string[] | undefined;
@@ -1086,7 +1086,7 @@ declare const registryManifestSchema: z.ZodObject<{
1086
1086
  componentId: string;
1087
1087
  name: string;
1088
1088
  publicRef: string;
1089
- tier: "core" | "custom" | "utility" | "composition";
1089
+ tier: "custom" | "utility" | "composition" | "core";
1090
1090
  examples: unknown[];
1091
1091
  files: {
1092
1092
  path: string;
@@ -1143,8 +1143,8 @@ declare const registryManifestSchema: z.ZodObject<{
1143
1143
  label: string;
1144
1144
  };
1145
1145
  version: string;
1146
- channel: string;
1147
1146
  generatedAt: string;
1147
+ channel: string;
1148
1148
  packageFiles: {
1149
1149
  path: string;
1150
1150
  size: number;
@@ -1189,7 +1189,7 @@ declare const registryManifestSchema: z.ZodObject<{
1189
1189
  provenance?: Record<string, unknown> | undefined;
1190
1190
  category?: string | undefined;
1191
1191
  contract?: unknown;
1192
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
1192
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
1193
1193
  examples?: unknown[] | undefined;
1194
1194
  styleFiles?: string[] | undefined;
1195
1195
  tokenDependencies?: string[] | undefined;
@@ -1236,8 +1236,8 @@ declare const registryManifestSchema: z.ZodObject<{
1236
1236
  label: string;
1237
1237
  };
1238
1238
  version: string;
1239
- channel: string;
1240
1239
  generatedAt: string;
1240
+ channel: string;
1241
1241
  fcid?: string | undefined;
1242
1242
  contractVersionId?: string | undefined;
1243
1243
  packageFiles?: {
@@ -1450,7 +1450,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1450
1450
  componentId: string;
1451
1451
  name: string;
1452
1452
  publicRef: string;
1453
- tier: "core" | "custom" | "utility" | "composition";
1453
+ tier: "custom" | "utility" | "composition" | "core";
1454
1454
  examples: unknown[];
1455
1455
  files: {
1456
1456
  path: string;
@@ -1498,7 +1498,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1498
1498
  provenance?: Record<string, unknown> | undefined;
1499
1499
  category?: string | undefined;
1500
1500
  contract?: unknown;
1501
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
1501
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
1502
1502
  examples?: unknown[] | undefined;
1503
1503
  styleFiles?: string[] | undefined;
1504
1504
  tokenDependencies?: string[] | undefined;
@@ -1565,7 +1565,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1565
1565
  componentId: string;
1566
1566
  name: string;
1567
1567
  publicRef: string;
1568
- tier: "core" | "custom" | "utility" | "composition";
1568
+ tier: "custom" | "utility" | "composition" | "core";
1569
1569
  examples: unknown[];
1570
1570
  files: {
1571
1571
  path: string;
@@ -1622,8 +1622,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1622
1622
  label: string;
1623
1623
  };
1624
1624
  version: string;
1625
- channel: string;
1626
1625
  generatedAt: string;
1626
+ channel: string;
1627
1627
  packageFiles: {
1628
1628
  path: string;
1629
1629
  size: number;
@@ -1668,7 +1668,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1668
1668
  provenance?: Record<string, unknown> | undefined;
1669
1669
  category?: string | undefined;
1670
1670
  contract?: unknown;
1671
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
1671
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
1672
1672
  examples?: unknown[] | undefined;
1673
1673
  styleFiles?: string[] | undefined;
1674
1674
  tokenDependencies?: string[] | undefined;
@@ -1715,8 +1715,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1715
1715
  label: string;
1716
1716
  };
1717
1717
  version: string;
1718
- channel: string;
1719
1718
  generatedAt: string;
1719
+ channel: string;
1720
1720
  fcid?: string | undefined;
1721
1721
  contractVersionId?: string | undefined;
1722
1722
  packageFiles?: {
@@ -1754,15 +1754,15 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1754
1754
  }, "strip", z.ZodTypeAny, {
1755
1755
  path: string;
1756
1756
  size: number;
1757
+ content: string;
1757
1758
  contentType: string;
1758
1759
  sha256: string;
1759
- content: string;
1760
1760
  }, {
1761
1761
  path: string;
1762
1762
  size: number;
1763
+ content: string;
1763
1764
  contentType: string;
1764
1765
  sha256: string;
1765
- content: string;
1766
1766
  }>>;
1767
1767
  }, "strip", z.ZodTypeAny, {
1768
1768
  schemaVersion: "fragments.registry-artifact.v1";
@@ -1773,7 +1773,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1773
1773
  componentId: string;
1774
1774
  name: string;
1775
1775
  publicRef: string;
1776
- tier: "core" | "custom" | "utility" | "composition";
1776
+ tier: "custom" | "utility" | "composition" | "core";
1777
1777
  examples: unknown[];
1778
1778
  files: {
1779
1779
  path: string;
@@ -1830,8 +1830,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1830
1830
  label: string;
1831
1831
  };
1832
1832
  version: string;
1833
- channel: string;
1834
1833
  generatedAt: string;
1834
+ channel: string;
1835
1835
  packageFiles: {
1836
1836
  path: string;
1837
1837
  size: number;
@@ -1863,9 +1863,9 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1863
1863
  files: Record<string, {
1864
1864
  path: string;
1865
1865
  size: number;
1866
+ content: string;
1866
1867
  contentType: string;
1867
1868
  sha256: string;
1868
- content: string;
1869
1869
  }>;
1870
1870
  }, {
1871
1871
  schemaVersion: "fragments.registry-artifact.v1";
@@ -1886,7 +1886,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1886
1886
  provenance?: Record<string, unknown> | undefined;
1887
1887
  category?: string | undefined;
1888
1888
  contract?: unknown;
1889
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
1889
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
1890
1890
  examples?: unknown[] | undefined;
1891
1891
  styleFiles?: string[] | undefined;
1892
1892
  tokenDependencies?: string[] | undefined;
@@ -1933,8 +1933,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1933
1933
  label: string;
1934
1934
  };
1935
1935
  version: string;
1936
- channel: string;
1937
1936
  generatedAt: string;
1937
+ channel: string;
1938
1938
  fcid?: string | undefined;
1939
1939
  contractVersionId?: string | undefined;
1940
1940
  packageFiles?: {
@@ -1966,9 +1966,9 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1966
1966
  files: Record<string, {
1967
1967
  path: string;
1968
1968
  size: number;
1969
+ content: string;
1969
1970
  contentType: string;
1970
1971
  sha256: string;
1971
- content: string;
1972
1972
  }>;
1973
1973
  }>, {
1974
1974
  schemaVersion: "fragments.registry-artifact.v1";
@@ -1979,7 +1979,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
1979
1979
  componentId: string;
1980
1980
  name: string;
1981
1981
  publicRef: string;
1982
- tier: "core" | "custom" | "utility" | "composition";
1982
+ tier: "custom" | "utility" | "composition" | "core";
1983
1983
  examples: unknown[];
1984
1984
  files: {
1985
1985
  path: string;
@@ -2036,8 +2036,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
2036
2036
  label: string;
2037
2037
  };
2038
2038
  version: string;
2039
- channel: string;
2040
2039
  generatedAt: string;
2040
+ channel: string;
2041
2041
  packageFiles: {
2042
2042
  path: string;
2043
2043
  size: number;
@@ -2069,9 +2069,9 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
2069
2069
  files: Record<string, {
2070
2070
  path: string;
2071
2071
  size: number;
2072
+ content: string;
2072
2073
  contentType: string;
2073
2074
  sha256: string;
2074
- content: string;
2075
2075
  }>;
2076
2076
  }, {
2077
2077
  schemaVersion: "fragments.registry-artifact.v1";
@@ -2092,7 +2092,7 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
2092
2092
  provenance?: Record<string, unknown> | undefined;
2093
2093
  category?: string | undefined;
2094
2094
  contract?: unknown;
2095
- tier?: "core" | "custom" | "utility" | "composition" | undefined;
2095
+ tier?: "custom" | "utility" | "composition" | "core" | undefined;
2096
2096
  examples?: unknown[] | undefined;
2097
2097
  styleFiles?: string[] | undefined;
2098
2098
  tokenDependencies?: string[] | undefined;
@@ -2139,8 +2139,8 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
2139
2139
  label: string;
2140
2140
  };
2141
2141
  version: string;
2142
- channel: string;
2143
2142
  generatedAt: string;
2143
+ channel: string;
2144
2144
  fcid?: string | undefined;
2145
2145
  contractVersionId?: string | undefined;
2146
2146
  packageFiles?: {
@@ -2172,9 +2172,9 @@ declare const registryArtifactSchema: z.ZodEffects<z.ZodObject<{
2172
2172
  files: Record<string, {
2173
2173
  path: string;
2174
2174
  size: number;
2175
+ content: string;
2175
2176
  contentType: string;
2176
2177
  sha256: string;
2177
- content: string;
2178
2178
  }>;
2179
2179
  }>;
2180
2180
  declare const registryInstallReceiptFileSchema: z.ZodObject<{
@@ -1,3 +1,3 @@
1
1
  import 'zod';
2
2
  export { LegacySeverityLevel, Severity, SeverityLevel, legacySeverityLevelSchema, severityLevelSchema, severitySchema } from '../severity.js';
3
- export { b as AGENT_FORMAT_SCHEMA_VERSION, c as AgentErrorEnvelope, A as AgentFormat, d as AgentOutput, Q as FactEvidence, R as FactLocation, F as Finding, a as FindingFix, f as FindingReplaceClassTokenFix, g as FindingReplaceComponentFix, h as FindingReplaceImportFix, i as FindingReplacePropValueFix, j as FindingReplaceStyleValueFix, k as Fix, G as GovernanceVerdict, l as GovernanceVerdictMetadata, S as SuppressionDirective, V as ValidatorResult, n as Violation, o as agentErrorEnvelopeSchema, T as agentFindingSchema, p as agentFormatSchema, q as agentIntegritySchema, r as agentOutputSchema, U as agentPlanSchema, t as factEvidenceSchema, u as factLocationSchema, v as findingFixSchema, w as findingReplaceClassTokenFixSchema, x as findingReplaceComponentFixSchema, y as findingReplaceImportFixSchema, z as findingReplacePropValueFixSchema, B as findingReplaceStyleValueFixSchema, C as findingSchema, D as fixSchema, H as governanceVerdictMetadataSchema, J as governanceVerdictSchema, K as normalizeFinding, L as normalizeSeverity, M as normalizeViolation, N as suppressionDirectiveSchema, O as validatorResultSchema, P as violationSchema } from '../index-DbkPE46t.js';
3
+ export { b as AGENT_FORMAT_SCHEMA_VERSION, c as AgentErrorEnvelope, A as AgentFormat, d as AgentOutput, Q as FactEvidence, R as FactLocation, F as Finding, a as FindingFix, f as FindingReplaceClassTokenFix, g as FindingReplaceComponentFix, h as FindingReplaceImportFix, i as FindingReplacePropValueFix, j as FindingReplaceStyleValueFix, k as Fix, G as GovernanceVerdict, l as GovernanceVerdictMetadata, S as SuppressionDirective, V as ValidatorResult, n as Violation, o as agentErrorEnvelopeSchema, T as agentFindingSchema, p as agentFormatSchema, q as agentIntegritySchema, r as agentOutputSchema, U as agentPlanSchema, t as factEvidenceSchema, u as factLocationSchema, v as findingFixSchema, w as findingReplaceClassTokenFixSchema, x as findingReplaceComponentFixSchema, y as findingReplaceImportFixSchema, z as findingReplacePropValueFixSchema, B as findingReplaceStyleValueFixSchema, C as findingSchema, D as fixSchema, H as governanceVerdictMetadataSchema, J as governanceVerdictSchema, K as normalizeFinding, L as normalizeSeverity, M as normalizeViolation, N as suppressionDirectiveSchema, O as validatorResultSchema, P as violationSchema } from '../index-hZAlYCli.js';
@@ -1,4 +1,4 @@
1
- import { C as CompiledBlock, b as CompiledFragment } from './governance-pKrfh517.js';
1
+ import { C as CompiledBlock, b as CompiledFragment } from './governance-D9KtH-vg.js';
2
2
  import 'zod';
3
3
  import 'react';
4
4
  import './topology/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefragments/core",
3
- "version": "1.5.2",
3
+ "version": "1.7.0",
4
4
  "license": "MIT",
5
5
  "description": "Core types, schemas, and runtime API for Fragments component definitions",
6
6
  "author": "Conan McNicholl",
@@ -112,6 +112,7 @@
112
112
  },
113
113
  "scripts": {
114
114
  "build": "tsup",
115
+ "check:browser-types": "tsc -p test-fixtures/browser-consumer/tsconfig.json",
115
116
  "dev": "tsup --watch",
116
117
  "test": "vitest run",
117
118
  "typecheck": "tsc --noEmit",
@@ -0,0 +1,180 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ compileGlobalGovernanceFacts,
5
+ describePolicyExclude,
6
+ detectUnmatchedPolicyExcludes,
7
+ governanceConfigSchema,
8
+ matchPolicyExclude,
9
+ normalizePolicyExcludes,
10
+ type GovernanceConfig,
11
+ } from "../index.js";
12
+
13
+ describe("policy exclude normalization", () => {
14
+ it("accepts bare globs and { glob, reason } records alike", () => {
15
+ expect(
16
+ normalizePolicyExcludes([
17
+ "src/legacy/**",
18
+ { glob: "vendor/**", reason: "vendor drop, tracked in DS-611" },
19
+ ])
20
+ ).toEqual([
21
+ { glob: "src/legacy/**" },
22
+ { glob: "vendor/**", reason: "vendor drop, tracked in DS-611" },
23
+ ]);
24
+ });
25
+
26
+ it("returns undefined when nothing usable is authored", () => {
27
+ expect(normalizePolicyExcludes(undefined)).toBeUndefined();
28
+ expect(normalizePolicyExcludes([])).toBeUndefined();
29
+ expect(normalizePolicyExcludes(["", { reason: "no glob" }, 7])).toBeUndefined();
30
+ });
31
+
32
+ it("matches `**` across segments and `*` within one", () => {
33
+ const excludes = normalizePolicyExcludes(["src/legacy/vendor/**", "src/*.tsx"]);
34
+
35
+ expect(matchPolicyExclude(excludes, "src/legacy/vendor/deep/Widget.tsx")).toEqual({
36
+ glob: "src/legacy/vendor/**",
37
+ });
38
+ expect(matchPolicyExclude(excludes, "./src/App.tsx")).toEqual({ glob: "src/*.tsx" });
39
+ expect(matchPolicyExclude(excludes, "src\\App.tsx")).toEqual({ glob: "src/*.tsx" });
40
+ expect(matchPolicyExclude(excludes, "src/nested/App.tsx")).toBeUndefined();
41
+ expect(matchPolicyExclude(undefined, "src/App.tsx")).toBeUndefined();
42
+ });
43
+
44
+ it("describes an exclusion with its scope, glob, and reason", () => {
45
+ expect(
46
+ describePolicyExclude("style.rawColors.forbid", {
47
+ glob: "vendor/**",
48
+ reason: "vendor drop",
49
+ })
50
+ ).toBe("style.rawColors.forbid exclude matched vendor/** — vendor drop");
51
+ expect(describePolicyExclude("styles/no-raw-color", { glob: "vendor/**" })).toBe(
52
+ "styles/no-raw-color exclude matched vendor/**"
53
+ );
54
+ });
55
+ });
56
+
57
+ describe("global record excludes compile onto policy facts", () => {
58
+ const policy: GovernanceConfig = {
59
+ styles: [
60
+ {
61
+ kind: "style.rawColors.forbid",
62
+ except: [],
63
+ prefer: "token",
64
+ severity: "warn",
65
+ exclude: [{ glob: "src/legacy/vendor/**", reason: "vendor drop" }],
66
+ },
67
+ ],
68
+ jsx: [
69
+ {
70
+ kind: "jsx.importPath.prefer",
71
+ from: "@mui/material",
72
+ to: "@/components",
73
+ severity: "warn",
74
+ exclude: ["src/legacy/vendor/**"],
75
+ },
76
+ ],
77
+ };
78
+
79
+ it("is accepted by the shipped schema", () => {
80
+ expect(() => governanceConfigSchema.parse(policy)).not.toThrow();
81
+ });
82
+
83
+ it("threads the normalized excludes onto the compiled facts", () => {
84
+ const facts = compileGlobalGovernanceFacts(policy);
85
+ const color = facts.find((fact) => fact.kind === "style_raw_color_forbidden");
86
+ const importPath = facts.find((fact) => fact.kind === "jsx_import_path_preferred");
87
+
88
+ expect(color).toMatchObject({
89
+ exclude: [{ glob: "src/legacy/vendor/**", reason: "vendor drop" }],
90
+ });
91
+ expect(importPath).toMatchObject({ exclude: [{ glob: "src/legacy/vendor/**" }] });
92
+ });
93
+
94
+ it("keeps a user exclude alive when a preset record shares the same fact id", () => {
95
+ // A resolved policy is `[...presetRecords, ...userRecords]`, and the singleton style
96
+ // kinds are content-addressed on kind alone — so only one of the two survives
97
+ // indexing. The user's exemption must not be the casualty.
98
+ const facts = compileGlobalGovernanceFacts({
99
+ styles: [
100
+ { kind: "style.rawColors.forbid", except: [], prefer: "token", severity: "warn" },
101
+ {
102
+ kind: "style.rawColors.forbid",
103
+ except: [],
104
+ prefer: "token",
105
+ severity: "warn",
106
+ exclude: ["src/legacy/vendor/**"],
107
+ },
108
+ ],
109
+ });
110
+
111
+ const colorFacts = facts.filter((fact) => fact.kind === "style_raw_color_forbidden");
112
+ expect(colorFacts).toHaveLength(2);
113
+ // Both copies carry the union, so whichever the index keeps is the armed one.
114
+ for (const fact of colorFacts) {
115
+ expect(fact).toMatchObject({ exclude: [{ glob: "src/legacy/vendor/**" }] });
116
+ }
117
+ });
118
+
119
+ it("leaves fact identity untouched so fingerprints do not move", () => {
120
+ const withExclude = compileGlobalGovernanceFacts(policy);
121
+ const withoutExclude = compileGlobalGovernanceFacts({
122
+ styles: [{ kind: "style.rawColors.forbid", except: [], prefer: "token", severity: "warn" }],
123
+ jsx: [
124
+ {
125
+ kind: "jsx.importPath.prefer",
126
+ from: "@mui/material",
127
+ to: "@/components",
128
+ severity: "warn",
129
+ },
130
+ ],
131
+ });
132
+
133
+ expect(withExclude.map((fact) => fact.id)).toEqual(withoutExclude.map((fact) => fact.id));
134
+ // …and a policy with no excludes stays byte-identical to the pre-1.6.0 shape.
135
+ expect(withoutExclude.every((fact) => !("exclude" in fact))).toBe(true);
136
+ });
137
+ });
138
+
139
+ describe("detectUnmatchedPolicyExcludes", () => {
140
+ const policy: GovernanceConfig = {
141
+ styles: [
142
+ {
143
+ kind: "style.rawColors.forbid",
144
+ except: [],
145
+ prefer: "token",
146
+ severity: "warn",
147
+ exclude: ["src/legacy/vendor/**"],
148
+ },
149
+ ],
150
+ rules: { "styles/no-raw-spacing": { exclude: ["src/deleted-area/**"] } },
151
+ };
152
+
153
+ it("stays silent when every glob covers a scanned file", () => {
154
+ expect(
155
+ detectUnmatchedPolicyExcludes(policy, [
156
+ "src/legacy/vendor/Widget.tsx",
157
+ "src/deleted-area/Old.tsx",
158
+ ])
159
+ ).toEqual([]);
160
+ });
161
+
162
+ it("names each glob that scoped nothing, on both authoring routes", () => {
163
+ const diagnostics = detectUnmatchedPolicyExcludes(policy, ["src/App.tsx"]);
164
+
165
+ expect(diagnostics).toMatchObject([
166
+ {
167
+ code: "FUI9006",
168
+ kind: "unmatched-exclude",
169
+ path: "govern.rules.styles/no-raw-spacing.exclude",
170
+ },
171
+ { code: "FUI9006", kind: "unmatched-exclude", path: "govern.styles[0].exclude" },
172
+ ]);
173
+ expect(diagnostics[0]?.message).toContain("src/deleted-area/**");
174
+ expect(diagnostics[1]?.message).toContain("style.rawColors.forbid");
175
+ });
176
+
177
+ it("does not flag excludes when nothing was scanned at all", () => {
178
+ expect(detectUnmatchedPolicyExcludes(policy, [])).toEqual([]);
179
+ });
180
+ });
@@ -136,6 +136,19 @@ describe("buildAgentFormat", () => {
136
136
  });
137
137
  });
138
138
 
139
+ it("distinguishes affected files from all scanned files in the summary", () => {
140
+ const agentFormat = buildAgentFormat({
141
+ filesScanned: 7,
142
+ findings: [
143
+ finding({ fingerprint: "fp_1" }),
144
+ finding({ fingerprint: "fp_2", location: { file: "src/Button.tsx", line: 2, column: 1 } }),
145
+ ],
146
+ integrity: HEALTHY_INTEGRITY,
147
+ });
148
+
149
+ expect(agentFormat.summary).toBe("2 findings in 1 file; 7 scanned; score 67");
150
+ });
151
+
139
152
  it("marks an inert zero-rule scan indeterminate without inventing a score", () => {
140
153
  const agentFormat = buildAgentFormat({
141
154
  filesScanned: 1,
@@ -22,6 +22,7 @@ export function buildAgentFormat(input: BuildAgentFormatInput): AgentFormat {
22
22
  const zeroRulesLoaded = input.integrity.rulesLoaded === 0;
23
23
  const integrityFailed = !input.integrity.healthy || input.integrity.inert;
24
24
  const score = integrityFailed ? 0 : scoreFindings(input.findings);
25
+ const filesAffected = new Set(input.findings.map((finding) => finding.location.file)).size;
25
26
  const visibleFindings = uniqueFindings(input.findings).slice(0, input.maxFindings ?? 50);
26
27
  const agentFindings = visibleFindings.map((finding) => ({
27
28
  code: finding.code,
@@ -48,7 +49,7 @@ export function buildAgentFormat(input: BuildAgentFormatInput): AgentFormat {
48
49
  ? "indeterminate; zero_rules_loaded"
49
50
  : integrityFailed
50
51
  ? `failed with score ${score}; ${input.integrity.reasons.join("; ") || "governance integrity is unhealthy"}`
51
- : agentSummary(input.findings.length, input.filesScanned, score),
52
+ : agentSummary(input.findings.length, filesAffected, input.filesScanned, score),
52
53
  integrity: input.integrity,
53
54
  fix_first: agentFindings.filter((finding) => finding.plan.confidence >= 0.8),
54
55
  remaining: agentFindings.filter((finding) => finding.plan.confidence < 0.8),
@@ -119,9 +120,14 @@ function scoreFindings(findings: readonly Finding[]): number {
119
120
  return Math.max(1, Math.round(100 / (1 + penalty / 40)));
120
121
  }
121
122
 
122
- function agentSummary(count: number, filesScanned: number, score: number): string {
123
+ function agentSummary(
124
+ count: number,
125
+ filesAffected: number,
126
+ filesScanned: number,
127
+ score: number
128
+ ): string {
123
129
  if (count === 0) return `passed with score ${score}; no findings`;
124
- return `${count} finding${count === 1 ? "" : "s"} across ${filesScanned} file${filesScanned === 1 ? "" : "s"}; score ${score}`;
130
+ return `${count} finding${count === 1 ? "" : "s"} in ${filesAffected} file${filesAffected === 1 ? "" : "s"}; ${filesScanned} scanned; score ${score}`;
125
131
  }
126
132
 
127
133
  function planForFinding(finding: Finding): AgentFormat["fix_first"][number]["plan"] {