@pulse-compute/wasm-contracts 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 (97) hide show
  1. package/README.md +35 -2
  2. package/package.json +124 -6
  3. package/src/assets/contracts.js +296 -0
  4. package/src/config/provider-resolution.js +54 -0
  5. package/src/config/runtime-provider-kv.js +73 -0
  6. package/src/config/symbolic-refs.js +192 -0
  7. package/src/crypto/contracts.js +647 -0
  8. package/src/definitions/config-schema.js +30 -0
  9. package/src/definitions/handler-roles.js +66 -0
  10. package/src/definitions/path-grammar.js +11 -0
  11. package/src/definitions/router-api.js +149 -0
  12. package/src/diagnostics/codes.js +42 -0
  13. package/src/diagnostics/reporter.js +20 -0
  14. package/src/diagnostics.js +139 -0
  15. package/src/dispatch-table.js +23 -0
  16. package/src/entities/catalog.d.ts +7 -0
  17. package/src/entities/catalog.js +87 -0
  18. package/src/entities/contracts.d.ts +95 -0
  19. package/src/entities/contracts.js +122 -0
  20. package/src/entities/json-rpc.d.ts +27 -0
  21. package/src/entities/json-rpc.js +69 -0
  22. package/src/entities/runtime.d.ts +67 -0
  23. package/src/entities/runtime.js +288 -0
  24. package/src/events/contracts.js +456 -0
  25. package/src/execution-plan.js +42 -0
  26. package/src/grip/contracts.js +106 -0
  27. package/src/handler/backend-json-request-body.js +58 -0
  28. package/src/handler/beta-lifecycle-golden.js +97 -0
  29. package/src/handler/canonical-native-plan.js +143 -0
  30. package/src/handler/canonical-native-runtime.js +168 -0
  31. package/src/handler/canonical-runtime.js +13 -0
  32. package/src/handler/context.js +131 -0
  33. package/src/handler/ctx-contract.js +71 -0
  34. package/src/handler/doctor-readiness-aggregator.js +67 -0
  35. package/src/handler/effects.js +446 -0
  36. package/src/handler/io-lifecycle.js +134 -0
  37. package/src/handler/lifecycle-authoring-alignment.js +56 -0
  38. package/src/handler/lifecycle-readiness-aggregate.js +93 -0
  39. package/src/handler/reference-node-lifecycle-golden.js +73 -0
  40. package/src/handler/request-json-body.js +147 -0
  41. package/src/handler/runtime-api.js +471 -0
  42. package/src/handler/schema-decode-result.js +135 -0
  43. package/src/handler/schema-response-codec.js +141 -0
  44. package/src/handler/surface-contract.js +601 -0
  45. package/src/handler/types-contract.js +51 -0
  46. package/src/handler-table.js +28 -0
  47. package/src/host/backend-capabilities.js +24 -0
  48. package/src/host/capabilities.js +273 -0
  49. package/src/host/channel-broadcaster.js +60 -0
  50. package/src/host/json-body.js +80 -0
  51. package/src/host/request-result-headers.js +38 -0
  52. package/src/host/streaming-passthrough.js +90 -0
  53. package/src/host/wasm-host-abi.js +154 -0
  54. package/src/index.js +75 -0
  55. package/src/jwt/contracts.js +427 -0
  56. package/src/library/api-surface.js +99 -0
  57. package/src/library/contracts.js +273 -0
  58. package/src/library/manifest.js +317 -0
  59. package/src/library/second-package-owned-lowering.js +39 -0
  60. package/src/library/sidecars.js +147 -0
  61. package/src/logging.js +89 -0
  62. package/src/package/package-contract.js +975 -0
  63. package/src/package/package-contract.schema.json +303 -0
  64. package/src/package/runtime-pulse-live.js +100 -0
  65. package/src/path-table.js +12 -0
  66. package/src/path.js +275 -0
  67. package/src/project/config-factory.js +48 -0
  68. package/src/project/config-plan.js +409 -0
  69. package/src/project/config-runtime.js +46 -0
  70. package/src/project/javascript-application.js +212 -0
  71. package/src/project/javascript-core-capabilities.js +44 -0
  72. package/src/project/native-optimization.js +9 -0
  73. package/src/project/package-ownership.js +242 -0
  74. package/src/project/package-product-projection.js +171 -0
  75. package/src/project/reachable-graph-identity.js +219 -0
  76. package/src/project/reachable-graph-projections.js +279 -0
  77. package/src/project/reachable-graph-resolver.js +303 -0
  78. package/src/project/reachable-graph-v2.js +430 -0
  79. package/src/project/reachable-graph-v2.schema.json +285 -0
  80. package/src/project/reachable-graph.js +199 -0
  81. package/src/project/reachable-graph.schema.json +222 -0
  82. package/src/project/target-support-evidence.js +559 -0
  83. package/src/provider/canonical-provider.js +239 -0
  84. package/src/provider/final-wasm-policy.js +87 -0
  85. package/src/provider/toolchain.js +771 -0
  86. package/src/route-plan.js +43 -0
  87. package/src/s3/contracts.js +12 -0
  88. package/src/schema-json/compile.js +63 -0
  89. package/src/schema-json/contracts.js +10 -0
  90. package/src/schema-json/generic-parser.js +62 -0
  91. package/src/schema-json/registry.js +359 -0
  92. package/src/schema-json/semantic-trace.js +241 -0
  93. package/src/schema-json/v1.js +65 -0
  94. package/src/schema-json/v2.js +110 -0
  95. package/src/sha256.js +104 -0
  96. package/src/spine.js +381 -0
  97. package/src/stable-id.js +142 -0
