@fedify/fedify 2.4.0-dev.1668 → 2.4.0-dev.1727

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 (150) hide show
  1. package/README.md +1 -1
  2. package/dist/{assert-CJ1KXcvv.mjs → assert-CebeUf0C.mjs} +1 -1
  3. package/dist/assert_exists-DtzkXC47.mjs +28 -0
  4. package/dist/assert_false-3pLLLsO4.mjs +23 -0
  5. package/dist/assert_greater_or_equal-BApOTh1h.mjs +30 -0
  6. package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-g3WcyYNR.mjs} +1 -1
  7. package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-BN7yDMWe.mjs} +1 -1
  8. package/dist/assert_rejects-wFbSPn1O.mjs +39 -0
  9. package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-Bpjl4Psw.mjs} +1 -1
  10. package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BJ5wuvR2.mjs} +1 -1
  11. package/dist/{builder-iXZ5jNVg.mjs → builder-f5-YwHfj.mjs} +5 -3
  12. package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-BT7CiPbz.mjs} +191 -9
  13. package/dist/circuit-breaker-test-utils-BYIHoliq.mjs +14 -0
  14. package/dist/{client-CjF5IYQS.mjs → client-CtxvmazV.mjs} +63 -5
  15. package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
  16. package/dist/compat/mod.cjs +1 -1
  17. package/dist/compat/mod.d.cts +1 -1
  18. package/dist/compat/mod.d.ts +1 -1
  19. package/dist/compat/mod.js +1 -1
  20. package/dist/compat/outgoing-jsonld.test.mjs +3 -3
  21. package/dist/compat/public-audience.test.mjs +3 -3
  22. package/dist/compat/transformers.test.mjs +5 -5
  23. package/dist/{context-C05Rgxmu.d.cts → context-B-dsBjZ9.d.cts} +281 -7
  24. package/dist/{context-Cjnr0ig1.d.ts → context-DrLXudbA.d.ts} +281 -7
  25. package/dist/{deno-DtHcfB0f.mjs → deno-Deyoxesx.mjs} +1 -1
  26. package/dist/{docloader-0vAfF6XW.mjs → docloader-B3kuDBg9.mjs} +2 -2
  27. package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
  28. package/dist/federation/builder.test.mjs +18 -5
  29. package/dist/federation/circuit-breaker.test.mjs +626 -7
  30. package/dist/federation/collection.test.mjs +3 -3
  31. package/dist/federation/handler.test.mjs +14 -13
  32. package/dist/federation/idempotency.test.mjs +6 -6
  33. package/dist/federation/inbox.test.mjs +3 -3
  34. package/dist/federation/keycache.test.mjs +5 -5
  35. package/dist/federation/kv.test.mjs +3 -3
  36. package/dist/federation/metrics.test.mjs +4 -4
  37. package/dist/federation/middleware.test.mjs +37 -28
  38. package/dist/federation/mod.cjs +18 -540
  39. package/dist/federation/mod.d.cts +3 -4
  40. package/dist/federation/mod.d.ts +3 -4
  41. package/dist/federation/mod.js +1 -1
  42. package/dist/federation/mq.test.mjs +32 -5
  43. package/dist/federation/negotiation.test.mjs +4 -4
  44. package/dist/federation/retry.test.mjs +3 -3
  45. package/dist/federation/router.test.mjs +7 -7
  46. package/dist/federation/send.test.mjs +28 -26
  47. package/dist/federation/tasks/codec.test.mjs +4 -4
  48. package/dist/federation/tasks/enqueue.test.mjs +4 -4
  49. package/dist/federation/tasks/tasks.test.mjs +6 -6
  50. package/dist/federation/temporal.test.mjs +4 -4
  51. package/dist/federation/webfinger.test.mjs +74 -12
  52. package/dist/{middleware-DzIUFRO2.cjs → federation-BzRjeOky.cjs} +855 -114
  53. package/dist/{temporal-polyfill-BCdTHexd.cjs → http-AyKngJ_h.cjs} +2801 -104
  54. package/dist/{http-BbnOMQoy.js → http-Bw6s_dOB.js} +3 -3
  55. package/dist/{http-DUmWq5Pw.mjs → http-TjhZfiSN.mjs} +5 -5
  56. package/dist/{key-CM7wDnZG.mjs → key-b4kf4yZT.mjs} +2 -2
  57. package/dist/{kv-cache-zmgG1HUp.cjs → kv-cache-BTVaZrf-.cjs} +2 -4
  58. package/dist/{kv-cache-ZQ22hm44.js → kv-cache-CQsh-KUw.js} +1 -1
  59. package/dist/{kv-cache-DEXKemJ9.mjs → kv-cache-CpILvhUV.mjs} +1 -1
  60. package/dist/{ld-BttH_-8K.mjs → ld-CKujkAfG.mjs} +5 -5
  61. package/dist/{metrics-DT-CnD1g.mjs → metrics-AlCGTDrr.mjs} +2 -2
  62. package/dist/metrics-DVKYY6K1.d.cts +36 -0
  63. package/dist/metrics-DoRtI0qU.d.ts +36 -0
  64. package/dist/{middleware-DrroX3aD.mjs → middleware-C9Y5lLZN.mjs} +60 -43
  65. package/dist/{middleware-D3IAVMcT.js → middleware-CZcuedl-.js} +241 -40
  66. package/dist/{middleware-ChlYS0Af.mjs → middleware-LPbZz6-1.mjs} +1 -1
  67. package/dist/{mod-BrpVsSfY.d.cts → mod-CLHkCwLm.d.cts} +3 -3
  68. package/dist/{mod-B0hW12_O.d.cts → mod-CuDDaIIt.d.cts} +1 -1
  69. package/dist/{mod-COIAjwRS.d.ts → mod-D-IhOEOP.d.ts} +1 -1
  70. package/dist/{mod-DdONo-ep.d.cts → mod-D-abvu2C.d.cts} +1 -1
  71. package/dist/{mod-BsCE5Ihq.d.ts → mod-D6O8C7eN.d.ts} +1 -1
  72. package/dist/{mod-CNef4in0.d.ts → mod-DypxPS9H.d.ts} +3 -3
  73. package/dist/mod.cjs +24 -25
  74. package/dist/mod.d.cts +8 -9
  75. package/dist/mod.d.ts +8 -9
  76. package/dist/mod.js +7 -7
  77. package/dist/nodeinfo/client.test.mjs +64 -4
  78. package/dist/nodeinfo/handler.test.mjs +5 -5
  79. package/dist/nodeinfo/mod.cjs +1 -1
  80. package/dist/nodeinfo/mod.d.cts +1 -1
  81. package/dist/nodeinfo/mod.d.ts +1 -1
  82. package/dist/nodeinfo/mod.js +1 -1
  83. package/dist/nodeinfo/types.test.mjs +4 -4
  84. package/dist/otel/exporter.test.mjs +3 -3
  85. package/dist/otel/mod.cjs +0 -1
  86. package/dist/otel/mod.d.cts +1 -1
  87. package/dist/otel/mod.d.ts +1 -1
  88. package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-BQSHRLhg.mjs} +2 -2
  89. package/dist/{owner-DZALmhdl.mjs → owner-D1O8v8FB.mjs} +2 -2
  90. package/dist/{proof-CqA-2nfW.js → proof-BTBolSnd.js} +6 -6
  91. package/dist/{proof-a60oVJ_4.cjs → proof-DcSm-2eG.cjs} +10 -11
  92. package/dist/{proof-DiQ0uBal.mjs → proof-DeMR7Akd.mjs} +6 -6
  93. package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--9hx7IGx.mjs} +1 -1
  94. package/dist/runtime/mod.cjs +1 -1
  95. package/dist/runtime/mod.js +1 -1
  96. package/dist/{send-DcwZnQWG.mjs → send-DG7JG280.mjs} +3 -3
  97. package/dist/sig/accept.test.mjs +1 -1
  98. package/dist/sig/http.test.mjs +37 -11
  99. package/dist/sig/key.test.mjs +6 -6
  100. package/dist/sig/ld.test.mjs +9 -7
  101. package/dist/sig/mod.cjs +2 -2
  102. package/dist/sig/mod.d.cts +3 -3
  103. package/dist/sig/mod.d.ts +3 -3
  104. package/dist/sig/mod.js +2 -2
  105. package/dist/sig/owner.test.mjs +6 -6
  106. package/dist/sig/proof.test.mjs +10 -8
  107. package/dist/std__assert-CxFT0MNt.mjs +8 -0
  108. package/dist/{temporal-Bpxpsl2k.mjs → temporal-DA3jx8x1.mjs} +3 -3
  109. package/dist/testing/mod.d.mts +10 -0
  110. package/dist/testing/mod.mjs +2 -2
  111. package/dist/{testing-CO7ftO8A.mjs → testing-BX0Nicyq.mjs} +2 -2
  112. package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
  113. package/dist/{types-DC1pNgJA.js → types-Dtyf8lVj.js} +63 -5
  114. package/dist/{types-CPym83zc.cjs → types-RXm5YxpC.cjs} +62 -5
  115. package/dist/utils/docloader.test.mjs +7 -7
  116. package/dist/utils/kv-cache.test.mjs +1 -1
  117. package/dist/utils/mod.cjs +1 -1
  118. package/dist/utils/mod.d.cts +1 -1
  119. package/dist/utils/mod.d.ts +1 -1
  120. package/dist/utils/mod.js +1 -1
  121. package/package.json +7 -7
  122. package/skills/fedify/SKILL.md +462 -0
  123. package/dist/assert_rejects-EgstcQy5.mjs +0 -82
  124. package/dist/http-BpT5cE6r.cjs +0 -2694
  125. package/dist/metrics-DN13mCAw.d.cts +0 -294
  126. package/dist/metrics-v_e0n5GQ.d.ts +0 -294
  127. package/dist/std__assert-B2NnY9bq.mjs +0 -79
  128. /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
  129. /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
  130. /package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-CBlRfA-_.mjs} +0 -0
  131. /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
  132. /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
  133. /package/dist/{codec-BBga-FWI.mjs → codec-peqOdBaE.mjs} +0 -0
  134. /package/dist/{context-BJXxPElm.mjs → context-Dt36KFEJ.mjs} +0 -0
  135. /package/dist/{http-VyDTd4G3.d.cts → http-6TI0tI03.d.cts} +0 -0
  136. /package/dist/{http-lf8Hsd91.d.ts → http-CdrSn2ip.d.ts} +0 -0
  137. /package/dist/{keycache-_DHqz5z_.mjs → keycache-0Jk78XmB.mjs} +0 -0
  138. /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
  139. /package/dist/{kv-D6hNiMTK.d.ts → kv-BhNbN8Ed.d.cts} +0 -0
  140. /package/dist/{kv-gJ8LYbxX.d.cts → kv-BhNbN8Ed.d.ts} +0 -0
  141. /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
  142. /package/dist/{mq-Bo7Etgh6.mjs → mq-Dm1Wmtm-.mjs} +0 -0
  143. /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
  144. /package/dist/{owner-CnngXDNJ.d.ts → owner-DGSfntI-.d.ts} +0 -0
  145. /package/dist/{owner-DEvZuyOE.d.cts → owner-r_waTiQj.d.cts} +0 -0
  146. /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
  147. /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
  148. /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
  149. /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
  150. /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
@@ -1,4 +1,16 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
+ let _logtape_logtape = require("@logtape/logtape");
3
+ let _fedify_vocab = require("@fedify/vocab");
4
+ let _opentelemetry_api = require("@opentelemetry/api");
5
+ let byte_encodings_hex = require("byte-encodings/hex");
6
+ let structured_field_values = require("structured-field-values");
7
+ let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
8
+ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
9
+ let byte_encodings_base64 = require("byte-encodings/base64");
10
+ //#region deno.json
11
+ var name = "@fedify/fedify";
12
+ var version = "2.4.0-dev.1727+1eadcb04";
13
+ //#endregion
2
14
  //#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/chunks/root.js
3
15
  const NativeTemporal = globalThis.Temporal;
4
16
  //#endregion
@@ -150,7 +162,7 @@ function parseInt0(s) {
150
162
  function padNumber(digits, num) {
151
163
  return String(num).padStart(digits, "0");
152
164
  }
153
- const padNumber2 = /* @__PURE__ */ bindArgs(padNumber, 2);
165
+ const padNumber2 = /*@__PURE__*/ bindArgs(padNumber, 2);
154
166
  function compareNumbers(a, b) {
155
167
  return Math.sign(a - b);
156
168
  }
@@ -218,7 +230,7 @@ const unitNameMap = {
218
230
  month: 8,
219
231
  year: 9
220
232
  };
221
- const unitNamesAsc = /* @__PURE__ */ Object.keys(unitNameMap);
233
+ const unitNamesAsc = /*@__PURE__*/ Object.keys(unitNameMap);
222
234
  const nanoInMicro = nanoInMicro$1;
223
235
  const nanoInMilli = nanoInMilli$1;
224
236
  const nanoInSec = nanoInSec$1;
@@ -234,19 +246,19 @@ const unitNanoMap = [
234
246
  nanoInHour,
235
247
  nanoInUtcDay
236
248
  ];
237
- const bigNanoInMicro = /* @__PURE__ */ BigInt(nanoInMicro);
238
- const bigNanoInMilli = /* @__PURE__ */ BigInt(nanoInMilli);
239
- const bigNanoInSec = /* @__PURE__ */ BigInt(nanoInSec);
240
- const bigNanoInMinute = /* @__PURE__ */ BigInt(nanoInMinute);
241
- const bigNanoInHour = /* @__PURE__ */ BigInt(nanoInHour);
242
- const bigNanoInUtcDay = /* @__PURE__ */ BigInt(nanoInUtcDay);
249
+ const bigNanoInMicro = /*@__PURE__*/ BigInt(nanoInMicro);
250
+ const bigNanoInMilli = /*@__PURE__*/ BigInt(nanoInMilli);
251
+ const bigNanoInSec = /*@__PURE__*/ BigInt(nanoInSec);
252
+ const bigNanoInMinute = /*@__PURE__*/ BigInt(nanoInMinute);
253
+ const bigNanoInHour = /*@__PURE__*/ BigInt(nanoInHour);
254
+ const bigNanoInUtcDay = /*@__PURE__*/ BigInt(nanoInUtcDay);
243
255
  function divideBigNanoToExactNumber(bigNano, divisorNano) {
244
256
  const days = Number(bigNano / bigNanoInUtcDay);
245
257
  const timeNano = Number(bigNano % bigNanoInUtcDay);
246
258
  return days * (nanoInUtcDay / divisorNano) + (Math.trunc(timeNano / divisorNano) + timeNano % divisorNano / divisorNano);
247
259
  }
248
- const timeFieldNamesAsc = /* @__PURE__ */ unitNamesAsc.slice(0, 6);
249
- const timeGetters = /* @__PURE__ */ createPropGetters(timeFieldNamesAsc);
260
+ const timeFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.slice(0, 6);
261
+ const timeGetters = /*@__PURE__*/ createPropGetters(timeFieldNamesAsc);
250
262
  const yearFieldNamesAsc = ["year"];
251
263
  const dayFieldNamesAsc = ["day"];
252
264
  const calendarDateFieldNamesAsc = [
@@ -268,24 +280,24 @@ const monthDayFieldNames = [
268
280
  "month",
269
281
  "monthCode"
270
282
  ];
271
- const timeFieldNamesAlpha = /* @__PURE__ */ sortStrings(timeFieldNamesAsc);
272
- const yearFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearFieldNamesAsc);
273
- const yearMonthFieldNamesAlpha = /* @__PURE__ */ sortStrings(monthFieldNames, yearFieldNamesAsc);
274
- const yearMonthFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearMonthFieldNamesAlpha);
275
- const yearMonthCodeFieldNamesAlpha = /* @__PURE__ */ sortStrings(["monthCode"], yearFieldNamesAsc);
276
- const yearMonthCodeFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
277
- const monthCodeDayFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, ["monthCode"]);
278
- const dateFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, yearMonthFieldNamesAlpha);
279
- const dateFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthFieldNamesAlpha);
280
- const dateTimeFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc);
281
- const dateTimeFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc);
282
- const dateTimeAndOffsetFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames);
283
- const dateTimeAndOffsetFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames);
284
- const dateTimeAndZoneFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
285
- const dateTimeAndZoneFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
286
- const yearMonthCodeDayFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, yearMonthCodeFieldNamesAlpha);
287
- const yearMonthCodeDayFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
288
- const timeFieldDefaults = /* @__PURE__ */ zipPropsConst(timeFieldNamesAsc, 0);
283
+ const timeFieldNamesAlpha = /*@__PURE__*/ sortStrings(timeFieldNamesAsc);
284
+ const yearFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearFieldNamesAsc);
285
+ const yearMonthFieldNamesAlpha = /*@__PURE__*/ sortStrings(monthFieldNames, yearFieldNamesAsc);
286
+ const yearMonthFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthFieldNamesAlpha);
287
+ const yearMonthCodeFieldNamesAlpha = /*@__PURE__*/ sortStrings(["monthCode"], yearFieldNamesAsc);
288
+ const yearMonthCodeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
289
+ const monthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, ["monthCode"]);
290
+ const dateFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthFieldNamesAlpha);
291
+ const dateFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthFieldNamesAlpha);
292
+ const dateTimeFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc);
293
+ const dateTimeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc);
294
+ const dateTimeAndOffsetFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames);
295
+ const dateTimeAndOffsetFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames);
296
+ const dateTimeAndZoneFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
297
+ const dateTimeAndZoneFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
298
+ const yearMonthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthCodeFieldNamesAlpha);
299
+ const yearMonthCodeDayFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
300
+ const timeFieldDefaults = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, 0);
289
301
  function validateTimeFields(timeFields) {
290
302
  return constrainTimeFields(timeFields, 1), timeFields;
291
303
  }
@@ -514,20 +526,20 @@ function computeCalendarWeekOfYear(calendar, isoDate) {
514
526
  function computeCalendarYearOfWeek(calendar, isoDate) {
515
527
  return calendar === void 0 ? computeIsoWeekFields(isoDate).yearOfWeek : void 0;
516
528
  }
517
- const durationFieldNamesAsc = /* @__PURE__ */ unitNamesAsc.map((unitName) => unitName + "s");
518
- const durationGetters = /* @__PURE__ */ createPropGetters(durationFieldNamesAsc);
519
- const durationFieldNamesAlpha = /* @__PURE__ */ sortStrings(durationFieldNamesAsc);
520
- const durationTimeFieldNamesAsc = /* @__PURE__ */ durationFieldNamesAsc.slice(0, 6);
521
- const durationDateFieldNamesAsc = /* @__PURE__ */ durationFieldNamesAsc.slice(6);
522
- const durationCalendarFieldNamesAsc = /* @__PURE__ */ durationDateFieldNamesAsc.slice(1);
523
- const durationFieldIndexes = /* @__PURE__ */ durationFieldNamesAsc.reduce((indexes, fieldName, i) => (indexes[fieldName] = i, indexes), {});
524
- const durationFieldDefaults = /* @__PURE__ */ zipPropsConst(durationFieldNamesAsc, 0);
525
- const durationTimeFieldDefaults = /* @__PURE__ */ zipPropsConst(durationTimeFieldNamesAsc, 0);
526
- const clearDurationFields = /* @__PURE__ */ bindArgs(zeroOutProps, durationFieldNamesAsc);
529
+ const durationFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.map((unitName) => unitName + "s");
530
+ const durationGetters = /*@__PURE__*/ createPropGetters(durationFieldNamesAsc);
531
+ const durationFieldNamesAlpha = /*@__PURE__*/ sortStrings(durationFieldNamesAsc);
532
+ const durationTimeFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(0, 6);
533
+ const durationDateFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(6);
534
+ const durationCalendarFieldNamesAsc = /*@__PURE__*/ durationDateFieldNamesAsc.slice(1);
535
+ const durationFieldIndexes = /*@__PURE__*/ durationFieldNamesAsc.reduce((indexes, fieldName, i) => (indexes[fieldName] = i, indexes), {});
536
+ const durationFieldDefaults = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, 0);
537
+ const durationTimeFieldDefaults = /*@__PURE__*/ zipPropsConst(durationTimeFieldNamesAsc, 0);
538
+ const clearDurationFields = /*@__PURE__*/ bindArgs(zeroOutProps, durationFieldNamesAsc);
527
539
  function requirePropDefined(optionName, optionVal) {
528
540
  return optionVal ?? throwRangeError(missingField(optionName)), optionVal;
529
541
  }
530
- const requireString = /* @__PURE__ */ bindArgs(requireType, "string");
542
+ const requireString = /*@__PURE__*/ bindArgs(requireType, "string");
531
543
  function requireType(typeName, arg, entityName = typeName) {
532
544
  return typeof arg !== typeName && throwTypeError(invalidEntity(entityName, arg)), arg;
533
545
  }
@@ -639,17 +651,17 @@ function coerceChoiceOption(optionName, enumNameMap, options, defaultChoice = 0)
639
651
  const enumNum = enumNameMap[enumStr];
640
652
  return void 0 === enumNum && throwRangeError(invalidChoice(optionName, enumStr, enumNameMap)), enumNum;
641
653
  }
