@fedify/fedify 2.5.0-dev.2446 → 2.5.0-dev.2492
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 +3 -0
- package/dist/{activity-listener-C-BSbKI7.mjs → activity-listener-CELFAodc.mjs} +8 -4
- package/dist/{builder-Da8b9O6v.mjs → builder-DphK9v7f.mjs} +5 -5
- package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.cts} +0 -1
- package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.ts} +0 -1
- package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-D1djO5fa.mjs → compound-proof-cYtvlTgF.mjs} +2 -2
- package/dist/{context-8Tu9kea0.mjs → context-COHd1Rsh.mjs} +2 -0
- package/dist/{context-BdB54V-A.d.ts → context-DQJE9YfH.d.ts} +332 -6
- package/dist/{context-CQ9H8vNW.d.cts → context-DSuL3YWx.d.cts} +332 -6
- package/dist/{deno-eNpfj4oR.mjs → deno-iOAA7iRN.mjs} +1 -1
- package/dist/docloader-DCZkiTeF.mjs +136 -0
- package/dist/{exporter-DThFfe7P.mjs → exporter-DVG68ozH.mjs} +36 -30
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +6 -3
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +22 -7
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-actorless.test.d.mts +2 -0
- package/dist/federation/inbox-actorless.test.mjs +487 -0
- package/dist/federation/inbox-authentication.test.d.mts +2 -0
- package/dist/federation/inbox-authentication.test.mjs +116 -0
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox-view.test.d.mts +2 -0
- package/dist/federation/inbox-view.test.mjs +369 -0
- package/dist/federation/inbox.test.mjs +22 -12
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +470 -35
- package/dist/federation/mod.cjs +2 -1
- package/dist/federation/mod.d.cts +4 -4
- package/dist/federation/mod.d.ts +4 -4
- package/dist/federation/mod.js +8 -2
- package/dist/federation/mq.test.mjs +36 -1
- package/dist/federation/outgoing-proof.test.mjs +9 -6
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +13 -7
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +502 -93
- package/dist/federation/tasks/enqueue.test.mjs +3 -3
- package/dist/federation/tasks/tasks.test.mjs +4 -4
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{federation-BVH7drJA.cjs → federation-D0AZw_aw.cjs} +1415 -236
- package/dist/{http-BUJ5SFh6.mjs → http-BoJCMnjY.mjs} +204 -106
- package/dist/{http-DlNxxW_5.js → http-ChXUU_3c.js} +319 -123
- package/dist/{http-BfW3wyQ4.cjs → http-kv1LZw-r.cjs} +339 -125
- package/dist/inbox-BjihTKLo.mjs +312 -0
- package/dist/inbox-authentication-DflPYlLL.mjs +171 -0
- package/dist/inbox-view-D-skypg-.mjs +385 -0
- package/dist/{key-nYGY4QB9.d.cts → key-DbLNT-vA.d.ts} +1 -2
- package/dist/{key-DhsBxxZN.d.ts → key-DlHT4R6k.d.cts} +1 -2
- package/dist/{kv-cache-D_m3A241.mjs → kv-cache-Cmsyke4d.mjs} +1 -1
- package/dist/{kv-cache-7015lbrx.js → kv-cache-DQcte7ES.js} +73 -28
- package/dist/{kv-cache-kkBB0vbI.cjs → kv-cache-Ddh5sMxH.cjs} +72 -27
- package/dist/ld-CWVpo_NM.d.cts +250 -0
- package/dist/{ld-CD2CsPxq.mjs → ld-CdvcOEXQ.mjs} +52 -9
- package/dist/ld-Inagww4R.d.ts +250 -0
- package/dist/{metrics-D3LHk6Z4.mjs → metrics-CpxvUrBz.mjs} +1 -1
- package/dist/{middleware-N9eM16XE.js → middleware-BHb5toqA.js} +1413 -247
- package/dist/{middleware-CCDXvXOG.mjs → middleware-BKs2DLUF.mjs} +1 -1
- package/dist/{middleware-CzoWvrdZ.mjs → middleware-CoBLrTKX.mjs} +533 -132
- package/dist/{mod-B0dGgQsg.d.cts → mod-6TQ4UQZb.d.cts} +1 -1
- package/dist/mod-B4kBw1-8.d.ts +154 -0
- package/dist/{mod-DkhfZC1u.d.ts → mod-B7j30IQf.d.ts} +2 -9
- package/dist/mod-C1nT74IG.d.cts +154 -0
- package/dist/{mod-D0bxB8jS.d.ts → mod-CIeBgsOC.d.ts} +1 -1
- package/dist/{mod-B5-wdzmJ.d.cts → mod-YvA4Rk8J.d.cts} +2 -9
- package/dist/mod.cjs +6 -5
- package/dist/mod.d.cts +9 -9
- package/dist/mod.d.ts +9 -9
- package/dist/mod.js +6 -6
- package/dist/{mq-Dm1Wmtm-.mjs → mq-eF96QxUT.mjs} +6 -0
- package/dist/nodeinfo/client.test.mjs +337 -19
- package/dist/nodeinfo/handler.test.mjs +2 -2
- 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/otel/exporter.test.mjs +257 -1
- package/dist/otel/mod.cjs +36 -30
- package/dist/otel/mod.d.cts +13 -0
- package/dist/otel/mod.d.ts +13 -0
- package/dist/otel/mod.js +36 -30
- package/dist/{owner-C5-FVX6t.mjs → owner-DTnVpUKA.mjs} +3 -3
- package/dist/{portable-collection-Cony3_LC.mjs → portable-collection-CJeYTL61.mjs} +2 -2
- package/dist/{portable-collection-C-Z1SKDy.cjs → portable-collection-DciwBIia.cjs} +1 -1
- package/dist/{portable-collection-CC2zNAje.js → portable-collection-TmtpzD4E.js} +1 -1
- package/dist/{portable-key-DNc_flH-.mjs → portable-key-DcYok4ki.mjs} +1 -1
- package/dist/{proof-Cryb1c70.mjs → proof-GH7Poos_.mjs} +98 -10
- package/dist/{send-AF25pWaJ.mjs → send-BgGJh6pv.mjs} +174 -76
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +553 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +1 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +45 -5
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-BsAeuUJj.mjs → temporal-gJINzcL4.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +288 -2
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-t8xDeJZ5.mjs → testing-Dbr21vZk.mjs} +1 -1
- package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
- package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +344 -36
- package/dist/utils/kv-cache.test.mjs +1 -1
- 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/package.json +7 -7
- package/dist/docloader-B-0nqOB1.mjs +0 -91
- package/dist/inbox-CHvMaIFX.mjs +0 -188
- package/dist/mod-BRpER-B0.d.ts +0 -320
- package/dist/mod-xAfaLS4T.d.cts +0 -320
- package/dist/owner-BF0L9cMt.d.cts +0 -78
- package/dist/owner-BSzr5-I8.d.ts +0 -78
|
@@ -1,14 +1,43 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { o as JsonValue, s as NodeInfo, t as GetNodeInfoOptions } from "./client-
|
|
3
|
-
import { _ as InboxVerificationKey, d as InboxProofPolicyFailureReason, h as InboxVerificationAttempt, w as VerifyRequestFailureReason, y as HttpMessageSignaturesSpec } from "./key-
|
|
4
|
-
import {
|
|
2
|
+
import { o as JsonValue, s as NodeInfo, t as GetNodeInfoOptions } from "./client-3InDZbba.cjs";
|
|
3
|
+
import { _ as InboxVerificationKey, d as InboxProofPolicyFailureReason, h as InboxVerificationAttempt, w as VerifyRequestFailureReason, y as HttpMessageSignaturesSpec } from "./key-DlHT4R6k.cjs";
|
|
4
|
+
import { m as GetKeyOwnerOptions, n as EffectiveContextLoaderPolicy } from "./ld-CWVpo_NM.cjs";
|
|
5
5
|
import { n as KvStore, t as KvKey } from "./kv-B2mzK5tl.cjs";
|
|
6
6
|
import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObjectOptions, Multikey, Object as Object$1, Recipient, Tombstone, TraverseCollectionOptions } from "@fedify/vocab";
|
|
7
|
-
import { AuthenticatedDocumentLoaderFactory, DocumentLoader, DocumentLoaderFactory, GetUserAgentOptions, PortableObjectVerifier } from "@fedify/vocab-runtime";
|
|
7
|
+
import { AuthenticatedDocumentLoaderFactory, DocumentLoader, DocumentLoaderFactory, GetUserAgentOptions, PortableObjectVerifier, RemoteDocument } from "@fedify/vocab-runtime";
|
|
8
8
|
import { Link as Link$1, LookupWebFingerOptions, ResourceDescriptor } from "@fedify/webfinger";
|
|
9
9
|
import { MeterProvider, Span, Tracer, TracerProvider } from "@opentelemetry/api";
|
|
10
10
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
11
11
|
|
|
12
|
+
//#region src/federation/activity-listener.d.ts
|
|
13
|
+
/**
|
|
14
|
+
* Options for an inbox listener.
|
|
15
|
+
* @since 2.5.0
|
|
16
|
+
*/
|
|
17
|
+
interface InboxListenerOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Whether activities that declare no `actor` at all may reach the listener.
|
|
20
|
+
*
|
|
21
|
+
* Some protocols identify the requester without an `actor`; for example,
|
|
22
|
+
* Mastodon sends [FEP-7aa9] `FeatureRequest` activities without one. When
|
|
23
|
+
* this is `true`, such an activity is dispatched to the listener if Fedify
|
|
24
|
+
* authenticated at least one principal for it, i.e., the signer of its
|
|
25
|
+
* Object Integrity Proofs or Linked Data Signature, or otherwise the signer
|
|
26
|
+
* of the HTTP request, and its `id`, if any, has the same origin as that
|
|
27
|
+
* principal. The listener is then responsible for authorizing the request
|
|
28
|
+
* with {@link InboxContext.authentication}, e.g., with
|
|
29
|
+
* `featureInteraction.verifyRequest()` from
|
|
30
|
+
* `@fedify/interaction-controls`.
|
|
31
|
+
*
|
|
32
|
+
* An activity that declares an `actor` is never affected by this option;
|
|
33
|
+
* its actor must be authenticated as usual.
|
|
34
|
+
*
|
|
35
|
+
* [FEP-7aa9]: https://w3id.org/fep/7aa9
|
|
36
|
+
* @default `false`
|
|
37
|
+
*/
|
|
38
|
+
readonly allowActorless?: boolean;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
12
41
|
//#region src/federation/inbox-report.d.ts
|
|
13
42
|
/**
|
|
14
43
|
* A JSON value retained from the incoming request.
|
|
@@ -51,6 +80,13 @@ type InboxAuthentication = {
|
|
|
51
80
|
readonly type: "activityOriginMismatch";
|
|
52
81
|
readonly activityId: URL;
|
|
53
82
|
readonly actorIds: readonly URL[];
|
|
83
|
+
} | {
|
|
84
|
+
/**
|
|
85
|
+
* The activity declares no actor, and no principal was authenticated
|
|
86
|
+
* for it, although its listener allows activities without an actor.
|
|
87
|
+
* @since 2.5.0
|
|
88
|
+
*/
|
|
89
|
+
readonly type: "noAuthenticatedPrincipal";
|
|
54
90
|
};
|
|
55
91
|
} | {
|
|
56
92
|
readonly status: "skipped";
|
|
@@ -949,6 +985,18 @@ interface MessageQueue {
|
|
|
949
985
|
*/
|
|
950
986
|
readonly atomicEnqueueMany?: boolean;
|
|
951
987
|
/**
|
|
988
|
+
* The largest message, in bytes, that the backend can hold. Fedify compares
|
|
989
|
+
* it with a conservative estimate of a message's serialized size: the
|
|
990
|
+
* length of its JSON serialization encoded in UTF-8, plus 8 bytes for each
|
|
991
|
+
* JSON value in it, plus what encoding strings beyond Latin-1 as UTF-16
|
|
992
|
+
* adds, which also bounds binary serialization formats. When an
|
|
993
|
+
* incoming activity would make a larger inbox message, Fedify processes the
|
|
994
|
+
* activity right away instead of enqueueing it, as if there were no inbox
|
|
995
|
+
* queue. When omitted, messages are enqueued regardless of their size.
|
|
996
|
+
* @since 2.5.0
|
|
997
|
+
*/
|
|
998
|
+
readonly maxMessageSize?: number;
|
|
999
|
+
/**
|
|
952
1000
|
* Enqueues a message in the queue.
|
|
953
1001
|
* @param message The message to enqueue.
|
|
954
1002
|
* @param options Additional options for enqueuing the message.
|
|
@@ -1074,6 +1122,11 @@ declare class ParallelMessageQueue implements MessageQueue {
|
|
|
1074
1122
|
* @since 2.4.0
|
|
1075
1123
|
*/
|
|
1076
1124
|
readonly atomicEnqueueMany?: boolean;
|
|
1125
|
+
/**
|
|
1126
|
+
* Inherits the message size limit from the wrapped queue.
|
|
1127
|
+
* @since 2.5.0
|
|
1128
|
+
*/
|
|
1129
|
+
readonly maxMessageSize?: number;
|
|
1077
1130
|
readonly getDepth?: () => Promise<MessageQueueDepth>;
|
|
1078
1131
|
/**
|
|
1079
1132
|
* Constructs a new {@link ParallelMessageQueue} with the given queue and
|
|
@@ -1090,6 +1143,211 @@ declare class ParallelMessageQueue implements MessageQueue {
|
|
|
1090
1143
|
listen(handler: (message: any) => Promise<void> | void, options?: MessageQueueListenOptions): Promise<void>;
|
|
1091
1144
|
}
|
|
1092
1145
|
//#endregion
|
|
1146
|
+
//#region src/federation/inbox-authentication.d.ts
|
|
1147
|
+
/**
|
|
1148
|
+
* A party that Fedify authenticated for an incoming activity, independently
|
|
1149
|
+
* of the activity's declared `actor`.
|
|
1150
|
+
* @since 2.5.0
|
|
1151
|
+
*/
|
|
1152
|
+
interface InboxPrincipal {
|
|
1153
|
+
/**
|
|
1154
|
+
* What {@link InboxPrincipal.id} is:
|
|
1155
|
+
*
|
|
1156
|
+
* - `"actor"`: an actor whose ownership of the verifying key Fedify
|
|
1157
|
+
* confirmed with the actor's own document.
|
|
1158
|
+
* - `"portableAuthority"`: a DID whose verification method made a valid
|
|
1159
|
+
* Object Integrity Proof. It authenticates the [FEP-ef61] portable
|
|
1160
|
+
* actors of that DID, and nothing else.
|
|
1161
|
+
*
|
|
1162
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1163
|
+
*/
|
|
1164
|
+
readonly kind: "actor" | "portableAuthority";
|
|
1165
|
+
/** The actor ID, or the DID if {@link kind} is `"portableAuthority"`. */
|
|
1166
|
+
readonly id: URL;
|
|
1167
|
+
/**
|
|
1168
|
+
* How the principal was authenticated:
|
|
1169
|
+
*
|
|
1170
|
+
* - `"httpSignatures"`: it signed the HTTP request.
|
|
1171
|
+
* - `"linkedDataSignatures"`: it made the Linked Data Signature of the
|
|
1172
|
+
* activity.
|
|
1173
|
+
* - `"objectIntegrityProofs"`: it made an Object Integrity Proof of the
|
|
1174
|
+
* activity.
|
|
1175
|
+
* - `"origin"`: the activity was fetched again from the actor's own web
|
|
1176
|
+
* origin, which happens only for `Context.routeActivity()`.
|
|
1177
|
+
*/
|
|
1178
|
+
readonly mechanism: "httpSignatures" | "linkedDataSignatures" | "objectIntegrityProofs" | "origin";
|
|
1179
|
+
/** The key or verification method that authenticated the principal. */
|
|
1180
|
+
readonly keyId: URL | null;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Who Fedify authenticated for an incoming activity, and the interpretation of
|
|
1184
|
+
* the activity it authenticated. Inbox listeners receive it as
|
|
1185
|
+
* {@link InboxContext.authentication}.
|
|
1186
|
+
*
|
|
1187
|
+
* Authenticated principals are independent of the activity's declared
|
|
1188
|
+
* `actor`: an activity forwarded by a relay is signed by the relay in its
|
|
1189
|
+
* HTTP request, but its document may be signed by its author, and some
|
|
1190
|
+
* activities, such as [FEP-7aa9] `FeatureRequest`s sent by Mastodon, declare
|
|
1191
|
+
* no `actor` at all. A valid signature alone does not authorize anything;
|
|
1192
|
+
* use {@link isActorAuthenticated} to check whether a specific actor was
|
|
1193
|
+
* authenticated.
|
|
1194
|
+
*
|
|
1195
|
+
* [FEP-7aa9]: https://w3id.org/fep/7aa9
|
|
1196
|
+
* @since 2.5.0
|
|
1197
|
+
*/
|
|
1198
|
+
interface InboxAuthenticationEvidence {
|
|
1199
|
+
/**
|
|
1200
|
+
* Whether signatures were verified at all. It is `false` only when
|
|
1201
|
+
* signature verification is skipped, or when there is no evidence, e.g.,
|
|
1202
|
+
* for an activity queued by an older version of Fedify. In that case all
|
|
1203
|
+
* the principal lists are empty.
|
|
1204
|
+
*/
|
|
1205
|
+
readonly verified: boolean;
|
|
1206
|
+
/**
|
|
1207
|
+
* The principals that signed the HTTP request, i.e., the server that
|
|
1208
|
+
* delivered the activity, which may have forwarded it. It is empty when
|
|
1209
|
+
* the activity document was authenticated by its own signatures, so that
|
|
1210
|
+
* HTTP Signatures did not need to be verified.
|
|
1211
|
+
*/
|
|
1212
|
+
readonly requestSigners: readonly InboxPrincipal[];
|
|
1213
|
+
/**
|
|
1214
|
+
* The principals that made the verified Linked Data Signature or Object
|
|
1215
|
+
* Integrity Proofs of the activity document. All of them are listed when
|
|
1216
|
+
* there are several proofs.
|
|
1217
|
+
*/
|
|
1218
|
+
readonly documentSigners: readonly InboxPrincipal[];
|
|
1219
|
+
/**
|
|
1220
|
+
* The actors authenticated because `Context.routeActivity()` fetched the
|
|
1221
|
+
* activity again from their own web origin. It is always empty for
|
|
1222
|
+
* activities delivered to an inbox.
|
|
1223
|
+
*/
|
|
1224
|
+
readonly originAuthenticated: readonly InboxPrincipal[];
|
|
1225
|
+
/**
|
|
1226
|
+
* The activity as Fedify interpreted it while authenticating it, in
|
|
1227
|
+
* expanded JSON-LD. Typed vocabulary objects omit some values, such as
|
|
1228
|
+
* blank node references and values they cannot decode, so authorization
|
|
1229
|
+
* that has to count every value of a property, e.g., that a property has
|
|
1230
|
+
* exactly one value, should inspect this instead. It is deeply frozen.
|
|
1231
|
+
* It is empty when there is no evidence.
|
|
1232
|
+
*/
|
|
1233
|
+
readonly expandedActivity: readonly Record<string, unknown>[];
|
|
1234
|
+
/**
|
|
1235
|
+
* The base URL the activity was decoded with, e.g., the URL an activity
|
|
1236
|
+
* fetched by `Context.routeActivity()` was finally retrieved from, or
|
|
1237
|
+
* `null` if none was given. Relative IRIs left in
|
|
1238
|
+
* {@link expandedActivity} resolve against it, or against the activity's
|
|
1239
|
+
* own `@id` if it is `null`, as the decoder resolves them.
|
|
1240
|
+
*/
|
|
1241
|
+
readonly baseUrl: URL | null;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Options for {@link isActorAuthenticated}.
|
|
1245
|
+
* @since 2.5.0
|
|
1246
|
+
*/
|
|
1247
|
+
interface IsActorAuthenticatedOptions {
|
|
1248
|
+
/**
|
|
1249
|
+
* Which principals to consider:
|
|
1250
|
+
*
|
|
1251
|
+
* - `"effective"` (default): the document signers if there are any,
|
|
1252
|
+
* otherwise the request signers if there are any, otherwise the actors
|
|
1253
|
+
* authenticated by their origin.
|
|
1254
|
+
* - `"document"`: the document signers only.
|
|
1255
|
+
*/
|
|
1256
|
+
readonly scope?: "effective" | "document";
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Checks whether the given evidence authenticates the given actor.
|
|
1260
|
+
*
|
|
1261
|
+
* Actor IDs are compared exactly, except that [FEP-ef61] portable IDs and
|
|
1262
|
+
* compatible identifiers are compared by their canonical portable IDs, and
|
|
1263
|
+
* a portable authority (a DID) authenticates the portable actors of that DID.
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* ``` typescript
|
|
1267
|
+
* federation
|
|
1268
|
+
* .setInboxListeners("/users/{identifier}/inbox", "/inbox")
|
|
1269
|
+
* .on(Follow, async (ctx, follow) => {
|
|
1270
|
+
* if (follow.actorId == null) return;
|
|
1271
|
+
* // The actor is always authenticated for activities that declare one,
|
|
1272
|
+
* // but the same check works for any actor an activity names:
|
|
1273
|
+
* isActorAuthenticated(ctx.authentication, follow.actorId);
|
|
1274
|
+
* });
|
|
1275
|
+
* ```
|
|
1276
|
+
*
|
|
1277
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1278
|
+
* @param evidence The authentication evidence, usually
|
|
1279
|
+
* {@link InboxContext.authentication}.
|
|
1280
|
+
* @param actorId The actor ID to check.
|
|
1281
|
+
* @param options Options.
|
|
1282
|
+
* @returns `true` if the actor is authenticated.
|
|
1283
|
+
* @since 2.5.0
|
|
1284
|
+
*/
|
|
1285
|
+
declare function isActorAuthenticated(evidence: InboxAuthenticationEvidence, actorId: URL, options?: IsActorAuthenticatedOptions): boolean;
|
|
1286
|
+
/**
|
|
1287
|
+
* A {@link InboxPrincipal} as stored in a queued message.
|
|
1288
|
+
* @internal
|
|
1289
|
+
*/
|
|
1290
|
+
interface SerializedInboxPrincipal {
|
|
1291
|
+
readonly kind: InboxPrincipal["kind"];
|
|
1292
|
+
readonly id: string;
|
|
1293
|
+
readonly mechanism: InboxPrincipal["mechanism"];
|
|
1294
|
+
readonly keyId: string | null;
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* The principals of an {@link InboxAuthenticationEvidence} as stored in
|
|
1298
|
+
* a queued message. The expanded activity and the base URL are restored
|
|
1299
|
+
* from the queued verified view instead.
|
|
1300
|
+
* @internal
|
|
1301
|
+
*/
|
|
1302
|
+
interface SerializedInboxAuthentication {
|
|
1303
|
+
readonly verified: boolean;
|
|
1304
|
+
readonly requestSigners: readonly SerializedInboxPrincipal[];
|
|
1305
|
+
readonly documentSigners: readonly SerializedInboxPrincipal[];
|
|
1306
|
+
readonly originAuthenticated: readonly SerializedInboxPrincipal[];
|
|
1307
|
+
}
|
|
1308
|
+
//#endregion
|
|
1309
|
+
//#region src/federation/inbox-view.d.ts
|
|
1310
|
+
/**
|
|
1311
|
+
* A {@link ContextSnapshotEntry} as stored in a queued message. A response
|
|
1312
|
+
* that equals the one Fedify bundles for the URL is stored as a reference to
|
|
1313
|
+
* the bundled context, along with its hash for diagnostics.
|
|
1314
|
+
* @internal
|
|
1315
|
+
*/
|
|
1316
|
+
type SerializedContextSnapshotEntry = {
|
|
1317
|
+
readonly policy: EffectiveContextLoaderPolicy;
|
|
1318
|
+
readonly url: string; /** The SHA-256 of the canonical JSON of the whole response. */
|
|
1319
|
+
readonly sha256: string;
|
|
1320
|
+
} & ({
|
|
1321
|
+
readonly bundled: true;
|
|
1322
|
+
} | {
|
|
1323
|
+
readonly response: RemoteDocument;
|
|
1324
|
+
});
|
|
1325
|
+
/**
|
|
1326
|
+
* The interpretation of an incoming activity that Fedify authenticated, as
|
|
1327
|
+
* stored in a queued message. The document itself is not copied; it is
|
|
1328
|
+
* a field of the message.
|
|
1329
|
+
* @internal
|
|
1330
|
+
*/
|
|
1331
|
+
interface SerializedVerifiedActivityView {
|
|
1332
|
+
/** The format version. */
|
|
1333
|
+
readonly version: 1;
|
|
1334
|
+
/** The context loader policy the activity was parsed with. */
|
|
1335
|
+
readonly policy: EffectiveContextLoaderPolicy;
|
|
1336
|
+
/**
|
|
1337
|
+
* Which document the activity was parsed from, with its Linked Data
|
|
1338
|
+
* Signature detached: the `activity` of the message, i.e., the payload as
|
|
1339
|
+
* received, or its `normalizedActivity`, i.e., the payload compacted for
|
|
1340
|
+
* Linked Data Signatures.
|
|
1341
|
+
*/
|
|
1342
|
+
readonly source: "activity" | "normalizedActivity";
|
|
1343
|
+
/** The `baseUrl` option the activity was parsed with. */
|
|
1344
|
+
readonly baseUrl: string | null;
|
|
1345
|
+
/** The context responses the interpretation used. */
|
|
1346
|
+
readonly contexts: readonly SerializedContextSnapshotEntry[];
|
|
1347
|
+
/** The digest of the expanded activity. */
|
|
1348
|
+
readonly expandedDigest: string;
|
|
1349
|
+
}
|
|
1350
|
+
//#endregion
|
|
1093
1351
|
//#region src/federation/handler.d.ts
|
|
1094
1352
|
/**
|
|
1095
1353
|
* Options for the {@link respondWithObject} and
|
|
@@ -1504,6 +1762,21 @@ interface InboxMessage {
|
|
|
1504
1762
|
* @internal
|
|
1505
1763
|
*/
|
|
1506
1764
|
readonly ldSignatureVerified?: boolean;
|
|
1765
|
+
/**
|
|
1766
|
+
* The interpretation of the activity that Fedify authenticated, i.e., which
|
|
1767
|
+
* document it was parsed from and every JSON-LD context response the parse
|
|
1768
|
+
* used, so that the worker and its retries dispatch the very same
|
|
1769
|
+
* interpretation without loading contexts again. Absent in messages queued
|
|
1770
|
+
* by older Fedify versions.
|
|
1771
|
+
* @internal
|
|
1772
|
+
*/
|
|
1773
|
+
readonly verifiedView?: SerializedVerifiedActivityView;
|
|
1774
|
+
/**
|
|
1775
|
+
* Who Fedify authenticated for the activity. Absent in messages queued by
|
|
1776
|
+
* older Fedify versions.
|
|
1777
|
+
* @internal
|
|
1778
|
+
*/
|
|
1779
|
+
readonly authentication?: SerializedInboxAuthentication;
|
|
1507
1780
|
readonly started: string;
|
|
1508
1781
|
readonly attempt: number;
|
|
1509
1782
|
readonly identifier: string | null;
|
|
@@ -2884,11 +3157,27 @@ interface InboxListenerSetters<TContextData> {
|
|
|
2884
3157
|
/**
|
|
2885
3158
|
* Registers a listener for a specific incoming activity type.
|
|
2886
3159
|
*
|
|
3160
|
+
* @example
|
|
3161
|
+
* ``` typescript
|
|
3162
|
+
* federation
|
|
3163
|
+
* .setInboxListeners("/users/{identifier}/inbox", "/inbox")
|
|
3164
|
+
* // Mastodon sends FeatureRequest activities without an actor:
|
|
3165
|
+
* .on(FeatureRequest, async (ctx, request) => {
|
|
3166
|
+
* const verification = await featureInteraction.verifyRequest(ctx, {
|
|
3167
|
+
* request,
|
|
3168
|
+
* authentication: ctx.authentication,
|
|
3169
|
+
* });
|
|
3170
|
+
* // ...
|
|
3171
|
+
* }, { allowActorless: true });
|
|
3172
|
+
* ```
|
|
3173
|
+
*
|
|
2887
3174
|
* @param type A subclass of {@link Activity} to listen to.
|
|
2888
3175
|
* @param listener A callback to handle an incoming activity.
|
|
3176
|
+
* @param options Options for the listener. See
|
|
3177
|
+
* {@link InboxListenerOptions}. (Since 2.5.0.)
|
|
2889
3178
|
* @returns The setters object so that settings can be chained.
|
|
2890
3179
|
*/
|
|
2891
|
-
on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity
|
|
3180
|
+
on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity>, options?: InboxListenerOptions): InboxListenerSetters<TContextData>;
|
|
2892
3181
|
/**
|
|
2893
3182
|
* Registers an error handler for inbox listeners. Any exceptions thrown
|
|
2894
3183
|
* from the listeners are caught and passed to this handler.
|
|
@@ -4296,6 +4585,11 @@ interface RequestContext<TContextData> extends Context<TContextData> {
|
|
|
4296
4585
|
* Gets the public key of the sender, if any exists and it is verified.
|
|
4297
4586
|
* Otherwise, `null` is returned.
|
|
4298
4587
|
*
|
|
4588
|
+
* Successful results are reused for this request regardless of the options.
|
|
4589
|
+
* Pending checks and `null` results are reused for the same effective
|
|
4590
|
+
* document and context loaders, including failed lookups. Rejected checks
|
|
4591
|
+
* can be retried. Shared work uses the first caller's tracer provider.
|
|
4592
|
+
*
|
|
4299
4593
|
* This can be used for implementing [authorized fetch] (also known as
|
|
4300
4594
|
* secure mode) in ActivityPub.
|
|
4301
4595
|
*
|
|
@@ -4309,6 +4603,11 @@ interface RequestContext<TContextData> extends Context<TContextData> {
|
|
|
4309
4603
|
* Gets the public key of the sender, if any exists and it is verified.
|
|
4310
4604
|
* Otherwise, `null` is returned.
|
|
4311
4605
|
*
|
|
4606
|
+
* Successful results are reused for this request regardless of the options.
|
|
4607
|
+
* Pending checks and `null` results are reused for the same effective
|
|
4608
|
+
* document and context loaders, including failed lookups. Rejected checks
|
|
4609
|
+
* can be retried. Shared work uses the first caller's tracer provider.
|
|
4610
|
+
*
|
|
4312
4611
|
* This can be used for implementing [authorized fetch] (also known as
|
|
4313
4612
|
* secure mode) in ActivityPub.
|
|
4314
4613
|
*
|
|
@@ -4328,6 +4627,14 @@ interface RequestContext<TContextData> extends Context<TContextData> {
|
|
|
4328
4627
|
* Gets the owner of the signed key, if any exists and it is verified.
|
|
4329
4628
|
* Otherwise, `null` is returned.
|
|
4330
4629
|
*
|
|
4630
|
+
* Verified keys and owners are reused for this request regardless of the
|
|
4631
|
+
* options. Pending checks are shared for the same effective document and
|
|
4632
|
+
* context loaders. A `null` result from an owner lookup after key
|
|
4633
|
+
* verification is retained for those loaders. A `null` result caused by
|
|
4634
|
+
* an unverified key is not retained, allowing an owner lookup after another
|
|
4635
|
+
* loader verifies the key. Rejected checks can be retried. Shared work
|
|
4636
|
+
* uses the first caller's tracer provider.
|
|
4637
|
+
*
|
|
4331
4638
|
* This can be used for implementing [authorized fetch] (also known as
|
|
4332
4639
|
* secure mode) in ActivityPub.
|
|
4333
4640
|
*
|
|
@@ -4342,6 +4649,14 @@ interface RequestContext<TContextData> extends Context<TContextData> {
|
|
|
4342
4649
|
* Gets the owner of the signed key, if any exists and it is verified.
|
|
4343
4650
|
* Otherwise, `null` is returned.
|
|
4344
4651
|
*
|
|
4652
|
+
* Verified keys and owners are reused for this request regardless of the
|
|
4653
|
+
* options. Pending checks are shared for the same effective document and
|
|
4654
|
+
* context loaders. A `null` result from an owner lookup after key
|
|
4655
|
+
* verification is retained for those loaders. A `null` result caused by
|
|
4656
|
+
* an unverified key is not retained, allowing an owner lookup after another
|
|
4657
|
+
* loader verifies the key. Rejected checks can be retried. Shared work
|
|
4658
|
+
* uses the first caller's tracer provider.
|
|
4659
|
+
*
|
|
4345
4660
|
* This can be used for implementing [authorized fetch] (also known as
|
|
4346
4661
|
* secure mode) in ActivityPub.
|
|
4347
4662
|
*
|
|
@@ -4417,6 +4732,17 @@ interface InboxContext<TContextData> extends Context<TContextData> {
|
|
|
4417
4732
|
*/
|
|
4418
4733
|
readonly recipient: string | null;
|
|
4419
4734
|
/**
|
|
4735
|
+
* Who Fedify authenticated for the incoming activity, independently of its
|
|
4736
|
+
* declared `actor`, and the interpretation of the activity that was
|
|
4737
|
+
* authenticated. It is preserved through the inbox queue and its retries.
|
|
4738
|
+
*
|
|
4739
|
+
* Use it with {@link isActorAuthenticated} to authorize requests that do
|
|
4740
|
+
* not declare an `actor`, which reach a listener only if it is registered
|
|
4741
|
+
* with the `allowActorless` option.
|
|
4742
|
+
* @since 2.5.0
|
|
4743
|
+
*/
|
|
4744
|
+
readonly authentication: InboxAuthenticationEvidence;
|
|
4745
|
+
/**
|
|
4420
4746
|
* Creates a new context with the same properties as this one,
|
|
4421
4747
|
* but with the given data.
|
|
4422
4748
|
* @param data The new data to associate with the context.
|
|
@@ -4836,4 +5162,4 @@ interface ActorKeyPair extends CryptoKeyPair {
|
|
|
4836
5162
|
readonly multikey: Multikey;
|
|
4837
5163
|
}
|
|
4838
5164
|
//#endregion
|
|
4839
|
-
export { respondWithObject as $,
|
|
5165
|
+
export { respondWithObject as $, SendActivityError as $t, InboxChallengePolicy as A, CollectionCursor as At, createFederation as B, NodeInfoDispatcher as Bt, FederationBenchmarkOptions as C, parseCircuitBreakerKvState as Ct, FederationStartQueueOptions as D, ActorKeyPairsDispatcher as Dt, FederationOptions as E, ActorHandleMapper as Et, PortableInboxForwardingOptions as F, HashlinkMediaDispatcher as Ft, TaskEnqueueOptions as G, OutboxListenerErrorHandler as Gt, createFederationBuilder as H, ObjectDispatcher as Ht, Rfc6570Expression as I, HashlinkMediaRequest as It, CreateExponentialBackoffPolicyOptions as J, PortableCollectionOwnerMapper as Jt, TaskHandler as K, OutboxPermanentFailureHandler as Kt, FederationKvPrefixes as L, InboxErrorHandler as Lt, MediaUploaderSetters as M, CustomCollectionCounter as Mt, ObjectCallbackSetters as N, CustomCollectionCursor as Nt, IdempotencyKeyCallback as O, AuthorizePredicate as Ot, OutboxListenerSetters as P, CustomCollectionDispatcher as Pt, RespondWithObjectOptions as Q, WebFingerLinksDispatcher as Qt, FederationOrigin as R, InboxListener as Rt, Federation as S, normalizeCircuitBreakerOptions as St, FederationFetchOptions as T, ActorDispatcher as Tt, TaskDefinition as U, OutboxErrorHandler as Ut, Message as V, ObjectAuthorizePredicate as Vt, TaskDefinitionOptions as W, OutboxListener as Wt, RetryPolicy as X, UnverifiedActivityHandler as Xt, RetryContext as Y, SharedInboxKeyDispatcher as Yt, createExponentialBackoffPolicy as Z, UnverifiedActivityReason as Zt, ActorCallbackSetters as _, CircuitBreakerKvState as _t, GetObjectOptions as a, InboxAuthentication as an, InProcessMessageQueue as at, CustomCollectionCallbackSetters as b, CircuitBreakerStateChange as bt, IsSignedByAudienceOptions as c, InboxRequestOutcome as cn, MessageQueueDepth as ct, ParseUriResult as d, ParallelMessageQueue as dt, SenderKeyPair as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreaker as ft, SendActivityOptionsForCollection as g, CircuitBreakerFailurePolicy as gt, SendActivityOptions as h, CircuitBreakerCreateOptions as ht, GetActorOptions as i, ActivityTransformer as in, isActorAuthenticated as it, InboxListenerSetters as j, CollectionDispatcher as jt, IdempotencyStrategy as k, CollectionCounter as kt, OutboxContext as l, InboxRequestReport as ln, MessageQueueEnqueueOptions as lt, RouteActivityOptions as m, CircuitBreakerBeforeSendDecision as mt, Context as n, buildCollectionSynchronizationHeader as nn, InboxPrincipal as nt, GetSignedKeyOptions as o, InboxJsonValue as on, InProcessMessageQueueOptions as ot, RequestContext as p, CircuitBreakerActivityDrop as pt, TaskRegistry as q, PortableActorIdMapper as qt, ForwardActivityOptions as r, digest as rn, IsActorAuthenticatedOptions as rt, InboxContext as s, InboxRequestFinishedHandler as sn, MessageQueue as st, ActorKeyPair as t, PageItems as tn, InboxAuthenticationEvidence as tt, ParseUriOptions as u, InboxListenerOptions as un, MessageQueueListenOptions as ut, CollectionCallbackSetters as v, CircuitBreakerOptions as vt, FederationBuilder as w, ActorAliasMapper as wt, Federatable as x, NormalizedCircuitBreakerOptions as xt, ConstructorWithTypeId as y, CircuitBreakerState as yt, FederationQueueOptions as z, MediaUploaderCallback as zt };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { D as validateCryptoKey } from "./ld-CdvcOEXQ.mjs";
|
|
5
|
+
import { r as doubleKnockScoped } from "./http-BoJCMnjY.mjs";
|
|
6
|
+
import { getLogger } from "@logtape/logtape";
|
|
7
|
+
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, resolveDocumentLoaderTimeout, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
|
|
8
|
+
import { curry } from "es-toolkit";
|
|
9
|
+
import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
|
|
10
|
+
//#region src/utils/docloader.ts
|
|
11
|
+
const logger = getLogger([
|
|
12
|
+
"fedify",
|
|
13
|
+
"utils",
|
|
14
|
+
"docloader"
|
|
15
|
+
]);
|
|
16
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
17
|
+
/**
|
|
18
|
+
* Gets an authenticated {@link DocumentLoader} for the given identity.
|
|
19
|
+
* Note that an authenticated document loader intentionally does not cache
|
|
20
|
+
* the fetched documents.
|
|
21
|
+
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
22
|
+
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
23
|
+
* Each call, including its double-knocking retries, times out after
|
|
24
|
+
* 10 seconds by default; see {@link DocumentLoaderFactoryOptions.timeout}.
|
|
25
|
+
* @param identity The identity to get the document loader for.
|
|
26
|
+
* The actor's key pair.
|
|
27
|
+
* @param options The options for the document loader.
|
|
28
|
+
* @returns The authenticated document loader.
|
|
29
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
30
|
+
* @throws {RangeError} If the `timeout` option is invalid.
|
|
31
|
+
* @since 0.4.0
|
|
32
|
+
*/
|
|
33
|
+
function getAuthenticatedDocumentLoader(identity, options = {}) {
|
|
34
|
+
return createAuthenticatedDocumentLoader(identity, options);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The placeholder that the alternate document callback of
|
|
38
|
+
* `getRemoteDocument()` returns, so that the alternate document is loaded
|
|
39
|
+
* after the current hop is closed.
|
|
40
|
+
*/
|
|
41
|
+
const ALTERNATE_PLACEHOLDER = Object.freeze({
|
|
42
|
+
contextUrl: null,
|
|
43
|
+
document: null,
|
|
44
|
+
documentUrl: "about:blank"
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* The implementation of {@link getAuthenticatedDocumentLoader}. This is not
|
|
48
|
+
* exported from any entry point.
|
|
49
|
+
* @param identity The identity to get the document loader for.
|
|
50
|
+
* @param options The options for the document loader.
|
|
51
|
+
* @param deps The dependencies, for tests.
|
|
52
|
+
* @returns The authenticated document loader.
|
|
53
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
54
|
+
*/
|
|
55
|
+
function createAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, timeout, tracerProvider } = {}, deps = {}) {
|
|
56
|
+
validateCryptoKey(identity.privateKey);
|
|
57
|
+
const resolvedTimeout = resolveDocumentLoaderTimeout(timeout);
|
|
58
|
+
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
59
|
+
options?.signal?.throwIfAborted();
|
|
60
|
+
let currentUrl = new URL(url).href;
|
|
61
|
+
validateUrl(currentUrl);
|
|
62
|
+
visited.add(currentUrl);
|
|
63
|
+
const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
|
|
64
|
+
function follow(nextUrl) {
|
|
65
|
+
options?.signal?.throwIfAborted();
|
|
66
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
67
|
+
if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
68
|
+
redirected++;
|
|
69
|
+
}
|
|
70
|
+
const result = await doubleKnockScoped(originalRequest, identity, {
|
|
71
|
+
maxRedirection,
|
|
72
|
+
specDeterminer,
|
|
73
|
+
log: curry(logRequest)(logger),
|
|
74
|
+
tracerProvider,
|
|
75
|
+
signal: options?.signal,
|
|
76
|
+
validateRedirect: (nextUrl) => {
|
|
77
|
+
follow(nextUrl);
|
|
78
|
+
validateUrl(nextUrl);
|
|
79
|
+
visited.add(nextUrl);
|
|
80
|
+
currentUrl = nextUrl;
|
|
81
|
+
}
|
|
82
|
+
}, (hopUrl) => openHop(hopUrl, options), async (response) => {
|
|
83
|
+
options?.signal?.throwIfAborted();
|
|
84
|
+
let alternateUrl;
|
|
85
|
+
const document = await getRemoteDocument(currentUrl, response, (url) => {
|
|
86
|
+
alternateUrl = url;
|
|
87
|
+
return Promise.resolve(ALTERNATE_PLACEHOLDER);
|
|
88
|
+
}, options);
|
|
89
|
+
if (alternateUrl == null) return { document };
|
|
90
|
+
if (!response.bodyUsed && response.body?.locked !== true) response.body?.cancel().catch((error) => {
|
|
91
|
+
logger.debug("Failed to cancel the response body from {url}: {error}", {
|
|
92
|
+
url: currentUrl,
|
|
93
|
+
error
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return { alternateUrl };
|
|
97
|
+
});
|
|
98
|
+
options?.signal?.throwIfAborted();
|
|
99
|
+
if ("document" in result) return result.document;
|
|
100
|
+
follow(result.alternateUrl);
|
|
101
|
+
return load(result.alternateUrl, options, redirected, visited);
|
|
102
|
+
}
|
|
103
|
+
function openUnvalidatedHop(url, signal) {
|
|
104
|
+
if (deps.openHop != null) return deps.openHop(url);
|
|
105
|
+
return allowPrivateAddress ? openDirectFetch(url) : openPublicFetch(url, { signal });
|
|
106
|
+
}
|
|
107
|
+
async function openHop(url, options) {
|
|
108
|
+
try {
|
|
109
|
+
const session = await openUnvalidatedHop(url, options?.signal);
|
|
110
|
+
if (options?.signal?.aborted) {
|
|
111
|
+
await session.close();
|
|
112
|
+
options.signal.throwIfAborted();
|
|
113
|
+
}
|
|
114
|
+
return session;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
|
|
117
|
+
url,
|
|
118
|
+
error
|
|
119
|
+
});
|
|
120
|
+
else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
|
|
121
|
+
url,
|
|
122
|
+
error
|
|
123
|
+
});
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function validateUrl(url) {
|
|
128
|
+
const parsedUrl = new URL(url);
|
|
129
|
+
const protocol = parsedUrl.protocol;
|
|
130
|
+
if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
|
|
131
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
132
|
+
}
|
|
133
|
+
return withDocumentLoaderTimeout((url, options) => load(url, options), resolvedTimeout);
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { getAuthenticatedDocumentLoader as n, createAuthenticatedDocumentLoader as t };
|
|
@@ -189,33 +189,6 @@ var FedifySpanExporter = class {
|
|
|
189
189
|
record.spanId
|
|
190
190
|
];
|
|
191
191
|
await this.#kv.set(key, record, options);
|
|
192
|
-
await this.#updateTraceSummary(record, options);
|
|
193
|
-
}
|
|
194
|
-
async #setWithCasRetry(key, transform, options) {
|
|
195
|
-
if (this.#kv.cas != null) for (let attempt = 0; attempt < 3; attempt++) {
|
|
196
|
-
const existing = await this.#kv.get(key);
|
|
197
|
-
const newValue = transform(existing);
|
|
198
|
-
if (await this.#kv.cas(key, existing, newValue, options)) return;
|
|
199
|
-
}
|
|
200
|
-
const newValue = transform(await this.#kv.get(key));
|
|
201
|
-
await this.#kv.set(key, newValue, options);
|
|
202
|
-
}
|
|
203
|
-
async #updateTraceSummary(record, options) {
|
|
204
|
-
const summaryKey = [
|
|
205
|
-
...this.#keyPrefix,
|
|
206
|
-
"_summaries",
|
|
207
|
-
record.traceId
|
|
208
|
-
];
|
|
209
|
-
await this.#setWithCasRetry(summaryKey, (existing) => {
|
|
210
|
-
const activityCount = existing != null ? existing.activityCount + 1 : 1;
|
|
211
|
-
const activityTypes = existing != null ? existing.activityTypes.includes(record.activityType) ? existing.activityTypes : [...existing.activityTypes, record.activityType] : [record.activityType];
|
|
212
|
-
return {
|
|
213
|
-
traceId: existing?.traceId ?? record.traceId,
|
|
214
|
-
timestamp: existing?.timestamp ?? record.timestamp,
|
|
215
|
-
activityCount,
|
|
216
|
-
activityTypes
|
|
217
|
-
};
|
|
218
|
-
}, options);
|
|
219
192
|
}
|
|
220
193
|
/**
|
|
221
194
|
* Gets all activity records for a specific trace ID.
|
|
@@ -232,13 +205,46 @@ var FedifySpanExporter = class {
|
|
|
232
205
|
/**
|
|
233
206
|
* Gets recent traces with summary information.
|
|
234
207
|
*
|
|
208
|
+
* Each summary is computed directly from the activity records currently
|
|
209
|
+
* stored for each trace, rather than from a separately maintained
|
|
210
|
+
* counter. This guarantees that a trace is discoverable, and its
|
|
211
|
+
* `activityCount` accurate, as soon as its records are stored, even if
|
|
212
|
+
* an earlier attempt at summarizing it (in a previous version of this
|
|
213
|
+
* exporter) never completed, and that the count always reflects
|
|
214
|
+
* records that have not expired. It costs a full scan of every stored
|
|
215
|
+
* record on every call, including full inbound activity JSON. The
|
|
216
|
+
* `limit` option only truncates the returned summaries and does not
|
|
217
|
+
* reduce the scan. Callers that poll this method frequently should
|
|
218
|
+
* account for the total retained data when choosing a TTL and polling
|
|
219
|
+
* interval.
|
|
220
|
+
*
|
|
235
221
|
* @param options Options for the query.
|
|
236
222
|
* @returns An array of trace summaries.
|
|
237
223
|
*/
|
|
238
224
|
async getRecentTraces(options) {
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
for await (const entry of this.#kv.list(
|
|
225
|
+
const aggregates = /* @__PURE__ */ new Map();
|
|
226
|
+
const traceIdIndex = this.#keyPrefix.length;
|
|
227
|
+
for await (const entry of this.#kv.list(this.#keyPrefix)) {
|
|
228
|
+
const traceId = entry.key[traceIdIndex];
|
|
229
|
+
if (traceId == null || traceId === "_summaries") continue;
|
|
230
|
+
const record = entry.value;
|
|
231
|
+
if (record == null || typeof record.timestamp !== "string") continue;
|
|
232
|
+
const aggregate = aggregates.get(traceId);
|
|
233
|
+
if (aggregate == null) aggregates.set(traceId, {
|
|
234
|
+
timestamp: record.timestamp,
|
|
235
|
+
activityCount: 1,
|
|
236
|
+
activityTypes: [record.activityType]
|
|
237
|
+
});
|
|
238
|
+
else {
|
|
239
|
+
aggregate.activityCount++;
|
|
240
|
+
if (record.timestamp < aggregate.timestamp) aggregate.timestamp = record.timestamp;
|
|
241
|
+
if (!aggregate.activityTypes.includes(record.activityType)) aggregate.activityTypes.push(record.activityType);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const summaries = [...aggregates].map(([traceId, aggregate]) => ({
|
|
245
|
+
traceId,
|
|
246
|
+
...aggregate
|
|
247
|
+
}));
|
|
242
248
|
summaries.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
243
249
|
if (options?.limit != null) return summaries.slice(0, options.limit);
|
|
244
250
|
return summaries;
|