@pulse-compute/wasm-compiler 0.0.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -1
- package/bin/provider-proof-composition.js +34 -0
- package/bin/pulsewasm-extract.js +20 -0
- package/package.json +60 -5
- package/src/artifacts-dir.js +13 -0
- package/src/ast-json.js +52 -0
- package/src/build-manifest.js +354 -0
- package/src/canonical-api-compiler.js +269 -0
- package/src/canonical-native-compiler.js +411 -0
- package/src/canonical-native-plan.js +1478 -0
- package/src/canonical-project-compiler.js +1224 -0
- package/src/canonical-router-compiler.js +25 -0
- package/src/cli-intents.js +1235 -0
- package/src/cli.js +1927 -0
- package/src/codegen/assemblyscript-compile.js +3 -0
- package/src/codegen/assemblyscript-core.js +3 -0
- package/src/codegen/assemblyscript-shape.js +3 -0
- package/src/codegen/assemblyscript-wasm-smoke.js +3 -0
- package/src/codegen/backend-capabilities.js +3 -0
- package/src/codegen/channel-broadcaster.js +3 -0
- package/src/codegen/compiled-handlers.js +3 -0
- package/src/codegen/compiled-wasm-runtime.js +3 -0
- package/src/codegen/config-references.js +248 -0
- package/src/codegen/dispatch-ts.js +145 -0
- package/src/codegen/effect-composition.js +331 -0
- package/src/codegen/effect-runtime.js +418 -0
- package/src/codegen/execution-harness-ts.js +467 -0
- package/src/codegen/handler-bindings-ts.js +298 -0
- package/src/codegen/handler-library-contracts.js +3 -0
- package/src/codegen/host-capabilities.js +3 -0
- package/src/codegen/host-runtime-kernel.js +3 -0
- package/src/codegen/integrated-compiled-app.js +3 -0
- package/src/codegen/json-body.js +3 -0
- package/src/codegen/library-sidecars.js +3 -0
- package/src/codegen/local-harness-ts.js +453 -0
- package/src/codegen/pulse-wrapper.js +217 -0
- package/src/codegen/request-result-headers.js +3 -0
- package/src/codegen/schema-json-compile.js +3 -0
- package/src/codegen/schema-json-sidecar-v2.js +3 -0
- package/src/codegen/schema-json-sidecar.js +3 -0
- package/src/codegen/streaming-passthrough.js +3 -0
- package/src/codegen/wasm-host-abi.js +3 -0
- package/src/codegen/wasm-host-bridge.js +3 -0
- package/src/compiled-wasm-host-runtime-kv.js +3 -0
- package/src/config-resolver.js +813 -0
- package/src/crypto-requirement-planner.js +89 -0
- package/src/definitions/config-schema.js +14 -0
- package/src/definitions/handler-roles.js +14 -0
- package/src/definitions/path-grammar.js +14 -0
- package/src/definitions/router-api.js +14 -0
- package/src/diagnostics/codes.js +14 -0
- package/src/diagnostics/reporter.js +14 -0
- package/src/diagnostics.js +14 -0
- package/src/dispatch-table.js +400 -0
- package/src/events/event-emit.js +265 -0
- package/src/events/event-topology.js +127 -0
- package/src/execution-plan.js +463 -0
- package/src/extractor.js +2816 -0
- package/src/handler-eval.js +1154 -0
- package/src/handler-table.js +326 -0
- package/src/index.js +19 -0
- package/src/javascript-application-plan.js +181 -0
- package/src/kv-provider.js +3 -0
- package/src/path-table.js +60 -0
- package/src/path.js +14 -0
- package/src/patterns/config-define.js +30 -0
- package/src/patterns/dependency-call.js +18 -0
- package/src/patterns/env-lookup.js +13 -0
- package/src/patterns/handler-reference.js +29 -0
- package/src/patterns/path-literal.js +35 -0
- package/src/patterns/result.js +15 -0
- package/src/patterns/router-chain-call.js +50 -0
- package/src/patterns/router-construction.js +19 -0
- package/src/project/package-reachability.js +782 -0
- package/src/project/reachable-graph-builder.js +992 -0
- package/src/project/reachable-graph-contract.js +54 -0
- package/src/project/reachable-graph-implementation.js +36 -0
- package/src/project/router-module-linker.js +710 -0
- package/src/project-config-compiler.js +222 -0
- package/src/project-target-support.js +500 -0
- package/src/provider-toolchain.js +299 -0
- package/src/spine/async-surface-normalizer.js +328 -0
- package/src/spine/canonical-handler-ir.js +336 -0
- package/src/spine/canonical-native-module.js +87 -0
- package/src/spine/canonical-native-plan.js +89 -0
- package/src/spine/canonical-project.js +76 -0
- package/src/spine/canonical-router.js +155 -0
- package/src/spine/canonical-source.js +165 -0
- package/src/spine/diagnostic-authority.js +290 -0
- package/src/spine/equivalence.js +262 -0
- package/src/spine/guest-unit-stage.js +87 -0
- package/src/spine/handler-ir-emitter.js +455 -0
- package/src/spine/handler-ir-managed.js +1598 -0
- package/src/spine/handler-ir.js +797 -0
- package/src/spine/handler-surface-authority.js +588 -0
- package/src/spine/package-operation-seam.js +1015 -0
- package/src/spine/pipeline.js +202 -0
- package/src/spine/plain-handler-frontend.js +545 -0
- package/src/spine/provider-requirement-authority.js +208 -0
- package/src/spine/router-control-contract.js +17 -0
- package/src/spine/router-handler-frontend.js +514 -0
- package/src/spine/router-handler-ir.js +372 -0
- package/src/spine/router-topology-frontend.js +638 -0
- package/src/stable-id.js +14 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { PACKAGE_VERSION, normalizeArtifact } = require('./diagnostics.js');
|
|
4
|
+
const { compileRoutePath, joinPaths, publicPathPattern, stripScopedWildcard } = require('./path.js');
|
|
5
|
+
const { ENTRY_KIND_CODES, EXECUTION_PLAN_VERSION, EXECUTION_POLICY } = loadContractsExecutionPlan();
|
|
6
|
+
|
|
7
|
+
function loadContractsExecutionPlan() {
|
|
8
|
+
try {
|
|
9
|
+
return require('@pulse-compute/wasm-contracts/execution-plan');
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
12
|
+
return require('../../contracts/src/execution-plan.js');
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function handlerSlotMap(dispatchTable) {
|
|
19
|
+
const map = new Map();
|
|
20
|
+
for (const slot of dispatchTable?.handlerSlots || []) {
|
|
21
|
+
map.set(slot.id, slot);
|
|
22
|
+
}
|
|
23
|
+
return map;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function routeKey(method, path, handlerId) {
|
|
27
|
+
return `${String(method || '').toUpperCase()} ${path} ${handlerId || ''}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function routeLookup(dispatchTable) {
|
|
31
|
+
const byKey = new Map();
|
|
32
|
+
const byOrder = new Map();
|
|
33
|
+
for (const route of dispatchTable?.routes || []) {
|
|
34
|
+
const key = routeKey(route.method, route.path, route.handlers?.route?.id);
|
|
35
|
+
if (!byKey.has(key)) byKey.set(key, []);
|
|
36
|
+
byKey.get(key).push(route);
|
|
37
|
+
if (Number.isInteger(route.order)) byOrder.set(route.order, route);
|
|
38
|
+
}
|
|
39
|
+
return { byKey, byOrder };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handlerUseFromRef(ref, role, slots, order = 0) {
|
|
43
|
+
if (!ref) return undefined;
|
|
44
|
+
const id = ref.id || ref.value;
|
|
45
|
+
const slot = slots.get(id);
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
handlerId: id,
|
|
49
|
+
name: ref.name || slot?.name || id,
|
|
50
|
+
role,
|
|
51
|
+
slot: Number.isInteger(slot?.slot) ? slot.slot : null,
|
|
52
|
+
order,
|
|
53
|
+
evalStatus: slot?.eval?.status,
|
|
54
|
+
unresolved: !slot
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function channelUseFromRef(ref, slots) {
|
|
59
|
+
if (!ref) return undefined;
|
|
60
|
+
if (ref.kind === 'static') {
|
|
61
|
+
return { kind: 'static', value: ref.value };
|
|
62
|
+
}
|
|
63
|
+
if (ref.kind === 'handler' || ref.kind === 'ref' || ref.id || ref.value) {
|
|
64
|
+
const id = ref.id || ref.value;
|
|
65
|
+
const slot = slots.get(id);
|
|
66
|
+
return {
|
|
67
|
+
kind: 'handler',
|
|
68
|
+
id,
|
|
69
|
+
handlerId: id,
|
|
70
|
+
name: ref.name || slot?.name || id,
|
|
71
|
+
role: 'channel',
|
|
72
|
+
slot: Number.isInteger(slot?.slot) ? slot.slot : null,
|
|
73
|
+
evalStatus: slot?.eval?.status,
|
|
74
|
+
unresolved: !slot
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function compilePattern(path, options = {}) {
|
|
81
|
+
if (!path) return undefined;
|
|
82
|
+
const compiled = compileRoutePath(path, {
|
|
83
|
+
scoped: Boolean(options.scoped),
|
|
84
|
+
allowWildcard: true
|
|
85
|
+
});
|
|
86
|
+
return publicPathPattern(compiled);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function positiveInt(value, fallback) {
|
|
90
|
+
const number = Number(value);
|
|
91
|
+
return Number.isFinite(number) && number > 0 && Math.floor(number) === number ? number : fallback;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function configTimeouts(resolvedConfig) {
|
|
95
|
+
const runtime = resolvedConfig?.runtime || resolvedConfig?.config?.runtime || {};
|
|
96
|
+
const timeouts = runtime && typeof runtime === 'object' && !Array.isArray(runtime) ? runtime.timeouts || {} : {};
|
|
97
|
+
const defaultMs = positiveInt(timeouts.defaultMs, 5000);
|
|
98
|
+
const hardMs = positiveInt(timeouts.hardMs, 30000);
|
|
99
|
+
const effectDefaultMs = positiveInt(timeouts.effectDefaultMs, 5000);
|
|
100
|
+
const schedulerResolutionMs = positiveInt(timeouts.schedulerResolutionMs, 10);
|
|
101
|
+
return {
|
|
102
|
+
defaultMs: Math.min(defaultMs, hardMs),
|
|
103
|
+
hardMs,
|
|
104
|
+
effectDefaultMs: Math.min(effectDefaultMs, hardMs),
|
|
105
|
+
schedulerResolutionMs,
|
|
106
|
+
source: 'app'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function mergeTimeout(parent, override) {
|
|
111
|
+
const base = parent || configTimeouts();
|
|
112
|
+
const raw = override || {};
|
|
113
|
+
const requestedHardMs = raw.hardMs === undefined ? base.hardMs : positiveInt(raw.hardMs, base.hardMs);
|
|
114
|
+
const hardMs = Math.min(base.hardMs, requestedHardMs);
|
|
115
|
+
const defaultMs = Math.min(hardMs, raw.defaultMs === undefined ? base.defaultMs : positiveInt(raw.defaultMs, base.defaultMs));
|
|
116
|
+
const effectDefaultMs = Math.min(hardMs, raw.effectDefaultMs === undefined ? base.effectDefaultMs : positiveInt(raw.effectDefaultMs, base.effectDefaultMs));
|
|
117
|
+
const schedulerResolutionMs = raw.schedulerResolutionMs === undefined ? base.schedulerResolutionMs : positiveInt(raw.schedulerResolutionMs, base.schedulerResolutionMs);
|
|
118
|
+
return {
|
|
119
|
+
defaultMs,
|
|
120
|
+
hardMs,
|
|
121
|
+
effectDefaultMs,
|
|
122
|
+
schedulerResolutionMs,
|
|
123
|
+
source: raw.source || 'scope',
|
|
124
|
+
inheritsFrom: base.source || 'app'
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function scopeKey(scope) {
|
|
129
|
+
return JSON.stringify({
|
|
130
|
+
channel: scope.channel || null,
|
|
131
|
+
connect: scope.connect || [],
|
|
132
|
+
disconnect: scope.disconnect || [],
|
|
133
|
+
timeout: scope.timeout || null
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function createScopeRegistry() {
|
|
138
|
+
const scopes = [];
|
|
139
|
+
const keys = new Map();
|
|
140
|
+
|
|
141
|
+
function intern(scope) {
|
|
142
|
+
const normalized = {
|
|
143
|
+
channel: scope.channel || null,
|
|
144
|
+
connect: [...(scope.connect || [])],
|
|
145
|
+
disconnect: [...(scope.disconnect || [])],
|
|
146
|
+
timeout: scope.timeout ? { ...scope.timeout } : null
|
|
147
|
+
};
|
|
148
|
+
const key = scopeKey(normalized);
|
|
149
|
+
if (keys.has(key)) return keys.get(key);
|
|
150
|
+
const id = scopes.length;
|
|
151
|
+
const entry = { id, ...normalized };
|
|
152
|
+
scopes.push(entry);
|
|
153
|
+
keys.set(key, id);
|
|
154
|
+
return id;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { scopes, intern };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function cloneScope(scope) {
|
|
161
|
+
return {
|
|
162
|
+
channel: scope.channel || null,
|
|
163
|
+
connect: [...(scope.connect || [])],
|
|
164
|
+
disconnect: [...(scope.disconnect || [])],
|
|
165
|
+
timeout: scope.timeout ? { ...scope.timeout } : null
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function makeDiagnostic(code, message, hint, details = {}, loc) {
|
|
170
|
+
return {
|
|
171
|
+
pass: 'execution-plan',
|
|
172
|
+
code,
|
|
173
|
+
severity: 'error',
|
|
174
|
+
message,
|
|
175
|
+
hint,
|
|
176
|
+
loc: loc || { file: '<execution-plan>' },
|
|
177
|
+
details
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function buildExecutionPlan(routerTreeArtifact, dispatchTable, options = {}) {
|
|
182
|
+
const cwd = options.cwd || process.cwd();
|
|
183
|
+
const diagnostics = [];
|
|
184
|
+
const slots = handlerSlotMap(dispatchTable);
|
|
185
|
+
const routes = routeLookup(dispatchTable);
|
|
186
|
+
const entries = [];
|
|
187
|
+
const scopeRegistry = createScopeRegistry();
|
|
188
|
+
const appTimeout = configTimeouts(options.resolvedConfig);
|
|
189
|
+
const timeoutOverrides = [];
|
|
190
|
+
|
|
191
|
+
function addEntry(entry) {
|
|
192
|
+
const index = entries.length;
|
|
193
|
+
entries.push({
|
|
194
|
+
index,
|
|
195
|
+
nextIndex: index + 1,
|
|
196
|
+
...entry
|
|
197
|
+
});
|
|
198
|
+
return index;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function patch(index, fields) {
|
|
202
|
+
entries[index] = { ...entries[index], ...fields };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function findRoute(op, absolutePath) {
|
|
206
|
+
const handlerId = op.handler?.id || op.handler?.value;
|
|
207
|
+
const key = routeKey(op.method, absolutePath, handlerId);
|
|
208
|
+
const candidates = routes.byKey.get(key) || [];
|
|
209
|
+
if (candidates.length === 1) return candidates[0];
|
|
210
|
+
if (candidates.length > 1) {
|
|
211
|
+
return candidates.find((candidate) => candidate.order === op.order) || candidates[0];
|
|
212
|
+
}
|
|
213
|
+
if (routes.byOrder.has(op.order)) return routes.byOrder.get(op.order);
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function absoluteScopedPath(prefix, opPath) {
|
|
218
|
+
const joined = joinPaths(prefix || '/', stripScopedWildcard(opPath || '/'));
|
|
219
|
+
return joined === '/' ? '/*' : `${joined}/*`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function visitRouter(router, prefix, inheritedScope, routerPath) {
|
|
223
|
+
const scope = cloneScope(inheritedScope);
|
|
224
|
+
const pathForRouter = routerPath.concat(router.name);
|
|
225
|
+
const childrenByOrder = new Map();
|
|
226
|
+
for (const child of router.children || []) {
|
|
227
|
+
childrenByOrder.set(child.order, child);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
for (const op of router.ops || []) {
|
|
231
|
+
const scopeBefore = scopeRegistry.intern(scope);
|
|
232
|
+
|
|
233
|
+
if (op.kind === 'timeout') {
|
|
234
|
+
const before = scopeBefore;
|
|
235
|
+
const previous = scope.timeout ? { ...scope.timeout } : { ...appTimeout };
|
|
236
|
+
scope.timeout = mergeTimeout(previous, { ...(op.value || {}), source: 'scope' });
|
|
237
|
+
const after = scopeRegistry.intern(scope);
|
|
238
|
+
timeoutOverrides.push({
|
|
239
|
+
order: op.order,
|
|
240
|
+
router: router.name,
|
|
241
|
+
routerPath: pathForRouter,
|
|
242
|
+
beforeScopeId: before,
|
|
243
|
+
afterScopeId: after,
|
|
244
|
+
timeout: scope.timeout,
|
|
245
|
+
loc: op.loc
|
|
246
|
+
});
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (op.kind === 'channel') {
|
|
251
|
+
const before = scopeBefore;
|
|
252
|
+
const channel = channelUseFromRef(op.value, slots);
|
|
253
|
+
scope.channel = channel || null;
|
|
254
|
+
const after = scopeRegistry.intern(scope);
|
|
255
|
+
addEntry({
|
|
256
|
+
kind: 'channel',
|
|
257
|
+
kindCode: ENTRY_KIND_CODES.channel,
|
|
258
|
+
order: op.order,
|
|
259
|
+
router: router.name,
|
|
260
|
+
routerPath: pathForRouter,
|
|
261
|
+
scopeId: before,
|
|
262
|
+
nextScopeId: after,
|
|
263
|
+
channel,
|
|
264
|
+
loc: op.loc
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (op.kind === 'lifecycle') {
|
|
270
|
+
const before = scopeBefore;
|
|
271
|
+
const handler = handlerUseFromRef(op.handler, `lifecycle:${op.event}`, slots, (op.event === 'connect' ? scope.connect : scope.disconnect).length);
|
|
272
|
+
if (op.event === 'connect') scope.connect.push(handler);
|
|
273
|
+
if (op.event === 'disconnect') scope.disconnect.push(handler);
|
|
274
|
+
const after = scopeRegistry.intern(scope);
|
|
275
|
+
addEntry({
|
|
276
|
+
kind: 'lifecycle',
|
|
277
|
+
kindCode: ENTRY_KIND_CODES.lifecycle,
|
|
278
|
+
order: op.order,
|
|
279
|
+
router: router.name,
|
|
280
|
+
routerPath: pathForRouter,
|
|
281
|
+
scopeId: before,
|
|
282
|
+
nextScopeId: after,
|
|
283
|
+
event: op.event,
|
|
284
|
+
handler,
|
|
285
|
+
loc: op.loc
|
|
286
|
+
});
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (op.kind === 'mount') {
|
|
291
|
+
const child = childrenByOrder.get(op.order);
|
|
292
|
+
const mountPath = joinPaths(prefix || '/', stripScopedWildcard(op.path || '/'));
|
|
293
|
+
const mountPattern = compilePattern(mountPath, { scoped: true });
|
|
294
|
+
const mountIndex = addEntry({
|
|
295
|
+
kind: 'mount',
|
|
296
|
+
kindCode: ENTRY_KIND_CODES.mount,
|
|
297
|
+
order: op.order,
|
|
298
|
+
router: router.name,
|
|
299
|
+
routerPath: pathForRouter,
|
|
300
|
+
path: mountPath,
|
|
301
|
+
pattern: mountPattern,
|
|
302
|
+
scopeId: scopeBefore,
|
|
303
|
+
childRouter: op.router,
|
|
304
|
+
childStartIndex: null,
|
|
305
|
+
parentContinueIndex: null,
|
|
306
|
+
loc: op.loc
|
|
307
|
+
});
|
|
308
|
+
const childStartIndex = entries.length;
|
|
309
|
+
if (child && child.router) {
|
|
310
|
+
visitRouter(child.router, mountPath, scope, pathForRouter);
|
|
311
|
+
} else {
|
|
312
|
+
diagnostics.push(makeDiagnostic(
|
|
313
|
+
'PULSEWASM_EXECUTION_PLAN_MISSING_MOUNT_CHILD',
|
|
314
|
+
`Mounted router "${op.router}" was not available while building the execution plan.`,
|
|
315
|
+
'Run router tree resolution before execution-plan generation.',
|
|
316
|
+
{ router: op.router, order: op.order },
|
|
317
|
+
op.loc
|
|
318
|
+
));
|
|
319
|
+
}
|
|
320
|
+
const parentContinueIndex = entries.length;
|
|
321
|
+
patch(mountIndex, {
|
|
322
|
+
childStartIndex: childStartIndex === parentContinueIndex ? parentContinueIndex : childStartIndex,
|
|
323
|
+
parentContinueIndex,
|
|
324
|
+
nextIndex: parentContinueIndex
|
|
325
|
+
});
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (op.kind === 'use') {
|
|
330
|
+
const scoped = Boolean(op.path);
|
|
331
|
+
const absolutePath = scoped ? absoluteScopedPath(prefix || '/', op.path) : undefined;
|
|
332
|
+
const handler = handlerUseFromRef(op.handler, 'middleware', slots, 0);
|
|
333
|
+
addEntry({
|
|
334
|
+
kind: 'use',
|
|
335
|
+
kindCode: ENTRY_KIND_CODES.use,
|
|
336
|
+
order: op.order,
|
|
337
|
+
router: router.name,
|
|
338
|
+
routerPath: pathForRouter,
|
|
339
|
+
path: scoped ? stripScopedWildcard(absolutePath) : null,
|
|
340
|
+
pattern: scoped ? compilePattern(stripScopedWildcard(absolutePath), { scoped: true }) : null,
|
|
341
|
+
scoped,
|
|
342
|
+
scopeId: scopeBefore,
|
|
343
|
+
handler,
|
|
344
|
+
loc: op.loc
|
|
345
|
+
});
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (op.kind === 'error') {
|
|
350
|
+
const handler = handlerUseFromRef(op.handler, 'error', slots, 0);
|
|
351
|
+
addEntry({
|
|
352
|
+
kind: 'error',
|
|
353
|
+
kindCode: ENTRY_KIND_CODES.error,
|
|
354
|
+
order: op.order,
|
|
355
|
+
router: router.name,
|
|
356
|
+
routerPath: pathForRouter,
|
|
357
|
+
scopeId: scopeBefore,
|
|
358
|
+
handler,
|
|
359
|
+
loc: op.loc
|
|
360
|
+
});
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (op.route || op.kind === 'get' || op.kind === 'post') {
|
|
365
|
+
const absolutePath = joinPaths(prefix || '/', op.path || '/');
|
|
366
|
+
const route = findRoute(op, absolutePath);
|
|
367
|
+
if (!route) {
|
|
368
|
+
diagnostics.push(makeDiagnostic(
|
|
369
|
+
'PULSEWASM_EXECUTION_PLAN_ROUTE_NOT_FOUND',
|
|
370
|
+
`Route ${op.method || op.kind.toUpperCase()} ${absolutePath} was not found in dispatch-table.json.`,
|
|
371
|
+
'Run route-plan, stable-ids, and dispatch-table before execution-plan generation.',
|
|
372
|
+
{ method: op.method, path: absolutePath, handlerId: op.handler?.id, order: op.order },
|
|
373
|
+
op.loc
|
|
374
|
+
));
|
|
375
|
+
}
|
|
376
|
+
addEntry({
|
|
377
|
+
kind: 'route',
|
|
378
|
+
kindCode: ENTRY_KIND_CODES.route,
|
|
379
|
+
order: op.order,
|
|
380
|
+
router: router.name,
|
|
381
|
+
routerPath: pathForRouter,
|
|
382
|
+
method: op.method,
|
|
383
|
+
path: absolutePath,
|
|
384
|
+
pattern: route?.pattern || compilePattern(absolutePath),
|
|
385
|
+
runtimeId: route?.runtimeId ?? null,
|
|
386
|
+
stableId: route?.stableId,
|
|
387
|
+
routeId: route?.runtimeId ?? null,
|
|
388
|
+
handler: route?.handlers?.route || handlerUseFromRef(op.handler, 'route', slots, 0),
|
|
389
|
+
scopeId: scopeBefore,
|
|
390
|
+
routePlan: route ? {
|
|
391
|
+
middleware: route.handlers?.middleware || [],
|
|
392
|
+
error: route.handlers?.error || [],
|
|
393
|
+
lifecycle: route.handlers?.lifecycle || { connect: [], disconnect: [] },
|
|
394
|
+
channel: route.handlers?.channel
|
|
395
|
+
} : undefined,
|
|
396
|
+
loc: op.loc
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const root = routerTreeArtifact?.tree || routerTreeArtifact;
|
|
403
|
+
if (!root) {
|
|
404
|
+
diagnostics.push(makeDiagnostic(
|
|
405
|
+
'PULSEWASM_EXECUTION_PLAN_MISSING_TREE',
|
|
406
|
+
'Cannot build execution plan without a resolved router tree.',
|
|
407
|
+
'Run router tree resolution before execution-plan generation.'
|
|
408
|
+
));
|
|
409
|
+
} else {
|
|
410
|
+
visitRouter(root, '/', { channel: null, connect: [], disconnect: [], timeout: appTimeout }, []);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
414
|
+
if (!Number.isInteger(entries[i].nextIndex)) entries[i].nextIndex = i + 1;
|
|
415
|
+
if (entries[i].nextIndex > entries.length) entries[i].nextIndex = entries.length;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const executableEntries = entries.filter((entry) => entry.kind === 'use' || entry.kind === 'route' || entry.kind === 'error');
|
|
419
|
+
const summary = {
|
|
420
|
+
entries: entries.length,
|
|
421
|
+
executableEntries: executableEntries.length,
|
|
422
|
+
routes: entries.filter((entry) => entry.kind === 'route').length,
|
|
423
|
+
middleware: entries.filter((entry) => entry.kind === 'use').length,
|
|
424
|
+
errors: entries.filter((entry) => entry.kind === 'error').length,
|
|
425
|
+
mounts: entries.filter((entry) => entry.kind === 'mount').length,
|
|
426
|
+
scopeTransitions: entries.filter((entry) => entry.kind === 'channel' || entry.kind === 'lifecycle').length + timeoutOverrides.length,
|
|
427
|
+
timeoutOverrides: timeoutOverrides.length,
|
|
428
|
+
scopes: scopeRegistry.scopes.length,
|
|
429
|
+
diagnostics: diagnostics.length
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const executionPlan = normalizeArtifact({
|
|
433
|
+
version: EXECUTION_PLAN_VERSION,
|
|
434
|
+
generatedBy: options.generatedBy || PACKAGE_VERSION,
|
|
435
|
+
source: dispatchTable?.source || routerTreeArtifact?.source,
|
|
436
|
+
entryRouter: routerTreeArtifact?.entryRouter || root?.name,
|
|
437
|
+
policy: EXECUTION_POLICY,
|
|
438
|
+
routePlanVersion: dispatchTable?.routePlanVersion,
|
|
439
|
+
dispatchTableVersion: dispatchTable?.version,
|
|
440
|
+
pathPolicy: dispatchTable?.pathPolicy,
|
|
441
|
+
entryKindCodes: ENTRY_KIND_CODES,
|
|
442
|
+
timeoutPolicy: {
|
|
443
|
+
appDefault: appTimeout,
|
|
444
|
+
scopeOverridesAreStaticMetadata: true,
|
|
445
|
+
scopeMayTightenButNotExceedAppHardDeadline: true,
|
|
446
|
+
hardDeadlineCannotBeExtended: true,
|
|
447
|
+
assetsAreNotV1EffectKind: true
|
|
448
|
+
},
|
|
449
|
+
timeoutOverrides,
|
|
450
|
+
scopes: scopeRegistry.scopes,
|
|
451
|
+
entries,
|
|
452
|
+
summary
|
|
453
|
+
}, cwd);
|
|
454
|
+
|
|
455
|
+
return { executionPlan, diagnostics };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
module.exports = {
|
|
459
|
+
EXECUTION_PLAN_VERSION,
|
|
460
|
+
EXECUTION_POLICY,
|
|
461
|
+
ENTRY_KIND_CODES,
|
|
462
|
+
buildExecutionPlan
|
|
463
|
+
};
|