@usefragments/core 1.5.0 → 1.5.2

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 (65) hide show
  1. package/dist/{chunk-HXGE3O2F.js → chunk-BAHCOAVG.js} +133 -7
  2. package/dist/chunk-BAHCOAVG.js.map +1 -0
  3. package/dist/{chunk-57QDBEHQ.js → chunk-ZHS52OT4.js} +3 -9
  4. package/dist/chunk-ZHS52OT4.js.map +1 -0
  5. package/dist/codes/index.d.ts +2 -2
  6. package/dist/codes/index.js +2 -2
  7. package/dist/compiled-types/index.d.ts +1 -1
  8. package/dist/generate/index.d.ts +1 -1
  9. package/dist/{governance-BLsyk56o.d.ts → governance-pKrfh517.d.ts} +264 -4
  10. package/dist/{index-h_yWj15D.d.ts → index-DbkPE46t.d.ts} +40 -0
  11. package/dist/index.d.ts +472 -28
  12. package/dist/index.js +561 -39
  13. package/dist/index.js.map +1 -1
  14. package/dist/react-types.d.ts +1 -1
  15. package/dist/schemas/index.d.ts +1 -1
  16. package/dist/schemas/index.js +1 -1
  17. package/dist/test-utils.d.ts +1 -1
  18. package/package.json +1 -1
  19. package/src/agent-format.test.ts +1 -0
  20. package/src/canonical-bridge.ts +46 -0
  21. package/src/canonical-direction.ts +118 -0
  22. package/src/codes/__tests__/codes.test.ts +1 -1
  23. package/src/codes/codes.ts +29 -0
  24. package/src/config.ts +20 -0
  25. package/src/contract/preimage.test.ts +40 -0
  26. package/src/contract/preimage.ts +20 -0
  27. package/src/effective-governance-inputs.test.ts +52 -0
  28. package/src/effective-governance-inputs.ts +106 -0
  29. package/src/facts/builders.ts +25 -1
  30. package/src/facts/compile.ts +20 -2
  31. package/src/facts/fact-index.ts +12 -1
  32. package/src/facts/facts.test.ts +14 -18
  33. package/src/facts/types.ts +25 -6
  34. package/src/governance-integrity.test.ts +127 -0
  35. package/src/governance-integrity.ts +326 -4
  36. package/src/governance.test.ts +87 -1
  37. package/src/governance.ts +121 -0
  38. package/src/index.ts +34 -5
  39. package/src/rules/__tests__/fix-emission-invariant.test.ts +35 -4
  40. package/src/rules/components-prefer-library.test.ts +103 -0
  41. package/src/rules/components-prefer-library.ts +30 -4
  42. package/src/rules/fix-availability.ts +1 -0
  43. package/src/rules/index.ts +7 -0
  44. package/src/rules/jsx-preferred-import-path.ts +45 -9
  45. package/src/rules/rules.test.ts +202 -6
  46. package/src/rules/spacing-resolution.ts +2 -2
  47. package/src/rules/styles-no-raw-color.test.ts +6 -1
  48. package/src/rules/styles-no-raw-color.ts +3 -2
  49. package/src/rules/styles-no-raw-dimensions.ts +5 -7
  50. package/src/rules/styles-no-raw-spacing.test.ts +48 -0
  51. package/src/rules/styles-no-raw-spacing.ts +10 -7
  52. package/src/rules/styles-no-raw-typography.ts +7 -4
  53. package/src/rules/taxonomy.test.ts +3 -0
  54. package/src/rules/tiers.ts +2 -0
  55. package/src/rules/token-candidates.ts +36 -0
  56. package/src/rules/tokens-require-dual-fallback.ts +2 -1
  57. package/src/rules/tokens-upstream-drift.test.ts +111 -0
  58. package/src/rules/tokens-upstream-drift.ts +57 -0
  59. package/src/rules/utils.ts +22 -14
  60. package/src/schema.ts +13 -0
  61. package/src/schemas/index.ts +5 -8
  62. package/src/token-types.ts +71 -1
  63. package/src/types.ts +27 -3
  64. package/dist/chunk-57QDBEHQ.js.map +0 -1
  65. package/dist/chunk-HXGE3O2F.js.map +0 -1
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ import {
49
49
  bridgeSourceViolations,
50
50
  byCode,
51
51
  byRuleId,
52
+ canonicalBridgeV1Schema,
52
53
  canonicalJson,
53
54
  compileComponentFacts,
54
55
  compileGlobalGovernanceFacts,
@@ -111,7 +112,7 @@ import {
111
112
  ownedImportsEqual,
112
113
  resolveComponentGovernance,
113
114
  scaleGovernanceRecordSchema
114
- } from "./chunk-HXGE3O2F.js";
115
+ } from "./chunk-BAHCOAVG.js";
115
116
  import {
116
117
  AGENT_FORMAT_SCHEMA_VERSION,
117
118
  agentErrorEnvelopeSchema,
@@ -136,7 +137,7 @@ import {
136
137
  suppressionDirectiveSchema,
137
138
  validatorResultSchema,
138
139
  violationSchema
139
- } from "./chunk-57QDBEHQ.js";
140
+ } from "./chunk-ZHS52OT4.js";
140
141
  import {
141
142
  OWNED_PACKAGE_IDENTITY_EPOCH,
142
143
  OWNED_PACKAGE_IDENTITY_SOURCE_SHA256,
@@ -1687,6 +1688,14 @@ var tokenConfigSchema = z4.object({
1687
1688
  // vocabulary seeds the known-token set (#7c). A packages-only tokens block
1688
1689
  // is valid — see the superRefine below.
1689
1690
  packages: z4.array(z4.string().min(1)).optional(),
1691
+ aliases: z4.record(z4.string().min(1)).optional(),
1692
+ upstream: z4.array(
1693
+ z4.object({
1694
+ packageName: z4.string().min(1),
1695
+ version: z4.string().min(1).optional(),
1696
+ digest: z4.string().regex(/^[a-f0-9]{64}$/u).optional()
1697
+ })
1698
+ ).optional(),
1690
1699
  exclude: z4.array(repoRelativePathSchema).optional(),
1691
1700
  themeSelectors: z4.record(z4.string()).optional(),
1692
1701
  enabled: z4.boolean().optional(),
@@ -1832,9 +1841,14 @@ var governedFragmentDefinitionSchema = z4.object({
1832
1841
  var recipeDefinitionSchema = blockDefinitionSchema;
1833
1842
 
1834
1843
  // src/config.ts
1844
+ var RAW_CONFIG_DECLARATION = /* @__PURE__ */ Symbol.for("@usefragments/core/raw-config-declaration");
1835
1845
  function formatZodErrors(errors) {
1836
1846
  return errors.map((error) => ` - ${error.path.join(".")}: ${error.message}`).join("\n");
1837
1847
  }
1848
+ function configDeclarationForDiagnostics(config) {
1849
+ if (!config || typeof config !== "object") return config;
1850
+ return config[RAW_CONFIG_DECLARATION] ?? config;
1851
+ }
1838
1852
  function defineConfig(config) {
1839
1853
  const normalized = normalizeGovernanceConfig(config);
1840
1854
  const result = fragmentsConfigSchema.safeParse(normalized);
@@ -1842,6 +1856,12 @@ function defineConfig(config) {
1842
1856
  throw new Error(`Invalid fragments config:
1843
1857
  ${formatZodErrors(result.error.errors)}`);
1844
1858
  }
1859
+ Object.defineProperty(result.data, RAW_CONFIG_DECLARATION, {
1860
+ value: normalized,
1861
+ enumerable: false,
1862
+ configurable: false,
1863
+ writable: false
1864
+ });
1845
1865
  return result.data;
1846
1866
  }
1847
1867
 
@@ -4775,13 +4795,20 @@ function isExemptColor(value, except) {
4775
4795
  }
4776
4796
  return false;
4777
4797
  }
4778
- function tokenReference(name) {
4779
- if (name.startsWith("$")) return name;
4780
- const cssVarName = name.startsWith("--") ? name : `--${name.replace(/\./g, "-")}`;
4781
- return `var(${cssVarName})`;
4798
+ function tokenReference(token) {
4799
+ if (token.referenceFormat === "css-var") {
4800
+ const cssVarName = [token.name, ...token.sourceNames ?? []].find(
4801
+ (name) => name.startsWith("--")
4802
+ );
4803
+ return cssVarName ? `var(${cssVarName})` : token.name;
4804
+ }
4805
+ if (token.referenceFormat === "scss-var") {
4806
+ return [token.name, ...token.sourceNames ?? []].find((name) => name.startsWith("$")) ?? (token.name.startsWith("--") ? `$${token.name.slice(2)}` : token.name);
4807
+ }
4808
+ return token.sourceNames?.[0] ?? token.name;
4782
4809
  }
4783
4810
  function isApplicableTokenReference(referenceFormat) {
4784
- return referenceFormat === void 0 || referenceFormat === "css-var" || referenceFormat === "scss-var";
4811
+ return referenceFormat === "css-var" || referenceFormat === "scss-var";
4785
4812
  }
4786
4813
 
4787
4814
  // src/rules/a11y-required-accessible-name.ts
@@ -4938,6 +4965,77 @@ function isEnforceableHtmlEquivalent(args) {
4938
4965
  return builtIn !== null && builtIn.canonical === args.canonical;
4939
4966
  }
4940
4967
 
4968
+ // src/canonical-direction.ts
4969
+ function projectCanonicalDirectionConflicts(ix, canonicalSources) {
4970
+ const npmSources = canonicalSources.filter(
4971
+ (source) => source.kind === "npm"
4972
+ );
4973
+ if (npmSources.length === 0) return [];
4974
+ const identityByComponent = new Map(
4975
+ ix.byKind("component_identity").map((identity) => [identity.componentKey, identity])
4976
+ );
4977
+ const conflicts = [];
4978
+ for (const definition of ix.byKind("component_definition")) {
4979
+ if (!definition.exported || definition.renderRoot.resolution !== "canonical") continue;
4980
+ const target = npmTarget(definition.renderRoot.canonical, npmSources);
4981
+ if (!target) continue;
4982
+ const identity = identityByComponent.get(definition.componentKey);
4983
+ if (identity?.decisionId && decisionTargetsExport(identity.canonicalTarget, target.exportName)) {
4984
+ if (identity.state === "shadow") continue;
4985
+ if (identity.state === "variant" && identity.confidence === "confirmed") {
4986
+ conflicts.push({
4987
+ ...target,
4988
+ wrapperComponentKey: definition.componentKey,
4989
+ wrapperFile: definition.file,
4990
+ wrapperExportName: definition.exportName,
4991
+ state: "sanctioned"
4992
+ });
4993
+ continue;
4994
+ }
4995
+ }
4996
+ conflicts.push({
4997
+ ...target,
4998
+ wrapperComponentKey: definition.componentKey,
4999
+ wrapperFile: definition.file,
5000
+ wrapperExportName: definition.exportName,
5001
+ state: "unresolved"
5002
+ });
5003
+ }
5004
+ return conflicts.sort(
5005
+ (left, right) => left.packageName.localeCompare(right.packageName) || left.exportName.localeCompare(right.exportName) || left.wrapperComponentKey.localeCompare(right.wrapperComponentKey)
5006
+ );
5007
+ }
5008
+ function canonicalDirectionConflictsTarget(conflicts, importPath, exportName) {
5009
+ if (!importPath) return false;
5010
+ return conflicts.some(
5011
+ (conflict) => ownedImportMatchesRoot(importPath, conflict.packageName) && conflict.exportName === exportName
5012
+ );
5013
+ }
5014
+ function npmTarget(canonicalTarget, sources) {
5015
+ const separator = canonicalTarget.lastIndexOf("#");
5016
+ if (separator <= 0 || separator === canonicalTarget.length - 1) return null;
5017
+ const importPath = canonicalTarget.slice(0, separator);
5018
+ const imported = canonicalTarget.slice(separator + 1).split(".").at(-1);
5019
+ if (!imported) return null;
5020
+ for (const source of sources) {
5021
+ if (!ownedImportMatchesRoot(importPath, source.specifier)) continue;
5022
+ const subpath = packageSubpath(importPath, source.specifier);
5023
+ const exportName = imported === "default" ? subpath.split("/").filter(Boolean).at(-1) : imported;
5024
+ if (!exportName) continue;
5025
+ if (source.exclude?.includes(exportName)) continue;
5026
+ if (source.include && !source.include.includes(exportName)) continue;
5027
+ return { packageName: source.specifier, exportName };
5028
+ }
5029
+ return null;
5030
+ }
5031
+ function packageSubpath(importPath, packageName) {
5032
+ return importPath === packageName ? "" : importPath.slice(packageName.length + 1);
5033
+ }
5034
+ function decisionTargetsExport(target, exportName) {
5035
+ if (!target) return true;
5036
+ return target === exportName || target.split("#").at(-1)?.split(".").at(-1) === exportName;
5037
+ }
5038
+
4941
5039
  // src/rules/components-prefer-library.ts
4942
5040
  var RULE_ID2 = "components/prefer-library";
4943
5041
  var RULE_VERSION2 = "1";
@@ -5081,8 +5179,12 @@ function findClassNameReimpl(node, classTokensByNode, targets) {
5081
5179
  function ruleComponentsPreferLibrary(ix) {
5082
5180
  const policy = ix.policy.ruleConfig(RULE_ID2);
5083
5181
  if (!policy?.enabled) return [];
5084
- const sources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
5085
- const mappings = canonicalMappingsFromPolicy(policy.options?.canonicalMappings);
5182
+ const configuredSources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
5183
+ const conflicts = projectCanonicalDirectionConflicts(ix, configuredSources);
5184
+ const sources = configuredSources.map((source) => suppressConflictedExports(source, conflicts));
5185
+ const mappings = canonicalMappingsFromPolicy(policy.options?.canonicalMappings).filter(
5186
+ (mapping) => !canonicalDirectionConflictsTarget(conflicts, mapping.importPath, mapping.name)
5187
+ );
5086
5188
  if (sources.length === 0 && mappings.length === 0) return [];
5087
5189
  const importsByFileAndLocal = indexImportsByFileAndLocal(ix);
5088
5190
  const propsByNode = indexPropsByNodeId(ix);
@@ -5345,6 +5447,16 @@ function ruleComponentsPreferLibrary(ix) {
5345
5447
  }
5346
5448
  return findings;
5347
5449
  }
5450
+ function suppressConflictedExports(source, conflicts) {
5451
+ if (source.kind !== "npm") return source;
5452
+ const names = conflicts.filter((conflict) => ownedImportMatchesRoot(source.specifier, conflict.packageName)).map((conflict) => conflict.exportName);
5453
+ if (names.length === 0) return source;
5454
+ const blocked = new Set(names);
5455
+ if (source.include) {
5456
+ return { ...source, include: source.include.filter((name) => !blocked.has(name)) };
5457
+ }
5458
+ return { ...source, exclude: [.../* @__PURE__ */ new Set([...source.exclude ?? [], ...names])].sort() };
5459
+ }
5348
5460
  function indexShadowRenderRootNodeIds(ix) {
5349
5461
  const identities = ix.byKind("component_identity");
5350
5462
  if (identities.length === 0) return /* @__PURE__ */ new Set();
@@ -6117,6 +6229,18 @@ function ruleJsxPreferredImportPath(ix) {
6117
6229
  const seen = /* @__PURE__ */ new Set();
6118
6230
  for (const usage of ix.byKind("usage_import")) {
6119
6231
  const matches = policies.filter((policy) => {
6232
+ if (policy.bridge) {
6233
+ if (policy.bridge.implementationFiles.some((file) => sameRepoPath(file, usage.file))) {
6234
+ return false;
6235
+ }
6236
+ if (!ownedImportMatchesRoot(usage.source, policy.from)) return false;
6237
+ if (ownedImportsEqual(usage.source, policy.from)) {
6238
+ return usage.imported === policy.bridge.underlyingExportName;
6239
+ }
6240
+ const subpath = usage.source.slice(policy.from.length + 1);
6241
+ const leaf = subpath.split("/").at(-1);
6242
+ return leaf === policy.bridge.underlyingExportName && (usage.imported === "default" || usage.imported === policy.bridge.underlyingExportName);
6243
+ }
6120
6244
  if (!ownedImportsEqual(policy.from, usage.source)) return false;
6121
6245
  if (policy.imported !== void 0 && policy.imported !== usage.imported) return false;
6122
6246
  return true;
@@ -6132,7 +6256,7 @@ function ruleJsxPreferredImportPath(ix) {
6132
6256
  ruleId: RULE_ID7,
6133
6257
  ruleVersion: RULE_VERSION8,
6134
6258
  severity: policy.severity,
6135
- message: `Import from "${usage.source}" should use "${policy.to}".`,
6259
+ message: policy.bridge ? `Import ${policy.bridge.underlyingExportName} through the confirmed local ${policy.bridge.localExportName} wrapper from "${policy.to}".` : `Import from "${usage.source}" should use "${policy.to}".`,
6136
6260
  location: usage.location,
6137
6261
  evidence: ix.evidence([usage.id, policy.id]),
6138
6262
  fingerprintIdentity: {
@@ -6143,7 +6267,7 @@ function ruleJsxPreferredImportPath(ix) {
6143
6267
  line: usage.location.line,
6144
6268
  column: usage.location.column
6145
6269
  },
6146
- fix: {
6270
+ fix: policy.bridge ? void 0 : {
6147
6271
  kind: "replaceImport",
6148
6272
  title: policy.because ?? `Replace import path with "${policy.to}"`,
6149
6273
  from: usage.source,
@@ -6155,7 +6279,14 @@ function ruleJsxPreferredImportPath(ix) {
6155
6279
  suggestedImport: policy.to,
6156
6280
  imported: usage.imported,
6157
6281
  local: usage.local,
6158
- rawValue: usage.source
6282
+ rawValue: usage.source,
6283
+ ...policy.bridge ? {
6284
+ canonicalBridge: true,
6285
+ canonicalTarget: policy.bridge.componentKey,
6286
+ suggestedComponent: policy.bridge.localExportName,
6287
+ implementationFiles: policy.bridge.implementationFiles,
6288
+ decisionSource: policy.bridge.decisionSource
6289
+ } : {}
6159
6290
  }
6160
6291
  })
6161
6292
  );
@@ -6163,6 +6294,12 @@ function ruleJsxPreferredImportPath(ix) {
6163
6294
  }
6164
6295
  return findings;
6165
6296
  }
6297
+ function sameRepoPath(left, right) {
6298
+ return normalizeRepoPath(left) === normalizeRepoPath(right);
6299
+ }
6300
+ function normalizeRepoPath(path) {
6301
+ return path.replace(/\\/gu, "/").replace(/^\.\//u, "");
6302
+ }
6166
6303
 
6167
6304
  // src/rules/props-invalid-value.ts
6168
6305
  var RULE_ID8 = "props/invalid-value";
@@ -6373,13 +6510,36 @@ function srgbGamma(linear) {
6373
6510
  return clamped <= 31308e-7 ? 12.92 * clamped : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
6374
6511
  }
6375
6512
 
6513
+ // src/rules/token-candidates.ts
6514
+ function normalizedTokenValue(token) {
6515
+ const value = token.value.trim();
6516
+ return token.category === "color" ? normalizeColor(value) : value.replace(/\s+/g, " ");
6517
+ }
6518
+ function localTokenCandidates(ix, category) {
6519
+ const all = ix.tokens.list();
6520
+ const upstreamByName = /* @__PURE__ */ new Map();
6521
+ for (const token of all) {
6522
+ if (token.role !== "upstream") continue;
6523
+ const matches = upstreamByName.get(token.name) ?? [];
6524
+ matches.push(token);
6525
+ upstreamByName.set(token.name, matches);
6526
+ }
6527
+ return all.filter((token) => {
6528
+ if (token.role === "upstream" || category !== void 0 && token.category !== category) {
6529
+ return false;
6530
+ }
6531
+ const matches = upstreamByName.get(token.upstreamName ?? token.name) ?? [];
6532
+ return matches.length !== 1 || normalizedTokenValue(token) === normalizedTokenValue(matches[0]);
6533
+ });
6534
+ }
6535
+
6376
6536
  // src/rules/styles-no-raw-color.ts
6377
6537
  var RULE_ID9 = "styles/no-raw-color";
6378
6538
  var RULE_VERSION10 = "1";
6379
6539
  function ruleStylesNoRawColor(ix) {
6380
6540
  const policy = ix.policy.rawColorPolicy();
6381
6541
  if (!policy) return [];
6382
- const colorTokens = ix.tokens.byCategory("color");
6542
+ const colorTokens = localTokenCandidates(ix, "color");
6383
6543
  const preferLabel = policy.prefer === "token" ? "design token" : "CSS variable";
6384
6544
  const findings = [];
6385
6545
  const advisorySeverity = policy.severity === "error" ? "warn" : policy.severity;
@@ -6633,7 +6793,7 @@ function normalizeColor2(value) {
6633
6793
  return lower;
6634
6794
  }
6635
6795
  function buildTokenFix(property, rawValue, rawColor, match, ix) {
6636
- const reference = tokenReference(match.token.name);
6796
+ const reference = tokenReference(match.token);
6637
6797
  const value = replaceRawColor(rawValue, rawColor, reference);
6638
6798
  return emitFix(
6639
6799
  {
@@ -6668,8 +6828,8 @@ function ruleStylesNoRawDimensions(ix) {
6668
6828
  const policy = ix.policy.rawDimensionPolicy();
6669
6829
  if (!policy) return [];
6670
6830
  const appliesTo = new Set(policy.appliesTo);
6671
- const spacingTokens = ix.tokens.byCategory("spacing");
6672
- const radiusTokens = ix.tokens.byCategory("radius");
6831
+ const spacingTokens = localTokenCandidates(ix, "spacing");
6832
+ const radiusTokens = localTokenCandidates(ix, "radius");
6673
6833
  const preferLabel = policy.prefer === "token" ? "design token" : "CSS variable";
6674
6834
  const findings = [];
6675
6835
  for (const decl of ix.byKind("style_declaration")) {
@@ -6880,9 +7040,7 @@ function normalizeDimension(value) {
6880
7040
  return `${parsed.value}${parsed.unit ?? "px"}`;
6881
7041
  }
6882
7042
  function tokenVar(token) {
6883
- if (token.name.startsWith("$")) return token.name;
6884
- const cssVarName = token.name.startsWith("--") ? token.name : `--${token.name.replace(/\./g, "-")}`;
6885
- return `var(${cssVarName})`;
7043
+ return tokenReference(token);
6886
7044
  }
6887
7045
 
6888
7046
  // src/rules/spacing-resolution.ts
@@ -6920,7 +7078,7 @@ function resolveSpacingValue(input) {
6920
7078
  continue;
6921
7079
  }
6922
7080
  anyViolation = true;
6923
- const reference = tokenReference(exactToken.name);
7081
+ const reference = tokenReference(exactToken);
6924
7082
  if (!normalized.deterministicFix) allValuePreserving = false;
6925
7083
  suggestedParts.push(reference);
6926
7084
  if (!firstViolation) {
@@ -6952,7 +7110,7 @@ function resolveSpacingValue(input) {
6952
7110
  } else {
6953
7111
  const nearestToken = input.tokens.get(Math.abs(nearest));
6954
7112
  suggestedToken = nearestToken?.name;
6955
- suggestedPart = nearestToken ? tokenReference(nearestToken.name) : input.bareNumberFix ? `${nearest}` : `${nearest}${input.scale.unit}`;
7113
+ suggestedPart = nearestToken ? tokenReference(nearestToken) : input.bareNumberFix ? `${nearest}` : `${nearest}${input.scale.unit}`;
6956
7114
  suggestedParts.push(suggestedPart);
6957
7115
  if (!normalized.deterministicFix) reliableSuggestion = false;
6958
7116
  }
@@ -7004,7 +7162,7 @@ function ruleStylesNoRawSpacing(ix) {
7004
7162
  const lookupFor = (scale) => {
7005
7163
  let lookup = lookupCache.get(scale.name);
7006
7164
  if (!lookup) {
7007
- lookup = buildSpacingTokenLookup(ix.tokens.byCategory("spacing"), scale);
7165
+ lookup = buildSpacingTokenLookup(localTokenCandidates(ix, "spacing"), scale);
7008
7166
  lookupCache.set(scale.name, lookup);
7009
7167
  }
7010
7168
  return lookup;
@@ -7042,7 +7200,7 @@ function checkDeclaration(ix, decl, lookupFor) {
7042
7200
  ruleId: RULE_ID11,
7043
7201
  ruleVersion: RULE_VERSION12,
7044
7202
  severity: policy.severity,
7045
- message: spacingMessage(decl.property, decl.value, allowed, scale.unit, checked),
7203
+ message: spacingMessage(decl.property, decl.value, allowed, scale, checked),
7046
7204
  location: decl.location,
7047
7205
  evidence: ix.evidence([decl.id, policy.id, scale.id]),
7048
7206
  fingerprintIdentity: {
@@ -7097,7 +7255,7 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
7097
7255
  ruleId: RULE_ID11,
7098
7256
  ruleVersion: RULE_VERSION12,
7099
7257
  severity: policy.severity,
7100
- message: spacingMessage(inline.property, inline.value, allowed, scale.unit, checked),
7258
+ message: spacingMessage(inline.property, inline.value, allowed, scale, checked),
7101
7259
  location: node.location,
7102
7260
  evidence: ix.evidence(evidenceIds),
7103
7261
  fingerprintIdentity: {
@@ -7138,21 +7296,21 @@ function buildSpacingFix(property, checked, ix) {
7138
7296
  deterministic: true
7139
7297
  },
7140
7298
  {
7141
- symbols: tokenSymbolsInText(checked.suggestedValue),
7299
+ symbols: checked.matchedToken ? [checked.matchedToken] : tokenSymbolsInText(checked.suggestedValue),
7142
7300
  editKind: "replaceStyleValue",
7143
7301
  valuePreserving: checked.deterministicFix
7144
7302
  },
7145
7303
  ix
7146
7304
  );
7147
7305
  }
7148
- function spacingMessage(property, value, allowed, unit, checked) {
7306
+ function spacingMessage(property, value, allowed, scale, checked) {
7149
7307
  if (checked.reason === "token-equivalent" && checked.matchedToken) {
7150
7308
  return `\`${property}: ${value}\` is a raw literal that matches token \`${checked.matchedToken}\`. Reference the token instead.`;
7151
7309
  }
7152
7310
  const sample = allowed.slice().sort((a, b) => a - b).slice(0, 6);
7153
- const suffix = `${sample.join(unit + ", ")}${unit}`;
7311
+ const suffix = `${sample.join(scale.unit + ", ")}${scale.unit}`;
7154
7312
  const ellipsis = allowed.length > sample.length ? ", \u2026" : "";
7155
- return `\`${property}: ${value}\` is not on the spacing scale. Allowed: ${suffix}${ellipsis}.`;
7313
+ return `\`${property}: ${value}\` is not on the \`${scale.name}\` scale. Allowed: ${suffix}${ellipsis}. Configure \`govern.scales.${scale.name}\` to change it.`;
7156
7314
  }
7157
7315
 
7158
7316
  // src/rules/styles-no-raw-typography.ts
@@ -7254,7 +7412,7 @@ function ruleStylesNoRawTypography(ix) {
7254
7412
  }
7255
7413
  function buildTypographyTokenLookup(ix, scale) {
7256
7414
  const out = /* @__PURE__ */ new Map();
7257
- for (const token of ix.tokens.byCategory("typography")) {
7415
+ for (const token of localTokenCandidates(ix, "typography")) {
7258
7416
  const parsed = parseLengthValue(token.value);
7259
7417
  if (!parsed || parsed.unit === null) continue;
7260
7418
  const normalized = normalizeLengthForScale(parsed, scale);
@@ -7276,7 +7434,7 @@ function checkFontSize(raw, allowed, scale, tokens, bareNumberFix = false) {
7276
7434
  if (matchesScale(normalized.value, allowed)) {
7277
7435
  if (!exactToken) return null;
7278
7436
  return {
7279
- suggestedValue: tokenReference(exactToken.name),
7437
+ suggestedValue: tokenReference(exactToken),
7280
7438
  fixEmittable: true,
7281
7439
  deterministicFix: normalized.deterministicFix,
7282
7440
  reason: "token-equivalent",
@@ -7288,7 +7446,7 @@ function checkFontSize(raw, allowed, scale, tokens, bareNumberFix = false) {
7288
7446
  return { fixEmittable: false, deterministicFix: false, reason: "off-scale" };
7289
7447
  }
7290
7448
  const nearestToken = tokens.get(Math.abs(nearest));
7291
- const suggestedValue = nearestToken ? tokenReference(nearestToken.name) : bareNumberFix ? `${nearest}` : `${nearest}${scale.unit}`;
7449
+ const suggestedValue = nearestToken ? tokenReference(nearestToken) : bareNumberFix ? `${nearest}` : `${nearest}${scale.unit}`;
7292
7450
  return {
7293
7451
  suggestedValue,
7294
7452
  fixEmittable: normalized.deterministicFix,
@@ -7307,7 +7465,7 @@ function buildFix(property, checked, ix) {
7307
7465
  deterministic: true
7308
7466
  },
7309
7467
  {
7310
- symbols: tokenSymbolsInText(checked.suggestedValue),
7468
+ symbols: checked.matchedToken ? [checked.matchedToken] : tokenSymbolsInText(checked.suggestedValue),
7311
7469
  editKind: "replaceStyleValue",
7312
7470
  valuePreserving: checked.deterministicFix
7313
7471
  },
@@ -7986,7 +8144,7 @@ var SASS_FILE = /\.(scss|sass)$/i;
7986
8144
  function ruleTokensRequireDualFallback(ix) {
7987
8145
  const policy = ix.policy.ruleConfig(RULE_ID20);
7988
8146
  if (!policy?.enabled) return [];
7989
- const tokensByCssVariable = indexTokensByCssVariable(ix.tokens.list());
8147
+ const tokensByCssVariable = indexTokensByCssVariable(localTokenCandidates(ix));
7990
8148
  const findings = [];
7991
8149
  for (const decl of ix.byKind("style_declaration")) {
7992
8150
  if (!SASS_FILE.test(decl.file)) continue;
@@ -8135,6 +8293,57 @@ function skipToCloseParen(value, openParenIndex) {
8135
8293
  return value.length;
8136
8294
  }
8137
8295
 
8296
+ // src/rules/tokens-upstream-drift.ts
8297
+ var RULE_ID22 = "tokens/upstream-drift";
8298
+ var RULE_VERSION23 = "1";
8299
+ function ruleTokensUpstreamDrift(ix) {
8300
+ const local = ix.tokens.list().filter((token) => token.role !== "upstream");
8301
+ const upstream = ix.tokens.list().filter((token) => token.role === "upstream");
8302
+ const upstreamByName = /* @__PURE__ */ new Map();
8303
+ for (const token of upstream) {
8304
+ const matches = upstreamByName.get(token.name) ?? [];
8305
+ matches.push(token);
8306
+ upstreamByName.set(token.name, matches);
8307
+ }
8308
+ const findings = [];
8309
+ for (const token of local) {
8310
+ const upstreamName = token.upstreamName ?? token.name;
8311
+ const matches = (upstreamByName.get(upstreamName) ?? []).sort(
8312
+ (a, b) => (a.sourceIdentity ?? "").localeCompare(b.sourceIdentity ?? "")
8313
+ );
8314
+ if (matches.length !== 1) continue;
8315
+ const expected = matches[0];
8316
+ if (normalizedTokenValue(token) === normalizedTokenValue(expected)) continue;
8317
+ const location = token.location ?? {
8318
+ file: token.sourceIdentity ?? "tokens",
8319
+ line: 1,
8320
+ column: 1
8321
+ };
8322
+ findings.push(
8323
+ makeFinding({
8324
+ ruleId: RULE_ID22,
8325
+ ruleVersion: RULE_VERSION23,
8326
+ severity: "warn",
8327
+ message: `Local token ${token.name} is ${token.value}, but upstream ${upstreamName} is ${expected.value}.`,
8328
+ location,
8329
+ evidence: ix.evidence([token.id, expected.id]),
8330
+ fingerprintIdentity: {
8331
+ local: token.name,
8332
+ upstream: upstreamName,
8333
+ localSource: token.sourceIdentity,
8334
+ upstreamSource: expected.sourceIdentity
8335
+ },
8336
+ attributes: {
8337
+ local: { name: token.name, value: token.value, location: token.location },
8338
+ upstream: { name: expected.name, value: expected.value, location: expected.location },
8339
+ autoFix: false
8340
+ }
8341
+ })
8342
+ );
8343
+ }
8344
+ return findings;
8345
+ }
8346
+
8138
8347
  // src/rules/emit-gate.ts
8139
8348
  var BLOCKING_RULE_ALLOWLIST = /* @__PURE__ */ new Set([
8140
8349
  "styles/no-raw-color",
@@ -8175,6 +8384,8 @@ var RULE_TIER = {
8175
8384
  // canonical-component bypass (FUI1004)
8176
8385
  "tokens/css-vars-must-be-defined": "contract",
8177
8386
  // token drift (FUI2015)
8387
+ "tokens/upstream-drift": "contract",
8388
+ // authored local/upstream value drift (FUI2017)
8178
8389
  // ---- Tier B — generic hygiene -----------------------------------------
8179
8390
  "components/unknown-prop": "hygiene",
8180
8391
  "components/forbidden-prop-value": "hygiene",
@@ -8218,7 +8429,8 @@ function hygieneTierRuleIds() {
8218
8429
  var VOCABULARY_ORDER = [
8219
8430
  "components/shadow-component",
8220
8431
  "components/prefer-library",
8221
- "tokens/css-vars-must-be-defined"
8432
+ "tokens/css-vars-must-be-defined",
8433
+ "tokens/upstream-drift"
8222
8434
  ];
8223
8435
  function vocabularyRank(ruleId) {
8224
8436
  const index = VOCABULARY_ORDER.indexOf(ruleId);
@@ -8342,6 +8554,11 @@ var RULES = [
8342
8554
  version: "1",
8343
8555
  run: ruleTokensCssVarsMustBeDefined
8344
8556
  },
8557
+ {
8558
+ id: "tokens/upstream-drift",
8559
+ version: "1",
8560
+ run: ruleTokensUpstreamDrift
8561
+ },
8345
8562
  {
8346
8563
  id: "theme/no-theme-coupled-literal",
8347
8564
  version: "1",
@@ -8406,6 +8623,207 @@ function policyDeclaresCssVars(policy) {
8406
8623
  function dedupe(values) {
8407
8624
  return [...new Set(values)];
8408
8625
  }
8626
+ var RULE_FAMILY_IDS = /* @__PURE__ */ new Set(["tokens/hardcoded-values", "components/usage", "a11y/wcag"]);
8627
+ var CONSUMED_RULE_IDS = new Set(Object.keys(RULE_TIER));
8628
+ var RECOGNIZED_RULE_IDS = /* @__PURE__ */ new Set([...CONSUMED_RULE_IDS, ...RULE_FAMILY_IDS]);
8629
+ var RULE_CONFIG_KEYS = /* @__PURE__ */ new Set(["enabled", "severity", "options"]);
8630
+ var PASSTHROUGH_KEYS = [
8631
+ {
8632
+ path: ["tokens"],
8633
+ keys: [
8634
+ "include",
8635
+ "sources",
8636
+ "packages",
8637
+ "aliases",
8638
+ "upstream",
8639
+ "exclude",
8640
+ "themeSelectors",
8641
+ "enabled",
8642
+ "format",
8643
+ "namespace"
8644
+ ]
8645
+ },
8646
+ {
8647
+ path: ["screenshots"],
8648
+ keys: ["viewport", "threshold", "delay", "outputDir", "themes"]
8649
+ },
8650
+ {
8651
+ path: ["service"],
8652
+ keys: ["poolSize", "idleTimeout"]
8653
+ },
8654
+ {
8655
+ path: ["registry"],
8656
+ keys: ["requireStory", "publicOnly", "categoryDepth", "includeProps", "embedFragments"]
8657
+ },
8658
+ {
8659
+ path: ["govern", "tailwind"],
8660
+ keys: ["palette"]
8661
+ },
8662
+ {
8663
+ path: ["govern", "agent"],
8664
+ keys: ["repairOrder"]
8665
+ },
8666
+ {
8667
+ path: ["govern", "ci"],
8668
+ keys: ["failOnWarnings"]
8669
+ }
8670
+ ];
8671
+ function objectRecord(value) {
8672
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
8673
+ return value;
8674
+ }
8675
+ function valueAtPath(value, path) {
8676
+ let current = value;
8677
+ for (const segment of path) {
8678
+ const record = objectRecord(current);
8679
+ if (!record) return void 0;
8680
+ current = record[segment];
8681
+ }
8682
+ return current;
8683
+ }
8684
+ function configPath(path) {
8685
+ return path.reduce(
8686
+ (output, segment) => typeof segment === "number" ? `${output}[${segment}]` : output ? `${output}.${segment}` : segment,
8687
+ ""
8688
+ );
8689
+ }
8690
+ function unconsumedKeyDiagnostic(path) {
8691
+ const renderedPath = configPath(path);
8692
+ return {
8693
+ code: "FUI9004",
8694
+ kind: "unconsumed-key",
8695
+ severity: "warn",
8696
+ path: renderedPath,
8697
+ message: `${renderedPath} is not consumed by Fragments and has no effect. Remove it or use a supported config key.`
8698
+ };
8699
+ }
8700
+ function collectStrippedKeys(authored, parsed, path, diagnostics) {
8701
+ if (Array.isArray(authored)) {
8702
+ if (!Array.isArray(parsed)) return;
8703
+ authored.forEach((item, index) => {
8704
+ collectStrippedKeys(item, parsed[index], [...path, index], diagnostics);
8705
+ });
8706
+ return;
8707
+ }
8708
+ const authoredRecord = objectRecord(authored);
8709
+ const parsedRecord = objectRecord(parsed);
8710
+ if (!authoredRecord || !parsedRecord) return;
8711
+ for (const key of Object.keys(authoredRecord).sort()) {
8712
+ if (!Object.prototype.hasOwnProperty.call(parsedRecord, key)) {
8713
+ diagnostics.push(unconsumedKeyDiagnostic([...path, key]));
8714
+ continue;
8715
+ }
8716
+ collectStrippedKeys(authoredRecord[key], parsedRecord[key], [...path, key], diagnostics);
8717
+ }
8718
+ }
8719
+ function collectPassthroughKeys(authored, path, allowed, diagnostics) {
8720
+ const record = objectRecord(valueAtPath(authored, path));
8721
+ if (!record) return;
8722
+ for (const key of Object.keys(record).sort()) {
8723
+ if (!allowed.has(key)) diagnostics.push(unconsumedKeyDiagnostic([...path, key]));
8724
+ }
8725
+ }
8726
+ function collectRuleConfigKeys(authored, path, diagnostics) {
8727
+ const rules = objectRecord(valueAtPath(authored, path));
8728
+ if (!rules) return;
8729
+ for (const ruleId of Object.keys(rules).sort()) {
8730
+ if (!RECOGNIZED_RULE_IDS.has(ruleId)) {
8731
+ diagnostics.push(unconsumedKeyDiagnostic([...path, ruleId]));
8732
+ continue;
8733
+ }
8734
+ const config = objectRecord(rules[ruleId]);
8735
+ if (!config) continue;
8736
+ for (const key of Object.keys(config).sort()) {
8737
+ if (!RULE_CONFIG_KEYS.has(key)) {
8738
+ diagnostics.push(unconsumedKeyDiagnostic([...path, ruleId, key]));
8739
+ }
8740
+ }
8741
+ }
8742
+ }
8743
+ function collectPassthroughRecordValues(authored, path, allowed, diagnostics) {
8744
+ const entries = objectRecord(valueAtPath(authored, path));
8745
+ if (!entries) return;
8746
+ for (const [entryName, value] of Object.entries(entries).sort(
8747
+ ([left], [right]) => left.localeCompare(right)
8748
+ )) {
8749
+ const record = objectRecord(value);
8750
+ if (!record) continue;
8751
+ for (const key of Object.keys(record).sort()) {
8752
+ if (!allowed.has(key)) {
8753
+ diagnostics.push(unconsumedKeyDiagnostic([...path, entryName, key]));
8754
+ }
8755
+ }
8756
+ }
8757
+ }
8758
+ function stableConfigDiagnostics(diagnostics) {
8759
+ const unique = /* @__PURE__ */ new Map();
8760
+ for (const diagnostic of diagnostics) {
8761
+ unique.set(`${diagnostic.code}\0${diagnostic.path}`, diagnostic);
8762
+ }
8763
+ return [...unique.values()].sort(
8764
+ (left, right) => left.path.localeCompare(right.path) || left.code.localeCompare(right.code) || left.message.localeCompare(right.message)
8765
+ );
8766
+ }
8767
+ function detectUnconsumedConfigKeys(authoredConfig, parsedConfig) {
8768
+ const diagnostics = [];
8769
+ collectStrippedKeys(authoredConfig, parsedConfig, [], diagnostics);
8770
+ for (const boundary of PASSTHROUGH_KEYS) {
8771
+ collectPassthroughKeys(
8772
+ authoredConfig,
8773
+ boundary.path,
8774
+ new Set(boundary.keys),
8775
+ diagnostics
8776
+ );
8777
+ }
8778
+ collectRuleConfigKeys(authoredConfig, ["govern", "rules"], diagnostics);
8779
+ collectPassthroughRecordValues(
8780
+ authoredConfig,
8781
+ ["govern", "agents"],
8782
+ /* @__PURE__ */ new Set(["rules"]),
8783
+ diagnostics
8784
+ );
8785
+ collectPassthroughKeys(authoredConfig, ["govern", "audit"], /* @__PURE__ */ new Set(), diagnostics);
8786
+ collectPassthroughRecordValues(authoredConfig, ["govern", "runners"], /* @__PURE__ */ new Set(), diagnostics);
8787
+ return stableConfigDiagnostics(diagnostics);
8788
+ }
8789
+ function effectiveScaleBindings(policy) {
8790
+ const bindings = [];
8791
+ for (const style of policy?.styles ?? []) {
8792
+ if (style.kind === "style.rawSpacing.mustMatchScale") {
8793
+ bindings.push({
8794
+ label: "Spacing properties",
8795
+ mechanism: "style.rawSpacing.mustMatchScale",
8796
+ scale: style.scale
8797
+ });
8798
+ } else if (style.kind === "style.fontSize.mustMatchScale") {
8799
+ bindings.push({
8800
+ label: "Font-size properties",
8801
+ mechanism: "style.fontSize.mustMatchScale",
8802
+ scale: style.scale
8803
+ });
8804
+ }
8805
+ }
8806
+ return bindings;
8807
+ }
8808
+ function detectOrphanGovernanceScales(declaredPolicy, effectivePolicy) {
8809
+ const bindings = effectiveScaleBindings(effectivePolicy);
8810
+ const referenced = new Set(bindings.map((binding) => binding.scale));
8811
+ const diagnostics = [];
8812
+ for (const scale of Object.keys(declaredPolicy?.scales ?? {}).sort()) {
8813
+ if (referenced.has(scale)) continue;
8814
+ const path = `govern.scales.${scale}`;
8815
+ const preferred = (scale.toLowerCase().includes("spac") ? bindings.find((binding) => binding.mechanism === "style.rawSpacing.mustMatchScale") : void 0) ?? (scale.toLowerCase().includes("font") ? bindings.find((binding) => binding.mechanism === "style.fontSize.mustMatchScale") : void 0) ?? bindings[0];
8816
+ const remediation = preferred ? `${preferred.label} are bound to the scale named "${preferred.scale}" \u2014 rename the key to "${preferred.scale}" or bind it via ${preferred.mechanism}.` : `Bind it via style.rawSpacing.mustMatchScale or style.fontSize.mustMatchScale, or remove it.`;
8817
+ diagnostics.push({
8818
+ code: "FUI9005",
8819
+ kind: "orphan-scale",
8820
+ severity: "warn",
8821
+ path,
8822
+ message: `${path} is not referenced by any property policy. ${remediation}`
8823
+ });
8824
+ }
8825
+ return stableConfigDiagnostics(diagnostics);
8826
+ }
8409
8827
  function summarize(status, flags) {
8410
8828
  if (status === "inert") {
8411
8829
  return "Governance inert \u2014 no enforceable rule family is armed";
@@ -8421,6 +8839,7 @@ function summarize(status, flags) {
8421
8839
  }
8422
8840
  function evaluateGovernanceIntegrity(input) {
8423
8841
  const configs = effectiveRuleConfigs(input.policy);
8842
+ const configDiagnostics = stableConfigDiagnostics(input.configDiagnostics ?? []);
8424
8843
  const policyArmed = input.policy !== void 0 && input.policySource !== "none";
8425
8844
  const policyFamily = {
8426
8845
  id: "policy",
@@ -8431,19 +8850,23 @@ function evaluateGovernanceIntegrity(input) {
8431
8850
  policyFamily.reason = "no governance policy resolved";
8432
8851
  }
8433
8852
  const componentsConfig = configs.get("components/prefer-library");
8434
- const componentsArmed = componentsConfig?.enabled === true && hasEffectiveComponentVocabulary(componentsConfig.options);
8853
+ const confirmedBridges = input.policy?.canonicalBridges ?? [];
8854
+ const componentsArmed = confirmedBridges.length > 0 || componentsConfig?.enabled === true && hasEffectiveComponentVocabulary(componentsConfig.options);
8435
8855
  const componentsFamily = {
8436
8856
  id: "components",
8437
8857
  armed: componentsArmed,
8438
- rules: ["components/prefer-library"]
8858
+ rules: [
8859
+ ...confirmedBridges.length > 0 ? ["imports/preferred-path"] : [],
8860
+ ...componentsConfig?.enabled === true ? ["components/prefer-library"] : []
8861
+ ]
8439
8862
  };
8440
8863
  if (!componentsArmed) {
8441
8864
  if (componentsConfig?.enabled === true) {
8442
8865
  componentsFamily.reason = "components/prefer-library enabled but no effective canonical source";
8443
- componentsFamily.remediation = "add govern.canonicalSources (a directory source, or npm/registry with a non-empty include) or designSystem.path/packageName";
8866
+ componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
8444
8867
  } else {
8445
8868
  componentsFamily.reason = "components/prefer-library not enabled";
8446
- componentsFamily.remediation = "add govern.canonicalSources (a directory source, or npm/registry with a non-empty include) or designSystem.path/packageName";
8869
+ componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
8447
8870
  }
8448
8871
  }
8449
8872
  const cssVarsActive = input.cssVarsActive ?? policyDeclaresCssVars(input.policy);
@@ -8517,6 +8940,17 @@ function evaluateGovernanceIntegrity(input) {
8517
8940
  families.map((family) => family.remediation).filter((remediation) => remediation !== void 0)
8518
8941
  );
8519
8942
  const summary = summarize(status, { componentsArmed, tokensArmed, blockingArmed });
8943
+ const configuredRuleCount = [...configs.keys()].filter(
8944
+ (ruleId) => CONSUMED_RULE_IDS.has(ruleId)
8945
+ ).length;
8946
+ const activeRuleCount = [...configs.entries()].filter(
8947
+ ([ruleId, config]) => CONSUMED_RULE_IDS.has(ruleId) && config.enabled
8948
+ ).length;
8949
+ const roster = {
8950
+ configured: configuredRuleCount + configDiagnostics.length,
8951
+ active: activeRuleCount,
8952
+ inert: configDiagnostics.length
8953
+ };
8520
8954
  return {
8521
8955
  status,
8522
8956
  declared: input.declared,
@@ -8525,10 +8959,80 @@ function evaluateGovernanceIntegrity(input) {
8525
8959
  families,
8526
8960
  armed,
8527
8961
  summary,
8528
- remediations
8962
+ remediations,
8963
+ configDiagnostics,
8964
+ roster
8529
8965
  };
8530
8966
  }
8531
8967
 
8968
+ // src/canonical-bridge.ts
8969
+ function canonicalBridgeContractMappings(bridges) {
8970
+ return (bridges ?? []).map((bridge) => ({
8971
+ component: `${bridge.underlying.packageName}#${bridge.underlying.exportName}`,
8972
+ canonical: bridge.local.componentKey,
8973
+ status: "confirmed",
8974
+ importPath: bridge.local.moduleSpecifier,
8975
+ bridge: {
8976
+ underlyingPackageName: bridge.underlying.packageName,
8977
+ underlyingExportName: bridge.underlying.exportName,
8978
+ localComponentKey: bridge.local.componentKey,
8979
+ localExportName: bridge.local.exportName,
8980
+ implementationFiles: [...bridge.local.implementationFiles],
8981
+ decisionSource: bridge.decision.source
8982
+ }
8983
+ }));
8984
+ }
8985
+
8986
+ // src/effective-governance-inputs.ts
8987
+ var EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA = "effective-governance-inputs:v1";
8988
+ function compileEffectiveGovernanceInputs(options) {
8989
+ const projection = {
8990
+ schema: EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA,
8991
+ policySource: options.policySource,
8992
+ config: {
8993
+ path: options.configPath ? normalizePath2(options.configPath) : null,
8994
+ digest: options.configDigest
8995
+ },
8996
+ components: {
8997
+ canonicalSources: sortRecords(options.policy?.canonicalSources ?? []),
8998
+ canonicalBridges: sortRecords(options.policy?.canonicalBridges ?? []),
8999
+ definitionFiles: normalizePaths(options.componentDefinitionFiles)
9000
+ },
9001
+ tokens: {
9002
+ declared: normalizePaths(options.tokens.declared),
9003
+ loadedDefinitions: options.tokens.loadedDefinitions,
9004
+ diagnostics: [...options.tokens.diagnostics].sort(),
9005
+ ...options.tokens.catalog ? {
9006
+ catalog: {
9007
+ ...options.tokens.catalog,
9008
+ configuredSources: normalizePaths(options.tokens.catalog.configuredSources),
9009
+ parsedSources: normalizePaths(options.tokens.catalog.parsedSources),
9010
+ definitions: sortRecords(options.tokens.catalog.definitions),
9011
+ diagnostics: sortRecords(options.tokens.catalog.diagnostics),
9012
+ references: sortRecords(options.tokens.catalog.references),
9013
+ ...options.tokens.catalog.sourceStates ? { sourceStates: sortRecords(options.tokens.catalog.sourceStates) } : {}
9014
+ }
9015
+ } : {}
9016
+ },
9017
+ eligibility: {
9018
+ include: normalizePaths(options.eligibility.include),
9019
+ exclude: normalizePaths(options.eligibility.exclude)
9020
+ }
9021
+ };
9022
+ return { ...projection, inputsHash: contractHash(projection) };
9023
+ }
9024
+ function normalizePaths(values) {
9025
+ return [...new Set(values.map(normalizePath2))].sort();
9026
+ }
9027
+ function normalizePath2(value) {
9028
+ return value.replace(/\\/gu, "/").replace(/^\.\//u, "");
9029
+ }
9030
+ function sortRecords(values) {
9031
+ return [...values].sort(
9032
+ (left, right) => canonicalPreimage(left).localeCompare(canonicalPreimage(right))
9033
+ );
9034
+ }
9035
+
8532
9036
  // src/contract/preimage.ts
8533
9037
  var CONTRACT_DOMAINS = ["components", "tokens", "canonicalMap", "policy"];
8534
9038
  var CONTRACT_PREIMAGE_SCHEMA = "fcid-preimage:v1";
@@ -8584,6 +9088,15 @@ function projectCanonicalMap(mappings) {
8584
9088
  component: mapping.component,
8585
9089
  canonical: mapping.canonical,
8586
9090
  importPath: mapping.importPath ? projectV1OwnedImportIdentity(mapping.importPath) : void 0,
9091
+ bridge: mapping.bridge ? {
9092
+ underlyingPackageName: projectV1OwnedImportIdentity(
9093
+ mapping.bridge.underlyingPackageName
9094
+ ),
9095
+ underlyingExportName: mapping.bridge.underlyingExportName,
9096
+ localComponentKey: projectV1OwnedComponentId(mapping.bridge.localComponentKey),
9097
+ localExportName: mapping.bridge.localExportName,
9098
+ implementationFiles: [...mapping.bridge.implementationFiles].sort()
9099
+ } : void 0,
8587
9100
  resolves: mapping.resolves ? sortCanonical(mapping.resolves) : void 0,
8588
9101
  propMapping: mapping.propMapping ? sortCanonical(
8589
9102
  mapping.propMapping.map((entry) => ({
@@ -9016,6 +9529,7 @@ export {
9016
9529
  DEFAULTS,
9017
9530
  DEFAULT_ENHANCED_STYLE_PROPERTIES,
9018
9531
  DEFAULT_STYLE_PROPERTIES,
9532
+ EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA,
9019
9533
  EVIDENCE_ORDER,
9020
9534
  EXPLAIN_URL_BASE,
9021
9535
  FRAGMENTS_INTERNAL_RULE_IDS,
@@ -9070,6 +9584,9 @@ export {
9070
9584
  byRuleId,
9071
9585
  calculateDeltaE,
9072
9586
  canBlock,
9587
+ canonicalBridgeContractMappings,
9588
+ canonicalBridgeV1Schema,
9589
+ canonicalDirectionConflictsTarget,
9073
9590
  canonicalJson,
9074
9591
  canonicalPreimage,
9075
9592
  canonicalizeOwnedComponentId,
@@ -9092,6 +9609,7 @@ export {
9092
9609
  compareStylesWithTokens,
9093
9610
  compileBlock,
9094
9611
  compileComponentFacts,
9612
+ compileEffectiveGovernanceInputs,
9095
9613
  compileFragment,
9096
9614
  compileGlobalGovernanceFacts,
9097
9615
  compileRecipe,
@@ -9104,6 +9622,7 @@ export {
9104
9622
  compositionMetadataSchema,
9105
9623
  compositionPatternSchema,
9106
9624
  computeRegistryHash,
9625
+ configDeclarationForDiagnostics,
9107
9626
  containsTailwindV4Theme,
9108
9627
  contractComponentsFromFragments,
9109
9628
  contractHash,
@@ -9114,7 +9633,9 @@ export {
9114
9633
  defineConfig,
9115
9634
  defineFragment,
9116
9635
  defineRecipe,
9636
+ detectOrphanGovernanceScales,
9117
9637
  detectSubComponentPaths,
9638
+ detectUnconsumedConfigKeys,
9118
9639
  diffContractDomains,
9119
9640
  discoverComponents,
9120
9641
  dtcgTokenFileSchema,
@@ -9287,6 +9808,7 @@ export {
9287
9808
  parseTokenFile,
9288
9809
  parseTokens,
9289
9810
  portableRepoPathError,
9811
+ projectCanonicalDirectionConflicts,
9290
9812
  projectContractPreimage,
9291
9813
  projectV1OwnedComponentId,
9292
9814
  projectV1OwnedImportIdentity,