@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.
Files changed (104) hide show
  1. package/README.md +53 -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 +269 -0
  9. package/src/canonical-native-compiler.js +411 -0
  10. package/src/canonical-native-plan.js +1478 -0
  11. package/src/canonical-project-compiler.js +1224 -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 +328 -0
  83. package/src/spine/canonical-handler-ir.js +336 -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 +455 -0
  93. package/src/spine/handler-ir-managed.js +1598 -0
  94. package/src/spine/handler-ir.js +797 -0
  95. package/src/spine/handler-surface-authority.js +588 -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 +545 -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 +514 -0
  102. package/src/spine/router-handler-ir.js +372 -0
  103. package/src/spine/router-topology-frontend.js +638 -0
  104. 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
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-library-kit/compiler').handlerLibraryContracts;
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-library-kit/compiler').hostCapabilities;
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-host-runtime/compiler/host-runtime-kernel');
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-runtime-core-as/compiler/integrated-compiled-app');
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-host-runtime/compiler/json-body');
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('@pulse-compute/wasm-library-kit/compiler').librarySidecars;