@@ -0,0 +1,601 @@
1
+ 'use strict';
2
+
3
+ const HANDLER_SURFACE_CONTRACT_VERSION = 'pulse.handler-surface.v4';
4
+ const HANDLER_SURFACE_CONTRACT_ID = 'pulse.handler-surface';
5
+
6
+ const HANDLER_SURFACE_CLASSES = Object.freeze([
7
+ 'handler-wrapper',
8
+ 'sync',
9
+ 'effect',
10
+ 'terminal-transfer',
11
+ 'package-owned',
12
+ 'javascript-only'
13
+ ]);
14
+
15
+ const AWAIT_POLICIES = Object.freeze([
16
+ 'not-applicable',
17
+ 'forbidden',
18
+ 'synchronous-statement-only',
19
+ 'optional-redundant-warning',
20
+ 'required-when-consumed',
21
+ 'return-adopted-or-required',
22
+ 'manifest-owned',
23
+ 'native-boundary'
24
+ ]);
25
+
26
+ const HANDLER_AUTHORING_POLICY_VERSION = 'pulse.handler-authoring-policy.v1';
27
+ const HANDLER_AUTHORING_MODES = Object.freeze({
28
+ ASYNC_REQUIRED: 'async-required',
29
+ SYNCHRONOUS_COMPATIBILITY: 'synchronous-compatibility'
30
+ });
31
+
32
+ const HANDLER_SURFACE_DIAGNOSTICS = Object.freeze({
33
+ handlerAsyncRequired: 'PULSE_HANDLER_ASYNC_REQUIRED',
34
+ redundantSyncAwait: 'PULSE_AWAIT_SYNC_REDUNDANT',
35
+ logAwaitForbidden: 'PULSE_LOG_AWAIT_FORBIDDEN',
36
+ effectAwaitRequired: 'PULSE_EFFECT_AWAIT_REQUIRED',
37
+ nextTerminal: 'PULSE_ROUTER_NEXT_TERMINAL',
38
+ nativeAwaitUnsupported: 'PULSE_NATIVE_AWAIT_UNSUPPORTED',
39
+ schemaRequired: 'PULSE_SCHEMA_REQUIRED',
40
+ unsupportedSurface: 'PULSE_HANDLER_SURFACE_UNSUPPORTED'
41
+ });
42
+
43
+ function freezeList(values) {
44
+ return Object.freeze(Array.from(values || []));
45
+ }
46
+
47
+ function surface(definition) {
48
+ return Object.freeze({
49
+ ...definition,
50
+ publicForms: freezeList(definition.publicForms),
51
+ validPositions: freezeList(definition.validPositions),
52
+ targetSupport: Object.freeze({
53
+ javascript: definition.targetSupport?.javascript !== false,
54
+ native: definition.targetSupport?.native !== false
55
+ })
56
+ });
57
+ }
58
+
59
+ const HANDLER_SURFACE_DEFINITIONS = Object.freeze([
60
+ surface({
61
+ id: 'handler.managed-async-wrapper',
62
+ class: 'handler-wrapper',
63
+ canonicalOperation: 'handler.managed',
64
+ publicForms: ['async handler', 'async middleware', 'async error handler'],
65
+ awaitPolicy: 'not-applicable',
66
+ validPositions: ['declaration'],
67
+ targetSupport: { javascript: true, native: true },
68
+ nativeBehavior: 'erase-wrapper',
69
+ status: 'supported'
70
+ }),
71
+ surface({
72
+ id: 'router.next',
73
+ class: 'terminal-transfer',
74
+ canonicalOperation: 'router.transfer.normal-or-error',
75
+ publicForms: ['next()', 'next(error)'],
76
+ awaitPolicy: 'forbidden',
77
+ validPositions: ['return'],
78
+ targetSupport: { javascript: true, native: true },
79
+ nativeBehavior: 'emit-router-transfer',
80
+ status: 'existing'
81
+ }),
82
+ surface({
83
+ id: 'ctx.param',
84
+ class: 'sync',
85
+ canonicalOperation: 'context.param.string',
86
+ publicForms: ['ctx.param(name)'],
87
+ awaitPolicy: 'optional-redundant-warning',
88
+ validPositions: ['expression', 'return', 'condition'],
89
+ targetSupport: { javascript: true, native: true },
90
+ nativeBehavior: 'direct-context-lookup',
91
+ status: 'existing-alias-normalization'
92
+ }),
93
+ surface({
94
+ id: 'ctx.req.method',
95
+ class: 'sync',
96
+ canonicalOperation: 'context.request.method',
97
+ publicForms: ['ctx.req.method'],
98
+ awaitPolicy: 'optional-redundant-warning',
99
+ validPositions: ['expression', 'return', 'condition'],
100
+ targetSupport: { javascript: true, native: true },
101
+ nativeBehavior: 'direct-request-frame-lookup',
102
+ status: 'existing-alias-normalization'
103
+ }),
104
+ surface({
105
+ id: 'ctx.req.url',
106
+ class: 'sync',
107
+ canonicalOperation: 'context.request.url',
108
+ publicForms: ['ctx.req.url'],
109
+ awaitPolicy: 'optional-redundant-warning',
110
+ validPositions: ['expression', 'return', 'condition'],
111
+ targetSupport: { javascript: true, native: true },
112
+ nativeBehavior: 'direct-request-frame-lookup',
113
+ status: 'supported'
114
+ }),
115
+ surface({
116
+ id: 'ctx.req.path',
117
+ class: 'sync',
118
+ canonicalOperation: 'context.request.path',
119
+ publicForms: ['ctx.req.path'],
120
+ awaitPolicy: 'optional-redundant-warning',
121
+ validPositions: ['expression', 'return', 'condition'],
122
+ targetSupport: { javascript: true, native: true },
123
+ nativeBehavior: 'direct-request-frame-lookup',
124
+ status: 'existing-alias-normalization'
125
+ }),
126
+ surface({
127
+ id: 'ctx.req.header',
128
+ class: 'sync',
129
+ canonicalOperation: 'context.request.header.first',
130
+ publicForms: ['ctx.req.header(name)'],
131
+ awaitPolicy: 'optional-redundant-warning',
132
+ validPositions: ['expression', 'return', 'condition'],
133
+ targetSupport: { javascript: true, native: true },
134
+ nativeBehavior: 'direct-request-header-lookup',
135
+ status: 'existing-alias-normalization'
136
+ }),
137
+ surface({
138
+ id: 'ctx.req.headers',
139
+ class: 'sync',
140
+ canonicalOperation: 'context.request.headers.snapshot',
141
+ publicForms: ['ctx.req.headers'],
142
+ awaitPolicy: 'optional-redundant-warning',
143
+ validPositions: ['expression', 'return'],
144
+ targetSupport: { javascript: true, native: true },
145
+ nativeBehavior: 'immutable-request-header-snapshot',
146
+ status: 'supported'
147
+ }),
148
+ surface({
149
+ id: 'ctx.event.type',
150
+ class: 'sync',
151
+ canonicalOperation: 'context.event.type',
152
+ publicForms: ['ctx.event.type'],
153
+ awaitPolicy: 'optional-redundant-warning',
154
+ validPositions: ['expression', 'condition'],
155
+ targetSupport: { javascript: true, native: true },
156
+ nativeBehavior: 'compile-time exact event selection literal',
157
+ status: 'supported'
158
+ }),
159
+ surface({
160
+ id: 'ctx.event.payload',
161
+ class: 'sync',
162
+ canonicalOperation: 'context.event.payload',
163
+ publicForms: ['ctx.event.payload'],
164
+ awaitPolicy: 'optional-redundant-warning',
165
+ validPositions: ['expression', 'condition'],
166
+ targetSupport: { javascript: true, native: true },
167
+ nativeBehavior: 'host-owned immutable schema-validated value handle',
168
+ schemaPolicy: 'registration-owned',
169
+ status: 'supported'
170
+ }),
171
+ surface({
172
+ id: 'ctx.state.get',
173
+ class: 'sync',
174
+ canonicalOperation: 'context.state.get.string',
175
+ publicForms: ['ctx.state.get(key)'],
176
+ awaitPolicy: 'optional-redundant-warning',
177
+ validPositions: ['expression', 'return', 'condition'],
178
+ targetSupport: { javascript: true, native: true },
179
+ nativeBehavior: 'request-local-string-table-get',
180
+ status: 'restore-public-surface'
181
+ }),
182
+ surface({
183
+ id: 'ctx.state.set',
184
+ class: 'sync',
185
+ canonicalOperation: 'context.state.set.string',
186
+ publicForms: ['ctx.state.set(key, value)'],
187
+ awaitPolicy: 'optional-redundant-warning',
188
+ validPositions: ['statement'],
189
+ targetSupport: { javascript: true, native: true },
190
+ nativeBehavior: 'request-local-string-table-set',
191
+ status: 'restore-public-surface'
192
+ }),
193
+ ...['error', 'warn', 'info', 'debug'].map((level) => surface({
194
+ id: `ctx.log.${level}`,
195
+ class: 'sync',
196
+ canonicalOperation: `logging.${level}`,
197
+ publicForms: [`ctx.log.${level}(message)`],
198
+ awaitPolicy: 'synchronous-statement-only',
199
+ validPositions: ['statement'],
200
+ targetSupport: { javascript: true, native: true },
201
+ nativeBehavior: 'emit-synchronous-log-or-prune-by-resolved-reporting-level',
202
+ status: 'supported'
203
+ })),
204
+ surface({
205
+ id: 'ctx.json',
206
+ class: 'sync',
207
+ canonicalOperation: 'result.json',
208
+ publicForms: ['ctx.json(value, options?)'],
209
+ awaitPolicy: 'optional-redundant-warning',
210
+ validPositions: ['return'],
211
+ targetSupport: { javascript: true, native: true },
212
+ nativeBehavior: 'construct-canonical-result',
213
+ status: 'existing-alias-normalization'
214
+ }),
215
+ surface({
216
+ id: 'ctx.text',
217
+ class: 'sync',
218
+ canonicalOperation: 'result.text',
219
+ publicForms: ['ctx.text(value, options?)'],
220
+ awaitPolicy: 'optional-redundant-warning',
221
+ validPositions: ['return'],
222
+ targetSupport: { javascript: true, native: true },
223
+ nativeBehavior: 'construct-canonical-result',
224
+ status: 'existing-alias-normalization'
225
+ }),
226
+ surface({
227
+ id: 'ctx.response',
228
+ class: 'sync',
229
+ canonicalOperation: 'result.response',
230
+ publicForms: ['ctx.response(input?)'],
231
+ awaitPolicy: 'optional-redundant-warning',
232
+ validPositions: ['return'],
233
+ targetSupport: { javascript: true, native: true },
234
+ nativeBehavior: 'construct-canonical-result',
235
+ status: 'existing-alias-normalization'
236
+ }),
237
+ surface({
238
+ id: 'ctx.req.text',
239
+ class: 'effect',
240
+ canonicalOperation: 'effect.request-body.text',
241
+ publicForms: ['ctx.req.text()'],
242
+ awaitPolicy: 'required-when-consumed',
243
+ validPositions: ['await-expression', 'return'],
244
+ targetSupport: { javascript: true, native: true },
245
+ nativeBehavior: 'emit-effect-and-continuation',
246
+ status: 'supported'
247
+ }),
248
+ surface({
249
+ id: 'ctx.req.json.schema',
250
+ class: 'effect',
251
+ canonicalOperation: 'effect.request-body.json.schema',
252
+ publicForms: ["ctx.req.json<T>('schema.id')"],
253
+ awaitPolicy: 'required-when-consumed',
254
+ validPositions: ['await-expression', 'return'],
255
+ targetSupport: { javascript: true, native: true },
256
+ nativeBehavior: 'emit-body-effect-and-specialized-decode',
257
+ schemaPolicy: 'literal-schema-required',
258
+ status: 'supported'
259
+ }),
260
+ surface({
261
+ id: 'ctx.req.json.generic',
262
+ class: 'effect',
263
+ canonicalOperation: 'effect.request-body.json.generic',
264
+ publicForms: ['ctx.req.json<T>()'],
265
+ awaitPolicy: 'required-when-consumed',
266
+ validPositions: ['await-expression', 'return'],
267
+ targetSupport: { javascript: true, native: true },
268
+ nativeBehavior: 'emit-body-effect-and-generic-parser',
269
+ schemaPolicy: 'allowed-only-when-pulse-strict-false',
270
+ status: 'supported-when-nonstrict'
271
+ }),
272
+ surface({
273
+ id: 'ctx.parallel',
274
+ class: 'effect',
275
+ canonicalOperation: 'effect.group.parallel',
276
+ publicForms: ['ctx.parallel({ key: pulseEffect, ... })'],
277
+ awaitPolicy: 'required-when-consumed',
278
+ validPositions: ['await-expression'],
279
+ targetSupport: { javascript: true, native: true },
280
+ nativeBehavior: 'emit-explicit-effect-group-and-continuation',
281
+ shapePolicy: 'static-nonempty-object-literal-with-fixed-non-index-string-keys-and-pulse-effect-values',
282
+ status: 'supported'
283
+ }),
284
+ surface({
285
+ id: 'ctx.fetch.projected',
286
+ class: 'effect',
287
+ canonicalOperation: 'effect.backend-fetch.projected',
288
+ publicForms: ['ctx.fetch(url, init?).json<T>(schemaId?)', 'ctx.fetch(url, init?).text()'],
289
+ awaitPolicy: 'required-when-consumed',
290
+ validPositions: ['await-expression', 'return'],
291
+ targetSupport: { javascript: true, native: true },
292
+ nativeBehavior: 'emit-fetch-effect-projection-and-continuation',
293
+ status: 'supported'
294
+ }),
295
+ surface({
296
+ id: 'ctx.fetch.opaque-return',
297
+ class: 'effect',
298
+ canonicalOperation: 'effect.backend-fetch.opaque-return',
299
+ publicForms: ['return ctx.fetch(url, init?)'],
300
+ awaitPolicy: 'return-adopted-or-required',
301
+ validPositions: ['return', 'await-expression'],
302
+ targetSupport: { javascript: true, native: true },
303
+ nativeBehavior: 'emit-terminal-fetch-effect',
304
+ status: 'supported'
305
+ }),
306
+ surface({
307
+ id: 'ctx.config.get',
308
+ class: 'effect',
309
+ canonicalOperation: 'effect.binding.config.get',
310
+ publicForms: ['ctx.config.get(name)'],
311
+ awaitPolicy: 'required-when-consumed',
312
+ validPositions: ['await-expression', 'return'],
313
+ targetSupport: { javascript: true, native: true },
314
+ nativeBehavior: 'emit-config-binding-effect',
315
+ status: 'supported'
316
+ }),
317
+ surface({
318
+ id: 'ctx.secret.get',
319
+ class: 'effect',
320
+ canonicalOperation: 'effect.binding.secret.get',
321
+ publicForms: ['ctx.secret.get(name)'],
322
+ awaitPolicy: 'required-when-consumed',
323
+ validPositions: ['await-expression', 'return'],
324
+ targetSupport: { javascript: true, native: true },
325
+ nativeBehavior: 'emit-secret-binding-effect',
326
+ redaction: 'required',
327
+ status: 'supported'
328
+ }),
329
+ surface({
330
+ id: 'ctx.kv.get',
331
+ class: 'effect',
332
+ canonicalOperation: 'effect.kv.get',
333
+ publicForms: ["ctx.kv<T>('namespace').get(key)"],
334
+ awaitPolicy: 'required-when-consumed',
335
+ validPositions: ['await-expression', 'return'],
336
+ targetSupport: { javascript: true, native: true },
337
+ nativeBehavior: 'emit-kv-get-effect',
338
+ status: 'supported'
339
+ }),
340
+ surface({
341
+ id: 'ctx.kv.put',
342
+ class: 'effect',
343
+ canonicalOperation: 'effect.kv.put',
344
+ publicForms: ["ctx.kv<T>('namespace').put(key, value)"],
345
+ awaitPolicy: 'required-when-consumed',
346
+ validPositions: ['await-expression', 'return'],
347
+ targetSupport: { javascript: true, native: true },
348
+ nativeBehavior: 'emit-kv-put-effect',
349
+ status: 'supported'
350
+ }),
351
+ ...['getVersioned', 'insertIfAbsent', 'compareAndSwap'].map((method) => surface({
352
+ id: `ctx.kv.${method}`, class: 'effect', canonicalOperation: `effect.kv.${method}`,
353
+ publicForms: [`ctx.kv<T>('namespace').${method}(${method === 'getVersioned' ? 'key' : method === 'compareAndSwap' ? 'key, generation, value' : 'key, value'})`],
354
+ awaitPolicy: 'required-when-consumed', validPositions: ['await-expression', 'parallel-member'],
355
+ targetSupport: { javascript: true, native: true }, nativeBehavior: 'emit-conditional-kv-effect-and-continuation',
356
+ redaction: 'key-generation-value-required', status: 'supported'
357
+ })),
358
+ surface({
359
+ id: 'ctx.emit',
360
+ class: 'effect',
361
+ canonicalOperation: 'effect.event.emit',
362
+ publicForms: ["ctx.emit(type, { schema: schemaId, payload })", "ctx.emit(type, { schema: null })"],
363
+ awaitPolicy: 'required-when-consumed',
364
+ validPositions: ['await-expression', 'parallel-member'],
365
+ targetSupport: { javascript: true, native: true },
366
+ nativeBehavior: 'emit-canonical-event-effect-and-continuation',
367
+ schemaPolicy: 'literal-type-and-schema-required',
368
+ redaction: 'payload-required',
369
+ status: 'supported'
370
+ }),
371
+ surface({
372
+ id: 'package.operation',
373
+ class: 'package-owned',
374
+ canonicalOperation: 'package.operation',
375
+ publicForms: ['manifest-declared-package-operation'],
376
+ awaitPolicy: 'manifest-owned',
377
+ validPositions: ['manifest-declared'],
378
+ targetSupport: { javascript: true, native: true },
379
+ nativeBehavior: 'delegate-canonical-package-operation-to-trusted-lowerer',
380
+ status: 'existing-seam'
381
+ }),
382
+ surface({
383
+ id: 'javascript.await',
384
+ class: 'javascript-only',
385
+ canonicalOperation: 'javascript.await',
386
+ publicForms: ['await arbitraryLibraryCall()'],
387
+ awaitPolicy: 'native-boundary',
388
+ validPositions: ['await-expression'],
389
+ targetSupport: { javascript: true, native: false },
390
+ nativeBehavior: 'record-first-native-eligibility-boundary',
391
+ status: 'supported-diagnostic'
392
+ })
393
+ ]);
394
+
395
+ function buildHandlerSurfaceRegistry(definitions = HANDLER_SURFACE_DEFINITIONS) {
396
+ const byId = new Map();
397
+ const byCanonicalOperation = new Map();
398
+ for (const definition of definitions) {
399
+ if (!definition || typeof definition !== 'object') throw new TypeError('Handler surface definition must be an object.');
400
+ if (!definition.id || typeof definition.id !== 'string') throw new TypeError('Handler surface definition requires an id.');
401
+ if (!HANDLER_SURFACE_CLASSES.includes(definition.class)) throw new TypeError(`Handler surface ${definition.id} has unsupported class ${definition.class}.`);
402
+ if (!AWAIT_POLICIES.includes(definition.awaitPolicy)) throw new TypeError(`Handler surface ${definition.id} has unsupported await policy ${definition.awaitPolicy}.`);
403
+ if (byId.has(definition.id)) throw new TypeError(`Duplicate handler surface id: ${definition.id}`);
404
+ if (byCanonicalOperation.has(definition.canonicalOperation)) {
405
+ throw new TypeError(`Duplicate canonical handler operation: ${definition.canonicalOperation}`);
406
+ }
407
+ byId.set(definition.id, definition);
408
+ byCanonicalOperation.set(definition.canonicalOperation, definition);
409
+ }
410
+ return Object.freeze({
411
+ version: HANDLER_SURFACE_CONTRACT_VERSION,
412
+ contractId: HANDLER_SURFACE_CONTRACT_ID,
413
+ definitions: Object.freeze(Array.from(definitions)),
414
+ get(id) {
415
+ return byId.get(id);
416
+ },
417
+ getByCanonicalOperation(operation) {
418
+ return byCanonicalOperation.get(operation);
419
+ },
420
+ has(id) {
421
+ return byId.has(id);
422
+ }
423
+ });
424
+ }
425
+
426
+ const DEFAULT_HANDLER_SURFACE_REGISTRY = buildHandlerSurfaceRegistry();
427
+
428
+ function classifyHandlerSurface(surfaceId, options = {}) {
429
+ const definition = DEFAULT_HANDLER_SURFACE_REGISTRY.get(surfaceId);
430
+ if (!definition) {
431
+ return Object.freeze({
432
+ ok: false,
433
+ surfaceId,
434
+ classification: 'unsupported',
435
+ severity: 'error',
436
+ code: HANDLER_SURFACE_DIAGNOSTICS.unsupportedSurface,
437
+ message: `Unsupported Pulse handler surface: ${surfaceId}`
438
+ });
439
+ }
440
+
441
+ if (surfaceId === 'ctx.req.json.generic' && options.strict !== false) {
442
+ return Object.freeze({
443
+ ok: false,
444
+ surfaceId,
445
+ definition,
446
+ classification: definition.class,
447
+ severity: 'error',
448
+ code: HANDLER_SURFACE_DIAGNOSTICS.schemaRequired,
449
+ message: 'Schema-less request JSON requires pulse.strict to be false.'
450
+ });
451
+ }
452
+
453
+ return Object.freeze({
454
+ ok: true,
455
+ surfaceId,
456
+ definition,
457
+ classification: definition.class,
458
+ canonicalOperation: definition.canonicalOperation,
459
+ nativeEligible: definition.targetSupport.native
460
+ });
461
+ }
462
+
463
+ function classifyAwaitUse(surfaceId, options = {}) {
464
+ const classified = classifyHandlerSurface(surfaceId, options);
465
+ if (!classified.ok) return classified;
466
+ const definition = classified.definition;
467
+ const awaited = options.awaited === true;
468
+ const consumed = options.consumed !== false;
469
+ const position = options.position || (awaited ? 'await-expression' : 'expression');
470
+
471
+ if (definition.awaitPolicy === 'not-applicable') {
472
+ return Object.freeze({ ...classified, action: 'preserve-handler-shape', severity: 'none', code: null });
473
+ }
474
+ if (definition.awaitPolicy === 'forbidden') {
475
+ if (awaited || position !== 'return') {
476
+ return Object.freeze({
477
+ ...classified,
478
+ ok: false,
479
+ action: 'reject',
480
+ severity: 'error',
481
+ code: HANDLER_SURFACE_DIAGNOSTICS.nextTerminal,
482
+ message: 'next() is a terminal Router control transfer and must be returned directly.'
483
+ });
484
+ }
485
+ return Object.freeze({ ...classified, action: 'emit-terminal-transfer', severity: 'none', code: null });
486
+ }
487
+ if (definition.awaitPolicy === 'synchronous-statement-only') {
488
+ if (awaited || position !== 'statement') {
489
+ return Object.freeze({
490
+ ...classified,
491
+ ok: false,
492
+ action: 'reject',
493
+ severity: 'error',
494
+ code: HANDLER_SURFACE_DIAGNOSTICS.logAwaitForbidden,
495
+ message: `${surfaceId} is synchronous, returns void, and must be used as a standalone statement.`
496
+ });
497
+ }
498
+ return Object.freeze({ ...classified, action: 'lower-sync-statement', severity: 'none', code: null });
499
+ }
500
+ if (definition.awaitPolicy === 'optional-redundant-warning') {
501
+ if (awaited) {
502
+ return Object.freeze({
503
+ ...classified,
504
+ action: 'erase-await',
505
+ severity: 'warning',
506
+ code: HANDLER_SURFACE_DIAGNOSTICS.redundantSyncAwait,
507
+ message: `await is redundant for synchronous Pulse surface ${surfaceId}.`
508
+ });
509
+ }
510
+ return Object.freeze({ ...classified, action: 'lower-sync', severity: 'none', code: null });
511
+ }
512
+ if (definition.awaitPolicy === 'required-when-consumed') {
513
+ if (!awaited && consumed && position !== 'return') {
514
+ return Object.freeze({
515
+ ...classified,
516
+ ok: false,
517
+ action: 'reject',
518
+ severity: 'error',
519
+ code: HANDLER_SURFACE_DIAGNOSTICS.effectAwaitRequired,
520
+ message: `Pulse effect ${surfaceId} must be awaited before its value is consumed.`
521
+ });
522
+ }
523
+ return Object.freeze({
524
+ ...classified,
525
+ action: position === 'return' && !awaited ? 'lower-returned-effect' : 'lower-effect',
526
+ severity: 'none',
527
+ code: null
528
+ });
529
+ }
530
+ if (definition.awaitPolicy === 'return-adopted-or-required') {
531
+ if (!awaited && position !== 'return') {
532
+ return Object.freeze({
533
+ ...classified,
534
+ ok: false,
535
+ action: 'reject',
536
+ severity: 'error',
537
+ code: HANDLER_SURFACE_DIAGNOSTICS.effectAwaitRequired,
538
+ message: `Pulse effect ${surfaceId} must be awaited unless returned as the terminal handler result.`
539
+ });
540
+ }
541
+ return Object.freeze({
542
+ ...classified,
543
+ action: awaited ? 'lower-effect' : 'lower-terminal-effect',
544
+ severity: 'none',
545
+ code: null
546
+ });
547
+ }
548
+ if (definition.awaitPolicy === 'manifest-owned') {
549
+ return Object.freeze({ ...classified, action: 'delegate-to-package-contract', severity: 'none', code: null });
550
+ }
551
+ if (definition.awaitPolicy === 'native-boundary') {
552
+ return Object.freeze({
553
+ ...classified,
554
+ action: 'record-native-boundary',
555
+ severity: 'error-for-native',
556
+ code: HANDLER_SURFACE_DIAGNOSTICS.nativeAwaitUnsupported,
557
+ message: 'Native Pulse supports await only on recognized Pulse effects.'
558
+ });
559
+ }
560
+ throw new TypeError(`Unhandled await policy ${definition.awaitPolicy}.`);
561
+ }
562
+
563
+ function handlerSurfaceContract(definitions) {
564
+ return Object.freeze({
565
+ version: HANDLER_SURFACE_CONTRACT_VERSION,
566
+ contractId: HANDLER_SURFACE_CONTRACT_ID,
567
+ classes: HANDLER_SURFACE_CLASSES,
568
+ awaitPolicies: AWAIT_POLICIES,
569
+ diagnostics: HANDLER_SURFACE_DIAGNOSTICS,
570
+ definitions,
571
+ policies: Object.freeze({
572
+ managedHandlersAsyncShaped: true,
573
+ nativePromiseRuntime: false,
574
+ nativeAsyncify: false,
575
+ redundantSyncAwaitIsFatal: false,
576
+ arbitraryAwaitChangesTargetAutomatically: false,
577
+ authoredThrowSupported: false,
578
+ nextIsTerminal: true
579
+ })
580
+ });
581
+ }
582
+
583
+ function defaultHandlerSurfaceContract() {
584
+ return handlerSurfaceContract(HANDLER_SURFACE_DEFINITIONS);
585
+ }
586
+
587
+ module.exports = {
588
+ HANDLER_SURFACE_CONTRACT_VERSION,
589
+ HANDLER_AUTHORING_POLICY_VERSION,
590
+ HANDLER_AUTHORING_MODES,
591
+ HANDLER_SURFACE_CONTRACT_ID,
592
+ HANDLER_SURFACE_CLASSES,
593
+ AWAIT_POLICIES,
594
+ HANDLER_SURFACE_DIAGNOSTICS,
595
+ HANDLER_SURFACE_DEFINITIONS,
596
+ DEFAULT_HANDLER_SURFACE_REGISTRY,
597
+ buildHandlerSurfaceRegistry,
598
+ classifyHandlerSurface,
599
+ classifyAwaitUse,
600
+ defaultHandlerSurfaceContract
601
+ };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ const { HANDLER_SURFACE_DEFINITIONS, HANDLER_SURFACE_CONTRACT_VERSION } = require('./surface-contract.js');
4
+
5
+ const HANDLER_TYPES_CONTRACT_VERSION = 'pulse.handler-types.v4';
6
+ const RUNTIME_AUTHORING_VERSION = 'pulse.runtime-authoring.v4';
7
+
8
+ function defaultHandlerTypesContract() {
9
+ return Object.freeze({
10
+ version: HANDLER_TYPES_CONTRACT_VERSION,
11
+ runtimeVersion: RUNTIME_AUTHORING_VERSION,
12
+ handlerSurfaceVersion: HANDLER_SURFACE_CONTRACT_VERSION,
13
+ surfaceCount: HANDLER_SURFACE_DEFINITIONS.length,
14
+ handlersAsyncShaped: true,
15
+ pulseEffectsPromiseCompatible: true,
16
+ routerNextTerminal: true,
17
+ ctxState: Object.freeze({
18
+ shape: 'execution-local-string-map',
19
+ get: 'string | undefined',
20
+ set: 'void'
21
+ }),
22
+ executionContexts: Object.freeze({
23
+ shared: Object.freeze(['state', 'log', 'fetch', 'parallel', 'config', 'secret', 'kv', 'emit']),
24
+ http: Object.freeze(['req', 'json', 'text', 'response']),
25
+ route: Object.freeze(['param']),
26
+ event: Object.freeze(['event.type', 'event.payload']),
27
+ emitPublished: true
28
+ }),
29
+ compiler: Object.freeze({
30
+ acceptsAsyncHandlers: true,
31
+ acceptsAwait: true,
32
+ acceptsEventHandlers: true,
33
+ eventEmitRecognition: true,
34
+ eventEmitNativeLowering: false,
35
+ awaitAuthority: 'ctx-surface-classification',
36
+ enablementWave: 'sprint1-wave2'
37
+ }),
38
+ native: Object.freeze({
39
+ runtimeChanged: true,
40
+ promiseRuntimeAdded: false,
41
+ asyncifyAdded: false,
42
+ eventEntryImplemented: false
43
+ })
44
+ });
45
+ }
46
+
47
+ module.exports = Object.freeze({
48
+ HANDLER_TYPES_CONTRACT_VERSION,
49
+ RUNTIME_AUTHORING_VERSION,
50
+ defaultHandlerTypesContract
51
+ });
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const HANDLER_TABLE_VERSION = 'pulsewasm.handler-table.v1';
4
+ const HANDLER_STABLE_INPUT_VERSION = 'pulsewasm.handler-stable-input.v1';
5
+
6
+ const HANDLER_STABLE_ID_POLICY = Object.freeze({
7
+ algorithm: 'sha256',
8
+ prefix: 'handler',
9
+ input: 'file + kind + local/export name for declarations, loc for inline handlers, and sourceTextHash',
10
+ version: HANDLER_STABLE_INPUT_VERSION
11
+ });
12
+
13
+ const HANDLER_ID_POLICY = Object.freeze({
14
+ stable: HANDLER_STABLE_ID_POLICY
15
+ });
16
+
17
+ const HANDLER_TABLE_POLICY = Object.freeze({
18
+ version: HANDLER_TABLE_VERSION,
19
+ idPolicy: HANDLER_ID_POLICY
20
+ });
21
+
22
+ module.exports = {
23
+ HANDLER_TABLE_VERSION,
24
+ HANDLER_STABLE_INPUT_VERSION,
25
+ HANDLER_STABLE_ID_POLICY,
26
+ HANDLER_ID_POLICY,
27
+ HANDLER_TABLE_POLICY
28
+ };