@fedify/fedify 2.4.0-dev.1504 → 2.4.0-dev.1528

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 (109) hide show
  1. package/README.md +10 -7
  2. package/dist/{assert-DvVHoIZ0.mjs → assert-OguE97r2.mjs} +1 -1
  3. package/dist/{assert_instance_of-CxoC8bVq.mjs → assert_instance_of-DBC5X09g.mjs} +1 -1
  4. package/dist/{assert_not_equals-C8vcTTA3.mjs → assert_not_equals-DkVK8oqV.mjs} +1 -1
  5. package/dist/{assert_rejects-DAdaaIWM.mjs → assert_rejects-DN60FHPX.mjs} +2 -2
  6. package/dist/{assert_strict_equals-BzqyKuYL.mjs → assert_strict_equals-XEgZAlrj.mjs} +1 -1
  7. package/dist/{assert_throws-zN79lgIC.mjs → assert_throws-BOkhLGYc.mjs} +1 -1
  8. package/dist/{builder-Ck0Fl_a5.mjs → builder-B6myMguW.mjs} +3 -23
  9. package/dist/compat/mod.d.cts +1 -1
  10. package/dist/compat/mod.d.ts +1 -1
  11. package/dist/compat/outgoing-jsonld.test.mjs +3 -3
  12. package/dist/compat/public-audience.test.mjs +3 -3
  13. package/dist/compat/transformers.test.mjs +5 -5
  14. package/dist/{context-CYF8X0ft.d.cts → context-BBVLF7lx.d.cts} +272 -543
  15. package/dist/{context-BF5B4ZDk.d.ts → context-BU6jSQdo.d.ts} +272 -542
  16. package/dist/{context-BBRTgkrs.mjs → context-DVoTs_wM.mjs} +2 -8
  17. package/dist/{deno-D3gb4X9v.mjs → deno-CVNsRJe9.mjs} +1 -1
  18. package/dist/{docloader-DUKu0GzG.mjs → docloader-CB8AGpw3.mjs} +3 -3
  19. package/dist/federation/builder.test.mjs +5 -5
  20. package/dist/federation/circuit-breaker.test.mjs +5 -5
  21. package/dist/federation/collection.test.mjs +3 -3
  22. package/dist/federation/handler.test.mjs +13 -13
  23. package/dist/federation/idempotency.test.mjs +6 -6
  24. package/dist/federation/inbox.test.mjs +3 -3
  25. package/dist/federation/keycache.test.mjs +5 -5
  26. package/dist/federation/kv.test.mjs +3 -3
  27. package/dist/federation/metrics.test.mjs +4 -4
  28. package/dist/federation/middleware.test.mjs +19 -200
  29. package/dist/federation/mod.cjs +259 -3
  30. package/dist/federation/mod.d.cts +4 -4
  31. package/dist/federation/mod.d.ts +4 -4
  32. package/dist/federation/mod.js +257 -1
  33. package/dist/federation/mq.test.mjs +261 -103
  34. package/dist/federation/negotiation.test.mjs +4 -4
  35. package/dist/federation/retry.test.mjs +3 -3
  36. package/dist/federation/router.test.mjs +5 -5
  37. package/dist/federation/send.test.mjs +10 -10
  38. package/dist/federation/temporal.test.mjs +4 -4
  39. package/dist/federation/webfinger.test.mjs +6 -6
  40. package/dist/{http-CrZzUAtV.js → http-B0-9u_1F.js} +17 -7
  41. package/dist/{http-D9p2_d2X.cjs → http-EkV7OslM.cjs} +17 -7
  42. package/dist/{http-BNFQkiEZ.mjs → http-L7wqkbZR.mjs} +4 -4
  43. package/dist/{key-BM_SRwBV.mjs → key-Ct8zNgGk.mjs} +18 -8
  44. package/dist/{kv-cache-BE1QIPdL.mjs → kv-cache-CUT9OGpX.mjs} +1 -1
  45. package/dist/{kv-cache-Cna211RR.cjs → kv-cache-CbB50co6.cjs} +1 -1
  46. package/dist/{kv-cache-Ch_Zv5Yq.js → kv-cache-Dh8ZoQfC.js} +1 -1
  47. package/dist/{ld-ubkf03eb.mjs → ld-D6I7H18J.mjs} +3 -3
  48. package/dist/{metrics-BXR36qzu.mjs → metrics-DvClPYaE.mjs} +1 -1
  49. package/dist/{middleware-DUNeIkXN.cjs → middleware-B0ujKP-0.cjs} +51 -694
  50. package/dist/{middleware-pzCP79He.mjs → middleware-CGqUj4XY.mjs} +1 -1
  51. package/dist/{middleware-lB4vtShP.js → middleware-DX9Fd37u.js} +52 -683
  52. package/dist/{middleware-iP9VRMus.mjs → middleware-RxFBNV2C.mjs} +67 -310
  53. package/dist/{mod-BHEB3xhE.d.cts → mod-C0F6kvgS.d.cts} +1 -1
  54. package/dist/{mod-4WGAuR6X.d.ts → mod-vPYVoa5n.d.ts} +1 -1
  55. package/dist/mod.cjs +6 -6
  56. package/dist/mod.d.cts +5 -5
  57. package/dist/mod.d.ts +5 -5
  58. package/dist/mod.js +5 -5
  59. package/dist/{mq-DXMt_qCY.d.cts → mq-D-nlpY04.d.ts} +0 -34
  60. package/dist/{mq-B5_hohs7.d.ts → mq-D8uSFzxe.d.cts} +0 -34
  61. package/dist/nodeinfo/client.test.mjs +3 -3
  62. package/dist/nodeinfo/handler.test.mjs +5 -5
  63. package/dist/nodeinfo/types.test.mjs +4 -4
  64. package/dist/otel/exporter.test.mjs +3 -3
  65. package/dist/{outgoing-jsonld-BNL8AC14.mjs → outgoing-jsonld-L_DbOaFe.mjs} +1 -1
  66. package/dist/{owner-Bf1KkncT.mjs → owner-BqPUA8Z6.mjs} +2 -2
  67. package/dist/{proof-Q781Ks5q.mjs → proof-BJKoRFBc.mjs} +5 -5
  68. package/dist/{proof-BMcsi8q_.js → proof-DCCmmEuJ.js} +1 -1
  69. package/dist/{proof-yeAU1MyT.cjs → proof-eZme7HYR.cjs} +1 -1
  70. package/dist/{send-DFV0S_n9.mjs → send-DVtEjoJ6.mjs} +3 -3
  71. package/dist/sig/accept.test.mjs +1 -1
  72. package/dist/sig/http.test.mjs +8 -8
  73. package/dist/sig/key.test.mjs +56 -6
  74. package/dist/sig/ld.test.mjs +7 -7
  75. package/dist/sig/mod.cjs +2 -2
  76. package/dist/sig/mod.js +2 -2
  77. package/dist/sig/owner.test.mjs +6 -6
  78. package/dist/sig/proof.test.mjs +8 -8
  79. package/dist/{std__assert-Dh6uLf_q.mjs → std__assert-BBjXFNOb.mjs} +4 -4
  80. package/dist/{temporal-CyOjsqPj.mjs → temporal-B06FcmVK.mjs} +1 -1
  81. package/dist/testing/mod.d.mts +8 -1037
  82. package/dist/testing/mod.mjs +2 -3
  83. package/dist/utils/docloader.test.mjs +6 -6
  84. package/dist/utils/kv-cache.test.mjs +1 -1
  85. package/dist/utils/mod.cjs +1 -1
  86. package/dist/utils/mod.js +1 -1
  87. package/package.json +7 -12
  88. package/dist/codec-CmlPqjNX.mjs +0 -103
  89. package/dist/federation/tasks/codec.test.d.mts +0 -2
  90. package/dist/federation/tasks/codec.test.mjs +0 -292
  91. package/dist/federation/tasks/enqueue.test.d.mts +0 -2
  92. package/dist/federation/tasks/enqueue.test.mjs +0 -992
  93. package/dist/federation/tasks/tasks.test.d.mts +0 -2
  94. package/dist/federation/tasks/tasks.test.mjs +0 -555
  95. package/dist/mq-03vS-C9P.mjs +0 -276
  96. package/dist/testing-Dy-WhqpN.mjs +0 -77
  97. /package/dist/{accept-CgDcxvjV.mjs → accept-CPkZzmGN.mjs} +0 -0
  98. /package/dist/{activity-listener-BeTGV3wc.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  99. /package/dist/{assert_equals-PBVKNMJd.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
  100. /package/dist/{circuit-breaker-hJBB6jwA.mjs → circuit-breaker-CSWsyoef.mjs} +0 -0
  101. /package/dist/{client-B_A6mfn3.mjs → client-ByXmQhYD.mjs} +0 -0
  102. /package/dist/{collection-Dp5ky45w.mjs → collection-Cc3DVAhE.mjs} +0 -0
  103. /package/dist/{keycache-CaOR6NYg.mjs → keycache-BeU0LCII.mjs} +0 -0
  104. /package/dist/{keys-CSYsOMFG.mjs → keys-DGu1NFwu.mjs} +0 -0
  105. /package/dist/{kv-CIvR3Non.mjs → kv-rV3vodCc.mjs} +0 -0
  106. /package/dist/{negotiation-m_L2nyS3.mjs → negotiation-DDstyBvc.mjs} +0 -0
  107. /package/dist/{public-audience-c9zmYKgA.mjs → public-audience-Cvbr2Gzt.mjs} +0 -0
  108. /package/dist/{retry-DI4O-zYl.mjs → retry-CXg_MBI-.mjs} +0 -0
  109. /package/dist/{types-BFowWFTT.mjs → types-J53Kw7so.mjs} +0 -0
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-D9p2_d2X.cjs");
6
- const require_proof = require("./proof-yeAU1MyT.cjs");
5
+ const require_http = require("./http-EkV7OslM.cjs");
6
+ const require_proof = require("./proof-eZme7HYR.cjs");
7
7
  const require_types = require("./types-KC4QAoxe.cjs");
8
- const require_kv_cache = require("./kv-cache-Cna211RR.cjs");
8
+ const require_kv_cache = require("./kv-cache-CbB50co6.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_uri_template = require("@fedify/uri-template");
11
11
  let _fedify_vocab = require("@fedify/vocab");
@@ -18,7 +18,6 @@ let _fedify_vocab_runtime_jsonld = require("@fedify/vocab-runtime/jsonld");
18
18
  _fedify_vocab_runtime_jsonld = require_chunk.__toESM(_fedify_vocab_runtime_jsonld, 1);
19
19
  let _fedify_webfinger = require("@fedify/webfinger");
20
20
  let _opentelemetry_sdk_metrics = require("@opentelemetry/sdk-metrics");
21
- let devalue = require("devalue");
22
21
  let node_url = require("node:url");
23
22
  //#region src/federation/activity-listener.ts
24
23
  var ActivityListenerSet = class {
@@ -109,7 +108,6 @@ var FederationBuilderImpl = class {
109
108
  idempotencyStrategy;
110
109
  collectionTypeIds;
111
110
  collectionCallbacks;
112
- taskDefinitions;
113
111
  /**
114
112
  * Symbol registry for unique identification of unnamed symbols.
115
113
  */
@@ -120,7 +118,6 @@ var FederationBuilderImpl = class {
120
118
  this.objectTypeIds = {};
121
119
  this.collectionCallbacks = {};
122
120
  this.collectionTypeIds = {};
123
- this.taskDefinitions = /* @__PURE__ */ new Map();
124
121
  }
125
122
  /**
126
123
  * Builds the federation object.
@@ -159,7 +156,6 @@ var FederationBuilderImpl = class {
159
156
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
160
157
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
161
158
  f.idempotencyStrategy = this.idempotencyStrategy;
162
- f.taskDefinitions = new Map(this.taskDefinitions);
163
159
  return f;
164
160
  }
165
161
  _getTracer() {
@@ -292,23 +288,6 @@ var FederationBuilderImpl = class {
292
288
  setWebFingerLinksDispatcher(dispatcher) {
293
289
  this.webFingerLinksDispatcher = dispatcher;
294
290
  }
295
- defineTask(name, options) {
296
- if (this.taskDefinitions.has(name)) throw new TypeError(`Task ${JSON.stringify(name)} is already defined.`);
297
- const handle = {
298
- name,
299
- schema: options.schema
300
- };
301
- this.taskDefinitions.set(name, {
302
- name,
303
- schema: options.schema,
304
- handle,
305
- handler: options.handler,
306
- onError: options.onError,
307
- retryPolicy: options.retryPolicy,
308
- queue: options.queue
309
- });
310
- return handle;
311
- }
312
291
  /**
313
292
  * The RFC 6570 template-literal `path` overloads were removed for
314
293
  * type-checking efficiency, so the URI variable types can no longer be
@@ -1414,7 +1393,7 @@ function acceptsJsonLd(request) {
1414
1393
  }
1415
1394
  //#endregion
1416
1395
  //#region src/federation/temporal.ts
1417
- function isPlainObject$1(value) {
1396
+ function isPlainObject(value) {
1418
1397
  return typeof value === "object" && value != null && !Array.isArray(value);
1419
1398
  }
1420
1399
  function normalizeDateTimeLiteral(value) {
@@ -1452,11 +1431,11 @@ const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
1452
1431
  const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
1453
1432
  function hasMalformedExpandedDateTimeLiteral(value) {
1454
1433
  if (Array.isArray(value)) return value.some(hasMalformedExpandedDateTimeLiteral);
1455
- return isPlainObject$1(value) && "@value" in value && isMalformedDateTimeLiteral(value["@value"]);
1434
+ return isPlainObject(value) && "@value" in value && isMalformedDateTimeLiteral(value["@value"]);
1456
1435
  }
1457
1436
  function hasMalformedExpandedQuestionClosedLiteral(value) {
1458
1437
  if (Array.isArray(value)) return value.some(hasMalformedExpandedQuestionClosedLiteral);
1459
- if (!isPlainObject$1(value) || !("@value" in value)) return false;
1438
+ if (!isPlainObject(value) || !("@value" in value)) return false;
1460
1439
  const literal = value["@value"];
1461
1440
  if (typeof literal === "boolean") return false;
1462
1441
  if (typeof literal !== "string") return false;
@@ -1466,11 +1445,11 @@ function hasMalformedExpandedQuestionClosedLiteral(value) {
1466
1445
  }
1467
1446
  function hasMalformedExpandedDurationLiteral(value) {
1468
1447
  if (Array.isArray(value)) return value.some(hasMalformedExpandedDurationLiteral);
1469
- return isPlainObject$1(value) && "@value" in value && isMalformedDurationLiteral(value["@value"]);
1448
+ return isPlainObject(value) && "@value" in value && isMalformedDurationLiteral(value["@value"]);
1470
1449
  }
1471
1450
  function hasMalformedKnownTemporalLiteralInternal(value, visited) {
1472
1451
  if (Array.isArray(value)) return value.some((item) => hasMalformedKnownTemporalLiteralInternal(item, visited));
1473
- if (!isPlainObject$1(value)) return false;
1452
+ if (!isPlainObject(value)) return false;
1474
1453
  if (visited.has(value)) return false;
1475
1454
  visited.add(value);
1476
1455
  if ("@value" in value) return false;
@@ -3574,529 +3553,6 @@ function createExponentialBackoffPolicy(options = {}) {
3574
3553
  };
3575
3554
  }
3576
3555
  //#endregion
3577
- //#region src/federation/tasks/codec.ts
3578
- /**
3579
- * Serializes and deserializes task payloads for the queue, preserving
3580
- * `@fedify/vocab` objects across the wire by reducing them to JSON-LD and
3581
- * rebuilding them on the worker with the bound {@link TaskCodecLoaders}.
3582
- * @internal
3583
- */
3584
- var TaskCodec = class TaskCodec {
3585
- options;
3586
- constructor(options) {
3587
- this.options = options;
3588
- }
3589
- /** Serializes `data`, encoding any vocabulary object as its JSON-LD. */
3590
- serialize = (data) => (0, devalue.stringifyAsync)(data, { Vocab: this.#stringifyVocab });
3591
- /** Deserializes `raw`, rebuilding any encoded vocabulary object. */
3592
- deserialize = (raw) => this.#revive(/* @__PURE__ */ new Map())((0, devalue.parse)(raw, { Vocab: VocabHolder.from }));
3593
- /** Validates `data` against `schema`, then serializes it. */
3594
- encode = async (schema, data) => this.serialize(await TaskCodec.validate(schema, data));
3595
- /** Deserializes `raw`, then validates the result against `schema`. */
3596
- decode = async (schema, raw) => TaskCodec.validate(schema, await this.deserialize(raw));
3597
- /** Validates `data` against `schema`, returning its parsed output. */
3598
- static validate = async (schema, data) => getValueIfSchema(await schema["~standard"].validate(data));
3599
- #stringifyVocab = (value) => isVocab(value) && this.#toWire(value);
3600
- #toWire = async (value) => ({
3601
- kind: value instanceof _fedify_vocab.Link ? "link" : "object",
3602
- jsonLd: await value.toJsonLd({
3603
- format: "expand",
3604
- ...this.options
3605
- })
3606
- });
3607
- #revive = (seen) => {
3608
- const inner = async (node) => {
3609
- if (node === null || typeof node !== "object") return node;
3610
- if (seen.has(node)) return seen.get(node);
3611
- for (const reviver of this.#classRevivers) {
3612
- const out = reviver(seen, inner, node);
3613
- if (out !== void 0) return await out;
3614
- }
3615
- return node;
3616
- };
3617
- return inner;
3618
- };
3619
- #classRevivers = [
3620
- classReviver(isInstanceOf(VocabHolder), ({ kind, jsonLd }) => kind === "link" ? _fedify_vocab.Link.fromJsonLd(jsonLd, this.options) : _fedify_vocab.Object.fromJsonLd(jsonLd, this.options), () => {}),
3621
- classReviver(isInstanceOf(Array), () => [], async (revive, node, arr) => {
3622
- for (const item of await Array.fromAsync(node, revive)) arr.push(item);
3623
- }),
3624
- classReviver(isInstanceOf(Map), () => /* @__PURE__ */ new Map(), async (revive, node, map) => {
3625
- for (const [k, v] of node) map.set(await revive(k), await revive(v));
3626
- }),
3627
- classReviver(isInstanceOf(Set), () => /* @__PURE__ */ new Set(), async (revive, node, set) => {
3628
- for (const v of await Array.fromAsync(node, revive)) set.add(v);
3629
- }),
3630
- classReviver(isPlainObject, () => ({}), async (revive, node, obj) => {
3631
- for (const [k, v] of globalThis.Object.entries(node)) obj[k] = await revive(v);
3632
- })
3633
- ];
3634
- };
3635
- const isVocab = (value) => value instanceof _fedify_vocab.Object || value instanceof _fedify_vocab.Link;
3636
- const isPlainObject = (value) => value === null || typeof value !== "object" ? false : isObjectPrototype(globalThis.Object.getPrototypeOf(value));
3637
- const isObjectPrototype = (proto) => proto === null || proto === globalThis.Object.prototype;
3638
- const isInstanceOf = (cls) => (v) => v instanceof cls;
3639
- function getValueIfSchema(result) {
3640
- assertSchema(result);
3641
- return result.value;
3642
- }
3643
- function assertSchema(result) {
3644
- if (result.issues && result.issues.length > 0) throw new TypeError(`Task data failed schema validation: ${JSON.stringify(result.issues)}`);
3645
- }
3646
- /**
3647
- * A vocabulary object parked by the synchronous decode reviver, held until
3648
- * the async revive pass can `fromJsonLd()` it back into an instance.
3649
- */
3650
- var VocabHolder = class VocabHolder {
3651
- kind;
3652
- jsonLd;
3653
- constructor(kind, jsonLd) {
3654
- this.kind = kind;
3655
- this.jsonLd = jsonLd;
3656
- }
3657
- static from = ({ kind, jsonLd }) => new VocabHolder(kind, jsonLd);
3658
- };
3659
- /**
3660
- * Ties a container filter to its empty-shell `init` and child-filling `set`
3661
- * through one type parameter—a correlation the heterogeneous reviver list
3662
- * cannot carry, which previously forced `@ts-ignore` at the dispatch site.
3663
- */
3664
- const classReviver = (filter, init, set) => (seen, revive, node) => {
3665
- if (!filter(node)) return void 0;
3666
- return (async () => {
3667
- const out = await init(node);
3668
- seen.set(node, out);
3669
- await set(revive, node, out);
3670
- return out;
3671
- })();
3672
- };
3673
- //#endregion
3674
- //#region src/federation/mq.ts
3675
- /**
3676
- * A message queue that processes messages in the same process.
3677
- * Do not use this in production as it does neither persist messages nor
3678
- * distribute them across multiple processes.
3679
- *
3680
- * @since 0.5.0
3681
- */
3682
- var InProcessMessageQueue = class {
3683
- #messages;
3684
- #monitors;
3685
- #pollIntervalMs;
3686
- #delayedMessages;
3687
- /**
3688
- * Tracks which ordering keys are currently being processed to ensure
3689
- * sequential processing for messages with the same key.
3690
- */
3691
- #processingKeys;
3692
- /**
3693
- * In-process message queue does not provide native retry mechanisms.
3694
- * @since 1.7.0
3695
- */
3696
- nativeRetrial = false;
3697
- /**
3698
- * In-process message queue does not deduplicate messages natively.
3699
- * @since 2.4.0
3700
- */
3701
- nativeDeduplication = false;
3702
- /**
3703
- * Constructs a new {@link InProcessMessageQueue} with the given options.
3704
- * @param options Additional options for the in-process message queue.
3705
- */
3706
- constructor(options = {}) {
3707
- this.#messages = [];
3708
- this.#monitors = {};
3709
- this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
3710
- this.#delayedMessages = 0;
3711
- this.#processingKeys = /* @__PURE__ */ new Set();
3712
- }
3713
- enqueue(message, options) {
3714
- const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
3715
- if (delay > 0) {
3716
- this.#delayedMessages++;
3717
- setTimeout(() => {
3718
- this.#delayedMessages--;
3719
- this.#enqueueReady(message, options);
3720
- }, delay);
3721
- return Promise.resolve();
3722
- }
3723
- this.#enqueueReady(message, options);
3724
- return Promise.resolve();
3725
- }
3726
- #enqueueReady(message, options) {
3727
- const orderingKey = options?.orderingKey ?? null;
3728
- this.#messages.push({
3729
- message,
3730
- orderingKey
3731
- });
3732
- this.#notifyMonitors();
3733
- }
3734
- #notifyMonitors() {
3735
- for (const monitorId in this.#monitors) this.#monitors[monitorId]();
3736
- }
3737
- enqueueMany(messages, options) {
3738
- if (messages.length === 0) return Promise.resolve();
3739
- const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
3740
- if (delay > 0) {
3741
- const delayedCount = messages.length;
3742
- const deferredMessages = [...messages];
3743
- this.#delayedMessages += delayedCount;
3744
- setTimeout(() => {
3745
- this.#delayedMessages -= delayedCount;
3746
- this.#enqueueManyReady(deferredMessages, options);
3747
- }, delay);
3748
- return Promise.resolve();
3749
- }
3750
- this.#enqueueManyReady(messages, options);
3751
- return Promise.resolve();
3752
- }
3753
- #enqueueManyReady(messages, options) {
3754
- const orderingKey = options?.orderingKey ?? null;
3755
- for (const message of messages) this.#messages.push({
3756
- message,
3757
- orderingKey
3758
- });
3759
- this.#notifyMonitors();
3760
- }
3761
- async listen(handler, options = {}) {
3762
- const signal = options.signal;
3763
- while (signal == null || !signal.aborted) {
3764
- const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
3765
- if (idx >= 0) {
3766
- const { message, orderingKey } = this.#messages.splice(idx, 1)[0];
3767
- if (orderingKey != null) this.#processingKeys.add(orderingKey);
3768
- try {
3769
- await handler(message);
3770
- } finally {
3771
- if (orderingKey != null) this.#processingKeys.delete(orderingKey);
3772
- }
3773
- } else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
3774
- else await this.#wait(10, signal);
3775
- }
3776
- }
3777
- getDepth() {
3778
- const ready = this.#messages.length;
3779
- const delayed = this.#delayedMessages;
3780
- return Promise.resolve({
3781
- queued: ready + delayed,
3782
- ready,
3783
- delayed
3784
- });
3785
- }
3786
- #wait(ms, signal) {
3787
- let timer = null;
3788
- return Promise.any([new Promise((resolve) => {
3789
- signal?.addEventListener("abort", () => {
3790
- if (timer != null) clearTimeout(timer);
3791
- resolve();
3792
- }, { once: true });
3793
- const monitorId = crypto.randomUUID();
3794
- this.#monitors[monitorId] = () => {
3795
- delete this.#monitors[monitorId];
3796
- if (timer != null) clearTimeout(timer);
3797
- resolve();
3798
- };
3799
- }), new Promise((resolve) => timer = setTimeout(resolve, ms))]);
3800
- }
3801
- };
3802
- /**
3803
- * A message queue that processes messages in parallel. It takes another
3804
- * {@link MessageQueue}, and processes messages in parallel up to a certain
3805
- * number of workers.
3806
- *
3807
- * Actually, it's rather a decorator than a queue itself.
3808
- *
3809
- * Note that the workers do not run in truly parallel, in the sense that they
3810
- * are not running in separate threads or processes. They are running in the
3811
- * same process, but are scheduled to run in parallel. Hence, this is useful
3812
- * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
3813
- * workloads.
3814
- *
3815
- * When using `ParallelMessageQueue`, the ordering guarantee is preserved
3816
- * *only if* the underlying queue implementation delivers messages in a wrapper
3817
- * format that includes the `__fedify_ordering_key__` property. Currently,
3818
- * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
3819
- * For other queue implementations (e.g., `InProcessMessageQueue`,
3820
- * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
3821
- * `AmqpMessageQueue`), the ordering key cannot be detected by
3822
- * `ParallelMessageQueue`, so ordering guarantees are handled by those
3823
- * implementations directly rather than at the `ParallelMessageQueue` level.
3824
- *
3825
- * Messages with the same ordering key will never be processed concurrently
3826
- * by different workers, ensuring sequential processing within each key.
3827
- * Messages with different ordering keys (or no ordering key) can still be
3828
- * processed in parallel.
3829
- *
3830
- * @since 1.0.0
3831
- */
3832
- var ParallelMessageQueue = class ParallelMessageQueue {
3833
- queue;
3834
- workers;
3835
- /**
3836
- * Inherits the native retry capability from the wrapped queue.
3837
- * @since 1.7.0
3838
- */
3839
- nativeRetrial;
3840
- /**
3841
- * Inherits the native deduplication capability from the wrapped queue.
3842
- * @since 2.4.0
3843
- */
3844
- nativeDeduplication;
3845
- getDepth;
3846
- /**
3847
- * Tracks which ordering keys are currently being processed to ensure
3848
- * sequential processing for messages with the same key.
3849
- */
3850
- #processingKeys = /* @__PURE__ */ new Set();
3851
- /**
3852
- * Pending messages waiting for their ordering key to become available.
3853
- */
3854
- #pendingMessages = [];
3855
- /**
3856
- * Constructs a new {@link ParallelMessageQueue} with the given queue and
3857
- * number of workers.
3858
- * @param queue The message queue to use under the hood. Note that
3859
- * {@link ParallelMessageQueue} cannot be nested.
3860
- * @param workers The number of workers to process messages in parallel.
3861
- * @throws {TypeError} If the given queue is an instance of
3862
- * {@link ParallelMessageQueue}.
3863
- */
3864
- constructor(queue, workers) {
3865
- if (queue instanceof ParallelMessageQueue) throw new TypeError("Cannot nest ParallelMessageQueue.");
3866
- this.queue = queue;
3867
- this.workers = workers;
3868
- this.nativeRetrial = queue.nativeRetrial;
3869
- this.nativeDeduplication = queue.nativeDeduplication;
3870
- if (queue.getDepth != null) this.getDepth = () => queue.getDepth();
3871
- }
3872
- enqueue(message, options) {
3873
- return this.queue.enqueue(message, options);
3874
- }
3875
- async enqueueMany(messages, options) {
3876
- if (this.queue.enqueueMany == null) {
3877
- if (messages.length === 1) {
3878
- await this.queue.enqueue(messages[0], options);
3879
- return;
3880
- }
3881
- if (options?.deduplicationKey != null) throw new TypeError("Cannot enqueue a batch with a deduplicationKey: the wrapped queue does not implement enqueueMany, so ParallelMessageQueue would have to fan out to individual enqueue() calls that cannot share one deduplicationKey atomically. Wrap a queue that implements enqueueMany instead.");
3882
- const errors = (await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)))).filter((r) => r.status === "rejected").map((r) => r.reason);
3883
- if (errors.length > 1) throw new AggregateError(errors, "Failed to enqueue messages.");
3884
- else if (errors.length === 1) throw errors[0];
3885
- return;
3886
- }
3887
- await this.queue.enqueueMany(messages, options);
3888
- }
3889
- /**
3890
- * Extracts ordering key from a message if present.
3891
- *
3892
- * This method only works for queue implementations that deliver messages
3893
- * in the wrapper format with `__fedify_ordering_key__` property. Currently,
3894
- * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
3895
- *
3896
- * For other queue implementations (`InProcessMessageQueue`,
3897
- * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
3898
- * `AmqpMessageQueue`), messages are delivered as raw payloads without the
3899
- * wrapper, so the ordering key cannot be detected here. Those
3900
- * implementations handle ordering guarantees internally.
3901
- */
3902
- #extractOrderingKey(message) {
3903
- if (message != null && typeof message === "object") {
3904
- if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
3905
- }
3906
- }
3907
- listen(handler, options = {}) {
3908
- const workers = /* @__PURE__ */ new Map();
3909
- return this.queue.listen(async (message) => {
3910
- while (workers.size >= this.workers) {
3911
- const consumedId = await Promise.any(workers.values());
3912
- workers.delete(consumedId);
3913
- }
3914
- const workerId = crypto.randomUUID();
3915
- const orderingKey = this.#extractOrderingKey(message);
3916
- if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
3917
- this.#pendingMessages.push({
3918
- message,
3919
- orderingKey,
3920
- resolve
3921
- });
3922
- });
3923
- if (orderingKey != null) this.#processingKeys.add(orderingKey);
3924
- const promise = this.#work(workerId, handler, message, orderingKey);
3925
- workers.set(workerId, promise);
3926
- }, options);
3927
- }
3928
- async #work(workerId, handler, message, orderingKey) {
3929
- await this.#sleep(0);
3930
- try {
3931
- await handler(message);
3932
- } finally {
3933
- if (orderingKey != null) {
3934
- this.#processingKeys.delete(orderingKey);
3935
- const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
3936
- if (pendingIdx >= 0) this.#pendingMessages.splice(pendingIdx, 1)[0].resolve();
3937
- }
3938
- }
3939
- return workerId;
3940
- }
3941
- #sleep(ms) {
3942
- return new Promise((resolve) => setTimeout(resolve, ms));
3943
- }
3944
- };
3945
- //#endregion
3946
- //#region src/federation/tasks/enqueue.ts
3947
- /**
3948
- * The enqueue pipeline for custom background tasks. `ContextImpl.enqueueTask`
3949
- * and `ContextImpl.enqueueTaskMany` delegate to {@link enqueueTasks} so the
3950
- * handle validation, deduplication planning, payload encoding, and queue
3951
- * dispatch live in one cohesive place instead of one oversized method.
3952
- *
3953
- * @module
3954
- */
3955
- /**
3956
- * Validates the task handle, plans deduplication, encodes every payload, then
3957
- * dispatches the resulting messages to the queue. A single item flows through
3958
- * the same pipeline as a batch, so {@link Context.enqueueTask} and
3959
- * {@link Context.enqueueTaskMany} share one implementation.
3960
- * @template TContextData The context data to pass to the {@link Context}.
3961
- * @template TData The type of the task payload, inferred from the task's schema.
3962
- * @param ctx The enqueueing dependencies assembled by `ContextImpl`.
3963
- * @param task The handle returned by `defineTask()`.
3964
- * @param items The payloads to enqueue, in order.
3965
- * @param options The enqueue options governing delay, ordering, and dedup.
3966
- * @internal
3967
- */
3968
- const enqueueTasks = (ctx) => async function(task, items, options) {
3969
- const def = ctx.federation.taskDefinitions.get(task.name);
3970
- if (def == null || def.handle !== task) throw new TypeError(`Task ${JSON.stringify(task.name)} is not defined on this federation; pass a handle returned by its defineTask().`);
3971
- const queue = ctx.federation.resolveTaskQueue(task.name);
3972
- if (queue == null) throw new TypeError("No message queue is configured for tasks; pass `queue` to createFederation() or to defineTask().");
3973
- if (items.length < 1) return;
3974
- const plan = planDeduplication(ctx, queue, task.name, options, items.length);
3975
- const messages = await Promise.all(items.map(encodeTaskMessage(ctx.codec, ctx.origin, task, options)));
3976
- const claim = await claimDeduplication(ctx, plan, task.name);
3977
- if (!claim.proceed) return;
3978
- if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
3979
- try {
3980
- await dispatch(queue, messages, {
3981
- delay: getDurationIfDefined(options.delay),
3982
- orderingKey: options.orderingKey,
3983
- deduplicationKey: claim.forwardedDeduplicationKey
3984
- });
3985
- } catch (error) {
3986
- if (claim.rollback != null) try {
3987
- await claim.rollback();
3988
- } catch (rollbackError) {
3989
- logger$1.warn("Failed to roll back the deduplication marker for task {taskName} after a failed enqueue; it will expire by TTL. {rollbackError}", {
3990
- taskName: task.name,
3991
- rollbackError
3992
- });
3993
- }
3994
- throw error;
3995
- }
3996
- };
3997
- const getDurationIfDefined = (item) => item == null ? void 0 : Temporal.Duration.from(item);
3998
- /**
3999
- * Decides how a `deduplicationKey` (if any) is honored: forwarded to a native
4000
- * queue, claimed via `cas`, or—when neither is available—dropped or rejected
4001
- * per the federation's `taskDeduplicationFallback`. Throws the fail-fast
4002
- * `TypeError`s so they precede the encode.
4003
- */
4004
- function planDeduplication(ctx, queue, taskName, options, itemCount) {
4005
- if (options.deduplicationKey == null) return { kind: "none" };
4006
- const key = options.deduplicationKey;
4007
- const native = queue.nativeDeduplication === true;
4008
- const canCas = ctx.federation.kv.cas != null;
4009
- const canBatchAtomically = queue.enqueueMany != null && !(queue instanceof ParallelMessageQueue && queue.queue.enqueueMany == null);
4010
- if (itemCount > 1 && !canBatchAtomically && (native || canCas)) throw new TypeError(`Task ${JSON.stringify(taskName)} was enqueued as a batch with a deduplicationKey, but its message queue does not implement enqueueMany; a multi-item batch cannot be deduplicated atomically without it. Implement enqueueMany on the queue, or enqueue the tasks individually with enqueueTask().`);
4011
- if (native) return {
4012
- kind: "native",
4013
- key
4014
- };
4015
- if (canCas) return {
4016
- kind: "cas",
4017
- key
4018
- };
4019
- if (ctx.federation.taskDeduplicationFallback === "closed") throw new TypeError("deduplicationKey was set but the message queue does not declare nativeDeduplication and the key-value store exposes no conditional write (cas); set taskDeduplicationFallback to \"open\" to proceed without deduplication, or use a backend that supports it.");
4020
- return {
4021
- kind: "open",
4022
- key
4023
- };
4024
- }
4025
- /**
4026
- * Executes the planned deduplication once the payloads are encoded. A native
4027
- * plan forwards its key to the queue; a `cas` plan claims the marker and stops
4028
- * the enqueue when it loses the race; an `open` plan logs and proceeds.
4029
- * @returns Whether to proceed, and the key (if any) to forward to the queue.
4030
- */
4031
- async function claimDeduplication(ctx, plan, taskName) {
4032
- switch (plan.kind) {
4033
- case "native": return {
4034
- proceed: true,
4035
- forwardedDeduplicationKey: plan.key
4036
- };
4037
- case "cas": {
4038
- const cacheKey = [...ctx.federation.kvPrefixes.taskDeduplication, plan.key];
4039
- const token = crypto.randomUUID();
4040
- if (!await ctx.federation.kv.cas(cacheKey, void 0, token, { ttl: ctx.federation.taskDeduplicationTtl })) return { proceed: false };
4041
- return {
4042
- proceed: true,
4043
- rollback: async () => {
4044
- await ctx.federation.kv.cas(cacheKey, token, void 0);
4045
- }
4046
- };
4047
- }
4048
- case "open": logger$1.debug("deduplicationKey {deduplicationKey} for task {taskName} ignored: the message queue declares no nativeDeduplication and the key-value store has no cas; proceeding (taskDeduplicationFallback is \"open\").", {
4049
- deduplicationKey: plan.key,
4050
- taskName
4051
- });
4052
- case "none": return { proceed: true };
4053
- default: throw new TypeError(`Unknown deduplication plan: ${JSON.stringify(plan)}`);
4054
- }
4055
- }
4056
- /**
4057
- * Sends the encoded messages to the queue, picking the bulk path when the
4058
- * queue implements `enqueueMany` and otherwise fanning out parallel single
4059
- * enqueues. The fan-out drops `deduplicationKey`, which is only ever set for a
4060
- * native plan that the bulk paths already cover.
4061
- */
4062
- async function dispatch(queue, messages, options) {
4063
- if (messages.length === 1) await queue.enqueue(messages[0], options);
4064
- else if (queue.enqueueMany != null) await queue.enqueueMany(messages, options);
4065
- else {
4066
- const fanoutOptions = {
4067
- delay: options.delay,
4068
- orderingKey: options.orderingKey
4069
- };
4070
- await Promise.all(messages.map((m) => queue.enqueue(m, fanoutOptions)));
4071
- }
4072
- }
4073
- /**
4074
- * Builds the per-payload encoder: validates and serializes the payload, then
4075
- * stamps the message envelope with a fresh id, the context's origin, and the
4076
- * active trace context. Curried so the batch encode reuses one bound encoder.
4077
- */
4078
- const encodeTaskMessage = (codec, origin, task, options) => async (data) => {
4079
- const encoded = await codec.encode(task.schema, data);
4080
- const carrier = {};
4081
- _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
4082
- return {
4083
- type: "task",
4084
- id: crypto.randomUUID(),
4085
- baseUrl: origin,
4086
- taskName: task.name,
4087
- data: encoded,
4088
- started: Temporal.Now.instant().toString(),
4089
- attempt: 0,
4090
- orderingKey: options.orderingKey,
4091
- traceContext: carrier
4092
- };
4093
- };
4094
- const logger$1 = (0, _logtape_logtape.getLogger)([
4095
- "fedify",
4096
- "federation",
4097
- "task"
4098
- ]);
4099
- //#endregion
4100
3556
  //#region src/federation/webfinger.ts
