@trokky/trokky 3.1.0 → 3.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/dist/adapters/cloudflare-d1/cloudflare-d1-adapter.d.ts +132 -0
- package/dist/adapters/cloudflare-d1/cloudflare-d1-adapter.d.ts.map +1 -0
- package/dist/adapters/cloudflare-d1/cloudflare-d1-adapter.js +811 -0
- package/dist/adapters/cloudflare-d1/cloudflare-d1-adapter.js.map +1 -0
- package/dist/adapters/cloudflare-d1/index.d.ts +5 -0
- package/dist/adapters/cloudflare-d1/index.d.ts.map +1 -0
- package/dist/adapters/cloudflare-d1/index.js +12 -0
- package/dist/adapters/cloudflare-d1/index.js.map +1 -0
- package/dist/adapters/cloudflare-d1/rate-limit-store.d.ts +46 -0
- package/dist/adapters/cloudflare-d1/rate-limit-store.d.ts.map +1 -0
- package/dist/adapters/cloudflare-d1/rate-limit-store.js +96 -0
- package/dist/adapters/cloudflare-d1/rate-limit-store.js.map +1 -0
- package/dist/adapters/cloudflare-d1/types.d.ts +69 -0
- package/dist/adapters/cloudflare-d1/types.d.ts.map +1 -0
- package/dist/adapters/cloudflare-d1/types.js +2 -0
- package/dist/adapters/cloudflare-d1/types.js.map +1 -0
- package/dist/adapters/cloudflare-r2/cloudflare-r2-adapter.d.ts +86 -0
- package/dist/adapters/cloudflare-r2/cloudflare-r2-adapter.d.ts.map +1 -0
- package/dist/adapters/cloudflare-r2/cloudflare-r2-adapter.js +493 -0
- package/dist/adapters/cloudflare-r2/cloudflare-r2-adapter.js.map +1 -0
- package/dist/adapters/cloudflare-r2/index.d.ts +3 -0
- package/dist/adapters/cloudflare-r2/index.d.ts.map +1 -0
- package/dist/adapters/cloudflare-r2/index.js +11 -0
- package/dist/adapters/cloudflare-r2/index.js.map +1 -0
- package/dist/adapters/cloudflare-r2/types.d.ts +43 -0
- package/dist/adapters/cloudflare-r2/types.d.ts.map +1 -0
- package/dist/adapters/cloudflare-r2/types.js +2 -0
- package/dist/adapters/cloudflare-r2/types.js.map +1 -0
- package/dist/core/media/image-processor.d.ts.map +1 -1
- package/dist/core/media/image-processor.js +5 -13
- package/dist/core/media/image-processor.js.map +1 -1
- package/dist/core/media/processors/sharp.d.ts.map +1 -1
- package/dist/core/media/processors/sharp.js +13 -2
- package/dist/core/media/processors/sharp.js.map +1 -1
- package/dist/core/media/processors/types.d.ts +1 -1
- package/dist/core/media/processors/types.d.ts.map +1 -1
- package/dist/core/security/rate-limit-store.d.ts +56 -0
- package/dist/core/security/rate-limit-store.d.ts.map +1 -0
- package/dist/core/security/rate-limit-store.js +55 -0
- package/dist/core/security/rate-limit-store.js.map +1 -0
- package/dist/core/security/rate-limiter.d.ts +30 -2
- package/dist/core/security/rate-limiter.d.ts.map +1 -1
- package/dist/core/security/rate-limiter.js +50 -19
- package/dist/core/security/rate-limiter.js.map +1 -1
- package/dist/core/services/media-service.d.ts +34 -0
- package/dist/core/services/media-service.d.ts.map +1 -1
- package/dist/core/services/media-service.js +172 -120
- package/dist/core/services/media-service.js.map +1 -1
- package/dist/core/utils/retry.d.ts +45 -0
- package/dist/core/utils/retry.d.ts.map +1 -0
- package/dist/core/utils/retry.js +47 -0
- package/dist/core/utils/retry.js.map +1 -0
- package/dist/integrations/workers/adapter.d.ts +28 -0
- package/dist/integrations/workers/adapter.d.ts.map +1 -0
- package/dist/integrations/workers/adapter.js +111 -0
- package/dist/integrations/workers/adapter.js.map +1 -0
- package/dist/integrations/workers/index.d.ts +22 -0
- package/dist/integrations/workers/index.d.ts.map +1 -0
- package/dist/integrations/workers/index.js +60 -0
- package/dist/integrations/workers/index.js.map +1 -0
- package/dist/routes/auth/auth-flow-store.d.ts +17 -0
- package/dist/routes/auth/auth-flow-store.d.ts.map +1 -1
- package/dist/routes/auth/auth-flow-store.js +27 -0
- package/dist/routes/auth/auth-flow-store.js.map +1 -1
- package/dist/routes/auth/oauth.d.ts.map +1 -1
- package/dist/routes/auth/oauth.js +5 -12
- package/dist/routes/auth/oauth.js.map +1 -1
- package/dist/routes/auth/passkey.d.ts.map +1 -1
- package/dist/routes/auth/passkey.js +5 -8
- package/dist/routes/auth/passkey.js.map +1 -1
- package/dist/structure/validation/schemas.d.ts +60 -60
- package/package.json +20 -1
- package/dist/core/media/processors/cloudflare-images.d.ts +0 -83
- package/dist/core/media/processors/cloudflare-images.d.ts.map +0 -1
- package/dist/core/media/processors/cloudflare-images.js +0 -241
- package/dist/core/media/processors/cloudflare-images.js.map +0 -1
- package/dist/core/media/processors/cloudflare-transform-store.d.ts +0 -127
- package/dist/core/media/processors/cloudflare-transform-store.d.ts.map +0 -1
- package/dist/core/media/processors/cloudflare-transform-store.js +0 -237
- package/dist/core/media/processors/cloudflare-transform-store.js.map +0 -1
- package/dist/core/media/processors/cloudflare-transformations.d.ts +0 -81
- package/dist/core/media/processors/cloudflare-transformations.d.ts.map +0 -1
- package/dist/core/media/processors/cloudflare-transformations.js +0 -144
- package/dist/core/media/processors/cloudflare-transformations.js.map +0 -1
- package/dist/core/media/processors/workers-images.d.ts +0 -112
- package/dist/core/media/processors/workers-images.d.ts.map +0 -1
- package/dist/core/media/processors/workers-images.js +0 -257
- package/dist/core/media/processors/workers-images.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharp.d.ts","sourceRoot":"","sources":["../../../../src/core/media/processors/sharp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"sharp.d.ts","sourceRoot":"","sources":["../../../../src/core/media/processors/sharp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EACL,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAA;AAMnB;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,MAAM,CAA+C;gBAEjD,MAAM,EAAE,oBAAoB;YAK1B,sBAAsB;IAyB9B,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,cAAc,CAAC;IA4E1B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM;IAOpD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAatC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Self-hosted deployments
|
|
10
10
|
* - Development with immediate processing
|
|
11
11
|
*/
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
12
13
|
import { ImageProcessor } from './types.js';
|
|
13
14
|
import { createLogger } from '../../utils/logger.js';
|
|
14
15
|
/**
|
|
@@ -24,8 +25,18 @@ export class SharpImageProcessor extends ImageProcessor {
|
|
|
24
25
|
async ensureSharpInitialized() {
|
|
25
26
|
if (!this.sharp) {
|
|
26
27
|
try {
|
|
27
|
-
//
|
|
28
|
-
|
|
28
|
+
// sharp is loaded through `require`, not `import`, and that is deliberate.
|
|
29
|
+
//
|
|
30
|
+
// A bundler resolves a literal `import('sharp')` even when the branch never executes,
|
|
31
|
+
// and the surviving reference breaks a Cloudflare Workers build before any code runs —
|
|
32
|
+
// sharp pulls in `detect-libc`, which wants `fs` and `child_process`. Hiding the
|
|
33
|
+
// specifier behind a variable does not help either: workerd rejects a non-literal
|
|
34
|
+
// dynamic specifier at parse time. A plain `require` call is followed by neither, so
|
|
35
|
+
// the reference disappears from an edge bundle and still resolves in Node, where sharp
|
|
36
|
+
// is CommonJS anyway. `password-hash.ts` loads bcrypt the same way, for the same reason.
|
|
37
|
+
//
|
|
38
|
+
// Do not turn this back into `await import('sharp')`.
|
|
39
|
+
const sharpModule = createRequire(import.meta.url)('sharp');
|
|
29
40
|
this.sharp = sharpModule.default || sharpModule;
|
|
30
41
|
}
|
|
31
42
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharp.js","sourceRoot":"","sources":["../../../../src/core/media/processors/sharp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,cAAc,EAIf,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAKpD;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,cAAc;IAC7C,KAAK,CAAe;IACpB,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAA;IAE7D,YAAY,MAA4B;QACtC,KAAK,CAAC,MAAM,CAAC,CAAA;QACb,mDAAmD;IACrD,CAAC;IAEO,KAAK,CAAC,sBAAsB;QAClC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,sDAAsD;gBACtD,MAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"sharp.js","sourceRoot":"","sources":["../../../../src/core/media/processors/sharp.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,7 +22,7 @@ export interface ProcessedImage {
|
|
|
22
22
|
variants: Record<string, ProcessedImageVariant>;
|
|
23
23
|
}
|
|
24
24
|
export interface ImageProcessorConfig {
|
|
25
|
-
type: 'none' | 'sharp' | 'cloudflare-images' | '
|
|
25
|
+
type: 'none' | 'sharp' | 'cloudflare-images' | 'imagekit' | 'imgix' | 'custom';
|
|
26
26
|
variants?: ImageVariant[];
|
|
27
27
|
options?: Record<string, unknown>;
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/media/processors/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAC1D;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;CAChD;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,mBAAmB,GAAG,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/media/processors/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAC1D;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;CAChD;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,mBAAmB,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC9E,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED;;GAEG;AACH,8BAAsB,cAAc;IAClC,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAA;gBAE1B,MAAM,EAAE,oBAAoB;IAIxC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CACnB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,cAAc,CAAC;IAE1B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM;IAEnE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpD;;OAEG;IACH,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,YAAY,EAyBhD,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a rate limiter keeps its counters.
|
|
3
|
+
*
|
|
4
|
+
* In one long-lived process a `Map` is the whole story. On an edge runtime it is
|
|
5
|
+
* a security bug wearing a performance costume: every isolate keeps its own
|
|
6
|
+
* counters, so the effective limit is multiplied by however many isolates happen
|
|
7
|
+
* to be warm, and nobody notices because the limiter still *looks* like it works.
|
|
8
|
+
*
|
|
9
|
+
* The naive fix — one shared write per request — is unaffordable here, because
|
|
10
|
+
* `checkRateLimit` guards media reads as well as sign-ins.
|
|
11
|
+
*
|
|
12
|
+
* So the unit of exchange is a **lease**, not a request. A limiter asks the store
|
|
13
|
+
* for a slice of the window's quota, spends it locally at no cost, and goes back
|
|
14
|
+
* only when the slice runs out. One round trip per `leaseSize` requests instead
|
|
15
|
+
* of one per request.
|
|
16
|
+
*
|
|
17
|
+
* The error this introduces leans the safe way. An isolate that leases ten and
|
|
18
|
+
* serves two before it is evicted has still spent ten of the window's budget, so
|
|
19
|
+
* churn makes the limit *stricter* than configured, never looser. Being briefly
|
|
20
|
+
* too strict under load is a far better failure than silently admitting ten times
|
|
21
|
+
* the traffic you asked for.
|
|
22
|
+
*/
|
|
23
|
+
export interface RateLimitStore {
|
|
24
|
+
/**
|
|
25
|
+
* Reserve up to `want` units of the quota for `key` in the window starting at
|
|
26
|
+
* `windowStart`, and report how many were actually granted — `0` when the
|
|
27
|
+
* window is exhausted. Implementations must apply the increment atomically:
|
|
28
|
+
* two callers reserving at once must not both receive the last unit.
|
|
29
|
+
*/
|
|
30
|
+
reserve(request: RateLimitReservation): Promise<number>;
|
|
31
|
+
}
|
|
32
|
+
export interface RateLimitReservation {
|
|
33
|
+
/** The counter's identity: the operation, or whatever the key generator built. */
|
|
34
|
+
key: string;
|
|
35
|
+
/** Start of the fixed window, in epoch milliseconds. */
|
|
36
|
+
windowStart: number;
|
|
37
|
+
/** Window length, so a store can expire its own rows without being told twice. */
|
|
38
|
+
windowMs: number;
|
|
39
|
+
/** How many units the caller would like. */
|
|
40
|
+
want: number;
|
|
41
|
+
/** The most that may be granted across all callers for this key and window. */
|
|
42
|
+
max: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Counters in process memory. Exact, free, and correct for a single process —
|
|
46
|
+
* which is every Node deployment. It is the default for that reason.
|
|
47
|
+
*/
|
|
48
|
+
export declare class MemoryRateLimitStore implements RateLimitStore {
|
|
49
|
+
private readonly counts;
|
|
50
|
+
reserve({ key, windowStart, want, max }: RateLimitReservation): Promise<number>;
|
|
51
|
+
/** Drop counters for windows that are well past. */
|
|
52
|
+
cleanup(windowMs: number, now?: number): void;
|
|
53
|
+
/** Units already spent for a key in the current window. Test seam. */
|
|
54
|
+
used(key: string, windowStart: number): number;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=rate-limit-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-store.d.ts","sourceRoot":"","sources":["../../../src/core/security/rate-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxD;AAED,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAA;IACX,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2D;IAErE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAc5F,oDAAoD;IAC7C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IASxD,sEAAsE;IAC/D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;CAItD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a rate limiter keeps its counters.
|
|
3
|
+
*
|
|
4
|
+
* In one long-lived process a `Map` is the whole story. On an edge runtime it is
|
|
5
|
+
* a security bug wearing a performance costume: every isolate keeps its own
|
|
6
|
+
* counters, so the effective limit is multiplied by however many isolates happen
|
|
7
|
+
* to be warm, and nobody notices because the limiter still *looks* like it works.
|
|
8
|
+
*
|
|
9
|
+
* The naive fix — one shared write per request — is unaffordable here, because
|
|
10
|
+
* `checkRateLimit` guards media reads as well as sign-ins.
|
|
11
|
+
*
|
|
12
|
+
* So the unit of exchange is a **lease**, not a request. A limiter asks the store
|
|
13
|
+
* for a slice of the window's quota, spends it locally at no cost, and goes back
|
|
14
|
+
* only when the slice runs out. One round trip per `leaseSize` requests instead
|
|
15
|
+
* of one per request.
|
|
16
|
+
*
|
|
17
|
+
* The error this introduces leans the safe way. An isolate that leases ten and
|
|
18
|
+
* serves two before it is evicted has still spent ten of the window's budget, so
|
|
19
|
+
* churn makes the limit *stricter* than configured, never looser. Being briefly
|
|
20
|
+
* too strict under load is a far better failure than silently admitting ten times
|
|
21
|
+
* the traffic you asked for.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Counters in process memory. Exact, free, and correct for a single process —
|
|
25
|
+
* which is every Node deployment. It is the default for that reason.
|
|
26
|
+
*/
|
|
27
|
+
export class MemoryRateLimitStore {
|
|
28
|
+
counts = new Map();
|
|
29
|
+
async reserve({ key, windowStart, want, max }) {
|
|
30
|
+
const current = this.counts.get(key);
|
|
31
|
+
if (!current || current.windowStart < windowStart) {
|
|
32
|
+
const granted = Math.min(want, max);
|
|
33
|
+
this.counts.set(key, { used: granted, windowStart });
|
|
34
|
+
return granted;
|
|
35
|
+
}
|
|
36
|
+
const granted = Math.min(want, Math.max(0, max - current.used));
|
|
37
|
+
current.used += granted;
|
|
38
|
+
return granted;
|
|
39
|
+
}
|
|
40
|
+
/** Drop counters for windows that are well past. */
|
|
41
|
+
cleanup(windowMs, now = Date.now()) {
|
|
42
|
+
const cutoff = now - windowMs * 2;
|
|
43
|
+
for (const [key, value] of this.counts.entries()) {
|
|
44
|
+
if (value.windowStart < cutoff) {
|
|
45
|
+
this.counts.delete(key);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Units already spent for a key in the current window. Test seam. */
|
|
50
|
+
used(key, windowStart) {
|
|
51
|
+
const current = this.counts.get(key);
|
|
52
|
+
return current && current.windowStart >= windowStart ? current.used : 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=rate-limit-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-store.js","sourceRoot":"","sources":["../../../src/core/security/rate-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyBH;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IACd,MAAM,GAAG,IAAI,GAAG,EAAiD,CAAA;IAE3E,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,EAAwB;QACxE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAEpC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,GAAG,WAAW,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YACnC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAA;YACpD,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/D,OAAO,CAAC,IAAI,IAAI,OAAO,CAAA;QACvB,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,oDAAoD;IAC7C,OAAO,CAAC,QAAgB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAA;QACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YACjD,IAAI,KAAK,CAAC,WAAW,GAAG,MAAM,EAAE,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,sEAAsE;IAC/D,IAAI,CAAC,GAAW,EAAE,WAAmB;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACpC,OAAO,OAAO,IAAI,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IACzE,CAAC;CACF"}
|
|
@@ -1,15 +1,43 @@
|
|
|
1
|
+
import { type RateLimitStore } from './rate-limit-store.js';
|
|
1
2
|
export interface RateLimitConfig {
|
|
2
3
|
windowMs: number;
|
|
3
4
|
maxRequests: number;
|
|
4
5
|
keyGenerator?: (operation: string, context?: Record<string, unknown>) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Where counters live. Defaults to process memory, which is exact and free in
|
|
8
|
+
* a single process. Supply a shared store on a runtime that spreads requests
|
|
9
|
+
* across isolates, or the limit is silently multiplied by their number.
|
|
10
|
+
*/
|
|
11
|
+
store?: RateLimitStore;
|
|
12
|
+
/**
|
|
13
|
+
* How many units to take from the store at a time. `1` consults the store on
|
|
14
|
+
* every request: exact, and the right choice for an in-process store. A larger
|
|
15
|
+
* lease trades a bounded, fail-safe strictness for far fewer round trips, which
|
|
16
|
+
* is what makes a shared store affordable on hot paths. See rate-limit-store.ts.
|
|
17
|
+
*/
|
|
18
|
+
leaseSize?: number;
|
|
5
19
|
}
|
|
6
20
|
export declare class RateLimiter {
|
|
7
|
-
private operationCounts;
|
|
8
21
|
private readonly defaultConfig;
|
|
9
|
-
constructor(config?: Partial<RateLimitConfig>);
|
|
10
22
|
private config;
|
|
23
|
+
private readonly store;
|
|
24
|
+
private readonly memoryStore;
|
|
25
|
+
private readonly leaseSize;
|
|
26
|
+
private readonly leases;
|
|
27
|
+
constructor(config?: Partial<RateLimitConfig>);
|
|
11
28
|
checkRateLimit(operation: string, context?: Record<string, unknown>): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Drop bookkeeping for windows that are well past. Only meaningful for the
|
|
31
|
+
* in-memory store; a shared store expires its own rows.
|
|
32
|
+
*/
|
|
12
33
|
cleanup(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Requests left in the current window, as far as this instance can tell.
|
|
36
|
+
* With a shared store and a lease larger than one this is a local view: it
|
|
37
|
+
* counts what this instance still holds, not what every instance has spent.
|
|
38
|
+
*/
|
|
13
39
|
getRemainingRequests(operation: string): number;
|
|
14
40
|
}
|
|
41
|
+
export { MemoryRateLimitStore } from './rate-limit-store.js';
|
|
42
|
+
export type { RateLimitStore, RateLimitReservation } from './rate-limit-store.js';
|
|
15
43
|
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/core/security/rate-limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/core/security/rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEjF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAA;IAC/E;;;;OAIG;IACH,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAQD,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAG7B;IAED,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;gBAEtC,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM;IAUpC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BhG;;;OAGG;IACI,OAAO,IAAI,IAAI;IAYtB;;;;OAIG;IACI,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;CAevD;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -1,48 +1,79 @@
|
|
|
1
1
|
import { RateLimitError } from '../errors/index.js';
|
|
2
|
+
import { MemoryRateLimitStore } from './rate-limit-store.js';
|
|
2
3
|
export class RateLimiter {
|
|
3
|
-
operationCounts = new Map();
|
|
4
4
|
defaultConfig = {
|
|
5
5
|
windowMs: 60 * 1000, // 1 minute
|
|
6
6
|
maxRequests: 1000
|
|
7
7
|
};
|
|
8
|
+
config;
|
|
9
|
+
store;
|
|
10
|
+
memoryStore;
|
|
11
|
+
leaseSize;
|
|
12
|
+
leases = new Map();
|
|
8
13
|
constructor(config = {}) {
|
|
9
14
|
this.config = { ...this.defaultConfig, ...config };
|
|
15
|
+
const store = config.store ?? new MemoryRateLimitStore();
|
|
16
|
+
this.store = store;
|
|
17
|
+
// Kept typed so cleanup() can still prune, which only makes sense in memory.
|
|
18
|
+
this.memoryStore = store instanceof MemoryRateLimitStore ? store : null;
|
|
19
|
+
this.leaseSize = Math.max(1, config.leaseSize ?? 1);
|
|
10
20
|
}
|
|
11
|
-
config;
|
|
12
21
|
async checkRateLimit(operation, context) {
|
|
13
|
-
const key = this.config.keyGenerator
|
|
14
|
-
? this.config.keyGenerator(operation, context)
|
|
15
|
-
: operation;
|
|
22
|
+
const key = this.config.keyGenerator ? this.config.keyGenerator(operation, context) : operation;
|
|
16
23
|
const now = Date.now();
|
|
17
24
|
const windowStart = Math.floor(now / this.config.windowMs) * this.config.windowMs;
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
this.operationCounts.set(key, { count: 1, windowStart });
|
|
25
|
+
const lease = this.leases.get(key);
|
|
26
|
+
if (lease && lease.windowStart === windowStart && lease.remaining > 0) {
|
|
27
|
+
lease.remaining -= 1;
|
|
22
28
|
return;
|
|
23
29
|
}
|
|
24
|
-
|
|
30
|
+
const granted = await this.store.reserve({
|
|
31
|
+
key,
|
|
32
|
+
windowStart,
|
|
33
|
+
windowMs: this.config.windowMs,
|
|
34
|
+
want: this.leaseSize,
|
|
35
|
+
max: this.config.maxRequests
|
|
36
|
+
});
|
|
37
|
+
if (granted <= 0) {
|
|
38
|
+
// A spent lease from this window must not be resurrected by a later call.
|
|
39
|
+
this.leases.set(key, { windowStart, remaining: 0 });
|
|
25
40
|
throw new RateLimitError(operation);
|
|
26
41
|
}
|
|
27
|
-
|
|
42
|
+
// One unit pays for this request; the rest is credit for the next ones.
|
|
43
|
+
this.leases.set(key, { windowStart, remaining: granted - 1 });
|
|
28
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Drop bookkeeping for windows that are well past. Only meaningful for the
|
|
47
|
+
* in-memory store; a shared store expires its own rows.
|
|
48
|
+
*/
|
|
29
49
|
cleanup() {
|
|
30
50
|
const now = Date.now();
|
|
31
|
-
const cutoff = now - this.config.windowMs * 2;
|
|
32
|
-
for (const [key,
|
|
33
|
-
if (
|
|
34
|
-
this.
|
|
51
|
+
const cutoff = now - this.config.windowMs * 2;
|
|
52
|
+
for (const [key, lease] of this.leases.entries()) {
|
|
53
|
+
if (lease.windowStart < cutoff) {
|
|
54
|
+
this.leases.delete(key);
|
|
35
55
|
}
|
|
36
56
|
}
|
|
57
|
+
this.memoryStore?.cleanup(this.config.windowMs, now);
|
|
37
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Requests left in the current window, as far as this instance can tell.
|
|
61
|
+
* With a shared store and a lease larger than one this is a local view: it
|
|
62
|
+
* counts what this instance still holds, not what every instance has spent.
|
|
63
|
+
*/
|
|
38
64
|
getRemainingRequests(operation) {
|
|
39
65
|
const now = Date.now();
|
|
40
66
|
const windowStart = Math.floor(now / this.config.windowMs) * this.config.windowMs;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
if (this.memoryStore) {
|
|
68
|
+
const used = this.memoryStore.used(operation, windowStart);
|
|
69
|
+
const held = this.leases.get(operation);
|
|
70
|
+
const unspent = held && held.windowStart === windowStart ? held.remaining : 0;
|
|
71
|
+
// `used` counts leased units, some of which are still in hand.
|
|
72
|
+
return Math.max(0, this.config.maxRequests - used + unspent);
|
|
44
73
|
}
|
|
45
|
-
|
|
74
|
+
const lease = this.leases.get(operation);
|
|
75
|
+
return lease && lease.windowStart === windowStart ? lease.remaining : this.config.maxRequests;
|
|
46
76
|
}
|
|
47
77
|
}
|
|
78
|
+
export { MemoryRateLimitStore } from './rate-limit-store.js';
|
|
48
79
|
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/core/security/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/core/security/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,21 @@ export interface MediaServiceDependencies {
|
|
|
15
15
|
eventsEnabled: boolean;
|
|
16
16
|
getImageProcessor: () => ImageProcessor;
|
|
17
17
|
}
|
|
18
|
+
/** One step of media processing that did not succeed, even after retries. */
|
|
19
|
+
export interface VariantFailure {
|
|
20
|
+
stage: 'process' | 'save-variant' | 'persist-metadata';
|
|
21
|
+
variant?: string;
|
|
22
|
+
error: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What happened while generating variants, recorded on the media file when it was not clean.
|
|
26
|
+
* Its absence means every variant was generated and stored.
|
|
27
|
+
*/
|
|
28
|
+
export interface ImageProcessingReport {
|
|
29
|
+
status: 'partial' | 'failed';
|
|
30
|
+
failures: VariantFailure[];
|
|
31
|
+
failedAt: string;
|
|
32
|
+
}
|
|
18
33
|
/**
|
|
19
34
|
* Media upload, retrieval, image variants and filename sanitization.
|
|
20
35
|
*/
|
|
@@ -22,6 +37,25 @@ export declare class MediaService {
|
|
|
22
37
|
private readonly deps;
|
|
23
38
|
constructor(deps: MediaServiceDependencies);
|
|
24
39
|
uploadMedia(file: File): Promise<MediaFile>;
|
|
40
|
+
/**
|
|
41
|
+
* Retry policy for the remote-backed steps of media processing.
|
|
42
|
+
*
|
|
43
|
+
* Cloudflare's Images service fails a small share of calls transiently — measured at roughly
|
|
44
|
+
* one in fifteen for a 6MB JPEG — and every one of those used to cost a thumbnail silently.
|
|
45
|
+
* Input the service will never accept is not retried: that is a permanent failure and should
|
|
46
|
+
* surface immediately rather than after three waits.
|
|
47
|
+
*/
|
|
48
|
+
private retryOptions;
|
|
49
|
+
/**
|
|
50
|
+
* Generate every variant for an image and write them to storage.
|
|
51
|
+
*
|
|
52
|
+
* Shared by upload and regeneration so both get the same retries and the same reporting. A
|
|
53
|
+
* failure never throws: the original file is already stored and is what the caller actually
|
|
54
|
+
* uploaded. But a failure is never swallowed either — it comes back in `report`, which the
|
|
55
|
+
* caller attaches to the media record, so an image with missing variants says so instead of
|
|
56
|
+
* looking complete.
|
|
57
|
+
*/
|
|
58
|
+
private generateVariants;
|
|
25
59
|
getMedia(id: string): Promise<MediaFile | null>;
|
|
26
60
|
updateMedia(id: string, metadata: Record<string, any>): Promise<MediaFile>;
|
|
27
61
|
getMediaContent(id: string): Promise<ArrayBuffer | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/media-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"media-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/media-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAEtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,6BAA6B,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,SAAS,EAET,eAAe,EAChB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,YAAY,CAAA;IACpB,YAAY,EAAE,mBAAmB,CAAA;IACjC,WAAW,EAAE,WAAW,CAAA;IACxB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,eAAe,EAAE,OAAO,CAAA;IACxB,QAAQ,EAAE,cAAc,CAAA;IACxB,aAAa,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,MAAM,cAAc,CAAA;CACxC;AAGD,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,GAAG,cAAc,GAAG,kBAAkB,CAAA;IACtD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC5B,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAQD;;GAEG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAG9C,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC;IA2FxD;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAqBpB;;;;;;;;OAQG;YACW,gBAAgB;IAsFjB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAY/C,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAuB1E,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAYxD,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAalF,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2CtC,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IA0F7D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM;IAIpD,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC;IAKlD,4BAA4B,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;IAiDhD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAgC1C,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAIlD"}
|