@shapeshift-labs/frontier-lang-compiler 0.2.124 → 0.2.126

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
@@ -308,9 +308,13 @@ On a local Node v26.1.0 smoke fixture with 10 small JS/TS files and 36 scanned
308
308
  stage files for the delta case, baseline project merge JSON was 115 KB at a
309
309
  21.6 ms median. `includeOutputProjectSymbolGraph` raised the returned JSON to
310
310
  17.8 MB at a 303.1 ms median, and `includeProjectGraphDelta` raised it to
311
- 83.0 MB at a 1,466.8 ms median. Keep these paths behind admission-queue caps
312
- for file count, total source bytes, graph edge count, and serialized artifact
313
- bytes, or expose summary/lazy graph materialization before using them broadly.
311
+ 83.0 MB at a 1,466.8 ms median. Pass `projectGraphLimits` for admission queues:
312
+ `maxFiles`, `maxSourceBytes`, `maxImportEdges`, `maxExportEdges`, and
313
+ `maxSerializedBytes` produce `project-graph-limit-exceeded` conflicts with the
314
+ stage, limit kind, actual value, and configured limit. Limit failures block
315
+ admission and omit oversized project graph artifacts from the returned result.
316
+ Invalid limits such as negative numbers, `NaN`, or infinity fail closed with
317
+ `project-graph-limit-invalid`.
314
318
 
315
319
  High-risk native features also have explicit evidence policies. These policies are advisory in this package: they tell a swarm or admission queue what evidence is missing without silently changing the existing readiness classification.
316
320
 
@@ -53,6 +53,22 @@ export type JsTsProjectSafeMergeOutputProjectImports =
53
53
 
54
54
  export type JsTsProjectGraphStageName = 'base' | 'worker' | 'head' | 'output' | string;
55
55
 
