@fedify/testing 2.2.0-dev.802 → 2.2.0-pr.695.16

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/mod.cjs CHANGED
@@ -1,6 +1,5 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- let _fedify_fedify_sig = require("@fedify/fedify/sig");
4
3
  let _fedify_vocab = require("@fedify/vocab");
5
4
  let _fedify_fedify_federation = require("@fedify/fedify/federation");
6
5
  let es_toolkit = require("es-toolkit");
@@ -113,9 +112,6 @@ function createRequestContext(args) {
113
112
  * @since 1.8.0
114
113
  */
115
114
  function createInboxContext(args) {
116
- const forwardActivity = args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
117
- throw new Error("Not implemented");
118
- });
119
115
  return {
120
116
  ...createContext(args),
121
117
  clone: args.clone ?? ((data) => createInboxContext({
@@ -123,29 +119,9 @@ function createInboxContext(args) {
123
119
  data
124
120
  })),
125
121
  recipient: args.recipient ?? null,
126
- forwardActivity
127
- };
128
- }
129
- /**
130
- * Creates an OutboxContext for testing purposes.
131
- * Not exported - used internally only. Public API is in mock.ts
132
- * @param args Partial OutboxContext properties
133
- * @returns An OutboxContext instance
134
- * @since 2.2.0
135
- */
136
- function createOutboxContext(args) {
137
- const forwardActivity = args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
138
- throw new Error("Not implemented");
139
- });
140
- return {
141
- ...createContext(args),
142
- clone: args.clone ?? ((data) => createOutboxContext({
143
- ...args,
144
- data
145
- })),
146
- identifier: args.identifier,
147
- hasDeliveredActivity: args.hasDeliveredActivity ?? (() => false),
148
- forwardActivity
122
+ forwardActivity: args.forwardActivity ?? ((_params) => {
123
+ throw new Error("Not implemented");
124
+ })
149
125
  };
150
126
  }
151
127
  //#endregion
@@ -155,41 +131,17 @@ const noopTracerProvider = { getTracer: () => ({
155
131
  startSpan: () => void 0
156
132
  }) };
157
133
  /**
158
- * Helper function to expand URI templates used by the mock.
159
- * Supports the RFC 6570 operators accepted by Fedify's identifier paths.
134
+ * Helper function to expand URI templates with values.
135
+ * Supports simple placeholders like {identifier}, etc.
160
136
  * @param template The URI template pattern
161
137
  * @param values The values to substitute
162
138
  * @returns The expanded URI path
163
139
  */
164
140
  function expandUriTemplate(template, values) {
165
- return template.replace(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g, (match, operator, key) => {
166
- const value = values[key];
167
- if (value == null) return match;
168
- switch (operator) {
169
- case "": return encodeURIComponent(value);
170
- case "+": return encodeURI(value);
171
- case "#": return `#${encodeURI(value)}`;
172
- case ".": return `.${encodeURIComponent(value)}`;
173
- case "/": return `/${encodeURIComponent(value)}`;
174
- case ";": return `;${key}=${encodeURIComponent(value)}`;
175
- case "?": return `?${key}=${encodeURIComponent(value)}`;
176
- case "&": return `&${key}=${encodeURIComponent(value)}`;
177
- default: return match;
178
- }
141
+ return template.replace(/{([^}]+)}/g, (match, key) => {
142
+ return values[key] || match;
179
143
  });
180
144
  }
181
- function validateOutboxListenerPath(path, dispatcherPath) {
182
- if (!path.startsWith("/")) throw new TypeError("Path must start with a slash.");
183
- if (dispatcherPath != null && dispatcherPath !== path) throw new TypeError("Outbox listener path and outbox dispatcher path must match.");
184
- const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
185
- if (operatorMatches.some((match) => [
186
- "?",
187
- "&",
188
- "#"
189
- ].includes(match[1]) && match[2] === "identifier")) throw new TypeError("Path for outbox cannot use query or fragment expansion for identifier.");
190
- const variables = operatorMatches.map((match) => match[2]);
191
- if (variables.length !== 1 || variables[0] !== "identifier") throw new TypeError("Path for outbox must have exactly one variable named identifier.");
192
- }
193
145
  /**
194
146
  * A mock implementation of the {@link Federation} interface for unit testing.
195
147
  * This class provides a way to test Fedify applications without needing
@@ -246,17 +198,12 @@ var MockFederation = class {
246
198
  objectDispatchers = /* @__PURE__ */ new Map();
247
199
  inboxDispatcher;
248
200
  outboxDispatcher;
249
- outboxAuthorizePredicate;
250
- outboxDispatcherAuthorizePredicate;
251
- outboxListenerErrorHandler;
252
201
  followingDispatcher;
253
202
  followersDispatcher;
254
203
  likedDispatcher;
255
204
  featuredDispatcher;
256
205
  featuredTagsDispatcher;
257
206
  inboxListeners = /* @__PURE__ */ new Map();
258
- outboxListeners = /* @__PURE__ */ new Map();
259
- outboxListenersInitialized = false;
260
207
  contextData;
261
208
  receivedActivities = [];
262
209
  constructor(options = {}) {
@@ -298,17 +245,13 @@ var MockFederation = class {
298
245
  };
299
246
  }
300
247
  setOutboxDispatcher(path, dispatcher) {
301
- validateOutboxListenerPath(path, this.outboxListenersInitialized ? this.outboxPath : void 0);
302
248
  this.outboxDispatcher = dispatcher;
303
249
  this.outboxPath = path;
304
250
  return {
305
251
  setCounter: () => this,
306
252
  setFirstCursor: () => this,
307
253
  setLastCursor: () => this,
308
- authorize: (predicate) => {
309
- this.outboxDispatcherAuthorizePredicate = predicate;
310
- return this;
311
- }
254
+ authorize: () => this
312
255
  };
313
256
  }
314
257
  setFollowingDispatcher(path, dispatcher) {
@@ -386,28 +329,6 @@ var MockFederation = class {
386
329
  }
387
330
  };
388
331
  }
