@shapeshift-labs/frontier-lang-compiler 0.2.162 → 0.2.163

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 (21) hide show
  1. package/dist/internal/index-impl/moduleHostResourceImportMetadata.js +47 -0
  2. package/dist/internal/index-impl/projectSymbolGraphCompilerAdvancedTypeMetadata.js +84 -12
  3. package/dist/internal/index-impl/projectSymbolGraphCompilerFacts.js +1 -1
  4. package/dist/internal/index-impl/projectSymbolGraphCssModules.js +48 -4
  5. package/dist/internal/index-impl/projectSymbolGraphJsxPropRecordFields.js +91 -0
  6. package/dist/internal/index-impl/projectSymbolGraphJsxPropValues.js +26 -135
  7. package/dist/internal/index-impl/projectSymbolGraphJsxRecords.js +4 -31
  8. package/dist/internal/index-impl/projectSymbolGraphJsxRenderReturnCollectionHelpers.js +201 -0
  9. package/dist/internal/index-impl/projectSymbolGraphJsxRenderReturnCollections.js +210 -0
  10. package/dist/internal/index-impl/projectSymbolGraphJsxRenderReturns.js +12 -5
  11. package/dist/internal/index-impl/projectSymbolGraphJsxSpreadPropValues.js +196 -0
  12. package/dist/internal/index-impl/projectSymbolGraphJsxStaticLiterals.js +207 -0
  13. package/dist/internal/index-impl/projectSymbolGraphModuleResolution.js +12 -14
  14. package/dist/internal/index-impl/projectSymbolGraphPackageConditions.js +22 -17
  15. package/dist/internal/index-impl/syntaxModuleDeclarationEntries.js +27 -1
  16. package/dist/js-ts-safe-project-merge-html-css-matrix.js +7 -2
  17. package/dist/js-ts-safe-project-merge-html-css-summary.js +90 -8
  18. package/dist/js-ts-safe-project-merge-html-css.js +137 -3
  19. package/dist/js-ts-safe-project-merge-jsx-graph-conflict-details.js +9 -0
  20. package/dist/js-ts-safe-project-merge.js +3 -0
  21. package/package.json +1 -1
