@fedify/fedify 2.4.0-dev.1634 → 2.4.0-pr.934.40
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 +3 -0
- package/dist/{builder-DYAAegyy.mjs → builder-BBPwtB2v.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-lxX6I7as.d.cts → context-C05Rgxmu.d.cts} +1 -1
- package/dist/{context-Zii1f5xS.d.ts → context-Cjnr0ig1.d.ts} +1 -1
- package/dist/{deno-C30u61BX.mjs → deno-D3c0o_N0.mjs} +1 -1
- package/dist/{docloader-C8JfZ3bD.mjs → docloader-BCecHIxU.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +3 -5
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +288 -9
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +279 -1
- package/dist/federation/mq.test.mjs +20 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +39 -2
- package/dist/federation/tasks/tasks.test.mjs +3 -3
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-D_cl_PbN.js → http-Ctb_KhZ8.js} +1 -1
- package/dist/{http-DJEaNMSB.mjs → http-Dm0JAUa_.mjs} +3 -3
- package/dist/{http-DiMJDFtd.cjs → http-jr0TkgZe.cjs} +1 -1
- package/dist/{key-C1tsoBAW.mjs → key-C_gNKtxm.mjs} +2 -2
- package/dist/{kv-cache-D9Tia3Td.mjs → kv-cache-B7H7lZ84.mjs} +1 -1
- package/dist/{kv-cache-EL9jIFqA.js → kv-cache-BxUZRBC2.js} +1 -1
- package/dist/{kv-cache-BInLi0Ro.cjs → kv-cache-D-hbjJAD.cjs} +1 -1
- package/dist/{ld-CDBNQmZD.mjs → ld-DAPAVL_7.mjs} +3 -3
- package/dist/{metrics-De7b7Hgb.mjs → metrics-BMaatuZi.mjs} +1 -1
- package/dist/{metrics-CoUJLQhZ.d.cts → metrics-DN13mCAw.d.cts} +19 -1
- package/dist/{metrics-Dc4jR-IC.d.ts → metrics-v_e0n5GQ.d.ts} +19 -1
- package/dist/{middleware-BPHpdYl4.cjs → middleware-BW7WQltk.cjs} +40 -325
- package/dist/{middleware-8oyKaRYF.js → middleware-CfZR_riY.js} +6 -278
- package/dist/{middleware-DloBgcWp.mjs → middleware-DfPBAh9L.mjs} +1 -1
- package/dist/{middleware-CPlgpBML.mjs → middleware-O2LHXFEk.mjs} +14 -15
- package/dist/{mod-BtEaIEcD.d.cts → mod-BrpVsSfY.d.cts} +1 -1
- package/dist/{mod-DLc2Ot6f.d.ts → mod-BsCE5Ihq.d.ts} +1 -1
- package/dist/{mod-UMmveiO0.d.ts → mod-CNef4in0.d.ts} +1 -1
- package/dist/{mod-gOlPDuT8.d.cts → mod-DdONo-ep.d.cts} +1 -1
- package/dist/mod.cjs +6 -6
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +5 -5
- package/dist/{mq-llHKivST.mjs → mq-Bo7Etgh6.mjs} +6 -0
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-CB3dPva0.mjs → owner-Dcx-czqn.mjs} +2 -2
- package/dist/{proof-BqDtIxGz.mjs → proof-BkAxvrjt.mjs} +3 -3
- package/dist/{proof-DT5SgzkH.js → proof-D-H_PnJW.js} +1 -1
- package/dist/{proof-BI4Wn6Ur.cjs → proof-NY0D8Oux.cjs} +1 -1
- package/dist/{send-ClWims8x.mjs → send-D_j7E-oC.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-DyITEDcU.mjs → temporal-yE5EcfNX.mjs} +1 -1
- package/dist/testing/mod.d.mts +14 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/skills/fedify/SKILL.md +462 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_chunk = require("./chunk-Czpeue4M.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-DGwl-hD1.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-jr0TkgZe.cjs");
|
|
5
5
|
const require_temporal_polyfill = require("./temporal-polyfill-BCdTHexd.cjs");
|
|
6
|
-
const require_proof = require("./proof-
|
|
6
|
+
const require_proof = require("./proof-NY0D8Oux.cjs");
|
|
7
7
|
const require_types = require("./types-CPym83zc.cjs");
|
|
8
|
-
const require_kv_cache = require("./kv-cache-
|
|
8
|
+
const require_kv_cache = require("./kv-cache-D-hbjJAD.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");
|
|
@@ -723,7 +723,7 @@ function createFederationBuilder() {
|
|
|
723
723
|
}
|
|
724
724
|
//#endregion
|
|
725
725
|
//#region src/federation/circuit-breaker.ts
|
|
726
|
-
const Temporal$
|
|
726
|
+
const Temporal$7 = require_temporal_polyfill.Temporal;
|
|
727
727
|
const MAX_CUSTOM_FAILURE_HISTORY = 100;
|
|
728
728
|
/**
|
|
729
729
|
* Tracks reachability state for remote outbox delivery hosts.
|
|
@@ -739,7 +739,7 @@ var CircuitBreaker = class {
|
|
|
739
739
|
this.#kv = options.kv;
|
|
740
740
|
this.#prefix = options.prefix;
|
|
741
741
|
this.#options = normalizeCircuitBreakerOptions(options.options ?? {});
|
|
742
|
-
this.#now = options.now ?? (() => Temporal$
|
|
742
|
+
this.#now = options.now ?? (() => Temporal$7.Now.instant());
|
|
743
743
|
this.#stateChangeObserver = options.stateChangeObserver;
|
|
744
744
|
}
|
|
745
745
|
get options() {
|
|
@@ -752,7 +752,7 @@ var CircuitBreaker = class {
|
|
|
752
752
|
async beforeSend(remoteHost, message) {
|
|
753
753
|
const heldSince = parseHeldSince(message.circuitHeldSince);
|
|
754
754
|
const now = this.#now();
|
|
755
|
-
if (heldSince != null && Temporal$
|
|
755
|
+
if (heldSince != null && Temporal$7.Instant.compare(heldSince.add(this.#options.heldActivityTtl), now) <= 0) return {
|
|
756
756
|
type: "drop",
|
|
757
757
|
heldSince
|
|
758
758
|
};
|
|
@@ -764,12 +764,12 @@ var CircuitBreaker = class {
|
|
|
764
764
|
probe: false
|
|
765
765
|
};
|
|
766
766
|
if (oldState.state === "half-open") {
|
|
767
|
-
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$
|
|
767
|
+
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$7.Instant.from(oldState.halfOpened);
|
|
768
768
|
if (halfOpened != null) {
|
|
769
769
|
const staleAt = halfOpened.add(this.#options.recoveryDelay);
|
|
770
|
-
if (Temporal$
|
|
770
|
+
if (Temporal$7.Instant.compare(now, staleAt) < 0) {
|
|
771
771
|
const releaseAt = now.add(this.#options.releaseInterval);
|
|
772
|
-
const retryAt = Temporal$
|
|
772
|
+
const retryAt = Temporal$7.Instant.compare(releaseAt, staleAt) < 0 ? releaseAt : staleAt;
|
|
773
773
|
const cappedRetryAt = this.#capHeldRetryAt(now, heldSince, retryAt);
|
|
774
774
|
return {
|
|
775
775
|
type: "hold",
|
|
@@ -791,8 +791,8 @@ var CircuitBreaker = class {
|
|
|
791
791
|
lastConflictingState = "half-open";
|
|
792
792
|
continue;
|
|
793
793
|
}
|
|
794
|
-
const probeAt = (oldState.opened == null ? now : Temporal$
|
|
795
|
-
if (Temporal$
|
|
794
|
+
const probeAt = (oldState.opened == null ? now : Temporal$7.Instant.from(oldState.opened)).add(this.#options.recoveryDelay);
|
|
795
|
+
if (Temporal$7.Instant.compare(now, probeAt) < 0) {
|
|
796
796
|
const retryAt = this.#capHeldRetryAt(now, heldSince, probeAt);
|
|
797
797
|
return {
|
|
798
798
|
type: "hold",
|
|
@@ -855,7 +855,7 @@ var CircuitBreaker = class {
|
|
|
855
855
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
856
856
|
const oldState = await this.#get(remoteHost);
|
|
857
857
|
if (oldState?.state === "open") return void 0;
|
|
858
|
-
const oldFailures = oldState?.failures.map(Temporal$
|
|
858
|
+
const oldFailures = oldState?.failures.map(Temporal$7.Instant.from) ?? [];
|
|
859
859
|
const failures = this.#options.pruneFailures([...oldFailures, now], now);
|
|
860
860
|
let newState;
|
|
861
861
|
let transition;
|
|
@@ -905,7 +905,7 @@ var CircuitBreaker = class {
|
|
|
905
905
|
}
|
|
906
906
|
#capHeldRetryAt(now, heldSince, retryAt) {
|
|
907
907
|
const expiresAt = (heldSince ?? now).add(this.#options.heldActivityTtl);
|
|
908
|
-
return Temporal$
|
|
908
|
+
return Temporal$7.Instant.compare(expiresAt, retryAt) < 0 ? expiresAt : retryAt;
|
|
909
909
|
}
|
|
910
910
|
async #get(remoteHost) {
|
|
911
911
|
return parseCircuitBreakerKvState(await this.#kv.get(this.#key(remoteHost)));
|
|
@@ -976,13 +976,13 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
976
976
|
assertPositiveDuration(failureWindow, "failureWindow");
|
|
977
977
|
pruneFailures = (timestamps, now) => {
|
|
978
978
|
const earliest = now.subtract(failureWindow);
|
|
979
|
-
return timestamps.filter((timestamp) => Temporal$
|
|
979
|
+
return timestamps.filter((timestamp) => Temporal$7.Instant.compare(timestamp, earliest) >= 0).slice(-failureThreshold);
|
|
980
980
|
};
|
|
981
981
|
failure = (timestamps) => {
|
|
982
982
|
if (timestamps.length < failureThreshold) return false;
|
|
983
983
|
const first = timestamps[timestamps.length - failureThreshold];
|
|
984
984
|
const last = timestamps[timestamps.length - 1];
|
|
985
|
-
return Temporal$
|
|
985
|
+
return Temporal$7.Duration.compare(first.until(last), failureWindow) <= 0;
|
|
986
986
|
};
|
|
987
987
|
} else {
|
|
988
988
|
failure = options.failure;
|
|
@@ -999,19 +999,19 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
999
999
|
};
|
|
1000
1000
|
}
|
|
1001
1001
|
function toInstantDuration(duration) {
|
|
1002
|
-
const parsed = Temporal$
|
|
1003
|
-
return Temporal$
|
|
1002
|
+
const parsed = Temporal$7.Duration.from(duration);
|
|
1003
|
+
return Temporal$7.Duration.from({ milliseconds: Math.trunc(parsed.total({
|
|
1004
1004
|
unit: "millisecond",
|
|
1005
|
-
relativeTo: Temporal$
|
|
1005
|
+
relativeTo: Temporal$7.PlainDateTime.from("2026-01-01T00:00:00")
|
|
1006
1006
|
})) });
|
|
1007
1007
|
}
|
|
1008
1008
|
function assertPositiveDuration(duration, name) {
|
|
1009
|
-
if (Temporal$
|
|
1009
|
+
if (Temporal$7.Duration.compare(duration, { seconds: 0 }) <= 0) throw new RangeError(`${name} must be a positive duration.`);
|
|
1010
1010
|
}
|
|
1011
1011
|
function parseHeldSince(value) {
|
|
1012
1012
|
if (value == null) return void 0;
|
|
1013
1013
|
try {
|
|
1014
|
-
return Temporal$
|
|
1014
|
+
return Temporal$7.Instant.from(value);
|
|
1015
1015
|
} catch (error) {
|
|
1016
1016
|
(0, _logtape_logtape.getLogger)([
|
|
1017
1017
|
"fedify",
|
|
@@ -1035,7 +1035,7 @@ function parseCircuitBreakerKvState(value) {
|
|
|
1035
1035
|
const isInstantString = (v) => {
|
|
1036
1036
|
if (typeof v !== "string") return false;
|
|
1037
1037
|
try {
|
|
1038
|
-
Temporal$
|
|
1038
|
+
Temporal$7.Instant.from(v);
|
|
1039
1039
|
return true;
|
|
1040
1040
|
} catch {
|
|
1041
1041
|
return false;
|
|
@@ -1095,7 +1095,7 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
1095
1095
|
}
|
|
1096
1096
|
//#endregion
|
|
1097
1097
|
//#region src/federation/inbox.ts
|
|
1098
|
-
const Temporal$
|
|
1098
|
+
const Temporal$6 = require_temporal_polyfill.Temporal;
|
|
1099
1099
|
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
1100
1100
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
1101
1101
|
"fedify",
|
|
@@ -1242,7 +1242,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
1242
1242
|
return "error";
|
|
1243
1243
|
}
|
|
1244
1244
|
require_http.recordInboxActivity(meterProvider, "processed", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
1245
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$
|
|
1245
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$6.Duration.from({ days: 1 }) });
|
|
1246
1246
|
logger.info("Activity {activityId} has been processed.", {
|
|
1247
1247
|
activityId: activity.id?.href,
|
|
1248
1248
|
activity: json,
|
|
@@ -1254,7 +1254,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
1254
1254
|
}
|
|
1255
1255
|
//#endregion
|
|
1256
1256
|
//#region src/federation/keycache.ts
|
|
1257
|
-
const Temporal$
|
|
1257
|
+
const Temporal$5 = require_temporal_polyfill.Temporal;
|
|
1258
1258
|
var KvKeyCache = class {
|
|
1259
1259
|
kv;
|
|
1260
1260
|
prefix;
|
|
@@ -1265,7 +1265,7 @@ var KvKeyCache = class {
|
|
|
1265
1265
|
this.kv = kv;
|
|
1266
1266
|
this.prefix = prefix;
|
|
1267
1267
|
this.options = options;
|
|
1268
|
-
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$
|
|
1268
|
+
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$5.Duration.from({ minutes: 10 });
|
|
1269
1269
|
this.nullKeys = /* @__PURE__ */ new Map();
|
|
1270
1270
|
}
|
|
1271
1271
|
#getFetchErrorKey(keyId) {
|
|
@@ -1278,13 +1278,13 @@ var KvKeyCache = class {
|
|
|
1278
1278
|
async get(keyId) {
|
|
1279
1279
|
const negativeExpiration = this.nullKeys.get(keyId.href);
|
|
1280
1280
|
if (negativeExpiration != null) {
|
|
1281
|
-
if (Temporal$
|
|
1281
|
+
if (Temporal$5.Now.instant().until(negativeExpiration).sign >= 0) return null;
|
|
1282
1282
|
this.nullKeys.delete(keyId.href);
|
|
1283
1283
|
}
|
|
1284
1284
|
const serialized = await this.kv.get([...this.prefix, keyId.href]);
|
|
1285
1285
|
if (serialized === void 0) return void 0;
|
|
1286
1286
|
if (serialized === null) {
|
|
1287
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
1287
|
+
this.nullKeys.set(keyId.href, Temporal$5.Now.instant().add(this.unavailableKeyTtl));
|
|
1288
1288
|
return null;
|
|
1289
1289
|
}
|
|
1290
1290
|
try {
|
|
@@ -1300,7 +1300,7 @@ var KvKeyCache = class {
|
|
|
1300
1300
|
}
|
|
1301
1301
|
async set(keyId, key) {
|
|
1302
1302
|
if (key == null) {
|
|
1303
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
1303
|
+
this.nullKeys.set(keyId.href, Temporal$5.Now.instant().add(this.unavailableKeyTtl));
|
|
1304
1304
|
await this.kv.set([...this.prefix, keyId.href], null, { ttl: this.unavailableKeyTtl });
|
|
1305
1305
|
return;
|
|
1306
1306
|
}
|
|
@@ -1439,7 +1439,7 @@ function acceptsJsonLd(request) {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
//#endregion
|
|
1441
1441
|
//#region src/federation/temporal.ts
|
|
1442
|
-
const Temporal$
|
|
1442
|
+
const Temporal$4 = require_temporal_polyfill.Temporal;
|
|
1443
1443
|
function isPlainObject$1(value) {
|
|
1444
1444
|
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1445
1445
|
}
|
|
@@ -1449,7 +1449,7 @@ function normalizeDateTimeLiteral(value) {
|
|
|
1449
1449
|
function isMalformedDateTimeLiteral(value) {
|
|
1450
1450
|
if (typeof value !== "string") return false;
|
|
1451
1451
|
try {
|
|
1452
|
-
Temporal$
|
|
1452
|
+
Temporal$4.Instant.from(normalizeDateTimeLiteral(value));
|
|
1453
1453
|
return false;
|
|
1454
1454
|
} catch {
|
|
1455
1455
|
return true;
|
|
@@ -1458,7 +1458,7 @@ function isMalformedDateTimeLiteral(value) {
|
|
|
1458
1458
|
function isMalformedDurationLiteral(value) {
|
|
1459
1459
|
if (typeof value !== "string") return false;
|
|
1460
1460
|
try {
|
|
1461
|
-
Temporal$
|
|
1461
|
+
Temporal$4.Duration.from(value);
|
|
1462
1462
|
return false;
|
|
1463
1463
|
} catch {
|
|
1464
1464
|
return true;
|
|
@@ -1529,7 +1529,7 @@ async function hasMalformedKnownTemporalLiteral(value, contextLoader) {
|
|
|
1529
1529
|
}
|
|
1530
1530
|
//#endregion
|
|
1531
1531
|
//#region src/federation/handler.ts
|
|
1532
|
-
const Temporal$
|
|
1532
|
+
const Temporal$3 = require_temporal_polyfill.Temporal;
|
|
1533
1533
|
const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
|
|
1534
1534
|
function isRemoteContextLoadingFailure$1(error) {
|
|
1535
1535
|
return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
|
|
@@ -3284,7 +3284,7 @@ async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
|
|
|
3284
3284
|
}
|
|
3285
3285
|
async function setKey(kv, key, policy) {
|
|
3286
3286
|
const seconds = policy.nonceTtlSeconds ?? 300;
|
|
3287
|
-
const ttl = Temporal$
|
|
3287
|
+
const ttl = Temporal$3.Duration.from({ seconds });
|
|
3288
3288
|
await kv.set(key, true, { ttl });
|
|
3289
3289
|
}
|
|
3290
3290
|
const DEF_COMPONENTS = [
|
|
@@ -3759,7 +3759,7 @@ function serializeDataPointType(dataPointType) {
|
|
|
3759
3759
|
}
|
|
3760
3760
|
//#endregion
|
|
3761
3761
|
//#region src/federation/retry.ts
|
|
3762
|
-
const Temporal$
|
|
3762
|
+
const Temporal$2 = require_temporal_polyfill.Temporal;
|
|
3763
3763
|
/**
|
|
3764
3764
|
* Creates an exponential backoff retry policy. The delay between retries
|
|
3765
3765
|
* starts at the `initialDelay` and is multiplied by the `factor` for each
|
|
@@ -3771,8 +3771,8 @@ const Temporal$3 = require_temporal_polyfill.Temporal;
|
|
|
3771
3771
|
* @since 0.12.0
|
|
3772
3772
|
*/
|
|
3773
3773
|
function createExponentialBackoffPolicy(options = {}) {
|
|
3774
|
-
const initialDelay = Temporal$
|
|
3775
|
-
const maxDelay = Temporal$
|
|
3774
|
+
const initialDelay = Temporal$2.Duration.from(options.initialDelay ?? { seconds: 1 });
|
|
3775
|
+
const maxDelay = Temporal$2.Duration.from(options.maxDelay ?? { hours: 12 });
|
|
3776
3776
|
const maxAttempts = options.maxAttempts ?? 10;
|
|
3777
3777
|
const factor = options.factor ?? 2;
|
|
3778
3778
|
const jitter = options.jitter ?? true;
|
|
@@ -3784,8 +3784,8 @@ function createExponentialBackoffPolicy(options = {}) {
|
|
|
3784
3784
|
milliseconds *= 1 + Math.random();
|
|
3785
3785
|
milliseconds = Math.round(milliseconds);
|
|
3786
3786
|
}
|
|
3787
|
-
const delay = Temporal$
|
|
3788
|
-
return Temporal$
|
|
3787
|
+
const delay = Temporal$2.Duration.from({ milliseconds });
|
|
3788
|
+
return Temporal$2.Duration.compare(delay, maxDelay) > 0 ? maxDelay : delay;
|
|
3789
3789
|
};
|
|
3790
3790
|
}
|
|
3791
3791
|
//#endregion
|
|
@@ -3907,279 +3907,6 @@ const classReviver = (filter, init, set) => (seen, revive, node) => {
|
|
|
3907
3907
|
})();
|
|
3908
3908
|
};
|
|
3909
3909
|
//#endregion
|
|
3910
|
-
//#region src/federation/mq.ts
|
|
3911
|
-
const Temporal$2 = require_temporal_polyfill.Temporal;
|
|
3912
|
-
/**
|
|
3913
|
-
* A message queue that processes messages in the same process.
|
|
3914
|
-
* Do not use this in production as it does neither persist messages nor
|
|
3915
|
-
* distribute them across multiple processes.
|
|
3916
|
-
*
|
|
3917
|
-
* @since 0.5.0
|
|
3918
|
-
*/
|
|
3919
|
-
var InProcessMessageQueue = class {
|
|
3920
|
-
#messages;
|
|
3921
|
-
#monitors;
|
|
3922
|
-
#pollIntervalMs;
|
|
3923
|
-
#delayedMessages;
|
|
3924
|
-
/**
|
|
3925
|
-
* Tracks which ordering keys are currently being processed to ensure
|
|
3926
|
-
* sequential processing for messages with the same key.
|
|
3927
|
-
*/
|
|
3928
|
-
#processingKeys;
|
|
3929
|
-
/**
|
|
3930
|
-
* In-process message queue does not provide native retry mechanisms.
|
|
3931
|
-
* @since 1.7.0
|
|
3932
|
-
*/
|
|
3933
|
-
nativeRetrial = false;
|
|
3934
|
-
/**
|
|
3935
|
-
* In-process message queue does not deduplicate messages natively.
|
|
3936
|
-
* @since 2.4.0
|
|
3937
|
-
*/
|
|
3938
|
-
nativeDeduplication = false;
|
|
3939
|
-
/**
|
|
3940
|
-
* Constructs a new {@link InProcessMessageQueue} with the given options.
|
|
3941
|
-
* @param options Additional options for the in-process message queue.
|
|
3942
|
-
*/
|
|
3943
|
-
constructor(options = {}) {
|
|
3944
|
-
this.#messages = [];
|
|
3945
|
-
this.#monitors = {};
|
|
3946
|
-
this.#pollIntervalMs = Temporal$2.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
3947
|
-
this.#delayedMessages = 0;
|
|
3948
|
-
this.#processingKeys = /* @__PURE__ */ new Set();
|
|
3949
|
-
}
|
|
3950
|
-
enqueue(message, options) {
|
|
3951
|
-
const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
3952
|
-
if (delay > 0) {
|
|
3953
|
-
this.#delayedMessages++;
|
|
3954
|
-
setTimeout(() => {
|
|
3955
|
-
this.#delayedMessages--;
|
|
3956
|
-
this.#enqueueReady(message, options);
|
|
3957
|
-
}, delay);
|
|
3958
|
-
return Promise.resolve();
|
|
3959
|
-
}
|
|
3960
|
-
this.#enqueueReady(message, options);
|
|
3961
|
-
return Promise.resolve();
|
|
3962
|
-
}
|
|
3963
|
-
#enqueueReady(message, options) {
|
|
3964
|
-
const orderingKey = options?.orderingKey ?? null;
|
|
3965
|
-
this.#messages.push({
|
|
3966
|
-
message,
|
|
3967
|
-
orderingKey
|
|
3968
|
-
});
|
|
3969
|
-
this.#notifyMonitors();
|
|
3970
|
-
}
|
|
3971
|
-
#notifyMonitors() {
|
|
3972
|
-
for (const monitorId in this.#monitors) this.#monitors[monitorId]();
|
|
3973
|
-
}
|
|
3974
|
-
enqueueMany(messages, options) {
|
|
3975
|
-
if (messages.length === 0) return Promise.resolve();
|
|
3976
|
-
const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
|
|
3977
|
-
if (delay > 0) {
|
|
3978
|
-
const delayedCount = messages.length;
|
|
3979
|
-
const deferredMessages = [...messages];
|
|
3980
|
-
this.#delayedMessages += delayedCount;
|
|
3981
|
-
setTimeout(() => {
|
|
3982
|
-
this.#delayedMessages -= delayedCount;
|
|
3983
|
-
this.#enqueueManyReady(deferredMessages, options);
|
|
3984
|
-
}, delay);
|
|
3985
|
-
return Promise.resolve();
|
|
3986
|
-
}
|
|
3987
|
-
this.#enqueueManyReady(messages, options);
|
|
3988
|
-
return Promise.resolve();
|
|
3989
|
-
}
|
|
3990
|
-
#enqueueManyReady(messages, options) {
|
|
3991
|
-
const orderingKey = options?.orderingKey ?? null;
|
|
3992
|
-
for (const message of messages) this.#messages.push({
|
|
3993
|
-
message,
|
|
3994
|
-
orderingKey
|
|
3995
|
-
});
|
|
3996
|
-
this.#notifyMonitors();
|
|
3997
|
-
}
|
|
3998
|
-
async listen(handler, options = {}) {
|
|
3999
|
-
const signal = options.signal;
|
|
4000
|
-
while (signal == null || !signal.aborted) {
|
|
4001
|
-
const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
|
|
4002
|
-
if (idx >= 0) {
|
|
4003
|
-
const { message, orderingKey } = this.#messages.splice(idx, 1)[0];
|
|
4004
|
-
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
4005
|
-
try {
|
|
4006
|
-
await handler(message);
|
|
4007
|
-
} finally {
|
|
4008
|
-
if (orderingKey != null) this.#processingKeys.delete(orderingKey);
|
|
4009
|
-
}
|
|
4010
|
-
} else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
|
|
4011
|
-
else await this.#wait(10, signal);
|
|
4012
|
-
}
|
|
4013
|
-
}
|
|
4014
|
-
getDepth() {
|
|
4015
|
-
const ready = this.#messages.length;
|
|
4016
|
-
const delayed = this.#delayedMessages;
|
|
4017
|
-
return Promise.resolve({
|
|
4018
|
-
queued: ready + delayed,
|
|
4019
|
-
ready,
|
|
4020
|
-
delayed
|
|
4021
|
-
});
|
|
4022
|
-
}
|
|
4023
|
-
#wait(ms, signal) {
|
|
4024
|
-
let timer = null;
|
|
4025
|
-
return Promise.any([new Promise((resolve) => {
|
|
4026
|
-
signal?.addEventListener("abort", () => {
|
|
4027
|
-
if (timer != null) clearTimeout(timer);
|
|
4028
|
-
resolve();
|
|
4029
|
-
}, { once: true });
|
|
4030
|
-
const monitorId = crypto.randomUUID();
|
|
4031
|
-
this.#monitors[monitorId] = () => {
|
|
4032
|
-
delete this.#monitors[monitorId];
|
|
4033
|
-
if (timer != null) clearTimeout(timer);
|
|
4034
|
-
resolve();
|
|
4035
|
-
};
|
|
4036
|
-
}), new Promise((resolve) => timer = setTimeout(resolve, ms))]);
|
|
4037
|
-
}
|
|
4038
|
-
};
|
|
4039
|
-
/**
|
|
4040
|
-
* A message queue that processes messages in parallel. It takes another
|
|
4041
|
-
* {@link MessageQueue}, and processes messages in parallel up to a certain
|
|
4042
|
-
* number of workers.
|
|
4043
|
-
*
|
|
4044
|
-
* Actually, it's rather a decorator than a queue itself.
|
|
4045
|
-
*
|
|
4046
|
-
* Note that the workers do not run in truly parallel, in the sense that they
|
|
4047
|
-
* are not running in separate threads or processes. They are running in the
|
|
4048
|
-
* same process, but are scheduled to run in parallel. Hence, this is useful
|
|
4049
|
-
* for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
|
|
4050
|
-
* workloads.
|
|
4051
|
-
*
|
|
4052
|
-
* When using `ParallelMessageQueue`, the ordering guarantee is preserved
|
|
4053
|
-
* *only if* the underlying queue implementation delivers messages in a wrapper
|
|
4054
|
-
* format that includes the `__fedify_ordering_key__` property. Currently,
|
|
4055
|
-
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
4056
|
-
* For other queue implementations (e.g., `InProcessMessageQueue`,
|
|
4057
|
-
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
4058
|
-
* `AmqpMessageQueue`), the ordering key cannot be detected by
|
|
4059
|
-
* `ParallelMessageQueue`, so ordering guarantees are handled by those
|
|
4060
|
-
* implementations directly rather than at the `ParallelMessageQueue` level.
|
|
4061
|
-
*
|
|
4062
|
-
* Messages with the same ordering key will never be processed concurrently
|
|
4063
|
-
* by different workers, ensuring sequential processing within each key.
|
|
4064
|
-
* Messages with different ordering keys (or no ordering key) can still be
|
|
4065
|
-
* processed in parallel.
|
|
4066
|
-
*
|
|
4067
|
-
* @since 1.0.0
|
|
4068
|
-
*/
|
|
4069
|
-
var ParallelMessageQueue = class ParallelMessageQueue {
|
|
4070
|
-
queue;
|
|
4071
|
-
workers;
|
|
4072
|
-
/**
|
|
4073
|
-
* Inherits the native retry capability from the wrapped queue.
|
|
4074
|
-
* @since 1.7.0
|
|
4075
|
-
*/
|
|
4076
|
-
nativeRetrial;
|
|
4077
|
-
/**
|
|
4078
|
-
* Inherits the native deduplication capability from the wrapped queue.
|
|
4079
|
-
* @since 2.4.0
|
|
4080
|
-
*/
|
|
4081
|
-
nativeDeduplication;
|
|
4082
|
-
getDepth;
|
|
4083
|
-
/**
|
|
4084
|
-
* Tracks which ordering keys are currently being processed to ensure
|
|
4085
|
-
* sequential processing for messages with the same key.
|
|
4086
|
-
*/
|
|
4087
|
-
#processingKeys = /* @__PURE__ */ new Set();
|
|
4088
|
-
/**
|
|
4089
|
-
* Pending messages waiting for their ordering key to become available.
|
|
4090
|
-
*/
|
|
4091
|
-
#pendingMessages = [];
|
|
4092
|
-
/**
|
|
4093
|
-
* Constructs a new {@link ParallelMessageQueue} with the given queue and
|
|
4094
|
-
* number of workers.
|
|
4095
|
-
* @param queue The message queue to use under the hood. Note that
|
|
4096
|
-
* {@link ParallelMessageQueue} cannot be nested.
|
|
4097
|
-
* @param workers The number of workers to process messages in parallel.
|
|
4098
|
-
* @throws {TypeError} If the given queue is an instance of
|
|
4099
|
-
* {@link ParallelMessageQueue}.
|
|
4100
|
-
*/
|
|
4101
|
-
constructor(queue, workers) {
|
|
4102
|
-
if (queue instanceof ParallelMessageQueue) throw new TypeError("Cannot nest ParallelMessageQueue.");
|
|
4103
|
-
this.queue = queue;
|
|
4104
|
-
this.workers = workers;
|
|
4105
|
-
this.nativeRetrial = queue.nativeRetrial;
|
|
4106
|
-
this.nativeDeduplication = queue.nativeDeduplication;
|
|
4107
|
-
if (queue.getDepth != null) this.getDepth = () => queue.getDepth();
|
|
4108
|
-
}
|
|
4109
|
-
enqueue(message, options) {
|
|
4110
|
-
return this.queue.enqueue(message, options);
|
|
4111
|
-
}
|
|
4112
|
-
async enqueueMany(messages, options) {
|
|
4113
|
-
if (this.queue.enqueueMany == null) {
|
|
4114
|
-
if (messages.length === 1) {
|
|
4115
|
-
await this.queue.enqueue(messages[0], options);
|
|
4116
|
-
return;
|
|
4117
|
-
}
|
|
4118
|
-
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.");
|
|
4119
|
-
const errors = (await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)))).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4120
|
-
if (errors.length > 1) throw new AggregateError(errors, "Failed to enqueue messages.");
|
|
4121
|
-
else if (errors.length === 1) throw errors[0];
|
|
4122
|
-
return;
|
|
4123
|
-
}
|
|
4124
|
-
await this.queue.enqueueMany(messages, options);
|
|
4125
|
-
}
|
|
4126
|
-
/**
|
|
4127
|
-
* Extracts ordering key from a message if present.
|
|
4128
|
-
*
|
|
4129
|
-
* This method only works for queue implementations that deliver messages
|
|
4130
|
-
* in the wrapper format with `__fedify_ordering_key__` property. Currently,
|
|
4131
|
-
* only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
|
|
4132
|
-
*
|
|
4133
|
-
* For other queue implementations (`InProcessMessageQueue`,
|
|
4134
|
-
* `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
|
|
4135
|
-
* `AmqpMessageQueue`), messages are delivered as raw payloads without the
|
|
4136
|
-
* wrapper, so the ordering key cannot be detected here. Those
|
|
4137
|
-
* implementations handle ordering guarantees internally.
|
|
4138
|
-
*/
|
|
4139
|
-
#extractOrderingKey(message) {
|
|
4140
|
-
if (message != null && typeof message === "object") {
|
|
4141
|
-
if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
|
|
4142
|
-
}
|
|
4143
|
-
}
|
|
4144
|
-
listen(handler, options = {}) {
|
|
4145
|
-
const workers = /* @__PURE__ */ new Map();
|
|
4146
|
-
return this.queue.listen(async (message) => {
|
|
4147
|
-
while (workers.size >= this.workers) {
|
|
4148
|
-
const consumedId = await Promise.any(workers.values());
|
|
4149
|
-
workers.delete(consumedId);
|
|
4150
|
-
}
|
|
4151
|
-
const workerId = crypto.randomUUID();
|
|
4152
|
-
const orderingKey = this.#extractOrderingKey(message);
|
|
4153
|
-
if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
|
|
4154
|
-
this.#pendingMessages.push({
|
|
4155
|
-
message,
|
|
4156
|
-
orderingKey,
|
|
4157
|
-
resolve
|
|
4158
|
-
});
|
|
4159
|
-
});
|
|
4160
|
-
if (orderingKey != null) this.#processingKeys.add(orderingKey);
|
|
4161
|
-
const promise = this.#work(workerId, handler, message, orderingKey);
|
|
4162
|
-
workers.set(workerId, promise);
|
|
4163
|
-
}, options);
|
|
4164
|
-
}
|
|
4165
|
-
async #work(workerId, handler, message, orderingKey) {
|
|
4166
|
-
await this.#sleep(0);
|
|
4167
|
-
try {
|
|
4168
|
-
await handler(message);
|
|
4169
|
-
} finally {
|
|
4170
|
-
if (orderingKey != null) {
|
|
4171
|
-
this.#processingKeys.delete(orderingKey);
|
|
4172
|
-
const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
|
|
4173
|
-
if (pendingIdx >= 0) this.#pendingMessages.splice(pendingIdx, 1)[0].resolve();
|
|
4174
|
-
}
|
|
4175
|
-
}
|
|
4176
|
-
return workerId;
|
|
4177
|
-
}
|
|
4178
|
-
#sleep(ms) {
|
|
4179
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4180
|
-
}
|
|
4181
|
-
};
|
|
4182
|
-
//#endregion
|
|
4183
3910
|
//#region src/federation/tasks/enqueue.ts
|
|
4184
3911
|
/**
|
|
4185
3912
|
* The enqueue pipeline for custom background tasks. `ContextImpl.enqueueTask`
|
|
@@ -4247,8 +3974,8 @@ function planDeduplication(ctx, queue, taskName, options, itemCount) {
|
|
|
4247
3974
|
const key = options.deduplicationKey;
|
|
4248
3975
|
const native = queue.nativeDeduplication === true;
|
|
4249
3976
|
const canCas = ctx.federation.kv.cas != null;
|
|
4250
|
-
const canBatchAtomically = queue.enqueueMany != null &&
|
|
4251
|
-
if (itemCount > 1 && !canBatchAtomically && (native || canCas)) throw new TypeError(`Task ${JSON.stringify(taskName)} was enqueued as a batch with a deduplicationKey, but its message queue does not
|
|
3977
|
+
const canBatchAtomically = queue.enqueueMany != null && queue.atomicEnqueueMany !== false;
|
|
3978
|
+
if (itemCount > 1 && !canBatchAtomically && (native || canCas)) throw new TypeError(`Task ${JSON.stringify(taskName)} was enqueued as a batch with a deduplicationKey, but its message queue does not provide an atomic enqueueMany implementation; a multi-item batch cannot be deduplicated atomically without it. Use a queue with atomic batch enqueueing, or enqueue the tasks individually with enqueueTask().`);
|
|
4252
3979
|
if (native) return {
|
|
4253
3980
|
kind: "native",
|
|
4254
3981
|
key
|
|
@@ -7420,18 +7147,6 @@ Object.defineProperty(exports, "CircuitBreaker", {
|
|
|
7420
7147
|
return CircuitBreaker;
|
|
7421
7148
|
}
|
|
7422
7149
|
});
|
|
7423
|
-
Object.defineProperty(exports, "InProcessMessageQueue", {
|
|
7424
|
-
enumerable: true,
|
|
7425
|
-
get: function() {
|
|
7426
|
-
return InProcessMessageQueue;
|
|
7427
|
-
}
|
|
7428
|
-
});
|
|
7429
|
-
Object.defineProperty(exports, "ParallelMessageQueue", {
|
|
7430
|
-
enumerable: true,
|
|
7431
|
-
get: function() {
|
|
7432
|
-
return ParallelMessageQueue;
|
|
7433
|
-
}
|
|
7434
|
-
});
|
|
7435
7150
|
Object.defineProperty(exports, "SendActivityError", {
|
|
7436
7151
|
enumerable: true,
|
|
7437
7152
|
get: function() {
|