389
- setOutboxListeners(outboxPath) {
390
- if (this.outboxListenersInitialized) throw new TypeError("Outbox listeners already set.");
391
- validateOutboxListenerPath(outboxPath, this.outboxPath);
392
- this.outboxListenersInitialized = true;
393
- this.outboxPath = outboxPath;
394
- const self = this;
395
- return {
396
- on(type, listener) {
397
- if (self.outboxListeners.has(type)) throw new TypeError("Listener already set for this type.");
398
- self.outboxListeners.set(type, listener);
399
- return this;
400
- },
401
- onError(handler) {
402
- self.outboxListenerErrorHandler = handler;
403
- return this;
404
- },
405
- authorize(predicate) {
406
- self.outboxAuthorizePredicate = predicate;
407
- return this;
408
- }
409
- };
410
- }
411
332
  setOutboxPermanentFailureHandler(_handler) {}
412
333
  async startQueue(contextData, options) {
413
334
  this.contextData = contextData;
@@ -424,10 +345,8 @@ var MockFederation = class {
424
345
  }
425
346
  createContext(baseUrlOrRequest, contextData) {
426
347
  const mockFederation = this;
427
- const request = baseUrlOrRequest instanceof Request ? baseUrlOrRequest : null;
428
348
  return new MockContext({
429
- url: request == null ? baseUrlOrRequest : new URL(request.url),
430
- request,
349
+ url: baseUrlOrRequest instanceof Request ? new URL(baseUrlOrRequest.url) : baseUrlOrRequest,
431
350
  data: contextData,
432
351
  federation: mockFederation
433
352
  });
@@ -455,81 +374,6 @@ var MockFederation = class {
455
374
  }), activity);
456
375
  }
