@fedify/fedify 2.4.0-dev.2253 → 2.4.0-dev.2259
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-DdUWFCwL.mjs → builder-CBNa1Uw2.mjs} +8 -2
- 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-CcWDD5VB.mjs → compound-proof-CMPv4UK0.mjs} +20 -3
- package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
- package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
- package/dist/{deno-D9uhdIuD.mjs → deno-C8XaXmPB.mjs} +1 -1
- package/dist/{docloader-fbmPhCdS.mjs → docloader-CpdFLwAg.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +76 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +4 -3
- package/dist/federation/mod.d.ts +4 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- 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 +4 -4
- package/dist/federation/portable-inbox.test.mjs +67 -6
- 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 +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-B858-Ylk.cjs → federation-D6giSGUT.cjs} +524 -118
- package/dist/{http-CUyBaVsQ.mjs → http-BqSbUEiQ.mjs} +141 -98
- package/dist/{http-Bp0MKAK3.cjs → http-Cki-npuA.cjs} +456 -116
- package/dist/{http-BoGE-nk7.js → http-OMtKlbx1.js} +439 -117
- package/dist/{http-DGx0KOws.d.ts → key-C1nv7_jD.d.ts} +638 -229
- package/dist/{http-BrwKINQn.d.cts → key-DYxuyd-s.d.cts} +638 -229
- package/dist/{key-BlTaKABr.mjs → key-DecAtABm.mjs} +30 -3
- package/dist/{kv-cache-Dmqd7s2a.mjs → kv-cache-CWWlMv-s.mjs} +1 -1
- package/dist/{kv-cache-BnWemxH2.cjs → kv-cache-D73yVcE5.cjs} +1 -1
- package/dist/{kv-cache-DhMzUXEE.js → kv-cache-J0v3UbUn.js} +1 -1
- package/dist/{ld-Dx8r_qJU.mjs → ld-UsN-TIPh.mjs} +40 -5
- package/dist/{metrics-DFkZkLrO.mjs → metrics-DWCn4VXX.mjs} +2 -1
- package/dist/{middleware-DC018B4u.js → middleware-BqzfIlUO.js} +524 -118
- package/dist/{middleware-B0RRI88k.mjs → middleware-CkjwiEBd.mjs} +512 -127
- package/dist/{middleware-BNhF-Ayx.mjs → middleware-DHn2q0L1.mjs} +1 -1
- package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/{mod-Cy9VNHVw.d.ts → mod-CavhAgLw.d.cts} +5 -253
- package/dist/{mod-DoTWIc7F.d.cts → mod-CtpF2BU5.d.ts} +5 -253
- package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
- package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
- package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-B61jjp5K.mjs → owner-CEBn81LD.mjs} +3 -3
- package/dist/{portable-collection-DhEA_nWT.cjs → portable-collection-DqTUBKY7.cjs} +1 -1
- package/dist/{portable-collection-BnktjQ04.js → portable-collection-ZkUNjtb7.js} +1 -1
- package/dist/{portable-collection-CyBJDEzs.mjs → portable-collection-qFSYw4TG.mjs} +2 -2
- package/dist/{portable-key-CPN0QZSA.mjs → portable-key-LlMkP-vz.mjs} +1 -1
- package/dist/{proof-CZrjvoJ3.mjs → proof-BHRf_jMG.mjs} +142 -22
- package/dist/{send-enxOnmZo.mjs → send-DjG8DDU_.mjs} +3 -3
- 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 +19 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- 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 +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- 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.d.mts +2 -0
- package/dist/sig/portable-interop.test.mjs +156 -0
- package/dist/sig/portable-key.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +161 -3
- 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/{temporal-CBVv0xRB.mjs → temporal-BRFH_dte.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +315 -28
- package/dist/utils/docloader.test.mjs +2 -2
- 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/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- package/package.json +6 -6
package/dist/testing/mod.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { ExportResultCode } from "@opentelemetry/core";
|
|
4
4
|
import { Router } from "@fedify/uri-template";
|
|
5
|
-
import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObjectOptions, Multikey, Note, Object as Object$1, Recipient, Tombstone, TraverseCollectionOptions } from "@fedify/vocab";
|
|
5
|
+
import { Activity, Actor, Collection, CryptographicKey, DataIntegrityProof, Hashtag, Link, LookupObjectOptions, Multikey, Note, Object as Object$1, Recipient, Tombstone, TraverseCollectionOptions } from "@fedify/vocab";
|
|
6
6
|
import { Link as Link$1, LookupWebFingerOptions, ResourceDescriptor } from "@fedify/webfinger";
|
|
7
7
|
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
8
8
|
import { AuthenticatedDocumentLoaderFactory, DocumentLoader, DocumentLoaderFactory, GetUserAgentOptions, PortableObjectVerifier } from "@fedify/vocab-runtime";
|
|
@@ -169,6 +169,215 @@ interface GetNodeInfoOptions {
|
|
|
169
169
|
userAgent?: GetUserAgentOptions | string;
|
|
170
170
|
}
|
|
171
171
|
//#endregion
|
|
172
|
+
//#region src/sig/proof.d.ts
|
|
173
|
+
/**
|
|
174
|
+
* The reason why {@link verifyPortableObjectProof} could not verify a portable
|
|
175
|
+
* object proof.
|
|
176
|
+
* @since 2.4.0
|
|
177
|
+
*/
|
|
178
|
+
type VerifyPortableObjectProofFailureReason = {
|
|
179
|
+
/**
|
|
180
|
+
* The document has neither a portable `ap:` or `ap+ef61:` ID nor an
|
|
181
|
+
* FEP-ef61 compatible identifier.
|
|
182
|
+
*/
|
|
183
|
+
readonly type: "notPortableObject";
|
|
184
|
+
} | {
|
|
185
|
+
/**
|
|
186
|
+
* The document is a portable collection without an Object Integrity
|
|
187
|
+
* Proof. Its trust policy is outside this verifier.
|
|
188
|
+
*/
|
|
189
|
+
readonly type: "unsecuredCollection";
|
|
190
|
+
} | {
|
|
191
|
+
/** The portable document is a core type outside FEP-ef61 proof policy. */readonly type: "unsupportedObjectType"; /** The FEP-2277 core type of the document. */
|
|
192
|
+
readonly objectType: "verificationMethod" | "publicKey" | "link";
|
|
193
|
+
} | {
|
|
194
|
+
/** A portable actor, activity, or object has no proof. */readonly type: "missingProof";
|
|
195
|
+
} | {
|
|
196
|
+
/**
|
|
197
|
+
* A portable actor's `gateways` is missing or empty, or has an item that
|
|
198
|
+
* is not an HTTP(S) URI with an empty path, query, and fragment.
|
|
199
|
+
* The document is rejected before its proofs are verified, so this
|
|
200
|
+
* reason does not mean that the proofs are valid.
|
|
201
|
+
*/
|
|
202
|
+
readonly type: "invalidGateways";
|
|
203
|
+
} | {
|
|
204
|
+
/** The proof is malformed, unsupported, or cryptographically invalid. */readonly type: "invalidProof"; /** The zero-based index of the invalid proof. */
|
|
205
|
+
readonly proofIndex: number;
|
|
206
|
+
} | {
|
|
207
|
+
/** The proof's verification method is not a valid DID URL. */readonly type: "unsupportedVerificationMethod"; /** The zero-based index of the proof. */
|
|
208
|
+
readonly proofIndex: number; /** The unsupported verification method. */
|
|
209
|
+
readonly verificationMethod: URL;
|
|
210
|
+
} | {
|
|
211
|
+
/** The verification method DID does not match the portable ID authority. */readonly type: "verificationMethodMismatch"; /** The zero-based index of the proof. */
|
|
212
|
+
readonly proofIndex: number; /** The portable object ID. */
|
|
213
|
+
readonly objectId: URL; /** The mismatching verification method. */
|
|
214
|
+
readonly verificationMethod: URL;
|
|
215
|
+
};
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/sig/compound-proof.d.ts
|
|
218
|
+
/** Why compound-proof discovery could not produce an atomic result. */
|
|
219
|
+
type CompoundProofDiscoveryFailureReason = {
|
|
220
|
+
readonly type: "invalidJsonTree";
|
|
221
|
+
readonly path: string;
|
|
222
|
+
} | {
|
|
223
|
+
readonly type: "unsupportedProofShape";
|
|
224
|
+
readonly path: string;
|
|
225
|
+
} | {
|
|
226
|
+
readonly type: "limitExceeded";
|
|
227
|
+
readonly limit: "depth" | "maps" | "proofs" | "bytes";
|
|
228
|
+
readonly maximum: number;
|
|
229
|
+
readonly actual: number;
|
|
230
|
+
readonly path: string;
|
|
231
|
+
};
|
|
232
|
+
/** Why a discovered map-local proof did not verify. */
|
|
233
|
+
type CompoundProofVerificationFailureReason = {
|
|
234
|
+
/** A nested secured map does not carry its own JSON-LD context. */readonly type: "missingContext";
|
|
235
|
+
} | {
|
|
236
|
+
/** The direct proof is malformed, unsupported, or invalid. */readonly type: "invalidProof";
|
|
237
|
+
};
|
|
238
|
+
/** Why one portable map did not pass compound proof policy. */
|
|
239
|
+
type CompoundPortableObjectFailureReason = CompoundProofVerificationFailureReason | VerifyPortableObjectProofFailureReason | {
|
|
240
|
+
/** The portable map could not be interpreted by the policy layer. */readonly type: "invalidPortableObject";
|
|
241
|
+
};
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/sig/http.d.ts
|
|
244
|
+
/**
|
|
245
|
+
* The standard to use for signing and verifying HTTP signatures.
|
|
246
|
+
* @since 1.6.0
|
|
247
|
+
*/
|
|
248
|
+
type HttpMessageSignaturesSpec =
|
|
249
|
+
/**
|
|
250
|
+
* The Signing HTTP Messages (draft-cavage-http-signatures-12) specification,
|
|
251
|
+
* which is widely adopted and used in the fediverse (as of May 2025).
|
|
252
|
+
*/
|
|
253
|
+
"draft-cavage-http-signatures-12"
|
|
254
|
+
/**
|
|
255
|
+
* The HTTP Message Signatures (RFC 9421) specification, which is the
|
|
256
|
+
* finalized standard but not widely adopted yet (as of May 2025).
|
|
257
|
+
*/
|
|
258
|
+
| "rfc9421";
|
|
259
|
+
/**
|
|
260
|
+
* The reason why {@link verifyRequestDetailed} could not verify a request.
|
|
261
|
+
* @since 2.1.0
|
|
262
|
+
*/
|
|
263
|
+
type VerifyRequestFailureReason = {
|
|
264
|
+
readonly type: "keyFetchError";
|
|
265
|
+
readonly keyId: URL;
|
|
266
|
+
readonly result: FetchKeyErrorResult;
|
|
267
|
+
} | {
|
|
268
|
+
readonly type: "invalidSignature";
|
|
269
|
+
readonly keyId?: URL;
|
|
270
|
+
} | {
|
|
271
|
+
readonly type: "noSignature";
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/sig/verification.d.ts
|
|
275
|
+
/**
|
|
276
|
+
* A snapshot of a key actually used to check a signature.
|
|
277
|
+
* @since 2.4.0
|
|
278
|
+
*/
|
|
279
|
+
type InboxVerificationKey = {
|
|
280
|
+
readonly type: "cryptographicKey";
|
|
281
|
+
readonly id: URL | null;
|
|
282
|
+
readonly ownerId: URL | null;
|
|
283
|
+
readonly publicKey: CryptoKey;
|
|
284
|
+
} | {
|
|
285
|
+
readonly type: "multikey";
|
|
286
|
+
readonly id: URL | null;
|
|
287
|
+
readonly controllerId: URL | null;
|
|
288
|
+
readonly publicKey: CryptoKey;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Why a cryptographic check failed.
|
|
292
|
+
* @since 2.4.0
|
|
293
|
+
*/
|
|
294
|
+
type InboxSignatureFailureReason = {
|
|
295
|
+
readonly type: "invalidSignature";
|
|
296
|
+
} | {
|
|
297
|
+
readonly type: "keyFetchError";
|
|
298
|
+
readonly keyId: URL;
|
|
299
|
+
readonly result: FetchKeyErrorResult;
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* The signature or proof being checked.
|
|
303
|
+
* @since 2.4.0
|
|
304
|
+
*/
|
|
305
|
+
type InboxSignatureIdentity = {
|
|
306
|
+
readonly mechanism: "http";
|
|
307
|
+
readonly spec: HttpMessageSignaturesSpec;
|
|
308
|
+
readonly label: string | null;
|
|
309
|
+
} | {
|
|
310
|
+
readonly mechanism: "linkedData";
|
|
311
|
+
} | {
|
|
312
|
+
readonly mechanism: "objectIntegrity";
|
|
313
|
+
readonly proofId: string | null; /** Index within this evaluation, not a global proof identifier. */
|
|
314
|
+
readonly proofIndex: number | null;
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Evidence from one signature/proof, including cached-key retries.
|
|
318
|
+
* @since 2.4.0
|
|
319
|
+
*/
|
|
320
|
+
type InboxSignatureCheck = InboxSignatureIdentity & {
|
|
321
|
+
/** The declaration as received, including invalid IRIs. */readonly declaredKeyId: string | null; /** Keys actually tried, in use order; refresh details are not a retry API. */
|
|
322
|
+
readonly triedKeys: readonly InboxVerificationKey[];
|
|
323
|
+
} & ({
|
|
324
|
+
readonly status: "verified";
|
|
325
|
+
readonly key: InboxVerificationKey;
|
|
326
|
+
} | {
|
|
327
|
+
readonly status: "rejected";
|
|
328
|
+
readonly reason: InboxSignatureFailureReason;
|
|
329
|
+
} | {
|
|
330
|
+
readonly status: "error";
|
|
331
|
+
readonly error: unknown;
|
|
332
|
+
});
|
|
333
|
+
/**
|
|
334
|
+
* A bounded proof-policy diagnostic.
|
|
335
|
+
* @since 2.4.0
|
|
336
|
+
*/
|
|
337
|
+
type InboxProofPolicyFailureReason = VerifyPortableObjectProofFailureReason | CompoundPortableObjectFailureReason | CompoundProofDiscoveryFailureReason | {
|
|
338
|
+
readonly type: "invalidObjectId" | "invalidDocument" | "subjectMismatch";
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Why a complete verification evaluation was rejected.
|
|
342
|
+
* @since 2.4.0
|
|
343
|
+
*/
|
|
344
|
+
type InboxVerificationFailureReason = {
|
|
345
|
+
readonly type: "noSignature";
|
|
346
|
+
} | {
|
|
347
|
+
readonly type: "signatureVerificationFailed";
|
|
348
|
+
} | {
|
|
349
|
+
readonly type: "uncoveredAttribution";
|
|
350
|
+
readonly attributionIds: readonly URL[];
|
|
351
|
+
} | {
|
|
352
|
+
readonly type: "missingOwner";
|
|
353
|
+
} | {
|
|
354
|
+
readonly type: "proofPolicy";
|
|
355
|
+
readonly reason: InboxProofPolicyFailureReason;
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* A logical verification evaluation, separate from inbox authentication.
|
|
359
|
+
* @since 2.4.0
|
|
360
|
+
*/
|
|
361
|
+
type InboxVerificationAttempt = {
|
|
362
|
+
readonly mechanism: "http" | "linkedData" | "objectIntegrity";
|
|
363
|
+
readonly subject: {
|
|
364
|
+
readonly id: URL | null; /** RFC 6901 pointer; empty for the root, null when unavailable. */
|
|
365
|
+
readonly pointer: string | null;
|
|
366
|
+
};
|
|
367
|
+
readonly checks: readonly InboxSignatureCheck[];
|
|
368
|
+
} & ({
|
|
369
|
+
readonly status: "verified"; /** Direct references to successful members of checks. */
|
|
370
|
+
readonly signatures: readonly Extract<InboxSignatureCheck, {
|
|
371
|
+
status: "verified";
|
|
372
|
+
}>[];
|
|
373
|
+
} | {
|
|
374
|
+
readonly status: "rejected";
|
|
375
|
+
readonly reason: InboxVerificationFailureReason;
|
|
376
|
+
} | {
|
|
377
|
+
readonly status: "error";
|
|
378
|
+
readonly error: unknown;
|
|
379
|
+
});
|
|
380
|
+
//#endregion
|
|
172
381
|
//#region src/sig/key.d.ts
|
|
173
382
|
/**
|
|
174
383
|
* Detailed fetch failure information from {@link fetchKeyDetailed}.
|
|
@@ -203,43 +412,111 @@ interface GetKeyOwnerOptions {
|
|
|
203
412
|
tracerProvider?: TracerProvider;
|
|
204
413
|
}
|
|
205
414
|
//#endregion
|
|
206
|
-
//#region src/
|
|
415
|
+
//#region src/federation/inbox-report.d.ts
|
|
207
416
|
/**
|
|
208
|
-
* A
|
|
209
|
-
* @since
|
|
417
|
+
* A JSON value retained from the incoming request.
|
|
418
|
+
* @since 2.4.0
|
|
210
419
|
*/
|
|
211
|
-
type
|
|
212
|
-
|
|
213
|
-
|
|
420
|
+
type InboxJsonValue = null | boolean | number | string | readonly InboxJsonValue[] | {
|
|
421
|
+
readonly [key: string]: InboxJsonValue;
|
|
422
|
+
};
|
|
214
423
|
/**
|
|
215
|
-
* The
|
|
216
|
-
* @since
|
|
424
|
+
* The final inbox authentication decision.
|
|
425
|
+
* @since 2.4.0
|
|
217
426
|
*/
|
|
218
|
-
type
|
|
427
|
+
type InboxAuthentication = {
|
|
428
|
+
readonly status: "verified"; /** Direct references to the attempts that authenticated this delivery. */
|
|
429
|
+
readonly attempts: readonly Extract<InboxVerificationAttempt, {
|
|
430
|
+
status: "verified";
|
|
431
|
+
}>[];
|
|
432
|
+
} | {
|
|
433
|
+
readonly status: "rejected";
|
|
434
|
+
readonly reason: {
|
|
435
|
+
readonly type: "verificationFailed";
|
|
436
|
+
} | {
|
|
437
|
+
readonly type: "actorKeyMismatch";
|
|
438
|
+
readonly key: InboxVerificationKey;
|
|
439
|
+
readonly actorIds: readonly URL[];
|
|
440
|
+
} | {
|
|
441
|
+
readonly type: "proofPolicy";
|
|
442
|
+
readonly policy: "portableActor" | "portableActivity" | "compound";
|
|
443
|
+
readonly detail?: InboxProofPolicyFailureReason;
|
|
444
|
+
} | {
|
|
445
|
+
readonly type: "invalidNonce";
|
|
446
|
+
};
|
|
447
|
+
} | {
|
|
448
|
+
readonly status: "skipped";
|
|
449
|
+
} | {
|
|
450
|
+
readonly status: "notDetermined";
|
|
451
|
+
};
|
|
219
452
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
453
|
+
* How an inbox request finished.
|
|
454
|
+
* @since 2.4.0
|
|
222
455
|
*/
|
|
223
|
-
|
|
456
|
+
type InboxRequestOutcome = {
|
|
457
|
+
readonly type: "response";
|
|
458
|
+
readonly status: number;
|
|
459
|
+
readonly disposition: "processed" | "enqueued" | "duplicate" | "unhandled" | "customResponse";
|
|
460
|
+
} | {
|
|
461
|
+
readonly type: "response";
|
|
462
|
+
readonly status: number;
|
|
463
|
+
readonly disposition: "rejected";
|
|
464
|
+
readonly reason: "recipientNotFound" | "invalidJson" | "invalidJsonLd" | "invalidActivity" | "bodyTooLarge" | "missingActor" | "authentication";
|
|
465
|
+
} | {
|
|
466
|
+
readonly type: "response";
|
|
467
|
+
readonly status: number;
|
|
468
|
+
readonly disposition: "failed";
|
|
469
|
+
readonly reason: "bodyUnavailable" | "enqueueError" | "listenerError";
|
|
470
|
+
readonly error?: unknown;
|
|
471
|
+
} | {
|
|
472
|
+
readonly type: "exception";
|
|
473
|
+
readonly stage: "prepare" | "parse" | "verify" | "policy" | "dispatch" | "respond"; /** The original thrown value, including null/undefined. */
|
|
474
|
+
readonly error: unknown;
|
|
475
|
+
};
|
|
224
476
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
477
|
+
* Observations of a single inbox delivery request.
|
|
478
|
+
* @since 2.4.0
|
|
227
479
|
*/
|
|
228
|
-
|
|
480
|
+
interface InboxRequestReport {
|
|
481
|
+
/** The delivery route, including a portable gateway route. */
|
|
482
|
+
readonly inbox: {
|
|
483
|
+
readonly kind: "personal" | "shared" | "portable";
|
|
484
|
+
readonly recipient: string | null;
|
|
485
|
+
};
|
|
486
|
+
/** Unavailable differs from a successfully parsed JSON null. */
|
|
487
|
+
readonly payload: {
|
|
488
|
+
readonly status: "unavailable";
|
|
489
|
+
} | {
|
|
490
|
+
readonly status: "parsed";
|
|
491
|
+
readonly value: InboxJsonValue;
|
|
492
|
+
};
|
|
493
|
+
/** The Activity obtained by the normal processing flow, if any. */
|
|
494
|
+
readonly activity: Activity | null;
|
|
495
|
+
/** Logical evaluations, including unsuccessful fallback paths. */
|
|
496
|
+
readonly attempts: readonly InboxVerificationAttempt[];
|
|
497
|
+
/** Cryptographic success alone does not imply this decision is verified. */
|
|
498
|
+
readonly authentication: InboxAuthentication;
|
|
499
|
+
/** Response status snapshot or the original exception. */
|
|
500
|
+
readonly outcome: InboxRequestOutcome;
|
|
501
|
+
}
|
|
229
502
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
503
|
+
* Observes an inbox delivery after processing, before fetch resolves/rethrows.
|
|
504
|
+
* The callback is awaited. Its errors are logged and swallowed, preserving
|
|
505
|
+
* the original response/exception. The last registered callback replaces the
|
|
506
|
+
* previous one. It excludes inbox collection GETs, unmatched routes,
|
|
507
|
+
* programmatic routing, and queue workers. This is not an atomic persistence
|
|
508
|
+
* guarantee. Keys/owner/controller declarations remain untrusted unless the
|
|
509
|
+
* final authentication decision verifies them.
|
|
510
|
+
* @since 2.4.0
|
|
232
511
|
*/
|
|
233
|
-
type
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
readonly type: "noSignature";
|
|
242
|
-
};
|
|
512
|
+
type InboxRequestFinishedHandler<TContextData> = (context: RequestContext<TContextData>, report: InboxRequestReport) => void | Promise<void>;
|
|
513
|
+
//#endregion
|
|
514
|
+
//#region src/compat/types.d.ts
|
|
515
|
+
/**
|
|
516
|
+
* A function that transforms an activity object.
|
|
517
|
+
* @since 1.4.0
|
|
518
|
+
*/
|
|
519
|
+
type ActivityTransformer<TContextData> = (activity: Activity, context: Context<TContextData>) => Activity;
|
|
243
520
|
//#endregion
|
|
244
521
|
//#region src/federation/collection.d.ts
|
|
245
522
|
/**
|
|
@@ -2755,6 +3032,16 @@ interface InboxListenerSetters<TContextData> {
|
|
|
2755
3032
|
* @returns The setters object so that settings can be chained.
|
|
2756
3033
|
*/
|
|
2757
3034
|
onError(handler: InboxErrorHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
3035
|
+
/**
|
|
3036
|
+
* Observes each configured inbox delivery once, before fetch finishes.
|
|
3037
|
+
* The callback is awaited and its errors do not alter delivery processing.
|
|
3038
|
+
* Calling this again replaces the previous callback. Built federations
|
|
3039
|
+
* retain the callback registered at build time.
|
|
3040
|
+
* @param handler The request completion observer.
|
|
3041
|
+
* @returns This setter for chaining.
|
|
3042
|
+
* @since 2.4.0
|
|
3043
|
+
*/
|
|
3044
|
+
onRequestFinished(handler: InboxRequestFinishedHandler<TContextData>): InboxListenerSetters<TContextData>;
|
|
2758
3045
|
/**
|
|
2759
3046
|
* Registers a callback for incoming activities whose HTTP signatures could
|
|
2760
3047
|
* not be verified.
|
|
@@ -5,9 +5,9 @@ import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
6
|
import "../std__assert-DvMDRPes.mjs";
|
|
7
7
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
8
|
-
import { f as verifyRequest } from "../http-
|
|
8
|
+
import { f as verifyRequest } from "../http-BqSbUEiQ.mjs";
|
|
9
9
|
import { i as rsaPrivateKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
10
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
10
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-CpdFLwAg.mjs";
|
|
11
11
|
import { configure, reset } from "@logtape/logtape";
|
|
12
12
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
13
13
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as kvCache, t as MockKvStore } from "../kv-cache-
|
|
4
|
+
import { n as kvCache, t as MockKvStore } from "../kv-cache-CWWlMv-s.mjs";
|
|
5
5
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
6
6
|
import { deepStrictEqual, throws } from "node:assert";
|
|
7
7
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
package/dist/utils/mod.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_kv_cache = require("../kv-cache-
|
|
3
|
+
const require_kv_cache = require("../kv-cache-D73yVcE5.cjs");
|
|
4
4
|
exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
|
|
5
5
|
exports.kvCache = require_kv_cache.kvCache;
|
package/dist/utils/mod.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-
|
|
2
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-Dw-nPxQh.cjs";
|
|
3
3
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/dist/utils/mod.d.ts
CHANGED
package/dist/utils/mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-
|
|
3
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-J0v3UbUn.js";
|
|
4
4
|
export { getAuthenticatedDocumentLoader, kvCache };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { CryptographicKey } from "@fedify/vocab";
|
|
5
|
+
//#region src/sig/verification.ts
|
|
6
|
+
const verificationObservation = Symbol("verificationObservation");
|
|
7
|
+
function snapshotKey(key) {
|
|
8
|
+
if (key.publicKey == null) throw new TypeError("Verification key has no public key.");
|
|
9
|
+
const id = key.id == null ? null : new URL(key.id.href);
|
|
10
|
+
return key instanceof CryptographicKey ? {
|
|
11
|
+
type: "cryptographicKey",
|
|
12
|
+
id,
|
|
13
|
+
ownerId: key.ownerId == null ? null : new URL(key.ownerId.href),
|
|
14
|
+
publicKey: key.publicKey
|
|
15
|
+
} : {
|
|
16
|
+
type: "multikey",
|
|
17
|
+
id,
|
|
18
|
+
controllerId: key.controllerId == null ? null : new URL(key.controllerId.href),
|
|
19
|
+
publicKey: key.publicKey
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function triedKey(options, key) {
|
|
23
|
+
const check = options[verificationObservation]?.check;
|
|
24
|
+
if (check != null) check.triedKeys.push(snapshotKey(key));
|
|
25
|
+
}
|
|
26
|
+
async function observeCheck(options, identity, declaredKeyId, operation) {
|
|
27
|
+
const observation = options[verificationObservation];
|
|
28
|
+
if (observation?.attempt == null) return await operation(observation ?? { attempts: [] });
|
|
29
|
+
const check = {
|
|
30
|
+
identity,
|
|
31
|
+
declaredKeyId,
|
|
32
|
+
triedKeys: []
|
|
33
|
+
};
|
|
34
|
+
const base = {
|
|
35
|
+
...identity,
|
|
36
|
+
declaredKeyId,
|
|
37
|
+
triedKeys: check.triedKeys
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
const key = await operation({
|
|
41
|
+
...observation,
|
|
42
|
+
check
|
|
43
|
+
});
|
|
44
|
+
observation.attempt.checks.push(key == null ? {
|
|
45
|
+
...base,
|
|
46
|
+
status: "rejected",
|
|
47
|
+
reason: check.reason ?? { type: "invalidSignature" }
|
|
48
|
+
} : {
|
|
49
|
+
...base,
|
|
50
|
+
status: "verified",
|
|
51
|
+
key: check.triedKeys.at(-1) ?? snapshotKey(key)
|
|
52
|
+
});
|
|
53
|
+
return key;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
observation.attempt.checks.push({
|
|
56
|
+
...base,
|
|
57
|
+
status: "error",
|
|
58
|
+
error
|
|
59
|
+
});
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function observeAttempt(options, mechanism, operation, isVerified) {
|
|
64
|
+
const observation = options[verificationObservation];
|
|
65
|
+
if (observation == null) return await operation({ attempts: [] });
|
|
66
|
+
const attempt = { checks: [] };
|
|
67
|
+
const base = {
|
|
68
|
+
mechanism,
|
|
69
|
+
subject: observation.subject == null ? {
|
|
70
|
+
id: null,
|
|
71
|
+
pointer: ""
|
|
72
|
+
} : {
|
|
73
|
+
id: observation.subject.id == null ? null : new URL(observation.subject.id.href),
|
|
74
|
+
pointer: observation.subject.pointer
|
|
75
|
+
},
|
|
76
|
+
checks: attempt.checks
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
const value = await operation({
|
|
80
|
+
...observation,
|
|
81
|
+
subject: base.subject,
|
|
82
|
+
attempt,
|
|
83
|
+
check: void 0
|
|
84
|
+
});
|
|
85
|
+
observation.attempts.push(isVerified(value) ? {
|
|
86
|
+
...base,
|
|
87
|
+
status: "verified",
|
|
88
|
+
signatures: attempt.checks.filter((check) => check.status === "verified")
|
|
89
|
+
} : {
|
|
90
|
+
...base,
|
|
91
|
+
status: "rejected",
|
|
92
|
+
reason: attempt.reason ?? { type: "signatureVerificationFailed" }
|
|
93
|
+
});
|
|
94
|
+
return value;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
observation.attempts.push({
|
|
97
|
+
...base,
|
|
98
|
+
status: "error",
|
|
99
|
+
error
|
|
100
|
+
});
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { verificationObservation as a, triedKey as i, observeCheck as n, snapshotKey as r, observeAttempt as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/fedify",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.2259+d9c843fc",
|
|
4
4
|
"description": "An ActivityPub server framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ActivityPub",
|
|
@@ -151,10 +151,10 @@
|
|
|
151
151
|
"structured-field-values": "^2.0.4",
|
|
152
152
|
"temporal-polyfill": "^1.0.1",
|
|
153
153
|
"urlpattern-polyfill": "^10.1.0",
|
|
154
|
-
"@fedify/
|
|
155
|
-
"@fedify/
|
|
156
|
-
"@fedify/
|
|
157
|
-
"@fedify/
|
|
154
|
+
"@fedify/uri-template": "2.4.0-dev.2259+d9c843fc",
|
|
155
|
+
"@fedify/vocab-runtime": "2.4.0-dev.2259+d9c843fc",
|
|
156
|
+
"@fedify/webfinger": "2.4.0-dev.2259+d9c843fc",
|
|
157
|
+
"@fedify/vocab": "2.4.0-dev.2259+d9c843fc"
|
|
158
158
|
},
|
|
159
159
|
"devDependencies": {
|
|
160
160
|
"@std/assert": "npm:@jsr/std__assert@^0.226.0",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"typescript": "^6.0.0",
|
|
168
168
|
"wrangler": "^4.17.0",
|
|
169
169
|
"@fedify/fixture": "2.0.0",
|
|
170
|
-
"@fedify/vocab-tools": "^2.4.0-dev.
|
|
170
|
+
"@fedify/vocab-tools": "^2.4.0-dev.2259+d9c843fc"
|
|
171
171
|
},
|
|
172
172
|
"scripts": {
|
|
173
173
|
"build:self": "tsdown",
|