@4xeoz/re-entry 0.2.0

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 (29) hide show
  1. package/README.md +337 -0
  2. package/node_modules/@webmcp-challenge/reentry-core/README.md +112 -0
  3. package/node_modules/@webmcp-challenge/reentry-core/package.json +38 -0
  4. package/node_modules/@webmcp-challenge/reentry-core/protocol/test-vectors/v0.1.json +47 -0
  5. package/node_modules/@webmcp-challenge/reentry-core/src/agent-adapter.mjs +438 -0
  6. package/node_modules/@webmcp-challenge/reentry-core/src/cloud-receiver-http.mjs +268 -0
  7. package/node_modules/@webmcp-challenge/reentry-core/src/host-sdk.mjs +278 -0
  8. package/node_modules/@webmcp-challenge/reentry-core/src/index.mjs +3 -0
  9. package/node_modules/@webmcp-challenge/reentry-core/src/local-connector-client.mjs +530 -0
  10. package/node_modules/@webmcp-challenge/reentry-core/src/managed-context-adapter.mjs +275 -0
  11. package/node_modules/@webmcp-challenge/reentry-core/src/protocol.mjs +845 -0
  12. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-core.mjs +867 -0
  13. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-delivery.mjs +613 -0
  14. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-http-contract.mjs +24 -0
  15. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-support.mjs +151 -0
  16. package/node_modules/@webmcp-challenge/reentry-core/src/sqlite-receiver-schema.mjs +184 -0
  17. package/node_modules/@webmcp-challenge/reentry-core/src/sqlite-receiver-store.mjs +573 -0
  18. package/package.json +44 -0
  19. package/src/browser-prompt.mjs +18 -0
  20. package/src/codex-discovery.mjs +246 -0
  21. package/src/codex-exec-adapter.mjs +197 -0
  22. package/src/codex-queue-adapter.mjs +214 -0
  23. package/src/credentials.mjs +87 -0
  24. package/src/index.mjs +6 -0
  25. package/src/local-connector.mjs +82 -0
  26. package/src/macos-service.mjs +184 -0
  27. package/src/main.mjs +733 -0
  28. package/src/pairing-client.mjs +545 -0
  29. package/src/terminal-ui.mjs +99 -0
