@pulse-compute/wasm-compiler 0.0.0 → 1.0.0-beta.2
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 +53 -1
- package/bin/provider-proof-composition.js +34 -0
- package/bin/pulsewasm-extract.js +20 -0
- package/package.json +60 -5
- package/src/artifacts-dir.js +13 -0
- package/src/ast-json.js +52 -0
- package/src/build-manifest.js +354 -0
- package/src/canonical-api-compiler.js +269 -0
- package/src/canonical-native-compiler.js +411 -0
- package/src/canonical-native-plan.js +1478 -0
- package/src/canonical-project-compiler.js +1224 -0
- package/src/canonical-router-compiler.js +25 -0
- package/src/cli-intents.js +1235 -0
- package/src/cli.js +1927 -0
- package/src/codegen/assemblyscript-compile.js +3 -0
- package/src/codegen/assemblyscript-core.js +3 -0
- package/src/codegen/assemblyscript-shape.js +3 -0
- package/src/codegen/assemblyscript-wasm-smoke.js +3 -0
- package/src/codegen/backend-capabilities.js +3 -0
- package/src/codegen/channel-broadcaster.js +3 -0
- package/src/codegen/compiled-handlers.js +3 -0
- package/src/codegen/compiled-wasm-runtime.js +3 -0
- package/src/codegen/config-references.js +248 -0
- package/src/codegen/dispatch-ts.js +145 -0
- package/src/codegen/effect-composition.js +331 -0
- package/src/codegen/effect-runtime.js +418 -0
- package/src/codegen/execution-harness-ts.js +467 -0
- package/src/codegen/handler-bindings-ts.js +298 -0
- package/src/codegen/handler-library-contracts.js +3 -0
- package/src/codegen/host-capabilities.js +3 -0
- package/src/codegen/host-runtime-kernel.js +3 -0
- package/src/codegen/integrated-compiled-app.js +3 -0
- package/src/codegen/json-body.js +3 -0
- package/src/codegen/library-sidecars.js +3 -0
- package/src/codegen/local-harness-ts.js +453 -0
- package/src/codegen/pulse-wrapper.js +217 -0
- package/src/codegen/request-result-headers.js +3 -0
- package/src/codegen/schema-json-compile.js +3 -0
- package/src/codegen/schema-json-sidecar-v2.js +3 -0
- package/src/codegen/schema-json-sidecar.js +3 -0
- package/src/codegen/streaming-passthrough.js +3 -0
- package/src/codegen/wasm-host-abi.js +3 -0
- package/src/codegen/wasm-host-bridge.js +3 -0
- package/src/compiled-wasm-host-runtime-kv.js +3 -0
- package/src/config-resolver.js +813 -0
- package/src/crypto-requirement-planner.js +89 -0
- package/src/definitions/config-schema.js +14 -0
- package/src/definitions/handler-roles.js +14 -0
- package/src/definitions/path-grammar.js +14 -0
- package/src/definitions/router-api.js +14 -0
- package/src/diagnostics/codes.js +14 -0
- package/src/diagnostics/reporter.js +14 -0
- package/src/diagnostics.js +14 -0
- package/src/dispatch-table.js +400 -0
- package/src/events/event-emit.js +265 -0
- package/src/events/event-topology.js +127 -0
- package/src/execution-plan.js +463 -0
- package/src/extractor.js +2816 -0
- package/src/handler-eval.js +1154 -0
- package/src/handler-table.js +326 -0
- package/src/index.js +19 -0
- package/src/javascript-application-plan.js +181 -0
- package/src/kv-provider.js +3 -0
- package/src/path-table.js +60 -0
- package/src/path.js +14 -0
- package/src/patterns/config-define.js +30 -0
- package/src/patterns/dependency-call.js +18 -0
- package/src/patterns/env-lookup.js +13 -0
- package/src/patterns/handler-reference.js +29 -0
- package/src/patterns/path-literal.js +35 -0
- package/src/patterns/result.js +15 -0
- package/src/patterns/router-chain-call.js +50 -0
- package/src/patterns/router-construction.js +19 -0
- package/src/project/package-reachability.js +782 -0
- package/src/project/reachable-graph-builder.js +992 -0
- package/src/project/reachable-graph-contract.js +54 -0
- package/src/project/reachable-graph-implementation.js +36 -0
- package/src/project/router-module-linker.js +710 -0
- package/src/project-config-compiler.js +222 -0
- package/src/project-target-support.js +500 -0
- package/src/provider-toolchain.js +299 -0
- package/src/spine/async-surface-normalizer.js +328 -0
- package/src/spine/canonical-handler-ir.js +336 -0
- package/src/spine/canonical-native-module.js +87 -0
- package/src/spine/canonical-native-plan.js +89 -0
- package/src/spine/canonical-project.js +76 -0
- package/src/spine/canonical-router.js +155 -0
- package/src/spine/canonical-source.js +165 -0
- package/src/spine/diagnostic-authority.js +290 -0
- package/src/spine/equivalence.js +262 -0
- package/src/spine/guest-unit-stage.js +87 -0
- package/src/spine/handler-ir-emitter.js +455 -0
- package/src/spine/handler-ir-managed.js +1598 -0
- package/src/spine/handler-ir.js +797 -0
- package/src/spine/handler-surface-authority.js +588 -0
- package/src/spine/package-operation-seam.js +1015 -0
- package/src/spine/pipeline.js +202 -0
- package/src/spine/plain-handler-frontend.js +545 -0
- package/src/spine/provider-requirement-authority.js +208 -0
- package/src/spine/router-control-contract.js +17 -0
- package/src/spine/router-handler-frontend.js +514 -0
- package/src/spine/router-handler-ir.js +372 -0
- package/src/spine/router-topology-frontend.js +638 -0
- package/src/stable-id.js +14 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
|
|
5
|
+
function loadGraphContract() {
|
|
6
|
+
try {
|
|
7
|
+
return require('@pulse-compute/wasm-contracts/project/reachable-graph');
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
10
|
+
return require('../../../contracts/src/project/reachable-graph.js');
|
|
11
|
+
}
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const graph = loadGraphContract();
|
|
17
|
+
|
|
18
|
+
function loadProductProjectionContract() {
|
|
19
|
+
try {
|
|
20
|
+
return require('@pulse-compute/wasm-contracts/project/package-product-projection');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
23
|
+
return require('../../../contracts/src/project/package-product-projection.js');
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const productProjection = loadProductProjectionContract();
|
|
30
|
+
|
|
31
|
+
const COMPILER_PACKAGE_REACHABILITY_VERSION = 'pulse.compiler-package-reachability.v2';
|
|
32
|
+
const KNOWN_PROVIDER_PACKAGE_PATTERN = /^@pulse-compute\/(?:provider-|wasm-provider-)/;
|
|
33
|
+
const KNOWN_LIFECYCLE_SYMBOLS = Object.freeze(['bindAndServe', 'listen', 'serve', 'start']);
|
|
34
|
+
|
|
35
|
+
class PackageReachabilityError extends Error {
|
|
36
|
+
constructor(message, diagnostics = [], detail = {}) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'PackageReachabilityError';
|
|
39
|
+
this.code = 'PULSE_PACKAGE_REACHABILITY_FAILED';
|
|
40
|
+
this.diagnostics = Object.freeze([...diagnostics]);
|
|
41
|
+
this.detail = Object.freeze({ ...detail });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function sourceLocation(context, module, node) {
|
|
46
|
+
const offset = node && typeof node.getStart === 'function' ? node.getStart(module.sourceFile) : 0;
|
|
47
|
+
const point = module.sourceFile.getLineAndCharacterOfPosition(offset);
|
|
48
|
+
return Object.freeze({ file: module.path, line: point.line + 1, column: point.character + 1 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function graphDiagnostic(code, message, source, detail = {}) {
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
code,
|
|
54
|
+
kind: 'PackageReachabilityDiagnostic',
|
|
55
|
+
severity: 'error',
|
|
56
|
+
message,
|
|
57
|
+
file: source.file,
|
|
58
|
+
position: Object.freeze({ line: source.line, column: source.column }),
|
|
59
|
+
detail: Object.freeze({ ...detail })
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function mapByModulePath(manifest) {
|
|
64
|
+
return new Map(manifest.modules.filter((entry) => entry.kind === 'project').map((entry) => [entry.path, entry]));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function packageRecordForPrivateModule(manifest, module) {
|
|
68
|
+
return manifest.modules.find((entry) => entry.kind === 'package'
|
|
69
|
+
&& entry.packageName === module.packageName
|
|
70
|
+
&& entry.packageVersion === module.packageVersion
|
|
71
|
+
&& entry.packageSubpath === module.packageSubpath
|
|
72
|
+
&& entry.packageManifestHash === module.packageManifestHash);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function packageRelation(context, module, relation) {
|
|
76
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
77
|
+
if (!resolution) return undefined;
|
|
78
|
+
const target = context.packageModules.get(resolution.targetKey);
|
|
79
|
+
if (!target) return undefined;
|
|
80
|
+
return Object.freeze({ relation, resolution, target });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function originKey(origin) {
|
|
84
|
+
return [origin.packageKey, origin.importedName, origin.contractId || '', origin.via].join('\u0000');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function compatibleOrigin(left, right) {
|
|
88
|
+
return left.packageKey === right.packageKey && left.importedName === right.importedName && left.contractId === right.contractId && left.productRole === right.productRole;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setBinding(map, name, origin, diagnostics, source, field) {
|
|
92
|
+
if (!name) return false;
|
|
93
|
+
const current = map.get(name);
|
|
94
|
+
if (!current) {
|
|
95
|
+
map.set(name, Object.freeze({ ...origin }));
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (compatibleOrigin(current, origin)) {
|
|
99
|
+
if (current.via === origin.via && current.direct === origin.direct) return false;
|
|
100
|
+
const preferred = current.direct && !origin.direct ? current : (!current.direct && origin.direct ? origin : current);
|
|
101
|
+
if (preferred !== current) map.set(name, Object.freeze({ ...preferred }));
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
diagnostics.push(graphDiagnostic(
|
|
105
|
+
'PULSE_PACKAGE_BINDING_OWNERSHIP_AMBIGUOUS',
|
|
106
|
+
`Package binding ${field} ${name} resolves to more than one package-owned symbol.`,
|
|
107
|
+
source,
|
|
108
|
+
{
|
|
109
|
+
name,
|
|
110
|
+
existing: Object.freeze({ packageName: current.packageName, importedName: current.importedName, contractId: current.contractId, role: current.productRole }),
|
|
111
|
+
candidate: Object.freeze({ packageName: origin.packageName, importedName: origin.importedName, contractId: origin.contractId, role: origin.productRole })
|
|
112
|
+
}
|
|
113
|
+
));
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function cloneOrigin(origin, changes) {
|
|
118
|
+
return Object.freeze({ ...origin, ...changes });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function packageOrigin(target, importedName, source, via, direct) {
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
packageKey: target.key,
|
|
124
|
+
packageName: target.packageName,
|
|
125
|
+
packageSubpath: target.packageSubpath,
|
|
126
|
+
contractId: target.packageContract || null,
|
|
127
|
+
lowerableSubpath: target.contract && target.contract.lowerableSubpath || null,
|
|
128
|
+
productRole: target.productRole || (target.contract ? 'authoring' : null),
|
|
129
|
+
importedName,
|
|
130
|
+
source,
|
|
131
|
+
via,
|
|
132
|
+
direct
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function moduleOwnershipState(context) {
|
|
137
|
+
const states = new Map();
|
|
138
|
+
for (const module of context.projectModules.values()) {
|
|
139
|
+
states.set(module.path, { module, locals: new Map(), exports: new Map(), references: new Map() });
|
|
140
|
+
}
|
|
141
|
+
return states;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function isDeclarationName(node) {
|
|
145
|
+
const parent = node.parent;
|
|
146
|
+
if (!parent) return false;
|
|
147
|
+
if ((ts.isVariableDeclaration(parent) || ts.isParameter(parent) || ts.isFunctionDeclaration(parent)
|
|
148
|
+
|| ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)
|
|
149
|
+
|| ts.isMethodDeclaration(parent) || ts.isPropertyDeclaration(parent) || ts.isPropertySignature(parent)
|
|
150
|
+
|| ts.isTypeParameterDeclaration(parent) || ts.isEnumDeclaration(parent) || ts.isEnumMember(parent))
|
|
151
|
+
&& parent.name === node) return true;
|
|
152
|
+
if (ts.isPropertyAssignment(parent) && parent.name === node && !ts.isShorthandPropertyAssignment(parent)) return true;
|
|
153
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === node) return true;
|
|
154
|
+
if (ts.isQualifiedName(parent) && parent.right === node) return true;
|
|
155
|
+
if (ts.isLabeledStatement(parent) && parent.label === node) return true;
|
|
156
|
+
if ((ts.isBreakStatement(parent) || ts.isContinueStatement(parent)) && parent.label === node) return true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function inTypePosition(node) {
|
|
161
|
+
let current = node.parent;
|
|
162
|
+
while (current) {
|
|
163
|
+
if (ts.isTypeNode(current) || ts.isInterfaceDeclaration(current) || ts.isTypeAliasDeclaration(current)
|
|
164
|
+
|| ts.isImportTypeNode(current) || ts.isTypeParameterDeclaration(current)) return true;
|
|
165
|
+
if (ts.isExpression(current) || ts.isStatement(current) || ts.isSourceFile(current)) return false;
|
|
166
|
+
current = current.parent;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function collectValueReferences(context, state) {
|
|
172
|
+
function visit(node) {
|
|
173
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) return;
|
|
174
|
+
if (ts.isIdentifier(node) && !isDeclarationName(node) && !inTypePosition(node)) {
|
|
175
|
+
if (!state.references.has(node.text)) state.references.set(node.text, sourceLocation(context, state.module, node));
|
|
176
|
+
}
|
|
177
|
+
ts.forEachChild(node, visit);
|
|
178
|
+
}
|
|
179
|
+
visit(state.module.sourceFile);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function buildPackageBindingOwnership(context) {
|
|
183
|
+
const states = moduleOwnershipState(context);
|
|
184
|
+
const diagnostics = [];
|
|
185
|
+
|
|
186
|
+
for (const state of states.values()) {
|
|
187
|
+
collectValueReferences(context, state);
|
|
188
|
+
for (const relation of state.module.imports) {
|
|
189
|
+
if (relation.kind !== 'runtime-import') continue;
|
|
190
|
+
const resolved = packageRelation(context, state.module, relation);
|
|
191
|
+
if (!resolved) continue;
|
|
192
|
+
for (const binding of relation.bindings) {
|
|
193
|
+
setBinding(state.locals, binding.localName,
|
|
194
|
+
packageOrigin(resolved.target, binding.importedName, relation.source, 'direct-import', true),
|
|
195
|
+
diagnostics, relation.source, 'local');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
for (const relation of state.module.reExports) {
|
|
199
|
+
if (relation.typeOnly === true) continue;
|
|
200
|
+
const resolved = packageRelation(context, state.module, relation);
|
|
201
|
+
if (!resolved) continue;
|
|
202
|
+
if (relation.star) {
|
|
203
|
+
for (const symbol of resolved.target.contract && resolved.target.contract.facadeSymbols || []) {
|
|
204
|
+
setBinding(state.exports, symbol,
|
|
205
|
+
packageOrigin(resolved.target, symbol, relation.source, 'direct-re-export', true),
|
|
206
|
+
diagnostics, relation.source, 'export');
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
for (const mapping of relation.mappings) {
|
|
210
|
+
setBinding(state.exports, mapping.exportName,
|
|
211
|
+
packageOrigin(resolved.target, mapping.localName, relation.source, 'direct-re-export', true),
|
|
212
|
+
diagnostics, relation.source, 'export');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let changed = true;
|
|
219
|
+
let iterations = 0;
|
|
220
|
+
while (changed) {
|
|
221
|
+
changed = false;
|
|
222
|
+
iterations += 1;
|
|
223
|
+
if (iterations > Math.max(4, states.size * 4)) throw new Error('Package ownership propagation did not converge.');
|
|
224
|
+
for (const state of states.values()) {
|
|
225
|
+
for (const [exportName, localName] of state.module.localExports) {
|
|
226
|
+
const origin = state.locals.get(localName);
|
|
227
|
+
if (!origin) continue;
|
|
228
|
+
const via = origin.direct ? 'direct-re-export' : 'project-re-export';
|
|
229
|
+
changed = setBinding(state.exports, exportName, cloneOrigin(origin, { via, direct: origin.direct }), diagnostics, origin.source, 'export') || changed;
|
|
230
|
+
}
|
|
231
|
+
for (const relation of state.module.imports) {
|
|
232
|
+
if (relation.kind !== 'runtime-import') continue;
|
|
233
|
+
const resolution = state.module.resolutions.find((entry) => entry.relation === relation);
|
|
234
|
+
const targetState = resolution && states.get(resolution.targetKey);
|
|
235
|
+
if (!targetState) continue;
|
|
236
|
+
for (const binding of relation.bindings) {
|
|
237
|
+
if (binding.importedName === '*') continue;
|
|
238
|
+
const origin = targetState.exports.get(binding.importedName);
|
|
239
|
+
if (!origin) continue;
|
|
240
|
+
changed = setBinding(state.locals, binding.localName,
|
|
241
|
+
cloneOrigin(origin, { source: relation.source, via: 'project-import', direct: false }),
|
|
242
|
+
diagnostics, relation.source, 'local') || changed;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (const relation of state.module.reExports) {
|
|
246
|
+
if (relation.typeOnly === true) continue;
|
|
247
|
+
const resolution = state.module.resolutions.find((entry) => entry.relation === relation);
|
|
248
|
+
const targetState = resolution && states.get(resolution.targetKey);
|
|
249
|
+
if (!targetState) continue;
|
|
250
|
+
if (relation.star) {
|
|
251
|
+
for (const [exportName, origin] of targetState.exports) {
|
|
252
|
+
changed = setBinding(state.exports, exportName,
|
|
253
|
+
cloneOrigin(origin, { source: relation.source, via: 'project-re-export', direct: false }),
|
|
254
|
+
diagnostics, relation.source, 'export') || changed;
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
for (const mapping of relation.mappings) {
|
|
258
|
+
const origin = targetState.exports.get(mapping.localName);
|
|
259
|
+
if (!origin) continue;
|
|
260
|
+
changed = setBinding(state.exports, mapping.exportName,
|
|
261
|
+
cloneOrigin(origin, { source: relation.source, via: 'project-re-export', direct: false }),
|
|
262
|
+
diagnostics, relation.source, 'export') || changed;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (diagnostics.length > 0) throw new PackageReachabilityError(
|
|
270
|
+
`Package binding ownership failed with ${diagnostics.length} diagnostic(s).`,
|
|
271
|
+
diagnostics,
|
|
272
|
+
{ modules: states.size }
|
|
273
|
+
);
|
|
274
|
+
return states;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function packageLocalForCall(state, call) {
|
|
278
|
+
if (ts.isIdentifier(call.expression)) {
|
|
279
|
+
const origin = state.locals.get(call.expression.text);
|
|
280
|
+
if (!origin) return undefined;
|
|
281
|
+
return Object.freeze({ origin, symbol: origin.importedName, localName: call.expression.text });
|
|
282
|
+
}
|
|
283
|
+
if (ts.isPropertyAccessExpression(call.expression) && ts.isIdentifier(call.expression.expression)) {
|
|
284
|
+
const origin = state.locals.get(call.expression.expression.text);
|
|
285
|
+
if (!origin || origin.importedName !== '*') return undefined;
|
|
286
|
+
return Object.freeze({ origin, symbol: call.expression.name.text, localName: call.expression.expression.text });
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function addKnownLifecycleEdges(context, states) {
|
|
292
|
+
const findings = [];
|
|
293
|
+
const seen = new Set();
|
|
294
|
+
for (const state of states.values()) {
|
|
295
|
+
if (!state.module.runtime) continue;
|
|
296
|
+
let functionDepth = 0;
|
|
297
|
+
function visit(node) {
|
|
298
|
+
const entersFunction = ts.isFunctionLike(node) || ts.isClassLike(node);
|
|
299
|
+
if (entersFunction) functionDepth += 1;
|
|
300
|
+
if (functionDepth === 0 && ts.isCallExpression(node)) {
|
|
301
|
+
const resolved = packageLocalForCall(state, node);
|
|
302
|
+
if (resolved && KNOWN_PROVIDER_PACKAGE_PATTERN.test(resolved.origin.packageName)
|
|
303
|
+
&& KNOWN_LIFECYCLE_SYMBOLS.includes(resolved.symbol)) {
|
|
304
|
+
const source = sourceLocation(context, state.module, node);
|
|
305
|
+
const key = [state.module.path, resolved.origin.packageKey, resolved.symbol, source.file, source.line, source.column].join('\u0000');
|
|
306
|
+
if (!seen.has(key)) {
|
|
307
|
+
seen.add(key);
|
|
308
|
+
const target = context.packageModules.get(resolved.origin.packageKey);
|
|
309
|
+
const classification = target && target.classification;
|
|
310
|
+
const edge = {
|
|
311
|
+
kind: 'lifecycle',
|
|
312
|
+
from: state.module.path,
|
|
313
|
+
to: resolved.origin.packageKey,
|
|
314
|
+
specifier: target && target.specifier || resolved.origin.packageName,
|
|
315
|
+
resolutionKind: classification && classification.kind || 'package-root',
|
|
316
|
+
runtime: true,
|
|
317
|
+
importedNames: [resolved.symbol],
|
|
318
|
+
exportedNames: [],
|
|
319
|
+
packageContract: target && target.packageContract || null,
|
|
320
|
+
source
|
|
321
|
+
};
|
|
322
|
+
context.edges.push(edge);
|
|
323
|
+
findings.push(Object.freeze({ ...edge, symbol: resolved.symbol, packageName: resolved.origin.packageName }));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
ts.forEachChild(node, visit);
|
|
328
|
+
if (entersFunction) functionDepth -= 1;
|
|
329
|
+
}
|
|
330
|
+
visit(state.module.sourceFile);
|
|
331
|
+
}
|
|
332
|
+
return Object.freeze(findings);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function packageRelationsForModule(context, targetKey) {
|
|
336
|
+
const records = [];
|
|
337
|
+
for (const module of context.projectModules.values()) {
|
|
338
|
+
for (const relation of [...module.imports, ...module.reExports]) {
|
|
339
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
340
|
+
if (!resolution || resolution.targetKey !== targetKey) continue;
|
|
341
|
+
records.push(Object.freeze({ module, relation }));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return records;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
function unwrapExpression(node) {
|
|
349
|
+
let current = node;
|
|
350
|
+
while (current && (ts.isParenthesizedExpression(current) || ts.isAsExpression(current)
|
|
351
|
+
|| ts.isNonNullExpression(current) || ts.isTypeAssertionExpression(current)
|
|
352
|
+
|| (typeof ts.isSatisfiesExpression === 'function' && ts.isSatisfiesExpression(current)))) current = current.expression;
|
|
353
|
+
return current;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function isProfileTokenCall(node) {
|
|
357
|
+
const current = unwrapExpression(node);
|
|
358
|
+
return Boolean(current && ts.isCallExpression(current)
|
|
359
|
+
&& current.arguments.length === 0
|
|
360
|
+
&& ts.isPropertyAccessExpression(current.expression)
|
|
361
|
+
&& current.expression.name.text === 'profile');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function collectPackageCompositions(context, states) {
|
|
365
|
+
const records = [];
|
|
366
|
+
const diagnostics = [];
|
|
367
|
+
for (const state of states.values()) {
|
|
368
|
+
function visit(node) {
|
|
369
|
+
if (ts.isCallExpression(node)) {
|
|
370
|
+
const expression = unwrapExpression(node.expression);
|
|
371
|
+
if (expression && ts.isIdentifier(expression)) {
|
|
372
|
+
const origin = state.locals.get(expression.text);
|
|
373
|
+
if (origin && origin.productRole === 'helper' && origin.contractId) {
|
|
374
|
+
const packageModule = context.packageModules.get(origin.packageKey);
|
|
375
|
+
const contract = packageModule && packageModule.productContract;
|
|
376
|
+
const source = sourceLocation(context, state.module, node);
|
|
377
|
+
const valid = node.arguments.length === 1 && isProfileTokenCall(node.arguments[0]);
|
|
378
|
+
records.push(Object.freeze({
|
|
379
|
+
modulePath: state.module.path,
|
|
380
|
+
localName: expression.text,
|
|
381
|
+
contractId: origin.contractId,
|
|
382
|
+
packageName: origin.packageName,
|
|
383
|
+
helperSymbol: origin.importedName,
|
|
384
|
+
source,
|
|
385
|
+
valid,
|
|
386
|
+
compositionStatus: contract && contract.composition.status || 'not-realized',
|
|
387
|
+
reasonCode: contract && contract.composition.reasonCode || 'PULSE_PACKAGE_COMPOSITION_HELPER_NOT_REALIZED'
|
|
388
|
+
}));
|
|
389
|
+
if (!valid) diagnostics.push(graphDiagnostic(
|
|
390
|
+
'PULSE_PACKAGE_COMPOSITION_HELPER_USE_INVALID',
|
|
391
|
+
`Package composition helper ${expression.text} must receive exactly one direct app.profile() token.`,
|
|
392
|
+
source,
|
|
393
|
+
{ packageName: origin.packageName, contractId: origin.contractId, argumentCount: node.arguments.length }
|
|
394
|
+
));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
ts.forEachChild(node, visit);
|
|
399
|
+
}
|
|
400
|
+
visit(state.module.sourceFile);
|
|
401
|
+
}
|
|
402
|
+
return Object.freeze({ records: Object.freeze(records), diagnostics: Object.freeze(diagnostics) });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function derivePackageProductProjection(context, manifest, states, compositions) {
|
|
406
|
+
const projectByPath = mapByModulePath(manifest);
|
|
407
|
+
const contracts = context.packageContractCatalog.productCatalog.contracts.map((entry) => ({
|
|
408
|
+
contractId: entry.contractId,
|
|
409
|
+
packageName: entry.npmPackage,
|
|
410
|
+
packageVersion: entry.packageVersion,
|
|
411
|
+
metadataHash: entry.metadataHash,
|
|
412
|
+
profileFragment: entry.ownership.profileFragment,
|
|
413
|
+
helperImports: entry.ownership.helperImports,
|
|
414
|
+
reExportImports: entry.ownership.reExportImports,
|
|
415
|
+
authoringImport: entry.authoring.import,
|
|
416
|
+
authoringSymbols: entry.authoring.symbols,
|
|
417
|
+
compositionStatus: entry.composition.status,
|
|
418
|
+
compositionHelper: entry.composition.helperSymbol,
|
|
419
|
+
compositionReasonCode: entry.composition.reasonCode,
|
|
420
|
+
nativeTarget: entry.targets.native,
|
|
421
|
+
javascriptTarget: entry.targets.javascript,
|
|
422
|
+
conformanceStatus: entry.conformance.status,
|
|
423
|
+
conformanceFixtureRoots: entry.conformance.fixtureRoots,
|
|
424
|
+
conformanceSemanticCases: entry.conformance.semanticCases
|
|
425
|
+
}));
|
|
426
|
+
const bindings = [];
|
|
427
|
+
for (const state of states.values()) {
|
|
428
|
+
const projectModule = projectByPath.get(state.module.path);
|
|
429
|
+
if (!projectModule) continue;
|
|
430
|
+
for (const [localName, origin] of state.locals) {
|
|
431
|
+
if (!origin.contractId || !origin.productRole) continue;
|
|
432
|
+
const packageModule = context.packageModules.get(origin.packageKey);
|
|
433
|
+
const packageRecord = packageModule && packageRecordForPrivateModule(manifest, packageModule);
|
|
434
|
+
if (!packageRecord) continue;
|
|
435
|
+
bindings.push({
|
|
436
|
+
moduleId: projectModule.id,
|
|
437
|
+
localName,
|
|
438
|
+
exportName: null,
|
|
439
|
+
importedName: origin.importedName,
|
|
440
|
+
packageModuleId: packageRecord.id,
|
|
441
|
+
packageName: origin.packageName,
|
|
442
|
+
contractId: origin.contractId,
|
|
443
|
+
role: origin.productRole,
|
|
444
|
+
source: origin.source,
|
|
445
|
+
via: origin.via,
|
|
446
|
+
direct: origin.direct,
|
|
447
|
+
used: state.references.has(localName)
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
for (const [exportName, origin] of state.exports) {
|
|
451
|
+
if (!origin.contractId || !origin.productRole) continue;
|
|
452
|
+
const packageModule = context.packageModules.get(origin.packageKey);
|
|
453
|
+
const packageRecord = packageModule && packageRecordForPrivateModule(manifest, packageModule);
|
|
454
|
+
if (!packageRecord) continue;
|
|
455
|
+
bindings.push({
|
|
456
|
+
moduleId: projectModule.id,
|
|
457
|
+
localName: null,
|
|
458
|
+
exportName,
|
|
459
|
+
importedName: origin.importedName,
|
|
460
|
+
packageModuleId: packageRecord.id,
|
|
461
|
+
packageName: origin.packageName,
|
|
462
|
+
contractId: origin.contractId,
|
|
463
|
+
role: origin.productRole,
|
|
464
|
+
source: origin.source,
|
|
465
|
+
via: origin.via,
|
|
466
|
+
direct: origin.direct,
|
|
467
|
+
used: false
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const selectedContracts = [...new Set(bindings.filter((entry) => entry.used).map((entry) => entry.contractId))];
|
|
472
|
+
const composedContracts = [...new Set(compositions.records.filter((entry) => entry.valid).map((entry) => entry.contractId))];
|
|
473
|
+
const activeContracts = new Set([...selectedContracts, ...composedContracts]);
|
|
474
|
+
const fragmentKeys = new Set(Object.keys(context.projectFragments || {}));
|
|
475
|
+
const fragments = contracts.map((entry) => ({
|
|
476
|
+
key: entry.profileFragment,
|
|
477
|
+
contractId: entry.contractId,
|
|
478
|
+
packageName: entry.packageName,
|
|
479
|
+
present: fragmentKeys.has(entry.profileFragment),
|
|
480
|
+
selected: activeContracts.has(entry.contractId)
|
|
481
|
+
}));
|
|
482
|
+
for (const key of [...fragmentKeys].sort()) {
|
|
483
|
+
if (!fragments.some((entry) => entry.key === key)) fragments.push({ key, contractId: null, packageName: null, present: true, selected: false });
|
|
484
|
+
}
|
|
485
|
+
return productProjection.normalizePackageProductProjection({
|
|
486
|
+
graphHash: manifest.graphHash,
|
|
487
|
+
contracts,
|
|
488
|
+
bindings,
|
|
489
|
+
fragments,
|
|
490
|
+
selectedContracts,
|
|
491
|
+
composedContracts
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function derivePackageReachabilityProjection(context, manifest, states) {
|
|
496
|
+
const projectByPath = mapByModulePath(manifest);
|
|
497
|
+
const packages = [];
|
|
498
|
+
const bindings = [];
|
|
499
|
+
const selectedContracts = new Set();
|
|
500
|
+
for (const module of context.packageModules.values()) {
|
|
501
|
+
const record = packageRecordForPrivateModule(manifest, module);
|
|
502
|
+
if (!record) continue;
|
|
503
|
+
const relations = packageRelationsForModule(context, module.key);
|
|
504
|
+
const importSources = relations.filter((entry) => entry.relation.kind === 'runtime-import').map((entry) => entry.relation.source);
|
|
505
|
+
const reExportSources = relations.filter((entry) => entry.relation.kind === 're-export' && entry.relation.typeOnly !== true).map((entry) => entry.relation.source);
|
|
506
|
+
let nativeStatus = 'unsupported';
|
|
507
|
+
if (module.coreAuthoring) nativeStatus = 'core-authoring';
|
|
508
|
+
else if (module.packageContract && module.contract && module.contract.targetSupport.native === true) nativeStatus = 'trusted-lowerable';
|
|
509
|
+
if (module.runtime && nativeStatus === 'trusted-lowerable') selectedContracts.add(module.packageContract);
|
|
510
|
+
packages.push({
|
|
511
|
+
moduleId: record.id,
|
|
512
|
+
packageName: module.packageName,
|
|
513
|
+
packageVersion: module.packageVersion,
|
|
514
|
+
packageSubpath: module.packageSubpath,
|
|
515
|
+
owner: module.owner,
|
|
516
|
+
runtime: module.runtime === true,
|
|
517
|
+
contractId: module.packageContract || null,
|
|
518
|
+
lowerableSubpath: module.contract && module.contract.lowerableSubpath || null,
|
|
519
|
+
facadeSymbols: module.contract && module.contract.facadeSymbols || [],
|
|
520
|
+
importSources,
|
|
521
|
+
reExportSources,
|
|
522
|
+
nativeStatus
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
for (const state of states.values()) {
|
|
526
|
+
const projectModule = projectByPath.get(state.module.path);
|
|
527
|
+
if (!projectModule) continue;
|
|
528
|
+
for (const [localName, origin] of state.locals) {
|
|
529
|
+
const packageModule = context.packageModules.get(origin.packageKey);
|
|
530
|
+
const packageRecord = packageModule && packageRecordForPrivateModule(manifest, packageModule);
|
|
531
|
+
if (!packageRecord) continue;
|
|
532
|
+
bindings.push({
|
|
533
|
+
moduleId: projectModule.id,
|
|
534
|
+
localName,
|
|
535
|
+
exportName: null,
|
|
536
|
+
importedName: origin.importedName,
|
|
537
|
+
packageModuleId: packageRecord.id,
|
|
538
|
+
packageName: origin.packageName,
|
|
539
|
+
contractId: origin.contractId,
|
|
540
|
+
source: origin.source,
|
|
541
|
+
via: origin.via,
|
|
542
|
+
direct: origin.direct,
|
|
543
|
+
used: state.references.has(localName)
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
for (const [exportName, origin] of state.exports) {
|
|
547
|
+
const packageModule = context.packageModules.get(origin.packageKey);
|
|
548
|
+
const packageRecord = packageModule && packageRecordForPrivateModule(manifest, packageModule);
|
|
549
|
+
if (!packageRecord) continue;
|
|
550
|
+
bindings.push({
|
|
551
|
+
moduleId: projectModule.id,
|
|
552
|
+
localName: null,
|
|
553
|
+
exportName,
|
|
554
|
+
importedName: origin.importedName,
|
|
555
|
+
packageModuleId: packageRecord.id,
|
|
556
|
+
packageName: origin.packageName,
|
|
557
|
+
contractId: origin.contractId,
|
|
558
|
+
source: origin.source,
|
|
559
|
+
via: origin.via,
|
|
560
|
+
direct: origin.direct,
|
|
561
|
+
used: false
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return graph.normalizePackageReachabilityProjection({
|
|
566
|
+
graphVersion: manifest.version,
|
|
567
|
+
graphHash: manifest.graphHash,
|
|
568
|
+
packages,
|
|
569
|
+
bindings,
|
|
570
|
+
selectedContracts: [...selectedContracts]
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function deriveApplicationEntrySafety(manifest) {
|
|
575
|
+
const modulesById = new Map(manifest.modules.map((entry) => [entry.id, entry]));
|
|
576
|
+
return graph.normalizeApplicationEntrySafety({
|
|
577
|
+
graphHash: manifest.graphHash,
|
|
578
|
+
lifecycleEdges: manifest.edges.filter((edge) => edge.kind === 'lifecycle').map((edge) => {
|
|
579
|
+
const from = modulesById.get(edge.from);
|
|
580
|
+
const target = modulesById.get(edge.to);
|
|
581
|
+
return {
|
|
582
|
+
edgeId: edge.id,
|
|
583
|
+
moduleId: edge.from,
|
|
584
|
+
packageModuleId: edge.to,
|
|
585
|
+
packageName: target.packageName,
|
|
586
|
+
symbol: edge.importedNames[0] || '<unknown>',
|
|
587
|
+
source: edge.source
|
|
588
|
+
};
|
|
589
|
+
})
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function relationSpecifier(context, moduleKey) {
|
|
594
|
+
const relations = packageRelationsForModule(context, moduleKey).filter((entry) => entry.relation.kind !== 'type-import');
|
|
595
|
+
relations.sort((left, right) => left.relation.source.file.localeCompare(right.relation.source.file)
|
|
596
|
+
|| left.relation.source.line - right.relation.source.line
|
|
597
|
+
|| left.relation.source.column - right.relation.source.column);
|
|
598
|
+
return relations[0] && relations[0].relation.specifier;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function deriveNativeEligibilityProjection(context, manifest, packageReachability, entrySafety, packageProduct, compositions) {
|
|
602
|
+
const projectByPath = mapByModulePath(manifest);
|
|
603
|
+
const packageById = new Map(packageReachability.packages.map((entry) => [entry.moduleId, entry]));
|
|
604
|
+
const blockers = [];
|
|
605
|
+
|
|
606
|
+
for (const packageEntry of packageReachability.packages) {
|
|
607
|
+
if (!packageEntry.runtime || packageEntry.nativeStatus !== 'unsupported') continue;
|
|
608
|
+
const privateModule = [...context.packageModules.values()].find((entry) => {
|
|
609
|
+
const record = packageRecordForPrivateModule(manifest, entry);
|
|
610
|
+
return record && record.id === packageEntry.moduleId;
|
|
611
|
+
});
|
|
612
|
+
if (!privateModule) continue;
|
|
613
|
+
if (privateModule.productContract && ['helper', 're-export'].includes(privateModule.productRole)) continue;
|
|
614
|
+
const relations = packageRelationsForModule(context, privateModule.key).filter((entry) => entry.relation.kind !== 'type-import');
|
|
615
|
+
for (const relation of relations) {
|
|
616
|
+
const projectModule = projectByPath.get(relation.module.path);
|
|
617
|
+
if (!projectModule) continue;
|
|
618
|
+
const knownSubpaths = context.packageContractsByPackage.get(privateModule.packageName) || [];
|
|
619
|
+
const unsupportedSubpath = knownSubpaths.length > 0 && !privateModule.contract;
|
|
620
|
+
blockers.push({
|
|
621
|
+
kind: unsupportedSubpath ? 'unsupported-package-subpath' : 'unsupported-package',
|
|
622
|
+
code: unsupportedSubpath ? 'PULSE_PACKAGE_SUBPATH_UNSUPPORTED' : 'PULSE_NATIVE_IMPORT_UNSUPPORTED',
|
|
623
|
+
moduleId: projectModule.id,
|
|
624
|
+
handlerIds: [],
|
|
625
|
+
packageName: privateModule.packageName,
|
|
626
|
+
packageSubpath: privateModule.packageSubpath,
|
|
627
|
+
contractId: null,
|
|
628
|
+
specifier: relation.relation.specifier,
|
|
629
|
+
source: relation.relation.source,
|
|
630
|
+
message: unsupportedSubpath
|
|
631
|
+
? `Package ${privateModule.packageName} participates in Pulse native lowering only through ${knownSubpaths.map((entry) => entry.lowerableSubpath).join(', ')}; ${relation.relation.specifier} is not a native-lowerable subpath.`
|
|
632
|
+
: `Runtime package import ${relation.relation.specifier} has no trusted Pulse native contract.`
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const modulesById = new Map(manifest.modules.map((entry) => [entry.id, entry]));
|
|
638
|
+
for (const edge of manifest.edges) {
|
|
639
|
+
if (!edge.runtime) continue;
|
|
640
|
+
const target = modulesById.get(edge.to);
|
|
641
|
+
const sourceModule = modulesById.get(edge.from);
|
|
642
|
+
if (!target || target.kind !== 'external' || !sourceModule || sourceModule.kind !== 'project') continue;
|
|
643
|
+
blockers.push({
|
|
644
|
+
kind: 'unsupported-package',
|
|
645
|
+
code: 'PULSE_NATIVE_IMPORT_UNSUPPORTED',
|
|
646
|
+
moduleId: sourceModule.id,
|
|
647
|
+
handlerIds: [],
|
|
648
|
+
packageName: null,
|
|
649
|
+
packageSubpath: null,
|
|
650
|
+
contractId: null,
|
|
651
|
+
specifier: edge.specifier,
|
|
652
|
+
source: edge.source,
|
|
653
|
+
message: `Runtime external import ${edge.specifier} has no Pulse native realization.`
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
for (const binding of packageReachability.bindings) {
|
|
658
|
+
if (!binding.used || !['project-import', 'project-re-export'].includes(binding.via) || !binding.contractId) continue;
|
|
659
|
+
blockers.push({
|
|
660
|
+
kind: 'package-re-export-lowering-deferred',
|
|
661
|
+
code: 'PULSE_PACKAGE_REEXPORT_LOWERING_DEFERRED',
|
|
662
|
+
moduleId: binding.moduleId,
|
|
663
|
+
handlerIds: [],
|
|
664
|
+
packageName: binding.packageName,
|
|
665
|
+
packageSubpath: packageById.get(binding.packageModuleId).packageSubpath,
|
|
666
|
+
contractId: binding.contractId,
|
|
667
|
+
specifier: binding.packageName,
|
|
668
|
+
source: binding.source,
|
|
669
|
+
message: `Package-owned symbol ${binding.importedName} reaches application code through a project re-export. Direct package ownership is recorded, but re-exported package lowering is deferred to the package-facade proof.`
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
const modulePathById = new Map(manifest.modules.map((entry) => [entry.id, entry.path]));
|
|
675
|
+
const validCompositionBindings = new Set(compositions.records
|
|
676
|
+
.filter((entry) => entry.valid)
|
|
677
|
+
.map((entry) => `${entry.modulePath}:${entry.localName}`));
|
|
678
|
+
for (const binding of packageProduct.bindings) {
|
|
679
|
+
if (!binding.used || !binding.localName || !['helper', 're-export'].includes(binding.role)) continue;
|
|
680
|
+
const modulePath = modulePathById.get(binding.moduleId);
|
|
681
|
+
if (modulePath && validCompositionBindings.has(`${modulePath}:${binding.localName}`)) continue;
|
|
682
|
+
blockers.push({
|
|
683
|
+
kind: 'unsupported-package-subpath',
|
|
684
|
+
code: 'PULSE_PACKAGE_SUBPATH_UNSUPPORTED',
|
|
685
|
+
moduleId: binding.moduleId,
|
|
686
|
+
handlerIds: [],
|
|
687
|
+
packageName: binding.packageName,
|
|
688
|
+
packageSubpath: '.',
|
|
689
|
+
contractId: binding.contractId,
|
|
690
|
+
specifier: binding.packageName,
|
|
691
|
+
source: binding.source,
|
|
692
|
+
message: `Package root ${binding.packageName} is reserved for direct composition-helper use with app.profile(); other runtime use remains unsupported.`
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
for (const composition of compositions.records) {
|
|
697
|
+
if (!composition.valid || composition.compositionStatus === 'supported') continue;
|
|
698
|
+
const projectModule = projectByPath.get(composition.modulePath);
|
|
699
|
+
if (!projectModule) continue;
|
|
700
|
+
blockers.push({
|
|
701
|
+
kind: 'package-composition-not-realized',
|
|
702
|
+
code: composition.reasonCode || 'PULSE_PACKAGE_COMPOSITION_HELPER_NOT_REALIZED',
|
|
703
|
+
moduleId: projectModule.id,
|
|
704
|
+
handlerIds: [],
|
|
705
|
+
packageName: composition.packageName,
|
|
706
|
+
packageSubpath: '.',
|
|
707
|
+
contractId: composition.contractId,
|
|
708
|
+
specifier: composition.packageName,
|
|
709
|
+
source: composition.source,
|
|
710
|
+
message: `Package composition helper ${composition.helperSymbol} is graph-owned by ${composition.contractId}, but its composition behavior is ${composition.compositionStatus}.`
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
for (const finding of entrySafety.lifecycleEdges) {
|
|
715
|
+
blockers.push({
|
|
716
|
+
kind: 'application-entry-lifecycle-side-effect',
|
|
717
|
+
code: 'PULSE_APPLICATION_ENTRY_LIFECYCLE_SIDE_EFFECT',
|
|
718
|
+
moduleId: finding.moduleId,
|
|
719
|
+
handlerIds: [],
|
|
720
|
+
packageName: finding.packageName,
|
|
721
|
+
packageSubpath: '.',
|
|
722
|
+
contractId: null,
|
|
723
|
+
specifier: finding.packageName,
|
|
724
|
+
source: finding.source,
|
|
725
|
+
message: `The configured application entry graph invokes known provider lifecycle operation ${finding.symbol} during module evaluation.`
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const handlersByModule = new Map();
|
|
730
|
+
for (const handler of manifest.handlers) {
|
|
731
|
+
if (!handlersByModule.has(handler.moduleId)) handlersByModule.set(handler.moduleId, []);
|
|
732
|
+
handlersByModule.get(handler.moduleId).push(handler.id);
|
|
733
|
+
}
|
|
734
|
+
const normalizedBlockers = blockers.map((blocker) => ({ ...blocker, handlerIds: handlersByModule.get(blocker.moduleId) || [] }));
|
|
735
|
+
const preview = graph.normalizeNativeEligibilityProjection({ graphHash: manifest.graphHash, blockers: normalizedBlockers, handlers: [] });
|
|
736
|
+
const blockersByModule = new Map();
|
|
737
|
+
for (const blocker of preview.blockers) {
|
|
738
|
+
if (!blockersByModule.has(blocker.moduleId)) blockersByModule.set(blocker.moduleId, []);
|
|
739
|
+
blockersByModule.get(blocker.moduleId).push(blocker.id);
|
|
740
|
+
}
|
|
741
|
+
return graph.normalizeNativeEligibilityProjection({
|
|
742
|
+
graphHash: manifest.graphHash,
|
|
743
|
+
blockers: normalizedBlockers,
|
|
744
|
+
handlers: manifest.handlers.map((handler) => ({
|
|
745
|
+
handlerId: handler.id,
|
|
746
|
+
moduleId: handler.moduleId,
|
|
747
|
+
eligible: !blockersByModule.has(handler.moduleId),
|
|
748
|
+
blockerIds: blockersByModule.get(handler.moduleId) || []
|
|
749
|
+
}))
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function deriveReachableGraphProjections(context, manifest, statesInput) {
|
|
754
|
+
const states = statesInput || buildPackageBindingOwnership(context);
|
|
755
|
+
const compositions = collectPackageCompositions(context, states);
|
|
756
|
+
if (compositions.diagnostics.length > 0) throw new PackageReachabilityError('Package composition validation failed.', compositions.diagnostics, { diagnostics: compositions.diagnostics });
|
|
757
|
+
const packageReachability = derivePackageReachabilityProjection(context, manifest, states);
|
|
758
|
+
const packageProduct = derivePackageProductProjection(context, manifest, states, compositions);
|
|
759
|
+
const entrySafety = deriveApplicationEntrySafety(manifest);
|
|
760
|
+
const nativeEligibility = deriveNativeEligibilityProjection(context, manifest, packageReachability, entrySafety, packageProduct, compositions);
|
|
761
|
+
return Object.freeze({
|
|
762
|
+
version: COMPILER_PACKAGE_REACHABILITY_VERSION,
|
|
763
|
+
packageReachability,
|
|
764
|
+
packageProduct,
|
|
765
|
+
entrySafety,
|
|
766
|
+
nativeEligibility
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
module.exports = Object.freeze({
|
|
771
|
+
COMPILER_PACKAGE_REACHABILITY_VERSION,
|
|
772
|
+
KNOWN_PROVIDER_PACKAGE_PATTERN,
|
|
773
|
+
KNOWN_LIFECYCLE_SYMBOLS,
|
|
774
|
+
PackageReachabilityError,
|
|
775
|
+
buildPackageBindingOwnership,
|
|
776
|
+
addKnownLifecycleEdges,
|
|
777
|
+
derivePackageReachabilityProjection,
|
|
778
|
+
derivePackageProductProjection,
|
|
779
|
+
deriveApplicationEntrySafety,
|
|
780
|
+
deriveNativeEligibilityProjection,
|
|
781
|
+
deriveReachableGraphProjections
|
|
782
|
+
});
|