@shapeshift-labs/frontier-lang-css 0.1.21 → 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
@@ -1,4 +1,4 @@
1
- import type { FrontierLangDocument } from '@shapeshift-labs/frontier-lang-kernel'; import type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; import type { CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js'; import type { CssModuleContractProof, CssModuleContractProofRecord } from './css-module-contract-proof.js'; import type { CssSafeMergeShorthandExpansionEvidence, CssShorthandExpansionEvidence } from './shorthand-expansion.js'; import type { CssSafeMergeSelectorTargetEvidence, CssSelectorTargetEquivalence, CssSelectorTargetProof } from './selector-target-proof.js'; export type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; export type { CssDependencyGraphChange, CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js'; export type { CssModuleContractProof, CssModuleContractProofRecord } from './css-module-contract-proof.js'; export type { CssSafeMergeChangedShorthandExpansion, CssSafeMergeShorthandExpansionEvidence, CssSafeMergeShorthandExpansionSideEvidence, CssShorthandExpansionEvidence, CssShorthandLonghandExpansion } from './shorthand-expansion.js'; export type { CssSafeMergeSelectorMove, CssSafeMergeSelectorTargetEvidence, CssSafeMergeSelectorTargetRebaseProof, CssSafeMergeSelectorTargetSideEvidence, CssSelectorTargetEquivalence, CssSelectorTargetProof } from './selector-target-proof.js';
1
+ import type { FrontierLangDocument } from '@shapeshift-labs/frontier-lang-kernel'; import type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; import type { CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js'; import type { CssModuleContractProof, CssModuleContractProofRecord } from './css-module-contract-proof.js'; import type { CssScopedCascadeProof, CssScopedCascadeProofRecord } from './scoped-cascade-proof.js'; import type { CssSafeMergeShorthandExpansionEvidence, CssShorthandExpansionEvidence } from './shorthand-expansion.js'; import type { CssSafeMergeSelectorTargetEvidence, CssSelectorTargetEquivalence, CssSelectorTargetProof } from './selector-target-proof.js'; export type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; export type { CssDependencyGraphChange, CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js'; export type { CssModuleContractProof, CssModuleContractProofRecord } from './css-module-contract-proof.js'; export type { CssScopedCascadeProof, CssScopedCascadeProofRecord } from './scoped-cascade-proof.js'; export type { CssSafeMergeChangedShorthandExpansion, CssSafeMergeShorthandExpansionEvidence, CssSafeMergeShorthandExpansionSideEvidence, CssShorthandExpansionEvidence, CssShorthandLonghandExpansion } from './shorthand-expansion.js'; export type { CssSafeMergeSelectorMove, CssSafeMergeSelectorTargetEvidence, CssSafeMergeSelectorTargetRebaseProof, CssSafeMergeSelectorTargetSideEvidence, CssSelectorTargetEquivalence, CssSelectorTargetProof } from './selector-target-proof.js';
2
2
 
3
3
  export interface CssProjectionOptions {
4
4
  readonly banner?: string;
@@ -13,6 +13,16 @@ 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>>;
24
+ readonly cssScopedCascadeProof?: CssScopedCascadeProof; readonly cssScopedCascadeProofs?: readonly CssScopedCascadeProof[];
25
+ readonly cssSourceBoundScopedCascadeProof?: CssScopedCascadeProof; readonly cssSourceBoundScopedCascadeProofs?: readonly CssScopedCascadeProof[];
16
26
  readonly cssCascadeRuntimeProof?: CssCascadeRuntimeProof; readonly cssCascadeRuntimeProofs?: readonly CssCascadeRuntimeProof[];
17
27
  readonly cssSourceBoundCascadeProof?: CssCascadeRuntimeProof; readonly cssSourceBoundCascadeProofs?: readonly CssCascadeRuntimeProof[];
18
28
  readonly selectorTargetGraphHash?: string;
@@ -127,6 +137,7 @@ export interface CssSemanticRecord {
127
137
  readonly scopeKey?: string;
128
138
  readonly statementText?: string;
129
139
  readonly blockText?: string;
140
+ readonly scopedCascadeGraphShapeKey?: string;
130
141
  readonly scopedCascadeGraphHash?: string;
131
142
  readonly selectorTargetGraphHash?: string;
132
143
  readonly sourceSpan: CssSourceSpan;
@@ -228,6 +239,7 @@ export interface CssSafeMergeAdmission {
228
239
  readonly reviewRequired: boolean; readonly reasonCodes: readonly string[];
229
240
  readonly browserCascadeEquivalenceClaim?: true;
230
241
  readonly cssCascadeRuntimeProofs?: readonly CssCascadeRuntimeProofRecord[];
242
+ readonly cssScopedCascadeProofs?: readonly CssScopedCascadeProofRecord[];
231
243
  readonly cssDependencyGraphProofs?: readonly CssDependencyGraphProofRecord[];
232
244
  readonly cssModuleContractProofs?: readonly CssModuleContractProofRecord[];
233
245
  }
@@ -245,6 +257,7 @@ export interface CssSafeMergeResult {
245
257
  readonly workerChangedCssModuleContracts?: number; readonly headChangedCssModuleContracts?: number;
246
258
  readonly parserEvidence?: CssSafeMergeParserEvidence; readonly shorthandExpansionEvidence?: CssSafeMergeShorthandExpansionEvidence; readonly selectorTargetEvidence?: CssSafeMergeSelectorTargetEvidence; readonly dependencyGraphEvidence?: CssDependencyGraphEvidence;
247
259
  readonly cssModuleContractProofs?: readonly CssModuleContractProofRecord[];
260
+ readonly scopedCascadeProofs?: readonly CssScopedCascadeProofRecord[];
248
261
  readonly cascadeRuntimeProofs?: readonly CssCascadeRuntimeProofRecord[];
249
262
  readonly dependencyGraphProofs?: readonly CssDependencyGraphProofRecord[];
250
263
  }
@@ -252,18 +265,22 @@ export interface CssSafeMergeResult {
252
265
  export interface CssSafeMergeParserEvidence {
253
266
  readonly kind: 'frontier.lang.cssSafeMergeParserEvidence'; readonly version: 1; readonly parserNames: readonly string[];
254
267
  readonly sourceCodeLocationInfo: boolean; readonly parserBackedSourceSpans: boolean; readonly parserBackedDeclarationSpans: boolean; readonly parserBackedTriviaHashes: boolean;
255
- 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>>;
256
269
  }
257
270
 
258
271
  export interface CssSafeMergeParserSideEvidence {
259
272
  readonly parserName: string; readonly sourceCodeLocationInfo: boolean; readonly parserBackedSourceSpans: boolean; readonly parserBackedDeclarationSpans: boolean; readonly parserBackedTriviaHashes: boolean;
260
- 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;
261
274
  }
262
275
 
263
276
  export interface CssSafeMergeInput {
264
277
  readonly id?: string; readonly sourcePath?: string; readonly baseSourceText?: string; readonly workerSourceText?: string; readonly headSourceText?: string;
265
278
  readonly cssModule?: boolean; readonly cssModules?: boolean; readonly generatedClassNameMap?: Readonly<Record<string, string>>;
266
279
  readonly generatedClassNameMapHash?: string; readonly jsTsUseSiteGraphHash?: string; readonly cssModuleCompositionGraphHash?: string; readonly icssGraphHash?: string; readonly scopedCascadeGraphHash?: string;
280
+ readonly cssScopedCascadeProof?: CssScopedCascadeProof; readonly cssScopedCascadeProofs?: readonly CssScopedCascadeProof[]; readonly cssScopedCascadeProofsByPath?: Readonly<Record<string, CssScopedCascadeProof | readonly CssScopedCascadeProof[]>>;
281
+ readonly cssSourceBoundScopedCascadeProof?: CssScopedCascadeProof; readonly cssSourceBoundScopedCascadeProofs?: readonly CssScopedCascadeProof[]; readonly cssSourceBoundScopedCascadeProofsByPath?: Readonly<Record<string, CssScopedCascadeProof | readonly CssScopedCascadeProof[]>>;
282
+ readonly scopedCascadeProof?: CssScopedCascadeProof; readonly scopedCascadeProofs?: readonly CssScopedCascadeProof[]; readonly scopedCascadeProofsByPath?: Readonly<Record<string, CssScopedCascadeProof | readonly CssScopedCascadeProof[]>>;
283
+ readonly sourceBoundScopedCascadeProof?: CssScopedCascadeProof; readonly sourceBoundScopedCascadeProofs?: readonly CssScopedCascadeProof[]; readonly sourceBoundScopedCascadeProofsByPath?: Readonly<Record<string, CssScopedCascadeProof | readonly CssScopedCascadeProof[]>>;
267
284
  readonly cssCascadeRuntimeProof?: CssCascadeRuntimeProof; readonly cssCascadeRuntimeProofs?: readonly CssCascadeRuntimeProof[]; readonly cssCascadeRuntimeProofsByPath?: Readonly<Record<string, CssCascadeRuntimeProof | readonly CssCascadeRuntimeProof[]>>;
268
285
  readonly cssSourceBoundCascadeProof?: CssCascadeRuntimeProof; readonly cssSourceBoundCascadeProofs?: readonly CssCascadeRuntimeProof[];
269
286
  readonly cssSourceBoundCascadeProofsByPath?: Readonly<Record<string, CssCascadeRuntimeProof | readonly CssCascadeRuntimeProof[]>>;
@@ -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');
@@ -0,0 +1,61 @@
1
+ export interface CssScopedCascadeProof {
2
+ readonly id?: string;
3
+ readonly kind: 'css-scoped-cascade-proof' | 'css-source-bound-scoped-cascade-proof' | 'css-scoped-cascade-runtime-proof' | 'css-source-bound-scoped-cascade-runtime-proof' | string;
4
+ readonly status: 'passed' | string;
5
+ readonly proofLevel?: string;
6
+ readonly sourcePath?: string;
7
+ readonly reasonCode?: string;
8
+ readonly reasonCodes?: readonly string[];
9
+ readonly side?: 'worker' | 'head' | string;
10
+ readonly sides?: readonly string[];
11
+ readonly cascadeKey?: string;
12
+ readonly cascadeKeys?: readonly string[];
13
+ readonly ruleKey?: string;
14
+ readonly ruleKeys?: readonly string[];
15
+ readonly selectors?: readonly string[];
16
+ readonly scopes?: readonly string[];
17
+ readonly property?: string;
18
+ readonly properties?: readonly string[];
19
+ readonly scopedCascadeGraphShapeKey?: string;
20
+ readonly scopedCascadeGraphShapeKeys?: readonly string[];
21
+ readonly shapeKey?: string;
22
+ readonly shapeKeys?: readonly string[];
23
+ readonly scopedCascadeGraphHash?: string;
24
+ readonly graphHash?: string;
25
+ readonly baseScopedCascadeGraphHash?: string;
26
+ readonly workerScopedCascadeGraphHash?: string;
27
+ readonly headScopedCascadeGraphHash?: string;
28
+ readonly scopedCascadeGraphHashes?: Readonly<Record<string, string>>;
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>>>>;
33
+ readonly baseSourceText?: string; readonly workerSourceText?: string; readonly headSourceText?: string; readonly outputSourceText?: string; readonly mergedSourceText?: string;
34
+ readonly baseSourceHash?: string; readonly workerSourceHash?: string; readonly headSourceHash?: string; readonly outputSourceHash?: string; readonly mergedSourceHash?: string;
35
+ readonly sourceTexts?: Readonly<Record<string, string>>;
36
+ readonly sources?: Readonly<Record<string, string>>;
37
+ readonly sourceHashes?: Readonly<Record<string, string>>;
38
+ readonly hashes?: Readonly<Record<string, string>>;
39
+ }
40
+
41
+ export interface CssScopedCascadeProofRecord {
42
+ readonly id?: string;
43
+ readonly kind: string;
44
+ readonly status: 'passed';
45
+ readonly proofLevel: string;
46
+ readonly reasonCode: string;
47
+ readonly side: string;
48
+ readonly cascadeKey: string;
49
+ readonly ruleKey?: string;
50
+ readonly property?: string;
51
+ readonly scopes?: readonly string[];
52
+ readonly scopedCascadeGraphShapeKey?: string;
53
+ readonly sourcePath?: string;
54
+ readonly scopedCascadeGraphHash?: string;
55
+ readonly scopedCascadeGraphHashes?: Readonly<Record<string, string>>;
56
+ readonly scopedCascadeGraphShapeKeys?: Readonly<Record<string, string>>;
57
+ readonly baseSourceHash?: string;
58
+ readonly workerSourceHash?: string;
59
+ readonly headSourceHash?: string;
60
+ readonly outputSourceHash?: string;
61
+ }
@@ -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
  }
@@ -0,0 +1,182 @@
1
+ function scopedCascadeChanges(changed, indexes) {
2
+ return [
3
+ ...scopedCascadeChangesForSide(changed.worker, 'worker', indexes.base),
4
+ ...scopedCascadeChangesForSide(changed.head, 'head', indexes.base)
5
+ ];
6
+ }
7
+
8
+ function scopedCascadeChangesForSide(changes, side, baseIndex) {
9
+ const baseScopeKeys = new Set([...baseIndex.declarations.values()]
10
+ .filter((entry) => entry.scopes?.length)
11
+ .map((entry) => scopeListKey(entry.scopes)));
12
+ return changes.flatMap((change) => {
13
+ const entries = [change.before, change.after].filter((entry) => entry?.scopes?.length);
14
+ if (!entries.length || !entries.some((entry) => baseScopeKeys.has(scopeListKey(entry.scopes)))) return [];
15
+ const entry = change.after ?? change.before;
16
+ const graphRoles = scopedCascadeGraphRoles(change, side);
17
+ return [{
18
+ side,
19
+ changeKind: change.kind,
20
+ reasonCode: 'css-scoped-cascade-equivalence-unproved',
21
+ reasonCodes: unique(['css-scoped-cascade-equivalence-unproved', ...entries.flatMap((item) => scopedCascadeReasonCodes(item.scopes))]),
22
+ cascadeKey: change.key,
23
+ ruleKey: entry.ruleKey,
24
+ selectors: entry.selectors,
25
+ scopes: entry.scopes,
26
+ scopedCascadeGraphShapeKey: entry.scopedCascadeGraphShapeKey,
27
+ property: entry.property,
28
+ specificity: entry.specificity,
29
+ scopedCascadeGraphReady: graphRoles.every((item) => typeof item.hash === 'string'),
30
+ scopedCascadeGraphHash: graphRoles.find((item) => typeof item.hash === 'string')?.hash,
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')),
33
+ before: scopedCascadeDeclarationDetails(change.before),
34
+ after: scopedCascadeDeclarationDetails(change.after)
35
+ }];
36
+ });
37
+ }
38
+
39
+ function admitScopedCascadeProofs({ id, sourcePath, input, changes, binding, hash }) {
40
+ const proofs = scopedCascadeProofCandidates(input, sourcePath);
41
+ const admitted = [];
42
+ const conflicts = [];
43
+ for (const change of changes) {
44
+ const proof = proofs.find((candidate) => isScopedCascadeProofForChange(candidate, change, sourcePath, binding, hash));
45
+ if (proof) admitted.push(scopedCascadeProofRecord(proof, change, sourcePath, binding, hash));
46
+ else conflicts.push(conflict(id, sourcePath, 'css-scoped-cascade-proof-blocked', change.reasonCode, change));
47
+ }
48
+ return { proofs: admitted, conflicts };
49
+ }
50
+
51
+ function scopedCascadeProofCandidates(input = {}, sourcePath) {
52
+ return [
53
+ input.cssScopedCascadeProof,
54
+ input.cssScopedCascadeProofs,
55
+ input.cssScopedCascadeProofsByPath?.[sourcePath],
56
+ input.cssSourceBoundScopedCascadeProof,
57
+ input.cssSourceBoundScopedCascadeProofs,
58
+ input.cssSourceBoundScopedCascadeProofsByPath?.[sourcePath],
59
+ input.scopedCascadeProof,
60
+ input.scopedCascadeProofs,
61
+ input.scopedCascadeProofsByPath?.[sourcePath],
62
+ input.sourceBoundScopedCascadeProof,
63
+ input.sourceBoundScopedCascadeProofs,
64
+ input.sourceBoundScopedCascadeProofsByPath?.[sourcePath]
65
+ ].flatMap(asArray).filter(Boolean);
66
+ }
67
+
68
+ function isScopedCascadeProofForChange(proof, change, sourcePath, binding, hash) {
69
+ return Boolean(proof && typeof proof === 'object') &&
70
+ ScopedCascadeProofKinds.has(proof.kind) &&
71
+ proof.status === 'passed' &&
72
+ proof.sourcePath === sourcePath &&
73
+ change.scopedCascadeGraphReady === true &&
74
+ proofCoversAny(proof.reasonCode, proof.reasonCodes, change.reasonCodes) &&
75
+ proofCoversValue(proof.side, proof.sides, change.side) &&
76
+ proofCoversValue(proof.cascadeKey, proof.cascadeKeys, change.cascadeKey) &&
77
+ proofCoversValue(proof.property, proof.properties, change.property) &&
78
+ scopedCascadeRuleMatches(proof, change) &&
79
+ scopedCascadeGraphHashMatches(proof, change) &&
80
+ proofSourceMatches(proof, 'base', binding.base, hash) &&
81
+ proofSourceMatches(proof, 'worker', binding.worker, hash) &&
82
+ proofSourceMatches(proof, 'head', binding.head, hash) &&
83
+ proofSourceMatches(proof, 'output', binding.output, hash);
84
+ }
85
+
86
+ function scopedCascadeRuleMatches(proof, change) {
87
+ const ruleKeyMatches = proof.ruleKey === change.ruleKey || proof.ruleKeys?.includes(change.ruleKey);
88
+ const selectorsMatch = selectorListKey(proof.selectors) === selectorListKey(change.selectors);
89
+ const scopesMatch = scopeListKey(proof.scopes) === scopeListKey(change.scopes);
90
+ return ruleKeyMatches || (selectorsMatch && scopesMatch);
91
+ }
92
+
93
+ function scopedCascadeGraphHashMatches(proof, change) {
94
+ const hashes = Object.entries(change.scopedCascadeGraphHashes ?? {});
95
+ if (!hashes.length) return false;
96
+ return hashes.every(([role, expected]) => {
97
+ const sharedHash = firstString(proof.scopedCascadeGraphHash, proof.graphHash);
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));
103
+ });
104
+ }
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
+
117
+ function scopedCascadeProofRecord(proof, change, sourcePath, binding, hash) {
118
+ return {
119
+ id: proof.id,
120
+ kind: proof.kind,
121
+ status: 'passed',
122
+ proofLevel: proof.proofLevel ?? 'css-scoped-cascade-source-bound',
123
+ reasonCode: change.reasonCode,
124
+ side: change.side,
125
+ cascadeKey: change.cascadeKey,
126
+ ruleKey: change.ruleKey,
127
+ property: change.property,
128
+ scopes: change.scopes,
129
+ scopedCascadeGraphShapeKey: change.scopedCascadeGraphShapeKey,
130
+ sourcePath,
131
+ scopedCascadeGraphHash: change.scopedCascadeGraphHash,
132
+ scopedCascadeGraphHashes: change.scopedCascadeGraphHashes,
133
+ baseSourceHash: hash?.(binding.base),
134
+ workerSourceHash: hash?.(binding.worker),
135
+ headSourceHash: hash?.(binding.head),
136
+ outputSourceHash: hash?.(binding.output)
137
+ };
138
+ }
139
+
140
+ function scopedCascadeGraphRoles(change, side) {
141
+ return [
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
144
+ ].filter(Boolean);
145
+ }
146
+
147
+ function scopedCascadeDeclarationDetails(entry) {
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;
149
+ }
150
+
151
+ function scopedCascadeReasonCodes(scopes = []) {
152
+ return scopes.map((scope) => /^@([-\w]+)/.exec(scope)?.[1]).filter(Boolean).map((name) => `css-${name}-cascade-scope-unproved`);
153
+ }
154
+
155
+ function proofSourceMatches(proof, role, sourceText, hash) {
156
+ if (typeof sourceText !== 'string') return false;
157
+ const sourceHash = hash?.(sourceText);
158
+ const textFields = role === 'output' ? ['outputSourceText', 'mergedSourceText'] : [`${role}SourceText`];
159
+ const hashFields = role === 'output' ? ['outputSourceHash', 'mergedSourceHash'] : [`${role}SourceHash`];
160
+ const aliases = role === 'output' ? ['output', 'merged'] : [role];
161
+ return textFields.some((field) => proof[field] === sourceText) ||
162
+ aliases.some((alias) => proof.sourceTexts?.[alias] === sourceText || proof.sources?.[alias] === sourceText) ||
163
+ hashFields.some((field) => sourceHash !== undefined && proof[field] === sourceHash) ||
164
+ aliases.some((alias) => sourceHash !== undefined && (proof.sourceHashes?.[alias] === sourceHash || proof.hashes?.[alias] === sourceHash));
165
+ }
166
+
167
+ function conflict(id, sourcePath, code, reasonCode, details = {}) {
168
+ return { code, gateId: 'css-semantic-merge', sourcePath, details: { reasonCode, conflictKey: `css#${id}#${reasonCode}#${details.cascadeKey ?? sourcePath ?? 'source'}`, ...details } };
169
+ }
170
+
171
+ function proofCoversAny(value, values, expectedValues = []) { return expectedValues.some((expected) => proofCoversValue(value, values, expected)); }
172
+ function proofCoversValue(value, values, expected) { return value === expected || (Array.isArray(values) && values.includes(expected)); }
173
+ function asArray(value) { return Array.isArray(value) ? value : value === undefined ? [] : [value]; }
174
+ function selectorListKey(value = []) { return Array.isArray(value) ? value.join(',') : undefined; }
175
+ function scopeListKey(value = []) { return Array.isArray(value) ? value.join('::') : undefined; }
176
+ function firstString(...values) { return values.find((value) => typeof value === 'string' && value.length > 0); }
177
+ function unique(values) { return [...new Set(values.filter(Boolean))]; }
178
+
179
+ const ScopedCascadeProofKinds = new Set(['css-scoped-cascade-proof', 'css-source-bound-scoped-cascade-proof', 'css-scoped-cascade-runtime-proof', 'css-source-bound-scoped-cascade-runtime-proof']);
180
+ const ScopedCascadeProofGapCodes = new Set(['css-scoped-cascade-equivalence-unproved', 'css-media-cascade-scope-unproved', 'css-supports-cascade-scope-unproved', 'css-container-cascade-scope-unproved', 'css-layer-cascade-scope-unproved', 'css-scope-cascade-scope-unproved']);
181
+
182
+ export { ScopedCascadeProofGapCodes, admitScopedCascadeProofs, scopedCascadeChanges };
@@ -1,6 +1,7 @@
1
1
  import { cssModuleContractChanges, sheetOptions, unsupportedSourceShapeChanges } from './semantic-merge-css-modules.js';
2
2
  import { admitCssModuleContractProofs } from './semantic-merge-css-module-proofs.js';
3
3
  import { admitCascadeRuntimeProofs } from './semantic-merge-cascade-runtime.js';
4
+ import { ScopedCascadeProofGapCodes, admitScopedCascadeProofs, scopedCascadeChanges } from './semantic-merge-scoped-cascade.js';
4
5
  import { admitCssDependencyGraphProofs, mergeCssDependencyGraphEvidence } from './dependency-graph.js';
5
6
  import { mergeSelectorTargetEvidence, planSelectorTargetRebase } from './semantic-merge-selector-targets.js';
6
7
  import { applyAtRuleBlockChanges, atRuleBlockEntry, atRuleBlockOverlapConflicts, atRuleOccurrenceKey, changedAtRuleBlocks, renderAtRuleBlock, renderAtRuleStatement } from './semantic-merge-at-rules.js';
@@ -47,20 +48,15 @@ function safeMergeCssSource(input = {}, context = {}) {
47
48
  const dependencyGraphEvidence = mergeCssDependencyGraphEvidence(sheets, changed);
48
49
  const selectorTargetPlan = planSelectorTargetRebase(id, sourcePath, mergeSelectorTargetEvidence(sheets, changed), changed, { ...input, sourceBinding: { base, worker, head }, hashSemanticValue: hash });
49
50
  const shapeChanges = unsupportedSourceShapeChanges(sheets, changed, hash);
51
+ const scopedChanges = scopedCascadeChanges(selectorTargetPlan.changed, indexes);
50
52
  const mergedIndex = applyAtRuleBlockChanges(applyAtRuleBlockChanges(applyDeclarationChanges(applyDeclarationChanges(indexes.base, selectorTargetPlan.changed.head), selectorTargetPlan.changed.worker), blockChanges.head), blockChanges.worker);
51
53
  const mergedSourceText = renderDeclarationIndex(mergedIndex);
52
- const cascadeRuntimeAdmission = admitCascadeRuntimeProofs({
53
- id,
54
- sourcePath,
55
- input,
56
- sourceShapeChanges: shapeChanges,
57
- binding: { base, worker, head, output: mergedSourceText },
58
- hash
59
- });
54
+ const scopedCascadeAdmission = admitScopedCascadeProofs({ id, sourcePath, input, changes: scopedChanges, binding: { base, worker, head, output: mergedSourceText }, hash });
55
+ const cascadeRuntimeAdmission = admitCascadeRuntimeProofs({ id, sourcePath, input, sourceShapeChanges: shapeChanges, binding: { base, worker, head, output: mergedSourceText }, hash });
60
56
  const dependencyGraphAdmission = admitCssDependencyGraphProofs({ id, sourcePath, input, dependencyGraphEvidence, binding: { base, worker, head, output: mergedSourceText }, hash });
61
57
  const cssModuleAdmission = admitCssModuleContractProofs({ id, sourcePath, input, moduleChanges, binding: { base, worker, head, output: mergedSourceText }, hash });
62
- const conflicts = [...parserConflicts, ...duplicateCascadeKeyConflicts, ...proofConflicts, ...overlapConflicts, ...cssModuleAdmission.conflicts, ...cascadeRuntimeAdmission.conflicts, ...dependencyGraphAdmission.conflicts, ...selectorTargetPlan.conflicts];
63
- if (conflicts.length) return blocked(id, sourcePath, 'css-semantic-merge-conflict', conflicts, { parserEvidence, shorthandExpansionEvidence, dependencyGraphEvidence, selectorTargetEvidence: selectorTargetPlan.evidence, cssModuleContractProofs: cssModuleAdmission.proofs, cascadeRuntimeProofs: cascadeRuntimeAdmission.proofs, dependencyGraphProofs: dependencyGraphAdmission.proofs, ...blockedMergeCandidate(input, mergedSourceText, hash) });
58
+ const conflicts = [...parserConflicts, ...duplicateCascadeKeyConflicts, ...proofConflicts, ...overlapConflicts, ...cssModuleAdmission.conflicts, ...scopedCascadeAdmission.conflicts, ...cascadeRuntimeAdmission.conflicts, ...dependencyGraphAdmission.conflicts, ...selectorTargetPlan.conflicts];
59
+ if (conflicts.length) return blocked(id, sourcePath, 'css-semantic-merge-conflict', conflicts, { parserEvidence, shorthandExpansionEvidence, dependencyGraphEvidence, selectorTargetEvidence: selectorTargetPlan.evidence, cssModuleContractProofs: cssModuleAdmission.proofs, scopedCascadeProofs: scopedCascadeAdmission.proofs, cascadeRuntimeProofs: cascadeRuntimeAdmission.proofs, dependencyGraphProofs: dependencyGraphAdmission.proofs, ...blockedMergeCandidate(input, mergedSourceText, hash) });
64
60
  return merged(id, sourcePath, mergedSourceText, 'semantic-declaration-merge', hash, {
65
61
  baseSheetHash: sheets.base.sheetHash,
66
62
  workerSheetHash: sheets.worker.sheetHash,
@@ -74,6 +70,7 @@ function safeMergeCssSource(input = {}, context = {}) {
74
70
  dependencyGraphEvidence,
75
71
  selectorTargetEvidence: selectorTargetPlan.evidence,
76
72
  cssModuleContractProofs: cssModuleAdmission.proofs,
73
+ scopedCascadeProofs: scopedCascadeAdmission.proofs,
77
74
  cascadeRuntimeProofs: cascadeRuntimeAdmission.proofs,
78
75
  dependencyGraphProofs: dependencyGraphAdmission.proofs,
79
76
  browserCascadeEquivalenceClaim: cascadeRuntimeAdmission.proofs.length > 0
@@ -109,6 +106,8 @@ function declarationIndex(sheet, hash) {
109
106
  declarationOrdinal: declaration.ordinal,
110
107
  declarationHash: declaration.declarationHash,
111
108
  shorthandExpansion: deterministicShorthandExpansion(declaration.property, declaration.value, hash),
109
+ scopedCascadeGraphShapeKey: record.scopedCascadeGraphShapeKey,
110
+ scopedCascadeGraphHash: record.scopedCascadeGraphHash,
112
111
  selectorTargetGraphHash: record.selectorTargetGraphHash,
113
112
  proofGaps: proofGapsForDeclaration(record, declaration)
114
113
  };
@@ -165,6 +164,7 @@ function mergeParserEvidence(sheets) {
165
164
  parserBackedDeclarationSpans: entries.every(([, evidence]) => evidence.parserBackedDeclarationSpans === true),
166
165
  parserBackedTriviaHashes: entries.every(([, evidence]) => evidence.parserBackedTriviaHashes === true),
167
166
  scopedCascadeGraphHashPresent: entries.every(([, evidence]) => evidence.scopedCascadeGraphHashPresent === true),
167
+ scopedCascadeGraphShapeHashPresent: entries.every(([, evidence]) => evidence.scopedCascadeGraphShapeHashPresent === true),
168
168
  parseErrors: entries.reduce((sum, [, evidence]) => sum + evidence.parseErrors, 0),
169
169
  sides: Object.fromEntries(entries)
170
170
  };
@@ -180,6 +180,8 @@ function sheetParserEvidence(sheet) {
180
180
  parserBackedDeclarationSpans: declarations.some((declaration) => declaration.sourceSpan?.startOffset !== undefined),
181
181
  parserBackedTriviaHashes: records.some((record) => record.parser === 'postcss' && typeof record.rawTextHash === 'string'),
182
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,
183
185
  parseErrors: sheet.parser?.parseErrors?.length ?? 0,
184
186
  recordCount: records.length,
185
187
  declarationCount: declarations.length
@@ -209,6 +211,7 @@ function shorthandOverlapConflicts(id, sourcePath, workerChanges, headChanges) {
209
211
  }
210
212
 
211
213
  function canAdmitProofGap(gap, entry, changed, indexes) {
214
+ if (ScopedCascadeProofGapCodes.has(gap.code)) return true;
212
215
  if (gap.code !== 'css-shorthand-expansion-unproved' || !entry) return false;
213
216
  const group = shorthandGroupForProperty(entry.property);
214
217
  if (!group || entry.shorthandExpansion?.status !== 'expanded' || hasRelatedExistingDeclaration(entry, indexes)) return false;
@@ -296,7 +299,7 @@ function result(id, sourcePath, status, body) {
296
299
  reviewRequired: status !== 'merged',
297
300
  reasonCodes: unique((body.conflicts ?? []).map((item) => item.details.reasonCode)),
298
301
  browserCascadeEquivalenceClaim: browserCascadeEquivalenceClaim || undefined,
299
- cssCascadeRuntimeProofs: body.cascadeRuntimeProofs?.length ? body.cascadeRuntimeProofs : undefined, cssDependencyGraphProofs: body.dependencyGraphProofs?.length ? body.dependencyGraphProofs : undefined, cssModuleContractProofs: body.cssModuleContractProofs?.length ? body.cssModuleContractProofs : undefined
302
+ cssCascadeRuntimeProofs: body.cascadeRuntimeProofs?.length ? body.cascadeRuntimeProofs : undefined, cssScopedCascadeProofs: body.scopedCascadeProofs?.length ? body.scopedCascadeProofs : undefined, cssDependencyGraphProofs: body.dependencyGraphProofs?.length ? body.dependencyGraphProofs : undefined, cssModuleContractProofs: body.cssModuleContractProofs?.length ? body.cssModuleContractProofs : undefined
300
303
  }
301
304
  };
302
305
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-css",
3
- "version": "0.1.21",
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",