457
376
  /**
458
- * Simulates posting an activity to a local actor outbox.
459
- * This method is specific to the mock implementation and is used for
460
- * testing purposes.
461
- *
462
- * @param identifier The identifier of the outbox owner.
463
- * @param activity The activity to post.
464
- * @returns A promise that resolves when the activity has been processed.
465
- * @since 2.2.0
466
- */
467
- async postOutboxActivity(identifier, activity) {
468
- if (!this.outboxListenersInitialized) throw new Error("MockFederation.postOutboxActivity(): setOutboxListeners() is not initialized.");
469
- let ctor = activity.constructor;
470
- let listener = this.outboxListeners.get(ctor);
471
- while (listener == null && ctor !== _fedify_vocab.Activity) {
472
- ctor = globalThis.Object.getPrototypeOf(ctor);
473
- listener = this.outboxListeners.get(ctor);
474
- }
475
- if (listener != null && this.contextData === void 0) throw new Error("MockFederation.postOutboxActivity(): contextData is not initialized. Please provide contextData through the constructor or call startQueue() before posting activities.");
476
- const origin = new URL(this.options.origin ?? "https://example.com");
477
- const routingContext = this.createContext(origin, this.contextData);
478
- const postedJson = await activity.toJsonLd({ contextLoader: routingContext.contextLoader });
479
- const request = new Request(routingContext.getOutboxUri(identifier), {
480
- method: "POST",
481
- body: JSON.stringify(postedJson),
482
- headers: { "content-type": "application/activity+json" }
483
- });
484
- const baseContext = this.createContext(request, this.contextData);
485
- const rawActivity = postedJson;
486
- const deliveryState = { delivered: false };
487
- const createMockOutboxContext = () => createOutboxContext({
488
- ...baseContext,
489
- clone: void 0,
490
- federation: this,
491
- identifier,
492
- hasDeliveredActivity: () => deliveryState.delivered,
493
- sendActivity: async (sender, recipients, outboundActivity, options) => {
494
- await baseContext.sendActivity(sender, recipients, outboundActivity, options);
495
- deliveryState.delivered = true;
496
- },
497
- forwardActivity: async (forwarder, recipients, options) => {
498
- const hasProof = (0, _fedify_fedify_sig.hasProofLike)(rawActivity);
499
- const hasLds = (0, _fedify_fedify_sig.hasSignatureLike)(rawActivity);
500
- if (options?.skipIfUnsigned && !hasProof && !hasLds) return;
501
- await baseContext.sendActivity(forwarder, recipients, activity, {
502
- ...options,
503
- rawActivity
504
- });
505
- deliveryState.delivered = true;
506
- }
507
- });
508
- const actor = await baseContext.getActor(identifier);
509
- if (actor == null) throw new Error(`Actor ${JSON.stringify(identifier)} not found.`);
510
- const authorizePredicate = this.outboxAuthorizePredicate ?? this.outboxDispatcherAuthorizePredicate;
511
- if (authorizePredicate != null && !await authorizePredicate(baseContext, identifier)) throw new Error("Unauthorized.");
512
- const expectedActorId = actor.id ?? baseContext.getActorUri(identifier);
513
- if (activity.actorIds.length < 1) {
514
- const error = /* @__PURE__ */ new Error("The posted activity has no actor.");
515
- await this.outboxListenerErrorHandler?.(createMockOutboxContext(), error);
516
- throw error;
517
- }
518
- if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
519
- const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
520
- await this.outboxListenerErrorHandler?.(createMockOutboxContext(), error);
521
- throw error;
522
- }
523
- if (listener == null) return;
524
- const context = createMockOutboxContext();
525
- try {
526
- await listener(context, activity);
527
- } catch (error) {
528
- await this.outboxListenerErrorHandler?.(context, error);
529
- throw error;
530
- }
531
- }
532
- /**
533
377
  * Clears all sent activities from the mock federation.
534
378
  * This method is specific to the mock implementation and is used for
535
379
  * testing purposes.
@@ -658,7 +502,7 @@ var MockContext = class MockContext {
658
502
  this.host = url.host;
659
503
  this.hostname = url.hostname;
660
504
  this.url = url;
661
- this.request = options.request ?? new Request(url);
505
+ this.request = new Request(url);
662
506
  this.data = options.data;
663
507
  this.federation = options.federation;
664
508
  this.documentLoader = options.documentLoader ?? (async (url) => ({
@@ -827,12 +671,11 @@ var MockContext = class MockContext {
827
671
  lookupWebFinger(_resource, _options) {
828
672
  return Promise.resolve(null);
829
673
  }
830
- sendActivity(sender, recipients, activity, options) {
674
+ sendActivity(sender, recipients, activity, _options) {
831
675
  this.sentActivities.push({
832
676
  sender,
833
677
  recipients,
834
- activity,
835
- rawActivity: options?.rawActivity
678
+ activity
836
679
  });
837
680
  if (this.federation instanceof MockFederation) {
838
681
  const queued = this.federation.queueStarted;
@@ -840,7 +683,6 @@ var MockContext = class MockContext {
840
683
  queued,
841
684
  queue: queued ? "outbox" : void 0,
842
685
  activity,
843
- rawActivity: options?.rawActivity,
844
686
  sentOrder: ++this.federation.sentCounter
845
687
  });
846
688
  }
@@ -1052,7 +894,6 @@ const getRandomKey = (prefix) => `fedify_test_${prefix}_${crypto.randomUUID()}`;
1052
894
  exports.createContext = createContext;
1053
895
  exports.createFederation = createFederation;
1054
896
  exports.createInboxContext = createInboxContext;
1055
- exports.createOutboxContext = createOutboxContext;
1056
897
  exports.createRequestContext = createRequestContext;
1057
898
  exports.getRandomKey = getRandomKey;
1058
899
  exports.testMessageQueue = testMessageQueue;
package/dist/mod.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Context, Federation, InboxContext, OutboxContext, RequestContext } from "@fedify/fedify/federation";
1
+ import { Context, Federation, InboxContext, RequestContext } from "@fedify/fedify/federation";
2
2
  import { Activity } from "@fedify/vocab";
3
3
  import { MessageQueue } from "@fedify/fedify";
4
4
 
@@ -27,12 +27,6 @@ declare function createRequestContext<TContextData>(args: Partial<RequestContext
27
27
  */
