@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.
Files changed (65) hide show
  1. package/dist/{builder-CR-ZQZJR.js → builder-B7WWCOdc.js} +3 -3
  2. package/dist/compat/mod.d.cts +2 -2
  3. package/dist/compat/mod.d.ts +2 -2
  4. package/dist/compat/transformers.test.js +11 -11
  5. package/dist/{context-C7vzWilY.d.ts → context-BNNWbaZL.d.ts} +51 -8
  6. package/dist/{context-Bns6uTJq.js → context-CZ5llAss.js} +12 -12
  7. package/dist/{context-CrB9RFy5.d.cts → context-D7JEVvXJ.d.cts} +51 -8
  8. package/dist/{deno-DOeSTfhz.js → deno-fe0u4LiE.js} +8 -2
  9. package/dist/{docloader-C1AGOQQm.js → docloader-C9Dmxf-K.js} +2 -2
  10. package/dist/federation/builder.test.js +3 -3
  11. package/dist/federation/handler.test.js +12 -12
  12. package/dist/federation/idempotency.test.js +11 -11
  13. package/dist/federation/inbox.test.js +2 -2
  14. package/dist/federation/middleware.test.js +63 -11
  15. package/dist/federation/mod.cjs +5 -5
  16. package/dist/federation/mod.d.cts +2 -2
  17. package/dist/federation/mod.d.ts +2 -2
  18. package/dist/federation/mod.js +5 -5
  19. package/dist/federation/mq.test.js +162 -10
  20. package/dist/federation/send.test.js +5 -5
  21. package/dist/federation/webfinger.test.js +12 -12
  22. package/dist/{federation-B431K2gm.cjs → federation-CE0CJ_0G.cjs} +94 -10
  23. package/dist/{federation-BbZwNNWj.js → federation-D6FVaeAR.js} +94 -10
  24. package/dist/{http-i8VdY2df.js → http-7r8B3dF_.js} +8 -2
  25. package/dist/{http-C1LrW2D3.cjs → http-DMi5iyiI.cjs} +8 -2
  26. package/dist/{http-DGkEl4mw.js → http-oCBlFLKT.js} +2 -2
  27. package/dist/{inbox-Cp87HRVs.js → inbox-Dm1rfkdg.js} +1 -1
  28. package/dist/{key-3_vbTfr8.js → key-BGzsKfpZ.js} +1 -1
  29. package/dist/{kv-cache-DOgKf8lH.cjs → kv-cache-C0AvpI7U.cjs} +1 -1
  30. package/dist/{kv-cache-B4CFgxYq.js → kv-cache-CETRZwoP.js} +1 -1
  31. package/dist/{ld-BQxhMgOJ.js → ld-BaO1-A5Y.js} +2 -2
  32. package/dist/{middleware-C52jAnyA.js → middleware-BDJNulB_.js} +4 -4
  33. package/dist/{middleware-DbwwSEOr.js → middleware-BlOT9luD.js} +65 -22
  34. package/dist/{middleware-D4Eexdig.cjs → middleware-C0Vj7vNo.cjs} +59 -16
  35. package/dist/{middleware-DZ-OdkE6.js → middleware-CnGBoMop.js} +11 -11
  36. package/dist/middleware-DT3JkH-g.cjs +12 -0
  37. package/dist/{middleware-ixha9m0b.js → middleware-R0w-WauH.js} +59 -16
  38. package/dist/{mod-0qnPv4EC.d.cts → mod-054TSUXs.d.cts} +1 -1
  39. package/dist/{mod-C3SOvTD1.d.ts → mod-BhDb3RBS.d.ts} +1 -1
  40. package/dist/{mod-D6pS5_xJ.d.cts → mod-C74sRHP8.d.cts} +1 -1
  41. package/dist/{mod-waqu-BL_.d.ts → mod-YpmzboJc.d.ts} +1 -1
  42. package/dist/mod.cjs +5 -5
  43. package/dist/mod.d.cts +3 -3
  44. package/dist/mod.d.ts +3 -3
  45. package/dist/mod.js +5 -5
  46. package/dist/nodeinfo/handler.test.js +12 -12
  47. package/dist/{owner-Bod8mCab.js → owner-BqIhDQWW.js} +1 -1
  48. package/dist/{proof-vG4ePLF2.js → proof-Cg6AAAWI.js} +1 -1
  49. package/dist/{proof--60Adidu.js → proof-DoHt7qrS.js} +2 -2
  50. package/dist/{proof-C8MAHWxQ.cjs → proof-vSvvLbTh.cjs} +1 -1
  51. package/dist/{send-B3fWpYN9.js → send-CO2ZYT96.js} +2 -2
  52. package/dist/sig/http.test.js +3 -3
  53. package/dist/sig/key.test.js +2 -2
  54. package/dist/sig/ld.test.js +3 -3
  55. package/dist/sig/mod.cjs +2 -2
  56. package/dist/sig/mod.js +2 -2
  57. package/dist/sig/owner.test.js +3 -3
  58. package/dist/sig/proof.test.js +3 -3
  59. package/dist/testing/mod.d.ts +23 -7
  60. package/dist/testing/mod.js +1 -1
  61. package/dist/utils/docloader.test.js +4 -4
  62. package/dist/utils/mod.cjs +2 -2
  63. package/dist/utils/mod.js +2 -2
  64. package/package.json +10 -8
  65. package/dist/middleware-XsmW-sAa.cjs +0 -12
