@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,326 @@
1
+ 'use strict';
2
+
3
+ const { PACKAGE_VERSION, normalizeArtifact, stableFileName } = require('./diagnostics.js');
4
+ const { createHandlerStableId, createHandlerStableInput } = require('./stable-id.js');
5
+ const { DEFAULT_HANDLER_ROLE_REGISTRY, ROLE_SIGNATURES, roleForOperation } = require('./definitions/handler-roles.js');
6
+ const { HANDLER_ID_POLICY, HANDLER_TABLE_VERSION } = loadContractsHandlerTable();
7
+ const EVENT_ROLE_SIGNATURE = Object.freeze({ params: 1, display: '(ctx) => Promise<void>' });
8
+
9
+ function loadContractsHandlerTable() {
10
+ try {
11
+ return require('@pulse-compute/wasm-contracts/handler-table');
12
+ } catch (error) {
13
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
14
+ return require('../../contracts/src/handler-table.js');
15
+ }
16
+ throw error;
17
+ }
18
+ }
19
+
20
+ function clone(value) {
21
+ return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
22
+ }
23
+
24
+ function uniqueSorted(values) {
25
+ return Array.from(new Set(values)).sort();
26
+ }
27
+
28
+ function handlerDisplayName(entry) {
29
+ return entry.localName || entry.exportName || entry.inlineName || entry.id;
30
+ }
31
+
32
+ function createUse(router, op, role, ref) {
33
+ const use = {
34
+ role,
35
+ router: router.name,
36
+ opKind: op.kind,
37
+ order: op.order,
38
+ statementOrder: op.statementOrder,
39
+ loc: ref?.loc || op.loc
40
+ };
41
+ if (op.route || op.kind === 'get' || op.kind === 'post') {
42
+ use.method = op.method || op.kind.toUpperCase();
43
+ use.path = op.path;
44
+ }
45
+ if (op.kind === 'use' && op.path) use.path = op.path;
46
+ if (op.kind === 'lifecycle') use.event = op.event;
47
+ if (role === 'event') {
48
+ use.event = op.type;
49
+ use.schemaId = op.declaration && op.declaration.schemaId;
50
+ }
51
+ return use;
52
+ }
53
+
54
+ function expectedSignatureForRole(role, registry = DEFAULT_HANDLER_ROLE_REGISTRY) {
55
+ const definition = registry.get(role);
56
+ return definition ? { params: definition.params, display: definition.display } : undefined;
57
+ }
58
+
59
+ function validateEntryForRole(entry, role, use, diagnostics, options = {}) {
60
+ entry._validatedRoleKeys ||= new Set();
61
+ const key = `${entry.id}:${role}`;
62
+ if (entry._validatedRoleKeys.has(key)) return;
63
+ entry._validatedRoleKeys.add(key);
64
+
65
+ const expected = typeof options.expectedSignatureForRole === 'function'
66
+ ? options.expectedSignatureForRole(role, entry, use)
67
+ : expectedSignatureForRole(role);
68
+ const signature = entry.signature || {};
69
+ const loc = entry.loc || use?.loc || { file: entry.file || '<unknown>' };
70
+
71
+ if (!expected) return;
72
+
73
+ // Sprint 1 Wave 2: managed Router handlers are async-shaped authoring functions.
74
+ // Promise runtime semantics remain rejected by the shared handler-surface normalizer.
75
+
76
+ if ((signature.nonIdentifierParams || []).length > 0) {
77
+ diagnostics.push({
78
+ pass: 'handler-table',
79
+ code: 'PULSEWASM_UNSUPPORTED_HANDLER_PARAMETER',
80
+ severity: 'error',
81
+ message: `Handler "${handlerDisplayName(entry)}" uses destructured or non-identifier parameters.`,
82
+ hint: `Use the explicit ${expected.display} role signature.`,
83
+ loc
84
+ });
85
+ }
86
+
87
+ const expectedParams = Array.isArray(expected.params) ? expected.params : [expected.params];
88
+ if (!expectedParams.includes(signature.paramCount)) {
89
+ diagnostics.push({
90
+ pass: 'handler-table',
91
+ code: 'PULSEWASM_ROLE_SIGNATURE_MISMATCH',
92
+ severity: 'error',
93
+ message: `Handler "${handlerDisplayName(entry)}" is used as ${role} but has ${signature.paramCount ?? 0} parameter(s).`,
94
+ hint: `Expected ${expected.display}.`,
95
+ loc
96
+ });
97
+ }
98
+ }
99
+
100
+ function cleanEntry(entry) {
101
+ const out = { ...entry };
102
+ delete out._validatedRoleKeys;
103
+ out.roles = uniqueSorted(out.roles || []);
104
+ return out;
105
+ }
106
+
107
+ function buildHandlerTable(ir, options = {}) {
108
+ const cwd = options.cwd || process.cwd();
109
+ const diagnostics = [];
110
+ const declarationsByName = new Map();
111
+ const entriesBySymbolName = new Map();
112
+ const entriesById = new Map();
113
+ const handlers = [];
114
+ let nextOrder = 0;
115
+
116
+ for (const decl of ir.handlerIR || []) {
117
+ declarationsByName.set(decl.symbolId || decl.name, decl);
118
+ }
119
+
120
+ function addEntry(entry) {
121
+ const existing = entriesById.get(entry.id);
122
+ if (existing) return existing;
123
+ entry.order = nextOrder++;
124
+ entry.roles ||= [];
125
+ entry.uses ||= [];
126
+ entriesById.set(entry.id, entry);
127
+ handlers.push(entry);
128
+ if (entry.symbolId) entriesBySymbolName.set(entry.symbolId, entry);
129
+ else if (entry.localName) entriesBySymbolName.set(entry.localName, entry);
130
+ return entry;
131
+ }
132
+
133
+ function entryFromDeclaration(decl) {
134
+ const localName = decl.localName || decl.name;
135
+ const symbolId = decl.symbolId && decl.symbolId !== localName ? decl.symbolId : undefined;
136
+ const lookupName = symbolId || localName;
137
+ const existing = entriesBySymbolName.get(lookupName);
138
+ if (existing) return existing;
139
+ const stableInput = createHandlerStableInput({
140
+ file: decl.file,
141
+ kind: decl.kind,
142
+ localName,
143
+ exportName: decl.exportName,
144
+ inline: false,
145
+ sourceTextHash: decl.sourceTextHash
146
+ });
147
+ const id = createHandlerStableId({
148
+ file: decl.file,
149
+ kind: decl.kind,
150
+ localName,
151
+ exportName: decl.exportName,
152
+ inline: false,
153
+ sourceTextHash: decl.sourceTextHash
154
+ });
155
+ return addEntry({
156
+ id,
157
+ stableInput,
158
+ kind: decl.kind,
159
+ ...(symbolId ? { symbolId } : {}),
160
+ localName,
161
+ exportName: decl.exportName,
162
+ file: decl.file,
163
+ loc: decl.loc,
164
+ sourceTextHash: decl.sourceTextHash,
165
+ declaration: {
166
+ topLevel: true,
167
+ exported: Boolean(decl.exportName)
168
+ },
169
+ signature: decl.signature
170
+ });
171
+ }
172
+
173
+ function entryFromInline(ref, role, router, op) {
174
+ const inlineName = `<inline:${role}:${router.name}:${op.order}>`;
175
+ const file = stableFileName(ref.loc?.file || ir.source, cwd);
176
+ const stableLoc = ref.loc
177
+ ? {
178
+ file,
179
+ start: ref.loc.start,
180
+ end: ref.loc.end
181
+ }
182
+ : undefined;
183
+ const stableInput = createHandlerStableInput({
184
+ file,
185
+ kind: ref.inlineKind || 'inline-function',
186
+ localName: null,
187
+ exportName: null,
188
+ inline: true,
189
+ loc: stableLoc,
190
+ sourceTextHash: ref.sourceTextHash
191
+ });
192
+ const id = createHandlerStableId({
193
+ file,
194
+ kind: ref.inlineKind || 'inline-function',
195
+ localName: null,
196
+ exportName: null,
197
+ inline: true,
198
+ loc: stableLoc,
199
+ sourceTextHash: ref.sourceTextHash
200
+ });
201
+ return addEntry({
202
+ id,
203
+ stableInput,
204
+ kind: ref.inlineKind || 'inline-function',
205
+ inlineName,
206
+ file,
207
+ loc: ref.loc,
208
+ sourceTextHash: ref.sourceTextHash,
209
+ declaration: {
210
+ topLevel: false,
211
+ exported: false
212
+ },
213
+ signature: ref.signature
214
+ });
215
+ }
216
+
217
+ function resolveRef(ref, role, router, op) {
218
+ if (!ref) return ref;
219
+ if (ref.kind === 'static') return clone(ref);
220
+
221
+ let entry;
222
+ if (ref.kind === 'identifier' || ref.kind === 'ref') {
223
+ const decl = declarationsByName.get(ref.value);
224
+ if (!decl) {
225
+ diagnostics.push({
226
+ pass: 'handler-table',
227
+ code: 'PULSEWASM_UNRESOLVED_HANDLER_REFERENCE',
228
+ severity: 'error',
229
+ message: `Unable to resolve ${role} handler "${ref.value}" as a top-level handler declaration.`,
230
+ hint: 'Use a top-level function declaration, const arrow function, or const function expression in the same entry file.',
231
+ loc: ref.loc || op.loc
232
+ });
233
+ return { kind: 'unresolved', name: ref.value, role, loc: ref.loc || op.loc };
234
+ }
235
+ entry = entryFromDeclaration(decl);
236
+ } else if (ref.kind === 'inline') {
237
+ entry = entryFromInline(ref, role, router, op);
238
+ } else {
239
+ diagnostics.push({
240
+ pass: 'handler-table',
241
+ code: 'PULSEWASM_UNSUPPORTED_HANDLER_REFERENCE',
242
+ severity: 'error',
243
+ message: `Unsupported ${role} handler reference kind "${ref.kind}".`,
244
+ hint: 'Use a direct handler identifier or an inline function expression.',
245
+ loc: ref.loc || op.loc
246
+ });
247
+ return { kind: 'unsupported', name: ref.value, role, loc: ref.loc || op.loc };
248
+ }
249
+
250
+ const use = createUse(router, op, role, ref);
251
+ entry.roles.push(role);
252
+ entry.uses.push(use);
253
+ validateEntryForRole(entry, role, use, diagnostics, options);
254
+
255
+ return {
256
+ kind: 'handler',
257
+ id: entry.id,
258
+ name: entry.localName || entry.inlineName,
259
+ role,
260
+ loc: ref.loc || op.loc
261
+ };
262
+ }
263
+
264
+ const resolvedRouterIR = (ir.routerIR || []).map((router) => ({
265
+ ...router,
266
+ ops: (router.ops || []).map((op) => {
267
+ const next = clone(op);
268
+ const role = roleForOperation(next);
269
+ if (!role) return next;
270
+ if (next.kind === 'channel') {
271
+ if (next.value && next.value.kind !== 'static') {
272
+ next.value = resolveRef(next.value, role, router, next);
273
+ }
274
+ return next;
275
+ }
276
+ if (next.handler) next.handler = resolveRef(next.handler, role, router, next);
277
+ return next;
278
+ })
279
+ }));
280
+
281
+ const resolvedEventIR = (ir.eventIR || []).map((op) => {
282
+ const next = clone(op);
283
+ if (next.handler) next.handler = resolveRef(next.handler, 'event', { name: next.router }, next);
284
+ return next;
285
+ });
286
+
287
+ const artifactHandlers = handlers.map(cleanEntry);
288
+ const eventActive = resolvedEventIR.length > 0;
289
+ const artifact = normalizeArtifact({
290
+ version: HANDLER_TABLE_VERSION,
291
+ generatedBy: PACKAGE_VERSION,
292
+ source: ir.source,
293
+ idPolicy: HANDLER_ID_POLICY,
294
+ roleSignatures: eventActive ? { ...ROLE_SIGNATURES, event: EVENT_ROLE_SIGNATURE } : ROLE_SIGNATURES,
295
+ handlers: artifactHandlers,
296
+ summary: {
297
+ handlers: artifactHandlers.length,
298
+ declarationHandlers: artifactHandlers.filter((entry) => entry.declaration?.topLevel).length,
299
+ inlineHandlers: artifactHandlers.filter((entry) => !entry.declaration?.topLevel).length,
300
+ roles: uniqueSorted(artifactHandlers.flatMap((entry) => entry.roles || [])),
301
+ ...(eventActive ? { eventHandlers: artifactHandlers.filter((entry) => entry.roles.includes('event')).length } : {}),
302
+ diagnostics: diagnostics.length
303
+ }
304
+ }, cwd);
305
+
306
+ return {
307
+ handlerTable: artifact,
308
+ diagnostics,
309
+ resolvedIR: {
310
+ ...ir,
311
+ routerIR: resolvedRouterIR,
312
+ ...(eventActive ? { eventIR: resolvedEventIR } : {}),
313
+ handlerIR: artifact.handlers
314
+ }
315
+ };
316
+ }
317
+
318
+ module.exports = {
319
+ HANDLER_ID_POLICY,
320
+ HANDLER_TABLE_VERSION,
321
+ ROLE_SIGNATURES,
322
+ EVENT_ROLE_SIGNATURE,
323
+ buildHandlerTable,
324
+ expectedSignatureForRole,
325
+ roleForOperation
326
+ };
package/src/index.js ADDED
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ astJson: require('./ast-json.js'),
5
+ buildManifest: require('./build-manifest.js'),
6
+ cli: require('./cli.js'),
7
+ configResolver: require('./config-resolver.js'),
8
+ projectConfigCompiler: require('./project-config-compiler.js'),
9
+ diagnostics: require('./diagnostics.js'),
10
+ canonicalApiCompiler: require('./canonical-api-compiler.js'),
11
+ canonicalNativePlan: require('./canonical-native-plan.js'),
12
+ canonicalNativeCompiler: require('./canonical-native-compiler.js'),
13
+ dispatchTable: require('./dispatch-table.js'),
14
+ executionPlan: require('./execution-plan.js'),
15
+ extractor: require('./extractor.js'),
16
+ handlerEval: require('./handler-eval.js'),
17
+ handlerTable: require('./handler-table.js'),
18
+ pathTable: require('./path-table.js')
19
+ };
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const {
5
+ buildReachableProjectGraph,
6
+ ReachableProjectGraphError
7
+ } = require('./project/reachable-graph-builder.js');
8
+ const {
9
+ JAVASCRIPT_APPLICATION_PLAN_VERSION,
10
+ normalizeJavascriptApplicationPlan
11
+ } = require('@pulse-compute/wasm-contracts/project/javascript-application');
12
+
13
+ const JAVASCRIPT_APPLICATION_PLANNER_VERSION = 'pulse.javascript-application-planner.v1';
14
+ const CORE_APPLICATION_PACKAGES = new Set(['@pulse-compute/runtime', '@pulse-compute/pulse']);
15
+
16
+ class JavascriptApplicationPlanError extends Error {
17
+ constructor(code, message, detail = {}, diagnostics = []) {
18
+ super(message);
19
+ this.name = 'JavascriptApplicationPlanError';
20
+ this.code = code;
21
+ this.detail = Object.freeze({ ...detail });
22
+ this.diagnostics = Object.freeze([...diagnostics]);
23
+ }
24
+ }
25
+
26
+ function moduleSources(graph, moduleId) {
27
+ return Object.freeze(graph.edges
28
+ .filter((edge) => edge.runtime && edge.to === moduleId && edge.source)
29
+ .map((edge) => edge.source)
30
+ .sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column));
31
+ }
32
+
33
+ function packageStatus(module, graphBuild) {
34
+ if (CORE_APPLICATION_PACKAGES.has(module.packageName)) {
35
+ return Object.freeze({ status: 'supplied-core', runtimeEntry: module.packageName, reasonCode: null });
36
+ }
37
+ const product = graphBuild.packageProduct && graphBuild.packageProduct.contracts
38
+ .find((entry) => entry.packageName === module.packageName);
39
+ if (product) {
40
+ const target = product.javascriptTarget;
41
+ if (target && target.status === 'supported') {
42
+ return Object.freeze({ status: 'package-runtime', runtimeEntry: target.entry, reasonCode: null });
43
+ }
44
+ return Object.freeze({
45
+ status: 'unavailable',
46
+ runtimeEntry: null,
47
+ reasonCode: target && target.reasonCode || 'PULSE_JAVASCRIPT_PACKAGE_REALIZATION_UNAVAILABLE'
48
+ });
49
+ }
50
+ if (module.packageContract) {
51
+ return Object.freeze({
52
+ status: 'unavailable',
53
+ runtimeEntry: null,
54
+ reasonCode: 'PULSE_JAVASCRIPT_PACKAGE_REALIZATION_UNAVAILABLE'
55
+ });
56
+ }
57
+ return Object.freeze({ status: 'node-require', runtimeEntry: module.packageName, reasonCode: null });
58
+ }
59
+
60
+ function createPackageEntries(graphBuild) {
61
+ return graphBuild.graph.modules
62
+ .filter((module) => module.runtime && module.kind === 'package')
63
+ .map((module) => {
64
+ const status = packageStatus(module, graphBuild);
65
+ return Object.freeze({
66
+ moduleId: module.id,
67
+ packageName: module.packageName,
68
+ packageSubpath: module.packageSubpath,
69
+ contractId: module.packageContract,
70
+ status: status.status,
71
+ runtimeEntry: status.runtimeEntry,
72
+ reasonCode: status.reasonCode,
73
+ sources: moduleSources(graphBuild.graph, module.id)
74
+ });
75
+ });
76
+ }
77
+
78
+ function createBlockers(graphBuild, packages) {
79
+ const blockers = [];
80
+ if (graphBuild.entrySafety && graphBuild.entrySafety.importSafe === false) {
81
+ const first = graphBuild.nativeEligibility && graphBuild.nativeEligibility.blockers
82
+ .find((entry) => entry.kind === 'application-entry-lifecycle-side-effect');
83
+ blockers.push(Object.freeze({
84
+ code: 'PULSE_APPLICATION_ENTRY_LIFECYCLE_SIDE_EFFECT',
85
+ kind: 'application-entry-lifecycle-side-effect',
86
+ message: 'The configured Pulse application entry starts a known provider lifecycle during module evaluation.',
87
+ moduleId: first && first.moduleId || graphBuild.graph.entry,
88
+ source: first && first.source || { file: graphBuild.entryKey, line: 1, column: 1 }
89
+ }));
90
+ }
91
+ for (const entry of packages.filter((item) => item.status === 'unavailable')) {
92
+ const source = entry.sources[0] || { file: graphBuild.entryKey, line: 1, column: 1 };
93
+ blockers.push(Object.freeze({
94
+ code: entry.reasonCode || 'PULSE_JAVASCRIPT_PACKAGE_REALIZATION_UNAVAILABLE',
95
+ kind: 'javascript-package-realization-unavailable',
96
+ message: `Package ${entry.packageName}${entry.packageSubpath && entry.packageSubpath !== '.' ? entry.packageSubpath.slice(1) : ''} has no JavaScript realization for the selected target.`,
97
+ moduleId: entry.moduleId,
98
+ packageName: entry.packageName,
99
+ packageSubpath: entry.packageSubpath,
100
+ contractId: entry.contractId,
101
+ source
102
+ }));
103
+ }
104
+ return blockers;
105
+ }
106
+
107
+ function buildJavascriptApplicationPlan(entryFile, options = {}) {
108
+ const absoluteEntry = path.resolve(entryFile);
109
+ let graphBuild;
110
+ try {
111
+ graphBuild = buildReachableProjectGraph(absoluteEntry, {
112
+ rootDir: options.rootDir,
113
+ workspaceRoot: options.workspaceRoot,
114
+ configFile: options.configFile,
115
+ projectFragments: options.projectFragments,
116
+ tsconfig: options.tsconfig
117
+ });
118
+ } catch (error) {
119
+ if (error instanceof ReachableProjectGraphError || error && error.name === 'ReachableProjectGraphError') {
120
+ throw new JavascriptApplicationPlanError(
121
+ error.code || 'PULSE_JAVASCRIPT_APPLICATION_GRAPH_FAILED',
122
+ error.message,
123
+ error.detail,
124
+ error.diagnostics
125
+ );
126
+ }
127
+ throw error;
128
+ }
129
+ const entryModule = graphBuild.graph.modules.find((module) => module.id === graphBuild.graph.entry);
130
+ if (!entryModule || entryModule.kind !== 'project' || !entryModule.path) {
131
+ throw new JavascriptApplicationPlanError(
132
+ 'PULSE_JAVASCRIPT_APPLICATION_ENTRY_INVALID',
133
+ 'JavaScript application plan entry must resolve to a project module.',
134
+ { entryFile: absoluteEntry, graphEntry: graphBuild.graph.entry }
135
+ );
136
+ }
137
+ const packages = createPackageEntries(graphBuild);
138
+ const blockers = createBlockers(graphBuild, packages);
139
+ const requestedEnvelope = options.capabilityEnvelope || {};
140
+ const capabilityEnvelope = Object.freeze({
141
+ version: requestedEnvelope.version || 'pulse.javascript-capability-envelope.v1',
142
+ provider: requestedEnvelope.provider || options.provider || 'node',
143
+ status: requestedEnvelope.status || 'provider-adapters-deferred',
144
+ core: requestedEnvelope.core || Object.freeze({ request: true, response: true, state: true }),
145
+ effects: requestedEnvelope.effects || Object.freeze({ fetch: false, config: false, secret: false, kv: false }),
146
+ packages: Object.freeze(packages.map((entry) => Object.freeze({
147
+ contractId: entry.contractId,
148
+ packageName: entry.packageName,
149
+ status: entry.status
150
+ })))
151
+ });
152
+ const plan = normalizeJavascriptApplicationPlan({
153
+ provider: options.provider || 'node',
154
+ targetId: options.targetId || 'node-javascript',
155
+ entryPath: entryModule.path,
156
+ graphVersion: graphBuild.graph.version,
157
+ graphHash: graphBuild.graph.graphHash,
158
+ resolverHash: graphBuild.graph.resolver.resolverHash,
159
+ entryModuleId: graphBuild.graph.entry,
160
+ modules: graphBuild.graph.modules,
161
+ edges: graphBuild.graph.edges,
162
+ packages,
163
+ blockers,
164
+ packageProduct: graphBuild.packageProduct,
165
+ nativeEligibility: graphBuild.nativeEligibility,
166
+ entrySafety: graphBuild.entrySafety,
167
+ capabilityEnvelope,
168
+ providerLifecycleDeferred: options.providerLifecycleDeferred !== false
169
+ });
170
+ return Object.freeze({
171
+ ...plan,
172
+ plannerVersion: JAVASCRIPT_APPLICATION_PLANNER_VERSION
173
+ });
174
+ }
175
+
176
+ module.exports = Object.freeze({
177
+ JAVASCRIPT_APPLICATION_PLANNER_VERSION,
178
+ JAVASCRIPT_APPLICATION_PLAN_VERSION,
179
+ JavascriptApplicationPlanError,
180
+ buildJavascriptApplicationPlan
181
+ });
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-host-runtime/runtime/kv-provider');
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ const { PACKAGE_VERSION, normalizeArtifact } = require('./diagnostics.js');
4
+ const { PATH_POLICY, compileRoutePath, publicPathPattern } = require('./path.js');
5
+ const { PATH_TABLE_VERSION } = loadContractsPathTable();
6
+
7
+ function loadContractsPathTable() {
8
+ try {
9
+ return require('@pulse-compute/wasm-contracts/path-table');
10
+ } catch (error) {
11
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
12
+ return require('../../contracts/src/path-table.js');
13
+ }
14
+ throw error;
15
+ }
16
+ }
17
+
18
+ function buildPathTable(routePlan, options = {}) {
19
+ const cwd = options.cwd || process.cwd();
20
+ const routes = (routePlan.routes || []).map((route) => {
21
+ const compiled = route.pathPattern || publicPathPattern(compileRoutePath(route.path, { allowWildcard: true }));
22
+ return {
23
+ stableId: route.stableId,
24
+ runtimeId: route.runtimeId ?? route.id,
25
+ method: route.method,
26
+ path: route.path,
27
+ params: route.params || [],
28
+ pattern: compiled,
29
+ scopedMiddleware: route.scopedMiddleware || []
30
+ };
31
+ });
32
+
33
+ const scopedMiddlewareMatches = routes.reduce((sum, route) => sum + (route.scopedMiddleware || []).length, 0);
34
+ const wildcardRoutes = routes.filter((route) => route.pattern && route.pattern.wildcard).length;
35
+ const paramNames = new Set();
36
+ for (const route of routes) {
37
+ for (const param of route.params || []) paramNames.add(param);
38
+ }
39
+
40
+ return normalizeArtifact({
41
+ version: PATH_TABLE_VERSION,
42
+ generatedBy: options.generatedBy || routePlan.generatedBy || PACKAGE_VERSION,
43
+ source: routePlan.source,
44
+ entryRouter: routePlan.entryRouter,
45
+ policy: PATH_POLICY,
46
+ routes,
47
+ summary: {
48
+ routes: routes.length,
49
+ wildcardRoutes,
50
+ scopedMiddlewareMatches,
51
+ uniqueParams: paramNames.size,
52
+ paramNames: Array.from(paramNames).sort()
53
+ }
54
+ }, cwd);
55
+ }
56
+
57
+ module.exports = {
58
+ PATH_TABLE_VERSION,
59
+ buildPathTable
60
+ };
package/src/path.js ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ function loadContractsPath() {
4
+ try {
5
+ return require('@pulse-compute/wasm-contracts/path');
6
+ } catch (error) {
7
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
8
+ return require('../../contracts/src/path.js');
9
+ }
10
+ throw error;
11
+ }
12
+ }
13
+
14
+ module.exports = loadContractsPath();
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ function isTopLevelDefineConfigCall(ts, sourceFile, call) {
4
+ const parent = call.parent;
5
+ if (!parent) return false;
6
+ if (ts.isExportAssignment(parent)) return true;
7
+ if (ts.isVariableDeclaration(parent)) {
8
+ const declList = parent.parent;
9
+ const varStmt = declList && declList.parent;
10
+ return Boolean(varStmt && ts.isVariableStatement(varStmt) && varStmt.parent === sourceFile);
11
+ }
12
+ return false;
13
+ }
14
+
15
+ function collectDefineConfigCalls(ts, sourceFile) {
16
+ const calls = [];
17
+ function walk(node) {
18
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'defineConfig') {
19
+ calls.push(node);
20
+ }
21
+ ts.forEachChild(node, walk);
22
+ }
23
+ walk(sourceFile);
24
+ return calls;
25
+ }
26
+
27
+ module.exports = {
28
+ collectDefineConfigCalls,
29
+ isTopLevelDefineConfigCall
30
+ };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ function classifyCallExpression(ts, call, scope) {
4
+ const expr = call.expression;
5
+ if (ts.isIdentifier(expr)) {
6
+ if (expr.text === 'next') return { kind: 'continuation', name: 'next', resolved: true };
7
+ if (scope && scope.localFunctions && scope.localFunctions.has(expr.text)) {
8
+ return { kind: 'local-function', name: expr.text, resolved: true };
9
+ }
10
+ return { kind: 'identifier', name: expr.text, resolved: false };
11
+ }
12
+ if (ts.isPropertyAccessExpression(expr)) {
13
+ return { kind: 'member', name: expr.name.text, resolved: false };
14
+ }
15
+ return { kind: 'dynamic', name: call.getText ? call.getText() : '<dynamic>', resolved: false };
16
+ }
17
+
18
+ module.exports = { classifyCallExpression };
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ function isEnvCall(ts, expr, envParamName) {
4
+ if (ts.isIdentifier(expr) && expr.text === envParamName) {
5
+ return { method: 'string' };
6
+ }
7
+ if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === envParamName) {
8
+ return { method: expr.name.text, nameNode: expr.name };
9
+ }
10
+ return null;
11
+ }
12
+
13
+ module.exports = { isEnvCall };