@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,757 @@
1
+ 'use strict';
2
+
3
+ const http = require('node:http');
4
+ const https = require('node:https');
5
+
6
+ function loadHandlerEffectContracts() {
7
+ try {
8
+ return require('@pulse-compute/wasm-contracts/handler/effects');
9
+ } catch (error) {
10
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
11
+ return require('../../../../wasm/packages/contracts/src/handler/effects.js');
12
+ }
13
+ throw error;
14
+ }
15
+ }
16
+
17
+ function loadDiagnostics() {
18
+ try {
19
+ return require('@pulse-compute/wasm-contracts/diagnostics');
20
+ } catch (error) {
21
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
22
+ return require('../../../../wasm/packages/contracts/src/diagnostics.js');
23
+ }
24
+ throw error;
25
+ }
26
+ }
27
+
28
+ const routeEffects = loadHandlerEffectContracts();
29
+ const { normalizeDiagnostic } = loadDiagnostics();
30
+
31
+ function clone(value) {
32
+ if (Array.isArray(value)) return value.map(clone);
33
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
34
+ return value;
35
+ }
36
+
37
+ function normalizePlan(input) {
38
+ const plan = input && input.artifact && typeof input.artifact === 'object' ? input.artifact : input;
39
+ if (!plan || typeof plan !== 'object') return undefined;
40
+ return {
41
+ ...plan,
42
+ routes: Array.isArray(plan.routes) ? plan.routes.map(clone) : [],
43
+ continuations: Array.isArray(plan.continuations) ? plan.continuations.map(clone) : []
44
+ };
45
+ }
46
+
47
+ function makeDiagnostic(code, message, severity, details) {
48
+ return normalizeDiagnostic({
49
+ phase: 'node-route-handler-effects',
50
+ severity: severity || 'error',
51
+ code,
52
+ message,
53
+ hint: details && details.hint,
54
+ details,
55
+ loc: { file: '<node-route-handler-effects>' }
56
+ });
57
+ }
58
+
59
+ function normalizeHeaders(headers) {
60
+ if (!headers) return [];
61
+ if (Array.isArray(headers)) return headers.filter((entry) => entry && entry[0] !== undefined && entry[1] !== undefined).map(([name, value]) => [String(name), String(value)]);
62
+ if (typeof headers[Symbol.iterator] === 'function' && typeof headers !== 'string') return Array.from(headers, ([name, value]) => [String(name), String(value)]);
63
+ return Object.entries(headers).map(([name, value]) => [String(name), String(value)]);
64
+ }
65
+
66
+ function headerValue(headers, name) {
67
+ const lower = String(name || '').toLowerCase();
68
+ const match = normalizeHeaders(headers).find(([key]) => String(key).toLowerCase() === lower);
69
+ return match ? match[1] : undefined;
70
+ }
71
+
72
+ function pathParamNames(routePath) {
73
+ return Array.from(String(routePath || '').matchAll(/:([A-Za-z_][A-Za-z0-9_]*)/g), (match) => match[1]);
74
+ }
75
+
76
+ function defaultParamsForRoute(route) {
77
+ const params = {};
78
+ for (const name of pathParamNames(route && route.path)) params[name] = 'abc';
79
+ return params;
80
+ }
81
+
82
+ function evaluateExpression(expression, context = {}) {
83
+ if (expression === undefined || expression === null) return '';
84
+ if (typeof expression === 'string' || typeof expression === 'number' || typeof expression === 'boolean') return expression;
85
+ if (typeof expression !== 'object') return String(expression);
86
+ if (expression.kind === 'literal') return String(expression.value ?? '');
87
+ if (expression.kind === 'number') return Number(expression.value || 0);
88
+ if (expression.kind === 'boolean') return Boolean(expression.value);
89
+ if (expression.kind === 'ctx.param') return String((context.params || {})[expression.name] ?? '');
90
+ if (expression.kind === 'ctx.paramI32') return Number((context.params || {})[expression.name] || 0);
91
+ if (expression.kind === 'local') return evaluateExpression(expression.value, context);
92
+ if (expression.kind === 'concat') return (expression.parts || []).map((part) => evaluateExpression(part, context)).join('');
93
+ return String(expression.value ?? '');
94
+ }
95
+
96
+
97
+ function hasHeader(headers, name) {
98
+ const lower = String(name || '').toLowerCase();
99
+ return normalizeHeaders(headers).some(([key]) => String(key).toLowerCase() === lower);
100
+ }
101
+
102
+ function jsonTextForSchemaEncode(json, context = {}) {
103
+ if (!json || typeof json !== 'object') return undefined;
104
+ if (json.kind !== 'schema-encode') return undefined;
105
+ if (context.requestJsonText !== undefined) return String(context.requestJsonText);
106
+ if (context.requestBodyText !== undefined) return String(context.requestBodyText);
107
+ if (context.requestBody !== undefined) return typeof context.requestBody === 'string' ? context.requestBody : JSON.stringify(context.requestBody);
108
+ if (context.schemaEncodeRefs && json.source && json.source.local && context.schemaEncodeRefs[json.source.local] !== undefined) {
109
+ const value = context.schemaEncodeRefs[json.source.local];
110
+ return typeof value === 'string' ? value : JSON.stringify(value);
111
+ }
112
+ return '{}';
113
+ }
114
+
115
+ function requestFromEffect(effect, context = {}) {
116
+ const request = effect && effect.request ? effect.request : {};
117
+ const headers = (request.headers || []).map((header) => [String(header.name), String(evaluateExpression(header.value, context))]);
118
+ const out = {
119
+ method: String(request.method || 'GET').toUpperCase(),
120
+ path: String(evaluateExpression(request.path, context) || '/'),
121
+ headers
122
+ };
123
+ if ((request.bodyMode === 'json' || request.json) && request.json) {
124
+ const body = jsonTextForSchemaEncode(request.json, context);
125
+ out.bodyMode = 'json';
126
+ out.schema = request.json.schema;
127
+ out.body = body;
128
+ if (!hasHeader(out.headers, 'content-type')) out.headers.push(['content-type', 'application/json; charset=utf-8']);
129
+ if (!hasHeader(out.headers, 'content-length')) out.headers.push(['content-length', String(Buffer.byteLength(body || ''))]);
130
+ }
131
+ return out;
132
+ }
133
+
134
+ function normalizeResponse(value, request) {
135
+ const method = String(request && request.method || 'GET').toUpperCase();
136
+ if (value === undefined || value === null) return undefined;
137
+ if (typeof value === 'string' || Buffer.isBuffer(value)) {
138
+ return { status: 200, kind: method === 'HEAD' ? 'empty' : 'text', headers: [['Content-Type', 'text/plain; charset=utf-8']], body: method === 'HEAD' ? '' : String(value) };
139
+ }
140
+ if (typeof value === 'object') {
141
+ const kind = String(value.kind || '').toLowerCase();
142
+ const opaque = method !== 'HEAD' && (kind === 'stream' || kind === 'opaque' || value.bodyStream !== undefined || value.bodyHandle !== undefined);
143
+ if (opaque) {
144
+ return {
145
+ status: Number(value.status || 200),
146
+ kind: 'stream',
147
+ bodyClass: 'opaque',
148
+ headers: normalizeHeaders(value.headers),
149
+ bodyStream: value.bodyStream,
150
+ bodyHandle: value.bodyHandle,
151
+ responseRef: Number.isFinite(Number(value.responseRef)) ? Number(value.responseRef) : undefined,
152
+ streamRef: Number.isFinite(Number(value.streamRef)) ? Number(value.streamRef) : undefined,
153
+ hostOwnsStream: true,
154
+ wasmOwnsBytes: false
155
+ };
156
+ }
157
+ const body = value.body !== undefined ? value.body : value.text !== undefined ? value.text : value.content;
158
+ return {
159
+ status: Number(value.status || 200),
160
+ kind: method === 'HEAD' ? 'empty' : (value.kind || 'text'),
161
+ headers: normalizeHeaders(value.headers),
162
+ body: method === 'HEAD' ? '' : (Buffer.isBuffer(body) ? body.toString('utf8') : body === undefined ? '' : String(body))
163
+ };
164
+ }
165
+ return { status: 200, kind: method === 'HEAD' ? 'empty' : 'text', headers: [['Content-Type', 'text/plain; charset=utf-8']], body: method === 'HEAD' ? '' : String(value) };
166
+ }
167
+
168
+ function createDefaultBackendsFromPlan(planInput, defaultParams = {}) {
169
+ const plan = normalizePlan(planInput) || { routes: [] };
170
+ const backends = {};
171
+ for (const route of plan.routes || []) {
172
+ const params = { ...defaultParamsForRoute(route), ...defaultParams };
173
+ for (const resolve of route.resolves || []) {
174
+ for (const effect of resolve.effects || []) {
175
+ if (!effect || effect.kind !== 'backend-fetch') continue;
176
+ const request = requestFromEffect(effect, { route, params });
177
+ if (!backends[effect.backend]) backends[effect.backend] = {};
178
+ const response = {
179
+ status: 200,
180
+ kind: request.method === 'HEAD' ? 'empty' : 'text',
181
+ headers: [
182
+ ['Content-Type', 'text/plain; charset=utf-8'],
183
+ ['X-PulseWasm-Backend', effect.backend],
184
+ ['X-PulseWasm-Effect', effect.name || '$default'],
185
+ ['X-Count', effect.name === 'posts' || request.method === 'HEAD' ? '2' : '1']
186
+ ],
187
+ body: request.method === 'HEAD' ? '' : `node-route-effect:${resolve.groupShape === 'single' ? '$default' : (effect.name || '$default')}:${effect.backend}:${request.method}:${request.path}`
188
+ };
189
+ backends[effect.backend][request.path] = response;
190
+ backends[effect.backend][`${request.method} ${request.path}`] = response;
191
+ }
192
+ }
193
+ }
194
+ return backends;
195
+ }
196
+
197
+ function responseHandle(response, metadata = {}) {
198
+ const normalized = response || { status: 500, kind: 'text', headers: [], body: '', diagnostics: [] };
199
+ const opaque = normalized.kind === 'stream' || normalized.bodyClass === 'opaque' || normalized.bodyStream !== undefined || normalized.bodyHandle !== undefined;
200
+ function bodyText() { return opaque ? '' : (normalized.body === undefined || normalized.body === null ? '' : String(normalized.body)); }
201
+ return {
202
+ status() { return Number(normalized.status || 0); },
203
+ ok() { return this.status() >= 200 && this.status() < 300; },
204
+ kind() { return opaque ? 'stream' : String(normalized.kind || 'text'); },
205
+ bodyClass() { return opaque ? 'opaque' : 'structured'; },
206
+ header(name) { return headerValue(normalized.headers, name) || ''; },
207
+ text() { return bodyText(); },
208
+ jsonText() { return bodyText(); },
209
+ json(schema) {
210
+ const text = bodyText();
211
+ let parsed;
212
+ let parseError;
213
+ try { parsed = text ? JSON.parse(text) : null; }
214
+ catch (error) { parseError = error; }
215
+ return {
216
+ ok() { return !parseError; },
217
+ errorText() { return parseError ? JSON.stringify({ error: 'schema_decode_failed', schema: String(schema || ''), message: parseError.message }) : ''; },
218
+ jsonText() { return text; },
219
+ getString(path) { const value = parsed && parsed[String(path)]; return value === undefined || value === null ? '' : String(value); },
220
+ getI32(path) { return Number.parseInt(this.getString(path), 10) || 0; },
221
+ getU32(path) { const value = Number.parseInt(this.getString(path), 10) || 0; return value < 0 ? 0 : value; },
222
+ getF64(path) { return Number.parseFloat(this.getString(path)) || 0; },
223
+ getBool(path) { return Boolean(parsed && parsed[String(path)]); },
224
+ has(path) { return Boolean(parsed && Object.prototype.hasOwnProperty.call(parsed, String(path))); },
225
+ schema: String(schema || ''),
226
+ response: this
227
+ };
228
+ },
229
+ get diagnostics() { return clone(normalized.diagnostics || []); },
230
+ toJSON() {
231
+ return {
232
+ status: this.status(),
233
+ ok: this.ok(),
234
+ kind: opaque ? 'stream' : (normalized.kind || 'text'),
235
+ bodyClass: opaque ? 'opaque' : 'structured',
236
+ request: metadata.request ? clone(metadata.request) : undefined,
237
+ backend: metadata.backend,
238
+ headers: normalizeHeaders(normalized.headers),
239
+ body: opaque ? undefined : (normalized.body === undefined ? '' : String(normalized.body)),
240
+ text: opaque ? undefined : this.text(),
241
+ bodyStream: opaque ? normalized.bodyStream : undefined,
242
+ bodyHandle: opaque ? normalized.bodyHandle : undefined,
243
+ responseRef: opaque ? normalized.responseRef : undefined,
244
+ streamRef: opaque ? normalized.streamRef : undefined,
245
+ hostOwnsStream: opaque ? true : undefined,
246
+ wasmOwnsBytes: opaque ? false : undefined,
247
+ diagnostics: clone(normalized.diagnostics || [])
248
+ };
249
+ }
250
+ };
251
+ }
252
+
253
+ function createResultFactory(responseHeaders) {
254
+ function normalizeOptions(options) {
255
+ const headers = [];
256
+ if (options && options.headers) {
257
+ for (const [name, value] of Object.entries(options.headers)) headers.push([String(name), String(value)]);
258
+ }
259
+ return headers;
260
+ }
261
+ return {
262
+ text(status, body, options) { return { status: Number(status), kind: 'text', headers: [...responseHeaders, ...normalizeOptions(options)], body: String(body ?? '') }; },
263
+ jsonText(status, body, options) { return { status: Number(status), kind: 'json-text', headers: [...responseHeaders, ...normalizeOptions(options)], body: String(body ?? '') }; },
264
+ json(schema, value, options) {
265
+ const body = value && typeof value.jsonText === 'function' ? value.jsonText() : (typeof value === 'string' ? value : JSON.stringify(value ?? null));
266
+ return { status: Number(options && options.status || 200), kind: 'json-text', schema: String(schema || ''), headers: [...responseHeaders, ...normalizeOptions(options)], body };
267
+ },
268
+ empty(status, options) { return { status: Number(status), kind: 'empty', headers: [...responseHeaders, ...normalizeOptions(options)], body: '' }; }
269
+ };
270
+ }
271
+
272
+ function createRouteEffectContinuationContext(resolved) {
273
+ const responseHeaders = [];
274
+ return {
275
+ resolved(name) {
276
+ const key = name === undefined ? '$default' : String(name);
277
+ const entry = resolved[key];
278
+ if (!entry) {
279
+ const diagnostic = makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectResolvedNameMissing, `Node route-effect continuation tried to read missing resolved effect ${JSON.stringify(key)}.`, 'error', { name: key });
280
+ return responseHandle({ status: 500, kind: 'text', headers: [], body: '', diagnostics: [diagnostic] }, { name: key });
281
+ }
282
+ return responseHandle(entry.response, { backend: entry.effect.backend, request: entry.request });
283
+ },
284
+ result: createResultFactory(responseHeaders),
285
+ response: {
286
+ header: {
287
+ set(name, value) {
288
+ const lower = String(name).toLowerCase();
289
+ for (let index = responseHeaders.length - 1; index >= 0; index -= 1) if (String(responseHeaders[index][0]).toLowerCase() === lower) responseHeaders.splice(index, 1);
290
+ responseHeaders.push([String(name), String(value)]);
291
+ },
292
+ append(name, value) { responseHeaders.push([String(name), String(value)]); },
293
+ delete(name) {
294
+ const lower = String(name).toLowerCase();
295
+ for (let index = responseHeaders.length - 1; index >= 0; index -= 1) if (String(responseHeaders[index][0]).toLowerCase() === lower) responseHeaders.splice(index, 1);
296
+ }
297
+ }
298
+ }
299
+ };
300
+ }
301
+
302
+ function routeMatches(route, selector) {
303
+ if (!route) return false;
304
+ if (!selector || Object.keys(selector).length === 0) return true;
305
+ if (selector.routeId && route.routeId === selector.routeId) return true;
306
+ if (selector.handlerName && route.handlerName === selector.handlerName) return true;
307
+ if (selector.path && route.path === selector.path) return true;
308
+ return false;
309
+ }
310
+
311
+ function createErrorResponse(status, diagnostic) {
312
+ return responseHandle({ status, kind: 'text', headers: [['Content-Type', 'text/plain; charset=utf-8']], body: '', diagnostics: [diagnostic] });
313
+ }
314
+
315
+ function createNodeRouteHandlerEffectProvider(options = {}) {
316
+ const plan = normalizePlan(options.plan || options.routeHandlerEffectPlan || options.effectPlan);
317
+ const diagnostics = [];
318
+ if (!plan) {
319
+ diagnostics.push(makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectPlanRequired, 'Node route-effect provider requires route-handler-effect-plan.json.', 'error', { hint: 'Emit route-handler-effect-plan.json before invoking the Node provider proof.' }));
320
+ }
321
+ const defaultParams = options.defaultParams || {};
322
+ const backends = options.backends || createDefaultBackendsFromPlan(plan, defaultParams);
323
+ const continuations = options.continuations || {};
324
+
325
+ function executeFetch(effect, context = {}) {
326
+ const route = context.route;
327
+ const params = { ...defaultParams, ...(context.params || {}) };
328
+ const request = requestFromEffect(effect, { route, params, requestJsonText: context.requestJsonText, requestBodyText: context.requestBodyText, requestBody: context.requestBody, schemaEncodeRefs: context.schemaEncodeRefs });
329
+ if (!routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS.includes(request.method)) {
330
+ return createErrorResponse(405, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectUnsupportedMethod, `Node route-effect provider proof only supports ${routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS.join(' / ')} backend fetch methods.`, 'error', { method: request.method, allowedMethods: routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS }));
331
+ }
332
+ const backend = backends[effect.backend];
333
+ if (!backend) {
334
+ return createErrorResponse(502, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectBackendMissing, `Node route-effect provider proof has no fixture backend named ${JSON.stringify(effect.backend)}.`, 'error', { backend: effect.backend }));
335
+ }
336
+ const value = backend[`${request.method} ${request.path}`] || backend[request.path];
337
+ const response = normalizeResponse(value, request);
338
+ if (!response) {
339
+ return createErrorResponse(404, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectBackendResponseMissing, `Node route-effect provider proof has no response fixture for ${effect.backend} ${request.path}.`, 'error', { backend: effect.backend, method: request.method, path: request.path }));
340
+ }
341
+ return responseHandle(response, { backend: effect.backend, request });
342
+ }
343
+
344
+ function executeResolve(routeOrSelector, resolveOrOptions, paramsInput) {
345
+ let route = routeOrSelector;
346
+ let resolve = resolveOrOptions;
347
+ let params = paramsInput || {};
348
+ let selector = {};
349
+ if (!route || !Array.isArray(route.resolves)) {
350
+ selector = routeOrSelector || {};
351
+ route = (plan && plan.routes || []).find((candidate) => routeMatches(candidate, selector));
352
+ resolve = route && (route.resolves || [])[selector.resolveIndex || 0];
353
+ params = selector.params || params;
354
+ }
355
+ if (!plan) return { status: 'error', diagnostics: diagnostics.map(clone), resolved: {}, finalResult: undefined };
356
+ if (!route) return { status: 'error', diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectRouteNotFound, 'Node route-effect provider proof could not find a matching planned route.', 'error', { selector: routeOrSelector })], resolved: {}, finalResult: undefined };
357
+ if (!resolve) return { status: 'error', route: { routeId: route.routeId, method: route.method, path: route.path, handlerName: route.handlerName }, diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectNoPlannedEffects, 'Node route-effect provider proof requires at least one ctx.resolve boundary for the selected route.', 'error', { routeId: route.routeId, path: route.path })], resolved: {}, finalResult: undefined };
358
+
359
+ const mergedParams = { ...defaultParamsForRoute(route), ...params };
360
+ const resolved = {};
361
+ const diagnosticsForRun = [];
362
+ for (const effect of resolve.effects || []) {
363
+ if (!effect || effect.kind !== 'backend-fetch') continue;
364
+ const request = requestFromEffect(effect, { route, params: mergedParams, requestJsonText: selector.requestJsonText, requestBodyText: selector.requestBodyText, requestBody: selector.requestBody });
365
+ const handle = executeFetch(effect, { route, params: mergedParams, requestJsonText: selector.requestJsonText, requestBodyText: selector.requestBodyText, requestBody: selector.requestBody });
366
+ const snapshot = handle.toJSON();
367
+ const name = effect.name || '$default';
368
+ resolved[name] = {
369
+ effect: clone(effect),
370
+ backend: effect.backend,
371
+ request,
372
+ status: snapshot.status,
373
+ ok: snapshot.ok,
374
+ kind: snapshot.kind,
375
+ headers: snapshot.headers,
376
+ body: snapshot.body,
377
+ diagnostics: snapshot.diagnostics,
378
+ response: { status: snapshot.status, kind: snapshot.kind, headers: snapshot.headers, body: snapshot.body, diagnostics: snapshot.diagnostics }
379
+ };
380
+ diagnosticsForRun.push(...(snapshot.diagnostics || []));
381
+ }
382
+
383
+ const continuationName = resolve.continuation && resolve.continuation.name;
384
+ const plannedContinuation = (plan.continuations || []).find((entry) => entry && entry.name === continuationName);
385
+ const continuation = continuationName ? continuations[continuationName] : undefined;
386
+ let finalResult;
387
+ if (!plannedContinuation) {
388
+ const diagnostic = makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectContinuationMissing, 'Node route-effect provider proof could not find the planned top-level continuation.', 'error', { continuation: continuationName });
389
+ diagnosticsForRun.push(diagnostic);
390
+ finalResult = { status: 500, kind: 'text', body: '', headers: [], diagnostics: [diagnostic] };
391
+ } else if (continuation) {
392
+ try {
393
+ finalResult = continuation(createRouteEffectContinuationContext(resolved));
394
+ } catch (error) {
395
+ const diagnostic = makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectSmokeFailed, `Node route-effect continuation threw during proof execution: ${error && error.message ? error.message : String(error)}.`, 'error', { continuation: continuationName });
396
+ diagnosticsForRun.push(diagnostic);
397
+ finalResult = { status: 500, kind: 'text', body: '', headers: [], diagnostics: [diagnostic] };
398
+ }
399
+ } else {
400
+ const accessNames = (plannedContinuation.resultAccesses || []).map((access) => access.name || '$default');
401
+ const names = accessNames.length > 0 ? accessNames : Object.keys(resolved);
402
+ let status = 200;
403
+ let body = '';
404
+ for (const name of names) {
405
+ const entry = resolved[name];
406
+ if (!entry) continue;
407
+ if (!entry.ok && status === 200) status = entry.status;
408
+ body += entry.body || '';
409
+ }
410
+ finalResult = { status, kind: 'text', body, headers: [['Content-Type', 'text/plain; charset=utf-8']], diagnostics: [] };
411
+ }
412
+ const finalDiagnostics = [...diagnosticsForRun, ...((finalResult && finalResult.diagnostics) || [])];
413
+ const hasErrors = finalDiagnostics.some((entry) => (entry.severity || 'error') === 'error');
414
+ return {
415
+ status: hasErrors ? 'error' : 'resolved',
416
+ provider: 'node',
417
+ runtimeVersion: routeEffects.ROUTE_HANDLER_NODE_EFFECT_RUNTIME_VERSION,
418
+ routeId: route && route.routeId,
419
+ route: route ? { routeId: route.routeId, method: route.method, path: route.path, handlerName: route.handlerName } : undefined,
420
+ groupShape: resolve.groupShape,
421
+ continuation: continuationName,
422
+ continuationPlanned: Boolean(plannedContinuation),
423
+ effectCount: (resolve.effects || []).length,
424
+ effects: Object.entries(resolved).map(([name, value]) => ({ name, backend: value.backend, request: value.request, response: { status: value.status, ok: value.ok, kind: value.kind, headers: value.headers, body: value.body, diagnostics: value.diagnostics || [] } })),
425
+ resolved,
426
+ finalResult: finalResult || { status: 204, kind: 'empty', headers: [], body: '' },
427
+ diagnostics: finalDiagnostics,
428
+ responseHandleSurface: [...routeEffects.ROUTE_HANDLER_CONTEXT_SURFACE.resolvedFetchResponse],
429
+ continuationResultAccesses: plannedContinuation ? clone(plannedContinuation.resultAccesses || []) : [],
430
+ continuationResponseMethodCalls: plannedContinuation ? clone(plannedContinuation.responseMethodCalls || []) : [],
431
+ lifecycle: {
432
+ handlerReturnedResolveRequest: true,
433
+ nodeExecutedEffectGroup: !hasErrors,
434
+ hostExecutedEffectGroup: !hasErrors,
435
+ continuationReentered: !hasErrors && Boolean(plannedContinuation),
436
+ continuationReadResolvedHandles: !hasErrors && Boolean(plannedContinuation),
437
+ compiledWasmSuspendResumeBridgeUsed: false
438
+ }
439
+ };
440
+ }
441
+
442
+ function executeRoute(selector = {}) {
443
+ return executeResolve(selector);
444
+ }
445
+
446
+ const plannedResolveBoundaries = [];
447
+ for (const route of plan && plan.routes || []) {
448
+ for (const resolve of route.resolves || []) if (resolve && Array.isArray(resolve.effects) && resolve.effects.length > 0) plannedResolveBoundaries.push({ route, resolve });
449
+ }
450
+ return {
451
+ version: routeEffects.ROUTE_HANDLER_NODE_EFFECT_RUNTIME_VERSION,
452
+ provider: 'node',
453
+ mode: options.mode || 'local-backend-fixture',
454
+ plan,
455
+ backends,
456
+ diagnostics,
457
+ plannedResolveBoundaries,
458
+ executeFetch,
459
+ executeFetchEffect: executeFetch,
460
+ executeResolve,
461
+ executeResolveBoundary: executeResolve,
462
+ executeRoute,
463
+ createContinuationContext: createRouteEffectContinuationContext
464
+ };
465
+ }
466
+
467
+
468
+ function normalizeLiveOriginsFromResolvedConfig(resolvedConfig) {
469
+ const artifact = resolvedConfig && resolvedConfig.artifact && typeof resolvedConfig.artifact === 'object' ? resolvedConfig.artifact : resolvedConfig;
470
+ const backends = artifact && artifact.runtime && artifact.runtime.capabilities && artifact.runtime.capabilities.backends;
471
+ const out = {};
472
+ for (const [name, config] of Object.entries(backends || {})) {
473
+ if (!config || typeof config !== 'object') continue;
474
+ if (!config.baseUrl && !config.origin && !config.url) continue;
475
+ out[name] = {
476
+ baseUrl: String(config.baseUrl || config.origin || config.url),
477
+ allowedMethods: Array.isArray(config.allowedMethods) ? config.allowedMethods.map((method) => String(method).toUpperCase()) : undefined,
478
+ requestBody: Array.isArray(config.requestBody) ? config.requestBody.map((mode) => String(mode)) : undefined,
479
+ timeoutMs: config.timeoutMs,
480
+ headers: normalizeHeaders(config.headers && (config.headers.inject || config.headers.default || config.headers) || []),
481
+ symbolicRef: config.symbolicRef || config.ref || undefined
482
+ };
483
+ }
484
+ return out;
485
+ }
486
+
487
+ function normalizeLiveOriginConfig(value) {
488
+ if (!value) return undefined;
489
+ if (typeof value === 'string') return { baseUrl: value };
490
+ if (typeof value !== 'object') return undefined;
491
+ const baseUrl = value.baseUrl || value.origin || value.url;
492
+ if (!baseUrl) return undefined;
493
+ return {
494
+ ...value,
495
+ baseUrl: String(baseUrl),
496
+ allowedMethods: Array.isArray(value.allowedMethods) ? value.allowedMethods.map((method) => String(method).toUpperCase()) : undefined,
497
+ requestBody: Array.isArray(value.requestBody) ? value.requestBody.map((mode) => String(mode)) : undefined,
498
+ headers: normalizeHeaders(value.headers || [])
499
+ };
500
+ }
501
+
502
+ function redactedOriginUrl(origin, options = {}) {
503
+ if (!origin) return undefined;
504
+ if (origin.symbolicRef) return origin.symbolicRef;
505
+ if (options.redactOriginUrls === false) return origin.baseUrl;
506
+ try {
507
+ const url = new URL(origin.baseUrl);
508
+ if (url.hostname === '127.0.0.1' || url.hostname === 'localhost') return '$local-reference-origin';
509
+ return `${url.protocol}//${url.hostname}`;
510
+ } catch (_) {
511
+ return '$invalid-origin-url';
512
+ }
513
+ }
514
+
515
+ function requestLiveOrigin(originInput, request, options = {}) {
516
+ const origin = normalizeLiveOriginConfig(originInput);
517
+ if (!origin) {
518
+ return Promise.resolve({
519
+ status: 502,
520
+ kind: 'text',
521
+ headers: [['Content-Type', 'text/plain; charset=utf-8']],
522
+ body: '',
523
+ diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeLiveOriginRequired, 'Node live-origin route-effect proof requires an origin base URL for the planned backend.', 'error', { backend: options.backend })]
524
+ });
525
+ }
526
+
527
+ let url;
528
+ try {
529
+ url = new URL(request.path || '/', origin.baseUrl);
530
+ } catch (error) {
531
+ return Promise.resolve({
532
+ status: 502,
533
+ kind: 'text',
534
+ headers: [['Content-Type', 'text/plain; charset=utf-8']],
535
+ body: '',
536
+ diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeLiveOriginInvalidUrl, `Node live-origin route-effect proof could not build a valid URL for backend ${JSON.stringify(options.backend)}.`, 'error', { backend: options.backend, baseUrl: redactedOriginUrl(origin, options), path: request.path, error: error && error.message ? error.message : String(error) })]
537
+ });
538
+ }
539
+
540
+ const method = String(request.method || 'GET').toUpperCase();
541
+ const headers = {};
542
+ for (const [name, value] of [...normalizeHeaders(origin.headers), ...normalizeHeaders(request.headers)]) headers[name] = value;
543
+ if (!Object.keys(headers).some((name) => String(name).toLowerCase() === 'connection')) headers.Connection = 'close';
544
+ const client = url.protocol === 'https:' ? https : http;
545
+ const timeoutMs = Number(options.timeoutMs || origin.timeoutMs || 2000);
546
+
547
+ return new Promise((resolve) => {
548
+ const req = client.request(url, { method, headers, timeout: timeoutMs, agent: false }, (res) => {
549
+ let body = '';
550
+ res.setEncoding('utf8');
551
+ res.on('data', (chunk) => { body += chunk; });
552
+ res.on('end', () => {
553
+ const responseHeaders = [];
554
+ for (const [name, value] of Object.entries(res.headers || {})) {
555
+ if (Array.isArray(value)) for (const entry of value) responseHeaders.push([name, String(entry)]);
556
+ else if (value !== undefined) responseHeaders.push([name, String(value)]);
557
+ }
558
+ resolve({
559
+ status: Number(res.statusCode || 0),
560
+ kind: method === 'HEAD' ? 'empty' : ((headerValue(responseHeaders, 'content-type') || '').includes('json') ? 'json-text' : 'text'),
561
+ headers: responseHeaders,
562
+ body: method === 'HEAD' ? '' : body,
563
+ diagnostics: []
564
+ });
565
+ });
566
+ });
567
+ req.on('timeout', () => {
568
+ req.destroy(new Error(`Node live-origin route-effect proof timed out after ${timeoutMs}ms.`));
569
+ });
570
+ req.on('error', (error) => {
571
+ resolve({
572
+ status: 502,
573
+ kind: 'text',
574
+ headers: [['Content-Type', 'text/plain; charset=utf-8']],
575
+ body: '',
576
+ diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeLiveOriginFetchFailed, `Node live-origin route-effect proof failed to fetch backend ${JSON.stringify(options.backend)}.`, 'error', { backend: options.backend, baseUrl: redactedOriginUrl(origin, options), method, path: request.path, error: error && error.message ? error.message : String(error) })]
577
+ });
578
+ });
579
+ if (request.body !== undefined) req.write(request.body);
580
+ req.end();
581
+ });
582
+ }
583
+
584
+ function createNodeLiveOriginRouteHandlerEffectProvider(options = {}) {
585
+ const plan = normalizePlan(options.plan || options.routeHandlerEffectPlan || options.effectPlan);
586
+ const diagnostics = [];
587
+ if (!plan) {
588
+ diagnostics.push(makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectPlanRequired, 'Node live-origin route-effect provider requires route-handler-effect-plan.json.', 'error', { hint: 'Emit route-handler-effect-plan.json before invoking the Node live-origin provider proof.' }));
589
+ }
590
+ const defaultParams = options.defaultParams || {};
591
+ const origins = { ...normalizeLiveOriginsFromResolvedConfig(options.resolvedConfig), ...(options.origins || options.liveOrigins || {}) };
592
+ const continuations = options.continuations || {};
593
+
594
+ async function executeFetch(effect, context = {}) {
595
+ const route = context.route;
596
+ const params = { ...defaultParams, ...(context.params || {}) };
597
+ const request = requestFromEffect(effect, { route, params, requestJsonText: context.requestJsonText, requestBodyText: context.requestBodyText, requestBody: context.requestBody, schemaEncodeRefs: context.schemaEncodeRefs });
598
+ if (!routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS.includes(request.method)) {
599
+ return createErrorResponse(405, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectUnsupportedMethod, `Node live-origin route-effect provider proof only supports ${routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS.join(' / ')} backend fetch methods.`, 'error', { method: request.method, allowedMethods: routeEffects.ROUTE_HANDLER_EFFECT_ALLOWED_METHODS }));
600
+ }
601
+ const origin = normalizeLiveOriginConfig(origins[effect.backend]);
602
+ if (!origin) {
603
+ return createErrorResponse(502, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectBackendMissing, `Node live-origin route-effect provider proof has no origin backend named ${JSON.stringify(effect.backend)}.`, 'error', { backend: effect.backend, hint: 'Provide runtime.capabilities.backends.<name>.baseUrl or pass liveOrigins to the provider proof.' }));
604
+ }
605
+ if (Array.isArray(origin.allowedMethods) && !origin.allowedMethods.includes(request.method)) {
606
+ return createErrorResponse(405, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.backendMethodUnsupported, `Node live-origin backend ${JSON.stringify(effect.backend)} does not allow method ${request.method}.`, 'error', { backend: effect.backend, method: request.method, allowedMethods: origin.allowedMethods }));
607
+ }
608
+ const bodyMode = request.bodyMode || 'none';
609
+ if (Array.isArray(origin.requestBody) && !origin.requestBody.includes(bodyMode)) {
610
+ return createErrorResponse(415, makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.backendRequestBodyModeUnsupported, `Node live-origin backend ${JSON.stringify(effect.backend)} does not allow request body mode ${bodyMode}.`, 'error', { backend: effect.backend, bodyMode, requestBody: origin.requestBody }));
611
+ }
612
+ const response = await requestLiveOrigin(origin, request, { ...options, backend: effect.backend });
613
+ return responseHandle(response, { backend: effect.backend, request });
614
+ }
615
+
616
+ async function executeResolve(routeOrSelector, resolveOrOptions, paramsInput) {
617
+ let route = routeOrSelector;
618
+ let resolve = resolveOrOptions;
619
+ let params = paramsInput || {};
620
+ let selector = {};
621
+ if (!route || !Array.isArray(route.resolves)) {
622
+ selector = routeOrSelector || {};
623
+ route = (plan && plan.routes || []).find((candidate) => routeMatches(candidate, selector));
624
+ resolve = route && (route.resolves || [])[selector.resolveIndex || 0];
625
+ params = selector.params || params;
626
+ }
627
+ if (!plan) return { status: 'error', diagnostics: diagnostics.map(clone), resolved: {}, finalResult: undefined };
628
+ if (!route) return { status: 'error', diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectRouteNotFound, 'Node live-origin route-effect provider proof could not find a matching planned route.', 'error', { selector: routeOrSelector })], resolved: {}, finalResult: undefined };
629
+ if (!resolve) return { status: 'error', route: { routeId: route.routeId, method: route.method, path: route.path, handlerName: route.handlerName }, diagnostics: [makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectNoPlannedEffects, 'Node live-origin route-effect provider proof requires at least one ctx.resolve boundary for the selected route.', 'error', { routeId: route.routeId, path: route.path })], resolved: {}, finalResult: undefined };
630
+
631
+ const mergedParams = { ...defaultParamsForRoute(route), ...params };
632
+ const diagnosticsForRun = [];
633
+ const pairs = await Promise.all((resolve.effects || []).filter((effect) => effect && effect.kind === 'backend-fetch').map(async (effect) => {
634
+ const request = requestFromEffect(effect, { route, params: mergedParams, requestJsonText: selector.requestJsonText, requestBodyText: selector.requestBodyText, requestBody: selector.requestBody });
635
+ const handle = await executeFetch(effect, { route, params: mergedParams, requestJsonText: selector.requestJsonText, requestBodyText: selector.requestBodyText, requestBody: selector.requestBody });
636
+ const snapshot = handle.toJSON();
637
+ return { effect, request, snapshot };
638
+ }));
639
+ const resolved = {};
640
+ for (const { effect, request, snapshot } of pairs) {
641
+ const name = effect.name || '$default';
642
+ resolved[name] = {
643
+ effect: clone(effect),
644
+ backend: effect.backend,
645
+ request,
646
+ status: snapshot.status,
647
+ ok: snapshot.ok,
648
+ kind: snapshot.kind,
649
+ headers: snapshot.headers,
650
+ body: snapshot.body,
651
+ diagnostics: snapshot.diagnostics,
652
+ response: { status: snapshot.status, kind: snapshot.kind, headers: snapshot.headers, body: snapshot.body, diagnostics: snapshot.diagnostics }
653
+ };
654
+ diagnosticsForRun.push(...(snapshot.diagnostics || []));
655
+ }
656
+
657
+ const continuationName = resolve.continuation && resolve.continuation.name;
658
+ const plannedContinuation = (plan.continuations || []).find((entry) => entry && entry.name === continuationName);
659
+ const continuation = continuationName ? continuations[continuationName] : undefined;
660
+ let finalResult;
661
+ if (!plannedContinuation) {
662
+ const diagnostic = makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectContinuationMissing, 'Node live-origin route-effect provider proof could not find the planned top-level continuation.', 'error', { continuation: continuationName });
663
+ diagnosticsForRun.push(diagnostic);
664
+ finalResult = { status: 500, kind: 'text', body: '', headers: [], diagnostics: [diagnostic] };
665
+ } else if (continuation) {
666
+ try {
667
+ finalResult = continuation(createRouteEffectContinuationContext(resolved));
668
+ } catch (error) {
669
+ const diagnostic = makeDiagnostic(routeEffects.ROUTE_HANDLER_EFFECT_DIAGNOSTICS.nodeEffectSmokeFailed, `Node live-origin route-effect continuation threw during proof execution: ${error && error.message ? error.message : String(error)}.`, 'error', { continuation: continuationName });
670
+ diagnosticsForRun.push(diagnostic);
671
+ finalResult = { status: 500, kind: 'text', body: '', headers: [], diagnostics: [diagnostic] };
672
+ }
673
+ } else {
674
+ const accessNames = (plannedContinuation.resultAccesses || []).map((access) => access.name || '$default');
675
+ const names = accessNames.length > 0 ? accessNames : Object.keys(resolved);
676
+ let status = 200;
677
+ let body = '';
678
+ for (const name of names) {
679
+ const entry = resolved[name];
680
+ if (!entry) continue;
681
+ if (!entry.ok && status === 200) status = entry.status;
682
+ body += entry.body || '';
683
+ }
684
+ finalResult = { status, kind: 'text', body, headers: [['Content-Type', 'text/plain; charset=utf-8']], diagnostics: [] };
685
+ }
686
+ const finalDiagnostics = [...diagnosticsForRun, ...((finalResult && finalResult.diagnostics) || [])];
687
+ const hasErrors = finalDiagnostics.some((entry) => (entry.severity || 'error') === 'error');
688
+ return {
689
+ status: hasErrors ? 'error' : 'resolved',
690
+ provider: 'node',
691
+ runtimeVersion: routeEffects.ROUTE_HANDLER_NODE_LIVE_ORIGIN_EFFECT_RUNTIME_VERSION,
692
+ routeId: route && route.routeId,
693
+ route: route ? { routeId: route.routeId, method: route.method, path: route.path, handlerName: route.handlerName } : undefined,
694
+ groupShape: resolve.groupShape,
695
+ continuation: continuationName,
696
+ continuationPlanned: Boolean(plannedContinuation),
697
+ effectCount: (resolve.effects || []).length,
698
+ effects: Object.entries(resolved).map(([name, value]) => ({ name, backend: value.backend, request: value.request, response: { status: value.status, ok: value.ok, kind: value.kind, headers: value.headers, body: value.body, diagnostics: value.diagnostics || [] } })),
699
+ resolved,
700
+ finalResult: finalResult || { status: 204, kind: 'empty', headers: [], body: '' },
701
+ diagnostics: finalDiagnostics,
702
+ responseHandleSurface: [...routeEffects.ROUTE_HANDLER_CONTEXT_SURFACE.resolvedFetchResponse],
703
+ continuationResultAccesses: plannedContinuation ? clone(plannedContinuation.resultAccesses || []) : [],
704
+ continuationResponseMethodCalls: plannedContinuation ? clone(plannedContinuation.responseMethodCalls || []) : [],
705
+ lifecycle: {
706
+ handlerReturnedResolveRequest: true,
707
+ nodeExecutedEffectGroup: !hasErrors,
708
+ hostExecutedEffectGroup: !hasErrors,
709
+ liveOriginNetworkFetch: true,
710
+ continuationReentered: !hasErrors && Boolean(plannedContinuation),
711
+ continuationReadResolvedHandles: !hasErrors && Boolean(plannedContinuation),
712
+ compiledWasmSuspendResumeBridgeUsed: false
713
+ }
714
+ };
715
+ }
716
+
717
+ async function executeRoute(selector = {}) {
718
+ return executeResolve(selector);
719
+ }
720
+
721
+ const plannedResolveBoundaries = [];
722
+ for (const route of plan && plan.routes || []) {
723
+ for (const resolve of route.resolves || []) if (resolve && Array.isArray(resolve.effects) && resolve.effects.length > 0) plannedResolveBoundaries.push({ route, resolve });
724
+ }
725
+ return {
726
+ version: routeEffects.ROUTE_HANDLER_NODE_LIVE_ORIGIN_EFFECT_RUNTIME_VERSION,
727
+ provider: 'node',
728
+ mode: options.mode || 'live-origin-http',
729
+ plan,
730
+ origins,
731
+ diagnostics,
732
+ plannedResolveBoundaries,
733
+ executeFetch,
734
+ executeFetchEffect: executeFetch,
735
+ executeResolve,
736
+ executeResolveBoundary: executeResolve,
737
+ executeRoute,
738
+ createContinuationContext: createRouteEffectContinuationContext,
739
+ originSummary: Object.keys(origins || {}).sort().map((backend) => ({ backend, baseUrl: redactedOriginUrl(normalizeLiveOriginConfig(origins[backend]), options) }))
740
+ };
741
+ }
742
+
743
+ module.exports = {
744
+ createNodeRouteHandlerEffectProvider,
745
+ createNodeRouteEffectProvider: createNodeRouteHandlerEffectProvider,
746
+ createNodeLiveOriginRouteHandlerEffectProvider,
747
+ createNodeLiveOriginRouteEffectProvider: createNodeLiveOriginRouteHandlerEffectProvider,
748
+ createRouteEffectContinuationContext,
749
+ createDefaultBackendsFromPlan,
750
+ buildDefaultBackends: createDefaultBackendsFromPlan,
751
+ normalizeLiveOriginsFromResolvedConfig,
752
+ requestLiveOrigin,
753
+ evaluateExpression,
754
+ requestFromEffect,
755
+ normalizeHeaders,
756
+ headerValue
757
+ };