@@ -7,11 +7,13 @@ function htmlCssProjectSummary(files) {
7
7
  htmlParserEvidenceFiles: htmlFiles.filter(hasHtmlParserEvidence).length, cssParserEvidenceFiles: cssFiles.filter(hasCssParserEvidence).length, htmlCssParserEvidenceFiles: htmlCssFiles.filter((file) => hasHtmlParserEvidence(file) || hasCssParserEvidence(file)).length,
8
8
  htmlParserEvidenceFailedFiles: htmlFiles.filter(hasParserEvidenceFailure).length, cssParserEvidenceFailedFiles: cssFiles.filter(hasParserEvidenceFailure).length, htmlCssParserEvidenceFailedFiles: htmlCssFiles.filter(hasParserEvidenceFailure).length,
9
9
  htmlIdentityEvidenceFiles: htmlFiles.filter(hasHtmlIdentityEvidence).length, cssSelectorTargetEvidenceFiles: cssFiles.filter(hasCssSelectorTargetEvidence).length, htmlCssStructuralTargetEvidenceFiles: htmlCssFiles.filter((file) => hasHtmlIdentityEvidence(file) || hasCssSelectorTargetEvidence(file)).length,
10
- htmlExplicitIdentityEvidenceFiles: htmlFiles.filter(hasHtmlExplicitIdentityEvidence).length, htmlPathOnlyIdentityResidualFiles: htmlFiles.filter(hasHtmlPathOnlyIdentityResidual).length,
10
+ cssSelectorTargetGraphEvidenceFiles: cssFiles.filter(hasCssSelectorTargetGraphEvidence).length, cssSelectorSpecificityEvidenceFiles: cssFiles.filter(hasCssSelectorSpecificityEvidence).length, cssSelectorTargetMoveFiles: cssFiles.filter(hasCssSelectorTargetMove).length,
11
+ htmlExplicitIdentityEvidenceFiles: htmlFiles.filter(hasHtmlExplicitIdentityEvidence).length, htmlPathOnlyIdentityResidualFiles: htmlFiles.filter(hasHtmlPathOnlyIdentityResidual).length, htmlDuplicateIdentityEvidenceFiles: htmlFiles.filter(hasHtmlDuplicateIdentityEvidence).length, htmlDuplicateIdentityKeys: htmlFiles.reduce((sum, file) => sum + htmlDuplicateIdentityKeyCount(file), 0),
11
12
  htmlRuntimeBoundaryEvidenceFiles: htmlFiles.filter(hasHtmlRuntimeBoundaryEvidence).length, htmlFrameworkBoundaryEvidenceFiles: htmlFiles.filter(hasHtmlFrameworkBoundaryEvidence).length, htmlProofGapBlockedFiles: htmlFiles.filter(hasHtmlProofGapBlockedConflict).length,
12
13
  htmlIdentityEvidenceFailedFiles: htmlFiles.filter(hasHtmlIdentityEvidenceFailure).length, cssSelectorTargetConflictFiles: cssFiles.filter(hasCssSelectorTargetConflict).length, htmlCssStructuralTargetEvidenceFailedFiles: htmlCssFiles.filter((file) => hasHtmlIdentityEvidenceFailure(file) || hasCssSelectorTargetConflict(file)).length,
13
14
  cssSelectorTargetRebasedFiles: cssFiles.filter(hasCssSelectorTargetRebase).length,
14
15
  cssScopedCascadeFiles: cssFiles.filter(hasCssScopedCascadeScope).length, cssScopedCascadeEvidenceFiles: cssFiles.filter(hasCssScopedCascadeEvidence).length, cssScopedCascadeBlockedFiles: cssFiles.filter(hasCssScopedCascadeMissingProof).length,
16
+ cssDependencySurfaceFiles: cssFiles.filter(hasCssDependencySurface).length, cssDependencyGraphEvidenceFiles: cssFiles.filter((file) => hasCssDependencySurface(file) && hasCssDependencyGraphEvidence(file)).length, cssDependencyGraphMissingProofFiles: cssFiles.filter(hasCssDependencyGraphMissingProof).length, cssDependencyGraphBlockedFiles: cssFiles.filter(hasCssDependencyGraphBlockedConflict).length,
15
17
  htmlCssBrowserRuntimeProofs: htmlCssFiles.filter(hasBrowserRuntimeProof).length
16
18
  };
17
19
  }