642
- const coerceSmallestUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, smallestUnitStr);
643
- const coerceLargestUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, "largestUnit");
644
- const coerceTotalUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, "unit");
645
- const coerceOverflow = /* @__PURE__ */ bindArgs(coerceChoiceOption, "overflow", overflowMap);
646
- const coerceEpochDisambig = /* @__PURE__ */ bindArgs(coerceChoiceOption, "disambiguation", epochDisambigMap);
647
- const coerceOffsetDisambig = /* @__PURE__ */ bindArgs(coerceChoiceOption, "offset", offsetDisambigMap);
648
- const coerceCalendarDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "calendarName", calendarDisplayMap);
649
- const coerceTimeZoneDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "timeZoneName", timeZoneDisplayMap);
650
- const coerceOffsetDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "offset", offsetDisplayMap);
651
- const coerceRoundingMode = /* @__PURE__ */ bindArgs(coerceChoiceOption, "roundingMode", roundingModeMap);
652
- const coerceDirection = /* @__PURE__ */ bindArgs(coerceChoiceOption, "direction", directionMap);
654
+ const coerceSmallestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, smallestUnitStr);
655
+ const coerceLargestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "largestUnit");
656
+ const coerceTotalUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "unit");
657
+ const coerceOverflow = /*@__PURE__*/ bindArgs(coerceChoiceOption, "overflow", overflowMap);
658
+ const coerceEpochDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "disambiguation", epochDisambigMap);
659
+ const coerceOffsetDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisambigMap);
660
+ const coerceCalendarDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "calendarName", calendarDisplayMap);
661
+ const coerceTimeZoneDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "timeZoneName", timeZoneDisplayMap);
662
+ const coerceOffsetDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisplayMap);
663
+ const coerceRoundingMode = /*@__PURE__*/ bindArgs(coerceChoiceOption, "roundingMode", roundingModeMap);
664
+ const coerceDirection = /*@__PURE__*/ bindArgs(coerceChoiceOption, "direction", directionMap);
653
665
  function validateRoundingInc(roundingInc, smallestUnit, allowManyLargeUnits, solarMode) {
654
666
  const upUnitNano = solarMode ? nanoInUtcDay : unitNanoMap[smallestUnit + 1];
655
667
  if (upUnitNano) {
@@ -703,8 +715,8 @@ function refineZonedFieldOptions(options, defaultOffsetDisambig = 0) {
703
715
  epochDisambig
704
716
  ];
705
717
  }
706
- const epochNanoMax = /* @__PURE__ */ BigInt(1e8) * bigNanoInUtcDay;
707
- const epochNanoMin = /* @__PURE__ */ BigInt(-1e8) * bigNanoInUtcDay;
718
+ const epochNanoMax = /*@__PURE__*/ BigInt(1e8) * bigNanoInUtcDay;
719
+ const epochNanoMin = /*@__PURE__*/ BigInt(-1e8) * bigNanoInUtcDay;
708
720
  const plainDateEpochNanoMin = epochNanoMin - bigNanoInUtcDay;
709
721
  const isoYearMonthIndexMin = -3261848;
710
722
  function checkIsoYearMonthInBounds(isoDate) {
@@ -975,11 +987,12 @@ function nudgeRelativeDuration(sign, durationFields, endEpochNano, _largestUnit,
975
987
  }
976
988
  function getTimeZoneTransitionEpochNanoseconds(slots, options) {
977
989
  return slots.timeZone.U(slots.epochNanoseconds, ((options) => {
978
- const res = coerceDirection(normalizeOptionsOrString(options, "direction"), 0);
990
+ const normalizedOptions = normalizeOptionsOrString(options, "direction");
991
+ const res = coerceDirection(normalizedOptions, 0);
979
992
  return res || throwRangeError(invalidEntity("direction", res)), res;
980
993
  })(options));
981
994
  }
982
- const zonedEpochSlotsToIso = /* @__PURE__ */ memoize(_zonedEpochSlotsToIso, WeakMap);
995
+ const zonedEpochSlotsToIso = /*@__PURE__*/ memoize(_zonedEpochSlotsToIso, WeakMap);
983
996
  function _zonedEpochSlotsToIso(slots) {
984
997
  const { epochNanoseconds, timeZone } = slots;
985
998
  const offsetNanoseconds = timeZone.C(epochNanoseconds);
@@ -1545,7 +1558,7 @@ function resolveTimeFields(fields, overflow) {
1545
1558
  ...fields
1546
1559
  }), overflow);
1547
1560
  }
1548
- const offsetRegExp = /* @__PURE__ */ createRegExp("([+-])(\\d{2})(?::?(\\d{2})(?::?(\\d{2})(?:[.,](\\d{1,9}))?)?)?");
1561
+ const offsetRegExp = /*@__PURE__*/ createRegExp("([+-])(\\d{2})(?::?(\\d{2})(?::?(\\d{2})(?:[.,](\\d{1,9}))?)?)?");
1549
1562
  function parseOffsetNano(s) {
1550
1563
  const offsetNano = parseOffsetNanoMaybe(s);
1551
1564
  return void 0 === offsetNano && throwRangeError(failedParse(s)), offsetNano;
@@ -1580,13 +1593,13 @@ const dateFieldRefiners = {
1580
1593
  },
1581
1594
  day: toPositiveIntegerWithTruncation
1582
1595
  };
1583
- const timeFieldRefiners = /* @__PURE__ */ zipPropsConst(timeFieldNamesAsc, toIntegerWithTrunc);
1584
- const durationFieldRefiners = /* @__PURE__ */ zipPropsConst(durationFieldNamesAsc, toStrictInteger);
1596
+ const timeFieldRefiners = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, toIntegerWithTrunc);
1597
+ const durationFieldRefiners = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, toStrictInteger);
1585
1598
  const offsetFieldRefiners = { offset(offsetString) {
1586
1599
  return parseOffsetNano(toStringViaPrimitive(offsetString));
1587
1600
  } };
