@objectstack/plugin-webhooks 17.0.0 → 17.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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SysWebhook
3
- } from "./chunk-GDCWDVDT.js";
3
+ } from "./chunk-XERWWQKN.js";
4
4
 
5
5
  // src/webhook-secret.ts
6
6
  var WEBHOOK_SECRET_FIELD = "signing_secret";
@@ -194,7 +194,7 @@ var AutoEnqueuer = class {
194
194
  this.droppedForSecret = /* @__PURE__ */ new Set();
195
195
  this.subscriptionsObject = opts.subscriptionsObject ?? "sys_webhook";
196
196
  this.refreshIntervalMs = opts.refreshIntervalMs ?? 6e4;
197
- this.logger = opts.logger ?? {};
197
+ this.logger = opts.logger;
198
198
  }
199
199
  /**
200
200
  * Load the subscription cache and start listening for events.
@@ -219,7 +219,7 @@ var AutoEnqueuer = class {
219
219
  if (this.refreshIntervalMs > 0) {
220
220
  this.refreshTimer = setInterval(() => {
221
221
  this.refresh().catch(
222
- (err) => this.logger.warn?.("[webhook-auto-enqueuer] periodic refresh failed", err)
222
+ (err) => this.logger?.warn?.("[webhook-auto-enqueuer] periodic refresh failed", err)
223
223
  );
224
224
  }, this.refreshIntervalMs);
225
225
  this.refreshTimer.unref?.();
@@ -252,7 +252,7 @@ var AutoEnqueuer = class {
252
252
  rearmAfterCryptoRegistered() {
253
253
  const inFlight = this.refreshing ?? Promise.resolve();
254
254
  void inFlight.catch(() => void 0).then(() => this.running ? this.refresh() : void 0).catch(
255
- (err) => this.logger.warn?.(
255
+ (err) => this.logger?.warn?.(
256
256
  "[webhook-auto-enqueuer] re-arm after CryptoProvider registration failed",
257
257
  err
258
258
  )
@@ -276,7 +276,7 @@ var AutoEnqueuer = class {
276
276
  where: { active: true }
277
277
  });
278
278
  } catch (err) {
279
- this.logger.warn?.(
279
+ this.logger?.warn?.(
280
280
  `[webhook-auto-enqueuer] failed to load ${this.subscriptionsObject}`,
281
281
  err
282
282
  );
@@ -300,7 +300,7 @@ var AutoEnqueuer = class {
300
300
  if (!live.has(id)) this.droppedForSecret.delete(id);
301
301
  }
302
302
  }
303
- this.logger.debug?.("[webhook-auto-enqueuer] cache refreshed", {
303
+ this.logger?.debug?.("[webhook-auto-enqueuer] cache refreshed", {
304
304
  objects: this.subscriptions.size,
305
305
  rows: rows.length
306
306
  });
@@ -371,14 +371,14 @@ var AutoEnqueuer = class {
371
371
  reportWriteFailure(sub, eventId, err, verb) {
372
372
  const meta = { webhook: sub.name, eventId, err: err?.message ?? err };
373
373
  if (!sub.parkedReason) {
374
- this.logger.warn?.(`[webhook-auto-enqueuer] ${verb} failed`, meta);
374
+ this.logger?.warn?.(`[webhook-auto-enqueuer] ${verb} failed`, meta);
375
375
  return;
376
376
  }
377
377
  const message = `[webhook-auto-enqueuer] could not record the undeliverable event for webhook '${sub.name}' \u2014 the subscription is parked for an unresolvable credential, and this event is now DISCARDED WITH NO TRACE in sys_http_delivery, which is the durability gap #8069 closes. Most likely cause: the enqueue callback was wired directly to IHttpOutbox.enqueue instead of MessagingService.enqueueHttp \u2014 only the messaging seam routes a parked event to recordUndeliverable(), and the delivery door refuses it rather than minting a pending row that would be sent UNSIGNED.`;
378
- if (typeof this.logger.error === "function") {
379
- this.logger.error(message, err, meta);
378
+ if (typeof this.logger?.error === "function") {
379
+ this.logger?.error(message, err, meta);
380
380
  } else {
381
- this.logger.warn?.(message, meta);
381
+ this.logger?.warn?.(message, meta);
382
382
  }
383
383
  }
384
384
  park(sub, err, credential) {
@@ -430,7 +430,7 @@ var AutoEnqueuer = class {
430
430
  }
431
431
  const legacy = readLegacySecret(row?.definition_json);
432
432
  if (legacy) {
433
- this.logger.warn?.(
433
+ this.logger?.warn?.(
434
434
  `[webhook-auto-enqueuer] webhook '${sub.name}' still carries its signing secret as CLEARTEXT in definition_json, readable over the data API (#7799). Signing continues from it; run the boot sweep (migrateLegacyWebhookSecrets) with a CryptoProvider wired to move it into sys_secret.`,
435
435
  { id: sub.id }
436
436
  );
@@ -480,7 +480,7 @@ var AutoEnqueuer = class {
480
480
  }
481
481
  const legacy = readLegacyHeaders(row?.definition_json);
482
482
  if (legacy) {
483
- this.logger.warn?.(
483
+ this.logger?.warn?.(
484
484
  `[webhook-auto-enqueuer] webhook '${sub.name}' still carries its custom headers as CLEARTEXT in definition_json, readable over the data API (#7986) \u2014 that map is the ordinary place an Authorization header goes. Delivery continues from it; run the boot sweep (migrateLegacyWebhookSecrets) with a CryptoProvider wired to move them into sys_secret.`,
485
485
  { id: sub.id }
486
486
  );
@@ -523,7 +523,7 @@ var AutoEnqueuer = class {
523
523
  err: err?.message ?? err
524
524
  };
525
525
  if (this.droppedForSecret.has(sub.id)) {
526
- this.logger.debug?.(
526
+ this.logger?.debug?.(
527
527
  `[webhook-auto-enqueuer] webhook '${sub.name}' is still dropped for an unresolvable ${credential.noun} (${credential.issues})`,
528
528
  meta
529
529
  );
@@ -531,10 +531,10 @@ var AutoEnqueuer = class {
531
531
  }
532
532
  this.droppedForSecret.add(sub.id);
533
533
  const message = `[webhook-auto-enqueuer] webhook '${sub.name}' holds ${credential.article} that could not be decrypted \u2014 the subscription is PARKED rather than ${credential.ratherThan} (${credential.issue}), so every matching record change is discarded with NO delivery, while the row keeps reading active:true in Setup. Each discarded event IS recorded in sys_http_delivery as a dead row with 0 attempts carrying this cause (#8069) \u2014 look there for the backlog; those rows can never be sent or redelivered, because a parked row has no HMAC signature. Fix: register a CryptoProvider (engine.setCryptoProvider \u2014 LocalCryptoProvider in dev, KMS/Vault in production) with the same key the ${credential.noun} was written under, and make sure the sys_secret row is reachable; the subscription re-arms on registration (#8022) and at the next periodic refresh.`;
534
- if (typeof this.logger.error === "function") {
535
- this.logger.error(message, err, meta);
534
+ if (typeof this.logger?.error === "function") {
535
+ this.logger?.error(message, err, meta);
536
536
  } else {
537
- this.logger.warn?.(message, meta);
537
+ this.logger?.warn?.(message, meta);
538
538
  }
539
539
  }
540
540
  parseRow(row) {
@@ -559,7 +559,7 @@ var AutoEnqueuer = class {
559
559
  const normalized = triggerList.map((t) => t.trim().toLowerCase()).filter(Boolean);
560
560
  const unknown = normalized.filter((t) => !DISPATCHABLE_WEBHOOK_TRIGGERS.has(t));
561
561
  if (unknown.length > 0) {
562
- this.logger.warn?.(
562
+ this.logger?.warn?.(
563
563
  `[webhook-auto-enqueuer] webhook '${row.name ?? row.id}' declares trigger(s) the engine never emits: ${unknown.join(", ")} \u2014 ignored. Dispatchable triggers: ${[...DISPATCHABLE_WEBHOOK_TRIGGERS].join(", ")}.`,
564
564
  { id: row.id, unknown }
565
565
  );
@@ -568,7 +568,7 @@ var AutoEnqueuer = class {
568
568
  normalized.filter((t) => DISPATCHABLE_WEBHOOK_TRIGGERS.has(t))
569
569
  );
570
570
  if (triggers.size === 0) {
571
- this.logger.warn?.(
571
+ this.logger?.warn?.(
572
572
  `[webhook-auto-enqueuer] webhook '${row.name ?? row.id}' has no dispatchable triggers \u2014 it will NEVER fire (rule webhook/without-triggers): there is no manual fire path (#3196), so this row is dead while looking armed in Setup. Declare one of: ${[...DISPATCHABLE_WEBHOOK_TRIGGERS].join(", ")}, or set it inactive if it should be off.`,
573
573
  { id: row.id }
574
574
  );
@@ -625,7 +625,7 @@ var AutoEnqueuer = class {
625
625
  const payload = event.payload ?? {};
626
626
  const recordId = payload.recordId;
627
627
  if (typeof recordId !== "string" || recordId === "") {
628
- this.logger.warn?.(
628
+ this.logger?.warn?.(
629
629
  "[webhook-auto-enqueuer] dropping off-contract data event: payload is not a DataEvent (no top-level string `recordId`) \u2014 fix the producer",
630
630
  { type: event.type, object: event.object }
631
631
  );
@@ -694,7 +694,7 @@ var AutoEnqueuer = class {
694
694
  const payload = event.payload ?? {};
695
695
  const matched = payload.matched;
696
696
  if (typeof matched !== "number" || !Number.isInteger(matched) || matched < 0) {
697
- this.logger.warn?.(
697
+ this.logger?.warn?.(
698
698
  "[webhook-auto-enqueuer] dropping off-contract bulk data event: payload is not a BulkDataEvent (no top-level non-negative integer `matched`) \u2014 fix the producer",
699
699
  { type: event.type, object: event.object }
700
700
  );
@@ -702,7 +702,7 @@ var AutoEnqueuer = class {
702
702
  }
703
703
  const eventUuid = payload.id;
704
704
  if (typeof eventUuid !== "string" || eventUuid === "") {
705
- this.logger.warn?.(
705
+ this.logger?.warn?.(
706
706
  "[webhook-auto-enqueuer] dropping off-contract bulk data event: payload has no top-level string `id` to dedup on \u2014 fix the producer",
707
707
  { type: event.type, object: event.object }
708
708
  );
@@ -739,7 +739,7 @@ var AutoEnqueuer = class {
739
739
  if (event.object !== this.subscriptionsObject) return;
740
740
  if (!event.type?.startsWith("data.record.") && !event.type?.startsWith("data.records.")) return;
741
741
  this.refresh().catch(
742
- (err) => this.logger.warn?.("[webhook-auto-enqueuer] self-heal refresh failed", err)
742
+ (err) => this.logger?.warn?.("[webhook-auto-enqueuer] self-heal refresh failed", err)
743
743
  );
744
744
  }
745
745
  /** Test / admin accessor. */
