@shapeshift-labs/frontier-lang-css 0.1.22 → 0.1.23

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.
package/README.md CHANGED
@@ -232,11 +232,11 @@ const merge = safeMergeCssSource({
232
232
  });
233
233
  ```
234
234
 
235
- `sourceMap.mappings` links emitted rule blocks back to Frontier Lang semantic node ids. `createCssSemanticMergeEvidence` records selectors, specificity, declarations, custom properties, `var()` fallback references, animation/keyframe links, font-face links, URL asset references, cascade keys, statement-form at-rules, block-form runtime at-rules, CSS Modules exports, ICSS edges, scoped cascade graph proof hashes, source spans, stable hashes, and fail-closed proof gaps for cascade/render-sensitive CSS surfaces. `safeMergeCssSource` admits independent declaration edits by cascade key, including existing scoped `@media` / `@supports` / `@container` / `@layer` declarations when a scoped cascade graph proof hash is supplied, carries deterministic box-shorthand expansion evidence for supported margin/padding/inset/gap/scroll/border-edge families, carries source-bound dependency graph evidence into the merge result, blocks duplicate ordered occurrences of the same cascade key until ordered cascade evidence exists, blocks dependency-affecting declaration edits unless an exact source-bound dependency graph proof is supplied, blocks selector-target rebases unless the host supplies a source-bound selector target graph hash and the parser proves selector specificity is invariant, blocks parallel edits whose CSS shorthand expansions overlap a longhand or sub-shorthand, preserves unchanged statement-form at-rules such as `@layer reset, components;`, preserves unchanged block-form runtime at-rules such as `@keyframes` and `@font-face`, and for `.module.css` files it classifies exported local classes, `composes`, and ICSS import/export records as explicit merge contracts. Cascade-sensitive source-shape changes still block by default, but a host can attach a `css-source-bound-cascade-runtime-proof` / `css-cascade-runtime-proof` that is bound to the exact source path, reason, side, shape key, and base/worker/head/output source hashes to admit that specific change.
235
+ `sourceMap.mappings` links emitted rule blocks back to Frontier Lang semantic node ids. `createCssSemanticMergeEvidence` records selectors, specificity, declarations, custom properties, `var()` fallback references, animation/keyframe links, font-face links, URL asset references, cascade keys, statement-form at-rules, block-form runtime at-rules, CSS Modules exports, ICSS edges, scoped cascade graph proof hashes keyed by scope shape, source spans, stable hashes, and fail-closed proof gaps for cascade/render-sensitive CSS surfaces. `safeMergeCssSource` admits independent declaration edits by cascade key, carries deterministic box-shorthand expansion evidence for supported margin/padding/inset/gap/scroll/border-edge families, carries source-bound dependency graph evidence into the merge result, blocks duplicate ordered occurrences of the same cascade key until ordered cascade evidence exists, blocks dependency-affecting declaration edits unless an exact source-bound dependency graph proof is supplied, blocks selector-target rebases unless the host supplies a source-bound selector target graph hash and the parser proves selector specificity is invariant, blocks changed scoped declarations under `@media` / `@supports` / `@container` / `@layer` / `@scope` unless an exact source-bound scoped cascade proof matches the scope shape key, scoped graph hash, and base/worker/head/output source hashes, blocks parallel edits whose CSS shorthand expansions overlap a longhand or sub-shorthand, preserves unchanged statement-form at-rules such as `@layer reset, components;`, preserves unchanged block-form runtime at-rules such as `@keyframes` and `@font-face`, and for `.module.css` files it classifies exported local classes, `composes`, and ICSS import/export records as explicit merge contracts. Cascade-sensitive source-shape changes still block by default, but a host can attach a `css-source-bound-cascade-runtime-proof` / `css-cascade-runtime-proof` that is bound to the exact source path, reason, side, shape key, and base/worker/head/output source hashes to admit that specific change.
236
236
 
237
237
  ## Support Boundary
238
238
 
239
- - Ready evidence: style rules, selectors, specificity, declarations, source-bound dependency graph hashes for custom properties, `var()` fallbacks, animations/keyframes, font faces, and URL assets, statement-form at-rules, block-form runtime at-rules, CSS Modules local exports, generated class-name map coverage, JS/TS use-site graph hashes, composition graph hashes, ICSS graph hashes, scoped cascade graph hashes, source-bound cascade runtime proofs, source spans, stable hashes.
240
- - Safe merge: independent declarations with non-overlapping cascade keys, no duplicate ordered occurrences for any changed cascade key, deterministic box-shorthand expansion evidence for supported shorthand families, non-overlapping shorthand expansion sets, and no changed dependency graph surface; unchanged statement-form at-rules and unchanged block-form runtime at-rules preserved in canonical output; existing scoped declaration edits when scoped cascade graph proof is supplied; dependency-affecting declaration edits only when an exact source-bound dependency graph proof is supplied; selector-target rebases only when a matching selector target graph hash is supplied and before/after selector specificity is identical; source-shape/cascade-sensitive edits only when an exact source-bound cascade runtime proof is supplied; explicit CSS Modules export additions/deletions when generated class-name and JS/TS use-site graph proof is supplied; composition edits when composition graph proof is supplied; ICSS edits when ICSS graph proof is supplied. Output is a canonical CSS render and not a byte/trivia-preserving claim.
241
- - Review-only gaps: duplicate ordered occurrences of the same cascade key, incomplete generated class-name maps, unproved dependency graph changes, unproved CSS Modules use-site graphs, unproved composition or ICSS graphs, ambiguous shorthand value expansion/equivalence such as `font`, layered `background`, and runtime-substituted `var()` / `env()` shorthand values, statement-form at-rule order/condition changes, one-sided or structurally changed scoped cascade under `@media` / `@supports` / `@container` / `@layer`, changed runtime blocks such as `@keyframes`, `@font-face`, `@page`, and `@property`, browser layout and render equivalence.
239
+ - Ready evidence: style rules, selectors, specificity, declarations, source-bound dependency graph hashes for custom properties, `var()` fallbacks, animations/keyframes, font faces, and URL assets, statement-form at-rules, block-form runtime at-rules, CSS Modules local exports, generated class-name map coverage, JS/TS use-site graph hashes, composition graph hashes, ICSS graph hashes, scoped cascade graph hashes by scope shape key, source-bound scoped cascade proofs, source-bound cascade runtime proofs, source spans, stable hashes.
240
+ - Safe merge: independent declarations with non-overlapping cascade keys, no duplicate ordered occurrences for any changed cascade key, deterministic box-shorthand expansion evidence for supported shorthand families, non-overlapping shorthand expansion sets, and no changed dependency graph surface; unchanged statement-form at-rules and unchanged block-form runtime at-rules preserved in canonical output; changed scoped declaration edits only when an exact source-bound scoped cascade proof matches the scope shape key, scoped graph hash, and base/worker/head/output source hashes; dependency-affecting declaration edits only when an exact source-bound dependency graph proof is supplied; selector-target rebases only when a matching selector target graph hash is supplied and before/after selector specificity is identical; source-shape/cascade-sensitive edits only when an exact source-bound cascade runtime proof is supplied; explicit CSS Modules export additions/deletions when generated class-name and JS/TS use-site graph proof is supplied; composition edits when composition graph proof is supplied; ICSS edits when ICSS graph proof is supplied. Output is a canonical CSS render and not a byte/trivia-preserving claim.
241
+ - Review-only gaps: duplicate ordered occurrences of the same cascade key, incomplete generated class-name maps, unproved dependency graph changes, unproved CSS Modules use-site graphs, unproved composition or ICSS graphs, ambiguous shorthand value expansion/equivalence such as `font`, layered `background`, and runtime-substituted `var()` / `env()` shorthand values, statement-form at-rule order/condition changes, one-sided or structurally changed scoped cascade under `@media` / `@supports` / `@container` / `@layer` / `@scope`, changed runtime blocks such as `@keyframes`, `@font-face`, `@page`, and `@property`, browser layout and render equivalence.
242
242
  - Claims: dependency graph evidence is an admission/review signal only unless an exact source-bound dependency graph proof admits the changed dependency surface; `autoMergeClaim`, `semanticEquivalenceClaim`, and `browserRenderEquivalenceClaim` remain false. `browserCascadeEquivalenceClaim` is only true on a safe-merge result when a source-bound cascade runtime proof admits the specific cascade-sensitive source-shape change.
package/dist/index.d.ts CHANGED
@@ -13,6 +13,14 @@ export interface CssProjectionOptions {
13
13
  readonly cssModuleCompositionGraphHash?: string;
14
14
  readonly icssGraphHash?: string;
15
15
  readonly scopedCascadeGraphHash?: string;
16
+ readonly scopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
17
+ readonly cssScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
18
+ readonly baseScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
19
+ readonly workerScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
20
+ readonly headScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
21
+ readonly baseCssScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
22
+ readonly workerCssScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
23
+ readonly headCssScopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
16
24
  readonly cssScopedCascadeProof?: CssScopedCascadeProof; readonly cssScopedCascadeProofs?: readonly CssScopedCascadeProof[];
17
25
  readonly cssSourceBoundScopedCascadeProof?: CssScopedCascadeProof; readonly cssSourceBoundScopedCascadeProofs?: readonly CssScopedCascadeProof[];
18
26
  readonly cssCascadeRuntimeProof?: CssCascadeRuntimeProof; readonly cssCascadeRuntimeProofs?: readonly CssCascadeRuntimeProof[];
@@ -129,6 +137,7 @@ export interface CssSemanticRecord {
129
137
  readonly scopeKey?: string;
130
138
  readonly statementText?: string;
131
139
  readonly blockText?: string;
140
+ readonly scopedCascadeGraphShapeKey?: string;
132
141
  readonly scopedCascadeGraphHash?: string;
133
142
  readonly selectorTargetGraphHash?: string;
134
143
  readonly sourceSpan: CssSourceSpan;
@@ -256,12 +265,12 @@ export interface CssSafeMergeResult {
256
265
  export interface CssSafeMergeParserEvidence {
257
266
  readonly kind: 'frontier.lang.cssSafeMergeParserEvidence'; readonly version: 1; readonly parserNames: readonly string[];
258
267
  readonly sourceCodeLocationInfo: boolean; readonly parserBackedSourceSpans: boolean; readonly parserBackedDeclarationSpans: boolean; readonly parserBackedTriviaHashes: boolean;
259
- readonly scopedCascadeGraphHashPresent: boolean; readonly parseErrors: number; readonly sides: Readonly<Record<string, CssSafeMergeParserSideEvidence>>;
268
+ readonly scopedCascadeGraphHashPresent: boolean; readonly scopedCascadeGraphShapeHashPresent?: boolean; readonly parseErrors: number; readonly sides: Readonly<Record<string, CssSafeMergeParserSideEvidence>>;
260
269
  }
261
270
 
262
271
  export interface CssSafeMergeParserSideEvidence {
263
272
  readonly parserName: string; readonly sourceCodeLocationInfo: boolean; readonly parserBackedSourceSpans: boolean; readonly parserBackedDeclarationSpans: boolean; readonly parserBackedTriviaHashes: boolean;
264
- readonly scopedCascadeGraphHashPresent: boolean; readonly parseErrors: number; readonly recordCount: number; readonly declarationCount: number;
273
+ readonly scopedCascadeGraphHashPresent: boolean; readonly scopedCascadeGraphShapeHashPresent?: boolean; readonly scopedCascadeGraphShapeKeys?: number; readonly parseErrors: number; readonly recordCount: number; readonly declarationCount: number;
265
274
  }
266
275
 
267
276
  export interface CssSafeMergeInput {
@@ -42,9 +42,11 @@ function postcssRuleRecord(node, scopes, sourceHash, options) {
42
42
  const selectors = String(node.selector ?? '').split(',').map((selector) => selector.trim()).filter(Boolean);
43
43
  const declarations = (node.nodes ?? []).filter((child) => child.type === 'decl').map(postcssDeclaration);
44
44
  const nestedChildren = (node.nodes ?? []).filter((child) => child.type !== 'decl' && child.type !== 'comment');
45
+ const scopedCascadeGraphShapeKey = scopedCascadeGraphShapeKeyForScopes(scopes);
46
+ const scopedCascadeGraphHash = scopedCascadeGraphHashForShape(scopedCascadeGraphShapeKey, options);
45
47
  const proofGaps = [
46
48
  ...declarations.filter((declaration) => ShorthandProperties.has(declaration.property)).map((declaration) => proofGap('css-shorthand-expansion-unproved', `CSS shorthand ${declaration.property} needs longhand expansion evidence.`)),
47
- ...scopes.length && !options.scopedCascadeGraphHash ? [proofGap('css-scoped-cascade-equivalence-unproved', 'Scoped cascade equivalence requires browser/style evidence.')] : [],
49
+ ...scopes.length && !scopedCascadeGraphHash ? [proofGap('css-scoped-cascade-equivalence-unproved', 'Scoped cascade equivalence requires browser/style evidence.')] : [],
48
50
  ...nestedChildren.length ? [proofGap('css-nesting-semantic-unproved', 'CSS nested rule semantics require nesting expansion evidence.')] : []
49
51
  ];
50
52
  return compactRecord({
@@ -60,7 +62,8 @@ function postcssRuleRecord(node, scopes, sourceHash, options) {
60
62
  declarationHash: hashSemanticValue({ kind: 'frontier.lang.css.declaration.v2.postcss', scopes, selectors, property: declaration.property, rawProperty: declaration.rawProperty, value: declaration.value, important: declaration.important })
61
63
  })),
62
64
  customProperties: declarations.filter((declaration) => declaration.property.startsWith('--')).map((declaration) => declaration.property),
63
- scopedCascadeGraphHash: scopes.length ? options.scopedCascadeGraphHash : undefined,
65
+ scopedCascadeGraphShapeKey,
66
+ scopedCascadeGraphHash,
64
67
  selectorTargetGraphHash: options.selectorTargetGraphHash,
65
68
  sourceSpan: sourceSpanFromPostcss(node.source, options.sourcePath),
66
69
  sourceHash,
@@ -89,9 +92,12 @@ function postcssAtRuleRecord(node, scopes, sourceHash, options) {
89
92
  const atRuleName = String(node.name ?? 'unknown').toLowerCase();
90
93
  const conditionText = String(node.params ?? '').trim();
91
94
  const rawText = rawPostcssText(node);
95
+ const scopeKey = postcssAtRuleScopeKey(node);
96
+ const scopedCascadeGraphShapeKey = ScopeAtRules.has(atRuleName) ? scopedCascadeGraphShapeKeyForScopes([...scopes, scopeKey]) : undefined;
97
+ const scopedCascadeGraphHash = scopedCascadeGraphHashForShape(scopedCascadeGraphShapeKey, options);
92
98
  const proofGaps = [];
93
99
  if (RuntimeAtRules.has(atRuleName)) proofGaps.push(proofGap(`css-${atRuleName}-runtime-equivalence-unproved`, `CSS @${atRuleName} semantics require browser evidence.`));
94
- if (ScopeAtRules.has(atRuleName) && !options.scopedCascadeGraphHash) proofGaps.push(proofGap(`css-${atRuleName}-cascade-scope-unproved`, `CSS @${atRuleName} scoped cascade requires condition evaluation evidence.`));
100
+ if (ScopeAtRules.has(atRuleName) && !scopedCascadeGraphHash) proofGaps.push(proofGap(`css-${atRuleName}-cascade-scope-unproved`, `CSS @${atRuleName} scoped cascade requires condition evaluation evidence.`));
95
101
  if (!node.nodes?.length && atRuleName === 'layer') proofGaps.push(proofGap('css-layer-order-statement-unsupported', 'CSS @layer statement order requires cascade order evidence.'));
96
102
  else if (!node.nodes?.length) proofGaps.push(proofGap(`css-${atRuleName}-statement-equivalence-unproved`, `CSS @${atRuleName} statement semantics require host evidence.`));
97
103
  const kind = node.nodes?.length ? 'at-rule' : 'at-rule-statement';
@@ -101,10 +107,11 @@ function postcssAtRuleRecord(node, scopes, sourceHash, options) {
101
107
  conditionText,
102
108
  statementText: kind === 'at-rule-statement' ? rawText : undefined,
103
109
  blockText: kind === 'at-rule' ? rawText : undefined,
104
- scopeKey: postcssAtRuleScopeKey(node),
110
+ scopeKey,
105
111
  scopes,
106
112
  dependencyTokens: atRuleDependencyTokens(node, atRuleName),
107
- scopedCascadeGraphHash: ScopeAtRules.has(atRuleName) ? options.scopedCascadeGraphHash : undefined,
113
+ scopedCascadeGraphShapeKey,
114
+ scopedCascadeGraphHash,
108
115
  sourceSpan: sourceSpanFromPostcss(node.source, options.sourcePath),
109
116
  sourceHash,
110
117
  rawTextHash: hashSemanticValue({ kind: 'frontier.lang.css.rawAtRuleText.v1', text: rawText }),
@@ -118,6 +125,19 @@ function postcssAtRuleScopeKey(node) {
118
125
  return `@${String(node.name ?? 'unknown').toLowerCase()} ${String(node.params ?? '').trim()}`.trim();
119
126
  }
120
127
 
128
+ function scopedCascadeGraphShapeKeyForScopes(scopes = []) {
129
+ return scopes.length ? scopes.join('::') : undefined;
130
+ }
131
+
132
+ function scopedCascadeGraphHashForShape(shapeKey, options) {
133
+ if (!shapeKey) return undefined;
134
+ return scopedCascadeGraphHashesByShapeKey(options)?.[shapeKey] ?? options.scopedCascadeGraphHash;
135
+ }
136
+
137
+ function scopedCascadeGraphHashesByShapeKey(options) {
138
+ return options.scopedCascadeGraphHashesByShapeKey ?? options.cssScopedCascadeGraphHashesByShapeKey ?? options.scopedCascadeGraphHashes;
139
+ }
140
+
121
141
  function atRuleDependencyTokens(node, atRuleName) {
122
142
  if (atRuleName !== 'font-face') return undefined;
123
143
  const declarations = (node.nodes ?? []).filter((child) => child.type === 'decl');
@@ -16,6 +16,10 @@ export interface CssScopedCascadeProof {
16
16
  readonly scopes?: readonly string[];
17
17
  readonly property?: string;
18
18
  readonly properties?: readonly string[];
19
+ readonly scopedCascadeGraphShapeKey?: string;
20
+ readonly scopedCascadeGraphShapeKeys?: readonly string[];
21
+ readonly shapeKey?: string;
22
+ readonly shapeKeys?: readonly string[];
19
23
  readonly scopedCascadeGraphHash?: string;
20
24
  readonly graphHash?: string;
21
25
  readonly baseScopedCascadeGraphHash?: string;
@@ -23,6 +27,9 @@ export interface CssScopedCascadeProof {
23
27
  readonly headScopedCascadeGraphHash?: string;
24
28
  readonly scopedCascadeGraphHashes?: Readonly<Record<string, string>>;
25
29
  readonly graphHashes?: Readonly<Record<string, string>>;
30
+ readonly scopedCascadeGraphHashesByShapeKey?: Readonly<Record<string, string>>;
31
+ readonly graphHashesByShapeKey?: Readonly<Record<string, string>>;
32
+ readonly scopedCascadeGraphHashesByRoleAndShape?: Readonly<Record<string, Readonly<Record<string, string>>>>;
26
33
  readonly baseSourceText?: string; readonly workerSourceText?: string; readonly headSourceText?: string; readonly outputSourceText?: string; readonly mergedSourceText?: string;
27
34
  readonly baseSourceHash?: string; readonly workerSourceHash?: string; readonly headSourceHash?: string; readonly outputSourceHash?: string; readonly mergedSourceHash?: string;
28
35
  readonly sourceTexts?: Readonly<Record<string, string>>;
@@ -42,9 +49,11 @@ export interface CssScopedCascadeProofRecord {
42
49
  readonly ruleKey?: string;
43
50
  readonly property?: string;
44
51
  readonly scopes?: readonly string[];
52
+ readonly scopedCascadeGraphShapeKey?: string;
45
53
  readonly sourcePath?: string;
46
54
  readonly scopedCascadeGraphHash?: string;
47
55
  readonly scopedCascadeGraphHashes?: Readonly<Record<string, string>>;
56
+ readonly scopedCascadeGraphShapeKeys?: Readonly<Record<string, string>>;
48
57
  readonly baseSourceHash?: string;
49
58
  readonly workerSourceHash?: string;
50
59
  readonly headSourceHash?: string;
@@ -12,6 +12,7 @@ function sheetOptions(input, side, sourcePath) {
12
12
  cssModuleCompositionGraphHash: input[`${prefix}CssModuleCompositionGraphHash`] ?? input.cssModuleCompositionGraphHash,
13
13
  icssGraphHash: input[`${prefix}IcssGraphHash`] ?? input.icssGraphHash,
14
14
  scopedCascadeGraphHash: input[`${prefix}ScopedCascadeGraphHash`] ?? input.scopedCascadeGraphHash,
15
+ scopedCascadeGraphHashesByShapeKey: input[`${prefix}ScopedCascadeGraphHashesByShapeKey`] ?? input[`${prefix}CssScopedCascadeGraphHashesByShapeKey`] ?? input.scopedCascadeGraphHashesByShapeKey ?? input.cssScopedCascadeGraphHashesByShapeKey,
15
16
  selectorTargetGraphHash: input[`${prefix}SelectorTargetGraphHash`] ?? input.selectorTargetGraphHash
16
17
  };
17
18
  }
@@ -23,11 +23,13 @@ function scopedCascadeChangesForSide(changes, side, baseIndex) {
23
23
  ruleKey: entry.ruleKey,
24
24
  selectors: entry.selectors,
25
25
  scopes: entry.scopes,
26
+ scopedCascadeGraphShapeKey: entry.scopedCascadeGraphShapeKey,
26
27
  property: entry.property,
27
28
  specificity: entry.specificity,
28
29
  scopedCascadeGraphReady: graphRoles.every((item) => typeof item.hash === 'string'),
29
30
  scopedCascadeGraphHash: graphRoles.find((item) => typeof item.hash === 'string')?.hash,
30
31
  scopedCascadeGraphHashes: Object.fromEntries(graphRoles.map((item) => [item.role, item.hash]).filter(([, value]) => typeof value === 'string')),
32
+ scopedCascadeGraphShapeKeys: Object.fromEntries(graphRoles.map((item) => [item.role, item.shapeKey]).filter(([, value]) => typeof value === 'string')),
31
33
  before: scopedCascadeDeclarationDetails(change.before),
32
34
  after: scopedCascadeDeclarationDetails(change.after)
33
35
  }];
@@ -93,13 +95,25 @@ function scopedCascadeGraphHashMatches(proof, change) {
93
95
  if (!hashes.length) return false;
94
96
  return hashes.every(([role, expected]) => {
95
97
  const sharedHash = firstString(proof.scopedCascadeGraphHash, proof.graphHash);
96
- return sharedHash === expected ||
97
- proof[`${role}ScopedCascadeGraphHash`] === expected ||
98
- proof.scopedCascadeGraphHashes?.[role] === expected ||
99
- proof.graphHashes?.[role] === expected;
98
+ const shapeKey = change.scopedCascadeGraphShapeKeys?.[role] ?? change.scopedCascadeGraphShapeKey;
99
+ return proof.scopedCascadeGraphHashesByShapeKey?.[shapeKey] === expected ||
100
+ proof.graphHashesByShapeKey?.[shapeKey] === expected ||
101
+ proof.scopedCascadeGraphHashesByRoleAndShape?.[role]?.[shapeKey] === expected ||
102
+ ((sharedHash === expected || proof[`${role}ScopedCascadeGraphHash`] === expected || proof.scopedCascadeGraphHashes?.[role] === expected || proof.graphHashes?.[role] === expected) && proofShapeMatches(proof, shapeKey));
100
103
  });
101
104
  }
102
105
 
106
+ function proofShapeMatches(proof, shapeKey) {
107
+ if (!shapeKey) return true;
108
+ if (proof.scopedCascadeGraphShapeKey === shapeKey || proof.shapeKey === shapeKey) return true;
109
+ if (proof.scopedCascadeGraphShapeKeys?.includes?.(shapeKey) || proof.shapeKeys?.includes?.(shapeKey)) return true;
110
+ return !hasProofShapeBinding(proof);
111
+ }
112
+
113
+ function hasProofShapeBinding(proof) {
114
+ return Boolean(proof.scopedCascadeGraphShapeKey || proof.shapeKey || proof.scopedCascadeGraphShapeKeys || proof.shapeKeys || proof.scopedCascadeGraphHashesByShapeKey || proof.graphHashesByShapeKey || proof.scopedCascadeGraphHashesByRoleAndShape);
115
+ }
116
+
103
117
  function scopedCascadeProofRecord(proof, change, sourcePath, binding, hash) {
104
118
  return {
105
119
  id: proof.id,
@@ -112,6 +126,7 @@ function scopedCascadeProofRecord(proof, change, sourcePath, binding, hash) {
112
126
  ruleKey: change.ruleKey,
113
127
  property: change.property,
114
128
  scopes: change.scopes,
129
+ scopedCascadeGraphShapeKey: change.scopedCascadeGraphShapeKey,
115
130
  sourcePath,
116
131
  scopedCascadeGraphHash: change.scopedCascadeGraphHash,
117
132
  scopedCascadeGraphHashes: change.scopedCascadeGraphHashes,
@@ -124,13 +139,13 @@ function scopedCascadeProofRecord(proof, change, sourcePath, binding, hash) {
124
139
 
125
140
  function scopedCascadeGraphRoles(change, side) {
126
141
  return [
127
- change.before?.scopedCascadeGraphHash ? { role: 'base', hash: change.before.scopedCascadeGraphHash } : undefined,
128
- change.after?.scopedCascadeGraphHash ? { role: side, hash: change.after.scopedCascadeGraphHash } : undefined
142
+ change.before?.scopedCascadeGraphHash ? { role: 'base', hash: change.before.scopedCascadeGraphHash, shapeKey: change.before.scopedCascadeGraphShapeKey } : undefined,
143
+ change.after?.scopedCascadeGraphHash ? { role: side, hash: change.after.scopedCascadeGraphHash, shapeKey: change.after.scopedCascadeGraphShapeKey } : undefined
129
144
  ].filter(Boolean);
130
145
  }
131
146
 
132
147
  function scopedCascadeDeclarationDetails(entry) {
133
- return entry ? { property: entry.property, value: entry.value, ruleKey: entry.ruleKey, cascadeKey: entry.key, scopes: entry.scopes, scopedCascadeGraphHash: entry.scopedCascadeGraphHash } : undefined;
148
+ return entry ? { property: entry.property, value: entry.value, ruleKey: entry.ruleKey, cascadeKey: entry.key, scopes: entry.scopes, scopedCascadeGraphShapeKey: entry.scopedCascadeGraphShapeKey, scopedCascadeGraphHash: entry.scopedCascadeGraphHash } : undefined;
134
149
  }
135
150
 
136
151
  function scopedCascadeReasonCodes(scopes = []) {
@@ -106,6 +106,7 @@ function declarationIndex(sheet, hash) {
106
106
  declarationOrdinal: declaration.ordinal,
107
107
  declarationHash: declaration.declarationHash,
108
108
  shorthandExpansion: deterministicShorthandExpansion(declaration.property, declaration.value, hash),
109
+ scopedCascadeGraphShapeKey: record.scopedCascadeGraphShapeKey,
109
110
  scopedCascadeGraphHash: record.scopedCascadeGraphHash,
110
111
  selectorTargetGraphHash: record.selectorTargetGraphHash,
111
112
  proofGaps: proofGapsForDeclaration(record, declaration)
@@ -163,6 +164,7 @@ function mergeParserEvidence(sheets) {
163
164
  parserBackedDeclarationSpans: entries.every(([, evidence]) => evidence.parserBackedDeclarationSpans === true),
164
165
  parserBackedTriviaHashes: entries.every(([, evidence]) => evidence.parserBackedTriviaHashes === true),
165
166
  scopedCascadeGraphHashPresent: entries.every(([, evidence]) => evidence.scopedCascadeGraphHashPresent === true),
167
+ scopedCascadeGraphShapeHashPresent: entries.every(([, evidence]) => evidence.scopedCascadeGraphShapeHashPresent === true),
166
168
  parseErrors: entries.reduce((sum, [, evidence]) => sum + evidence.parseErrors, 0),
167
169
  sides: Object.fromEntries(entries)
168
170
  };
@@ -178,6 +180,8 @@ function sheetParserEvidence(sheet) {
178
180
  parserBackedDeclarationSpans: declarations.some((declaration) => declaration.sourceSpan?.startOffset !== undefined),
179
181
  parserBackedTriviaHashes: records.some((record) => record.parser === 'postcss' && typeof record.rawTextHash === 'string'),
180
182
  scopedCascadeGraphHashPresent: records.every((record) => !(record.scopes?.length) || Boolean(record.scopedCascadeGraphHash)),
183
+ scopedCascadeGraphShapeHashPresent: records.every((record) => !(record.scopes?.length) || Boolean(record.scopedCascadeGraphHash && record.scopedCascadeGraphShapeKey)),
184
+ scopedCascadeGraphShapeKeys: unique(records.filter((record) => record.scopes?.length).map((record) => record.scopedCascadeGraphShapeKey)).length,
181
185
  parseErrors: sheet.parser?.parseErrors?.length ?? 0,
182
186
  recordCount: records.length,
183
187
  declarationCount: declarations.length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-css",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "CSS semantic merge evidence and projection adapter for Frontier Lang semantic source documents.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",