@fedify/fedify 2.3.1 → 2.3.2
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 +11 -8
- package/dist/{builder-hivpmhlD.mjs → builder-Dx4EI2YS.mjs} +4 -2
- package/dist/{circuit-breaker-CSWsyoef.mjs → circuit-breaker-DLS_n1x5.mjs} +190 -7
- package/dist/circuit-breaker-test-utils-BVnv3T76.mjs +14 -0
- package/dist/{client-ByXmQhYD.mjs → client-D1KuWxjH.mjs} +63 -5
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-BBVLF7lx.d.cts → context-Bjswu3eC.d.cts} +18 -1
- package/dist/{context-BU6jSQdo.d.ts → context-COxc_O-c.d.ts} +18 -1
- package/dist/{deno-C8RG0HM3.mjs → deno-B8lcQ4Ms.mjs} +1 -1
- package/dist/{docloader-YZA0m_3O.mjs → docloader-BMjxpyJD.mjs} +2 -2
- package/dist/federation/builder.test.mjs +13 -1
- package/dist/federation/circuit-breaker.test.mjs +622 -3
- package/dist/federation/handler.test.mjs +3 -3
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +16 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- 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 +69 -7
- package/dist/{http-BajqV0pH.cjs → http-CpdQD2ke.cjs} +1 -1
- package/dist/{http-DPOMa1DD.js → http-DVZxcZEr.js} +1 -1
- package/dist/{http-PCt0862M.mjs → http-DlSQMJsb.mjs} +3 -3
- package/dist/{key-BKYwmbur.mjs → key-BqT9YrM3.mjs} +2 -2
- package/dist/{kv-cache-IjRceERu.js → kv-cache-B52kTPj4.js} +1 -1
- package/dist/{kv-cache-CVG3m2_-.cjs → kv-cache-B8gm4cVB.cjs} +1 -1
- package/dist/{kv-cache-Ceg0c1wh.mjs → kv-cache-BD6BgCTF.mjs} +1 -1
- package/dist/{ld-Dcw2skfn.mjs → ld-hwRDfZqq.mjs} +3 -3
- package/dist/{metrics-Pue1x4ta.mjs → metrics-DxJzdAgZ.mjs} +1 -1
- package/dist/{middleware-By8lqc18.js → middleware-B2rDMvhv.js} +231 -29
- package/dist/{middleware-WU_5fbGS.mjs → middleware-CubmqVku.mjs} +49 -32
- package/dist/{middleware-BiXEAbn2.cjs → middleware-D2F1jSer.cjs} +231 -29
- package/dist/{middleware-CyxqoeW9.mjs → middleware-DxCKBFFT.mjs} +1 -1
- package/dist/{mod-C0F6kvgS.d.cts → mod-0bIqGrKQ.d.cts} +1 -1
- package/dist/{mod-vPYVoa5n.d.ts → mod-3H9Gym5G.d.ts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +61 -1
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/{owner-pVJ0Twi7.mjs → owner-Btzxz2vj.mjs} +2 -2
- package/dist/{proof-HzavUYxN.cjs → proof-BdnNZ9OM.cjs} +1 -1
- package/dist/{proof-DA8Kbchf.js → proof-C_kcg7vF.js} +1 -1
- package/dist/{proof-pXYODmNV.mjs → proof-D2Z0Wa9X.mjs} +3 -3
- package/dist/{send-BfAp60BJ.mjs → send-BljvjEOa.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-HZHBbbk4.mjs → temporal-Dg8Hb3lz.mjs} +1 -1
- package/dist/{types-CAY3OdLq.js → types-DTONwNqL.js} +63 -5
- package/dist/{types-KC4QAoxe.cjs → types-m8u9L86X.cjs} +62 -4
- 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
- package/skills/fedify/SKILL.md +462 -0
|
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
|
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
const require_chunk = require("./chunk-DDcVe30Y.cjs");
|
|
4
4
|
const require_transformers = require("./transformers-NeAONrAq.cjs");
|
|
5
|
-
const require_http = require("./http-
|
|
6
|
-
const require_proof = require("./proof-
|
|
7
|
-
const require_types = require("./types-
|
|
8
|
-
const require_kv_cache = require("./kv-cache-
|
|
5
|
+
const require_http = require("./http-CpdQD2ke.cjs");
|
|
6
|
+
const require_proof = require("./proof-BdnNZ9OM.cjs");
|
|
7
|
+
const require_types = require("./types-m8u9L86X.cjs");
|
|
8
|
+
const require_kv_cache = require("./kv-cache-B8gm4cVB.cjs");
|
|
9
9
|
let _logtape_logtape = require("@logtape/logtape");
|
|
10
10
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
11
11
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -138,6 +138,8 @@ var FederationBuilderImpl = class {
|
|
|
138
138
|
f.webFingerLinksDispatcher = this.webFingerLinksDispatcher;
|
|
139
139
|
f.objectCallbacks = { ...this.objectCallbacks };
|
|
140
140
|
f.objectTypeIds = { ...this.objectTypeIds };
|
|
141
|
+
f.collectionCallbacks = { ...this.collectionCallbacks };
|
|
142
|
+
f.collectionTypeIds = { ...this.collectionTypeIds };
|
|
141
143
|
f.inboxPath = this.inboxPath;
|
|
142
144
|
f.outboxPath = this.outboxPath;
|
|
143
145
|
f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
|
|
@@ -681,6 +683,13 @@ function createFederationBuilder() {
|
|
|
681
683
|
//#endregion
|
|
682
684
|
//#region src/federation/circuit-breaker.ts
|
|
683
685
|
const MAX_CUSTOM_FAILURE_HISTORY = 100;
|
|
686
|
+
const LEGACY_SWEEP_MARKER = ["__fedify_meta", "circuit_breaker_state_ttl_sweep_v1"];
|
|
687
|
+
const LEGACY_SWEEP_DELETING_MARKER = { __fedifyDeletingCircuitBreakerLegacyState: true };
|
|
688
|
+
const CIRCUIT_BREAKER_STATE_VERSION = 1;
|
|
689
|
+
const LEGACY_SWEEP_LOCK_TTL = Temporal.Duration.from({ minutes: 5 });
|
|
690
|
+
const LEGACY_SWEEP_RETRY_WINDOW = Temporal.Duration.from({ hours: 168 });
|
|
691
|
+
const LEGACY_SWEEP_WAIT_INTERVAL = 10;
|
|
692
|
+
const LEGACY_SWEEP_CAS_ATTEMPTS = 3;
|
|
684
693
|
/**
|
|
685
694
|
* Tracks reachability state for remote outbox delivery hosts.
|
|
686
695
|
* @since 2.3.0
|
|
@@ -690,6 +699,8 @@ var CircuitBreaker = class {
|
|
|
690
699
|
#prefix;
|
|
691
700
|
#options;
|
|
692
701
|
#now;
|
|
702
|
+
#legacySweep;
|
|
703
|
+
#legacySweepDone;
|
|
693
704
|
#stateChangeObserver;
|
|
694
705
|
constructor(options) {
|
|
695
706
|
this.#kv = options.kv;
|
|
@@ -706,6 +717,7 @@ var CircuitBreaker = class {
|
|
|
706
717
|
return now.until(this.#capHeldRetryAt(now, heldSince, now.add(delay)));
|
|
707
718
|
}
|
|
708
719
|
async beforeSend(remoteHost, message) {
|
|
720
|
+
this.#sweepLegacyStates();
|
|
709
721
|
const heldSince = parseHeldSince(message.circuitHeldSince);
|
|
710
722
|
const now = this.#now();
|
|
711
723
|
if (heldSince != null && Temporal.Instant.compare(heldSince.add(this.#options.heldActivityTtl), now) <= 0) return {
|
|
@@ -787,6 +799,7 @@ var CircuitBreaker = class {
|
|
|
787
799
|
throw new Error(`Failed to update circuit breaker state for ${remoteHost}`);
|
|
788
800
|
}
|
|
789
801
|
async recordSuccess(remoteHost) {
|
|
802
|
+
this.#sweepLegacyStates();
|
|
790
803
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
791
804
|
const oldState = await this.#get(remoteHost);
|
|
792
805
|
if (oldState == null) return void 0;
|
|
@@ -807,6 +820,7 @@ var CircuitBreaker = class {
|
|
|
807
820
|
return await this.recordSuccess(remoteHost);
|
|
808
821
|
}
|
|
809
822
|
async recordFailure(remoteHost) {
|
|
823
|
+
this.#sweepLegacyStates();
|
|
810
824
|
const now = this.#now();
|
|
811
825
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
812
826
|
const oldState = await this.#get(remoteHost);
|
|
@@ -854,26 +868,122 @@ var CircuitBreaker = class {
|
|
|
854
868
|
}
|
|
855
869
|
}
|
|
856
870
|
async getState(remoteHost) {
|
|
857
|
-
|
|
871
|
+
this.#sweepLegacyStates();
|
|
872
|
+
return stripStoredCircuitBreakerState(await this.#get(remoteHost));
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* The currently running background legacy sweep, if any.
|
|
876
|
+
* @internal
|
|
877
|
+
*/
|
|
878
|
+
get pendingSweep() {
|
|
879
|
+
return this.#legacySweep;
|
|
858
880
|
}
|
|
859
881
|
#key(remoteHost) {
|
|
860
882
|
return [...this.#prefix, remoteHost];
|
|
861
883
|
}
|
|
884
|
+
#legacySweepMarkerKey() {
|
|
885
|
+
return [...this.#prefix, ...LEGACY_SWEEP_MARKER];
|
|
886
|
+
}
|
|
887
|
+
#sweepLegacyStates() {
|
|
888
|
+
if (this.#kv.cas == null) return;
|
|
889
|
+
if (this.#options.stateTtl == null) return;
|
|
890
|
+
if (this.#legacySweep != null) return;
|
|
891
|
+
if (this.#isLegacySweepDone()) return;
|
|
892
|
+
this.#legacySweep = this.#sweepLegacyStatesImpl().catch((error) => {
|
|
893
|
+
(0, _logtape_logtape.getLogger)([
|
|
894
|
+
"fedify",
|
|
895
|
+
"federation",
|
|
896
|
+
"circuit"
|
|
897
|
+
]).warn("Failed to sweep legacy circuit breaker state:\n{error}", { error });
|
|
898
|
+
}).finally(() => {
|
|
899
|
+
this.#legacySweep = void 0;
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
async #sweepLegacyStatesImpl() {
|
|
903
|
+
const markerKey = this.#legacySweepMarkerKey();
|
|
904
|
+
const marker = await this.#acquireLegacySweep(markerKey);
|
|
905
|
+
if (marker === "done") return;
|
|
906
|
+
try {
|
|
907
|
+
for await (const { key, value } of this.#kv.list(this.#prefix)) {
|
|
908
|
+
if (key.length !== this.#prefix.length + 1) continue;
|
|
909
|
+
await this.#migrateLegacyState(key, value);
|
|
910
|
+
}
|
|
911
|
+
} catch (error) {
|
|
912
|
+
await this.#deleteIfUnchanged(markerKey, marker);
|
|
913
|
+
throw error;
|
|
914
|
+
}
|
|
915
|
+
const finishedMarker = this.#finishLegacySweepMarker(marker);
|
|
916
|
+
if (await this.#kv.cas(markerKey, marker, finishedMarker)) this.#rememberLegacySweepMarker(finishedMarker);
|
|
917
|
+
}
|
|
918
|
+
#finishLegacySweepMarker(marker) {
|
|
919
|
+
const retryUntil = getLegacySweepRetryUntil(marker);
|
|
920
|
+
if (retryUntil != null && Temporal.Instant.compare(this.#now(), Temporal.Instant.from(retryUntil)) < 0) return {
|
|
921
|
+
state: "done",
|
|
922
|
+
retryUntil
|
|
923
|
+
};
|
|
924
|
+
return { state: "final" };
|
|
925
|
+
}
|
|
926
|
+
async #migrateLegacyState(key, value) {
|
|
927
|
+
if (isCurrentCircuitBreakerState(value)) return;
|
|
928
|
+
const state = parseCircuitBreakerKvState(value);
|
|
929
|
+
if (state != null) {
|
|
930
|
+
await this.#kv.cas(key, value, markCircuitBreakerState(state), this.#setOptions());
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
await this.#deleteIfUnchanged(key, value);
|
|
934
|
+
}
|
|
935
|
+
async #deleteIfUnchanged(key, value) {
|
|
936
|
+
if (await this.#kv.cas(key, value, LEGACY_SWEEP_DELETING_MARKER, { ttl: LEGACY_SWEEP_LOCK_TTL })) await this.#kv.delete(key);
|
|
937
|
+
}
|
|
938
|
+
async #acquireLegacySweep(markerKey) {
|
|
939
|
+
for (let attempt = 0; attempt < LEGACY_SWEEP_CAS_ATTEMPTS; attempt++) {
|
|
940
|
+
const marker = await this.#kv.get(markerKey);
|
|
941
|
+
const now = this.#now();
|
|
942
|
+
if (isLegacySweepActive(marker, now)) {
|
|
943
|
+
this.#rememberLegacySweepMarker(marker);
|
|
944
|
+
return "done";
|
|
945
|
+
}
|
|
946
|
+
const retryUntil = isLegacySweepRetryDue(marker, now) ? marker.retryUntil : getLegacySweepRetryUntil(marker) ?? now.add(LEGACY_SWEEP_RETRY_WINDOW).toString();
|
|
947
|
+
const sweeping = {
|
|
948
|
+
state: "sweeping",
|
|
949
|
+
started: now.toString(),
|
|
950
|
+
retryUntil
|
|
951
|
+
};
|
|
952
|
+
if (await this.#kv.cas(markerKey, marker ?? void 0, sweeping, { ttl: LEGACY_SWEEP_LOCK_TTL })) return sweeping;
|
|
953
|
+
await delay(LEGACY_SWEEP_WAIT_INTERVAL);
|
|
954
|
+
}
|
|
955
|
+
return "done";
|
|
956
|
+
}
|
|
957
|
+
#isLegacySweepDone() {
|
|
958
|
+
if (this.#legacySweepDone === "final") return true;
|
|
959
|
+
if (this.#legacySweepDone == null) return false;
|
|
960
|
+
if (Temporal.Instant.compare(this.#now(), this.#legacySweepDone) < 0) return true;
|
|
961
|
+
this.#legacySweepDone = void 0;
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
#rememberLegacySweepMarker(marker) {
|
|
965
|
+
const doneUntil = getLegacySweepDoneUntil(marker, this.#now());
|
|
966
|
+
if (doneUntil != null) this.#legacySweepDone = doneUntil;
|
|
967
|
+
}
|
|
862
968
|
#capHeldRetryAt(now, heldSince, retryAt) {
|
|
863
969
|
const expiresAt = (heldSince ?? now).add(this.#options.heldActivityTtl);
|
|
864
970
|
return Temporal.Instant.compare(expiresAt, retryAt) < 0 ? expiresAt : retryAt;
|
|
865
971
|
}
|
|
866
972
|
async #get(remoteHost) {
|
|
867
|
-
return
|
|
973
|
+
return parseStoredCircuitBreakerKvState(await this.#kv.get(this.#key(remoteHost)));
|
|
868
974
|
}
|
|
869
975
|
async #replace(remoteHost, oldState, newState) {
|
|
870
976
|
const key = this.#key(remoteHost);
|
|
977
|
+
const storedState = newState == null ? void 0 : markCircuitBreakerState(newState);
|
|
871
978
|
if (this.#kv.cas == null) {
|
|
872
|
-
if (
|
|
873
|
-
else await this.#kv.set(key,
|
|
979
|
+
if (storedState == null) await this.#kv.delete(key);
|
|
980
|
+
else await this.#kv.set(key, storedState, this.#setOptions());
|
|
874
981
|
return true;
|
|
875
982
|
}
|
|
876
|
-
return await this.#kv.cas(key, oldState,
|
|
983
|
+
return await this.#kv.cas(key, oldState, storedState, storedState == null ? void 0 : this.#setOptions());
|
|
984
|
+
}
|
|
985
|
+
#setOptions() {
|
|
986
|
+
return this.#options.stateTtl == null ? void 0 : { ttl: this.#options.stateTtl };
|
|
877
987
|
}
|
|
878
988
|
async #notifyStateChange(remoteHost, previousState, newState) {
|
|
879
989
|
try {
|
|
@@ -913,18 +1023,25 @@ var CircuitBreaker = class {
|
|
|
913
1023
|
* @param options The public circuit breaker options supplied to Fedify.
|
|
914
1024
|
* @returns The normalized failure predicate, failure pruning function,
|
|
915
1025
|
* duration values, and optional callbacks with defaults applied.
|
|
916
|
-
* @throws {RangeError} If any configured duration is not positive
|
|
1026
|
+
* @throws {RangeError} If any configured duration is not positive, or if
|
|
1027
|
+
* `stateTtl` is shorter than `recoveryDelay`.
|
|
917
1028
|
* @throws {TypeError} If `failureThreshold` is not a positive integer.
|
|
918
1029
|
*/
|
|
919
1030
|
function normalizeCircuitBreakerOptions(options) {
|
|
920
1031
|
const recoveryDelay = toInstantDuration(options.recoveryDelay ?? { minutes: 30 });
|
|
921
1032
|
const heldActivityTtl = toInstantDuration(options.heldActivityTtl ?? { hours: 168 });
|
|
922
1033
|
const releaseInterval = toInstantDuration(options.releaseInterval ?? { seconds: 1 });
|
|
1034
|
+
const configuredStateTtl = options.stateTtl == null ? void 0 : toInstantDuration(options.stateTtl);
|
|
923
1035
|
assertPositiveDuration(recoveryDelay, "recoveryDelay");
|
|
924
1036
|
assertPositiveDuration(heldActivityTtl, "heldActivityTtl");
|
|
925
1037
|
assertPositiveDuration(releaseInterval, "releaseInterval");
|
|
1038
|
+
if (configuredStateTtl != null) {
|
|
1039
|
+
assertPositiveDuration(configuredStateTtl, "stateTtl");
|
|
1040
|
+
if (Temporal.Duration.compare(configuredStateTtl, recoveryDelay) < 0) throw new RangeError("stateTtl must be at least recoveryDelay.");
|
|
1041
|
+
}
|
|
926
1042
|
let failure;
|
|
927
1043
|
let pruneFailures;
|
|
1044
|
+
let stateTtl;
|
|
928
1045
|
if (options.failure == null) {
|
|
929
1046
|
const failureThreshold = options.failureThreshold ?? 5;
|
|
930
1047
|
if (!Number.isInteger(failureThreshold) || failureThreshold <= 0) throw new TypeError("failureThreshold must be a positive integer.");
|
|
@@ -940,9 +1057,11 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
940
1057
|
const last = timestamps[timestamps.length - 1];
|
|
941
1058
|
return Temporal.Duration.compare(first.until(last), failureWindow) <= 0;
|
|
942
1059
|
};
|
|
1060
|
+
stateTtl = configuredStateTtl ?? maxDuration(recoveryDelay.add(failureWindow), recoveryDelay.add(heldActivityTtl));
|
|
943
1061
|
} else {
|
|
944
1062
|
failure = options.failure;
|
|
945
1063
|
pruneFailures = (timestamps) => timestamps.slice(-MAX_CUSTOM_FAILURE_HISTORY);
|
|
1064
|
+
stateTtl = configuredStateTtl;
|
|
946
1065
|
}
|
|
947
1066
|
return {
|
|
948
1067
|
failure,
|
|
@@ -950,10 +1069,72 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
950
1069
|
recoveryDelay,
|
|
951
1070
|
heldActivityTtl,
|
|
952
1071
|
releaseInterval,
|
|
1072
|
+
stateTtl,
|
|
953
1073
|
onStateChange: options.onStateChange,
|
|
954
1074
|
onActivityDrop: options.onActivityDrop
|
|
955
1075
|
};
|
|
956
1076
|
}
|
|
1077
|
+
function maxDuration(duration, ...durations) {
|
|
1078
|
+
return durations.reduce((max, candidate) => Temporal.Duration.compare(candidate, max) > 0 ? candidate : max, duration);
|
|
1079
|
+
}
|
|
1080
|
+
function isLegacySweepDone(value, now) {
|
|
1081
|
+
return getLegacySweepDoneUntil(value, now) != null;
|
|
1082
|
+
}
|
|
1083
|
+
function getLegacySweepDoneUntil(value, now) {
|
|
1084
|
+
if (typeof value !== "object" || value == null || !("state" in value)) return;
|
|
1085
|
+
if (value.state === "final") return "final";
|
|
1086
|
+
if (value.state === "done" && "retryUntil" in value && typeof value.retryUntil === "string") try {
|
|
1087
|
+
const retryUntil = Temporal.Instant.from(value.retryUntil);
|
|
1088
|
+
return Temporal.Instant.compare(now, retryUntil) < 0 ? retryUntil : void 0;
|
|
1089
|
+
} catch {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function isLegacySweepRetryDue(value, now) {
|
|
1094
|
+
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "done" || !("retryUntil" in value) || typeof value.retryUntil !== "string") return false;
|
|
1095
|
+
try {
|
|
1096
|
+
return Temporal.Instant.compare(now, Temporal.Instant.from(value.retryUntil)) >= 0;
|
|
1097
|
+
} catch {
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function isLegacySweepActive(value, now) {
|
|
1102
|
+
return isLegacySweepDone(value, now) || isLegacySweepInProgress(value, now);
|
|
1103
|
+
}
|
|
1104
|
+
function isLegacySweepInProgress(value, now) {
|
|
1105
|
+
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "sweeping" || !("started" in value) || typeof value.started !== "string") return false;
|
|
1106
|
+
try {
|
|
1107
|
+
return Temporal.Instant.compare(now, Temporal.Instant.from(value.started).add(LEGACY_SWEEP_LOCK_TTL)) < 0;
|
|
1108
|
+
} catch {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function getLegacySweepRetryUntil(value) {
|
|
1113
|
+
if (typeof value !== "object" || value == null || !("retryUntil" in value) || typeof value.retryUntil !== "string") return;
|
|
1114
|
+
try {
|
|
1115
|
+
Temporal.Instant.from(value.retryUntil);
|
|
1116
|
+
return value.retryUntil;
|
|
1117
|
+
} catch {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
function isCurrentCircuitBreakerState(value) {
|
|
1122
|
+
return typeof value === "object" && value != null && "__fedifyCircuitBreakerStateVersion" in value && value.__fedifyCircuitBreakerStateVersion === CIRCUIT_BREAKER_STATE_VERSION;
|
|
1123
|
+
}
|
|
1124
|
+
function markCircuitBreakerState(state) {
|
|
1125
|
+
return {
|
|
1126
|
+
...state,
|
|
1127
|
+
__fedifyCircuitBreakerStateVersion: CIRCUIT_BREAKER_STATE_VERSION
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
function stripStoredCircuitBreakerState(state) {
|
|
1131
|
+
if (state == null) return void 0;
|
|
1132
|
+
const { __fedifyCircuitBreakerStateVersion: _, ...publicState } = state;
|
|
1133
|
+
return publicState;
|
|
1134
|
+
}
|
|
1135
|
+
function delay(milliseconds) {
|
|
1136
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1137
|
+
}
|
|
957
1138
|
function toInstantDuration(duration) {
|
|
958
1139
|
const parsed = Temporal.Duration.from(duration);
|
|
959
1140
|
return Temporal.Duration.from({ milliseconds: Math.trunc(parsed.total({
|
|
@@ -988,6 +1169,9 @@ function parseHeldSince(value) {
|
|
|
988
1169
|
* valid instant strings, or `undefined` when the stored value is malformed.
|
|
989
1170
|
*/
|
|
990
1171
|
function parseCircuitBreakerKvState(value) {
|
|
1172
|
+
return stripStoredCircuitBreakerState(parseStoredCircuitBreakerKvState(value));
|
|
1173
|
+
}
|
|
1174
|
+
function parseStoredCircuitBreakerKvState(value) {
|
|
991
1175
|
const isInstantString = (v) => {
|
|
992
1176
|
if (typeof v !== "string") return false;
|
|
993
1177
|
try {
|
|
@@ -1007,7 +1191,8 @@ function parseCircuitBreakerKvState(value) {
|
|
|
1007
1191
|
state: record.state,
|
|
1008
1192
|
failures: record.failures,
|
|
1009
1193
|
...record.opened == null ? {} : { opened: record.opened },
|
|
1010
|
-
...record.halfOpened == null ? {} : { halfOpened: record.halfOpened }
|
|
1194
|
+
...record.halfOpened == null ? {} : { halfOpened: record.halfOpened },
|
|
1195
|
+
...record.__fedifyCircuitBreakerStateVersion === CIRCUIT_BREAKER_STATE_VERSION ? { __fedifyCircuitBreakerStateVersion: CIRCUIT_BREAKER_STATE_VERSION } : {}
|
|
1011
1196
|
};
|
|
1012
1197
|
}
|
|
1013
1198
|
//#endregion
|
|
@@ -3559,6 +3744,24 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
3559
3744
|
"webfinger",
|
|
3560
3745
|
"server"
|
|
3561
3746
|
]);
|
|
3747
|
+
function getWebFingerSubjectAndAliases({ resourceUrl, actorUri, preferredUsername, acctUsername, host, contextHost }) {
|
|
3748
|
+
const aliases = [];
|
|
3749
|
+
let subject = resourceUrl.href;
|
|
3750
|
+
if (resourceUrl.protocol != "acct:" && preferredUsername != null) {
|
|
3751
|
+
aliases.push(`acct:${preferredUsername}@${host ?? contextHost}`);
|
|
3752
|
+
if (host != null && host !== contextHost) aliases.push(`acct:${preferredUsername}@${contextHost}`);
|
|
3753
|
+
}
|
|
3754
|
+
if (resourceUrl.href !== actorUri.href) aliases.push(actorUri.href);
|
|
3755
|
+
if (resourceUrl.protocol === "acct:" && host != null && host !== contextHost && !resourceUrl.href.endsWith(`@${host}`)) {
|
|
3756
|
+
subject = `acct:${preferredUsername ?? acctUsername}@${host}`;
|
|
3757
|
+
aliases.push(resourceUrl.href);
|
|
3758
|
+
if (preferredUsername != null && preferredUsername !== "" && preferredUsername !== acctUsername) aliases.push(`acct:${preferredUsername}@${contextHost}`);
|
|
3759
|
+
}
|
|
3760
|
+
return {
|
|
3761
|
+
subject,
|
|
3762
|
+
aliases
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3562
3765
|
/**
|
|
3563
3766
|
* Handles a WebFinger request. You would not typically call this function
|
|
3564
3767
|
* directly, but instead use {@link Federation.fetch} method.
|
|
@@ -3663,6 +3866,7 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
3663
3866
|
return identifier;
|
|
3664
3867
|
}
|
|
3665
3868
|
let identifier = null;
|
|
3869
|
+
let acctUsername = null;
|
|
3666
3870
|
const uriParsed = context.parseUri(resourceUrl);
|
|
3667
3871
|
if (uriParsed?.type != "actor") {
|
|
3668
3872
|
const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
|
|
@@ -3676,8 +3880,9 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
3676
3880
|
const normalizedHost = portMatch == null ? (0, node_url.domainToASCII)(match[2].toLowerCase()) : (0, node_url.domainToASCII)(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
|
|
3677
3881
|
if (normalizedHost != context.url.host && normalizedHost != host) return await onNotFound(request);
|
|
3678
3882
|
else {
|
|
3679
|
-
|
|
3680
|
-
|
|
3883
|
+
acctUsername = match[1];
|
|
3884
|
+
identifier = await mapUsernameToIdentifier(acctUsername);
|
|
3885
|
+
resourceUrl = new URL(`acct:${acctUsername}@${normalizedHost}`);
|
|
3681
3886
|
}
|
|
3682
3887
|
}
|
|
3683
3888
|
} else identifier = uriParsed.identifier;
|
|
@@ -3691,9 +3896,10 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
3691
3896
|
status: 410,
|
|
3692
3897
|
headers: { "Access-Control-Allow-Origin": "*" }
|
|
3693
3898
|
});
|
|
3899
|
+
const actorUri = context.getActorUri(identifier);
|
|
3694
3900
|
const links = [{
|
|
3695
3901
|
rel: "self",
|
|
3696
|
-
href:
|
|
3902
|
+
href: actorUri.href,
|
|
3697
3903
|
type: "application/activity+json"
|
|
3698
3904
|
}];
|
|
3699
3905
|
for (const url of actor.urls) if (url instanceof _fedify_vocab.Link && url.href != null) links.push({
|
|
@@ -3717,23 +3923,19 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
|
|
|
3717
3923
|
const customLinks = await webFingerLinksDispatcher(context, resourceUrl);
|
|
3718
3924
|
if (customLinks != null) for (const link of customLinks) links.push(link);
|
|
3719
3925
|
}
|
|
3720
|
-
const aliases =
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
}
|
|
3731
|
-
const jrd = {
|
|
3732
|
-
subject: resourceUrl.href,
|
|
3926
|
+
const { subject, aliases } = getWebFingerSubjectAndAliases({
|
|
3927
|
+
resourceUrl,
|
|
3928
|
+
actorUri,
|
|
3929
|
+
preferredUsername: actor.preferredUsername,
|
|
3930
|
+
acctUsername,
|
|
3931
|
+
host,
|
|
3932
|
+
contextHost: context.url.host
|
|
3933
|
+
});
|
|
3934
|
+
return new Response(JSON.stringify({
|
|
3935
|
+
subject,
|
|
3733
3936
|
aliases,
|
|
3734
3937
|
links
|
|
3735
|
-
}
|
|
3736
|
-
return new Response(JSON.stringify(jrd), { headers: {
|
|
3938
|
+
}), { headers: {
|
|
3737
3939
|
"Content-Type": "application/jrd+json",
|
|
3738
3940
|
"Access-Control-Allow-Origin": "*"
|
|
3739
3941
|
} });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { G as ActorDispatcher, K as ActorHandleMapper, W as ActorAliasMapper, l as RequestContext, pt as WebFingerLinksDispatcher } from "./context-
|
|
2
|
+
import { G as ActorDispatcher, K as ActorHandleMapper, W as ActorAliasMapper, l as RequestContext, pt as WebFingerLinksDispatcher } from "./context-Bjswu3eC.cjs";
|
|
3
3
|
import { MeterProvider, Span, Tracer } from "@opentelemetry/api";
|
|
4
4
|
import { RouterError, RouterOptions, RouterRouteResult } from "@fedify/uri-template";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { G as ActorDispatcher, K as ActorHandleMapper, W as ActorAliasMapper, l as RequestContext, pt as WebFingerLinksDispatcher } from "./context-
|
|
2
|
+
import { G as ActorDispatcher, K as ActorHandleMapper, W as ActorAliasMapper, l as RequestContext, pt as WebFingerLinksDispatcher } from "./context-COxc_O-c.js";
|
|
3
3
|
import { RouterError, RouterOptions, RouterRouteResult } from "@fedify/uri-template";
|
|
4
4
|
import { MeterProvider, Span, Tracer } from "@opentelemetry/api";
|
|
5
5
|
|
package/dist/mod.cjs
CHANGED
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
4
4
|
require("./chunk-DDcVe30Y.cjs");
|
|
5
5
|
const require_transformers = require("./transformers-NeAONrAq.cjs");
|
|
6
6
|
require("./compat/mod.cjs");
|
|
7
|
-
const require_http = require("./http-
|
|
8
|
-
const require_middleware = require("./middleware-
|
|
9
|
-
const require_proof = require("./proof-
|
|
10
|
-
const require_types = require("./types-
|
|
11
|
-
const require_kv_cache = require("./kv-cache-
|
|
7
|
+
const require_http = require("./http-CpdQD2ke.cjs");
|
|
8
|
+
const require_middleware = require("./middleware-D2F1jSer.cjs");
|
|
9
|
+
const require_proof = require("./proof-BdnNZ9OM.cjs");
|
|
10
|
+
const require_types = require("./types-m8u9L86X.cjs");
|
|
11
|
+
const require_kv_cache = require("./kv-cache-B8gm4cVB.cjs");
|
|
12
12
|
const require_federation_mod = require("./federation/mod.cjs");
|
|
13
13
|
require("./nodeinfo/mod.cjs");
|
|
14
14
|
require("./runtime/mod.cjs");
|
package/dist/mod.d.cts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { a as InboundService, c as OutboundService, d as Software, f as Usage, i as parseNodeInfo, l as Protocol, n as ParseNodeInfoOptions, o as JsonValue, p as nodeInfoToJson, r as getNodeInfo, s as NodeInfo, t as GetNodeInfoOptions, u as Services } from "./client-CAM_bQXx.cjs";
|
|
3
3
|
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "./http-VyDTd4G3.cjs";
|
|
4
4
|
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "./owner-DEvZuyOE.cjs";
|
|
5
|
-
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, jt as ActivityTransformer, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "./context-
|
|
5
|
+
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, jt as ActivityTransformer, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "./context-Bjswu3eC.cjs";
|
|
6
6
|
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-gJ8LYbxX.cjs";
|
|
7
7
|
import { a as MessageQueueEnqueueOptions, i as MessageQueueDepth, n as InProcessMessageQueueOptions, o as MessageQueueListenOptions, r as MessageQueue, s as ParallelMessageQueue, t as InProcessMessageQueue } from "./mq-D8uSFzxe.cjs";
|
|
8
8
|
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
|
|
9
|
-
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-
|
|
9
|
+
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-0bIqGrKQ.cjs";
|
|
10
10
|
import { _ as hasSignatureLike, a as createProof, b as verifySignature, c as verifyObject, d as SignJsonLdOptions, f as VerifyJsonLdOptions, g as detachSignature, h as createSignature, i as VerifyProofOptions, l as verifyProof, m as attachSignature, n as SignObjectOptions, o as hasProofLike, p as VerifySignatureOptions, r as VerifyObjectOptions, s as signObject, t as CreateProofOptions, u as CreateSignatureOptions, v as signJsonLd, y as verifyJsonLd } from "./mod-B0hW12_O.cjs";
|
|
11
11
|
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-yvIXFAEi.cjs";
|
|
12
12
|
export * from "@fedify/vocab-runtime";
|
package/dist/mod.d.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { a as InboundService, c as OutboundService, d as Software, f as Usage, i as parseNodeInfo, l as Protocol, n as ParseNodeInfoOptions, o as JsonValue, p as nodeInfoToJson, r as getNodeInfo, s as NodeInfo, t as GetNodeInfoOptions, u as Services } from "./client-CSddvgWN.js";
|
|
3
3
|
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "./http-lf8Hsd91.js";
|
|
4
4
|
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "./owner-CnngXDNJ.js";
|
|
5
|
-
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, jt as ActivityTransformer, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "./context-
|
|
5
|
+
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, jt as ActivityTransformer, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "./context-COxc_O-c.js";
|
|
6
6
|
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-D6hNiMTK.js";
|
|
7
7
|
import { a as MessageQueueEnqueueOptions, i as MessageQueueDepth, n as InProcessMessageQueueOptions, o as MessageQueueListenOptions, r as MessageQueue, s as ParallelMessageQueue, t as InProcessMessageQueue } from "./mq-D-nlpY04.js";
|
|
8
8
|
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.js";
|
|
9
|
-
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-
|
|
9
|
+
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-3H9Gym5G.js";
|
|
10
10
|
import { _ as hasSignatureLike, a as createProof, b as verifySignature, c as verifyObject, d as SignJsonLdOptions, f as VerifyJsonLdOptions, g as detachSignature, h as createSignature, i as VerifyProofOptions, l as verifyProof, m as attachSignature, n as SignObjectOptions, o as hasProofLike, p as VerifySignatureOptions, r as VerifyObjectOptions, s as signObject, t as CreateProofOptions, u as CreateSignatureOptions, v as signJsonLd, y as verifyJsonLd } from "./mod-COIAjwRS.js";
|
|
11
11
|
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-DFvNJcNb.js";
|
|
12
12
|
export * from "@fedify/vocab-runtime";
|
package/dist/mod.js
CHANGED
|
@@ -3,11 +3,11 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
3
3
|
import "./chunk-CRNNMoPX.js";
|
|
4
4
|
import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-BGMIq1cs.js";
|
|
5
5
|
import "./compat/mod.js";
|
|
6
|
-
import { A as formatAcceptSignature, M as parseAcceptSignature, N as validateAcceptSignature, a as verifyRequestDetailed, c as fetchKeyDetailed, i as verifyRequest, j as fulfillAcceptSignature, l as generateCryptoKeyPair, o as exportJwk, r as signRequest, s as fetchKey, u as importJwk } from "./http-
|
|
7
|
-
import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "./middleware-
|
|
8
|
-
import { C as verifySignature, S as verifyJsonLd, _ as hasSignatureLike, a as verifyProof, b as signJsonLd, c as getKeyOwner, d as attachSignature, i as verifyObject, m as detachSignature, n as hasProofLike, p as createSignature, r as signObject, s as doesActorOwnKey, t as createProof } from "./proof-
|
|
9
|
-
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-
|
|
10
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
6
|
+
import { A as formatAcceptSignature, M as parseAcceptSignature, N as validateAcceptSignature, a as verifyRequestDetailed, c as fetchKeyDetailed, i as verifyRequest, j as fulfillAcceptSignature, l as generateCryptoKeyPair, o as exportJwk, r as signRequest, s as fetchKey, u as importJwk } from "./http-DVZxcZEr.js";
|
|
7
|
+
import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "./middleware-B2rDMvhv.js";
|
|
8
|
+
import { C as verifySignature, S as verifyJsonLd, _ as hasSignatureLike, a as verifyProof, b as signJsonLd, c as getKeyOwner, d as attachSignature, i as verifyObject, m as detachSignature, n as hasProofLike, p as createSignature, r as signObject, s as doesActorOwnKey, t as createProof } from "./proof-C_kcg7vF.js";
|
|
9
|
+
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-DTONwNqL.js";
|
|
10
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-B52kTPj4.js";
|
|
11
11
|
import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError } from "./federation/mod.js";
|
|
12
12
|
import "./nodeinfo/mod.js";
|
|
13
13
|
import "./runtime/mod.js";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as esm_default } from "../esm-vrlUxr60.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
6
|
import "../std__assert-BBjXFNOb.mjs";
|
|
7
|
-
import { a as parseProtocol, c as parseUsage, i as parseOutboundService, n as parseInboundService, o as parseServices, r as parseNodeInfo, s as parseSoftware, t as getNodeInfo } from "../client-
|
|
7
|
+
import { a as parseProtocol, c as parseUsage, i as parseOutboundService, n as parseInboundService, o as parseServices, r as parseNodeInfo, s as parseSoftware, t as getNodeInfo } from "../client-D1KuWxjH.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/nodeinfo/client.test.ts
|
|
10
10
|
test("getNodeInfo()", async (t) => {
|
|
@@ -62,6 +62,66 @@ test("getNodeInfo()", async (t) => {
|
|
|
62
62
|
});
|
|
63
63
|
esm_default.hardReset();
|
|
64
64
|
});
|
|
65
|
+
test("getNodeInfo() blocks SSRF", async (t) => {
|
|
66
|
+
esm_default.spyGlobal();
|
|
67
|
+
await t.step("link href pointing to a loopback address", async () => {
|
|
68
|
+
esm_default.removeRoutes();
|
|
69
|
+
esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
|
|
70
|
+
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
|
71
|
+
href: "http://127.0.0.1:8080/internal"
|
|
72
|
+
}] } });
|
|
73
|
+
let internalFetched = false;
|
|
74
|
+
esm_default.get("http://127.0.0.1:8080/internal", () => {
|
|
75
|
+
internalFetched = true;
|
|
76
|
+
return { body: { secret: "INTERNAL_DATA_LEAKED" } };
|
|
77
|
+
});
|
|
78
|
+
assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
|
|
79
|
+
assertEquals(internalFetched, false);
|
|
80
|
+
});
|
|
81
|
+
await t.step("link href using a data: URL", async () => {
|
|
82
|
+
esm_default.removeRoutes();
|
|
83
|
+
esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
|
|
84
|
+
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
|
85
|
+
href: "data:application/json,{\"software\":{\"name\":\"x\"}}"
|
|
86
|
+
}] } });
|
|
87
|
+
assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
|
|
88
|
+
});
|
|
89
|
+
await t.step("redirect to a private address", async () => {
|
|
90
|
+
esm_default.removeRoutes();
|
|
91
|
+
esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
|
|
92
|
+
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
|
93
|
+
href: "https://example.com/nodeinfo/2.1"
|
|
94
|
+
}] } });
|
|
95
|
+
esm_default.get("https://example.com/nodeinfo/2.1", {
|
|
96
|
+
status: 302,
|
|
97
|
+
headers: { Location: "https://127.0.0.1:8080/internal" }
|
|
98
|
+
});
|
|
99
|
+
let internalFetched = false;
|
|
100
|
+
esm_default.get("https://127.0.0.1:8080/internal", () => {
|
|
101
|
+
internalFetched = true;
|
|
102
|
+
return { body: { secret: "INTERNAL_DATA_LEAKED" } };
|
|
103
|
+
});
|
|
104
|
+
assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
|
|
105
|
+
assertEquals(internalFetched, false);
|
|
106
|
+
});
|
|
107
|
+
await t.step("direct fetch of a private address", async () => {
|
|
108
|
+
esm_default.removeRoutes();
|
|
109
|
+
let internalFetched = false;
|
|
110
|
+
esm_default.get("http://127.0.0.1:8080/nodeinfo", () => {
|
|
111
|
+
internalFetched = true;
|
|
112
|
+
return { body: { software: {
|
|
113
|
+
name: "x",
|
|
114
|
+
version: "1.0.0"
|
|
115
|
+
} } };
|
|
116
|
+
});
|
|
117
|
+
assertEquals(await getNodeInfo("http://127.0.0.1:8080/nodeinfo", {
|
|
118
|
+
direct: true,
|
|
119
|
+
parse: "none"
|
|
120
|
+
}), void 0);
|
|
121
|
+
assertEquals(internalFetched, false);
|
|
122
|
+
});
|
|
123
|
+
esm_default.hardReset();
|
|
124
|
+
});
|
|
65
125
|
test("parseNodeInfo()", () => {
|
|
66
126
|
const input = {
|
|
67
127
|
software: {
|
|
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-DVoTs_wM.mjs";
|
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
6
|
import "../std__assert-BBjXFNOb.mjs";
|
|
7
7
|
import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
|
|
8
|
-
import { _ as handleNodeInfo, o as createFederation, v as handleNodeInfoJrd } from "../middleware-
|
|
8
|
+
import { _ as handleNodeInfo, o as createFederation, v as handleNodeInfoJrd } from "../middleware-CubmqVku.mjs";
|
|
9
9
|
import { test } from "@fedify/fixture";
|
|
10
10
|
//#region src/nodeinfo/handler.test.ts
|
|
11
11
|
test("handleNodeInfo()", async () => {
|
package/dist/nodeinfo/mod.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { Temporal } = require("@js-temporal/polyfill");
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
-
const require_types = require("../types-
|
|
4
|
+
const require_types = require("../types-m8u9L86X.cjs");
|
|
5
5
|
exports.getNodeInfo = require_types.getNodeInfo;
|
|
6
6
|
exports.nodeInfoToJson = require_types.nodeInfoToJson;
|
|
7
7
|
exports.parseNodeInfo = require_types.parseNodeInfo;
|
package/dist/nodeinfo/mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "../types-
|
|
3
|
+
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "../types-DTONwNqL.js";
|
|
4
4
|
export { getNodeInfo, nodeInfoToJson, parseNodeInfo };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-B8lcQ4Ms.mjs";
|
|
5
|
+
import "./key-BqT9YrM3.mjs";
|
|
6
6
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
7
7
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
8
8
|
import { getDocumentLoader } from "@fedify/vocab-runtime";
|