4101
3557
  const logger = (0, _logtape_logtape.getLogger)([
4102
3558
  "fedify",
@@ -4404,6 +3860,15 @@ function isPermanentRemoteContextError(error) {
4404
3860
  function isPermanentInboxParseError(error) {
4405
3861
  return error instanceof Error && (error.name === "UnsafeJsonLdError" || error instanceof require_proof.InvalidContextReferenceError || isPermanentRemoteContextError(error) || error.name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure(error)) || error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:|Invalid @id:)/.test(error.message) || require_proof.isInvalidUrlTypeError(error));
4406
3862
  }
3863
+ function hasLinkedDataSignatureJsonLdProcessingError(error) {
3864
+ if (error.message.startsWith("Maximum deep iterations exceeded")) return true;
3865
+ const cause = error.cause instanceof Error ? error.cause : error.details?.cause;
3866
+ if (error.name === "jsonld.InvalidUrl" && error.details?.code === "loading remote context failed" && cause instanceof Error) return cause.message.startsWith("Maximum deep iterations exceeded") || cause.name.startsWith("jsonld.");
3867
+ return error.name !== "jsonld.InvalidUrl" && error.name.startsWith("jsonld.");
3868
+ }
3869
+ function isLinkedDataSignatureJsonLdProcessingError(error) {
3870
+ return error instanceof Error && hasLinkedDataSignatureJsonLdProcessingError(error);
3871
+ }
4407
3872
  /**
4408
3873
  * Create a new {@link Federation} instance.
4409
3874
  * @param options Parameters for initializing the instance.
@@ -4421,8 +3886,9 @@ var FederationImpl = class extends FederationBuilderImpl {
4421
3886
  inboxQueue;
4422
3887
  outboxQueue;
4423
3888
  fanoutQueue;
4424
- taskQueue;
4425
- startedQueues;
3889
+ inboxQueueStarted;
3890
+ outboxQueueStarted;
3891
+ fanoutQueueStarted;
4426
3892
  manuallyStartQueue;
4427
3893
  origin;
4428
3894
  documentLoaderFactory;
@@ -4436,10 +3902,6 @@ var FederationImpl = class extends FederationBuilderImpl {
4436
3902
  skipSignatureVerification;
4437
3903
  outboxRetryPolicy;
4438
3904
  inboxRetryPolicy;
4439
- taskRetryPolicy;
4440
- taskQueueResolution;
4441
- taskDeduplicationTtl;
4442
- taskDeduplicationFallback;
4443
3905
  circuitBreaker;
4444
3906
  activityTransformers;
4445
3907
  _tracerProvider;
@@ -4479,24 +3941,20 @@ var FederationImpl = class extends FederationBuilderImpl {
4479
3941
  httpMessageSignaturesSpec: ["_fedify", "httpMessageSignaturesSpec"],
4480
3942
  acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
4481
3943
  circuitBreaker: ["_fedify", "circuit"],
4482
- taskDeduplication: ["_fedify", "taskDeduplication"],
4483
3944
  ...options.kvPrefixes ?? {}
4484
3945
  };
4485
3946
  if (options.queue == null) {
4486
3947
  this.inboxQueue = void 0;
4487
3948
  this.outboxQueue = void 0;
4488
3949
  this.fanoutQueue = void 0;
4489
- this.taskQueue = void 0;
4490
3950
  } else if ("enqueue" in options.queue && "listen" in options.queue) {
4491
3951
  this.inboxQueue = options.queue;
4492
3952
  this.outboxQueue = options.queue;
4493
3953
  this.fanoutQueue = options.queue;
4494
- this.taskQueue = void 0;
4495
3954
  } else {
4496
3955
  this.inboxQueue = options.queue.inbox;
4497
3956
  this.outboxQueue = options.queue.outbox;
4498
3957
  this.fanoutQueue = options.queue.fanout;
4499
- this.taskQueue = options.queue.task;
4500
3958
  }
4501
3959
  if (options.circuitBreaker !== false && this.outboxQueue != null) {
4502
3960
  this.circuitBreaker = new CircuitBreaker({
@@ -4517,7 +3975,9 @@ var FederationImpl = class extends FederationBuilderImpl {
4517
3975
  ]).warn("The configured key-value store does not support CAS; outbound delivery circuit breaker updates may race under concurrent workers.");
4518
3976
  }
4519
3977
  }
4520
- this.startedQueues = /* @__PURE__ */ new Set();
3978
+ this.inboxQueueStarted = false;
3979
+ this.outboxQueueStarted = false;
3980
+ this.fanoutQueueStarted = false;
4521
3981
  this.manuallyStartQueue = options.manuallyStartQueue ?? false;
4522
3982
  if (options.origin != null) if (typeof options.origin === "string") {
4523
3983
  if (!URL.canParse(options.origin) || !options.origin.match(/^https?:\/\//)) throw new TypeError(`Invalid origin: ${JSON.stringify(options.origin)}`);
@@ -4599,10 +4059,6 @@ var FederationImpl = class extends FederationBuilderImpl {
4599
4059
  this.inboxChallengePolicy = options.inboxChallengePolicy;
4600
4060
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
4601
4061
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
4602
- this.taskRetryPolicy = options.taskRetryPolicy ?? createExponentialBackoffPolicy();
4603
- this.taskQueueResolution = options.taskQueueResolution ?? "fallback";
4604
- this.taskDeduplicationTtl = Temporal.Duration.from(options.taskDeduplicationTtl ?? { hours: 1 });
4605
- this.taskDeduplicationFallback = options.taskDeduplicationFallback ?? "open";
4606
4062
  this.activityTransformers = options.activityTransformers ?? require_transformers.getDefaultActivityTransformers();
4607
4063
  this._tracerProvider = options.tracerProvider;
4608
4064
  if (benchmarkMode) {
@@ -4651,33 +4107,28 @@ var FederationImpl = class extends FederationBuilderImpl {
4651
4107
  _getTracer() {
4652
4108
  return this.tracerProvider.getTracer(require_http.name, require_http.version);
4653
4109
  }
4654
- resolveTaskQueue(taskName) {
4655
- const resolved = this.taskDefinitions.get(taskName)?.queue ?? this.taskQueue;
4656
- if (resolved != null) return resolved;
4657
- return this.taskQueueResolution === "strict" ? void 0 : this.outboxQueue;
4658
- }
4659
4110
  async _startQueueInternal(ctxData, signal, queue) {
4660
- const taskQueue = this.taskQueue ?? (this.taskQueueResolution === "fallback" ? this.outboxQueue : void 0);
4661
- const customQueues = this.taskDefinitions.values().map((def) => ["task", def.queue]);
4662
- const targets = [
4663
- ["inbox", this.inboxQueue],
4664
- ["outbox", this.outboxQueue],
4665
- ["fanout", this.fanoutQueue],
4666
- ["task", taskQueue],
4667
- ...customQueues
4668
- ];
4111
+ if (this.inboxQueue == null && this.outboxQueue == null) return;
4669
4112
  const logger = (0, _logtape_logtape.getLogger)([
4670
4113
  "fedify",
4671
4114
  "federation",
4672
4115
  "queue"
4673
4116
  ]);
4674
4117
  const promises = [];
4675
- for (const [role, target] of targets) {
4676
- if (target == null || !(queue == null || queue === role)) continue;
4677
- if (this.startedQueues.has(target)) continue;
4678
- this.startedQueues.add(target);
4679
- logger.debug("Starting a {role} queue worker.", { role });
4680
- promises.push(target.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4118
+ if (this.inboxQueue != null && (queue == null || queue === "inbox") && !this.inboxQueueStarted) {
4119
+ logger.debug("Starting an inbox task worker.");
4120
+ this.inboxQueueStarted = true;
4121
+ promises.push(this.inboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4122
+ }
4123
+ if (this.outboxQueue != null && this.outboxQueue !== this.inboxQueue && (queue == null || queue === "outbox") && !this.outboxQueueStarted) {
4124
+ logger.debug("Starting an outbox task worker.");
4125
+ this.outboxQueueStarted = true;
4126
+ promises.push(this.outboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4127
+ }
4128
+ if (this.fanoutQueue != null && this.fanoutQueue !== this.inboxQueue && this.fanoutQueue !== this.outboxQueue && (queue == null || queue === "fanout") && !this.fanoutQueueStarted) {
4129
+ logger.debug("Starting a fanout task worker.");
4130
+ this.fanoutQueueStarted = true;
4131
+ promises.push(this.fanoutQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4681
4132
  }
4682
4133
  await Promise.all(promises);
4683
4134
  }
@@ -4800,7 +4251,7 @@ var FederationImpl = class extends FederationBuilderImpl {
4800
4251
  }
4801
4252
  });
4802
4253
  });
4803
- } else if (message.type === "task") await this.#listenTaskMessage(contextData, message);
4254
+ }
4804
4255
  });
4805
4256
  }
4806
4257
  async #listenFanoutMessage(data, message) {
@@ -5364,77 +4815,6 @@ var FederationImpl = class extends FederationBuilderImpl {
5364
4815
  listenerSpan.end();
5365
4816
  });
5366
4817
  }
5367
- async #listenTaskMessage(contextData, message) {
5368
- const logger = (0, _logtape_logtape.getLogger)([
5369
- "fedify",
5370
- "federation",
5371
- "task"
5372
- ]);
5373
- const def = this.taskDefinitions.get(message.taskName);
5374
- if (def == null) {
5375
- logger.warn("Received a custom task {taskName} with no registered handler; dropping.", { taskName: message.taskName });
5376
- return;
5377
- }
5378
- const context = this.#createContext(new URL(message.baseUrl), contextData);
5379
- let data;
5380
- try {
5381
- data = await context.codec.decode(def.schema, message.data);
5382
- } catch (error) {
5383
- logger.error("Custom task {taskName} payload could not be decoded or validated; dropping:\n{error}", {
5384
- taskName: message.taskName,
5385
- error
5386
- });
5387
- return;
5388
- }
5389
- try {
5390
- await def.handler(context, data);
5391
- } catch (error) {
5392
- if (def.onError != null) try {
5393
- await def.onError(context, error, data);
5394
- } catch (onErrorError) {
5395
- logger.error("onError for custom task {taskName} threw:\n{error}", {
5396
- taskName: message.taskName,
5397
- error: onErrorError
5398
- });
5399
- }
5400
- const queue = this.resolveTaskQueue(def.name);
5401
- if (queue?.nativeRetrial) throw error;
5402
- const retryPolicy = def.retryPolicy ?? this.taskRetryPolicy;
5403
- let elapsedTime = Temporal.Duration.from({ seconds: 0 });
5404
- try {
5405
- elapsedTime = Temporal.Instant.from(message.started).until(Temporal.Now.instant());
5406
- } catch (parseError) {
5407
- logger.error("Custom task {taskName} has an unparsable started time {started}; treating elapsedTime as zero:\n{error}", {
5408
- taskName: message.taskName,
5409
- started: message.started,
5410
- error: parseError
5411
- });
5412
- }
5413
- const delay = retryPolicy({
5414
- elapsedTime,
5415
- attempts: message.attempt
5416
- });
5417
- if (delay != null && queue != null) {
5418
- logger.error("Custom task {taskName} failed (attempt #{attempt}); retry...:\n{error}", {
5419
- taskName: message.taskName,
5420
- attempt: message.attempt,
5421
- error
5422
- });
5423
- const retryMessage = {
5424
- ...message,
5425
- attempt: message.attempt + 1
5426
- };
5427
- await queue.enqueue(retryMessage, {
5428
- delay: clampNegativeDelay(delay),
5429
- orderingKey: message.orderingKey
5430
- });
5431
- } else logger.error("Custom task {taskName} failed after {attempt} attempts; giving up:\n{error}", {
5432
- taskName: message.taskName,
5433
- attempt: message.attempt,
5434
- error
5435
- });
5436
- }
5437
- }
5438
4818
  startQueue(contextData, options = {}) {
5439
4819
  return this._startQueueInternal(contextData, options.signal, options.queue);
5440
4820
  }
