@usefragments/core 1.0.0 → 1.2.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 (42) hide show
  1. package/dist/{chunk-R3X7UBHU.js → chunk-BQQTMGLA.js} +24 -2
  2. package/dist/chunk-BQQTMGLA.js.map +1 -0
  3. package/dist/{chunk-243QYRUF.js → chunk-DBVTSUMT.js} +71 -44
  4. package/dist/chunk-DBVTSUMT.js.map +1 -0
  5. package/dist/codes/index.d.ts +1 -1
  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-BOa3KyiJ.d.ts → governance-DYSirwJu.d.ts} +22 -1
  10. package/dist/index.d.ts +8 -6
  11. package/dist/index.js +156 -19
  12. package/dist/index.js.map +1 -1
  13. package/dist/react-types.d.ts +1 -1
  14. package/dist/schemas/index.d.ts +797 -4
  15. package/dist/schemas/index.js +9 -1
  16. package/dist/test-utils.d.ts +1 -1
  17. package/package.json +1 -1
  18. package/src/agent-format.test.ts +72 -2
  19. package/src/agent-format.ts +10 -5
  20. package/src/codes/__tests__/codes.test.ts +57 -7
  21. package/src/codes/codes.ts +44 -42
  22. package/src/conform-prove.ts +4 -8
  23. package/src/contract/index.ts +3 -0
  24. package/src/contract/preimage.test.ts +50 -0
  25. package/src/contract/preimage.ts +56 -0
  26. package/src/facts/builders.ts +2 -0
  27. package/src/facts/fact-index.ts +4 -0
  28. package/src/facts/facts.test.ts +49 -0
  29. package/src/facts/types.ts +1 -0
  30. package/src/governance-integrity.test.ts +2 -0
  31. package/src/governance-integrity.ts +7 -1
  32. package/src/index.ts +9 -0
  33. package/src/rules/components-prefer-library.ts +165 -7
  34. package/src/rules/emit-gate.test.ts +24 -2
  35. package/src/rules/emit-gate.ts +15 -7
  36. package/src/rules/fix-availability.ts +33 -0
  37. package/src/rules/rules.test.ts +178 -23
  38. package/src/rules/taxonomy.test.ts +6 -3
  39. package/src/rules/tiers.ts +1 -1
  40. package/src/schemas/index.ts +25 -1
  41. package/dist/chunk-243QYRUF.js.map +0 -1
  42. package/dist/chunk-R3X7UBHU.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import {
2
- ComponentGraphEngine
3
- } from "./chunk-SH4KPIYH.js";
4
1
  import {
5
2
  generateContext
6
3
  } from "./chunk-X34IA4LR.js";
4
+ import {
5
+ ComponentGraphEngine
6
+ } from "./chunk-SH4KPIYH.js";
7
7
  import {
8
8
  resolveArea
9
9
  } from "./chunk-3LLRNCPX.js";
