@fedify/fedify 2.3.0-dev.994 → 2.3.0-pr.809.37
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/README.md +16 -7
- package/dist/{assert-DikXweDx.mjs → assert-OguE97r2.mjs} +1 -1
- package/dist/{assert_instance_of-C4Ri6VuN.mjs → assert_instance_of-DBC5X09g.mjs} +1 -1
- package/dist/{assert_not_equals--wG9hV7u.mjs → assert_not_equals-DkVK8oqV.mjs} +1 -1
- package/dist/{assert_rejects-B-qJtC9Z.mjs → assert_rejects-DN60FHPX.mjs} +28 -3
- package/dist/{assert_strict_equals-Dmjbg-bA.mjs → assert_strict_equals-XEgZAlrj.mjs} +1 -1
- package/dist/{assert_throws-4NwKEy2q.mjs → assert_throws-BOkhLGYc.mjs} +1 -1
- package/dist/{builder-yAlpiIqP.mjs → builder-Dl5Sdmc9.mjs} +96 -42
- package/dist/{chunk-nlSIicah.js → chunk-CRNNMoPX.js} +2 -2
- package/dist/circuit-breaker-CSWsyoef.mjs +337 -0
- package/dist/{client-z-8dc-e1.d.cts → client-CAM_bQXx.d.cts} +1 -0
- package/dist/{client-AtlibPOU.d.ts → client-CSddvgWN.d.ts} +1 -2
- package/dist/compat/mod.d.cts +2 -1
- package/dist/compat/mod.d.ts +2 -3
- package/dist/compat/mod.js +3 -3
- package/dist/compat/outgoing-jsonld.test.mjs +4 -4
- package/dist/compat/public-audience.test.mjs +4 -4
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/{context-BzH2-ajs.d.ts → context-BBVLF7lx.d.cts} +342 -269
- package/dist/{context-DJGagtNd.d.cts → context-BU6jSQdo.d.ts} +344 -266
- package/dist/{context-Dk_tacqz.mjs → context-DVoTs_wM.mjs} +64 -5
- package/dist/{deno-XBVlKnOX.mjs → deno-BID9qTPk.mjs} +1 -1
- package/dist/{docloader-DPp-X6gk.mjs → docloader-BYFuVwwi.mjs} +4 -4
- package/dist/{esm-DVILvP5e.mjs → esm-vrlUxr60.mjs} +4 -7
- package/dist/federation/builder.test.mjs +163 -11
- package/dist/federation/circuit-breaker.test.d.mts +2 -0
- package/dist/federation/circuit-breaker.test.mjs +446 -0
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +1770 -29
- package/dist/federation/idempotency.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +4 -4
- package/dist/federation/keycache.test.mjs +5 -5
- package/dist/federation/kv.test.mjs +2 -2
- package/dist/federation/metrics.test.d.mts +2 -0
- package/dist/federation/metrics.test.mjs +634 -0
- package/dist/federation/middleware.test.mjs +4036 -146
- package/dist/federation/mod.cjs +195 -14
- package/dist/federation/mod.d.cts +6 -4
- package/dist/federation/mod.d.ts +6 -6
- package/dist/federation/mod.js +192 -14
- package/dist/federation/mq.test.mjs +176 -15
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/retry.test.mjs +3 -3
- package/dist/federation/router.test.mjs +190 -9
- package/dist/federation/send.test.mjs +153 -15
- package/dist/federation/temporal.test.d.mts +2 -0
- package/dist/federation/temporal.test.mjs +71 -0
- package/dist/federation/webfinger.test.mjs +150 -5
- package/dist/{http-B4rrzxtg.js → http-B47Jg9iX.js} +994 -64
- package/dist/{http-CSlLA54v.mjs → http-CO59u1Yk.mjs} +146 -47
- package/dist/{http-BrGqJDXL.cjs → http-CjZwUPiT.cjs} +1099 -61
- package/dist/{http-aQzN9Ayi.d.ts → http-VyDTd4G3.d.cts} +15 -3
- package/dist/{http-CrGuipxe.d.cts → http-lf8Hsd91.d.ts} +15 -1
- package/dist/{key-Bzx--sHD.mjs → key-Dqdlo3mG.mjs} +43 -18
- package/dist/{kv-CbLNp3zQ.d.cts → kv-D6hNiMTK.d.ts} +1 -0
- package/dist/{kv-cache-DI-Sk7-I.cjs → kv-cache-BQ1-zDKX.cjs} +20 -3
- package/dist/{kv-cache-EMIqoIuB.js → kv-cache-Dh2ZH9Gr.js} +20 -3
- package/dist/{kv-cache-U__xU4qR.mjs → kv-cache-DhLkLv0P.mjs} +21 -3
- package/dist/{kv-GFYnFoOl.d.ts → kv-gJ8LYbxX.d.cts} +1 -3
- package/dist/ld-JbCByP5P.mjs +626 -0
- package/dist/metrics-ColY3QKj.mjs +815 -0
- package/dist/{middleware-zjZ6AFCW.mjs → middleware-Buqzuiaf.mjs} +1 -1
- package/dist/{middleware-An4DjES4.cjs → middleware-CWQ3Yi07.cjs} +2318 -623
- package/dist/{middleware-K1g6bEkV.mjs → middleware-Cg4EyWsq.mjs} +1639 -373
- package/dist/{middleware-nidH7VZH.js → middleware-iAJY2aU-.js} +2260 -556
- package/dist/{mod-CR8soWa9.d.ts → mod-B0hW12_O.d.cts} +26 -4
- package/dist/mod-C0F6kvgS.d.cts +182 -0
- package/dist/{mod-Cr3f-ACa.d.cts → mod-COIAjwRS.d.ts} +26 -2
- package/dist/{mod-CMEbIaNh.d.cts → mod-DFvNJcNb.d.ts} +56 -4
- package/dist/mod-vPYVoa5n.d.ts +182 -0
- package/dist/{mod-CLgIXe9w.d.ts → mod-yvIXFAEi.d.cts} +56 -6
- package/dist/mod.cjs +9 -6
- package/dist/mod.d.cts +12 -10
- package/dist/mod.d.ts +12 -12
- package/dist/mod.js +11 -11
- package/dist/mq-D-nlpY04.d.ts +208 -0
- package/dist/mq-D8uSFzxe.d.cts +208 -0
- package/dist/{negotiation-SQvQgUqe.mjs → negotiation-DDstyBvc.mjs} +29 -0
- package/dist/nodeinfo/client.test.mjs +4 -4
- package/dist/nodeinfo/handler.test.mjs +4 -4
- package/dist/nodeinfo/mod.d.cts +2 -1
- package/dist/nodeinfo/mod.d.ts +2 -3
- package/dist/nodeinfo/mod.js +3 -3
- package/dist/nodeinfo/types.test.mjs +3 -3
- package/dist/otel/exporter.test.mjs +28 -25
- package/dist/otel/mod.cjs +6 -5
- package/dist/otel/mod.d.cts +5 -3
- package/dist/otel/mod.d.ts +5 -5
- package/dist/otel/mod.js +8 -7
- package/dist/{outgoing-jsonld-CNmZLixq.mjs → outgoing-jsonld-L_DbOaFe.mjs} +2 -2
- package/dist/{owner-C-tqfvxn.mjs → owner-B4LgOmzV.mjs} +2 -2
- package/dist/{owner-CptqhsOy.d.cts → owner-CnngXDNJ.d.ts} +2 -1
- package/dist/{owner-74ARJ5TL.d.ts → owner-DEvZuyOE.d.cts} +2 -3
- package/dist/{proof-BlmRPzrK.mjs → proof-BoSGOq9q.mjs} +26 -8
- package/dist/{proof-Bbbwf8_x.js → proof-CzhZuawt.js} +378 -15
- package/dist/{proof-BW89QMVB.cjs → proof-DvMI-3vq.cjs} +432 -15
- package/dist/runtime/mod.d.cts +1 -0
- package/dist/runtime/mod.d.ts +1 -2
- package/dist/runtime/mod.js +3 -3
- package/dist/{send-05pJLcb6.mjs → send-ByK1Q7DK.mjs} +72 -26
- package/dist/sig/http.test.mjs +356 -33
- package/dist/sig/key.test.mjs +103 -6
- package/dist/sig/ld.test.mjs +696 -7
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +4 -3
- package/dist/sig/mod.d.ts +4 -5
- package/dist/sig/mod.js +4 -4
- package/dist/sig/owner.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +169 -8
- package/dist/{std__assert-CRDpx_HF.mjs → std__assert-BBjXFNOb.mjs} +5 -30
- package/dist/temporal-LpjyAjKb.mjs +95 -0
- package/dist/testing/mod.d.mts +110 -10
- package/dist/testing/mod.mjs +1 -2
- package/dist/{transformers-ve6e2xcg.js → transformers-BGMIq1cs.js} +2 -2
- package/dist/{types-hvL8ElAs.js → types-CAY3OdLq.js} +2 -2
- package/dist/utils/docloader.test.mjs +6 -6
- package/dist/utils/kv-cache.test.mjs +67 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +2 -1
- package/dist/utils/mod.d.ts +2 -3
- package/dist/utils/mod.js +3 -3
- package/dist/vocab/cjs.test.mjs +1 -1
- package/dist/vocab/mod.d.cts +1 -0
- package/dist/vocab/mod.d.ts +1 -2
- package/dist/vocab/mod.js +2 -2
- package/package.json +16 -16
- package/dist/ld-DR78beiv.mjs +0 -279
- package/dist/middleware-BFBaR0mF.cjs +0 -4
- package/dist/mod-2d12ffz3.d.ts +0 -64
- package/dist/mod-D35TRn09.d.cts +0 -62
- package/dist/router-CrMLXoOr.mjs +0 -114
- package/dist/{activity-listener-ell7W1s9.mjs → activity-listener-tztVvlNb.mjs} +0 -0
- package/dist/{assert_equals-Ew3jOFa3.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
- package/dist/{client-D_1QpnWt.mjs → client-ByXmQhYD.mjs} +1 -1
- package/dist/{collection-D-HqUuA2.mjs → collection-Cc3DVAhE.mjs} +0 -0
- package/dist/{keycache-EGATflN-.mjs → keycache-BeU0LCII.mjs} +0 -0
- package/dist/{public-audience-DYFHzm_c.mjs → public-audience-Cvbr2Gzt.mjs} +1 -1
- /package/dist/{retry-bMXBL97A.mjs → retry-CXg_MBI-.mjs} +0 -0
|
@@ -1,14 +1,72 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { getLogger } from "@logtape/logtape";
|
|
5
|
+
import "@opentelemetry/sdk-trace-base";
|
|
6
|
+
import "@opentelemetry/core";
|
|
7
|
+
import { RouterError } from "@fedify/uri-template";
|
|
6
8
|
import { lookupObject, traverseCollection } from "@fedify/vocab";
|
|
7
9
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
8
|
-
import { trace } from "@opentelemetry/api";
|
|
10
|
+
import { metrics, trace } from "@opentelemetry/api";
|
|
11
|
+
//#region ../fixture/dist/mod.js
|
|
12
|
+
const logger = getLogger(["fixture", "docloader"]);
|
|
13
|
+
/**
|
|
14
|
+
* A mock of the document loader. This does not make any actual HTTP requests
|
|
15
|
+
* towards the remote server, but looks up the local fixture files instead.
|
|
16
|
+
*
|
|
17
|
+
* For instance, `mockDocumentLoader("http://example.com/foo/bar")` will look up
|
|
18
|
+
* the file `testing/fixtures/http/example.com/foo/bar` (no suffix) and return
|
|
19
|
+
* its content as the response.
|
|
20
|
+
*/
|
|
21
|
+
async function mockDocumentLoader(resource, _options) {
|
|
22
|
+
const url = new URL(resource);
|
|
23
|
+
if ("navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers") {
|
|
24
|
+
const testUrl = new URL(url);
|
|
25
|
+
testUrl.hostname += ".test";
|
|
26
|
+
const resp = await fetch(testUrl);
|
|
27
|
+
if (resp.ok) {
|
|
28
|
+
const document = await resp.json();
|
|
29
|
+
logger.debug("Successfully fetched fixture {resource}: {status} {statusText}\n{body}", {
|
|
30
|
+
resource,
|
|
31
|
+
status: resp.status,
|
|
32
|
+
statusText: resp.statusText,
|
|
33
|
+
body: document
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
contextUrl: null,
|
|
37
|
+
document,
|
|
38
|
+
documentUrl: resource
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const error = await resp.text();
|
|
42
|
+
logger.error("Failed to fetch fixture {resource}: {error}", {
|
|
43
|
+
resource,
|
|
44
|
+
error
|
|
45
|
+
});
|
|
46
|
+
throw new Error(error);
|
|
47
|
+
}
|
|
48
|
+
const fixturePath = `${"Deno" in globalThis ? "." : "@fedify/fixture"}/fixtures/${url.host}${url.pathname}.json`;
|
|
49
|
+
let document;
|
|
50
|
+
try {
|
|
51
|
+
document = (await import(fixturePath, { with: { type: "json" } })).default;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.error("Failed to read fixture file {path}: {error}", {
|
|
54
|
+
path: fixturePath,
|
|
55
|
+
error
|
|
56
|
+
});
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
contextUrl: null,
|
|
61
|
+
document,
|
|
62
|
+
documentUrl: resource
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const testDefinitions = [];
|
|
66
|
+
//#endregion
|
|
9
67
|
//#region src/testing/context.ts
|
|
10
68
|
function createContext(values) {
|
|
11
|
-
const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getCollectionUri, getOutboxUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity } = values;
|
|
69
|
+
const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, meterProvider, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getCollectionUri, getOutboxUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity } = values;
|
|
12
70
|
function throwRouterError() {
|
|
13
71
|
throw new RouterError("Not implemented");
|
|
14
72
|
}
|
|
@@ -21,6 +79,7 @@ function createContext(values) {
|
|
|
21
79
|
hostname: url.hostname,
|
|
22
80
|
documentLoader: documentLoader ?? mockDocumentLoader,
|
|
23
81
|
contextLoader: contextLoader ?? mockDocumentLoader,
|
|
82
|
+
meterProvider: meterProvider ?? metrics.getMeterProvider(),
|
|
24
83
|
tracerProvider: tracerProvider ?? trace.getTracerProvider(),
|
|
25
84
|
clone: clone ?? ((data) => createContext({
|
|
26
85
|
...values,
|
|
@@ -117,4 +176,4 @@ function createOutboxContext(args) {
|
|
|
117
176
|
};
|
|
118
177
|
}
|
|
119
178
|
//#endregion
|
|
120
|
-
export { createOutboxContext as n, createRequestContext as r, createInboxContext as t };
|
|
179
|
+
export { testDefinitions as i, createOutboxContext as n, createRequestContext as r, createInboxContext as t };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { o as validateCryptoKey } from "./key-
|
|
5
|
-
import { n as doubleKnock } from "./http-
|
|
6
|
-
import { curry } from "es-toolkit";
|
|
7
|
-
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
4
|
+
import { o as validateCryptoKey } from "./key-Dqdlo3mG.mjs";
|
|
5
|
+
import { n as doubleKnock } from "./http-CO59u1Yk.mjs";
|
|
8
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
|
+
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
8
|
+
import { curry } from "es-toolkit";
|
|
9
9
|
//#region src/utils/docloader.ts
|
|
10
10
|
const logger = getLogger([
|
|
11
11
|
"fedify",
|
|
@@ -9,7 +9,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
9
9
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
10
|
var __getProtoOf = Object.getPrototypeOf;
|
|
11
11
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
12
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
13
13
|
var __copyProps = (to, from, except, desc) => {
|
|
14
14
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
15
15
|
key = keys[i];
|
|
@@ -25,8 +25,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
enumerable: true
|
|
26
26
|
}) : target, mod));
|
|
27
27
|
//#endregion
|
|
28
|
-
//#region ../../node_modules/.pnpm/
|
|
29
|
-
var
|
|
28
|
+
//#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
|
|
29
|
+
var import_glob_to_regexp = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
30
30
|
module.exports = function(glob, opts) {
|
|
31
31
|
if (typeof glob !== "string") throw new TypeError("Expected a string");
|
|
32
32
|
var str = String(glob);
|
|
@@ -97,9 +97,7 @@ var require_glob_to_regexp = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
97
97
|
if (!flags || !~flags.indexOf("g")) reStr = "^" + reStr + "$";
|
|
98
98
|
return new RegExp(reStr, flags);
|
|
99
99
|
};
|
|
100
|
-
}));
|
|
101
|
-
//#endregion
|
|
102
|
-
//#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
|
|
100
|
+
})))(), 1);
|
|
103
101
|
/**
|
|
104
102
|
* @param {string|RegExp} input The route pattern
|
|
105
103
|
* @param {boolean} [loose] Allow open-ended matching. Ignored with `RegExp` input.
|
|
@@ -454,7 +452,6 @@ function normalizeHeaders(headers) {
|
|
|
454
452
|
}
|
|
455
453
|
//#endregion
|
|
456
454
|
//#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/Matchers.js
|
|
457
|
-
var import_glob_to_regexp = /* @__PURE__ */ __toESM(require_glob_to_regexp(), 1);
|
|
458
455
|
const isUrlMatcher = (matcher) => matcher instanceof RegExp || typeof matcher === "string" || typeof matcher === "object" && "href" in matcher;
|
|
459
456
|
const isFunctionMatcher = (matcher) => typeof matcher === "function";
|
|
460
457
|
const stringMatchers = {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
+
import { r as assertExists } from "../std__assert-BBjXFNOb.mjs";
|
|
6
|
+
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|
|
8
7
|
import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { r as createFederationBuilder } from "../builder-Dl5Sdmc9.mjs";
|
|
9
|
+
import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
|
|
11
10
|
import { Activity, Note, Person } from "@fedify/vocab";
|
|
11
|
+
import { test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/builder.test.ts
|
|
13
13
|
test("FederationBuilder", async (t) => {
|
|
14
14
|
await t.step("should build a Federation object with registered components", async () => {
|
|
@@ -17,7 +17,11 @@ test("FederationBuilder", async (t) => {
|
|
|
17
17
|
const actorDispatcher = (_ctx, _identifier) => {
|
|
18
18
|
return null;
|
|
19
19
|
};
|
|
20
|
-
|
|
20
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier}", actorDispatcher).mapActorAlias("/actor/{id}", "instance"), RouterError, "Path for actor alias must have no variables.");
|
|
21
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier}", actorDispatcher).mapActorAlias("/actor", "instance").mapActorAlias("/bot", "instance"), RouterError, "Actor alias for \"instance\" already set.");
|
|
22
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier}", actorDispatcher).mapActorAlias("/actor", "instance").mapActorAlias("/actor", "bot"), RouterError, "Actor alias path \"/actor\" conflicts with existing route \"actorAlias:instance\".");
|
|
23
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier}", actorDispatcher).mapActorAlias("/actor", ""), RouterError, "Identifier cannot be empty.");
|
|
24
|
+
builder.setActorDispatcher("/users/{identifier}", actorDispatcher).mapActorAlias("/actor", "instance");
|
|
21
25
|
const inboxListener = (_ctx, _activity) => {};
|
|
22
26
|
builder.setInboxListeners("/users/{identifier}/inbox").on(Activity, inboxListener);
|
|
23
27
|
const outboxListener = (_ctx, _activity) => {};
|
|
@@ -52,6 +56,7 @@ test("FederationBuilder", async (t) => {
|
|
|
52
56
|
const impl = federation;
|
|
53
57
|
assertEquals(impl.router.route("/.well-known/webfinger")?.name, "webfinger");
|
|
54
58
|
assertEquals(impl.router.route("/users/test123")?.name, "actor");
|
|
59
|
+
assertEquals(impl.router.route("/actor")?.name, "actorAlias:instance");
|
|
55
60
|
assertEquals(impl.router.route("/users/test123/inbox")?.name, "inbox");
|
|
56
61
|
assertEquals(impl.router.route("/users/test123/outbox")?.name, "outbox");
|
|
57
62
|
assertEquals(impl.router.route("/notes/456")?.name, `object:${Note.typeId.href}`);
|
|
@@ -69,6 +74,40 @@ test("FederationBuilder", async (t) => {
|
|
|
69
74
|
assertEquals(impl.router.build("inbox", { identifier: "user1" }), "/users/user1/inbox");
|
|
70
75
|
await builder.build({ kv });
|
|
71
76
|
});
|
|
77
|
+
await t.step("passes benchmarkMode to the built federation", async () => {
|
|
78
|
+
const builder = createFederationBuilder();
|
|
79
|
+
const impl = await builder.build({
|
|
80
|
+
kv: new MemoryKvStore(),
|
|
81
|
+
benchmarkMode: true
|
|
82
|
+
});
|
|
83
|
+
assertEquals(impl.benchmarkMode, true);
|
|
84
|
+
assertEquals(impl.allowPrivateAddress, true);
|
|
85
|
+
assertEquals(impl.signatureTimeWindow, false);
|
|
86
|
+
const overriddenImpl = await builder.build({
|
|
87
|
+
kv: new MemoryKvStore(),
|
|
88
|
+
benchmarkMode: true,
|
|
89
|
+
allowPrivateAddress: false,
|
|
90
|
+
signatureTimeWindow: { minutes: 10 }
|
|
91
|
+
});
|
|
92
|
+
assertEquals(overriddenImpl.benchmarkMode, true);
|
|
93
|
+
assertEquals(overriddenImpl.allowPrivateAddress, false);
|
|
94
|
+
assertEquals(overriddenImpl.signatureTimeWindow, { minutes: 10 });
|
|
95
|
+
});
|
|
96
|
+
await t.step("should snapshot router state on build", async () => {
|
|
97
|
+
const builder = createFederationBuilder();
|
|
98
|
+
const kv = new MemoryKvStore();
|
|
99
|
+
const noteRouteName = `object:${Note.typeId.href}`;
|
|
100
|
+
builder.setActorDispatcher("/users/{identifier}", () => null);
|
|
101
|
+
const impl1 = await builder.build({ kv });
|
|
102
|
+
builder.setObjectDispatcher(Note, "/notes/{id}", () => null);
|
|
103
|
+
assertEquals(impl1.router.route("/notes/1"), null);
|
|
104
|
+
const impl2 = await builder.build({ kv });
|
|
105
|
+
assertEquals(impl2.router.route("/notes/1")?.name, noteRouteName);
|
|
106
|
+
impl1.router.add("/leaked/{id}", "leaked");
|
|
107
|
+
assertEquals(impl1.router.route("/leaked/1")?.name, "leaked");
|
|
108
|
+
assertEquals(impl2.router.route("/leaked/1"), null);
|
|
109
|
+
assertEquals((await builder.build({ kv })).router.route("/leaked/1"), null);
|
|
110
|
+
});
|
|
72
111
|
await t.step("should build with default options", async () => {
|
|
73
112
|
const builder = createFederationBuilder();
|
|
74
113
|
const kv = new MemoryKvStore();
|
|
@@ -84,15 +123,128 @@ test("FederationBuilder", async (t) => {
|
|
|
84
123
|
assertThrows(() => builder.setOutboxListeners("/users/{identifier}/outbox/{extra}"), RouterError);
|
|
85
124
|
assertThrows(() => builder.setOutboxListeners("/users/{identifier}/outbox/{identifier}"), RouterError);
|
|
86
125
|
const builderAfterInvalid = createFederationBuilder();
|
|
87
|
-
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier}/outbox/{extra}"),
|
|
126
|
+
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier}/outbox/{extra}"), RouteTemplateOptionsNotMatchedError);
|
|
127
|
+
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier:3}/outbox"), DisallowedVarSpecModifierError);
|
|
128
|
+
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier*}/outbox"), DisallowedVarSpecModifierError);
|
|
129
|
+
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier,identifier}/outbox"), DuplicateRouteVariableError);
|
|
88
130
|
builderAfterInvalid.setOutboxListeners("/users/{identifier}/outbox");
|
|
89
131
|
const builder2 = createFederationBuilder();
|
|
90
|
-
builder2.setOutboxListeners("/users{
|
|
132
|
+
builder2.setOutboxListeners("/users/{identifier}/outbox");
|
|
91
133
|
assertThrows(() => builder2.setOutboxDispatcher("/actors/{identifier}/outbox", () => ({ items: [] })), RouterError);
|
|
92
134
|
const builder3 = createFederationBuilder();
|
|
93
|
-
assertThrows(() => builder3.setOutboxListeners("/users{?identifier}/outbox"),
|
|
135
|
+
assertThrows(() => builder3.setOutboxListeners("/users{?identifier}/outbox"), DisallowedOperatorError);
|
|
136
|
+
const builder3a = createFederationBuilder();
|
|
137
|
+
assertThrows(() => builder3a.setOutboxListeners("/users{;identifier}/outbox"), DisallowedOperatorError);
|
|
138
|
+
const builder3b = createFederationBuilder();
|
|
139
|
+
assertThrows(() => builder3b.setOutboxListeners("/users{.identifier}/outbox"), DisallowedOperatorError);
|
|
94
140
|
const builder4 = createFederationBuilder();
|
|
95
|
-
assertThrows(() => builder4.setOutboxDispatcher("/users{?identifier}/outbox", () => ({ items: [] })),
|
|
141
|
+
assertThrows(() => builder4.setOutboxDispatcher("/users{?identifier}/outbox", () => ({ items: [] })), DisallowedOperatorError);
|
|
142
|
+
const builder5 = createFederationBuilder();
|
|
143
|
+
assertThrows(() => builder5.setOutboxDispatcher("/users/{identifier:3}/outbox", () => ({ items: [] })), DisallowedVarSpecModifierError);
|
|
144
|
+
});
|
|
145
|
+
await t.step("rejects non-segment-boundary identifier operators at registration for required-identifier routes", () => {
|
|
146
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users{/identifier}", () => null), DisallowedOperatorError);
|
|
147
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("{/identifier}", () => null), DisallowedOperatorError);
|
|
148
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{/identifier}", () => null), DisallowedOperatorError);
|
|
149
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users{?identifier}", () => null), DisallowedOperatorError);
|
|
150
|
+
assertThrows(() => createFederationBuilder().setInboxListeners("/users{/identifier}/inbox"), DisallowedOperatorError);
|
|
151
|
+
assertThrows(() => createFederationBuilder().setOutboxListeners("/users{/identifier}/outbox"), DisallowedOperatorError);
|
|
152
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier:3}", () => null), DisallowedVarSpecModifierError);
|
|
153
|
+
assertThrows(() => createFederationBuilder().setActorDispatcher("/users/{identifier*}", () => null), DisallowedVarSpecModifierError);
|
|
154
|
+
createFederationBuilder().setActorDispatcher("/users/{identifier}", () => null);
|
|
155
|
+
createFederationBuilder().setInboxListeners("/users/{identifier}/inbox");
|
|
156
|
+
});
|
|
157
|
+
await t.step("every required-identifier setter rejects every omissible operator", () => {
|
|
158
|
+
const omissibleExprs = [
|
|
159
|
+
"{/identifier}",
|
|
160
|
+
"{?identifier}",
|
|
161
|
+
"{;identifier}",
|
|
162
|
+
"{.identifier}"
|
|
163
|
+
];
|
|
164
|
+
const registrars = [
|
|
165
|
+
["setActorDispatcher", (expr) => createFederationBuilder().setActorDispatcher(`/users${expr}`, () => null)],
|
|
166
|
+
["setInboxListeners", (expr) => createFederationBuilder().setInboxListeners(`/users${expr}/inbox`)],
|
|
167
|
+
["setOutboxListeners", (expr) => createFederationBuilder().setOutboxListeners(`/users${expr}/outbox`)],
|
|
168
|
+
["setOutboxDispatcher", (expr) => createFederationBuilder().setOutboxDispatcher(`/users${expr}/outbox`, () => ({ items: [] }))],
|
|
169
|
+
["setFollowingDispatcher", (expr) => createFederationBuilder().setFollowingDispatcher(`/users${expr}/following`, () => ({ items: [] }))],
|
|
170
|
+
["setFollowersDispatcher", (expr) => createFederationBuilder().setFollowersDispatcher(`/users${expr}/followers`, () => ({ items: [] }))],
|
|
171
|
+
["setLikedDispatcher", (expr) => createFederationBuilder().setLikedDispatcher(`/users${expr}/liked`, () => ({ items: [] }))],
|
|
172
|
+
["setFeaturedDispatcher", (expr) => createFederationBuilder().setFeaturedDispatcher(`/users${expr}/featured`, () => ({ items: [] }))],
|
|
173
|
+
["setFeaturedTagsDispatcher", (expr) => createFederationBuilder().setFeaturedTagsDispatcher(`/users${expr}/tags`, () => ({ items: [] }))]
|
|
174
|
+
];
|
|
175
|
+
for (const [name, register] of registrars) {
|
|
176
|
+
for (const expr of omissibleExprs) assertThrows(() => register(expr), DisallowedOperatorError, void 0, `${name} must reject ${expr} at registration`);
|
|
177
|
+
register("{identifier}");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
await t.step("empty or missing identifier segments produce a runtime no-match", async () => {
|
|
181
|
+
const kv = new MemoryKvStore();
|
|
182
|
+
const builder = createFederationBuilder();
|
|
183
|
+
builder.setActorDispatcher("/users/{identifier}", () => null);
|
|
184
|
+
builder.setInboxListeners("/users/{identifier}/inbox");
|
|
185
|
+
builder.setOutboxDispatcher("/users/{identifier}/outbox", () => ({ items: [] }));
|
|
186
|
+
builder.setObjectDispatcher(Note, "/notes/{id}", () => null);
|
|
187
|
+
const impl = await builder.build({ kv });
|
|
188
|
+
assertEquals(impl.router.route("/users/alice")?.name, "actor");
|
|
189
|
+
assertEquals(impl.router.route("/users/alice/inbox")?.name, "inbox");
|
|
190
|
+
assertEquals(impl.router.route("/users/alice/outbox")?.name, "outbox");
|
|
191
|
+
assertEquals(impl.router.route("/notes/1")?.name, `object:${Note.typeId.href}`);
|
|
192
|
+
assertEquals(impl.router.route("/users/"), null);
|
|
193
|
+
assertEquals(impl.router.route("/users//inbox"), null);
|
|
194
|
+
assertEquals(impl.router.route("/users//outbox"), null);
|
|
195
|
+
assertEquals(impl.router.route("/notes/"), null);
|
|
196
|
+
});
|
|
197
|
+
await t.step("object dispatcher optional-operator routes no-match when unbound", async () => {
|
|
198
|
+
const kv = new MemoryKvStore();
|
|
199
|
+
const objectName = `object:${Note.typeId.href}`;
|
|
200
|
+
const query = createFederationBuilder();
|
|
201
|
+
query.setObjectDispatcher(Note, "/notes{?id}", () => null);
|
|
202
|
+
const queryImpl = await query.build({ kv });
|
|
203
|
+
assertEquals(queryImpl.router.route("/notes"), null);
|
|
204
|
+
assertEquals(queryImpl.router.route("/notes?id=1")?.name, objectName);
|
|
205
|
+
const matrix = createFederationBuilder();
|
|
206
|
+
matrix.setObjectDispatcher(Note, "/notes{;id}", () => null);
|
|
207
|
+
const matrixImpl = await matrix.build({ kv });
|
|
208
|
+
assertEquals(matrixImpl.router.route("/notes"), null);
|
|
209
|
+
assertEquals(matrixImpl.router.route("/notes;id=1")?.name, objectName);
|
|
210
|
+
const label = createFederationBuilder();
|
|
211
|
+
label.setObjectDispatcher(Note, "/notes{.id}", () => null);
|
|
212
|
+
const labelImpl = await label.build({ kv });
|
|
213
|
+
assertEquals(labelImpl.router.route("/notes"), null);
|
|
214
|
+
assertEquals(labelImpl.router.route("/notes.1")?.name, objectName);
|
|
215
|
+
});
|
|
216
|
+
await t.step("custom collection routes no-match empty or unbound variables", async () => {
|
|
217
|
+
const kv = new MemoryKvStore();
|
|
218
|
+
const builder = createFederationBuilder();
|
|
219
|
+
builder.setCollectionDispatcher("samples", Note, "/groups/{id}", () => ({ items: [] }));
|
|
220
|
+
builder.setCollectionDispatcher("optionals", Note, "/optional-groups{?id}", () => ({ items: [] }));
|
|
221
|
+
builder.setCollectionDispatcher("matrixOptionals", Note, "/matrix-groups{;id}", () => ({ items: [] }));
|
|
222
|
+
builder.setCollectionDispatcher("labelOptionals", Note, "/label-groups{.id}", () => ({ items: [] }));
|
|
223
|
+
const impl = await builder.build({ kv });
|
|
224
|
+
assertEquals(impl.router.route("/groups/1"), {
|
|
225
|
+
name: "collection:samples",
|
|
226
|
+
values: { id: "1" },
|
|
227
|
+
template: "/groups/{id}"
|
|
228
|
+
});
|
|
229
|
+
assertEquals(impl.router.route("/optional-groups?id=1"), {
|
|
230
|
+
name: "collection:optionals",
|
|
231
|
+
values: { id: "1" },
|
|
232
|
+
template: "/optional-groups{?id}"
|
|
233
|
+
});
|
|
234
|
+
assertEquals(impl.router.route("/matrix-groups;id=1"), {
|
|
235
|
+
name: "collection:matrixOptionals",
|
|
236
|
+
values: { id: "1" },
|
|
237
|
+
template: "/matrix-groups{;id}"
|
|
238
|
+
});
|
|
239
|
+
assertEquals(impl.router.route("/label-groups.1"), {
|
|
240
|
+
name: "collection:labelOptionals",
|
|
241
|
+
values: { id: "1" },
|
|
242
|
+
template: "/label-groups{.id}"
|
|
243
|
+
});
|
|
244
|
+
assertEquals(impl.router.route("/groups/"), null);
|
|
245
|
+
assertEquals(impl.router.route("/optional-groups"), null);
|
|
246
|
+
assertEquals(impl.router.route("/matrix-groups"), null);
|
|
247
|
+
assertEquals(impl.router.route("/label-groups"), null);
|
|
96
248
|
});
|
|
97
249
|
await t.step("should pass build options correctly", async () => {
|
|
98
250
|
const builder = createFederationBuilder();
|