28
28
  type TestInboxContext<TContextData> = InboxContext<TContextData>;
29
29
  /**
30
- * Test-specific OutboxContext type alias.
31
- * This indirection helps avoid JSR type analyzer issues.
32
- * @since 2.2.0
33
- */
34
- type TestOutboxContext<TContextData> = OutboxContext<TContextData>;
35
- /**
36
30
  * Creates an InboxContext for testing purposes.
37
31
  * Not exported - used internally only. Public API is in mock.ts
38
32
  * @param args Partial InboxContext properties
@@ -45,19 +39,6 @@ declare function createInboxContext<TContextData>(args: Partial<InboxContext<TCo
45
39
  recipient?: string | null;
46
40
  federation: Federation<TContextData>;
47
41
  }): TestInboxContext<TContextData>;
48
- /**
49
- * Creates an OutboxContext for testing purposes.
50
- * Not exported - used internally only. Public API is in mock.ts
51
- * @param args Partial OutboxContext properties
52
- * @returns An OutboxContext instance
53
- * @since 2.2.0
54
- */
55
- declare function createOutboxContext<TContextData>(args: Partial<OutboxContext<TContextData>> & {
56
- url?: URL;
57
- data: TContextData;
58
- identifier: string;
59
- federation: Federation<TContextData>;
60
- }): TestOutboxContext<TContextData>;
61
42
  //#endregion
62
43
  //#region src/mock.d.ts
63
44
  /**
@@ -71,8 +52,6 @@ interface SentActivity {
71
52
  queue?: "inbox" | "outbox" | "fanout";
72
53
  /** The activity that was sent. */
73
54
  activity: Activity;
74
- /** The raw forwarded payload, if preserved by the caller. */
75
- rawActivity?: unknown;
76
55
  /** The order in which the activity was sent (auto-incrementing counter). */
77
56
  sentOrder: number;
78
57
  }
@@ -87,7 +66,6 @@ interface TestContext<TContextData> extends Omit<Context<TContextData>, "clone">
87
66
  sender: any;
88
67
  recipients: any;
89
68
  activity: Activity;
90
- rawActivity?: unknown;
91
69
  }>;
92
70
  reset(): void;
93
71
  }
@@ -101,7 +79,6 @@ interface TestFederation<TContextData> extends Omit<Federation<TContextData>, "c
101
79
  queueStarted: boolean;
102
80
  sentCounter: number;
103
81
  receiveActivity(activity: Activity): Promise<void>;
104
- postOutboxActivity(identifier: string, activity: Activity): Promise<void>;
105
82
  reset(): void;
106
83
  createContext(baseUrlOrRequest: URL | Request, contextData: TContextData): TestContext<TContextData>;
107
84
  }