56
+ export type JsTsProjectGraphLimitKind =
57
+ | 'source-files'
58
+ | 'source-bytes'
59
+ | 'import-edges'
60
+ | 'export-edges'
61
+ | 'serialized-bytes'
62
+ | string;
63
+
64
+ export interface JsTsProjectGraphLimits {
65
+ readonly maxFiles?: number;
66
+ readonly maxSourceBytes?: number;
67
+ readonly maxImportEdges?: number;
68
+ readonly maxExportEdges?: number;
69
+ readonly maxSerializedBytes?: number;
70
+ }
71
+
56
72
  export type JsTsProjectSafeMergeProjectGraphImportsByStage = Readonly<{
57
73
  base?: JsTsProjectSafeMergeOutputProjectImports;
58
74
  worker?: JsTsProjectSafeMergeOutputProjectImports;
@@ -72,6 +88,7 @@ export interface JsTsProjectSafeMergeInput {
72
88
  readonly allowFileDeletes?: boolean;
73
89
  readonly includeOutputProjectSymbolGraph?: boolean;
74
90
  readonly includeProjectGraphDelta?: boolean;
91
+ readonly projectGraphLimits?: JsTsProjectGraphLimits;
75
92
  readonly outputProjectImports?: JsTsProjectSafeMergeOutputProjectImports;
76
93
  readonly baseProjectImports?: JsTsProjectSafeMergeOutputProjectImports;
77
94
  readonly workerProjectImports?: JsTsProjectSafeMergeOutputProjectImports;
@@ -132,6 +149,7 @@ export interface JsTsProjectSafeMergeAdmission {
132
149
  export interface JsTsProjectGraphDeltaStageSummary {
133
150
  readonly stage: JsTsProjectGraphStageName;
134
151
  readonly sourceFiles: number;
152
+ readonly sourceBytes: number;
135
153
  readonly documents: number;
136
154
  readonly symbols: number;
137
155
  readonly fileHashes: number;
@@ -143,6 +161,8 @@ export interface JsTsProjectGraphDeltaStageSummary {
143
161
  readonly suppliedImports: number;
144
162
  readonly matchedSuppliedImports: number;
145
163
  readonly scannerFallbackImports: number;
164
+ readonly serializedBytes?: number;
165
+ readonly limitConflicts: number;
146
166
  }
147
167
 
148
168
  export interface JsTsProjectGraphDeltaStage {
@@ -152,6 +172,7 @@ export interface JsTsProjectGraphDeltaStage {
152
172
  readonly projectImport?: NativeProjectImportResult;
153
173
  readonly projectSymbolGraph?: NativeProjectSymbolGraphSummary;
154
174
  readonly summary: JsTsProjectGraphDeltaStageSummary;
175
+ readonly limitConflicts?: readonly JsTsSafeMergeConflict[];
155
176
  }
156
177
 
157
178
  export interface JsTsProjectGraphDeltaSummary {
@@ -165,6 +186,9 @@ export interface JsTsProjectGraphDeltaSummary {
165
186
  readonly suppliedImports: number;
166
187
  readonly matchedSuppliedImports: number;
167
188
  readonly scannerFallbackImports: number;
189
+ readonly sourceBytes: number;
190
+ readonly serializedBytes: number;
191
+ readonly limitConflicts: number;
168
192
  readonly conflicts: number;
169
193
  readonly publicContractConflicts: number;
170
194
  readonly reExportIdentityConflicts: number;
@@ -201,6 +225,7 @@ export interface JsTsProjectSafeMergeResult {
201
225
  readonly projectGraphConflicts: number;
202
226
  readonly outputProjectGraphConflicts: number;
203
227
  readonly projectGraphDeltaConflicts: number;
228
+ readonly projectGraphLimitConflicts: number;
204
229
  readonly projectGraphPublicContractConflicts: number;
205
230
  readonly projectGraphReExportIdentityConflicts: number;
206
231
  readonly projectGraphImportTargetConflicts: number;
@@ -200,6 +200,7 @@ function validateCrossSideAddedNames(workerPlan, headPlan, context) {
200
200
  const nameKey = `${entry.kind}:${name}`;
201
201
  const headEntry = headEntriesByName.get(nameKey);
202
202
  if (headEntry) {
203
+ if (shouldDeferReExportNameConflict(entry, headEntry, context)) continue;
203
204
  const typeAliasConflict = entry.declarationInfo?.declarationKind === 'type'
204
205
  || headEntry.declarationInfo?.declarationKind === 'type';
205
206
  addConflict(context, {
@@ -216,6 +217,16 @@ function validateCrossSideAddedNames(workerPlan, headPlan, context) {
216
217
  }
217
218
  }
218
219
 
220
+ function shouldDeferReExportNameConflict(left, right, context) {
221
+ return context.deferReExportIdentityConflictsToProjectGraph === true
222
+ && isReExportEntry(left)
223
+ && isReExportEntry(right);
224
+ }
225
+
226
+ function isReExportEntry(entry) {
227
+ return entry?.kind === 'export' && entry.declarationInfo?.reExport === true;
228
+ }
229
+
219
230
  function validateCrossSideImportAdditions(workerPlan, headPlan, context) {
220
231
  for (const [key, workerAdditions] of workerPlan.importAdditions) {
221
232
  const headAdditions = headPlan.importAdditions.get(key) ?? [];
@@ -5,6 +5,7 @@ export function createMergeContext(input) {
5
5
  id: String(input.id ?? 'js_ts_safe_merge'),
6
6
  sourcePath: input.sourcePath,
7
7
  language: input.language ?? 'typescript',
8
+ deferReExportIdentityConflictsToProjectGraph: input.deferReExportIdentityConflictsToProjectGraph === true,
8
9
  conflicts: [],
9
10
  blockedGateIds: new Set(),
10
11
  gateReasonCodes: new Map()
@@ -24,6 +24,7 @@ export function validateLedgerUniqueness(ledger, context) {
24
24
  const nameKey = `${entry.kind}:${name}`;
25
25
  const existing = nameOwners.get(nameKey);
26
26
  if (existing) {
27
+ if (shouldDeferMergedReExportNameConflict(ledger, existing, entry, context)) continue;
27
28
  addConflict(context, {
28
29
  code: JsTsSafeMergeConflictCodes.duplicateName,
29
30
  gateId: JsTsSafeMergeGateIds.uniqueNames,
@@ -65,6 +66,17 @@ function validateUniqueImportSpecifiers(entry, side, context) {
65
66
  }
66
67
  }
67
68
 
69
+ function shouldDeferMergedReExportNameConflict(ledger, left, right, context) {
70
+ return ledger.label === 'merged'
71
+ && context.deferReExportIdentityConflictsToProjectGraph === true
72
+ && isReExportEntry(left)
73
+ && isReExportEntry(right);
74
+ }
75
+
76
+ function isReExportEntry(entry) {
77
+ return entry?.kind === 'export' && entry.declarationInfo?.reExport === true;
78
+ }
79
+
68
80
  export function indexBaseLedger(base, context) {
69
81
  const entriesByKey = new Map();
70
82
  for (const entry of base.entries) entriesByKey.set(entry.key, entry);
@@ -19,7 +19,7 @@ export function classifyStatement(text, start, end) {
19
19
  if (unsupported) return { unsupported, text, start, end };
20
20
  return {
21
21
  kind: declarationInfo.kind,
22
- key: `${declarationInfo.kind}:${declarationInfo.names.join('|')}`,
22
+ key: declarationLedgerKey(declarationInfo),
23
23
  text,
24
24
  start,
25
25
  end,
@@ -126,6 +126,50 @@ function parseDeclarationInfo(text) {
126
126
  const defaultClass = trimmed.match(/^export\s+default\s+(?:abstract\s+)?class(?:\s+([A-Za-z_$][\w$]*))?\b/);
127
127
  if (defaultClass) return { kind: 'declaration', names: ['default'], declarationKind: 'class', exported: true, defaultExport: true };
128
128
 
129
+ const namespaceReExport = trimmed.match(/^export\s+(type\s+)?\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\3\s*;?$/);
130
+ if (namespaceReExport) {
131
+ return {
132
+ kind: 'export',
133
+ names: [namespaceReExport[2]],
134
+ declarationKind: 're-export-namespace',
135
+ exported: true,
136
+ typeOnly: Boolean(namespaceReExport[1]),
137
+ reExport: true,
138
+ moduleSpecifier: namespaceReExport[4],
139
+ namespace: namespaceReExport[2]
140
+ };
141
+ }
142
+
143
+ const starReExport = trimmed.match(/^export\s+(type\s+)?\*\s+from\s+(['"])([^'"]+)\2\s*;?$/);
144
+ if (starReExport) {
145
+ return {
146
+ kind: 'export',
147
+ names: [],
148
+ declarationKind: 're-export-star',
149
+ exported: true,
150
+ typeOnly: Boolean(starReExport[1]),
151
+ reExport: true,
152
+ moduleSpecifier: starReExport[3],
153
+ exportStar: true
154
+ };
155
+ }
156
+
157
+ const namedReExport = trimmed.match(/^export\s+(type\s+)?\{([\s\S]+)\}\s+from\s+(['"])([^'"]+)\3\s*;?$/);
158
+ if (namedReExport) {
159
+ const names = splitCommaList(namedReExport[2]).map((part) => parseExportSpecifierName(part)).filter(Boolean);
160
+ const expectedCount = splitCommaList(namedReExport[2]).filter((part) => part.trim()).length;
161
+ if (names.length !== expectedCount || names.length === 0) return undefined;
162
+ return {
163
+ kind: 'export',
164
+ names,
165
+ declarationKind: 're-export-list',
166
+ exported: true,
167
+ typeOnly: Boolean(namedReExport[1]),
168
+ reExport: true,
169
+ moduleSpecifier: namedReExport[4]
170
+ };
171
+ }
172
+
129
173
  const namedExport = trimmed.match(/^export\s+(type\s+)?\{([\s\S]+)\}\s*;?$/);
130
174
  if (namedExport) {
131
175
  const names = splitCommaList(namedExport[2]).map((part) => parseExportSpecifierName(part)).filter(Boolean);
@@ -154,6 +198,19 @@ function parseDeclarationInfo(text) {
154
198
  return undefined;
155
199
  }
156
200
 
201
+ function declarationLedgerKey(declarationInfo) {
202
+ if (declarationInfo.reExport) {
203
+ return [
204
+ declarationInfo.kind,
205
+ declarationInfo.declarationKind,
206
+ declarationInfo.moduleSpecifier ?? '',
207
+ declarationInfo.typeOnly ? 'type' : 'value',
208
+ declarationInfo.exportStar ? '*' : declarationInfo.names.join('|')
209
+ ].join(':');
210
+ }
211
+ return `${declarationInfo.kind}:${declarationInfo.names.join('|')}`;
212
+ }
213
+
157
214
  function unsupportedDeclarationPolicy(text, declarationInfo) {
158
215
  const trimmed = text.trim();
159
216
  if (/^\s*@/m.test(text)) {
@@ -2,6 +2,8 @@ import { compactRecord } from './js-ts-safe-merge-context.js';
2
2
  import { projectGraphDeltaConflicts } from './js-ts-safe-project-merge-graph-delta-conflicts.js';
3
3
 
4
4
  function outputProjectGraphConflicts(projectSymbolGraph) {
5
+ const limitConflicts = Array.isArray(projectSymbolGraph?.limitConflicts) ? projectSymbolGraph.limitConflicts : [];
6
+ projectSymbolGraph = projectSymbolGraph?.projectSymbolGraph ?? projectSymbolGraph;
5
7
  const importEdges = Array.isArray(projectSymbolGraph?.importEdges) ? projectSymbolGraph.importEdges : [];
6
8
  const missingModuleGroups = new Map();
7
9
  const missingSymbolGroups = new Map();
@@ -21,6 +23,7 @@ function outputProjectGraphConflicts(projectSymbolGraph) {
21
23
  }
22
24
  }
23
25
  return [
26
+ ...limitConflicts,
24
27
  ...[...missingModuleGroups.values()].map(projectGraphMissingImportConflict),
25
28
  ...[...missingSymbolGroups.values()].map(projectGraphMissingTargetConflict)
26
29
  ];
@@ -2,12 +2,14 @@ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
2
  import { compactRecord } from './js-ts-safe-merge-context.js';
3
3
 
4
4
  function projectGraphDeltaConflicts(projectGraphDelta) {
5
+ const limitConflicts = projectGraphDeltaLimitConflicts(projectGraphDelta);
5
6
  const baseGraph = projectGraphDelta?.stages?.base?.projectSymbolGraph;
6
7
  const workerGraph = projectGraphDelta?.stages?.worker?.projectSymbolGraph;
7
8
  const headGraph = projectGraphDelta?.stages?.head?.projectSymbolGraph;
8
9
  const outputGraph = projectGraphDelta?.stages?.output?.projectSymbolGraph;
9
- if (!baseGraph || !workerGraph || !headGraph) return [];
10
+ if (!baseGraph || !workerGraph || !headGraph) return limitConflicts;
10
11
  return [
12
+ ...limitConflicts,
11
13
  ...changedIdentityConflicts({
12
14
  code: 'project-public-contract-delta-conflict',
13
15
  label: 'public contract',
@@ -43,11 +45,16 @@ function addProjectGraphDeltaConflictSummary(projectGraphDelta, conflicts) {
43
45
  conflicts: conflicts.length,
44
46
  publicContractConflicts: conflicts.filter((conflict) => conflict.code === 'project-public-contract-delta-conflict').length,
45
47
  reExportIdentityConflicts: conflicts.filter((conflict) => conflict.code === 'project-re-export-identity-delta-conflict').length,
46
- importTargetConflicts: conflicts.filter((conflict) => conflict.code === 'project-import-target-delta-conflict').length
48
+ importTargetConflicts: conflicts.filter((conflict) => conflict.code === 'project-import-target-delta-conflict').length,
49
+ limitConflicts: conflicts.filter((conflict) => conflict.code === 'project-graph-limit-exceeded').length
47
50
  }
48
51
  };
49
52
  }
50
53
 
54
+ function projectGraphDeltaLimitConflicts(projectGraphDelta) {
55
+ return Object.values(projectGraphDelta?.stages ?? {}).flatMap((stage) => stage?.limitConflicts ?? []);
56
+ }
57
+
51
58
  function changedIdentityConflicts(input) {
52
59
  const base = recordsByIdentityKey(input.baseRecords, input.identityKey);
53
60
  const worker = recordsByIdentityKey(input.workerRecords, input.identityKey);
@@ -0,0 +1,117 @@
1
+ import { compactRecord } from './js-ts-safe-merge-context.js';
2
+
3
+ const encoder = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
4
+ const PROJECT_GRAPH_LIMIT_CODE = 'project-graph-limit-exceeded';
5
+ const PROJECT_GRAPH_INVALID_LIMIT_CODE = 'project-graph-limit-invalid';
6
+ const LIMIT_FIELDS = [
7
+ ['maxFiles', 'source-files'],
8
+ ['maxSourceBytes', 'source-bytes'],
9
+ ['maxImportEdges', 'import-edges'],
10
+ ['maxExportEdges', 'export-edges'],
11
+ ['maxSerializedBytes', 'serialized-bytes']
12
+ ];
13
+
14
+ function normalizeProjectGraphLimits(value) {
15
+ if (!value || typeof value !== 'object') return {};
16
+ const limits = {};
17
+ const invalidLimits = [];
18
+ for (const [field, limitKind] of LIMIT_FIELDS) {
19
+ if (!Object.prototype.hasOwnProperty.call(value, field) || value[field] === undefined) continue;
20
+ const normalized = normalizeLimit(value[field]);
21
+ if (normalized === undefined) invalidLimits.push({ field, limitKind, value: String(value[field]), valueType: typeof value[field] });
22
+ else limits[field] = normalized;
23
+ }
24
+ return compactRecord({ ...limits, invalidLimits: invalidLimits.length ? invalidLimits : undefined });
25
+ }
26
+
27
+ function projectGraphSourceStats(files) {
28
+ return {
29
+ sourceFiles: files.length,
30
+ sourceBytes: files.reduce((total, file) => total + utf8ByteLength(file?.sourceText ?? ''), 0)
31
+ };
32
+ }
33
+
34
+ function projectGraphSourceLimitConflicts(limits, stage, stats) {
35
+ return [
36
+ ...projectGraphInvalidLimitConflicts(limits, stage),
37
+ limitConflict(limits.maxFiles, stage, 'source-files', stats.sourceFiles),
38
+ limitConflict(limits.maxSourceBytes, stage, 'source-bytes', stats.sourceBytes)
39
+ ].filter(Boolean);
40
+ }
41
+
42
+ function projectGraphInvalidLimitConflicts(limits, stage) {
43
+ return (limits.invalidLimits ?? []).map((invalid) => ({
44
+ code: PROJECT_GRAPH_INVALID_LIMIT_CODE,
45
+ gateId: 'project-graph-limit',
46
+ message: `Project graph ${stage} stage received invalid ${invalid.limitKind} limit ${JSON.stringify(invalid.value)}.`,
47
+ details: compactRecord({
48
+ reasonCode: PROJECT_GRAPH_INVALID_LIMIT_CODE,
49
+ conflictKey: `project-graph-limit#${stage}#${invalid.limitKind}#invalid`,
50
+ stage,
51
+ limitKind: invalid.limitKind,
52
+ limitField: invalid.field,
53
+ limitValue: invalid.value,
54
+ limitValueType: invalid.valueType
55
+ })
56
+ }));
57
+ }
58
+
59
+ function projectGraphEdgeLimitConflicts(limits, stage, projectSymbolGraph) {
60
+ const importEdges = Array.isArray(projectSymbolGraph?.importEdges) ? projectSymbolGraph.importEdges.length : 0;
61
+ const exportEdges = Array.isArray(projectSymbolGraph?.exportEdges) ? projectSymbolGraph.exportEdges.length : 0;
62
+ return [
63
+ limitConflict(limits.maxImportEdges, stage, 'import-edges', importEdges),
64
+ limitConflict(limits.maxExportEdges, stage, 'export-edges', exportEdges)
65
+ ].filter(Boolean);
66
+ }
67
+
68
+ function projectGraphSerializedLimitConflict(limits, stage, artifact) {
69
+ const serializedBytes = serializedByteLength(artifact);
70
+ return {
71
+ serializedBytes,
72
+ conflict: limitConflict(limits.maxSerializedBytes, stage, 'serialized-bytes', serializedBytes)
73
+ };
74
+ }
75
+
76
+ function limitConflict(limit, stage, limitKind, actual) {
77
+ if (limit === undefined || actual <= limit) return undefined;
78
+ return {
79
+ code: PROJECT_GRAPH_LIMIT_CODE,
80
+ gateId: 'project-graph-limit',
81
+ message: `Project graph ${stage} stage exceeded ${limitKind} limit: ${actual} > ${limit}.`,
82
+ details: compactRecord({
83
+ reasonCode: PROJECT_GRAPH_LIMIT_CODE,
84
+ conflictKey: `project-graph-limit#${stage}#${limitKind}`,
85
+ stage,
86
+ limitKind,
87
+ actual,
88
+ limit
89
+ })
90
+ };
91
+ }
92
+
93
+ function normalizeLimit(value) {
94
+ if (value === null) return undefined;
95
+ const limit = Number(value);
96
+ return Number.isFinite(limit) && limit >= 0 ? Math.floor(limit) : undefined;
97
+ }
98
+
99
+ function serializedByteLength(value) {
100
+ return utf8ByteLength(JSON.stringify(value));
101
+ }
102
+
103
+ function utf8ByteLength(value) {
104
+ const text = String(value ?? '');
105
+ if (encoder) return encoder.encode(text).length;
106
+ return unescape(encodeURIComponent(text)).length;
107
+ }
108
+
109
+ export {
110
+ PROJECT_GRAPH_INVALID_LIMIT_CODE,
111
+ PROJECT_GRAPH_LIMIT_CODE,
112
+ normalizeProjectGraphLimits,
113
+ projectGraphEdgeLimitConflicts,
114
+ projectGraphSerializedLimitConflict,
115
+ projectGraphSourceLimitConflicts,
116
+ projectGraphSourceStats
117
+ };
@@ -3,6 +3,13 @@ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
3
3
  import { compactRecord } from './js-ts-safe-merge-context.js';
4
4
  import { createNativeProjectImportResult } from './internal/index-impl/createNativeProjectImportResult.js';
5
5
  import { importNativeSource } from './internal/index-impl/importNativeSource.js';
6
+ import {
7
+ normalizeProjectGraphLimits,
8
+ projectGraphEdgeLimitConflicts,
9
+ projectGraphSerializedLimitConflict,
10
+ projectGraphSourceLimitConflicts,
11
+ projectGraphSourceStats
12
+ } from './js-ts-safe-project-merge-graph-limits.js';
6
13
 
7
14
  function createJsTsProjectSafeMergeGraphArtifacts(input, outputFiles, mergeId) {
8
15
  return createProjectGraphStageArtifacts(input, outputFiles, mergeId, 'output', projectImportsForStage(input, 'output'));
@@ -32,12 +39,28 @@ function createJsTsProjectSafeMergeGraphDelta(input, files, outputFiles, mergeId
32
39
  suppliedImports: sumStageSummary(stageSummaries, 'suppliedImports'),
33
40
  matchedSuppliedImports: sumStageSummary(stageSummaries, 'matchedSuppliedImports'),
34
41
  scannerFallbackImports: sumStageSummary(stageSummaries, 'scannerFallbackImports'),
42
+ sourceBytes: sumStageSummary(stageSummaries, 'sourceBytes'),
43
+ serializedBytes: sumStageSummary(stageSummaries, 'serializedBytes'),
44
+ limitConflicts: sumStageSummary(stageSummaries, 'limitConflicts'),
35
45
  stageSummaries
36
46
  }
37
47
  };
38
48
  }
39
49
 
40
50
  function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stageImports) {
51
+ const limits = normalizeProjectGraphLimits(input.projectGraphLimits);
52
+ const sourceStats = projectGraphSourceStats(files);
53
+ const sourceLimitConflicts = projectGraphSourceLimitConflicts(limits, stageName, sourceStats);
54
+ const suppliedImports = normalizeProjectImports(stageImports);
55
+ const projectGraphImportSource = {
56
+ stage: stageName,
57
+ suppliedImports: suppliedImports.length,
58
+ matchedSuppliedImports: 0,
59
+ scannerFallbackImports: 0
60
+ };
61
+ if (sourceLimitConflicts.length) {
62
+ return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, undefined, sourceLimitConflicts);
63
+ }
41
64
  const sources = files.map((file) => ({
42
65
  id: `${mergeId}_${stageName}_${idFragment(file.sourcePath)}`,
43
66
  language: file.language ?? input.language ?? languageForPath(file.sourcePath),
@@ -46,7 +69,6 @@ function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stag
46
69
  sourceHash: file.sourceHash,
47
70
  metadata: { semanticImportExpected: true, projectSafeMergeStage: stageName, projectSafeMergeOutput: stageName === 'output' }
48
71
  }));
49
- const suppliedImports = normalizeProjectImports(stageImports);
50
72
  const importSelections = sources.map((source) => {
51
73
  const suppliedImport = matchingProjectImport(source, suppliedImports);
52
74
  return {
@@ -55,12 +77,8 @@ function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stag
55
77
  };
56
78
  });
57
79
  const imports = importSelections.map((selection) => selection.importResult);
58
- const projectGraphImportSource = {
59
- stage: stageName,
60
- suppliedImports: suppliedImports.length,
61
- matchedSuppliedImports: importSelections.filter((selection) => selection.sourceKind === `supplied-${stageName}-project-import`).length,
62
- scannerFallbackImports: importSelections.filter((selection) => selection.sourceKind === `lightweight-${stageName}-project-scan`).length
63
- };
80
+ projectGraphImportSource.matchedSuppliedImports = importSelections.filter((selection) => selection.sourceKind === `supplied-${stageName}-project-import`).length;
81
+ projectGraphImportSource.scannerFallbackImports = importSelections.filter((selection) => selection.sourceKind === `lightweight-${stageName}-project-scan`).length;
64
82
  const projectImport = createNativeProjectImportResult({
65
83
  id: `${mergeId}_${stageName}_project_import`,
66
84
  projectRoot: input.projectRoot,
@@ -74,13 +92,32 @@ function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stag
74
92
  ...(stageName === 'output' ? { outputProjectImportSource: projectGraphImportSource } : {})
75
93
  }
76
94
  }, imports);
95
+ const edgeLimitConflicts = projectGraphEdgeLimitConflicts(limits, stageName, projectImport.projectSymbolGraph);
96
+ const serialized = projectGraphSerializedLimitConflict(limits, stageName, {
97
+ projectImport,
98
+ projectSymbolGraph: projectImport.projectSymbolGraph
99
+ });
100
+ const limitConflicts = [...edgeLimitConflicts, serialized.conflict].filter(Boolean);
101
+ if (limitConflicts.length) {
102
+ return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, projectImport.projectSymbolGraph, limitConflicts, serialized.serializedBytes);
103
+ }
77
104
  return {
78
105
  kind: 'frontier.lang.jsTsProjectGraphStage',
79
106
  version: 1,
80
107
  stage: stageName,
81
108
  projectImport,
82
109
  projectSymbolGraph: projectImport.projectSymbolGraph,
83
- summary: projectGraphStageSummary(stageName, projectImport.projectSymbolGraph, projectGraphImportSource)
110
+ summary: projectGraphStageSummary(stageName, projectImport.projectSymbolGraph, projectGraphImportSource, sourceStats, serialized.serializedBytes, [])
111
+ };
112
+ }
113
+
114
+ function limitedProjectGraphStage(stageName, importSource, sourceStats, projectSymbolGraph, limitConflicts, serializedBytes) {
115
+ return {
116
+ kind: 'frontier.lang.jsTsProjectGraphStage',
117
+ version: 1,
118
+ stage: stageName,
119
+ summary: projectGraphStageSummary(stageName, projectSymbolGraph, importSource, sourceStats, serializedBytes, limitConflicts),
120
+ limitConflicts
84
121
  };
85
122
  }
86
123
 
@@ -113,11 +150,12 @@ function matchingProjectImport(source, imports) {
113
150
  });
114
151
  }
115
152
 
116
- function projectGraphStageSummary(stageName, projectSymbolGraph, importSource) {
153
+ function projectGraphStageSummary(stageName, projectSymbolGraph, importSource, sourceStats, serializedBytes, limitConflicts) {
117
154
  const importEdges = Array.isArray(projectSymbolGraph?.importEdges) ? projectSymbolGraph.importEdges : [];
118
155
  return {
119
156
  stage: stageName,
120
- sourceFiles: projectSymbolGraph?.sourceCount ?? 0,
157
+ sourceFiles: projectSymbolGraph?.sourceCount ?? sourceStats.sourceFiles,
158
+ sourceBytes: sourceStats.sourceBytes,
121
159
  documents: projectSymbolGraph?.documentCount ?? 0,
122
160
  symbols: projectSymbolGraph?.symbolCount ?? 0,
123
161
  fileHashes: projectSymbolGraph?.fileHashes?.length ?? 0,
@@ -128,7 +166,9 @@ function projectGraphStageSummary(stageName, projectSymbolGraph, importSource) {
128
166
  unresolvedImportEdges: importEdges.filter(isMissingProjectImportEdge).length,
129
167
  suppliedImports: importSource.suppliedImports,
130
168
  matchedSuppliedImports: importSource.matchedSuppliedImports,
131
- scannerFallbackImports: importSource.scannerFallbackImports
169
+ scannerFallbackImports: importSource.scannerFallbackImports,
170
+ serializedBytes,
171
+ limitConflicts: limitConflicts.length
132
172
  };
133
173
  }
134
174
 
@@ -25,7 +25,7 @@ function safeMergeJsTsProject(input = {}) {
25
25
  const graphArtifacts = projectGraphDelta?.stages?.output ?? (blockedFiles.length === 0 && input.includeOutputProjectSymbolGraph
26
26
  ? createJsTsProjectSafeMergeGraphArtifacts(input, outputFiles, id)
27
27
  : undefined);
28
- const outputGraphConflicts = outputProjectGraphConflicts(graphArtifacts?.projectSymbolGraph);
28
+ const outputGraphConflicts = outputProjectGraphConflicts(projectGraphDelta ? graphArtifacts?.projectSymbolGraph : graphArtifacts);
29
29
  const deltaGraphConflicts = projectGraphDeltaConflicts(projectGraphDelta);
30
30
  const projectGraphDeltaWithConflicts = addProjectGraphDeltaConflictSummary(projectGraphDelta, deltaGraphConflicts);
31
31
  const graphConflicts = [...outputGraphConflicts, ...deltaGraphConflicts];
@@ -60,7 +60,7 @@ function safeMergeJsTsProject(input = {}) {
60
60
  reasonCodes,
61
61
  conflictKeys
62
62
  },
63
- summary: projectSummary(fileResults, graphConflicts),
63
+ summary: projectSummary(fileResults, graphConflicts, Boolean(projectGraphDelta)),
64
64
  metadata: compactRecord({
65
65
  workerChangeSetId: input.workerChangeSetId,
66
66
  headChangeSetId: input.headChangeSetId,
@@ -71,6 +71,7 @@ function safeMergeJsTsProject(input = {}) {
71
71
  projectGraphConflicts: graphConflicts.length || undefined,
72
72
  outputProjectGraphConflicts: outputGraphConflicts.length || undefined,
73
73
  projectGraphDeltaConflicts: deltaGraphConflicts.length || undefined,
74
+ projectGraphLimitConflicts: graphConflicts.filter((conflict) => conflict.gateId === 'project-graph-limit').length || undefined,
74
75
  autoMergeClaim: false,
75
76
  semanticEquivalenceClaim: false
76
77
  })
@@ -110,6 +111,7 @@ function mergeProjectFile(file, input, projectId) {
110
111
  const result = safeMergeJsTsSource({
111
112
  ...input,
112
113
  ...context,
114
+ deferReExportIdentityConflictsToProjectGraph: input.includeProjectGraphDelta === true,
113
115
  id: `${projectId}_${safeId(file.sourcePath)}`,
114
116
  baseSourceText: base,
115
117
  workerSourceText: worker,
@@ -254,18 +256,21 @@ function sourceLedgersForFile(input, sourcePath) {
254
256
  return byPath ?? (input.sourceLedgers?.base || input.sourceLedgers?.worker || input.sourceLedgers?.head ? input.sourceLedgers : undefined);
255
257
  }
256
258
 
257
- function projectSummary(files, graphConflicts = []) {
259
+ function projectSummary(files, graphConflicts = [], hasProjectGraphDelta = false) {
258
260
  const byOperation = {};
259
261
  for (const file of files) byOperation[file.operation] = (byOperation[file.operation] ?? 0) + 1;
260
- const deltaConflicts = graphConflicts.filter((conflict) => conflict.gateId === 'project-graph-delta');
262
+ const limitConflicts = graphConflicts.filter((conflict) => conflict.gateId === 'project-graph-limit');
263
+ const deltaConflicts = graphConflicts.filter((conflict) => conflict.gateId === 'project-graph-delta' || (hasProjectGraphDelta && conflict.gateId === 'project-graph-limit'));
264
+ const outputConflicts = graphConflicts.filter((conflict) => conflict.gateId === 'project-symbol-graph' || (!hasProjectGraphDelta && conflict.gateId === 'project-graph-limit'));
261
265
  return {
262
266
  files: files.length,
263
267
  mergedFiles: files.filter((file) => file.status === 'merged').length,
264
268
  blockedFiles: files.filter((file) => file.status === 'blocked').length,
265
269
  outputFiles: files.filter((file) => typeof file.outputSourceText === 'string').length,
266
270
  projectGraphConflicts: graphConflicts.length,
267
- outputProjectGraphConflicts: graphConflicts.length - deltaConflicts.length,
271
+ outputProjectGraphConflicts: outputConflicts.length,
268
272
  projectGraphDeltaConflicts: deltaConflicts.length,
273
+ projectGraphLimitConflicts: limitConflicts.length,
269
274
  projectGraphPublicContractConflicts: deltaConflicts.filter((conflict) => conflict.code === 'project-public-contract-delta-conflict').length,
270
275
  projectGraphReExportIdentityConflicts: deltaConflicts.filter((conflict) => conflict.code === 'project-re-export-identity-delta-conflict').length,
271
276
  projectGraphImportTargetConflicts: deltaConflicts.filter((conflict) => conflict.code === 'project-import-target-delta-conflict').length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.124",
3
+ "version": "0.2.126",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",