@fedify/fedify 2.4.0-pr.936.41 → 2.5.0-dev.2271
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 +50 -39
- package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
- package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
- package/dist/assert_exists-cffSpJ7X.mjs +28 -0
- package/dist/assert_false-CPdRnc3E.mjs +23 -0
- package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
- package/dist/assert_rejects-CTptdYck.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
- package/dist/assertion_error-B-cx0fFA.mjs +31 -0
- package/dist/audience-CHQ-FlLD.mjs +95 -0
- package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
- package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
- package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +71 -3
- package/dist/compat/public-audience.test.mjs +14 -5
- package/dist/compat/secured-document.test.d.mts +2 -0
- package/dist/compat/secured-document.test.mjs +58 -0
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/compound-proof-B-9mXxFW.mjs +989 -0
- package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
- package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
- package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
- package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
- package/dist/dist-DJQM4u7i.mjs +189 -0
- package/dist/docloader-DBoYbvYW.mjs +87 -0
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/audience.test.d.mts +2 -0
- package/dist/federation/audience.test.mjs +130 -0
- package/dist/federation/builder.test.mjs +54 -5
- package/dist/federation/circuit-breaker.test.mjs +838 -7
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/compound-fanout.test.d.mts +2 -0
- package/dist/federation/compound-fanout.test.mjs +228 -0
- package/dist/federation/handler-compound-proof.test.d.mts +2 -0
- package/dist/federation/handler-compound-proof.test.mjs +384 -0
- package/dist/federation/handler.test.mjs +385 -15
- package/dist/federation/hashlink-media.test.d.mts +2 -0
- package/dist/federation/hashlink-media.test.mjs +422 -0
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +215 -14
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +5 -5
- package/dist/federation/middleware.test.mjs +967 -35
- package/dist/federation/mod.cjs +18 -261
- package/dist/federation/mod.d.cts +5 -5
- package/dist/federation/mod.d.ts +5 -5
- package/dist/federation/mod.js +279 -1
- package/dist/federation/mq.test.mjs +123 -47
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/outgoing-proof.test.d.mts +2 -0
- package/dist/federation/outgoing-proof.test.mjs +931 -0
- package/dist/federation/portable-actor.test.d.mts +2 -0
- package/dist/federation/portable-actor.test.mjs +496 -0
- package/dist/federation/portable-chain.test.d.mts +2 -0
- package/dist/federation/portable-chain.test.mjs +370 -0
- package/dist/federation/portable-collection.test.d.mts +2 -0
- package/dist/federation/portable-collection.test.mjs +832 -0
- package/dist/federation/portable-delivery.test.d.mts +2 -0
- package/dist/federation/portable-delivery.test.mjs +746 -0
- package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
- package/dist/federation/portable-gateway-keys.test.mjs +686 -0
- package/dist/federation/portable-inbox.test.d.mts +2 -0
- package/dist/federation/portable-inbox.test.mjs +1535 -0
- package/dist/federation/portable-parse-uri.test.d.mts +2 -0
- package/dist/federation/portable-parse-uri.test.mjs +348 -0
- package/dist/federation/portable-route-activity.test.d.mts +2 -0
- package/dist/federation/portable-route-activity.test.mjs +259 -0
- package/dist/federation/portable.test.d.mts +2 -0
- package/dist/federation/portable.test.mjs +1072 -0
- package/dist/federation/retry.test.mjs +4 -3
- package/dist/federation/router.test.mjs +9 -159
- package/dist/federation/send.test.mjs +245 -27
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +42 -4
- package/dist/federation/tasks/tasks.test.mjs +7 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +275 -12
- package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
- package/dist/http-B5JT115n.cjs +10630 -0
- package/dist/http-DydWCB7R.js +6527 -0
- package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
- package/dist/key-BspPiXqn.mjs +951 -0
- package/dist/key-C1nv7_jD.d.ts +1125 -0
- package/dist/key-DYxuyd-s.d.cts +1125 -0
- package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
- package/dist/keycache-Da8A8mZa.mjs +200 -0
- package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
- package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
- package/dist/kv-DBRWVx3Q.mjs +104 -0
- package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
- package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
- package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
- package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
- package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
- package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
- package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
- package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
- package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
- package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/mod-CavhAgLw.d.cts +320 -0
- package/dist/mod-CtpF2BU5.d.ts +320 -0
- package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
- package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
- package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +40 -39
- package/dist/mod.d.cts +10 -11
- package/dist/mod.d.ts +10 -11
- package/dist/mod.js +9 -9
- package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
- package/dist/nodeinfo/client.test.mjs +139 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
- package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
- package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
- package/dist/owner-DYgfTFHy.mjs +267 -0
- package/dist/portable-collection-BpsVsRov.js +670 -0
- package/dist/portable-collection-C74P1vtC.mjs +418 -0
- package/dist/portable-collection-DZd2bm7M.cjs +686 -0
- package/dist/portable-key-BIMLrAP4.mjs +549 -0
- package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
- package/dist/proof-Dxy6ByqI.mjs +1299 -0
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
- package/dist/router-u5O5KCQr.mjs +156 -0
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/secured-document-CxzZZ9nL.mjs +35 -0
- package/dist/send-C4dDpAxm.mjs +495 -0
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
- package/dist/sig/compound-proof-discovery.test.mjs +331 -0
- package/dist/sig/compound-proof-producer.test.d.mts +2 -0
- package/dist/sig/compound-proof-producer.test.mjs +107 -0
- package/dist/sig/compound-proof-verification.test.d.mts +2 -0
- package/dist/sig/compound-proof-verification.test.mjs +833 -0
- package/dist/sig/compound-proof.test.d.mts +2 -0
- package/dist/sig/compound-proof.test.mjs +411 -0
- package/dist/sig/http.test.mjs +702 -31
- package/dist/sig/key-owner-evidence.test.d.mts +2 -0
- package/dist/sig/key-owner-evidence.test.mjs +100 -0
- package/dist/sig/key.test.mjs +220 -8
- package/dist/sig/ld.test.mjs +65 -7
- package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
- package/dist/sig/mitra-receive-interop.test.mjs +260 -0
- package/dist/sig/mitra-send-interop.test.d.mts +2 -0
- package/dist/sig/mitra-send-interop.test.mjs +461 -0
- package/dist/sig/mod.cjs +18 -16
- package/dist/sig/mod.d.cts +4 -4
- package/dist/sig/mod.d.ts +4 -4
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +108 -9
- package/dist/sig/portable-collection.test.d.mts +2 -0
- package/dist/sig/portable-collection.test.mjs +697 -0
- package/dist/sig/portable-interop.test.d.mts +2 -0
- package/dist/sig/portable-interop.test.mjs +156 -0
- package/dist/sig/portable-key-id.test.d.mts +2 -0
- package/dist/sig/portable-key-id.test.mjs +67 -0
- package/dist/sig/portable-key.test.d.mts +2 -0
- package/dist/sig/portable-key.test.mjs +1439 -0
- package/dist/sig/proof.test.mjs +1753 -16
- package/dist/sig/tootik-request-interop.test.d.mts +2 -0
- package/dist/sig/tootik-request-interop.test.mjs +190 -0
- package/dist/sig/tootik-send-interop.test.d.mts +2 -0
- package/dist/sig/tootik-send-interop.test.mjs +331 -0
- package/dist/sig/verification.test.d.mts +2 -0
- package/dist/sig/verification.test.mjs +310 -0
- package/dist/std__assert-DvMDRPes.mjs +8 -0
- package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
- package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
- package/dist/testing/context.test.d.mts +2 -0
- package/dist/testing/context.test.mjs +19 -0
- package/dist/testing/kv.test.d.mts +2 -0
- package/dist/testing/kv.test.mjs +101 -0
- package/dist/testing/mod.d.mts +1617 -48
- package/dist/testing/mod.mjs +6 -3
- package/dist/testing-t8xDeJZ5.mjs +8 -0
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
- package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
- package/dist/utils/docloader.test.mjs +530 -8
- package/dist/utils/kv-cache.test.mjs +2 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- package/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +8 -8
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/docloader-e1khNUKy.mjs +0 -49
- package/dist/http-CsmQ-BQ4.js +0 -2483
- package/dist/http-VyDTd4G3.d.cts +0 -553
- package/dist/http-hlIjmewS.cjs +0 -2694
- package/dist/http-lf8Hsd91.d.ts +0 -553
- package/dist/key-DfPsX8hv.mjs +0 -453
- package/dist/keycache-_DHqz5z_.mjs +0 -97
- package/dist/metrics-CoUJLQhZ.d.cts +0 -276
- package/dist/metrics-Dc4jR-IC.d.ts +0 -276
- package/dist/mod-B0hW12_O.d.cts +0 -307
- package/dist/mod-COIAjwRS.d.ts +0 -307
- package/dist/owner-Br9YYzn_.mjs +0 -120
- package/dist/proof-B4ngoaNQ.mjs +0 -316
- package/dist/proof-B6E0XUXJ.js +0 -1415
- package/dist/proof-NN4xCQGL.cjs +0 -1562
- package/dist/send-CxHKUNzv.mjs +0 -239
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { a as verificationObservation } from "../verification-Dpfcw8ml.mjs";
|
|
5
|
+
import { h as verifyJsonLd, p as signJsonLd } from "../ld-CzaP-hJl.mjs";
|
|
6
|
+
import { a as signObject, s as verifyObject } from "../proof-Dxy6ByqI.mjs";
|
|
7
|
+
import { l as signRequest, p as verifyRequestDetailed } from "../http-siciWkmx.mjs";
|
|
8
|
+
import { i as rsaPrivateKey2, n as ed25519PrivateKey, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
9
|
+
import { Activity, Create, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
10
|
+
import { FetchError } from "@fedify/vocab-runtime";
|
|
11
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
|
+
import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
|
|
13
|
+
//#region src/sig/verification.test.ts
|
|
14
|
+
const loaders = {
|
|
15
|
+
documentLoader: mockDocumentLoader,
|
|
16
|
+
contextLoader: mockDocumentLoader
|
|
17
|
+
};
|
|
18
|
+
function observation() {
|
|
19
|
+
return { attempts: [] };
|
|
20
|
+
}
|
|
21
|
+
test("LDS evidence has no checks for absent or malformed signatures", async () => {
|
|
22
|
+
const json = await new Create({ actor: rsaPublicKey2.ownerId }).toJsonLd(loaders);
|
|
23
|
+
for (const signature of [
|
|
24
|
+
void 0,
|
|
25
|
+
null,
|
|
26
|
+
"invalid",
|
|
27
|
+
[],
|
|
28
|
+
{}
|
|
29
|
+
]) {
|
|
30
|
+
const evidence = observation();
|
|
31
|
+
strictEqual(await verifyJsonLd(signature === void 0 ? json : {
|
|
32
|
+
...json,
|
|
33
|
+
signature
|
|
34
|
+
}, {
|
|
35
|
+
...loaders,
|
|
36
|
+
[verificationObservation]: evidence
|
|
37
|
+
}), false);
|
|
38
|
+
const attempt = evidence.attempts[0];
|
|
39
|
+
ok(attempt.status === "rejected");
|
|
40
|
+
strictEqual(attempt.reason.type, signature === void 0 ? "noSignature" : "signatureVerificationFailed");
|
|
41
|
+
deepStrictEqual(attempt.checks, []);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
test("signature evidence preserves stale and fresh HTTP keys for both specs", async () => {
|
|
45
|
+
for (const spec of ["draft-cavage-http-signatures-12", "rfc9421"]) {
|
|
46
|
+
const evidence = observation();
|
|
47
|
+
const signed = await signRequest(new Request("https://example.com/inbox", {
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: "{}"
|
|
50
|
+
}), rsaPrivateKey2, rsaPublicKey2.id, { spec });
|
|
51
|
+
const stale = rsaPublicKey1.clone({ id: rsaPublicKey2.id });
|
|
52
|
+
strictEqual((await verifyRequestDetailed(signed, {
|
|
53
|
+
...loaders,
|
|
54
|
+
[verificationObservation]: evidence,
|
|
55
|
+
keyCache: {
|
|
56
|
+
get: () => Promise.resolve(stale),
|
|
57
|
+
set: () => Promise.resolve()
|
|
58
|
+
}
|
|
59
|
+
})).verified, true);
|
|
60
|
+
const attempt = evidence.attempts[0];
|
|
61
|
+
ok(attempt.status === "verified");
|
|
62
|
+
strictEqual(attempt.checks.length, 1);
|
|
63
|
+
const check = attempt.checks[0];
|
|
64
|
+
ok(check.status === "verified");
|
|
65
|
+
strictEqual(check.triedKeys.length, 2);
|
|
66
|
+
strictEqual(check.triedKeys[0].publicKey, rsaPublicKey1.publicKey);
|
|
67
|
+
deepStrictEqual(await crypto.subtle.exportKey("jwk", check.triedKeys[1].publicKey), await crypto.subtle.exportKey("jwk", rsaPublicKey2.publicKey));
|
|
68
|
+
strictEqual(check.key, check.triedKeys[1]);
|
|
69
|
+
strictEqual(attempt.signatures[0], check);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
test("signature evidence retains the failed key when HTTP refresh cannot load", async () => {
|
|
73
|
+
for (const spec of ["draft-cavage-http-signatures-12", "rfc9421"]) {
|
|
74
|
+
const evidence = observation();
|
|
75
|
+
const signed = await signRequest(new Request("https://example.com/inbox", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
body: "{}"
|
|
78
|
+
}), rsaPrivateKey2, rsaPublicKey2.id, { spec });
|
|
79
|
+
const response = new Response(null, { status: 410 });
|
|
80
|
+
strictEqual((await verifyRequestDetailed(signed, {
|
|
81
|
+
...loaders,
|
|
82
|
+
[verificationObservation]: evidence,
|
|
83
|
+
documentLoader: () => Promise.reject(new FetchError(rsaPublicKey2.id, "gone", response)),
|
|
84
|
+
keyCache: {
|
|
85
|
+
get: () => Promise.resolve(rsaPublicKey1.clone({ id: rsaPublicKey2.id })),
|
|
86
|
+
set: () => Promise.resolve()
|
|
87
|
+
}
|
|
88
|
+
})).verified, false);
|
|
89
|
+
const check = evidence.attempts[0].checks[0];
|
|
90
|
+
ok(check.status === "rejected");
|
|
91
|
+
strictEqual(check.reason.type, "keyFetchError");
|
|
92
|
+
strictEqual(check.triedKeys[0].publicKey, rsaPublicKey1.publicKey);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
test("signature evidence preserves failed LDS keys and successful stale-key refresh", async () => {
|
|
96
|
+
const signed = await signJsonLd(await new Create({ actor: new URL("https://example.com/person") }).toJsonLd(loaders), rsaPrivateKey2, rsaPublicKey2.id, loaders);
|
|
97
|
+
const evidence = observation();
|
|
98
|
+
strictEqual(await verifyJsonLd(signed, {
|
|
99
|
+
...loaders,
|
|
100
|
+
[verificationObservation]: evidence,
|
|
101
|
+
keyCache: {
|
|
102
|
+
get: () => Promise.resolve(rsaPublicKey1.clone({ id: rsaPublicKey2.id })),
|
|
103
|
+
set: () => Promise.resolve()
|
|
104
|
+
}
|
|
105
|
+
}), false);
|
|
106
|
+
const attempt = evidence.attempts[0];
|
|
107
|
+
ok(attempt.status === "rejected");
|
|
108
|
+
strictEqual(attempt.reason.type, "missingOwner");
|
|
109
|
+
strictEqual(attempt.checks[0].status, "verified");
|
|
110
|
+
strictEqual(attempt.checks[0].triedKeys.length, 2);
|
|
111
|
+
const bad = observation();
|
|
112
|
+
strictEqual(await verifyJsonLd(signed, {
|
|
113
|
+
...loaders,
|
|
114
|
+
[verificationObservation]: bad,
|
|
115
|
+
documentLoader: async () => ({
|
|
116
|
+
contextUrl: null,
|
|
117
|
+
documentUrl: rsaPublicKey2.id.href,
|
|
118
|
+
document: await new CryptographicKey({
|
|
119
|
+
id: rsaPublicKey2.id,
|
|
120
|
+
publicKey: rsaPublicKey1.publicKey
|
|
121
|
+
}).toJsonLd(loaders)
|
|
122
|
+
})
|
|
123
|
+
}), false);
|
|
124
|
+
strictEqual(bad.attempts[0].checks[0].status, "rejected");
|
|
125
|
+
strictEqual(bad.attempts[0].checks[0].triedKeys.length, 1);
|
|
126
|
+
});
|
|
127
|
+
test("signature evidence preserves multiple valid proofs despite uncovered attribution", async () => {
|
|
128
|
+
const second = await signObject(await signObject(new Create({ actor: new URL("https://example.com/person") }), ed25519PrivateKey, ed25519Multikey.id, loaders), ed25519PrivateKey, ed25519Multikey.id, loaders);
|
|
129
|
+
const evidence = observation();
|
|
130
|
+
strictEqual(await verifyObject(Activity, await second.toJsonLd(loaders), {
|
|
131
|
+
...loaders,
|
|
132
|
+
[verificationObservation]: evidence
|
|
133
|
+
}), null);
|
|
134
|
+
const attempt = evidence.attempts[0];
|
|
135
|
+
ok(attempt.status === "rejected");
|
|
136
|
+
strictEqual(attempt.reason.type, "uncoveredAttribution");
|
|
137
|
+
strictEqual(attempt.checks.length, 2);
|
|
138
|
+
ok(attempt.checks.every((check) => check.status === "verified"));
|
|
139
|
+
});
|
|
140
|
+
test("signature evidence preserves OIP stale keys when fresh verification fails", async () => {
|
|
141
|
+
const signed = await signObject(new Create({ actor: ed25519Multikey.controllerId }), ed25519PrivateKey, ed25519Multikey.id, loaders);
|
|
142
|
+
const pair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
|
|
143
|
+
const stale = new Multikey({
|
|
144
|
+
id: ed25519Multikey.id,
|
|
145
|
+
controller: null,
|
|
146
|
+
publicKey: pair.publicKey
|
|
147
|
+
});
|
|
148
|
+
const evidence = observation();
|
|
149
|
+
strictEqual(await verifyObject(Activity, await signed.toJsonLd(loaders), {
|
|
150
|
+
...loaders,
|
|
151
|
+
[verificationObservation]: evidence,
|
|
152
|
+
keyCache: {
|
|
153
|
+
get: () => Promise.resolve(stale),
|
|
154
|
+
set: () => Promise.resolve()
|
|
155
|
+
},
|
|
156
|
+
documentLoader: async () => ({
|
|
157
|
+
contextUrl: null,
|
|
158
|
+
documentUrl: ed25519Multikey.id.href,
|
|
159
|
+
document: await stale.toJsonLd(loaders)
|
|
160
|
+
})
|
|
161
|
+
}), null);
|
|
162
|
+
const check = evidence.attempts[0].checks[0];
|
|
163
|
+
strictEqual(check.status, "rejected");
|
|
164
|
+
strictEqual(check.triedKeys.length, 2);
|
|
165
|
+
strictEqual(check.triedKeys[0].publicKey, pair.publicKey);
|
|
166
|
+
});
|
|
167
|
+
test("OIP check preserves the literal declared key ID", async () => {
|
|
168
|
+
const json = await (await signObject(new Create({ actor: ed25519Multikey.controllerId }), ed25519PrivateKey, ed25519Multikey.id, loaders)).toJsonLd(loaders);
|
|
169
|
+
const proof = json.proof;
|
|
170
|
+
const declaredKeyId = "https://EXAMPLE.com/person2#key4";
|
|
171
|
+
proof.verificationMethod = declaredKeyId;
|
|
172
|
+
const evidence = observation();
|
|
173
|
+
await verifyObject(Activity, json, {
|
|
174
|
+
...loaders,
|
|
175
|
+
[verificationObservation]: evidence
|
|
176
|
+
});
|
|
177
|
+
strictEqual(evidence.attempts[0].checks[0].declaredKeyId, declaredKeyId);
|
|
178
|
+
});
|
|
179
|
+
test("signature observation cannot turn a cached miss into a metadata exception", async () => {
|
|
180
|
+
const json = await signJsonLd(await new Create({ actor: new URL("https://example.com/person") }).toJsonLd(loaders), rsaPrivateKey2, rsaPublicKey2.id, loaders);
|
|
181
|
+
const evidence = observation();
|
|
182
|
+
const cache = {
|
|
183
|
+
get: () => Promise.resolve(null),
|
|
184
|
+
set: () => Promise.resolve(),
|
|
185
|
+
getFetchError: () => Promise.reject(/* @__PURE__ */ new Error("diagnostic unavailable"))
|
|
186
|
+
};
|
|
187
|
+
strictEqual(await verifyJsonLd(json, {
|
|
188
|
+
...loaders,
|
|
189
|
+
keyCache: cache,
|
|
190
|
+
[verificationObservation]: evidence
|
|
191
|
+
}), false);
|
|
192
|
+
strictEqual(evidence.attempts[0].checks[0].status, "rejected");
|
|
193
|
+
});
|
|
194
|
+
test("signature observation preserves rejection for consumed fetch-error responses", async () => {
|
|
195
|
+
const json = await signJsonLd(await new Create({ actor: new URL("https://example.com/person") }).toJsonLd(loaders), rsaPrivateKey2, rsaPublicKey2.id, loaders);
|
|
196
|
+
const response = new Response("gone", { status: 410 });
|
|
197
|
+
await response.text();
|
|
198
|
+
const error = new FetchError(rsaPublicKey2.id, "gone", response);
|
|
199
|
+
const options = {
|
|
200
|
+
...loaders,
|
|
201
|
+
documentLoader: () => Promise.reject(error)
|
|
202
|
+
};
|
|
203
|
+
strictEqual(await verifyJsonLd(json, options), false);
|
|
204
|
+
const evidence = observation();
|
|
205
|
+
strictEqual(await verifyJsonLd(json, {
|
|
206
|
+
...options,
|
|
207
|
+
[verificationObservation]: evidence
|
|
208
|
+
}), false);
|
|
209
|
+
const check = evidence.attempts[0].checks[0];
|
|
210
|
+
ok(check.status === "rejected" && check.reason.type === "keyFetchError");
|
|
211
|
+
let cloneThrows = false;
|
|
212
|
+
try {
|
|
213
|
+
response.clone();
|
|
214
|
+
} catch {
|
|
215
|
+
cloneThrows = true;
|
|
216
|
+
}
|
|
217
|
+
if (cloneThrows) {
|
|
218
|
+
ok("error" in check.reason.result);
|
|
219
|
+
strictEqual(check.reason.result.error, error);
|
|
220
|
+
} else {
|
|
221
|
+
ok("status" in check.reason.result);
|
|
222
|
+
strictEqual(check.reason.result.status, 410);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
test("OIP check retains aliased declarations without extra context fetches", async () => {
|
|
226
|
+
const json = await (await signObject(new Create({ actor: ed25519Multikey.controllerId }), ed25519PrivateKey, ed25519Multikey.id, loaders)).toJsonLd(loaders);
|
|
227
|
+
const proof = json.proof;
|
|
228
|
+
const context = { vm: {
|
|
229
|
+
"@id": "https://w3id.org/security#verificationMethod",
|
|
230
|
+
"@type": "@id"
|
|
231
|
+
} };
|
|
232
|
+
const contextUrl = "https://example.com/custom-proof-context";
|
|
233
|
+
json["@context"] = [...json["@context"], contextUrl];
|
|
234
|
+
proof["@context"] = [...proof["@context"], contextUrl];
|
|
235
|
+
const declaredKeyId = "https://EXAMPLE.com/person2#key4";
|
|
236
|
+
proof.vm = declaredKeyId;
|
|
237
|
+
delete proof.verificationMethod;
|
|
238
|
+
const run = async (evidence) => {
|
|
239
|
+
let fetches = 0;
|
|
240
|
+
await verifyObject(Activity, structuredClone(json), {
|
|
241
|
+
...loaders,
|
|
242
|
+
contextLoader: async (url, options) => {
|
|
243
|
+
fetches++;
|
|
244
|
+
if (url === contextUrl) return {
|
|
245
|
+
contextUrl: null,
|
|
246
|
+
documentUrl: url,
|
|
247
|
+
document: { "@context": context }
|
|
248
|
+
};
|
|
249
|
+
return await mockDocumentLoader(url, options);
|
|
250
|
+
},
|
|
251
|
+
...evidence == null ? {} : { [verificationObservation]: evidence }
|
|
252
|
+
});
|
|
253
|
+
return fetches;
|
|
254
|
+
};
|
|
255
|
+
const baselineFetches = await run();
|
|
256
|
+
const evidence = observation();
|
|
257
|
+
strictEqual(await run(evidence), baselineFetches);
|
|
258
|
+
strictEqual(evidence.attempts[0].checks[0].declaredKeyId, declaredKeyId);
|
|
259
|
+
});
|
|
260
|
+
test("OIP check preserves compact node identifiers and their aliases", async () => {
|
|
261
|
+
for (const idProperty of [
|
|
262
|
+
"id",
|
|
263
|
+
"rawId",
|
|
264
|
+
"scopedId"
|
|
265
|
+
]) {
|
|
266
|
+
const json = await (await signObject(new Create({ actor: ed25519Multikey.controllerId }), ed25519PrivateKey, ed25519Multikey.id, loaders)).toJsonLd(loaders);
|
|
267
|
+
const proof = json.proof;
|
|
268
|
+
const context = {
|
|
269
|
+
rawId: "@id",
|
|
270
|
+
vm: {
|
|
271
|
+
"@id": "https://w3id.org/security#verificationMethod",
|
|
272
|
+
"@type": "@id",
|
|
273
|
+
"@context": { scopedId: "@id" }
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
json["@context"] = [...json["@context"], context];
|
|
277
|
+
proof["@context"] = [...proof["@context"], context];
|
|
278
|
+
const declaredKeyId = "https://EXAMPLE.com/person2#key4";
|
|
279
|
+
if (idProperty === "scopedId") {
|
|
280
|
+
proof.vm = { [idProperty]: declaredKeyId };
|
|
281
|
+
delete proof.verificationMethod;
|
|
282
|
+
} else proof.verificationMethod = { [idProperty]: declaredKeyId };
|
|
283
|
+
const evidence = observation();
|
|
284
|
+
await verifyObject(Activity, json, {
|
|
285
|
+
...loaders,
|
|
286
|
+
[verificationObservation]: evidence
|
|
287
|
+
});
|
|
288
|
+
strictEqual(evidence.attempts[0].checks[0].declaredKeyId, declaredKeyId);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
test("signature evidence excludes keys loaded before canonicalization errors", async () => {
|
|
292
|
+
const json = await (await signObject(new Create({ actor: ed25519Multikey.controllerId }), ed25519PrivateKey, ed25519Multikey.id, loaders)).toJsonLd(loaders);
|
|
293
|
+
json.extra = JSON.parse("1e400");
|
|
294
|
+
const evidence = observation();
|
|
295
|
+
let thrown = false;
|
|
296
|
+
try {
|
|
297
|
+
await verifyObject(Activity, json, {
|
|
298
|
+
...loaders,
|
|
299
|
+
[verificationObservation]: evidence
|
|
300
|
+
});
|
|
301
|
+
} catch {
|
|
302
|
+
thrown = true;
|
|
303
|
+
}
|
|
304
|
+
strictEqual(thrown, true);
|
|
305
|
+
const check = evidence.attempts[0].checks[0];
|
|
306
|
+
strictEqual(check.status, "error");
|
|
307
|
+
deepStrictEqual(check.triedKeys, []);
|
|
308
|
+
});
|
|
309
|
+
//#endregion
|
|
310
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import "./assert_equals-Z6IcEbWU.mjs";
|
|
5
|
+
import "./assert_throws-BYITOvPO.mjs";
|
|
6
|
+
import "./assert_rejects-CTptdYck.mjs";
|
|
7
|
+
import "./assert_strict_equals-CYvBmkpC.mjs";
|
|
8
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import { t as MemoryKvStore } from "./kv-
|
|
4
|
+
import { t as mockDocumentLoader } from "./dist-DJQM4u7i.mjs";
|
|
5
|
+
import { t as MemoryKvStore } from "./kv-DCHkaJ9-.mjs";
|
|
6
6
|
import { Note } from "@fedify/vocab";
|
|
7
7
|
//#region src/testing/tasks.ts
|
|
8
8
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { c as getNormalizationContextLoader } from "./ld-
|
|
4
|
+
import { c as getNormalizationContextLoader } from "./ld-CzaP-hJl.mjs";
|
|
5
5
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
6
6
|
//#region src/federation/temporal.ts
|
|
7
7
|
function isPlainObject(value) {
|
|
@@ -28,7 +28,7 @@ function isMalformedDurationLiteral(value) {
|
|
|
28
28
|
return true;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
31
|
+
const TEMPORAL_DATE_TIME_IRIS = /* @__PURE__ */ new Set([
|
|
32
32
|
"https://www.w3.org/ns/activitystreams#deleted",
|
|
33
33
|
"https://www.w3.org/ns/activitystreams#endTime",
|
|
34
34
|
"https://www.w3.org/ns/activitystreams#published",
|
|
@@ -37,7 +37,7 @@ const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
|
37
37
|
"http://purl.org/dc/terms/created",
|
|
38
38
|
"https://w3id.org/security#created"
|
|
39
39
|
]);
|
|
40
|
-
const TEMPORAL_DURATION_IRIS = new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
40
|
+
const TEMPORAL_DURATION_IRIS = /* @__PURE__ */ new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
41
41
|
const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
|
|
42
42
|
const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
|
|
43
43
|
function hasMalformedExpandedDateTimeLiteral(value) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { n as test } from "../dist-DJQM4u7i.mjs";
|
|
5
|
+
import { strictEqual } from "node:assert/strict";
|
|
6
|
+
//#region src/testing/context.test.ts
|
|
7
|
+
test("ignored nested test steps return false", async (t) => {
|
|
8
|
+
let executed = false;
|
|
9
|
+
strictEqual(await t.step({
|
|
10
|
+
name: "ignored nested test step",
|
|
11
|
+
ignore: true,
|
|
12
|
+
fn() {
|
|
13
|
+
executed = true;
|
|
14
|
+
}
|
|
15
|
+
}), false);
|
|
16
|
+
strictEqual(executed, false);
|
|
17
|
+
});
|
|
18
|
+
//#endregion
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { n as test } from "../dist-DJQM4u7i.mjs";
|
|
5
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
|
+
import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
|
|
7
|
+
import { n as baseOptions, o as stringSchema, t as MockQueue } from "../tasks-IG3pHfvZ.mjs";
|
|
8
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
9
|
+
import { o as createFederation } from "../middleware-D7_LO-Vs.mjs";
|
|
10
|
+
//#region src/testing/kv.test.ts
|
|
11
|
+
test("ManualClockKvStore keeps an entry at its exact expiration instant", async () => {
|
|
12
|
+
const kv = new ManualClockKvStore();
|
|
13
|
+
const ttl = Temporal.Duration.from({ minutes: 10 });
|
|
14
|
+
await kv.set(["k"], "v", { ttl });
|
|
15
|
+
kv.advance(ttl);
|
|
16
|
+
assertEquals(kv.now, Temporal.Instant.fromEpochMilliseconds(6e5));
|
|
17
|
+
assertEquals(await kv.get(["k"]), "v");
|
|
18
|
+
kv.advance({ nanoseconds: 1 });
|
|
19
|
+
assertEquals(await kv.get(["k"]), void 0);
|
|
20
|
+
});
|
|
21
|
+
test("ManualClockKvStore.list() applies the same boundary", async () => {
|
|
22
|
+
const kv = new ManualClockKvStore();
|
|
23
|
+
const ttl = Temporal.Duration.from({ minutes: 10 });
|
|
24
|
+
await kv.set(["k"], "v", { ttl });
|
|
25
|
+
kv.advance(ttl);
|
|
26
|
+
assertEquals(await Array.fromAsync(kv.list()), [{
|
|
27
|
+
key: ["k"],
|
|
28
|
+
value: "v"
|
|
29
|
+
}]);
|
|
30
|
+
kv.advance({ nanoseconds: 1 });
|
|
31
|
+
assertEquals(await Array.fromAsync(kv.list()), []);
|
|
32
|
+
});
|
|
33
|
+
test("ManualClockKvStore.cas() applies the same boundary", async () => {
|
|
34
|
+
const ttl = Temporal.Duration.from({ minutes: 10 });
|
|
35
|
+
const atBoundary = new ManualClockKvStore();
|
|
36
|
+
await atBoundary.set(["k"], "v", { ttl });
|
|
37
|
+
atBoundary.advance(ttl);
|
|
38
|
+
assertEquals(await atBoundary.cas?.(["k"], "v", "w"), true);
|
|
39
|
+
const pastBoundary = new ManualClockKvStore();
|
|
40
|
+
await pastBoundary.set(["k"], "v", { ttl });
|
|
41
|
+
pastBoundary.advance(ttl);
|
|
42
|
+
pastBoundary.advance({ nanoseconds: 1 });
|
|
43
|
+
assertEquals(await pastBoundary.cas?.(["k"], "v", "w"), false);
|
|
44
|
+
assertEquals(await pastBoundary.get(["k"]), void 0);
|
|
45
|
+
});
|
|
46
|
+
test("ManualClockKvStore.cas() without a TTL clears the expiration", async () => {
|
|
47
|
+
const kv = new ManualClockKvStore();
|
|
48
|
+
const ttl = Temporal.Duration.from({ minutes: 10 });
|
|
49
|
+
await kv.set(["k"], "v", { ttl });
|
|
50
|
+
assertEquals(await kv.cas?.(["k"], "v", "w"), true);
|
|
51
|
+
kv.advance({ hours: 1 });
|
|
52
|
+
assertEquals(await kv.get(["k"]), "w");
|
|
53
|
+
});
|
|
54
|
+
test("ManualClockKvStore withholds the TTL from the wrapped store", async () => {
|
|
55
|
+
const inner = new MemoryKvStore();
|
|
56
|
+
const kv = new ManualClockKvStore(inner);
|
|
57
|
+
await kv.set(["k"], "v", { ttl: Temporal.Duration.from({ nanoseconds: 1 }) });
|
|
58
|
+
assertEquals(await inner.get(["k"]), "v");
|
|
59
|
+
assertEquals(await kv.get(["k"]), "v");
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* A {@link KvStore} without `cas()`, standing in for backends that offer no
|
|
63
|
+
* conditional write.
|
|
64
|
+
*/
|
|
65
|
+
var CaslessKvStore = class {
|
|
66
|
+
#inner = new MemoryKvStore();
|
|
67
|
+
get(key) {
|
|
68
|
+
return this.#inner.get(key);
|
|
69
|
+
}
|
|
70
|
+
set(key, value, options) {
|
|
71
|
+
return this.#inner.set(key, value, options);
|
|
72
|
+
}
|
|
73
|
+
delete(key) {
|
|
74
|
+
return this.#inner.delete(key);
|
|
75
|
+
}
|
|
76
|
+
list(prefix) {
|
|
77
|
+
return this.#inner.list(prefix);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
test("ManualClockKvStore exposes cas() only when the wrapped store does", () => {
|
|
81
|
+
assertEquals(new ManualClockKvStore(new CaslessKvStore()).cas, void 0);
|
|
82
|
+
assertEquals(typeof new ManualClockKvStore(new MemoryKvStore()).cas, "function");
|
|
83
|
+
});
|
|
84
|
+
test("ManualClockKvStore over a store without cas() keeps the open deduplication fallback", async () => {
|
|
85
|
+
const queue = new MockQueue();
|
|
86
|
+
const federation = createFederation({
|
|
87
|
+
...baseOptions,
|
|
88
|
+
kv: new ManualClockKvStore(new CaslessKvStore()),
|
|
89
|
+
queue: { task: queue },
|
|
90
|
+
taskDeduplicationFallback: "open"
|
|
91
|
+
});
|
|
92
|
+
const task = federation.defineTask("casless-open-fallback", {
|
|
93
|
+
schema: stringSchema,
|
|
94
|
+
handler: () => {}
|
|
95
|
+
});
|
|
96
|
+
await federation.createContext(new URL("https://example.com/"), void 0).enqueueTask(task, "payload", { deduplicationKey: "k" });
|
|
97
|
+
assertEquals(queue.enqueued.length, 1);
|
|
98
|
+
assertEquals(queue.enqueued[0].options?.deduplicationKey, void 0);
|
|
99
|
+
});
|
|
100
|
+
//#endregion
|
|
101
|
+
export {};
|