@@ -217,4 +194,4 @@ declare function testMessageQueue<MQ extends MessageQueue>(getMessageQueue: () =
217
194
  declare function waitFor(predicate: () => boolean, timeoutMs: number): Promise<void>;
218
195
  declare const getRandomKey: (prefix: string) => string;
219
196
  //#endregion
220
- export { type TestMessageQueueOptions, createContext, createFederation, createInboxContext, createOutboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
197
+ export { type TestMessageQueueOptions, createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
package/dist/mod.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { Activity } from "@fedify/vocab";
3
- import { Context, Federation, InboxContext, OutboxContext, RequestContext } from "@fedify/fedify/federation";
3
+ import { Context, Federation, InboxContext, RequestContext } from "@fedify/fedify/federation";
4
4
  import { MessageQueue } from "@fedify/fedify";
5
5
 
6
6
  //#region src/context.d.ts
@@ -28,12 +28,6 @@ declare function createRequestContext<TContextData>(args: Partial<RequestContext
28
28
  */
29
29
  type TestInboxContext<TContextData> = InboxContext<TContextData>;
30
30
  /**
31
- * Test-specific OutboxContext type alias.
32
- * This indirection helps avoid JSR type analyzer issues.
33
- * @since 2.2.0
34
- */
35
- type TestOutboxContext<TContextData> = OutboxContext<TContextData>;
36
- /**
37
31
  * Creates an InboxContext for testing purposes.
38
32
  * Not exported - used internally only. Public API is in mock.ts
39
33
  * @param args Partial InboxContext properties
@@ -46,19 +40,6 @@ declare function createInboxContext<TContextData>(args: Partial<InboxContext<TCo
46
40
  recipient?: string | null;
47
41
  federation: Federation<TContextData>;
48
42
  }): TestInboxContext<TContextData>;
49
- /**
50
- * Creates an OutboxContext for testing purposes.
51
- * Not exported - used internally only. Public API is in mock.ts
52
- * @param args Partial OutboxContext properties
53
- * @returns An OutboxContext instance
54
- * @since 2.2.0
55
- */
56
- declare function createOutboxContext<TContextData>(args: Partial<OutboxContext<TContextData>> & {
57
- url?: URL;
58
- data: TContextData;
59
- identifier: string;
60
- federation: Federation<TContextData>;
61
- }): TestOutboxContext<TContextData>;
62
43
  //#endregion
63
44
  //#region src/mock.d.ts
64
45
  /**
@@ -72,8 +53,6 @@ interface SentActivity {
72
53
  queue?: "inbox" | "outbox" | "fanout";
73
54
  /** The activity that was sent. */
74
55
  activity: Activity;
75
- /** The raw forwarded payload, if preserved by the caller. */
76
- rawActivity?: unknown;
77
56
  /** The order in which the activity was sent (auto-incrementing counter). */
78
57
  sentOrder: number;
79
58
  }
@@ -88,7 +67,6 @@ interface TestContext<TContextData> extends Omit<Context<TContextData>, "clone">
88
67
  sender: any;
89
68
  recipients: any;
90
69
  activity: Activity;
91
- rawActivity?: unknown;
92
70
  }>;
93
71
  reset(): void;
94
72
  }
@@ -102,7 +80,6 @@ interface TestFederation<TContextData> extends Omit<Federation<TContextData>, "c
102
80
  queueStarted: boolean;
103
81
  sentCounter: number;
104
82
  receiveActivity(activity: Activity): Promise<void>;
105
- postOutboxActivity(identifier: string, activity: Activity): Promise<void>;
106
83
  reset(): void;
107
84
  createContext(baseUrlOrRequest: URL | Request, contextData: TContextData): TestContext<TContextData>;
108
85
  }
