@fedify/fedify 2.5.0-dev.2446 → 2.5.0-dev.2448
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/{activity-listener-C-BSbKI7.mjs → activity-listener-CELFAodc.mjs} +8 -4
- package/dist/{builder-Da8b9O6v.mjs → builder-DbC8bn6k.mjs} +5 -5
- 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-heeXW2_A.mjs} +2 -2
- package/dist/{context-CQ9H8vNW.d.cts → context-BazwQrHn.d.cts} +304 -4
- package/dist/{context-8Tu9kea0.mjs → context-COHd1Rsh.mjs} +2 -0
- package/dist/{context-BdB54V-A.d.ts → context-GlHrzdSP.d.ts} +304 -4
- package/dist/{deno-eNpfj4oR.mjs → deno-Dpewt_jJ.mjs} +1 -1
- package/dist/{docloader-B-0nqOB1.mjs → docloader-CpsDyqVN.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 +5 -5
- package/dist/federation/handler.test.mjs +21 -6
- 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 +9 -9
- package/dist/federation/mod.cjs +2 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +8 -2
- package/dist/federation/mq.test.mjs +36 -1
- package/dist/federation/outgoing-proof.test.mjs +4 -4
- 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 +6 -6
- 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 +3 -3
- 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-D8YexkP1.cjs} +1172 -131
- package/dist/{http-DlNxxW_5.js → http-BBr-VLil.js} +143 -16
- package/dist/{http-BUJ5SFh6.mjs → http-BZkyQIkb.mjs} +4 -4
- package/dist/{http-BfW3wyQ4.cjs → http-CAl7Num5.cjs} +161 -16
- package/dist/inbox-authentication-DflPYlLL.mjs +171 -0
- package/dist/inbox-view-NE9wXdLo.mjs +385 -0
- package/dist/inbox-wDuMKaFW.mjs +312 -0
- package/dist/{kv-cache-D_m3A241.mjs → kv-cache-CTQSfkmp.mjs} +1 -1
- package/dist/{kv-cache-kkBB0vbI.cjs → kv-cache-CjgCOz3i.cjs} +1 -1
- package/dist/{kv-cache-7015lbrx.js → kv-cache-U7uz5nx4.js} +1 -1
- package/dist/{ld-CD2CsPxq.mjs → ld-CQFuiah9.mjs} +52 -9
- package/dist/ld-CcuZ4NZa.d.ts +250 -0
- package/dist/ld-Cel1RRZ5.d.cts +250 -0
- package/dist/{metrics-D3LHk6Z4.mjs → metrics-B-QxRSSR.mjs} +1 -1
- package/dist/{middleware-CCDXvXOG.mjs → middleware-BdlNa8BD.mjs} +1 -1
- package/dist/{middleware-CzoWvrdZ.mjs → middleware-CuVa0UuS.mjs} +459 -98
- package/dist/{middleware-N9eM16XE.js → middleware-P-AZEQMn.js} +1169 -141
- package/dist/mod-B2783_3v.d.cts +154 -0
- package/dist/mod-BHQE-7WQ.d.ts +154 -0
- package/dist/{mod-D0bxB8jS.d.ts → mod-DjZfJj9R.d.ts} +1 -1
- package/dist/{mod-B0dGgQsg.d.cts → mod-DvWOv2Ho.d.cts} +1 -1
- package/dist/mod.cjs +5 -4
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/{mq-Dm1Wmtm-.mjs → mq-eF96QxUT.mjs} +6 -0
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-C5-FVX6t.mjs → owner-ccKkQOAf.mjs} +3 -3
- package/dist/{portable-collection-Cony3_LC.mjs → portable-collection-CndnpXfo.mjs} +2 -2
- package/dist/{portable-collection-CC2zNAje.js → portable-collection-DUlQ3s2M.js} +1 -1
- package/dist/{portable-collection-C-Z1SKDy.cjs → portable-collection-TC-kvkVI.cjs} +1 -1
- package/dist/{portable-key-DNc_flH-.mjs → portable-key-D5tFOoV-.mjs} +1 -1
- package/dist/{proof-Cryb1c70.mjs → proof-DPoF06nz.mjs} +98 -10
- package/dist/{send-AF25pWaJ.mjs → send-CF44O1Df.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 +2 -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 +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 +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.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +44 -4
- 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-XlZ4Wb94.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +262 -2
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-t8xDeJZ5.mjs → testing-Dbr21vZk.mjs} +1 -1
- 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.js +1 -1
- package/package.json +7 -7
- 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
package/dist/testing/mod.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import { Router } from "@fedify/uri-template";
|
|
|
5
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
|
-
import { AuthenticatedDocumentLoaderFactory, DocumentLoader, DocumentLoaderFactory, GetUserAgentOptions, PortableObjectVerifier } from "@fedify/vocab-runtime";
|
|
8
|
+
import { AuthenticatedDocumentLoaderFactory, DocumentLoader, DocumentLoaderFactory, GetUserAgentOptions, PortableObjectVerifier, RemoteDocument } from "@fedify/vocab-runtime";
|
|
9
9
|
import { MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
10
10
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
11
11
|
|
|
@@ -416,6 +416,35 @@ interface GetKeyOwnerOptions {
|
|
|
416
416
|
tracerProvider?: TracerProvider;
|
|
417
417
|
}
|
|
418
418
|
//#endregion
|
|
419
|
+
//#region src/federation/activity-listener.d.ts
|
|
420
|
+
/**
|
|
421
|
+
* Options for an inbox listener.
|
|
422
|
+
* @since 2.5.0
|
|
423
|
+
*/
|
|
424
|
+
interface InboxListenerOptions {
|
|
425
|
+
/**
|
|
426
|
+
* Whether activities that declare no `actor` at all may reach the listener.
|
|
427
|
+
*
|
|
428
|
+
* Some protocols identify the requester without an `actor`; for example,
|
|
429
|
+
* Mastodon sends [FEP-7aa9] `FeatureRequest` activities without one. When
|
|
430
|
+
* this is `true`, such an activity is dispatched to the listener if Fedify
|
|
431
|
+
* authenticated at least one principal for it, i.e., the signer of its
|
|
432
|
+
* Object Integrity Proofs or Linked Data Signature, or otherwise the signer
|
|
433
|
+
* of the HTTP request, and its `id`, if any, has the same origin as that
|
|
434
|
+
* principal. The listener is then responsible for authorizing the request
|
|
435
|
+
* with {@link InboxContext.authentication}, e.g., with
|
|
436
|
+
* `featureInteraction.verifyRequest()` from
|
|
437
|
+
* `@fedify/interaction-controls`.
|
|
438
|
+
*
|
|
439
|
+
* An activity that declares an `actor` is never affected by this option;
|
|
440
|
+
* its actor must be authenticated as usual.
|
|
441
|
+
*
|
|
442
|
+
* [FEP-7aa9]: https://w3id.org/fep/7aa9
|
|
443
|
+
* @default `false`
|
|
444
|
+
*/
|
|
445
|
+
readonly allowActorless?: boolean;
|
|
446
|
+
}
|
|
447
|
+
//#endregion
|
|
419
448
|
//#region src/federation/inbox-report.d.ts
|
|
420
449
|
/**
|
|
421
450
|
* A JSON value retained from the incoming request.
|
|
@@ -458,6 +487,13 @@ type InboxAuthentication = {
|
|
|
458
487
|
readonly type: "activityOriginMismatch";
|
|
459
488
|
readonly activityId: URL;
|
|
460
489
|
readonly actorIds: readonly URL[];
|
|
490
|
+
} | {
|
|
491
|
+
/**
|
|
492
|
+
* The activity declares no actor, and no principal was authenticated
|
|
493
|
+
* for it, although its listener allows activities without an actor.
|
|
494
|
+
* @since 2.5.0
|
|
495
|
+
*/
|
|
496
|
+
readonly type: "noAuthenticatedPrincipal";
|
|
461
497
|
};
|
|
462
498
|
} | {
|
|
463
499
|
readonly status: "skipped";
|
|
@@ -1311,6 +1347,18 @@ interface MessageQueue {
|
|
|
1311
1347
|
* @since 2.4.0
|
|
1312
1348
|
*/
|
|
1313
1349
|
readonly atomicEnqueueMany?: boolean;
|
|
1350
|
+
/**
|
|
1351
|
+
* The largest message, in bytes, that the backend can hold. Fedify compares
|
|
1352
|
+
* it with a conservative estimate of a message's serialized size: the
|
|
1353
|
+
* length of its JSON serialization encoded in UTF-8, plus 8 bytes for each
|
|
1354
|
+
* JSON value in it, plus what encoding strings beyond Latin-1 as UTF-16
|
|
1355
|
+
* adds, which also bounds binary serialization formats. When an
|
|
1356
|
+
* incoming activity would make a larger inbox message, Fedify processes the
|
|
1357
|
+
* activity right away instead of enqueueing it, as if there were no inbox
|
|
1358
|
+
* queue. When omitted, messages are enqueued regardless of their size.
|
|
1359
|
+
* @since 2.5.0
|
|
1360
|
+
*/
|
|
1361
|
+
readonly maxMessageSize?: number;
|
|
1314
1362
|
/**
|
|
1315
1363
|
* Enqueues a message in the queue.
|
|
1316
1364
|
* @param message The message to enqueue.
|
|
@@ -1349,6 +1397,176 @@ interface MessageQueue {
|
|
|
1349
1397
|
getDepth?(): Promise<MessageQueueDepth>;
|
|
1350
1398
|
}
|
|
1351
1399
|
//#endregion
|
|
1400
|
+
//#region src/federation/inbox-authentication.d.ts
|
|
1401
|
+
/**
|
|
1402
|
+
* A party that Fedify authenticated for an incoming activity, independently
|
|
1403
|
+
* of the activity's declared `actor`.
|
|
1404
|
+
* @since 2.5.0
|
|
1405
|
+
*/
|
|
1406
|
+
interface InboxPrincipal {
|
|
1407
|
+
/**
|
|
1408
|
+
* What {@link InboxPrincipal.id} is:
|
|
1409
|
+
*
|
|
1410
|
+
* - `"actor"`: an actor whose ownership of the verifying key Fedify
|
|
1411
|
+
* confirmed with the actor's own document.
|
|
1412
|
+
* - `"portableAuthority"`: a DID whose verification method made a valid
|
|
1413
|
+
* Object Integrity Proof. It authenticates the [FEP-ef61] portable
|
|
1414
|
+
* actors of that DID, and nothing else.
|
|
1415
|
+
*
|
|
1416
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1417
|
+
*/
|
|
1418
|
+
readonly kind: "actor" | "portableAuthority";
|
|
1419
|
+
/** The actor ID, or the DID if {@link kind} is `"portableAuthority"`. */
|
|
1420
|
+
readonly id: URL;
|
|
1421
|
+
/**
|
|
1422
|
+
* How the principal was authenticated:
|
|
1423
|
+
*
|
|
1424
|
+
* - `"httpSignatures"`: it signed the HTTP request.
|
|
1425
|
+
* - `"linkedDataSignatures"`: it made the Linked Data Signature of the
|
|
1426
|
+
* activity.
|
|
1427
|
+
* - `"objectIntegrityProofs"`: it made an Object Integrity Proof of the
|
|
1428
|
+
* activity.
|
|
1429
|
+
* - `"origin"`: the activity was fetched again from the actor's own web
|
|
1430
|
+
* origin, which happens only for `Context.routeActivity()`.
|
|
1431
|
+
*/
|
|
1432
|
+
readonly mechanism: "httpSignatures" | "linkedDataSignatures" | "objectIntegrityProofs" | "origin";
|
|
1433
|
+
/** The key or verification method that authenticated the principal. */
|
|
1434
|
+
readonly keyId: URL | null;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Who Fedify authenticated for an incoming activity, and the interpretation of
|
|
1438
|
+
* the activity it authenticated. Inbox listeners receive it as
|
|
1439
|
+
* {@link InboxContext.authentication}.
|
|
1440
|
+
*
|
|
1441
|
+
* Authenticated principals are independent of the activity's declared
|
|
1442
|
+
* `actor`: an activity forwarded by a relay is signed by the relay in its
|
|
1443
|
+
* HTTP request, but its document may be signed by its author, and some
|
|
1444
|
+
* activities, such as [FEP-7aa9] `FeatureRequest`s sent by Mastodon, declare
|
|
1445
|
+
* no `actor` at all. A valid signature alone does not authorize anything;
|
|
1446
|
+
* use {@link isActorAuthenticated} to check whether a specific actor was
|
|
1447
|
+
* authenticated.
|
|
1448
|
+
*
|
|
1449
|
+
* [FEP-7aa9]: https://w3id.org/fep/7aa9
|
|
1450
|
+
* @since 2.5.0
|
|
1451
|
+
*/
|
|
1452
|
+
interface InboxAuthenticationEvidence {
|
|
1453
|
+
/**
|
|
1454
|
+
* Whether signatures were verified at all. It is `false` only when
|
|
1455
|
+
* signature verification is skipped, or when there is no evidence, e.g.,
|
|
1456
|
+
* for an activity queued by an older version of Fedify. In that case all
|
|
1457
|
+
* the principal lists are empty.
|
|
1458
|
+
*/
|
|
1459
|
+
readonly verified: boolean;
|
|
1460
|
+
/**
|
|
1461
|
+
* The principals that signed the HTTP request, i.e., the server that
|
|
1462
|
+
* delivered the activity, which may have forwarded it. It is empty when
|
|
1463
|
+
* the activity document was authenticated by its own signatures, so that
|
|
1464
|
+
* HTTP Signatures did not need to be verified.
|
|
1465
|
+
*/
|
|
1466
|
+
readonly requestSigners: readonly InboxPrincipal[];
|
|
1467
|
+
/**
|
|
1468
|
+
* The principals that made the verified Linked Data Signature or Object
|
|
1469
|
+
* Integrity Proofs of the activity document. All of them are listed when
|
|
1470
|
+
* there are several proofs.
|
|
1471
|
+
*/
|
|
1472
|
+
readonly documentSigners: readonly InboxPrincipal[];
|
|
1473
|
+
/**
|
|
1474
|
+
* The actors authenticated because `Context.routeActivity()` fetched the
|
|
1475
|
+
* activity again from their own web origin. It is always empty for
|
|
1476
|
+
* activities delivered to an inbox.
|
|
1477
|
+
*/
|
|
1478
|
+
readonly originAuthenticated: readonly InboxPrincipal[];
|
|
1479
|
+
/**
|
|
1480
|
+
* The activity as Fedify interpreted it while authenticating it, in
|
|
1481
|
+
* expanded JSON-LD. Typed vocabulary objects omit some values, such as
|
|
1482
|
+
* blank node references and values they cannot decode, so authorization
|
|
1483
|
+
* that has to count every value of a property, e.g., that a property has
|
|
1484
|
+
* exactly one value, should inspect this instead. It is deeply frozen.
|
|
1485
|
+
* It is empty when there is no evidence.
|
|
1486
|
+
*/
|
|
1487
|
+
readonly expandedActivity: readonly Record<string, unknown>[];
|
|
1488
|
+
/**
|
|
1489
|
+
* The base URL the activity was decoded with, e.g., the URL an activity
|
|
1490
|
+
* fetched by `Context.routeActivity()` was finally retrieved from, or
|
|
1491
|
+
* `null` if none was given. Relative IRIs left in
|
|
1492
|
+
* {@link expandedActivity} resolve against it, or against the activity's
|
|
1493
|
+
* own `@id` if it is `null`, as the decoder resolves them.
|
|
1494
|
+
*/
|
|
1495
|
+
readonly baseUrl: URL | null;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* A {@link InboxPrincipal} as stored in a queued message.
|
|
1499
|
+
* @internal
|
|
1500
|
+
*/
|
|
1501
|
+
interface SerializedInboxPrincipal {
|
|
1502
|
+
readonly kind: InboxPrincipal["kind"];
|
|
1503
|
+
readonly id: string;
|
|
1504
|
+
readonly mechanism: InboxPrincipal["mechanism"];
|
|
1505
|
+
readonly keyId: string | null;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* The principals of an {@link InboxAuthenticationEvidence} as stored in
|
|
1509
|
+
* a queued message. The expanded activity and the base URL are restored
|
|
1510
|
+
* from the queued verified view instead.
|
|
1511
|
+
* @internal
|
|
1512
|
+
*/
|
|
1513
|
+
interface SerializedInboxAuthentication {
|
|
1514
|
+
readonly verified: boolean;
|
|
1515
|
+
readonly requestSigners: readonly SerializedInboxPrincipal[];
|
|
1516
|
+
readonly documentSigners: readonly SerializedInboxPrincipal[];
|
|
1517
|
+
readonly originAuthenticated: readonly SerializedInboxPrincipal[];
|
|
1518
|
+
}
|
|
1519
|
+
//#endregion
|
|
1520
|
+
//#region src/sig/ld.d.ts
|
|
1521
|
+
/**
|
|
1522
|
+
* How a context loader marked by {@link markEffectiveContextLoader} already
|
|
1523
|
+
* resolves contexts.
|
|
1524
|
+
* @internal
|
|
1525
|
+
*/
|
|
1526
|
+
type EffectiveContextLoaderPolicy = "standard" | "normalization";
|
|
1527
|
+
//#endregion
|
|
1528
|
+
//#region src/federation/inbox-view.d.ts
|
|
1529
|
+
/**
|
|
1530
|
+
* A {@link ContextSnapshotEntry} as stored in a queued message. A response
|
|
1531
|
+
* that equals the one Fedify bundles for the URL is stored as a reference to
|
|
1532
|
+
* the bundled context, along with its hash for diagnostics.
|
|
1533
|
+
* @internal
|
|
1534
|
+
*/
|
|
1535
|
+
type SerializedContextSnapshotEntry = {
|
|
1536
|
+
readonly policy: EffectiveContextLoaderPolicy;
|
|
1537
|
+
readonly url: string; /** The SHA-256 of the canonical JSON of the whole response. */
|
|
1538
|
+
readonly sha256: string;
|
|
1539
|
+
} & ({
|
|
1540
|
+
readonly bundled: true;
|
|
1541
|
+
} | {
|
|
1542
|
+
readonly response: RemoteDocument;
|
|
1543
|
+
});
|
|
1544
|
+
/**
|
|
1545
|
+
* The interpretation of an incoming activity that Fedify authenticated, as
|
|
1546
|
+
* stored in a queued message. The document itself is not copied; it is
|
|
1547
|
+
* a field of the message.
|
|
1548
|
+
* @internal
|
|
1549
|
+
*/
|
|
1550
|
+
interface SerializedVerifiedActivityView {
|
|
1551
|
+
/** The format version. */
|
|
1552
|
+
readonly version: 1;
|
|
1553
|
+
/** The context loader policy the activity was parsed with. */
|
|
1554
|
+
readonly policy: EffectiveContextLoaderPolicy;
|
|
1555
|
+
/**
|
|
1556
|
+
* Which document the activity was parsed from, with its Linked Data
|
|
1557
|
+
* Signature detached: the `activity` of the message, i.e., the payload as
|
|
1558
|
+
* received, or its `normalizedActivity`, i.e., the payload compacted for
|
|
1559
|
+
* Linked Data Signatures.
|
|
1560
|
+
*/
|
|
1561
|
+
readonly source: "activity" | "normalizedActivity";
|
|
1562
|
+
/** The `baseUrl` option the activity was parsed with. */
|
|
1563
|
+
readonly baseUrl: string | null;
|
|
1564
|
+
/** The context responses the interpretation used. */
|
|
1565
|
+
readonly contexts: readonly SerializedContextSnapshotEntry[];
|
|
1566
|
+
/** The digest of the expanded activity. */
|
|
1567
|
+
readonly expandedDigest: string;
|
|
1568
|
+
}
|
|
1569
|
+
//#endregion
|
|
1352
1570
|
//#region src/federation/retry.d.ts
|
|
1353
1571
|
/**
|
|
1354
1572
|
* The context passed to a {@link RetryPolicy} callback.
|
|
@@ -1683,6 +1901,21 @@ interface InboxMessage {
|
|
|
1683
1901
|
* @internal
|
|
1684
1902
|
*/
|
|
1685
1903
|
readonly ldSignatureVerified?: boolean;
|
|
1904
|
+
/**
|
|
1905
|
+
* The interpretation of the activity that Fedify authenticated, i.e., which
|
|
1906
|
+
* document it was parsed from and every JSON-LD context response the parse
|
|
1907
|
+
* used, so that the worker and its retries dispatch the very same
|
|
1908
|
+
* interpretation without loading contexts again. Absent in messages queued
|
|
1909
|
+
* by older Fedify versions.
|
|
1910
|
+
* @internal
|
|
1911
|
+
*/
|
|
1912
|
+
readonly verifiedView?: SerializedVerifiedActivityView;
|
|
1913
|
+
/**
|
|
1914
|
+
* Who Fedify authenticated for the activity. Absent in messages queued by
|
|
1915
|
+
* older Fedify versions.
|
|
1916
|
+
* @internal
|
|
1917
|
+
*/
|
|
1918
|
+
readonly authentication?: SerializedInboxAuthentication;
|
|
1686
1919
|
readonly started: string;
|
|
1687
1920
|
readonly attempt: number;
|
|
1688
1921
|
readonly identifier: string | null;
|
|
@@ -3034,11 +3267,27 @@ interface InboxListenerSetters<TContextData> {
|
|
|
3034
3267
|
/**
|
|
3035
3268
|
* Registers a listener for a specific incoming activity type.
|
|
3036
3269
|
*
|
|
3270
|
+
* @example
|
|
3271
|
+
* ``` typescript
|
|
3272
|
+
* federation
|
|
3273
|
+
* .setInboxListeners("/users/{identifier}/inbox", "/inbox")
|
|
3274
|
+
* // Mastodon sends FeatureRequest activities without an actor:
|
|
3275
|
+
* .on(FeatureRequest, async (ctx, request) => {
|
|
3276
|
+
* const verification = await featureInteraction.verifyRequest(ctx, {
|
|
3277
|
+
* request,
|
|
3278
|
+
* authentication: ctx.authentication,
|
|
3279
|
+
* });
|
|
3280
|
+
* // ...
|
|
3281
|
+
* }, { allowActorless: true });
|
|
3282
|
+
* ```
|
|
3283
|
+
*
|
|
3037
3284
|
* @param type A subclass of {@link Activity} to listen to.
|
|
3038
3285
|
* @param listener A callback to handle an incoming activity.
|
|
3286
|
+
* @param options Options for the listener. See
|
|
3287
|
+
* {@link InboxListenerOptions}. (Since 2.5.0.)
|
|
3039
3288
|
* @returns The setters object so that settings can be chained.
|
|
3040
3289
|
*/
|
|
3041
|
-
on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity
|
|
3290
|
+
on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity>, options?: InboxListenerOptions): InboxListenerSetters<TContextData>;
|
|
3042
3291
|
/**
|
|
3043
3292
|
* Registers an error handler for inbox listeners. Any exceptions thrown
|
|
3044
3293
|
* from the listeners are caught and passed to this handler.
|
|
@@ -4566,6 +4815,17 @@ interface InboxContext<TContextData> extends Context<TContextData> {
|
|
|
4566
4815
|
* @since 1.2.0
|
|
4567
4816
|
*/
|
|
4568
4817
|
readonly recipient: string | null;
|
|
4818
|
+
/**
|
|
4819
|
+
* Who Fedify authenticated for the incoming activity, independently of its
|
|
4820
|
+
* declared `actor`, and the interpretation of the activity that was
|
|
4821
|
+
* authenticated. It is preserved through the inbox queue and its retries.
|
|
4822
|
+
*
|
|
4823
|
+
* Use it with {@link isActorAuthenticated} to authorize requests that do
|
|
4824
|
+
* not declare an `actor`, which reach a listener only if it is registered
|
|
4825
|
+
* with the `allowActorless` option.
|
|
4826
|
+
* @since 2.5.0
|
|
4827
|
+
*/
|
|
4828
|
+
readonly authentication: InboxAuthenticationEvidence;
|
|
4569
4829
|
/**
|
|
4570
4830
|
* Creates a new context with the same properties as this one,
|
|
4571
4831
|
* but with the given data.
|
package/dist/testing/mod.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { r as testDefinitions } from "../dist-DJQM4u7i.mjs";
|
|
5
|
-
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-
|
|
5
|
+
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-COHd1Rsh.mjs";
|
|
6
6
|
import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
|
|
7
7
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, r as envelopeSchema, t as MockQueue } from "../tasks-IG3pHfvZ.mjs";
|
|
8
|
-
import "../testing-
|
|
8
|
+
import "../testing-Dbr21vZk.mjs";
|
|
9
9
|
export { ManualClockKvStore, MockQueue, baseOptions, createInboxContext, createOutboxContext, createRequestContext, envelopeSchema, makeSchema, numberSchema, stringSchema, testDefinitions };
|
|
@@ -2,7 +2,7 @@ import "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import "./dist-DJQM4u7i.mjs";
|
|
5
|
-
import "./context-
|
|
5
|
+
import "./context-COHd1Rsh.mjs";
|
|
6
6
|
import "./kv-DBRWVx3Q.mjs";
|
|
7
7
|
import "./tasks-IG3pHfvZ.mjs";
|
|
8
8
|
export {};
|
|
@@ -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-BZkyQIkb.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-CpsDyqVN.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-CTQSfkmp.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-CjgCOz3i.cjs");
|
|
4
4
|
exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
|
|
5
5
|
exports.kvCache = require_kv_cache.kvCache;
|
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-U7uz5nx4.js";
|
|
4
4
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/fedify",
|
|
3
|
-
"version": "2.5.0-dev.
|
|
3
|
+
"version": "2.5.0-dev.2448+756933d0",
|
|
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/vocab": "2.5.0-dev.2448+756933d0",
|
|
155
|
+
"@fedify/webfinger": "2.5.0-dev.2448+756933d0",
|
|
156
|
+
"@fedify/uri-template": "2.5.0-dev.2448+756933d0",
|
|
157
|
+
"@fedify/vocab-runtime": "2.5.0-dev.2448+756933d0"
|
|
158
158
|
},
|
|
159
159
|
"devDependencies": {
|
|
160
160
|
"@std/assert": "npm:@jsr/std__assert@^0.226.0",
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
"tsx": "^4.21.0",
|
|
167
167
|
"typescript": "^6.0.0",
|
|
168
168
|
"wrangler": "^4.17.0",
|
|
169
|
-
"@fedify/
|
|
170
|
-
"@fedify/
|
|
169
|
+
"@fedify/vocab-tools": "^2.5.0-dev.2448+756933d0",
|
|
170
|
+
"@fedify/fixture": "2.0.0"
|
|
171
171
|
},
|
|
172
172
|
"scripts": {
|
|
173
173
|
"build:self": "tsdown",
|
package/dist/inbox-CHvMaIFX.mjs
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { Temporal } from "temporal-polyfill";
|
|
2
|
-
import "urlpattern-polyfill";
|
|
3
|
-
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as getAuthenticationOrigin } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
|
-
import { n as version, t as name } from "./deno-eNpfj4oR.mjs";
|
|
6
|
-
import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-D3LHk6Z4.mjs";
|
|
7
|
-
import { getLogger } from "@logtape/logtape";
|
|
8
|
-
import { getTypeId } from "@fedify/vocab";
|
|
9
|
-
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
10
|
-
//#region src/federation/inbox.ts
|
|
11
|
-
/**
|
|
12
|
-
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
13
|
-
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
14
|
-
*
|
|
15
|
-
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
16
|
-
* and its portable actors, including compatible identifiers on any gateway,
|
|
17
|
-
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
18
|
-
* opaque URN, never matches.
|
|
19
|
-
*
|
|
20
|
-
* The caller is responsible for rejecting a missing primary actor ID before
|
|
21
|
-
* relying on this check.
|
|
22
|
-
*
|
|
23
|
-
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
function actorsHaveSameOrigin(activity) {
|
|
27
|
-
if (activity.id == null) return true;
|
|
28
|
-
const origin = getAuthenticationOrigin(activity.id);
|
|
29
|
-
return origin != null && activity.actorIds.every((actor) => getAuthenticationOrigin(actor) === origin);
|
|
30
|
-
}
|
|
31
|
-
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
32
|
-
const logger = getLogger([
|
|
33
|
-
"fedify",
|
|
34
|
-
"federation",
|
|
35
|
-
"inbox"
|
|
36
|
-
]);
|
|
37
|
-
if (activity.actorId == null) {
|
|
38
|
-
logger.error("Missing actor.", { activity: json });
|
|
39
|
-
span.setStatus({
|
|
40
|
-
code: SpanStatusCode.ERROR,
|
|
41
|
-
message: "Missing actor."
|
|
42
|
-
});
|
|
43
|
-
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
44
|
-
return "missingActor";
|
|
45
|
-
}
|
|
46
|
-
let cacheKey = null;
|
|
47
|
-
if (activity.id != null) {
|
|
48
|
-
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
49
|
-
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
50
|
-
let keyString;
|
|
51
|
-
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
52
|
-
else switch (strategy) {
|
|
53
|
-
case "global":
|
|
54
|
-
keyString = activity.id.href;
|
|
55
|
-
break;
|
|
56
|
-
case "per-origin":
|
|
57
|
-
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
58
|
-
break;
|
|
59
|
-
case "per-inbox":
|
|
60
|
-
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
61
|
-
break;
|
|
62
|
-
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
63
|
-
}
|
|
64
|
-
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
65
|
-
}
|
|
66
|
-
if (cacheKey != null) {
|
|
67
|
-
if (await kv.get(cacheKey) === true) {
|
|
68
|
-
logger.debug("Activity {activityId} has already been processed.", {
|
|
69
|
-
activityId: activity.id?.href,
|
|
70
|
-
activity: json,
|
|
71
|
-
recipient
|
|
72
|
-
});
|
|
73
|
-
span.setStatus({
|
|
74
|
-
code: SpanStatusCode.UNSET,
|
|
75
|
-
message: `Activity ${activity.id?.href} has already been processed.`
|
|
76
|
-
});
|
|
77
|
-
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
78
|
-
return "alreadyProcessed";
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
82
|
-
if (queue != null) {
|
|
83
|
-
const carrier = {};
|
|
84
|
-
propagation.inject(context.active(), carrier);
|
|
85
|
-
try {
|
|
86
|
-
await queue.enqueue({
|
|
87
|
-
type: "inbox",
|
|
88
|
-
id: crypto.randomUUID(),
|
|
89
|
-
baseUrl: ctx.origin,
|
|
90
|
-
activity: originalJson ?? json,
|
|
91
|
-
...normalizedActivity == null ? {} : { normalizedActivity },
|
|
92
|
-
...ldSignatureVerified == null ? {} : { ldSignatureVerified },
|
|
93
|
-
identifier: recipient,
|
|
94
|
-
attempt: 0,
|
|
95
|
-
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
-
traceContext: carrier
|
|
97
|
-
});
|
|
98
|
-
} catch (error) {
|
|
99
|
-
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
100
|
-
error,
|
|
101
|
-
activityId: activity.id?.href,
|
|
102
|
-
activity: json,
|
|
103
|
-
recipient
|
|
104
|
-
});
|
|
105
|
-
span.setStatus({
|
|
106
|
-
code: SpanStatusCode.ERROR,
|
|
107
|
-
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
108
|
-
});
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
|
|
112
|
-
role: "inbox",
|
|
113
|
-
queue,
|
|
114
|
-
activityType: getTypeId(activity).href
|
|
115
|
-
}, 0);
|
|
116
|
-
recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
|
|
117
|
-
logger.info("Activity {activityId} is enqueued.", {
|
|
118
|
-
activityId: activity.id?.href,
|
|
119
|
-
activity: json,
|
|
120
|
-
recipient
|
|
121
|
-
});
|
|
122
|
-
return "enqueued";
|
|
123
|
-
}
|
|
124
|
-
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
125
|
-
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
126
|
-
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
127
|
-
if (dispatched == null) {
|
|
128
|
-
logger.error("Unsupported activity type:\n{activity}", {
|
|
129
|
-
activity: json,
|
|
130
|
-
recipient
|
|
131
|
-
});
|
|
132
|
-
span.setStatus({
|
|
133
|
-
code: SpanStatusCode.UNSET,
|
|
134
|
-
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
135
|
-
});
|
|
136
|
-
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
137
|
-
span.end();
|
|
138
|
-
return "unsupportedActivity";
|
|
139
|
-
}
|
|
140
|
-
const { class: cls, listener } = dispatched;
|
|
141
|
-
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
142
|
-
try {
|
|
143
|
-
const activityType = getTypeId(activity).href;
|
|
144
|
-
const started = performance.now();
|
|
145
|
-
try {
|
|
146
|
-
const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
|
|
147
|
-
await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
|
|
148
|
-
} finally {
|
|
149
|
-
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
150
|
-
}
|
|
151
|
-
} catch (error) {
|
|
152
|
-
try {
|
|
153
|
-
await inboxErrorHandler?.(ctx, error);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
156
|
-
error,
|
|
157
|
-
activityId: activity.id?.href,
|
|
158
|
-
activity: json,
|
|
159
|
-
recipient
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
163
|
-
error,
|
|
164
|
-
activityId: activity.id?.href,
|
|
165
|
-
activity: json,
|
|
166
|
-
recipient
|
|
167
|
-
});
|
|
168
|
-
span.setStatus({
|
|
169
|
-
code: SpanStatusCode.ERROR,
|
|
170
|
-
message: String(error)
|
|
171
|
-
});
|
|
172
|
-
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
173
|
-
span.end();
|
|
174
|
-
return "error";
|
|
175
|
-
}
|
|
176
|
-
recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
|
|
177
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
178
|
-
logger.info("Activity {activityId} has been processed.", {
|
|
179
|
-
activityId: activity.id?.href,
|
|
180
|
-
activity: json,
|
|
181
|
-
recipient
|
|
182
|
-
});
|
|
183
|
-
span.end();
|
|
184
|
-
return "success";
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
//#endregion
|
|
188
|
-
export { routeActivity as n, actorsHaveSameOrigin as t };
|