@@ -0,0 +1,438 @@
1
+ import {
2
+ PROTOCOL_VERSION,
3
+ validateContinuationReceipt,
4
+ } from "./protocol.mjs";
5
+
6
+ export const AGENT_ACTIVATION_TYPE = "webmcp.agent_activation";
7
+ export const AGENT_ACTIVATION_RESULT_TYPE = "webmcp.agent_activation_result";
8
+ export const AGENT_ADAPTER_CAPABILITIES = Object.freeze([
9
+ "managed_context_resume",
10
+ "eligible_browser",
11
+ "canonical_page_navigation",
12
+ "page_bound_webmcp",
13
+ ]);
14
+
15
+ const DELIVERY_LEASE_TYPE = "webmcp.delivery_lease";
16
+ const MAXIMUM_ATTEMPTS = 100;
17
+ const MINIMUM_TIMEOUT_MS = 100;
18
+ const MAXIMUM_TIMEOUT_MS = 60_000;
19
+ const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/;
20
+ const CLAIM_TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/;
21
+ const DISPATCH_FIELDS = Object.freeze(["adapter", "lease", "now", "timeoutMs"]);
22
+ const CREATE_FIELDS = Object.freeze(["lease", "now"]);
23
+ const VALIDATE_RESULT_FIELDS = Object.freeze(["activation", "result"]);
24
+ const LEASE_FIELDS = Object.freeze([
25
+ "type",
26
+ "protocol_version",
27
+ "delivery_id",
28
+ "event_id",
29
+ "attempt",
30
+ "lease_token",
31
+ "lease_expires_at",
32
+ "continuation",
33
+ "receipt",
34
+ ]);
35
+ const CONTINUATION_FIELDS = Object.freeze([
36
+ "correlation_id",
37
+ "workflow_id",
38
+ "event_type",
39
+ "event_sequence",
40
+ "state_version",
41
+ "occurred_at",
42
+ "canonical_url",
43
+ ]);
44
+ const ACTIVATION_FIELDS = Object.freeze([
45
+ "type",
46
+ "protocol_version",
47
+ "delivery_id",
48
+ "event_id",
49
+ "attempt",
50
+ "lease_expires_at",
51
+ "continuation",
52
+ "receipt",
53
+ ]);
54
+ const RESULT_FIELDS = Object.freeze([
55
+ "type",
56
+ "protocol_version",
57
+ "delivery_id",
58
+ "event_id",
59
+ "attempt",
60
+ "outcome",
61
+ "code",
62
+ "unavailable_capability",
63
+ ]);
64
+ const RESULT_RULES = Object.freeze({
65
+ accepted: Object.freeze({
66
+ codes: Object.freeze(["activation_dispatch_accepted"]),
67
+ capability: "absent",
68
+ }),
69
+ unsupported: Object.freeze({
70
+ codes: Object.freeze(["required_capability_unavailable"]),
71
+ capability: "required",
72
+ }),
73
+ rejected: Object.freeze({
74
+ codes: Object.freeze(["activation_rejected"]),
75
+ capability: "absent",
76
+ }),
77
+ outcome_unknown: Object.freeze({
78
+ codes: Object.freeze([
79
+ "activation_outcome_unknown",
80
+ "adapter_invocation_failed",
81
+ "adapter_invocation_timed_out",
82
+ "adapter_result_invalid",
83
+ ]),
84
+ capability: "absent",
85
+ }),
86
+ });
87
+ const TIMEOUT = Symbol("agent-adapter-timeout");
88
+
89
+ export class AgentAdapterContractError extends Error {
90
+ constructor(code, message) {
91
+ super(message);
92
+ this.name = "AgentAdapterContractError";
93
+ this.code = code;
94
+ }
95
+ }
96
+
97
+ export function createAgentActivation(input) {
98
+ requireExactRecord(input, CREATE_FIELDS, "Agent activation creation input");
99
+ const now = requireDate(input.now, "Agent activation current time");
100
+ const lease = normalizeLease(input.lease, now);
101
+ return deepFreeze({
102
+ type: AGENT_ACTIVATION_TYPE,
103
+ protocol_version: PROTOCOL_VERSION,
104
+ delivery_id: lease.delivery_id,
105
+ event_id: lease.event_id,
106
+ attempt: lease.attempt,
107
+ lease_expires_at: lease.lease_expires_at,
108
+ continuation: lease.continuation,
109
+ receipt: lease.receipt,
110
+ });
111
+ }
112
+
113
+ export function validateAgentActivation(activation) {
114
+ return deepFreeze(normalizeActivation(activation));
115
+ }
116
+
117
+ export function validateAgentActivationResult(input) {
118
+ requireExactRecord(input, VALIDATE_RESULT_FIELDS, "Agent result validation input");
119
+ const activation = validateAgentActivation(input.activation);
120
+ return normalizeResult(input.result, activation);
121
+ }
122
+
123
+ export async function dispatchAgentActivation(input) {
124
+ requireExactRecord(input, DISPATCH_FIELDS, "Agent activation dispatch input");
125
+ const timeoutMs = requireTimeout(input.timeoutMs);
126
+ const activation = createAgentActivation({ lease: input.lease, now: input.now });
127
+ const adapter = requireAdapter(input.adapter);
128
+ const remainingMs = Date.parse(activation.lease_expires_at) - input.now.getTime();
129
+ const effectiveTimeoutMs = Math.max(1, Math.min(timeoutMs, remainingMs));
130
+
131
+ let timer;
132
+ let rawResult;
133
+ try {
134
+ rawResult = await Promise.race([
135
+ Promise.resolve().then(() => adapter.activate(activation)),
136
+ new Promise((resolve) => {
137
+ timer = setTimeout(resolve, effectiveTimeoutMs, TIMEOUT);
138
+ }),
139
+ ]);
140
+ } catch {
141
+ return unknownResult(activation, "adapter_invocation_failed");
142
+ } finally {
143
+ if (timer !== undefined) clearTimeout(timer);
144
+ }
145
+
146
+ if (rawResult === TIMEOUT) {
147
+ return unknownResult(activation, "adapter_invocation_timed_out");
148
+ }
149
+ try {
150
+ return validateAgentActivationResult({ activation, result: rawResult });
151
+ } catch {
152
+ return unknownResult(activation, "adapter_result_invalid");
153
+ }
154
+ }
155
+
156
+ function normalizeLease(value, now) {
157
+ requireExactRecord(value, LEASE_FIELDS, "Delivery lease");
158
+ if (
159
+ value.type !== DELIVERY_LEASE_TYPE ||
160
+ value.protocol_version !== PROTOCOL_VERSION
161
+ ) {
162
+ throw contractError("agent_activation_lease_invalid", "Delivery lease version is unsupported");
163
+ }
164
+ const leaseExpiresAt = requireTimestamp(
165
+ value.lease_expires_at,
166
+ "Delivery lease expiry",
167
+ );
168
+ if (Date.parse(leaseExpiresAt) <= now.getTime()) {
169
+ throw contractError("agent_activation_expired", "Delivery lease has expired");
170
+ }
171
+ const continuation = normalizeContinuation(value.continuation);
172
+ let receipt;
173
+ try {
174
+ receipt = validateContinuationReceipt(value.receipt);
175
+ } catch {
176
+ throw contractError("agent_activation_receipt_invalid", "Continuation receipt is invalid");
177
+ }
178
+ if (Date.parse(receipt.expires_at) <= now.getTime()) {
179
+ throw contractError("agent_activation_expired", "Continuation receipt has expired");
180
+ }
181
+ if (Date.parse(leaseExpiresAt) > Date.parse(receipt.expires_at)) {
182
+ throw contractError(
183
+ "agent_activation_scope_invalid",
184
+ "Delivery lease exceeds continuation receipt authority",
185
+ );
186
+ }
187
+ if (
188
+ continuation.correlation_id !== receipt.correlation_id ||
189
+ continuation.workflow_id !== receipt.workflow_id ||
190
+ continuation.event_type !== receipt.event_type ||
191
+ continuation.canonical_url !== receipt.canonical_url
192
+ ) {
193
+ throw contractError(
194
+ "agent_activation_scope_invalid",
195
+ "Delivery continuation and receipt do not match",
196
+ );
197
+ }
198
+ requireClaimToken(value.lease_token);
199
+ return {
200
+ delivery_id: requireIdentifier(value.delivery_id, "Delivery identifier"),
201
+ event_id: requireIdentifier(value.event_id, "Event identifier"),
202
+ attempt: requireAttempt(value.attempt),
203
+ lease_expires_at: leaseExpiresAt,
204
+ continuation,
205
+ receipt,
206
+ };
207
+ }
208
+
209
+ function normalizeActivation(value) {
210
+ requireExactRecord(value, ACTIVATION_FIELDS, "Agent activation");
211
+ if (
212
+ value.type !== AGENT_ACTIVATION_TYPE ||
213
+ value.protocol_version !== PROTOCOL_VERSION
214
+ ) {
215
+ throw contractError("agent_activation_invalid", "Agent activation version is unsupported");
216
+ }
217
+ const continuation = normalizeContinuation(value.continuation);
218
+ let receipt;
219
+ try {
220
+ receipt = validateContinuationReceipt(value.receipt);
221
+ } catch {
222
+ throw contractError("agent_activation_receipt_invalid", "Continuation receipt is invalid");
223
+ }
224
+ if (
225
+ continuation.correlation_id !== receipt.correlation_id ||
226
+ continuation.workflow_id !== receipt.workflow_id ||
227
+ continuation.event_type !== receipt.event_type ||
228
+ continuation.canonical_url !== receipt.canonical_url
229
+ ) {
230
+ throw contractError("agent_activation_scope_invalid", "Agent activation scope is invalid");
231
+ }
232
+ const leaseExpiresAt = requireTimestamp(
233
+ value.lease_expires_at,
234
+ "Activation lease expiry",
235
+ );
236
+ if (Date.parse(leaseExpiresAt) > Date.parse(receipt.expires_at)) {
237
+ throw contractError(
238
+ "agent_activation_scope_invalid",
239
+ "Agent activation lease exceeds receipt authority",
240
+ );
241
+ }
242
+ return {
243
+ type: AGENT_ACTIVATION_TYPE,
244
+ protocol_version: PROTOCOL_VERSION,
245
+ delivery_id: requireIdentifier(value.delivery_id, "Activation delivery identifier"),
246
+ event_id: requireIdentifier(value.event_id, "Activation event identifier"),
247
+ attempt: requireAttempt(value.attempt),
248
+ lease_expires_at: leaseExpiresAt,
249
+ continuation,
250
+ receipt,
251
+ };
252
+ }
253
+
254
+ function normalizeContinuation(value) {
255
+ requireExactRecord(value, CONTINUATION_FIELDS, "Delivery continuation");
256
+ if (
257
+ value.event_sequence !== 1 ||
258
+ !Number.isSafeInteger(value.state_version) ||
259
+ value.state_version < 0
260
+ ) {
261
+ throw contractError("agent_activation_continuation_invalid", "Continuation state is invalid");
262
+ }
263
+ return {
264
+ correlation_id: requireIdentifier(value.correlation_id, "Continuation correlation identifier"),
265
+ workflow_id: requireIdentifier(value.workflow_id, "Continuation workflow identifier"),
266
+ event_type: requireIdentifier(value.event_type, "Continuation event type"),
267
+ event_sequence: 1,
268
+ state_version: value.state_version,
269
+ occurred_at: requireTimestamp(value.occurred_at, "Continuation occurrence time"),
270
+ canonical_url: value.canonical_url,
271
+ };
272
+ }
273
+
274
+ function normalizeResult(value, activation) {
275
+ requireExactRecord(value, RESULT_FIELDS, "Agent activation result");
276
+ if (
277
+ value.type !== AGENT_ACTIVATION_RESULT_TYPE ||
278
+ value.protocol_version !== PROTOCOL_VERSION ||
279
+ value.delivery_id !== activation.delivery_id ||
280
+ value.event_id !== activation.event_id ||
281
+ value.attempt !== activation.attempt
282
+ ) {
283
+ throw contractError("agent_adapter_result_invalid", "Agent activation result is mismatched");
284
+ }
285
+ const rule = RESULT_RULES[value.outcome];
286
+ if (!rule || !rule.codes.includes(value.code)) {
287
+ throw contractError("agent_adapter_result_invalid", "Agent activation result is unsupported");
288
+ }
289
+ const capability = value.unavailable_capability;
290
+ if (
291
+ (rule.capability === "required" && !AGENT_ADAPTER_CAPABILITIES.includes(capability)) ||
292
+ (rule.capability === "absent" && capability !== null)
293
+ ) {
294
+ throw contractError("agent_adapter_result_invalid", "Agent activation capability is invalid");
295
+ }
296
+ return deepFreeze({
297
+ type: AGENT_ACTIVATION_RESULT_TYPE,
298
+ protocol_version: PROTOCOL_VERSION,
299
+ delivery_id: activation.delivery_id,
300
+ event_id: activation.event_id,
301
+ attempt: activation.attempt,
302
+ outcome: value.outcome,
303
+ code: value.code,
304
+ unavailable_capability: capability,
305
+ });
306
+ }
307
+
308
+ function unknownResult(activation, code) {
309
+ return deepFreeze({
310
+ type: AGENT_ACTIVATION_RESULT_TYPE,
311
+ protocol_version: PROTOCOL_VERSION,
312
+ delivery_id: activation.delivery_id,
313
+ event_id: activation.event_id,
314
+ attempt: activation.attempt,
315
+ outcome: "outcome_unknown",
316
+ code,
317
+ unavailable_capability: null,
318
+ });
319
+ }
320
+
321
+ function requireAdapter(value) {
322
+ if (!value || (typeof value !== "object" && typeof value !== "function")) {
323
+ throw contractError("agent_adapter_invalid", "Agent adapter must implement activate");
324
+ }
325
+ let activate;
326
+ try {
327
+ activate = value.activate;
328
+ } catch {
329
+ throw contractError("agent_adapter_invalid", "Agent adapter must implement activate");
330
+ }
331
+ if (typeof activate !== "function") {
332
+ throw contractError("agent_adapter_invalid", "Agent adapter must implement activate");
333
+ }
334
+ return { activate: activate.bind(value) };
335
+ }
336
+
337
+ function requireTimeout(value) {
338
+ if (
339
+ !Number.isSafeInteger(value) ||
340
+ value < MINIMUM_TIMEOUT_MS ||
341
+ value > MAXIMUM_TIMEOUT_MS
342
+ ) {
343
+ throw contractError(
344
+ "agent_adapter_timeout_invalid",
345
+ "Agent adapter timeout must be between 100 and 60000 milliseconds",
346
+ );
347
+ }
348
+ return value;
349
+ }
350
+
351
+ function requireDate(value, label) {
352
+ if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
353
+ throw contractError("agent_activation_time_invalid", `${label} must be a valid Date`);
354
+ }
355
+ return new Date(value.getTime());
356
+ }
357
+
358
+ function requireIdentifier(value, label) {
359
+ if (
360
+ typeof value !== "string" ||
361
+ Buffer.byteLength(value, "utf8") > 160 ||
362
+ !IDENTIFIER_PATTERN.test(value)
363
+ ) {
364
+ throw contractError("agent_activation_identifier_invalid", `${label} is invalid`);
365
+ }
366
+ return value;
367
+ }
368
+
369
+ function requireAttempt(value) {
370
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAXIMUM_ATTEMPTS) {
371
+ throw contractError("agent_activation_attempt_invalid", "Activation attempt is invalid");
372
+ }
373
+ return value;
374
+ }
375
+
376
+ function requireTimestamp(value, label) {
377
+ if (typeof value !== "string" || value.length > 27) {
378
+ throw contractError("agent_activation_timestamp_invalid", `${label} is invalid`);
379
+ }
380
+ const parsed = Date.parse(value);
381
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
382
+ throw contractError("agent_activation_timestamp_invalid", `${label} is invalid`);
383
+ }
384
+ return value;
385
+ }
386
+
387
+ function requireClaimToken(value) {
388
+ if (typeof value !== "string" || !CLAIM_TOKEN_PATTERN.test(value)) {
389
+ throw contractError("agent_activation_lease_invalid", "Delivery lease token is invalid");
390
+ }
391
+ const decoded = Buffer.from(value, "base64url");
392
+ if (decoded.length !== 32 || decoded.toString("base64url") !== value) {
393
+ throw contractError("agent_activation_lease_invalid", "Delivery lease token is invalid");
394
+ }
395
+ }
396
+
397
+ function requireExactRecord(value, expectedFields, label) {
398
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
399
+ throw contractError("agent_adapter_contract_invalid", `${label} must be an object`);
400
+ }
401
+ const prototype = Object.getPrototypeOf(value);
402
+ if (prototype !== Object.prototype && prototype !== null) {
403
+ throw contractError("agent_adapter_contract_invalid", `${label} must be a plain object`);
404
+ }
405
+ for (const key of Reflect.ownKeys(value)) {
406
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
407
+ if (
408
+ typeof key === "symbol" ||
409
+ !descriptor?.enumerable ||
410
+ !("value" in descriptor)
411
+ ) {
412
+ throw contractError(
413
+ "agent_adapter_contract_invalid",
414
+ `${label} must contain enumerable data fields only`,
415
+ );
416
+ }
417
+ }
418
+ const actual = Object.keys(value).sort();
419
+ const expected = [...expectedFields].sort();
420
+ if (
421
+ actual.length !== expected.length ||
422
+ actual.some((field, index) => field !== expected[index])
423
+ ) {
424
+ throw contractError("agent_adapter_contract_invalid", `${label} fields are invalid`);
425
+ }
426
+ }
427
+
428
+ function deepFreeze(value) {
429
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
430
+ for (const child of Object.values(value)) deepFreeze(child);
431
+ Object.freeze(value);
432
+ }
433
+ return value;
434
+ }
435
+
436
+ function contractError(code, message) {
437
+ return new AgentAdapterContractError(code, message);
438
+ }
@@ -0,0 +1,268 @@
1
+ import { TextDecoder } from "node:util";
2
+
3
+ import { canonicalJson } from "./protocol.mjs";
4
+ import {
5
+ ReceiverAuthorizationError,
6
+ ReceiverConflictError,
7
+ ReceiverInvariantError,
8
+ ReceiverNotFoundError,
9
+ ReceiverScopeError,
10
+ ReceiverValidationError,
11
+ } from "./receiver-core.mjs";
12
+ import {
13
+ ACKNOWLEDGEMENT_REQUEST_FIELDS,
14
+ CLAIM_REQUEST_FIELDS,
15
+ RECEIVER_HTTP_CONTENT_TYPE,
16
+ RECEIVER_HTTP_LIMITS,
17
+ RECEIVER_HTTP_ROUTES,
18
+ } from "./receiver-http-contract.mjs";
19
+
20
+ export { RECEIVER_HTTP_LIMITS, RECEIVER_HTTP_ROUTES } from "./receiver-http-contract.mjs";
21
+
22
+ const HANDLER_OPTION_FIELDS = Object.freeze(["receiver"]);
23
+ const RECEIVER_METHODS = Object.freeze([
24
+ "acceptEvent",
25
+ "claimDelivery",
26
+ "acknowledgeDelivery",
27
+ ]);
28
+ const ROUTE_PATHS = new Set(Object.values(RECEIVER_HTTP_ROUTES));
29
+ const CORE_ERROR_TYPES = Object.freeze([
30
+ ReceiverValidationError,
31
+ ReceiverAuthorizationError,
32
+ ReceiverConflictError,
33
+ ReceiverScopeError,
34
+ ReceiverNotFoundError,
35
+ ReceiverInvariantError,
36
+ ]);
37
+ const ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,95}$/;
38
+ const CONTENT_TYPE_PATTERN = /^application\/json(?:\s*;\s*charset=utf-8)?$/i;
39
+
40
+ export function createCloudReceiverHttpHandler(options) {
41
+ requireExactRecord(options, HANDLER_OPTION_FIELDS, "Cloud Receiver HTTP options");
42
+ requireReceiver(options.receiver);
43
+
44
+ return function cloudReceiverHttpHandler(request, response) {
45
+ handleRequest(options.receiver, request, response).catch((error) => {
46
+ if (response.destroyed) return;
47
+ if (response.headersSent) {
48
+ response.destroy();
49
+ return;
50
+ }
51
+ const failure = classifyFailure(error);
52
+ writeJson(response, failure.statusCode, { error: { code: failure.code } }, failure.headers);
53
+ });
54
+ };
55
+ }
56
+
57
+ async function handleRequest(receiver, request, response) {
58
+ const route = parseRoute(request.url);
59
+ if (!route) {
60
+ throw httpFailure("http_route_not_found", 404);
61
+ }
62
+ if (request.method !== "POST") {
63
+ throw httpFailure("http_method_not_allowed", 405, { Allow: "POST" });
64
+ }
65
+ requireJsonContentType(request);
66
+ if (getDistinctHeaderValues(request, "content-encoding").length > 0) {
67
+ throw httpFailure("http_content_type_invalid", 415);
68
+ }
69
+ const body = await readJsonBody(request);
70
+
71
+ if (route === RECEIVER_HTTP_ROUTES.event) {
72
+ const result = requireSynchronousResult(receiver.acceptEvent(body));
73
+ writeJson(response, 202, result);
74
+ return;
75
+ }
76
+ if (route === RECEIVER_HTTP_ROUTES.claim) {
77
+ requireExactRecord(body, CLAIM_REQUEST_FIELDS, "Delivery claim request");
78
+ const result = requireSynchronousResult(receiver.claimDelivery({
79
+ connectorToken: body.connector_token,
80
+ claimToken: body.claim_token,
81
+ }));
82
+ if (result === null) {
83
+ writeNoContent(response);
84
+ return;
85
+ }
86
+ writeJson(response, 200, result);
87
+ return;
88
+ }
89
+
90
+ requireExactRecord(body, ACKNOWLEDGEMENT_REQUEST_FIELDS, "Delivery acknowledgement request");
91
+ const result = requireSynchronousResult(receiver.acknowledgeDelivery({
92
+ connectorToken: body.connector_token,
93
+ deliveryId: body.delivery_id,
94
+ leaseToken: body.lease_token,
95
+ effectToken: body.effect_token,
96
+ }));
97
+ writeJson(response, 200, result);
98
+ }
99
+
100
+ function parseRoute(value) {
101
+ if (typeof value !== "string" || value.length > 256) return null;
102
+ return ROUTE_PATHS.has(value) ? value : null;
103
+ }
104
+
105
+ function requireJsonContentType(request) {
106
+ const values = getDistinctHeaderValues(request, "content-type");
107
+ if (values.length !== 1 || !CONTENT_TYPE_PATTERN.test(values[0])) {
108
+ throw httpFailure("http_content_type_invalid", 415);
109
+ }
110
+ }
111
+
112
+ function getDistinctHeaderValues(request, name) {
113
+ const distinct = request.headersDistinct?.[name];
114
+ if (Array.isArray(distinct)) return distinct;
115
+ const value = request.headers?.[name];
116
+ if (value === undefined) return [];
117
+ return Array.isArray(value) ? value : [value];
118
+ }
119
+
120
+ async function readJsonBody(request) {
121
+ const declaredLength = getDistinctHeaderValues(request, "content-length");
122
+ if (declaredLength.length > 1) {
123
+ throw httpFailure("http_body_invalid", 400);
124
+ }
125
+ if (declaredLength.length === 1) {
126
+ if (!/^(?:0|[1-9][0-9]*)$/.test(declaredLength[0])) {
127
+ throw httpFailure("http_body_invalid", 400);
128
+ }
129
+ if (Number(declaredLength[0]) > RECEIVER_HTTP_LIMITS.requestBytes) {
130
+ throw httpFailure("http_body_too_large", 413);
131
+ }
132
+ }
133
+
134
+ const chunks = [];
135
+ let size = 0;
136
+ for await (const chunk of request) {
137
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
138
+ size += bytes.length;
139
+ if (size > RECEIVER_HTTP_LIMITS.requestBytes) {
140
+ throw httpFailure("http_body_too_large", 413);
141
+ }
142
+ chunks.push(bytes);
143
+ }
144
+ if (size === 0) {
145
+ throw httpFailure("http_body_invalid", 400);
146
+ }
147
+
148
+ let text;
149
+ try {
150
+ text = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks));
151
+ } catch {
152
+ throw httpFailure("http_body_invalid", 400);
153
+ }
154
+ let value;
155
+ try {
156
+ value = JSON.parse(text);
157
+ } catch {
158
+ throw httpFailure("http_body_invalid", 400);
159
+ }
160
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
161
+ throw httpFailure("http_body_invalid", 400);
162
+ }
163
+ return value;
164
+ }
165
+
166
+ function requireExactRecord(value, expectedFields, label) {
167
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
168
+ throw httpFailure("http_body_invalid", 400);
169
+ }
170
+ const prototype = Object.getPrototypeOf(value);
171
+ if (prototype !== Object.prototype && prototype !== null) {
172
+ throw httpFailure("http_body_invalid", 400);
173
+ }
174
+ for (const key of Reflect.ownKeys(value)) {
175
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
176
+ if (typeof key === "symbol" || !descriptor?.enumerable || !("value" in descriptor)) {
177
+ throw httpFailure("http_body_invalid", 400);
178
+ }
179
+ }
180
+ const actual = Object.keys(value).sort();
181
+ const expected = [...expectedFields].sort();
182
+ if (
183
+ actual.length !== expected.length ||
184
+ actual.some((field, index) => field !== expected[index])
185
+ ) {
186
+ throw httpFailure("http_body_invalid", 400, undefined, `${label} fields are invalid`);
187
+ }
188
+ }
189
+
190
+ function requireSynchronousResult(value) {
191
+ if (value && typeof value.then === "function") {
192
+ throw new TypeError("Receiver Core HTTP methods must be synchronous");
193
+ }
194
+ return value;
195
+ }
196
+
197
+ function writeJson(response, statusCode, body, additionalHeaders = undefined) {
198
+ const payload = canonicalJson(body);
199
+ response.writeHead(statusCode, {
200
+ "Cache-Control": "no-store",
201
+ "Content-Length": Buffer.byteLength(payload),
202
+ "Content-Type": RECEIVER_HTTP_CONTENT_TYPE,
203
+ Pragma: "no-cache",
204
+ "X-Content-Type-Options": "nosniff",
205
+ ...additionalHeaders,
206
+ });
207
+ response.end(payload);
208
+ }
209
+
210
+ function writeNoContent(response) {
211
+ response.writeHead(204, {
212
+ "Cache-Control": "no-store",
213
+ "Content-Length": 0,
214
+ Pragma: "no-cache",
215
+ "X-Content-Type-Options": "nosniff",
216
+ });
217
+ response.end();
218
+ }
219
+
220
+ function classifyFailure(error) {
221
+ if (error instanceof CloudReceiverHttpError) {
222
+ return error;
223
+ }
224
+ if (
225
+ CORE_ERROR_TYPES.some((ErrorType) => error instanceof ErrorType) &&
226
+ Number.isInteger(error.statusCode) &&
227
+ error.statusCode >= 400 &&
228
+ error.statusCode <= 599 &&
229
+ typeof error.code === "string" &&
230
+ ERROR_CODE_PATTERN.test(error.code)
231
+ ) {
232
+ return {
233
+ code: error.code,
234
+ statusCode: error.statusCode,
235
+ headers: undefined,
236
+ };
237
+ }
238
+ return {
239
+ code: "receiver_internal_error",
240
+ statusCode: 500,
241
+ headers: undefined,
242
+ };
243
+ }
244
+
245
+ function httpFailure(code, statusCode, headers, message = code) {
246
+ return new CloudReceiverHttpError(code, statusCode, headers, message);
247
+ }
248
+
249
+ class CloudReceiverHttpError extends Error {
250
+ constructor(code, statusCode, headers, message) {
251
+ super(message);
252
+ this.name = "CloudReceiverHttpError";
253
+ this.code = code;
254
+ this.statusCode = statusCode;
255
+ this.headers = headers;
256
+ }
257
+ }
258
+
259
+ function requireReceiver(receiver) {
260
+ if (!receiver || typeof receiver !== "object") {
261
+ throw new TypeError("Cloud Receiver HTTP adapter requires a Receiver Core");
262
+ }
263
+ for (const method of RECEIVER_METHODS) {
264
+ if (typeof receiver[method] !== "function") {
265
+ throw new TypeError(`Cloud Receiver HTTP adapter Receiver is missing ${method}`);
266
+ }
267
+ }
268
+ }