@@ -218,4 +195,4 @@ declare function testMessageQueue<MQ extends MessageQueue>(getMessageQueue: () =
218
195
  declare function waitFor(predicate: () => boolean, timeoutMs: number): Promise<void>;
219
196
  declare const getRandomKey: (prefix: string) => string;
220
197
  //#endregion
221
- export { type TestMessageQueueOptions, createContext, createFederation, createInboxContext, createOutboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
198
+ export { type TestMessageQueueOptions, createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
package/dist/mod.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import { hasProofLike, hasSignatureLike } from "@fedify/fedify/sig";
3
- import { Activity, CryptographicKey, Multikey, lookupObject, traverseCollection } from "@fedify/vocab";
2
+ import { CryptographicKey, Multikey, lookupObject, traverseCollection } from "@fedify/vocab";
4
3
  import { RouterError } from "@fedify/fedify/federation";
5
4
  import { delay } from "es-toolkit";
6
5
  import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
@@ -112,9 +111,6 @@ function createRequestContext(args) {
112
111
  * @since 1.8.0
113
112
  */
114
113
  function createInboxContext(args) {
115
- const forwardActivity = args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
116
- throw new Error("Not implemented");
117
- });
118
114
  return {
119
115
  ...createContext(args),
120
116
  clone: args.clone ?? ((data) => createInboxContext({
@@ -122,29 +118,9 @@ function createInboxContext(args) {
122
118
  data
123
119
  })),
124
120
  recipient: args.recipient ?? null,
125
- forwardActivity
126
- };
127
- }
128
- /**
129
- * Creates an OutboxContext for testing purposes.
130
- * Not exported - used internally only. Public API is in mock.ts
131
- * @param args Partial OutboxContext properties
132
- * @returns An OutboxContext instance
133
- * @since 2.2.0
134
- */
135
- function createOutboxContext(args) {
136
- const forwardActivity = args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
137
- throw new Error("Not implemented");
138
- });
139
- return {
140
- ...createContext(args),
141
- clone: args.clone ?? ((data) => createOutboxContext({
142
- ...args,
143
- data
144
- })),
145
- identifier: args.identifier,
146
- hasDeliveredActivity: args.hasDeliveredActivity ?? (() => false),
147
- forwardActivity
121
+ forwardActivity: args.forwardActivity ?? ((_params) => {
122
+ throw new Error("Not implemented");
123
+ })
148
124
  };
149
125
  }
150
126
  //#endregion
@@ -154,41 +130,17 @@ const noopTracerProvider = { getTracer: () => ({
154
130
  startSpan: () => void 0
155
131
  }) };
156
132
  /**
157
- * Helper function to expand URI templates used by the mock.
158
- * Supports the RFC 6570 operators accepted by Fedify's identifier paths.
133
+ * Helper function to expand URI templates with values.
134
+ * Supports simple placeholders like {identifier}, etc.
159
135
  * @param template The URI template pattern
160
136
  * @param values The values to substitute
161
137
  * @returns The expanded URI path
162
138
  */
163
139
  function expandUriTemplate(template, values) {
164
- return template.replace(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g, (match, operator, key) => {
165
- const value = values[key];
166
- if (value == null) return match;
167
- switch (operator) {
168
- case "": return encodeURIComponent(value);
169
- case "+": return encodeURI(value);
170
- case "#": return `#${encodeURI(value)}`;
171
- case ".": return `.${encodeURIComponent(value)}`;
172
- case "/": return `/${encodeURIComponent(value)}`;
173
- case ";": return `;${key}=${encodeURIComponent(value)}`;
174
- case "?": return `?${key}=${encodeURIComponent(value)}`;
175
- case "&": return `&${key}=${encodeURIComponent(value)}`;
176
- default: return match;
177
- }
140
+ return template.replace(/{([^}]+)}/g, (match, key) => {
141
+ return values[key] || match;
178
142
  });
179
143
  }
180
- function validateOutboxListenerPath(path, dispatcherPath) {
181
- if (!path.startsWith("/")) throw new TypeError("Path must start with a slash.");
182
- if (dispatcherPath != null && dispatcherPath !== path) throw new TypeError("Outbox listener path and outbox dispatcher path must match.");
183
- const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
184
- if (operatorMatches.some((match) => [
185
- "?",
186
- "&",
187
- "#"
188
- ].includes(match[1]) && match[2] === "identifier")) throw new TypeError("Path for outbox cannot use query or fragment expansion for identifier.");
189
- const variables = operatorMatches.map((match) => match[2]);
190
- if (variables.length !== 1 || variables[0] !== "identifier") throw new TypeError("Path for outbox must have exactly one variable named identifier.");
191
- }
192
144
  /**
193
145
  * A mock implementation of the {@link Federation} interface for unit testing.
194
146
  * This class provides a way to test Fedify applications without needing
@@ -245,17 +197,12 @@ var MockFederation = class {
245
197
  objectDispatchers = /* @__PURE__ */ new Map();
246
198
  inboxDispatcher;
247
199
  outboxDispatcher;
248
- outboxAuthorizePredicate;
249
- outboxDispatcherAuthorizePredicate;
250
- outboxListenerErrorHandler;
251
200
  followingDispatcher;
252
201
  followersDispatcher;
253
202
  likedDispatcher;
254
203
  featuredDispatcher;
255
204
  featuredTagsDispatcher;
256
205
  inboxListeners = /* @__PURE__ */ new Map();
257
- outboxListeners = /* @__PURE__ */ new Map();
258
- outboxListenersInitialized = false;
259
206
  contextData;
260
207
  receivedActivities = [];
261
208
  constructor(options = {}) {
@@ -297,17 +244,13 @@ var MockFederation = class {
297
244
  };
298
245
  }
299
246
  setOutboxDispatcher(path, dispatcher) {
300
- validateOutboxListenerPath(path, this.outboxListenersInitialized ? this.outboxPath : void 0);
301
247
  this.outboxDispatcher = dispatcher;
302
248
  this.outboxPath = path;
303
249
  return {
304
250
  setCounter: () => this,
305
251
  setFirstCursor: () => this,
306
252
  setLastCursor: () => this,
307
- authorize: (predicate) => {
308
- this.outboxDispatcherAuthorizePredicate = predicate;
309
- return this;
310
- }
253
+ authorize: () => this
311
254
  };
312
255
  }
313
256
  setFollowingDispatcher(path, dispatcher) {
@@ -385,28 +328,6 @@ var MockFederation = class {
385
328
  }
386
329
  };
387
330
  }
388
- setOutboxListeners(outboxPath) {
389
- if (this.outboxListenersInitialized) throw new TypeError("Outbox listeners already set.");
390
- validateOutboxListenerPath(outboxPath, this.outboxPath);
391
- this.outboxListenersInitialized = true;
392
- this.outboxPath = outboxPath;
393
- const self = this;
394
- return {
395
- on(type, listener) {
396
- if (self.outboxListeners.has(type)) throw new TypeError("Listener already set for this type.");
397
- self.outboxListeners.set(type, listener);
398
- return this;
399
- },
400
- onError(handler) {
401
- self.outboxListenerErrorHandler = handler;
402
- return this;
403
- },
404
- authorize(predicate) {
405
- self.outboxAuthorizePredicate = predicate;
406
- return this;
407
- }
408
- };
409
- }
410
331
  setOutboxPermanentFailureHandler(_handler) {}
411
332
  async startQueue(contextData, options) {
412
333
  this.contextData = contextData;
@@ -423,10 +344,8 @@ var MockFederation = class {
423
344
  }
424
345
  createContext(baseUrlOrRequest, contextData) {
425
346
  const mockFederation = this;
426
- const request = baseUrlOrRequest instanceof Request ? baseUrlOrRequest : null;
427
347
  return new MockContext({
428
- url: request == null ? baseUrlOrRequest : new URL(request.url),
429
- request,
348
+ url: baseUrlOrRequest instanceof Request ? new URL(baseUrlOrRequest.url) : baseUrlOrRequest,
430
349
  data: contextData,
431
350
  federation: mockFederation
432
351
  });
@@ -454,81 +373,6 @@ var MockFederation = class {
454
373
  }), activity);
455
374
  }
