@pulse-compute/provider-node 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 (38) hide show
  1. package/README.md +60 -1
  2. package/package.json +61 -6
  3. package/src/capabilities.js +56 -0
  4. package/src/compiler/assets-provider-proof.js +434 -0
  5. package/src/compiler/backend-json-request-body-proof.js +90 -0
  6. package/src/compiler/index.js +38 -0
  7. package/src/compiler/node-adapter.js +452 -0
  8. package/src/compiler/node-compiled-wasm-assets-lifecycle-proof.js +435 -0
  9. package/src/compiler/request-json-body-proof.js +181 -0
  10. package/src/compiler/route-handler-compiled-wasm-bridge.js +323 -0
  11. package/src/compiler/route-handler-effect-proof.js +215 -0
  12. package/src/compiler/route-handler-live-origin-proof.js +372 -0
  13. package/src/compiler/schema-decode-result-proof.js +180 -0
  14. package/src/compiler/schema-response-codec-proof.js +149 -0
  15. package/src/events/reference-adapter.js +145 -0
  16. package/src/index.js +57 -0
  17. package/src/javascript/assets-adapter.js +68 -0
  18. package/src/javascript/bindings-adapter.js +120 -0
  19. package/src/javascript/fetch-adapter.js +187 -0
  20. package/src/javascript/grip-broadcast.js +243 -0
  21. package/src/javascript/jwt-verifier.js +137 -0
  22. package/src/javascript/lifecycle.js +69 -0
  23. package/src/javascript/node-adapter.js +281 -0
  24. package/src/javascript/package-effects.js +78 -0
  25. package/src/javascript/runtime-host.js +165 -0
  26. package/src/javascript/source-package.js +439 -0
  27. package/src/javascript/support.js +205 -0
  28. package/src/javascript/target-support-policy.js +134 -0
  29. package/src/javascript/target.js +37 -0
  30. package/src/javascript/test-runtime.js +111 -0
  31. package/src/native/target.js +78 -0
  32. package/src/runtime/assets-provider.js +776 -0
  33. package/src/runtime/canonical-api-runtime.js +632 -0
  34. package/src/runtime/compiled-wasm-node-adapter-kv.js +15 -0
  35. package/src/runtime/index.js +17 -0
  36. package/src/runtime/jwt-verifier.js +264 -0
  37. package/src/runtime/route-handler-effects.js +757 -0
  38. package/src/toolchain.js +258 -0
