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