@columnus/shared 0.1.59 → 0.1.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assetSettings.d.ts +16 -0
- package/dist/assetSettings.js.map +1 -1
- package/dist/auditRedact.d.ts +36 -0
- package/dist/auditRedact.js +4602 -0
- package/dist/auditRedact.js.map +1 -0
- package/dist/bffProtocol.d.ts +22 -22
- package/dist/index.d.ts +2 -0
- package/dist/index.js +127 -1
- package/dist/index.js.map +1 -1
- package/dist/proposeRateLimit.d.ts +22 -0
- package/dist/proposeRateLimit.js +33 -0
- package/dist/proposeRateLimit.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ProposeRateLimitKind = "api_token" | "share_invite" | "public_form" | "client_key";
|
|
2
|
+
export type ProposeRateLimitConfig = {
|
|
3
|
+
max: number;
|
|
4
|
+
windowMs: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const DEFAULT_PROPOSE_RATE_LIMITS: Record<ProposeRateLimitKind, ProposeRateLimitConfig>;
|
|
7
|
+
export declare function parseProposeRateLimitInt(raw: string | undefined, fallback: number): number;
|
|
8
|
+
export declare function proposeRateLimitBucket(kind: ProposeRateLimitKind, id: string): string;
|
|
9
|
+
export type SlidingWindowState = {
|
|
10
|
+
hits: number[];
|
|
11
|
+
};
|
|
12
|
+
export type RateLimitCheckResult = {
|
|
13
|
+
allowed: true;
|
|
14
|
+
next: SlidingWindowState;
|
|
15
|
+
} | {
|
|
16
|
+
allowed: false;
|
|
17
|
+
retryAfterMs: number;
|
|
18
|
+
};
|
|
19
|
+
/** Sliding-window counter: prune stale hits, reject when at capacity. */
|
|
20
|
+
export declare function checkSlidingWindowRateLimit(state: SlidingWindowState, now: number, config: ProposeRateLimitConfig): RateLimitCheckResult;
|
|
21
|
+
export declare const PROPOSE_RATE_LIMITED_CODE: "propose_rate_limited";
|
|
22
|
+
export declare function proposeRateLimitedMessage(retryAfterMs: number): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/proposeRateLimit.ts
|
|
2
|
+
var DEFAULT_PROPOSE_RATE_LIMITS = {
|
|
3
|
+
api_token: { max: 30, windowMs: 6e4 },
|
|
4
|
+
share_invite: { max: 120, windowMs: 6e4 },
|
|
5
|
+
public_form: { max: 10, windowMs: 6e4 },
|
|
6
|
+
client_key: { max: 60, windowMs: 6e4 }
|
|
7
|
+
};
|
|
8
|
+
function parseProposeRateLimitInt(raw, fallback) {
|
|
9
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
10
|
+
const parsed = Number.parseInt(raw, 10);
|
|
11
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
12
|
+
}
|
|
13
|
+
function proposeRateLimitBucket(kind, id) {
|
|
14
|
+
return `${kind}:${id}`;
|
|
15
|
+
}
|
|
16
|
+
function checkSlidingWindowRateLimit(state, now, config) {
|
|
17
|
+
if (config.max === 0) return { allowed: true, next: state };
|
|
18
|
+
const hits = state.hits.filter((timestamp) => now - timestamp < config.windowMs);
|
|
19
|
+
if (hits.length >= config.max) {
|
|
20
|
+
const oldest = hits[0] ?? now;
|
|
21
|
+
return { allowed: false, retryAfterMs: Math.max(1, oldest + config.windowMs - now) };
|
|
22
|
+
}
|
|
23
|
+
return { allowed: true, next: { hits: [...hits, now] } };
|
|
24
|
+
}
|
|
25
|
+
var PROPOSE_RATE_LIMITED_CODE = "propose_rate_limited";
|
|
26
|
+
function proposeRateLimitedMessage(retryAfterMs) {
|
|
27
|
+
const seconds = Math.max(1, Math.ceil(retryAfterMs / 1e3));
|
|
28
|
+
return `Too many proposal submissions. Try again in ${seconds} second${seconds === 1 ? "" : "s"}.`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { DEFAULT_PROPOSE_RATE_LIMITS, PROPOSE_RATE_LIMITED_CODE, checkSlidingWindowRateLimit, parseProposeRateLimitInt, proposeRateLimitBucket, proposeRateLimitedMessage };
|
|
32
|
+
//# sourceMappingURL=proposeRateLimit.js.map
|
|
33
|
+
//# sourceMappingURL=proposeRateLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proposeRateLimit.ts"],"names":[],"mappings":";AAOO,IAAM,2BAAA,GAAoF;AAAA,EAC/F,SAAA,EAAW,EAAE,GAAA,EAAK,EAAA,EAAI,UAAU,GAAA,EAAO;AAAA,EACvC,YAAA,EAAc,EAAE,GAAA,EAAK,GAAA,EAAK,UAAU,GAAA,EAAO;AAAA,EAC3C,WAAA,EAAa,EAAE,GAAA,EAAK,EAAA,EAAI,UAAU,GAAA,EAAO;AAAA,EACzC,UAAA,EAAY,EAAE,GAAA,EAAK,EAAA,EAAI,UAAU,GAAA;AACnC;AAEO,SAAS,wBAAA,CAAyB,KAAyB,QAAA,EAA0B;AAC1F,EAAA,IAAI,QAAQ,MAAA,IAAa,GAAA,CAAI,IAAA,EAAK,KAAM,IAAI,OAAO,QAAA;AACnD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,GAAA,EAAK,EAAE,CAAA;AACtC,EAAA,OAAO,OAAO,QAAA,CAAS,MAAM,CAAA,IAAK,MAAA,IAAU,IAAI,MAAA,GAAS,QAAA;AAC3D;AAEO,SAAS,sBAAA,CAAuB,MAA4B,EAAA,EAAoB;AACrF,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AACtB;AAWO,SAAS,2BAAA,CACd,KAAA,EACA,GAAA,EACA,MAAA,EACsB;AACtB,EAAA,IAAI,MAAA,CAAO,QAAQ,CAAA,EAAG,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,MAAM,KAAA,EAAM;AAC1D,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,CAAC,SAAA,KAAc,GAAA,GAAM,SAAA,GAAY,MAAA,CAAO,QAAQ,CAAA;AAC/E,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,MAAA,CAAO,GAAA,EAAK;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,CAAC,CAAA,IAAK,GAAA;AAC1B,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,YAAA,EAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,MAAA,GAAS,MAAA,CAAO,QAAA,GAAW,GAAG,CAAA,EAAE;AAAA,EACrF;AACA,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,EAAE,IAAA,EAAM,CAAC,GAAG,IAAA,EAAM,GAAG,CAAA,EAAE,EAAE;AACzD;AAEO,IAAM,yBAAA,GAA4B;AAElC,SAAS,0BAA0B,YAAA,EAA8B;AACtE,EAAA,MAAM,OAAA,GAAU,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,YAAA,GAAe,GAAI,CAAC,CAAA;AAC1D,EAAA,OAAO,+CAA+C,OAAO,CAAA,OAAA,EAAU,OAAA,KAAY,CAAA,GAAI,KAAK,GAAG,CAAA,CAAA,CAAA;AACjG","file":"proposeRateLimit.js","sourcesContent":["export type ProposeRateLimitKind = \"api_token\" | \"share_invite\" | \"public_form\" | \"client_key\";\n\nexport type ProposeRateLimitConfig = {\n max: number;\n windowMs: number;\n};\n\nexport const DEFAULT_PROPOSE_RATE_LIMITS: Record<ProposeRateLimitKind, ProposeRateLimitConfig> = {\n api_token: { max: 30, windowMs: 60_000 },\n share_invite: { max: 120, windowMs: 60_000 },\n public_form: { max: 10, windowMs: 60_000 },\n client_key: { max: 60, windowMs: 60_000 },\n};\n\nexport function parseProposeRateLimitInt(raw: string | undefined, fallback: number): number {\n if (raw === undefined || raw.trim() === \"\") return fallback;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\nexport function proposeRateLimitBucket(kind: ProposeRateLimitKind, id: string): string {\n return `${kind}:${id}`;\n}\n\nexport type SlidingWindowState = {\n hits: number[];\n};\n\nexport type RateLimitCheckResult =\n | { allowed: true; next: SlidingWindowState }\n | { allowed: false; retryAfterMs: number };\n\n/** Sliding-window counter: prune stale hits, reject when at capacity. */\nexport function checkSlidingWindowRateLimit(\n state: SlidingWindowState,\n now: number,\n config: ProposeRateLimitConfig,\n): RateLimitCheckResult {\n if (config.max === 0) return { allowed: true, next: state };\n const hits = state.hits.filter((timestamp) => now - timestamp < config.windowMs);\n if (hits.length >= config.max) {\n const oldest = hits[0] ?? now;\n return { allowed: false, retryAfterMs: Math.max(1, oldest + config.windowMs - now) };\n }\n return { allowed: true, next: { hits: [...hits, now] } };\n}\n\nexport const PROPOSE_RATE_LIMITED_CODE = \"propose_rate_limited\" as const;\n\nexport function proposeRateLimitedMessage(retryAfterMs: number): string {\n const seconds = Math.max(1, Math.ceil(retryAfterMs / 1000));\n return `Too many proposal submissions. Try again in ${seconds} second${seconds === 1 ? \"\" : \"s\"}.`;\n}\n"]}
|