@@ -0,0 +1,435 @@
1
+ 'use strict';
2
+
3
+ function loadAssetsContracts() {
4
+ try {
5
+ return require('@pulse-compute/wasm-contracts/assets/contracts');
6
+ } catch (error) {
7
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
8
+ return require('../../../../wasm/packages/contracts/src/assets/contracts.js');
9
+ }
10
+ throw error;
11
+ }
12
+ }
13
+
14
+ function loadDiagnostics() {
15
+ try {
16
+ return require('@pulse-compute/wasm-contracts/diagnostics');
17
+ } catch (error) {
18
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
19
+ return require('../../../../wasm/packages/contracts/src/diagnostics.js');
20
+ }
21
+ throw error;
22
+ }
23
+ }
24
+
25
+ const assetsContracts = loadAssetsContracts();
26
+ const { PACKAGE_VERSION, normalizeArtifact, normalizeDiagnostic, stableFileName } = loadDiagnostics();
27
+ const {
28
+ createNodeCompiledWasmAssetsLifecycleRuntime,
29
+ normalizeHeaders
30
+ } = require('../runtime/assets-provider.js');
31
+
32
+ const phaseName = 'node-compiled-wasm-assets-lifecycle-proof';
33
+
34
+ function clone(value) {
35
+ if (Array.isArray(value)) return value.map(clone);
36
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
37
+ return value;
38
+ }
39
+
40
+ function normalizePlan(input) {
41
+ const plan = input && input.artifact ? input.artifact : input;
42
+ if (!plan || typeof plan !== 'object') return undefined;
43
+ return {
44
+ ...plan,
45
+ entries: Array.isArray(plan.entries) ? plan.entries.map(clone) : []
46
+ };
47
+ }
48
+
49
+ function normalizeSidecarPlan(input) {
50
+ const plan = input && input.artifact ? input.artifact : input;
51
+ if (!plan || typeof plan !== 'object') return undefined;
52
+ return {
53
+ ...plan,
54
+ terminalAssetResponseEffects: Array.isArray(plan.terminalAssetResponseEffects) ? plan.terminalAssetResponseEffects.map(clone) : []
55
+ };
56
+ }
57
+
58
+ function makeDiagnostic(code, message, severity, details, hint) {
59
+ return normalizeDiagnostic({
60
+ phase: phaseName,
61
+ severity: severity || 'error',
62
+ code,
63
+ message,
64
+ hint: hint || (details && details.hint),
65
+ details,
66
+ loc: { file: '<node-compiled-wasm-assets-lifecycle-proof>' }
67
+ });
68
+ }
69
+
70
+ function check(name, passed, details, failures, diagnostics) {
71
+ const entry = { name, status: passed ? 'ok' : 'error', details: details || {} };
72
+ if (!passed) {
73
+ failures.push(entry);
74
+ diagnostics.push(makeDiagnostic(
75
+ assetsContracts.ASSETS_DIAGNOSTIC_CODES.NODE_COMPILED_LIFECYCLE_SMOKE_FAILED,
76
+ `Node compiled-Wasm assets lifecycle check failed: ${name}.`,
77
+ 'error',
78
+ { check: name, details: entry.details }
79
+ ));
80
+ }
81
+ return entry;
82
+ }
83
+
84
+ function headerValue(headers, name) {
85
+ const lower = String(name).toLowerCase();
86
+ const found = normalizeHeaders(headers).find(([key]) => String(key).toLowerCase() === lower);
87
+ return found ? found[1] : undefined;
88
+ }
89
+
90
+ function resultBody(result) {
91
+ if (!result) return undefined;
92
+ if (result.body !== undefined) return String(result.body);
93
+ if (result.kind === 'stream' && result.bodyStream && Array.isArray(result.bodyStream.chunks)) {
94
+ return result.bodyStream.chunks.map((chunk) => Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk)).join('');
95
+ }
96
+ return undefined;
97
+ }
98
+
99
+ function sanitizeResult(result) {
100
+ if (!result) return undefined;
101
+ return {
102
+ status: result.status,
103
+ kind: result.kind,
104
+ body: resultBody(result),
105
+ bodyLength: resultBody(result) ? resultBody(result).length : 0,
106
+ headers: normalizeHeaders(result.headers),
107
+ diagnostics: (result.diagnostics || []).map((entry) => ({ code: entry.code, severity: entry.severity, message: entry.message }))
108
+ };
109
+ }
110
+
111
+ function findEffect(effects, method) {
112
+ const upper = String(method || 'GET').toUpperCase();
113
+ return effects.find((entry) => String(entry.method || 'GET').toUpperCase() === upper) || effects[0];
114
+ }
115
+
116
+ function codeSet(result) {
117
+ return new Set((result && result.diagnostics || []).map((entry) => entry.code));
118
+ }
119
+
120
+ function negativeCaseFlag(negativeCases, name, fallback) {
121
+ if (negativeCases && Object.prototype.hasOwnProperty.call(negativeCases, name)) return negativeCases[name] === true;
122
+ return Boolean(fallback);
123
+ }
124
+
125
+ function buildNodeCompiledWasmAssetsLifecycleProof(inputs = {}) {
126
+ const cwd = inputs.cwd || process.cwd();
127
+ const generatedBy = inputs.generatedBy || PACKAGE_VERSION;
128
+ const plan = normalizePlan(inputs.assetsLoweringPlan || inputs.plan);
129
+ const sidecarPlan = normalizeSidecarPlan(inputs.assetsCompiledWasmSidecarPlan || inputs.sidecarPlan);
130
+ const diagnostics = [];
131
+ const checks = [];
132
+ const failures = [];
133
+ const negativeCases = inputs.negativeCases || {};
134
+
135
+ if (!plan) {
136
+ diagnostics.push(makeDiagnostic(
137
+ assetsContracts.ASSETS_DIAGNOSTIC_CODES.NODE_PROVIDER_PLAN_REQUIRED,
138
+ 'Node compiled-Wasm assets lifecycle proof requires an assets-lowering-plan artifact.',
139
+ 'error',
140
+ { sourceArtifact: assetsContracts.ASSETS_LOWERING_PLAN_VERSION }
141
+ ));
142
+ }
143
+ if (!sidecarPlan) {
144
+ diagnostics.push(makeDiagnostic(
145
+ assetsContracts.ASSETS_DIAGNOSTIC_CODES.SIDECAR_FILE_MISSING,
146
+ 'Node compiled-Wasm assets lifecycle proof requires an assets-compiled-wasm-sidecar-plan artifact.',
147
+ 'error',
148
+ { sourceArtifact: assetsContracts.ASSETS_COMPILED_WASM_SIDECAR_PLAN_ARTIFACT }
149
+ ));
150
+ }
151
+
152
+ const terminalEffects = sidecarPlan && sidecarPlan.terminalAssetResponseEffects || [];
153
+ const getEffect = findEffect(terminalEffects, 'GET');
154
+ const headEffect = findEffect(terminalEffects, 'HEAD');
155
+ const missingEffect = getEffect ? {
156
+ ...clone(getEffect),
157
+ id: `${getEffect.id || 'asset'}-missing`,
158
+ key: '/__pulsewasm-missing-asset__',
159
+ method: 'GET'
160
+ } : undefined;
161
+
162
+ let runtime;
163
+ let executions = [];
164
+ let storeConfigProbeCodes = new Set();
165
+ try {
166
+ if (plan && sidecarPlan) {
167
+ runtime = createNodeCompiledWasmAssetsLifecycleRuntime({
168
+ cwd,
169
+ configRoot: inputs.configRoot,
170
+ assetConfig: inputs.assetConfig,
171
+ resolvedConfig: inputs.resolvedConfig,
172
+ assetsLoweringPlan: plan,
173
+ assetsCompiledWasmSidecarPlan: sidecarPlan,
174
+ extraTerminalEffects: missingEffect ? [missingEffect] : [],
175
+ mode: inputs.mode || 'compiled-wasm-assets-lifecycle'
176
+ });
177
+ diagnostics.push(...(runtime.diagnostics || []));
178
+ executions = runtime.executeAll();
179
+ }
180
+ } catch (error) {
181
+ diagnostics.push(makeDiagnostic(
182
+ assetsContracts.ASSETS_DIAGNOSTIC_CODES.NODE_COMPILED_LIFECYCLE_SMOKE_FAILED,
183
+ 'Node compiled-Wasm assets lifecycle smoke execution failed.',
184
+ 'error',
185
+ { message: error && error.message ? error.message : String(error), stack: error && error.stack ? error.stack : undefined }
186
+ ));
187
+ }
188
+
189
+ if (plan && sidecarPlan) {
190
+ try {
191
+ const probe = createNodeCompiledWasmAssetsLifecycleRuntime({
192
+ cwd,
193
+ configRoot: inputs.configRoot,
194
+ assetsLoweringPlan: plan,
195
+ assetsCompiledWasmSidecarPlan: sidecarPlan,
196
+ mode: 'compiled-wasm-assets-lifecycle-missing-store-config-probe'
197
+ });
198
+ storeConfigProbeCodes = codeSet(probe);
199
+ } catch (error) {
200
+ if (error && Array.isArray(error.diagnostics)) storeConfigProbeCodes = new Set(error.diagnostics.map((entry) => entry.code));
201
+ }
202
+ }
203
+
204
+ const byId = new Map(executions.map((entry) => [entry.effect && entry.effect.id, entry]));
205
+ const getExecution = getEffect ? byId.get(getEffect.id) : undefined;
206
+ const headExecution = headEffect ? byId.get(headEffect.id) : undefined;
207
+ const missingExecution = missingEffect ? byId.get(missingEffect.id) : undefined;
208
+ const getResult = getExecution && getExecution.response;
209
+ const headResult = headExecution && headExecution.response;
210
+ const missingResult = missingExecution && missingExecution.response;
211
+
212
+ checks.push(check(
213
+ 'compiled_wasm_module_imports_assets_hostcalls',
214
+ Boolean(runtime && runtime.wasm.imports.some((entry) => entry.module === 'pulse_assets' && entry.name === 'pulse_assets_lookup') && runtime.wasm.imports.some((entry) => entry.module === 'pulse_assets' && entry.name === 'pulse_assets_respond')),
215
+ { imports: runtime && runtime.wasm.imports },
216
+ failures,
217
+ diagnostics
218
+ ));
219
+ checks.push(check(
220
+ 'terminal_asset_response_effect_validated',
221
+ executions.length >= terminalEffects.length && executions.every((entry) => entry.resultHandle > 0 && entry.lookup && entry.response),
222
+ { executions: executions.map((entry) => ({ id: entry.effect && entry.effect.id, resultHandle: entry.resultHandle, lookup: entry.lookup && { store: entry.lookup.store, key: entry.lookup.key, method: entry.lookup.method }, response: sanitizeResult(entry.response) })) },
223
+ failures,
224
+ diagnostics
225
+ ));
226
+ checks.push(check(
227
+ 'get_text_asset_validated',
228
+ Boolean(getResult && getResult.status === 200 && getResult.kind === 'text' && resultBody(getResult) && resultBody(getResult).length > 0),
229
+ { response: sanitizeResult(getResult) },
230
+ failures,
231
+ diagnostics
232
+ ));
233
+ checks.push(check(
234
+ 'head_asset_bodyless_validated',
235
+ Boolean(headResult && headResult.status === 200 && headResult.kind === 'empty' && headResult.body === undefined),
236
+ { response: sanitizeResult(headResult) },
237
+ failures,
238
+ diagnostics
239
+ ));
240
+ checks.push(check(
241
+ 'headers_validated',
242
+ Boolean(getResult && headerValue(getResult.headers, 'content-type') && headerValue(getResult.headers, 'x-pulsewasm-asset-key') === (getEffect && getEffect.key)),
243
+ { contentType: getResult && headerValue(getResult.headers, 'content-type'), assetKey: getResult && headerValue(getResult.headers, 'x-pulsewasm-asset-key') },
244
+ failures,
245
+ diagnostics
246
+ ));
247
+ checks.push(check(
248
+ 'cache_control_validated',
249
+ Boolean(getResult && headerValue(getResult.headers, 'cache-control') === (getEffect && getEffect.cacheControl || 'public, max-age=60')),
250
+ { expected: getEffect && getEffect.cacheControl || 'public, max-age=60', actual: getResult && headerValue(getResult.headers, 'cache-control') },
251
+ failures,
252
+ diagnostics
253
+ ));
254
+ checks.push(check(
255
+ 'missing_asset_validated',
256
+ Boolean(missingResult && missingResult.status === 404 && missingResult.kind === 'empty'),
257
+ { response: sanitizeResult(missingResult) },
258
+ failures,
259
+ diagnostics
260
+ ));
261
+ checks.push(check(
262
+ 'explicit_store_config_required',
263
+ storeConfigProbeCodes.has(assetsContracts.ASSETS_DIAGNOSTIC_CODES.STORE_CONFIG_REQUIRED),
264
+ { diagnosticCodes: Array.from(storeConfigProbeCodes).sort() },
265
+ failures,
266
+ diagnostics
267
+ ));
268
+
269
+ const sidecarRestrictions = sidecarPlan && sidecarPlan.restrictions || {};
270
+ const dynamicKeysRejected = negativeCaseFlag(negativeCases, 'dynamicKeysRejected', sidecarRestrictions.dynamicKeysSupported === false);
271
+ const handleEscapeRejected = negativeCaseFlag(negativeCases, 'handleEscapeRejected', sidecarRestrictions.handleEscapeSupported === false);
272
+ const binaryBodyReserved = negativeCaseFlag(negativeCases, 'binaryBodyReserved', sidecarRestrictions.binaryBodySupported === false);
273
+ const streamPlanOnly = negativeCaseFlag(negativeCases, 'streamBodyPlanOnly', sidecarRestrictions.streamBodyCompiledWasmReadiness === 'plan-only');
274
+
275
+ checks.push(check('dynamic_keys_rejected', dynamicKeysRejected, { diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.DYNAMIC_KEY_UNSUPPORTED }, failures, diagnostics));
276
+ checks.push(check('asset_handle_escape_rejected', handleEscapeRejected, { diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.HANDLE_ESCAPE_UNSUPPORTED }, failures, diagnostics));
277
+ checks.push(check('compiled_binary_body_reserved', binaryBodyReserved, { diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.COMPILED_BINARY_RESERVED }, failures, diagnostics));
278
+ checks.push(check('compiled_stream_body_plan_only', streamPlanOnly, { diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.COMPILED_STREAM_PLAN_ONLY }, failures, diagnostics));
279
+
280
+ const errorDiagnostics = diagnostics.filter((entry) => (entry.severity || 'error') === 'error');
281
+ const artifact = normalizeArtifact({
282
+ version: assetsContracts.ASSETS_NODE_COMPILED_WASM_LIFECYCLE_PROOF_VERSION,
283
+ generatedBy,
284
+ phase: phaseName,
285
+ artifact: assetsContracts.ASSETS_NODE_COMPILED_WASM_LIFECYCLE_PROOF_ARTIFACT,
286
+ provider: 'node',
287
+ providerPackage: '@pulse-compute/provider-node',
288
+ contractId: assetsContracts.ASSETS_CONTRACT_ID,
289
+ status: errorDiagnostics.length === 0 ? 'ok' : 'error',
290
+ sourceAssetsLoweringPlanVersion: plan && plan.version,
291
+ sourceAssetsCompiledWasmSidecarPlanVersion: sidecarPlan && sidecarPlan.version,
292
+ loweringPlanConnected: Boolean(plan),
293
+ sidecarPlanConnected: Boolean(sidecarPlan),
294
+ compiledWasmLowering: true,
295
+ compiledWasmRuntimeBehaviorImplemented: errorDiagnostics.length === 0,
296
+ terminalAssetResponseEffectValidated: checks.some((entry) => entry.name === 'terminal_asset_response_effect_validated' && entry.status === 'ok'),
297
+ getTextAssetValidated: checks.some((entry) => entry.name === 'get_text_asset_validated' && entry.status === 'ok'),
298
+ headAssetValidated: checks.some((entry) => entry.name === 'head_asset_bodyless_validated' && entry.status === 'ok'),
299
+ headersValidated: checks.some((entry) => entry.name === 'headers_validated' && entry.status === 'ok'),
300
+ cacheControlValidated: checks.some((entry) => entry.name === 'cache_control_validated' && entry.status === 'ok'),
301
+ missingAssetValidated: checks.some((entry) => entry.name === 'missing_asset_validated' && entry.status === 'ok'),
302
+ storeConfigRequired: checks.some((entry) => entry.name === 'explicit_store_config_required' && entry.status === 'ok'),
303
+ dynamicKeysRejected,
304
+ handleEscapeRejected,
305
+ binaryBodyReserved,
306
+ streamBodyReadiness: 'plan-only',
307
+ userAuthoredAsync: false,
308
+ promises: false,
309
+ asyncAwait: false,
310
+ asyncify: false,
311
+ runtime: {
312
+ version: assetsContracts.ASSETS_NODE_COMPILED_WASM_LIFECYCLE_RUNTIME_VERSION,
313
+ package: '@pulse-compute/provider-node/runtime/assets-provider',
314
+ mode: inputs.mode || 'compiled-wasm-assets-lifecycle',
315
+ actualAssemblyScriptCompilerRequired: false,
316
+ generatedInMemoryWasmModule: true
317
+ },
318
+ wasmModule: runtime ? {
319
+ byteLength: runtime.wasm.byteLength,
320
+ imports: runtime.wasm.imports,
321
+ exports: runtime.wasm.exports,
322
+ routeExports: runtime.wasm.exports.filter((entry) => entry.kind === 'function' && String(entry.name).startsWith('pulse_assets_route_')).map((entry) => entry.name)
323
+ } : undefined,
324
+ storeConfig: runtime ? {
325
+ explicit: true,
326
+ stores: runtime.storeConfig.configuredStores.map((entry) => ({
327
+ store: entry.store,
328
+ mode: entry.mode,
329
+ methods: entry.methods,
330
+ responseBody: entry.responseBody,
331
+ cacheControl: entry.cacheControl,
332
+ dir: stableFileName(entry.dir, cwd)
333
+ }))
334
+ } : undefined,
335
+ entriesConsumed: terminalEffects.map(clone),
336
+ syntheticEffects: missingEffect ? [{ id: missingEffect.id, key: missingEffect.key, method: missingEffect.method, purpose: 'missing asset deterministic 404 proof' }] : [],
337
+ smoke: {
338
+ checks,
339
+ failedChecks: failures,
340
+ executions: executions.map((entry) => ({
341
+ index: entry.index,
342
+ exportName: entry.exportName,
343
+ resultHandle: entry.resultHandle,
344
+ effect: entry.effect,
345
+ lookup: entry.lookup,
346
+ response: sanitizeResult(entry.response)
347
+ }))
348
+ },
349
+ negativeCases: {
350
+ storeConfigRequired: {
351
+ rejected: storeConfigProbeCodes.has(assetsContracts.ASSETS_DIAGNOSTIC_CODES.STORE_CONFIG_REQUIRED),
352
+ diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.STORE_CONFIG_REQUIRED
353
+ },
354
+ dynamicKey: {
355
+ rejected: dynamicKeysRejected,
356
+ diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.DYNAMIC_KEY_UNSUPPORTED,
357
+ loweringDiagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.NON_LITERAL_LOOKUP
358
+ },
359
+ handleEscape: {
360
+ rejected: handleEscapeRejected,
361
+ diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.HANDLE_ESCAPE_UNSUPPORTED
362
+ },
363
+ binaryBody: {
364
+ reserved: binaryBodyReserved,
365
+ diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.COMPILED_BINARY_RESERVED,
366
+ loweringDiagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.BINARY_BODY_RESERVED
367
+ },
368
+ streamBody: {
369
+ readiness: 'plan-only',
370
+ diagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.COMPILED_STREAM_PLAN_ONLY,
371
+ loweringDiagnostic: assetsContracts.ASSETS_DIAGNOSTIC_CODES.STREAM_PROVIDER_NOT_WIRED
372
+ }
373
+ },
374
+ policy: {
375
+ providerOwnsRuntimeBehavior: true,
376
+ compilerOrchestratesOnly: true,
377
+ packageOwnsSidecarAndLowerer: true,
378
+ sidecarPlanRequired: true,
379
+ storeConfigRequired: true,
380
+ continuationRequired: false,
381
+ directProviderProofFallback: false,
382
+ compiledWasmModuleExecuted: Boolean(runtime && executions.length > 0),
383
+ assemblyScriptInstallRequiredForProof: false
384
+ },
385
+ scope: {
386
+ methods: clone(assetsContracts.ASSETS_ALLOWED_METHODS),
387
+ payloadModes: ['text-response-body'],
388
+ localStoreMode: true,
389
+ uploads: false,
390
+ mutationApis: false,
391
+ binaryBody: false,
392
+ streamBodyReadiness: 'plan-only',
393
+ dynamicKeys: false,
394
+ handleEscape: false
395
+ },
396
+ summary: {
397
+ compiledWasmLowering: true,
398
+ compiledWasmRuntimeBehaviorImplemented: errorDiagnostics.length === 0,
399
+ terminalAssetResponseEffectValidated: checks.some((entry) => entry.name === 'terminal_asset_response_effect_validated' && entry.status === 'ok'),
400
+ getTextAssetValidated: checks.some((entry) => entry.name === 'get_text_asset_validated' && entry.status === 'ok'),
401
+ headAssetValidated: checks.some((entry) => entry.name === 'head_asset_bodyless_validated' && entry.status === 'ok'),
402
+ headersValidated: checks.some((entry) => entry.name === 'headers_validated' && entry.status === 'ok'),
403
+ cacheControlValidated: checks.some((entry) => entry.name === 'cache_control_validated' && entry.status === 'ok'),
404
+ missingAssetValidated: checks.some((entry) => entry.name === 'missing_asset_validated' && entry.status === 'ok'),
405
+ storeConfigRequired: checks.some((entry) => entry.name === 'explicit_store_config_required' && entry.status === 'ok'),
406
+ dynamicKeysRejected,
407
+ handleEscapeRejected,
408
+ binaryBodyReserved,
409
+ streamBodyReadiness: 'plan-only',
410
+ userAuthoredAsync: false,
411
+ promises: false,
412
+ asyncAwait: false,
413
+ asyncify: false,
414
+ checks: checks.length,
415
+ failedChecks: failures.length,
416
+ diagnostics: diagnostics.length,
417
+ errorDiagnostics: errorDiagnostics.length,
418
+ errors: errorDiagnostics.length
419
+ },
420
+ diagnostics
421
+ }, cwd);
422
+
423
+ return {
424
+ artifact,
425
+ diagnostics,
426
+ runtime,
427
+ smoke: artifact.smoke,
428
+ hasErrors: errorDiagnostics.length > 0
429
+ };
430
+ }
431
+
432
+ module.exports = {
433
+ phaseName,
434
+ buildNodeCompiledWasmAssetsLifecycleProof
435
+ };
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ function loadRequestJsonContracts() {
7
+ const local = path.resolve(__dirname, '../../../../wasm/packages/contracts/src/handler/request-json-body.js');
8
+ if (fs.existsSync(local)) return require(local);
9
+ return require('@pulse-compute/wasm-contracts/handler/request-json-body');
10
+ }
11
+ function loadDiagnostics() {
12
+ const local = path.resolve(__dirname, '../../../../wasm/packages/contracts/src/diagnostics.js');
13
+ if (fs.existsSync(local)) return require(local);
14
+ return require('@pulse-compute/wasm-contracts/diagnostics');
15
+ }
16
+ function loadArtifactsDir() {
17
+ const local = path.resolve(__dirname, '../../../../wasm/packages/build-support/src/artifacts-dir.js');
18
+ if (fs.existsSync(local)) return require(local);
19
+ return require('@pulse-compute/wasm-build-support/artifacts-dir');
20
+ }
21
+ const requestJson = loadRequestJsonContracts();
22
+ const { PACKAGE_VERSION, normalizeArtifact, normalizeDiagnostic } = loadDiagnostics();
23
+ const { getDefaultArtifactsDir } = loadArtifactsDir();
24
+ const { createCompiledHandlerBridgeRuntime, findCompiledHandlerWasm } = require('./route-handler-compiled-wasm-bridge.js');
25
+
26
+ const phaseName = 'node-request-json-body-proof';
27
+
28
+ function clone(value) {
29
+ if (Array.isArray(value)) return value.map(clone);
30
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
31
+ return value;
32
+ }
33
+ function normalizePlan(input) {
34
+ const plan = input && input.artifact && typeof input.artifact === 'object' ? input.artifact : input;
35
+ if (!plan || typeof plan !== 'object') return undefined;
36
+ return { ...plan, routes: Array.isArray(plan.routes) ? plan.routes.map(clone) : [] };
37
+ }
38
+ function requestJsonRoutes(plan) {
39
+ return (Array.isArray(plan && plan.routes) ? plan.routes : []).filter((route) => route && route.required);
40
+ }
41
+ function makeDiagnostic(code, message, severity, details, hint) {
42
+ return normalizeDiagnostic({ phase: phaseName, severity: severity || 'error', code, message, hint, details, loc: { file: '<node-request-json-body-proof>' } });
43
+ }
44
+ function check(name, ok, details, failures, diagnostics) {
45
+ const entry = { name, status: ok ? 'ok' : 'error', details: sanitize(details || {}) };
46
+ if (!ok) {
47
+ failures.push(entry);
48
+ diagnostics.push(makeDiagnostic(requestJson.REQUEST_JSON_BODY_DIAGNOSTICS.compiledSmokeFailed, `Node request JSON body proof check failed: ${name}.`, 'error', entry.details));
49
+ }
50
+ return entry;
51
+ }
52
+ function sanitize(value) {
53
+ if (Array.isArray(value)) return value.map(sanitize);
54
+ if (!value || typeof value !== 'object') return value;
55
+ const out = {};
56
+ for (const [key, entry] of Object.entries(value)) {
57
+ if (key === 'body') { out.body = String(entry || ''); out.bodyPreview = String(entry || '').slice(0, 240); }
58
+ else out[key] = sanitize(entry);
59
+ }
60
+ return out;
61
+ }
62
+ function runRoute(runtime, index, variant) {
63
+ const fnName = variant === 'invalid' ? `bridge_run_request_json_route_${index}_invalid` : `bridge_run_request_json_route_${index}`;
64
+ const fn = runtime.exports[fnName];
65
+ if (typeof fn !== 'function') throw new Error(`Missing request JSON bridge export ${fnName}.`);
66
+ const status = fn();
67
+ return { status, result: runtime.readResult(), bodyReadCount: runtime.requestBodyReadCount(), parseCount: runtime.requestJsonParseCount() };
68
+ }
69
+ function runNoBodyEffectRoute(runtime) {
70
+ const fn = runtime.exports.bridge_run_effect_route_0;
71
+ if (typeof fn !== 'function') return undefined;
72
+ const kind = fn();
73
+ return { kind, bodyReadCount: runtime.requestBodyReadCount(), parseCount: runtime.requestJsonParseCount(), result: runtime.readResult() };
74
+ }
75
+ function buildNodeRequestJsonBodyProof(options = {}) {
76
+ const cwd = options.cwd || process.cwd();
77
+ const outDir = options.outDir || getDefaultArtifactsDir(cwd);
78
+ const generatedBy = options.generatedBy || PACKAGE_VERSION;
79
+ const diagnostics = [];
80
+ const failures = [];
81
+ const checks = [];
82
+ const plan = normalizePlan(options.requestJsonBodyPlan || options.plan);
83
+ if (!plan) diagnostics.push(makeDiagnostic(requestJson.REQUEST_JSON_BODY_DIAGNOSTICS.planMissing, 'Node request JSON body proof requires request-json-body-plan.json.', 'error', { hint: 'Enable request-json-body-plan before requesting the Node proof.' }));
84
+ const routes = requestJsonRoutes(plan || {});
85
+ if (plan && routes.length === 0) diagnostics.push(makeDiagnostic(requestJson.REQUEST_JSON_BODY_DIAGNOSTICS.planMissing, 'Node request JSON body proof requires at least one route using ctx.req.json/parse.', 'error', { routes: (plan.routes || []).length }));
86
+ const wasmFile = findCompiledHandlerWasm(options.compiledHandlers, cwd, outDir, options.compiledHandlersCwd || options.sourceCwd);
87
+ if (!wasmFile) diagnostics.push(makeDiagnostic(requestJson.REQUEST_JSON_BODY_DIAGNOSTICS.wasmMissing, 'Node request JSON body proof requires the compiled-handler Wasm artifact.', 'error', { hint: 'Enable compiled handlers together with the request JSON body proof.' }));
88
+
89
+ let runtime;
90
+ const smoke = { results: {}, checks, failedChecks: failures };
91
+ if (wasmFile && diagnostics.filter((entry) => (entry.severity || 'error') === 'error').length === 0) {
92
+ try {
93
+ runtime = createCompiledHandlerBridgeRuntime(wasmFile);
94
+ const noBody = runNoBodyEffectRoute(runtime);
95
+ if (noBody) {
96
+ smoke.results.noBodyRoute = sanitize(noBody);
97
+ checks.push(check('request_json_not_read_on_unrelated_effect_route', noBody.bodyReadCount === 0 && noBody.parseCount === 0, noBody, failures, diagnostics));
98
+ }
99
+ for (let index = 0; index < routes.length; index += 1) {
100
+ const route = routes[index];
101
+ const valid = runRoute(runtime, index, 'valid');
102
+ smoke.results[`route${index}`] = sanitize({ route: { method: route.method, path: route.path, handlerName: route.handlerName }, valid });
103
+ const validReturnedResolveBoundary = valid.result && valid.result.kind === 'resolve';
104
+ checks.push(check(`request_json_route_${index}_valid_status`, (valid.status >= 200 && valid.status < 300) || (validReturnedResolveBoundary && valid.status === 0), valid, failures, diagnostics));
105
+ checks.push(check(`request_json_route_${index}_lazy_read_once`, valid.bodyReadCount === 1, valid, failures, diagnostics));
106
+ checks.push(check(`request_json_route_${index}_parse_once`, valid.parseCount === 1, valid, failures, diagnostics));
107
+ if (validReturnedResolveBoundary) {
108
+ checks.push(check(`request_json_route_${index}_effect_boundary_returned`, true, valid.result, failures, diagnostics));
109
+ } else {
110
+ checks.push(check(`request_json_route_${index}_body_visible`, /body-123/.test(valid.result.body || ''), valid.result, failures, diagnostics));
111
+ }
112
+ const invalid = runRoute(runtime, index, 'invalid');
113
+ smoke.results[`route${index}Invalid`] = sanitize({ route: { method: route.method, path: route.path, handlerName: route.handlerName }, invalid });
114
+ checks.push(check(`request_json_route_${index}_invalid_json_status`, invalid.status === 400, invalid, failures, diagnostics));
115
+ checks.push(check(`request_json_route_${index}_invalid_json_error`, /invalid_json/.test(invalid.result.body || ''), invalid.result, failures, diagnostics));
116
+ }
117
+ checks.push(check('request_json_module_has_no_js_handler_imports', runtime.imports.filter((entry) => entry.module === 'pulsewasm_handlers').length === 0, { imports: runtime.imports }, failures, diagnostics));
118
+ } catch (error) {
119
+ diagnostics.push(makeDiagnostic(requestJson.REQUEST_JSON_BODY_DIAGNOSTICS.compiledSmokeFailed, 'Node request JSON body proof smoke execution failed.', 'error', { message: error && error.message ? error.message : String(error), stack: error && error.stack ? error.stack : undefined }));
120
+ }
121
+ }
122
+ const errorDiagnostics = diagnostics.filter((entry) => (entry.severity || 'error') === 'error');
123
+ const compiledHandlerFiles = Array.isArray(options.compiledHandlers && options.compiledHandlers.files) ? options.compiledHandlers.files : [];
124
+ const compiledHandlerOutputs = Array.isArray(options.compiledHandlers && options.compiledHandlers.outputFiles) ? options.compiledHandlers.outputFiles : [];
125
+ const artifact = normalizeArtifact({
126
+ version: requestJson.NODE_REQUEST_JSON_BODY_PROOF_VERSION,
127
+ generatedBy,
128
+ phase: requestJson.NODE_REQUEST_JSON_BODY_PROOF_PHASE,
129
+ artifact: requestJson.NODE_REQUEST_JSON_BODY_PROOF_ARTIFACT,
130
+ contractId: requestJson.REQUEST_JSON_BODY_CONTRACT_ID,
131
+ provider: 'node',
132
+ status: errorDiagnostics.length === 0 ? 'ok' : 'error',
133
+ sourcePlanVersion: plan && plan.version,
134
+ sourcePlanArtifact: plan && plan.artifact,
135
+ requestJsonBodyPlanConnected: Boolean(plan),
136
+ compiledHandlersConnected: Boolean(options.compiledHandlers),
137
+ compiledHandlerPlanVersion: options.compiledHandlers && options.compiledHandlers.artifact && options.compiledHandlers.artifact.version,
138
+ runtime: { version: requestJson.NODE_REQUEST_JSON_BODY_RUNTIME_VERSION, package: '@pulse-compute/wasm-runtime-core-as/compiler/compiled-handlers', wasm: wasmFile ? path.relative(cwd, wasmFile).replace(/\\/g, '/') : undefined },
139
+ scope: requestJson.defaultNodeRequestJsonBodyProofPolicy(),
140
+ lazyEvaluation: JSON.parse(JSON.stringify(requestJson.REQUEST_JSON_BODY_LAZY_POLICY)),
141
+ surface: JSON.parse(JSON.stringify(requestJson.REQUEST_JSON_BODY_SURFACE)),
142
+ accessors: [...requestJson.REQUEST_JSON_BODY_ACCESSORS],
143
+ reservedAccessors: [...requestJson.REQUEST_JSON_BODY_RESERVED_ACCESSORS],
144
+ compiledWasmRuntimeImplemented: errorDiagnostics.length === 0,
145
+ nodeCompiledProofImplemented: errorDiagnostics.length === 0,
146
+ providerNetworkFetchRequired: false,
147
+ backendJsonRequestBodyImplemented: false,
148
+ binaryRequestBodyParsing: false,
149
+ streamRequestBodyParsing: false,
150
+ asyncAwait: false,
151
+ promises: false,
152
+ asyncify: false,
153
+ routesConsumed: routes.map((route, index) => ({ index, routeId: route.routeId, method: route.method, path: route.path, handlerName: route.handlerName, reads: route.reads, accessors: route.accessors })),
154
+ smoke,
155
+ summary: {
156
+ routesWithJsonBodyReads: routes.length,
157
+ compiled: Boolean(wasmFile) && errorDiagnostics.length === 0,
158
+ executed: checks.length > 0 && errorDiagnostics.length === 0,
159
+ checks: checks.length,
160
+ failedChecks: failures.length,
161
+ lazyReadValidated: checks.some((entry) => entry.name.includes('lazy_read_once') && entry.status === 'ok'),
162
+ parseCacheValidated: checks.some((entry) => entry.name.includes('parse_once') && entry.status === 'ok'),
163
+ invalidJsonValidated: checks.some((entry) => entry.name.includes('invalid_json_status') && entry.status === 'ok'),
164
+ noEagerReadValidated: checks.some((entry) => entry.name === 'request_json_not_read_on_unrelated_effect_route' && entry.status === 'ok'),
165
+ noJsHandlerImports: checks.some((entry) => entry.name === 'request_json_module_has_no_js_handler_imports' && entry.status === 'ok'),
166
+ providerBehaviorImplemented: false,
167
+ compiledWasmRuntimeImplemented: errorDiagnostics.length === 0,
168
+ binaryRequestBodyParsing: false,
169
+ streamRequestBodyParsing: false,
170
+ diagnostics: diagnostics.length,
171
+ errorDiagnostics: errorDiagnostics.length,
172
+ errors: errorDiagnostics.length
173
+ },
174
+ generatedFiles: compiledHandlerFiles.map((file) => ({ file: file.file, bytes: file.bytes, sha256: file.sha256 })),
175
+ generatedOutputs: compiledHandlerOutputs.map((file) => ({ file: file.file, kind: file.kind, bytes: file.bytes, sha256: file.sha256 })),
176
+ diagnostics
177
+ }, cwd);
178
+ return { artifact, diagnostics, failures, files: compiledHandlerFiles, outputFiles: compiledHandlerOutputs.length ? compiledHandlerOutputs : (wasmFile ? [{ file: path.relative(cwd, wasmFile).replace(/\\/g, '/'), kind: 'compiled-handler-wasm', bytes: fs.statSync(wasmFile).size }] : []), wasmFile };
179
+ }
180
+
181
+ module.exports = { phaseName, buildNodeRequestJsonBodyProof };