@fedify/fedify 2.4.0-dev.1508 → 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 (108) hide show
  1. package/dist/{assert-DvVHoIZ0.mjs → assert-OguE97r2.mjs} +1 -1
  2. package/dist/{assert_instance_of-CxoC8bVq.mjs → assert_instance_of-DBC5X09g.mjs} +1 -1
  3. package/dist/{assert_not_equals-C8vcTTA3.mjs → assert_not_equals-DkVK8oqV.mjs} +1 -1
  4. package/dist/{assert_rejects-DAdaaIWM.mjs → assert_rejects-DN60FHPX.mjs} +2 -2
  5. package/dist/{assert_strict_equals-BzqyKuYL.mjs → assert_strict_equals-XEgZAlrj.mjs} +1 -1
  6. package/dist/{assert_throws-zN79lgIC.mjs → assert_throws-BOkhLGYc.mjs} +1 -1
  7. package/dist/{builder-DlWCek6Z.mjs → builder-B6myMguW.mjs} +3 -23
  8. package/dist/compat/mod.d.cts +1 -1
  9. package/dist/compat/mod.d.ts +1 -1
  10. package/dist/compat/outgoing-jsonld.test.mjs +3 -3
  11. package/dist/compat/public-audience.test.mjs +3 -3
  12. package/dist/compat/transformers.test.mjs +5 -5
  13. package/dist/{context-CYF8X0ft.d.cts → context-BBVLF7lx.d.cts} +272 -543
  14. package/dist/{context-BF5B4ZDk.d.ts → context-BU6jSQdo.d.ts} +272 -542
  15. package/dist/{context-BBRTgkrs.mjs → context-DVoTs_wM.mjs} +2 -8
  16. package/dist/{deno-nexucTAX.mjs → deno-CVNsRJe9.mjs} +1 -1
  17. package/dist/{docloader-DAdEN549.mjs → docloader-CB8AGpw3.mjs} +3 -3
  18. package/dist/federation/builder.test.mjs +5 -5
  19. package/dist/federation/circuit-breaker.test.mjs +5 -5
  20. package/dist/federation/collection.test.mjs +3 -3
  21. package/dist/federation/handler.test.mjs +13 -13
  22. package/dist/federation/idempotency.test.mjs +6 -6
  23. package/dist/federation/inbox.test.mjs +3 -3
  24. package/dist/federation/keycache.test.mjs +5 -5
  25. package/dist/federation/kv.test.mjs +3 -3
  26. package/dist/federation/metrics.test.mjs +4 -4
  27. package/dist/federation/middleware.test.mjs +19 -200
  28. package/dist/federation/mod.cjs +259 -3
  29. package/dist/federation/mod.d.cts +4 -4
  30. package/dist/federation/mod.d.ts +4 -4
  31. package/dist/federation/mod.js +257 -1
  32. package/dist/federation/mq.test.mjs +261 -103
  33. package/dist/federation/negotiation.test.mjs +4 -4
  34. package/dist/federation/retry.test.mjs +3 -3
  35. package/dist/federation/router.test.mjs +5 -5
  36. package/dist/federation/send.test.mjs +10 -10
  37. package/dist/federation/temporal.test.mjs +4 -4
  38. package/dist/federation/webfinger.test.mjs +6 -6
  39. package/dist/{http-Dv3147TL.js → http-B0-9u_1F.js} +1 -1
  40. package/dist/{http-BCj9dlA_.cjs → http-EkV7OslM.cjs} +1 -1
  41. package/dist/{http-inQqS3yx.mjs → http-L7wqkbZR.mjs} +4 -4
  42. package/dist/{key-CGrr6-I2.mjs → key-Ct8zNgGk.mjs} +2 -2
  43. package/dist/{kv-cache-C3qkFkbt.mjs → kv-cache-CUT9OGpX.mjs} +1 -1
  44. package/dist/{kv-cache-c8X6svwE.cjs → kv-cache-CbB50co6.cjs} +1 -1
  45. package/dist/{kv-cache-C2oTEXyb.js → kv-cache-Dh8ZoQfC.js} +1 -1
  46. package/dist/{ld-fnWaNT8y.mjs → ld-D6I7H18J.mjs} +3 -3
  47. package/dist/{metrics-C5hMINAa.mjs → metrics-DvClPYaE.mjs} +1 -1
  48. package/dist/{middleware-DeVzbg1I.cjs → middleware-B0ujKP-0.cjs} +30 -690
  49. package/dist/{middleware-CQlq6ykI.mjs → middleware-CGqUj4XY.mjs} +1 -1
  50. package/dist/{middleware-vQ_ZpgcX.js → middleware-DX9Fd37u.js} +31 -679
  51. package/dist/{middleware-Dl2nyVgB.mjs → middleware-RxFBNV2C.mjs} +46 -306
  52. package/dist/{mod-BHEB3xhE.d.cts → mod-C0F6kvgS.d.cts} +1 -1
  53. package/dist/{mod-4WGAuR6X.d.ts → mod-vPYVoa5n.d.ts} +1 -1
  54. package/dist/mod.cjs +6 -6
  55. package/dist/mod.d.cts +5 -5
  56. package/dist/mod.d.ts +5 -5
  57. package/dist/mod.js +5 -5
  58. package/dist/{mq-DXMt_qCY.d.cts → mq-D-nlpY04.d.ts} +0 -34
  59. package/dist/{mq-B5_hohs7.d.ts → mq-D8uSFzxe.d.cts} +0 -34
  60. package/dist/nodeinfo/client.test.mjs +3 -3
  61. package/dist/nodeinfo/handler.test.mjs +5 -5
  62. package/dist/nodeinfo/types.test.mjs +4 -4
  63. package/dist/otel/exporter.test.mjs +3 -3
  64. package/dist/{outgoing-jsonld-BNL8AC14.mjs → outgoing-jsonld-L_DbOaFe.mjs} +1 -1
  65. package/dist/{owner-Bq2eZyO_.mjs → owner-BqPUA8Z6.mjs} +2 -2
  66. package/dist/{proof-DZCjcFCd.mjs → proof-BJKoRFBc.mjs} +5 -5
  67. package/dist/{proof-CTG9azxN.js → proof-DCCmmEuJ.js} +1 -1
  68. package/dist/{proof-Buz7I8wj.cjs → proof-eZme7HYR.cjs} +1 -1
  69. package/dist/{send-B253S5gN.mjs → send-DVtEjoJ6.mjs} +3 -3
  70. package/dist/sig/accept.test.mjs +1 -1
  71. package/dist/sig/http.test.mjs +8 -8
  72. package/dist/sig/key.test.mjs +6 -6
  73. package/dist/sig/ld.test.mjs +7 -7
  74. package/dist/sig/mod.cjs +2 -2
  75. package/dist/sig/mod.js +2 -2
  76. package/dist/sig/owner.test.mjs +6 -6
  77. package/dist/sig/proof.test.mjs +8 -8
  78. package/dist/{std__assert-Dh6uLf_q.mjs → std__assert-BBjXFNOb.mjs} +4 -4
  79. package/dist/{temporal-d6TshGjq.mjs → temporal-B06FcmVK.mjs} +1 -1
  80. package/dist/testing/mod.d.mts +8 -1037
  81. package/dist/testing/mod.mjs +2 -3
  82. package/dist/utils/docloader.test.mjs +6 -6
  83. package/dist/utils/kv-cache.test.mjs +1 -1
  84. package/dist/utils/mod.cjs +1 -1
  85. package/dist/utils/mod.js +1 -1
  86. package/package.json +7 -9
  87. package/dist/codec-CmlPqjNX.mjs +0 -103
  88. package/dist/federation/tasks/codec.test.d.mts +0 -2
  89. package/dist/federation/tasks/codec.test.mjs +0 -292
  90. package/dist/federation/tasks/enqueue.test.d.mts +0 -2
  91. package/dist/federation/tasks/enqueue.test.mjs +0 -992
  92. package/dist/federation/tasks/tasks.test.d.mts +0 -2
  93. package/dist/federation/tasks/tasks.test.mjs +0 -555
  94. package/dist/mq-03vS-C9P.mjs +0 -276
  95. package/dist/testing-Dy-WhqpN.mjs +0 -77
  96. /package/dist/{accept-CgDcxvjV.mjs → accept-CPkZzmGN.mjs} +0 -0
  97. /package/dist/{activity-listener-BeTGV3wc.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  98. /package/dist/{assert_equals-PBVKNMJd.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
  99. /package/dist/{circuit-breaker-hJBB6jwA.mjs → circuit-breaker-CSWsyoef.mjs} +0 -0
  100. /package/dist/{client-B_A6mfn3.mjs → client-ByXmQhYD.mjs} +0 -0
  101. /package/dist/{collection-Dp5ky45w.mjs → collection-Cc3DVAhE.mjs} +0 -0
  102. /package/dist/{keycache-CaOR6NYg.mjs → keycache-BeU0LCII.mjs} +0 -0
  103. /package/dist/{keys-CSYsOMFG.mjs → keys-DGu1NFwu.mjs} +0 -0
  104. /package/dist/{kv-CIvR3Non.mjs → kv-rV3vodCc.mjs} +0 -0
  105. /package/dist/{negotiation-m_L2nyS3.mjs → negotiation-DDstyBvc.mjs} +0 -0
  106. /package/dist/{public-audience-c9zmYKgA.mjs → public-audience-Cvbr2Gzt.mjs} +0 -0
  107. /package/dist/{retry-DI4O-zYl.mjs → retry-CXg_MBI-.mjs} +0 -0
  108. /package/dist/{types-BFowWFTT.mjs → types-J53Kw7so.mjs} +0 -0
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./chunk-CRNNMoPX.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
5
- import { A as formatAcceptSignature, D as recordOutboxEnqueue, E as recordOutboxActivity, F as version, O as recordWebFingerHandle, P as name, S as recordCollectionTotalItems, T as recordInboxActivity, a as verifyRequestDetailed, b as recordCollectionPageItems, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as registerQueueDepthGauge, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordCircuitBreakerStateChange, w as recordFanoutRecipients, x as recordCollectionRequest, y as recordCollectionDispatchDuration } from "./http-Dv3147TL.js";
6
- import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-CTG9azxN.js";
5
+ import { A as formatAcceptSignature, D as recordOutboxEnqueue, E as recordOutboxActivity, F as version, O as recordWebFingerHandle, P as name, S as recordCollectionTotalItems, T as recordInboxActivity, a as verifyRequestDetailed, b as recordCollectionPageItems, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as registerQueueDepthGauge, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordCircuitBreakerStateChange, w as recordFanoutRecipients, x as recordCollectionRequest, y as recordCollectionDispatchDuration } from "./http-B0-9u_1F.js";
6
+ import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-DCCmmEuJ.js";
7
7
  import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-C2oTEXyb.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Dh8ZoQfC.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
11
11
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -17,7 +17,6 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_
17
17
  import jsonld from "@fedify/vocab-runtime/jsonld";
18
18
  import { lookupWebFinger } from "@fedify/webfinger";
19
19
  import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
20
- import { parse, stringifyAsync } from "devalue";
21
20
  import { domainToASCII } from "node:url";
22
21
  //#region src/federation/activity-listener.ts
23
22
  var ActivityListenerSet = class {
@@ -108,7 +107,6 @@ var FederationBuilderImpl = class {
108
107
  idempotencyStrategy;
109
108
  collectionTypeIds;
110
109
  collectionCallbacks;
111
- taskDefinitions;
112
110
  /**
113
111
  * Symbol registry for unique identification of unnamed symbols.
114
112
  */
@@ -119,7 +117,6 @@ var FederationBuilderImpl = class {
119
117
  this.objectTypeIds = {};
120
118
  this.collectionCallbacks = {};
121
119
  this.collectionTypeIds = {};
122
- this.taskDefinitions = /* @__PURE__ */ new Map();
123
120
  }
124
121
  /**
125
122
  * Builds the federation object.
@@ -158,7 +155,6 @@ var FederationBuilderImpl = class {
158
155
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
159
156
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
160
157
  f.idempotencyStrategy = this.idempotencyStrategy;
161
- f.taskDefinitions = new Map(this.taskDefinitions);
162
158
  return f;
163
159
  }
164
160
  _getTracer() {
@@ -291,23 +287,6 @@ var FederationBuilderImpl = class {
291
287
  setWebFingerLinksDispatcher(dispatcher) {
292
288
  this.webFingerLinksDispatcher = dispatcher;
293
289
  }
294
- defineTask(name, options) {
295
- if (this.taskDefinitions.has(name)) throw new TypeError(`Task ${JSON.stringify(name)} is already defined.`);
296
- const handle = {
297
- name,
298
- schema: options.schema
299
- };
300
- this.taskDefinitions.set(name, {
301
- name,
302
- schema: options.schema,
303
- handle,
304
- handler: options.handler,
305
- onError: options.onError,
306
- retryPolicy: options.retryPolicy,
307
- queue: options.queue
308
- });
309
- return handle;
310
- }
311
290
  /**
312
291
  * The RFC 6570 template-literal `path` overloads were removed for
313
292
  * type-checking efficiency, so the URI variable types can no longer be
@@ -1413,7 +1392,7 @@ function acceptsJsonLd(request) {
1413
1392
  }
1414
1393
  //#endregion
1415
1394
  //#region src/federation/temporal.ts
1416
- function isPlainObject$1(value) {
1395
+ function isPlainObject(value) {
1417
1396
  return typeof value === "object" && value != null && !Array.isArray(value);
1418
1397
  }
1419
1398
  function normalizeDateTimeLiteral(value) {
@@ -1451,11 +1430,11 @@ const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
1451
1430
  const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
1452
1431
  function hasMalformedExpandedDateTimeLiteral(value) {
1453
1432
  if (Array.isArray(value)) return value.some(hasMalformedExpandedDateTimeLiteral);
1454
- return isPlainObject$1(value) && "@value" in value && isMalformedDateTimeLiteral(value["@value"]);
1433
+ return isPlainObject(value) && "@value" in value && isMalformedDateTimeLiteral(value["@value"]);
1455
1434
  }
1456
1435
  function hasMalformedExpandedQuestionClosedLiteral(value) {
1457
1436
  if (Array.isArray(value)) return value.some(hasMalformedExpandedQuestionClosedLiteral);
1458
- if (!isPlainObject$1(value) || !("@value" in value)) return false;
1437
+ if (!isPlainObject(value) || !("@value" in value)) return false;
1459
1438
  const literal = value["@value"];
1460
1439
  if (typeof literal === "boolean") return false;
1461
1440
  if (typeof literal !== "string") return false;
@@ -1465,11 +1444,11 @@ function hasMalformedExpandedQuestionClosedLiteral(value) {
1465
1444
  }
1466
1445
  function hasMalformedExpandedDurationLiteral(value) {
1467
1446
  if (Array.isArray(value)) return value.some(hasMalformedExpandedDurationLiteral);
1468
- return isPlainObject$1(value) && "@value" in value && isMalformedDurationLiteral(value["@value"]);
1447
+ return isPlainObject(value) && "@value" in value && isMalformedDurationLiteral(value["@value"]);
1469
1448
  }
1470
1449
  function hasMalformedKnownTemporalLiteralInternal(value, visited) {
1471
1450
  if (Array.isArray(value)) return value.some((item) => hasMalformedKnownTemporalLiteralInternal(item, visited));
1472
- if (!isPlainObject$1(value)) return false;
1451
+ if (!isPlainObject(value)) return false;
1473
1452
  if (visited.has(value)) return false;
1474
1453
  visited.add(value);
1475
1454
  if ("@value" in value) return false;
@@ -3573,529 +3552,6 @@ function createExponentialBackoffPolicy(options = {}) {
3573
3552
  };
3574
3553
  }
3575
3554
  //#endregion
3576
- //#region src/federation/tasks/codec.ts
3577
- /**
3578
- * Serializes and deserializes task payloads for the queue, preserving
3579
- * `@fedify/vocab` objects across the wire by reducing them to JSON-LD and
3580
- * rebuilding them on the worker with the bound {@link TaskCodecLoaders}.
3581
- * @internal
3582
- */
3583
- var TaskCodec = class TaskCodec {
3584
- options;
3585
- constructor(options) {
3586
- this.options = options;
3587
- }
3588
- /** Serializes `data`, encoding any vocabulary object as its JSON-LD. */
3589
- serialize = (data) => stringifyAsync(data, { Vocab: this.#stringifyVocab });
3590
- /** Deserializes `raw`, rebuilding any encoded vocabulary object. */
3591
- deserialize = (raw) => this.#revive(/* @__PURE__ */ new Map())(parse(raw, { Vocab: VocabHolder.from }));
3592
- /** Validates `data` against `schema`, then serializes it. */
3593
- encode = async (schema, data) => this.serialize(await TaskCodec.validate(schema, data));
3594
- /** Deserializes `raw`, then validates the result against `schema`. */
3595
- decode = async (schema, raw) => TaskCodec.validate(schema, await this.deserialize(raw));
3596
- /** Validates `data` against `schema`, returning its parsed output. */
3597
- static validate = async (schema, data) => getValueIfSchema(await schema["~standard"].validate(data));
3598
- #stringifyVocab = (value) => isVocab(value) && this.#toWire(value);
3599
- #toWire = async (value) => ({
3600
- kind: value instanceof Link ? "link" : "object",
3601
- jsonLd: await value.toJsonLd({
3602
- format: "expand",
3603
- ...this.options
3604
- })
3605
- });
3606
- #revive = (seen) => {
3607
- const inner = async (node) => {
3608
- if (node === null || typeof node !== "object") return node;
3609
- if (seen.has(node)) return seen.get(node);
3610
- for (const reviver of this.#classRevivers) {
3611
- const out = reviver(seen, inner, node);
3612
- if (out !== void 0) return await out;
3613
- }
3614
- return node;
3615
- };
3616
- return inner;
3617
- };
3618
- #classRevivers = [
3619
- classReviver(isInstanceOf(VocabHolder), ({ kind, jsonLd }) => kind === "link" ? Link.fromJsonLd(jsonLd, this.options) : Object$1.fromJsonLd(jsonLd, this.options), () => {}),
3620
- classReviver(isInstanceOf(Array), () => [], async (revive, node, arr) => {
3621
- for (const item of await Array.fromAsync(node, revive)) arr.push(item);
3622
- }),
3623
- classReviver(isInstanceOf(Map), () => /* @__PURE__ */ new Map(), async (revive, node, map) => {
3624
- for (const [k, v] of node) map.set(await revive(k), await revive(v));
3625
- }),
3626
- classReviver(isInstanceOf(Set), () => /* @__PURE__ */ new Set(), async (revive, node, set) => {
3627
- for (const v of await Array.fromAsync(node, revive)) set.add(v);
3628
- }),
3629
- classReviver(isPlainObject, () => ({}), async (revive, node, obj) => {
3630
- for (const [k, v] of globalThis.Object.entries(node)) obj[k] = await revive(v);
3631
- })
3632
- ];
3633
- };
3634
- const isVocab = (value) => value instanceof Object$1 || value instanceof Link;
3635
- const isPlainObject = (value) => value === null || typeof value !== "object" ? false : isObjectPrototype(globalThis.Object.getPrototypeOf(value));
3636
- const isObjectPrototype = (proto) => proto === null || proto === globalThis.Object.prototype;
3637
- const isInstanceOf = (cls) => (v) => v instanceof cls;
3638
- function getValueIfSchema(result) {
3639
- assertSchema(result);
3640
- return result.value;
3641
- }
3642
- function assertSchema(result) {
3643
- if (result.issues && result.issues.length > 0) throw new TypeError(`Task data failed schema validation: ${JSON.stringify(result.issues)}`);
3644
- }
3645
- /**
3646
- * A vocabulary object parked by the synchronous decode reviver, held until
3647
- * the async revive pass can `fromJsonLd()` it back into an instance.
3648
- */
3649
- var VocabHolder = class VocabHolder {
3650
- kind;
3651
- jsonLd;
3652
- constructor(kind, jsonLd) {
3653
- this.kind = kind;
3654
- this.jsonLd = jsonLd;
3655
- }
3656
- static from = ({ kind, jsonLd }) => new VocabHolder(kind, jsonLd);
3657
- };
3658
- /**
3659
- * Ties a container filter to its empty-shell `init` and child-filling `set`
3660
- * through one type parameter—a correlation the heterogeneous reviver list
3661
- * cannot carry, which previously forced `@ts-ignore` at the dispatch site.
3662
- */
3663
- const classReviver = (filter, init, set) => (seen, revive, node) => {
3664
- if (!filter(node)) return void 0;
3665
- return (async () => {
3666
- const out = await init(node);
3667
- seen.set(node, out);
3668
- await set(revive, node, out);
3669
- return out;
3670
- })();
3671
- };
3672
- //#endregion
3673
- //#region src/federation/mq.ts
3674
- /**
3675
- * A message queue that processes messages in the same process.
3676
- * Do not use this in production as it does neither persist messages nor
3677
- * distribute them across multiple processes.
3678
- *
3679
- * @since 0.5.0
3680
- */
3681
- var InProcessMessageQueue = class {
3682
- #messages;
3683
- #monitors;
3684
- #pollIntervalMs;
3685
- #delayedMessages;
3686
- /**
3687
- * Tracks which ordering keys are currently being processed to ensure
3688
- * sequential processing for messages with the same key.
3689
- */
3690
- #processingKeys;
3691
- /**
3692
- * In-process message queue does not provide native retry mechanisms.
3693
- * @since 1.7.0
3694
- */
3695
- nativeRetrial = false;
3696
- /**
3697
- * In-process message queue does not deduplicate messages natively.
3698
- * @since 2.4.0
3699
- */
3700
- nativeDeduplication = false;
3701
- /**
3702
- * Constructs a new {@link InProcessMessageQueue} with the given options.
3703
- * @param options Additional options for the in-process message queue.
3704
- */
3705
- constructor(options = {}) {
3706
- this.#messages = [];
3707
- this.#monitors = {};
3708
- this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
3709
- this.#delayedMessages = 0;
3710
- this.#processingKeys = /* @__PURE__ */ new Set();
3711
- }
3712
- enqueue(message, options) {
3713
- const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
3714
- if (delay > 0) {
3715
- this.#delayedMessages++;
3716
- setTimeout(() => {
3717
- this.#delayedMessages--;
3718
- this.#enqueueReady(message, options);
3719
- }, delay);
3720
- return Promise.resolve();
3721
- }
3722
- this.#enqueueReady(message, options);
3723
- return Promise.resolve();
3724
- }
3725
- #enqueueReady(message, options) {
3726
- const orderingKey = options?.orderingKey ?? null;
3727
- this.#messages.push({
3728
- message,
3729
- orderingKey
3730
- });
3731
- this.#notifyMonitors();
3732
- }
3733
- #notifyMonitors() {
3734
- for (const monitorId in this.#monitors) this.#monitors[monitorId]();
3735
- }
3736
- enqueueMany(messages, options) {
3737
- if (messages.length === 0) return Promise.resolve();
3738
- const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
3739
- if (delay > 0) {
3740
- const delayedCount = messages.length;
3741
- const deferredMessages = [...messages];
3742
- this.#delayedMessages += delayedCount;
3743
- setTimeout(() => {
3744
- this.#delayedMessages -= delayedCount;
3745
- this.#enqueueManyReady(deferredMessages, options);
3746
- }, delay);
3747
- return Promise.resolve();
3748
- }
3749
- this.#enqueueManyReady(messages, options);
3750
- return Promise.resolve();
3751
- }
3752
- #enqueueManyReady(messages, options) {
3753
- const orderingKey = options?.orderingKey ?? null;
3754
- for (const message of messages) this.#messages.push({
3755
- message,
3756
- orderingKey
3757
- });
3758
- this.#notifyMonitors();
3759
- }
3760
- async listen(handler, options = {}) {
3761
- const signal = options.signal;
3762
- while (signal == null || !signal.aborted) {
3763
- const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
3764
- if (idx >= 0) {
3765
- const { message, orderingKey } = this.#messages.splice(idx, 1)[0];
3766
- if (orderingKey != null) this.#processingKeys.add(orderingKey);
3767
- try {
3768
- await handler(message);
3769
- } finally {
3770
- if (orderingKey != null) this.#processingKeys.delete(orderingKey);
3771
- }
3772
- } else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
3773
- else await this.#wait(10, signal);
3774
- }
3775
- }
3776
- getDepth() {
3777
- const ready = this.#messages.length;
3778
- const delayed = this.#delayedMessages;
3779
- return Promise.resolve({
3780
- queued: ready + delayed,
3781
- ready,
3782
- delayed
3783
- });
3784
- }
3785
- #wait(ms, signal) {
3786
- let timer = null;
3787
- return Promise.any([new Promise((resolve) => {
3788
- signal?.addEventListener("abort", () => {
3789
- if (timer != null) clearTimeout(timer);
3790
- resolve();
3791
- }, { once: true });
3792
- const monitorId = crypto.randomUUID();
3793
- this.#monitors[monitorId] = () => {
3794
- delete this.#monitors[monitorId];
3795
- if (timer != null) clearTimeout(timer);
3796
- resolve();
3797
- };
3798
- }), new Promise((resolve) => timer = setTimeout(resolve, ms))]);
3799
- }
3800
- };
3801
- /**
3802
- * A message queue that processes messages in parallel. It takes another
3803
- * {@link MessageQueue}, and processes messages in parallel up to a certain
3804
- * number of workers.
3805
- *
3806
- * Actually, it's rather a decorator than a queue itself.
3807
- *
3808
- * Note that the workers do not run in truly parallel, in the sense that they
3809
- * are not running in separate threads or processes. They are running in the
3810
- * same process, but are scheduled to run in parallel. Hence, this is useful
3811
- * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
3812
- * workloads.
3813
- *
3814
- * When using `ParallelMessageQueue`, the ordering guarantee is preserved
3815
- * *only if* the underlying queue implementation delivers messages in a wrapper
3816
- * format that includes the `__fedify_ordering_key__` property. Currently,
3817
- * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
3818
- * For other queue implementations (e.g., `InProcessMessageQueue`,
3819
- * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
3820
- * `AmqpMessageQueue`), the ordering key cannot be detected by
3821
- * `ParallelMessageQueue`, so ordering guarantees are handled by those
3822
- * implementations directly rather than at the `ParallelMessageQueue` level.
3823
- *
3824
- * Messages with the same ordering key will never be processed concurrently
3825
- * by different workers, ensuring sequential processing within each key.
3826
- * Messages with different ordering keys (or no ordering key) can still be
3827
- * processed in parallel.
3828
- *
3829
- * @since 1.0.0
3830
- */
3831
- var ParallelMessageQueue = class ParallelMessageQueue {
3832
- queue;
3833
- workers;
3834
- /**
3835
- * Inherits the native retry capability from the wrapped queue.
3836
- * @since 1.7.0
3837
- */
3838
- nativeRetrial;
3839
- /**
3840
- * Inherits the native deduplication capability from the wrapped queue.
3841
- * @since 2.4.0
3842
- */
3843
- nativeDeduplication;
3844
- getDepth;
3845
- /**
3846
- * Tracks which ordering keys are currently being processed to ensure
3847
- * sequential processing for messages with the same key.
3848
- */
3849
- #processingKeys = /* @__PURE__ */ new Set();
3850
- /**
3851
- * Pending messages waiting for their ordering key to become available.
3852
- */
3853
- #pendingMessages = [];
3854
- /**
3855
- * Constructs a new {@link ParallelMessageQueue} with the given queue and
3856
- * number of workers.
3857
- * @param queue The message queue to use under the hood. Note that
3858
- * {@link ParallelMessageQueue} cannot be nested.
3859
- * @param workers The number of workers to process messages in parallel.
3860
- * @throws {TypeError} If the given queue is an instance of
3861
- * {@link ParallelMessageQueue}.
3862
- */
3863
- constructor(queue, workers) {
3864
- if (queue instanceof ParallelMessageQueue) throw new TypeError("Cannot nest ParallelMessageQueue.");
3865
- this.queue = queue;
3866
- this.workers = workers;
3867
- this.nativeRetrial = queue.nativeRetrial;
3868
- this.nativeDeduplication = queue.nativeDeduplication;
3869
- if (queue.getDepth != null) this.getDepth = () => queue.getDepth();
3870
- }
3871
- enqueue(message, options) {
3872
- return this.queue.enqueue(message, options);
3873
- }
3874
- async enqueueMany(messages, options) {
3875
- if (this.queue.enqueueMany == null) {
3876
- if (messages.length === 1) {
3877
- await this.queue.enqueue(messages[0], options);
3878
- return;
3879
- }
3880
- 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.");
3881
- const errors = (await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)))).filter((r) => r.status === "rejected").map((r) => r.reason);
3882
- if (errors.length > 1) throw new AggregateError(errors, "Failed to enqueue messages.");
3883
- else if (errors.length === 1) throw errors[0];
3884
- return;
3885
- }
3886
- await this.queue.enqueueMany(messages, options);
3887
- }
3888
- /**
3889
- * Extracts ordering key from a message if present.
3890
- *
3891
- * This method only works for queue implementations that deliver messages
3892
- * in the wrapper format with `__fedify_ordering_key__` property. Currently,
3893
- * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
3894
- *
3895
- * For other queue implementations (`InProcessMessageQueue`,
3896
- * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
3897
- * `AmqpMessageQueue`), messages are delivered as raw payloads without the
3898
- * wrapper, so the ordering key cannot be detected here. Those
3899
- * implementations handle ordering guarantees internally.
3900
- */
3901
- #extractOrderingKey(message) {
3902
- if (message != null && typeof message === "object") {
3903
- if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
3904
- }
3905
- }
3906
- listen(handler, options = {}) {
3907
- const workers = /* @__PURE__ */ new Map();
3908
- return this.queue.listen(async (message) => {
3909
- while (workers.size >= this.workers) {
3910
- const consumedId = await Promise.any(workers.values());
3911
- workers.delete(consumedId);
3912
- }
3913
- const workerId = crypto.randomUUID();
3914
- const orderingKey = this.#extractOrderingKey(message);
3915
- if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
3916
- this.#pendingMessages.push({
3917
- message,
3918
- orderingKey,
3919
- resolve
3920
- });
3921
- });
3922
- if (orderingKey != null) this.#processingKeys.add(orderingKey);
3923
- const promise = this.#work(workerId, handler, message, orderingKey);
3924
- workers.set(workerId, promise);
3925
- }, options);
3926
- }
3927
- async #work(workerId, handler, message, orderingKey) {
3928
- await this.#sleep(0);
3929
- try {
3930
- await handler(message);
3931
- } finally {
3932
- if (orderingKey != null) {
3933
- this.#processingKeys.delete(orderingKey);
3934
- const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
3935
- if (pendingIdx >= 0) this.#pendingMessages.splice(pendingIdx, 1)[0].resolve();
3936
- }
3937
- }
3938
- return workerId;
3939
- }
3940
- #sleep(ms) {
3941
- return new Promise((resolve) => setTimeout(resolve, ms));
3942
- }
3943
- };
3944
- //#endregion
3945
- //#region src/federation/tasks/enqueue.ts
3946
- /**
3947
- * The enqueue pipeline for custom background tasks. `ContextImpl.enqueueTask`
3948
- * and `ContextImpl.enqueueTaskMany` delegate to {@link enqueueTasks} so the
3949
- * handle validation, deduplication planning, payload encoding, and queue
3950
- * dispatch live in one cohesive place instead of one oversized method.
3951
- *
3952
- * @module
3953
- */
3954
- /**
3955
- * Validates the task handle, plans deduplication, encodes every payload, then
3956
- * dispatches the resulting messages to the queue. A single item flows through
3957
- * the same pipeline as a batch, so {@link Context.enqueueTask} and
3958
- * {@link Context.enqueueTaskMany} share one implementation.
3959
- * @template TContextData The context data to pass to the {@link Context}.
3960
- * @template TData The type of the task payload, inferred from the task's schema.
3961
- * @param ctx The enqueueing dependencies assembled by `ContextImpl`.
3962
- * @param task The handle returned by `defineTask()`.
3963
- * @param items The payloads to enqueue, in order.
3964
- * @param options The enqueue options governing delay, ordering, and dedup.
3965
- * @internal
3966
- */
3967
- const enqueueTasks = (ctx) => async function(task, items, options) {
3968
- const def = ctx.federation.taskDefinitions.get(task.name);
3969
- 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().`);
3970
- const queue = ctx.federation.resolveTaskQueue(task.name);
3971
- if (queue == null) throw new TypeError("No message queue is configured for tasks; pass `queue` to createFederation() or to defineTask().");
3972
- if (items.length < 1) return;
3973
- const plan = planDeduplication(ctx, queue, task.name, options, items.length);
3974
- const messages = await Promise.all(items.map(encodeTaskMessage(ctx.codec, ctx.origin, task, options)));
3975
- const claim = await claimDeduplication(ctx, plan, task.name);
3976
- if (!claim.proceed) return;
3977
- if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
3978
- try {
3979
- await dispatch(queue, messages, {
3980
- delay: getDurationIfDefined(options.delay),
3981
- orderingKey: options.orderingKey,
3982
- deduplicationKey: claim.forwardedDeduplicationKey
3983
- });
3984
- } catch (error) {
3985
- if (claim.rollback != null) try {
3986
- await claim.rollback();
3987
- } catch (rollbackError) {
3988
- logger$1.warn("Failed to roll back the deduplication marker for task {taskName} after a failed enqueue; it will expire by TTL. {rollbackError}", {
3989
- taskName: task.name,
3990
- rollbackError
3991
- });
3992
- }
3993
- throw error;
3994
- }
3995
- };
3996
- const getDurationIfDefined = (item) => item == null ? void 0 : Temporal.Duration.from(item);
3997
- /**
3998
- * Decides how a `deduplicationKey` (if any) is honored: forwarded to a native
3999
- * queue, claimed via `cas`, or—when neither is available—dropped or rejected
4000
- * per the federation's `taskDeduplicationFallback`. Throws the fail-fast
4001
- * `TypeError`s so they precede the encode.
4002
- */
4003
- function planDeduplication(ctx, queue, taskName, options, itemCount) {
4004
- if (options.deduplicationKey == null) return { kind: "none" };
4005
- const key = options.deduplicationKey;
4006
- const native = queue.nativeDeduplication === true;
4007
- const canCas = ctx.federation.kv.cas != null;
4008
- const canBatchAtomically = queue.enqueueMany != null && !(queue instanceof ParallelMessageQueue && queue.queue.enqueueMany == null);
4009
- 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().`);
4010
- if (native) return {
4011
- kind: "native",
4012
- key
4013
- };
4014
- if (canCas) return {
4015
- kind: "cas",
4016
- key
4017
- };
4018
- 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.");
4019
- return {
4020
- kind: "open",
4021
- key
4022
- };
4023
- }
4024
- /**
4025
- * Executes the planned deduplication once the payloads are encoded. A native
4026
- * plan forwards its key to the queue; a `cas` plan claims the marker and stops
4027
- * the enqueue when it loses the race; an `open` plan logs and proceeds.
4028
- * @returns Whether to proceed, and the key (if any) to forward to the queue.
4029
- */
4030
- async function claimDeduplication(ctx, plan, taskName) {
4031
- switch (plan.kind) {
4032
- case "native": return {
4033
- proceed: true,
4034
- forwardedDeduplicationKey: plan.key
4035
- };
4036
- case "cas": {
4037
- const cacheKey = [...ctx.federation.kvPrefixes.taskDeduplication, plan.key];
4038
- const token = crypto.randomUUID();
4039
- if (!await ctx.federation.kv.cas(cacheKey, void 0, token, { ttl: ctx.federation.taskDeduplicationTtl })) return { proceed: false };
4040
- return {
4041
- proceed: true,
4042
- rollback: async () => {
4043
- await ctx.federation.kv.cas(cacheKey, token, void 0);
4044
- }
4045
- };
4046
- }
4047
- 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\").", {
4048
- deduplicationKey: plan.key,
4049
- taskName
4050
- });
4051
- case "none": return { proceed: true };
4052
- default: throw new TypeError(`Unknown deduplication plan: ${JSON.stringify(plan)}`);
4053
- }
4054
- }
4055
- /**
4056
- * Sends the encoded messages to the queue, picking the bulk path when the
4057
- * queue implements `enqueueMany` and otherwise fanning out parallel single
4058
- * enqueues. The fan-out drops `deduplicationKey`, which is only ever set for a
4059
- * native plan that the bulk paths already cover.
4060
- */
4061
- async function dispatch(queue, messages, options) {
4062
- if (messages.length === 1) await queue.enqueue(messages[0], options);
4063
- else if (queue.enqueueMany != null) await queue.enqueueMany(messages, options);
4064
- else {
4065
- const fanoutOptions = {
4066
- delay: options.delay,
4067
- orderingKey: options.orderingKey
4068
- };
4069
- await Promise.all(messages.map((m) => queue.enqueue(m, fanoutOptions)));
4070
- }
4071
- }
4072
- /**
4073
- * Builds the per-payload encoder: validates and serializes the payload, then
4074
- * stamps the message envelope with a fresh id, the context's origin, and the
4075
- * active trace context. Curried so the batch encode reuses one bound encoder.
4076
- */
4077
- const encodeTaskMessage = (codec, origin, task, options) => async (data) => {
4078
- const encoded = await codec.encode(task.schema, data);
4079
- const carrier = {};
4080
- propagation.inject(context.active(), carrier);
4081
- return {
4082
- type: "task",
4083
- id: crypto.randomUUID(),
4084
- baseUrl: origin,
4085
- taskName: task.name,
4086
- data: encoded,
4087
- started: Temporal.Now.instant().toString(),
4088
- attempt: 0,
4089
- orderingKey: options.orderingKey,
4090
- traceContext: carrier
4091
- };
4092
- };
4093
- const logger$1 = getLogger([
4094
- "fedify",
4095
- "federation",
4096
- "task"
4097
- ]);
4098
- //#endregion
4099
3555
  //#region src/federation/webfinger.ts