@@ -32,46 +34,126 @@ function hasCssParserEvidence(file) {
32
34
  function hasParserEvidenceFailure(file) { return (file?.result?.parserEvidence?.parseErrors ?? 0) > 0; }
33
35
  function hasHtmlIdentityEvidence(file) {
34
36
  const evidence = file?.result?.identityEvidence;
35
- return evidence?.parserBackedStructuralSpans === true && evidence.structuralAddressability === true;
37
+ return evidence?.parserBackedStructuralSpans === true && evidence.structuralAddressability === true && !hasDuplicateExplicitIdentityKeys(evidence);
36
38
  }
37
39
  function hasHtmlExplicitIdentityEvidence(file) {
38
40
  const evidence = file?.result?.identityEvidence;
39
41
  return hasHtmlIdentityEvidence(file) && evidence.explicitIdentityAvailable === true;
40
42
  }
41
43
  function hasHtmlPathOnlyIdentityResidual(file) { return (file?.result?.identityEvidence?.pathOnlyIdentityElements ?? 0) > 0; }
42
- function hasHtmlRuntimeBoundaryEvidence(file) { return (file?.result?.identityEvidence?.runtimeBoundaryElements ?? 0) > 0; }
43
- function hasHtmlFrameworkBoundaryEvidence(file) { return (file?.result?.identityEvidence?.frameworkBoundaryElements ?? 0) > 0; }
44
+ function hasHtmlRuntimeBoundaryEvidence(file) {
45
+ return (file?.result?.identityEvidence?.runtimeBoundaryElements ?? 0) > 0 || hasRuntimeBoundaryConflict(file);
46
+ }
47
+ function hasHtmlFrameworkBoundaryEvidence(file) {
48
+ return (file?.result?.identityEvidence?.frameworkBoundaryElements ?? 0) > 0 || hasFrameworkBoundaryConflict(file);
49
+ }
44
50
  function hasHtmlIdentityEvidenceFailure(file) {
45
51
  const evidence = file?.result?.identityEvidence;
46
- return Boolean(evidence) && (evidence.parserBackedStructuralSpans !== true || evidence.structuralAddressability !== true);
52
+ return Boolean(evidence) && (evidence.parserBackedStructuralSpans !== true || evidence.structuralAddressability !== true || hasDuplicateExplicitIdentityKeys(evidence));
47
53
  }
48
54
  function hasCssSelectorTargetEvidence(file) {
49
55
  const evidence = file?.result?.selectorTargetEvidence;
50
56
  return evidence?.parserBackedRuleSpans === true;
51
57
  }
58
+ function hasCssSelectorTargetGraphEvidence(file) {
59
+ return file?.result?.selectorTargetEvidence?.selectorTargetGraphHashPresent === true;
60
+ }
61
+ function hasCssSelectorSpecificityEvidence(file) {
62
+ const sides = Object.values(file?.result?.selectorTargetEvidence?.sides ?? {});
63
+ return sides.length > 0 && sides.every((side) => (side?.ruleCount ?? 0) === 0 || (side?.selectorSpecificityRecords ?? 0) >= side.ruleCount);
64
+ }
65
+ function hasCssSelectorTargetMove(file) {
66
+ return (file?.result?.selectorTargetEvidence?.selectorMoveCount ?? 0) > 0;
67
+ }
52
68
  function hasCssSelectorTargetConflict(file) {
53
69
  return (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => conflict.code === 'css-selector-target-conflict');
54
70
  }
55
71
  function hasHtmlProofGapBlockedConflict(file) {
56
72
  return (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => conflict.code === 'html-proof-gap-blocked');
57
73
  }
74
+ function hasRuntimeBoundaryConflict(file) {
75
+ return (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => HtmlRuntimeBoundaryReasonCodes.has(conflict?.details?.reasonCode));
76
+ }
77
+ function hasFrameworkBoundaryConflict(file) {
78
+ return (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => HtmlFrameworkBoundaryReasonCodes.has(conflict?.details?.reasonCode));
79
+ }
58
80
  function hasCssSelectorTargetRebase(file) { return (file?.result?.selectorTargetEvidence?.rebasedChangeCount ?? 0) > 0; }
59
- function hasCssScopedCascadeScope(file) { return cssScopedRuleCount(file) > 0; }
81
+ function hasCssScopedCascadeScope(file) {
82
+ return cssScopedRuleCount(file) > 0 || hasCssScopedCascadeProofReason(file) || file?.result?.parserEvidence?.scopedCascadeGraphHashPresent === false;
83
+ }
60
84
  function hasCssScopedCascadeEvidence(file) {
61
- return hasCssScopedCascadeScope(file) && file?.result?.parserEvidence?.scopedCascadeGraphHashPresent === true;
85
+ return hasCssScopedCascadeScope(file) && !hasCssScopedCascadeMissingProof(file) && file?.result?.parserEvidence?.scopedCascadeGraphHashPresent === true;
62
86
  }
63
87
  function hasCssScopedCascadeMissingProof(file) {
64
- return hasCssScopedCascadeScope(file) && (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => ScopedCascadeMissingProofReasonCodes.has(conflict?.details?.reasonCode));
88
+ return hasCssScopedCascadeProofReason(file);
89
+ }
90
+ function hasCssScopedCascadeProofReason(file) {
91
+ return (file?.result?.conflicts ?? file?.conflicts ?? []).some((conflict) => ScopedCascadeMissingProofReasonCodes.has(conflict?.details?.reasonCode));
65
92
  }
66
93
  function cssScopedRuleCount(file) {
67
94
  const sides = Object.values(file?.result?.selectorTargetEvidence?.sides ?? {});
68
95
  return sides.reduce((count, side) => Math.max(count, side?.scopedRuleCount ?? 0), 0);
69
96
  }
97
+ function hasCssDependencySurface(file) {
98
+ return hasCssDependencyEvidenceSurface(file) || hasCssDependencyTextSurface(file) || hasCssDependencyConflictSurface(file);
99
+ }
100
+ function hasCssDependencyGraphEvidence(file) {
101
+ return cssDependencyEvidenceRecords(file).some((evidence) => evidence.dependencyGraphHashPresent === true || evidence.cssDependencyGraphHashPresent === true || typeof evidence.dependencyGraphHash === 'string' || typeof evidence.cssDependencyGraphHash === 'string' || typeof evidence.graphHash === 'string' || typeof evidence.customPropertyGraphHash === 'string' || typeof evidence.animationGraphHash === 'string' || typeof evidence.fontFaceGraphHash === 'string' || typeof evidence.assetGraphHash === 'string');
102
+ }
103
+ function hasCssDependencyGraphMissingProof(file) { return hasCssDependencySurface(file) && !hasCssDependencyGraphEvidence(file); }
104
+ function hasCssDependencyEvidenceSurface(file) {
105
+ return cssDependencyEvidenceRecords(file).some((evidence) => evidence.hasDependencySurface === true || (evidence.dependencySurfaceCount ?? 0) > 0 || (evidence.customPropertyDefinitions ?? 0) > 0 || (evidence.customPropertyReferences ?? 0) > 0 || (evidence.varReferences ?? 0) > 0 || (evidence.varFallbackReferences ?? 0) > 0 || (evidence.animationNameLinks ?? 0) > 0 || (evidence.keyframeLinks ?? 0) > 0 || (evidence.fontFaceLinks ?? 0) > 0 || (evidence.urlAssetReferences ?? 0) > 0);
106
+ }
107
+ function hasCssDependencyTextSurface(file) {
108
+ const sourceText = cssDependencySourceText(file);
109
+ return sourceText.length > 0 && CssDependencySurfacePatterns.some((pattern) => pattern.test(sourceText));
110
+ }
111
+ function hasCssDependencyConflictSurface(file) {
112
+ return cssFileConflicts(file).some(isCssDependencyConflict);
113
+ }
114
+ function hasCssDependencyGraphBlockedConflict(file) {
115
+ return cssFileConflicts(file).some((conflict) => isCssDependencyConflict(conflict) || CssDependencyMissingProofReasonCodes.has(conflict?.details?.reasonCode));
116
+ }
117
+ function cssDependencyEvidenceRecords(file) {
118
+ const result = file?.result ?? {};
119
+ return [result.dependencyEvidence, result.cssDependencyEvidence, result.dependencyGraphEvidence, result.cssDependencyGraphEvidence, result.parserEvidence?.dependencyEvidence, result.parserEvidence?.cssDependencyEvidence, result.parserEvidence?.dependencyGraphEvidence, result.parserEvidence?.cssDependencyGraphEvidence].filter(isPlainObject);
120
+ }
121
+ function cssDependencySourceText(file) {
122
+ return [file?.outputSourceText, file?.sourceText, file?.result?.mergedSourceText].filter((value) => typeof value === 'string').join('\n');
123
+ }
124
+ function cssFileConflicts(file) { return file?.result?.conflicts ?? file?.conflicts ?? []; }
125
+ function isCssDependencyConflict(conflict) {
126
+ const details = conflict?.details ?? {};
127
+ const codes = [conflict?.code, details.reasonCode, details.proofGap?.code].map((value) => String(value ?? ''));
128
+ return codes.some((code) => CssDependencyCodeFragments.some((fragment) => code.includes(fragment))) || isCssDependencyAtRule(details.before) || isCssDependencyAtRule(details.after);
129
+ }
130
+ function isCssDependencyAtRule(shape) { return CssDependencyAtRuleNames.has(String(shape?.atRuleName ?? '').toLowerCase()); }
70
131
  function hasBrowserRuntimeProof(file) {
71
132
  const admission = file?.result?.admission ?? file?.admission ?? {};
72
133
  return admission.browserRuntimeEquivalenceClaim === true || admission.browserCascadeEquivalenceClaim === true || admission.browserRenderEquivalenceClaim === true;
73
134
  }
135
+ function hasHtmlDuplicateIdentityEvidence(file) { return hasDuplicateExplicitIdentityKeys(file?.result?.identityEvidence); }
136
+ function htmlDuplicateIdentityKeyCount(file) {
137
+ return duplicateExplicitIdentityKeys(file?.result?.identityEvidence).length;
138
+ }
139
+ function hasDuplicateExplicitIdentityKeys(evidence) {
140
+ return duplicateExplicitIdentityKeys(evidence).length > 0;
141
+ }
142
+ function duplicateExplicitIdentityKeys(evidence) {
143
+ return Object.values(evidence?.sides ?? {}).flatMap((side) => {
144
+ const counts = new Map();
145
+ for (const key of side?.explicitIdentityKeys ?? []) counts.set(key, (counts.get(key) ?? 0) + 1);
146
+ return [...counts.entries()].filter(([, count]) => count > 1).map(([key]) => key);
147
+ });
148
+ }
74
149
 
75
150
  const ScopedCascadeMissingProofReasonCodes = 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']);
151
+ const HtmlRuntimeBoundaryReasonCodes = new Set(['script-runtime-boundary', 'style-runtime-boundary', 'template-runtime-boundary', 'slot-runtime-boundary', 'custom-element-runtime-boundary', 'event-handler-runtime-boundary', 'inline-style-runtime-boundary', 'iframe-runtime-boundary', 'iframe-srcdoc-runtime-boundary']);
152
+ const HtmlFrameworkBoundaryReasonCodes = new Set(['framework-directive-boundary', 'custom-element-runtime-boundary']);
153
+ const CssDependencyMissingProofReasonCodes = new Set(['css-dependency-graph-evidence-missing', 'css-custom-property-dependency-graph-unproved', 'css-var-fallback-dependency-graph-unproved', 'css-animation-name-keyframes-graph-unproved', 'css-font-face-dependency-graph-unproved', 'css-url-asset-dependency-graph-unproved']);
154
+ const CssDependencyAtRuleNames = new Set(['keyframes', 'font-face']);
155
+ const CssDependencyCodeFragments = ['custom-property', 'var-fallback', 'variable-dependency', 'dependency-graph', 'keyframes', 'animation-name', 'font-face', 'url-asset', 'asset-dependency'];
156
+ const CssDependencySurfacePatterns = [/(^|[;{\s])--[-_A-Za-z][\w-]*\s*:/, /\bvar\s*\(/i, /@(?:-[\w]+-)?keyframes\b/i, /(^|[;{\s])animation(?:-name)?\s*:/i, /@font-face\b/i, /\burl\s*\(/i];
157
+ function isPlainObject(value) { return Boolean(value && typeof value === 'object' && !Array.isArray(value)); }
76
158
 
77
159
  export { htmlCssProjectSummary };
@@ -1,7 +1,7 @@
1
1
  import { safeMergeCssSource } from '@shapeshift-labs/frontier-lang-css';
2
2
  import { safeMergeHtmlSource } from '@shapeshift-labs/frontier-lang-html';
3
3
  import { compactRecord } from './js-ts-safe-merge-context.js';
4
- import { hashText, safeId } from './js-ts-safe-project-merge-core.js';
4
+ import { hashText, safeId, uniqueStrings } from './js-ts-safe-project-merge-core.js';
5
5
 
6
6
  function projectFileLanguage(file, input) {
7
7
  return file.language ?? inferLanguageFromPath(file.sourcePath) ?? input.language ?? 'typescript';
@@ -12,8 +12,12 @@ function maybeMergeHtmlCssProjectFile(options) {
12
12
  const language = String(context.language ?? '').toLowerCase();
13
13
  const merge = language === 'html' ? safeMergeHtmlSource : language === 'css' ? safeMergeCssSource : undefined;
14
14
  if (!merge) return undefined;
15
- const result = merge({ ...sourceInput, ...htmlCssMergeOptionsForProjectFile(input, file.sourcePath, language), ...context, id: `${projectId}_${safeId(file.sourcePath)}`, baseSourceText: base, workerSourceText: worker, headSourceText: head });
16
- return result.status === 'merged' ? mergedHtmlCssFile(file, context, result, language) : blockedHtmlCssFile(file, context, result);
15
+ const resultId = `${projectId}_${safeId(file.sourcePath)}`;
16
+ const result = merge({ ...sourceInput, ...htmlCssMergeOptionsForProjectFile(input, file.sourcePath, language), ...context, id: resultId, baseSourceText: base, workerSourceText: worker, headSourceText: head });
17
+ const admittedResult = language === 'html' && result.status === 'merged'
18
+ ? blockHtmlProofGapChanges({ result, id: resultId, sourcePath: file.sourcePath, base, worker, head }) ?? result
19
+ : result;
20
+ return admittedResult.status === 'merged' ? mergedHtmlCssFile(file, context, admittedResult, language) : blockedHtmlCssFile(file, context, admittedResult);
17
21
  }
18
22
 
19
23
  function inferLanguageFromPath(sourcePath) {
@@ -32,6 +36,136 @@ function htmlCssMergeOptionsForProjectFile(input, sourcePath, language) {
32
36
  return compactRecord({ ...(language === 'css' ? input.cssMergeOptions ?? input.styleMergeOptions : input.htmlMergeOptions ?? input.markupMergeOptions), ...(byPath?.[sourcePath] ?? {}) });
33
37
  }
34
38
 
39
+ function blockHtmlProofGapChanges({ result, id, sourcePath, base, worker, head }) {
40
+ const conflicts = [
41
+ htmlDuplicateIdentityConflict(result),
42
+ ...htmlRuntimeBoundaryChanges(base, worker, head).map((change) => htmlProofGapConflict(id, sourcePath, change.reasonCode, change))
43
+ ].filter(Boolean);
44
+ if (!conflicts.length) return undefined;
45
+ const allConflicts = [...(result.conflicts ?? []), ...conflicts];
46
+ const { mergedSourceText, mergedSourceHash, ...rest } = result;
47
+ return compactRecord({
48
+ ...rest,
49
+ status: 'blocked',
50
+ operation: 'blocked',
51
+ conflicts: allConflicts,
52
+ admission: blockedHtmlProofGapAdmission(result.admission, allConflicts),
53
+ autoMergeClaim: false,
54
+ semanticEquivalenceClaim: false,
55
+ browserRuntimeEquivalenceClaim: false
56
+ });
57
+ }
58
+
59
+ function htmlDuplicateIdentityConflict(result) {
60
+ const duplicates = duplicateHtmlExplicitIdentityKeys(result?.identityEvidence);
61
+ if (!duplicates.length) return undefined;
62
+ return htmlProofGapConflict(result.id, result.sourcePath, 'html-duplicate-explicit-identity', {
63
+ boundary: 'html-explicit-identity',
64
+ duplicateIdentityKeys: duplicates
65
+ }, 'html-duplicate-identity-blocked');
66
+ }
67
+
68
+ function duplicateHtmlExplicitIdentityKeys(identityEvidence) {
69
+ return Object.entries(identityEvidence?.sides ?? {}).flatMap(([side, evidence]) => {
70
+ const counts = new Map();
71
+ for (const key of evidence?.explicitIdentityKeys ?? []) counts.set(key, (counts.get(key) ?? 0) + 1);
72
+ return [...counts.entries()]
73
+ .filter(([, count]) => count > 1)
74
+ .map(([key, count]) => ({ side, key, count }));
75
+ });
76
+ }
77
+
78
+ function htmlRuntimeBoundaryChanges(base, worker, head) {
79
+ const baseEventHandlers = htmlEventHandlerBoundaryFingerprint(base);
80
+ return [
81
+ htmlRuntimeBoundaryChange('worker', base, worker, baseEventHandlers),
82
+ htmlRuntimeBoundaryChange('head', base, head, baseEventHandlers)
83
+ ].filter(Boolean);
84
+ }
85
+
86
+ function htmlRuntimeBoundaryChange(side, base, sourceText, baseEventHandlers) {
87
+ if (sourceText === base) return undefined;
88
+ const eventHandlers = htmlEventHandlerBoundaryFingerprint(sourceText);
89
+ if (eventHandlers === baseEventHandlers) return undefined;
90
+ return {
91
+ side,
92
+ reasonCode: 'event-handler-runtime-boundary',
93
+ boundary: 'html-event-handler-attribute',
94
+ boundaryAttributes: htmlEventHandlerBoundaryAttributeNames(sourceText)
95
+ };
96
+ }
97
+
98
+ function htmlEventHandlerBoundaryFingerprint(sourceText) {
99
+ return htmlEventHandlerBoundaryAttributes(sourceText)
100
+ .map((attribute) => `${attribute.tagName}:${attribute.name}=${String(attribute.value)}`)
101
+ .sort()
102
+ .join('\n');
103
+ }
104
+
105
+ function htmlEventHandlerBoundaryAttributeNames(sourceText) {
106
+ return uniqueStrings(htmlEventHandlerBoundaryAttributes(sourceText).map((attribute) => attribute.name));
107
+ }
108
+
109
+ function htmlEventHandlerBoundaryAttributes(sourceText) {
110
+ const attributes = [];
111
+ for (const tag of String(sourceText ?? '').matchAll(/<[A-Za-z][\w:-]*(?:\s+[^<>]*?)?\/?>/g)) {
112
+ const parsed = /^<([A-Za-z][\w:-]*)([\s\S]*?)\/?>$/.exec(tag[0]);
113
+ if (!parsed) continue;
114
+ const tagName = parsed[1].toLowerCase();
115
+ for (const attribute of parseHtmlAttributes(parsed[2] ?? '')) {
116
+ if (/^on[\w:.-]+$/i.test(attribute.name)) attributes.push({ ...attribute, name: attribute.name.toLowerCase(), tagName });
117
+ }
118
+ }
119
+ return attributes;
120
+ }
121
+
122
+ function parseHtmlAttributes(text) {
123
+ const attributes = [];
124
+ const pattern = /([:@A-Za-z_][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
125
+ for (const match of text.matchAll(pattern)) attributes.push({ name: match[1], value: match[2] ?? match[3] ?? match[4] ?? true });
126
+ return attributes;
127
+ }
128
+
129
+ function htmlProofGapConflict(id, sourcePath, reasonCode, details = {}, code = 'html-proof-gap-blocked') {
130
+ return {
131
+ code,
132
+ gateId: 'html-semantic-merge',
133
+ sourcePath,
134
+ details: compactRecord({
135
+ reasonCode,
136
+ conflictKey: `html#${id}#${reasonCode}#${details.side ?? details.duplicateIdentityKeys?.[0]?.key ?? sourcePath ?? 'source'}`,
137
+ proofGap: {
138
+ code: reasonCode,
139
+ status: 'not-claimed',
140
+ summary: htmlProofGapSummary(reasonCode),
141
+ failClosed: true,
142
+ semanticEquivalenceClaim: false
143
+ },
144
+ ...details
145
+ })
146
+ };
147
+ }
148
+
149
+ function htmlProofGapSummary(reasonCode) {
150
+ if (reasonCode === 'html-duplicate-explicit-identity') return 'Duplicate explicit HTML identity keys make structural target admission ambiguous.';
151
+ if (reasonCode === 'event-handler-runtime-boundary') return 'HTML event handler attributes execute in the browser runtime and require source-bound host evidence.';
152
+ return 'HTML proof gap requires source-bound evidence before structural merge admission.';
153
+ }
154
+
155
+ function blockedHtmlProofGapAdmission(admission = {}, conflicts = []) {
156
+ return {
157
+ ...admission,
158
+ status: 'blocked',
159
+ action: 'human-review',
160
+ reviewRequired: true,
161
+ autoApplyCandidate: false,
162
+ autoMergeClaim: false,
163
+ semanticEquivalenceClaim: false,
164
+ browserRuntimeEquivalenceClaim: false,
165
+ reasonCodes: uniqueStrings([...(admission.reasonCodes ?? []), ...conflicts.map((conflict) => conflict.details?.reasonCode ?? conflict.code)])
166
+ };
167
+ }
168
+
35
169
  function mergedHtmlCssFile(file, context, result, language) {
36
170
  return compactRecord({
37
171
  kind: 'frontier.lang.jsTsProjectSafeMergeFile', version: 1, sourcePath: file.sourcePath, language: context.language, status: 'merged', operation: `merged-${language}-source`,
@@ -98,6 +98,15 @@ function jsxPropDetails(record) {
98
98
  propValueReferenceRoot: record.propValueReferenceRoot, propValueReferencePath: record.propValueReferencePath,
99
99
  propValueOptionalReference: record.propValueOptionalReference, propValueOptionalReferenceSegments: record.propValueOptionalReferenceSegments,
100
100
  propValueOptionalReferenceSegmentIndexes: record.propValueOptionalReferenceSegmentIndexes, propValueOptionalNullishBoundaryCount: record.propValueOptionalNullishBoundaryCount,
101
+ propValueClaimScope: record.propValueClaimScope, propValueRenderEquivalenceClaim: record.propValueRenderEquivalenceClaim,
102
+ propValueStaticSpreadSourceKind: record.propValueStaticSpreadSourceKind, propValueStaticSpreadSourceName: record.propValueStaticSpreadSourceName,
103
+ propValueStaticSpreadPropEntries: record.propValueStaticSpreadPropEntries, propValueStaticSpreadPropNames: record.propValueStaticSpreadPropNames,
104
+ propValueStaticSpreadPropCount: record.propValueStaticSpreadPropCount,
105
+ propValueStaticSpreadEffectivePropEntries: record.propValueStaticSpreadEffectivePropEntries, propValueStaticSpreadEffectivePropNames: record.propValueStaticSpreadEffectivePropNames,
106
+ propValueStaticSpreadExplicitOverridePropNames: record.propValueStaticSpreadExplicitOverridePropNames,
107
+ propValueStaticSpreadOverridesExplicitPropNames: record.propValueStaticSpreadOverridesExplicitPropNames,
108
+ propValueStaticSpreadDuplicatePropNames: record.propValueStaticSpreadDuplicatePropNames,
109
+ propValueStaticSpreadPrecedenceStatus: record.propValueStaticSpreadPrecedenceStatus,
101
110
  propValueDynamicText: record.propValueDynamicText, propValueDynamicBlockerReasonCode: record.propValueDynamicBlockerReasonCode,
102
111
  propValueExpressionHash: record.propValueExpressionHash, propValueSignatureHash: record.propValueSignatureHash,
103
112
  componentPropRenderFlowStatus: record.componentPropRenderFlowStatus, componentPropRenderFlowReasonCode: record.componentPropRenderFlowReasonCode,
@@ -259,6 +259,9 @@ function mergeProjectFile(file, input, projectId, projectSymbolRenames) {
259
259
  policy: file.policy ?? file.mergePolicy ?? policyForFile(input, file.sourcePath)
260
260
  });
261
261
  if (result.status !== 'merged') {
262
+ if (base === worker && base === head) {
263
+ return syntheticFile(file, context, base, 'unchanged-identical');
264
+ }
262
265
  return maybeMergeImportSpecifierRemovalFile(file, context, result, input)
263
266
  ?? mergeBlockedFile(file, context, result);
264
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.162",
3
+ "version": "0.2.163",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",