@@ -3,9 +3,9 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { deno_default } from "./deno-DOeSTfhz.js";
6
+ import { deno_default } from "./deno-fe0u4LiE.js";
7
7
  import { Router, RouterError } from "./router-D9eI0s4b.js";
8
- import { InboxListenerSet } from "./inbox-Cp87HRVs.js";
8
+ import { InboxListenerSet } from "./inbox-Dm1rfkdg.js";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { getTypeId } from "@fedify/vocab";
11
11
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -44,7 +44,7 @@ var FederationBuilderImpl = class {
44
44
  this.collectionTypeIds = {};
45
45
  }
46
46
  async build(options) {
47
- const { FederationImpl } = await import("./middleware-DZ-OdkE6.js");
47
+ const { FederationImpl } = await import("./middleware-CnGBoMop.js");
48
48
  const f = new FederationImpl(options);
49
49
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
50
50
  f.router = this.router.clone();
@@ -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 { ActivityTransformer } from "../context-CrB9RFy5.cjs";
4
+ import { ActivityTransformer } from "../context-D7JEVvXJ.cjs";
5
5
  import "../kv-B4vFhIYL.cjs";
6
- import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-D6pS5_xJ.cjs";
6
+ import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-C74sRHP8.cjs";
7
7
  export { ActivityTransformer, actorDehydrator, autoIdAssigner, getDefaultActivityTransformers };
@@ -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 { ActivityTransformer } from "../context-C7vzWilY.js";
6
+ import { ActivityTransformer } from "../context-BNNWbaZL.js";
7
7
  import "../kv-CYySNrsn.js";
8
- import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-waqu-BL_.js";
8
+ import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-YpmzboJc.js";
9
9
  export { ActivityTransformer, actorDehydrator, autoIdAssigner, getDefaultActivityTransformers };
@@ -8,25 +8,25 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
8
8
  import { assert } from "../assert-MZs1qjMx.js";
9
9
  import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
10
10
  import { MemoryKvStore } from "../kv-QzKcOQgP.js";
11
- import "../deno-DOeSTfhz.js";
12
- import { FederationImpl, actorDehydrator, autoIdAssigner } from "../middleware-DbwwSEOr.js";
11
+ import "../deno-fe0u4LiE.js";
12
+ import { FederationImpl, actorDehydrator, autoIdAssigner } 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-3_vbTfr8.js";
17
- import "../http-DGkEl4mw.js";
18
- import "../ld-BQxhMgOJ.js";
19
- import "../owner-Bod8mCab.js";
20
- import "../proof--60Adidu.js";
21
- import "../docloader-C1AGOQQm.js";
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-Cp87HRVs.js";
24
- import "../builder-CR-ZQZJR.js";
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-B3fWpYN9.js";
29
+ import "../send-CO2ZYT96.js";
30
30
  import { Follow, Person } from "@fedify/vocab";
31
31
 
32
32
  //#region src/compat/transformers.test.ts
@@ -317,7 +317,19 @@ interface MessageQueueEnqueueOptions {
317
317
  *
318
318
  * It must not be negative.
319
319
  */
320
- delay?: Temporal.Duration;
320
+ readonly delay?: Temporal.Duration;
321
+ /**
322
+ * An optional key that ensures messages with the same ordering key are
323
+ * processed sequentially (one at a time). Messages with different ordering
324
+ * keys (or no ordering key) may be processed in parallel.
325
+ *
326
+ * This is useful for ensuring that related messages are processed in order,
327
+ * such as ensuring that a `Delete` activity is processed after a `Create`
328
+ * activity for the same object.
329
+ *
330
+ * @since 2.0.0
331
+ */
332
+ readonly orderingKey?: string;
321
333
  }
322
334
  /**
323
335
  * Additional options for listening to a message queue.
@@ -418,6 +430,21 @@ declare class InProcessMessageQueue implements MessageQueue {
418
430
  * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
419
431
  * workloads.
420
432
  *
433
+ * When using `ParallelMessageQueue`, the ordering guarantee is preserved
434
+ * *only if* the underlying queue implementation delivers messages in a wrapper
435
+ * format that includes the `__fedify_ordering_key__` property. Currently,
436
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
437
+ * For other queue implementations (e.g., `InProcessMessageQueue`,
438
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
439
+ * `AmqpMessageQueue`), the ordering key cannot be detected by
440
+ * `ParallelMessageQueue`, so ordering guarantees are handled by those
441
+ * implementations directly rather than at the `ParallelMessageQueue` level.
442
+ *
443
+ * Messages with the same ordering key will never be processed concurrently
444
+ * by different workers, ensuring sequential processing within each key.
445
+ * Messages with different ordering keys (or no ordering key) can still be
446
+ * processed in parallel.
447
+ *
421
448
  * @since 1.0.0
422
449
  */
423
450
  declare class ParallelMessageQueue implements MessageQueue {
@@ -597,6 +624,7 @@ interface FanoutMessage {
597
624
  readonly activityId?: string;
598
625
  readonly activityType: string;
599
626
  readonly collectionSync?: string;
627
+ readonly orderingKey?: string;
600
628
  readonly traceContext: Readonly<Record<string, string>>;
601
629
  }
602
630
  interface OutboxMessage {
@@ -612,6 +640,7 @@ interface OutboxMessage {
612
640
  readonly started: string;
613
641
  readonly attempt: number;
614
642
  readonly headers: Readonly<Record<string, string>>;
643
+ readonly orderingKey?: string;
615
644
  readonly traceContext: Readonly<Record<string, string>>;
616
645
  }
617
646
  interface InboxMessage {
@@ -1047,7 +1076,7 @@ interface Federatable<TContextData> {
1047
1076
  * ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
1048
1077
  * The path must have no variables.
1049
1078
  * @returns An object to register inbox listeners.
1050
- * @throws {RouteError} Thrown if the path pattern is invalid.
1079
+ * @throws {RouterError} Thrown if the path pattern is invalid.
1051
1080
  */
1052
1081
  setInboxListeners(inboxPath: `${string}${Rfc6570Expression<"identifier">}${string}` | `${string}${Rfc6570Expression<"handle">}${string}`, sharedInboxPath?: string): InboxListenerSetters<TContextData>;
1053
1082
  /**
@@ -1766,7 +1795,7 @@ interface Context<TContextData> {
1766
1795
  * @param cls The class of the object.
1767
1796
  * @param values The values to pass to the object dispatcher.
1768
1797
  * @returns The object's URI.
1769
- * @throws {RouteError} If no object dispatcher is available for the class.
1798
+ * @throws {RouterError} If no object dispatcher is available for the class.
1770
1799
  * @throws {TypeError} If values are invalid.
1771
1800
  * @since 0.7.0
1772
1801
  */
@@ -1998,7 +2027,7 @@ interface Context<TContextData> {
1998
2027
  handle: string;
1999
2028
  }, recipients: Recipient | Recipient[], activity: Activity, options?: SendActivityOptions): Promise<void>;
2000
2029
  /**
2001
- * Sends an activity to the outboxes of the sender's followers.
2030
+ * Sends an activity to the inboxes of the sender's followers.
2002
2031
  * @param sender The sender's identifier or the sender's username.
2003
2032
  * @param recipients In this case, it must be `"followers"`.
2004
2033
  * @param activity The activity to send.
@@ -2039,7 +2068,7 @@ interface Context<TContextData> {
2039
2068
  * @param name The name of the collection, which can be a string or a symbol.
2040
2069
  * @param values The values of the URI parameters.
2041
2070
  * @return The URI of the collection.
2042
- * @throws {RouteError} If no object dispatcher is available for the name.
2071
+ * @throws {RouterError} If no object dispatcher is available for the name.
2043
2072
  * @throws {TypeError} If values are invalid.
2044
2073
  * @since 1.8.0
2045
2074
  */
@@ -2318,7 +2347,7 @@ interface SendActivityOptions {
2318
2347
  /**
2319
2348
  * Whether to prefer the shared inbox for the recipients.
2320
2349
  */
2321
- preferSharedInbox?: boolean;
2350
+ readonly preferSharedInbox?: boolean;
2322
2351
  /**
2323
2352
  * Whether to send the activity immediately, without enqueuing it.
2324
2353
  * If `true`, the activity will be sent immediately and the retrial
@@ -2326,7 +2355,7 @@ interface SendActivityOptions {
2326
2355
  *
2327
2356
  * @since 0.3.0
2328
2357
  */
2329
- immediate?: boolean;
2358
+ readonly immediate?: boolean;
2330
2359
  /**
2331
2360
  * Determines how activities are queued when sent to multiple recipients.
2332
2361
  *
@@ -2343,7 +2372,7 @@ interface SendActivityOptions {
2343
2372
  * @default `"auto"`
2344
2373
  * @since 1.5.0
2345
2374
  */
2346
- fanout?: "auto" | "skip" | "force";
2375
+ readonly fanout?: "auto" | "skip" | "force";
2347
2376
  /**
2348
2377
  * The base URIs to exclude from the recipients' inboxes. It is useful
2349
2378
  * for excluding the recipients having the same shared inbox with the sender.
@@ -2353,6 +2382,20 @@ interface SendActivityOptions {
2353
2382
  * @since 0.9.0
2354
2383
  */
2355
2384
  readonly excludeBaseUris?: readonly URL[];
2385
+ /**
2386
+ * An optional key to ensure ordered delivery of activities. Activities with
2387
+ * the same `orderingKey` are guaranteed to be delivered in the order they
2388
+ * were enqueued, per recipient server.
2389
+ *
2390
+ * Typical use case: pass the object ID (e.g., `Note` ID) to ensure that
2391
+ * `Create`, `Update`, and `Delete` activities for the same object are
2392
+ * delivered in order.
2393
+ *
2394
+ * When omitted, no ordering is guaranteed (maximum parallelism).
2395
+ *
2396
+ * @since 2.0.0
2397
+ */
2398
+ readonly orderingKey?: string;
2356
2399
  }
2357
2400
  /**
2358
2401
  * Options for {@link Context.sendActivity} method when sending to a collection.
@@ -12,7 +12,7 @@ import { trace } from "@opentelemetry/api";
12
12
  //#region src/testing/context.ts
13
13
  function createContext(values) {
14
14
  const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getCollectionUri, getOutboxUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity } = values;
15
- function throwRouteError() {
15
+ function throwRouterError() {
16
16
  throw new RouterError("Not implemented");
17
17
  }
18
18
  return {
@@ -29,17 +29,17 @@ function createContext(values) {
29
29
  ...values,
30
30
  data: data$1
31
31
  })),
32
- getNodeInfoUri: getNodeInfoUri ?? throwRouteError,
33
- getActorUri: getActorUri ?? throwRouteError,
34
- getObjectUri: getObjectUri ?? throwRouteError,
35
- getCollectionUri: getCollectionUri ?? throwRouteError,
36
- getOutboxUri: getOutboxUri ?? throwRouteError,
37
- getInboxUri: getInboxUri ?? throwRouteError,
38
- getFollowingUri: getFollowingUri ?? throwRouteError,
39
- getFollowersUri: getFollowersUri ?? throwRouteError,
40
- getLikedUri: getLikedUri ?? throwRouteError,
41
- getFeaturedUri: getFeaturedUri ?? throwRouteError,
42
- getFeaturedTagsUri: getFeaturedTagsUri ?? throwRouteError,
32
+ getNodeInfoUri: getNodeInfoUri ?? throwRouterError,
33
+ getActorUri: getActorUri ?? throwRouterError,
34
+ getObjectUri: getObjectUri ?? throwRouterError,
35
+ getCollectionUri: getCollectionUri ?? throwRouterError,
36
+ getOutboxUri: getOutboxUri ?? throwRouterError,
37
+ getInboxUri: getInboxUri ?? throwRouterError,
38
+ getFollowingUri: getFollowingUri ?? throwRouterError,
39
+ getFollowersUri: getFollowersUri ?? throwRouterError,
40
+ getLikedUri: getLikedUri ?? throwRouterError,
41
+ getFeaturedUri: getFeaturedUri ?? throwRouterError,
42
+ getFeaturedTagsUri: getFeaturedTagsUri ?? throwRouterError,
43
43
  parseUri: parseUri ?? ((_uri) => {
44
44
  throw new Error("Not implemented");
45
45
  }),
@@ -315,7 +315,19 @@ interface MessageQueueEnqueueOptions {
315
315
  *
316
316
  * It must not be negative.
317
317
  */
318
- delay?: Temporal.Duration;
318
+ readonly delay?: Temporal.Duration;
319
+ /**
320
+ * An optional key that ensures messages with the same ordering key are
321
+ * processed sequentially (one at a time). Messages with different ordering
322
+ * keys (or no ordering key) may be processed in parallel.
323
+ *
324
+ * This is useful for ensuring that related messages are processed in order,
325
+ * such as ensuring that a `Delete` activity is processed after a `Create`
326
+ * activity for the same object.
327
+ *
328
+ * @since 2.0.0
329
+ */
330
+ readonly orderingKey?: string;
319
331
  }
320
332
  /**
321
333
  * Additional options for listening to a message queue.
@@ -416,6 +428,21 @@ declare class InProcessMessageQueue implements MessageQueue {
416
428
  * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
417
429
  * workloads.
418
430
  *
431
+ * When using `ParallelMessageQueue`, the ordering guarantee is preserved
432
+ * *only if* the underlying queue implementation delivers messages in a wrapper
433
+ * format that includes the `__fedify_ordering_key__` property. Currently,
434
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
435
+ * For other queue implementations (e.g., `InProcessMessageQueue`,
436
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
437
+ * `AmqpMessageQueue`), the ordering key cannot be detected by
438
+ * `ParallelMessageQueue`, so ordering guarantees are handled by those
439
+ * implementations directly rather than at the `ParallelMessageQueue` level.
440
+ *
441
+ * Messages with the same ordering key will never be processed concurrently
442
+ * by different workers, ensuring sequential processing within each key.
443
+ * Messages with different ordering keys (or no ordering key) can still be
444
+ * processed in parallel.
445
+ *
419
446
  * @since 1.0.0
420
447
  */
421
448
  declare class ParallelMessageQueue implements MessageQueue {
@@ -595,6 +622,7 @@ interface FanoutMessage {
595
622
  readonly activityId?: string;
596
623
  readonly activityType: string;
597
624
  readonly collectionSync?: string;
625
+ readonly orderingKey?: string;
598
626
  readonly traceContext: Readonly<Record<string, string>>;
599
627
  }
600
628
  interface OutboxMessage {
@@ -610,6 +638,7 @@ interface OutboxMessage {
610
638
  readonly started: string;
611
639
  readonly attempt: number;
612
640
  readonly headers: Readonly<Record<string, string>>;
641
+ readonly orderingKey?: string;
613
642
  readonly traceContext: Readonly<Record<string, string>>;
614
643
  }
615
644
  interface InboxMessage {
@@ -1045,7 +1074,7 @@ interface Federatable<TContextData> {
1045
1074
  * ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
1046
1075
  * The path must have no variables.
1047
1076
  * @returns An object to register inbox listeners.
1048
- * @throws {RouteError} Thrown if the path pattern is invalid.
1077
+ * @throws {RouterError} Thrown if the path pattern is invalid.
1049
1078
  */
1050
1079
  setInboxListeners(inboxPath: `${string}${Rfc6570Expression<"identifier">}${string}` | `${string}${Rfc6570Expression<"handle">}${string}`, sharedInboxPath?: string): InboxListenerSetters<TContextData>;
1051
1080
  /**
@@ -1764,7 +1793,7 @@ interface Context<TContextData> {
1764
1793
  * @param cls The class of the object.
1765
1794
  * @param values The values to pass to the object dispatcher.
1766
1795
  * @returns The object's URI.
1767
- * @throws {RouteError} If no object dispatcher is available for the class.
1796
+ * @throws {RouterError} If no object dispatcher is available for the class.
1768
1797
  * @throws {TypeError} If values are invalid.
1769
1798
  * @since 0.7.0
1770
1799
  */
@@ -1996,7 +2025,7 @@ interface Context<TContextData> {
1996
2025
  handle: string;
1997
2026
  }, recipients: Recipient | Recipient[], activity: Activity, options?: SendActivityOptions): Promise<void>;
1998
2027
  /**
1999
- * Sends an activity to the outboxes of the sender's followers.
2028
+ * Sends an activity to the inboxes of the sender's followers.
2000
2029
  * @param sender The sender's identifier or the sender's username.
2001
2030
  * @param recipients In this case, it must be `"followers"`.
2002
2031
  * @param activity The activity to send.
@@ -2037,7 +2066,7 @@ interface Context<TContextData> {
2037
2066
  * @param name The name of the collection, which can be a string or a symbol.
2038
2067
  * @param values The values of the URI parameters.
2039
2068
  * @return The URI of the collection.
2040
- * @throws {RouteError} If no object dispatcher is available for the name.
2069
+ * @throws {RouterError} If no object dispatcher is available for the name.
2041
2070
  * @throws {TypeError} If values are invalid.
2042
2071
  * @since 1.8.0
2043
2072
  */
@@ -2316,7 +2345,7 @@ interface SendActivityOptions {
2316
2345
  /**
2317
2346
  * Whether to prefer the shared inbox for the recipients.
2318
2347
  */
2319
- preferSharedInbox?: boolean;
2348
+ readonly preferSharedInbox?: boolean;
2320
2349
  /**
2321
2350
  * Whether to send the activity immediately, without enqueuing it.
2322
2351
  * If `true`, the activity will be sent immediately and the retrial
@@ -2324,7 +2353,7 @@ interface SendActivityOptions {
2324
2353
  *
2325
2354
  * @since 0.3.0
2326
2355
  */
2327
- immediate?: boolean;
2356
+ readonly immediate?: boolean;
2328
2357
  /**
2329
2358
  * Determines how activities are queued when sent to multiple recipients.
2330
2359
  *
@@ -2341,7 +2370,7 @@ interface SendActivityOptions {
2341
2370
  * @default `"auto"`
2342
2371
  * @since 1.5.0
2343
2372
  */
2344
- fanout?: "auto" | "skip" | "force";
2373
+ readonly fanout?: "auto" | "skip" | "force";
2345
2374
  /**
2346
2375
  * The base URIs to exclude from the recipients' inboxes. It is useful
2347
2376
  * for excluding the recipients having the same shared inbox with the sender.
@@ -2351,6 +2380,20 @@ interface SendActivityOptions {
2351
2380
  * @since 0.9.0
2352
2381
  */
2353
2382
  readonly excludeBaseUris?: readonly URL[];
2383
+ /**
2384
+ * An optional key to ensure ordered delivery of activities. Activities with
2385
+ * the same `orderingKey` are guaranteed to be delivered in the order they
2386
+ * were enqueued, per recipient server.
2387
+ *
2388
+ * Typical use case: pass the object ID (e.g., `Note` ID) to ensure that
2389
+ * `Create`, `Update`, and `Delete` activities for the same object are
2390
+ * delivered in order.
2391
+ *
2392
+ * When omitted, no ordering is guaranteed (maximum parallelism).
2393
+ *
2394
+ * @since 2.0.0
2395
+ */
2396
+ readonly orderingKey?: string;
2354
2397
  }
2355
2398
  /**
2356
2399
  * Options for {@link Context.sendActivity} method when sending to a collection.
@@ -5,7 +5,7 @@
5
5
 
6
6
  //#region deno.json
7
7
  var name = "@fedify/fedify";
8
- var version = "2.0.0-dev.241+58c8126c";
8
+ var version = "2.0.0-dev.279+ce1bdc22";
9
9
  var license = "MIT";
10
10
  var exports = {
11
11
  ".": "./src/mod.ts",
@@ -49,7 +49,13 @@ var exclude = [
49
49
  "src/cfworkers/server.js",
50
50
  "src/cfworkers/server.js.map"
51
51
  ];
52
- var publish = { "exclude": ["**/*.test.ts", "src/testing/"] };
52
+ var publish = { "exclude": [
53
+ "**/*.test.ts",
54
+ "src/testing/",
55
+ "tsdown.config.ts",
56
+ "scripts/",
57
+ "wrangler.toml"
58
+ ] };
53
59
  var tasks = {
54
60
  "codegen": "deno task -f @fedify/vocab compile",
55
61
  "cache": {
@@ -3,8 +3,8 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { validateCryptoKey } from "./key-3_vbTfr8.js";
7
- import { doubleKnock } from "./http-DGkEl4mw.js";
6
+ import { validateCryptoKey } from "./key-BGzsKfpZ.js";
7
+ import { doubleKnock } from "./http-oCBlFLKT.js";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { curry } from "es-toolkit";
10
10
  import { UrlError, createActivityPubRequest, getDocumentLoader, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
@@ -8,10 +8,10 @@ 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-DOeSTfhz.js";
11
+ import "../deno-fe0u4LiE.js";
12
12
  import "../router-D9eI0s4b.js";
13
- import "../inbox-Cp87HRVs.js";
14
- import { createFederationBuilder } from "../builder-CR-ZQZJR.js";
13
+ import "../inbox-Dm1rfkdg.js";
14
+ import { createFederationBuilder } from "../builder-B7WWCOdc.js";
15
15
  import { assertExists } from "../std__assert-DWivtrGR.js";
16
16
  import "../assert_rejects-Ce45JcFg.js";
17
17
  import { assertThrows } from "../assert_throws-BNXdRGWP.js";
@@ -8,30 +8,30 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
8
8
  import { assert } from "../assert-MZs1qjMx.js";
9
9
  import "../assert_instance_of-DHz7EHNU.js";
10
10
  import { MemoryKvStore } from "../kv-QzKcOQgP.js";
11
- import "../deno-DOeSTfhz.js";
12
- import { createFederation, handleActor, handleCollection, handleCustomCollection, handleInbox, handleObject, respondWithObject, respondWithObjectIfAcceptable } from "../middleware-DbwwSEOr.js";
11
+ import "../deno-fe0u4LiE.js";
12
+ import { createFederation, handleActor, handleCollection, handleCustomCollection, handleInbox, handleObject, respondWithObject, respondWithObjectIfAcceptable } 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-3_vbTfr8.js";
17
- import { signRequest } from "../http-DGkEl4mw.js";
18
- import "../ld-BQxhMgOJ.js";
19
- import "../owner-Bod8mCab.js";
20
- import "../proof--60Adidu.js";
21
- import "../docloader-C1AGOQQm.js";
16
+ import "../key-BGzsKfpZ.js";
17
+ import { signRequest } from "../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 { InboxListenerSet } from "../inbox-Cp87HRVs.js";
24
- import "../builder-CR-ZQZJR.js";
23
+ import { InboxListenerSet } from "../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-B3fWpYN9.js";
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 { createInboxContext, createRequestContext } from "../context-Bns6uTJq.js";
34
+ import { createInboxContext, createRequestContext } from "../context-CZ5llAss.js";
35
35
  import { rsaPrivateKey3, rsaPublicKey2, rsaPublicKey3 } from "../keys-ZbcByPg9.js";
36
36
  import { Create, Note, Person } from "@fedify/vocab";
37
37
 
@@ -8,25 +8,25 @@ 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-DOeSTfhz.js";
12
- import { createFederation } from "../middleware-DbwwSEOr.js";
11
+ import "../deno-fe0u4LiE.js";
12
+ import { createFederation } 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-3_vbTfr8.js";
17
- import "../http-DGkEl4mw.js";
18
- import "../ld-BQxhMgOJ.js";
19
- import "../owner-Bod8mCab.js";
20
- import { signObject } from "../proof--60Adidu.js";
21
- import "../docloader-C1AGOQQm.js";
16
+ import "../key-BGzsKfpZ.js";
17
+ import "../http-oCBlFLKT.js";
18
+ import "../ld-BaO1-A5Y.js";
19
+ import "../owner-BqIhDQWW.js";
20
+ import { signObject } from "../proof-DoHt7qrS.js";
21
+ import "../docloader-C9Dmxf-K.js";
22
22
  import "../kv-cache-B__dHl7g.js";
23
- import "../inbox-Cp87HRVs.js";
24
- import "../builder-CR-ZQZJR.js";
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-B3fWpYN9.js";
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";
@@ -5,8 +5,8 @@
5
5
 
6
6
  import { test } from "../dist-B5f6a8Tt.js";
7
7
  import { assertEquals } from "../assert_equals-DSbWqCm3.js";
8
- import "../deno-DOeSTfhz.js";
9
- import { InboxListenerSet } from "../inbox-Cp87HRVs.js";
8
+ import "../deno-fe0u4LiE.js";
9
+ import { InboxListenerSet } from "../inbox-Dm1rfkdg.js";
10
10
  import { assertThrows } from "../assert_throws-BNXdRGWP.js";
11
11
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
12
12
 
@@ -8,25 +8,25 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
8
8
  import { assert } from "../assert-MZs1qjMx.js";
9
9
  import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
10
10
  import { MemoryKvStore } from "../kv-QzKcOQgP.js";
11
- import "../deno-DOeSTfhz.js";
12
- import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "../middleware-DbwwSEOr.js";
11
+ import "../deno-fe0u4LiE.js";
12
+ import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "../middleware-BlOT9luD.js";
13
13
  import "../client-Dg7OfUDA.js";
14
14
  import { RouterError } from "../router-D9eI0s4b.js";
15
15
  import "../types-CPz01LGH.js";
16
- import "../key-3_vbTfr8.js";
17
- import { signRequest, verifyRequest } from "../http-DGkEl4mw.js";
18
- import { detachSignature, signJsonLd, verifyJsonLd } from "../ld-BQxhMgOJ.js";
19
- import { doesActorOwnKey } from "../owner-Bod8mCab.js";
20
- import { signObject, verifyObject } from "../proof--60Adidu.js";
21
- import { fetchDocumentLoader, getAuthenticatedDocumentLoader } from "../docloader-C1AGOQQm.js";
16
+ import "../key-BGzsKfpZ.js";
17
+ import { signRequest, verifyRequest } from "../http-oCBlFLKT.js";
18
+ import { detachSignature, signJsonLd, verifyJsonLd } from "../ld-BaO1-A5Y.js";
19
+ import { doesActorOwnKey } from "../owner-BqIhDQWW.js";
20
+ import { signObject, verifyObject } from "../proof-DoHt7qrS.js";
21
+ import { fetchDocumentLoader, getAuthenticatedDocumentLoader } from "../docloader-C9Dmxf-K.js";
22
22
  import "../kv-cache-B__dHl7g.js";
23
- import "../inbox-Cp87HRVs.js";
24
- import "../builder-CR-ZQZJR.js";
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-B3fWpYN9.js";
29
+ import "../send-CO2ZYT96.js";
30
30
  import { assertStrictEquals } from "../std__assert-DWivtrGR.js";
31
31
  import { assertFalse, assertRejects } from "../assert_rejects-Ce45JcFg.js";
32
32
  import { assertThrows } from "../assert_throws-BNXdRGWP.js";
@@ -1635,6 +1635,7 @@ test("ContextImpl.sendActivity()", async (t) => {
1635
1635
  sharedInbox: false
1636
1636
  } },
1637
1637
  keys: queue.messages[0].type === "fanout" ? queue.messages[0].keys : [],
1638
+ orderingKey: void 0,
1638
1639
  traceContext: {}
1639
1640
  }]);
1640
1641
  });
@@ -1732,6 +1733,57 @@ test("ContextImpl.sendActivity()", async (t) => {
1732
1733
  });
1733
1734
  assertNotEquals(collectionSyncHeader, null);
1734
1735
  });
1736
+ queue.clear();
1737
+ await t.step("orderingKey with fanout: \"force\"", async () => {
1738
+ const activity = new vocab.Create({
1739
+ id: new URL("https://example.com/activity/ordering-1"),
1740
+ actor: new URL("https://example.com/person")
1741
+ });
1742
+ await ctx2.sendActivity({ username: "john" }, {
1743
+ id: new URL("https://example.com/recipient"),
1744
+ inboxId: new URL("https://example.com/inbox")
1745
+ }, activity, {
1746
+ fanout: "force",
1747
+ orderingKey: "https://example.com/note/1"
1748
+ });
1749
+ assertEquals(queue.messages.length, 1);
1750
+ const fanoutMessage = queue.messages[0];
1751
+ assertEquals(fanoutMessage.type, "fanout");
1752
+ if (fanoutMessage.type === "fanout") assertEquals(fanoutMessage.orderingKey, "https://example.com/note/1");
1753
+ });
1754
+ queue.clear();
1755
+ await t.step("orderingKey with fanout: \"skip\"", async () => {
1756
+ const activity = new vocab.Create({
1757
+ id: new URL("https://example.com/activity/ordering-2"),
1758
+ actor: new URL("https://example.com/person")
1759
+ });
1760
+ await ctx2.sendActivity({ username: "john" }, {
1761
+ id: new URL("https://example.com/recipient"),
1762
+ inboxId: new URL("https://example.com/inbox")
1763
+ }, activity, {
1764
+ fanout: "skip",
1765
+ orderingKey: "https://example.com/note/2"
1766
+ });
1767
+ assertEquals(queue.messages.length, 1);
1768
+ const outboxMessage = queue.messages[0];
1769
+ assertEquals(outboxMessage.type, "outbox");
1770
+ if (outboxMessage.type === "outbox") assertEquals(outboxMessage.orderingKey, "https://example.com/note/2\nhttps://example.com");
1771
+ });
1772
+ queue.clear();
1773
+ await t.step("orderingKey not specified", async () => {
1774
+ const activity = new vocab.Create({
1775
+ id: new URL("https://example.com/activity/ordering-3"),
1776
+ actor: new URL("https://example.com/person")
1777
+ });
1778
+ await ctx2.sendActivity({ username: "john" }, {
1779
+ id: new URL("https://example.com/recipient"),
1780
+ inboxId: new URL("https://example.com/inbox")
1781
+ }, activity, { fanout: "force" });
1782
+ assertEquals(queue.messages.length, 1);
1783
+ const fanoutMessage2 = queue.messages[0];
1784
+ assertEquals(fanoutMessage2.type, "fanout");
1785
+ if (fanoutMessage2.type === "fanout") assertEquals(fanoutMessage2.orderingKey, void 0);
1786
+ });
1735
1787
  esm_default.hardReset();
1736
1788
  });
1737
1789
  test({