@pulse-compute/wasm-compiler 0.0.0 → 1.0.0-beta.1
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 +52 -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 +245 -0
- package/src/canonical-native-compiler.js +411 -0
- package/src/canonical-native-plan.js +1477 -0
- package/src/canonical-project-compiler.js +1218 -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 +312 -0
- package/src/spine/canonical-handler-ir.js +335 -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 +454 -0
- package/src/spine/handler-ir-managed.js +1597 -0
- package/src/spine/handler-ir.js +797 -0
- package/src/spine/handler-surface-authority.js +585 -0
- package/src/spine/package-operation-seam.js +1015 -0
- package/src/spine/pipeline.js +202 -0
- package/src/spine/plain-handler-frontend.js +537 -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 +513 -0
- package/src/spine/router-handler-ir.js +372 -0
- package/src/spine/router-topology-frontend.js +635 -0
- package/src/stable-id.js +14 -0
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const ts = require('typescript');
|
|
6
|
+
const { buildSchemaJsonCompile } = require('@pulse-compute/wasm-schema-json/compiler/schema-json-compile');
|
|
7
|
+
const { buildCanonicalSchemaBundle } = require('@pulse-compute/wasm-schema-json/compiler/canonical-schema-codecs');
|
|
8
|
+
const {
|
|
9
|
+
recognizeProjectPackageOperations,
|
|
10
|
+
combinePackageOperationRecognitions,
|
|
11
|
+
packageExtensionsForRecognition,
|
|
12
|
+
attachPackageOperationRecognition,
|
|
13
|
+
packageOperationRecognitionForCompiled
|
|
14
|
+
} = require('./spine/package-operation-seam.js');
|
|
15
|
+
const { compileCanonicalFile, compileCanonicalSource, stableSourceName } = require('./canonical-api-compiler.js');
|
|
16
|
+
const { detectCanonicalRouterSource, compileCanonicalRouterSource } = require('./canonical-router-compiler.js');
|
|
17
|
+
const { handlerIrsForCanonicalRouterOutput } = require('./spine/canonical-router.js');
|
|
18
|
+
const {
|
|
19
|
+
ManagedHandlerCompileError,
|
|
20
|
+
compileManagedHandlerDescriptors
|
|
21
|
+
} = require('./spine/handler-ir-managed.js');
|
|
22
|
+
const { executeCanonicalProjectSpine } = require('./spine/canonical-project.js');
|
|
23
|
+
const { planProjectCrypto } = require('./crypto-requirement-planner.js');
|
|
24
|
+
const {
|
|
25
|
+
PROJECT_GRAPH_BUILDER_VERSION,
|
|
26
|
+
ReachableProjectGraphError,
|
|
27
|
+
buildReachableProjectGraph,
|
|
28
|
+
finalizeReachableProjectGraph,
|
|
29
|
+
reachableProjectGraphProjections,
|
|
30
|
+
projectGraphContext
|
|
31
|
+
} = require('./project/reachable-graph-builder.js');
|
|
32
|
+
const {
|
|
33
|
+
ROUTER_MODULE_LINKER_VERSION,
|
|
34
|
+
RouterModuleLinkError,
|
|
35
|
+
linkProjectPlainHandler,
|
|
36
|
+
linkProjectRouterModules,
|
|
37
|
+
resolveProjectRoot
|
|
38
|
+
} = require('./project/router-module-linker.js');
|
|
39
|
+
const {
|
|
40
|
+
APPLICATION_IR_ENVELOPE_VERSION
|
|
41
|
+
} = require('@pulse-compute/wasm-contracts/project/package-ownership');
|
|
42
|
+
const {
|
|
43
|
+
CANONICAL_PACKAGE_INSPECTION_VERSION
|
|
44
|
+
} = require('@pulse-compute/wasm-contracts/package/package-contract');
|
|
45
|
+
|
|
46
|
+
const CANONICAL_PROJECT_COMPILER_VERSION = 'pulse.canonical-project-compiler.v4';
|
|
47
|
+
|
|
48
|
+
function deepFreeze(value) {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
for (const entry of value) deepFreeze(entry);
|
|
51
|
+
return Object.freeze(value);
|
|
52
|
+
}
|
|
53
|
+
if (value && typeof value === 'object') {
|
|
54
|
+
for (const entry of Object.values(value)) deepFreeze(entry);
|
|
55
|
+
return Object.freeze(value);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function managedHandlerInspection(bundle) {
|
|
61
|
+
if (!bundle) return null;
|
|
62
|
+
return deepFreeze({
|
|
63
|
+
version: bundle.version,
|
|
64
|
+
descriptorVersion: bundle.descriptorVersion,
|
|
65
|
+
role: bundle.role,
|
|
66
|
+
bundleHash: bundle.bundleHash,
|
|
67
|
+
summary: bundle.summary,
|
|
68
|
+
handlers: bundle.handlers.map((handler) => ({
|
|
69
|
+
id: handler.id,
|
|
70
|
+
source: {
|
|
71
|
+
file: handler.source.file,
|
|
72
|
+
exportName: handler.source.exportName
|
|
73
|
+
},
|
|
74
|
+
schemas: {
|
|
75
|
+
input: handler.parameters.input.schemaId,
|
|
76
|
+
output: handler.result.schemaId
|
|
77
|
+
},
|
|
78
|
+
effects: {
|
|
79
|
+
count: handler.effects.count,
|
|
80
|
+
sites: handler.effects.sites.map((site) => ({
|
|
81
|
+
kind: site.kind,
|
|
82
|
+
capability: site.capability,
|
|
83
|
+
...(site.providerKind ? { providerKind: site.providerKind } : {}),
|
|
84
|
+
...(site.operation ? { operation: site.operation } : {})
|
|
85
|
+
})),
|
|
86
|
+
packageOperations: handler.effects.packageOperations.map((effect) => ({
|
|
87
|
+
contractId: effect.contractId,
|
|
88
|
+
package: effect.package,
|
|
89
|
+
kind: effect.kind,
|
|
90
|
+
capability: effect.capability,
|
|
91
|
+
...(effect.providerKind ? { providerKind: effect.providerKind } : {}),
|
|
92
|
+
...(effect.operation ? { operation: effect.operation } : {})
|
|
93
|
+
})),
|
|
94
|
+
continuations: handler.effects.continuations.length,
|
|
95
|
+
logging: handler.effects.logging.length
|
|
96
|
+
},
|
|
97
|
+
providerRequirements: {
|
|
98
|
+
capabilities: handler.providerRequirements.capabilities,
|
|
99
|
+
providerKinds: handler.providerRequirements.providerKinds,
|
|
100
|
+
operations: handler.providerRequirements.operations.map((operation) => ({
|
|
101
|
+
kind: operation.kind,
|
|
102
|
+
capability: operation.capability,
|
|
103
|
+
...(operation.providerKind ? { providerKind: operation.providerKind } : {}),
|
|
104
|
+
...(operation.operation ? { operation: operation.operation } : {})
|
|
105
|
+
})),
|
|
106
|
+
packages: handler.providerRequirements.packages.map((entry) => ({
|
|
107
|
+
contractId: entry.contractId,
|
|
108
|
+
package: entry.package
|
|
109
|
+
})),
|
|
110
|
+
hostCapabilities: handler.providerRequirements.hostCapabilities
|
|
111
|
+
},
|
|
112
|
+
eligibility: handler.eligibility,
|
|
113
|
+
handlerHash: handler.handlerHash
|
|
114
|
+
})),
|
|
115
|
+
policy: {
|
|
116
|
+
declaredHandlersOnly: true,
|
|
117
|
+
runtimeValuesExcluded: true,
|
|
118
|
+
rawPayloadsExcluded: true,
|
|
119
|
+
rawRequestIdsExcluded: true,
|
|
120
|
+
resolvedSecretsExcluded: true,
|
|
121
|
+
providerObjectsExcluded: true
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function packageInspectionProjection(recognition, managedHandlers) {
|
|
127
|
+
const artifacts = recognition.inspectionArtifacts || [];
|
|
128
|
+
const managed = managedHandlerInspection(managedHandlers);
|
|
129
|
+
return deepFreeze({
|
|
130
|
+
version: CANONICAL_PACKAGE_INSPECTION_VERSION,
|
|
131
|
+
artifacts,
|
|
132
|
+
managedHandlers: managed,
|
|
133
|
+
summary: {
|
|
134
|
+
packages: recognition.packages.length,
|
|
135
|
+
artifacts: artifacts.length,
|
|
136
|
+
declaredHandlers: managed ? managed.handlers.length : 0,
|
|
137
|
+
handlerEffects: managed ? managed.handlers.reduce((sum, handler) => sum + handler.effects.count, 0) : 0
|
|
138
|
+
},
|
|
139
|
+
policy: {
|
|
140
|
+
packageOwnedStaticArtifacts: true,
|
|
141
|
+
canonicalManagedHandlerInspection: true,
|
|
142
|
+
buildArtifactFilesDeclaredByPackage: true,
|
|
143
|
+
providerObjectsExcluded: true,
|
|
144
|
+
runtimeValuesExcluded: true,
|
|
145
|
+
automaticFallback: false
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function managedHandlerRuntimeImportKeys(descriptors) {
|
|
151
|
+
return (descriptors || [])
|
|
152
|
+
.filter((descriptor) => descriptor.origin && descriptor.source && descriptor.origin.file !== descriptor.source.file)
|
|
153
|
+
.map((descriptor) => `${descriptor.origin.file}\u0000${descriptor.source.localName}`)
|
|
154
|
+
.sort();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function terminalPackageIntrinsicApplication(root, recognition) {
|
|
158
|
+
if (!root || root.kind !== 'handler') return null;
|
|
159
|
+
const candidates = (recognition.intrinsics || []).filter((intrinsic) => (
|
|
160
|
+
intrinsic
|
|
161
|
+
&& intrinsic.valueKind === 'response'
|
|
162
|
+
&& typeof intrinsic.compilerName === 'string'
|
|
163
|
+
&& intrinsic.compilerName.length > 0
|
|
164
|
+
&& Array.isArray(intrinsic.argumentIndexes)
|
|
165
|
+
&& intrinsic.argumentIndexes.length === 1
|
|
166
|
+
&& intrinsic.argumentIndexes[0] === 0
|
|
167
|
+
&& intrinsic.loc
|
|
168
|
+
&& String(intrinsic.loc.file || '').replace(/\\/g, '/') === String(root.module.path || '').replace(/\\/g, '/')
|
|
169
|
+
));
|
|
170
|
+
if (candidates.length !== 1) return null;
|
|
171
|
+
const intrinsic = candidates[0];
|
|
172
|
+
const staticArguments = (intrinsic.staticArguments || []).map((value) => JSON.stringify(value));
|
|
173
|
+
const argumentsSource = [...staticArguments, 'ctx'].join(', ');
|
|
174
|
+
return deepFreeze({
|
|
175
|
+
version: 'pulse.terminal-package-intrinsic-application.v1',
|
|
176
|
+
contractId: intrinsic.contractId,
|
|
177
|
+
package: intrinsic.package,
|
|
178
|
+
intrinsic: intrinsic.intrinsic,
|
|
179
|
+
compilerName: intrinsic.compilerName,
|
|
180
|
+
sourceText: `export default async function handler(ctx) {\n return ${intrinsic.compilerName}(${argumentsSource});\n}\n`,
|
|
181
|
+
policy: {
|
|
182
|
+
inspectionAndLoweringOnly: true,
|
|
183
|
+
providerRealizationRequired: true,
|
|
184
|
+
automaticFallback: false
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function terminalPackageApplicationInspection(application, recognition) {
|
|
190
|
+
if (!application) return null;
|
|
191
|
+
const plan = recognition.publicExtensions && recognition.publicExtensions.plans
|
|
192
|
+
&& recognition.publicExtensions.plans.find((entry) => entry.contractId === application.contractId);
|
|
193
|
+
const inspectionArtifact = (recognition.inspectionArtifacts || [])
|
|
194
|
+
.find((entry) => entry.contractId === application.contractId && entry.kind === 'declaration-and-target-evidence');
|
|
195
|
+
return deepFreeze({
|
|
196
|
+
version: application.version,
|
|
197
|
+
contractId: application.contractId,
|
|
198
|
+
package: application.package,
|
|
199
|
+
intrinsic: application.intrinsic,
|
|
200
|
+
compilerName: application.compilerName,
|
|
201
|
+
planHash: inspectionArtifact && inspectionArtifact.data && inspectionArtifact.data.planHash || null,
|
|
202
|
+
declarationStatus: plan && plan.status || 'ok',
|
|
203
|
+
realization: 'provider-dependent',
|
|
204
|
+
policy: application.policy
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const APPLICATION_TOOLING_FRAGMENT_KEYS = new Set(['dev', 'outDir', 'schemas']);
|
|
209
|
+
|
|
210
|
+
function applicationPackageFragments(input) {
|
|
211
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return {};
|
|
212
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => !APPLICATION_TOOLING_FRAGMENT_KEYS.has(key)));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function createApplicationEnvelope(router, projectMetadata) {
|
|
216
|
+
if (!router || !projectMetadata) return undefined;
|
|
217
|
+
const selected = projectMetadata.selectedProfile;
|
|
218
|
+
const selectedProfile = selected && typeof selected === 'object'
|
|
219
|
+
? { name: selected.name, source: selected.source }
|
|
220
|
+
: (selected ? { name: String(selected), source: projectMetadata.profileSource || 'unknown' } : null);
|
|
221
|
+
const project = deepFreeze({
|
|
222
|
+
constructionMode: router.applicationConstructionMode || 'router',
|
|
223
|
+
projectHash: projectMetadata.projectHash || null,
|
|
224
|
+
configPlanHash: projectMetadata.configPlanHash || projectMetadata.planHash || null,
|
|
225
|
+
selectedProfile,
|
|
226
|
+
strict: projectMetadata.strict !== false,
|
|
227
|
+
symbolicBindings: projectMetadata.bindings || { config: [], secret: [] },
|
|
228
|
+
packageFragments: applicationPackageFragments(projectMetadata.fragments),
|
|
229
|
+
target: projectMetadata.target || (selected && selected.target) || null,
|
|
230
|
+
host: projectMetadata.host || (selected && selected.host) || null
|
|
231
|
+
});
|
|
232
|
+
return deepFreeze({
|
|
233
|
+
version: APPLICATION_IR_ENVELOPE_VERSION,
|
|
234
|
+
kind: 'pulse.application-ir',
|
|
235
|
+
rootKind: router.applicationKind || 'router',
|
|
236
|
+
router: router.metadata,
|
|
237
|
+
project
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class CanonicalProjectCompileError extends Error {
|
|
242
|
+
constructor(message, diagnostics = [], detail = {}, code = 'PULSE_PROJECT_COMPILE_FAILED') {
|
|
243
|
+
super(message);
|
|
244
|
+
this.name = 'CanonicalProjectCompileError';
|
|
245
|
+
this.code = code;
|
|
246
|
+
this.diagnostics = Object.freeze([...diagnostics]);
|
|
247
|
+
this.detail = Object.freeze({ ...detail });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function projectionDiagnostic(blocker) {
|
|
252
|
+
return Object.freeze({
|
|
253
|
+
code: blocker.code,
|
|
254
|
+
kind: 'CanonicalProjectCompileDiagnostic',
|
|
255
|
+
severity: 'error',
|
|
256
|
+
message: blocker.message,
|
|
257
|
+
file: blocker.source.file,
|
|
258
|
+
position: Object.freeze({ line: blocker.source.line, column: blocker.source.column }),
|
|
259
|
+
detail: Object.freeze({
|
|
260
|
+
blockerId: blocker.id,
|
|
261
|
+
blockerKind: blocker.kind,
|
|
262
|
+
moduleId: blocker.moduleId,
|
|
263
|
+
handlerIds: blocker.handlerIds,
|
|
264
|
+
packageName: blocker.packageName,
|
|
265
|
+
packageSubpath: blocker.packageSubpath,
|
|
266
|
+
contractId: blocker.contractId,
|
|
267
|
+
specifier: blocker.specifier,
|
|
268
|
+
automaticFallback: false
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function cryptoPlanningDiagnostic(error, entryFile) {
|
|
274
|
+
return Object.freeze({
|
|
275
|
+
code: error && error.code || 'PULSE_CRYPTO_REALIZATION_UNAVAILABLE',
|
|
276
|
+
kind: 'CanonicalProjectCompileDiagnostic',
|
|
277
|
+
severity: 'error',
|
|
278
|
+
message: error && error.message || 'Crypto realization planning failed.',
|
|
279
|
+
file: entryFile,
|
|
280
|
+
position: Object.freeze({ line: 1, column: 1 }),
|
|
281
|
+
detail: Object.freeze({
|
|
282
|
+
...(error && error.details || {}),
|
|
283
|
+
automaticFallback: false
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function planCanonicalProjectCrypto(options, entryFile, recognizedRequirements = []) {
|
|
289
|
+
const metadata = options.applicationProjectMetadata || {};
|
|
290
|
+
const selected = metadata.selectedProfile;
|
|
291
|
+
const profile = selected && typeof selected === 'object'
|
|
292
|
+
? selected.name
|
|
293
|
+
: (selected ? String(selected) : null);
|
|
294
|
+
const packageRequirements = [
|
|
295
|
+
...(options.cryptoRequirements || []),
|
|
296
|
+
...(recognizedRequirements || [])
|
|
297
|
+
];
|
|
298
|
+
const hasReachableCryptoRequirement = packageRequirements.some(
|
|
299
|
+
(entry) => entry && entry.reachable === true && Array.isArray(entry.algorithms) && entry.algorithms.length > 0
|
|
300
|
+
);
|
|
301
|
+
if (!metadata.crypto && !hasReachableCryptoRequirement) return undefined;
|
|
302
|
+
try {
|
|
303
|
+
return planProjectCrypto({
|
|
304
|
+
declaration: metadata.crypto && metadata.crypto.declaration,
|
|
305
|
+
packageRequirements,
|
|
306
|
+
targetDescriptor: options.packageTargetDescriptor,
|
|
307
|
+
target: options.packageTarget || metadata.target || null,
|
|
308
|
+
profile,
|
|
309
|
+
profileSelectionSource: selected && typeof selected === 'object'
|
|
310
|
+
? selected.source
|
|
311
|
+
: metadata.profileSource,
|
|
312
|
+
configurationSource: metadata.crypto && metadata.crypto.source
|
|
313
|
+
});
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (!error || !String(error.code || '').startsWith('PULSE_CRYPTO_')) throw error;
|
|
316
|
+
const diagnostic = cryptoPlanningDiagnostic(error, entryFile);
|
|
317
|
+
throw new CanonicalProjectCompileError(
|
|
318
|
+
diagnostic.message,
|
|
319
|
+
[diagnostic],
|
|
320
|
+
diagnostic.detail,
|
|
321
|
+
diagnostic.code
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function assertGraphPreflight(graphBuild, entryFile, options = {}) {
|
|
327
|
+
const nativeEligibilityMode = options.nativeEligibilityMode || 'enforce';
|
|
328
|
+
if (!['enforce', 'record'].includes(nativeEligibilityMode)) {
|
|
329
|
+
throw new TypeError('nativeEligibilityMode must be enforce or record.');
|
|
330
|
+
}
|
|
331
|
+
if (nativeEligibilityMode === 'record') return;
|
|
332
|
+
if (graphBuild.entrySafety && graphBuild.entrySafety.importSafe === false) {
|
|
333
|
+
const blocker = graphBuild.nativeEligibility.blockers.find((entry) => entry.kind === 'application-entry-lifecycle-side-effect');
|
|
334
|
+
throw new CanonicalProjectCompileError(
|
|
335
|
+
'The configured Pulse application entry starts a known provider lifecycle during module evaluation.',
|
|
336
|
+
blocker ? [projectionDiagnostic(blocker)] : [],
|
|
337
|
+
{ entryFile, entrySafety: graphBuild.entrySafety },
|
|
338
|
+
'PULSE_APPLICATION_ENTRY_LIFECYCLE_SIDE_EFFECT'
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const blockers = graphBuild.nativeEligibility
|
|
342
|
+
? graphBuild.nativeEligibility.blockers.filter((entry) => entry.kind !== 'application-entry-lifecycle-side-effect')
|
|
343
|
+
: [];
|
|
344
|
+
if (blockers.length > 0) {
|
|
345
|
+
throw new CanonicalProjectCompileError(
|
|
346
|
+
`The reachable project graph has ${blockers.length} native eligibility blocker(s).`,
|
|
347
|
+
blockers.map(projectionDiagnostic),
|
|
348
|
+
{
|
|
349
|
+
entryFile,
|
|
350
|
+
nativeEligibility: graphBuild.nativeEligibility,
|
|
351
|
+
packageReachability: graphBuild.packageReachability,
|
|
352
|
+
automaticFallback: false
|
|
353
|
+
},
|
|
354
|
+
blockers[0].code
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function prepareProjectPackageOperations(entryFile, options = {}) {
|
|
360
|
+
const recognition = recognizeProjectPackageOperations(entryFile, {
|
|
361
|
+
...options,
|
|
362
|
+
generatedBy: CANONICAL_PROJECT_COMPILER_VERSION
|
|
363
|
+
});
|
|
364
|
+
if (recognition.errors.length > 0) {
|
|
365
|
+
throw new CanonicalProjectCompileError(
|
|
366
|
+
`Package-owned canonical lowering failed with ${recognition.errors.length} diagnostic(s).`,
|
|
367
|
+
recognition.errors,
|
|
368
|
+
{ entryFile: path.resolve(entryFile), contracts: recognition.selectedContracts },
|
|
369
|
+
'PULSE_PACKAGE_LOWERING_FAILED'
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
return recognition;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function prepareReachableProjectPackageOperations(graphBuild, options = {}) {
|
|
376
|
+
const context = projectGraphContext(graphBuild);
|
|
377
|
+
if (!context) throw new TypeError('prepareReachableProjectPackageOperations requires a reachable project graph build.');
|
|
378
|
+
const recognitions = [];
|
|
379
|
+
for (const module of [...context.projectModules.values()].filter((entry) => entry.runtime).sort((left, right) => left.path.localeCompare(right.path))) {
|
|
380
|
+
const selectedContracts = new Set();
|
|
381
|
+
for (const relation of [...module.imports, ...module.reExports]) {
|
|
382
|
+
if (relation.kind === 'type-import' || relation.typeOnly === true) continue;
|
|
383
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
384
|
+
const target = resolution && context.packageModules.get(resolution.targetKey);
|
|
385
|
+
if (target && target.packageContract) selectedContracts.add(target.packageContract);
|
|
386
|
+
}
|
|
387
|
+
if (selectedContracts.size === 0) continue;
|
|
388
|
+
const preserveSingleFilePublicEnvelope = graphBuild.summary.runtimeProjectModules === 1 && module.path === graphBuild.entryKey;
|
|
389
|
+
recognitions.push(prepareProjectPackageOperations(module.absolutePath, {
|
|
390
|
+
...options,
|
|
391
|
+
sourceText: module.sourceText,
|
|
392
|
+
sourceName: module.path,
|
|
393
|
+
// Keep the sealed single-file envelope while making its public source
|
|
394
|
+
// identity checkout-independent. Absolute paths remain private graph
|
|
395
|
+
// facts and must not feed project or native-plan hashes.
|
|
396
|
+
...(preserveSingleFilePublicEnvelope ? { publicSourceName: module.path } : {}),
|
|
397
|
+
selectedContracts: [...selectedContracts].sort()
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
const recognition = combinePackageOperationRecognitions(recognitions);
|
|
401
|
+
const expected = graphBuild.packageReachability.selectedContracts;
|
|
402
|
+
const missing = expected.filter((contractId) => !recognition.selectedContracts.includes(contractId));
|
|
403
|
+
if (missing.length > 0) throw new CanonicalProjectCompileError(
|
|
404
|
+
`Reachable package contract(s) were not recognized in their owning project modules: ${missing.join(', ')}.`,
|
|
405
|
+
[],
|
|
406
|
+
{ missing, selectedContracts: expected },
|
|
407
|
+
'PULSE_PACKAGE_CONTRACT_NOT_RECOGNIZED'
|
|
408
|
+
);
|
|
409
|
+
return recognition;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function packageEffectLookup(recognition) {
|
|
413
|
+
const byFileAndStart = new Map();
|
|
414
|
+
for (const operation of recognition.operations || []) {
|
|
415
|
+
const effect = operation.canonicalEffect;
|
|
416
|
+
const file = String(effect && effect.loc && effect.loc.file || '').replace(/\\/g, '/');
|
|
417
|
+
const start = Number(effect && effect.range && effect.range.start);
|
|
418
|
+
if (file && Number.isSafeInteger(start)) byFileAndStart.set(`${file}\u0000${start}`, effect);
|
|
419
|
+
}
|
|
420
|
+
return (call) => {
|
|
421
|
+
const sourceFile = call && typeof call.getSourceFile === 'function' ? call.getSourceFile() : undefined;
|
|
422
|
+
const file = String(sourceFile && sourceFile.fileName || '').replace(/\\/g, '/');
|
|
423
|
+
const start = call && sourceFile && typeof call.getStart === 'function' ? call.getStart(sourceFile) : NaN;
|
|
424
|
+
return byFileAndStart.get(`${file}\u0000${start}`);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function packageIntrinsicLookup(recognition) {
|
|
429
|
+
const byFileAndStart = new Map();
|
|
430
|
+
for (const intrinsic of recognition.intrinsics || []) {
|
|
431
|
+
const file = String(intrinsic && intrinsic.loc && intrinsic.loc.file || '').replace(/\\/g, '/');
|
|
432
|
+
const start = Number(intrinsic && intrinsic.range && intrinsic.range.start);
|
|
433
|
+
if (file && Number.isSafeInteger(start)) byFileAndStart.set(`${file}\u0000${start}`, intrinsic);
|
|
434
|
+
}
|
|
435
|
+
return (call) => {
|
|
436
|
+
const sourceFile = call && typeof call.getSourceFile === 'function' ? call.getSourceFile() : undefined;
|
|
437
|
+
const file = String(sourceFile && sourceFile.fileName || '').replace(/\\/g, '/');
|
|
438
|
+
const start = call && sourceFile && typeof call.getStart === 'function' ? call.getStart(sourceFile) : NaN;
|
|
439
|
+
return byFileAndStart.get(`${file}\u0000${start}`);
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
function packageResultAdapterLookup(recognition) {
|
|
445
|
+
const byFileAndStart = new Map();
|
|
446
|
+
for (const adapter of recognition.resultAdapters || []) {
|
|
447
|
+
const file = String(adapter && adapter.loc && adapter.loc.file || '').replace(/\\/g, '/');
|
|
448
|
+
const start = Number(adapter && adapter.range && adapter.range.start);
|
|
449
|
+
if (file && Number.isSafeInteger(start)) byFileAndStart.set(`${file}\u0000${start}`, adapter);
|
|
450
|
+
}
|
|
451
|
+
return (call) => {
|
|
452
|
+
const sourceFile = call && typeof call.getSourceFile === 'function' ? call.getSourceFile() : undefined;
|
|
453
|
+
const file = String(sourceFile && sourceFile.fileName || '').replace(/\\/g, '/');
|
|
454
|
+
const start = call && sourceFile && typeof call.getStart === 'function' ? call.getStart(sourceFile) : NaN;
|
|
455
|
+
return byFileAndStart.get(`${file}\u0000${start}`);
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function normalizedModulePath(value) {
|
|
460
|
+
return String(value || '').replace(/\\/g, '/');
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function modulePathCandidates(value, rootDir) {
|
|
464
|
+
const normalized = normalizedModulePath(value);
|
|
465
|
+
if (!normalized) return new Set();
|
|
466
|
+
const candidates = new Set([normalized]);
|
|
467
|
+
if (path.isAbsolute(normalized)) {
|
|
468
|
+
candidates.add(normalizedModulePath(path.normalize(normalized)));
|
|
469
|
+
return candidates;
|
|
470
|
+
}
|
|
471
|
+
candidates.add(normalizedModulePath(path.resolve(rootDir || process.cwd(), normalized)));
|
|
472
|
+
candidates.add(normalizedModulePath(path.resolve(process.cwd(), normalized)));
|
|
473
|
+
return candidates;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function sameModulePath(left, right, rootDir) {
|
|
477
|
+
const leftCandidates = modulePathCandidates(left, rootDir);
|
|
478
|
+
const rightCandidates = modulePathCandidates(right, rootDir);
|
|
479
|
+
return [...leftCandidates].some((candidate) => rightCandidates.has(candidate));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function callPrintKey(printer, sourceFile, call) {
|
|
483
|
+
return printer.printNode(ts.EmitHint.Expression, call, sourceFile).trim();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function callExpressions(sourceFile) {
|
|
487
|
+
const calls = [];
|
|
488
|
+
function visit(node) {
|
|
489
|
+
if (ts.isCallExpression(node)) calls.push(node);
|
|
490
|
+
ts.forEachChild(node, visit);
|
|
491
|
+
}
|
|
492
|
+
visit(sourceFile);
|
|
493
|
+
return calls;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function sourceDiagnosticForPackageEffect(effect, message, detail = {}) {
|
|
497
|
+
const start = effect && effect.loc && effect.loc.start;
|
|
498
|
+
return Object.freeze({
|
|
499
|
+
code: 'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED',
|
|
500
|
+
kind: 'CanonicalProjectCompileDiagnostic',
|
|
501
|
+
severity: 'error',
|
|
502
|
+
message,
|
|
503
|
+
file: normalizedModulePath(effect && effect.loc && effect.loc.file || '<unknown>'),
|
|
504
|
+
position: Object.freeze({
|
|
505
|
+
line: Number(start && start.line || 1),
|
|
506
|
+
column: Number(start && start.column || 1)
|
|
507
|
+
}),
|
|
508
|
+
detail: Object.freeze({ ...detail })
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function routerGeneratedPackageEffectLookup(router, recognition, options = {}) {
|
|
513
|
+
const effects = [...(recognition && recognition.operations || [])].map((operation) => operation.canonicalEffect);
|
|
514
|
+
if (!router || effects.length === 0) return undefined;
|
|
515
|
+
const records = handlerIrsForCanonicalRouterOutput(router) || [];
|
|
516
|
+
const generatedSourceFile = ts.createSourceFile(
|
|
517
|
+
String(router.fileName || 'app.ts'),
|
|
518
|
+
String(router.sourceText || ''),
|
|
519
|
+
ts.ScriptTarget.ES2022,
|
|
520
|
+
true,
|
|
521
|
+
ts.ScriptKind.TS
|
|
522
|
+
);
|
|
523
|
+
const generatedCalls = callExpressions(generatedSourceFile);
|
|
524
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
525
|
+
const generatedByStart = new Map();
|
|
526
|
+
const usedGeneratedStarts = new Set();
|
|
527
|
+
|
|
528
|
+
for (const record of records) {
|
|
529
|
+
const operationIr = record && record.operationIr;
|
|
530
|
+
const generatedRange = record && record.canonicalIr && record.canonicalIr.router
|
|
531
|
+
&& record.canonicalIr.router.entry && record.canonicalIr.router.entry.generatedRange;
|
|
532
|
+
if (!operationIr || !generatedRange || !operationIr.sourceFile || !operationIr.handler) continue;
|
|
533
|
+
const originalFile = normalizedModulePath(operationIr.sourceFile.fileName || operationIr.file);
|
|
534
|
+
const handlerStart = Number(operationIr.handler.getStart(operationIr.sourceFile));
|
|
535
|
+
const handlerEnd = Number(operationIr.handler.getEnd());
|
|
536
|
+
const expected = effects.filter((effect) => {
|
|
537
|
+
const effectFile = normalizedModulePath(effect && effect.loc && effect.loc.file);
|
|
538
|
+
const start = Number(effect && effect.range && effect.range.start);
|
|
539
|
+
const end = Number(effect && effect.range && effect.range.end);
|
|
540
|
+
return sameModulePath(effectFile, originalFile, options.rootDir)
|
|
541
|
+
&& Number.isSafeInteger(start)
|
|
542
|
+
&& Number.isSafeInteger(end)
|
|
543
|
+
&& start >= handlerStart
|
|
544
|
+
&& end <= handlerEnd;
|
|
545
|
+
}).sort((left, right) => Number(left.range.start) - Number(right.range.start));
|
|
546
|
+
if (expected.length === 0) continue;
|
|
547
|
+
|
|
548
|
+
const originalCalls = callExpressions(operationIr.sourceFile);
|
|
549
|
+
const originalByStart = new Map(originalCalls.map((call) => [call.getStart(operationIr.sourceFile), call]));
|
|
550
|
+
const candidates = generatedCalls.filter((call) => {
|
|
551
|
+
const start = call.getStart(generatedSourceFile);
|
|
552
|
+
return start >= Number(generatedRange.start) && start < Number(generatedRange.end);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
for (const effect of expected) {
|
|
556
|
+
const originalCall = originalByStart.get(Number(effect.range.start));
|
|
557
|
+
if (!originalCall) {
|
|
558
|
+
throw new CanonicalProjectCompileError(
|
|
559
|
+
`Unable to recover the source call for reachable package effect ${effect.kind}.`,
|
|
560
|
+
[sourceDiagnosticForPackageEffect(effect, `Unable to recover the source call for reachable package effect ${effect.kind}.`, {
|
|
561
|
+
contractId: effect.contractId,
|
|
562
|
+
range: effect.range
|
|
563
|
+
})],
|
|
564
|
+
{ effect },
|
|
565
|
+
'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED'
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
const key = callPrintKey(printer, operationIr.sourceFile, originalCall);
|
|
569
|
+
const candidate = candidates.find((call) => {
|
|
570
|
+
const start = call.getStart(generatedSourceFile);
|
|
571
|
+
return !usedGeneratedStarts.has(start) && callPrintKey(printer, generatedSourceFile, call) === key;
|
|
572
|
+
});
|
|
573
|
+
if (!candidate) {
|
|
574
|
+
throw new CanonicalProjectCompileError(
|
|
575
|
+
`Unable to link generated Router source to package effect ${effect.kind} from ${originalFile}.`,
|
|
576
|
+
[sourceDiagnosticForPackageEffect(effect, `Unable to link generated Router source to package effect ${effect.kind}.`, {
|
|
577
|
+
contractId: effect.contractId,
|
|
578
|
+
call: key,
|
|
579
|
+
routerEntry: record.entryStableId
|
|
580
|
+
})],
|
|
581
|
+
{ effect, generatedRange, call: key },
|
|
582
|
+
'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED'
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
const generatedStart = candidate.getStart(generatedSourceFile);
|
|
586
|
+
usedGeneratedStarts.add(generatedStart);
|
|
587
|
+
generatedByStart.set(generatedStart, effect);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (generatedByStart.size !== effects.length) {
|
|
592
|
+
const linked = new Set([...generatedByStart.values()]);
|
|
593
|
+
const missing = effects.filter((effect) => !linked.has(effect));
|
|
594
|
+
const first = missing[0];
|
|
595
|
+
throw new CanonicalProjectCompileError(
|
|
596
|
+
`Generated Router source linked ${generatedByStart.size} of ${effects.length} reachable package effect(s).`,
|
|
597
|
+
missing.map((effect) => sourceDiagnosticForPackageEffect(effect, `Reachable package effect ${effect.kind} was not linked into generated Router source.`, {
|
|
598
|
+
contractId: effect.contractId
|
|
599
|
+
})),
|
|
600
|
+
{ missing },
|
|
601
|
+
'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED'
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return (call) => {
|
|
606
|
+
if (!call) return undefined;
|
|
607
|
+
const sourceFile = call.getSourceFile && call.getSourceFile();
|
|
608
|
+
return sourceFile ? generatedByStart.get(call.getStart(sourceFile)) : undefined;
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function routerGeneratedPackageResultAdapterLookup(router, recognition, options = {}) {
|
|
613
|
+
const adapters = [...(recognition && recognition.resultAdapters || [])];
|
|
614
|
+
if (!router || adapters.length === 0) return undefined;
|
|
615
|
+
const records = handlerIrsForCanonicalRouterOutput(router) || [];
|
|
616
|
+
const generatedSourceFile = ts.createSourceFile(String(router.fileName || 'app.ts'), String(router.sourceText || ''), ts.ScriptTarget.ES2022, true, ts.ScriptKind.TS);
|
|
617
|
+
const generatedCalls = callExpressions(generatedSourceFile);
|
|
618
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
619
|
+
const generatedByStart = new Map();
|
|
620
|
+
const usedGeneratedStarts = new Set();
|
|
621
|
+
for (const record of records) {
|
|
622
|
+
const operationIr = record && record.operationIr;
|
|
623
|
+
const generatedRange = record && record.canonicalIr && record.canonicalIr.router && record.canonicalIr.router.entry && record.canonicalIr.router.entry.generatedRange;
|
|
624
|
+
if (!operationIr || !generatedRange || !operationIr.sourceFile || !operationIr.handler) continue;
|
|
625
|
+
const originalFile = normalizedModulePath(operationIr.sourceFile.fileName || operationIr.file);
|
|
626
|
+
const handlerStart = Number(operationIr.handler.getStart(operationIr.sourceFile));
|
|
627
|
+
const handlerEnd = Number(operationIr.handler.getEnd());
|
|
628
|
+
const expected = adapters.filter((adapter) => sameModulePath(
|
|
629
|
+
normalizedModulePath(adapter && adapter.loc && adapter.loc.file),
|
|
630
|
+
originalFile,
|
|
631
|
+
options.rootDir
|
|
632
|
+
)
|
|
633
|
+
&& Number(adapter && adapter.range && adapter.range.start) >= handlerStart
|
|
634
|
+
&& Number(adapter && adapter.range && adapter.range.end) <= handlerEnd)
|
|
635
|
+
.sort((a,b)=>Number(a.range.start)-Number(b.range.start));
|
|
636
|
+
const originalCalls = callExpressions(operationIr.sourceFile);
|
|
637
|
+
const originalByStart = new Map(originalCalls.map((call) => [call.getStart(operationIr.sourceFile), call]));
|
|
638
|
+
const candidates = generatedCalls.filter((call) => {
|
|
639
|
+
const start = call.getStart(generatedSourceFile);
|
|
640
|
+
return start >= Number(generatedRange.start) && start < Number(generatedRange.end);
|
|
641
|
+
});
|
|
642
|
+
for (const adapter of expected) {
|
|
643
|
+
const originalCall = originalByStart.get(Number(adapter.range.start));
|
|
644
|
+
if (!originalCall) continue;
|
|
645
|
+
const key = callPrintKey(printer, operationIr.sourceFile, originalCall);
|
|
646
|
+
const candidate = candidates.find((call) => {
|
|
647
|
+
const start = call.getStart(generatedSourceFile);
|
|
648
|
+
return !usedGeneratedStarts.has(start) && callPrintKey(printer, generatedSourceFile, call) === key;
|
|
649
|
+
});
|
|
650
|
+
if (!candidate) continue;
|
|
651
|
+
const start = candidate.getStart(generatedSourceFile);
|
|
652
|
+
usedGeneratedStarts.add(start);
|
|
653
|
+
generatedByStart.set(start, adapter);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return (call) => {
|
|
657
|
+
if (!call) return undefined;
|
|
658
|
+
const sourceFile = call.getSourceFile && call.getSourceFile();
|
|
659
|
+
return sourceFile ? generatedByStart.get(call.getStart(sourceFile)) : undefined;
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function routerGeneratedPackageIntrinsicLookup(router, recognition, options = {}) {
|
|
664
|
+
const intrinsics = [...(recognition && recognition.intrinsics || [])];
|
|
665
|
+
if (!router || intrinsics.length === 0) return undefined;
|
|
666
|
+
const records = handlerIrsForCanonicalRouterOutput(router) || [];
|
|
667
|
+
const generatedSourceFile = ts.createSourceFile(
|
|
668
|
+
String(router.fileName || 'app.ts'),
|
|
669
|
+
String(router.sourceText || ''),
|
|
670
|
+
ts.ScriptTarget.ES2022,
|
|
671
|
+
true,
|
|
672
|
+
ts.ScriptKind.TS
|
|
673
|
+
);
|
|
674
|
+
const generatedCalls = callExpressions(generatedSourceFile);
|
|
675
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
676
|
+
const generatedByStart = new Map();
|
|
677
|
+
const usedGeneratedStarts = new Set();
|
|
678
|
+
|
|
679
|
+
for (const record of records) {
|
|
680
|
+
const operationIr = record && record.operationIr;
|
|
681
|
+
const generatedRange = record && record.canonicalIr && record.canonicalIr.router
|
|
682
|
+
&& record.canonicalIr.router.entry && record.canonicalIr.router.entry.generatedRange;
|
|
683
|
+
if (!operationIr || !generatedRange || !operationIr.sourceFile || !operationIr.handler) continue;
|
|
684
|
+
const originalFile = normalizedModulePath(operationIr.sourceFile.fileName || operationIr.file);
|
|
685
|
+
const handlerStart = Number(operationIr.handler.getStart(operationIr.sourceFile));
|
|
686
|
+
const handlerEnd = Number(operationIr.handler.getEnd());
|
|
687
|
+
const expected = intrinsics.filter((intrinsic) => {
|
|
688
|
+
const intrinsicFile = normalizedModulePath(intrinsic && intrinsic.loc && intrinsic.loc.file);
|
|
689
|
+
const start = Number(intrinsic && intrinsic.range && intrinsic.range.start);
|
|
690
|
+
const end = Number(intrinsic && intrinsic.range && intrinsic.range.end);
|
|
691
|
+
return sameModulePath(intrinsicFile, originalFile, options.rootDir)
|
|
692
|
+
&& Number.isSafeInteger(start)
|
|
693
|
+
&& Number.isSafeInteger(end)
|
|
694
|
+
&& start >= handlerStart
|
|
695
|
+
&& end <= handlerEnd;
|
|
696
|
+
}).sort((left, right) => Number(left.range.start) - Number(right.range.start));
|
|
697
|
+
if (expected.length === 0) continue;
|
|
698
|
+
|
|
699
|
+
const originalCalls = callExpressions(operationIr.sourceFile);
|
|
700
|
+
const originalByStart = new Map(originalCalls.map((call) => [call.getStart(operationIr.sourceFile), call]));
|
|
701
|
+
const candidates = generatedCalls.filter((call) => {
|
|
702
|
+
const start = call.getStart(generatedSourceFile);
|
|
703
|
+
return start >= Number(generatedRange.start) && start < Number(generatedRange.end);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
for (const intrinsic of expected) {
|
|
707
|
+
const originalCall = originalByStart.get(Number(intrinsic.range.start));
|
|
708
|
+
const key = originalCall && callPrintKey(printer, operationIr.sourceFile, originalCall);
|
|
709
|
+
const candidate = key && candidates.find((call) => {
|
|
710
|
+
const start = call.getStart(generatedSourceFile);
|
|
711
|
+
return !usedGeneratedStarts.has(start) && callPrintKey(printer, generatedSourceFile, call) === key;
|
|
712
|
+
});
|
|
713
|
+
if (!candidate) {
|
|
714
|
+
throw new CanonicalProjectCompileError(
|
|
715
|
+
`Unable to link generated Router source to package intrinsic ${intrinsic.kind} from ${originalFile}.`,
|
|
716
|
+
[sourceDiagnosticForPackageEffect(intrinsic, `Unable to link generated Router source to package intrinsic ${intrinsic.kind}.`, {
|
|
717
|
+
contractId: intrinsic.contractId,
|
|
718
|
+
call: key || null,
|
|
719
|
+
routerEntry: record.entryStableId
|
|
720
|
+
})],
|
|
721
|
+
{ intrinsic, generatedRange, call: key || null },
|
|
722
|
+
'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED'
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
const generatedStart = candidate.getStart(generatedSourceFile);
|
|
726
|
+
usedGeneratedStarts.add(generatedStart);
|
|
727
|
+
generatedByStart.set(generatedStart, intrinsic);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (generatedByStart.size !== intrinsics.length) {
|
|
732
|
+
const linked = new Set([...generatedByStart.values()]);
|
|
733
|
+
const missing = intrinsics.filter((intrinsic) => !linked.has(intrinsic));
|
|
734
|
+
throw new CanonicalProjectCompileError(
|
|
735
|
+
`Generated Router source linked ${generatedByStart.size} of ${intrinsics.length} reachable package intrinsic(s).`,
|
|
736
|
+
missing.map((intrinsic) => sourceDiagnosticForPackageEffect(
|
|
737
|
+
intrinsic,
|
|
738
|
+
`Reachable package intrinsic ${intrinsic.kind} was not linked into generated Router source.`,
|
|
739
|
+
{ contractId: intrinsic.contractId }
|
|
740
|
+
)),
|
|
741
|
+
{ missing },
|
|
742
|
+
'PULSE_PACKAGE_EFFECT_SOURCE_LINK_FAILED'
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return (call) => {
|
|
747
|
+
if (!call) return undefined;
|
|
748
|
+
const sourceFile = call.getSourceFile && call.getSourceFile();
|
|
749
|
+
return sourceFile ? generatedByStart.get(call.getStart(sourceFile)) : undefined;
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function compileProjectPackageExtensions(entryFile, options = {}) {
|
|
754
|
+
return packageExtensionsForRecognition(prepareProjectPackageOperations(entryFile, options));
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function emptySchemaBundle(options = {}) {
|
|
758
|
+
return buildCanonicalSchemaBundle([], {
|
|
759
|
+
defaultNamespace: options.defaultNamespace || 'app',
|
|
760
|
+
contentTypePolicy: options.contentTypePolicy || 'accept-json-or-missing',
|
|
761
|
+
maxBytes: options.maxBytes || 65536
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function attachSchemaRegistryDeclaration(bundle, registry) {
|
|
766
|
+
if (!registry) return bundle;
|
|
767
|
+
return Object.freeze({
|
|
768
|
+
...bundle,
|
|
769
|
+
declaredSchemaIds: Object.freeze(registry.schemas.map((entry) => entry.id)),
|
|
770
|
+
deferredSchemaIds: Object.freeze([]),
|
|
771
|
+
registryIrVersion: registry.version,
|
|
772
|
+
registryHash: registry.registryHash,
|
|
773
|
+
fullCodecRealization: true
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function schemaResolvedConfig(schemaConfig = {}, options = {}) {
|
|
778
|
+
const schemas = Array.isArray(schemaConfig.entries) ? schemaConfig.entries : [];
|
|
779
|
+
return {
|
|
780
|
+
source: options.configFile ? path.resolve(options.configFile) : path.join(path.resolve(options.rootDir || process.cwd()), 'pulse.config.ts'),
|
|
781
|
+
runtime: {
|
|
782
|
+
payload: {
|
|
783
|
+
json: {
|
|
784
|
+
target: schemas.length > 0 ? 'schema' : 'generic',
|
|
785
|
+
defaultNamespace: schemaConfig.defaultNamespace || 'app',
|
|
786
|
+
contentTypePolicy: schemaConfig.contentTypePolicy || 'accept-json-or-missing',
|
|
787
|
+
maxBytes: schemaConfig.maxBytes || 65536,
|
|
788
|
+
schemas: schemas.map((schema) => ({
|
|
789
|
+
namespace: schema.namespace,
|
|
790
|
+
name: schema.name,
|
|
791
|
+
type: schema.type,
|
|
792
|
+
source: schema.source,
|
|
793
|
+
codec: 'json',
|
|
794
|
+
fields: schema.fields
|
|
795
|
+
}))
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function compileProjectSchemas(schemaConfig = {}, options = {}) {
|
|
803
|
+
const rootDir = path.resolve(options.rootDir || process.cwd());
|
|
804
|
+
const entries = Array.isArray(schemaConfig.entries) ? schemaConfig.entries : [];
|
|
805
|
+
const registry = schemaConfig.registry || null;
|
|
806
|
+
const registryActive = Boolean(registry && Array.isArray(registry.schemas) && registry.schemas.length > 0);
|
|
807
|
+
const declaredDependencies = Array.isArray(schemaConfig.dependencies)
|
|
808
|
+
? schemaConfig.dependencies.map((file) => path.resolve(file))
|
|
809
|
+
: [];
|
|
810
|
+
if (registry) {
|
|
811
|
+
const bundle = attachSchemaRegistryDeclaration(buildCanonicalSchemaBundle(registry, {
|
|
812
|
+
contentTypePolicy: schemaConfig.contentTypePolicy,
|
|
813
|
+
maxBytes: schemaConfig.maxBytes
|
|
814
|
+
}), registry);
|
|
815
|
+
return Object.freeze({
|
|
816
|
+
active: registryActive,
|
|
817
|
+
bundle,
|
|
818
|
+
diagnostics: Object.freeze([]),
|
|
819
|
+
warnings: Object.freeze([]),
|
|
820
|
+
dependencies: Object.freeze([...new Set(declaredDependencies)].sort()),
|
|
821
|
+
compiledSchemas: registry.schemas,
|
|
822
|
+
compilerVersion: 'pulse.schema-registry-extractor.v1',
|
|
823
|
+
summary: Object.freeze({
|
|
824
|
+
schemas: registry.schemas.length,
|
|
825
|
+
responseCases: registry.responses.length,
|
|
826
|
+
codecTableHash: bundle.codecTableHash
|
|
827
|
+
}),
|
|
828
|
+
registry,
|
|
829
|
+
codecInputs: schemaConfig.codecInputs || null,
|
|
830
|
+
legacyBridge: null,
|
|
831
|
+
realization: registryActive ? 'cross-target-codecs' : 'inactive'
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (entries.length === 0) {
|
|
835
|
+
const bundle = emptySchemaBundle(schemaConfig);
|
|
836
|
+
return Object.freeze({
|
|
837
|
+
active: registryActive,
|
|
838
|
+
bundle,
|
|
839
|
+
diagnostics: Object.freeze([]),
|
|
840
|
+
warnings: Object.freeze([]),
|
|
841
|
+
dependencies: Object.freeze([...new Set(declaredDependencies)].sort()),
|
|
842
|
+
compiledSchemas: Object.freeze([]),
|
|
843
|
+
compilerVersion: undefined,
|
|
844
|
+
registry,
|
|
845
|
+
codecInputs: schemaConfig.codecInputs || null,
|
|
846
|
+
legacyBridge: schemaConfig.legacyBridge || null,
|
|
847
|
+
realization: 'inactive'
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const resolvedConfig = schemaResolvedConfig(schemaConfig, { ...options, rootDir });
|
|
852
|
+
const result = buildSchemaJsonCompile({
|
|
853
|
+
cwd: rootDir,
|
|
854
|
+
generatedBy: CANONICAL_PROJECT_COMPILER_VERSION,
|
|
855
|
+
resolvedConfig
|
|
856
|
+
});
|
|
857
|
+
const errors = (result.diagnostics || []).filter((entry) => String(entry.severity || 'error') === 'error');
|
|
858
|
+
if (errors.length > 0 || result.artifact.status !== 'ok') {
|
|
859
|
+
throw new CanonicalProjectCompileError(
|
|
860
|
+
`JSON schema compilation failed with ${errors.length || result.diagnostics.length} diagnostic(s).`,
|
|
861
|
+
errors.length > 0 ? errors : result.diagnostics,
|
|
862
|
+
{ rootDir, configFile: options.configFile, schemaIds: entries.map((entry) => entry.id) }
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const bundle = buildCanonicalSchemaBundle(result.compiledSchemas, {
|
|
867
|
+
defaultNamespace: schemaConfig.defaultNamespace,
|
|
868
|
+
contentTypePolicy: schemaConfig.contentTypePolicy,
|
|
869
|
+
maxBytes: schemaConfig.maxBytes
|
|
870
|
+
});
|
|
871
|
+
const compiledDependencies = result.dependencies && result.dependencies.length > 0
|
|
872
|
+
? result.dependencies
|
|
873
|
+
: entries.filter((entry) => typeof entry.source === 'string' && entry.source).map((entry) => path.resolve(rootDir, entry.source));
|
|
874
|
+
const dependencies = [...new Set([...declaredDependencies, ...compiledDependencies].map((file) => path.resolve(file)))].sort();
|
|
875
|
+
return Object.freeze({
|
|
876
|
+
active: true,
|
|
877
|
+
bundle,
|
|
878
|
+
diagnostics: Object.freeze([...(result.diagnostics || [])]),
|
|
879
|
+
warnings: Object.freeze([...(result.warnings || [])]),
|
|
880
|
+
dependencies: Object.freeze(dependencies),
|
|
881
|
+
compiledSchemas: Object.freeze(result.compiledSchemas.map((schema) => Object.freeze({ ...schema, fields: Object.freeze(schema.fields.map((field) => Object.freeze({ ...field }))) }))),
|
|
882
|
+
compilerVersion: result.artifact.version,
|
|
883
|
+
summary: Object.freeze({ ...result.artifact.summary }),
|
|
884
|
+
registry,
|
|
885
|
+
codecInputs: schemaConfig.codecInputs || null,
|
|
886
|
+
legacyBridge: schemaConfig.legacyBridge || null,
|
|
887
|
+
realization: 'legacy'
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function replaceGeneratedMetadata(source, metadata, mode) {
|
|
892
|
+
const text = String(source);
|
|
893
|
+
const metadataSource = JSON.stringify(metadata, null, 2);
|
|
894
|
+
const startMarker = mode === 'esm'
|
|
895
|
+
? 'export const metadata = Object.freeze('
|
|
896
|
+
: 'const __pulse_metadata = ';
|
|
897
|
+
const endMarker = mode === 'esm'
|
|
898
|
+
? ');\nexport const schemaCodecs'
|
|
899
|
+
: ';\nmodule.exports = Object.freeze({';
|
|
900
|
+
const start = text.indexOf(startMarker);
|
|
901
|
+
const end = start < 0 ? -1 : text.indexOf(endMarker, start + startMarker.length);
|
|
902
|
+
if (start < 0 || end < 0) {
|
|
903
|
+
throw new CanonicalProjectCompileError(
|
|
904
|
+
'Package schema references could not be linked into generated canonical metadata.',
|
|
905
|
+
[],
|
|
906
|
+
{ mode },
|
|
907
|
+
'PULSE_PACKAGE_SCHEMA_METADATA_LINK_FAILED'
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
const suffix = `${endMarker}${text.slice(end + endMarker.length)}`;
|
|
911
|
+
return `${text.slice(0, start)}${startMarker}${metadataSource}${suffix}`;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function attachPackageSchemaReferences(compiled, recognition) {
|
|
915
|
+
const packageReferences = Array.isArray(recognition && recognition.schemaReferences)
|
|
916
|
+
? recognition.schemaReferences
|
|
917
|
+
: [];
|
|
918
|
+
if (packageReferences.length === 0) return compiled;
|
|
919
|
+
const references = [];
|
|
920
|
+
const seen = new Set();
|
|
921
|
+
for (const input of [...(compiled.metadata.schemaReferences || []), ...packageReferences]) {
|
|
922
|
+
const reference = {
|
|
923
|
+
id: String(input.id || ''),
|
|
924
|
+
usage: String(input.usage || ''),
|
|
925
|
+
capability: String(input.capability || ''),
|
|
926
|
+
...(input.file ? { file: String(input.file).replace(/\\/g, '/') } : {}),
|
|
927
|
+
position: Object.freeze({
|
|
928
|
+
line: Number(input.position && input.position.line || 1),
|
|
929
|
+
column: Number(input.position && input.position.column || 1),
|
|
930
|
+
offset: Number(input.position && input.position.offset || 0)
|
|
931
|
+
})
|
|
932
|
+
};
|
|
933
|
+
const key = [
|
|
934
|
+
reference.id,
|
|
935
|
+
reference.usage,
|
|
936
|
+
reference.capability,
|
|
937
|
+
reference.file || compiled.metadata.file || '',
|
|
938
|
+
reference.position.offset
|
|
939
|
+
].join('\u0000');
|
|
940
|
+
if (!reference.id || !reference.usage || !reference.capability || seen.has(key)) continue;
|
|
941
|
+
seen.add(key);
|
|
942
|
+
references.push(Object.freeze(reference));
|
|
943
|
+
}
|
|
944
|
+
references.sort((left, right) => String(left.file || compiled.metadata.file || '').localeCompare(String(right.file || compiled.metadata.file || ''))
|
|
945
|
+
|| left.position.offset - right.position.offset
|
|
946
|
+
|| left.id.localeCompare(right.id)
|
|
947
|
+
|| left.usage.localeCompare(right.usage));
|
|
948
|
+
const metadata = deepFreeze({
|
|
949
|
+
...compiled.metadata,
|
|
950
|
+
capabilities: Object.freeze([...new Set([
|
|
951
|
+
...(compiled.metadata.capabilities || []).map(String),
|
|
952
|
+
...packageReferences.map((reference) => String(reference.capability || '')).filter(Boolean)
|
|
953
|
+
])].sort()),
|
|
954
|
+
schemaReferences: Object.freeze(references),
|
|
955
|
+
schemaReferenceCount: references.length
|
|
956
|
+
});
|
|
957
|
+
return Object.freeze({
|
|
958
|
+
...compiled,
|
|
959
|
+
metadata,
|
|
960
|
+
generatedSource: replaceGeneratedMetadata(compiled.generatedSource, metadata, 'commonjs'),
|
|
961
|
+
generatedEsmSource: replaceGeneratedMetadata(compiled.generatedEsmSource, metadata, 'esm')
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function compileCanonicalProjectLegacy(entryFile, options = {}) {
|
|
966
|
+
const absoluteEntry = path.resolve(entryFile);
|
|
967
|
+
const rootDir = path.resolve(options.rootDir || path.dirname(absoluteEntry));
|
|
968
|
+
let graphBuild;
|
|
969
|
+
let rootResolution;
|
|
970
|
+
try {
|
|
971
|
+
graphBuild = buildReachableProjectGraph(absoluteEntry, {
|
|
972
|
+
rootDir,
|
|
973
|
+
workspaceRoot: options.workspaceRoot,
|
|
974
|
+
tsconfigFile: options.tsconfigFile,
|
|
975
|
+
resolver: options.moduleResolver,
|
|
976
|
+
configFile: options.configFile,
|
|
977
|
+
projectFragments: options.applicationProjectMetadata && options.applicationProjectMetadata.fragments
|
|
978
|
+
});
|
|
979
|
+
rootResolution = resolveProjectRoot(graphBuild);
|
|
980
|
+
} catch (error) {
|
|
981
|
+
if (error instanceof ReachableProjectGraphError || error instanceof RouterModuleLinkError) {
|
|
982
|
+
throw new CanonicalProjectCompileError(error.message, error.diagnostics, error.detail, error.code);
|
|
983
|
+
}
|
|
984
|
+
throw error;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const root = rootResolution && rootResolution.root;
|
|
988
|
+
const multiModule = graphBuild.summary.runtimeProjectModules > 1 || (root && root.module && root.module.path !== graphBuild.entryKey);
|
|
989
|
+
if (!root || !['router', 'handler'].includes(root.kind)) throw new CanonicalProjectCompileError(
|
|
990
|
+
'The configured entry must resolve its default export to a Router, Pulse application, or canonical handler.',
|
|
991
|
+
[Object.freeze({
|
|
992
|
+
code: 'PULSE_PROJECT_APPLICATION_ROOT_UNRESOLVED',
|
|
993
|
+
kind: 'CanonicalProjectCompileDiagnostic',
|
|
994
|
+
severity: 'error',
|
|
995
|
+
message: 'The configured entry default export did not resolve to a project-owned Router or handler.',
|
|
996
|
+
file: graphBuild.entryKey,
|
|
997
|
+
position: Object.freeze({ line: 1, column: 1 }),
|
|
998
|
+
detail: Object.freeze({ resolvedKind: root && root.kind || 'unknown', resolvedModule: root && root.module && root.module.path || null })
|
|
999
|
+
})],
|
|
1000
|
+
{ entryFile: absoluteEntry, graphVersion: graphBuild.graph.version },
|
|
1001
|
+
'PULSE_PROJECT_APPLICATION_ROOT_UNRESOLVED'
|
|
1002
|
+
);
|
|
1003
|
+
|
|
1004
|
+
let cryptoRealizationPlan = planCanonicalProjectCrypto(options, graphBuild.entryKey || absoluteEntry);
|
|
1005
|
+
assertGraphPreflight(graphBuild, absoluteEntry, options);
|
|
1006
|
+
|
|
1007
|
+
const schema = compileProjectSchemas(options.schemas || {}, {
|
|
1008
|
+
rootDir,
|
|
1009
|
+
configFile: options.configFile
|
|
1010
|
+
});
|
|
1011
|
+
const packageOperationRecognition = prepareReachableProjectPackageOperations(graphBuild, {
|
|
1012
|
+
rootDir,
|
|
1013
|
+
workspaceRoot: options.workspaceRoot,
|
|
1014
|
+
schemaBundle: schema.bundle
|
|
1015
|
+
});
|
|
1016
|
+
let managedHandlers = null;
|
|
1017
|
+
if (packageOperationRecognition.managedHandlers.length > 0) {
|
|
1018
|
+
try {
|
|
1019
|
+
managedHandlers = compileManagedHandlerDescriptors({
|
|
1020
|
+
graphBuild,
|
|
1021
|
+
descriptors: packageOperationRecognition.managedHandlers,
|
|
1022
|
+
packageOperationRecognition
|
|
1023
|
+
});
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
if (error instanceof ManagedHandlerCompileError) {
|
|
1026
|
+
throw new CanonicalProjectCompileError(
|
|
1027
|
+
error.message,
|
|
1028
|
+
error.diagnostics,
|
|
1029
|
+
{ entryFile: absoluteEntry, descriptors: packageOperationRecognition.managedHandlers.length },
|
|
1030
|
+
error.code
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
throw error;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (packageOperationRecognition.cryptoRequirements.length > 0) {
|
|
1037
|
+
cryptoRealizationPlan = planCanonicalProjectCrypto(
|
|
1038
|
+
options,
|
|
1039
|
+
graphBuild.entryKey || absoluteEntry,
|
|
1040
|
+
packageOperationRecognition.cryptoRequirements
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
const terminalPackageApplication = terminalPackageIntrinsicApplication(root, packageOperationRecognition);
|
|
1044
|
+
|
|
1045
|
+
let linkedProjectModules;
|
|
1046
|
+
if (multiModule) {
|
|
1047
|
+
try {
|
|
1048
|
+
linkedProjectModules = root.kind === 'router'
|
|
1049
|
+
? linkProjectRouterModules(graphBuild, { rootDir })
|
|
1050
|
+
: linkProjectPlainHandler(graphBuild, {
|
|
1051
|
+
rootDir,
|
|
1052
|
+
consumedRuntimeImports: managedHandlerRuntimeImportKeys(packageOperationRecognition.managedHandlers)
|
|
1053
|
+
});
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
if (error instanceof RouterModuleLinkError) {
|
|
1056
|
+
throw new CanonicalProjectCompileError(error.message, error.diagnostics, error.detail, error.code);
|
|
1057
|
+
}
|
|
1058
|
+
throw error;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const authoringSourceText = linkedProjectModules ? linkedProjectModules.rootSourceText : fs.readFileSync(absoluteEntry, 'utf8');
|
|
1063
|
+
const authoringFileName = linkedProjectModules ? linkedProjectModules.rootModule.path : stableSourceName(absoluteEntry, { ...options, rootDir });
|
|
1064
|
+
const baseAuthoringOptions = {
|
|
1065
|
+
...options,
|
|
1066
|
+
rootDir,
|
|
1067
|
+
fileName: authoringFileName,
|
|
1068
|
+
strict: options.strict === true,
|
|
1069
|
+
handlerAuthoring: options.handlerAuthoring,
|
|
1070
|
+
requireAsync: options.requireAsync === true,
|
|
1071
|
+
requireEffectAwait: options.requireEffectAwait === true,
|
|
1072
|
+
eventSchemaIds: schema.bundle.declaredSchemaIds || schema.bundle.schemaIds || [],
|
|
1073
|
+
...(linkedProjectModules && linkedProjectModules.kind === 'router' ? { linkedProjectModules } : {})
|
|
1074
|
+
};
|
|
1075
|
+
const isRouterAuthoring = linkedProjectModules ? linkedProjectModules.kind === 'router' : detectCanonicalRouterSource(authoringSourceText, baseAuthoringOptions);
|
|
1076
|
+
const packageExtensions = packageExtensionsForRecognition(packageOperationRecognition);
|
|
1077
|
+
const authoringOptions = {
|
|
1078
|
+
...baseAuthoringOptions,
|
|
1079
|
+
packageEffects: packageExtensions.effects,
|
|
1080
|
+
packageIntrinsics: packageExtensions.intrinsics || [],
|
|
1081
|
+
packageResultAdapters: packageExtensions.resultAdapters || [],
|
|
1082
|
+
packageEffectForCall: packageEffectLookup(packageOperationRecognition),
|
|
1083
|
+
packageIntrinsicForCall: packageIntrinsicLookup(packageOperationRecognition),
|
|
1084
|
+
packageResultAdapterForCall: packageResultAdapterLookup(packageOperationRecognition)
|
|
1085
|
+
};
|
|
1086
|
+
const router = isRouterAuthoring
|
|
1087
|
+
? compileCanonicalRouterSource(authoringSourceText, authoringOptions)
|
|
1088
|
+
: undefined;
|
|
1089
|
+
const compilerSourceText = router
|
|
1090
|
+
? router.sourceText
|
|
1091
|
+
: terminalPackageApplication
|
|
1092
|
+
? terminalPackageApplication.sourceText
|
|
1093
|
+
: authoringSourceText;
|
|
1094
|
+
const generatedPackageEffectForCall = router
|
|
1095
|
+
? routerGeneratedPackageEffectLookup(router, packageOperationRecognition, { rootDir })
|
|
1096
|
+
: packageEffectLookup(packageOperationRecognition);
|
|
1097
|
+
const generatedPackageResultAdapterForCall = router
|
|
1098
|
+
? routerGeneratedPackageResultAdapterLookup(router, packageOperationRecognition, { rootDir })
|
|
1099
|
+
: packageResultAdapterLookup(packageOperationRecognition);
|
|
1100
|
+
const generatedPackageIntrinsicForCall = router
|
|
1101
|
+
? routerGeneratedPackageIntrinsicLookup(router, packageOperationRecognition, { rootDir })
|
|
1102
|
+
: packageIntrinsicLookup(packageOperationRecognition);
|
|
1103
|
+
const applicationEnvelope = createApplicationEnvelope(router, options.applicationProjectMetadata);
|
|
1104
|
+
const compileOptions = {
|
|
1105
|
+
...options,
|
|
1106
|
+
rootDir,
|
|
1107
|
+
fileName: authoringFileName,
|
|
1108
|
+
schemaBundle: schema.bundle,
|
|
1109
|
+
packageEffects: packageExtensions.effects,
|
|
1110
|
+
packageIntrinsics: terminalPackageApplication ? [] : packageExtensions.intrinsics || [],
|
|
1111
|
+
packageResultAdapters: packageExtensions.resultAdapters || [],
|
|
1112
|
+
packageEffectForCall: generatedPackageEffectForCall,
|
|
1113
|
+
packageIntrinsicForCall: terminalPackageApplication ? undefined : generatedPackageIntrinsicForCall,
|
|
1114
|
+
packageResultAdapterForCall: generatedPackageResultAdapterForCall,
|
|
1115
|
+
allowedRuntimeImports: packageExtensions.allowedRuntimeImports,
|
|
1116
|
+
authoringSourceText,
|
|
1117
|
+
compilerPrelude: router && router.compilerPrelude,
|
|
1118
|
+
compilerOwnedCalls: router && router.compilerOwnedCalls,
|
|
1119
|
+
compilerOwnedIntrinsics: packageExtensions.intrinsics || [],
|
|
1120
|
+
metadataExtensions: Object.freeze({
|
|
1121
|
+
...(router ? {
|
|
1122
|
+
authoring: Object.freeze({
|
|
1123
|
+
kind: router.applicationKind || 'router',
|
|
1124
|
+
version: router.applicationKind === 'pulse' ? 'pulse.application-authoring.v1' : router.authoringVersion,
|
|
1125
|
+
...(router.applicationKind === 'pulse' ? { constructionMode: router.applicationConstructionMode } : {}),
|
|
1126
|
+
...(router.normalization ? { normalization: router.normalization } : {})
|
|
1127
|
+
}),
|
|
1128
|
+
router: router.metadata,
|
|
1129
|
+
...(router.userAuthoredAsync ? { userAuthoredAsync: true } : {}),
|
|
1130
|
+
...((router.warnings && router.warnings.length > 0) ? { warnings: router.warnings } : {}),
|
|
1131
|
+
...(router.normalization ? { routerNormalization: router.normalization } : {})
|
|
1132
|
+
} : {}),
|
|
1133
|
+
...(applicationEnvelope ? { application: applicationEnvelope } : {})
|
|
1134
|
+
}),
|
|
1135
|
+
strict: options.strict === true,
|
|
1136
|
+
handlerAuthoring: router ? undefined : options.handlerAuthoring,
|
|
1137
|
+
requireAsync: router ? false : options.requireAsync === true,
|
|
1138
|
+
requireEffectAwait: router ? false : options.requireEffectAwait === true,
|
|
1139
|
+
internalGeneratedHandler: Boolean(router)
|
|
1140
|
+
};
|
|
1141
|
+
const compiledBase = router
|
|
1142
|
+
? compileCanonicalSource(compilerSourceText, compileOptions)
|
|
1143
|
+
: terminalPackageApplication
|
|
1144
|
+
? compileCanonicalSource(compilerSourceText, compileOptions)
|
|
1145
|
+
: compileCanonicalFile(linkedProjectModules ? linkedProjectModules.rootModule.absolutePath : absoluteEntry, compileOptions);
|
|
1146
|
+
const compiled = attachPackageSchemaReferences(compiledBase, packageOperationRecognition);
|
|
1147
|
+
const rootReference = root ? [{
|
|
1148
|
+
module: root.module.path,
|
|
1149
|
+
exportName: root.exportName || 'default',
|
|
1150
|
+
localName: root.localName,
|
|
1151
|
+
role: root.kind === 'router' ? 'router' : 'handler',
|
|
1152
|
+
wrappers: [],
|
|
1153
|
+
source: {
|
|
1154
|
+
file: root.module.path,
|
|
1155
|
+
line: Number(root.declaration && root.declaration.loc && root.declaration.loc.start && root.declaration.loc.start.line || 1),
|
|
1156
|
+
column: Number(root.declaration && root.declaration.loc && root.declaration.loc.start && root.declaration.loc.start.column || 1)
|
|
1157
|
+
}
|
|
1158
|
+
}] : [];
|
|
1159
|
+
const reachableGraph = linkedProjectModules ? linkedProjectModules.graph : finalizeReachableProjectGraph(graphBuild, rootReference);
|
|
1160
|
+
const graphProjections = reachableProjectGraphProjections(graphBuild, reachableGraph);
|
|
1161
|
+
const project = Object.freeze({
|
|
1162
|
+
...compiled,
|
|
1163
|
+
projectCompilerVersion: CANONICAL_PROJECT_COMPILER_VERSION,
|
|
1164
|
+
graphBuilderVersion: PROJECT_GRAPH_BUILDER_VERSION,
|
|
1165
|
+
reachableGraph,
|
|
1166
|
+
packageReachability: graphProjections.packageReachability,
|
|
1167
|
+
packageProduct: graphProjections.packageProduct,
|
|
1168
|
+
entrySafety: graphProjections.entrySafety,
|
|
1169
|
+
nativeEligibility: graphProjections.nativeEligibility,
|
|
1170
|
+
cryptoRealizationPlan,
|
|
1171
|
+
moduleLinkage: Object.freeze({
|
|
1172
|
+
version: linkedProjectModules ? ROUTER_MODULE_LINKER_VERSION : 'pulse.router-module-linker.not-required.v1',
|
|
1173
|
+
linked: Boolean(linkedProjectModules),
|
|
1174
|
+
projectFiles: linkedProjectModules ? linkedProjectModules.projectFiles : graphBuild.projectModulePaths,
|
|
1175
|
+
runtimeProjectFiles: linkedProjectModules ? linkedProjectModules.runtimeProjectFiles : graphBuild.runtimeProjectModulePaths,
|
|
1176
|
+
summary: linkedProjectModules ? linkedProjectModules.summary : graphBuild.summary
|
|
1177
|
+
}),
|
|
1178
|
+
schema,
|
|
1179
|
+
packageExtensions,
|
|
1180
|
+
packageInspection: packageInspectionProjection(packageOperationRecognition, managedHandlers),
|
|
1181
|
+
...(terminalPackageApplication ? {
|
|
1182
|
+
packageApplication: terminalPackageApplicationInspection(terminalPackageApplication, packageOperationRecognition)
|
|
1183
|
+
} : {}),
|
|
1184
|
+
router,
|
|
1185
|
+
...(router && router.eventTopology ? {
|
|
1186
|
+
eventTopology: router.eventTopology,
|
|
1187
|
+
eventCatalog: router.eventCatalog,
|
|
1188
|
+
eventOutboundRequirements: router.eventOutboundRequirements
|
|
1189
|
+
} : {}),
|
|
1190
|
+
...(applicationEnvelope ? { application: applicationEnvelope } : {}),
|
|
1191
|
+
watchFiles: Object.freeze([...new Set([
|
|
1192
|
+
...graphBuild.projectFiles,
|
|
1193
|
+
...schema.dependencies,
|
|
1194
|
+
...(packageOperationRecognition.dependencies || [])
|
|
1195
|
+
].map((file) => path.resolve(file)))])
|
|
1196
|
+
});
|
|
1197
|
+
attachPackageOperationRecognition(project, packageOperationRecognition);
|
|
1198
|
+
return project;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function compileCanonicalProject(entryFile, options = {}) {
|
|
1202
|
+
return executeCanonicalProjectSpine(entryFile, options, compileCanonicalProjectLegacy);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function packageRealizationArtifactsForCompiled(compiled) {
|
|
1206
|
+
const recognition = packageOperationRecognitionForCompiled(compiled);
|
|
1207
|
+
return Object.freeze([...(recognition && recognition.realizationArtifacts || [])]);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
module.exports = Object.freeze({
|
|
1211
|
+
CANONICAL_PROJECT_COMPILER_VERSION,
|
|
1212
|
+
CanonicalProjectCompileError,
|
|
1213
|
+
schemaResolvedConfig,
|
|
1214
|
+
compileProjectSchemas,
|
|
1215
|
+
compileProjectPackageExtensions,
|
|
1216
|
+
compileCanonicalProject,
|
|
1217
|
+
packageRealizationArtifactsForCompiled
|
|
1218
|
+
});
|