@@ -1062,6 +1062,98 @@ function unbindWebhookProvenanceStamp(engine) {
1062
1062
  }
1063
1063
  }
1064
1064
 
1065
+ // src/webhook-headers-gate.ts
1066
+ var WEBHOOK_HEADERS_SHAPE_REFUSAL_CODE = "VALIDATION_ERROR";
1067
+ var WEBHOOK_HEADERS_SHAPE_REFUSAL_STATUS = 400;
1068
+ var DECLARED_SHAPE = 'Custom HTTP headers sent with each delivery, as a JSON object ({"Authorization": "Bearer ..."})';
1069
+ var WebhookHeadersShapeError = class extends Error {
1070
+ constructor(object, field, diagnosis) {
1071
+ super(
1072
+ `Custom headers refused for "${object}.${field}": ${diagnosis}. The required shape is a FLAT JSON object of string values, which is what the field itself asks for \u2014 its description reads: "${DECLARED_SHAPE}". This is checked at the write door because one step later there is nothing left to check: the engine encrypts this value into sys_secret and every read path returns only the mask, so a stored value that can never be used is indistinguishable from one that works until the next delivery tries to send it \u2014 at which point the subscription parks and the report arrives an unbounded time later, in a different surface from the one it was typed into (#7986, #8558, #8566). ${HEADERS_REMEDY}`
1073
+ );
1074
+ this.code = WEBHOOK_HEADERS_SHAPE_REFUSAL_CODE;
1075
+ this.status = WEBHOOK_HEADERS_SHAPE_REFUSAL_STATUS;
1076
+ this.name = "WebhookHeadersShapeError";
1077
+ this.object = object;
1078
+ this.field = field;
1079
+ }
1080
+ };
1081
+ function describeParsed(parsed) {
1082
+ if (parsed === null) return "null";
1083
+ if (Array.isArray(parsed)) return "a JSON array";
1084
+ if (typeof parsed !== "object") return `a JSON ${typeof parsed}`;
1085
+ const entries = Object.entries(parsed);
1086
+ if (entries.length === 0) {
1087
+ return 'an EMPTY JSON object, which is not the same thing as "send no custom headers"';
1088
+ }
1089
+ const bad = entries.filter(([, v]) => typeof v !== "string");
1090
+ if (bad.length > 0) {
1091
+ const named = bad.map(([k, v]) => `${JSON.stringify(k)} (${Array.isArray(v) ? "array" : v === null ? "null" : typeof v})`).join(", ");
1092
+ return `a JSON object, but the wire carries only strings and ${bad.length === 1 ? "this value is" : "these values are"} not a string: ${named}`;
1093
+ }
1094
+ return "a JSON object the header seam does not accept";
1095
+ }
1096
+ function describeRejected(value) {
1097
+ if (typeof value === "string") {
1098
+ let parsed;
1099
+ try {
1100
+ parsed = JSON.parse(value);
1101
+ } catch {
1102
+ return "the value is a string that is not valid JSON at all \u2014 check for unquoted keys or values ({X-Team: crm}), single quotes instead of double, or a trailing comma";
1103
+ }
1104
+ return `the value parses as JSON but is ${describeParsed(parsed)}`;
1105
+ }
1106
+ return `the value is ${describeParsed(value)}`;
1107
+ }
1108
+ function assertWritableWebhookHeaders(data, object = WEBHOOK_OBJECT, field = WEBHOOK_HEADERS_FIELD) {
1109
+ if (!data || typeof data !== "object") return;
1110
+ if (!Object.prototype.hasOwnProperty.call(data, field)) return;
1111
+ const value = data[field];
1112
+ if (value === null || typeof value === "undefined") return;
1113
+ if (value === "") return;
1114
+ if (isOpaqueSecretForm(value)) return;
1115
+ let serialized;
1116
+ if (typeof value === "string") {
1117
+ serialized = value;
1118
+ } else {
1119
+ try {
1120
+ serialized = JSON.stringify(value);
1121
+ } catch {
1122
+ throw new WebhookHeadersShapeError(
1123
+ object,
1124
+ field,
1125
+ "the value cannot be serialized to JSON at all (it contains a circular reference)"
1126
+ );
1127
+ }
1128
+ if (typeof serialized !== "string") {
1129
+ throw new WebhookHeadersShapeError(object, field, `the value is a ${typeof value}`);
1130
+ }
1131
+ }
1132
+ if (parseStoredHeaders(serialized)) return;
1133
+ throw new WebhookHeadersShapeError(object, field, describeRejected(value));
1134
+ }
1135
+ var WEBHOOK_HEADERS_GATE_PACKAGE = "plugin-webhooks:headers-shape-gate";
1136
+ var GATE_PRIORITY = 50;
1137
+ function bindWebhookHeadersShapeGate(engine, logger) {
1138
+ if (typeof engine?.registerHook !== "function") return;
1139
+ const handler = (ctx) => {
1140
+ assertWritableWebhookHeaders(ctx?.input?.data);
1141
+ };
1142
+ for (const event of ["beforeInsert", "beforeUpdate"]) {
1143
+ engine.registerHook(event, handler, {
1144
+ object: WEBHOOK_OBJECT,
1145
+ packageId: WEBHOOK_HEADERS_GATE_PACKAGE,
1146
+ priority: GATE_PRIORITY
1147
+ });
1148
+ }
1149
+ logger?.info?.("[webhook] headers_secret shape gate bound (refuses non-flat-string-map plaintext)");
1150
+ }
1151
+ function unbindWebhookHeadersShapeGate(engine) {
1152
+ if (typeof engine?.unregisterHooksByPackage === "function") {
1153
+ engine.unregisterHooksByPackage(WEBHOOK_HEADERS_GATE_PACKAGE);
1154
+ }
1155
+ }
1156
+
1065
1157
  // src/webhook-outbox-plugin.ts
