@fedify/fedify 2.1.0-dev.503 → 2.1.0-dev.513
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/{builder-BHUnSQtB.js → builder-DSYM2n7q.js} +9 -3
- package/dist/compat/mod.d.cts +3 -3
- package/dist/compat/mod.d.ts +3 -3
- package/dist/compat/transformers.test.js +12 -12
- package/dist/{context-DZJhUmzF.d.ts → context--RwChtri.d.ts} +54 -2
- package/dist/{context-D3QkEtZd.d.cts → context-DL0cPpPV.d.cts} +54 -2
- package/dist/{deno-BYerLnry.js → deno-aj03PJW6.js} +1 -1
- package/dist/{docloader-MSkogD2T.js → docloader-tMQxWuTM.js} +2 -2
- package/dist/federation/builder.test.js +14 -3
- package/dist/federation/handler.test.js +97 -13
- package/dist/federation/idempotency.test.js +12 -12
- package/dist/federation/inbox.test.js +2 -2
- package/dist/federation/keycache.test.js +46 -2
- package/dist/federation/middleware.test.js +206 -12
- package/dist/federation/mod.cjs +4 -4
- package/dist/federation/mod.d.cts +4 -4
- package/dist/federation/mod.d.ts +4 -4
- package/dist/federation/mod.js +4 -4
- package/dist/federation/send.test.js +5 -5
- package/dist/federation/webfinger.test.js +12 -12
- package/dist/{http-DkHdFfrc.d.ts → http-BbfOqHGG.d.ts} +80 -8
- package/dist/{http-CSX1-Mgi.js → http-CoM8qFZV.js} +295 -101
- package/dist/{http-Cz3MlXAZ.d.cts → http-DsqqmkXi.d.cts} +80 -8
- package/dist/{http-DJT6NciB.cjs → http-_l2XRk4S.cjs} +305 -99
- package/dist/{http-S2U3qDwN.js → http-pa5yLKK7.js} +153 -57
- package/dist/{inbox-BaA0g5I_.js → inbox-EI2EtQri.js} +1 -1
- package/dist/{key-DCdTVZiK.js → key-j1AjF3HL.js} +145 -47
- package/dist/keycache-C7k8s1Bk.js +102 -0
- package/dist/{kv-cache-CQPL_aGY.js → kv-cache-CMk-kfWp.js} +1 -1
- package/dist/{kv-cache-Vtxhbo1W.cjs → kv-cache-D0IkOVYm.cjs} +1 -1
- package/dist/{ld-CrX7pQda.js → ld-CD48Tb3_.js} +2 -2
- package/dist/middleware-BQhgZunR.cjs +12 -0
- package/dist/{middleware-MlO5iUeZ.js → middleware-BRIM_AL5.js} +158 -22
- package/dist/{middleware-C8PKuPrm.js → middleware-CWMuqleA.js} +4 -4
- package/dist/{middleware-D4S6i4A_.cjs → middleware-D1Awtgcf.cjs} +158 -22
- package/dist/{middleware-BelSJK7m.js → middleware-DbmQ5-Ph.js} +100 -24
- package/dist/{middleware-CfI9C9Xy.js → middleware-b9jiK13s.js} +12 -12
- package/dist/{mod-CwZXZJ9d.d.ts → mod-BugwI0JN.d.ts} +1 -1
- package/dist/{mod-DPkRU3EK.d.cts → mod-CFBU2OT3.d.cts} +1 -1
- package/dist/{mod-DUWcVv49.d.ts → mod-CvxylbuV.d.ts} +1 -1
- package/dist/{mod-DVwHUI_x.d.cts → mod-DE8MYisy.d.cts} +1 -1
- package/dist/{mod-DXsQakeS.d.cts → mod-DKG0ovjR.d.cts} +1 -1
- package/dist/{mod-DnSsduJF.d.ts → mod-DcfFNgYf.d.ts} +1 -1
- package/dist/{mod-Di3W5OdP.d.cts → mod-Dp0kK0hO.d.cts} +1 -1
- package/dist/{mod-DosD6NsG.d.ts → mod-Z7lIaCfo.d.ts} +1 -1
- package/dist/mod.cjs +8 -4
- package/dist/mod.d.cts +8 -8
- package/dist/mod.d.ts +8 -8
- package/dist/mod.js +7 -5
- package/dist/nodeinfo/handler.test.js +12 -12
- package/dist/otel/exporter.test.js +43 -2
- package/dist/otel/mod.cjs +7 -1
- package/dist/otel/mod.d.cts +12 -0
- package/dist/otel/mod.d.ts +12 -0
- package/dist/otel/mod.js +7 -1
- package/dist/{owner-BAlnLKMO.js → owner-ZjRdCtVA.js} +1 -1
- package/dist/{proof-BgUVmaJz.js → proof-CQfh4S5r.js} +1 -1
- package/dist/{proof-CR5RUAmy.cjs → proof-D2auLGZD.cjs} +1 -1
- package/dist/{proof-DMgHaXNJ.js → proof-gmdzyEsv.js} +2 -2
- package/dist/{send-B2aZYf9A.js → send-1lNwij0f.js} +2 -2
- package/dist/sig/http.test.js +85 -5
- package/dist/sig/key.test.js +70 -3
- package/dist/sig/ld.test.js +3 -3
- package/dist/sig/mod.cjs +4 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.js +3 -3
- package/dist/sig/proof.test.js +3 -3
- package/dist/testing/mod.d.ts +92 -0
- package/dist/utils/docloader.test.js +4 -4
- package/dist/utils/mod.cjs +2 -2
- package/dist/utils/mod.d.cts +2 -2
- package/dist/utils/mod.d.ts +2 -2
- package/dist/utils/mod.js +2 -2
- package/package.json +5 -5
- package/dist/keycache-DRxpZ5r9.js +0 -48
- package/dist/middleware-D4XcpSBG.cjs +0 -12
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
globalThis.addEventListener = () => {};
|
|
5
5
|
|
|
6
|
-
import { deno_default } from "./deno-
|
|
6
|
+
import { deno_default } from "./deno-aj03PJW6.js";
|
|
7
7
|
import { Router, RouterError } from "./router-D9eI0s4b.js";
|
|
8
|
-
import { InboxListenerSet } from "./inbox-
|
|
8
|
+
import { InboxListenerSet } from "./inbox-EI2EtQri.js";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
10
|
import { getTypeId } from "@fedify/vocab";
|
|
11
11
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -29,6 +29,7 @@ var FederationBuilderImpl = class {
|
|
|
29
29
|
inboxListeners;
|
|
30
30
|
inboxErrorHandler;
|
|
31
31
|
sharedInboxKeyDispatcher;
|
|
32
|
+
unverifiedActivityHandler;
|
|
32
33
|
outboxPermanentFailureHandler;
|
|
33
34
|
idempotencyStrategy;
|
|
34
35
|
collectionTypeIds;
|
|
@@ -45,7 +46,7 @@ var FederationBuilderImpl = class {
|
|
|
45
46
|
this.collectionTypeIds = {};
|
|
46
47
|
}
|
|
47
48
|
async build(options) {
|
|
48
|
-
const { FederationImpl } = await import("./middleware-
|
|
49
|
+
const { FederationImpl } = await import("./middleware-b9jiK13s.js");
|
|
49
50
|
const f = new FederationImpl(options);
|
|
50
51
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
51
52
|
f.router = this.router.clone();
|
|
@@ -67,6 +68,7 @@ var FederationBuilderImpl = class {
|
|
|
67
68
|
f.inboxListeners = this.inboxListeners?.clone();
|
|
68
69
|
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
69
70
|
f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
|
|
71
|
+
f.unverifiedActivityHandler = this.unverifiedActivityHandler;
|
|
70
72
|
f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
|
|
71
73
|
f.idempotencyStrategy = this.idempotencyStrategy;
|
|
72
74
|
return f;
|
|
@@ -443,6 +445,10 @@ var FederationBuilderImpl = class {
|
|
|
443
445
|
this.inboxErrorHandler = handler;
|
|
444
446
|
return setters;
|
|
445
447
|
},
|
|
448
|
+
onUnverifiedActivity: (handler) => {
|
|
449
|
+
this.unverifiedActivityHandler = handler;
|
|
450
|
+
return setters;
|
|
451
|
+
},
|
|
446
452
|
setSharedKeyDispatcher: (dispatcher) => {
|
|
447
453
|
this.sharedInboxKeyDispatcher = dispatcher;
|
|
448
454
|
return setters;
|
package/dist/compat/mod.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../client-C97KOq3x.cjs";
|
|
2
|
-
import "../http-
|
|
2
|
+
import "../http-DsqqmkXi.cjs";
|
|
3
3
|
import "../owner-1AbPBOOZ.cjs";
|
|
4
|
-
import { ActivityTransformer } from "../context-
|
|
4
|
+
import { ActivityTransformer } from "../context-DL0cPpPV.cjs";
|
|
5
5
|
import "../kv-BL4nlICN.cjs";
|
|
6
|
-
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-
|
|
6
|
+
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-DE8MYisy.cjs";
|
|
7
7
|
export { ActivityTransformer, actorDehydrator, autoIdAssigner, getDefaultActivityTransformers };
|
package/dist/compat/mod.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import "../client-BxMZiQaD.js";
|
|
4
|
-
import "../http-
|
|
4
|
+
import "../http-BbfOqHGG.js";
|
|
5
5
|
import "../owner-gd0Q9FuU.js";
|
|
6
|
-
import { ActivityTransformer } from "../context
|
|
6
|
+
import { ActivityTransformer } from "../context--RwChtri.js";
|
|
7
7
|
import "../kv-DXEUEP6z.js";
|
|
8
|
-
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-
|
|
8
|
+
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "../mod-Z7lIaCfo.js";
|
|
9
9
|
export { ActivityTransformer, actorDehydrator, autoIdAssigner, getDefaultActivityTransformers };
|
|
@@ -8,25 +8,25 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import { assert } from "../assert-MZs1qjMx.js";
|
|
9
9
|
import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import "../deno-
|
|
12
|
-
import { FederationImpl, actorDehydrator, autoIdAssigner } from "../middleware-
|
|
11
|
+
import "../deno-aj03PJW6.js";
|
|
12
|
+
import { FederationImpl, actorDehydrator, autoIdAssigner } from "../middleware-DbmQ5-Ph.js";
|
|
13
13
|
import "../client-Dg7OfUDA.js";
|
|
14
14
|
import "../router-D9eI0s4b.js";
|
|
15
15
|
import "../types-CPz01LGH.js";
|
|
16
|
-
import "../key-
|
|
17
|
-
import "../http-
|
|
18
|
-
import "../ld-
|
|
19
|
-
import "../owner-
|
|
20
|
-
import "../proof-
|
|
21
|
-
import "../docloader-
|
|
16
|
+
import "../key-j1AjF3HL.js";
|
|
17
|
+
import "../http-pa5yLKK7.js";
|
|
18
|
+
import "../ld-CD48Tb3_.js";
|
|
19
|
+
import "../owner-ZjRdCtVA.js";
|
|
20
|
+
import "../proof-gmdzyEsv.js";
|
|
21
|
+
import "../docloader-tMQxWuTM.js";
|
|
22
22
|
import "../kv-cache-B__dHl7g.js";
|
|
23
|
-
import "../inbox-
|
|
24
|
-
import "../builder-
|
|
23
|
+
import "../inbox-EI2EtQri.js";
|
|
24
|
+
import "../builder-DSYM2n7q.js";
|
|
25
25
|
import "../collection-CcnIw1qY.js";
|
|
26
|
-
import "../keycache-
|
|
26
|
+
import "../keycache-C7k8s1Bk.js";
|
|
27
27
|
import "../negotiation-5NPJL6zp.js";
|
|
28
28
|
import "../retry-D4GJ670a.js";
|
|
29
|
-
import "../send-
|
|
29
|
+
import "../send-1lNwij0f.js";
|
|
30
30
|
import { Follow, Person } from "@fedify/vocab";
|
|
31
31
|
|
|
32
32
|
//#region src/compat/transformers.test.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { GetNodeInfoOptions, JsonValue, NodeInfo } from "./client-BxMZiQaD.js";
|
|
4
|
-
import { HttpMessageSignaturesSpec } from "./http-
|
|
4
|
+
import { HttpMessageSignaturesSpec, VerifyRequestFailureReason } from "./http-BbfOqHGG.js";
|
|
5
5
|
import { GetKeyOwnerOptions } from "./owner-gd0Q9FuU.js";
|
|
6
6
|
import { KvKey, KvStore } from "./kv-DXEUEP6z.js";
|
|
7
7
|
import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObjectOptions, Multikey, Object as Object$1, Recipient, TraverseCollectionOptions } from "@fedify/vocab";
|
|
@@ -216,6 +216,29 @@ type CollectionCursor<TContext extends Context<TContextData>, TContextData, TFil
|
|
|
216
216
|
*/
|
|
217
217
|
type InboxListener<TContextData, TActivity extends Activity> = (context: InboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
|
|
218
218
|
/**
|
|
219
|
+
* The reason why an incoming activity could not be verified.
|
|
220
|
+
*
|
|
221
|
+
* Unlike inbox listeners registered through {@link InboxListenerSetters.on},
|
|
222
|
+
* unverified activity handlers are called only when the activity payload could
|
|
223
|
+
* be parsed but its HTTP signatures could not be verified.
|
|
224
|
+
*
|
|
225
|
+
* @since 2.1.0
|
|
226
|
+
*/
|
|
227
|
+
type UnverifiedActivityReason = VerifyRequestFailureReason;
|
|
228
|
+
/**
|
|
229
|
+
* A callback that handles activities whose signatures could not be verified.
|
|
230
|
+
*
|
|
231
|
+
* Returning a {@link Response} overrides Fedify's default `401 Unauthorized`
|
|
232
|
+
* response. Returning `void` keeps the default behavior.
|
|
233
|
+
*
|
|
234
|
+
* @template TContextData The context data to pass to the {@link Context}.
|
|
235
|
+
* @param context The request context.
|
|
236
|
+
* @param activity The incoming activity that could be parsed.
|
|
237
|
+
* @param reason The reason why signature verification failed.
|
|
238
|
+
* @since 2.1.0
|
|
239
|
+
*/
|
|
240
|
+
type UnverifiedActivityHandler<TContextData> = (context: RequestContext<TContextData>, activity: Activity, reason: UnverifiedActivityReason) => void | Response | Promise<void | Response>;
|
|
241
|
+
/**
|
|
219
242
|
* A callback that handles errors in an inbox.
|
|
220
243
|
*
|
|
221
244
|
* @template TContextData The context data to pass to the {@link Context}.
|
|
@@ -1628,6 +1651,35 @@ interface InboxListenerSetters<TContextData> {
|
|
|
1628
1651
|
*/
|
|
1629
1652
|
onError(handler: InboxErrorHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
1630
1653
|
/**
|
|
1654
|
+
* Registers a callback for incoming activities whose HTTP signatures could
|
|
1655
|
+
* not be verified.
|
|
1656
|
+
*
|
|
1657
|
+
* The regular inbox listeners registered through {@link on} continue to
|
|
1658
|
+
* receive only verified activities. This hook is an opt-in escape hatch for
|
|
1659
|
+
* applications that need to inspect unverified deliveries and optionally
|
|
1660
|
+
* override the default `401 Unauthorized` response.
|
|
1661
|
+
*
|
|
1662
|
+
* @example
|
|
1663
|
+
* ``` typescript
|
|
1664
|
+
* federation
|
|
1665
|
+
* .setInboxListeners("/users/{identifier}/inbox", "/inbox")
|
|
1666
|
+
* .onUnverifiedActivity((ctx, activity, reason) => {
|
|
1667
|
+
* if (
|
|
1668
|
+
* reason.type === "keyFetchError" &&
|
|
1669
|
+
* "status" in reason.result &&
|
|
1670
|
+
* reason.result.status === 410
|
|
1671
|
+
* ) {
|
|
1672
|
+
* return new Response(null, { status: 202 });
|
|
1673
|
+
* }
|
|
1674
|
+
* });
|
|
1675
|
+
* ```
|
|
1676
|
+
*
|
|
1677
|
+
* @param handler A callback to handle an unverified activity.
|
|
1678
|
+
* @returns The setters object so that settings can be chained.
|
|
1679
|
+
* @since 2.1.0
|
|
1680
|
+
*/
|
|
1681
|
+
onUnverifiedActivity(handler: UnverifiedActivityHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
1682
|
+
/**
|
|
1631
1683
|
* Configures a callback to dispatch the key pair for the authenticated
|
|
1632
1684
|
* document loader of the {@link Context} passed to the shared inbox listener.
|
|
1633
1685
|
*
|
|
@@ -2493,4 +2545,4 @@ interface ActorKeyPair extends CryptoKeyPair {
|
|
|
2493
2545
|
readonly multikey: Multikey;
|
|
2494
2546
|
}
|
|
2495
2547
|
//#endregion
|
|
2496
|
-
export { ActivityTransformer, ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable };
|
|
2548
|
+
export { ActivityTransformer, ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GetNodeInfoOptions, JsonValue, NodeInfo } from "./client-C97KOq3x.cjs";
|
|
2
|
-
import { HttpMessageSignaturesSpec } from "./http-
|
|
2
|
+
import { HttpMessageSignaturesSpec, VerifyRequestFailureReason } from "./http-DsqqmkXi.cjs";
|
|
3
3
|
import { GetKeyOwnerOptions } from "./owner-1AbPBOOZ.cjs";
|
|
4
4
|
import { KvKey, KvStore } from "./kv-BL4nlICN.cjs";
|
|
5
5
|
import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObjectOptions, Multikey, Object as Object$1, Recipient, TraverseCollectionOptions } from "@fedify/vocab";
|
|
@@ -214,6 +214,29 @@ type CollectionCursor<TContext extends Context<TContextData>, TContextData, TFil
|
|
|
214
214
|
*/
|
|
215
215
|
type InboxListener<TContextData, TActivity extends Activity> = (context: InboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
|
|
216
216
|
/**
|
|
217
|
+
* The reason why an incoming activity could not be verified.
|
|
218
|
+
*
|
|
219
|
+
* Unlike inbox listeners registered through {@link InboxListenerSetters.on},
|
|
220
|
+
* unverified activity handlers are called only when the activity payload could
|
|
221
|
+
* be parsed but its HTTP signatures could not be verified.
|
|
222
|
+
*
|
|
223
|
+
* @since 2.1.0
|
|
224
|
+
*/
|
|
225
|
+
type UnverifiedActivityReason = VerifyRequestFailureReason;
|
|
226
|
+
/**
|
|
227
|
+
* A callback that handles activities whose signatures could not be verified.
|
|
228
|
+
*
|
|
229
|
+
* Returning a {@link Response} overrides Fedify's default `401 Unauthorized`
|
|
230
|
+
* response. Returning `void` keeps the default behavior.
|
|
231
|
+
*
|
|
232
|
+
* @template TContextData The context data to pass to the {@link Context}.
|
|
233
|
+
* @param context The request context.
|
|
234
|
+
* @param activity The incoming activity that could be parsed.
|
|
235
|
+
* @param reason The reason why signature verification failed.
|
|
236
|
+
* @since 2.1.0
|
|
237
|
+
*/
|
|
238
|
+
type UnverifiedActivityHandler<TContextData> = (context: RequestContext<TContextData>, activity: Activity, reason: UnverifiedActivityReason) => void | Response | Promise<void | Response>;
|
|
239
|
+
/**
|
|
217
240
|
* A callback that handles errors in an inbox.
|
|
218
241
|
*
|
|
219
242
|
* @template TContextData The context data to pass to the {@link Context}.
|
|
@@ -1626,6 +1649,35 @@ interface InboxListenerSetters<TContextData> {
|
|
|
1626
1649
|
*/
|
|
1627
1650
|
onError(handler: InboxErrorHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
1628
1651
|
/**
|
|
1652
|
+
* Registers a callback for incoming activities whose HTTP signatures could
|
|
1653
|
+
* not be verified.
|
|
1654
|
+
*
|
|
1655
|
+
* The regular inbox listeners registered through {@link on} continue to
|
|
1656
|
+
* receive only verified activities. This hook is an opt-in escape hatch for
|
|
1657
|
+
* applications that need to inspect unverified deliveries and optionally
|
|
1658
|
+
* override the default `401 Unauthorized` response.
|
|
1659
|
+
*
|
|
1660
|
+
* @example
|
|
1661
|
+
* ``` typescript
|
|
1662
|
+
* federation
|
|
1663
|
+
* .setInboxListeners("/users/{identifier}/inbox", "/inbox")
|
|
1664
|
+
* .onUnverifiedActivity((ctx, activity, reason) => {
|
|
1665
|
+
* if (
|
|
1666
|
+
* reason.type === "keyFetchError" &&
|
|
1667
|
+
* "status" in reason.result &&
|
|
1668
|
+
* reason.result.status === 410
|
|
1669
|
+
* ) {
|
|
1670
|
+
* return new Response(null, { status: 202 });
|
|
1671
|
+
* }
|
|
1672
|
+
* });
|
|
1673
|
+
* ```
|
|
1674
|
+
*
|
|
1675
|
+
* @param handler A callback to handle an unverified activity.
|
|
1676
|
+
* @returns The setters object so that settings can be chained.
|
|
1677
|
+
* @since 2.1.0
|
|
1678
|
+
*/
|
|
1679
|
+
onUnverifiedActivity(handler: UnverifiedActivityHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
1680
|
+
/**
|
|
1629
1681
|
* Configures a callback to dispatch the key pair for the authenticated
|
|
1630
1682
|
* document loader of the {@link Context} passed to the shared inbox listener.
|
|
1631
1683
|
*
|
|
@@ -2491,4 +2543,4 @@ interface ActorKeyPair extends CryptoKeyPair {
|
|
|
2491
2543
|
readonly multikey: Multikey;
|
|
2492
2544
|
}
|
|
2493
2545
|
//#endregion
|
|
2494
|
-
export { ActivityTransformer, ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable };
|
|
2546
|
+
export { ActivityTransformer, ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable };
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
globalThis.addEventListener = () => {};
|
|
5
5
|
|
|
6
|
-
import { validateCryptoKey } from "./key-
|
|
7
|
-
import { doubleKnock } from "./http-
|
|
6
|
+
import { validateCryptoKey } from "./key-j1AjF3HL.js";
|
|
7
|
+
import { doubleKnock } from "./http-pa5yLKK7.js";
|
|
8
8
|
import { getLogger } from "@logtape/logtape";
|
|
9
9
|
import { curry } from "es-toolkit";
|
|
10
10
|
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
@@ -8,10 +8,10 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import "../assert-MZs1qjMx.js";
|
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import "../deno-
|
|
11
|
+
import "../deno-aj03PJW6.js";
|
|
12
12
|
import "../router-D9eI0s4b.js";
|
|
13
|
-
import "../inbox-
|
|
14
|
-
import { createFederationBuilder } from "../builder-
|
|
13
|
+
import "../inbox-EI2EtQri.js";
|
|
14
|
+
import { createFederationBuilder } from "../builder-DSYM2n7q.js";
|
|
15
15
|
import { assertExists } from "../std__assert-DWivtrGR.js";
|
|
16
16
|
import "../assert_rejects-Ce45JcFg.js";
|
|
17
17
|
import { assertThrows } from "../assert_throws-BNXdRGWP.js";
|
|
@@ -123,6 +123,17 @@ test("FederationBuilder", async (t) => {
|
|
|
123
123
|
const implRfc = federationRfc;
|
|
124
124
|
assertEquals(implRfc.firstKnock, "rfc9421");
|
|
125
125
|
});
|
|
126
|
+
await t.step("should copy unverified activity handler into built federation", async () => {
|
|
127
|
+
const builder = createFederationBuilder();
|
|
128
|
+
const kv = new MemoryKvStore();
|
|
129
|
+
const handler = (_ctx, _activity, _reason) => {
|
|
130
|
+
return;
|
|
131
|
+
};
|
|
132
|
+
builder.setInboxListeners("/users/{identifier}/inbox").onUnverifiedActivity(handler);
|
|
133
|
+
const federation = await builder.build({ kv });
|
|
134
|
+
const impl = federation;
|
|
135
|
+
assertEquals(impl.unverifiedActivityHandler, handler);
|
|
136
|
+
});
|
|
126
137
|
await t.step("should register multiple object dispatchers and verify them", async () => {
|
|
127
138
|
const builder = createFederationBuilder();
|
|
128
139
|
const kv = new MemoryKvStore();
|
|
@@ -8,25 +8,25 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import { assert } from "../assert-MZs1qjMx.js";
|
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import "../deno-
|
|
12
|
-
import { createFederation, handleActor, handleCollection, handleCustomCollection, handleInbox, handleObject, respondWithObject, respondWithObjectIfAcceptable } from "../middleware-
|
|
11
|
+
import "../deno-aj03PJW6.js";
|
|
12
|
+
import { createFederation, handleActor, handleCollection, handleCustomCollection, handleInbox, handleObject, respondWithObject, respondWithObjectIfAcceptable } from "../middleware-DbmQ5-Ph.js";
|
|
13
13
|
import "../client-Dg7OfUDA.js";
|
|
14
14
|
import "../router-D9eI0s4b.js";
|
|
15
15
|
import "../types-CPz01LGH.js";
|
|
16
|
-
import "../key-
|
|
17
|
-
import { signRequest } from "../http-
|
|
18
|
-
import "../ld-
|
|
19
|
-
import "../owner-
|
|
20
|
-
import "../proof-
|
|
21
|
-
import "../docloader-
|
|
16
|
+
import "../key-j1AjF3HL.js";
|
|
17
|
+
import { signRequest } from "../http-pa5yLKK7.js";
|
|
18
|
+
import "../ld-CD48Tb3_.js";
|
|
19
|
+
import "../owner-ZjRdCtVA.js";
|
|
20
|
+
import "../proof-gmdzyEsv.js";
|
|
21
|
+
import "../docloader-tMQxWuTM.js";
|
|
22
22
|
import "../kv-cache-B__dHl7g.js";
|
|
23
|
-
import { InboxListenerSet } from "../inbox-
|
|
24
|
-
import "../builder-
|
|
23
|
+
import { InboxListenerSet } from "../inbox-EI2EtQri.js";
|
|
24
|
+
import "../builder-DSYM2n7q.js";
|
|
25
25
|
import "../collection-CcnIw1qY.js";
|
|
26
|
-
import "../keycache-
|
|
26
|
+
import "../keycache-C7k8s1Bk.js";
|
|
27
27
|
import "../negotiation-5NPJL6zp.js";
|
|
28
28
|
import "../retry-D4GJ670a.js";
|
|
29
|
-
import "../send-
|
|
29
|
+
import "../send-1lNwij0f.js";
|
|
30
30
|
import "../std__assert-DWivtrGR.js";
|
|
31
31
|
import "../assert_rejects-Ce45JcFg.js";
|
|
32
32
|
import "../assert_throws-BNXdRGWP.js";
|
|
@@ -34,6 +34,7 @@ import "../assert_not_equals-C80BG-_5.js";
|
|
|
34
34
|
import { createInboxContext, createRequestContext } from "../context-CZ5llAss.js";
|
|
35
35
|
import { rsaPrivateKey3, rsaPublicKey2, rsaPublicKey3 } from "../keys-ZbcByPg9.js";
|
|
36
36
|
import { Create, Note, Person } from "@fedify/vocab";
|
|
37
|
+
import { FetchError } from "@fedify/vocab-runtime";
|
|
37
38
|
|
|
38
39
|
//#region src/federation/handler.test.ts
|
|
39
40
|
test("handleActor()", async () => {
|
|
@@ -1562,12 +1563,95 @@ test("handleInbox() records OpenTelemetry span events", async () => {
|
|
|
1562
1563
|
assertEquals(events.length, 1);
|
|
1563
1564
|
const event = events[0];
|
|
1564
1565
|
assert(event.attributes != null);
|
|
1565
|
-
assertEquals(event.attributes["activitypub.activity.verified"],
|
|
1566
|
+
assertEquals(event.attributes["activitypub.activity.verified"], false);
|
|
1566
1567
|
assertEquals(event.attributes["http_signatures.verified"], false);
|
|
1567
1568
|
assert(typeof event.attributes["activitypub.activity.json"] === "string");
|
|
1568
1569
|
const recordedActivity = JSON.parse(event.attributes["activitypub.activity.json"]);
|
|
1569
1570
|
assertEquals(recordedActivity.id, "https://example.com/activity");
|
|
1570
1571
|
assertEquals(recordedActivity.type, "Create");
|
|
1571
1572
|
});
|
|
1573
|
+
test("handleInbox() records unverified HTTP signature details", async () => {
|
|
1574
|
+
const [tracerProvider, exporter] = createTestTracerProvider();
|
|
1575
|
+
const kv = new MemoryKvStore();
|
|
1576
|
+
const federation = createFederation({
|
|
1577
|
+
kv,
|
|
1578
|
+
tracerProvider
|
|
1579
|
+
});
|
|
1580
|
+
const keyId = new URL("https://gone.example/users/someone#main-key");
|
|
1581
|
+
const activity = new Create({
|
|
1582
|
+
id: new URL("https://example.com/activity"),
|
|
1583
|
+
actor: new URL("https://gone.example/users/someone"),
|
|
1584
|
+
object: new Note({
|
|
1585
|
+
id: new URL("https://example.com/note"),
|
|
1586
|
+
content: "Hello, world!"
|
|
1587
|
+
})
|
|
1588
|
+
});
|
|
1589
|
+
const request = new Request("https://example.com/users/someone/inbox", {
|
|
1590
|
+
method: "POST",
|
|
1591
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
1592
|
+
body: JSON.stringify(await activity.toJsonLd())
|
|
1593
|
+
});
|
|
1594
|
+
const signed = await signRequest(request, rsaPrivateKey3, keyId);
|
|
1595
|
+
const documentLoader = (url) => {
|
|
1596
|
+
if (url === keyId.href) throw new FetchError(keyId, `HTTP 410: ${keyId.href}`, new Response(null, { status: 410 }));
|
|
1597
|
+
return mockDocumentLoader(url);
|
|
1598
|
+
};
|
|
1599
|
+
const context = createRequestContext({
|
|
1600
|
+
federation,
|
|
1601
|
+
request: signed,
|
|
1602
|
+
url: new URL(signed.url),
|
|
1603
|
+
data: void 0,
|
|
1604
|
+
documentLoader,
|
|
1605
|
+
contextLoader: mockDocumentLoader,
|
|
1606
|
+
getActorUri(identifier) {
|
|
1607
|
+
return new URL(`https://example.com/users/${identifier}`);
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
const actorDispatcher = (ctx, identifier) => {
|
|
1611
|
+
if (identifier !== "someone") return null;
|
|
1612
|
+
return new Person({
|
|
1613
|
+
id: ctx.getActorUri(identifier),
|
|
1614
|
+
name: "Someone",
|
|
1615
|
+
inbox: new URL("https://example.com/users/someone/inbox"),
|
|
1616
|
+
publicKey: rsaPublicKey2
|
|
1617
|
+
});
|
|
1618
|
+
};
|
|
1619
|
+
const response = await handleInbox(signed, {
|
|
1620
|
+
recipient: "someone",
|
|
1621
|
+
context,
|
|
1622
|
+
inboxContextFactory(_activity) {
|
|
1623
|
+
return createInboxContext({
|
|
1624
|
+
...context,
|
|
1625
|
+
clone: void 0
|
|
1626
|
+
});
|
|
1627
|
+
},
|
|
1628
|
+
kv,
|
|
1629
|
+
kvPrefixes: {
|
|
1630
|
+
activityIdempotence: ["activityIdempotence"],
|
|
1631
|
+
publicKey: ["publicKey"]
|
|
1632
|
+
},
|
|
1633
|
+
actorDispatcher,
|
|
1634
|
+
inboxListeners: new InboxListenerSet(),
|
|
1635
|
+
inboxErrorHandler: void 0,
|
|
1636
|
+
unverifiedActivityHandler() {
|
|
1637
|
+
return new Response("", { status: 202 });
|
|
1638
|
+
},
|
|
1639
|
+
onNotFound: (_request) => new Response("Not found", { status: 404 }),
|
|
1640
|
+
signatureTimeWindow: false,
|
|
1641
|
+
skipSignatureVerification: false,
|
|
1642
|
+
tracerProvider
|
|
1643
|
+
});
|
|
1644
|
+
assertEquals(response.status, 202);
|
|
1645
|
+
const verifySpans = exporter.getSpans("http_signatures.verify");
|
|
1646
|
+
assertEquals(verifySpans.length, 1);
|
|
1647
|
+
assertEquals(verifySpans[0].attributes["http_signatures.failure_reason"], "keyFetchError");
|
|
1648
|
+
assertEquals(verifySpans[0].attributes["http_signatures.key_fetch_status"], 410);
|
|
1649
|
+
const events = exporter.getEvents("activitypub.inbox", "activitypub.activity.received");
|
|
1650
|
+
assertEquals(events.length, 1);
|
|
1651
|
+
const event = events[0];
|
|
1652
|
+
assert(event.attributes != null);
|
|
1653
|
+
assertEquals(event.attributes["http_signatures.failure_reason"], "keyFetchError");
|
|
1654
|
+
assertEquals(event.attributes["http_signatures.key_fetch_status"], 410);
|
|
1655
|
+
});
|
|
1572
1656
|
|
|
1573
1657
|
//#endregion
|
|
@@ -8,25 +8,25 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import "../assert-MZs1qjMx.js";
|
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import "../deno-
|
|
12
|
-
import { createFederation } from "../middleware-
|
|
11
|
+
import "../deno-aj03PJW6.js";
|
|
12
|
+
import { createFederation } from "../middleware-DbmQ5-Ph.js";
|
|
13
13
|
import "../client-Dg7OfUDA.js";
|
|
14
14
|
import "../router-D9eI0s4b.js";
|
|
15
15
|
import "../types-CPz01LGH.js";
|
|
16
|
-
import "../key-
|
|
17
|
-
import "../http-
|
|
18
|
-
import "../ld-
|
|
19
|
-
import "../owner-
|
|
20
|
-
import { signObject } from "../proof-
|
|
21
|
-
import "../docloader-
|
|
16
|
+
import "../key-j1AjF3HL.js";
|
|
17
|
+
import "../http-pa5yLKK7.js";
|
|
18
|
+
import "../ld-CD48Tb3_.js";
|
|
19
|
+
import "../owner-ZjRdCtVA.js";
|
|
20
|
+
import { signObject } from "../proof-gmdzyEsv.js";
|
|
21
|
+
import "../docloader-tMQxWuTM.js";
|
|
22
22
|
import "../kv-cache-B__dHl7g.js";
|
|
23
|
-
import "../inbox-
|
|
24
|
-
import "../builder-
|
|
23
|
+
import "../inbox-EI2EtQri.js";
|
|
24
|
+
import "../builder-DSYM2n7q.js";
|
|
25
25
|
import "../collection-CcnIw1qY.js";
|
|
26
|
-
import "../keycache-
|
|
26
|
+
import "../keycache-C7k8s1Bk.js";
|
|
27
27
|
import "../negotiation-5NPJL6zp.js";
|
|
28
28
|
import "../retry-D4GJ670a.js";
|
|
29
|
-
import "../send-
|
|
29
|
+
import "../send-1lNwij0f.js";
|
|
30
30
|
import "../std__assert-DWivtrGR.js";
|
|
31
31
|
import "../assert_rejects-Ce45JcFg.js";
|
|
32
32
|
import "../assert_throws-BNXdRGWP.js";
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { test } from "../dist-B5f6a8Tt.js";
|
|
7
7
|
import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
8
|
-
import "../deno-
|
|
9
|
-
import { InboxListenerSet } from "../inbox-
|
|
8
|
+
import "../deno-aj03PJW6.js";
|
|
9
|
+
import { InboxListenerSet } from "../inbox-EI2EtQri.js";
|
|
10
10
|
import { assertThrows } from "../assert_throws-BNXdRGWP.js";
|
|
11
11
|
import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
|
|
12
12
|
|
|
@@ -8,7 +8,7 @@ import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
|
|
8
8
|
import { assert } from "../assert-MZs1qjMx.js";
|
|
9
9
|
import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
|
|
10
10
|
import { MemoryKvStore } from "../kv-QzKcOQgP.js";
|
|
11
|
-
import { KvKeyCache } from "../keycache-
|
|
11
|
+
import { KvKeyCache } from "../keycache-C7k8s1Bk.js";
|
|
12
12
|
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
13
13
|
|
|
14
14
|
//#region src/federation/keycache.test.ts
|
|
@@ -29,6 +29,7 @@ test("KvKeyCache.set()", async () => {
|
|
|
29
29
|
});
|
|
30
30
|
await cache.set(new URL("https://example.com/null"), null);
|
|
31
31
|
assert(cache.nullKeys.has("https://example.com/null"));
|
|
32
|
+
assertEquals(await kv.get(["pk", "https://example.com/null"]), null);
|
|
32
33
|
});
|
|
33
34
|
test("KvKeyCache.get()", async () => {
|
|
34
35
|
const kv = new MemoryKvStore();
|
|
@@ -49,8 +50,51 @@ test("KvKeyCache.get()", async () => {
|
|
|
49
50
|
const multikey = await cache.get(new URL("https://example.com/key2"));
|
|
50
51
|
assertInstanceOf(multikey, Multikey);
|
|
51
52
|
assertEquals(multikey?.id?.href, "https://example.com/key2");
|
|
52
|
-
cache.nullKeys.
|
|
53
|
+
cache.nullKeys.set("https://example.com/null", Temporal.Now.instant().add(Temporal.Duration.from({ minutes: 10 })));
|
|
53
54
|
assertEquals(await cache.get(new URL("https://example.com/null")), null);
|
|
55
|
+
await kv.set(["pk", "https://example.com/null2"], null);
|
|
56
|
+
const cache2 = new KvKeyCache(kv, ["pk"]);
|
|
57
|
+
assertEquals(await cache2.get(new URL("https://example.com/null2")), null);
|
|
58
|
+
});
|
|
59
|
+
test("KvKeyCache fetch error metadata", async () => {
|
|
60
|
+
const kv = new MemoryKvStore();
|
|
61
|
+
const cache = new KvKeyCache(kv, ["pk"]);
|
|
62
|
+
const keyId = new URL("https://example.com/key");
|
|
63
|
+
await cache.setFetchError(keyId, {
|
|
64
|
+
status: 410,
|
|
65
|
+
response: new Response("gone", {
|
|
66
|
+
status: 410,
|
|
67
|
+
statusText: "Gone",
|
|
68
|
+
headers: { "content-type": "text/plain" }
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
const httpError = await cache.getFetchError(keyId);
|
|
72
|
+
assert(httpError != null && "status" in httpError);
|
|
73
|
+
if (httpError == null || !("status" in httpError)) throw new Error("Expected HTTP fetch error metadata.");
|
|
74
|
+
assertEquals(httpError.status, 410);
|
|
75
|
+
assertEquals(httpError.response.status, 410);
|
|
76
|
+
assertEquals(await httpError.response.text(), "gone");
|
|
77
|
+
await cache.setFetchError(keyId, { error: Object.assign(/* @__PURE__ */ new Error("boom"), { name: "TypeError" }) });
|
|
78
|
+
const nonHttpError = await cache.getFetchError(keyId);
|
|
79
|
+
assert(nonHttpError != null && "error" in nonHttpError);
|
|
80
|
+
if (nonHttpError == null || !("error" in nonHttpError)) throw new Error("Expected non-HTTP fetch error metadata.");
|
|
81
|
+
assertEquals(nonHttpError.error.name, "TypeError");
|
|
82
|
+
assertEquals(nonHttpError.error.message, "boom");
|
|
83
|
+
await cache.setFetchError(keyId, null);
|
|
84
|
+
assertEquals(await cache.getFetchError(keyId), void 0);
|
|
85
|
+
});
|
|
86
|
+
test("KvKeyCache unavailable entries expire", async () => {
|
|
87
|
+
const kv = new MemoryKvStore();
|
|
88
|
+
const cache = new KvKeyCache(kv, ["pk"], { unavailableKeyTtl: Temporal.Duration.from({ milliseconds: 1 }) });
|
|
89
|
+
const keyId = new URL("https://example.com/expired");
|
|
90
|
+
await cache.set(keyId, null);
|
|
91
|
+
await cache.setFetchError(keyId, {
|
|
92
|
+
status: 410,
|
|
93
|
+
response: new Response(null, { status: 410 })
|
|
94
|
+
});
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
96
|
+
assertEquals(await cache.get(keyId), void 0);
|
|
97
|
+
assertEquals(await cache.getFetchError(keyId), void 0);
|
|
54
98
|
});
|
|
55
99
|
|
|
56
100
|
//#endregion
|