@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,298 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { getDefaultArtifactsDir } = require('@pulse-compute/wasm-build-support/artifacts-dir');
|
|
5
|
+
const crypto = require('node:crypto');
|
|
6
|
+
const { PACKAGE_VERSION, normalizeArtifact, stableFileName } = require('../diagnostics.js');
|
|
7
|
+
|
|
8
|
+
const HANDLER_BINDINGS_VERSION = 'pulsewasm.handler-bindings.v1';
|
|
9
|
+
|
|
10
|
+
function sha256(text) {
|
|
11
|
+
return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function tsJson(value) {
|
|
15
|
+
return JSON.stringify(value, null, 2)
|
|
16
|
+
.replace(/</g, '\\u003c')
|
|
17
|
+
.replace(/>/g, '\\u003e')
|
|
18
|
+
.replace(/&/g, '\\u0026');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function header() {
|
|
22
|
+
return [
|
|
23
|
+
'/*',
|
|
24
|
+
' * Generated by PulseWasm Phase 8B.',
|
|
25
|
+
' * Do not edit by hand.',
|
|
26
|
+
' *',
|
|
27
|
+
' * This file is generated from dispatch-table.json + handler-table.json only.',
|
|
28
|
+
' * It must not re-read user source or re-infer route semantics.',
|
|
29
|
+
' */',
|
|
30
|
+
''
|
|
31
|
+
].join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeFile(file, text) {
|
|
35
|
+
return {
|
|
36
|
+
file,
|
|
37
|
+
bytes: Buffer.byteLength(text, 'utf8'),
|
|
38
|
+
sha256: sha256(text),
|
|
39
|
+
text
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function stableIdToIdentifier(id) {
|
|
44
|
+
const cleaned = String(id || '').replace(/[^A-Za-z0-9_$]/g, '_');
|
|
45
|
+
return /^[A-Za-z_$]/.test(cleaned) ? cleaned : `handler_${cleaned}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function pathWithoutTsExtension(filePath) {
|
|
49
|
+
return filePath.replace(/\.(mts|cts|tsx|ts)$/i, '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function relativeImportSpecifier({ fromDir, toFile }) {
|
|
53
|
+
let rel = path.relative(fromDir, toFile).replace(/\\/g, '/');
|
|
54
|
+
if (!rel.startsWith('.')) rel = `./${rel}`;
|
|
55
|
+
return pathWithoutTsExtension(rel);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function findHandler(handlerTable, id) {
|
|
59
|
+
return (handlerTable?.handlers || []).find((entry) => entry.id === id);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function displayName(entry, slot) {
|
|
63
|
+
return entry?.localName || entry?.exportName || slot?.name || entry?.inlineName || entry?.id || slot?.id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function bindingDiagnosticsForSlot(slot, entry) {
|
|
67
|
+
const name = displayName(entry, slot);
|
|
68
|
+
const loc = entry?.loc || slot?.loc || { file: entry?.file || slot?.file || '<handler>' };
|
|
69
|
+
const diagnostics = [];
|
|
70
|
+
|
|
71
|
+
if (!entry) {
|
|
72
|
+
diagnostics.push({
|
|
73
|
+
pass: 'handler-bindings',
|
|
74
|
+
code: 'PULSEWASM_HANDLER_BINDING_UNKNOWN_HANDLER',
|
|
75
|
+
severity: 'error',
|
|
76
|
+
message: `Unable to find handler table entry for dispatch handler slot ${slot?.slot ?? '<unknown>'}.`,
|
|
77
|
+
hint: 'Regenerate dispatch-table.json and handler-table.json from the same build.',
|
|
78
|
+
loc
|
|
79
|
+
});
|
|
80
|
+
return diagnostics;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!entry.declaration?.topLevel) {
|
|
84
|
+
diagnostics.push({
|
|
85
|
+
pass: 'handler-bindings',
|
|
86
|
+
code: 'PULSEWASM_HANDLER_BINDING_INLINE_UNSUPPORTED',
|
|
87
|
+
severity: 'error',
|
|
88
|
+
message: `Handler "${name}" is inline and cannot be imported into generated handler-slot bindings.`,
|
|
89
|
+
hint: 'Move the handler to an exported top-level function declaration or exported const function/arrow expression.',
|
|
90
|
+
loc
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!entry.declaration?.exported) {
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
pass: 'handler-bindings',
|
|
97
|
+
code: 'PULSEWASM_HANDLER_BINDING_NOT_EXPORTED',
|
|
98
|
+
severity: 'error',
|
|
99
|
+
message: `Handler "${name}" is not exported, so generated dispatch cannot bind it by import.`,
|
|
100
|
+
hint: 'Export every handler used by PulseWasm codegen, for example `export function handler(ctx, next) {}`.',
|
|
101
|
+
loc
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!entry.exportName && entry.declaration?.exported) {
|
|
106
|
+
diagnostics.push({
|
|
107
|
+
pass: 'handler-bindings',
|
|
108
|
+
code: 'PULSEWASM_HANDLER_BINDING_EXPORT_NAME_MISSING',
|
|
109
|
+
severity: 'error',
|
|
110
|
+
message: `Handler "${name}" is marked exported but no export name was recorded.`,
|
|
111
|
+
hint: 'Use a direct named export in the same module as the route registration.',
|
|
112
|
+
loc
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return diagnostics;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildHandlerBindingsSource(bindings) {
|
|
120
|
+
const importsByFile = new Map();
|
|
121
|
+
for (const binding of bindings) {
|
|
122
|
+
const list = importsByFile.get(binding.importPath) || [];
|
|
123
|
+
list.push(binding);
|
|
124
|
+
importsByFile.set(binding.importPath, list);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const importLines = Array.from(importsByFile.entries())
|
|
128
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
129
|
+
.map(([importPath, list]) => {
|
|
130
|
+
const specifiers = list
|
|
131
|
+
.slice()
|
|
132
|
+
.sort((a, b) => a.exportName.localeCompare(b.exportName) || a.bindingName.localeCompare(b.bindingName))
|
|
133
|
+
.map((binding) => `${binding.exportName} as ${binding.bindingName}`)
|
|
134
|
+
.join(', ');
|
|
135
|
+
return `import { ${specifiers} } from ${JSON.stringify(importPath)}`;
|
|
136
|
+
})
|
|
137
|
+
.join('\n');
|
|
138
|
+
|
|
139
|
+
const bindingMeta = bindings.map((binding) => ({
|
|
140
|
+
slot: binding.slot,
|
|
141
|
+
id: binding.id,
|
|
142
|
+
name: binding.name,
|
|
143
|
+
exportName: binding.exportName,
|
|
144
|
+
bindingName: binding.bindingName,
|
|
145
|
+
roles: binding.roles,
|
|
146
|
+
kind: binding.kind,
|
|
147
|
+
file: binding.file,
|
|
148
|
+
importPath: binding.importPath,
|
|
149
|
+
signature: binding.signature
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
const entries = bindings
|
|
153
|
+
.map((binding) => ` {
|
|
154
|
+
slot: ${binding.slot},
|
|
155
|
+
id: ${JSON.stringify(binding.id)},
|
|
156
|
+
name: ${JSON.stringify(binding.name)},
|
|
157
|
+
exportName: ${JSON.stringify(binding.exportName)},
|
|
158
|
+
bindingName: ${JSON.stringify(binding.bindingName)},
|
|
159
|
+
roles: ${tsJson(binding.roles)},
|
|
160
|
+
kind: ${JSON.stringify(binding.kind)},
|
|
161
|
+
file: ${JSON.stringify(binding.file)},
|
|
162
|
+
importPath: ${JSON.stringify(binding.importPath)},
|
|
163
|
+
signature: ${tsJson(binding.signature)},
|
|
164
|
+
handler: ${binding.bindingName} as PulseHandler
|
|
165
|
+
}`)
|
|
166
|
+
.join(',\n');
|
|
167
|
+
|
|
168
|
+
return `${header()}${importLines ? `${importLines}\n\n` : ''}export type PulseNext = (...args: unknown[]) => void
|
|
169
|
+
export type PulseHandler = (...args: unknown[]) => unknown
|
|
170
|
+
|
|
171
|
+
export type BoundHandlerSlot = {
|
|
172
|
+
readonly slot: number
|
|
173
|
+
readonly id: string
|
|
174
|
+
readonly name: string
|
|
175
|
+
readonly exportName: string
|
|
176
|
+
readonly bindingName: string
|
|
177
|
+
readonly roles: readonly string[]
|
|
178
|
+
readonly kind: string
|
|
179
|
+
readonly file: string
|
|
180
|
+
readonly importPath: string
|
|
181
|
+
readonly signature: {
|
|
182
|
+
readonly async: boolean
|
|
183
|
+
readonly generator: boolean
|
|
184
|
+
readonly paramCount: number
|
|
185
|
+
readonly params: readonly string[]
|
|
186
|
+
readonly nonIdentifierParams: readonly string[]
|
|
187
|
+
readonly returnsPromise: boolean
|
|
188
|
+
}
|
|
189
|
+
readonly handler: PulseHandler
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const HANDLER_BINDINGS_META = ${tsJson(bindingMeta)} as const
|
|
193
|
+
|
|
194
|
+
export const HANDLER_SLOT_BINDINGS = [
|
|
195
|
+
${entries}
|
|
196
|
+
] as const satisfies readonly BoundHandlerSlot[]
|
|
197
|
+
|
|
198
|
+
export const HANDLER_SLOT_MAP: ReadonlyMap<number, PulseHandler> = new Map(
|
|
199
|
+
HANDLER_SLOT_BINDINGS.map((binding) => [binding.slot, binding.handler] as const)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
export const HANDLER_ID_MAP: ReadonlyMap<string, PulseHandler> = new Map(
|
|
203
|
+
HANDLER_SLOT_BINDINGS.map((binding) => [binding.id, binding.handler] as const)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
export function getHandlerBySlot(slot: number): PulseHandler | undefined {
|
|
207
|
+
return HANDLER_SLOT_MAP.get(slot)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function getHandlerById(id: string): PulseHandler | undefined {
|
|
211
|
+
return HANDLER_ID_MAP.get(id)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function assertHandlerBindingCoverage(expectedSlots: readonly number[]): void {
|
|
215
|
+
for (const slot of expectedSlots) {
|
|
216
|
+
if (!HANDLER_SLOT_MAP.has(slot)) {
|
|
217
|
+
throw new Error('Missing generated handler binding for slot ' + slot)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildTypeScriptHandlerBindings(dispatchTable, handlerTable, options = {}) {
|
|
225
|
+
const cwd = options.cwd || process.cwd();
|
|
226
|
+
const outDir = options.outDir ? path.resolve(cwd, options.outDir) : getDefaultArtifactsDir(cwd);
|
|
227
|
+
const generatedDir = path.resolve(outDir, 'generated');
|
|
228
|
+
const diagnostics = [];
|
|
229
|
+
const slots = dispatchTable?.handlerSlots || [];
|
|
230
|
+
const bindings = [];
|
|
231
|
+
|
|
232
|
+
for (const slot of slots) {
|
|
233
|
+
const entry = findHandler(handlerTable, slot.id);
|
|
234
|
+
diagnostics.push(...bindingDiagnosticsForSlot(slot, entry));
|
|
235
|
+
if (!entry || !entry.declaration?.topLevel || !entry.declaration?.exported || !entry.exportName) continue;
|
|
236
|
+
|
|
237
|
+
const sourceFile = path.resolve(cwd, entry.file);
|
|
238
|
+
const importPath = relativeImportSpecifier({ fromDir: generatedDir, toFile: sourceFile });
|
|
239
|
+
const bindingName = stableIdToIdentifier(entry.id);
|
|
240
|
+
bindings.push({
|
|
241
|
+
slot: slot.slot,
|
|
242
|
+
id: entry.id,
|
|
243
|
+
name: displayName(entry, slot),
|
|
244
|
+
exportName: entry.exportName,
|
|
245
|
+
bindingName,
|
|
246
|
+
roles: entry.roles || slot.roles || [],
|
|
247
|
+
kind: entry.kind,
|
|
248
|
+
file: stableFileName(sourceFile, cwd),
|
|
249
|
+
importPath,
|
|
250
|
+
signature: entry.signature || slot.signature || {},
|
|
251
|
+
loc: entry.loc || slot.loc
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const file = makeFile('generated/handler-slots.ts', buildHandlerBindingsSource(bindings));
|
|
256
|
+
const artifact = normalizeArtifact({
|
|
257
|
+
version: HANDLER_BINDINGS_VERSION,
|
|
258
|
+
generatedBy: options.generatedBy || PACKAGE_VERSION,
|
|
259
|
+
source: dispatchTable?.source,
|
|
260
|
+
sourceDispatchTableVersion: dispatchTable?.version,
|
|
261
|
+
handlerTableVersion: handlerTable?.version,
|
|
262
|
+
policy: {
|
|
263
|
+
phase: '8B',
|
|
264
|
+
sourceOfTruth: 'dispatch-table.json + handler-table.json',
|
|
265
|
+
reReadsSource: false,
|
|
266
|
+
reInfersRouteSemantics: false,
|
|
267
|
+
requiresExportedTopLevelHandlers: true,
|
|
268
|
+
inlineHandlersSupported: false,
|
|
269
|
+
invocationHarness: false,
|
|
270
|
+
wasmCodegen: false
|
|
271
|
+
},
|
|
272
|
+
output: {
|
|
273
|
+
language: 'typescript',
|
|
274
|
+
module: 'esm',
|
|
275
|
+
directory: 'generated',
|
|
276
|
+
files: [{ file: file.file, bytes: file.bytes, sha256: file.sha256 }]
|
|
277
|
+
},
|
|
278
|
+
bindings: bindings.map(({ loc, ...binding }) => binding),
|
|
279
|
+
summary: {
|
|
280
|
+
handlerSlots: slots.length,
|
|
281
|
+
bindings: bindings.length,
|
|
282
|
+
diagnostics: diagnostics.length,
|
|
283
|
+
files: 1
|
|
284
|
+
}
|
|
285
|
+
}, cwd);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
version: HANDLER_BINDINGS_VERSION,
|
|
289
|
+
artifact,
|
|
290
|
+
files: [file],
|
|
291
|
+
diagnostics
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = {
|
|
296
|
+
HANDLER_BINDINGS_VERSION,
|
|
297
|
+
buildTypeScriptHandlerBindings
|
|
298
|
+
};
|