@fedify/fedify 2.3.0-dev.1158 → 2.3.0-dev.1172
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-B66L9i5E.mjs → builder-JoFBmqfM.mjs} +2 -2
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{deno-O_rwum1q.mjs → deno-Cb_y5qEi.mjs} +1 -1
- package/dist/{docloader-Ct8PhKFS.mjs → docloader-Bv4TW6eo.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +305 -3
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +80 -1
- package/dist/federation/middleware.test.mjs +20 -6
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-BoRhhcgB.mjs → http--aE0vk2u.mjs} +3 -3
- package/dist/{http-CFP8WMMv.js → http-C0XZv7iH.js} +92 -2
- package/dist/{http-DlPd_LYM.cjs → http-D_HNhC57.cjs} +115 -1
- package/dist/{key-DyATZSWG.mjs → key-Cl_bixZo.mjs} +2 -2
- package/dist/{kv-cache-BJo6COYN.cjs → kv-cache-CdOuPFgC.cjs} +1 -1
- package/dist/{kv-cache-DeJE8EeD.mjs → kv-cache-DQUblF4f.mjs} +1 -1
- package/dist/{kv-cache-dH0biV98.js → kv-cache-DsbVBK7Y.js} +1 -1
- package/dist/{ld-B8wjsKDJ.mjs → ld-xVq6y31b.mjs} +3 -3
- package/dist/{metrics-oMUWaw6W.mjs → metrics-CKticT28.mjs} +92 -2
- package/dist/{middleware-DwZ1ofL9.js → middleware-BSuEI4Qf.js} +318 -107
- package/dist/{middleware-BzOa0ncb.mjs → middleware-BmPIKmb4.mjs} +1 -1
- package/dist/{middleware-xtTRaiJL.mjs → middleware-DHM2Pjqf.mjs} +327 -116
- package/dist/{middleware-CcJyVEpv.cjs → middleware-hxnyAewn.cjs} +318 -107
- package/dist/mod.cjs +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-BxjgK8PG.mjs → owner-DmU2qEh_.mjs} +2 -2
- package/dist/{proof-42Q9NiqN.mjs → proof-1XBgQ0Z0.mjs} +3 -3
- package/dist/{proof-B_6gAVQ2.js → proof-CmS6yxgt.js} +1 -1
- package/dist/{proof-CfttNzWW.cjs → proof-wm6UxUoM.cjs} +1 -1
- package/dist/{send-R1_K46CH.mjs → send-CmtB8w5D.mjs} +3 -3
- 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/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/{temporal-8kDX3E4q.mjs → temporal-DE9_a2nI.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 +6 -6
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { a as instrumentDocumentLoader, d as
|
|
4
|
+
import { n as version, t as name } from "./deno-Cb_y5qEi.mjs";
|
|
5
|
+
import { _ as recordOutboxActivity, a as instrumentDocumentLoader, c as recordCollectionDispatchDuration, d as recordCollectionTotalItems, h as recordInboxActivity, i as getRemoteHost, l as recordCollectionPageItems, m as recordFanoutRecipients, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionRequest, v as recordOutboxEnqueue, y as recordWebFingerHandle } from "./metrics-CKticT28.mjs";
|
|
6
6
|
import { t as formatAcceptSignature } from "./accept-CceiKpCy.mjs";
|
|
7
|
-
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-
|
|
8
|
-
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http
|
|
9
|
-
import { t as getAuthenticatedDocumentLoader } from "./docloader-
|
|
10
|
-
import { n as kvCache } from "./kv-cache-
|
|
11
|
-
import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-
|
|
12
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
7
|
+
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-Cl_bixZo.mjs";
|
|
8
|
+
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http--aE0vk2u.mjs";
|
|
9
|
+
import { t as getAuthenticatedDocumentLoader } from "./docloader-Bv4TW6eo.mjs";
|
|
10
|
+
import { n as kvCache } from "./kv-cache-DQUblF4f.mjs";
|
|
11
|
+
import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-xVq6y31b.mjs";
|
|
12
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DmU2qEh_.mjs";
|
|
13
13
|
import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-BgFLCJQ_.mjs";
|
|
14
|
-
import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-
|
|
14
|
+
import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-1XBgQ0Z0.mjs";
|
|
15
15
|
import { t as getNodeInfo } from "./client-B_A6mfn3.mjs";
|
|
16
16
|
import { t as nodeInfoToJson } from "./types-BFowWFTT.mjs";
|
|
17
|
-
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-
|
|
17
|
+
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-JoFBmqfM.mjs";
|
|
18
18
|
import { t as buildCollectionSynchronizationHeader } from "./collection-CA3V5zyK.mjs";
|
|
19
19
|
import { t as KvKeyCache } from "./keycache-BYMd8q7F.mjs";
|
|
20
20
|
import { t as acceptsJsonLd } from "./negotiation-CDW-_gUU.mjs";
|
|
21
|
-
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-
|
|
21
|
+
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-DE9_a2nI.mjs";
|
|
22
22
|
import { t as createExponentialBackoffPolicy } from "./retry-_VvV0h9f.mjs";
|
|
23
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-
|
|
23
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CmtB8w5D.mjs";
|
|
24
24
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
25
25
|
import { RouterError } from "@fedify/uri-template";
|
|
26
26
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -402,6 +402,33 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
402
402
|
Vary: "Accept"
|
|
403
403
|
} });
|
|
404
404
|
}
|
|
405
|
+
const BUILT_IN_COLLECTION_METRIC_KINDS = new Set([
|
|
406
|
+
"inbox",
|
|
407
|
+
"outbox",
|
|
408
|
+
"following",
|
|
409
|
+
"followers",
|
|
410
|
+
"liked",
|
|
411
|
+
"featured",
|
|
412
|
+
"featured_tags"
|
|
413
|
+
]);
|
|
414
|
+
function getCollectionMetricKind(name) {
|
|
415
|
+
const normalized = name.trim().replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/\s+/g, "_");
|
|
416
|
+
return BUILT_IN_COLLECTION_METRIC_KINDS.has(normalized) ? normalized : "custom";
|
|
417
|
+
}
|
|
418
|
+
function collectionAttributes(base, result, response) {
|
|
419
|
+
return {
|
|
420
|
+
...base,
|
|
421
|
+
result,
|
|
422
|
+
...response == null ? {} : { statusCode: response.status }
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
426
|
+
const attrs = collectionAttributes(base, result, options.response);
|
|
427
|
+
recordCollectionRequest(meterProvider, attrs);
|
|
428
|
+
if (options.dispatchDurationMs != null) recordCollectionDispatchDuration(meterProvider, options.dispatchDurationMs, attrs);
|
|
429
|
+
if (options.itemCount != null) recordCollectionPageItems(meterProvider, options.itemCount, attrs);
|
|
430
|
+
if (options.totalItems != null) recordCollectionTotalItems(meterProvider, options.totalItems, attrs);
|
|
431
|
+
}
|
|
405
432
|
/**
|
|
406
433
|
* Handles a collection request.
|
|
407
434
|
* @template TItem The type of items in the collection.
|
|
@@ -412,36 +439,118 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
412
439
|
* @param parameters The parameters for handling the collection.
|
|
413
440
|
* @returns A promise that resolves to an HTTP response.
|
|
414
441
|
*/
|
|
415
|
-
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
|
|
442
|
+
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
416
443
|
const spanName = name$1.trim().replace(/\s+/g, "_");
|
|
417
444
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
418
445
|
const tracer = tracerProvider.getTracer(name, version);
|
|
419
446
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
447
|
+
const metricBase = {
|
|
448
|
+
kind: getCollectionMetricKind(name$1),
|
|
449
|
+
page: cursor != null,
|
|
450
|
+
dispatcher: "built_in"
|
|
451
|
+
};
|
|
452
|
+
let dispatchDurationMs;
|
|
453
|
+
let itemCount;
|
|
454
|
+
let totalItemCount;
|
|
455
|
+
const finish = (response, result) => {
|
|
456
|
+
recordCollectionMetrics(meterProvider, metricBase, result, {
|
|
457
|
+
response,
|
|
458
|
+
dispatchDurationMs,
|
|
459
|
+
itemCount,
|
|
460
|
+
totalItems: totalItemCount
|
|
461
|
+
});
|
|
462
|
+
return response;
|
|
463
|
+
};
|
|
464
|
+
try {
|
|
465
|
+
if (collectionCallbacks == null) return finish(await onNotFound(request), "not_found");
|
|
466
|
+
let collection;
|
|
467
|
+
const baseUri = uriGetter(identifier);
|
|
468
|
+
if (cursor == null) {
|
|
469
|
+
const firstCursor = await collectionCallbacks.firstCursor?.(context, identifier);
|
|
470
|
+
const totalItems = filter == null ? await collectionCallbacks.counter?.(context, identifier) : void 0;
|
|
471
|
+
totalItemCount = totalItems == null ? void 0 : Number(totalItems);
|
|
472
|
+
if (firstCursor == null) {
|
|
473
|
+
const itemsOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection ${spanName}`, {
|
|
474
|
+
kind: SpanKind.SERVER,
|
|
475
|
+
attributes: {
|
|
476
|
+
"activitypub.collection.id": baseUri.href,
|
|
477
|
+
"activitypub.collection.type": OrderedCollection.typeId.href
|
|
478
|
+
}
|
|
479
|
+
}, async (span) => {
|
|
480
|
+
if (totalItemCount != null) span.setAttribute("activitypub.collection.total_items", totalItemCount);
|
|
481
|
+
const started = performance.now();
|
|
482
|
+
try {
|
|
483
|
+
const page = await collectionCallbacks.dispatcher(context, identifier, null, filter);
|
|
484
|
+
dispatchDurationMs = getDurationMs(started);
|
|
485
|
+
if (page == null) {
|
|
486
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
487
|
+
return await onNotFound(request);
|
|
488
|
+
}
|
|
489
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
490
|
+
itemCount = items.length;
|
|
491
|
+
span.setAttribute("fedify.collection.items", itemCount);
|
|
492
|
+
return items;
|
|
493
|
+
} catch (e) {
|
|
494
|
+
if (dispatchDurationMs == null) dispatchDurationMs = getDurationMs(started);
|
|
495
|
+
span.setStatus({
|
|
496
|
+
code: SpanStatusCode.ERROR,
|
|
497
|
+
message: String(e)
|
|
498
|
+
});
|
|
499
|
+
throw e;
|
|
500
|
+
} finally {
|
|
501
|
+
span.end();
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
if (itemsOrResponse instanceof Response) return finish(itemsOrResponse, "not_found");
|
|
505
|
+
collection = new OrderedCollection({
|
|
506
|
+
id: baseUri,
|
|
507
|
+
totalItems: totalItemCount ?? null,
|
|
508
|
+
items: itemsOrResponse
|
|
509
|
+
});
|
|
510
|
+
} else {
|
|
511
|
+
const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
|
|
512
|
+
const first = new URL(context.url);
|
|
513
|
+
first.searchParams.set("cursor", firstCursor);
|
|
514
|
+
let last = null;
|
|
515
|
+
if (lastCursor != null) {
|
|
516
|
+
last = new URL(context.url);
|
|
517
|
+
last.searchParams.set("cursor", lastCursor);
|
|
518
|
+
}
|
|
519
|
+
collection = new OrderedCollection({
|
|
520
|
+
id: baseUri,
|
|
521
|
+
totalItems: totalItemCount ?? null,
|
|
522
|
+
first,
|
|
523
|
+
last
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
const uri = new URL(baseUri);
|
|
528
|
+
uri.searchParams.set("cursor", cursor);
|
|
529
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
428
530
|
kind: SpanKind.SERVER,
|
|
429
531
|
attributes: {
|
|
430
|
-
"activitypub.collection.id":
|
|
431
|
-
"activitypub.collection.type":
|
|
532
|
+
"activitypub.collection.id": uri.href,
|
|
533
|
+
"activitypub.collection.type": OrderedCollectionPage.typeId.href,
|
|
534
|
+
"fedify.collection.cursor": cursor
|
|
432
535
|
}
|
|
433
536
|
}, async (span) => {
|
|
434
|
-
|
|
537
|
+
const started = performance.now();
|
|
435
538
|
try {
|
|
436
|
-
const page = await collectionCallbacks.dispatcher(context, identifier,
|
|
539
|
+
const page = await collectionCallbacks.dispatcher(context, identifier, cursor, filter);
|
|
540
|
+
dispatchDurationMs = getDurationMs(started);
|
|
437
541
|
if (page == null) {
|
|
438
542
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
439
543
|
return await onNotFound(request);
|
|
440
544
|
}
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
545
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
546
|
+
itemCount = items.length;
|
|
547
|
+
span.setAttribute("fedify.collection.items", itemCount);
|
|
548
|
+
return {
|
|
549
|
+
...page,
|
|
550
|
+
items
|
|
551
|
+
};
|
|
444
552
|
} catch (e) {
|
|
553
|
+
if (dispatchDurationMs == null) dispatchDurationMs = getDurationMs(started);
|
|
445
554
|
span.setStatus({
|
|
446
555
|
code: SpanStatusCode.ERROR,
|
|
447
556
|
message: String(e)
|
|
@@ -451,87 +560,44 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
451
560
|
span.end();
|
|
452
561
|
}
|
|
453
562
|
});
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
} else {
|
|
461
|
-
const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
|
|
462
|
-
const first = new URL(context.url);
|
|
463
|
-
first.searchParams.set("cursor", firstCursor);
|
|
464
|
-
let last = null;
|
|
465
|
-
if (lastCursor != null) {
|
|
466
|
-
last = new URL(context.url);
|
|
467
|
-
last.searchParams.set("cursor", lastCursor);
|
|
563
|
+
if (pageOrResponse instanceof Response) return finish(pageOrResponse, "not_found");
|
|
564
|
+
const { items, prevCursor, nextCursor } = pageOrResponse;
|
|
565
|
+
let prev = null;
|
|
566
|
+
if (prevCursor != null) {
|
|
567
|
+
prev = new URL(context.url);
|
|
568
|
+
prev.searchParams.set("cursor", prevCursor);
|
|
468
569
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
last
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
const uri = new URL(baseUri);
|
|
478
|
-
uri.searchParams.set("cursor", cursor);
|
|
479
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
480
|
-
kind: SpanKind.SERVER,
|
|
481
|
-
attributes: {
|
|
482
|
-
"activitypub.collection.id": uri.href,
|
|
483
|
-
"activitypub.collection.type": OrderedCollectionPage.typeId.href,
|
|
484
|
-
"fedify.collection.cursor": cursor
|
|
570
|
+
let next = null;
|
|
571
|
+
if (nextCursor != null) {
|
|
572
|
+
next = new URL(context.url);
|
|
573
|
+
next.searchParams.set("cursor", nextCursor);
|
|
485
574
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
} catch (e) {
|
|
496
|
-
span.setStatus({
|
|
497
|
-
code: SpanStatusCode.ERROR,
|
|
498
|
-
message: String(e)
|
|
499
|
-
});
|
|
500
|
-
throw e;
|
|
501
|
-
} finally {
|
|
502
|
-
span.end();
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
if (pageOrResponse instanceof Response) return pageOrResponse;
|
|
506
|
-
const { items, prevCursor, nextCursor } = pageOrResponse;
|
|
507
|
-
let prev = null;
|
|
508
|
-
if (prevCursor != null) {
|
|
509
|
-
prev = new URL(context.url);
|
|
510
|
-
prev.searchParams.set("cursor", prevCursor);
|
|
575
|
+
const partOf = new URL(context.url);
|
|
576
|
+
partOf.searchParams.delete("cursor");
|
|
577
|
+
collection = new OrderedCollectionPage({
|
|
578
|
+
id: uri,
|
|
579
|
+
prev,
|
|
580
|
+
next,
|
|
581
|
+
items,
|
|
582
|
+
partOf
|
|
583
|
+
});
|
|
511
584
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
next = new URL(context.url);
|
|
515
|
-
next.searchParams.set("cursor", nextCursor);
|
|
585
|
+
if (collectionCallbacks.authorizePredicate != null) {
|
|
586
|
+
if (!await collectionCallbacks.authorizePredicate(context, identifier)) return finish(await onUnauthorized(request), "unauthorized");
|
|
516
587
|
}
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
588
|
+
const jsonLd = await collection.toJsonLd(context);
|
|
589
|
+
return finish(new Response(JSON.stringify(jsonLd), { headers: {
|
|
590
|
+
"Content-Type": "application/activity+json",
|
|
591
|
+
Vary: "Accept"
|
|
592
|
+
} }), "served");
|
|
593
|
+
} catch (e) {
|
|
594
|
+
recordCollectionMetrics(meterProvider, metricBase, "error", {
|
|
595
|
+
dispatchDurationMs,
|
|
596
|
+
itemCount,
|
|
597
|
+
totalItems: totalItemCount
|
|
525
598
|
});
|
|
599
|
+
throw e;
|
|
526
600
|
}
|
|
527
|
-
if (collectionCallbacks.authorizePredicate != null) {
|
|
528
|
-
if (!await collectionCallbacks.authorizePredicate(context, identifier)) return await onUnauthorized(request);
|
|
529
|
-
}
|
|
530
|
-
const jsonLd = await collection.toJsonLd(context);
|
|
531
|
-
return new Response(JSON.stringify(jsonLd), { headers: {
|
|
532
|
-
"Content-Type": "application/activity+json",
|
|
533
|
-
Vary: "Accept"
|
|
534
|
-
} });
|
|
535
601
|
}
|
|
536
602
|
/**
|
|
537
603
|
* Filters collection items based on the provided predicate.
|
|
@@ -1234,11 +1300,31 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1234
1300
|
* @since 1.8.0
|
|
1235
1301
|
*/
|
|
1236
1302
|
const handleCustomCollection = exceptWrapper(_handleCustomCollection);
|
|
1237
|
-
|
|
1303
|
+
const pendingCollectionMetricRecorders = /* @__PURE__ */ new WeakMap();
|
|
1304
|
+
function deferPendingCollectionMetrics(error, recorder) {
|
|
1305
|
+
if (error == null || typeof error !== "object" && typeof error !== "function") return false;
|
|
1306
|
+
pendingCollectionMetricRecorders.set(error, recorder);
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
function recordDeferredPendingCollectionMetrics(error, result, response) {
|
|
1310
|
+
if (error == null || typeof error !== "object" && typeof error !== "function") return;
|
|
1311
|
+
const recorder = pendingCollectionMetricRecorders.get(error);
|
|
1312
|
+
pendingCollectionMetricRecorders.delete(error);
|
|
1313
|
+
recorder?.(result, response);
|
|
1314
|
+
}
|
|
1315
|
+
async function _handleCustomCollection(request, { name, values, context, tracerProvider, meterProvider, collectionCallbacks: callbacks, filterPredicate }) {
|
|
1238
1316
|
verifyDefined(callbacks);
|
|
1239
1317
|
await authIfNeeded(context, values, callbacks);
|
|
1240
1318
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
1241
|
-
|
|
1319
|
+
const handler = new CustomCollectionHandler(name, values, context, callbacks, tracerProvider, meterProvider, Collection, CollectionPage, filterPredicate).fetchCollection(cursor);
|
|
1320
|
+
try {
|
|
1321
|
+
const response = await handler.toJsonLd().then(respondAsActivity);
|
|
1322
|
+
handler.recordPendingCollectionMetrics("served", response);
|
|
1323
|
+
return response;
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
if (!deferPendingCollectionMetrics(e, (result, response) => handler.recordPendingCollectionMetrics(result, response))) handler.recordPendingCollectionMetrics("error");
|
|
1326
|
+
throw e;
|
|
1327
|
+
}
|
|
1242
1328
|
}
|
|
1243
1329
|
/**
|
|
1244
1330
|
* Handles an ordered collection request.
|
|
@@ -1252,11 +1338,19 @@ async function _handleCustomCollection(request, { name, values, context, tracerP
|
|
|
1252
1338
|
* @since 1.8.0
|
|
1253
1339
|
*/
|
|
1254
1340
|
const handleOrderedCollection = exceptWrapper(_handleOrderedCollection);
|
|
1255
|
-
async function _handleOrderedCollection(request, { name, values, context, tracerProvider, collectionCallbacks: callbacks, filterPredicate }) {
|
|
1341
|
+
async function _handleOrderedCollection(request, { name, values, context, tracerProvider, meterProvider, collectionCallbacks: callbacks, filterPredicate }) {
|
|
1256
1342
|
verifyDefined(callbacks);
|
|
1257
1343
|
await authIfNeeded(context, values, callbacks);
|
|
1258
1344
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
1259
|
-
|
|
1345
|
+
const handler = new CustomCollectionHandler(name, values, context, callbacks, tracerProvider, meterProvider, OrderedCollection, OrderedCollectionPage, filterPredicate).fetchCollection(cursor);
|
|
1346
|
+
try {
|
|
1347
|
+
const response = await handler.toJsonLd().then(respondAsActivity);
|
|
1348
|
+
handler.recordPendingCollectionMetrics("served", response);
|
|
1349
|
+
return response;
|
|
1350
|
+
} catch (e) {
|
|
1351
|
+
if (!deferPendingCollectionMetrics(e, (result, response) => handler.recordPendingCollectionMetrics(result, response))) handler.recordPendingCollectionMetrics("error");
|
|
1352
|
+
throw e;
|
|
1353
|
+
}
|
|
1260
1354
|
}
|
|
1261
1355
|
/**
|
|
1262
1356
|
* Handling custom collections with support for pagination and filtering.
|
|
@@ -1276,6 +1370,7 @@ var CustomCollectionHandler = class {
|
|
|
1276
1370
|
context;
|
|
1277
1371
|
callbacks;
|
|
1278
1372
|
tracerProvider;
|
|
1373
|
+
meterProvider;
|
|
1279
1374
|
Collection;
|
|
1280
1375
|
CollectionPage;
|
|
1281
1376
|
filterPredicate;
|
|
@@ -1303,6 +1398,7 @@ var CustomCollectionHandler = class {
|
|
|
1303
1398
|
*/
|
|
1304
1399
|
#dispatcher;
|
|
1305
1400
|
#collection = null;
|
|
1401
|
+
#pendingCollectionMetrics = [];
|
|
1306
1402
|
/**
|
|
1307
1403
|
* Creates a new CustomCollection instance.
|
|
1308
1404
|
* @param name The name of the collection.
|
|
@@ -1314,12 +1410,13 @@ var CustomCollectionHandler = class {
|
|
|
1314
1410
|
* @param CollectionPage The CollectionPage constructor.
|
|
1315
1411
|
* @param filterPredicate Optional filter predicate for items.
|
|
1316
1412
|
*/
|
|
1317
|
-
constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
|
|
1413
|
+
constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
|
|
1318
1414
|
this.name = name$2;
|
|
1319
1415
|
this.values = values;
|
|
1320
1416
|
this.context = context;
|
|
1321
1417
|
this.callbacks = callbacks;
|
|
1322
1418
|
this.tracerProvider = tracerProvider;
|
|
1419
|
+
this.meterProvider = meterProvider;
|
|
1323
1420
|
this.Collection = Collection;
|
|
1324
1421
|
this.CollectionPage = CollectionPage;
|
|
1325
1422
|
this.filterPredicate = filterPredicate;
|
|
@@ -1372,10 +1469,12 @@ var CustomCollectionHandler = class {
|
|
|
1372
1469
|
const { prevCursor, nextCursor } = pages;
|
|
1373
1470
|
const partOf = new URL(id);
|
|
1374
1471
|
partOf.searchParams.delete("cursor");
|
|
1472
|
+
const items = this.filterItems(pages.items);
|
|
1473
|
+
this.recordPendingCollectionItemCount(true, items.length);
|
|
1375
1474
|
return {
|
|
1376
1475
|
id,
|
|
1377
1476
|
partOf,
|
|
1378
|
-
items
|
|
1477
|
+
items,
|
|
1379
1478
|
prev: this.appendToUrl(prevCursor),
|
|
1380
1479
|
next: this.appendToUrl(nextCursor)
|
|
1381
1480
|
};
|
|
@@ -1388,11 +1487,16 @@ var CustomCollectionHandler = class {
|
|
|
1388
1487
|
*/
|
|
1389
1488
|
async getProps(firstCursor) {
|
|
1390
1489
|
const lastCursor = await this.callbacks.lastCursor?.(this.context, this.values);
|
|
1490
|
+
const totalItems = await this.totalItems;
|
|
1491
|
+
if (totalItems != null) this.#pendingCollectionMetrics.push({
|
|
1492
|
+
page: false,
|
|
1493
|
+
totalItems: Number(totalItems)
|
|
1494
|
+
});
|
|
1391
1495
|
return {
|
|
1392
1496
|
id: this.#id,
|
|
1393
1497
|
first: this.appendToUrl(firstCursor),
|
|
1394
1498
|
last: this.appendToUrl(lastCursor),
|
|
1395
|
-
totalItems
|
|
1499
|
+
totalItems
|
|
1396
1500
|
};
|
|
1397
1501
|
}
|
|
1398
1502
|
/**
|
|
@@ -1402,10 +1506,12 @@ var CustomCollectionHandler = class {
|
|
|
1402
1506
|
async getPropsWithoutCursor() {
|
|
1403
1507
|
const totalItems = await this.totalItems;
|
|
1404
1508
|
const pages = await this.getPages({ totalItems });
|
|
1509
|
+
const items = this.filterItems(pages.items);
|
|
1510
|
+
this.recordPendingCollectionItemCount(false, items.length);
|
|
1405
1511
|
return {
|
|
1406
1512
|
id: this.#id,
|
|
1407
1513
|
totalItems,
|
|
1408
|
-
items
|
|
1514
|
+
items
|
|
1409
1515
|
};
|
|
1410
1516
|
}
|
|
1411
1517
|
/**
|
|
@@ -1440,12 +1546,24 @@ var CustomCollectionHandler = class {
|
|
|
1440
1546
|
* @returns A function that handles the span operation.
|
|
1441
1547
|
*/
|
|
1442
1548
|
spanPages = ({ totalItems = null, cursor = null }) => async (span) => {
|
|
1549
|
+
const pageMetricBase = this.metricBase(cursor !== null);
|
|
1550
|
+
const started = performance.now();
|
|
1443
1551
|
try {
|
|
1444
1552
|
if (totalItems !== null) span.setAttribute(this.ATTRS.TOTAL_ITEMS, totalItems);
|
|
1445
1553
|
const page = await this.dispatch(cursor);
|
|
1554
|
+
const durationMs = getDurationMs(started);
|
|
1446
1555
|
span.setAttribute(this.ATTRS.ITEMS, page.items.length);
|
|
1556
|
+
this.#pendingCollectionMetrics.push({
|
|
1557
|
+
page: pageMetricBase.page,
|
|
1558
|
+
dispatchDurationMs: durationMs,
|
|
1559
|
+
totalItems: totalItems == null ? void 0 : Number(totalItems)
|
|
1560
|
+
});
|
|
1447
1561
|
return page;
|
|
1448
1562
|
} catch (e) {
|
|
1563
|
+
this.#pendingCollectionMetrics.push({
|
|
1564
|
+
page: cursor !== null,
|
|
1565
|
+
dispatchDurationMs: getDurationMs(started)
|
|
1566
|
+
});
|
|
1449
1567
|
const message = e instanceof Error ? e.message : String(e);
|
|
1450
1568
|
span.setStatus({
|
|
1451
1569
|
code: SpanStatusCode.ERROR,
|
|
@@ -1472,6 +1590,37 @@ var CustomCollectionHandler = class {
|
|
|
1472
1590
|
filterItems(items) {
|
|
1473
1591
|
return filterCollectionItems(items, this.name, this.filterPredicate);
|
|
1474
1592
|
}
|
|
1593
|
+
metricBase(page) {
|
|
1594
|
+
return {
|
|
1595
|
+
kind: "custom",
|
|
1596
|
+
page,
|
|
1597
|
+
dispatcher: "custom"
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
metricAttributes(page, result, response) {
|
|
1601
|
+
return collectionAttributes(this.metricBase(page), result, response);
|
|
1602
|
+
}
|
|
1603
|
+
recordPendingCollectionItemCount(page, itemCount) {
|
|
1604
|
+
for (let i = this.#pendingCollectionMetrics.length - 1; i >= 0; i--) {
|
|
1605
|
+
const measurement = this.#pendingCollectionMetrics[i];
|
|
1606
|
+
if (measurement.page === page && measurement.dispatchDurationMs != null && measurement.itemCount == null) {
|
|
1607
|
+
measurement.itemCount = itemCount;
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
this.#pendingCollectionMetrics.push({
|
|
1612
|
+
page,
|
|
1613
|
+
itemCount
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
recordPendingCollectionMetrics(result, response) {
|
|
1617
|
+
for (const measurement of this.#pendingCollectionMetrics.splice(0)) {
|
|
1618
|
+
const attrs = this.metricAttributes(measurement.page, result, response);
|
|
1619
|
+
if (measurement.dispatchDurationMs != null) recordCollectionDispatchDuration(this.meterProvider, measurement.dispatchDurationMs, attrs);
|
|
1620
|
+
if (measurement.itemCount != null) recordCollectionPageItems(this.meterProvider, measurement.itemCount, attrs);
|
|
1621
|
+
if (measurement.totalItems != null) recordCollectionTotalItems(this.meterProvider, measurement.totalItems, attrs);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1475
1624
|
/**
|
|
1476
1625
|
* Appends a cursor to the URL if it exists.
|
|
1477
1626
|
* @param cursor The cursor to append, or null/undefined.
|
|
@@ -1535,14 +1684,36 @@ var CustomCollectionHandler = class {
|
|
|
1535
1684
|
*/
|
|
1536
1685
|
function exceptWrapper(handler) {
|
|
1537
1686
|
return async (request, handlerParams) => {
|
|
1687
|
+
const page = new URL(request.url).searchParams.get("cursor") != null;
|
|
1688
|
+
const { meterProvider } = handlerParams;
|
|
1689
|
+
const metricBase = {
|
|
1690
|
+
kind: "custom",
|
|
1691
|
+
page,
|
|
1692
|
+
dispatcher: "custom"
|
|
1693
|
+
};
|
|
1538
1694
|
try {
|
|
1539
|
-
|
|
1695
|
+
const response = await handler(request, handlerParams);
|
|
1696
|
+
recordCollectionRequest(meterProvider, collectionAttributes(metricBase, "served", response));
|
|
1697
|
+
return response;
|
|
1540
1698
|
} catch (error) {
|
|
1541
1699
|
const { onNotFound, onUnauthorized } = handlerParams;
|
|
1542
1700
|
switch (error?.constructor) {
|
|
1543
|
-
case ItemsNotFoundError:
|
|
1544
|
-
|
|
1545
|
-
|
|
1701
|
+
case ItemsNotFoundError: {
|
|
1702
|
+
const response = await onNotFound(request);
|
|
1703
|
+
recordDeferredPendingCollectionMetrics(error, "not_found", response);
|
|
1704
|
+
recordCollectionRequest(meterProvider, collectionAttributes(metricBase, "not_found", response));
|
|
1705
|
+
return response;
|
|
1706
|
+
}
|
|
1707
|
+
case UnauthorizedError: {
|
|
1708
|
+
const response = await onUnauthorized(request);
|
|
1709
|
+
recordDeferredPendingCollectionMetrics(error, "unauthorized", response);
|
|
1710
|
+
recordCollectionRequest(meterProvider, collectionAttributes(metricBase, "unauthorized", response));
|
|
1711
|
+
return response;
|
|
1712
|
+
}
|
|
1713
|
+
default:
|
|
1714
|
+
recordDeferredPendingCollectionMetrics(error, "error");
|
|
1715
|
+
recordCollectionRequest(meterProvider, collectionAttributes(metricBase, "error"));
|
|
1716
|
+
throw error;
|
|
1546
1717
|
}
|
|
1547
1718
|
}
|
|
1548
1719
|
};
|
|
@@ -2930,7 +3101,15 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2930
3101
|
}
|
|
2931
3102
|
if (request.method !== "POST" && !acceptsJsonLd(request)) {
|
|
2932
3103
|
metricState.endpoint = "not_acceptable";
|
|
2933
|
-
|
|
3104
|
+
const response = await onNotAcceptable(request);
|
|
3105
|
+
const collectionRoute = getCollectionMetricRoute(routeName);
|
|
3106
|
+
if (collectionRoute != null) recordCollectionRequest(this._meterProvider, {
|
|
3107
|
+
...collectionRoute,
|
|
3108
|
+
page: url.searchParams.get("cursor") != null,
|
|
3109
|
+
result: "not_acceptable",
|
|
3110
|
+
statusCode: response.status
|
|
3111
|
+
});
|
|
3112
|
+
return response;
|
|
2934
3113
|
}
|
|
2935
3114
|
switch (routeName) {
|
|
2936
3115
|
case "actor":
|
|
@@ -2991,6 +3170,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2991
3170
|
context,
|
|
2992
3171
|
collectionCallbacks: this.outboxCallbacks,
|
|
2993
3172
|
tracerProvider: this.tracerProvider,
|
|
3173
|
+
meterProvider: this._meterProvider,
|
|
2994
3174
|
onUnauthorized,
|
|
2995
3175
|
onNotFound
|
|
2996
3176
|
});
|
|
@@ -3002,6 +3182,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3002
3182
|
context,
|
|
3003
3183
|
collectionCallbacks: this.inboxCallbacks,
|
|
3004
3184
|
tracerProvider: this.tracerProvider,
|
|
3185
|
+
meterProvider: this._meterProvider,
|
|
3005
3186
|
onUnauthorized,
|
|
3006
3187
|
onNotFound
|
|
3007
3188
|
});
|
|
@@ -3041,6 +3222,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3041
3222
|
context,
|
|
3042
3223
|
collectionCallbacks: this.followingCallbacks,
|
|
3043
3224
|
tracerProvider: this.tracerProvider,
|
|
3225
|
+
meterProvider: this._meterProvider,
|
|
3044
3226
|
onUnauthorized,
|
|
3045
3227
|
onNotFound
|
|
3046
3228
|
});
|
|
@@ -3064,6 +3246,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3064
3246
|
filterPredicate: baseUrl != null ? ((i) => (i instanceof URL ? i.href : i.id?.href ?? "").startsWith(baseUrl)) : void 0,
|
|
3065
3247
|
collectionCallbacks: this.followersCallbacks,
|
|
3066
3248
|
tracerProvider: this.tracerProvider,
|
|
3249
|
+
meterProvider: this._meterProvider,
|
|
3067
3250
|
onUnauthorized,
|
|
3068
3251
|
onNotFound
|
|
3069
3252
|
});
|
|
@@ -3075,6 +3258,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3075
3258
|
context,
|
|
3076
3259
|
collectionCallbacks: this.likedCallbacks,
|
|
3077
3260
|
tracerProvider: this.tracerProvider,
|
|
3261
|
+
meterProvider: this._meterProvider,
|
|
3078
3262
|
onUnauthorized,
|
|
3079
3263
|
onNotFound
|
|
3080
3264
|
});
|
|
@@ -3085,6 +3269,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3085
3269
|
context,
|
|
3086
3270
|
collectionCallbacks: this.featuredCallbacks,
|
|
3087
3271
|
tracerProvider: this.tracerProvider,
|
|
3272
|
+
meterProvider: this._meterProvider,
|
|
3088
3273
|
onUnauthorized,
|
|
3089
3274
|
onNotFound
|
|
3090
3275
|
});
|
|
@@ -3095,6 +3280,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3095
3280
|
context,
|
|
3096
3281
|
collectionCallbacks: this.featuredTagsCallbacks,
|
|
3097
3282
|
tracerProvider: this.tracerProvider,
|
|
3283
|
+
meterProvider: this._meterProvider,
|
|
3098
3284
|
onUnauthorized,
|
|
3099
3285
|
onNotFound
|
|
3100
3286
|
});
|
|
@@ -3107,6 +3293,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3107
3293
|
values: route.values,
|
|
3108
3294
|
collectionCallbacks: callbacks,
|
|
3109
3295
|
tracerProvider: this.tracerProvider,
|
|
3296
|
+
meterProvider: this._meterProvider,
|
|
3110
3297
|
onUnauthorized,
|
|
3111
3298
|
onNotFound
|
|
3112
3299
|
});
|
|
@@ -3120,6 +3307,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3120
3307
|
values: route.values,
|
|
3121
3308
|
collectionCallbacks: callbacks,
|
|
3122
3309
|
tracerProvider: this.tracerProvider,
|
|
3310
|
+
meterProvider: this._meterProvider,
|
|
3123
3311
|
onUnauthorized,
|
|
3124
3312
|
onNotFound
|
|
3125
3313
|
});
|
|
@@ -3152,6 +3340,29 @@ function getEndpointCategory(routeName) {
|
|
|
3152
3340
|
default: return "not_found";
|
|
3153
3341
|
}
|
|
3154
3342
|
}
|
|
3343
|
+
function getCollectionMetricRoute(routeName) {
|
|
3344
|
+
switch (routeName) {
|
|
3345
|
+
case "inbox":
|
|
3346
|
+
case "outbox":
|
|
3347
|
+
case "following":
|
|
3348
|
+
case "followers":
|
|
3349
|
+
case "liked":
|
|
3350
|
+
case "featured": return {
|
|
3351
|
+
kind: routeName,
|
|
3352
|
+
dispatcher: "built_in"
|
|
3353
|
+
};
|
|
3354
|
+
case "featuredTags": return {
|
|
3355
|
+
kind: "featured_tags",
|
|
3356
|
+
dispatcher: "built_in"
|
|
3357
|
+
};
|
|
3358
|
+
case "collection":
|
|
3359
|
+
case "orderedCollection": return {
|
|
3360
|
+
kind: "custom",
|
|
3361
|
+
dispatcher: "custom"
|
|
3362
|
+
};
|
|
3363
|
+
default: return;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3155
3366
|
const FANOUT_THRESHOLD = 5;
|
|
3156
3367
|
var ContextImpl = class ContextImpl {
|
|
3157
3368
|
url;
|