1066
1158
  var WebhookOutboxPlugin = class {
1067
1159
  constructor(options = {}) {
@@ -1115,7 +1207,7 @@ var WebhookOutboxPlugin = class {
1115
1207
  try {
1116
1208
  const i18n = ctx.getService("i18n");
1117
1209
  if (i18n && typeof i18n.loadTranslations === "function") {
1118
- const { WebhooksTranslations } = await import("./translations-U32QIEPB.js");
1210
+ const { WebhooksTranslations } = await import("./translations-IAKF6NAP.js");
1119
1211
  for (const [locale, data] of Object.entries(WebhooksTranslations)) {
1120
1212
  i18n.loadTranslations(locale, data);
1121
1213
  }
@@ -1136,16 +1228,44 @@ var WebhookOutboxPlugin = class {
1136
1228
  autoEnqueue: autoEnqueueOpt !== false
1137
1229
  });
1138
1230
  }
1139
- async dispose() {
1231
+ /**
1232
+ * Teardown — the kernel's ONLY teardown hook.
1233
+ *
1234
+ * [#10772] This body used to be spelled `dispose()`. `Plugin`
1235
+ * (`@objectstack/core`'s `types.ts`) declares `init()`, `start?(ctx)` and
1236
+ * `destroy?()` and no `dispose()`, and `ObjectKernel.performShutdown()` /
1237
+ * `LiteKernel.destroy()` walk the plugins in reverse calling
1238
+ * `plugin.destroy()` — so after `await kernel.shutdown()` had RESOLVED the
1239
+ * auto-enqueuer was still running and both engine hooks were still bound.
1240
+ * Measured on the same revision: `dispose()` had ZERO callers anywhere in
1241
+ * the repo, so this teardown had never run in any process at all.
1242
+ *
1243
+ * Idempotent: `boundEngine` is cleared as it is unbound, so a second
1244
+ * teardown is a no-op rather than a second unbind.
1245
+ */
1246
+ async destroy() {
1140
1247
  await this.autoEnqueuer?.stop();
1141
1248
  if (this.boundEngine) {
1142
1249
  try {
1143
1250
  unbindWebhookProvenanceStamp(this.boundEngine);
1144
1251
  } catch {
1145
1252
  }
1253
+ try {
1254
+ unbindWebhookHeadersShapeGate(this.boundEngine);
1255
+ } catch {
1256
+ }
1146
1257
  this.boundEngine = void 0;
1147
1258
  }
1148
1259
  }
1260
+ /**
1261
+ * Retained alias for {@link destroy}. Kept because it is public API of an
1262
+ * exported class: an embedder may have learned to call it directly
1263
+ * precisely BECAUSE the kernel never did, and deleting it would break them.
1264
+ * Same signature, same return type — a direct caller sees no change.
1265
+ */
1266
+ async dispose() {
1267
+ await this.destroy();
1268
+ }
1149
1269
  getMessaging(ctx) {
1150
1270
  const svc = this.tryGetService(ctx, ["messaging"]);
1151
1271
  return svc && typeof svc.enqueueHttp === "function" ? svc : void 0;
@@ -1170,6 +1290,7 @@ var WebhookOutboxPlugin = class {
1170
1290
  }
1171
1291
  this.boundEngine = engine;
1172
1292
  bindWebhookProvenanceStamp(engine, ctx.logger);
1293
+ bindWebhookHeadersShapeGate(engine, ctx.logger);
1173
1294
  let metadataService;
1174
1295
  try {
1175
1296
  metadataService = ctx.getService("metadata");
@@ -1256,8 +1377,21 @@ var WebhookOutboxPlugin = class {
1256
1377
  }
1257
1378
  /**
1258
1379
  * Mount POST /api/v1/webhooks/redeliver on the host Hono app, if one is
1259
- * available. Delegates to `messaging.redeliverHttp(deliveryId)`. Auth is the
1260
- * better-auth session cookie — every authenticated user counts.
1380
+ * available. Delegates to `messaging.redeliverHttp(deliveryId, …)`. Auth is
1381
+ * the better-auth session cookie — every authenticated user counts.
1382
+ *
1383
+ * [#10740] Which is precisely why the caller's ACTIVE ORGANIZATION is
1384
+ * resolved here and threaded into the call. `sys_http_delivery` is
1385
+ * tenant-scoped, and this is the one door on it a request can reach: an
1386
+ * unscoped replay from here is an authenticated user reaching another
1387
+ * organization's delivery row on a walled deployment. With the tenant
1388
+ * threaded, a row outside the caller's organization is simply not found.
1389
+ *
1390
+ * ⚠️ A session with no active organization threads `undefined`, and the
1391
+ * driver's tenant-audit line then fires for that write. That is deliberate:
1392
+ * the deployment could not tell us who is asking, and reporting the gap is
1393
+ * the correct outcome. ⛔ It is never repaired with `bypassTenantAudit`,
1394
+ * which would silence the report without closing anything.
1261
1395
  */
1262
1396
  registerAdminRoutes(ctx) {
1263
1397
  const http = this.tryGetService(ctx, ["http-server"]);
@@ -1269,8 +1403,9 @@ var WebhookOutboxPlugin = class {
1269
1403
  const messaging = this.getMessaging(ctx);
1270
1404
  if (!rawApp || !messaging) return;
1271
1405
  rawApp.post("/api/v1/webhooks/redeliver", async (c) => {
1272
- const userId = await this.resolveSessionUserId(ctx, c);
1273
- if (!userId) {
1406
+ const session = await this.resolveSession(ctx, c);
1407
+ const userId = session?.user?.id;
1408
+ if (typeof userId !== "string" || userId.length === 0) {
1274
1409
  return c.json(
1275
1410
  { success: false, error: { code: "UNAUTHENTICATED", message: "Sign in to redeliver webhook deliveries." } },
1276
1411
  401
@@ -1290,8 +1425,10 @@ var WebhookOutboxPlugin = class {
1290
1425
  );
1291
1426
  }
1292
1427
  try {
1293
- const row = await messaging.redeliverHttp(deliveryId);
1294
- ctx.logger.info?.("[webhook-outbox] redelivered", { deliveryId, requestedBy: userId });
1428
+ const activeOrg = session?.session?.activeOrganizationId;
1429
+ const tenantId = typeof activeOrg === "string" && activeOrg.length > 0 ? activeOrg : void 0;
1430
+ const row = await messaging.redeliverHttp(deliveryId, { tenantId });
1431
+ ctx.logger.info?.("[webhook-outbox] redelivered", { deliveryId, requestedBy: userId, tenantId });
1295
1432
  return c.json({ success: true, data: { id: row.id, status: row.status } });
1296
1433
  } catch (err) {
1297
1434
  const code = err?.code;
@@ -1310,7 +1447,18 @@ var WebhookOutboxPlugin = class {
1310
1447
  });
1311
1448
  ctx.logger.info?.("[webhook-outbox] redeliver endpoint mounted at POST /api/v1/webhooks/redeliver");
1312
1449
  }
1313
- async resolveSessionUserId(ctx, c) {
1450
+ /**
1451
+ * [#10740] The better-auth session envelope (`{ user, session }`) for this
1452
+ * request, or `undefined`.
1453
+ *
1454
+ * Widened from the previous `resolveSessionUserId` because the route now
1455
+ * needs two facts from ONE lookup: who is asking (`user.id`, the
1456
+ * authentication gate) and which organization they are asking as
1457
+ * (`session.activeOrganizationId`, the tenant threaded into the write).
1458
+ * Resolving them separately would mean two `getSession` calls that can
1459
+ * disagree.
1460
+ */
1461
+ async resolveSession(ctx, c) {
1314
1462
  try {
1315
1463
  const authService = this.tryGetService(ctx, ["auth"]);
1316
1464
  if (!authService) return void 0;
@@ -1319,9 +1467,7 @@ var WebhookOutboxPlugin = class {
1319
1467
  api = await authService.getApi();
1320
1468
  }
1321
1469
  if (!api?.getSession) return void 0;
1322
- const session = await api.getSession({ headers: c.req.raw.headers });
1323
- const uid2 = session?.user?.id;
1324
- return typeof uid2 === "string" && uid2.length > 0 ? uid2 : void 0;
1470
+ return await api.getSession({ headers: c.req.raw.headers });
1325
1471
  } catch {
1326
1472
  return void 0;
1327
1473
  }
@@ -1331,8 +1477,14 @@ export {
1331
1477
  AutoEnqueuer,
1332
1478
  SysWebhook,
1333
1479
  WEBHOOK_HEADERS_FIELD,
1480
+ WEBHOOK_HEADERS_SHAPE_REFUSAL_CODE,
1481
+ WEBHOOK_HEADERS_SHAPE_REFUSAL_STATUS,
1334
1482
  WEBHOOK_SECRET_FIELD,
1483
+ WebhookHeadersShapeError,
1335
1484
  WebhookOutboxPlugin,
1336
- migrateLegacyWebhookSecrets
1485
+ assertWritableWebhookHeaders,
1486
+ bindWebhookHeadersShapeGate,
1487
+ migrateLegacyWebhookSecrets,
1488
+ unbindWebhookHeadersShapeGate
1337
1489
  };
1338
1490
  //# sourceMappingURL=index.js.map