@fedify/testing 2.0.0-dev.1561 → 2.0.0-dev.158

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 ADDED
@@ -0,0 +1,733 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const __fedify_fedify_federation = __toESM(require("@fedify/fedify/federation"));
25
+ const __fedify_vocab = __toESM(require("@fedify/vocab"));
26
+
27
+ //#region src/docloader.ts
28
+ const mockDocumentLoader = async (url) => ({
29
+ contextUrl: null,
30
+ document: {},
31
+ documentUrl: url
32
+ });
33
+
34
+ //#endregion
35
+ //#region src/context.ts
36
+ const noopTracerProvider$1 = { getTracer: () => ({
37
+ startActiveSpan: () => void 0,
38
+ startSpan: () => void 0
39
+ }) };
40
+ function createContext(values) {
41
+ 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, traverseCollection, lookupNodeInfo, lookupWebFinger, sendActivity, routeActivity } = values;
42
+ function throwRouteError() {
43
+ throw new __fedify_fedify_federation.RouterError("Not implemented");
44
+ }
45
+ return {
46
+ federation,
47
+ data,
48
+ origin: url.origin,
49
+ canonicalOrigin: canonicalOrigin ?? url.origin,
50
+ host: url.host,
51
+ hostname: url.hostname,
52
+ documentLoader: documentLoader ?? mockDocumentLoader,
53
+ contextLoader: contextLoader ?? mockDocumentLoader,
54
+ tracerProvider: tracerProvider ?? noopTracerProvider$1,
55
+ clone: clone ?? ((data$1) => createContext({
56
+ ...values,
57
+ data: data$1
58
+ })),
59
+ getNodeInfoUri: getNodeInfoUri ?? throwRouteError,
60
+ getActorUri: getActorUri ?? throwRouteError,
61
+ getObjectUri: getObjectUri ?? throwRouteError,
62
+ getCollectionUri: getCollectionUri ?? throwRouteError,
63
+ getOutboxUri: getOutboxUri ?? throwRouteError,
64
+ getInboxUri: getInboxUri ?? throwRouteError,
65
+ getFollowingUri: getFollowingUri ?? throwRouteError,
66
+ getFollowersUri: getFollowersUri ?? throwRouteError,
67
+ getLikedUri: getLikedUri ?? throwRouteError,
68
+ getFeaturedUri: getFeaturedUri ?? throwRouteError,
69
+ getFeaturedTagsUri: getFeaturedTagsUri ?? throwRouteError,
70
+ parseUri: parseUri ?? ((_uri) => {
71
+ throw new Error("Not implemented");
72
+ }),
73
+ getDocumentLoader: getDocumentLoader ?? ((_params) => {
74
+ throw new Error("Not implemented");
75
+ }),
76
+ getActorKeyPairs: getActorKeyPairs ?? ((_handle) => Promise.resolve([])),
77
+ lookupObject: lookupObject ?? ((uri, options = {}) => {
78
+ return (0, __fedify_vocab.lookupObject)(uri, {
79
+ documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
80
+ contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
81
+ });
82
+ }),
83
+ traverseCollection: traverseCollection ?? ((collection, options = {}) => {
84
+ return (0, __fedify_vocab.traverseCollection)(collection, {
85
+ documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
86
+ contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
87
+ });
88
+ }),
89
+ lookupNodeInfo: lookupNodeInfo ?? ((_params) => {
90
+ throw new Error("Not implemented");
91
+ }),
92
+ lookupWebFinger: lookupWebFinger ?? ((_resource, _options = {}) => {
93
+ return Promise.resolve(null);
94
+ }),
95
+ sendActivity: sendActivity ?? ((_params) => {
96
+ throw new Error("Not implemented");
97
+ }),
98
+ routeActivity: routeActivity ?? ((_params) => {
99
+ throw new Error("Not implemented");
100
+ })
101
+ };
102
+ }
103
+ /**
104
+ * Creates a RequestContext for testing purposes.
105
+ * Not exported - used internally only. Public API is in mock.ts
106
+ * @param args Partial RequestContext properties
107
+ * @returns A RequestContext instance
108
+ * @since 1.8.0
109
+ */
110
+ function createRequestContext(args) {
111
+ return {
112
+ ...createContext(args),
113
+ clone: args.clone ?? ((data) => createRequestContext({
114
+ ...args,
115
+ data
116
+ })),
117
+ request: args.request ?? new Request(args.url),
118
+ url: args.url,
119
+ getActor: args.getActor ?? (() => Promise.resolve(null)),
120
+ getObject: args.getObject ?? (() => Promise.resolve(null)),
121
+ getSignedKey: args.getSignedKey ?? (() => Promise.resolve(null)),
122
+ getSignedKeyOwner: args.getSignedKeyOwner ?? (() => Promise.resolve(null)),
123
+ sendActivity: args.sendActivity ?? ((_params) => {
124
+ throw new Error("Not implemented");
125
+ })
126
+ };
127
+ }
128
+ /**
129
+ * Creates an InboxContext for testing purposes.
130
+ * Not exported - used internally only. Public API is in mock.ts
131
+ * @param args Partial InboxContext properties
132
+ * @returns An InboxContext instance
133
+ * @since 1.8.0
134
+ */
135
+ function createInboxContext(args) {
136
+ return {
137
+ ...createContext(args),
138
+ clone: args.clone ?? ((data) => createInboxContext({
139
+ ...args,
140
+ data
141
+ })),
142
+ recipient: args.recipient ?? null,
143
+ forwardActivity: args.forwardActivity ?? ((_params) => {
144
+ throw new Error("Not implemented");
145
+ })
146
+ };
147
+ }
148
+
149
+ //#endregion
150
+ //#region src/mock.ts
151
+ const noopTracerProvider = { getTracer: () => ({
152
+ startActiveSpan: () => void 0,
153
+ startSpan: () => void 0
154
+ }) };
155
+ /**
156
+ * Helper function to expand URI templates with values.
157
+ * Supports simple placeholders like {identifier}, {handle}, etc.
158
+ * @param template The URI template pattern
159
+ * @param values The values to substitute
160
+ * @returns The expanded URI path
161
+ */
162
+ function expandUriTemplate(template, values) {
163
+ return template.replace(/{([^}]+)}/g, (match, key) => {
164
+ return values[key] || match;
165
+ });
166
+ }
167
+ /**
168
+ * A mock implementation of the {@link Federation} interface for unit testing.
169
+ * This class provides a way to test Fedify applications without needing
170
+ * a real federation setup.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * import { Create } from "@fedify/vocab";
175
+ * import { createFederation } from "@fedify/testing";
176
+ *
177
+ * // Create a mock federation with contextData
178
+ * const federation = createFederation<{ userId: string }>({
179
+ * contextData: { userId: "test-user" }
180
+ * });
181
+ *
182
+ * // Set up inbox listeners
183
+ * federation
184
+ * .setInboxListeners("/users/{identifier}/inbox")
185
+ * .on(Create, async (ctx: any, activity: any) => {
186
+ * console.log("Received:", activity);
187
+ * });
188
+ *
189
+ * // Simulate receiving an activity
190
+ * const createActivity = new Create({
191
+ * id: new URL("https://example.com/create/1"),
192
+ * actor: new URL("https://example.com/users/alice")
193
+ * });
194
+ * await federation.receiveActivity(createActivity);
195
+ * ```
196
+ *
197
+ * @template TContextData The context data to pass to the {@link Context}.
198
+ * @since 1.8.0
199
+ */
200
+ var MockFederation = class {
201
+ sentActivities = [];
202
+ queueStarted = false;
203
+ activeQueues = /* @__PURE__ */ new Set();
204
+ sentCounter = 0;
205
+ nodeInfoDispatcher;
206
+ webFingerDispatcher;
207
+ actorDispatchers = /* @__PURE__ */ new Map();
208
+ actorPath;
209
+ inboxPath;
210
+ outboxPath;
211
+ followingPath;
212
+ followersPath;
213
+ likedPath;
214
+ featuredPath;
215
+ featuredTagsPath;
216
+ nodeInfoPath;
217
+ sharedInboxPath;
218
+ objectPaths = /* @__PURE__ */ new Map();
219
+ objectDispatchers = /* @__PURE__ */ new Map();
220
+ inboxDispatcher;
221
+ outboxDispatcher;
222
+ followingDispatcher;
223
+ followersDispatcher;
224
+ likedDispatcher;
225
+ featuredDispatcher;
226
+ featuredTagsDispatcher;
227
+ inboxListeners = /* @__PURE__ */ new Map();
228
+ contextData;
229
+ receivedActivities = [];
230
+ constructor(options = {}) {
231
+ this.options = options;
232
+ this.contextData = options.contextData;
233
+ }
234
+ setNodeInfoDispatcher(path, dispatcher) {
235
+ this.nodeInfoDispatcher = dispatcher;
236
+ this.nodeInfoPath = path;
237
+ }
238
+ setWebFingerLinksDispatcher(dispatcher) {
239
+ this.webFingerDispatcher = dispatcher;
240
+ }
241
+ setActorDispatcher(path, dispatcher) {
242
+ this.actorDispatchers.set(path, dispatcher);
243
+ this.actorPath = path;
244
+ return {
245
+ setKeyPairsDispatcher: () => this,
246
+ mapHandle: () => this,
247
+ mapAlias: () => this,
248
+ authorize: () => this
249
+ };
250
+ }
251
+ setObjectDispatcher(cls, path, dispatcher) {
252
+ this.objectDispatchers.set(path, dispatcher);
253
+ this.objectPaths.set(cls.typeId.href, path);
254
+ return { authorize: () => this };
255
+ }
256
+ setInboxDispatcher(_path, dispatcher) {
257
+ this.inboxDispatcher = dispatcher;
258
+ return {
259
+ setCounter: () => this,
260
+ setFirstCursor: () => this,
261
+ setLastCursor: () => this,
262
+ authorize: () => this
263
+ };
264
+ }
265
+ setOutboxDispatcher(path, dispatcher) {
266
+ this.outboxDispatcher = dispatcher;
267
+ this.outboxPath = path;
268
+ return {
269
+ setCounter: () => this,
270
+ setFirstCursor: () => this,
271
+ setLastCursor: () => this,
272
+ authorize: () => this
273
+ };
274
+ }
275
+ setFollowingDispatcher(path, dispatcher) {
276
+ this.followingDispatcher = dispatcher;
277
+ this.followingPath = path;
278
+ return {
279
+ setCounter: () => this,
280
+ setFirstCursor: () => this,
281
+ setLastCursor: () => this,
282
+ authorize: () => this
283
+ };
284
+ }
285
+ setFollowersDispatcher(path, dispatcher) {
286
+ this.followersDispatcher = dispatcher;
287
+ this.followersPath = path;
288
+ return {
289
+ setCounter: () => this,
290
+ setFirstCursor: () => this,
291
+ setLastCursor: () => this,
292
+ authorize: () => this
293
+ };
294
+ }
295
+ setLikedDispatcher(path, dispatcher) {
296
+ this.likedDispatcher = dispatcher;
297
+ this.likedPath = path;
298
+ return {
299
+ setCounter: () => this,
300
+ setFirstCursor: () => this,
301
+ setLastCursor: () => this,
302
+ authorize: () => this
303
+ };
304
+ }
305
+ setFeaturedDispatcher(path, dispatcher) {
306
+ this.featuredDispatcher = dispatcher;
307
+ this.featuredPath = path;
308
+ return {
309
+ setCounter: () => this,
310
+ setFirstCursor: () => this,
311
+ setLastCursor: () => this,
312
+ authorize: () => this
313
+ };
314
+ }
315
+ setFeaturedTagsDispatcher(path, dispatcher) {
316
+ this.featuredTagsDispatcher = dispatcher;
317
+ this.featuredTagsPath = path;
318
+ return {
319
+ setCounter: () => this,
320
+ setFirstCursor: () => this,
321
+ setLastCursor: () => this,
322
+ authorize: () => this
323
+ };
324
+ }
325
+ setInboxListeners(inboxPath, sharedInboxPath) {
326
+ this.inboxPath = inboxPath;
327
+ this.sharedInboxPath = sharedInboxPath;
328
+ const self = this;
329
+ return {
330
+ on(type, listener) {
331
+ const typeName = type.name;
332
+ if (!self.inboxListeners.has(typeName)) self.inboxListeners.set(typeName, []);
333
+ self.inboxListeners.get(typeName).push(listener);
334
+ return this;
335
+ },
336
+ onError() {
337
+ return this;
338
+ },
339
+ setSharedKeyDispatcher() {
340
+ return this;
341
+ },
342
+ withIdempotency() {
343
+ return this;
344
+ }
345
+ };
346
+ }
347
+ async startQueue(contextData, options) {
348
+ this.contextData = contextData;
349
+ this.queueStarted = true;
350
+ if (options?.queue) this.activeQueues.add(options.queue);
351
+ else {
352
+ this.activeQueues.add("inbox");
353
+ this.activeQueues.add("outbox");
354
+ this.activeQueues.add("fanout");
355
+ }
356
+ }
357
+ async processQueuedTask(contextData, _message) {
358
+ this.contextData = contextData;
359
+ }
360
+ createContext(baseUrlOrRequest, contextData) {
361
+ const mockFederation = this;
362
+ const url = baseUrlOrRequest instanceof Request ? new URL(baseUrlOrRequest.url) : baseUrlOrRequest;
363
+ return new MockContext({
364
+ url,
365
+ data: contextData,
366
+ federation: mockFederation
367
+ });
368
+ }
369
+ async fetch(request, options) {
370
+ if (options.onNotFound) return options.onNotFound(request);
371
+ return new Response("Not Found", { status: 404 });
372
+ }
373
+ /**
374
+ * Simulates receiving an activity. This method is specific to the mock
375
+ * implementation and is used for testing purposes.
376
+ *
377
+ * @param activity The activity to receive.
378
+ * @returns A promise that resolves when the activity has been processed.
379
+ * @since 1.8.0
380
+ */
381
+ async receiveActivity(activity) {
382
+ this.receivedActivities.push(activity);
383
+ const typeName = activity.constructor.name;
384
+ const listeners = this.inboxListeners.get(typeName) || [];
385
+ if (listeners.length > 0 && this.contextData === void 0) throw new Error("MockFederation.receiveActivity(): contextData is not initialized. Please provide contextData through the constructor or call startQueue() before receiving activities.");
386
+ for (const listener of listeners) {
387
+ const context = createInboxContext({
388
+ data: this.contextData,
389
+ federation: this
390
+ });
391
+ await listener(context, activity);
392
+ }
393
+ }
394
+ /**
395
+ * Clears all sent activities from the mock federation.
396
+ * This method is specific to the mock implementation and is used for
397
+ * testing purposes.
398
+ *
399
+ * @since 1.8.0
400
+ */
401
+ reset() {
402
+ this.sentActivities = [];
403
+ }
404
+ setCollectionDispatcher(_name, _itemType, _path, _dispatcher) {
405
+ return {
406
+ setCounter: () => this,
407
+ setFirstCursor: () => this,
408
+ setLastCursor: () => this,
409
+ authorize: () => this
410
+ };
411
+ }
412
+ setOrderedCollectionDispatcher(_name, _itemType, _path, _dispatcher) {
413
+ return {
414
+ setCounter: () => this,
415
+ setFirstCursor: () => this,
416
+ setLastCursor: () => this,
417
+ authorize: () => this
418
+ };
419
+ }
420
+ };
421
+ /**
422
+ * Creates a mock Federation instance for testing purposes.
423
+ *
424
+ * @template TContextData The type of context data to use
425
+ * @param options Optional configuration for the mock federation
426
+ * @returns A Federation instance that can be used for testing
427
+ * @since 1.9.1
428
+ *
429
+ * @example
430
+ * ```typescript
431
+ * import { Create } from "@fedify/vocab";
432
+ * import { createFederation } from "@fedify/testing";
433
+ *
434
+ * // Create a mock federation with contextData
435
+ * const federation = createFederation<{ userId: string }>({
436
+ * contextData: { userId: "test-user" }
437
+ * });
438
+ *
439
+ * // Set up inbox listeners
440
+ * federation
441
+ * .setInboxListeners("/users/{identifier}/inbox")
442
+ * .on(Create, async (ctx, activity) => {
443
+ * console.log("Received:", activity);
444
+ * });
445
+ *
446
+ * // Simulate receiving an activity
447
+ * const createActivity = new Create({
448
+ * id: new URL("https://example.com/create/1"),
449
+ * actor: new URL("https://example.com/users/alice")
450
+ * });
451
+ * await federation.receiveActivity(createActivity);
452
+ *
453
+ * // Check sent activities
454
+ * console.log(federation.sentActivities);
455
+ * ```
456
+ */
457
+ function createFederation(options = {}) {
458
+ return new MockFederation(options);
459
+ }
460
+ /**
461
+ * A mock implementation of the {@link Context} interface for unit testing.
462
+ * This class provides a way to test Fedify applications without needing
463
+ * a real federation context.
464
+ *
465
+ * Note: This class is not exported from the public API to avoid JSR type
466
+ * analyzer issues. The MockContext class has complex type dependencies that
467
+ * can cause JSR's type analyzer to hang during processing (issue #468).
468
+ * Use {@link MockFederation.createContext}, {@link createContext},
469
+ * {@link createRequestContext}, or {@link createInboxContext} instead.
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * import { Person, Create } from "@fedify/vocab";
474
+ * import { createFederation } from "@fedify/testing";
475
+ *
476
+ * // Create a mock federation and context
477
+ * const federation = createFederation<{ userId: string }>();
478
+ * const context = federation.createContext(
479
+ * new URL("https://example.com"),
480
+ * { userId: "test-user" }
481
+ * );
482
+ *
483
+ * // Send an activity
484
+ * const recipient = new Person({ id: new URL("https://example.com/users/bob") });
485
+ * const activity = new Create({
486
+ * id: new URL("https://example.com/create/1"),
487
+ * actor: new URL("https://example.com/users/alice")
488
+ * });
489
+ * await context.sendActivity(
490
+ * { identifier: "alice" },
491
+ * recipient,
492
+ * activity
493
+ * );
494
+ *
495
+ * // Check sent activities from the federation
496
+ * const sent = federation.sentActivities;
497
+ * console.log(sent[0].activity);
498
+ * ```
499
+ *
500
+ * @template TContextData The context data to pass to the {@link Context}.
501
+ * @since 1.8.0
502
+ */
503
+ var MockContext = class MockContext {
504
+ origin;
505
+ canonicalOrigin;
506
+ host;
507
+ hostname;
508
+ data;
509
+ federation;
510
+ documentLoader;
511
+ contextLoader;
512
+ tracerProvider;
513
+ request;
514
+ url;
515
+ sentActivities = [];
516
+ constructor(options) {
517
+ const url = options.url ?? new URL("https://example.com");
518
+ this.origin = url.origin;
519
+ this.canonicalOrigin = url.origin;
520
+ this.host = url.host;
521
+ this.hostname = url.hostname;
522
+ this.url = url;
523
+ this.request = new Request(url);
524
+ this.data = options.data;
525
+ this.federation = options.federation;
526
+ this.documentLoader = options.documentLoader ?? (async (url$1) => ({
527
+ contextUrl: null,
528
+ document: {},
529
+ documentUrl: url$1
530
+ }));
531
+ this.contextLoader = options.contextLoader ?? this.documentLoader;
532
+ this.tracerProvider = options.tracerProvider ?? noopTracerProvider;
533
+ }
534
+ getActor(_handle) {
535
+ return Promise.resolve(null);
536
+ }
537
+ getObject(_cls, _values) {
538
+ return Promise.resolve(null);
539
+ }
540
+ getSignedKey() {
541
+ return Promise.resolve(null);
542
+ }
543
+ getSignedKeyOwner() {
544
+ return Promise.resolve(null);
545
+ }
546
+ clone(data) {
547
+ return new MockContext({
548
+ url: this.url,
549
+ data,
550
+ federation: this.federation,
551
+ documentLoader: this.documentLoader,
552
+ contextLoader: this.contextLoader,
553
+ tracerProvider: this.tracerProvider
554
+ });
555
+ }
556
+ getNodeInfoUri() {
557
+ if (this.federation instanceof MockFederation && this.federation.nodeInfoPath) return new URL(this.federation.nodeInfoPath, this.origin);
558
+ return new URL("/nodeinfo/2.0", this.origin);
559
+ }
560
+ getActorUri(identifier) {
561
+ if (this.federation instanceof MockFederation && this.federation.actorPath) {
562
+ const path = expandUriTemplate(this.federation.actorPath, {
563
+ identifier,
564
+ handle: identifier
565
+ });
566
+ return new URL(path, this.origin);
567
+ }
568
+ return new URL(`/users/${identifier}`, this.origin);
569
+ }
570
+ getObjectUri(cls, values) {
571
+ if (this.federation instanceof MockFederation) {
572
+ const pathTemplate = this.federation.objectPaths.get(cls.typeId.href);
573
+ if (pathTemplate) {
574
+ const path$1 = expandUriTemplate(pathTemplate, values);
575
+ return new URL(path$1, this.origin);
576
+ }
577
+ }
578
+ const path = globalThis.Object.entries(values).map(([key, value]) => `${key}/${value}`).join("/");
579
+ return new URL(`/objects/${cls.name.toLowerCase()}/${path}`, this.origin);
580
+ }
581
+ getOutboxUri(identifier) {
582
+ if (this.federation instanceof MockFederation && this.federation.outboxPath) {
583
+ const path = expandUriTemplate(this.federation.outboxPath, {
584
+ identifier,
585
+ handle: identifier
586
+ });
587
+ return new URL(path, this.origin);
588
+ }
589
+ return new URL(`/users/${identifier}/outbox`, this.origin);
590
+ }
591
+ getInboxUri(identifier) {
592
+ if (identifier) {
593
+ if (this.federation instanceof MockFederation && this.federation.inboxPath) {
594
+ const path = expandUriTemplate(this.federation.inboxPath, {
595
+ identifier,
596
+ handle: identifier
597
+ });
598
+ return new URL(path, this.origin);
599
+ }
600
+ return new URL(`/users/${identifier}/inbox`, this.origin);
601
+ }
602
+ if (this.federation instanceof MockFederation && this.federation.sharedInboxPath) return new URL(this.federation.sharedInboxPath, this.origin);
603
+ return new URL("/inbox", this.origin);
604
+ }
605
+ getFollowingUri(identifier) {
606
+ if (this.federation instanceof MockFederation && this.federation.followingPath) {
607
+ const path = expandUriTemplate(this.federation.followingPath, {
608
+ identifier,
609
+ handle: identifier
610
+ });
611
+ return new URL(path, this.origin);
612
+ }
613
+ return new URL(`/users/${identifier}/following`, this.origin);
614
+ }
615
+ getFollowersUri(identifier) {
616
+ if (this.federation instanceof MockFederation && this.federation.followersPath) {
617
+ const path = expandUriTemplate(this.federation.followersPath, {
618
+ identifier,
619
+ handle: identifier
620
+ });
621
+ return new URL(path, this.origin);
622
+ }
623
+ return new URL(`/users/${identifier}/followers`, this.origin);
624
+ }
625
+ getLikedUri(identifier) {
626
+ if (this.federation instanceof MockFederation && this.federation.likedPath) {
627
+ const path = expandUriTemplate(this.federation.likedPath, {
628
+ identifier,
629
+ handle: identifier
630
+ });
631
+ return new URL(path, this.origin);
632
+ }
633
+ return new URL(`/users/${identifier}/liked`, this.origin);
634
+ }
635
+ getFeaturedUri(identifier) {
636
+ if (this.federation instanceof MockFederation && this.federation.featuredPath) {
637
+ const path = expandUriTemplate(this.federation.featuredPath, {
638
+ identifier,
639
+ handle: identifier
640
+ });
641
+ return new URL(path, this.origin);
642
+ }
643
+ return new URL(`/users/${identifier}/featured`, this.origin);
644
+ }
645
+ getFeaturedTagsUri(identifier) {
646
+ if (this.federation instanceof MockFederation && this.federation.featuredTagsPath) {
647
+ const path = expandUriTemplate(this.federation.featuredTagsPath, {
648
+ identifier,
649
+ handle: identifier
650
+ });
651
+ return new URL(path, this.origin);
652
+ }
653
+ return new URL(`/users/${identifier}/tags`, this.origin);
654
+ }
655
+ getCollectionUri(_name, values) {
656
+ const path = globalThis.Object.entries(values).map(([key, value]) => `${key}/${value}`).join("/");
657
+ return new URL(`/collections/${String(_name)}/${path}`, this.origin);
658
+ }
659
+ parseUri(uri) {
660
+ if (uri.pathname.startsWith("/users/")) {
661
+ const parts = uri.pathname.split("/");
662
+ if (parts.length >= 3) return {
663
+ type: "actor",
664
+ identifier: parts[2],
665
+ handle: parts[2]
666
+ };
667
+ }
668
+ return null;
669
+ }
670
+ getActorKeyPairs(_identifier) {
671
+ return Promise.resolve([]);
672
+ }
673
+ getDocumentLoader(params) {
674
+ if ("keyId" in params) return this.documentLoader;
675
+ return Promise.resolve(this.documentLoader);
676
+ }
677
+ lookupObject(_uri, _options) {
678
+ return Promise.resolve(null);
679
+ }
680
+ traverseCollection(_collection, _options) {
681
+ return { async *[Symbol.asyncIterator]() {} };
682
+ }
683
+ lookupNodeInfo(_url, _options) {
684
+ return Promise.resolve(void 0);
685
+ }
686
+ lookupWebFinger(_resource, _options) {
687
+ return Promise.resolve(null);
688
+ }
689
+ sendActivity(sender, recipients, activity, _options) {
690
+ this.sentActivities.push({
691
+ sender,
692
+ recipients,
693
+ activity
694
+ });
695
+ if (this.federation instanceof MockFederation) {
696
+ const queued = this.federation.queueStarted;
697
+ this.federation.sentActivities.push({
698
+ queued,
699
+ queue: queued ? "outbox" : void 0,
700
+ activity,
701
+ sentOrder: ++this.federation.sentCounter
702
+ });
703
+ }
704
+ return Promise.resolve();
705
+ }
706
+ routeActivity(_recipient, _activity, _options) {
707
+ return Promise.resolve(true);
708
+ }
709
+ /**
710
+ * Gets all activities that have been sent through this mock context.
711
+ * This method is specific to the mock implementation and is used for
712
+ * testing purposes.
713
+ *
714
+ * @returns An array of sent activity records.
715
+ */
716
+ getSentActivities() {
717
+ return [...this.sentActivities];
718
+ }
719
+ /**
720
+ * Clears all sent activities from the mock context.
721
+ * This method is specific to the mock implementation and is used for
722
+ * testing purposes.
723
+ */
724
+ reset() {
725
+ this.sentActivities = [];
726
+ }
727
+ };
728
+
729
+ //#endregion
730
+ exports.createContext = createContext;
731
+ exports.createFederation = createFederation;
732
+ exports.createInboxContext = createInboxContext;
733
+ exports.createRequestContext = createRequestContext;