@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.
Files changed (104) hide show
  1. package/README.md +52 -1
  2. package/bin/provider-proof-composition.js +34 -0
  3. package/bin/pulsewasm-extract.js +20 -0
  4. package/package.json +60 -5
  5. package/src/artifacts-dir.js +13 -0
  6. package/src/ast-json.js +52 -0
  7. package/src/build-manifest.js +354 -0
  8. package/src/canonical-api-compiler.js +245 -0
  9. package/src/canonical-native-compiler.js +411 -0
  10. package/src/canonical-native-plan.js +1477 -0
  11. package/src/canonical-project-compiler.js +1218 -0
  12. package/src/canonical-router-compiler.js +25 -0
  13. package/src/cli-intents.js +1235 -0
  14. package/src/cli.js +1927 -0
  15. package/src/codegen/assemblyscript-compile.js +3 -0
  16. package/src/codegen/assemblyscript-core.js +3 -0
  17. package/src/codegen/assemblyscript-shape.js +3 -0
  18. package/src/codegen/assemblyscript-wasm-smoke.js +3 -0
  19. package/src/codegen/backend-capabilities.js +3 -0
  20. package/src/codegen/channel-broadcaster.js +3 -0
  21. package/src/codegen/compiled-handlers.js +3 -0
  22. package/src/codegen/compiled-wasm-runtime.js +3 -0
  23. package/src/codegen/config-references.js +248 -0
  24. package/src/codegen/dispatch-ts.js +145 -0
  25. package/src/codegen/effect-composition.js +331 -0
  26. package/src/codegen/effect-runtime.js +418 -0
  27. package/src/codegen/execution-harness-ts.js +467 -0
  28. package/src/codegen/handler-bindings-ts.js +298 -0
  29. package/src/codegen/handler-library-contracts.js +3 -0
  30. package/src/codegen/host-capabilities.js +3 -0
  31. package/src/codegen/host-runtime-kernel.js +3 -0
  32. package/src/codegen/integrated-compiled-app.js +3 -0
  33. package/src/codegen/json-body.js +3 -0
  34. package/src/codegen/library-sidecars.js +3 -0
  35. package/src/codegen/local-harness-ts.js +453 -0
  36. package/src/codegen/pulse-wrapper.js +217 -0
  37. package/src/codegen/request-result-headers.js +3 -0
  38. package/src/codegen/schema-json-compile.js +3 -0
  39. package/src/codegen/schema-json-sidecar-v2.js +3 -0
  40. package/src/codegen/schema-json-sidecar.js +3 -0
  41. package/src/codegen/streaming-passthrough.js +3 -0
  42. package/src/codegen/wasm-host-abi.js +3 -0
  43. package/src/codegen/wasm-host-bridge.js +3 -0
  44. package/src/compiled-wasm-host-runtime-kv.js +3 -0
  45. package/src/config-resolver.js +813 -0
  46. package/src/crypto-requirement-planner.js +89 -0
  47. package/src/definitions/config-schema.js +14 -0
  48. package/src/definitions/handler-roles.js +14 -0
  49. package/src/definitions/path-grammar.js +14 -0
  50. package/src/definitions/router-api.js +14 -0
  51. package/src/diagnostics/codes.js +14 -0
  52. package/src/diagnostics/reporter.js +14 -0
  53. package/src/diagnostics.js +14 -0
  54. package/src/dispatch-table.js +400 -0
  55. package/src/events/event-emit.js +265 -0
  56. package/src/events/event-topology.js +127 -0
  57. package/src/execution-plan.js +463 -0
  58. package/src/extractor.js +2816 -0
  59. package/src/handler-eval.js +1154 -0
  60. package/src/handler-table.js +326 -0
  61. package/src/index.js +19 -0
  62. package/src/javascript-application-plan.js +181 -0
  63. package/src/kv-provider.js +3 -0
  64. package/src/path-table.js +60 -0
  65. package/src/path.js +14 -0
  66. package/src/patterns/config-define.js +30 -0
  67. package/src/patterns/dependency-call.js +18 -0
  68. package/src/patterns/env-lookup.js +13 -0
  69. package/src/patterns/handler-reference.js +29 -0
  70. package/src/patterns/path-literal.js +35 -0
  71. package/src/patterns/result.js +15 -0
  72. package/src/patterns/router-chain-call.js +50 -0
  73. package/src/patterns/router-construction.js +19 -0
  74. package/src/project/package-reachability.js +782 -0
  75. package/src/project/reachable-graph-builder.js +992 -0
  76. package/src/project/reachable-graph-contract.js +54 -0
  77. package/src/project/reachable-graph-implementation.js +36 -0
  78. package/src/project/router-module-linker.js +710 -0
  79. package/src/project-config-compiler.js +222 -0
  80. package/src/project-target-support.js +500 -0
  81. package/src/provider-toolchain.js +299 -0
  82. package/src/spine/async-surface-normalizer.js +312 -0
  83. package/src/spine/canonical-handler-ir.js +335 -0
  84. package/src/spine/canonical-native-module.js +87 -0
  85. package/src/spine/canonical-native-plan.js +89 -0
  86. package/src/spine/canonical-project.js +76 -0
  87. package/src/spine/canonical-router.js +155 -0
  88. package/src/spine/canonical-source.js +165 -0
  89. package/src/spine/diagnostic-authority.js +290 -0
  90. package/src/spine/equivalence.js +262 -0
  91. package/src/spine/guest-unit-stage.js +87 -0
  92. package/src/spine/handler-ir-emitter.js +454 -0
  93. package/src/spine/handler-ir-managed.js +1597 -0
  94. package/src/spine/handler-ir.js +797 -0
  95. package/src/spine/handler-surface-authority.js +585 -0
  96. package/src/spine/package-operation-seam.js +1015 -0
  97. package/src/spine/pipeline.js +202 -0
  98. package/src/spine/plain-handler-frontend.js +537 -0
  99. package/src/spine/provider-requirement-authority.js +208 -0
  100. package/src/spine/router-control-contract.js +17 -0
  101. package/src/spine/router-handler-frontend.js +513 -0
  102. package/src/spine/router-handler-ir.js +372 -0
  103. package/src/spine/router-topology-frontend.js +635 -0
  104. 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
+ });