@@ -5523,10 +4903,18 @@ var FederationImpl = class extends FederationBuilderImpl {
5523
4903
  privateKey: pair.privateKey
5524
4904
  }))
5525
4905
  });
5526
- else jsonLd = await require_proof.signJsonLd(jsonLd, rsaKey.privateKey, rsaKey.keyId, {
5527
- contextLoader,
5528
- tracerProvider: this.tracerProvider
5529
- });
4906
+ else try {
4907
+ jsonLd = await require_proof.signJsonLd(jsonLd, rsaKey.privateKey, rsaKey.keyId, {
4908
+ contextLoader,
4909
+ tracerProvider: this.tracerProvider
4910
+ });
4911
+ } catch (error) {
4912
+ if (!isLinkedDataSignatureJsonLdProcessingError(error)) throw error;
4913
+ logger.warn("Failed to create a Linked Data signature for the activity {activityId}. The activity will be sent without a Linked Data signature.", {
4914
+ activityId,
4915
+ error
4916
+ });
4917
+ }
5530
4918
  if (!hasProof) logger.warn("No supported key found to create a proof for the activity {activityId}. The activity will be sent without a proof. In order to create a proof, at least one Ed25519 key must be provided.", {
5531
4919
  activityId,
5532
4920
  keys: keys.map((pair) => ({
@@ -6011,7 +5399,6 @@ var ContextImpl = class ContextImpl {
6011
5399
  documentLoader;
6012
5400
  contextLoader;
6013
5401
  invokedFromActorKeyPairsDispatcher;
6014
- #codec;
6015
5402
  constructor({ url, federation, data, documentLoader, contextLoader, invokedFromActorKeyPairsDispatcher }) {
6016
5403
  this.url = url;
6017
5404
  this.federation = federation;
@@ -6020,18 +5407,6 @@ var ContextImpl = class ContextImpl {
6020
5407
  this.contextLoader = contextLoader;
6021
5408
  this.invokedFromActorKeyPairsDispatcher = invokedFromActorKeyPairsDispatcher;
6022
5409
  }
6023
- /**
6024
- * A {@link TaskCodec} bound to this context's loaders, used to encode
6025
- * and decode custom task payloads. Lazily created and cached so a context
6026
- * that never enqueues or dispatches a task pays nothing.
6027
- * @internal
6028
- */
6029
- get codec() {
6030
- return this.#codec ??= new TaskCodec(this);
6031
- }
6032
- get #enqueueTasks() {
6033
- return enqueueTasks(this);
6034
- }
6035
5410
  clone(data) {
6036
5411
  return new ContextImpl({
6037
5412
  url: this.url,
@@ -6350,12 +5725,6 @@ var ContextImpl = class ContextImpl {
6350
5725
  allowPrivateAddress: this.federation.allowPrivateAddress
6351
5726
  });
6352
5727
  }
6353
- async enqueueTask(task, data, options = {}) {
6354
- await this.#enqueueTasks(task, [data], options);
6355
- }
6356
- async enqueueTaskMany(task, payloads, options = {}) {
6357
- await this.#enqueueTasks(task, payloads, options);
6358
- }
6359
5728
  sendActivity(sender, recipients, activity, options = {}) {
6360
5729
  return this.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
6361
5730
  kind: this.federation.outboxQueue == null || options.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
@@ -7072,18 +6441,6 @@ Object.defineProperty(exports, "CircuitBreaker", {
7072
6441
  return CircuitBreaker;
7073
6442
  }
7074
6443
  });
7075
- Object.defineProperty(exports, "InProcessMessageQueue", {
7076
- enumerable: true,
7077
- get: function() {
7078
- return InProcessMessageQueue;
7079
- }
7080
- });
7081
- Object.defineProperty(exports, "ParallelMessageQueue", {
7082
- enumerable: true,
7083
- get: function() {
7084
- return ParallelMessageQueue;
7085
- }
7086
- });
7087
6444
  Object.defineProperty(exports, "SendActivityError", {
7088
6445
  enumerable: true,
7089
6446
  get: function() {