@fedify/fedify 2.0.0-dev.241 → 2.0.0-dev.279
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-CR-ZQZJR.js → builder-B7WWCOdc.js} +3 -3
- package/dist/compat/mod.d.cts +2 -2
- package/dist/compat/mod.d.ts +2 -2
- package/dist/compat/transformers.test.js +11 -11
- package/dist/{context-C7vzWilY.d.ts → context-BNNWbaZL.d.ts} +51 -8
- package/dist/{context-Bns6uTJq.js → context-CZ5llAss.js} +12 -12
- package/dist/{context-CrB9RFy5.d.cts → context-D7JEVvXJ.d.cts} +51 -8
- package/dist/{deno-DOeSTfhz.js → deno-fe0u4LiE.js} +8 -2
- package/dist/{docloader-C1AGOQQm.js → docloader-C9Dmxf-K.js} +2 -2
- package/dist/federation/builder.test.js +3 -3
- package/dist/federation/handler.test.js +12 -12
- package/dist/federation/idempotency.test.js +11 -11
- package/dist/federation/inbox.test.js +2 -2
- package/dist/federation/middleware.test.js +63 -11
- package/dist/federation/mod.cjs +5 -5
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +5 -5
- package/dist/federation/mq.test.js +162 -10
- package/dist/federation/send.test.js +5 -5
- package/dist/federation/webfinger.test.js +12 -12
- package/dist/{federation-B431K2gm.cjs → federation-CE0CJ_0G.cjs} +94 -10
- package/dist/{federation-BbZwNNWj.js → federation-D6FVaeAR.js} +94 -10
- package/dist/{http-i8VdY2df.js → http-7r8B3dF_.js} +8 -2
- package/dist/{http-C1LrW2D3.cjs → http-DMi5iyiI.cjs} +8 -2
- package/dist/{http-DGkEl4mw.js → http-oCBlFLKT.js} +2 -2
- package/dist/{inbox-Cp87HRVs.js → inbox-Dm1rfkdg.js} +1 -1
- package/dist/{key-3_vbTfr8.js → key-BGzsKfpZ.js} +1 -1
- package/dist/{kv-cache-DOgKf8lH.cjs → kv-cache-C0AvpI7U.cjs} +1 -1
- package/dist/{kv-cache-B4CFgxYq.js → kv-cache-CETRZwoP.js} +1 -1
- package/dist/{ld-BQxhMgOJ.js → ld-BaO1-A5Y.js} +2 -2
- package/dist/{middleware-C52jAnyA.js → middleware-BDJNulB_.js} +4 -4
- package/dist/{middleware-DbwwSEOr.js → middleware-BlOT9luD.js} +65 -22
- package/dist/{middleware-D4Eexdig.cjs → middleware-C0Vj7vNo.cjs} +59 -16
- package/dist/{middleware-DZ-OdkE6.js → middleware-CnGBoMop.js} +11 -11
- package/dist/middleware-DT3JkH-g.cjs +12 -0
- package/dist/{middleware-ixha9m0b.js → middleware-R0w-WauH.js} +59 -16
- package/dist/{mod-0qnPv4EC.d.cts → mod-054TSUXs.d.cts} +1 -1
- package/dist/{mod-C3SOvTD1.d.ts → mod-BhDb3RBS.d.ts} +1 -1
- package/dist/{mod-D6pS5_xJ.d.cts → mod-C74sRHP8.d.cts} +1 -1
- package/dist/{mod-waqu-BL_.d.ts → mod-YpmzboJc.d.ts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +3 -3
- package/dist/mod.d.ts +3 -3
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.js +12 -12
- package/dist/{owner-Bod8mCab.js → owner-BqIhDQWW.js} +1 -1
- package/dist/{proof-vG4ePLF2.js → proof-Cg6AAAWI.js} +1 -1
- package/dist/{proof--60Adidu.js → proof-DoHt7qrS.js} +2 -2
- package/dist/{proof-C8MAHWxQ.cjs → proof-vSvvLbTh.cjs} +1 -1
- package/dist/{send-B3fWpYN9.js → send-CO2ZYT96.js} +2 -2
- package/dist/sig/http.test.js +3 -3
- package/dist/sig/key.test.js +2 -2
- package/dist/sig/ld.test.js +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.js +3 -3
- package/dist/sig/proof.test.js +3 -3
- package/dist/testing/mod.d.ts +23 -7
- package/dist/testing/mod.js +1 -1
- package/dist/utils/docloader.test.js +4 -4
- package/dist/utils/mod.cjs +2 -2
- package/dist/utils/mod.js +2 -2
- package/package.json +10 -8
- package/dist/middleware-XsmW-sAa.cjs +0 -12
package/dist/federation/mod.cjs
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
4
4
|
|
|
5
5
|
require('../transformers-BjBg6Lag.cjs');
|
|
6
|
-
require('../http-
|
|
7
|
-
const require_middleware = require('../middleware-
|
|
8
|
-
require('../proof-
|
|
9
|
-
const require_federation = require('../federation-
|
|
6
|
+
require('../http-DMi5iyiI.cjs');
|
|
7
|
+
const require_middleware = require('../middleware-C0Vj7vNo.cjs');
|
|
8
|
+
require('../proof-vSvvLbTh.cjs');
|
|
9
|
+
const require_federation = require('../federation-CE0CJ_0G.cjs');
|
|
10
10
|
require('../types-B6z6CqIz.cjs');
|
|
11
|
-
require('../kv-cache-
|
|
11
|
+
require('../kv-cache-C0AvpI7U.cjs');
|
|
12
12
|
|
|
13
13
|
exports.InProcessMessageQueue = require_federation.InProcessMessageQueue;
|
|
14
14
|
exports.MemoryKvStore = require_federation.MemoryKvStore;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../client-by-PEGAJ.cjs";
|
|
2
2
|
import "../http-ClB3pLcL.cjs";
|
|
3
3
|
import "../owner-C-zfmVAD.cjs";
|
|
4
|
-
import { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable } from "../context-
|
|
4
|
+
import { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable } from "../context-D7JEVvXJ.cjs";
|
|
5
5
|
import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore } from "../kv-B4vFhIYL.cjs";
|
|
6
|
-
import { WebFingerHandlerParameters, handleWebFinger } from "../mod-
|
|
6
|
+
import { WebFingerHandlerParameters, handleWebFinger } from "../mod-054TSUXs.cjs";
|
|
7
7
|
export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
|
package/dist/federation/mod.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
3
3
|
import "../client-CUTUGgvJ.js";
|
|
4
4
|
import "../http-DLBDPal9.js";
|
|
5
5
|
import "../owner-BgI8C-VY.js";
|
|
6
|
-
import { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable } from "../context-
|
|
6
|
+
import { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable } from "../context-BNNWbaZL.js";
|
|
7
7
|
import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore } from "../kv-CYySNrsn.js";
|
|
8
|
-
import { WebFingerHandlerParameters, handleWebFinger } from "../mod-
|
|
8
|
+
import { WebFingerHandlerParameters, handleWebFinger } from "../mod-BhDb3RBS.js";
|
|
9
9
|
export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CreateFederationOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
|
package/dist/federation/mod.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
|
|
5
5
|
import "../transformers-N_ip_y4P.js";
|
|
6
|
-
import "../http-
|
|
7
|
-
import { Router, RouterError, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable } from "../middleware-
|
|
8
|
-
import "../proof-
|
|
9
|
-
import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue } from "../federation-
|
|
6
|
+
import "../http-7r8B3dF_.js";
|
|
7
|
+
import { Router, RouterError, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable } from "../middleware-R0w-WauH.js";
|
|
8
|
+
import "../proof-Cg6AAAWI.js";
|
|
9
|
+
import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue } from "../federation-D6FVaeAR.js";
|
|
10
10
|
import "../types-8l28uC8o.js";
|
|
11
|
-
import "../kv-cache-
|
|
11
|
+
import "../kv-cache-CETRZwoP.js";
|
|
12
12
|
|
|
13
13
|
export { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
|
|
@@ -26,6 +26,11 @@ var InProcessMessageQueue = class {
|
|
|
26
26
|
#monitors;
|
|
27
27
|
#pollIntervalMs;
|
|
28
28
|
/**
|
|
29
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
30
|
+
* sequential processing for messages with the same key.
|
|
31
|
+
*/
|
|
32
|
+
#processingKeys;
|
|
33
|
+
/**
|
|
29
34
|
* In-process message queue does not provide native retry mechanisms.
|
|
30
35
|
* @since 1.7.0
|
|
31
36
|
*/
|
|
@@ -38,6 +43,7 @@ var InProcessMessageQueue = class {
|
|
|
38
43
|
this.#messages = [];
|
|
39
44
|
this.#monitors = {};
|
|
40
45
|
this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
46
|
+
this.#processingKeys = /* @__PURE__ */ new Set();
|
|
41
47
|
}
|
|
42
48
|
enqueue(message, options) {
|
|
43
49
|
const delay$1 = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
@@ -48,7 +54,11 @@ var InProcessMessageQueue = class {
|
|
|
48
54
|
}), delay$1);
|
|
49
55
|
return Promise.resolve();
|
|
50
56
|
}
|
|
51
|
-
|
|
57
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
58
|
+
this.#messages.push({
|
|
59
|
+
message,
|
|
60
|
+
orderingKey
|
|
61
|
+
});
|
|
52
62
|
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
53
63
|
return Promise.resolve();
|
|
54
64
|
}
|
|
@@ -62,18 +72,29 @@ var InProcessMessageQueue = class {
|
|
|
62
72
|
}), delay$1);
|
|
63
73
|
return Promise.resolve();
|
|
64
74
|
}
|
|
65
|
-
|
|
75
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
76
|
+
for (const message of messages) this.#messages.push({
|
|
77
|
+
message,
|
|
78
|
+
orderingKey
|
|
79
|
+
});
|
|
66
80
|
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
67
81
|
return Promise.resolve();
|
|
68
82
|
}
|
|
69
83
|
async listen(handler, options = {}) {
|
|
70
84
|
const signal = options.signal;
|
|
71
85
|
while (signal == null || !signal.aborted) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
|
|
87
|
+
if (idx >= 0) {
|
|
88
|
+
const queued = this.#messages.splice(idx, 1)[0];
|
|
89
|
+
const { message, orderingKey } = queued;
|
|
90
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
91
|
+
try {
|
|
92
|
+
await handler(message);
|
|
93
|
+
} finally {
|
|
94
|
+
if (orderingKey != null) this.#processingKeys.delete(orderingKey);
|
|
95
|
+
}
|
|
96
|
+
} else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
|
|
97
|
+
else await this.#wait(10, signal);
|
|
77
98
|
}
|
|
78
99
|
}
|
|
79
100
|
#wait(ms, signal) {
|
|
@@ -105,6 +126,21 @@ var InProcessMessageQueue = class {
|
|
|
105
126
|
* for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
|
|
106
127
|
* workloads.
|
|
107
128
|
*
|
|
129
|
+
* When using `ParallelMessageQueue`, the ordering guarantee is preserved
|
|
130
|
+
* *only if* the underlying queue implementation delivers messages in a wrapper
|
|
131
|
+
* format that includes the `__fedify_ordering_key__` property. Currently,
|
|
132
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
133
|
+
* For other queue implementations (e.g., `InProcessMessageQueue`,
|
|
134
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
135
|
+
* `AmqpMessageQueue`), the ordering key cannot be detected by
|
|
136
|
+
* `ParallelMessageQueue`, so ordering guarantees are handled by those
|
|
137
|
+
* implementations directly rather than at the `ParallelMessageQueue` level.
|
|
138
|
+
*
|
|
139
|
+
* Messages with the same ordering key will never be processed concurrently
|
|
140
|
+
* by different workers, ensuring sequential processing within each key.
|
|
141
|
+
* Messages with different ordering keys (or no ordering key) can still be
|
|
142
|
+
* processed in parallel.
|
|
143
|
+
*
|
|
108
144
|
* @since 1.0.0
|
|
109
145
|
*/
|
|
110
146
|
var ParallelMessageQueue = class ParallelMessageQueue {
|
|
@@ -116,6 +152,15 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
116
152
|
*/
|
|
117
153
|
nativeRetrial;
|
|
118
154
|
/**
|
|
155
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
156
|
+
* sequential processing for messages with the same key.
|
|
157
|
+
*/
|
|
158
|
+
#processingKeys = /* @__PURE__ */ new Set();
|
|
159
|
+
/**
|
|
160
|
+
* Pending messages waiting for their ordering key to become available.
|
|
161
|
+
*/
|
|
162
|
+
#pendingMessages = [];
|
|
163
|
+
/**
|
|
119
164
|
* Constructs a new {@link ParallelMessageQueue} with the given queue and
|
|
120
165
|
* number of workers.
|
|
121
166
|
* @param queue The message queue to use under the hood. Note that
|
|
@@ -143,6 +188,25 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
143
188
|
}
|
|
144
189
|
await this.queue.enqueueMany(messages, options);
|
|
145
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Extracts ordering key from a message if present.
|
|
193
|
+
*
|
|
194
|
+
* This method only works for queue implementations that deliver messages
|
|
195
|
+
* in the wrapper format with `__fedify_ordering_key__` property. Currently,
|
|
196
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
197
|
+
*
|
|
198
|
+
* For other queue implementations (`InProcessMessageQueue`,
|
|
199
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
200
|
+
* `AmqpMessageQueue`), messages are delivered as raw payloads without the
|
|
201
|
+
* wrapper, so the ordering key cannot be detected here. Those
|
|
202
|
+
* implementations handle ordering guarantees internally.
|
|
203
|
+
*/
|
|
204
|
+
#extractOrderingKey(message) {
|
|
205
|
+
if (message != null && typeof message === "object") {
|
|
206
|
+
if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
|
|
207
|
+
}
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
146
210
|
listen(handler, options = {}) {
|
|
147
211
|
const workers = /* @__PURE__ */ new Map();
|
|
148
212
|
return this.queue.listen(async (message) => {
|
|
@@ -151,13 +215,33 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
151
215
|
workers.delete(consumedId);
|
|
152
216
|
}
|
|
153
217
|
const workerId = crypto.randomUUID();
|
|
154
|
-
const
|
|
218
|
+
const orderingKey = this.#extractOrderingKey(message);
|
|
219
|
+
if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
|
|
220
|
+
this.#pendingMessages.push({
|
|
221
|
+
message,
|
|
222
|
+
orderingKey,
|
|
223
|
+
resolve
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
227
|
+
const promise = this.#work(workerId, handler, message, orderingKey);
|
|
155
228
|
workers.set(workerId, promise);
|
|
156
229
|
}, options);
|
|
157
230
|
}
|
|
158
|
-
async #work(workerId, handler, message) {
|
|
231
|
+
async #work(workerId, handler, message, orderingKey) {
|
|
159
232
|
await this.#sleep(0);
|
|
160
|
-
|
|
233
|
+
try {
|
|
234
|
+
await handler(message);
|
|
235
|
+
} finally {
|
|
236
|
+
if (orderingKey != null) {
|
|
237
|
+
this.#processingKeys.delete(orderingKey);
|
|
238
|
+
const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
|
|
239
|
+
if (pendingIdx >= 0) {
|
|
240
|
+
const pending = this.#pendingMessages.splice(pendingIdx, 1)[0];
|
|
241
|
+
pending.resolve();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
161
245
|
return workerId;
|
|
162
246
|
}
|
|
163
247
|
#sleep(ms) {
|
|
@@ -222,6 +306,74 @@ test("InProcessMessageQueue", async (t) => {
|
|
|
222
306
|
controller.abort();
|
|
223
307
|
await listening;
|
|
224
308
|
});
|
|
309
|
+
test("InProcessMessageQueue orderingKey", async (t) => {
|
|
310
|
+
const mq = new InProcessMessageQueue();
|
|
311
|
+
const orderTracker = {
|
|
312
|
+
keyA: [],
|
|
313
|
+
keyB: [],
|
|
314
|
+
noKey: []
|
|
315
|
+
};
|
|
316
|
+
const allMessages = [];
|
|
317
|
+
const controller = new AbortController();
|
|
318
|
+
const listening = mq.listen((message) => {
|
|
319
|
+
allMessages.push(message);
|
|
320
|
+
const trackKey = message.key ?? "noKey";
|
|
321
|
+
if (trackKey in orderTracker) orderTracker[trackKey].push(message.value);
|
|
322
|
+
}, controller);
|
|
323
|
+
await t.step("enqueue with ordering key", async () => {
|
|
324
|
+
await mq.enqueue({
|
|
325
|
+
key: "keyA",
|
|
326
|
+
value: 1
|
|
327
|
+
}, { orderingKey: "keyA" });
|
|
328
|
+
await mq.enqueue({
|
|
329
|
+
key: "keyB",
|
|
330
|
+
value: 1
|
|
331
|
+
}, { orderingKey: "keyB" });
|
|
332
|
+
await mq.enqueue({
|
|
333
|
+
key: "keyA",
|
|
334
|
+
value: 2
|
|
335
|
+
}, { orderingKey: "keyA" });
|
|
336
|
+
await mq.enqueue({
|
|
337
|
+
key: "keyB",
|
|
338
|
+
value: 2
|
|
339
|
+
}, { orderingKey: "keyB" });
|
|
340
|
+
await mq.enqueue({
|
|
341
|
+
key: "keyA",
|
|
342
|
+
value: 3
|
|
343
|
+
}, { orderingKey: "keyA" });
|
|
344
|
+
await mq.enqueue({
|
|
345
|
+
key: "keyB",
|
|
346
|
+
value: 3
|
|
347
|
+
}, { orderingKey: "keyB" });
|
|
348
|
+
await mq.enqueue({
|
|
349
|
+
key: null,
|
|
350
|
+
value: 1
|
|
351
|
+
});
|
|
352
|
+
await mq.enqueue({
|
|
353
|
+
key: null,
|
|
354
|
+
value: 2
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
await waitFor(() => allMessages.length >= 8, 3e4);
|
|
358
|
+
await t.step("verify ordering key order", () => {
|
|
359
|
+
assertEquals(orderTracker.keyA, [
|
|
360
|
+
1,
|
|
361
|
+
2,
|
|
362
|
+
3
|
|
363
|
+
], "Messages with orderingKey 'keyA' should be processed in order");
|
|
364
|
+
assertEquals(orderTracker.keyB, [
|
|
365
|
+
1,
|
|
366
|
+
2,
|
|
367
|
+
3
|
|
368
|
+
], "Messages with orderingKey 'keyB' should be processed in order");
|
|
369
|
+
});
|
|
370
|
+
await t.step("verify messages without ordering key", () => {
|
|
371
|
+
assertEquals(orderTracker.noKey.length, 2, "Messages without ordering key should all be received");
|
|
372
|
+
assert(orderTracker.noKey.includes(1) && orderTracker.noKey.includes(2), "Messages without ordering key should contain values 1 and 2");
|
|
373
|
+
});
|
|
374
|
+
controller.abort();
|
|
375
|
+
await listening;
|
|
376
|
+
});
|
|
225
377
|
test("MessageQueue.nativeRetrial", async (t) => {
|
|
226
378
|
if ("Deno" in globalThis && "openKv" in globalThis.Deno && typeof globalThis.Deno.openKv === "function") await t.step("DenoKvMessageQueue", async () => {
|
|
227
379
|
const packageName = () => "@fedify/denokv";
|
|
@@ -7,11 +7,11 @@ import { createTestTracerProvider, mockDocumentLoader, test } from "../dist-B5f6
|
|
|
7
7
|
import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
8
8
|
import { assert } from "../assert-MZs1qjMx.js";
|
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
|
10
|
-
import "../deno-
|
|
11
|
-
import "../key-
|
|
12
|
-
import { verifyRequest } from "../http-
|
|
13
|
-
import { doesActorOwnKey } from "../owner-
|
|
14
|
-
import { extractInboxes, sendActivity } from "../send-
|
|
10
|
+
import "../deno-fe0u4LiE.js";
|
|
11
|
+
import "../key-BGzsKfpZ.js";
|
|
12
|
+
import { verifyRequest } from "../http-oCBlFLKT.js";
|
|
13
|
+
import { doesActorOwnKey } from "../owner-BqIhDQWW.js";
|
|
14
|
+
import { extractInboxes, sendActivity } from "../send-CO2ZYT96.js";
|
|
15
15
|
import "../std__assert-DWivtrGR.js";
|
|
16
16
|
import { assertFalse, assertRejects } from "../assert_rejects-Ce45JcFg.js";
|
|
17
17
|
import "../assert_throws-BNXdRGWP.js";
|
|
@@ -8,30 +8,30 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import "../assert-MZs1qjMx.js";
|
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import "../deno-
|
|
12
|
-
import { createFederation, handleWebFinger } from "../middleware-
|
|
11
|
+
import "../deno-fe0u4LiE.js";
|
|
12
|
+
import { createFederation, handleWebFinger } from "../middleware-BlOT9luD.js";
|
|
13
13
|
import "../client-Dg7OfUDA.js";
|
|
14
14
|
import "../router-D9eI0s4b.js";
|
|
15
15
|
import "../types-CPz01LGH.js";
|
|
16
|
-
import "../key-
|
|
17
|
-
import "../http-
|
|
18
|
-
import "../ld-
|
|
19
|
-
import "../owner-
|
|
20
|
-
import "../proof
|
|
21
|
-
import "../docloader-
|
|
16
|
+
import "../key-BGzsKfpZ.js";
|
|
17
|
+
import "../http-oCBlFLKT.js";
|
|
18
|
+
import "../ld-BaO1-A5Y.js";
|
|
19
|
+
import "../owner-BqIhDQWW.js";
|
|
20
|
+
import "../proof-DoHt7qrS.js";
|
|
21
|
+
import "../docloader-C9Dmxf-K.js";
|
|
22
22
|
import "../kv-cache-B__dHl7g.js";
|
|
23
|
-
import "../inbox-
|
|
24
|
-
import "../builder-
|
|
23
|
+
import "../inbox-Dm1rfkdg.js";
|
|
24
|
+
import "../builder-B7WWCOdc.js";
|
|
25
25
|
import "../collection-CcnIw1qY.js";
|
|
26
26
|
import "../keycache-DRxpZ5r9.js";
|
|
27
27
|
import "../negotiation-5NPJL6zp.js";
|
|
28
28
|
import "../retry-D4GJ670a.js";
|
|
29
|
-
import "../send-
|
|
29
|
+
import "../send-CO2ZYT96.js";
|
|
30
30
|
import "../std__assert-DWivtrGR.js";
|
|
31
31
|
import "../assert_rejects-Ce45JcFg.js";
|
|
32
32
|
import "../assert_throws-BNXdRGWP.js";
|
|
33
33
|
import "../assert_not_equals-C80BG-_5.js";
|
|
34
|
-
import { createRequestContext } from "../context-
|
|
34
|
+
import { createRequestContext } from "../context-CZ5llAss.js";
|
|
35
35
|
import { Image, Link, Person } from "@fedify/vocab";
|
|
36
36
|
|
|
37
37
|
//#region src/federation/webfinger.test.ts
|
|
@@ -106,6 +106,11 @@ var InProcessMessageQueue = class {
|
|
|
106
106
|
#monitors;
|
|
107
107
|
#pollIntervalMs;
|
|
108
108
|
/**
|
|
109
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
110
|
+
* sequential processing for messages with the same key.
|
|
111
|
+
*/
|
|
112
|
+
#processingKeys;
|
|
113
|
+
/**
|
|
109
114
|
* In-process message queue does not provide native retry mechanisms.
|
|
110
115
|
* @since 1.7.0
|
|
111
116
|
*/
|
|
@@ -118,6 +123,7 @@ var InProcessMessageQueue = class {
|
|
|
118
123
|
this.#messages = [];
|
|
119
124
|
this.#monitors = {};
|
|
120
125
|
this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
126
|
+
this.#processingKeys = /* @__PURE__ */ new Set();
|
|
121
127
|
}
|
|
122
128
|
enqueue(message, options) {
|
|
123
129
|
const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
@@ -128,7 +134,11 @@ var InProcessMessageQueue = class {
|
|
|
128
134
|
}), delay);
|
|
129
135
|
return Promise.resolve();
|
|
130
136
|
}
|
|
131
|
-
|
|
137
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
138
|
+
this.#messages.push({
|
|
139
|
+
message,
|
|
140
|
+
orderingKey
|
|
141
|
+
});
|
|
132
142
|
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
133
143
|
return Promise.resolve();
|
|
134
144
|
}
|
|
@@ -142,18 +152,29 @@ var InProcessMessageQueue = class {
|
|
|
142
152
|
}), delay);
|
|
143
153
|
return Promise.resolve();
|
|
144
154
|
}
|
|
145
|
-
|
|
155
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
156
|
+
for (const message of messages) this.#messages.push({
|
|
157
|
+
message,
|
|
158
|
+
orderingKey
|
|
159
|
+
});
|
|
146
160
|
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
147
161
|
return Promise.resolve();
|
|
148
162
|
}
|
|
149
163
|
async listen(handler, options = {}) {
|
|
150
164
|
const signal = options.signal;
|
|
151
165
|
while (signal == null || !signal.aborted) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
166
|
+
const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
|
|
167
|
+
if (idx >= 0) {
|
|
168
|
+
const queued = this.#messages.splice(idx, 1)[0];
|
|
169
|
+
const { message, orderingKey } = queued;
|
|
170
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
171
|
+
try {
|
|
172
|
+
await handler(message);
|
|
173
|
+
} finally {
|
|
174
|
+
if (orderingKey != null) this.#processingKeys.delete(orderingKey);
|
|
175
|
+
}
|
|
176
|
+
} else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
|
|
177
|
+
else await this.#wait(10, signal);
|
|
157
178
|
}
|
|
158
179
|
}
|
|
159
180
|
#wait(ms, signal) {
|
|
@@ -185,6 +206,21 @@ var InProcessMessageQueue = class {
|
|
|
185
206
|
* for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
|
|
186
207
|
* workloads.
|
|
187
208
|
*
|
|
209
|
+
* When using `ParallelMessageQueue`, the ordering guarantee is preserved
|
|
210
|
+
* *only if* the underlying queue implementation delivers messages in a wrapper
|
|
211
|
+
* format that includes the `__fedify_ordering_key__` property. Currently,
|
|
212
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
213
|
+
* For other queue implementations (e.g., `InProcessMessageQueue`,
|
|
214
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
215
|
+
* `AmqpMessageQueue`), the ordering key cannot be detected by
|
|
216
|
+
* `ParallelMessageQueue`, so ordering guarantees are handled by those
|
|
217
|
+
* implementations directly rather than at the `ParallelMessageQueue` level.
|
|
218
|
+
*
|
|
219
|
+
* Messages with the same ordering key will never be processed concurrently
|
|
220
|
+
* by different workers, ensuring sequential processing within each key.
|
|
221
|
+
* Messages with different ordering keys (or no ordering key) can still be
|
|
222
|
+
* processed in parallel.
|
|
223
|
+
*
|
|
188
224
|
* @since 1.0.0
|
|
189
225
|
*/
|
|
190
226
|
var ParallelMessageQueue = class ParallelMessageQueue {
|
|
@@ -196,6 +232,15 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
196
232
|
*/
|
|
197
233
|
nativeRetrial;
|
|
198
234
|
/**
|
|
235
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
236
|
+
* sequential processing for messages with the same key.
|
|
237
|
+
*/
|
|
238
|
+
#processingKeys = /* @__PURE__ */ new Set();
|
|
239
|
+
/**
|
|
240
|
+
* Pending messages waiting for their ordering key to become available.
|
|
241
|
+
*/
|
|
242
|
+
#pendingMessages = [];
|
|
243
|
+
/**
|
|
199
244
|
* Constructs a new {@link ParallelMessageQueue} with the given queue and
|
|
200
245
|
* number of workers.
|
|
201
246
|
* @param queue The message queue to use under the hood. Note that
|
|
@@ -223,6 +268,25 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
223
268
|
}
|
|
224
269
|
await this.queue.enqueueMany(messages, options);
|
|
225
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Extracts ordering key from a message if present.
|
|
273
|
+
*
|
|
274
|
+
* This method only works for queue implementations that deliver messages
|
|
275
|
+
* in the wrapper format with `__fedify_ordering_key__` property. Currently,
|
|
276
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
277
|
+
*
|
|
278
|
+
* For other queue implementations (`InProcessMessageQueue`,
|
|
279
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
280
|
+
* `AmqpMessageQueue`), messages are delivered as raw payloads without the
|
|
281
|
+
* wrapper, so the ordering key cannot be detected here. Those
|
|
282
|
+
* implementations handle ordering guarantees internally.
|
|
283
|
+
*/
|
|
284
|
+
#extractOrderingKey(message) {
|
|
285
|
+
if (message != null && typeof message === "object") {
|
|
286
|
+
if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
|
|
287
|
+
}
|
|
288
|
+
return void 0;
|
|
289
|
+
}
|
|
226
290
|
listen(handler, options = {}) {
|
|
227
291
|
const workers = /* @__PURE__ */ new Map();
|
|
228
292
|
return this.queue.listen(async (message) => {
|
|
@@ -231,13 +295,33 @@ var ParallelMessageQueue = class ParallelMessageQueue {
|
|
|
231
295
|
workers.delete(consumedId);
|
|
232
296
|
}
|
|
233
297
|
const workerId = crypto.randomUUID();
|
|
234
|
-
const
|
|
298
|
+
const orderingKey = this.#extractOrderingKey(message);
|
|
299
|
+
if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
|
|
300
|
+
this.#pendingMessages.push({
|
|
301
|
+
message,
|
|
302
|
+
orderingKey,
|
|
303
|
+
resolve
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
307
|
+
const promise = this.#work(workerId, handler, message, orderingKey);
|
|
235
308
|
workers.set(workerId, promise);
|
|
236
309
|
}, options);
|
|
237
310
|
}
|
|
238
|
-
async #work(workerId, handler, message) {
|
|
311
|
+
async #work(workerId, handler, message, orderingKey) {
|
|
239
312
|
await this.#sleep(0);
|
|
240
|
-
|
|
313
|
+
try {
|
|
314
|
+
await handler(message);
|
|
315
|
+
} finally {
|
|
316
|
+
if (orderingKey != null) {
|
|
317
|
+
this.#processingKeys.delete(orderingKey);
|
|
318
|
+
const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
|
|
319
|
+
if (pendingIdx >= 0) {
|
|
320
|
+
const pending = this.#pendingMessages.splice(pendingIdx, 1)[0];
|
|
321
|
+
pending.resolve();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
241
325
|
return workerId;
|
|
242
326
|
}
|
|
243
327
|
#sleep(ms) {
|