456
375
  /**
457
- * Simulates posting an activity to a local actor outbox.
458
- * This method is specific to the mock implementation and is used for
459
- * testing purposes.
460
- *
461
- * @param identifier The identifier of the outbox owner.
462
- * @param activity The activity to post.
463
- * @returns A promise that resolves when the activity has been processed.
464
- * @since 2.2.0
465
- */
466
- async postOutboxActivity(identifier, activity) {
467
- if (!this.outboxListenersInitialized) throw new Error("MockFederation.postOutboxActivity(): setOutboxListeners() is not initialized.");
468
- let ctor = activity.constructor;
469
- let listener = this.outboxListeners.get(ctor);
470
- while (listener == null && ctor !== Activity) {
471
- ctor = globalThis.Object.getPrototypeOf(ctor);
472
- listener = this.outboxListeners.get(ctor);
473
- }
474
- if (listener != null && this.contextData === void 0) throw new Error("MockFederation.postOutboxActivity(): contextData is not initialized. Please provide contextData through the constructor or call startQueue() before posting activities.");
475
- const origin = new URL(this.options.origin ?? "https://example.com");
476
- const routingContext = this.createContext(origin, this.contextData);
477
- const postedJson = await activity.toJsonLd({ contextLoader: routingContext.contextLoader });
478
- const request = new Request(routingContext.getOutboxUri(identifier), {
479
- method: "POST",
480
- body: JSON.stringify(postedJson),
481
- headers: { "content-type": "application/activity+json" }
482
- });
483
- const baseContext = this.createContext(request, this.contextData);
484
- const rawActivity = postedJson;
485
- const deliveryState = { delivered: false };
486
- const createMockOutboxContext = () => createOutboxContext({
487
- ...baseContext,
488
- clone: void 0,
489
- federation: this,
490
- identifier,
491
- hasDeliveredActivity: () => deliveryState.delivered,
492
- sendActivity: async (sender, recipients, outboundActivity, options) => {
493
- await baseContext.sendActivity(sender, recipients, outboundActivity, options);
494
- deliveryState.delivered = true;
495
- },
496
- forwardActivity: async (forwarder, recipients, options) => {
497
- const hasProof = hasProofLike(rawActivity);
498
- const hasLds = hasSignatureLike(rawActivity);
499
- if (options?.skipIfUnsigned && !hasProof && !hasLds) return;
500
- await baseContext.sendActivity(forwarder, recipients, activity, {
501
- ...options,
502
- rawActivity
503
- });
504
- deliveryState.delivered = true;
505
- }
506
- });
507
- const actor = await baseContext.getActor(identifier);
508
- if (actor == null) throw new Error(`Actor ${JSON.stringify(identifier)} not found.`);
509
- const authorizePredicate = this.outboxAuthorizePredicate ?? this.outboxDispatcherAuthorizePredicate;
510
- if (authorizePredicate != null && !await authorizePredicate(baseContext, identifier)) throw new Error("Unauthorized.");
511
- const expectedActorId = actor.id ?? baseContext.getActorUri(identifier);
512
- if (activity.actorIds.length < 1) {
513
- const error = /* @__PURE__ */ new Error("The posted activity has no actor.");
514
- await this.outboxListenerErrorHandler?.(createMockOutboxContext(), error);
515
- throw error;
516
- }
517
- if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
518
- const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
519
- await this.outboxListenerErrorHandler?.(createMockOutboxContext(), error);
520
- throw error;
521
- }
522
- if (listener == null) return;
523
- const context = createMockOutboxContext();
524
- try {
525
- await listener(context, activity);
526
- } catch (error) {
527
- await this.outboxListenerErrorHandler?.(context, error);
528
- throw error;
529
- }
530
- }
531
- /**
532
376
  * Clears all sent activities from the mock federation.
533
377
  * This method is specific to the mock implementation and is used for
534
378
  * testing purposes.
@@ -657,7 +501,7 @@ var MockContext = class MockContext {
657
501
  this.host = url.host;
658
502
  this.hostname = url.hostname;
659
503
  this.url = url;
660
- this.request = options.request ?? new Request(url);
504
+ this.request = new Request(url);
661
505
  this.data = options.data;
662
506
  this.federation = options.federation;
663
507
  this.documentLoader = options.documentLoader ?? (async (url) => ({
@@ -826,12 +670,11 @@ var MockContext = class MockContext {
826
670
  lookupWebFinger(_resource, _options) {
827
671
  return Promise.resolve(null);
828
672
  }
829
- sendActivity(sender, recipients, activity, options) {
673
+ sendActivity(sender, recipients, activity, _options) {
830
674
  this.sentActivities.push({
831
675
  sender,
832
676
  recipients,
833
- activity,
834
- rawActivity: options?.rawActivity
677
+ activity
835
678
  });
836
679
  if (this.federation instanceof MockFederation) {
837
680
  const queued = this.federation.queueStarted;
@@ -839,7 +682,6 @@ var MockContext = class MockContext {
839
682
  queued,
840
683
  queue: queued ? "outbox" : void 0,
841
684
  activity,
842
- rawActivity: options?.rawActivity,
843
685
  sentOrder: ++this.federation.sentCounter
844
686
  });
845
687
  }
@@ -1048,4 +890,4 @@ async function waitFor(predicate, timeoutMs) {
1048
890
  }
1049
891
  const getRandomKey = (prefix) => `fedify_test_${prefix}_${crypto.randomUUID()}`;
1050
892
  //#endregion
1051
- export { createContext, createFederation, createInboxContext, createOutboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
893
+ export { createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/testing",
3
- "version": "2.2.0-dev.802+b6b09961",
3
+ "version": "2.2.0-pr.695.16+7a782334",
4
4
  "description": "Testing utilities for Fedify applications",
5
5
  "keywords": [
6
6
  "fedify",
@@ -50,7 +50,7 @@
50
50
  "package.json"
51
51
  ],
52
52
  "peerDependencies": {
53
- "@fedify/fedify": "^2.2.0-dev.802+b6b09961"
53
+ "@fedify/fedify": "^2.2.0-pr.695.16+7a782334"
54
54
  },
55
55
  "dependencies": {
56
56
  "es-toolkit": "1.43.0"