@@ -109,9 +109,13 @@ import {
109
109
  ownedImportsEqual,
110
110
  resolveComponentGovernance,
111
111
  scaleGovernanceRecordSchema
112
- } from "./chunk-243QYRUF.js";
112
+ } from "./chunk-DBVTSUMT.js";
113
113
  import {
114
+ AGENT_FORMAT_SCHEMA_VERSION,
115
+ agentErrorEnvelopeSchema,
114
116
  agentFormatSchema,
117
+ agentIntegritySchema,
118
+ agentOutputSchema,
115
119
  factEvidenceSchema,
116
120
  factLocationSchema,
117
121
  findingFixSchema,
@@ -130,7 +134,7 @@ import {
130
134
  suppressionDirectiveSchema,
131
135
  validatorResultSchema,
132
136
  violationSchema
133
- } from "./chunk-R3X7UBHU.js";
137
+ } from "./chunk-BQQTMGLA.js";
134
138
  import {
135
139
  OWNED_PACKAGE_IDENTITY_EPOCH,
136
140
  OWNED_PACKAGE_IDENTITY_SOURCE_SHA256,
@@ -382,7 +386,8 @@ async function proveCompliant(input, callbacks, options = {}) {
382
386
 
383
387
  // src/agent-format.ts
384
388
  function buildAgentFormat(input) {
385
- const score = scoreFindings(input.findings);
389
+ const integrityFailed = !input.integrity.healthy || input.integrity.inert;
390
+ const score = integrityFailed ? 0 : scoreFindings(input.findings);
386
391
  const visibleFindings = uniqueFindings(input.findings).slice(0, input.maxFindings ?? 50);
387
392
  const agentFindings = visibleFindings.map((finding) => ({
388
393
  code: finding.code,
@@ -398,10 +403,11 @@ function buildAgentFormat(input) {
398
403
  }));
399
404
  const hasErrors = input.findings.some((finding) => severityLevel(finding.severity) === "error");
400
405
  return {
401
- schemaVersion: "typestyle.agent.v1",
402
- passed: (input.passed ?? true) && !hasErrors,
406
+ schemaVersion: AGENT_FORMAT_SCHEMA_VERSION,
407
+ passed: !integrityFailed && (input.passed ?? true) && !hasErrors,
403
408
  score,
404
- summary: agentSummary(input.findings.length, input.filesScanned, score),
409
+ summary: integrityFailed ? `failed with score ${score}; ${input.integrity.reasons.join("; ") || "governance integrity is unhealthy"}` : agentSummary(input.findings.length, input.filesScanned, score),
410
+ integrity: input.integrity,
405
411
  fix_first: agentFindings.filter((finding) => finding.plan.confidence >= 0.8),
406
412
  remaining: agentFindings.filter((finding) => finding.plan.confidence < 0.8),
407
413
  suppression_template: {
@@ -4994,7 +5000,8 @@ function ruleComponentsPreferLibrary(ix) {
4994
5000
  if (node.element.includes(".")) continue;
4995
5001
  const props = propsByNode.get(node.id) ?? [];
4996
5002
  const textChildren = textByNode.get(node.id) ?? [];
4997
- const imported = importsByFileAndLocal.get(node.file)?.get(node.element);
5003
+ const importsForFile = importsByFileAndLocal.get(node.file);
5004
+ const imported = importsForFile?.get(node.element);
4998
5005
  const nodeInputType = node.element === "input" ? readStaticStringProp(props, "type") : void 0;
4999
5006
  const mapped = findMapping(node, nodeInputType, mappings);
5000
5007
  if (mapped && imported && isMappedCanonicalImport(imported, mapped)) continue;
@@ -5005,9 +5012,11 @@ function ruleComponentsPreferLibrary(ix) {
5005
5012
  const suggestedComponent2 = mapped.name;
5006
5013
  const suggestedImport2 = importPath ?? "the canonical library";
5007
5014
  const propMapping = mapped.propMapping ?? [];
5015
+ const propCompatibility = assessMappedPropCompatibility(props, propMapping);
5008
5016
  const roleMatch = isRoleMatch(node, mapped);
5009
5017
  const precisionTier2 = roleMatch ? "role-reimpl" : "exact-html";
5010
5018
  if (imported && importPath && !ownedImportsEqual(imported.source, importPath) && node.element === suggestedComponent2) {
5019
+ if (!isLocalExactComponentImportRedirectCandidate(imported, suggestedComponent2)) continue;
5011
5020
  const key = `${imported.file}\0${imported.local}\0${imported.source}\0${importPath}`;
5012
5021
  if (seenImportFixes.has(key)) continue;
5013
5022
  seenImportFixes.add(key);
@@ -5015,8 +5024,8 @@ function ruleComponentsPreferLibrary(ix) {
5015
5024
  makeFinding({
5016
5025
  ruleId: RULE_ID2,
5017
5026
  ruleVersion: RULE_VERSION2,
5018
- severity: policy.severity ?? "warn",
5019
- message: `<${node.element}> should import from ${importPath}.`,
5027
+ severity: capAdvisorySeverity(policy.severity),
5028
+ message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
5020
5029
  location: imported.location,
5021
5030
  evidence: ix.evidence([node.id, imported.id, policy.id]),
5022
5031
  fingerprintIdentity: {
@@ -5030,14 +5039,16 @@ function ruleComponentsPreferLibrary(ix) {
5030
5039
  title: `Replace import path with "${importPath}"`,
5031
5040
  from: imported.source,
5032
5041
  to: importPath,
5033
- deterministic: true
5042
+ deterministic: false
5034
5043
  },
5035
5044
  attributes: {
5036
5045
  rawValue: imported.source,
5037
5046
  suggestedComponent: suggestedComponent2,
5038
5047
  suggestedImport: importPath,
5039
5048
  canonical: mapped.canonical,
5040
- confidence: mapped.confidence
5049
+ confidence: mapped.confidence,
5050
+ advisory: true,
5051
+ contractProof: "missing"
5041
5052
  }
5042
5053
  })
5043
5054
  );
@@ -5067,7 +5078,11 @@ function ruleComponentsPreferLibrary(ix) {
5067
5078
  title: `Replace <${node.element}> with <${suggestedComponent2}>`,
5068
5079
  from: node.element,
5069
5080
  to: suggestedComponent2,
5070
- deterministic: true
5081
+ // The fact IR cannot prove that it observed every JSX
5082
+ // attribute shape (notably dynamic inline styles). The
5083
+ // conform AST preflight may promote this guided fix only
5084
+ // after it proves the complete prop surface directly.
5085
+ deterministic: false
5071
5086
  }
5072
5087
  } : {},
5073
5088
  attributes: {
@@ -5077,8 +5092,12 @@ function ruleComponentsPreferLibrary(ix) {
5077
5092
  canonical: mapped.canonical,
5078
5093
  confidence: mapped.confidence,
5079
5094
  propMapping,
5095
+ propCompatibility: propCompatibility.complete ? "observed-complete" : "incomplete",
5096
+ ...mapped.conformStatus === "confirmed" ? { canonicalMappingConfirmed: true } : {},
5097
+ ...propCompatibility.unprovenProps.length > 0 ? { unprovenProps: propCompatibility.unprovenProps } : {},
5080
5098
  precisionTier: precisionTier2,
5081
- ...roleMatch ? { matchedRole: node.role } : {}
5099
+ ...roleMatch ? { matchedRole: node.role } : {},
5100
+ ...isRawHtmlAdvisoryTier(precisionTier2) ? { advisory: true } : {}
5082
5101
  }
5083
5102
  })
5084
5103
  );
@@ -5121,6 +5140,47 @@ function ruleComponentsPreferLibrary(ix) {
5121
5140
  const suggestion = findSuggestionSource(suggestedComponent, sources);
5122
5141
  if (!suggestion) continue;
5123
5142
  if (!builtIn && !shouldSuggest(node, imported)) continue;
5143
+ if (node.element === suggestedComponent) {
5144
+ const importPath = canonicalImportPath(suggestion);
5145
+ if (!imported || !importPath || !isLocalExactComponentImportRedirectCandidate(imported, suggestedComponent)) {
5146
+ continue;
5147
+ }
5148
+ const key = `${imported.file}\0${imported.local}\0${imported.source}\0${importPath}`;
5149
+ if (seenImportFixes.has(key)) continue;
5150
+ seenImportFixes.add(key);
5151
+ findings.push(
5152
+ makeFinding({
5153
+ ruleId: RULE_ID2,
5154
+ ruleVersion: RULE_VERSION2,
5155
+ severity: capAdvisorySeverity(policy.severity),
5156
+ message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
5157
+ location: imported.location,
5158
+ evidence: ix.evidence([node.id, imported.id, policy.id]),
5159
+ fingerprintIdentity: {
5160
+ file: imported.file,
5161
+ local: imported.local,
5162
+ from: imported.source,
5163
+ to: importPath
5164
+ },
5165
+ fix: {
5166
+ kind: "replaceImport",
5167
+ title: `Replace import path with "${importPath}"`,
5168
+ from: imported.source,
5169
+ to: importPath,
5170
+ deterministic: false
5171
+ },
5172
+ attributes: {
5173
+ rawValue: imported.source,
5174
+ suggestedComponent,
5175
+ suggestedImport: importPath,
5176
+ suggestedImportSourceKind: suggestion.kind,
5177
+ advisory: true,
5178
+ contractProof: "missing"
5179
+ }
5180
+ })
5181
+ );
5182
+ continue;
5183
+ }
5124
5184
  if (suggestion.kind === "directory") {
5125
5185
  if (node.element === suggestedComponent) continue;
5126
5186
  if (!builtIn) continue;
@@ -5163,7 +5223,10 @@ function ruleComponentsPreferLibrary(ix) {
5163
5223
  title: `Replace ${rawElement} with <${suggestedComponent}>`,
5164
5224
  from: node.element,
5165
5225
  to: suggestedComponent,
5166
- deterministic: true
5226
+ // A canonical source proves that the target exists, not that
5227
+ // this native element's complete prop contract is compatible.
5228
+ // Only explicit canonical mappings carry that proof.
5229
+ deterministic: false
5167
5230
  }
5168
5231
  } : {},
5169
5232
  attributes: {
@@ -5172,6 +5235,7 @@ function ruleComponentsPreferLibrary(ix) {
5172
5235
  suggestedImport,
5173
5236
  suggestedImportSourceKind: suggestion.kind,
5174
5237
  precisionTier,
5238
+ propCompatibility: "unknown",
5175
5239
  ...builtIn?.matchedRole ? { matchedRole: builtIn.matchedRole } : {},
5176
5240
  ...builtIn?.inputType ? { matchedInputType: builtIn.inputType } : {},
5177
5241
  ...isRawHtmlAdvisoryTier(precisionTier) ? { advisory: true } : {}
@@ -5288,6 +5352,39 @@ function shouldSuggest(node, imported) {
5288
5352
  if (!/^[A-Z]/.test(node.element)) return false;
5289
5353
  return imported !== void 0;
5290
5354
  }
5355
+ function assessMappedPropCompatibility(props, mappings) {
5356
+ const byRawProp = new Map(mappings.map((mapping) => [mapping.rawProp, mapping]));
5357
+ const unprovenProps = props.flatMap((prop) => {
5358
+ if (prop.resolution === "spread") return [prop.prop];
5359
+ const mapping = byRawProp.get(prop.prop);
5360
+ if (!mapping || !mappingCanPreserveProp(mapping, prop)) return [prop.prop];
5361
+ return [];
5362
+ });
5363
+ return { complete: unprovenProps.length === 0, unprovenProps };
5364
+ }
5365
+ function mappingCanPreserveProp(mapping, prop) {
5366
+ const renames = mapping.rawProp !== mapping.canonicalProp;
5367
+ const valueMap = mapping.valueMap;
5368
+ const translatesValue = valueMap !== void 0 && Object.keys(valueMap).length > 0;
5369
+ if (renames && (!isIdentifier(mapping.rawProp) || !isIdentifier(mapping.canonicalProp))) {
5370
+ return false;
5371
+ }
5372
+ if (!translatesValue) return true;
5373
+ if (!isIdentifier(mapping.rawProp)) return false;
5374
+ return prop.resolution === "static" && typeof prop.value === "string" && Object.prototype.hasOwnProperty.call(valueMap, prop.value);
5375
+ }
5376
+ function isLocalExactComponentImportRedirectCandidate(usage, componentName) {
5377
+ return isUnaliasedNamedImport(usage, componentName) && isProjectLocalImportSource(usage.source);
5378
+ }
5379
+ function isUnaliasedNamedImport(usage, componentName) {
5380
+ return isSimpleComponentBinding(usage, componentName) && usage.imported === componentName;
5381
+ }
5382
+ function isSimpleComponentBinding(usage, componentName) {
5383
+ return usage.namespace !== true && usage.local === componentName;
5384
+ }
5385
+ function isProjectLocalImportSource(source) {
5386
+ return source.startsWith(".") || source.startsWith("/") || source.startsWith("@/") || source.startsWith("~/");
5387
+ }
5291
5388
  function sameHtmlTag(htmlEquivalent, element) {
5292
5389
  return htmlEquivalent !== void 0 && htmlEquivalent.toLowerCase() === element.toLowerCase();
5293
5390
  }
@@ -5320,6 +5417,7 @@ function isCanonicalMappingOption(value) {
5320
5417
  if (record.htmlType !== void 0 && typeof record.htmlType !== "string") return false;
5321
5418
  if (record.importPath !== void 0 && typeof record.importPath !== "string") return false;
5322
5419
  if (record.confidence !== void 0 && typeof record.confidence !== "number") return false;
5420
+ if (record.conformStatus !== void 0 && record.conformStatus !== "confirmed") return false;
5323
5421
  if (record.declaredHtmlEquivalent !== void 0 && typeof record.declaredHtmlEquivalent !== "boolean") {
5324
5422
  return false;
5325
5423
  }
@@ -5422,6 +5520,11 @@ function sourceLabel(source) {
5422
5520
  if (source.kind === "registry") return source.importPath ?? source.installPath;
5423
5521
  return source.path;
5424
5522
  }
5523
+ function canonicalImportPath(source) {
5524
+ if (source.kind === "npm") return source.specifier;
5525
+ if (source.kind === "registry") return source.importPath;
5526
+ return void 0;
5527
+ }
5425
5528
  function isCanonicalSourceImplementationFile(file, sources) {
5426
5529
  const normalizedFile = normalizePath(file);
5427
5530
  return sources.some((source) => {
@@ -7674,7 +7777,11 @@ function isDenyEligible(finding, failOnWarnings) {
7674
7777
  if (!BLOCKING_RULE_ALLOWLIST.has(finding.ruleId)) return false;
7675
7778
  if (finding.attributes?.advisory === true) return false;
7676
7779
  if (!gatesCi(finding, failOnWarnings)) return false;
7677
- if (finding.fix?.deterministic === false) return false;
7780
+ if (finding.fix?.deterministic === false) {
7781
+ if (finding.ruleId !== "components/prefer-library" || finding.attributes?.propCompatibility !== "observed-complete") {
7782
+ return false;
7783
+ }
7784
+ }
7678
7785
  return true;
7679
7786
  }
7680
7787
 
@@ -7682,7 +7789,7 @@ function isDenyEligible(finding, failOnWarnings) {
7682
7789
  var RULE_TIER = {
7683
7790
  // ---- Tier A — contract vocabulary -------------------------------------
7684
7791
  "components/prefer-library": "contract",
7685
- // canonical-component bypass (FUI1001/1004)
7792
+ // canonical-component bypass (FUI1004)
7686
7793
  "tokens/css-vars-must-be-defined": "contract",
7687
7794
  // token drift (FUI2015)
7688
7795
  // ---- Tier B — generic hygiene -----------------------------------------
@@ -7947,6 +8054,7 @@ function evaluateGovernanceIntegrity(input) {
7947
8054
  componentsFamily.remediation = "add govern.canonicalSources (a directory source, or npm/registry with a non-empty include) or designSystem.path/packageName";
7948
8055
  } else {
7949
8056
  componentsFamily.reason = "components/prefer-library not enabled";
8057
+ componentsFamily.remediation = "add govern.canonicalSources (a directory source, or npm/registry with a non-empty include) or designSystem.path/packageName";
7950
8058
  }
7951
8059
  }
7952
8060
  const cssVarsActive = input.cssVarsActive ?? policyDeclaresCssVars(input.policy);
@@ -8035,6 +8143,29 @@ function evaluateGovernanceIntegrity(input) {
8035
8143
  // src/contract/preimage.ts
8036
8144
  var CONTRACT_DOMAINS = ["components", "tokens", "canonicalMap", "policy"];
8037
8145
  var CONTRACT_PREIMAGE_SCHEMA = "fcid-preimage:v1";
8146
+ var CONTRACT_PREIMAGE_CAPABILITY_HEADER = "X-Fragments-Contract-Preimage";
8147
+ var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/u;
8148
+ function verifiedContractPreimageFromPin(pin) {
8149
+ if (pin.preimageSchema !== CONTRACT_PREIMAGE_SCHEMA || !SHA256_HEX_PATTERN.test(pin.contractHash) || !pin.domainHashes || typeof pin.domainHashes !== "object") {
8150
+ return null;
8151
+ }
8152
+ const keys = Object.keys(pin.domainHashes).sort();
8153
+ const expectedKeys = [...CONTRACT_DOMAINS].sort();
8154
+ if (keys.length !== expectedKeys.length || keys.some((key, index) => key !== expectedKeys[index])) {
8155
+ return null;
8156
+ }
8157
+ const domains = Object.fromEntries(
8158
+ CONTRACT_DOMAINS.map((domain) => [domain, pin.domainHashes?.[domain]])
8159
+ );
8160
+ if (CONTRACT_DOMAINS.some((domain) => !SHA256_HEX_PATTERN.test(domains[domain] ?? ""))) {
8161
+ return null;
8162
+ }
8163
+ const preimage = {
8164
+ schema: CONTRACT_PREIMAGE_SCHEMA,
8165
+ domains
8166
+ };
8167
+ return contractHash(preimage) === pin.contractHash ? preimage : null;
8168
+ }
8038
8169
  function sortCanonical(items) {
8039
8170
  return items.map((item) => ({ item, key: canonicalPreimage(item) })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ item }) => item);
8040
8171
  }
@@ -8358,11 +8489,13 @@ function readPreferLibraryRule(config) {
8358
8489
  }
8359
8490
  export {
8360
8491
  AGENT_CONTEXT_RELATIVE_PATH,
8492
+ AGENT_FORMAT_SCHEMA_VERSION,
8361
8493
  BLOCKING_RULE_ALLOWLIST,
8362
8494
  BRAND,
8363
8495
  CATALOG_FIXTURE_A_FCID,
8364
8496
  CODES,
8365
8497
  CONTRACT_DOMAINS,
8498
+ CONTRACT_PREIMAGE_CAPABILITY_HEADER,
8366
8499
  CONTRACT_PREIMAGE_SCHEMA,
8367
8500
  DEFAULTS,
8368
8501
  DEFAULT_ENHANCED_STYLE_PROPERTIES,
@@ -8388,7 +8521,10 @@ export {
8388
8521
  SEVERITIES,
8389
8522
  SEVERITY_RANK,
8390
8523
  SEVERITY_WEIGHTS,
8524
+ agentErrorEnvelopeSchema,
8391
8525
  agentFormatSchema,
8526
+ agentIntegritySchema,
8527
+ agentOutputSchema,
8392
8528
  aiMetadataSchema,
8393
8529
  analyzeComposition,
8394
8530
  asComponentId,
@@ -8705,6 +8841,7 @@ export {
8705
8841
  topologyBase,
8706
8842
  usePreviewVariantRuntime,
8707
8843
  validatorResultSchema,
8844
+ verifiedContractPreimageFromPin,
8708
8845
  violationSchema,
8709
8846
  vocabularyRank
8710
8847
  };