@vsaas/loopback 12.1.1 → 12.2.0
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 +6 -0
- package/common/models/rate-limit-state.json +121 -0
- package/common/models/two-factor-challenge.json +112 -0
- package/common/models/two-factor-credential.json +117 -0
- package/common/models/two-factor-recovery-code.json +63 -0
- package/common/models/user.json +30 -0
- package/dist/_virtual/_rolldown/runtime.cjs +9 -2
- package/dist/common/models/access-token.d.cts +3 -0
- package/dist/common/models/acl.cjs +1 -3
- package/dist/common/models/acl.d.cts +1 -0
- package/dist/common/models/application.d.cts +3 -0
- package/dist/common/models/change.cjs +21 -18
- package/dist/common/models/change.d.cts +1 -0
- package/dist/common/models/checkpoint.d.cts +3 -0
- package/dist/common/models/email.d.cts +3 -0
- package/dist/common/models/key-value-model.d.cts +3 -0
- package/dist/common/models/rate-limit-state.cjs +7 -0
- package/dist/common/models/rate-limit-state.d.cts +3 -0
- package/dist/common/models/rate-limit-state2.cjs +109 -0
- package/dist/common/models/role-mapping.d.cts +3 -0
- package/dist/common/models/role.cjs +12 -8
- package/dist/common/models/role.d.cts +1 -0
- package/dist/common/models/scope.d.cts +3 -0
- package/dist/common/models/two-factor-challenge.cjs +7 -0
- package/dist/common/models/two-factor-challenge.d.cts +3 -0
- package/dist/common/models/two-factor-challenge2.cjs +108 -0
- package/dist/common/models/two-factor-credential.cjs +7 -0
- package/dist/common/models/two-factor-credential.d.cts +3 -0
- package/dist/common/models/two-factor-credential2.cjs +103 -0
- package/dist/common/models/two-factor-recovery-code.cjs +7 -0
- package/dist/common/models/two-factor-recovery-code.d.cts +3 -0
- package/dist/common/models/two-factor-recovery-code2.cjs +57 -0
- package/dist/common/models/user.cjs +258 -21
- package/dist/common/models/user.d.cts +3 -0
- package/dist/common/models/user2.cjs +30 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +3 -0
- package/dist/lib/access-context.d.cts +33 -0
- package/dist/lib/application.d.cts +1 -0
- package/dist/lib/authentication-security-initializer.cjs +351 -0
- package/dist/lib/authentication-security-initializer.d.cts +4 -0
- package/dist/lib/builtin-models.cjs +12 -0
- package/dist/lib/builtin-models.d.cts +3 -0
- package/dist/lib/configure-shared-methods.cjs +7 -4
- package/dist/lib/configure-shared-methods.d.cts +3 -0
- package/dist/lib/connectors/base-connector.d.cts +6 -0
- package/dist/lib/connectors/mail-direct-transport.cjs +26 -23
- package/dist/lib/connectors/mail-direct-transport.d.cts +22 -0
- package/dist/lib/connectors/mail-stub-transport.d.cts +14 -0
- package/dist/lib/connectors/mail.d.cts +7 -0
- package/dist/lib/connectors/memory.d.cts +6 -0
- package/dist/lib/current-context.d.cts +3 -0
- package/dist/lib/globalize.d.cts +10 -0
- package/dist/lib/loopback.cjs +6 -0
- package/dist/lib/loopback.d.cts +1 -0
- package/dist/lib/model.d.cts +1 -0
- package/dist/lib/persisted-model.cjs +7 -12
- package/dist/lib/persisted-model.d.cts +1 -0
- package/dist/lib/rate-limit/algorithms/common.cjs +37 -0
- package/dist/lib/rate-limit/algorithms/failure-lockout.cjs +70 -0
- package/dist/lib/rate-limit/algorithms/fixed-window.cjs +61 -0
- package/dist/lib/rate-limit/algorithms/index.cjs +55 -0
- package/dist/lib/rate-limit/config.cjs +107 -0
- package/dist/lib/rate-limit/errors.cjs +58 -0
- package/dist/lib/rate-limit/index.cjs +15 -0
- package/dist/lib/rate-limit/index.d.cts +1 -0
- package/dist/lib/rate-limit/initializer.cjs +92 -0
- package/dist/lib/rate-limit/initializer.d.cts +4 -0
- package/dist/lib/rate-limit/limiter.cjs +368 -0
- package/dist/lib/rate-limit/registry.cjs +92 -0
- package/dist/lib/rate-limit/remote.cjs +180 -0
- package/dist/lib/rate-limit/remote.d.cts +6 -0
- package/dist/lib/rate-limit/stores/persisted-model.cjs +353 -0
- package/dist/lib/rate-limit/subject.cjs +127 -0
- package/dist/lib/rate-limit/types.cjs +1 -0
- package/dist/lib/rate-limit/types.d.cts +167 -0
- package/dist/lib/registry.d.cts +1 -0
- package/dist/lib/runtime.d.cts +10 -0
- package/dist/lib/server-app.d.cts +3 -0
- package/dist/lib/two-factor/config.cjs +199 -0
- package/dist/lib/two-factor/config.d.cts +55 -0
- package/dist/lib/two-factor/crypto.cjs +68 -0
- package/dist/lib/two-factor/crypto.d.cts +16 -0
- package/dist/lib/two-factor/errors.cjs +58 -0
- package/dist/lib/two-factor/errors.d.cts +18 -0
- package/dist/lib/two-factor/rate-limit.cjs +153 -0
- package/dist/lib/two-factor/rate-limit.d.cts +22 -0
- package/dist/lib/two-factor/recovery-codes.cjs +49 -0
- package/dist/lib/two-factor/recovery-codes.d.cts +16 -0
- package/dist/lib/two-factor/service.cjs +1108 -0
- package/dist/lib/two-factor/service.d.cts +78 -0
- package/dist/lib/two-factor/totp.cjs +50 -0
- package/dist/lib/two-factor/totp.d.cts +14 -0
- package/dist/lib/utils.d.cts +36 -0
- package/dist/package.cjs +46 -10
- package/dist/server/middleware/context.d.cts +3 -0
- package/dist/server/middleware/error-handler.d.cts +3 -0
- package/dist/server/middleware/favicon.d.cts +7 -0
- package/dist/server/middleware/rest.d.cts +6 -0
- package/dist/server/middleware/static.d.cts +3 -0
- package/dist/server/middleware/status.d.cts +19 -0
- package/dist/server/middleware/token.d.cts +3 -0
- package/dist/server/middleware/url-not-found.d.cts +13 -0
- package/docs/api-explorer-details.md +110 -0
- package/docs/application-guide.md +1110 -0
- package/docs/assets/explorer-api.png +0 -0
- package/docs/assets/explorer-endpoint.png +0 -0
- package/docs/assets/explorer-listing.png +0 -0
- package/docs/assets/explorer-req-res.png +0 -0
- package/docs/assets/lb-modules.png +0 -0
- package/docs/assets/loopback-architecture.png +0 -0
- package/docs/assets/loopback-concepts.png +0 -0
- package/docs/assets/loopback_ov.png +0 -0
- package/docs/rate-limiting.md +879 -0
- package/docs/two-factor-authentication.md +611 -0
- package/docs/two-factor-authenticator-loss-runbook.md +117 -0
- package/package.json +56 -20
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
//#region src/lib/rate-limit/algorithms/common.ts
|
|
3
|
+
function emptyRateLimitState() {
|
|
4
|
+
return {
|
|
5
|
+
failureCount: 0,
|
|
6
|
+
attemptCount: 0,
|
|
7
|
+
lockoutLevel: 0,
|
|
8
|
+
windowStartedAtMs: null,
|
|
9
|
+
lastAttemptAtMs: null,
|
|
10
|
+
lastFailureAtMs: null,
|
|
11
|
+
blockedUntilMs: null
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function cloneRateLimitState(state) {
|
|
15
|
+
const empty = emptyRateLimitState();
|
|
16
|
+
if (!state) return empty;
|
|
17
|
+
for (const key of Object.keys(empty)) {
|
|
18
|
+
const value = state[key];
|
|
19
|
+
if (value !== void 0) empty[key] = value;
|
|
20
|
+
}
|
|
21
|
+
return empty;
|
|
22
|
+
}
|
|
23
|
+
function isBlocked(state, nowMs) {
|
|
24
|
+
return state.blockedUntilMs != null && state.blockedUntilMs > nowMs;
|
|
25
|
+
}
|
|
26
|
+
var init_common = require_runtime.__esmMin((() => {}));
|
|
27
|
+
//#endregion
|
|
28
|
+
init_common();
|
|
29
|
+
exports.cloneRateLimitState = cloneRateLimitState;
|
|
30
|
+
exports.emptyRateLimitState = emptyRateLimitState;
|
|
31
|
+
Object.defineProperty(exports, "init_common", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function() {
|
|
34
|
+
return init_common;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
exports.isBlocked = isBlocked;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_common = require("./common.cjs");
|
|
3
|
+
//#region src/lib/rate-limit/algorithms/failure-lockout.ts
|
|
4
|
+
function normalizeFailureWindow(current, policy, nowMs) {
|
|
5
|
+
const state = require_common.cloneRateLimitState(current);
|
|
6
|
+
if (state.blockedUntilMs != null && state.blockedUntilMs <= nowMs) {
|
|
7
|
+
state.blockedUntilMs = null;
|
|
8
|
+
state.failureCount = 0;
|
|
9
|
+
state.windowStartedAtMs = null;
|
|
10
|
+
}
|
|
11
|
+
if (state.windowStartedAtMs != null && state.windowStartedAtMs + policy.observationWindowMs <= nowMs) {
|
|
12
|
+
state.failureCount = 0;
|
|
13
|
+
state.windowStartedAtMs = null;
|
|
14
|
+
}
|
|
15
|
+
if (state.lastFailureAtMs != null && state.lastFailureAtMs + policy.backoffResetAfterMs <= nowMs) state.lockoutLevel = 0;
|
|
16
|
+
return state;
|
|
17
|
+
}
|
|
18
|
+
function inspectFailureLockout(current, policy, nowMs) {
|
|
19
|
+
const state = normalizeFailureWindow(current, policy, nowMs);
|
|
20
|
+
return {
|
|
21
|
+
state,
|
|
22
|
+
allowed: !require_common.isBlocked(state, nowMs),
|
|
23
|
+
blockedUntilMs: require_common.isBlocked(state, nowMs) ? state.blockedUntilMs : null
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function blockDuration(policy, lockoutLevel) {
|
|
27
|
+
if (policy.strategy === "fixed") return policy.blockDurationMs;
|
|
28
|
+
const exponent = Math.min(30, Math.max(0, lockoutLevel - 1));
|
|
29
|
+
return Math.min(policy.maxBlockDurationMs, policy.blockDurationMs * Math.pow(policy.multiplier, exponent));
|
|
30
|
+
}
|
|
31
|
+
function consumeFailureLockout(current, policy, cost, nowMs) {
|
|
32
|
+
const state = normalizeFailureWindow(current, policy, nowMs);
|
|
33
|
+
if (require_common.isBlocked(state, nowMs)) return {
|
|
34
|
+
state,
|
|
35
|
+
allowed: false,
|
|
36
|
+
blockedUntilMs: state.blockedUntilMs
|
|
37
|
+
};
|
|
38
|
+
if (state.windowStartedAtMs == null) state.windowStartedAtMs = nowMs;
|
|
39
|
+
state.lastFailureAtMs = nowMs;
|
|
40
|
+
state.failureCount = Math.min(policy.maxFailedAttempts, state.failureCount + cost);
|
|
41
|
+
if (state.failureCount >= policy.maxFailedAttempts) {
|
|
42
|
+
state.failureCount = 0;
|
|
43
|
+
state.windowStartedAtMs = null;
|
|
44
|
+
state.lockoutLevel += 1;
|
|
45
|
+
state.blockedUntilMs = nowMs + blockDuration(policy, state.lockoutLevel);
|
|
46
|
+
return {
|
|
47
|
+
state,
|
|
48
|
+
allowed: false,
|
|
49
|
+
blockedUntilMs: state.blockedUntilMs
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
state,
|
|
54
|
+
allowed: true,
|
|
55
|
+
blockedUntilMs: null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
var init_failure_lockout = require_runtime.__esmMin((() => {
|
|
59
|
+
require_common.init_common();
|
|
60
|
+
}));
|
|
61
|
+
//#endregion
|
|
62
|
+
init_failure_lockout();
|
|
63
|
+
exports.consumeFailureLockout = consumeFailureLockout;
|
|
64
|
+
Object.defineProperty(exports, "init_failure_lockout", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function() {
|
|
67
|
+
return init_failure_lockout;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
exports.inspectFailureLockout = inspectFailureLockout;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_common = require("./common.cjs");
|
|
3
|
+
//#region src/lib/rate-limit/algorithms/fixed-window.ts
|
|
4
|
+
function normalizeWindow(current, policy, nowMs) {
|
|
5
|
+
const state = require_common.cloneRateLimitState(current);
|
|
6
|
+
const blockExpired = state.blockedUntilMs != null && state.blockedUntilMs <= nowMs;
|
|
7
|
+
const windowExpired = state.windowStartedAtMs != null && state.windowStartedAtMs + policy.observationWindowMs <= nowMs;
|
|
8
|
+
if (blockExpired || windowExpired) {
|
|
9
|
+
state.attemptCount = 0;
|
|
10
|
+
state.windowStartedAtMs = null;
|
|
11
|
+
state.blockedUntilMs = null;
|
|
12
|
+
}
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
function inspectFixedWindow(current, policy, nowMs) {
|
|
16
|
+
const state = normalizeWindow(current, policy, nowMs);
|
|
17
|
+
return {
|
|
18
|
+
state,
|
|
19
|
+
allowed: !require_common.isBlocked(state, nowMs),
|
|
20
|
+
blockedUntilMs: require_common.isBlocked(state, nowMs) ? state.blockedUntilMs : null
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function consumeFixedWindow(current, policy, cost, nowMs) {
|
|
24
|
+
const state = normalizeWindow(current, policy, nowMs);
|
|
25
|
+
if (require_common.isBlocked(state, nowMs)) return {
|
|
26
|
+
state,
|
|
27
|
+
allowed: false,
|
|
28
|
+
blockedUntilMs: state.blockedUntilMs
|
|
29
|
+
};
|
|
30
|
+
if (state.windowStartedAtMs == null) state.windowStartedAtMs = nowMs;
|
|
31
|
+
state.lastAttemptAtMs = nowMs;
|
|
32
|
+
if (cost > Math.max(0, policy.maxAttempts - state.attemptCount)) {
|
|
33
|
+
state.attemptCount = policy.maxAttempts;
|
|
34
|
+
state.blockedUntilMs = nowMs + policy.blockDurationMs;
|
|
35
|
+
return {
|
|
36
|
+
state,
|
|
37
|
+
allowed: false,
|
|
38
|
+
blockedUntilMs: state.blockedUntilMs
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
state.attemptCount += cost;
|
|
42
|
+
if (state.attemptCount === policy.maxAttempts) state.blockedUntilMs = nowMs + policy.blockDurationMs;
|
|
43
|
+
return {
|
|
44
|
+
state,
|
|
45
|
+
allowed: true,
|
|
46
|
+
blockedUntilMs: state.blockedUntilMs
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
var init_fixed_window = require_runtime.__esmMin((() => {
|
|
50
|
+
require_common.init_common();
|
|
51
|
+
}));
|
|
52
|
+
//#endregion
|
|
53
|
+
init_fixed_window();
|
|
54
|
+
exports.consumeFixedWindow = consumeFixedWindow;
|
|
55
|
+
Object.defineProperty(exports, "init_fixed_window", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
get: function() {
|
|
58
|
+
return init_fixed_window;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
exports.inspectFixedWindow = inspectFixedWindow;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_common = require("./common.cjs");
|
|
3
|
+
const require_failure_lockout = require("./failure-lockout.cjs");
|
|
4
|
+
const require_fixed_window = require("./fixed-window.cjs");
|
|
5
|
+
//#region src/lib/rate-limit/algorithms/index.ts
|
|
6
|
+
var algorithms_exports = /* @__PURE__ */ require_runtime.__exportAll({
|
|
7
|
+
cloneRateLimitState: () => require_common.cloneRateLimitState,
|
|
8
|
+
consumeFailureLockout: () => require_failure_lockout.consumeFailureLockout,
|
|
9
|
+
consumeFixedWindow: () => require_fixed_window.consumeFixedWindow,
|
|
10
|
+
consumeState: () => consumeState,
|
|
11
|
+
emptyRateLimitState: () => require_common.emptyRateLimitState,
|
|
12
|
+
inspectFailureLockout: () => require_failure_lockout.inspectFailureLockout,
|
|
13
|
+
inspectFixedWindow: () => require_fixed_window.inspectFixedWindow,
|
|
14
|
+
inspectState: () => inspectState,
|
|
15
|
+
isBlocked: () => require_common.isBlocked
|
|
16
|
+
});
|
|
17
|
+
function inspectState(state, policy, nowMs) {
|
|
18
|
+
if (policy.algorithm === "fixed-window") return require_fixed_window.inspectFixedWindow(state, policy, nowMs);
|
|
19
|
+
return require_failure_lockout.inspectFailureLockout(state, policy, nowMs);
|
|
20
|
+
}
|
|
21
|
+
function consumeState(state, policy, cost, nowMs) {
|
|
22
|
+
if (policy.algorithm === "fixed-window") return require_fixed_window.consumeFixedWindow(state, policy, cost, nowMs);
|
|
23
|
+
return require_failure_lockout.consumeFailureLockout(state, policy, cost, nowMs);
|
|
24
|
+
}
|
|
25
|
+
var init_algorithms = require_runtime.__esmMin((() => {
|
|
26
|
+
require_failure_lockout.init_failure_lockout();
|
|
27
|
+
require_fixed_window.init_fixed_window();
|
|
28
|
+
require_common.init_common();
|
|
29
|
+
}));
|
|
30
|
+
//#endregion
|
|
31
|
+
require_common.init_common();
|
|
32
|
+
require_failure_lockout.init_failure_lockout();
|
|
33
|
+
require_fixed_window.init_fixed_window();
|
|
34
|
+
init_algorithms();
|
|
35
|
+
Object.defineProperty(exports, "algorithms_exports", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function() {
|
|
38
|
+
return algorithms_exports;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
exports.cloneRateLimitState = require_common.cloneRateLimitState;
|
|
42
|
+
exports.consumeFailureLockout = require_failure_lockout.consumeFailureLockout;
|
|
43
|
+
exports.consumeFixedWindow = require_fixed_window.consumeFixedWindow;
|
|
44
|
+
exports.consumeState = consumeState;
|
|
45
|
+
exports.emptyRateLimitState = require_common.emptyRateLimitState;
|
|
46
|
+
Object.defineProperty(exports, "init_algorithms", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function() {
|
|
49
|
+
return init_algorithms;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
exports.inspectFailureLockout = require_failure_lockout.inspectFailureLockout;
|
|
53
|
+
exports.inspectFixedWindow = require_fixed_window.inspectFixedWindow;
|
|
54
|
+
exports.inspectState = inspectState;
|
|
55
|
+
exports.isBlocked = require_common.isBlocked;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_errors = require("./errors.cjs");
|
|
3
|
+
const require_subject = require("./subject.cjs");
|
|
4
|
+
//#region src/lib/rate-limit/config.ts
|
|
5
|
+
var config_exports = /* @__PURE__ */ require_runtime.__exportAll({
|
|
6
|
+
canonicalJson: () => canonicalJson,
|
|
7
|
+
fingerprintPolicy: () => fingerprintPolicy,
|
|
8
|
+
normalizeFailureLockoutPolicy: () => normalizeFailureLockoutPolicy,
|
|
9
|
+
normalizeFixedWindowPolicy: () => normalizeFixedWindowPolicy,
|
|
10
|
+
validateRateLimitCost: () => validateRateLimitCost
|
|
11
|
+
});
|
|
12
|
+
function assertInteger(value, name, minimum = 1, maximum = MAX_INTEGER) {
|
|
13
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) throw require_errors.rateLimitConfigurationError(`${name} must be an integer between ${minimum} and ${maximum}`);
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function canonicalize(value) {
|
|
17
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
18
|
+
if (value && typeof value === "object") {
|
|
19
|
+
const output = {};
|
|
20
|
+
for (const key of Object.keys(value).sort()) {
|
|
21
|
+
const current = value[key];
|
|
22
|
+
if (current !== void 0) output[key] = canonicalize(current);
|
|
23
|
+
}
|
|
24
|
+
return output;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function canonicalJson(value) {
|
|
29
|
+
return JSON.stringify(canonicalize(value));
|
|
30
|
+
}
|
|
31
|
+
function fingerprintPolicy(policy) {
|
|
32
|
+
return crypto.createHash("sha256").update(canonicalJson(policy)).digest("hex");
|
|
33
|
+
}
|
|
34
|
+
function normalizeFixedWindowPolicy(input) {
|
|
35
|
+
require_subject.validateSafeIdentifier(input.policyId, "policyId");
|
|
36
|
+
require_subject.validateSafeIdentifier(input.policyVersion, "policyVersion");
|
|
37
|
+
const normalized = {
|
|
38
|
+
policyId: input.policyId,
|
|
39
|
+
policyVersion: input.policyVersion,
|
|
40
|
+
algorithm: "fixed-window",
|
|
41
|
+
maxAttempts: assertInteger(input.maxAttempts, "maxAttempts"),
|
|
42
|
+
observationWindowMs: assertInteger(input.observationWindowMs, "observationWindowMs", 1, MAX_DURATION_MS),
|
|
43
|
+
blockDurationMs: assertInteger(input.blockDurationMs, "blockDurationMs", 1, MAX_DURATION_MS),
|
|
44
|
+
stateTtlMs: assertInteger(input.stateTtlMs, "stateTtlMs", 1, MAX_DURATION_MS)
|
|
45
|
+
};
|
|
46
|
+
if (normalized.stateTtlMs < Math.max(normalized.observationWindowMs, normalized.blockDurationMs)) throw require_errors.rateLimitConfigurationError("stateTtlMs must cover observationWindowMs and blockDurationMs");
|
|
47
|
+
return {
|
|
48
|
+
...normalized,
|
|
49
|
+
policyFingerprint: fingerprintPolicy(normalized)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function normalizeFailureLockoutPolicy(input) {
|
|
53
|
+
require_subject.validateSafeIdentifier(input.policyId, "policyId");
|
|
54
|
+
require_subject.validateSafeIdentifier(input.policyVersion, "policyVersion");
|
|
55
|
+
const strategy = input.strategy || "fixed";
|
|
56
|
+
if (strategy !== "fixed" && strategy !== "exponential") throw require_errors.rateLimitConfigurationError("strategy must be fixed or exponential");
|
|
57
|
+
const blockDurationMs = assertInteger(input.blockDurationMs, "blockDurationMs", 1, MAX_DURATION_MS);
|
|
58
|
+
const maxBlockDurationMs = assertInteger(input.maxBlockDurationMs || blockDurationMs, "maxBlockDurationMs", blockDurationMs, MAX_DURATION_MS);
|
|
59
|
+
const normalized = {
|
|
60
|
+
policyId: input.policyId,
|
|
61
|
+
policyVersion: input.policyVersion,
|
|
62
|
+
algorithm: "failure-lockout",
|
|
63
|
+
maxFailedAttempts: assertInteger(input.maxFailedAttempts, "maxFailedAttempts"),
|
|
64
|
+
observationWindowMs: assertInteger(input.observationWindowMs, "observationWindowMs", 1, MAX_DURATION_MS),
|
|
65
|
+
blockDurationMs,
|
|
66
|
+
strategy,
|
|
67
|
+
multiplier: assertInteger(input.multiplier || 2, "multiplier", 1, 100),
|
|
68
|
+
maxBlockDurationMs,
|
|
69
|
+
backoffResetAfterMs: assertInteger(input.backoffResetAfterMs, "backoffResetAfterMs", 1, MAX_DURATION_MS),
|
|
70
|
+
stateTtlMs: assertInteger(input.stateTtlMs, "stateTtlMs", 1, MAX_DURATION_MS)
|
|
71
|
+
};
|
|
72
|
+
if (normalized.stateTtlMs < Math.max(normalized.observationWindowMs, normalized.maxBlockDurationMs, normalized.backoffResetAfterMs)) throw require_errors.rateLimitConfigurationError("stateTtlMs must cover window, maximum block and backoff reset");
|
|
73
|
+
return {
|
|
74
|
+
...normalized,
|
|
75
|
+
policyFingerprint: fingerprintPolicy(normalized)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function validateRateLimitCost(cost, maximum = 1e5) {
|
|
79
|
+
return assertInteger(cost, "cost", 1, maximum);
|
|
80
|
+
}
|
|
81
|
+
var crypto, MAX_INTEGER, MAX_DURATION_MS;
|
|
82
|
+
var init_config = require_runtime.__esmMin((() => {
|
|
83
|
+
require_errors.init_errors();
|
|
84
|
+
require_subject.init_subject();
|
|
85
|
+
crypto = require("crypto");
|
|
86
|
+
MAX_INTEGER = 2147483647;
|
|
87
|
+
MAX_DURATION_MS = 31536e6;
|
|
88
|
+
}));
|
|
89
|
+
//#endregion
|
|
90
|
+
init_config();
|
|
91
|
+
exports.canonicalJson = canonicalJson;
|
|
92
|
+
Object.defineProperty(exports, "config_exports", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
get: function() {
|
|
95
|
+
return config_exports;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
exports.fingerprintPolicy = fingerprintPolicy;
|
|
99
|
+
Object.defineProperty(exports, "init_config", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
get: function() {
|
|
102
|
+
return init_config;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
exports.normalizeFailureLockoutPolicy = normalizeFailureLockoutPolicy;
|
|
106
|
+
exports.normalizeFixedWindowPolicy = normalizeFixedWindowPolicy;
|
|
107
|
+
exports.validateRateLimitCost = validateRateLimitCost;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
//#region src/lib/rate-limit/errors.ts
|
|
3
|
+
var errors_exports = /* @__PURE__ */ require_runtime.__exportAll({
|
|
4
|
+
RateLimitError: () => RateLimitError,
|
|
5
|
+
rateLimitConfigurationError: () => rateLimitConfigurationError,
|
|
6
|
+
rateLimitUnavailableError: () => rateLimitUnavailableError,
|
|
7
|
+
rateLimitedError: () => rateLimitedError
|
|
8
|
+
});
|
|
9
|
+
function rateLimitedError(blockedUntilMs, nowMs = Date.now(), code = "RATE_LIMITED") {
|
|
10
|
+
const retryAfter = blockedUntilMs == null ? 1 : Math.max(1, Math.ceil((blockedUntilMs - nowMs) / 1e3));
|
|
11
|
+
return new RateLimitError(code, "Too many requests", 429, {
|
|
12
|
+
retryAfter,
|
|
13
|
+
details: { retryAfter }
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function rateLimitUnavailableError(cause, code = "RATE_LIMIT_UNAVAILABLE") {
|
|
17
|
+
return new RateLimitError(code, "Rate limit service unavailable", 503, { cause });
|
|
18
|
+
}
|
|
19
|
+
function rateLimitConfigurationError(message) {
|
|
20
|
+
return new RateLimitError("RATE_LIMIT_CONFIGURATION_INVALID", message, 500);
|
|
21
|
+
}
|
|
22
|
+
var RateLimitError;
|
|
23
|
+
var init_errors = require_runtime.__esmMin((() => {
|
|
24
|
+
RateLimitError = class extends Error {
|
|
25
|
+
code;
|
|
26
|
+
statusCode;
|
|
27
|
+
details;
|
|
28
|
+
retryAfter;
|
|
29
|
+
cause;
|
|
30
|
+
constructor(code, message, statusCode, options = {}) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "RateLimitError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.statusCode = statusCode;
|
|
35
|
+
this.cause = options.cause;
|
|
36
|
+
this.details = options.details;
|
|
37
|
+
this.retryAfter = options.retryAfter;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}));
|
|
41
|
+
//#endregion
|
|
42
|
+
init_errors();
|
|
43
|
+
exports.RateLimitError = RateLimitError;
|
|
44
|
+
Object.defineProperty(exports, "errors_exports", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: function() {
|
|
47
|
+
return errors_exports;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(exports, "init_errors", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: function() {
|
|
53
|
+
return init_errors;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
exports.rateLimitConfigurationError = rateLimitConfigurationError;
|
|
57
|
+
exports.rateLimitUnavailableError = rateLimitUnavailableError;
|
|
58
|
+
exports.rateLimitedError = rateLimitedError;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_errors = require("./errors.cjs");
|
|
3
|
+
const require_subject = require("./subject.cjs");
|
|
4
|
+
const require_config = require("./config.cjs");
|
|
5
|
+
const require_limiter = require("./limiter.cjs");
|
|
6
|
+
const require_index = require("./algorithms/index.cjs");
|
|
7
|
+
const require_persisted_model = require("./stores/persisted-model.cjs");
|
|
8
|
+
const require_registry = require("./registry.cjs");
|
|
9
|
+
const require_lib_rate_limit_remote = require("./remote.cjs");
|
|
10
|
+
//#region src/lib/rate-limit/index.ts
|
|
11
|
+
var require_rate_limit = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
|
12
|
+
module.exports = Object.assign({}, (require_errors.init_errors(), require_runtime.__toCommonJS(require_errors.errors_exports)), (require_config.init_config(), require_runtime.__toCommonJS(require_config.config_exports)), (require_subject.init_subject(), require_runtime.__toCommonJS(require_subject.subject_exports)), (require_index.init_algorithms(), require_runtime.__toCommonJS(require_index.algorithms_exports)), (require_limiter.init_limiter(), require_runtime.__toCommonJS(require_limiter.limiter_exports)), (require_registry.init_registry(), require_runtime.__toCommonJS(require_registry.registry_exports)), (require_lib_rate_limit_remote.init_remote(), require_runtime.__toCommonJS(require_lib_rate_limit_remote.remote_exports)), (require_persisted_model.init_persisted_model(), require_runtime.__toCommonJS(require_persisted_model.persisted_model_exports)));
|
|
13
|
+
}));
|
|
14
|
+
//#endregion
|
|
15
|
+
module.exports = require_rate_limit();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_limiter = require("./limiter.cjs");
|
|
4
|
+
const require_registry = require("./registry.cjs");
|
|
5
|
+
const require_lib_rate_limit_remote = require("./remote.cjs");
|
|
6
|
+
//#region src/lib/rate-limit/initializer.ts
|
|
7
|
+
var initializer_exports = /* @__PURE__ */ require_runtime.__exportAll({ installRateLimiting: () => installRateLimiting });
|
|
8
|
+
function installRateLimiting(app) {
|
|
9
|
+
const registry = new require_registry.RateLimitRegistry(app);
|
|
10
|
+
const limiter = new require_limiter.RateLimiter(registry);
|
|
11
|
+
const registered = new require_limiter.RegisteredRateLimiter(limiter, registry);
|
|
12
|
+
let initialization = null;
|
|
13
|
+
require_lib_rate_limit_remote.installRemoteRateLimiting(app);
|
|
14
|
+
app.rateLimitRegistry = registry;
|
|
15
|
+
app.rateLimiter = registered;
|
|
16
|
+
app.rateLimit = {
|
|
17
|
+
limiter,
|
|
18
|
+
registry,
|
|
19
|
+
get ready() {
|
|
20
|
+
return app.rateLimitReadiness.ready;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
app.rateLimitReadiness = {
|
|
24
|
+
ready: false,
|
|
25
|
+
error: null
|
|
26
|
+
};
|
|
27
|
+
app.registerRateLimitProvider = function(name, factory) {
|
|
28
|
+
registry.registerProvider(name, factory);
|
|
29
|
+
return app;
|
|
30
|
+
};
|
|
31
|
+
app.registerRateLimitStore = function(name, store) {
|
|
32
|
+
registry.registerStore(name, store);
|
|
33
|
+
return app;
|
|
34
|
+
};
|
|
35
|
+
app.registerRateLimitSubjectKeyring = function(name, keyring) {
|
|
36
|
+
registry.registerKeyring(name, keyring);
|
|
37
|
+
return app;
|
|
38
|
+
};
|
|
39
|
+
app.cleanupRateLimitStates = function(options = {}, callback) {
|
|
40
|
+
const promise = Promise.resolve().then(async () => {
|
|
41
|
+
const modelName = options.model || "RateLimitState";
|
|
42
|
+
const model = app.models && app.models[modelName];
|
|
43
|
+
if (!model) throw new Error(`Rate limit state model ${modelName} was not found`);
|
|
44
|
+
return model.destroyAll({ expiresAt: { lt: options.now || /* @__PURE__ */ new Date() } });
|
|
45
|
+
});
|
|
46
|
+
if (typeof callback === "function") {
|
|
47
|
+
promise.then((result) => callback(null, result), callback);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
return promise;
|
|
51
|
+
};
|
|
52
|
+
app.initializeRateLimiting = function(callback) {
|
|
53
|
+
if (!initialization) initialization = Promise.resolve().then(async () => {
|
|
54
|
+
const config = app.get("rateLimit") || {};
|
|
55
|
+
registered.configureLimits(config.limits || {});
|
|
56
|
+
registry.configure(config);
|
|
57
|
+
await Promise.all(Object.keys(config.stores || {}).map((name) => registry.resolveStore(name)));
|
|
58
|
+
await app.validateRemoteRateLimits();
|
|
59
|
+
registry.freeze();
|
|
60
|
+
app.installRemoteRateLimits();
|
|
61
|
+
app.rateLimitReadiness.ready = true;
|
|
62
|
+
}).catch((error) => {
|
|
63
|
+
app.rateLimitReadiness.error = error;
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
if (typeof callback === "function") {
|
|
67
|
+
initialization.then(() => callback(null), callback);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
return initialization;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
var init_initializer = require_runtime.__esmMin((() => {
|
|
74
|
+
require_limiter.init_limiter();
|
|
75
|
+
require_registry.init_registry();
|
|
76
|
+
require_lib_rate_limit_remote.init_remote();
|
|
77
|
+
}));
|
|
78
|
+
//#endregion
|
|
79
|
+
init_initializer();
|
|
80
|
+
Object.defineProperty(exports, "init_initializer", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get: function() {
|
|
83
|
+
return init_initializer;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(exports, "initializer_exports", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
get: function() {
|
|
89
|
+
return initializer_exports;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
exports.installRateLimiting = installRateLimiting;
|