1588
- const dateTimeFieldRefiners = /* @__PURE__ */ Object.assign({}, dateFieldRefiners, timeFieldRefiners);
1589
- const zonedDateTimeFieldRefiners = /* @__PURE__ */ Object.assign({}, dateTimeFieldRefiners, offsetFieldRefiners);
1601
+ const dateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateFieldRefiners, timeFieldRefiners);
1602
+ const zonedDateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateTimeFieldRefiners, offsetFieldRefiners);
1590
1603
  function readAndRefineBagFields(bag, validFieldNames, fieldRefiners, requiredFieldNames, disallowEmpty = !requiredFieldNames) {
1591
1604
  const res = {};
1592
1605
  let anyMatching = 0;
@@ -1689,7 +1702,9 @@ const timeZonePeriodDaysByName = {
1689
1702
  const minPossibleTransitionSec = -388152e4;
1690
1703
  function refineTimeDisplayTuple(options, maxSmallestUnit = 4) {
1691
1704
  const subsecDigits = coerceFractionalSecondDigits(options);
1692
- return [coerceRoundingMode(options, 4), ...resolveSmallestUnitAndSubsecDigits(validateUnitRange(smallestUnitStr, coerceSmallestUnit(options), 0, maxSmallestUnit), subsecDigits)];
1705
+ const roundingMode = coerceRoundingMode(options, 4);
1706
+ const smallestUnit = coerceSmallestUnit(options);
1707
+ return [roundingMode, ...resolveSmallestUnitAndSubsecDigits(validateUnitRange(smallestUnitStr, smallestUnit, 0, maxSmallestUnit), subsecDigits)];
1693
1708
  }
1694
1709
  function refineDateDisplayOptions(options) {
1695
1710
  return coerceCalendarDisplay(normalizeOptions(options));
@@ -1864,15 +1879,17 @@ function resolveTimeZoneRecord(rawId) {
1864
1879
  kind: "fixed",
1865
1880
  ...offsetRecord
1866
1881
  };
1867
- return queryNamedTimeZoneRecord("UTC" === upperRawId ? "UTC" : ((rawId) => (badCharactersRegExp.test(rawId) && throwRangeError(invalidTimeZone(rawId)), icuRegExp.test(rawId) && throwRangeError("Forbidden ICU TimeZone"), rawId.toLowerCase().split("/").map((part, partI) => (part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part) ? part.toUpperCase() : part.replace(/baja|dumont|[a-z]+/g, (a, i) => a.length <= 2 && !partI || "in" === a || "chat" === a ? a.toUpperCase() : a.length > 2 || !i ? capitalize(a).replace(/island|noronha|murdo|rivadavia|urville/, capitalize) : a)).join("/")))(rawId));
1882
+ const normId = "UTC" === upperRawId ? "UTC" : ((rawId) => (badCharactersRegExp.test(rawId) && throwRangeError(invalidTimeZone(rawId)), icuRegExp.test(rawId) && throwRangeError("Forbidden ICU TimeZone"), rawId.toLowerCase().split("/").map((part, partI) => (part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part) ? part.toUpperCase() : part.replace(/baja|dumont|[a-z]+/g, (a, i) => a.length <= 2 && !partI || "in" === a || "chat" === a ? a.toUpperCase() : a.length > 2 || !i ? capitalize(a).replace(/island|noronha|murdo|rivadavia|urville/, capitalize) : a)).join("/")))(rawId);
1883
+ return queryNamedTimeZoneRecord(normId);
1868
1884
  }
1869
- const queryNamedTimeZoneRecord = /* @__PURE__ */ memoize((normId) => {
1885
+ const queryNamedTimeZoneRecord = /*@__PURE__*/ memoize((normId) => {
1870
1886
  if ("UTC" === normId) return {
1871
1887
  kind: "utc",
1872
1888
  id: normId,
1873
1889
  o: normId
1874
1890
  };
1875
- const format = queryTimeZoneIntlFormat(normId.toUpperCase());
1891
+ const upperNormId = normId.toUpperCase();
1892
+ const format = queryTimeZoneIntlFormat(upperNormId);
1876
1893
  return {
1877
1894
  kind: "named",
1878
1895
  id: normId,
@@ -1880,7 +1897,7 @@ const queryNamedTimeZoneRecord = /* @__PURE__ */ memoize((normId) => {
1880
1897
  o: format.resolvedOptions().timeZone
1881
1898
  };
1882
1899
  });
1883
- const queryTimeZoneIntlFormat = /* @__PURE__ */ memoize((upperNormId) => new RawDateTimeFormat("en-u-hc-h23", {
1900
+ const queryTimeZoneIntlFormat = /*@__PURE__*/ memoize((upperNormId) => new RawDateTimeFormat("en-u-hc-h23", {
1884
1901
  calendar: "iso8601",
1885
1902
  timeZone: upperNormId,
1886
1903
  era: "short",
@@ -1895,7 +1912,7 @@ function queryTimeZone(rawTimeZoneId) {
1895
1912
  const record = resolveTimeZoneRecord(rawTimeZoneId);
1896
1913
  return queryTimeZoneRecord(record.id, record);
1897
1914
  }
1898
- const queryTimeZoneRecord = /* @__PURE__ */ memoize((normTimeZoneId, record) => "named" === record.kind ? new IntlTimeZone(normTimeZoneId, record.o, record.format) : new FixedTimeZone(normTimeZoneId, record.o, "fixed" === record.kind ? record._ : 0));
1915
+ const queryTimeZoneRecord = /*@__PURE__*/ memoize((normTimeZoneId, record) => "named" === record.kind ? new IntlTimeZone(normTimeZoneId, record.o, record.format) : new FixedTimeZone(normTimeZoneId, record.o, "fixed" === record.kind ? record._ : 0));
1899
1916
  var FixedTimeZone = class {
1900
1917
  constructor(id, compareKey, offsetNano) {
1901
1918
  this.id = id, this.o = compareKey, this._ = offsetNano;
@@ -1977,7 +1994,8 @@ var IntlTimeZone = class {
1977
1994
  return void 0 !== intlParts.era && "bce" === normalizeEraName(intlParts.era) ? 1 - year : year;
1978
1995
  })(intlParts), parseInt(intlParts.month), parseInt(intlParts.day)) + 3600 * parseInt(intlParts.hour) + 60 * parseInt(intlParts.minute) + parseInt(intlParts.second) - epochSec;
1979
1996
  })(format), ((timeZoneId) => {
1980
- return timeZonePeriodDaysByName[timeZoneId.split("/").pop()] || 60;
1997
+ const timeZoneName = timeZoneId.split("/").pop();
1998
+ return timeZonePeriodDaysByName[timeZoneName] || 60;
1981
1999
  })(id));
1982
2000
  }
1983
2001
  C(epochNano) {
@@ -2201,12 +2219,12 @@ function offsetRegExpStr(separatorIndex) {
2201
2219
  return "([+-])" + timeRegExpStr(separatorIndex);
2202
2220
  }
2203
2221
  const dateTimeRegExpStr = "(?:(?:([+-])(\\d{6}))|(\\d{4}))(-?)(\\d{2})\\4(\\d{2})(?:[T ]" + timeRegExpStr(8) + "(Z|" + offsetRegExpStr(15) + ")?)?";
2204
- const yearMonthRegExp = /* @__PURE__ */ createRegExp("(?:(?:([+-])(\\d{6}))|(\\d{4}))-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
2205
- const monthDayRegExp = /* @__PURE__ */ createRegExp("(?:--)?(\\d{2})-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
2206
- const dateTimeRegExp = /* @__PURE__ */ createRegExp(dateTimeRegExpStr + "((?:\\[(!?)([^\\]]*)\\]){0,9})");
2207
- const timeRegExp = /* @__PURE__ */ createRegExp("T?" + timeRegExpStr(2) + `(${offsetRegExpStr(9)})?((?:\\[(!?)([^\\]]*)\\]){0,9})`);
2208
- const annotationRegExp = /* @__PURE__ */ new RegExp("\\[(!?)([^\\]]*)\\]", "g");
2209
- const durationRegExp = /* @__PURE__ */ createRegExp("([+-])?P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(?:T(?:(\\d+)(?:[.,](\\d{1,9}))?H)?(?:(\\d+)(?:[.,](\\d{1,9}))?M)?(?:(\\d+)(?:[.,](\\d{1,9}))?S)?)?");
2222
+ const yearMonthRegExp = /*@__PURE__*/ createRegExp("(?:(?:([+-])(\\d{6}))|(\\d{4}))-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
2223
+ const monthDayRegExp = /*@__PURE__*/ createRegExp("(?:--)?(\\d{2})-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
2224
+ const dateTimeRegExp = /*@__PURE__*/ createRegExp(dateTimeRegExpStr + "((?:\\[(!?)([^\\]]*)\\]){0,9})");
2225
+ const timeRegExp = /*@__PURE__*/ createRegExp("T?" + timeRegExpStr(2) + `(${offsetRegExpStr(9)})?((?:\\[(!?)([^\\]]*)\\]){0,9})`);
2226
+ const annotationRegExp = /*@__PURE__*/ new RegExp("\\[(!?)([^\\]]*)\\]", "g");
2227
+ const durationRegExp = /*@__PURE__*/ createRegExp("([+-])?P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(?:T(?:(\\d+)(?:[.,](\\d{1,9}))?H)?(?:(\\d+)(?:[.,](\\d{1,9}))?M)?(?:(\\d+)(?:[.,](\\d{1,9}))?S)?)?");
2210
2228
  function parseDateTimeLike(s) {
2211
2229
  const parts = dateTimeRegExp.exec(s);
2212
2230
  return parts ? ((parts) => {
@@ -2484,8 +2502,8 @@ const timeDefaultShapeFields = {
2484
2502
  minute: "numeric",
2485
2503
  second: "numeric"
2486
2504
  };
2487
- const dateTimeDefaultShapeFields = /* @__PURE__ */ Object.assign({}, dateDefaultShapeFields, timeDefaultShapeFields);
2488
- const zonedDateTimeDefaultShapeFields = /* @__PURE__ */ Object.assign({}, dateTimeDefaultShapeFields, { timeZoneName: "short" });
2505
+ const dateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateDefaultShapeFields, timeDefaultShapeFields);
2506
+ const zonedDateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateTimeDefaultShapeFields, { timeZoneName: "short" });
2489
2507
  const yearMonthDefaultShapeFields = {
2490
2508
  year: "numeric",
2491
2509
  month: "numeric"
@@ -2509,15 +2527,15 @@ const timeShapeFieldNames = [
2509
2527
  "fractionalSecondDigits",
2510
2528
  "timeStyle"
2511
2529
  ];
2512
- const dateTimeShapeFieldNames = /* @__PURE__ */ dateShapeFieldNames.concat(timeShapeFieldNames);
2513
- const yearMonthIgnoredFieldNames = /* @__PURE__ */ ["weekday", "day"].concat(timeShapeFieldNames);
2514
- const monthDayIgnoredFieldNames = /* @__PURE__ */ ["weekday", "year"].concat(timeShapeFieldNames);
2515
- const transformInstantOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], [], dateTimeDefaultShapeFields);
2516
- const transformZonedOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], [], zonedDateTimeDefaultShapeFields);
2517
- const transformDateTimeOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], ["timeZoneName"], dateTimeDefaultShapeFields);
2518
- const transformDateOptions = /* @__PURE__ */ createOptionsTransformer(dateShapeFieldNames, timeShapeFieldNames, ["timeZoneName"], dateDefaultShapeFields);
2519
- const transformTimeOptions = /* @__PURE__ */ createOptionsTransformer(timeShapeFieldNames, dateShapeFieldNames, ["timeZoneName", "era"], timeDefaultShapeFields);
2520
- const transformYearMonthOptions = /* @__PURE__ */ createOptionsTransformer([
2530
+ const dateTimeShapeFieldNames = /*@__PURE__*/ dateShapeFieldNames.concat(timeShapeFieldNames);
2531
+ const yearMonthIgnoredFieldNames = /*@__PURE__*/ ["weekday", "day"].concat(timeShapeFieldNames);
2532
+ const monthDayIgnoredFieldNames = /*@__PURE__*/ ["weekday", "year"].concat(timeShapeFieldNames);
2533
+ const transformInstantOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], dateTimeDefaultShapeFields);
2534
+ const transformZonedOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], zonedDateTimeDefaultShapeFields);
2535
+ const transformDateTimeOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], ["timeZoneName"], dateTimeDefaultShapeFields);
2536
+ const transformDateOptions = /*@__PURE__*/ createOptionsTransformer(dateShapeFieldNames, timeShapeFieldNames, ["timeZoneName"], dateDefaultShapeFields);
2537
+ const transformTimeOptions = /*@__PURE__*/ createOptionsTransformer(timeShapeFieldNames, dateShapeFieldNames, ["timeZoneName", "era"], timeDefaultShapeFields);
2538
+ const transformYearMonthOptions = /*@__PURE__*/ createOptionsTransformer([
2521
2539
  "year",
2522
2540
  "month",
2523
2541
  "dateStyle"
@@ -2539,7 +2557,7 @@ const transformYearMonthOptions = /* @__PURE__ */ createOptionsTransformer([
2539
2557
  month: "numeric"
2540
2558
  }
2541
2559
  });
2542
- const transformMonthDayOptions = /* @__PURE__ */ createOptionsTransformer([
2560
+ const transformMonthDayOptions = /*@__PURE__*/ createOptionsTransformer([
2543
2561
  "month",
2544
2562
  "day",
2545
2563
  "dateStyle"
@@ -2713,8 +2731,8 @@ function resolveBasicCalendarId(rawCalendarId) {
2713
2731
  function resolveBasicCalendarArg(rawCalendarId = isoCalendarId) {
2714
2732
  return resolveBasicCalendarId(rawCalendarId);
2715
2733
  }
2716
- const zonedDateTimeSlotsMap = /* @__PURE__ */ new WeakMap();
2717
- const ZonedDateTime = /* @__PURE__ */ defineTemporalClass(ZonedDateTimeBranding, class {
2734
+ const zonedDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
2735
+ const ZonedDateTime = /*@__PURE__*/ defineTemporalClass(ZonedDateTimeBranding, class {
2718
2736
  constructor(epochNanoseconds, timeZoneId, calendar = void 0) {
2719
2737
  const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
2720
2738
  const timeZone = queryTimeZone(refineTimeZoneId(timeZoneId));
@@ -2862,8 +2880,8 @@ function refineTimeZoneArg(arg) {
2862
2880
  }
2863
2881
  return ((arg) => resolveTimeZoneId(parseTimeZoneId(requireString(arg))))(arg);
2864
2882
  }
2865
- const instantSlotsMap = /* @__PURE__ */ new WeakMap();
2866
- const Instant = /* @__PURE__ */ defineTemporalClass(InstantBranding, class {
2883
+ const instantSlotsMap = /*@__PURE__*/ new WeakMap();
2884
+ const Instant = /*@__PURE__*/ defineTemporalClass(InstantBranding, class {
2867
2885
  constructor(epochNanoseconds) {
2868
2886
  const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
2869
2887
  initInstant(this, createEpochNanoSlots(epochNano));
@@ -2948,8 +2966,8 @@ const { toTemporalInstant: toTemporalInstant$1 } = { toTemporalInstant() {
2948
2966
  function initInstant(instance, slots) {
2949
2967
  return instantSlotsMap.set(instance, slots), attachDebugString(instance), instance;
2950
2968
  }
2951
- const plainMonthDaySlotsMap = /* @__PURE__ */ new WeakMap();
2952
- const PlainMonthDay = /* @__PURE__ */ defineTemporalClass(PlainMonthDayBranding, class {
2969
+ const plainMonthDaySlotsMap = /*@__PURE__*/ new WeakMap();
2970
+ const PlainMonthDay = /*@__PURE__*/ defineTemporalClass(PlainMonthDayBranding, class {
2953
2971
  constructor(isoMonth, isoDay, calendar = void 0, referenceIsoYear) {
2954
2972
  const isoMonthInt = toIntegerWithTrunc(isoMonth);
2955
2973
  const isoDayInt = toIntegerWithTrunc(isoDay);
@@ -3013,8 +3031,8 @@ function toPlainMonthDaySlots(arg, options) {
3013
3031
  function initPlainMonthDay(instance, slots) {
3014
3032
  return plainMonthDaySlotsMap.set(instance, slots), attachDebugString(instance), instance;
3015
3033
  }
3016
- const plainYearMonthSlotsMap = /* @__PURE__ */ new WeakMap();
3017
- const PlainYearMonth = /* @__PURE__ */ defineTemporalClass(PlainYearMonthBranding, class {
3034
+ const plainYearMonthSlotsMap = /*@__PURE__*/ new WeakMap();
3035
+ const PlainYearMonth = /*@__PURE__*/ defineTemporalClass(PlainYearMonthBranding, class {
3018
3036
  constructor(isoYear, isoMonth, calendar = void 0, referenceIsoDay) {
3019
3037
  const isoYearInt = toIntegerWithTrunc(isoYear);
3020
3038
  const isoMonthInt = toIntegerWithTrunc(isoMonth);
@@ -3106,8 +3124,8 @@ function getTemporalBrandingAndSlots(obj) {
3106
3124
  function validateBag(bag) {
3107
3125
  return (getTemporalBrandingAndSlots(bag) || void 0 !== bag.calendar || void 0 !== bag.timeZone) && throwTypeError("Invalid bag"), bag;
3108
3126
  }
3109
- const plainTimeSlotsMap = /* @__PURE__ */ new WeakMap();
3110
- const PlainTime = /* @__PURE__ */ defineTemporalClass(PlainTimeBranding, class {
3127
+ const plainTimeSlotsMap = /*@__PURE__*/ new WeakMap();
3128
+ const PlainTime = /*@__PURE__*/ defineTemporalClass(PlainTimeBranding, class {
3111
3129
  constructor(hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0) {
3112
3130
  const fields = validateTimeFields(mapProps(toIntegerWithTrunc, {
3113
3131
  hour,
@@ -3189,8 +3207,8 @@ function optionalToPlainTimeFields(timeArg) {
3189
3207
  function initPlainTime(instance, slots) {
3190
3208
  return plainTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
3191
3209
  }
3192
- const plainDateTimeSlotsMap = /* @__PURE__ */ new WeakMap();
3193
- const PlainDateTime = /* @__PURE__ */ defineTemporalClass(PlainDateTimeBranding, class {
3210
+ const plainDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
3211
+ const PlainDateTime = /*@__PURE__*/ defineTemporalClass(PlainDateTimeBranding, class {
3194
3212
  constructor(isoYear, isoMonth, isoDay, hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0, calendar = void 0) {
3195
3213
  const fields = checkIsoDateTimeInBounds(validateIsoDateTimeFields(mapProps(toIntegerWithTrunc, {
3196
3214
  year: isoYear,
@@ -3301,8 +3319,8 @@ function toPlainDateTimeSlots(arg, options) {
3301
3319
  function initPlainDateTime(instance, slots) {
3302
3320
  return plainDateTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
3303
3321
  }
3304
- const plainDateSlotsMap = /* @__PURE__ */ new WeakMap();
3305
- const PlainDate = /* @__PURE__ */ defineTemporalClass(PlainDateBranding, class {
3322
+ const plainDateSlotsMap = /*@__PURE__*/ new WeakMap();
3323
+ const PlainDate = /*@__PURE__*/ defineTemporalClass(PlainDateBranding, class {
3306
3324
  constructor(isoYear, isoMonth, isoDay, calendar = void 0) {
3307
3325
  const fields = checkIsoDateInBounds(validateIsoDateFields(mapProps(toIntegerWithTrunc, {
3308
3326
  year: isoYear,
@@ -3420,8 +3438,8 @@ function refineCalendarArg(arg) {
3420
3438
  }
3421
3439
  return ((arg) => resolveBasicCalendarId(parseCalendarId(requireString(arg))))(arg);
3422
3440
  }
3423
- const durationSlotsMap = /* @__PURE__ */ new WeakMap();
3424
- const Duration = /* @__PURE__ */ defineTemporalClass(DurationBranding, class {
3441
+ const durationSlotsMap = /*@__PURE__*/ new WeakMap();
3442
+ const Duration = /*@__PURE__*/ defineTemporalClass(DurationBranding, class {
3425
3443
  constructor(years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0, microseconds = 0, nanoseconds = 0) {
3426
3444
  const fields = validateDurationFields(mapProps(toStrictInteger, {
3427
3445
  years,
@@ -3514,7 +3532,7 @@ function refinePublicRelativeTo(relativeTo) {
3514
3532
  function initDuration(instance, slots) {
3515
3533
  return durationSlotsMap.set(instance, slots), attachDebugString(instance), instance;
3516
3534
  }
3517
- const Now = /* @__PURE__ */ Object.defineProperties({}, {
3535
+ const Now = /*@__PURE__*/ Object.defineProperties({}, {
3518
3536
  ...createStringTagDescriptors("Temporal.Now"),
3519
3537
  ...createPropDescriptors({
3520
3538
  timeZoneId() {
@@ -3538,7 +3556,7 @@ const Now = /* @__PURE__ */ Object.defineProperties({}, {
3538
3556
  }
3539
3557
  })
3540
3558
  });
3541
- const Temporal$1 = /* @__PURE__ */ Object.defineProperties({}, {
3559
+ const Temporal$2 = /*@__PURE__*/ Object.defineProperties({}, {
3542
3560
  ...createStringTagDescriptors("Temporal"),
3543
3561
  ...createPropDescriptors({
3544
3562
  PlainYearMonth,
@@ -3554,12 +3572,2691 @@ const Temporal$1 = /* @__PURE__ */ Object.defineProperties({}, {
3554
3572
  });
3555
3573
  //#endregion
3556
3574
  //#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/index.js
3557
- const Temporal = NativeTemporal || Temporal$1;
3575
+ const Temporal$1 = NativeTemporal || Temporal$2;
3558
3576
  NativeTemporal && Date.prototype.toTemporalInstant;
3559
3577
  //#endregion
3578
+ //#region src/sig/accept.ts
3579
+ /**
3580
+ * `Accept-Signature` header parsing, serialization, and validation utilities
3581
+ * for RFC 9421 §5 challenge-response negotiation.
3582
+ *
3583
+ * @module
3584
+ */
3585
+ /**
3586
+ * Parses an `Accept-Signature` header value (RFC 9421 §5.1) into an
3587
+ * array of {@link AcceptSignatureMember} objects.
3588
+ *
3589
+ * The `Accept-Signature` field is a Dictionary Structured Field
3590
+ * (RFC 8941 §3.2). Each dictionary member describes a single
3591
+ * requested message signature.
3592
+ *
3593
+ * On parse failure (malformed or empty header), returns an empty array.
3594
+ *
3595
+ * @param header The raw `Accept-Signature` header value string.
3596
+ * @returns An array of parsed members. Empty if the header is
3597
+ * malformed or empty.
3598
+ * @since 2.1.0
3599
+ */
3600
+ function parseAcceptSignature(header) {
3601
+ try {
3602
+ return parseEachSignature((0, structured_field_values.decodeDict)(header));
3603
+ } catch {
3604
+ (0, _logtape_logtape.getLogger)([
3605
+ "fedify",
3606
+ "sig",
3607
+ "http"
3608
+ ]).warn("Failed to parse Accept-Signature header: {header}", { header });
3609
+ return [];
3610
+ }
3611
+ }
3612
+ const compactObject = (obj) => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== void 0));
3613
+ const parseEachSignature = (dict) => Object.entries(dict).filter(([_, item]) => Array.isArray(item.value)).map(([label, item]) => ({
3614
+ label,
3615
+ components: item.value.filter((subitem) => typeof subitem.value === "string").map((subitem) => ({
3616
+ value: subitem.value,
3617
+ params: subitem.params ?? {}
3618
+ })),
3619
+ parameters: compactParams(item)
3620
+ }));
3621
+ const compactParams = (item) => {
3622
+ const { keyid, alg, created, expires, nonce, tag } = item.params ?? {};
3623
+ return compactObject({
3624
+ keyid: stringOrUndefined(keyid),
3625
+ alg: stringOrUndefined(alg),
3626
+ created: trueOrUndefined(created),
3627
+ expires: trueOrUndefined(expires),
3628
+ nonce: stringOrUndefined(nonce),
3629
+ tag: stringOrUndefined(tag)
3630
+ });
3631
+ };
3632
+ const stringOrUndefined = (v) => typeof v === "string" ? v : void 0;
3633
+ const trueOrUndefined = (v) => v === true ? true : void 0;
3634
+ /**
3635
+ * Serializes an array of {@link AcceptSignatureMember} objects into an
3636
+ * `Accept-Signature` header value string (RFC 9421 §5.1).
3637
+ *
3638
+ * The output is a Dictionary Structured Field (RFC 8941 §3.2).
3639
+ *
3640
+ * @param members The members to serialize.
3641
+ * @returns The serialized header value string.
3642
+ * @since 2.1.0
3643
+ */
3644
+ function formatAcceptSignature(members) {
3645
+ const items = members.map((member) => [member.label, new structured_field_values.Item(compToItems(member), compactParameters(member))]);
3646
+ return (0, structured_field_values.encodeDict)(Object.fromEntries(items));
3647
+ }
3648
+ const compToItems = (member) => member.components.map((c) => new structured_field_values.Item(c.value, c.params));
3649
+ const compactParameters = (member) => {
3650
+ const { keyid, alg, created, expires, nonce, tag } = member.parameters;
3651
+ return compactObject({
3652
+ keyid,
3653
+ alg,
3654
+ created,
3655
+ expires,
3656
+ nonce,
3657
+ tag
3658
+ });
3659
+ };
3660
+ /**
3661
+ * Filters out {@link AcceptSignatureMember} entries whose covered
3662
+ * components include response-only identifiers (`@status`) that are
3663
+ * not applicable to request-target messages, as required by
3664
+ * [RFC 9421 §5](https://www.rfc-editor.org/rfc/rfc9421#section-5).
3665
+ *
3666
+ * A warning is logged for each discarded entry.
3667
+ *
3668
+ * @param members The parsed `Accept-Signature` entries to validate.
3669
+ * @returns Only entries that are valid for request-target messages.
3670
+ * @since 2.1.0
3671
+ */
3672
+ function validateAcceptSignature(members) {
3673
+ const logger = (0, _logtape_logtape.getLogger)([
3674
+ "fedify",
3675
+ "sig",
3676
+ "http"
3677
+ ]);
3678
+ return members.filter((member) => {
3679
+ if (member.components.every((c) => c.value !== "@status")) return true;
3680
+ logLabel(logger, member.label);
3681
+ return false;
3682
+ });
3683
+ }
3684
+ const logLabel = (logger, label) => logger.warn("Discarding Accept-Signature member {label}: covered components include response-only identifier @status.", { label });
3685
+ /**
3686
+ * Attempts to translate an {@link AcceptSignatureMember} challenge into
3687
+ * RFC 9421 signing options that the local signer can fulfill.
3688
+ *
3689
+ * Returns `null` if the challenge cannot be fulfilled—for example, if
3690
+ * the requested `alg` or `keyid` is incompatible with the local key.
3691
+ *
3692
+ * Safety constraints:
3693
+ * - `alg`: only honored if it matches `localAlg`.
3694
+ * - `keyid`: only honored if it matches `localKeyId`.
3695
+ * - `components`: passed through exactly as requested, per RFC 9421 §5.2.
3696
+ * - `nonce`, `tag`, and `expires` are passed through directly.
3697
+ *
3698
+ * @param entry The challenge entry from the `Accept-Signature` header.
3699
+ * @param localKeyId The local key identifier (e.g., the actor key URL).
3700
+ * @param localAlg The algorithm of the local private key
3701
+ * (e.g., `"rsa-v1_5-sha256"`).
3702
+ * @returns Signing options if the challenge can be fulfilled, or `null`.
3703
+ * @since 2.1.0
3704
+ */
3705
+ function fulfillAcceptSignature(entry, localKeyId, localAlg) {
3706
+ if (entry.parameters.alg != null && entry.parameters.alg !== localAlg) return null;
3707
+ if (entry.parameters.keyid != null && entry.parameters.keyid !== localKeyId) return null;
3708
+ return {
3709
+ label: entry.label,
3710
+ components: entry.components,
3711
+ nonce: entry.parameters.nonce,
3712
+ tag: entry.parameters.tag,
3713
+ expires: entry.parameters.expires
3714
+ };
3715
+ }
3716
+ //#endregion
3717
+ //#region src/federation/metrics.ts
3718
+ var FederationMetrics = class {
3719
+ deliverySent;
3720
+ deliveryPermanentFailure;
3721
+ signatureVerificationFailure;
3722
+ signatureVerificationDuration;
3723
+ signatureKeyFetchDuration;
3724
+ deliveryDuration;
3725
+ inboxProcessingDuration;
3726
+ httpServerRequestCount;
3727
+ httpServerRequestDuration;
3728
+ queueTaskEnqueued;
3729
+ queueTaskStarted;
3730
+ queueTaskCompleted;
3731
+ queueTaskFailed;
3732
+ queueTaskDuration;
3733
+ queueTaskInFlight;
3734
+ queueDepth;
3735
+ fanoutRecipients;
3736
+ inboxActivity;
3737
+ outboxActivity;
3738
+ circuitBreakerStateChange;
3739
+ keyLookup;
3740
+ keyLookupDuration;
3741
+ documentFetch;
3742
+ documentFetchDuration;
3743
+ documentCache;
3744
+ webFingerHandle;
3745
+ webFingerHandleDuration;
3746
+ collectionRequest;
3747
+ collectionDispatchDuration;
3748
+ collectionPageItems;
3749
+ collectionTotalItems;
3750
+ constructor(meterProvider) {
3751
+ const meter = meterProvider.getMeter(name, version);
3752
+ this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
3753
+ description: "ActivityPub delivery attempts.",
3754
+ unit: "{attempt}"
3755
+ });
3756
+ this.deliveryPermanentFailure = meter.createCounter("activitypub.delivery.permanent_failure", {
3757
+ description: "ActivityPub deliveries abandoned as permanent failures.",
3758
+ unit: "{failure}"
3759
+ });
3760
+ this.signatureVerificationFailure = meter.createCounter("activitypub.signature.verification_failure", {
3761
+ description: "ActivityPub signature verification failures.",
3762
+ unit: "{failure}"
3763
+ });
3764
+ this.signatureVerificationDuration = meter.createHistogram("activitypub.signature.verification.duration", {
3765
+ description: "Duration of ActivityPub signature verification, including local key lookup and remote key fetches.",
3766
+ unit: "ms",
3767
+ advice: { explicitBucketBoundaries: [
3768
+ .1,
3769
+ .25,
3770
+ .5,
3771
+ 1,
3772
+ 2.5,
3773
+ 5,
3774
+ 10,
3775
+ 25,
3776
+ 50,
3777
+ 100,
3778
+ 250,
3779
+ 500,
3780
+ 1e3
3781
+ ] }
3782
+ });
3783
+ this.signatureKeyFetchDuration = meter.createHistogram("activitypub.signature.key_fetch.duration", {
3784
+ description: "Duration of public key lookup performed during ActivityPub signature verification.",
3785
+ unit: "ms"
3786
+ });
3787
+ this.deliveryDuration = meter.createHistogram("activitypub.delivery.duration", {
3788
+ description: "Duration of ActivityPub delivery attempts.",
3789
+ unit: "ms"
3790
+ });
3791
+ this.inboxProcessingDuration = meter.createHistogram("activitypub.inbox.processing_duration", {
3792
+ description: "Duration of ActivityPub inbox listener processing.",
3793
+ unit: "ms"
3794
+ });
3795
+ this.httpServerRequestCount = meter.createCounter("fedify.http.server.request.count", {
3796
+ description: "HTTP requests handled by Federation.fetch().",
3797
+ unit: "{request}"
3798
+ });
3799
+ this.httpServerRequestDuration = meter.createHistogram("fedify.http.server.request.duration", {
3800
+ description: "Duration of HTTP requests handled by Federation.fetch().",
3801
+ unit: "ms",
3802
+ advice: { explicitBucketBoundaries: [
3803
+ 5,
3804
+ 10,
3805
+ 25,
3806
+ 50,
3807
+ 75,
3808
+ 100,
3809
+ 250,
3810
+ 500,
3811
+ 750,
3812
+ 1e3,
3813
+ 2500,
3814
+ 5e3,
3815
+ 7500,
3816
+ 1e4
3817
+ ] }
3818
+ });
3819
+ this.queueTaskEnqueued = meter.createCounter("fedify.queue.task.enqueued", {
3820
+ description: "Tasks Fedify enqueued for inbox, outbox, or fanout work.",
3821
+ unit: "{task}"
3822
+ });
3823
+ this.queueTaskStarted = meter.createCounter("fedify.queue.task.started", {
3824
+ description: "Tasks Fedify began processing as a queue worker.",
3825
+ unit: "{task}"
3826
+ });
3827
+ this.queueTaskCompleted = meter.createCounter("fedify.queue.task.completed", {
3828
+ description: "Queue tasks Fedify finished processing without throwing.",
3829
+ unit: "{task}"
3830
+ });
3831
+ this.queueTaskFailed = meter.createCounter("fedify.queue.task.failed", {
3832
+ description: "Queue tasks Fedify abandoned because processing threw.",
3833
+ unit: "{task}"
3834
+ });
3835
+ this.queueTaskDuration = meter.createHistogram("fedify.queue.task.duration", {
3836
+ description: "Duration of queue task processing in Fedify workers.",
3837
+ unit: "ms",
3838
+ advice: { explicitBucketBoundaries: [
3839
+ 5,
3840
+ 10,
3841
+ 25,
3842
+ 50,
3843
+ 75,
3844
+ 100,
3845
+ 250,
3846
+ 500,
3847
+ 750,
3848
+ 1e3,
3849
+ 2500,
3850
+ 5e3,
3851
+ 7500,
3852
+ 1e4
3853
+ ] }
3854
+ });
3855
+ this.queueTaskInFlight = meter.createUpDownCounter("fedify.queue.task.in_flight", {
3856
+ description: "Queue tasks currently being processed in this Fedify process.",
3857
+ unit: "{task}"
3858
+ });
3859
+ this.queueDepth = meter.createObservableGauge("fedify.queue.depth", {
3860
+ description: "Messages waiting in configured Fedify queues, as reported by the queue backend.",
3861
+ unit: "{message}"
3862
+ });
3863
+ this.fanoutRecipients = meter.createHistogram("activitypub.fanout.recipients", {
3864
+ description: "Number of recipient inboxes produced by an ActivityPub fanout task.",
3865
+ unit: "{recipient}"
3866
+ });
3867
+ this.inboxActivity = meter.createCounter("activitypub.inbox.activity", {
3868
+ description: "ActivityPub activities observed at the inbox lifecycle level: queued, processed, retried, rejected, or abandoned.",
3869
+ unit: "{activity}"
3870
+ });
3871
+ this.outboxActivity = meter.createCounter("activitypub.outbox.activity", {
3872
+ description: "ActivityPub activities observed at the outbox lifecycle level: queued, retried, or abandoned. Per-recipient delivery counters live on `activitypub.delivery.*`.",
3873
+ unit: "{activity}"
3874
+ });
3875
+ this.circuitBreakerStateChange = meter.createCounter("activitypub.circuit_breaker.state_change", {
3876
+ description: "Outbound ActivityPub delivery circuit breaker changes.",
3877
+ unit: "{change}"
3878
+ });
3879
+ this.keyLookup = meter.createCounter("activitypub.key.lookup", {
3880
+ description: "Public-key lookup attempts performed by Fedify, including cache hits, local resolutions, and remote fetches.",
3881
+ unit: "{lookup}"
3882
+ });
3883
+ this.keyLookupDuration = meter.createHistogram("activitypub.key.lookup.duration", {
3884
+ description: "Duration of public-key lookups performed by Fedify, including cache hits, local resolutions, and any remote fetch.",
3885
+ unit: "ms",
3886
+ advice: { explicitBucketBoundaries: [
3887
+ 5,
3888
+ 10,
3889
+ 25,
3890
+ 50,
3891
+ 75,
3892
+ 100,
3893
+ 250,
3894
+ 500,
3895
+ 750,
3896
+ 1e3,
3897
+ 2500,
3898
+ 5e3,
3899
+ 7500,
3900
+ 1e4
3901
+ ] }
3902
+ });
3903
+ this.documentFetch = meter.createCounter("activitypub.document.fetch", {
3904
+ description: "Remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
3905
+ unit: "{fetch}"
3906
+ });
3907
+ this.documentFetchDuration = meter.createHistogram("activitypub.document.fetch.duration", {
3908
+ description: "Duration of remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
3909
+ unit: "ms",
3910
+ advice: { explicitBucketBoundaries: [
3911
+ 5,
3912
+ 10,
3913
+ 25,
3914
+ 50,
3915
+ 75,
3916
+ 100,
3917
+ 250,
3918
+ 500,
3919
+ 750,
3920
+ 1e3,
3921
+ 2500,
3922
+ 5e3,
3923
+ 7500,
3924
+ 1e4
3925
+ ] }
3926
+ });
3927
+ this.documentCache = meter.createCounter("activitypub.document.cache", {
3928
+ description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
3929
+ unit: "{lookup}"
3930
+ });
3931
+ this.webFingerHandle = meter.createCounter("webfinger.handle", {
3932
+ description: "Incoming WebFinger requests handled by Fedify, classified by terminal outcome.",
3933
+ unit: "{request}"
3934
+ });
3935
+ this.webFingerHandleDuration = meter.createHistogram("webfinger.handle.duration", {
3936
+ description: "Duration of incoming WebFinger request handling in Fedify.",
3937
+ unit: "ms",
3938
+ advice: { explicitBucketBoundaries: [
3939
+ 5,
3940
+ 10,
3941
+ 25,
3942
+ 50,
3943
+ 75,
3944
+ 100,
3945
+ 250,
3946
+ 500,
3947
+ 750,
3948
+ 1e3,
3949
+ 2500,
3950
+ 5e3,
3951
+ 7500,
3952
+ 1e4
3953
+ ] }
3954
+ });
3955
+ this.collectionRequest = meter.createCounter("activitypub.collection.request", {
3956
+ description: "ActivityPub collection and collection-page requests handled by Fedify.",
3957
+ unit: "{request}"
3958
+ });
3959
+ this.collectionDispatchDuration = meter.createHistogram("activitypub.collection.dispatch.duration", {
3960
+ description: "Duration of ActivityPub collection dispatcher callbacks.",
3961
+ unit: "ms",
3962
+ advice: { explicitBucketBoundaries: [
3963
+ 5,
3964
+ 10,
3965
+ 25,
3966
+ 50,
3967
+ 75,
3968
+ 100,
3969
+ 250,
3970
+ 500,
3971
+ 750,
3972
+ 1e3,
3973
+ 2500,
3974
+ 5e3,
3975
+ 7500,
3976
+ 1e4
3977
+ ] }
3978
+ });
3979
+ this.collectionPageItems = meter.createHistogram("activitypub.collection.page.items", {
3980
+ description: "Number of items Fedify materialized for an ActivityPub collection response.",
3981
+ unit: "{item}"
3982
+ });
3983
+ this.collectionTotalItems = meter.createHistogram("activitypub.collection.total_items", {
3984
+ description: "Total item count reported by ActivityPub collection counters.",
3985
+ unit: "{item}"
3986
+ });
3987
+ }
3988
+ recordDelivery(inbox, durationMs, success, activityType) {
3989
+ const deliveryAttributes = {
3990
+ "activitypub.remote.host": getRemoteHost(inbox),
3991
+ "activitypub.delivery.success": success
3992
+ };
3993
+ if (activityType != null) deliveryAttributes["activitypub.activity.type"] = activityType;
3994
+ this.deliverySent.add(1, deliveryAttributes);
3995
+ this.deliveryDuration.record(durationMs, deliveryAttributes);
3996
+ }
3997
+ recordPermanentFailure(inbox, statusCode) {
3998
+ this.deliveryPermanentFailure.add(1, {
3999
+ "activitypub.remote.host": getRemoteHost(inbox),
4000
+ "http.response.status_code": statusCode
4001
+ });
4002
+ }
4003
+ recordSignatureVerificationFailure(reason, remoteHost) {
4004
+ const attributes = { "activitypub.verification.failure_reason": reason };
4005
+ if (remoteHost != null) attributes["activitypub.remote.host"] = remoteHost;
4006
+ this.signatureVerificationFailure.add(1, attributes);
4007
+ }
4008
+ recordSignatureVerificationDuration(durationMs, kind, result, extra = {}) {
4009
+ const attributes = {
4010
+ "activitypub.signature.kind": kind,
4011
+ "activitypub.signature.result": result
4012
+ };
4013
+ if (extra.algorithm != null) attributes["http_signatures.algorithm"] = extra.algorithm;
4014
+ if (extra.failureReason != null) attributes["http_signatures.failure_reason"] = extra.failureReason;
4015
+ if (extra.ldType != null) attributes["ld_signatures.type"] = extra.ldType;
4016
+ if (extra.cryptosuite != null) attributes["object_integrity_proofs.cryptosuite"] = extra.cryptosuite;
4017
+ this.signatureVerificationDuration.record(durationMs, attributes);
4018
+ }
4019
+ recordSignatureKeyFetchDuration(durationMs, kind, result) {
4020
+ this.signatureKeyFetchDuration.record(durationMs, {
4021
+ "activitypub.signature.kind": kind,
4022
+ "activitypub.signature.key_fetch.result": result
4023
+ });
4024
+ }
4025
+ recordInboxProcessingDuration(activityType, durationMs) {
4026
+ this.inboxProcessingDuration.record(durationMs, { "activitypub.activity.type": activityType });
4027
+ }
4028
+ recordHttpServerRequest(method, endpoint, durationMs, options = {}) {
4029
+ const attributes = {
4030
+ "http.request.method": normalizeHttpMethod(method),
4031
+ "fedify.endpoint": endpoint
4032
+ };
4033
+ if (options.statusCode != null) attributes["http.response.status_code"] = options.statusCode;
4034
+ if (options.routeTemplate != null) attributes["fedify.route.template"] = options.routeTemplate;
4035
+ this.httpServerRequestCount.add(1, attributes);
4036
+ this.httpServerRequestDuration.record(durationMs, attributes);
4037
+ }
4038
+ recordQueueTaskEnqueued(common, attempt, count = 1) {
4039
+ const attributes = buildQueueTaskAttributes(common);
4040
+ attributes["fedify.queue.task.attempt"] = attempt;
4041
+ this.queueTaskEnqueued.add(count, attributes);
4042
+ }
4043
+ recordQueueTaskStarted(common) {
4044
+ this.queueTaskStarted.add(1, buildQueueTaskAttributes(common));
4045
+ }
4046
+ incrementQueueTaskInFlight(common) {
4047
+ this.queueTaskInFlight.add(1, buildQueueTaskInFlightAttributes(common));
4048
+ }
4049
+ decrementQueueTaskInFlight(common) {
4050
+ this.queueTaskInFlight.add(-1, buildQueueTaskInFlightAttributes(common));
4051
+ }
4052
+ recordQueueTaskOutcome(common, result, durationMs, failureReason) {
4053
+ const attributes = buildQueueTaskAttributes(common);
4054
+ attributes["fedify.queue.task.result"] = result;
4055
+ if (failureReason != null && result === "failed") attributes["fedify.task.failure_reason"] = failureReason;
4056
+ if (result === "completed") this.queueTaskCompleted.add(1, attributes);
4057
+ else if (result === "failed") this.queueTaskFailed.add(1, attributes);
4058
+ this.queueTaskDuration.record(durationMs, attributes);
4059
+ }
4060
+ recordFanoutRecipients(recipientCount, activityType) {
4061
+ const attributes = {};
4062
+ if (activityType != null) attributes["activitypub.activity.type"] = activityType;
4063
+ this.fanoutRecipients.record(recipientCount, attributes);
4064
+ }
4065
+ recordInboxActivity(result, activityType) {
4066
+ this.inboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
4067
+ }
4068
+ recordOutboxActivity(result, activityType) {
4069
+ this.outboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
4070
+ }
4071
+ recordCircuitBreakerStateChange(remoteHost, state) {
4072
+ this.circuitBreakerStateChange.add(1, {
4073
+ "activitypub.remote.host": remoteHost,
4074
+ "activitypub.circuit_breaker.state": state
4075
+ });
4076
+ }
4077
+ recordKeyLookup(attrs) {
4078
+ const attributes = {
4079
+ "activitypub.lookup.kind": "public_key",
4080
+ "activitypub.lookup.result": attrs.result,
4081
+ "activitypub.cache.enabled": attrs.cacheEnabled
4082
+ };
4083
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
4084
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
4085
+ this.keyLookup.add(1, attributes);
4086
+ this.keyLookupDuration.record(attrs.durationMs, attributes);
4087
+ }
4088
+ recordDocumentFetch(attrs) {
4089
+ const attributes = {
4090
+ "activitypub.lookup.kind": attrs.kind,
4091
+ "activitypub.lookup.result": attrs.result
4092
+ };
4093
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
4094
+ if (attrs.cacheEnabled != null) attributes["activitypub.cache.enabled"] = attrs.cacheEnabled;
4095
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
4096
+ this.documentFetch.add(1, attributes);
4097
+ this.documentFetchDuration.record(attrs.durationMs, attributes);
4098
+ }
4099
+ recordDocumentCache(attrs) {
4100
+ const attributes = {
4101
+ "activitypub.lookup.kind": attrs.kind,
4102
+ "activitypub.lookup.result": attrs.result
4103
+ };
4104
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
4105
+ this.documentCache.add(1, attributes);
4106
+ }
4107
+ recordWebFingerHandle(attrs) {
4108
+ const attributes = { "webfinger.handle.result": attrs.result };
4109
+ if (attrs.scheme != null) attributes["webfinger.resource.scheme"] = attrs.scheme;
4110
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
4111
+ this.webFingerHandle.add(1, attributes);
4112
+ this.webFingerHandleDuration.record(attrs.durationMs, attributes);
4113
+ }
4114
+ recordCollectionRequest(attrs) {
4115
+ this.collectionRequest.add(1, buildCollectionAttributes(attrs));
4116
+ }
4117
+ recordCollectionDispatchDuration(durationMs, attrs) {
4118
+ this.collectionDispatchDuration.record(durationMs, buildCollectionAttributes(attrs));
4119
+ }
4120
+ recordCollectionPageItems(itemCount, attrs) {
4121
+ this.collectionPageItems.record(itemCount, buildCollectionAttributes(attrs));
4122
+ }
4123
+ recordCollectionTotalItems(totalItems, attrs) {
4124
+ this.collectionTotalItems.record(totalItems, buildCollectionAttributes(attrs));
4125
+ }
4126
+ };
4127
+ function buildCollectionAttributes(attrs) {
4128
+ const attributes = {
4129
+ "activitypub.collection.kind": attrs.kind,
4130
+ "activitypub.collection.page": attrs.page,
4131
+ "activitypub.collection.result": attrs.result,
4132
+ "fedify.collection.dispatcher": attrs.dispatcher
4133
+ };
4134
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
4135
+ return attributes;
4136
+ }
4137
+ function buildActivityLifecycleAttributes(result, activityType) {
4138
+ const attributes = { "activitypub.processing.result": result };
4139
+ if (activityType != null) attributes["activitypub.activity.type"] = activityType;
4140
+ return attributes;
4141
+ }
4142
+ function buildQueueTaskAttributes(common) {
4143
+ const attributes = { "fedify.queue.role": common.role };
4144
+ const backend = getQueueBackend(common.queue);
4145
+ if (backend != null) attributes["fedify.queue.backend"] = backend;
4146
+ const nativeRetrial = common.queue?.nativeRetrial;
4147
+ if (typeof nativeRetrial === "boolean") attributes["fedify.queue.native_retrial"] = nativeRetrial;
4148
+ if (common.activityType != null) attributes["activitypub.activity.type"] = common.activityType;
4149
+ if (common.taskName != null) attributes["fedify.task.name"] = common.taskName;
4150
+ return attributes;
4151
+ }
4152
+ function buildQueueTaskInFlightAttributes(common) {
4153
+ return buildQueueTaskAttributes({
4154
+ role: common.role,
4155
+ queue: common.queue
4156
+ });
4157
+ }
4158
+ /**
4159
+ * Returns the constructor name of the given message queue, when it is a
4160
+ * meaningful identifier. Used as a best-effort `fedify.queue.backend`
4161
+ * attribute on queue task metrics; returns `undefined` for plain object
4162
+ * literals (whose constructor is `Object`) so the attribute does not appear
4163
+ * with a non-informative value.
4164
+ * @since 2.3.0
4165
+ */
4166
+ function getQueueBackend(queue) {
4167
+ const name = queue?.constructor?.name;
4168
+ if (name == null || name === "" || name === "Object") return void 0;
4169
+ return name;
4170
+ }
4171
+ /**
4172
+ * Registers a callback for observing queue backend depth.
4173
+ * @since 2.3.0
4174
+ */
4175
+ function registerQueueDepthGauge(meterProvider, entries, options = {}) {
4176
+ const uniqueQueues = /* @__PURE__ */ new Map();
4177
+ for (const { role, queue } of entries) {
4178
+ if (queue?.getDepth == null) continue;
4179
+ const roles = uniqueQueues.get(queue);
4180
+ if (roles == null) uniqueQueues.set(queue, [role]);
4181
+ else if (!roles.includes(role)) roles.push(role);
4182
+ }
4183
+ if (uniqueQueues.size < 1) return;
4184
+ const queueEntries = Array.from(uniqueQueues.entries());
4185
+ getFederationMetrics(meterProvider).queueDepth.addCallback(async (observableResult) => {
4186
+ await Promise.all(queueEntries.map(async ([queue, roles]) => {
4187
+ let depth;
4188
+ try {
4189
+ depth = await queue.getDepth();
4190
+ } catch {
4191
+ return;
4192
+ }
4193
+ if (depth == null) return;
4194
+ const attributes = buildQueueDepthAttributes(queue, roles, options);
4195
+ observableResult.observe(depth.queued, {
4196
+ ...attributes,
4197
+ "fedify.queue.depth.state": "queued"
4198
+ });
4199
+ if (depth.ready != null) observableResult.observe(depth.ready, {
4200
+ ...attributes,
4201
+ "fedify.queue.depth.state": "ready"
4202
+ });
4203
+ if (depth.delayed != null) observableResult.observe(depth.delayed, {
4204
+ ...attributes,
4205
+ "fedify.queue.depth.state": "delayed"
4206
+ });
4207
+ }));
4208
+ });
4209
+ }
4210
+ function buildQueueDepthAttributes(queue, roles, options) {
4211
+ const sortedRoles = roles.toSorted();
4212
+ const role = sortedRoles.length === 1 ? sortedRoles[0] : "shared";
4213
+ const attributes = { "fedify.queue.role": role };
4214
+ if (options.sourceId != null) attributes["fedify.federation.instance_id"] = options.sourceId;
4215
+ if (role === "shared") attributes["fedify.queue.roles"] = sortedRoles.join(",");
4216
+ const backend = getQueueBackend(queue);
4217
+ if (backend != null) attributes["fedify.queue.backend"] = backend;
4218
+ const nativeRetrial = queue.nativeRetrial;
4219
+ if (typeof nativeRetrial === "boolean") attributes["fedify.queue.native_retrial"] = nativeRetrial;
4220
+ return attributes;
4221
+ }
4222
+ /**
4223
+ * Records `fedify.queue.task.enqueued` for an outgoing outbox enqueue and,
4224
+ * for the initial attempt, also records
4225
+ * `activitypub.outbox.activity{queued}`.
4226
+ *
4227
+ * Both `Context.sendActivity()` and `OutboxContext.forwardActivity()` enqueue
4228
+ * outbox messages with the same metric attributes (role, queue, activity
4229
+ * type, attempt), so they share this helper rather than each defining a local
4230
+ * closure. Retry enqueues (attempt > 0) intentionally do not record a
4231
+ * second `activitypub.outbox.activity{queued}`; retries are reported as
4232
+ * `result=retried` from the retry-scheduling site, which has the failure
4233
+ * context.
4234
+ * @since 2.3.0
4235
+ */
4236
+ function recordOutboxEnqueue(meterProvider, outboxQueue, message) {
4237
+ const metrics = getFederationMetrics(meterProvider);
4238
+ metrics.recordQueueTaskEnqueued({
4239
+ role: "outbox",
4240
+ queue: outboxQueue,
4241
+ activityType: message.activityType
4242
+ }, message.attempt);
4243
+ if (message.attempt === 0) metrics.recordOutboxActivity("queued", message.activityType);
4244
+ }
4245
+ /**
4246
+ * Records `activitypub.fanout.recipients` with the number of recipient
4247
+ * inboxes a single fanout produced. The histogram is unitless count
4248
+ * (one measurement per fanout enqueue). Recipient URLs are deliberately
4249
+ * not recorded; only the activity type, when known.
4250
+ * @since 2.3.0
4251
+ */
4252
+ function recordFanoutRecipients(meterProvider, recipientCount, activityType) {
4253
+ getFederationMetrics(meterProvider).recordFanoutRecipients(recipientCount, activityType);
4254
+ }
4255
+ /**
4256
+ * Records one `activitypub.inbox.activity` measurement. The
4257
+ * `activitypub.processing.result` attribute is always present;
4258
+ * `activitypub.activity.type` is recorded only when Fedify already knows
4259
+ * the activity type.
4260
+ * @since 2.3.0
4261
+ */
4262
+ function recordInboxActivity(meterProvider, result, activityType) {
4263
+ getFederationMetrics(meterProvider).recordInboxActivity(result, activityType);
4264
+ }
4265
+ /**
4266
+ * Records one `activitypub.outbox.activity` measurement. The
4267
+ * `activitypub.processing.result` attribute is always present;
4268
+ * `activitypub.activity.type` is recorded only when Fedify already knows
4269
+ * the activity type (it is always known for outbox lifecycle events).
4270
+ * @since 2.3.0
4271
+ */
4272
+ function recordOutboxActivity(meterProvider, result, activityType) {
4273
+ getFederationMetrics(meterProvider).recordOutboxActivity(result, activityType);
4274
+ }
4275
+ /**
4276
+ * Records one outbound delivery circuit breaker state transition.
4277
+ * @since 2.3.0
4278
+ */
4279
+ function recordCircuitBreakerStateChange(meterProvider, remoteHost, state) {
4280
+ getFederationMetrics(meterProvider).recordCircuitBreakerStateChange(remoteHost, state);
4281
+ }
4282
+ /**
4283
+ * Records one measurement on `activitypub.key.lookup` (counter) and
4284
+ * `activitypub.key.lookup.duration` (histogram) for a public-key lookup.
4285
+ *
4286
+ * `activitypub.lookup.kind` is always recorded as `public_key`; the result
4287
+ * classification, remote host, HTTP status code (when an HTTP response was
4288
+ * received), and `activitypub.cache.enabled` are recorded as attributes on
4289
+ * both measurements. Full key URLs and key IDs are deliberately omitted to
4290
+ * keep cardinality bounded.
4291
+ * @since 2.3.0
4292
+ */
4293
+ function recordKeyLookup(meterProvider, attrs) {
4294
+ getFederationMetrics(meterProvider).recordKeyLookup(attrs);
4295
+ }
4296
+ /**
4297
+ * Records one measurement each on `activitypub.document.fetch` (counter)
4298
+ * and `activitypub.document.fetch.duration` (histogram) for one remote
4299
+ * JSON-LD document loader invocation, with bounded
4300
+ * `activitypub.lookup.kind` and `activitypub.lookup.result` attributes
4301
+ * plus the optional remote-host, cache-enabled, and HTTP status-code
4302
+ * attributes. Counter and histogram are always recorded together so
4303
+ * aggregate rate and latency views stay in sync.
4304
+ * @since 2.3.0
4305
+ */
4306
+ function recordDocumentFetch(meterProvider, attrs) {
4307
+ getFederationMetrics(meterProvider).recordDocumentFetch(attrs);
4308
+ }
4309
+ /**
4310
+ * Records one `activitypub.document.cache` measurement, classifying the
4311
+ * lookup as `hit` (the cache returned an entry) or `miss` (the cache was
4312
+ * consulted and returned nothing, prompting a delegate fetch).
4313
+ * @since 2.3.0
4314
+ */
4315
+ function recordDocumentCache(meterProvider, attrs) {
4316
+ getFederationMetrics(meterProvider).recordDocumentCache(attrs);
4317
+ }
4318
+ /**
4319
+ * Records one measurement on `webfinger.handle` (counter) and
4320
+ * `webfinger.handle.duration` (histogram) for an incoming WebFinger
4321
+ * request handled by Fedify. Counter and histogram are always recorded
4322
+ * together, with `webfinger.handle.result` set to one of `resolved`,
4323
+ * `invalid`, `not_found`, `tombstoned`, or `error`. The queried
4324
+ * resource string is deliberately excluded; it remains on the
4325
+ * `webfinger.handle` span for trace-level investigation.
4326
+ * @since 2.3.0
4327
+ */
4328
+ function recordWebFingerHandle(meterProvider, attrs) {
4329
+ getFederationMetrics(meterProvider).recordWebFingerHandle(attrs);
4330
+ }
4331
+ /**
4332
+ * Records one `activitypub.collection.request` measurement for a
4333
+ * collection or collection-page request handled by Fedify.
4334
+ * @since 2.3.0
4335
+ */
4336
+ function recordCollectionRequest(meterProvider, attrs) {
4337
+ getFederationMetrics(meterProvider).recordCollectionRequest(attrs);
4338
+ }
4339
+ /**
4340
+ * Records one `activitypub.collection.dispatch.duration` measurement for a
4341
+ * collection dispatcher callback invocation.
4342
+ * @since 2.3.0
4343
+ */
4344
+ function recordCollectionDispatchDuration(meterProvider, durationMs, attrs) {
4345
+ getFederationMetrics(meterProvider).recordCollectionDispatchDuration(durationMs, attrs);
4346
+ }
4347
+ /**
4348
+ * Records one `activitypub.collection.page.items` measurement when Fedify
4349
+ * has materialized collection items in memory.
4350
+ * @since 2.3.0
4351
+ */
4352
+ function recordCollectionPageItems(meterProvider, itemCount, attrs) {
4353
+ getFederationMetrics(meterProvider).recordCollectionPageItems(itemCount, attrs);
4354
+ }
4355
+ /**
4356
+ * Records one `activitypub.collection.total_items` measurement when a
4357
+ * collection counter has already reported a total item count.
4358
+ * @since 2.3.0
4359
+ */
4360
+ function recordCollectionTotalItems(meterProvider, totalItems, attrs) {
4361
+ getFederationMetrics(meterProvider).recordCollectionTotalItems(totalItems, attrs);
4362
+ }
4363
+ /**
4364
+ * Classifies a thrown value from a key or document fetch into the bounded
4365
+ * {@link LookupResult} taxonomy and, when an HTTP response was received,
4366
+ * surfaces its status code.
4367
+ *
4368
+ * - `FetchError` with a `Response` whose status is `404` or `410`:
4369
+ * `result=not_found` and the response status code.
4370
+ * - `FetchError` with any other `Response`: `result=error` and the
4371
+ * response status code.
4372
+ * - `FetchError` without a `Response`: `result=network_error`.
4373
+ * - An `AbortError` (typically from a cancelled fetch): `result=network_error`.
4374
+ * - A bare `TypeError` (the shape native `fetch()` raises on DNS, connect,
4375
+ * and TLS failures before any response is observed):
4376
+ * `result=network_error`.
4377
+ * - Any other value: `result=error`.
4378
+ * @since 2.3.0
4379
+ */
4380
+ function classifyFetchError(error) {
4381
+ if (error instanceof _fedify_vocab_runtime.FetchError) {
4382
+ if (error.response != null) {
4383
+ const status = error.response.status;
4384
+ return {
4385
+ result: status === 404 || status === 410 ? "not_found" : "error",
4386
+ statusCode: status
4387
+ };
4388
+ }
4389
+ return { result: "network_error" };
4390
+ }
4391
+ if (isAbortError$1(error)) return { result: "network_error" };
4392
+ if (error instanceof TypeError) return { result: "network_error" };
4393
+ return { result: "error" };
4394
+ }
4395
+ /**
4396
+ * Wraps a {@link DocumentLoader} so each invocation records one
4397
+ * measurement on `activitypub.document.fetch` (counter) and one on
4398
+ * `activitypub.document.fetch.duration` (histogram), classifying the
4399
+ * outcome via {@link classifyFetchError} when the wrapped loader throws
4400
+ * and as `fetched` on success. The wrapper rethrows whatever the
4401
+ * wrapped loader throws so caller behavior is unchanged.
4402
+ *
4403
+ * The wrapper records the host of the requested URL, including any
4404
+ * non-default port, on `activitypub.remote.host` when the URL parses; full
4405
+ * URLs, paths, and query strings are deliberately excluded to keep
4406
+ * cardinality bounded.
4407
+ * HTTP status codes are recorded only when the failure carries a
4408
+ * `Response` (currently, when the wrapped loader throws a
4409
+ * {@link FetchError} with a non-`null` `response`).
4410
+ * @since 2.3.0
4411
+ */
4412
+ function instrumentDocumentLoader(loader, options) {
4413
+ const meterProvider = options.meterProvider;
4414
+ if (meterProvider == null) return loader;
4415
+ return async (url, opts) => {
4416
+ const start = performance.now();
4417
+ let remoteUrl;
4418
+ try {
4419
+ remoteUrl = new URL(url);
4420
+ } catch {
4421
+ remoteUrl = void 0;
4422
+ }
4423
+ try {
4424
+ const result = await loader(url, opts);
4425
+ recordDocumentFetch(meterProvider, {
4426
+ durationMs: getDurationMs(start),
4427
+ kind: options.kind,
4428
+ result: "fetched",
4429
+ remoteUrl,
4430
+ cacheEnabled: options.cacheEnabled
4431
+ });
4432
+ return result;
4433
+ } catch (error) {
4434
+ const classified = classifyFetchError(error);
4435
+ recordDocumentFetch(meterProvider, {
4436
+ durationMs: getDurationMs(start),
4437
+ kind: options.kind,
4438
+ result: classified.result,
4439
+ remoteUrl,
4440
+ cacheEnabled: options.cacheEnabled,
4441
+ statusCode: classified.statusCode
4442
+ });
4443
+ throw error;
4444
+ }
4445
+ };
4446
+ }
4447
+ /**
4448
+ * Times an awaited public key fetch and records exactly one
4449
+ * `activitypub.signature.key_fetch.duration` measurement, classifying the
4450
+ * outcome as `hit`, `fetched`, or `error` based on the `cached` flag and
4451
+ * whether the returned key is non-null. Errors thrown by the fetch are
4452
+ * reported as `error` and rethrown, so verifier behavior is unchanged.
4453
+ *
4454
+ * Shared by the three signature verifiers (HTTP, Linked Data, Object
4455
+ * Integrity Proofs); the only per-call variation is the
4456
+ * `activitypub.signature.kind` attribute value.
4457
+ * @since 2.3.0
4458
+ */
4459
+ async function measureSignatureKeyFetch(meterProvider, kind, fetch) {
4460
+ const start = performance.now();
4461
+ try {
4462
+ const result = await fetch();
4463
+ getFederationMetrics(meterProvider).recordSignatureKeyFetchDuration(getDurationMs(start), kind, result.key != null ? result.cached ? "hit" : "fetched" : "error");
4464
+ return result;
4465
+ } catch (error) {
4466
+ getFederationMetrics(meterProvider).recordSignatureKeyFetchDuration(getDurationMs(start), kind, "error");
4467
+ throw error;
4468
+ }
4469
+ }
4470
+ /**
4471
+ * Whether the given thrown value is an `AbortError`.
4472
+ *
4473
+ * `processQueuedTask` distinguishes aborted tasks (recorded as
4474
+ * `fedify.queue.task.result=aborted`) from other failures so that backend
4475
+ * shutdown signals do not inflate the `fedify.queue.task.failed` counter.
4476
+ * @since 2.3.0
4477
+ */
4478
+ function isAbortError$1(error) {
4479
+ if (error == null || typeof error !== "object") return false;
4480
+ const name = error.name;
4481
+ return typeof name === "string" && name === "AbortError";
4482
+ }
4483
+ const KNOWN_HTTP_METHODS = /* @__PURE__ */ new Set([
4484
+ "CONNECT",
4485
+ "DELETE",
4486
+ "GET",
4487
+ "HEAD",
4488
+ "OPTIONS",
4489
+ "PATCH",
4490
+ "POST",
4491
+ "PUT",
4492
+ "QUERY",
4493
+ "TRACE"
4494
+ ]);
4495
+ function normalizeHttpMethod(method) {
4496
+ const upper = method.toUpperCase();
4497
+ return KNOWN_HTTP_METHODS.has(upper) ? upper : "_OTHER";
4498
+ }
4499
+ const federationMetrics = /* @__PURE__ */ new WeakMap();
4500
+ /**
4501
+ * Gets the cached Fedify metric instruments for a meter provider.
4502
+ * @since 2.3.0
4503
+ */
4504
+ function getFederationMetrics(meterProvider = _opentelemetry_api.metrics.getMeterProvider()) {
4505
+ let instruments = federationMetrics.get(meterProvider);
4506
+ if (instruments == null) {
4507
+ instruments = new FederationMetrics(meterProvider);
4508
+ federationMetrics.set(meterProvider, instruments);
4509
+ }
4510
+ return instruments;
4511
+ }
4512
+ /**
4513
+ * Gets the bounded remote host attribute value for a URL.
4514
+ * @since 2.3.0
4515
+ */
4516
+ function getRemoteHost(url) {
4517
+ return url.host;
4518
+ }
4519
+ /**
4520
+ * Gets an elapsed duration in milliseconds from a `performance.now()` value.
4521
+ * @since 2.3.0
4522
+ */
4523
+ function getDurationMs(start) {
4524
+ return Math.max(0, performance.now() - start);
4525
+ }
4526
+ //#endregion
4527
+ //#region src/sig/key.ts
4528
+ /**
4529
+ * Checks if the given key is valid and supported. No-op if the key is valid,
4530
+ * otherwise throws an error.
4531
+ * @param key The key to check.
4532
+ * @param type Which type of key to check. If not specified, the key can be
4533
+ * either public or private.
4534
+ * @throws {TypeError} If the key is invalid or unsupported.
4535
+ */
4536
+ function validateCryptoKey(key, type) {
4537
+ if (type != null && key.type !== type) throw new TypeError(`The key is not a ${type} key.`);
4538
+ if (!key.extractable) throw new TypeError("The key is not extractable.");
4539
+ if (key.algorithm.name !== "RSASSA-PKCS1-v1_5" && key.algorithm.name !== "Ed25519") throw new TypeError("Currently only RSASSA-PKCS1-v1_5 and Ed25519 keys are supported. More algorithms will be added in the future!");
4540
+ if (key.algorithm.name === "RSASSA-PKCS1-v1_5") {
4541
+ if (key.algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
4542
+ }
4543
+ }
4544
+ /**
4545
+ * Generates a key pair which is appropriate for Fedify.
4546
+ * @param algorithm The algorithm to use. Currently only RSASSA-PKCS1-v1_5 and
4547
+ * Ed25519 are supported.
4548
+ * @returns The generated key pair.
4549
+ * @throws {TypeError} If the algorithm is unsupported.
4550
+ */
4551
+ function generateCryptoKeyPair(algorithm) {
4552
+ if (algorithm == null) (0, _logtape_logtape.getLogger)([
4553
+ "fedify",
4554
+ "sig",
4555
+ "key"
4556
+ ]).warn("No algorithm specified. Using RSASSA-PKCS1-v1_5 by default, but it is recommended to specify the algorithm explicitly as the parameter will be required in the future.");
4557
+ if (algorithm == null || algorithm === "RSASSA-PKCS1-v1_5") return crypto.subtle.generateKey({
4558
+ name: "RSASSA-PKCS1-v1_5",
4559
+ modulusLength: 4096,
4560
+ publicExponent: new Uint8Array([
4561
+ 1,
4562
+ 0,
4563
+ 1
4564
+ ]),
4565
+ hash: "SHA-256"
4566
+ }, true, ["sign", "verify"]);
4567
+ else if (algorithm === "Ed25519") return crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
4568
+ throw new TypeError("Unsupported algorithm: " + algorithm);
4569
+ }
4570
+ /**
4571
+ * Exports a key in JWK format.
4572
+ * @param key The key to export. Either public or private key.
4573
+ * @returns The exported key in JWK format. The key is suitable for
4574
+ * serialization and storage.
4575
+ * @throws {TypeError} If the key is invalid or unsupported.
4576
+ */
4577
+ async function exportJwk(key) {
4578
+ validateCryptoKey(key);
4579
+ const jwk = await crypto.subtle.exportKey("jwk", key);
4580
+ if (jwk.crv === "Ed25519") jwk.alg = "Ed25519";
4581
+ return jwk;
4582
+ }
4583
+ /**
4584
+ * Imports a key from JWK format.
4585
+ * @param jwk The key in JWK format.
4586
+ * @param type Which type of key to import, either `"public"` or `"private"`.
4587
+ * @returns The imported key.
4588
+ * @throws {TypeError} If the key is invalid or unsupported.
4589
+ */
4590
+ async function importJwk(jwk, type) {
4591
+ let key;
4592
+ if (jwk.kty === "RSA" && jwk.alg === "RS256") key = await crypto.subtle.importKey("jwk", jwk, {
4593
+ name: "RSASSA-PKCS1-v1_5",
4594
+ hash: "SHA-256"
4595
+ }, true, type === "public" ? ["verify"] : ["sign"]);
4596
+ else if (jwk.kty === "OKP" && jwk.crv === "Ed25519") {
4597
+ if (navigator?.userAgent === "Cloudflare-Workers") {
4598
+ jwk = { ...jwk };
4599
+ delete jwk.alg;
4600
+ }
4601
+ key = await crypto.subtle.importKey("jwk", jwk, "Ed25519", true, type === "public" ? ["verify"] : ["sign"]);
4602
+ } else throw new TypeError("Unsupported JWK format.");
4603
+ validateCryptoKey(key, type);
4604
+ return key;
4605
+ }
4606
+ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
4607
+ tracerProvider ??= _opentelemetry_api.trace.getTracerProvider();
4608
+ return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
4609
+ kind: _opentelemetry_api.SpanKind.CLIENT,
4610
+ attributes: {
4611
+ "http.method": "GET",
4612
+ "url.full": keyId.href,
4613
+ "url.scheme": keyId.protocol.replace(/:$/, ""),
4614
+ "url.domain": keyId.hostname,
4615
+ "url.path": keyId.pathname,
4616
+ "url.query": keyId.search.replace(/^\?/, ""),
4617
+ "url.fragment": keyId.hash.replace(/^#/, "")
4618
+ }
4619
+ }, async (span) => {
4620
+ try {
4621
+ const result = await fetcher();
4622
+ span.setAttribute("activitypub.actor.key.cached", result.cached);
4623
+ return result;
4624
+ } catch (e) {
4625
+ span.setStatus({
4626
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
4627
+ message: String(e)
4628
+ });
4629
+ throw e;
4630
+ } finally {
4631
+ span.end();
4632
+ }
4633
+ });
4634
+ }
4635
+ /**
4636
+ * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
4637
+ * If the given URL contains an {@link Actor} object, it tries to find
4638
+ * the corresponding key in the `publicKey` or `assertionMethod` property.
4639
+ * @template T The type of the key to fetch. Either {@link CryptographicKey}
4640
+ * or {@link Multikey}.
4641
+ * @param keyId The URL of the key.
4642
+ * @param cls The class of the key to fetch. Either {@link CryptographicKey}
4643
+ * or {@link Multikey}.
4644
+ * @param options Options for fetching the key. See {@link FetchKeyOptions}.
4645
+ * @returns The fetched key or `null` if the key is not found.
4646
+ * @since 1.3.0
4647
+ */
4648
+ function fetchKey(keyId, cls, options = {}) {
4649
+ keyId = typeof keyId === "string" ? new URL(keyId) : keyId;
4650
+ return withFetchKeySpan(keyId, options.tracerProvider, () => fetchKeyInternal(keyId, cls, options));
4651
+ }
4652
+ /**
4653
+ * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL,
4654
+ * preserving transport-level fetch failures for callers that need to inspect
4655
+ * why the key could not be loaded.
4656
+ *
4657
+ * @template T The type of the key to fetch. Either {@link CryptographicKey}
4658
+ * or {@link Multikey}.
4659
+ * @param keyId The URL of the key.
4660
+ * @param cls The class of the key to fetch. Either {@link CryptographicKey}
4661
+ * or {@link Multikey}.
4662
+ * @param options Options for fetching the key.
4663
+ * @returns The fetched key, or detailed fetch failure information.
4664
+ * @since 2.1.0
4665
+ */
4666
+ async function fetchKeyDetailed(keyId, cls, options = {}) {
4667
+ const cacheKey = typeof keyId === "string" ? new URL(keyId) : keyId;
4668
+ return await withFetchKeySpan(cacheKey, options.tracerProvider, async () => {
4669
+ return await fetchKeyWithResult(cacheKey, cls, options, async (cacheKey, keyId, keyCache, logger) => {
4670
+ const fetchError = await keyCache?.getFetchError?.(cacheKey);
4671
+ if (fetchError != null) {
4672
+ logger.debug("Entry {keyId} found in cache with preserved fetch failure details.", { keyId });
4673
+ return {
4674
+ key: null,
4675
+ cached: true,
4676
+ fetchError
4677
+ };
4678
+ }
4679
+ logger.debug("Entry {keyId} found in cache, but no fetch failure details are available.", { keyId });
4680
+ return {
4681
+ key: null,
4682
+ cached: true
4683
+ };
4684
+ }, async (error, cacheKey, keyId, keyCache, logger) => {
4685
+ logger.debug("Failed to fetch key {keyId}.", {
4686
+ keyId,
4687
+ error
4688
+ });
4689
+ await keyCache?.set(cacheKey, null);
4690
+ if (error instanceof _fedify_vocab_runtime.FetchError && error.response != null) {
4691
+ const fetchError = {
4692
+ status: error.response.status,
4693
+ response: error.response.clone()
4694
+ };
4695
+ await keyCache?.setFetchError?.(cacheKey, fetchError);
4696
+ return {
4697
+ key: null,
4698
+ cached: false,
4699
+ fetchError
4700
+ };
4701
+ }
4702
+ const fetchError = { error: error instanceof Error ? error : new Error(String(error)) };
4703
+ await keyCache?.setFetchError?.(cacheKey, fetchError);
4704
+ return {
4705
+ key: null,
4706
+ cached: false,
4707
+ fetchError
4708
+ };
4709
+ });
4710
+ });
4711
+ }
4712
+ async function getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger) {
4713
+ if (keyCache == null) return null;
4714
+ const cachedKey = await keyCache.get(cacheKey);
4715
+ const hit = checkCachedKeyHit(cachedKey, keyId, cls, logger);
4716
+ if (hit != null) return hit;
4717
+ if (cachedKey === null) {
4718
+ logger.debug("Entry {keyId} found in cache, but it is unavailable.", { keyId });
4719
+ return {
4720
+ key: null,
4721
+ cached: true
4722
+ };
4723
+ }
4724
+ return null;
4725
+ }
4726
+ function checkCachedKeyHit(cachedKey, keyId, cls, logger) {
4727
+ if (cachedKey instanceof cls && cachedKey.publicKey != null) {
4728
+ logger.debug("Key {keyId} found in cache.", { keyId });
4729
+ return {
4730
+ key: cachedKey,
4731
+ cached: true
4732
+ };
4733
+ }
4734
+ return null;
4735
+ }
4736
+ async function clearFetchErrorMetadata(keyId, keyCache) {
4737
+ await keyCache?.setFetchError?.(keyId, null);
4738
+ }
4739
+ function isDidKeyUrl(keyId) {
4740
+ return keyId.protocol === "did:" && keyId.pathname.startsWith("key:");
4741
+ }
4742
+ async function doRawKeysMatch(left, right) {
4743
+ const [leftRaw, rightRaw] = await Promise.all([crypto.subtle.exportKey("raw", left), crypto.subtle.exportKey("raw", right)]);
4744
+ if (leftRaw.byteLength !== rightRaw.byteLength) return false;
4745
+ const leftBytes = new Uint8Array(leftRaw);
4746
+ const rightBytes = new Uint8Array(rightRaw);
4747
+ return leftBytes.every((byte, index) => byte === rightBytes[index]);
4748
+ }
4749
+ async function isCachedDidKeyValid(key, verificationMethod) {
4750
+ try {
4751
+ return key.id?.href === verificationMethod.id.href && key.controllerId?.href === verificationMethod.controller.href && await doRawKeysMatch(key.publicKey, verificationMethod.publicKey);
4752
+ } catch {
4753
+ return false;
4754
+ }
4755
+ }
4756
+ async function resolveDidKey(cacheKey, cls, keyCache, logger) {
4757
+ if (!isDidKeyUrl(cacheKey)) return null;
4758
+ const keyId = cacheKey.href;
4759
+ if (!(cls === _fedify_vocab.Multikey)) {
4760
+ logger.debug("Failed to resolve did:key {keyId}; did:key verification methods are only supported as Multikey values.", { keyId });
4761
+ return {
4762
+ key: null,
4763
+ cached: false
4764
+ };
4765
+ }
4766
+ let verificationMethod;
4767
+ try {
4768
+ verificationMethod = await (0, _fedify_vocab_runtime.parseDidKeyVerificationMethod)(cacheKey);
4769
+ } catch (error) {
4770
+ logger.debug("Failed to resolve did:key verification method {keyId}: {error}", {
4771
+ keyId,
4772
+ error
4773
+ });
4774
+ return {
4775
+ key: null,
4776
+ cached: false
4777
+ };
4778
+ }
4779
+ const hit = checkCachedKeyHit(await keyCache?.get(cacheKey), keyId, cls, logger);
4780
+ if (hit?.key instanceof _fedify_vocab.Multikey && await isCachedDidKeyValid(hit.key, verificationMethod)) return hit;
4781
+ const key = new _fedify_vocab.Multikey({
4782
+ id: verificationMethod.id,
4783
+ controller: verificationMethod.controller,
4784
+ publicKey: verificationMethod.publicKey
4785
+ });
4786
+ await keyCache?.set(cacheKey, key);
4787
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4788
+ logger.debug("Resolved did:key verification method {keyId}.", { keyId });
4789
+ return {
4790
+ key,
4791
+ cached: false
4792
+ };
4793
+ }
4794
+ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
4795
+ let object;
4796
+ try {
4797
+ object = await _fedify_vocab.Object.fromJsonLd(document, {
4798
+ documentLoader,
4799
+ contextLoader,
4800
+ tracerProvider
4801
+ });
4802
+ } catch (e) {
4803
+ if (!(e instanceof TypeError)) throw e;
4804
+ try {
4805
+ object = await cls.fromJsonLd(document, {
4806
+ documentLoader,
4807
+ contextLoader,
4808
+ tracerProvider
4809
+ });
4810
+ } catch (e) {
4811
+ if (e instanceof TypeError) {
4812
+ logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
4813
+ await keyCache?.set(cacheKey, null);
4814
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4815
+ return {
4816
+ key: null,
4817
+ cached: false
4818
+ };
4819
+ }
4820
+ throw e;
4821
+ }
4822
+ }
4823
+ let key = null;
4824
+ if (object instanceof cls) key = object;
4825
+ else if ((0, _fedify_vocab.isActor)(object)) {
4826
+ const keys = cls === _fedify_vocab.CryptographicKey ? object.getPublicKeys({
4827
+ documentLoader,
4828
+ contextLoader,
4829
+ suppressError: true,
4830
+ tracerProvider
4831
+ }) : object.getAssertionMethods({
4832
+ documentLoader,
4833
+ contextLoader,
4834
+ suppressError: true,
4835
+ tracerProvider
4836
+ });
4837
+ let length = 0;
4838
+ let lastKey = null;
4839
+ try {
4840
+ for await (const k of keys) {
4841
+ length++;
4842
+ lastKey = k;
4843
+ if (k.id?.href === keyId) {
4844
+ key = k;
4845
+ break;
4846
+ }
4847
+ }
4848
+ } catch (e) {
4849
+ if (!(e instanceof TypeError)) throw e;
4850
+ logger.debug("Failed to verify; a malformed key was encountered while iterating the keys of {keyId}; treating it as a missing key: {error}", {
4851
+ keyId,
4852
+ error: e
4853
+ });
4854
+ }
4855
+ const keyIdUrl = new URL(keyId);
4856
+ if (key == null && keyIdUrl.hash === "" && length === 1) key = lastKey;
4857
+ if (key == null) {
4858
+ logger.debug("Failed to verify; object {keyId} returned an {actorType}, but has no key matching {keyId}.", {
4859
+ keyId,
4860
+ actorType: object.constructor.name
4861
+ });
4862
+ await keyCache?.set(cacheKey, null);
4863
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4864
+ return {
4865
+ key: null,
4866
+ cached: false
4867
+ };
4868
+ }
4869
+ } else {
4870
+ logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
4871
+ await keyCache?.set(cacheKey, null);
4872
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4873
+ return {
4874
+ key: null,
4875
+ cached: false
4876
+ };
4877
+ }
4878
+ if (key.publicKey == null) {
4879
+ logger.debug("Failed to verify; key {keyId} has no publicKeyPem field.", { keyId });
4880
+ await keyCache?.set(cacheKey, null);
4881
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4882
+ return {
4883
+ key: null,
4884
+ cached: false
4885
+ };
4886
+ }
4887
+ if (keyCache != null) {
4888
+ await keyCache.set(cacheKey, key);
4889
+ logger.debug("Key {keyId} cached.", { keyId });
4890
+ }
4891
+ await clearFetchErrorMetadata(cacheKey, keyCache);
4892
+ return {
4893
+ key,
4894
+ cached: false
4895
+ };
4896
+ }
4897
+ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, onFetchError) {
4898
+ const start = performance.now();
4899
+ let outcome = { result: "error" };
4900
+ try {
4901
+ const logger = (0, _logtape_logtape.getLogger)([
4902
+ "fedify",
4903
+ "sig",
4904
+ "key"
4905
+ ]);
4906
+ const keyId = cacheKey.href;
4907
+ const keyCache = options.keyCache;
4908
+ const didKey = await resolveDidKey(cacheKey, cls, keyCache, logger);
4909
+ if (didKey != null) {
4910
+ outcome = { result: didKey.key == null ? "invalid" : didKey.cached ? "hit" : "fetched" };
4911
+ return didKey;
4912
+ }
4913
+ const cached = await getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger);
4914
+ if (cached?.key === null && cached.cached) {
4915
+ const cachedUnavailable = await onCachedUnavailable(cacheKey, keyId, keyCache, logger);
4916
+ outcome = { result: "hit" };
4917
+ return cachedUnavailable;
4918
+ }
4919
+ if (cached != null) {
4920
+ outcome = { result: "hit" };
4921
+ return cached;
4922
+ }
4923
+ logger.debug("Fetching key {keyId} to verify signature...", { keyId });
4924
+ let document;
4925
+ try {
4926
+ document = (await (options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId)).document;
4927
+ } catch (error) {
4928
+ const classified = classifyFetchError(error);
4929
+ const errored = await onFetchError(error, cacheKey, keyId, keyCache, logger);
4930
+ outcome = classified;
4931
+ return errored;
4932
+ }
4933
+ const resolved = await resolveFetchedKey(document, cacheKey, keyId, cls, options, logger);
4934
+ outcome = { result: resolved.key != null ? "fetched" : "invalid" };
4935
+ return resolved;
4936
+ } finally {
4937
+ recordKeyLookup(options.meterProvider, {
4938
+ durationMs: getDurationMs(start),
4939
+ result: outcome.result,
4940
+ remoteUrl: cacheKey,
4941
+ cacheEnabled: options.keyCache != null,
4942
+ statusCode: outcome.statusCode
4943
+ });
4944
+ }
4945
+ }
4946
+ async function fetchKeyInternal(keyId, cls, options = {}) {
4947
+ return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
4948
+ return {
4949
+ key: null,
4950
+ cached: true
4951
+ };
4952
+ }, async (error, cacheKey, keyId, keyCache, logger) => {
4953
+ logger.debug("Failed to fetch key {keyId}.", {
4954
+ keyId,
4955
+ error
4956
+ });
4957
+ await keyCache?.set(cacheKey, null);
4958
+ if (error instanceof _fedify_vocab_runtime.FetchError && error.response != null) await keyCache?.setFetchError?.(cacheKey, {
4959
+ status: error.response.status,
4960
+ response: error.response.clone()
4961
+ });
4962
+ else await keyCache?.setFetchError?.(cacheKey, { error: error instanceof Error ? error : new Error(String(error)) });
4963
+ return {
4964
+ key: null,
4965
+ cached: false
4966
+ };
4967
+ });
4968
+ }
4969
+ //#endregion
4970
+ //#region src/sig/http.ts
4971
+ const Temporal = Temporal$1;
4972
+ const DEFAULT_MAX_REDIRECTION = 20;
4973
+ const DOUBLE_KNOCK_TRANSPORT_RETRY_DELAY_MS = 100;
4974
+ /**
4975
+ * Signs a request using the given private key.
4976
+ * @param request The request to sign.
4977
+ * @param privateKey The private key to use for signing.
4978
+ * @param keyId The key ID to use for the signature. It will be used by the
4979
+ * verifier.
4980
+ * @returns The signed request.
4981
+ * @throws {TypeError} If the private key is invalid or unsupported.
4982
+ */
4983
+ async function signRequest(request, privateKey, keyId, options = {}) {
4984
+ validateCryptoKey(privateKey, "private");
4985
+ return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(name, version).startActiveSpan("http_signatures.sign", async (span) => {
4986
+ try {
4987
+ const spec = options.spec ?? "draft-cavage-http-signatures-12";
4988
+ let signed;
4989
+ if (spec === "rfc9421") signed = await signRequestRfc9421(request, privateKey, keyId, span, options.currentTime, options.body, options.rfc9421);
4990
+ else signed = await signRequestDraft(request, privateKey, keyId, span, options.currentTime, options.body);
4991
+ if (span.isRecording()) {
4992
+ span.setAttribute(_opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_METHOD, signed.method);
4993
+ span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_FULL, signed.url);
4994
+ for (const [name, value] of signed.headers) span.setAttribute((0, _opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_HEADER)(name), value);
4995
+ span.setAttribute("http_signatures.key_id", keyId.href);
4996
+ }
4997
+ return signed;
4998
+ } catch (error) {
4999
+ span.setStatus({
5000
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
5001
+ message: String(error)
5002
+ });
5003
+ throw error;
5004
+ } finally {
5005
+ span.end();
5006
+ }
5007
+ });
5008
+ }
5009
+ async function signRequestDraft(request, privateKey, keyId, span, currentTime, bodyBuffer) {
5010
+ if (privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
5011
+ const url = new URL(request.url);
5012
+ const body = bodyBuffer !== void 0 ? bodyBuffer : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
5013
+ const headers = new Headers(request.headers);
5014
+ if (!headers.has("Host")) headers.set("Host", url.host);
5015
+ if (!headers.has("Digest") && body != null) {
5016
+ const digest = await crypto.subtle.digest("SHA-256", body);
5017
+ headers.set("Digest", `SHA-256=${(0, byte_encodings_base64.encodeBase64)(digest)}`);
5018
+ if (span.isRecording()) span.setAttribute("http_signatures.digest.sha-256", (0, byte_encodings_hex.encodeHex)(digest));
5019
+ }
5020
+ if (!headers.has("Date")) headers.set("Date", currentTime == null ? (/* @__PURE__ */ new Date()).toUTCString() : new Date(currentTime.toString()).toUTCString());
5021
+ const serialized = [["(request-target)", `${request.method.toLowerCase()} ${url.pathname}`], ...headers];
5022
+ const headerNames = serialized.map(([name]) => name);
5023
+ const message = serialized.map(([name, value]) => `${name}: ${value.trim()}`).join("\n");
5024
+ const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, new TextEncoder().encode(message));
5025
+ const sigHeader = `keyId="${keyId.href}",algorithm="rsa-sha256",headers="${headerNames.join(" ")}",signature="${(0, byte_encodings_base64.encodeBase64)(signature)}"`;
5026
+ headers.set("Signature", sigHeader);
5027
+ if (span.isRecording()) {
5028
+ span.setAttribute("http_signatures.algorithm", "rsa-sha256");
5029
+ span.setAttribute("http_signatures.signature", (0, byte_encodings_hex.encodeHex)(signature));
5030
+ }
5031
+ return new Request(request, {
5032
+ headers,
5033
+ body
5034
+ });
5035
+ }
5036
+ function formatRfc9421SignatureParameters(params) {
5037
+ return Array.from(iterRfc9421(params)).join(";");
5038
+ }
5039
+ function* iterRfc9421(params) {
5040
+ yield `alg="${params.algorithm}"`;
5041
+ yield `keyid="${params.keyId.href}"`;
5042
+ yield `created=${params.created}`;
5043
+ if (params.expires != null) yield `expires=${params.expires}`;
5044
+ if (params.nonce != null) yield `nonce="${escapeSfString(params.nonce)}"`;
5045
+ if (params.tag != null) yield `tag="${escapeSfString(params.tag)}"`;
5046
+ }
5047
+ const escapeSfString = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
5048
+ function formatComponentId(component) {
5049
+ return (0, structured_field_values.encodeItem)(new structured_field_values.Item(component.value, component.params));
5050
+ }
5051
+ /**
5052
+ * Creates a signature base for a request according to RFC 9421.
5053
+ * @param request The request to create a signature base for.
5054
+ * @param components The components to include in the signature base.
5055
+ * @param parameters The signature parameters to include in the signature base.
5056
+ * @returns The signature base as a string.
5057
+ */
5058
+ function createRfc9421SignatureBase(request, components, parameters) {
5059
+ const url = new URL(request.url);
5060
+ return components.map((component) => {
5061
+ const id = formatComponentId(component);
5062
+ const derived = derivedComponents[component.value]?.(request, url);
5063
+ if (derived != null) return `${id}: ${derived}`;
5064
+ if (component.value.startsWith("@")) throw new Error(`Unsupported derived component: ${component.value}`);
5065
+ const header = request.headers.get(component.value);
5066
+ if (header == null) throw new Error(`Missing header: ${component.value}`);
5067
+ return `${id}: ${header}`;
5068
+ }).concat([`"@signature-params": (${components.map((c) => formatComponentId(c)).join(" ")});${parameters}`]).join("\n");
5069
+ }
5070
+ const derivedComponents = {
5071
+ "@method": (request) => request.method.toUpperCase(),
5072
+ "@target-uri": (_, url) => url.href,
5073
+ "@authority": (_, url) => url.host,
5074
+ "@scheme": (_, url) => url.protocol.slice(0, -1),
5075
+ "@request-target": (request, url) => `${request.method.toLowerCase()} ${url.pathname}${url.search}`,
5076
+ "@path": (_, url) => url.pathname,
5077
+ "@query": (_, { search }) => search.startsWith("?") ? search.slice(1) : search,
5078
+ "@query-param": () => {
5079
+ throw new Error("@query-param requires a parameter name");
5080
+ },
5081
+ "@status": () => {
5082
+ throw new Error("@status is only valid for responses");
5083
+ }
5084
+ };
5085
+ /**
5086
+ * Formats a signature using rfc9421 format.
5087
+ * @param signature The raw signature bytes.
5088
+ * @param components The components that were signed.
5089
+ * @param parameters The signature parameters.
5090
+ * @returns The formatted signature string.
5091
+ */
5092
+ function formatRfc9421Signature(signature, components, parameters, label = "sig1") {
5093
+ return [`${label}=(${components.map((c) => formatComponentId(c)).join(" ")});${parameters}`, `${label}=:${(0, byte_encodings_base64.encodeBase64)(signature)}:`];
5094
+ }
5095
+ /**
5096
+ * Parse RFC 9421 Signature-Input header.
5097
+ * @param signatureInput The Signature-Input header value.
5098
+ * @returns Parsed signature input parameters.
5099
+ */
5100
+ function parseRfc9421SignatureInput(signatureInput) {
5101
+ let dict;
5102
+ try {
5103
+ dict = (0, structured_field_values.decodeDict)(signatureInput);
5104
+ } catch (error) {
5105
+ (0, _logtape_logtape.getLogger)([
5106
+ "fedify",
5107
+ "sig",
5108
+ "http"
5109
+ ]).debug("Failed to parse Signature-Input header: {signatureInput}", {
5110
+ signatureInput,
5111
+ error
5112
+ });
5113
+ return {};
5114
+ }
5115
+ const result = {};
5116
+ for (const [label, item] of Object.entries(dict)) {
5117
+ if (!Array.isArray(item.value) || typeof item.params.keyid !== "string" || typeof item.params.created !== "number") continue;
5118
+ const components = item.value.filter((subitem) => typeof subitem.value === "string").map((subitem) => ({
5119
+ value: subitem.value,
5120
+ params: subitem.params ?? {}
5121
+ }));
5122
+ const params = (0, structured_field_values.encodeItem)(new structured_field_values.Item(0, item.params));
5123
+ result[label] = {
5124
+ keyId: item.params.keyid,
5125
+ alg: item.params.alg,
5126
+ created: item.params.created,
5127
+ nonce: typeof item.params.nonce === "string" ? item.params.nonce : void 0,
5128
+ tag: typeof item.params.tag === "string" ? item.params.tag : void 0,
5129
+ components,
5130
+ parameters: params.slice(params.indexOf(";") + 1)
5131
+ };
5132
+ }
5133
+ return result;
5134
+ }
5135
+ /**
5136
+ * Parse RFC 9421 Signature header.
5137
+ * @param signature The Signature header value.
5138
+ * @returns Parsed signature values.
5139
+ */
5140
+ function parseRfc9421Signature(signature) {
5141
+ let dict;
5142
+ try {
5143
+ dict = (0, structured_field_values.decodeDict)(signature);
5144
+ } catch (error) {
5145
+ (0, _logtape_logtape.getLogger)([
5146
+ "fedify",
5147
+ "sig",
5148
+ "http"
5149
+ ]).debug("Failed to parse Signature header: {signature}", {
5150
+ signature,
5151
+ error
5152
+ });
5153
+ return {};
5154
+ }
5155
+ const result = {};
5156
+ for (const [key, value] of Object.entries(dict)) if (value.value instanceof Uint8Array) result[key] = value.value;
5157
+ return result;
5158
+ }
5159
+ async function signRequestRfc9421(request, privateKey, keyId, span, currentTime, bodyBuffer, rfc9421Options) {
5160
+ if (privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
5161
+ const url = new URL(request.url);
5162
+ const body = bodyBuffer !== void 0 ? bodyBuffer : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
5163
+ const headers = new Headers(request.headers);
5164
+ if (!headers.has("Host")) headers.set("Host", url.host);
5165
+ if (!headers.has("Content-Digest") && body != null) {
5166
+ const digest = await crypto.subtle.digest("SHA-256", body);
5167
+ headers.set("Content-Digest", `sha-256=:${(0, byte_encodings_base64.encodeBase64)(digest)}:`);
5168
+ if (span.isRecording()) span.setAttribute("http_signatures.digest.sha-256", (0, byte_encodings_hex.encodeHex)(digest));
5169
+ }
5170
+ currentTime ??= Temporal.Now.instant();
5171
+ const created = currentTime.epochMilliseconds / 1e3 | 0;
5172
+ if (!headers.has("Date")) headers.set("Date", new Date(currentTime.toString()).toUTCString());
5173
+ const label = rfc9421Options?.label ?? "sig1";
5174
+ const components = [...rfc9421Options?.components ?? [
5175
+ {
5176
+ value: "@method",
5177
+ params: {}
5178
+ },
5179
+ {
5180
+ value: "@target-uri",
5181
+ params: {}
5182
+ },
5183
+ {
5184
+ value: "@authority",
5185
+ params: {}
5186
+ },
5187
+ {
5188
+ value: "host",
5189
+ params: {}
5190
+ },
5191
+ {
5192
+ value: "date",
5193
+ params: {}
5194
+ }
5195
+ ], ...body != null ? [{
5196
+ value: "content-digest",
5197
+ params: {}
5198
+ }] : []];
5199
+ const signatureParams = formatRfc9421SignatureParameters({
5200
+ algorithm: "rsa-v1_5-sha256",
5201
+ keyId,
5202
+ created,
5203
+ expires: rfc9421Options?.expires === true ? (currentTime.epochMilliseconds / 1e3 | 0) + 3600 : void 0,
5204
+ nonce: rfc9421Options?.nonce,
5205
+ tag: rfc9421Options?.tag
5206
+ });
5207
+ let signatureBase;
5208
+ try {
5209
+ signatureBase = createRfc9421SignatureBase(new Request(request.url, {
5210
+ method: request.method,
5211
+ headers
5212
+ }), components, signatureParams);
5213
+ } catch (error) {
5214
+ throw new TypeError(`Failed to create signature base: ${String(error)}; it is probably a bug in the implementation. Please report it at Fedify's issue tracker.`);
5215
+ }
5216
+ const signatureBytes = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, new TextEncoder().encode(signatureBase));
5217
+ const [signatureInput, signature] = formatRfc9421Signature(signatureBytes, components, signatureParams, label);
5218
+ const existingInput = headers.get("Signature-Input");
5219
+ headers.set("Signature-Input", existingInput != null ? `${existingInput}, ${signatureInput}` : signatureInput);
5220
+ const existingSignature = headers.get("Signature");
5221
+ headers.set("Signature", existingSignature != null ? `${existingSignature}, ${signature}` : signature);
5222
+ if (span.isRecording()) {
5223
+ span.setAttribute("http_signatures.algorithm", "rsa-v1_5-sha256");
5224
+ span.setAttribute("http_signatures.signature", (0, byte_encodings_hex.encodeHex)(signatureBytes));
5225
+ span.setAttribute("http_signatures.created", created.toString());
5226
+ }
5227
+ return new Request(request, {
5228
+ headers,
5229
+ body
5230
+ });
5231
+ }
5232
+ const supportedHashAlgorithms = {
5233
+ "sha": "SHA-1",
5234
+ "sha-256": "SHA-256",
5235
+ "sha-512": "SHA-512"
5236
+ };
5237
+ function noSignatureResult() {
5238
+ return {
5239
+ verified: false,
5240
+ reason: { type: "noSignature" }
5241
+ };
5242
+ }
5243
+ function invalidSignatureResult(keyId) {
5244
+ return keyId == null ? {
5245
+ verified: false,
5246
+ reason: { type: "invalidSignature" }
5247
+ } : {
5248
+ verified: false,
5249
+ reason: {
5250
+ type: "invalidSignature",
5251
+ keyId
5252
+ }
5253
+ };
5254
+ }
5255
+ function keyFetchErrorResult(keyId, result) {
5256
+ return {
5257
+ verified: false,
5258
+ reason: {
5259
+ type: "keyFetchError",
5260
+ keyId,
5261
+ result
5262
+ }
5263
+ };
5264
+ }
5265
+ function parseKeyId(value) {
5266
+ if (value == null) return null;
5267
+ try {
5268
+ return new URL(value);
5269
+ } catch {
5270
+ return null;
5271
+ }
5272
+ }
5273
+ function getKeyFetchErrorName(error) {
5274
+ return error.name || error.constructor.name || "Error";
5275
+ }
5276
+ /**
5277
+ * Known draft-cavage `algorithm` parameter values, used to keep the
5278
+ * `http_signatures.algorithm` metric attribute on a bounded set. The header
5279
+ * field is attacker-controlled and not used to select the verification
5280
+ * algorithm, so unknown values are dropped from the metric to prevent
5281
+ * cardinality blow-up.
5282
+ */
5283
+ const DRAFT_KNOWN_ALGORITHMS = /* @__PURE__ */ new Set([
5284
+ "ecdsa-sha256",
5285
+ "ecdsa-sha384",
5286
+ "ecdsa-sha512",
5287
+ "ed25519",
5288
+ "hs2019",
5289
+ "rsa-sha1",
5290
+ "rsa-sha256",
5291
+ "rsa-sha512"
5292
+ ]);
5293
+ function classifyHttpVerifyResult(result) {
5294
+ if (result.verified) return "verified";
5295
+ return result.reason.type === "noSignature" ? "missing" : "rejected";
5296
+ }
5297
+ function recordVerificationResult(span, result) {
5298
+ span.setAttribute("http_signatures.verified", result.verified);
5299
+ if (result.verified === true) return;
5300
+ const reason = result.reason;
5301
+ span.setAttribute("http_signatures.failure_reason", reason.type);
5302
+ if ("keyId" in reason && reason.keyId != null) span.setAttribute("http_signatures.key_id", reason.keyId.href);
5303
+ if (reason.type !== "keyFetchError") return;
5304
+ if ("status" in reason.result) span.setAttribute("http_signatures.key_fetch_status", reason.result.status);
5305
+ else span.setAttribute("http_signatures.key_fetch_error", getKeyFetchErrorName(reason.result.error));
5306
+ }
5307
+ /**
5308
+ * Verifies the signature of a request.
5309
+ *
5310
+ * Note that this function consumes the request body, so it should not be used
5311
+ * if the request body is already consumed. Consuming the request body after
5312
+ * calling this function is okay, since this function clones the request
5313
+ * under the hood.
5314
+ *
5315
+ * @param request The request to verify.
5316
+ * @param options Options for verifying the request.
5317
+ * @returns The public key of the verified signature, or `null` if the signature
5318
+ * could not be verified.
5319
+ */
5320
+ async function verifyRequest(request, options = {}) {
5321
+ const result = await verifyRequestDetailed(request, options);
5322
+ return result.verified ? result.key : null;
5323
+ }
5324
+ /**
5325
+ * Verifies the signature of a request and returns a structured failure reason
5326
+ * when verification does not succeed.
5327
+ *
5328
+ * @param request The request to verify.
5329
+ * @param options Options for verifying the request.
5330
+ * @returns The verified public key, or a structured verification failure.
5331
+ * @since 2.1.0
5332
+ */
5333
+ async function verifyRequestDetailed(request, options = {}) {
5334
+ return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(name, version).startActiveSpan("http_signatures.verify", async (span) => {
5335
+ if (span.isRecording()) {
5336
+ span.setAttribute(_opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_METHOD, request.method);
5337
+ span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_FULL, request.url);
5338
+ for (const [name, value] of request.headers) span.setAttribute((0, _opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_HEADER)(name), value);
5339
+ }
5340
+ const start = performance.now();
5341
+ const metricsContext = {};
5342
+ let result;
5343
+ let threw = false;
5344
+ try {
5345
+ let spec = options.spec;
5346
+ if (spec == null) spec = request.headers.has("Signature-Input") ? "rfc9421" : "draft-cavage-http-signatures-12";
5347
+ if (spec === "rfc9421") result = await verifyRequestRfc9421(request, span, metricsContext, options);
5348
+ else result = await verifyRequestDraft(request, span, metricsContext, options);
5349
+ recordVerificationResult(span, result);
5350
+ if (!result.verified) span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
5351
+ return result;
5352
+ } catch (error) {
5353
+ threw = true;
5354
+ span.setStatus({
5355
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
5356
+ message: String(error)
5357
+ });
5358
+ throw error;
5359
+ } finally {
5360
+ const classified = threw ? "error" : classifyHttpVerifyResult(result);
5361
+ const failureReason = result != null && !result.verified && result.reason.type !== "noSignature" ? result.reason.type : void 0;
5362
+ getFederationMetrics(options.meterProvider).recordSignatureVerificationDuration(getDurationMs(start), "http", classified, {
5363
+ algorithm: metricsContext.algorithm,
5364
+ failureReason
5365
+ });
5366
+ span.end();
5367
+ }
5368
+ });
5369
+ }
5370
+ async function verifyRequestDraft(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, meterProvider, tracerProvider } = {}) {
5371
+ const logger = (0, _logtape_logtape.getLogger)([
5372
+ "fedify",
5373
+ "sig",
5374
+ "http"
5375
+ ]);
5376
+ if (request.bodyUsed) {
5377
+ logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
5378
+ return noSignatureResult();
5379
+ } else if (request.body?.locked) {
5380
+ logger.error("Failed to verify; the request body is locked.", { url: request.url });
5381
+ return noSignatureResult();
5382
+ }
5383
+ const originalRequest = request;
5384
+ request = request.clone();
5385
+ const sigHeader = request.headers.get("Signature");
5386
+ if (sigHeader == null) {
5387
+ logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
5388
+ return noSignatureResult();
5389
+ }
5390
+ const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
5391
+ const parsedKeyId = parseKeyId(sigValues.keyId);
5392
+ const dateHeader = request.headers.get("Date");
5393
+ if (dateHeader == null) {
5394
+ logger.debug("Failed to verify; no Date header found.", { headers: Object.fromEntries(request.headers.entries()) });
5395
+ return invalidSignatureResult(parsedKeyId);
5396
+ }
5397
+ const digestHeader = request.headers.get("Digest");
5398
+ if (request.method !== "GET" && request.method !== "HEAD" && digestHeader == null) {
5399
+ logger.debug("Failed to verify; no Digest header found.", { headers: Object.fromEntries(request.headers.entries()) });
5400
+ return invalidSignatureResult(parsedKeyId);
5401
+ }
5402
+ let body = null;
5403
+ if (digestHeader != null) {
5404
+ body = await request.arrayBuffer();
5405
+ const digests = digestHeader.split(",").map((pair) => pair.includes("=") ? pair.split("=", 2) : [pair, ""]);
5406
+ let matched = false;
5407
+ for (let [algo, digestBase64] of digests) {
5408
+ algo = algo.trim().toLowerCase();
5409
+ if (!(algo in supportedHashAlgorithms)) continue;
5410
+ let digest;
5411
+ try {
5412
+ digest = (0, byte_encodings_base64.decodeBase64)(digestBase64);
5413
+ } catch (error) {
5414
+ logger.debug("Failed to verify; invalid base64 encoding: {digest}.", {
5415
+ digest: digestBase64,
5416
+ error
5417
+ });
5418
+ return invalidSignatureResult(parsedKeyId);
5419
+ }
5420
+ if (span.isRecording()) span.setAttribute(`http_signatures.digest.${algo}`, (0, byte_encodings_hex.encodeHex)(digest));
5421
+ const expectedDigest = await crypto.subtle.digest(supportedHashAlgorithms[algo], body);
5422
+ if (!timingSafeEqual(digest, new Uint8Array(expectedDigest))) {
5423
+ logger.debug("Failed to verify; digest mismatch ({algorithm}): {digest} != {expectedDigest}.", {
5424
+ algorithm: algo,
5425
+ digest: digestBase64,
5426
+ expectedDigest: (0, byte_encodings_base64.encodeBase64)(expectedDigest)
5427
+ });
5428
+ return invalidSignatureResult(parsedKeyId);
5429
+ }
5430
+ matched = true;
5431
+ }
5432
+ if (!matched) {
5433
+ logger.debug("Failed to verify; no supported digest algorithm found. Supported: {supportedAlgorithms}; found: {algorithms}.", {
5434
+ supportedAlgorithms: Object.keys(supportedHashAlgorithms),
5435
+ algorithms: digests.map(([algo]) => algo)
5436
+ });
5437
+ return invalidSignatureResult(parsedKeyId);
5438
+ }
5439
+ }
5440
+ const date = Temporal.Instant.from(new Date(dateHeader).toISOString());
5441
+ const now = currentTime ?? Temporal.Now.instant();
5442
+ if (timeWindow !== false) {
5443
+ const tw = timeWindow ?? { hours: 1 };
5444
+ if (Temporal.Instant.compare(date, now.add(tw)) > 0) {
5445
+ logger.debug("Failed to verify; Date is too far in the future.", {
5446
+ date: date.toString(),
5447
+ now: now.toString()
5448
+ });
5449
+ return invalidSignatureResult(parsedKeyId);
5450
+ } else if (Temporal.Instant.compare(date, now.subtract(tw)) < 0) {
5451
+ logger.debug("Failed to verify; Date is too far in the past.", {
5452
+ date: date.toString(),
5453
+ now: now.toString()
5454
+ });
5455
+ return invalidSignatureResult(parsedKeyId);
5456
+ }
5457
+ }
5458
+ if (!("keyId" in sigValues)) {
5459
+ logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
5460
+ return invalidSignatureResult(null);
5461
+ } else if (!("headers" in sigValues)) {
5462
+ logger.debug("Failed to verify; no headers field found in the Signature header.", { signature: sigHeader });
5463
+ return invalidSignatureResult(parsedKeyId);
5464
+ } else if (!("signature" in sigValues)) {
5465
+ logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
5466
+ return invalidSignatureResult(parsedKeyId);
5467
+ }
5468
+ if ("expires" in sigValues) {
5469
+ const expiresSeconds = parseInt(sigValues.expires);
5470
+ if (!Number.isInteger(expiresSeconds)) {
5471
+ logger.debug("Failed to verify; invalid expires field in the Signature header: {expires}.", {
5472
+ expires: sigValues.expires,
5473
+ signature: sigHeader
5474
+ });
5475
+ return invalidSignatureResult(parsedKeyId);
5476
+ }
5477
+ const expires = Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1e3);
5478
+ if (Temporal.Instant.compare(now, expires) > 0) {
5479
+ logger.debug("Failed to verify; signature expired at {expires} (now: {now}).", {
5480
+ expires: expires.toString(),
5481
+ now: now.toString(),
5482
+ signature: sigHeader
5483
+ });
5484
+ return invalidSignatureResult(parsedKeyId);
5485
+ }
5486
+ }
5487
+ if ("created" in sigValues) {
5488
+ const createdSeconds = parseInt(sigValues.created);
5489
+ if (!Number.isInteger(createdSeconds)) {
5490
+ logger.debug("Failed to verify; invalid created field in the Signature header: {created}.", {
5491
+ created: sigValues.created,
5492
+ signature: sigHeader
5493
+ });
5494
+ return invalidSignatureResult(parsedKeyId);
5495
+ }
5496
+ if (timeWindow !== false) {
5497
+ const created = Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1e3);
5498
+ const tw = timeWindow ?? { minutes: 1 };
5499
+ if (Temporal.Instant.compare(created, now.add(tw)) > 0) {
5500
+ logger.debug("Failed to verify; created is too far in the future.", {
5501
+ created: created.toString(),
5502
+ now: now.toString()
5503
+ });
5504
+ return invalidSignatureResult(parsedKeyId);
5505
+ } else if (Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
5506
+ logger.debug("Failed to verify; created is too far in the past.", {
5507
+ created: created.toString(),
5508
+ now: now.toString()
5509
+ });
5510
+ return invalidSignatureResult(parsedKeyId);
5511
+ }
5512
+ }
5513
+ }
5514
+ const { keyId, headers, signature } = sigValues;
5515
+ const keyIdUrl = parseKeyId(keyId);
5516
+ if (keyIdUrl == null) return invalidSignatureResult(null);
5517
+ span?.setAttribute("http_signatures.key_id", keyId);
5518
+ if ("algorithm" in sigValues) {
5519
+ span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
5520
+ const normalizedAlgorithm = sigValues.algorithm.toLowerCase();
5521
+ if (DRAFT_KNOWN_ALGORITHMS.has(normalizedAlgorithm)) metricsContext.algorithm = normalizedAlgorithm;
5522
+ }
5523
+ const { key, cached, fetchError } = await measureSignatureKeyFetch(meterProvider, "http", () => fetchKeyDetailed(keyIdUrl, _fedify_vocab.CryptographicKey, {
5524
+ documentLoader,
5525
+ contextLoader,
5526
+ keyCache,
5527
+ tracerProvider,
5528
+ meterProvider
5529
+ }));
5530
+ if (fetchError != null) return keyFetchErrorResult(keyIdUrl, fetchError);
5531
+ if (key == null) return invalidSignatureResult(keyIdUrl);
5532
+ const headerNames = headers.split(/\s+/g);
5533
+ if (!headerNames.includes("(request-target)") || !headerNames.includes("date")) {
5534
+ logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
5535
+ return invalidSignatureResult(keyIdUrl);
5536
+ }
5537
+ if (body != null && !headerNames.includes("digest")) {
5538
+ logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
5539
+ return invalidSignatureResult(keyIdUrl);
5540
+ }
5541
+ const message = headerNames.map((name) => `${name}: ` + (name === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name === "(created)" ? sigValues.created ?? "" : name === "(expires)" ? sigValues.expires ?? "" : name === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
5542
+ const sig = (0, byte_encodings_base64.decodeBase64)(signature);
5543
+ span?.setAttribute("http_signatures.signature", (0, byte_encodings_hex.encodeHex)(sig));
5544
+ if (!await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message))) {
5545
+ if (cached) {
5546
+ logger.debug("Failed to verify with the cached key {keyId}; signature {signature} is invalid. Retrying with the freshly fetched key...", {
5547
+ keyId,
5548
+ signature,
5549
+ message
5550
+ });
5551
+ return await verifyRequestDraft(originalRequest, span, metricsContext, {
5552
+ documentLoader,
5553
+ contextLoader,
5554
+ timeWindow,
5555
+ currentTime,
5556
+ keyCache: {
5557
+ get: () => Promise.resolve(void 0),
5558
+ set: async (keyId, key) => await keyCache?.set(keyId, key)
5559
+ },
5560
+ meterProvider,
5561
+ tracerProvider
5562
+ });
5563
+ }
5564
+ logger.debug("Failed to verify with the fetched key {keyId}; signature {signature} is invalid. Check if the key is correct or if the signed message is correct. The message to sign is:\n{message}", {
5565
+ keyId,
5566
+ signature,
5567
+ message
5568
+ });
5569
+ return invalidSignatureResult(keyIdUrl);
5570
+ }
5571
+ return {
5572
+ verified: true,
5573
+ key
5574
+ };
5575
+ }
5576
+ /**
5577
+ * RFC 9421 map of algorithm identifiers to WebCrypto algorithms
5578
+ */
5579
+ const rfc9421AlgorithmMap = {
5580
+ "rsa-v1_5-sha256": {
5581
+ name: "RSASSA-PKCS1-v1_5",
5582
+ hash: "SHA-256"
5583
+ },
5584
+ "rsa-v1_5-sha512": {
5585
+ name: "RSASSA-PKCS1-v1_5",
5586
+ hash: "SHA-512"
5587
+ },
5588
+ "rsa-pss-sha512": {
5589
+ name: "RSA-PSS",
5590
+ hash: "SHA-512"
5591
+ },
5592
+ "ecdsa-p256-sha256": {
5593
+ name: "ECDSA",
5594
+ hash: "SHA-256"
5595
+ },
5596
+ "ecdsa-p384-sha384": {
5597
+ name: "ECDSA",
5598
+ hash: "SHA-384"
5599
+ },
5600
+ "ed25519": { name: "Ed25519" }
5601
+ };
5602
+ /**
5603
+ * Verifies a Content-Digest header according to RFC 9421.
5604
+ * @param digestHeader The Content-Digest header value.
5605
+ * @param body The message body to verify against.
5606
+ * @returns Whether the digest is valid.
5607
+ */
5608
+ async function verifyRfc9421ContentDigest(digestHeader, body) {
5609
+ const digests = digestHeader.split(",").map((pair) => {
5610
+ pair = pair.trim();
5611
+ const pos = pair.indexOf("=");
5612
+ const algo = pos < 0 ? pair : pair.slice(0, pos);
5613
+ const value = pos < 0 ? "" : pair.slice(pos + 1);
5614
+ return {
5615
+ algo: algo.trim().toLowerCase(),
5616
+ value: value.trim()
5617
+ };
5618
+ });
5619
+ for (const { algo, value } of digests) {
5620
+ let hashAlgo;
5621
+ if (algo === "sha-256") hashAlgo = "SHA-256";
5622
+ else if (algo === "sha-512") hashAlgo = "SHA-512";
5623
+ else continue;
5624
+ const base64Match = value.match(/^:([^:]+):$/);
5625
+ if (!base64Match) continue;
5626
+ let digest;
5627
+ try {
5628
+ digest = (0, byte_encodings_base64.decodeBase64)(base64Match[1]);
5629
+ } catch {
5630
+ continue;
5631
+ }
5632
+ const calculatedDigest = await crypto.subtle.digest(hashAlgo, body);
5633
+ if (timingSafeEqual(digest, new Uint8Array(calculatedDigest))) return true;
5634
+ }
5635
+ return false;
5636
+ }
5637
+ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, meterProvider, tracerProvider } = {}) {
5638
+ const logger = (0, _logtape_logtape.getLogger)([
5639
+ "fedify",
5640
+ "sig",
5641
+ "http"
5642
+ ]);
5643
+ if (request.bodyUsed) {
5644
+ logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
5645
+ return noSignatureResult();
5646
+ } else if (request.body?.locked) {
5647
+ logger.error("Failed to verify; the request body is locked.", { url: request.url });
5648
+ return noSignatureResult();
5649
+ }
5650
+ const originalRequest = request;
5651
+ request = request.clone();
5652
+ const signatureInputHeader = request.headers.get("Signature-Input");
5653
+ if (!signatureInputHeader) {
5654
+ logger.debug("Failed to verify; no Signature-Input header found.", { headers: Object.fromEntries(request.headers.entries()) });
5655
+ return noSignatureResult();
5656
+ }
5657
+ const signatureHeader = request.headers.get("Signature");
5658
+ if (!signatureHeader) {
5659
+ logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
5660
+ return noSignatureResult();
5661
+ }
5662
+ const signatureInputs = parseRfc9421SignatureInput(signatureInputHeader);
5663
+ logger.debug("Parsed Signature-Input header: {signatureInputs}", { signatureInputs });
5664
+ const signatures = parseRfc9421Signature(signatureHeader);
5665
+ const signatureNames = Object.keys(signatureInputs);
5666
+ if (signatureNames.length === 0) {
5667
+ logger.debug("Failed to verify; no valid signatures found in Signature-Input header.", { header: signatureInputHeader });
5668
+ return invalidSignatureResult(null);
5669
+ }
5670
+ let failure = noSignatureResult();
5671
+ let failureAlgorithm;
5672
+ const setFailure = (result, algorithm) => {
5673
+ failure = result;
5674
+ failureAlgorithm = algorithm;
5675
+ };
5676
+ for (const sigName of signatureNames) {
5677
+ if (!signatures[sigName]) {
5678
+ setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
5679
+ continue;
5680
+ }
5681
+ const sigInput = signatureInputs[sigName];
5682
+ const sigBytes = signatures[sigName];
5683
+ const keyId = parseKeyId(sigInput.keyId);
5684
+ if (!sigInput.keyId) {
5685
+ logger.debug("Failed to verify; missing keyId in signature {signatureName}.", {
5686
+ signatureName: sigName,
5687
+ signatureInput: signatureInputHeader
5688
+ });
5689
+ setFailure(invalidSignatureResult(null));
5690
+ continue;
5691
+ }
5692
+ if (!sigInput.created) {
5693
+ logger.debug("Failed to verify; missing created timestamp in signature {signatureName}.", {
5694
+ signatureName: sigName,
5695
+ signatureInput: signatureInputHeader
5696
+ });
5697
+ setFailure(invalidSignatureResult(keyId));
5698
+ continue;
5699
+ }
5700
+ const signatureCreated = Temporal.Instant.fromEpochMilliseconds(sigInput.created * 1e3);
5701
+ const now = currentTime ?? Temporal.Now.instant();
5702
+ if (timeWindow !== false) {
5703
+ const tw = timeWindow ?? { hours: 1 };
5704
+ if (Temporal.Instant.compare(signatureCreated, now.add(tw)) > 0) {
5705
+ logger.debug("Failed to verify; signature created time is too far in the future.", {
5706
+ created: signatureCreated.toString(),
5707
+ now: now.toString()
5708
+ });
5709
+ setFailure(invalidSignatureResult(keyId));
5710
+ continue;
5711
+ } else if (Temporal.Instant.compare(signatureCreated, now.subtract(tw)) < 0) {
5712
+ logger.debug("Failed to verify; signature created time is too far in the past.", {
5713
+ created: signatureCreated.toString(),
5714
+ now: now.toString()
5715
+ });
5716
+ setFailure(invalidSignatureResult(keyId));
5717
+ continue;
5718
+ }
5719
+ }
5720
+ if (request.method !== "GET" && request.method !== "HEAD" && sigInput.components.some((c) => c.value === "content-digest")) {
5721
+ const contentDigestHeader = request.headers.get("Content-Digest");
5722
+ if (!contentDigestHeader) {
5723
+ logger.debug("Failed to verify; Content-Digest header required but not found.", { components: sigInput.components });
5724
+ setFailure(invalidSignatureResult(keyId));
5725
+ continue;
5726
+ }
5727
+ if (!await verifyRfc9421ContentDigest(contentDigestHeader, await request.arrayBuffer())) {
5728
+ logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
5729
+ setFailure(invalidSignatureResult(keyId));
5730
+ continue;
5731
+ }
5732
+ }
5733
+ span?.setAttribute("http_signatures.key_id", sigInput.keyId);
5734
+ span?.setAttribute("http_signatures.created", sigInput.created.toString());
5735
+ if (keyId == null) {
5736
+ setFailure(invalidSignatureResult(null));
5737
+ continue;
5738
+ }
5739
+ const { key, cached, fetchError } = await measureSignatureKeyFetch(meterProvider, "http", () => fetchKeyDetailed(keyId, _fedify_vocab.CryptographicKey, {
5740
+ documentLoader,
5741
+ contextLoader,
5742
+ keyCache,
5743
+ tracerProvider,
5744
+ meterProvider
5745
+ }));
5746
+ if (fetchError != null) {
5747
+ setFailure(keyFetchErrorResult(keyId, fetchError));
5748
+ continue;
5749
+ }
5750
+ if (!key) {
5751
+ logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
5752
+ setFailure(invalidSignatureResult(keyId));
5753
+ continue;
5754
+ }
5755
+ let alg = sigInput.alg?.toLowerCase();
5756
+ if (alg == null) {
5757
+ if (key.publicKey.algorithm.name === "RSASSA-PKCS1-v1_5") alg = "hash" in key.publicKey.algorithm ? key.publicKey.algorithm.hash === "SHA-512" ? "rsa-v1_5-sha512" : "rsa-v1_5-sha256" : "rsa-v1_5-sha256";
5758
+ else if (key.publicKey.algorithm.name === "RSA-PSS") alg = "rsa-pss-sha512";
5759
+ else if (key.publicKey.algorithm.name === "ECDSA") alg = "namedCurve" in key.publicKey.algorithm && key.publicKey.algorithm.namedCurve === "P-256" ? "ecdsa-p256-sha256" : "ecdsa-p384-sha384";
5760
+ else if (key.publicKey.algorithm.name === "Ed25519") alg = "ed25519";
5761
+ }
5762
+ if (alg) span?.setAttribute("http_signatures.algorithm", alg);
5763
+ const algorithm = alg && rfc9421AlgorithmMap[alg];
5764
+ const candidateAlgorithm = algorithm ? alg : void 0;
5765
+ if (!algorithm) {
5766
+ logger.debug("Failed to verify; unsupported algorithm: {algorithm}", {
5767
+ algorithm: sigInput.alg,
5768
+ supported: Object.keys(rfc9421AlgorithmMap)
5769
+ });
5770
+ setFailure(invalidSignatureResult(keyId));
5771
+ continue;
5772
+ }
5773
+ let signatureBase;
5774
+ try {
5775
+ signatureBase = createRfc9421SignatureBase(request, sigInput.components, sigInput.parameters);
5776
+ } catch (error) {
5777
+ logger.debug("Failed to create signature base for verification: {error}", {
5778
+ error,
5779
+ signatureInput: sigInput
5780
+ });
5781
+ setFailure(invalidSignatureResult(keyId), candidateAlgorithm);
5782
+ continue;
5783
+ }
5784
+ const signatureBaseBytes = new TextEncoder().encode(signatureBase);
5785
+ span?.setAttribute("http_signatures.signature", (0, byte_encodings_hex.encodeHex)(sigBytes));
5786
+ try {
5787
+ if (await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes)) {
5788
+ metricsContext.algorithm = candidateAlgorithm;
5789
+ return {
5790
+ verified: true,
5791
+ key,
5792
+ signatureLabel: sigName
5793
+ };
5794
+ } else if (cached) {
5795
+ logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
5796
+ return await verifyRequestRfc9421(originalRequest, span, metricsContext, {
5797
+ documentLoader,
5798
+ contextLoader,
5799
+ timeWindow,
5800
+ currentTime,
5801
+ keyCache: {
5802
+ get: () => Promise.resolve(void 0),
5803
+ set: async (keyId, key) => await keyCache?.set(keyId, key)
5804
+ },
5805
+ spec: "rfc9421",
5806
+ meterProvider,
5807
+ tracerProvider
5808
+ });
5809
+ } else {
5810
+ logger.debug("Failed to verify signature with fetched key {keyId}; signature invalid.", {
5811
+ keyId: sigInput.keyId,
5812
+ signatureBase
5813
+ });
5814
+ setFailure(invalidSignatureResult(keyId), candidateAlgorithm);
5815
+ }
5816
+ } catch (error) {
5817
+ logger.debug("Error during signature verification: {error}", {
5818
+ error,
5819
+ keyId: sigInput.keyId,
5820
+ algorithm: sigInput.alg
5821
+ });
5822
+ setFailure(invalidSignatureResult(keyId), candidateAlgorithm);
5823
+ }
5824
+ }
5825
+ metricsContext.algorithm = failureAlgorithm;
5826
+ return failure;
5827
+ }
5828
+ /**
5829
+ * Helper function to create a new Request for redirect handling.
5830
+ * @param request The original request.
5831
+ * @param location The redirect location.
5832
+ * @param body The request body as ArrayBuffer or null.
5833
+ * @returns A new Request object for the redirect.
5834
+ */
5835
+ function createRedirectRequest(request, location, body) {
5836
+ const url = new URL(location, request.url);
5837
+ return new Request(url, {
5838
+ method: request.method,
5839
+ headers: request.headers,
5840
+ body,
5841
+ redirect: "manual",
5842
+ signal: request.signal,
5843
+ mode: request.mode,
5844
+ credentials: request.credentials,
5845
+ referrer: request.referrer,
5846
+ referrerPolicy: request.referrerPolicy,
5847
+ integrity: request.integrity,
5848
+ keepalive: request.keepalive,
5849
+ cache: request.cache
5850
+ });
5851
+ }
5852
+ async function fetchDoubleKnockRequest(request, signedRequest, signal) {
5853
+ const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
5854
+ for (let attempt = 1;; attempt++) try {
5855
+ return await fetch(signedRequest, {
5856
+ redirect: "manual",
5857
+ signal
5858
+ });
5859
+ } catch (error) {
5860
+ const abortedSignal = getAbortedSignal(signal, request.signal, signedRequest.signal);
5861
+ if (abortedSignal != null) throw getAbortReason(abortedSignal);
5862
+ if (isAbortError(error)) throw error;
5863
+ if (attempt >= maxAttempts) throw createFetchError(request.url, error);
5864
+ await sleep(DOUBLE_KNOCK_TRANSPORT_RETRY_DELAY_MS, signal, request.signal, signedRequest.signal);
5865
+ }
5866
+ }
5867
+ function createFetchError(url, cause) {
5868
+ const error = new _fedify_vocab_runtime.FetchError(url, cause instanceof Error ? cause.message : String(cause));
5869
+ error.cause = cause;
5870
+ return error;
5871
+ }
5872
+ function isAbortError(error) {
5873
+ return error instanceof Error && error.name === "AbortError";
5874
+ }
5875
+ async function sleep(ms, ...signals) {
5876
+ const abortSignals = signals.filter((signal) => signal != null);
5877
+ const abortedSignal = getAbortedSignal(...abortSignals);
5878
+ if (abortedSignal != null) throw getAbortReason(abortedSignal);
5879
+ if (abortSignals.length < 1) {
5880
+ await new Promise((resolve) => setTimeout(resolve, ms));
5881
+ return;
5882
+ }
5883
+ await new Promise((resolve, reject) => {
5884
+ const removeAbortListeners = () => {
5885
+ for (const signal of abortSignals) signal.removeEventListener("abort", handleAbort);
5886
+ };
5887
+ const timeout = setTimeout(() => {
5888
+ removeAbortListeners();
5889
+ resolve();
5890
+ }, ms);
5891
+ function handleAbort(event) {
5892
+ clearTimeout(timeout);
5893
+ removeAbortListeners();
5894
+ reject(getAbortReason(event.currentTarget));
5895
+ }
5896
+ for (const signal of abortSignals) signal.addEventListener("abort", handleAbort, { once: true });
5897
+ });
5898
+ }
5899
+ function getAbortedSignal(...signals) {
5900
+ return signals.find((signal) => signal?.aborted);
5901
+ }
5902
+ function getAbortReason(signal) {
5903
+ return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
5904
+ }
5905
+ /**
5906
+ * Performs a double-knock request to the given URL. For the details of
5907
+ * double-knocking, see
5908
+ * <https://swicg.github.io/activitypub-http-signature/#how-to-upgrade-supported-versions>.
5909
+ * @param request The request to send.
5910
+ * @param identity The identity to use for signing the request.
5911
+ * @param options The options for double-knock requests.
5912
+ * @returns The response to the request.
5913
+ * @since 1.6.0
5914
+ */
5915
+ async function doubleKnock(request, identity, options = {}) {
5916
+ return await doubleKnockInternal(request, identity, options);
5917
+ }
5918
+ async function doubleKnockInternal(request, identity, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
5919
+ const { specDeterminer, log, tracerProvider, signal } = options;
5920
+ const maximumRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
5921
+ visited.add(request.url);
5922
+ const origin = new URL(request.url).origin;
5923
+ const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
5924
+ const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
5925
+ let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
5926
+ spec: firstTrySpec,
5927
+ tracerProvider,
5928
+ body
5929
+ });
5930
+ log?.(signedRequest);
5931
+ let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
5932
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
5933
+ if (redirected >= maximumRedirection) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
5934
+ const redirectRequest = createRedirectRequest(request, response.headers.get("Location"), body);
5935
+ if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
5936
+ return doubleKnockInternal(redirectRequest, identity, {
5937
+ ...options,
5938
+ body
5939
+ }, redirected + 1, visited);
5940
+ } else if (response.status === 400 || response.status === 401 || response.status > 401) {
5941
+ const logger = (0, _logtape_logtape.getLogger)([
5942
+ "fedify",
5943
+ "sig",
5944
+ "http"
5945
+ ]);
5946
+ const acceptSigHeader = response.headers.get("Accept-Signature");
5947
+ if (acceptSigHeader != null) {
5948
+ const entries = validateAcceptSignature(parseAcceptSignature(acceptSigHeader));
5949
+ const localKeyId = identity.keyId.href;
5950
+ const localAlg = "rsa-v1_5-sha256";
5951
+ let fulfilled = false;
5952
+ let challengeRequest;
5953
+ for (const entry of entries) {
5954
+ const rfc9421 = fulfillAcceptSignature(entry, localKeyId, localAlg);
5955
+ if (rfc9421 == null) continue;
5956
+ logger.debug("Received Accept-Signature challenge; accumulating label {label} and components {components}.", {
5957
+ label: rfc9421.label,
5958
+ components: rfc9421.components
5959
+ });
5960
+ try {
5961
+ challengeRequest = await signRequest(challengeRequest ?? request, identity.privateKey, identity.keyId, {
5962
+ spec: "rfc9421",
5963
+ tracerProvider,
5964
+ body,
5965
+ rfc9421
5966
+ });
5967
+ fulfilled = true;
5968
+ } catch (error) {
5969
+ logger.debug("Failed to fulfill Accept-Signature challenge entry {label}: {error}", {
5970
+ label: entry.label,
5971
+ error
5972
+ });
5973
+ }
5974
+ }
5975
+ if (fulfilled && challengeRequest != null) {
5976
+ signedRequest = challengeRequest;
5977
+ log?.(signedRequest);
5978
+ response = await fetch(signedRequest, {
5979
+ redirect: "manual",
5980
+ signal
5981
+ });
5982
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) return doubleKnock(createRedirectRequest(request, response.headers.get("Location"), body), identity, {
5983
+ ...options,
5984
+ body
5985
+ });
5986
+ }
5987
+ if (fulfilled && response.status < 300) {
5988
+ await specDeterminer?.rememberSpec(origin, "rfc9421");
5989
+ return response;
5990
+ }
5991
+ if (fulfilled && response.status !== 400 && response.status !== 401) return response;
5992
+ }
5993
+ const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
5994
+ logger.debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
5995
+ spec: firstTrySpec,
5996
+ secondSpec: spec,
5997
+ status: response.status,
5998
+ statusText: response.statusText
5999
+ });
6000
+ signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
6001
+ spec,
6002
+ tracerProvider,
6003
+ body
6004
+ });
6005
+ log?.(signedRequest);
6006
+ response = await fetchDoubleKnockRequest(request, signedRequest, signal);
6007
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
6008
+ if (redirected >= maximumRedirection) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
6009
+ const redirectRequest = createRedirectRequest(request, response.headers.get("Location"), body);
6010
+ if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
6011
+ return doubleKnockInternal(redirectRequest, identity, {
6012
+ ...options,
6013
+ body
6014
+ }, redirected + 1, visited);
6015
+ } else if (response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
6016
+ } else await specDeterminer?.rememberSpec(origin, firstTrySpec);
6017
+ return response;
6018
+ }
6019
+ /**
6020
+ * Performs a timing-safe equality comparison between two `Uint8Array` values.
6021
+ *
6022
+ * This function is designed to take a constant amount of time to execute,
6023
+ * dependent only on the length of the longer of the two arrays,
6024
+ * regardless of where the first difference in bytes occurs. This helps
6025
+ * prevent timing attacks.
6026
+ *
6027
+ * @param a The first bytes.
6028
+ * @param b The second bytes.
6029
+ * @returns `true` if the arrays are of the same length and contain the same
6030
+ * bytes, `false` otherwise.
6031
+ * @since 1.6.0
6032
+ */
6033
+ function timingSafeEqual(a, b) {
6034
+ const lenA = a.length;
6035
+ const lenB = b.length;
6036
+ const commonLength = Math.max(lenA, lenB);
6037
+ let result = 0;
6038
+ for (let i = 0; i < commonLength; i++) {
6039
+ const byteA = i < lenA ? a[i] : 0;
6040
+ const byteB = i < lenB ? b[i] : 0;
6041
+ result |= byteA ^ byteB;
6042
+ }
6043
+ result |= lenA ^ lenB;
6044
+ return result === 0;
6045
+ }
6046
+ //#endregion
3560
6047
  Object.defineProperty(exports, "Temporal", {
3561
6048
  enumerable: true,
3562
6049
  get: function() {
3563
- return Temporal;
6050
+ return Temporal$1;
6051
+ }
6052
+ });
6053
+ Object.defineProperty(exports, "doubleKnock", {
6054
+ enumerable: true,
6055
+ get: function() {
6056
+ return doubleKnock;
6057
+ }
6058
+ });
6059
+ Object.defineProperty(exports, "exportJwk", {
6060
+ enumerable: true,
6061
+ get: function() {
6062
+ return exportJwk;
6063
+ }
6064
+ });
6065
+ Object.defineProperty(exports, "fetchKey", {
6066
+ enumerable: true,
6067
+ get: function() {
6068
+ return fetchKey;
6069
+ }
6070
+ });
6071
+ Object.defineProperty(exports, "fetchKeyDetailed", {
6072
+ enumerable: true,
6073
+ get: function() {
6074
+ return fetchKeyDetailed;
6075
+ }
6076
+ });
6077
+ Object.defineProperty(exports, "formatAcceptSignature", {
6078
+ enumerable: true,
6079
+ get: function() {
6080
+ return formatAcceptSignature;
6081
+ }
6082
+ });
6083
+ Object.defineProperty(exports, "fulfillAcceptSignature", {
6084
+ enumerable: true,
6085
+ get: function() {
6086
+ return fulfillAcceptSignature;
6087
+ }
6088
+ });
6089
+ Object.defineProperty(exports, "generateCryptoKeyPair", {
6090
+ enumerable: true,
6091
+ get: function() {
6092
+ return generateCryptoKeyPair;
6093
+ }
6094
+ });
6095
+ Object.defineProperty(exports, "getDurationMs", {
6096
+ enumerable: true,
6097
+ get: function() {
6098
+ return getDurationMs;
6099
+ }
6100
+ });
6101
+ Object.defineProperty(exports, "getFederationMetrics", {
6102
+ enumerable: true,
6103
+ get: function() {
6104
+ return getFederationMetrics;
6105
+ }
6106
+ });
6107
+ Object.defineProperty(exports, "getRemoteHost", {
6108
+ enumerable: true,
6109
+ get: function() {
6110
+ return getRemoteHost;
6111
+ }
6112
+ });
6113
+ Object.defineProperty(exports, "importJwk", {
6114
+ enumerable: true,
6115
+ get: function() {
6116
+ return importJwk;
6117
+ }
6118
+ });
6119
+ Object.defineProperty(exports, "instrumentDocumentLoader", {
6120
+ enumerable: true,
6121
+ get: function() {
6122
+ return instrumentDocumentLoader;
6123
+ }
6124
+ });
6125
+ Object.defineProperty(exports, "isAbortError", {
6126
+ enumerable: true,
6127
+ get: function() {
6128
+ return isAbortError$1;
6129
+ }
6130
+ });
6131
+ Object.defineProperty(exports, "measureSignatureKeyFetch", {
6132
+ enumerable: true,
6133
+ get: function() {
6134
+ return measureSignatureKeyFetch;
6135
+ }
6136
+ });
6137
+ Object.defineProperty(exports, "name", {
6138
+ enumerable: true,
6139
+ get: function() {
6140
+ return name;
6141
+ }
6142
+ });
6143
+ Object.defineProperty(exports, "parseAcceptSignature", {
6144
+ enumerable: true,
6145
+ get: function() {
6146
+ return parseAcceptSignature;
6147
+ }
6148
+ });
6149
+ Object.defineProperty(exports, "parseRfc9421SignatureInput", {
6150
+ enumerable: true,
6151
+ get: function() {
6152
+ return parseRfc9421SignatureInput;
6153
+ }
6154
+ });
6155
+ Object.defineProperty(exports, "recordCircuitBreakerStateChange", {
6156
+ enumerable: true,
6157
+ get: function() {
6158
+ return recordCircuitBreakerStateChange;
6159
+ }
6160
+ });
6161
+ Object.defineProperty(exports, "recordCollectionDispatchDuration", {
6162
+ enumerable: true,
6163
+ get: function() {
6164
+ return recordCollectionDispatchDuration;
6165
+ }
6166
+ });
6167
+ Object.defineProperty(exports, "recordCollectionPageItems", {
6168
+ enumerable: true,
6169
+ get: function() {
6170
+ return recordCollectionPageItems;
6171
+ }
6172
+ });
6173
+ Object.defineProperty(exports, "recordCollectionRequest", {
6174
+ enumerable: true,
6175
+ get: function() {
6176
+ return recordCollectionRequest;
6177
+ }
6178
+ });
6179
+ Object.defineProperty(exports, "recordCollectionTotalItems", {
6180
+ enumerable: true,
6181
+ get: function() {
6182
+ return recordCollectionTotalItems;
6183
+ }
6184
+ });
6185
+ Object.defineProperty(exports, "recordDocumentCache", {
6186
+ enumerable: true,
6187
+ get: function() {
6188
+ return recordDocumentCache;
6189
+ }
6190
+ });
6191
+ Object.defineProperty(exports, "recordFanoutRecipients", {
6192
+ enumerable: true,
6193
+ get: function() {
6194
+ return recordFanoutRecipients;
6195
+ }
6196
+ });
6197
+ Object.defineProperty(exports, "recordInboxActivity", {
6198
+ enumerable: true,
6199
+ get: function() {
6200
+ return recordInboxActivity;
6201
+ }
6202
+ });
6203
+ Object.defineProperty(exports, "recordOutboxActivity", {
6204
+ enumerable: true,
6205
+ get: function() {
6206
+ return recordOutboxActivity;
6207
+ }
6208
+ });
6209
+ Object.defineProperty(exports, "recordOutboxEnqueue", {
6210
+ enumerable: true,
6211
+ get: function() {
6212
+ return recordOutboxEnqueue;
6213
+ }
6214
+ });
6215
+ Object.defineProperty(exports, "recordWebFingerHandle", {
6216
+ enumerable: true,
6217
+ get: function() {
6218
+ return recordWebFingerHandle;
6219
+ }
6220
+ });
6221
+ Object.defineProperty(exports, "registerQueueDepthGauge", {
6222
+ enumerable: true,
6223
+ get: function() {
6224
+ return registerQueueDepthGauge;
6225
+ }
6226
+ });
6227
+ Object.defineProperty(exports, "signRequest", {
6228
+ enumerable: true,
6229
+ get: function() {
6230
+ return signRequest;
6231
+ }
6232
+ });
6233
+ Object.defineProperty(exports, "validateAcceptSignature", {
6234
+ enumerable: true,
6235
+ get: function() {
6236
+ return validateAcceptSignature;
6237
+ }
6238
+ });
6239
+ Object.defineProperty(exports, "validateCryptoKey", {
6240
+ enumerable: true,
6241
+ get: function() {
6242
+ return validateCryptoKey;
6243
+ }
6244
+ });
6245
+ Object.defineProperty(exports, "verifyRequest", {
6246
+ enumerable: true,
6247
+ get: function() {
6248
+ return verifyRequest;
6249
+ }
6250
+ });
6251
+ Object.defineProperty(exports, "verifyRequestDetailed", {
6252
+ enumerable: true,
6253
+ get: function() {
6254
+ return verifyRequestDetailed;
6255
+ }
6256
+ });
6257
+ Object.defineProperty(exports, "version", {
6258
+ enumerable: true,
6259
+ get: function() {
6260
+ return version;
3564
6261
  }
3565
6262
  });