@fedify/fedify 2.4.0-dev.1668 → 2.4.0-dev.1727
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{assert-CJ1KXcvv.mjs → assert-CebeUf0C.mjs} +1 -1
- package/dist/assert_exists-DtzkXC47.mjs +28 -0
- package/dist/assert_false-3pLLLsO4.mjs +23 -0
- package/dist/assert_greater_or_equal-BApOTh1h.mjs +30 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-g3WcyYNR.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-BN7yDMWe.mjs} +1 -1
- package/dist/assert_rejects-wFbSPn1O.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-Bpjl4Psw.mjs} +1 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BJ5wuvR2.mjs} +1 -1
- package/dist/{builder-iXZ5jNVg.mjs → builder-f5-YwHfj.mjs} +5 -3
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-BT7CiPbz.mjs} +191 -9
- package/dist/circuit-breaker-test-utils-BYIHoliq.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CtxvmazV.mjs} +63 -5
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +3 -3
- package/dist/compat/public-audience.test.mjs +3 -3
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/{context-C05Rgxmu.d.cts → context-B-dsBjZ9.d.cts} +281 -7
- package/dist/{context-Cjnr0ig1.d.ts → context-DrLXudbA.d.ts} +281 -7
- package/dist/{deno-DtHcfB0f.mjs → deno-Deyoxesx.mjs} +1 -1
- package/dist/{docloader-0vAfF6XW.mjs → docloader-B3kuDBg9.mjs} +2 -2
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/builder.test.mjs +18 -5
- package/dist/federation/circuit-breaker.test.mjs +626 -7
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +14 -13
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +5 -5
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +4 -4
- package/dist/federation/middleware.test.mjs +37 -28
- package/dist/federation/mod.cjs +18 -540
- package/dist/federation/mod.d.cts +3 -4
- package/dist/federation/mod.d.ts +3 -4
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +32 -5
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/retry.test.mjs +3 -3
- package/dist/federation/router.test.mjs +7 -7
- package/dist/federation/send.test.mjs +28 -26
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +4 -4
- package/dist/federation/tasks/tasks.test.mjs +6 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +74 -12
- package/dist/{middleware-DzIUFRO2.cjs → federation-BzRjeOky.cjs} +855 -114
- package/dist/{temporal-polyfill-BCdTHexd.cjs → http-AyKngJ_h.cjs} +2801 -104
- package/dist/{http-BbnOMQoy.js → http-Bw6s_dOB.js} +3 -3
- package/dist/{http-DUmWq5Pw.mjs → http-TjhZfiSN.mjs} +5 -5
- package/dist/{key-CM7wDnZG.mjs → key-b4kf4yZT.mjs} +2 -2
- package/dist/{kv-cache-zmgG1HUp.cjs → kv-cache-BTVaZrf-.cjs} +2 -4
- package/dist/{kv-cache-ZQ22hm44.js → kv-cache-CQsh-KUw.js} +1 -1
- package/dist/{kv-cache-DEXKemJ9.mjs → kv-cache-CpILvhUV.mjs} +1 -1
- package/dist/{ld-BttH_-8K.mjs → ld-CKujkAfG.mjs} +5 -5
- package/dist/{metrics-DT-CnD1g.mjs → metrics-AlCGTDrr.mjs} +2 -2
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-DrroX3aD.mjs → middleware-C9Y5lLZN.mjs} +60 -43
- package/dist/{middleware-D3IAVMcT.js → middleware-CZcuedl-.js} +241 -40
- package/dist/{middleware-ChlYS0Af.mjs → middleware-LPbZz6-1.mjs} +1 -1
- package/dist/{mod-BrpVsSfY.d.cts → mod-CLHkCwLm.d.cts} +3 -3
- package/dist/{mod-B0hW12_O.d.cts → mod-CuDDaIIt.d.cts} +1 -1
- package/dist/{mod-COIAjwRS.d.ts → mod-D-IhOEOP.d.ts} +1 -1
- package/dist/{mod-DdONo-ep.d.cts → mod-D-abvu2C.d.cts} +1 -1
- package/dist/{mod-BsCE5Ihq.d.ts → mod-D6O8C7eN.d.ts} +1 -1
- package/dist/{mod-CNef4in0.d.ts → mod-DypxPS9H.d.ts} +3 -3
- package/dist/mod.cjs +24 -25
- package/dist/mod.d.cts +8 -9
- package/dist/mod.d.ts +8 -9
- package/dist/mod.js +7 -7
- package/dist/nodeinfo/client.test.mjs +64 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-BQSHRLhg.mjs} +2 -2
- package/dist/{owner-DZALmhdl.mjs → owner-D1O8v8FB.mjs} +2 -2
- package/dist/{proof-CqA-2nfW.js → proof-BTBolSnd.js} +6 -6
- package/dist/{proof-a60oVJ_4.cjs → proof-DcSm-2eG.cjs} +10 -11
- package/dist/{proof-DiQ0uBal.mjs → proof-DeMR7Akd.mjs} +6 -6
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--9hx7IGx.mjs} +1 -1
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/{send-DcwZnQWG.mjs → send-DG7JG280.mjs} +3 -3
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +37 -11
- package/dist/sig/key.test.mjs +6 -6
- package/dist/sig/ld.test.mjs +9 -7
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +10 -8
- package/dist/std__assert-CxFT0MNt.mjs +8 -0
- package/dist/{temporal-Bpxpsl2k.mjs → temporal-DA3jx8x1.mjs} +3 -3
- package/dist/testing/mod.d.mts +10 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-CO7ftO8A.mjs → testing-BX0Nicyq.mjs} +2 -2
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-Dtyf8lVj.js} +63 -5
- package/dist/{types-CPym83zc.cjs → types-RXm5YxpC.cjs} +62 -5
- package/dist/utils/docloader.test.mjs +7 -7
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/skills/fedify/SKILL.md +462 -0
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/http-BpT5cE6r.cjs +0 -2694
- package/dist/metrics-DN13mCAw.d.cts +0 -294
- package/dist/metrics-v_e0n5GQ.d.ts +0 -294
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-CBlRfA-_.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{codec-BBga-FWI.mjs → codec-peqOdBaE.mjs} +0 -0
- /package/dist/{context-BJXxPElm.mjs → context-Dt36KFEJ.mjs} +0 -0
- /package/dist/{http-VyDTd4G3.d.cts → http-6TI0tI03.d.cts} +0 -0
- /package/dist/{http-lf8Hsd91.d.ts → http-CdrSn2ip.d.ts} +0 -0
- /package/dist/{keycache-_DHqz5z_.mjs → keycache-0Jk78XmB.mjs} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-D6hNiMTK.d.ts → kv-BhNbN8Ed.d.cts} +0 -0
- /package/dist/{kv-gJ8LYbxX.d.cts → kv-BhNbN8Ed.d.ts} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{mq-Bo7Etgh6.mjs → mq-Dm1Wmtm-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{owner-CnngXDNJ.d.ts → owner-DGSfntI-.d.ts} +0 -0
- /package/dist/{owner-DEvZuyOE.d.cts → owner-r_waTiQj.d.cts} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const
|
|
3
|
-
const require_transformers = require("./transformers-
|
|
4
|
-
const require_http = require("./http-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const require_kv_cache = require("./kv-cache-zmgG1HUp.cjs");
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
|
+
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
+
const require_http = require("./http-AyKngJ_h.cjs");
|
|
5
|
+
const require_proof = require("./proof-DcSm-2eG.cjs");
|
|
6
|
+
const require_types = require("./types-RXm5YxpC.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-BTVaZrf-.cjs");
|
|
9
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
10
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
11
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -15,7 +14,7 @@ let es_toolkit = require("es-toolkit");
|
|
|
15
14
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
16
15
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
17
16
|
let _fedify_vocab_runtime_jsonld = require("@fedify/vocab-runtime/jsonld");
|
|
18
|
-
_fedify_vocab_runtime_jsonld =
|
|
17
|
+
_fedify_vocab_runtime_jsonld = require_rolldown_runtime.__toESM(_fedify_vocab_runtime_jsonld, 1);
|
|
19
18
|
let _fedify_webfinger = require("@fedify/webfinger");
|
|
20
19
|
let _opentelemetry_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
21
20
|
let devalue = require("devalue");
|
|
@@ -144,6 +143,8 @@ var FederationBuilderImpl = class {
|
|
|
144
143
|
f.webFingerLinksDispatcher = this.webFingerLinksDispatcher;
|
|
145
144
|
f.objectCallbacks = { ...this.objectCallbacks };
|
|
146
145
|
f.objectTypeIds = { ...this.objectTypeIds };
|
|
146
|
+
f.collectionCallbacks = { ...this.collectionCallbacks };
|
|
147
|
+
f.collectionTypeIds = { ...this.collectionTypeIds };
|
|
147
148
|
f.inboxPath = this.inboxPath;
|
|
148
149
|
f.outboxPath = this.outboxPath;
|
|
149
150
|
f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
|
|
@@ -723,8 +724,14 @@ function createFederationBuilder() {
|
|
|
723
724
|
}
|
|
724
725
|
//#endregion
|
|
725
726
|
//#region src/federation/circuit-breaker.ts
|
|
726
|
-
const Temporal$
|
|
727
|
-
const
|
|
727
|
+
const Temporal$9 = require_http.Temporal;
|
|
728
|
+
const LEGACY_SWEEP_MARKER = ["__fedify_meta", "circuit_breaker_state_ttl_sweep_v1"];
|
|
729
|
+
const LEGACY_SWEEP_DELETING_MARKER = { __fedifyDeletingCircuitBreakerLegacyState: true };
|
|
730
|
+
const CIRCUIT_BREAKER_STATE_VERSION = 1;
|
|
731
|
+
const LEGACY_SWEEP_LOCK_TTL = Temporal$9.Duration.from({ minutes: 5 });
|
|
732
|
+
const LEGACY_SWEEP_RETRY_WINDOW = Temporal$9.Duration.from({ hours: 168 });
|
|
733
|
+
const LEGACY_SWEEP_WAIT_INTERVAL = 10;
|
|
734
|
+
const LEGACY_SWEEP_CAS_ATTEMPTS = 3;
|
|
728
735
|
/**
|
|
729
736
|
* Tracks reachability state for remote outbox delivery hosts.
|
|
730
737
|
* @since 2.3.0
|
|
@@ -734,12 +741,14 @@ var CircuitBreaker = class {
|
|
|
734
741
|
#prefix;
|
|
735
742
|
#options;
|
|
736
743
|
#now;
|
|
744
|
+
#legacySweep;
|
|
745
|
+
#legacySweepDone;
|
|
737
746
|
#stateChangeObserver;
|
|
738
747
|
constructor(options) {
|
|
739
748
|
this.#kv = options.kv;
|
|
740
749
|
this.#prefix = options.prefix;
|
|
741
750
|
this.#options = normalizeCircuitBreakerOptions(options.options ?? {});
|
|
742
|
-
this.#now = options.now ?? (() => Temporal$
|
|
751
|
+
this.#now = options.now ?? (() => Temporal$9.Now.instant());
|
|
743
752
|
this.#stateChangeObserver = options.stateChangeObserver;
|
|
744
753
|
}
|
|
745
754
|
get options() {
|
|
@@ -750,9 +759,10 @@ var CircuitBreaker = class {
|
|
|
750
759
|
return now.until(this.#capHeldRetryAt(now, heldSince, now.add(delay)));
|
|
751
760
|
}
|
|
752
761
|
async beforeSend(remoteHost, message) {
|
|
762
|
+
this.#sweepLegacyStates();
|
|
753
763
|
const heldSince = parseHeldSince(message.circuitHeldSince);
|
|
754
764
|
const now = this.#now();
|
|
755
|
-
if (heldSince != null && Temporal$
|
|
765
|
+
if (heldSince != null && Temporal$9.Instant.compare(heldSince.add(this.#options.heldActivityTtl), now) <= 0) return {
|
|
756
766
|
type: "drop",
|
|
757
767
|
heldSince
|
|
758
768
|
};
|
|
@@ -764,12 +774,12 @@ var CircuitBreaker = class {
|
|
|
764
774
|
probe: false
|
|
765
775
|
};
|
|
766
776
|
if (oldState.state === "half-open") {
|
|
767
|
-
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$
|
|
777
|
+
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$9.Instant.from(oldState.halfOpened);
|
|
768
778
|
if (halfOpened != null) {
|
|
769
779
|
const staleAt = halfOpened.add(this.#options.recoveryDelay);
|
|
770
|
-
if (Temporal$
|
|
780
|
+
if (Temporal$9.Instant.compare(now, staleAt) < 0) {
|
|
771
781
|
const releaseAt = now.add(this.#options.releaseInterval);
|
|
772
|
-
const retryAt = Temporal$
|
|
782
|
+
const retryAt = Temporal$9.Instant.compare(releaseAt, staleAt) < 0 ? releaseAt : staleAt;
|
|
773
783
|
const cappedRetryAt = this.#capHeldRetryAt(now, heldSince, retryAt);
|
|
774
784
|
return {
|
|
775
785
|
type: "hold",
|
|
@@ -791,8 +801,8 @@ var CircuitBreaker = class {
|
|
|
791
801
|
lastConflictingState = "half-open";
|
|
792
802
|
continue;
|
|
793
803
|
}
|
|
794
|
-
const probeAt = (oldState.opened == null ? now : Temporal$
|
|
795
|
-
if (Temporal$
|
|
804
|
+
const probeAt = (oldState.opened == null ? now : Temporal$9.Instant.from(oldState.opened)).add(this.#options.recoveryDelay);
|
|
805
|
+
if (Temporal$9.Instant.compare(now, probeAt) < 0) {
|
|
796
806
|
const retryAt = this.#capHeldRetryAt(now, heldSince, probeAt);
|
|
797
807
|
return {
|
|
798
808
|
type: "hold",
|
|
@@ -831,6 +841,7 @@ var CircuitBreaker = class {
|
|
|
831
841
|
throw new Error(`Failed to update circuit breaker state for ${remoteHost}`);
|
|
832
842
|
}
|
|
833
843
|
async recordSuccess(remoteHost) {
|
|
844
|
+
this.#sweepLegacyStates();
|
|
834
845
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
835
846
|
const oldState = await this.#get(remoteHost);
|
|
836
847
|
if (oldState == null) return void 0;
|
|
@@ -851,11 +862,12 @@ var CircuitBreaker = class {
|
|
|
851
862
|
return await this.recordSuccess(remoteHost);
|
|
852
863
|
}
|
|
853
864
|
async recordFailure(remoteHost) {
|
|
865
|
+
this.#sweepLegacyStates();
|
|
854
866
|
const now = this.#now();
|
|
855
867
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
856
868
|
const oldState = await this.#get(remoteHost);
|
|
857
869
|
if (oldState?.state === "open") return void 0;
|
|
858
|
-
const oldFailures = oldState?.failures.map(Temporal$
|
|
870
|
+
const oldFailures = oldState?.failures.map(Temporal$9.Instant.from) ?? [];
|
|
859
871
|
const failures = this.#options.pruneFailures([...oldFailures, now], now);
|
|
860
872
|
let newState;
|
|
861
873
|
let transition;
|
|
@@ -898,26 +910,122 @@ var CircuitBreaker = class {
|
|
|
898
910
|
}
|
|
899
911
|
}
|
|
900
912
|
async getState(remoteHost) {
|
|
901
|
-
|
|
913
|
+
this.#sweepLegacyStates();
|
|
914
|
+
return stripStoredCircuitBreakerState(await this.#get(remoteHost));
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* The currently running background legacy sweep, if any.
|
|
918
|
+
* @internal
|
|
919
|
+
*/
|
|
920
|
+
get pendingSweep() {
|
|
921
|
+
return this.#legacySweep;
|
|
902
922
|
}
|
|
903
923
|
#key(remoteHost) {
|
|
904
924
|
return [...this.#prefix, remoteHost];
|
|
905
925
|
}
|
|
926
|
+
#legacySweepMarkerKey() {
|
|
927
|
+
return [...this.#prefix, ...LEGACY_SWEEP_MARKER];
|
|
928
|
+
}
|
|
929
|
+
#sweepLegacyStates() {
|
|
930
|
+
if (this.#kv.cas == null) return;
|
|
931
|
+
if (this.#options.stateTtl == null) return;
|
|
932
|
+
if (this.#legacySweep != null) return;
|
|
933
|
+
if (this.#isLegacySweepDone()) return;
|
|
934
|
+
this.#legacySweep = this.#sweepLegacyStatesImpl().catch((error) => {
|
|
935
|
+
(0, _logtape_logtape.getLogger)([
|
|
936
|
+
"fedify",
|
|
937
|
+
"federation",
|
|
938
|
+
"circuit"
|
|
939
|
+
]).warn("Failed to sweep legacy circuit breaker state:\n{error}", { error });
|
|
940
|
+
}).finally(() => {
|
|
941
|
+
this.#legacySweep = void 0;
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
async #sweepLegacyStatesImpl() {
|
|
945
|
+
const markerKey = this.#legacySweepMarkerKey();
|
|
946
|
+
const marker = await this.#acquireLegacySweep(markerKey);
|
|
947
|
+
if (marker === "done") return;
|
|
948
|
+
try {
|
|
949
|
+
for await (const { key, value } of this.#kv.list(this.#prefix)) {
|
|
950
|
+
if (key.length !== this.#prefix.length + 1) continue;
|
|
951
|
+
await this.#migrateLegacyState(key, value);
|
|
952
|
+
}
|
|
953
|
+
} catch (error) {
|
|
954
|
+
await this.#deleteIfUnchanged(markerKey, marker);
|
|
955
|
+
throw error;
|
|
956
|
+
}
|
|
957
|
+
const finishedMarker = this.#finishLegacySweepMarker(marker);
|
|
958
|
+
if (await this.#kv.cas(markerKey, marker, finishedMarker)) this.#rememberLegacySweepMarker(finishedMarker);
|
|
959
|
+
}
|
|
960
|
+
#finishLegacySweepMarker(marker) {
|
|
961
|
+
const retryUntil = getLegacySweepRetryUntil(marker);
|
|
962
|
+
if (retryUntil != null && Temporal$9.Instant.compare(this.#now(), Temporal$9.Instant.from(retryUntil)) < 0) return {
|
|
963
|
+
state: "done",
|
|
964
|
+
retryUntil
|
|
965
|
+
};
|
|
966
|
+
return { state: "final" };
|
|
967
|
+
}
|
|
968
|
+
async #migrateLegacyState(key, value) {
|
|
969
|
+
if (isCurrentCircuitBreakerState(value)) return;
|
|
970
|
+
const state = parseCircuitBreakerKvState(value);
|
|
971
|
+
if (state != null) {
|
|
972
|
+
await this.#kv.cas(key, value, markCircuitBreakerState(state), this.#setOptions());
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
await this.#deleteIfUnchanged(key, value);
|
|
976
|
+
}
|
|
977
|
+
async #deleteIfUnchanged(key, value) {
|
|
978
|
+
if (await this.#kv.cas(key, value, LEGACY_SWEEP_DELETING_MARKER, { ttl: LEGACY_SWEEP_LOCK_TTL })) await this.#kv.delete(key);
|
|
979
|
+
}
|
|
980
|
+
async #acquireLegacySweep(markerKey) {
|
|
981
|
+
for (let attempt = 0; attempt < LEGACY_SWEEP_CAS_ATTEMPTS; attempt++) {
|
|
982
|
+
const marker = await this.#kv.get(markerKey);
|
|
983
|
+
const now = this.#now();
|
|
984
|
+
if (isLegacySweepActive(marker, now)) {
|
|
985
|
+
this.#rememberLegacySweepMarker(marker);
|
|
986
|
+
return "done";
|
|
987
|
+
}
|
|
988
|
+
const retryUntil = isLegacySweepRetryDue(marker, now) ? marker.retryUntil : getLegacySweepRetryUntil(marker) ?? now.add(LEGACY_SWEEP_RETRY_WINDOW).toString();
|
|
989
|
+
const sweeping = {
|
|
990
|
+
state: "sweeping",
|
|
991
|
+
started: now.toString(),
|
|
992
|
+
retryUntil
|
|
993
|
+
};
|
|
994
|
+
if (await this.#kv.cas(markerKey, marker ?? void 0, sweeping, { ttl: LEGACY_SWEEP_LOCK_TTL })) return sweeping;
|
|
995
|
+
await delay(LEGACY_SWEEP_WAIT_INTERVAL);
|
|
996
|
+
}
|
|
997
|
+
return "done";
|
|
998
|
+
}
|
|
999
|
+
#isLegacySweepDone() {
|
|
1000
|
+
if (this.#legacySweepDone === "final") return true;
|
|
1001
|
+
if (this.#legacySweepDone == null) return false;
|
|
1002
|
+
if (Temporal$9.Instant.compare(this.#now(), this.#legacySweepDone) < 0) return true;
|
|
1003
|
+
this.#legacySweepDone = void 0;
|
|
1004
|
+
return false;
|
|
1005
|
+
}
|
|
1006
|
+
#rememberLegacySweepMarker(marker) {
|
|
1007
|
+
const doneUntil = getLegacySweepDoneUntil(marker, this.#now());
|
|
1008
|
+
if (doneUntil != null) this.#legacySweepDone = doneUntil;
|
|
1009
|
+
}
|
|
906
1010
|
#capHeldRetryAt(now, heldSince, retryAt) {
|
|
907
1011
|
const expiresAt = (heldSince ?? now).add(this.#options.heldActivityTtl);
|
|
908
|
-
return Temporal$
|
|
1012
|
+
return Temporal$9.Instant.compare(expiresAt, retryAt) < 0 ? expiresAt : retryAt;
|
|
909
1013
|
}
|
|
910
1014
|
async #get(remoteHost) {
|
|
911
|
-
return
|
|
1015
|
+
return parseStoredCircuitBreakerKvState(await this.#kv.get(this.#key(remoteHost)));
|
|
912
1016
|
}
|
|
913
1017
|
async #replace(remoteHost, oldState, newState) {
|
|
914
1018
|
const key = this.#key(remoteHost);
|
|
1019
|
+
const storedState = newState == null ? void 0 : markCircuitBreakerState(newState);
|
|
915
1020
|
if (this.#kv.cas == null) {
|
|
916
|
-
if (
|
|
917
|
-
else await this.#kv.set(key,
|
|
1021
|
+
if (storedState == null) await this.#kv.delete(key);
|
|
1022
|
+
else await this.#kv.set(key, storedState, this.#setOptions());
|
|
918
1023
|
return true;
|
|
919
1024
|
}
|
|
920
|
-
return await this.#kv.cas(key, oldState,
|
|
1025
|
+
return await this.#kv.cas(key, oldState, storedState, storedState == null ? void 0 : this.#setOptions());
|
|
1026
|
+
}
|
|
1027
|
+
#setOptions() {
|
|
1028
|
+
return this.#options.stateTtl == null ? void 0 : { ttl: this.#options.stateTtl };
|
|
921
1029
|
}
|
|
922
1030
|
async #notifyStateChange(remoteHost, previousState, newState) {
|
|
923
1031
|
try {
|
|
@@ -957,18 +1065,25 @@ var CircuitBreaker = class {
|
|
|
957
1065
|
* @param options The public circuit breaker options supplied to Fedify.
|
|
958
1066
|
* @returns The normalized failure predicate, failure pruning function,
|
|
959
1067
|
* duration values, and optional callbacks with defaults applied.
|
|
960
|
-
* @throws {RangeError} If any configured duration is not positive
|
|
1068
|
+
* @throws {RangeError} If any configured duration is not positive, or if
|
|
1069
|
+
* `stateTtl` is shorter than `recoveryDelay`.
|
|
961
1070
|
* @throws {TypeError} If `failureThreshold` is not a positive integer.
|
|
962
1071
|
*/
|
|
963
1072
|
function normalizeCircuitBreakerOptions(options) {
|
|
964
1073
|
const recoveryDelay = toInstantDuration(options.recoveryDelay ?? { minutes: 30 });
|
|
965
1074
|
const heldActivityTtl = toInstantDuration(options.heldActivityTtl ?? { hours: 168 });
|
|
966
1075
|
const releaseInterval = toInstantDuration(options.releaseInterval ?? { seconds: 1 });
|
|
1076
|
+
const configuredStateTtl = options.stateTtl == null ? void 0 : toInstantDuration(options.stateTtl);
|
|
967
1077
|
assertPositiveDuration(recoveryDelay, "recoveryDelay");
|
|
968
1078
|
assertPositiveDuration(heldActivityTtl, "heldActivityTtl");
|
|
969
1079
|
assertPositiveDuration(releaseInterval, "releaseInterval");
|
|
1080
|
+
if (configuredStateTtl != null) {
|
|
1081
|
+
assertPositiveDuration(configuredStateTtl, "stateTtl");
|
|
1082
|
+
if (Temporal$9.Duration.compare(configuredStateTtl, recoveryDelay) < 0) throw new RangeError("stateTtl must be at least recoveryDelay.");
|
|
1083
|
+
}
|
|
970
1084
|
let failure;
|
|
971
1085
|
let pruneFailures;
|
|
1086
|
+
let stateTtl;
|
|
972
1087
|
if (options.failure == null) {
|
|
973
1088
|
const failureThreshold = options.failureThreshold ?? 5;
|
|
974
1089
|
if (!Number.isInteger(failureThreshold) || failureThreshold <= 0) throw new TypeError("failureThreshold must be a positive integer.");
|
|
@@ -976,17 +1091,19 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
976
1091
|
assertPositiveDuration(failureWindow, "failureWindow");
|
|
977
1092
|
pruneFailures = (timestamps, now) => {
|
|
978
1093
|
const earliest = now.subtract(failureWindow);
|
|
979
|
-
return timestamps.filter((timestamp) => Temporal$
|
|
1094
|
+
return timestamps.filter((timestamp) => Temporal$9.Instant.compare(timestamp, earliest) >= 0).slice(-failureThreshold);
|
|
980
1095
|
};
|
|
981
1096
|
failure = (timestamps) => {
|
|
982
1097
|
if (timestamps.length < failureThreshold) return false;
|
|
983
1098
|
const first = timestamps[timestamps.length - failureThreshold];
|
|
984
1099
|
const last = timestamps[timestamps.length - 1];
|
|
985
|
-
return Temporal$
|
|
1100
|
+
return Temporal$9.Duration.compare(first.until(last), failureWindow) <= 0;
|
|
986
1101
|
};
|
|
1102
|
+
stateTtl = configuredStateTtl ?? maxDuration(recoveryDelay.add(failureWindow), recoveryDelay.add(heldActivityTtl));
|
|
987
1103
|
} else {
|
|
988
1104
|
failure = options.failure;
|
|
989
|
-
pruneFailures = (timestamps) => timestamps.slice(-
|
|
1105
|
+
pruneFailures = (timestamps) => timestamps.slice(-100);
|
|
1106
|
+
stateTtl = configuredStateTtl;
|
|
990
1107
|
}
|
|
991
1108
|
return {
|
|
992
1109
|
failure,
|
|
@@ -994,24 +1111,86 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
994
1111
|
recoveryDelay,
|
|
995
1112
|
heldActivityTtl,
|
|
996
1113
|
releaseInterval,
|
|
1114
|
+
stateTtl,
|
|
997
1115
|
onStateChange: options.onStateChange,
|
|
998
1116
|
onActivityDrop: options.onActivityDrop
|
|
999
1117
|
};
|
|
1000
1118
|
}
|
|
1119
|
+
function maxDuration(duration, ...durations) {
|
|
1120
|
+
return durations.reduce((max, candidate) => Temporal$9.Duration.compare(candidate, max) > 0 ? candidate : max, duration);
|
|
1121
|
+
}
|
|
1122
|
+
function isLegacySweepDone(value, now) {
|
|
1123
|
+
return getLegacySweepDoneUntil(value, now) != null;
|
|
1124
|
+
}
|
|
1125
|
+
function getLegacySweepDoneUntil(value, now) {
|
|
1126
|
+
if (typeof value !== "object" || value == null || !("state" in value)) return;
|
|
1127
|
+
if (value.state === "final") return "final";
|
|
1128
|
+
if (value.state === "done" && "retryUntil" in value && typeof value.retryUntil === "string") try {
|
|
1129
|
+
const retryUntil = Temporal$9.Instant.from(value.retryUntil);
|
|
1130
|
+
return Temporal$9.Instant.compare(now, retryUntil) < 0 ? retryUntil : void 0;
|
|
1131
|
+
} catch {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
function isLegacySweepRetryDue(value, now) {
|
|
1136
|
+
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "done" || !("retryUntil" in value) || typeof value.retryUntil !== "string") return false;
|
|
1137
|
+
try {
|
|
1138
|
+
return Temporal$9.Instant.compare(now, Temporal$9.Instant.from(value.retryUntil)) >= 0;
|
|
1139
|
+
} catch {
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function isLegacySweepActive(value, now) {
|
|
1144
|
+
return isLegacySweepDone(value, now) || isLegacySweepInProgress(value, now);
|
|
1145
|
+
}
|
|
1146
|
+
function isLegacySweepInProgress(value, now) {
|
|
1147
|
+
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "sweeping" || !("started" in value) || typeof value.started !== "string") return false;
|
|
1148
|
+
try {
|
|
1149
|
+
return Temporal$9.Instant.compare(now, Temporal$9.Instant.from(value.started).add(LEGACY_SWEEP_LOCK_TTL)) < 0;
|
|
1150
|
+
} catch {
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
function getLegacySweepRetryUntil(value) {
|
|
1155
|
+
if (typeof value !== "object" || value == null || !("retryUntil" in value) || typeof value.retryUntil !== "string") return;
|
|
1156
|
+
try {
|
|
1157
|
+
Temporal$9.Instant.from(value.retryUntil);
|
|
1158
|
+
return value.retryUntil;
|
|
1159
|
+
} catch {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
function isCurrentCircuitBreakerState(value) {
|
|
1164
|
+
return typeof value === "object" && value != null && "__fedifyCircuitBreakerStateVersion" in value && value.__fedifyCircuitBreakerStateVersion === CIRCUIT_BREAKER_STATE_VERSION;
|
|
1165
|
+
}
|
|
1166
|
+
function markCircuitBreakerState(state) {
|
|
1167
|
+
return {
|
|
1168
|
+
...state,
|
|
1169
|
+
__fedifyCircuitBreakerStateVersion: CIRCUIT_BREAKER_STATE_VERSION
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function stripStoredCircuitBreakerState(state) {
|
|
1173
|
+
if (state == null) return void 0;
|
|
1174
|
+
const { __fedifyCircuitBreakerStateVersion: _, ...publicState } = state;
|
|
1175
|
+
return publicState;
|
|
1176
|
+
}
|
|
1177
|
+
function delay(milliseconds) {
|
|
1178
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1179
|
+
}
|
|
1001
1180
|
function toInstantDuration(duration) {
|
|
1002
|
-
const parsed = Temporal$
|
|
1003
|
-
return Temporal$
|
|
1181
|
+
const parsed = Temporal$9.Duration.from(duration);
|
|
1182
|
+
return Temporal$9.Duration.from({ milliseconds: Math.trunc(parsed.total({
|
|
1004
1183
|
unit: "millisecond",
|
|
1005
|
-
relativeTo: Temporal$
|
|
1184
|
+
relativeTo: Temporal$9.PlainDateTime.from("2026-01-01T00:00:00")
|
|
1006
1185
|
})) });
|
|
1007
1186
|
}
|
|
1008
1187
|
function assertPositiveDuration(duration, name) {
|
|
1009
|
-
if (Temporal$
|
|
1188
|
+
if (Temporal$9.Duration.compare(duration, { seconds: 0 }) <= 0) throw new RangeError(`${name} must be a positive duration.`);
|
|
1010
1189
|
}
|
|
1011
1190
|
function parseHeldSince(value) {
|
|
1012
1191
|
if (value == null) return void 0;
|
|
1013
1192
|
try {
|
|
1014
|
-
return Temporal$
|
|
1193
|
+
return Temporal$9.Instant.from(value);
|
|
1015
1194
|
} catch (error) {
|
|
1016
1195
|
(0, _logtape_logtape.getLogger)([
|
|
1017
1196
|
"fedify",
|
|
@@ -1032,10 +1211,13 @@ function parseHeldSince(value) {
|
|
|
1032
1211
|
* valid instant strings, or `undefined` when the stored value is malformed.
|
|
1033
1212
|
*/
|
|
1034
1213
|
function parseCircuitBreakerKvState(value) {
|
|
1214
|
+
return stripStoredCircuitBreakerState(parseStoredCircuitBreakerKvState(value));
|
|
1215
|
+
}
|
|
1216
|
+
function parseStoredCircuitBreakerKvState(value) {
|
|
1035
1217
|
const isInstantString = (v) => {
|
|
1036
1218
|
if (typeof v !== "string") return false;
|
|
1037
1219
|
try {
|
|
1038
|
-
Temporal$
|
|
1220
|
+
Temporal$9.Instant.from(v);
|
|
1039
1221
|
return true;
|
|
1040
1222
|
} catch {
|
|
1041
1223
|
return false;
|
|
@@ -1051,7 +1233,8 @@ function parseCircuitBreakerKvState(value) {
|
|
|
1051
1233
|
state: record.state,
|
|
1052
1234
|
failures: record.failures,
|
|
1053
1235
|
...record.opened == null ? {} : { opened: record.opened },
|
|
1054
|
-
...record.halfOpened == null ? {} : { halfOpened: record.halfOpened }
|
|
1236
|
+
...record.halfOpened == null ? {} : { halfOpened: record.halfOpened },
|
|
1237
|
+
...record.__fedifyCircuitBreakerStateVersion === CIRCUIT_BREAKER_STATE_VERSION ? { __fedifyCircuitBreakerStateVersion: CIRCUIT_BREAKER_STATE_VERSION } : {}
|
|
1055
1238
|
};
|
|
1056
1239
|
}
|
|
1057
1240
|
//#endregion
|
|
@@ -1066,7 +1249,7 @@ function parseCircuitBreakerKvState(value) {
|
|
|
1066
1249
|
async function digest(uris) {
|
|
1067
1250
|
const processed = /* @__PURE__ */ new Set();
|
|
1068
1251
|
const encoder = new TextEncoder();
|
|
1069
|
-
const result = new Uint8Array(32);
|
|
1252
|
+
const result = /* @__PURE__ */ new Uint8Array(32);
|
|
1070
1253
|
for (const uri of uris) {
|
|
1071
1254
|
const u = uri instanceof URL ? uri.href : uri;
|
|
1072
1255
|
if (processed.has(u)) continue;
|
|
@@ -1095,7 +1278,7 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
1095
1278
|
}
|
|
1096
1279
|
//#endregion
|
|
1097
1280
|
//#region src/federation/inbox.ts
|
|
1098
|
-
const Temporal$
|
|
1281
|
+
const Temporal$8 = require_http.Temporal;
|
|
1099
1282
|
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
1100
1283
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
1101
1284
|
"fedify",
|
|
@@ -1242,7 +1425,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
1242
1425
|
return "error";
|
|
1243
1426
|
}
|
|
1244
1427
|
require_http.recordInboxActivity(meterProvider, "processed", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
1245
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$
|
|
1428
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$8.Duration.from({ days: 1 }) });
|
|
1246
1429
|
logger.info("Activity {activityId} has been processed.", {
|
|
1247
1430
|
activityId: activity.id?.href,
|
|
1248
1431
|
activity: json,
|
|
@@ -1254,7 +1437,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
1254
1437
|
}
|
|
1255
1438
|
//#endregion
|
|
1256
1439
|
//#region src/federation/keycache.ts
|
|
1257
|
-
const Temporal$
|
|
1440
|
+
const Temporal$7 = require_http.Temporal;
|
|
1258
1441
|
var KvKeyCache = class {
|
|
1259
1442
|
kv;
|
|
1260
1443
|
prefix;
|
|
@@ -1265,7 +1448,7 @@ var KvKeyCache = class {
|
|
|
1265
1448
|
this.kv = kv;
|
|
1266
1449
|
this.prefix = prefix;
|
|
1267
1450
|
this.options = options;
|
|
1268
|
-
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$
|
|
1451
|
+
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$7.Duration.from({ minutes: 10 });
|
|
1269
1452
|
this.nullKeys = /* @__PURE__ */ new Map();
|
|
1270
1453
|
}
|
|
1271
1454
|
#getFetchErrorKey(keyId) {
|
|
@@ -1278,13 +1461,13 @@ var KvKeyCache = class {
|
|
|
1278
1461
|
async get(keyId) {
|
|
1279
1462
|
const negativeExpiration = this.nullKeys.get(keyId.href);
|
|
1280
1463
|
if (negativeExpiration != null) {
|
|
1281
|
-
if (Temporal$
|
|
1464
|
+
if (Temporal$7.Now.instant().until(negativeExpiration).sign >= 0) return null;
|
|
1282
1465
|
this.nullKeys.delete(keyId.href);
|
|
1283
1466
|
}
|
|
1284
1467
|
const serialized = await this.kv.get([...this.prefix, keyId.href]);
|
|
1285
1468
|
if (serialized === void 0) return void 0;
|
|
1286
1469
|
if (serialized === null) {
|
|
1287
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
1470
|
+
this.nullKeys.set(keyId.href, Temporal$7.Now.instant().add(this.unavailableKeyTtl));
|
|
1288
1471
|
return null;
|
|
1289
1472
|
}
|
|
1290
1473
|
try {
|
|
@@ -1300,7 +1483,7 @@ var KvKeyCache = class {
|
|
|
1300
1483
|
}
|
|
1301
1484
|
async set(keyId, key) {
|
|
1302
1485
|
if (key == null) {
|
|
1303
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
1486
|
+
this.nullKeys.set(keyId.href, Temporal$7.Now.instant().add(this.unavailableKeyTtl));
|
|
1304
1487
|
await this.kv.set([...this.prefix, keyId.href], null, { ttl: this.unavailableKeyTtl });
|
|
1305
1488
|
return;
|
|
1306
1489
|
}
|
|
@@ -1439,7 +1622,7 @@ function acceptsJsonLd(request) {
|
|
|
1439
1622
|
}
|
|
1440
1623
|
//#endregion
|
|
1441
1624
|
//#region src/federation/temporal.ts
|
|
1442
|
-
const Temporal$
|
|
1625
|
+
const Temporal$6 = require_http.Temporal;
|
|
1443
1626
|
function isPlainObject$1(value) {
|
|
1444
1627
|
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1445
1628
|
}
|
|
@@ -1449,7 +1632,7 @@ function normalizeDateTimeLiteral(value) {
|
|
|
1449
1632
|
function isMalformedDateTimeLiteral(value) {
|
|
1450
1633
|
if (typeof value !== "string") return false;
|
|
1451
1634
|
try {
|
|
1452
|
-
Temporal$
|
|
1635
|
+
Temporal$6.Instant.from(normalizeDateTimeLiteral(value));
|
|
1453
1636
|
return false;
|
|
1454
1637
|
} catch {
|
|
1455
1638
|
return true;
|
|
@@ -1458,13 +1641,13 @@ function isMalformedDateTimeLiteral(value) {
|
|
|
1458
1641
|
function isMalformedDurationLiteral(value) {
|
|
1459
1642
|
if (typeof value !== "string") return false;
|
|
1460
1643
|
try {
|
|
1461
|
-
Temporal$
|
|
1644
|
+
Temporal$6.Duration.from(value);
|
|
1462
1645
|
return false;
|
|
1463
1646
|
} catch {
|
|
1464
1647
|
return true;
|
|
1465
1648
|
}
|
|
1466
1649
|
}
|
|
1467
|
-
const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
1650
|
+
const TEMPORAL_DATE_TIME_IRIS = /* @__PURE__ */ new Set([
|
|
1468
1651
|
"https://www.w3.org/ns/activitystreams#deleted",
|
|
1469
1652
|
"https://www.w3.org/ns/activitystreams#endTime",
|
|
1470
1653
|
"https://www.w3.org/ns/activitystreams#published",
|
|
@@ -1473,7 +1656,7 @@ const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
|
1473
1656
|
"http://purl.org/dc/terms/created",
|
|
1474
1657
|
"https://w3id.org/security#created"
|
|
1475
1658
|
]);
|
|
1476
|
-
const TEMPORAL_DURATION_IRIS = new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
1659
|
+
const TEMPORAL_DURATION_IRIS = /* @__PURE__ */ new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
1477
1660
|
const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
|
|
1478
1661
|
const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
|
|
1479
1662
|
function hasMalformedExpandedDateTimeLiteral(value) {
|
|
@@ -1529,7 +1712,7 @@ async function hasMalformedKnownTemporalLiteral(value, contextLoader) {
|
|
|
1529
1712
|
}
|
|
1530
1713
|
//#endregion
|
|
1531
1714
|
//#region src/federation/handler.ts
|
|
1532
|
-
const Temporal$
|
|
1715
|
+
const Temporal$5 = require_http.Temporal;
|
|
1533
1716
|
const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
|
|
1534
1717
|
function isRemoteContextLoadingFailure$1(error) {
|
|
1535
1718
|
return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
|
|
@@ -1618,7 +1801,7 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
1618
1801
|
Vary: "Accept"
|
|
1619
1802
|
} });
|
|
1620
1803
|
}
|
|
1621
|
-
const BUILT_IN_COLLECTION_METRIC_KINDS = new Set([
|
|
1804
|
+
const BUILT_IN_COLLECTION_METRIC_KINDS = /* @__PURE__ */ new Set([
|
|
1622
1805
|
"inbox",
|
|
1623
1806
|
"outbox",
|
|
1624
1807
|
"following",
|
|
@@ -1655,13 +1838,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
1655
1838
|
* @param parameters The parameters for handling the collection.
|
|
1656
1839
|
* @returns A promise that resolves to an HTTP response.
|
|
1657
1840
|
*/
|
|
1658
|
-
async function handleCollection(request, { name: name$
|
|
1659
|
-
const spanName = name$
|
|
1841
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
1842
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
1660
1843
|
tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
1661
1844
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
|
|
1662
1845
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
1663
1846
|
const metricBase = {
|
|
1664
|
-
kind: getCollectionMetricKind(name$
|
|
1847
|
+
kind: getCollectionMetricKind(name$2),
|
|
1665
1848
|
page: cursor != null,
|
|
1666
1849
|
dispatcher: "built_in"
|
|
1667
1850
|
};
|
|
@@ -1702,7 +1885,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
1702
1885
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
1703
1886
|
return await onNotFound(request);
|
|
1704
1887
|
}
|
|
1705
|
-
const items = filterCollectionItems(page.items, name$
|
|
1888
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
1706
1889
|
itemCount = items.length;
|
|
1707
1890
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
1708
1891
|
return items;
|
|
@@ -1742,7 +1925,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
1742
1925
|
} else {
|
|
1743
1926
|
const uri = new URL(baseUri);
|
|
1744
1927
|
uri.searchParams.set("cursor", cursor);
|
|
1745
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
1928
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
1746
1929
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
1747
1930
|
attributes: {
|
|
1748
1931
|
"activitypub.collection.id": uri.href,
|
|
@@ -1758,7 +1941,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
1758
1941
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
1759
1942
|
return await onNotFound(request);
|
|
1760
1943
|
}
|
|
1761
|
-
const items = filterCollectionItems(page.items, name$
|
|
1944
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
1762
1945
|
itemCount = items.length;
|
|
1763
1946
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
1764
1947
|
return {
|
|
@@ -2813,8 +2996,8 @@ var CustomCollectionHandler = class {
|
|
|
2813
2996
|
* @param CollectionPage The CollectionPage constructor.
|
|
2814
2997
|
* @param filterPredicate Optional filter predicate for items.
|
|
2815
2998
|
*/
|
|
2816
|
-
constructor(name$
|
|
2817
|
-
this.name = name$
|
|
2999
|
+
constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
|
|
3000
|
+
this.name = name$1;
|
|
2818
3001
|
this.values = values;
|
|
2819
3002
|
this.context = context;
|
|
2820
3003
|
this.callbacks = callbacks;
|
|
@@ -3229,7 +3412,7 @@ async function respondWithObjectIfAcceptable(object, request, options) {
|
|
|
3229
3412
|
return response;
|
|
3230
3413
|
}
|
|
3231
3414
|
function generateNonce() {
|
|
3232
|
-
const bytes = new Uint8Array(16);
|
|
3415
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
3233
3416
|
crypto.getRandomValues(bytes);
|
|
3234
3417
|
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3235
3418
|
}
|
|
@@ -3284,7 +3467,7 @@ async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
|
|
|
3284
3467
|
}
|
|
3285
3468
|
async function setKey(kv, key, policy) {
|
|
3286
3469
|
const seconds = policy.nonceTtlSeconds ?? 300;
|
|
3287
|
-
const ttl = Temporal$
|
|
3470
|
+
const ttl = Temporal$5.Duration.from({ seconds });
|
|
3288
3471
|
await kv.set(key, true, { ttl });
|
|
3289
3472
|
}
|
|
3290
3473
|
const DEF_COMPONENTS = [
|
|
@@ -3299,6 +3482,94 @@ const MIN_COMPONENTS = [
|
|
|
3299
3482
|
"@authority"
|
|
3300
3483
|
];
|
|
3301
3484
|
//#endregion
|
|
3485
|
+
//#region src/federation/kv.ts
|
|
3486
|
+
const Temporal$4 = require_http.Temporal;
|
|
3487
|
+
/**
|
|
3488
|
+
* A key–value store that stores values in memory.
|
|
3489
|
+
* Do not use this in production as it does not persist values.
|
|
3490
|
+
*
|
|
3491
|
+
* @since 0.5.0
|
|
3492
|
+
*/
|
|
3493
|
+
var MemoryKvStore = class {
|
|
3494
|
+
#values = {};
|
|
3495
|
+
#encodeKey(key) {
|
|
3496
|
+
return JSON.stringify(key);
|
|
3497
|
+
}
|
|
3498
|
+
/**
|
|
3499
|
+
* {@inheritDoc KvStore.get}
|
|
3500
|
+
*/
|
|
3501
|
+
get(key) {
|
|
3502
|
+
const encodedKey = this.#encodeKey(key);
|
|
3503
|
+
const entry = this.#values[encodedKey];
|
|
3504
|
+
if (entry == null) return Promise.resolve(void 0);
|
|
3505
|
+
const [value, expiration] = entry;
|
|
3506
|
+
if (expiration != null && Temporal$4.Now.instant().until(expiration).sign < 0) {
|
|
3507
|
+
delete this.#values[encodedKey];
|
|
3508
|
+
return Promise.resolve(void 0);
|
|
3509
|
+
}
|
|
3510
|
+
return Promise.resolve(value);
|
|
3511
|
+
}
|
|
3512
|
+
/**
|
|
3513
|
+
* {@inheritDoc KvStore.set}
|
|
3514
|
+
*/
|
|
3515
|
+
set(key, value, options) {
|
|
3516
|
+
const encodedKey = this.#encodeKey(key);
|
|
3517
|
+
const expiration = options?.ttl == null ? null : Temporal$4.Now.instant().add(options.ttl.round({ largestUnit: "hour" }));
|
|
3518
|
+
this.#values[encodedKey] = [value, expiration];
|
|
3519
|
+
return Promise.resolve();
|
|
3520
|
+
}
|
|
3521
|
+
/**
|
|
3522
|
+
* {@inheritDoc KvStore.delete}
|
|
3523
|
+
*/
|
|
3524
|
+
delete(key) {
|
|
3525
|
+
const encodedKey = this.#encodeKey(key);
|
|
3526
|
+
delete this.#values[encodedKey];
|
|
3527
|
+
return Promise.resolve();
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3530
|
+
* {@inheritDoc KvStore.cas}
|
|
3531
|
+
*/
|
|
3532
|
+
cas(key, expectedValue, newValue, options) {
|
|
3533
|
+
const encodedKey = this.#encodeKey(key);
|
|
3534
|
+
const entry = this.#values[encodedKey];
|
|
3535
|
+
let currentValue;
|
|
3536
|
+
if (entry == null) currentValue = void 0;
|
|
3537
|
+
else {
|
|
3538
|
+
const [value, expiration] = entry;
|
|
3539
|
+
if (expiration != null && Temporal$4.Now.instant().until(expiration).sign < 0) {
|
|
3540
|
+
delete this.#values[encodedKey];
|
|
3541
|
+
currentValue = void 0;
|
|
3542
|
+
} else currentValue = value;
|
|
3543
|
+
}
|
|
3544
|
+
if (!(0, es_toolkit.isEqual)(currentValue, expectedValue)) return Promise.resolve(false);
|
|
3545
|
+
const expiration = options?.ttl == null ? null : Temporal$4.Now.instant().add(options.ttl.round({ largestUnit: "hour" }));
|
|
3546
|
+
this.#values[encodedKey] = [newValue, expiration];
|
|
3547
|
+
return Promise.resolve(true);
|
|
3548
|
+
}
|
|
3549
|
+
/**
|
|
3550
|
+
* {@inheritDoc KvStore.list}
|
|
3551
|
+
*/
|
|
3552
|
+
async *list(prefix) {
|
|
3553
|
+
const now = Temporal$4.Now.instant();
|
|
3554
|
+
for (const [encodedKey, entry] of Object.entries(this.#values)) {
|
|
3555
|
+
const key = JSON.parse(encodedKey);
|
|
3556
|
+
if (prefix != null) {
|
|
3557
|
+
if (key.length < prefix.length) continue;
|
|
3558
|
+
if (!prefix.every((p, i) => key[i] === p)) continue;
|
|
3559
|
+
}
|
|
3560
|
+
const [value, expiration] = entry;
|
|
3561
|
+
if (expiration != null && now.until(expiration).sign < 0) {
|
|
3562
|
+
delete this.#values[encodedKey];
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
yield {
|
|
3566
|
+
key,
|
|
3567
|
+
value
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
};
|
|
3572
|
+
//#endregion
|
|
3302
3573
|
//#region src/nodeinfo/handler.ts
|
|
3303
3574
|
/**
|
|
3304
3575
|
* Handles a NodeInfo request. You would not typically call this function
|
|
@@ -3759,7 +4030,7 @@ function serializeDataPointType(dataPointType) {
|
|
|
3759
4030
|
}
|
|
3760
4031
|
//#endregion
|
|
3761
4032
|
//#region src/federation/retry.ts
|
|
3762
|
-
const Temporal$
|
|
4033
|
+
const Temporal$3 = require_http.Temporal;
|
|
3763
4034
|
/**
|
|
3764
4035
|
* Creates an exponential backoff retry policy. The delay between retries
|
|
3765
4036
|
* starts at the `initialDelay` and is multiplied by the `factor` for each
|
|
@@ -3771,8 +4042,8 @@ const Temporal$2 = require_temporal_polyfill.Temporal;
|
|
|
3771
4042
|
* @since 0.12.0
|
|
3772
4043
|
*/
|
|
3773
4044
|
function createExponentialBackoffPolicy(options = {}) {
|
|
3774
|
-
const initialDelay = Temporal$
|
|
3775
|
-
const maxDelay = Temporal$
|
|
4045
|
+
const initialDelay = Temporal$3.Duration.from(options.initialDelay ?? { seconds: 1 });
|
|
4046
|
+
const maxDelay = Temporal$3.Duration.from(options.maxDelay ?? { hours: 12 });
|
|
3776
4047
|
const maxAttempts = options.maxAttempts ?? 10;
|
|
3777
4048
|
const factor = options.factor ?? 2;
|
|
3778
4049
|
const jitter = options.jitter ?? true;
|
|
@@ -3784,8 +4055,8 @@ function createExponentialBackoffPolicy(options = {}) {
|
|
|
3784
4055
|
milliseconds *= 1 + Math.random();
|
|
3785
4056
|
milliseconds = Math.round(milliseconds);
|
|
3786
4057
|
}
|
|
3787
|
-
const delay = Temporal$
|
|
3788
|
-
return Temporal$
|
|
4058
|
+
const delay = Temporal$3.Duration.from({ milliseconds });
|
|
4059
|
+
return Temporal$3.Duration.compare(delay, maxDelay) > 0 ? maxDelay : delay;
|
|
3789
4060
|
};
|
|
3790
4061
|
}
|
|
3791
4062
|
//#endregion
|
|
@@ -3916,7 +4187,7 @@ const classReviver = (filter, init, set) => (seen, revive, node) => {
|
|
|
3916
4187
|
*
|
|
3917
4188
|
* @module
|
|
3918
4189
|
*/
|
|
3919
|
-
const Temporal$
|
|
4190
|
+
const Temporal$2 = require_http.Temporal;
|
|
3920
4191
|
/**
|
|
3921
4192
|
* Validates the task handle, plans deduplication, encodes every payload, then
|
|
3922
4193
|
* dispatches the resulting messages to the queue. A single item flows through
|
|
@@ -3954,7 +4225,7 @@ const enqueueTasks = (ctx) => async function(task, items, options) {
|
|
|
3954
4225
|
if (claim.rollback != null) try {
|
|
3955
4226
|
await claim.rollback();
|
|
3956
4227
|
} catch (rollbackError) {
|
|
3957
|
-
logger$
|
|
4228
|
+
logger$2.warn("Failed to roll back the deduplication marker for task {taskName} after a failed enqueue; it will expire by TTL. {rollbackError}", {
|
|
3958
4229
|
taskName: task.name,
|
|
3959
4230
|
rollbackError
|
|
3960
4231
|
});
|
|
@@ -3962,7 +4233,7 @@ const enqueueTasks = (ctx) => async function(task, items, options) {
|
|
|
3962
4233
|
throw error;
|
|
3963
4234
|
}
|
|
3964
4235
|
};
|
|
3965
|
-
const getDurationIfDefined = (item) => item == null ? void 0 : Temporal$
|
|
4236
|
+
const getDurationIfDefined = (item) => item == null ? void 0 : Temporal$2.Duration.from(item);
|
|
3966
4237
|
/**
|
|
3967
4238
|
* Decides how a `deduplicationKey` (if any) is honored: forwarded to a native
|
|
3968
4239
|
* queue, claimed via `cas`, or—when neither is available—dropped or rejected
|
|
@@ -4013,7 +4284,7 @@ async function claimDeduplication(ctx, plan, taskName) {
|
|
|
4013
4284
|
}
|
|
4014
4285
|
};
|
|
4015
4286
|
}
|
|
4016
|
-
case "open": logger$
|
|
4287
|
+
case "open": logger$2.debug("deduplicationKey {deduplicationKey} for task {taskName} ignored: the message queue declares no nativeDeduplication and the key-value store has no cas; proceeding (taskDeduplicationFallback is \"open\").", {
|
|
4017
4288
|
deduplicationKey: plan.key,
|
|
4018
4289
|
taskName
|
|
4019
4290
|
});
|
|
@@ -4068,13 +4339,13 @@ const encodeTaskMessage = (codec, origin, task, options) => async (data) => {
|
|
|
4068
4339
|
baseUrl: origin,
|
|
4069
4340
|
taskName: task.name,
|
|
4070
4341
|
data: encoded,
|
|
4071
|
-
started: Temporal$
|
|
4342
|
+
started: Temporal$2.Now.instant().toString(),
|
|
4072
4343
|
attempt: 0,
|
|
4073
4344
|
orderingKey: options.orderingKey,
|
|
4074
4345
|
traceContext: carrier
|
|
4075
4346
|
};
|
|
4076
4347
|
};
|
|
4077
|
-
const logger$
|
|
4348
|
+
const logger$2 = (0, _logtape_logtape.getLogger)([
|
|
4078
4349
|
"fedify",
|
|
4079
4350
|
"federation",
|
|
4080
4351
|
"task"
|
|
@@ -4102,11 +4373,29 @@ const classifyTaskError = (error) => require_http.isAbortError(error) ? { outcom
|
|
|
4102
4373
|
};
|
|
4103
4374
|
//#endregion
|
|
4104
4375
|
//#region src/federation/webfinger.ts
|
|
4105
|
-
const logger = (0, _logtape_logtape.getLogger)([
|
|
4376
|
+
const logger$1 = (0, _logtape_logtape.getLogger)([
|
|
4106
4377
|
"fedify",
|
|
4107
4378
|
"webfinger",
|
|
4108
4379
|
"server"
|
|
4109
4380
|
]);
|
|
4381
|
+
function getWebFingerSubjectAndAliases({ resourceUrl, actorUri, preferredUsername, acctUsername, host, contextHost }) {
|
|
4382
|
+
const aliases = [];
|
|
4383
|
+
let subject = resourceUrl.href;
|
|
4384
|
+
if (resourceUrl.protocol != "acct:" && preferredUsername != null) {
|
|
4385
|
+
aliases.push(`acct:${preferredUsername}@${host ?? contextHost}`);
|
|
4386
|
+
if (host != null && host !== contextHost) aliases.push(`acct:${preferredUsername}@${contextHost}`);
|
|
4387
|
+
}
|
|
4388
|
+
if (resourceUrl.href !== actorUri.href) aliases.push(actorUri.href);
|
|
4389
|
+
if (resourceUrl.protocol === "acct:" && host != null && host !== contextHost && !resourceUrl.href.endsWith(`@${host}`)) {
|
|
4390
|
+
subject = `acct:${preferredUsername ?? acctUsername}@${host}`;
|
|
4391
|
+
aliases.push(resourceUrl.href);
|
|
4392
|
+
if (preferredUsername != null && preferredUsername !== "" && preferredUsername !== acctUsername) aliases.push(`acct:${preferredUsername}@${contextHost}`);
|
|
4393
|
+
}
|
|
4394
|
+
return {
|
|
4395
|
+
subject,
|
|
4396
|
+
aliases
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4110
4399
|
/**
|
|
4111
4400
|
* Handles a WebFinger request. You would not typically call this function
|
|
4112
4401
|
* directly, but instead use {@link Federation.fetch} method.
|
|
@@ -4155,7 +4444,7 @@ async function handleWebFinger(request, options) {
|
|
|
4155
4444
|
});
|
|
4156
4445
|
}
|
|
4157
4446
|
}
|
|
4158
|
-
const WEBFINGER_HANDLE_SCHEME_WHITELIST = new Set([
|
|
4447
|
+
const WEBFINGER_HANDLE_SCHEME_WHITELIST = /* @__PURE__ */ new Set([
|
|
4159
4448
|
"acct",
|
|
4160
4449
|
"http",
|
|
4161
4450
|
"https",
|
|
@@ -4184,7 +4473,7 @@ function classifyWebFingerHandleResult(response, notFoundResponse) {
|
|
|
4184
4473
|
}
|
|
4185
4474
|
async function handleWebFingerInternal(request, { context, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span, webFingerLinksDispatcher }) {
|
|
4186
4475
|
if (actorDispatcher == null) {
|
|
4187
|
-
logger.error("Actor dispatcher is not set.");
|
|
4476
|
+
logger$1.error("Actor dispatcher is not set.");
|
|
4188
4477
|
return await onNotFound(request);
|
|
4189
4478
|
}
|
|
4190
4479
|
const resource = context.url.searchParams.get("resource");
|
|
@@ -4200,17 +4489,18 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
4200
4489
|
span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
|
|
4201
4490
|
async function mapUsernameToIdentifier(username) {
|
|
4202
4491
|
if (actorHandleMapper == null) {
|
|
4203
|
-
logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
|
|
4492
|
+
logger$1.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
|
|
4204
4493
|
return username;
|
|
4205
4494
|
}
|
|
4206
4495
|
const identifier = await actorHandleMapper(context, username);
|
|
4207
4496
|
if (identifier == null) {
|
|
4208
|
-
logger.error("Actor {username} not found.", { username });
|
|
4497
|
+
logger$1.error("Actor {username} not found.", { username });
|
|
4209
4498
|
return null;
|
|
4210
4499
|
}
|
|
4211
4500
|
return identifier;
|
|
4212
4501
|
}
|
|
4213
4502
|
let identifier = null;
|
|
4503
|
+
let acctUsername = null;
|
|
4214
4504
|
const uriParsed = context.parseUri(resourceUrl);
|
|
4215
4505
|
if (uriParsed?.type != "actor") {
|
|
4216
4506
|
const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
|
|
@@ -4224,24 +4514,26 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
4224
4514
|
const normalizedHost = portMatch == null ? (0, node_url.domainToASCII)(match[2].toLowerCase()) : (0, node_url.domainToASCII)(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
|
|
4225
4515
|
if (normalizedHost != context.url.host && normalizedHost != host) return await onNotFound(request);
|
|
4226
4516
|
else {
|
|
4227
|
-
|
|
4228
|
-
|
|
4517
|
+
acctUsername = match[1];
|
|
4518
|
+
identifier = await mapUsernameToIdentifier(acctUsername);
|
|
4519
|
+
resourceUrl = new URL(`acct:${acctUsername}@${normalizedHost}`);
|
|
4229
4520
|
}
|
|
4230
4521
|
}
|
|
4231
4522
|
} else identifier = uriParsed.identifier;
|
|
4232
4523
|
if (identifier == null) return await onNotFound(request);
|
|
4233
4524
|
const actor = await actorDispatcher(context, identifier);
|
|
4234
4525
|
if (actor == null) {
|
|
4235
|
-
logger.error("Actor {identifier} not found.", { identifier });
|
|
4526
|
+
logger$1.error("Actor {identifier} not found.", { identifier });
|
|
4236
4527
|
return await onNotFound(request);
|
|
4237
4528
|
}
|
|
4238
4529
|
if (actor instanceof _fedify_vocab.Tombstone) return new Response(null, {
|
|
4239
4530
|
status: 410,
|
|
4240
4531
|
headers: { "Access-Control-Allow-Origin": "*" }
|
|
4241
4532
|
});
|
|
4533
|
+
const actorUri = context.getActorUri(identifier);
|
|
4242
4534
|
const links = [{
|
|
4243
4535
|
rel: "self",
|
|
4244
|
-
href:
|
|
4536
|
+
href: actorUri.href,
|
|
4245
4537
|
type: "application/activity+json"
|
|
4246
4538
|
}];
|
|
4247
4539
|
for (const url of actor.urls) if (url instanceof _fedify_vocab.Link && url.href != null) links.push({
|
|
@@ -4265,30 +4557,26 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
4265
4557
|
const customLinks = await webFingerLinksDispatcher(context, resourceUrl);
|
|
4266
4558
|
if (customLinks != null) for (const link of customLinks) links.push(link);
|
|
4267
4559
|
}
|
|
4268
|
-
const aliases =
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
}
|
|
4279
|
-
const jrd = {
|
|
4280
|
-
subject: resourceUrl.href,
|
|
4560
|
+
const { subject, aliases } = getWebFingerSubjectAndAliases({
|
|
4561
|
+
resourceUrl,
|
|
4562
|
+
actorUri,
|
|
4563
|
+
preferredUsername: actor.preferredUsername,
|
|
4564
|
+
acctUsername,
|
|
4565
|
+
host,
|
|
4566
|
+
contextHost: context.url.host
|
|
4567
|
+
});
|
|
4568
|
+
return new Response(JSON.stringify({
|
|
4569
|
+
subject,
|
|
4281
4570
|
aliases,
|
|
4282
4571
|
links
|
|
4283
|
-
}
|
|
4284
|
-
return new Response(JSON.stringify(jrd), { headers: {
|
|
4572
|
+
}), { headers: {
|
|
4285
4573
|
"Content-Type": "application/jrd+json",
|
|
4286
4574
|
"Access-Control-Allow-Origin": "*"
|
|
4287
4575
|
} });
|
|
4288
4576
|
}
|
|
4289
4577
|
//#endregion
|
|
4290
4578
|
//#region src/federation/middleware.ts
|
|
4291
|
-
var middleware_exports = /* @__PURE__ */
|
|
4579
|
+
var middleware_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
4292
4580
|
ContextImpl: () => ContextImpl,
|
|
4293
4581
|
FederationImpl: () => FederationImpl,
|
|
4294
4582
|
InboxContextImpl: () => InboxContextImpl,
|
|
@@ -4296,11 +4584,11 @@ var middleware_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
|
4296
4584
|
OutboxContextImpl: () => OutboxContextImpl,
|
|
4297
4585
|
createFederation: () => createFederation
|
|
4298
4586
|
});
|
|
4299
|
-
const Temporal =
|
|
4587
|
+
const Temporal$1 = require_http.Temporal;
|
|
4300
4588
|
const circuitBreakerCasWarningKvStores = /* @__PURE__ */ new WeakSet();
|
|
4301
4589
|
let nextQueueDepthGaugeSourceId = 0;
|
|
4302
4590
|
const retryAfterHttpDate = /* @__PURE__ */ new RegExp("^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \\d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d{4} \\d{2}:\\d{2}:\\d{2} GMT|(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), \\d{2}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\\d{2} \\d{2}:\\d{2}:\\d{2} GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?: \\d|\\d{2}) \\d{2}:\\d{2}:\\d{2} \\d{4})$");
|
|
4303
|
-
function parseRetryAfter(headers, now = Temporal.Now.instant()) {
|
|
4591
|
+
function parseRetryAfter(headers, now = Temporal$1.Now.instant()) {
|
|
4304
4592
|
const value = headers.get("Retry-After");
|
|
4305
4593
|
if (value == null) return void 0;
|
|
4306
4594
|
const trimmed = value.trim();
|
|
@@ -4318,14 +4606,14 @@ function parseRetryAfter(headers, now = Temporal.Now.instant()) {
|
|
|
4318
4606
|
}
|
|
4319
4607
|
function parseRetryAfterDuration(durationLike) {
|
|
4320
4608
|
try {
|
|
4321
|
-
return Temporal.Duration.from(durationLike);
|
|
4609
|
+
return Temporal$1.Duration.from(durationLike);
|
|
4322
4610
|
} catch (error) {
|
|
4323
4611
|
if (error instanceof RangeError) return void 0;
|
|
4324
4612
|
throw error;
|
|
4325
4613
|
}
|
|
4326
4614
|
}
|
|
4327
4615
|
function clampNegativeDelay(delay) {
|
|
4328
|
-
return delay.sign < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay;
|
|
4616
|
+
return delay.sign < 0 ? Temporal$1.Duration.from({ seconds: 0 }) : delay;
|
|
4329
4617
|
}
|
|
4330
4618
|
function getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow) {
|
|
4331
4619
|
const relaxations = [];
|
|
@@ -4341,7 +4629,7 @@ function getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow) {
|
|
|
4341
4629
|
secureDefaultSeconds: 3600
|
|
4342
4630
|
});
|
|
4343
4631
|
else try {
|
|
4344
|
-
const seconds = Temporal.Duration.from(signatureTimeWindow).total({ unit: "seconds" });
|
|
4632
|
+
const seconds = Temporal$1.Duration.from(signatureTimeWindow).total({ unit: "seconds" });
|
|
4345
4633
|
if (seconds !== 3600) relaxations.push({
|
|
4346
4634
|
protection: "http_signature_time_window",
|
|
4347
4635
|
effect: "changed",
|
|
@@ -4376,7 +4664,7 @@ function getBenchmarkTriggerOptions(benchmarkOptions) {
|
|
|
4376
4664
|
};
|
|
4377
4665
|
}
|
|
4378
4666
|
function maxDelay(first, second) {
|
|
4379
|
-
return Temporal.Duration.compare(first, second) >= 0 ? first : second;
|
|
4667
|
+
return Temporal$1.Duration.compare(first, second) >= 0 ? first : second;
|
|
4380
4668
|
}
|
|
4381
4669
|
function isTransportDeliveryError(error) {
|
|
4382
4670
|
return error instanceof _fedify_vocab_runtime.FetchError || require_http.isAbortError(error);
|
|
@@ -4616,7 +4904,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
4616
4904
|
this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
4617
4905
|
this.taskRetryPolicy = options.taskRetryPolicy ?? createExponentialBackoffPolicy();
|
|
4618
4906
|
this.taskQueueResolution = options.taskQueueResolution ?? "fallback";
|
|
4619
|
-
this.taskDeduplicationTtl = Temporal.Duration.from(options.taskDeduplicationTtl ?? { hours: 1 });
|
|
4907
|
+
this.taskDeduplicationTtl = Temporal$1.Duration.from(options.taskDeduplicationTtl ?? { hours: 1 });
|
|
4620
4908
|
this.taskDeduplicationFallback = options.taskDeduplicationFallback ?? "open";
|
|
4621
4909
|
this.activityTransformers = options.activityTransformers ?? require_transformers.getDefaultActivityTransformers();
|
|
4622
4910
|
this._tracerProvider = options.tracerProvider;
|
|
@@ -5014,7 +5302,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5014
5302
|
const getPolicyDelay = () => {
|
|
5015
5303
|
if (!policyDelayCalculated) {
|
|
5016
5304
|
retryPolicyDelay = this.outboxRetryPolicy({
|
|
5017
|
-
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
5305
|
+
elapsedTime: Temporal$1.Instant.from(message.started).until(Temporal$1.Now.instant()),
|
|
5018
5306
|
attempts: message.attempt
|
|
5019
5307
|
});
|
|
5020
5308
|
policyDelayCalculated = true;
|
|
@@ -5203,7 +5491,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5203
5491
|
throw error;
|
|
5204
5492
|
}
|
|
5205
5493
|
const delay = this.inboxRetryPolicy({
|
|
5206
|
-
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
5494
|
+
elapsedTime: Temporal$1.Instant.from(message.started).until(Temporal$1.Now.instant()),
|
|
5207
5495
|
attempts: message.attempt
|
|
5208
5496
|
});
|
|
5209
5497
|
if (delay != null) {
|
|
@@ -5362,7 +5650,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5362
5650
|
await handleRetriableFailure(error);
|
|
5363
5651
|
return;
|
|
5364
5652
|
}
|
|
5365
|
-
if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
5653
|
+
if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal$1.Duration.from({ days: 1 }) });
|
|
5366
5654
|
logger.info("Activity {activityId} has been processed.", {
|
|
5367
5655
|
activityId: activity?.id?.href,
|
|
5368
5656
|
activity: message.activity,
|
|
@@ -5416,9 +5704,9 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5416
5704
|
const queue = this.resolveTaskQueue(def.name);
|
|
5417
5705
|
if (queue?.nativeRetrial) throw error;
|
|
5418
5706
|
const retryPolicy = def.retryPolicy ?? this.taskRetryPolicy;
|
|
5419
|
-
let elapsedTime = Temporal.Duration.from({ seconds: 0 });
|
|
5707
|
+
let elapsedTime = Temporal$1.Duration.from({ seconds: 0 });
|
|
5420
5708
|
try {
|
|
5421
|
-
elapsedTime = Temporal.Instant.from(message.started).until(Temporal.Now.instant());
|
|
5709
|
+
elapsedTime = Temporal$1.Instant.from(message.started).until(Temporal$1.Now.instant());
|
|
5422
5710
|
} catch (parseError) {
|
|
5423
5711
|
logger.error("Custom task {taskName} has an unparsable started time {started}; treating elapsedTime as zero:\n{error}", {
|
|
5424
5712
|
taskName: message.taskName,
|
|
@@ -7141,12 +7429,471 @@ function getRequestId(request) {
|
|
|
7141
7429
|
return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
7142
7430
|
}
|
|
7143
7431
|
//#endregion
|
|
7432
|
+
//#region src/federation/mq.ts
|
|
7433
|
+
const Temporal = require_http.Temporal;
|
|
7434
|
+
/**
|
|
7435
|
+
* A message queue that processes messages in the same process.
|
|
7436
|
+
* Do not use this in production as it does neither persist messages nor
|
|
7437
|
+
* distribute them across multiple processes.
|
|
7438
|
+
*
|
|
7439
|
+
* @since 0.5.0
|
|
7440
|
+
*/
|
|
7441
|
+
var InProcessMessageQueue = class {
|
|
7442
|
+
#messages;
|
|
7443
|
+
#monitors;
|
|
7444
|
+
#pollIntervalMs;
|
|
7445
|
+
#delayedMessages;
|
|
7446
|
+
/**
|
|
7447
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
7448
|
+
* sequential processing for messages with the same key.
|
|
7449
|
+
*/
|
|
7450
|
+
#processingKeys;
|
|
7451
|
+
/**
|
|
7452
|
+
* In-process message queue does not provide native retry mechanisms.
|
|
7453
|
+
* @since 1.7.0
|
|
7454
|
+
*/
|
|
7455
|
+
nativeRetrial = false;
|
|
7456
|
+
/**
|
|
7457
|
+
* In-process message queue does not deduplicate messages natively.
|
|
7458
|
+
* @since 2.4.0
|
|
7459
|
+
*/
|
|
7460
|
+
nativeDeduplication = false;
|
|
7461
|
+
/**
|
|
7462
|
+
* Constructs a new {@link InProcessMessageQueue} with the given options.
|
|
7463
|
+
* @param options Additional options for the in-process message queue.
|
|
7464
|
+
*/
|
|
7465
|
+
constructor(options = {}) {
|
|
7466
|
+
this.#messages = [];
|
|
7467
|
+
this.#monitors = {};
|
|
7468
|
+
this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
7469
|
+
this.#delayedMessages = 0;
|
|
7470
|
+
this.#processingKeys = /* @__PURE__ */ new Set();
|
|
7471
|
+
}
|
|
7472
|
+
enqueue(message, options) {
|
|
7473
|
+
const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
7474
|
+
if (delay > 0) {
|
|
7475
|
+
this.#delayedMessages++;
|
|
7476
|
+
setTimeout(() => {
|
|
7477
|
+
this.#delayedMessages--;
|
|
7478
|
+
this.#enqueueReady(message, options);
|
|
7479
|
+
}, delay);
|
|
7480
|
+
return Promise.resolve();
|
|
7481
|
+
}
|
|
7482
|
+
this.#enqueueReady(message, options);
|
|
7483
|
+
return Promise.resolve();
|
|
7484
|
+
}
|
|
7485
|
+
#enqueueReady(message, options) {
|
|
7486
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
7487
|
+
this.#messages.push({
|
|
7488
|
+
message,
|
|
7489
|
+
orderingKey
|
|
7490
|
+
});
|
|
7491
|
+
this.#notifyMonitors();
|
|
7492
|
+
}
|
|
7493
|
+
#notifyMonitors() {
|
|
7494
|
+
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
7495
|
+
}
|
|
7496
|
+
enqueueMany(messages, options) {
|
|
7497
|
+
if (messages.length === 0) return Promise.resolve();
|
|
7498
|
+
const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
7499
|
+
if (delay > 0) {
|
|
7500
|
+
const delayedCount = messages.length;
|
|
7501
|
+
const deferredMessages = [...messages];
|
|
7502
|
+
this.#delayedMessages += delayedCount;
|
|
7503
|
+
setTimeout(() => {
|
|
7504
|
+
this.#delayedMessages -= delayedCount;
|
|
7505
|
+
this.#enqueueManyReady(deferredMessages, options);
|
|
7506
|
+
}, delay);
|
|
7507
|
+
return Promise.resolve();
|
|
7508
|
+
}
|
|
7509
|
+
this.#enqueueManyReady(messages, options);
|
|
7510
|
+
return Promise.resolve();
|
|
7511
|
+
}
|
|
7512
|
+
#enqueueManyReady(messages, options) {
|
|
7513
|
+
const orderingKey = options?.orderingKey ?? null;
|
|
7514
|
+
for (const message of messages) this.#messages.push({
|
|
7515
|
+
message,
|
|
7516
|
+
orderingKey
|
|
7517
|
+
});
|
|
7518
|
+
this.#notifyMonitors();
|
|
7519
|
+
}
|
|
7520
|
+
async listen(handler, options = {}) {
|
|
7521
|
+
const signal = options.signal;
|
|
7522
|
+
while (signal == null || !signal.aborted) {
|
|
7523
|
+
const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
|
|
7524
|
+
if (idx >= 0) {
|
|
7525
|
+
const { message, orderingKey } = this.#messages.splice(idx, 1)[0];
|
|
7526
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
7527
|
+
try {
|
|
7528
|
+
await handler(message);
|
|
7529
|
+
} finally {
|
|
7530
|
+
if (orderingKey != null) this.#processingKeys.delete(orderingKey);
|
|
7531
|
+
}
|
|
7532
|
+
} else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
|
|
7533
|
+
else await this.#wait(10, signal);
|
|
7534
|
+
}
|
|
7535
|
+
}
|
|
7536
|
+
getDepth() {
|
|
7537
|
+
const ready = this.#messages.length;
|
|
7538
|
+
const delayed = this.#delayedMessages;
|
|
7539
|
+
return Promise.resolve({
|
|
7540
|
+
queued: ready + delayed,
|
|
7541
|
+
ready,
|
|
7542
|
+
delayed
|
|
7543
|
+
});
|
|
7544
|
+
}
|
|
7545
|
+
#wait(ms, signal) {
|
|
7546
|
+
let timer = null;
|
|
7547
|
+
return Promise.any([new Promise((resolve) => {
|
|
7548
|
+
signal?.addEventListener("abort", () => {
|
|
7549
|
+
if (timer != null) clearTimeout(timer);
|
|
7550
|
+
resolve();
|
|
7551
|
+
}, { once: true });
|
|
7552
|
+
const monitorId = crypto.randomUUID();
|
|
7553
|
+
this.#monitors[monitorId] = () => {
|
|
7554
|
+
delete this.#monitors[monitorId];
|
|
7555
|
+
if (timer != null) clearTimeout(timer);
|
|
7556
|
+
resolve();
|
|
7557
|
+
};
|
|
7558
|
+
}), new Promise((resolve) => timer = setTimeout(resolve, ms))]);
|
|
7559
|
+
}
|
|
7560
|
+
};
|
|
7561
|
+
/**
|
|
7562
|
+
* A message queue that processes messages in parallel. It takes another
|
|
7563
|
+
* {@link MessageQueue}, and processes messages in parallel up to a certain
|
|
7564
|
+
* number of workers.
|
|
7565
|
+
*
|
|
7566
|
+
* Actually, it's rather a decorator than a queue itself.
|
|
7567
|
+
*
|
|
7568
|
+
* Note that the workers do not run in truly parallel, in the sense that they
|
|
7569
|
+
* are not running in separate threads or processes. They are running in the
|
|
7570
|
+
* same process, but are scheduled to run in parallel. Hence, this is useful
|
|
7571
|
+
* for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
|
|
7572
|
+
* workloads.
|
|
7573
|
+
*
|
|
7574
|
+
* When using `ParallelMessageQueue`, the ordering guarantee is preserved
|
|
7575
|
+
* *only if* the underlying queue implementation delivers messages in a wrapper
|
|
7576
|
+
* format that includes the `__fedify_ordering_key__` property. Currently,
|
|
7577
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
7578
|
+
* For other queue implementations (e.g., `InProcessMessageQueue`,
|
|
7579
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
7580
|
+
* `AmqpMessageQueue`), the ordering key cannot be detected by
|
|
7581
|
+
* `ParallelMessageQueue`, so ordering guarantees are handled by those
|
|
7582
|
+
* implementations directly rather than at the `ParallelMessageQueue` level.
|
|
7583
|
+
*
|
|
7584
|
+
* Messages with the same ordering key will never be processed concurrently
|
|
7585
|
+
* by different workers, ensuring sequential processing within each key.
|
|
7586
|
+
* Messages with different ordering keys (or no ordering key) can still be
|
|
7587
|
+
* processed in parallel.
|
|
7588
|
+
*
|
|
7589
|
+
* @since 1.0.0
|
|
7590
|
+
*/
|
|
7591
|
+
var ParallelMessageQueue = class ParallelMessageQueue {
|
|
7592
|
+
queue;
|
|
7593
|
+
workers;
|
|
7594
|
+
/**
|
|
7595
|
+
* Inherits the native retry capability from the wrapped queue.
|
|
7596
|
+
* @since 1.7.0
|
|
7597
|
+
*/
|
|
7598
|
+
nativeRetrial;
|
|
7599
|
+
/**
|
|
7600
|
+
* Inherits the native deduplication capability from the wrapped queue.
|
|
7601
|
+
* @since 2.4.0
|
|
7602
|
+
*/
|
|
7603
|
+
nativeDeduplication;
|
|
7604
|
+
/**
|
|
7605
|
+
* Inherits the atomic batch capability from the wrapped queue.
|
|
7606
|
+
* @since 2.4.0
|
|
7607
|
+
*/
|
|
7608
|
+
atomicEnqueueMany;
|
|
7609
|
+
getDepth;
|
|
7610
|
+
/**
|
|
7611
|
+
* Tracks which ordering keys are currently being processed to ensure
|
|
7612
|
+
* sequential processing for messages with the same key.
|
|
7613
|
+
*/
|
|
7614
|
+
#processingKeys = /* @__PURE__ */ new Set();
|
|
7615
|
+
/**
|
|
7616
|
+
* Pending messages waiting for their ordering key to become available.
|
|
7617
|
+
*/
|
|
7618
|
+
#pendingMessages = [];
|
|
7619
|
+
/**
|
|
7620
|
+
* Constructs a new {@link ParallelMessageQueue} with the given queue and
|
|
7621
|
+
* number of workers.
|
|
7622
|
+
* @param queue The message queue to use under the hood. Note that
|
|
7623
|
+
* {@link ParallelMessageQueue} cannot be nested.
|
|
7624
|
+
* @param workers The number of workers to process messages in parallel.
|
|
7625
|
+
* @throws {TypeError} If the given queue is an instance of
|
|
7626
|
+
* {@link ParallelMessageQueue}.
|
|
7627
|
+
*/
|
|
7628
|
+
constructor(queue, workers) {
|
|
7629
|
+
if (queue instanceof ParallelMessageQueue) throw new TypeError("Cannot nest ParallelMessageQueue.");
|
|
7630
|
+
this.queue = queue;
|
|
7631
|
+
this.workers = workers;
|
|
7632
|
+
this.nativeRetrial = queue.nativeRetrial;
|
|
7633
|
+
this.nativeDeduplication = queue.nativeDeduplication;
|
|
7634
|
+
this.atomicEnqueueMany = queue.enqueueMany == null ? false : queue.atomicEnqueueMany;
|
|
7635
|
+
if (queue.getDepth != null) this.getDepth = () => queue.getDepth();
|
|
7636
|
+
}
|
|
7637
|
+
enqueue(message, options) {
|
|
7638
|
+
return this.queue.enqueue(message, options);
|
|
7639
|
+
}
|
|
7640
|
+
async enqueueMany(messages, options) {
|
|
7641
|
+
if (this.queue.enqueueMany == null) {
|
|
7642
|
+
if (messages.length === 1) {
|
|
7643
|
+
await this.queue.enqueue(messages[0], options);
|
|
7644
|
+
return;
|
|
7645
|
+
}
|
|
7646
|
+
if (options?.deduplicationKey != null) throw new TypeError("Cannot enqueue a batch with a deduplicationKey: the wrapped queue does not implement enqueueMany, so ParallelMessageQueue would have to fan out to individual enqueue() calls that cannot share one deduplicationKey atomically. Wrap a queue that implements enqueueMany instead.");
|
|
7647
|
+
const errors = (await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)))).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
7648
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to enqueue messages.");
|
|
7649
|
+
else if (errors.length === 1) throw errors[0];
|
|
7650
|
+
return;
|
|
7651
|
+
}
|
|
7652
|
+
await this.queue.enqueueMany(messages, options);
|
|
7653
|
+
}
|
|
7654
|
+
/**
|
|
7655
|
+
* Extracts ordering key from a message if present.
|
|
7656
|
+
*
|
|
7657
|
+
* This method only works for queue implementations that deliver messages
|
|
7658
|
+
* in the wrapper format with `__fedify_ordering_key__` property. Currently,
|
|
7659
|
+
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
7660
|
+
*
|
|
7661
|
+
* For other queue implementations (`InProcessMessageQueue`,
|
|
7662
|
+
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
7663
|
+
* `AmqpMessageQueue`), messages are delivered as raw payloads without the
|
|
7664
|
+
* wrapper, so the ordering key cannot be detected here. Those
|
|
7665
|
+
* implementations handle ordering guarantees internally.
|
|
7666
|
+
*/
|
|
7667
|
+
#extractOrderingKey(message) {
|
|
7668
|
+
if (message != null && typeof message === "object") {
|
|
7669
|
+
if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
|
|
7670
|
+
}
|
|
7671
|
+
}
|
|
7672
|
+
listen(handler, options = {}) {
|
|
7673
|
+
const workers = /* @__PURE__ */ new Map();
|
|
7674
|
+
return this.queue.listen(async (message) => {
|
|
7675
|
+
while (workers.size >= this.workers) {
|
|
7676
|
+
const consumedId = await Promise.any(workers.values());
|
|
7677
|
+
workers.delete(consumedId);
|
|
7678
|
+
}
|
|
7679
|
+
const workerId = crypto.randomUUID();
|
|
7680
|
+
const orderingKey = this.#extractOrderingKey(message);
|
|
7681
|
+
if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
|
|
7682
|
+
this.#pendingMessages.push({
|
|
7683
|
+
message,
|
|
7684
|
+
orderingKey,
|
|
7685
|
+
resolve
|
|
7686
|
+
});
|
|
7687
|
+
});
|
|
7688
|
+
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
7689
|
+
const promise = this.#work(workerId, handler, message, orderingKey);
|
|
7690
|
+
workers.set(workerId, promise);
|
|
7691
|
+
}, options);
|
|
7692
|
+
}
|
|
7693
|
+
async #work(workerId, handler, message, orderingKey) {
|
|
7694
|
+
await this.#sleep(0);
|
|
7695
|
+
try {
|
|
7696
|
+
await handler(message);
|
|
7697
|
+
} finally {
|
|
7698
|
+
if (orderingKey != null) {
|
|
7699
|
+
this.#processingKeys.delete(orderingKey);
|
|
7700
|
+
const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
|
|
7701
|
+
if (pendingIdx >= 0) this.#pendingMessages.splice(pendingIdx, 1)[0].resolve();
|
|
7702
|
+
}
|
|
7703
|
+
}
|
|
7704
|
+
return workerId;
|
|
7705
|
+
}
|
|
7706
|
+
#sleep(ms) {
|
|
7707
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7708
|
+
}
|
|
7709
|
+
};
|
|
7710
|
+
//#endregion
|
|
7711
|
+
//#region src/federation/router.ts
|
|
7712
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
7713
|
+
"fedify",
|
|
7714
|
+
"federation",
|
|
7715
|
+
"router",
|
|
7716
|
+
"deprecated"
|
|
7717
|
+
]);
|
|
7718
|
+
let deprecationWarned = false;
|
|
7719
|
+
function warnDeprecated() {
|
|
7720
|
+
if (deprecationWarned) return;
|
|
7721
|
+
deprecationWarned = true;
|
|
7722
|
+
logger.warn("The `Router` and `RouterError` classes from `@fedify/fedify` are deprecated. Please use `Router` from `@fedify/uri-template` instead.");
|
|
7723
|
+
}
|
|
7724
|
+
/**
|
|
7725
|
+
* URL router and constructor based on URI Template
|
|
7726
|
+
* ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
|
|
7727
|
+
*
|
|
7728
|
+
* @deprecated Import `Router` from `@fedify/uri-template` instead. This class
|
|
7729
|
+
* remains only for compatibility with older Fedify code. The
|
|
7730
|
+
* `@fedify/uri-template` router is the replacement implementation
|
|
7731
|
+
* and should be used directly in new code.
|
|
7732
|
+
*/
|
|
7733
|
+
var Router = class Router {
|
|
7734
|
+
#router;
|
|
7735
|
+
/**
|
|
7736
|
+
* Create a new {@link Router}.
|
|
7737
|
+
* @param options Options for the router.
|
|
7738
|
+
* @deprecated Use `new Router(options)` from `@fedify/uri-template`
|
|
7739
|
+
* instead.
|
|
7740
|
+
*/
|
|
7741
|
+
constructor(options) {
|
|
7742
|
+
this.#router = convertRouterError(() => new _fedify_uri_template.Router(options));
|
|
7743
|
+
}
|
|
7744
|
+
/**
|
|
7745
|
+
* Whether to ignore trailing slashes when matching paths.
|
|
7746
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead. This
|
|
7747
|
+
* accessor forwards to the underlying `@fedify/uri-template`
|
|
7748
|
+
* router so that post-construction mutation keeps working as
|
|
7749
|
+
* in older Fedify code.
|
|
7750
|
+
*/
|
|
7751
|
+
get trailingSlashInsensitive() {
|
|
7752
|
+
return this.#router.trailingSlashInsensitive;
|
|
7753
|
+
}
|
|
7754
|
+
set trailingSlashInsensitive(value) {
|
|
7755
|
+
this.#router.trailingSlashInsensitive = value;
|
|
7756
|
+
}
|
|
7757
|
+
/**
|
|
7758
|
+
* Clones this router.
|
|
7759
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead.
|
|
7760
|
+
*/
|
|
7761
|
+
clone() {
|
|
7762
|
+
return convertRouterError(() => {
|
|
7763
|
+
const clone = new Router();
|
|
7764
|
+
clone.#router = this.#router.clone();
|
|
7765
|
+
return clone;
|
|
7766
|
+
});
|
|
7767
|
+
}
|
|
7768
|
+
/**
|
|
7769
|
+
* Checks if a path name exists in the router.
|
|
7770
|
+
* @param name The name of the path.
|
|
7771
|
+
* @returns `true` if the path name exists, otherwise `false`.
|
|
7772
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead.
|
|
7773
|
+
*/
|
|
7774
|
+
has(name) {
|
|
7775
|
+
return convertRouterError(() => this.#router.has(name));
|
|
7776
|
+
}
|
|
7777
|
+
/**
|
|
7778
|
+
* Adds a new path rule to the router.
|
|
7779
|
+
* @param template The path pattern.
|
|
7780
|
+
* @param name The name of the path.
|
|
7781
|
+
* @returns The names of the variables in the path pattern.
|
|
7782
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead. In this
|
|
7783
|
+
* compatibility class, `add()` both registers the route and
|
|
7784
|
+
* returns the variables in the path pattern. In
|
|
7785
|
+
* `@fedify/uri-template`, these two responsibilities are split:
|
|
7786
|
+
* `router.add(template, name)` registers the route and returns
|
|
7787
|
+
* `void`, while the pure static method
|
|
7788
|
+
* `Router.variables(template)` returns the variable names. To
|
|
7789
|
+
* migrate, call `Router.variables(template)` when variables are
|
|
7790
|
+
* needed, then call `router.add(template, name)` to register the
|
|
7791
|
+
* route.
|
|
7792
|
+
*/
|
|
7793
|
+
add(template, name) {
|
|
7794
|
+
return convertRouterError(() => {
|
|
7795
|
+
(0, _fedify_uri_template.assertPath)(template);
|
|
7796
|
+
this.#router.add(template, name);
|
|
7797
|
+
return _fedify_uri_template.Router.variables(template);
|
|
7798
|
+
});
|
|
7799
|
+
}
|
|
7800
|
+
/**
|
|
7801
|
+
* Resolves a path name and values from a URL, if any match.
|
|
7802
|
+
* @param url The URL to resolve.
|
|
7803
|
+
* @returns The name of the path and its values, if any match. Otherwise,
|
|
7804
|
+
* `null`.
|
|
7805
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead. Unlike the
|
|
7806
|
+
* stricter `@fedify/uri-template` router, this compatibility
|
|
7807
|
+
* method keeps the old Fedify 2.x contract of returning `null`
|
|
7808
|
+
* (rather than throwing) for inputs that are not router paths.
|
|
7809
|
+
*/
|
|
7810
|
+
route(url) {
|
|
7811
|
+
return convertRouterError(() => {
|
|
7812
|
+
if (!(0, _fedify_uri_template.isPath)(url)) return null;
|
|
7813
|
+
return this.#router.route(url);
|
|
7814
|
+
});
|
|
7815
|
+
}
|
|
7816
|
+
/**
|
|
7817
|
+
* Constructs a URL/path from a path name and values.
|
|
7818
|
+
* @param name The name of the path.
|
|
7819
|
+
* @param values The values to expand the path with.
|
|
7820
|
+
* @returns The URL/path, if the name exists. Otherwise, `null`.
|
|
7821
|
+
* @deprecated Use `Router` from `@fedify/uri-template` instead.
|
|
7822
|
+
*/
|
|
7823
|
+
build(name, values) {
|
|
7824
|
+
return convertRouterError(() => this.#router.build(name, values));
|
|
7825
|
+
}
|
|
7826
|
+
};
|
|
7827
|
+
/**
|
|
7828
|
+
* An error thrown by the {@link Router}.
|
|
7829
|
+
* @deprecated Import `RouterError` from `@fedify/uri-template` instead.
|
|
7830
|
+
*/
|
|
7831
|
+
var RouterError = class extends _fedify_uri_template.RouterError {
|
|
7832
|
+
/**
|
|
7833
|
+
* Treats every `RouterError` from `@fedify/uri-template` as an instance of
|
|
7834
|
+
* this deprecated class.
|
|
7835
|
+
*
|
|
7836
|
+
* @deprecated Import `RouterError` from `@fedify/uri-template` instead.
|
|
7837
|
+
*/
|
|
7838
|
+
static [Symbol.hasInstance](instance) {
|
|
7839
|
+
return instance instanceof _fedify_uri_template.RouterError;
|
|
7840
|
+
}
|
|
7841
|
+
/**
|
|
7842
|
+
* Create a new {@link RouterError}.
|
|
7843
|
+
* @param message The error message.
|
|
7844
|
+
* @deprecated Import `RouterError` from `@fedify/uri-template` instead.
|
|
7845
|
+
*/
|
|
7846
|
+
constructor(message) {
|
|
7847
|
+
super(message);
|
|
7848
|
+
warnDeprecated();
|
|
7849
|
+
}
|
|
7850
|
+
};
|
|
7851
|
+
function convertRouterError(func) {
|
|
7852
|
+
try {
|
|
7853
|
+
warnDeprecated();
|
|
7854
|
+
return func();
|
|
7855
|
+
} catch (error) {
|
|
7856
|
+
if (error instanceof _fedify_uri_template.RouterError) throw new RouterError(error.message);
|
|
7857
|
+
throw error;
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7860
|
+
//#endregion
|
|
7144
7861
|
Object.defineProperty(exports, "CircuitBreaker", {
|
|
7145
7862
|
enumerable: true,
|
|
7146
7863
|
get: function() {
|
|
7147
7864
|
return CircuitBreaker;
|
|
7148
7865
|
}
|
|
7149
7866
|
});
|
|
7867
|
+
Object.defineProperty(exports, "InProcessMessageQueue", {
|
|
7868
|
+
enumerable: true,
|
|
7869
|
+
get: function() {
|
|
7870
|
+
return InProcessMessageQueue;
|
|
7871
|
+
}
|
|
7872
|
+
});
|
|
7873
|
+
Object.defineProperty(exports, "MemoryKvStore", {
|
|
7874
|
+
enumerable: true,
|
|
7875
|
+
get: function() {
|
|
7876
|
+
return MemoryKvStore;
|
|
7877
|
+
}
|
|
7878
|
+
});
|
|
7879
|
+
Object.defineProperty(exports, "ParallelMessageQueue", {
|
|
7880
|
+
enumerable: true,
|
|
7881
|
+
get: function() {
|
|
7882
|
+
return ParallelMessageQueue;
|
|
7883
|
+
}
|
|
7884
|
+
});
|
|
7885
|
+
Object.defineProperty(exports, "Router", {
|
|
7886
|
+
enumerable: true,
|
|
7887
|
+
get: function() {
|
|
7888
|
+
return Router;
|
|
7889
|
+
}
|
|
7890
|
+
});
|
|
7891
|
+
Object.defineProperty(exports, "RouterError", {
|
|
7892
|
+
enumerable: true,
|
|
7893
|
+
get: function() {
|
|
7894
|
+
return RouterError;
|
|
7895
|
+
}
|
|
7896
|
+
});
|
|
7150
7897
|
Object.defineProperty(exports, "SendActivityError", {
|
|
7151
7898
|
enumerable: true,
|
|
7152
7899
|
get: function() {
|
|
@@ -7189,12 +7936,6 @@ Object.defineProperty(exports, "handleWebFinger", {
|
|
|
7189
7936
|
return handleWebFinger;
|
|
7190
7937
|
}
|
|
7191
7938
|
});
|
|
7192
|
-
Object.defineProperty(exports, "middleware_exports", {
|
|
7193
|
-
enumerable: true,
|
|
7194
|
-
get: function() {
|
|
7195
|
-
return middleware_exports;
|
|
7196
|
-
}
|
|
7197
|
-
});
|
|
7198
7939
|
Object.defineProperty(exports, "normalizeCircuitBreakerOptions", {
|
|
7199
7940
|
enumerable: true,
|
|
7200
7941
|
get: function() {
|