4100
3556
  const logger = getLogger([
4101
3557
  "fedify",
@@ -4429,8 +3885,9 @@ var FederationImpl = class extends FederationBuilderImpl {
4429
3885
  inboxQueue;
4430
3886
  outboxQueue;
4431
3887
  fanoutQueue;
4432
- taskQueue;
4433
- startedQueues;
3888
+ inboxQueueStarted;
3889
+ outboxQueueStarted;
3890
+ fanoutQueueStarted;
4434
3891
  manuallyStartQueue;
4435
3892
  origin;
4436
3893
  documentLoaderFactory;
@@ -4444,10 +3901,6 @@ var FederationImpl = class extends FederationBuilderImpl {
4444
3901
  skipSignatureVerification;
4445
3902
  outboxRetryPolicy;
4446
3903
  inboxRetryPolicy;
4447
- taskRetryPolicy;
4448
- taskQueueResolution;
4449
- taskDeduplicationTtl;
4450
- taskDeduplicationFallback;
4451
3904
  circuitBreaker;
4452
3905
  activityTransformers;
4453
3906
  _tracerProvider;
@@ -4487,24 +3940,20 @@ var FederationImpl = class extends FederationBuilderImpl {
4487
3940
  httpMessageSignaturesSpec: ["_fedify", "httpMessageSignaturesSpec"],
4488
3941
  acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
4489
3942
  circuitBreaker: ["_fedify", "circuit"],
4490
- taskDeduplication: ["_fedify", "taskDeduplication"],
4491
3943
  ...options.kvPrefixes ?? {}
4492
3944
  };
4493
3945
  if (options.queue == null) {
4494
3946
  this.inboxQueue = void 0;
4495
3947
  this.outboxQueue = void 0;
4496
3948
  this.fanoutQueue = void 0;
4497
- this.taskQueue = void 0;
4498
3949
  } else if ("enqueue" in options.queue && "listen" in options.queue) {
4499
3950
  this.inboxQueue = options.queue;
4500
3951
  this.outboxQueue = options.queue;
4501
3952
  this.fanoutQueue = options.queue;
4502
- this.taskQueue = void 0;
4503
3953
  } else {
4504
3954
  this.inboxQueue = options.queue.inbox;
4505
3955
  this.outboxQueue = options.queue.outbox;
4506
3956
  this.fanoutQueue = options.queue.fanout;
4507
- this.taskQueue = options.queue.task;
4508
3957
  }
4509
3958
  if (options.circuitBreaker !== false && this.outboxQueue != null) {
4510
3959
  this.circuitBreaker = new CircuitBreaker({
@@ -4525,7 +3974,9 @@ var FederationImpl = class extends FederationBuilderImpl {
4525
3974
  ]).warn("The configured key-value store does not support CAS; outbound delivery circuit breaker updates may race under concurrent workers.");
4526
3975
  }
4527
3976
  }
4528
- this.startedQueues = /* @__PURE__ */ new Set();
3977
+ this.inboxQueueStarted = false;
3978
+ this.outboxQueueStarted = false;
3979
+ this.fanoutQueueStarted = false;
4529
3980
  this.manuallyStartQueue = options.manuallyStartQueue ?? false;
4530
3981
  if (options.origin != null) if (typeof options.origin === "string") {
4531
3982
  if (!URL.canParse(options.origin) || !options.origin.match(/^https?:\/\//)) throw new TypeError(`Invalid origin: ${JSON.stringify(options.origin)}`);
@@ -4607,10 +4058,6 @@ var FederationImpl = class extends FederationBuilderImpl {
4607
4058
  this.inboxChallengePolicy = options.inboxChallengePolicy;
4608
4059
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
4609
4060
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
4610
- this.taskRetryPolicy = options.taskRetryPolicy ?? createExponentialBackoffPolicy();
4611
- this.taskQueueResolution = options.taskQueueResolution ?? "fallback";
4612
- this.taskDeduplicationTtl = Temporal.Duration.from(options.taskDeduplicationTtl ?? { hours: 1 });
4613
- this.taskDeduplicationFallback = options.taskDeduplicationFallback ?? "open";
4614
4061
  this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
4615
4062
  this._tracerProvider = options.tracerProvider;
4616
4063
  if (benchmarkMode) {
@@ -4659,33 +4106,28 @@ var FederationImpl = class extends FederationBuilderImpl {
4659
4106
  _getTracer() {
4660
4107
  return this.tracerProvider.getTracer(name, version);
4661
4108
  }
4662
- resolveTaskQueue(taskName) {
4663
- const resolved = this.taskDefinitions.get(taskName)?.queue ?? this.taskQueue;
4664
- if (resolved != null) return resolved;
4665
- return this.taskQueueResolution === "strict" ? void 0 : this.outboxQueue;
4666
- }
4667
4109
  async _startQueueInternal(ctxData, signal, queue) {
4668
- const taskQueue = this.taskQueue ?? (this.taskQueueResolution === "fallback" ? this.outboxQueue : void 0);
4669
- const customQueues = this.taskDefinitions.values().map((def) => ["task", def.queue]);
4670
- const targets = [
4671
- ["inbox", this.inboxQueue],
4672
- ["outbox", this.outboxQueue],
4673
- ["fanout", this.fanoutQueue],
4674
- ["task", taskQueue],
4675
- ...customQueues
4676
- ];
4110
+ if (this.inboxQueue == null && this.outboxQueue == null) return;
4677
4111
  const logger = getLogger([
4678
4112
  "fedify",
4679
4113
  "federation",
4680
4114
  "queue"
4681
4115
  ]);
4682
4116
  const promises = [];
4683
- for (const [role, target] of targets) {
4684
- if (target == null || !(queue == null || queue === role)) continue;
4685
- if (this.startedQueues.has(target)) continue;
4686
- this.startedQueues.add(target);
4687
- logger.debug("Starting a {role} queue worker.", { role });
4688
- promises.push(target.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4117
+ if (this.inboxQueue != null && (queue == null || queue === "inbox") && !this.inboxQueueStarted) {
4118
+ logger.debug("Starting an inbox task worker.");
4119
+ this.inboxQueueStarted = true;
4120
+ promises.push(this.inboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4121
+ }
4122
+ if (this.outboxQueue != null && this.outboxQueue !== this.inboxQueue && (queue == null || queue === "outbox") && !this.outboxQueueStarted) {
4123
+ logger.debug("Starting an outbox task worker.");
4124
+ this.outboxQueueStarted = true;
4125
+ promises.push(this.outboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4126
+ }
4127
+ if (this.fanoutQueue != null && this.fanoutQueue !== this.inboxQueue && this.fanoutQueue !== this.outboxQueue && (queue == null || queue === "fanout") && !this.fanoutQueueStarted) {
4128
+ logger.debug("Starting a fanout task worker.");
4129
+ this.fanoutQueueStarted = true;
4130
+ promises.push(this.fanoutQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
4689
4131
  }
4690
4132
  await Promise.all(promises);
4691
4133
  }
@@ -4808,7 +4250,7 @@ var FederationImpl = class extends FederationBuilderImpl {
4808
4250
  }
4809
4251
  });
4810
4252
  });
4811
- } else if (message.type === "task") await this.#listenTaskMessage(contextData, message);
4253
+ }
4812
4254
  });
4813
4255
  }
4814
4256
  async #listenFanoutMessage(data, message) {
@@ -5372,77 +4814,6 @@ var FederationImpl = class extends FederationBuilderImpl {
5372
4814
  listenerSpan.end();
5373
4815
  });
5374
4816
  }
5375
- async #listenTaskMessage(contextData, message) {
5376
- const logger = getLogger([
5377
- "fedify",
5378
- "federation",
5379
- "task"
5380
- ]);
5381
- const def = this.taskDefinitions.get(message.taskName);
5382
- if (def == null) {
5383
- logger.warn("Received a custom task {taskName} with no registered handler; dropping.", { taskName: message.taskName });
5384
- return;
5385
- }
5386
- const context = this.#createContext(new URL(message.baseUrl), contextData);
5387
- let data;
5388
- try {
5389
- data = await context.codec.decode(def.schema, message.data);
5390
- } catch (error) {
5391
- logger.error("Custom task {taskName} payload could not be decoded or validated; dropping:\n{error}", {
5392
- taskName: message.taskName,
5393
- error
5394
- });
5395
- return;
5396
- }
5397
- try {
5398
- await def.handler(context, data);
5399
- } catch (error) {
5400
- if (def.onError != null) try {
5401
- await def.onError(context, error, data);
5402
- } catch (onErrorError) {
5403
- logger.error("onError for custom task {taskName} threw:\n{error}", {
5404
- taskName: message.taskName,
5405
- error: onErrorError
5406
- });
5407
- }
5408
- const queue = this.resolveTaskQueue(def.name);
5409
- if (queue?.nativeRetrial) throw error;
5410
- const retryPolicy = def.retryPolicy ?? this.taskRetryPolicy;
5411
- let elapsedTime = Temporal.Duration.from({ seconds: 0 });
5412
- try {
5413
- elapsedTime = Temporal.Instant.from(message.started).until(Temporal.Now.instant());
5414
- } catch (parseError) {
5415
- logger.error("Custom task {taskName} has an unparsable started time {started}; treating elapsedTime as zero:\n{error}", {
5416
- taskName: message.taskName,
5417
- started: message.started,
5418
- error: parseError
5419
- });
5420
- }
5421
- const delay = retryPolicy({
5422
- elapsedTime,
5423
- attempts: message.attempt
5424
- });
5425
- if (delay != null && queue != null) {
5426
- logger.error("Custom task {taskName} failed (attempt #{attempt}); retry...:\n{error}", {
5427
- taskName: message.taskName,
5428
- attempt: message.attempt,
5429
- error
5430
- });
5431
- const retryMessage = {
5432
- ...message,
5433
- attempt: message.attempt + 1
5434
- };
5435
- await queue.enqueue(retryMessage, {
5436
- delay: clampNegativeDelay(delay),
5437
- orderingKey: message.orderingKey
5438
- });
5439
- } else logger.error("Custom task {taskName} failed after {attempt} attempts; giving up:\n{error}", {
5440
- taskName: message.taskName,
5441
- attempt: message.attempt,
5442
- error
5443
- });
5444
- }
5445
- }
5446
4817
  startQueue(contextData, options = {}) {
5447
4818
  return this._startQueueInternal(contextData, options.signal, options.queue);
5448
4819
  }
@@ -6027,7 +5398,6 @@ var ContextImpl = class ContextImpl {
6027
5398
  documentLoader;
6028
5399
  contextLoader;
6029
5400
  invokedFromActorKeyPairsDispatcher;
6030
- #codec;
6031
5401
  constructor({ url, federation, data, documentLoader, contextLoader, invokedFromActorKeyPairsDispatcher }) {
6032
5402
  this.url = url;
6033
5403
  this.federation = federation;
@@ -6036,18 +5406,6 @@ var ContextImpl = class ContextImpl {
6036
5406
  this.contextLoader = contextLoader;
6037
5407
  this.invokedFromActorKeyPairsDispatcher = invokedFromActorKeyPairsDispatcher;
6038
5408
  }
6039
- /**
6040
- * A {@link TaskCodec} bound to this context's loaders, used to encode
6041
- * and decode custom task payloads. Lazily created and cached so a context
6042
- * that never enqueues or dispatches a task pays nothing.
6043
- * @internal
6044
- */
6045
- get codec() {
6046
- return this.#codec ??= new TaskCodec(this);
6047
- }
6048
- get #enqueueTasks() {
6049
- return enqueueTasks(this);
6050
- }
6051
5409
  clone(data) {
6052
5410
  return new ContextImpl({
6053
5411
  url: this.url,
@@ -6366,12 +5724,6 @@ var ContextImpl = class ContextImpl {
6366
5724
  allowPrivateAddress: this.federation.allowPrivateAddress
6367
5725
  });
6368
5726
  }
6369
- async enqueueTask(task, data, options = {}) {
6370
- await this.#enqueueTasks(task, [data], options);
6371
- }
6372
- async enqueueTaskMany(task, payloads, options = {}) {
6373
- await this.#enqueueTasks(task, payloads, options);
6374
- }
6375
5727
  sendActivity(sender, recipients, activity, options = {}) {
6376
5728
  return this.tracerProvider.getTracer(name, version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
6377
5729
  kind: this.federation.outboxQueue == null || options.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
@@ -7082,4 +6434,4 @@ function getRequestId(request) {
7082
6434
  return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
7083
6435
  }
7084
6436
  //#endregion
7085
- export { ParallelMessageQueue as a, respondWithObject as c, digest as d, CircuitBreaker as f, createFederationBuilder as h, InProcessMessageQueue as i, respondWithObjectIfAcceptable as l, parseCircuitBreakerKvState as m, middleware_exports as n, createExponentialBackoffPolicy as o, normalizeCircuitBreakerOptions as p, handleWebFinger as r, SendActivityError as s, createFederation as t, buildCollectionSynchronizationHeader as u };
6437
+ export { SendActivityError as a, buildCollectionSynchronizationHeader as c, normalizeCircuitBreakerOptions as d, parseCircuitBreakerKvState as f, createExponentialBackoffPolicy as i, digest as l, middleware_exports as n, respondWithObject as o, createFederationBuilder as p, handleWebFinger as r, respondWithObjectIfAcceptable as s, createFederation as t, CircuitBreaker as u };