@selvajs/server 0.2.0-beta.3 → 0.2.0-beta.5
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/LICENSE +1 -1
- package/README.md +40 -0
- package/dist/compute/client-cache.d.ts +7 -0
- package/dist/compute/client-cache.d.ts.map +1 -1
- package/dist/compute/client-cache.js +12 -1
- package/dist/compute/client-cache.js.map +1 -1
- package/dist/compute/index.d.ts +2 -2
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +2 -2
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/limits.d.ts +26 -5
- package/dist/compute/limits.d.ts.map +1 -1
- package/dist/compute/limits.js +51 -25
- package/dist/compute/limits.js.map +1 -1
- package/dist/compute/rate-limit.d.ts +40 -0
- package/dist/compute/rate-limit.d.ts.map +1 -1
- package/dist/compute/rate-limit.js +72 -1
- package/dist/compute/rate-limit.js.map +1 -1
- package/dist/compute/remote-definition.d.ts +7 -0
- package/dist/compute/remote-definition.d.ts.map +1 -1
- package/dist/compute/remote-definition.js +3 -1
- package/dist/compute/remote-definition.js.map +1 -1
- package/dist/compute/safe-url.d.ts +2 -1
- package/dist/compute/safe-url.d.ts.map +1 -1
- package/dist/compute/safe-url.js +31 -1
- package/dist/compute/safe-url.js.map +1 -1
- package/dist/compute/solve-cache-key.d.ts +5 -6
- package/dist/compute/solve-cache-key.d.ts.map +1 -1
- package/dist/compute/solve-cache-key.js +4 -3
- package/dist/compute/solve-cache-key.js.map +1 -1
- package/dist/compute/solve-cache-single-flight.d.ts +8 -1
- package/dist/compute/solve-cache-single-flight.d.ts.map +1 -1
- package/dist/compute/solve-cache-single-flight.js +4 -2
- package/dist/compute/solve-cache-single-flight.js.map +1 -1
- package/dist/compute/solve-pipeline.d.ts +31 -0
- package/dist/compute/solve-pipeline.d.ts.map +1 -1
- package/dist/compute/solve-pipeline.js +42 -1
- package/dist/compute/solve-pipeline.js.map +1 -1
- package/dist/definitions/load-for-render.d.ts +10 -1
- package/dist/definitions/load-for-render.d.ts.map +1 -1
- package/dist/definitions/load-for-render.js +33 -6
- package/dist/definitions/load-for-render.js.map +1 -1
- package/dist/logging/PinoLogger.d.ts +39 -0
- package/dist/logging/PinoLogger.d.ts.map +1 -0
- package/dist/logging/PinoLogger.js +189 -0
- package/dist/logging/PinoLogger.js.map +1 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +5 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/requestId.d.ts +45 -0
- package/dist/logging/requestId.d.ts.map +1 -0
- package/dist/logging/requestId.js +82 -0
- package/dist/logging/requestId.js.map +1 -0
- package/dist/providers/create-selva-providers.d.ts +11 -1
- package/dist/providers/create-selva-providers.d.ts.map +1 -1
- package/dist/providers/create-selva-providers.js +21 -8
- package/dist/providers/create-selva-providers.js.map +1 -1
- package/package.json +23 -19
- package/dist/access/__tests__/project-access-input.test.d.ts +0 -2
- package/dist/access/__tests__/project-access-input.test.d.ts.map +0 -1
- package/dist/access/__tests__/project-access-input.test.js +0 -146
- package/dist/access/__tests__/project-access-input.test.js.map +0 -1
- package/dist/compute/__tests__/client-cache.test.d.ts +0 -11
- package/dist/compute/__tests__/client-cache.test.d.ts.map +0 -1
- package/dist/compute/__tests__/client-cache.test.js +0 -218
- package/dist/compute/__tests__/client-cache.test.js.map +0 -1
- package/dist/compute/__tests__/definition-byte-cache.test.d.ts +0 -2
- package/dist/compute/__tests__/definition-byte-cache.test.d.ts.map +0 -1
- package/dist/compute/__tests__/definition-byte-cache.test.js +0 -107
- package/dist/compute/__tests__/definition-byte-cache.test.js.map +0 -1
- package/dist/compute/__tests__/rate-limit.test.d.ts +0 -2
- package/dist/compute/__tests__/rate-limit.test.d.ts.map +0 -1
- package/dist/compute/__tests__/rate-limit.test.js +0 -98
- package/dist/compute/__tests__/rate-limit.test.js.map +0 -1
- package/dist/compute/__tests__/remote-definition.test.d.ts +0 -11
- package/dist/compute/__tests__/remote-definition.test.d.ts.map +0 -1
- package/dist/compute/__tests__/remote-definition.test.js +0 -114
- package/dist/compute/__tests__/remote-definition.test.js.map +0 -1
- package/dist/compute/__tests__/safe-url.test.d.ts +0 -2
- package/dist/compute/__tests__/safe-url.test.d.ts.map +0 -1
- package/dist/compute/__tests__/safe-url.test.js +0 -71
- package/dist/compute/__tests__/safe-url.test.js.map +0 -1
- package/dist/compute/__tests__/solve-cache.test.d.ts +0 -13
- package/dist/compute/__tests__/solve-cache.test.d.ts.map +0 -1
- package/dist/compute/__tests__/solve-cache.test.js +0 -234
- package/dist/compute/__tests__/solve-cache.test.js.map +0 -1
- package/dist/compute/__tests__/solve-pipeline.test.d.ts +0 -11
- package/dist/compute/__tests__/solve-pipeline.test.d.ts.map +0 -1
- package/dist/compute/__tests__/solve-pipeline.test.js +0 -557
- package/dist/compute/__tests__/solve-pipeline.test.js.map +0 -1
- package/dist/compute/__tests__/transform-input.test.d.ts +0 -14
- package/dist/compute/__tests__/transform-input.test.d.ts.map +0 -1
- package/dist/compute/__tests__/transform-input.test.js +0 -120
- package/dist/compute/__tests__/transform-input.test.js.map +0 -1
- package/dist/definitions/__tests__/definition-service.test.d.ts +0 -14
- package/dist/definitions/__tests__/definition-service.test.d.ts.map +0 -1
- package/dist/definitions/__tests__/definition-service.test.js +0 -95
- package/dist/definitions/__tests__/definition-service.test.js.map +0 -1
- package/dist/definitions/__tests__/load-for-render.test.d.ts +0 -8
- package/dist/definitions/__tests__/load-for-render.test.d.ts.map +0 -1
- package/dist/definitions/__tests__/load-for-render.test.js +0 -196
- package/dist/definitions/__tests__/load-for-render.test.js.map +0 -1
- package/dist/definitions/__tests__/schema-extraction.test.d.ts +0 -11
- package/dist/definitions/__tests__/schema-extraction.test.d.ts.map +0 -1
- package/dist/definitions/__tests__/schema-extraction.test.js +0 -115
- package/dist/definitions/__tests__/schema-extraction.test.js.map +0 -1
- package/dist/http/__tests__/body-size.test.d.ts +0 -2
- package/dist/http/__tests__/body-size.test.d.ts.map +0 -1
- package/dist/http/__tests__/body-size.test.js +0 -24
- package/dist/http/__tests__/body-size.test.js.map +0 -1
- package/dist/http/__tests__/route-classifier.test.d.ts +0 -2
- package/dist/http/__tests__/route-classifier.test.d.ts.map +0 -1
- package/dist/http/__tests__/route-classifier.test.js +0 -54
- package/dist/http/__tests__/route-classifier.test.js.map +0 -1
- package/dist/http/__tests__/safe-redirect.test.d.ts +0 -2
- package/dist/http/__tests__/safe-redirect.test.d.ts.map +0 -1
- package/dist/http/__tests__/safe-redirect.test.js +0 -44
- package/dist/http/__tests__/safe-redirect.test.js.map +0 -1
- package/dist/http/__tests__/security-headers.test.d.ts +0 -2
- package/dist/http/__tests__/security-headers.test.d.ts.map +0 -1
- package/dist/http/__tests__/security-headers.test.js +0 -24
- package/dist/http/__tests__/security-headers.test.js.map +0 -1
- package/dist/ops/__tests__/semver.test.d.ts +0 -2
- package/dist/ops/__tests__/semver.test.d.ts.map +0 -1
- package/dist/ops/__tests__/semver.test.js +0 -38
- package/dist/ops/__tests__/semver.test.js.map +0 -1
- package/dist/providers/__tests__/create-selva-providers.test.d.ts +0 -7
- package/dist/providers/__tests__/create-selva-providers.test.d.ts.map +0 -1
- package/dist/providers/__tests__/create-selva-providers.test.js +0 -135
- package/dist/providers/__tests__/create-selva-providers.test.js.map +0 -1
- package/dist/tokens/__tests__/token-codec.test.d.ts +0 -2
- package/dist/tokens/__tests__/token-codec.test.d.ts.map +0 -1
- package/dist/tokens/__tests__/token-codec.test.js +0 -50
- package/dist/tokens/__tests__/token-codec.test.js.map +0 -1
- package/src/access/__tests__/project-access-input.test.ts +0 -175
- package/src/access/index.ts +0 -9
- package/src/access/project-access-input.ts +0 -145
- package/src/compute/__tests__/client-cache.test.ts +0 -244
- package/src/compute/__tests__/definition-byte-cache.test.ts +0 -129
- package/src/compute/__tests__/rate-limit.test.ts +0 -106
- package/src/compute/__tests__/remote-definition.test.ts +0 -140
- package/src/compute/__tests__/safe-url.test.ts +0 -72
- package/src/compute/__tests__/solve-cache.test.ts +0 -268
- package/src/compute/__tests__/solve-pipeline.test.ts +0 -660
- package/src/compute/__tests__/transform-input.test.ts +0 -162
- package/src/compute/client-cache.ts +0 -321
- package/src/compute/definition-byte-cache.ts +0 -168
- package/src/compute/index.ts +0 -82
- package/src/compute/limits.ts +0 -237
- package/src/compute/memory-solve-cache.ts +0 -0
- package/src/compute/rate-limit.ts +0 -110
- package/src/compute/remote-definition.ts +0 -149
- package/src/compute/safe-url.ts +0 -186
- package/src/compute/solve-cache-envelope.ts +0 -98
- package/src/compute/solve-cache-key.ts +0 -85
- package/src/compute/solve-cache-single-flight.ts +0 -59
- package/src/compute/solve-pipeline.ts +0 -603
- package/src/compute/transform-input.ts +0 -66
- package/src/definitions/__tests__/definition-service.test.ts +0 -131
- package/src/definitions/__tests__/load-for-render.test.ts +0 -244
- package/src/definitions/__tests__/schema-extraction.test.ts +0 -137
- package/src/definitions/definition-service.ts +0 -247
- package/src/definitions/index.ts +0 -22
- package/src/definitions/load-for-render.ts +0 -272
- package/src/definitions/schema-extraction.ts +0 -117
- package/src/errors/SentryErrorReporter.ts +0 -88
- package/src/errors/index.ts +0 -3
- package/src/http/__tests__/body-size.test.ts +0 -27
- package/src/http/__tests__/route-classifier.test.ts +0 -60
- package/src/http/__tests__/safe-redirect.test.ts +0 -47
- package/src/http/__tests__/security-headers.test.ts +0 -29
- package/src/http/body-size.ts +0 -24
- package/src/http/index.ts +0 -10
- package/src/http/redirect.ts +0 -16
- package/src/http/route-classifier.ts +0 -74
- package/src/http/security-headers.ts +0 -42
- package/src/index.ts +0 -27
- package/src/ops/__tests__/semver.test.ts +0 -42
- package/src/ops/index.ts +0 -3
- package/src/ops/semver.ts +0 -60
- package/src/providers/__tests__/create-selva-providers.test.ts +0 -172
- package/src/providers/__tests__/fixtures/override-config.mjs +0 -8
- package/src/providers/create-selva-providers.ts +0 -241
- package/src/providers/index.ts +0 -12
- package/src/tokens/__tests__/token-codec.test.ts +0 -63
- package/src/tokens/index.ts +0 -8
- package/src/tokens/token-codec.ts +0 -86
package/LICENSE
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @selvajs/server
|
|
2
|
+
|
|
3
|
+
Transport-agnostic compute/solve/definitions server building blocks for apps built on the
|
|
4
|
+
Selva engine: limits, rate limiting, SSRF guard, input transform, solve pipeline, definition
|
|
5
|
+
service, render loader, and structured logging. Consumed by `@selvajs/selva`; not tied to
|
|
6
|
+
SvelteKit or any specific HTTP framework.
|
|
7
|
+
|
|
8
|
+
Logging follows the same rule as the rest of the package: nothing here logs unless you wire
|
|
9
|
+
it. Modules that emit diagnostics take an optional `logger?: ILogger` and default to
|
|
10
|
+
`NoopLogger`, so an embedder that wires nothing gets silence rather than unsolicited stdout
|
|
11
|
+
writes. `@selvajs/server/logging` provides a pino-backed implementation (`createLogger`);
|
|
12
|
+
`pino` is an **optional** peer dependency, and without it `createLogger` degrades to a
|
|
13
|
+
console-backed logger rather than failing.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @selvajs/server
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Subpath exports
|
|
22
|
+
|
|
23
|
+
| Export | Contents |
|
|
24
|
+
| ----------------------------- | ------------------------------------------------------- |
|
|
25
|
+
| `@selvajs/server` | Root — package entry point |
|
|
26
|
+
| `@selvajs/server/compute` | Solve pipeline, SSRF guard, Rhino.Compute client wiring |
|
|
27
|
+
| `@selvajs/server/definitions` | Definition service + render loader |
|
|
28
|
+
| `@selvajs/server/providers` | Provider composition helpers |
|
|
29
|
+
| `@selvajs/server/tokens` | API token (PAT) codec |
|
|
30
|
+
| `@selvajs/server/errors` | Shared error types |
|
|
31
|
+
| `@selvajs/server/logging` | pino-backed `ILogger` + request-id correlation |
|
|
32
|
+
| `@selvajs/server/http` | Rate limiting, security headers, request limits |
|
|
33
|
+
| `@selvajs/server/access` | Access/permission checks |
|
|
34
|
+
| `@selvajs/server/ops` | Operational utilities |
|
|
35
|
+
|
|
36
|
+
Requires a `@selvajs/platform` provider (e.g. `@selvajs/local-provider` or
|
|
37
|
+
`@selvajs/supabase-provider`) for data/storage/auth.
|
|
38
|
+
|
|
39
|
+
See the root [CLAUDE.md](../../CLAUDE.md) for how this package fits into the wider Selva
|
|
40
|
+
architecture.
|
|
@@ -92,6 +92,13 @@ export interface ClientCacheConfig {
|
|
|
92
92
|
* `QUEUE_TIMEOUT`.
|
|
93
93
|
*/
|
|
94
94
|
queueWaitMs: number;
|
|
95
|
+
/**
|
|
96
|
+
* Byte budget for this client's in-process solve-response cache (scheduler
|
|
97
|
+
* L1), evicted LRU alongside its entry-count cap. Applies per warm client —
|
|
98
|
+
* total worst-case heap is this × `maxCachedClients`. `0` disables the L1
|
|
99
|
+
* response cache entirely (`ComputeLimits.computeResponseCacheBytes`).
|
|
100
|
+
*/
|
|
101
|
+
responseCacheMaxBytes: number;
|
|
95
102
|
/** Concise cache/timing logs. When false, `onDebugLog` is never invoked. */
|
|
96
103
|
debug: boolean;
|
|
97
104
|
/** VERBOSE lib-level logging (full solve request/response incl. geometry). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-cache.d.ts","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EAGjB,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAyB1B;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7E,mGAAmG;AACnG,MAAM,WAAW,cAAc;IAC9B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,cAAc,CAEjF;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;;;;OAQG;IACH,WAAW,EAAE;QAAE,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F;;;;;;;;OAQG;IACH,SAAS,EAAE;QACV,IAAI,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC;QACpE,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,yFAAyF;IACzF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,UAAU,EAAE,OAAO,CAAC;IACpB,qFAAqF;IACrF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yFAAyF;IACzF,0BAA0B,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7F;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IACzC,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"client-cache.d.ts","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EAGjB,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAyB1B;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7E,mGAAmG;AACnG,MAAM,WAAW,cAAc;IAC9B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,cAAc,CAEjF;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;;;;OAQG;IACH,WAAW,EAAE;QAAE,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F;;;;;;;;OAQG;IACH,SAAS,EAAE;QACV,IAAI,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC;QACpE,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,yFAAyF;IACzF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,UAAU,EAAE,OAAO,CAAC;IACpB,qFAAqF;IACrF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yFAAyF;IACzF,0BAA0B,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7F;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IACzC,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAuLxE"}
|
|
@@ -78,7 +78,12 @@ export function createClientCache(config) {
|
|
|
78
78
|
maxQueueDepth: config.maxQueueDepth || undefined,
|
|
79
79
|
queueWaitMs: config.queueWaitMs || undefined,
|
|
80
80
|
timeoutMs: config.maxSolveDurationMs,
|
|
81
|
-
cache:
|
|
81
|
+
// L1 response cache: count-capped AND byte-budgeted (audit C2) — entries
|
|
82
|
+
// can be as large as the response cap allows, so the count alone would
|
|
83
|
+
// let 20 × max-response-size pin gigabytes per warm client. `0` = off.
|
|
84
|
+
cache: config.responseCacheMaxBytes > 0
|
|
85
|
+
? { maxEntries: 20, ttlMs: 5 * 60_000, maxBytes: config.responseCacheMaxBytes }
|
|
86
|
+
: false,
|
|
82
87
|
// Solve large definitions by server cache-key (pointer) instead of
|
|
83
88
|
// re-uploading the full binary every solve. On a stale-pointer miss the
|
|
84
89
|
// client transparently re-uploads.
|
|
@@ -135,11 +140,15 @@ export function createClientCache(config) {
|
|
|
135
140
|
cache.set(key, existing);
|
|
136
141
|
return existing;
|
|
137
142
|
}
|
|
143
|
+
// A build is a Rhino.Compute handshake — expensive, and repeated builds for
|
|
144
|
+
// the same id mean churn (LRU thrash or config-rotation evictions).
|
|
145
|
+
debugLog(`[Compute/client-cache] miss — building warm client for server ${key}`);
|
|
138
146
|
const entry = await build(server, opts?.definitionGuid);
|
|
139
147
|
// Evict the least-recently-used entry before inserting when at capacity.
|
|
140
148
|
if (cache.size >= maxCachedClients) {
|
|
141
149
|
const oldestKey = cache.keys().next().value;
|
|
142
150
|
if (oldestKey !== undefined) {
|
|
151
|
+
debugLog(`[Compute/client-cache] LRU evicted warm client for server ${oldestKey} (cap ${maxCachedClients})`);
|
|
143
152
|
cache.get(oldestKey)?.scheduler.dispose();
|
|
144
153
|
cache.delete(oldestKey);
|
|
145
154
|
}
|
|
@@ -151,11 +160,13 @@ export function createClientCache(config) {
|
|
|
151
160
|
const key = id;
|
|
152
161
|
const entry = cache.get(key);
|
|
153
162
|
if (entry) {
|
|
163
|
+
debugLog(`[Compute/client-cache] evicted warm client for server ${key} (config change)`);
|
|
154
164
|
entry.scheduler.dispose();
|
|
155
165
|
cache.delete(key);
|
|
156
166
|
}
|
|
157
167
|
},
|
|
158
168
|
disposeAll() {
|
|
169
|
+
debugLog(`[Compute/client-cache] disposing all ${cache.size} warm client(s)`);
|
|
159
170
|
for (const entry of cache.values())
|
|
160
171
|
entry.scheduler.dispose();
|
|
161
172
|
cache.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-cache.js","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,kBAAkB,EAGlB,MAAM,kBAAkB,CAAC;AA2C1B,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,MAAkC;IAChE,OAAO,MAAM,CAAC,EAAoB,CAAC;AACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"client-cache.js","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"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"}
|
package/dist/compute/index.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ export { createMemorySolveResultCache, type MemorySolveResultCache, type SolveCa
|
|
|
4
4
|
export { deriveSolveCacheInputKey, type SolveCacheConfigSubset, type SolveCacheInputKey } from './solve-cache-key.js';
|
|
5
5
|
export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody, type EnvelopeHeader, type DecodedSolveCacheEntry } from './solve-cache-envelope.js';
|
|
6
6
|
export { createSolveCacheSingleFlight, type SolveCacheSingleFlight } from './solve-cache-single-flight.js';
|
|
7
|
-
export { createComputeRateLimiter, type ComputeRateLimiter, type ComputeRateLimiterConfig, type RateLimitResult } from './rate-limit.js';
|
|
7
|
+
export { createComputeRateLimiter, DEFAULT_MAX_KEYS, type ComputeRateLimiter, type ComputeRateLimiterConfig, type RateLimitResult } from './rate-limit.js';
|
|
8
8
|
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
|
|
9
9
|
export { transformInputParameter } from './transform-input.js';
|
|
10
10
|
export { createClientCache, serverIdentity, type ClientCache, type ClientCacheConfig, type CachedClient, type ResolvedServer, type ServerIdentity } from './client-cache.js';
|
|
11
11
|
export { createRemoteDefinitionFetcher, readBodyWithCap, type RemoteDefinitionFetcher, type RemoteDefinitionConfig } from './remote-definition.js';
|
|
12
|
-
export { runSolvePipeline, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type SolvePipelineArgs, type SolvePipelineCacheHook, type SolveOutcome, type SolveEnvelope, type SolvePhaseMetrics, type PipelineInput } from './solve-pipeline.js';
|
|
12
|
+
export { runSolvePipeline, adaptEnvelopeToEncoding, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type SolvePipelineArgs, type SolvePipelineCacheHook, type SolveOutcome, type SolveEnvelope, type SolvePhaseMetrics, type PipelineInput } from './solve-pipeline.js';
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACN,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,MAAM,qBAAqB,CAAC"}
|
package/dist/compute/index.js
CHANGED
|
@@ -5,10 +5,10 @@ export { createMemorySolveResultCache } from './memory-solve-cache.js';
|
|
|
5
5
|
export { deriveSolveCacheInputKey } from './solve-cache-key.js';
|
|
6
6
|
export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody } from './solve-cache-envelope.js';
|
|
7
7
|
export { createSolveCacheSingleFlight } from './solve-cache-single-flight.js';
|
|
8
|
-
export { createComputeRateLimiter } from './rate-limit.js';
|
|
8
|
+
export { createComputeRateLimiter, DEFAULT_MAX_KEYS } from './rate-limit.js';
|
|
9
9
|
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
|
|
10
10
|
export { transformInputParameter } from './transform-input.js';
|
|
11
11
|
export { createClientCache, serverIdentity } from './client-cache.js';
|
|
12
12
|
export { createRemoteDefinitionFetcher, readBodyWithCap } from './remote-definition.js';
|
|
13
|
-
export { runSolvePipeline, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER } from './solve-pipeline.js';
|
|
13
|
+
export { runSolvePipeline, adaptEnvelopeToEncoding, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER } from './solve-pipeline.js';
|
|
14
14
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EAIzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAG5B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EAGxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EAGf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAE5B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EAIzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAG5B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EAGxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EAGf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAE5B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EACN,iBAAiB,EACjB,cAAc,EAMd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EAOtB,MAAM,qBAAqB,CAAC"}
|
package/dist/compute/limits.d.ts
CHANGED
|
@@ -17,25 +17,26 @@
|
|
|
17
17
|
* live in `@selvajs/ui` and are bundled into the client, so they can't read
|
|
18
18
|
* env — they carry their own defaults.
|
|
19
19
|
*/
|
|
20
|
+
import { type ILogger } from '@selvajs/platform';
|
|
20
21
|
/** Env source: any string→string map. `$env/dynamic/private` satisfies this. */
|
|
21
22
|
export type EnvRecord = Record<string, string | undefined>;
|
|
22
23
|
/**
|
|
23
24
|
* Parse a positive integer env value. Returns `fallback` when absent, and warns
|
|
24
25
|
* + falls back when present but not a finite positive number.
|
|
25
26
|
*/
|
|
26
|
-
export declare function readPositiveInt(env: EnvRecord, name: string, fallback: number): number;
|
|
27
|
+
export declare function readPositiveInt(env: EnvRecord, name: string, fallback: number, logger?: ILogger): number;
|
|
27
28
|
/**
|
|
28
29
|
* Parse a non-negative integer env value — like {@link readPositiveInt} but `0`
|
|
29
30
|
* is a valid value (used by knobs where `0` means "disable", e.g. a cache
|
|
30
31
|
* budget). Returns `fallback` when absent, and warns + falls back when present
|
|
31
32
|
* but not a finite non-negative number.
|
|
32
33
|
*/
|
|
33
|
-
export declare function readNonNegativeInt(env: EnvRecord, name: string, fallback: number): number;
|
|
34
|
+
export declare function readNonNegativeInt(env: EnvRecord, name: string, fallback: number, logger?: ILogger): number;
|
|
34
35
|
/**
|
|
35
36
|
* Parse a boolean env flag. Accepts `true/1/yes/on` (case-insensitive) as true
|
|
36
37
|
* and `false/0/no/off` as false; any other / absent value falls back.
|
|
37
38
|
*/
|
|
38
|
-
export declare function readBool(env: EnvRecord, name: string, fallback: boolean): boolean;
|
|
39
|
+
export declare function readBool(env: EnvRecord, name: string, fallback: boolean, logger?: ILogger): boolean;
|
|
39
40
|
/** Fully-resolved server-side compute limits. */
|
|
40
41
|
export interface ComputeLimits {
|
|
41
42
|
/**
|
|
@@ -55,7 +56,9 @@ export interface ComputeLimits {
|
|
|
55
56
|
/**
|
|
56
57
|
* Largest .gh definition accepted on upload AND the largest remote definition
|
|
57
58
|
* fetched for compute — kept in lockstep so a remote URL can't smuggle a file
|
|
58
|
-
* past the upload cap.
|
|
59
|
+
* past the upload cap. Defaults to 50 MB to match Rhino.Compute's own
|
|
60
|
+
* `RHINO_COMPUTE_MAX_REQUEST_SIZE` default: a larger file 413s at compute
|
|
61
|
+
* regardless, so accepting it here only defers the failure.
|
|
59
62
|
*/
|
|
60
63
|
maxGhFileSize: number;
|
|
61
64
|
maxImageFileSize: number;
|
|
@@ -63,6 +66,9 @@ export interface ComputeLimits {
|
|
|
63
66
|
* /api/compute JSON *request* body cap (inputs + values, not the .gh). A
|
|
64
67
|
* `file`-widget input embeds geometry as base64 in `values`; must stay <=
|
|
65
68
|
* the adapter-node global BODY_SIZE_LIMIT or the global backstop rejects first.
|
|
69
|
+
* Defaults to 210 MB: the client allows a 150 MB raw file, base64 inflates it
|
|
70
|
+
* to ~200 MB on the wire, plus JSON envelope slack — and it matches the
|
|
71
|
+
* `BODY_SIZE_LIMIT=210M` shipped in `.env.example`.
|
|
66
72
|
*/
|
|
67
73
|
computeRequestMaxBytes: number;
|
|
68
74
|
/**
|
|
@@ -70,6 +76,10 @@ export interface ComputeLimits {
|
|
|
70
76
|
* in the response; guards V8's ~512 MB single-string wall (a `JSON.stringify`
|
|
71
77
|
* `RangeError`) and browser-tab OOM. Defensive backstop → clear 413 instead of
|
|
72
78
|
* an opaque crash (real fix is out-of-band streaming, ADR 0003).
|
|
79
|
+
*
|
|
80
|
+
* 300 MB is intentional and NOT a dev leftover — sized above any legitimate
|
|
81
|
+
* inline payload but below the V8 string wall. Unlike the request cap it is
|
|
82
|
+
* not bounded by BODY_SIZE_LIMIT, which only applies to inbound bodies.
|
|
73
83
|
*/
|
|
74
84
|
computeResponseMaxBytes: number;
|
|
75
85
|
/** Hard cap on fetching a remote definition — tracks `maxGhFileSize`. */
|
|
@@ -136,6 +146,17 @@ export interface ComputeLimits {
|
|
|
136
146
|
* span KB→hundreds of MB; env value is MB, converted here.
|
|
137
147
|
*/
|
|
138
148
|
computeDefinitionByteCacheBytes: number;
|
|
149
|
+
/**
|
|
150
|
+
* Total-byte budget (bytes) for EACH warm client's in-process solve-response
|
|
151
|
+
* cache (the scheduler L1 — the `fromCache` layer in front of Rhino.Compute).
|
|
152
|
+
* Responses range KB→100s of MB (`computeResponseMaxBytes` allows 300 MB), so
|
|
153
|
+
* the L1's entry-count cap alone can't bound heap; this adds a byte LRU on
|
|
154
|
+
* top. Applies PER warm client — worst-case heap is this × the number of
|
|
155
|
+
* distinct compute servers kept warm (`maxCachedClients`, default 16), though
|
|
156
|
+
* real deployments run 1–2 servers. `0` disables the L1 response cache
|
|
157
|
+
* entirely (every solve goes to compute or the L2). Env value is MB.
|
|
158
|
+
*/
|
|
159
|
+
computeResponseCacheBytes: number;
|
|
139
160
|
/**
|
|
140
161
|
* Default per-definition entry quota for the durable L2 solve cache (H1), used
|
|
141
162
|
* when a definition's `solveCacheLimit` is absent (inherit). A definition may
|
|
@@ -160,5 +181,5 @@ export interface ComputeLimits {
|
|
|
160
181
|
* always yields the same limits, and nothing is read ambiently. The app calls
|
|
161
182
|
* this once at its composition root with `$env/dynamic/private`.
|
|
162
183
|
*/
|
|
163
|
-
export declare function resolveComputeLimits(env: EnvRecord): ComputeLimits;
|
|
184
|
+
export declare function resolveComputeLimits(env: EnvRecord, logger?: ILogger): ComputeLimits;
|
|
164
185
|
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI7D,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAS3D;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,OAAc,GACpB,OAAO,CAaT;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC7B;;;;;;;;;OASG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,wBAAwB,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,8BAA8B,EAAE,MAAM,CAAC;IACvC,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,yBAAyB,EAAE,OAAO,CAAC;IACnC;;;;;;;;OAQG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC;;;;;;;;OAQG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,+BAA+B,EAAE,MAAM,CAAC;IACxC;;;;;;;;;OASG;IACH,yBAAyB,EAAE,MAAM,CAAC;IAClC;;;;;;;;OAQG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,GAAE,OAAc,GAAG,aAAa,CAmD1F"}
|
package/dist/compute/limits.js
CHANGED
|
@@ -17,18 +17,30 @@
|
|
|
17
17
|
* live in `@selvajs/ui` and are bundled into the client, so they can't read
|
|
18
18
|
* env — they carry their own defaults.
|
|
19
19
|
*/
|
|
20
|
+
import { NoopLogger } from '@selvajs/platform';
|
|
20
21
|
const MB = 1024 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* Optional structured logger for the env-parsing diagnostics below. Defaults to
|
|
24
|
+
* `NoopLogger`: this is library code, so an embedder that wires nothing gets
|
|
25
|
+
* silence rather than unsolicited stdout writes. The app passes its real logger.
|
|
26
|
+
*/
|
|
27
|
+
const noop = new NoopLogger();
|
|
21
28
|
/**
|
|
22
29
|
* Parse a positive integer env value. Returns `fallback` when absent, and warns
|
|
23
30
|
* + falls back when present but not a finite positive number.
|
|
24
31
|
*/
|
|
25
|
-
export function readPositiveInt(env, name, fallback) {
|
|
32
|
+
export function readPositiveInt(env, name, fallback, logger = noop) {
|
|
26
33
|
const raw = env[name];
|
|
27
34
|
if (!raw)
|
|
28
35
|
return fallback;
|
|
29
36
|
const parsed = Number(raw);
|
|
30
37
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
31
|
-
|
|
38
|
+
logger.warn('Invalid env value, falling back to default', {
|
|
39
|
+
component: 'selva',
|
|
40
|
+
envVar: name,
|
|
41
|
+
value: raw,
|
|
42
|
+
fallback
|
|
43
|
+
});
|
|
32
44
|
return fallback;
|
|
33
45
|
}
|
|
34
46
|
return Math.floor(parsed);
|
|
@@ -39,13 +51,18 @@ export function readPositiveInt(env, name, fallback) {
|
|
|
39
51
|
* budget). Returns `fallback` when absent, and warns + falls back when present
|
|
40
52
|
* but not a finite non-negative number.
|
|
41
53
|
*/
|
|
42
|
-
export function readNonNegativeInt(env, name, fallback) {
|
|
54
|
+
export function readNonNegativeInt(env, name, fallback, logger = noop) {
|
|
43
55
|
const raw = env[name];
|
|
44
56
|
if (!raw)
|
|
45
57
|
return fallback;
|
|
46
58
|
const parsed = Number(raw);
|
|
47
59
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
48
|
-
|
|
60
|
+
logger.warn('Invalid env value, falling back to default', {
|
|
61
|
+
component: 'selva',
|
|
62
|
+
envVar: name,
|
|
63
|
+
value: raw,
|
|
64
|
+
fallback
|
|
65
|
+
});
|
|
49
66
|
return fallback;
|
|
50
67
|
}
|
|
51
68
|
return Math.floor(parsed);
|
|
@@ -54,7 +71,7 @@ export function readNonNegativeInt(env, name, fallback) {
|
|
|
54
71
|
* Parse a boolean env flag. Accepts `true/1/yes/on` (case-insensitive) as true
|
|
55
72
|
* and `false/0/no/off` as false; any other / absent value falls back.
|
|
56
73
|
*/
|
|
57
|
-
export function readBool(env, name, fallback) {
|
|
74
|
+
export function readBool(env, name, fallback, logger = noop) {
|
|
58
75
|
const raw = env[name];
|
|
59
76
|
if (raw == null || raw === '')
|
|
60
77
|
return fallback;
|
|
@@ -63,7 +80,12 @@ export function readBool(env, name, fallback) {
|
|
|
63
80
|
return true;
|
|
64
81
|
if (v === 'false' || v === '0' || v === 'no' || v === 'off')
|
|
65
82
|
return false;
|
|
66
|
-
|
|
83
|
+
logger.warn('Invalid env value, falling back to default', {
|
|
84
|
+
component: 'selva',
|
|
85
|
+
envVar: name,
|
|
86
|
+
value: raw,
|
|
87
|
+
fallback
|
|
88
|
+
});
|
|
67
89
|
return fallback;
|
|
68
90
|
}
|
|
69
91
|
/**
|
|
@@ -71,40 +93,44 @@ export function readBool(env, name, fallback) {
|
|
|
71
93
|
* always yields the same limits, and nothing is read ambiently. The app calls
|
|
72
94
|
* this once at its composition root with `$env/dynamic/private`.
|
|
73
95
|
*/
|
|
74
|
-
export function resolveComputeLimits(env) {
|
|
75
|
-
const maxGhFileSize = readPositiveInt(env, 'MAX_GH_FILE_SIZE_BYTES',
|
|
96
|
+
export function resolveComputeLimits(env, logger = noop) {
|
|
97
|
+
const maxGhFileSize = readPositiveInt(env, 'MAX_GH_FILE_SIZE_BYTES', 50 * MB, logger);
|
|
76
98
|
return {
|
|
77
|
-
maxSolveDurationMs: readPositiveInt(env, 'MAX_SOLVE_DURATION_MS', 100_000),
|
|
78
|
-
rateLimitWindowMs: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_WINDOW_MS', 100_000),
|
|
79
|
-
rateLimitMaxRequests: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_MAX', 120),
|
|
99
|
+
maxSolveDurationMs: readPositiveInt(env, 'MAX_SOLVE_DURATION_MS', 100_000, logger),
|
|
100
|
+
rateLimitWindowMs: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_WINDOW_MS', 100_000, logger),
|
|
101
|
+
rateLimitMaxRequests: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_MAX', 120, logger),
|
|
80
102
|
maxGhFileSize,
|
|
81
|
-
maxImageFileSize: readPositiveInt(env, 'MAX_IMAGE_FILE_SIZE_BYTES', 10 * MB),
|
|
82
|
-
computeRequestMaxBytes: readPositiveInt(env, 'COMPUTE_REQUEST_MAX_BYTES',
|
|
83
|
-
computeResponseMaxBytes: readPositiveInt(env, 'COMPUTE_RESPONSE_MAX_BYTES', 300 * MB),
|
|
103
|
+
maxImageFileSize: readPositiveInt(env, 'MAX_IMAGE_FILE_SIZE_BYTES', 10 * MB, logger),
|
|
104
|
+
computeRequestMaxBytes: readPositiveInt(env, 'COMPUTE_REQUEST_MAX_BYTES', 210 * MB, logger),
|
|
105
|
+
computeResponseMaxBytes: readPositiveInt(env, 'COMPUTE_RESPONSE_MAX_BYTES', 300 * MB, logger),
|
|
84
106
|
// Tracks the upload cap so a remote URL can't smuggle a larger file.
|
|
85
107
|
remoteDefinitionMaxBytes: maxGhFileSize,
|
|
86
|
-
remoteDefinitionFetchTimeoutMs: readPositiveInt(env, 'REMOTE_DEFINITION_FETCH_TIMEOUT_MS', 30_000),
|
|
87
|
-
definitionCacheTtlMs: readPositiveInt(env, 'DEFINITION_CACHE_TTL_MS', 5 * 60 * 1000),
|
|
88
|
-
computeReuseDefinitionCache: readBool(env, 'COMPUTE_REUSE_DEFINITION_CACHE', true),
|
|
89
|
-
computeServerCachesolve: readBool(env, 'COMPUTE_SERVER_CACHESOLVE', true),
|
|
90
|
-
computeCacheErroredSolves: readBool(env, 'COMPUTE_CACHE_ERRORED_SOLVES', false),
|
|
108
|
+
remoteDefinitionFetchTimeoutMs: readPositiveInt(env, 'REMOTE_DEFINITION_FETCH_TIMEOUT_MS', 30_000, logger),
|
|
109
|
+
definitionCacheTtlMs: readPositiveInt(env, 'DEFINITION_CACHE_TTL_MS', 5 * 60 * 1000, logger),
|
|
110
|
+
computeReuseDefinitionCache: readBool(env, 'COMPUTE_REUSE_DEFINITION_CACHE', true, logger),
|
|
111
|
+
computeServerCachesolve: readBool(env, 'COMPUTE_SERVER_CACHESOLVE', true, logger),
|
|
112
|
+
computeCacheErroredSolves: readBool(env, 'COMPUTE_CACHE_ERRORED_SOLVES', false, logger),
|
|
91
113
|
// 4 matches rhino.compute's default --childcount; tune to the actual VM.
|
|
92
|
-
computeMaxConcurrentSolves: readPositiveInt(env, 'COMPUTE_MAX_CONCURRENT', 4),
|
|
114
|
+
computeMaxConcurrentSolves: readPositiveInt(env, 'COMPUTE_MAX_CONCURRENT', 4, logger),
|
|
93
115
|
// Both 0 (unbounded / no deadline) by default — nothing sheds until an
|
|
94
116
|
// operator who's measured their pool opts in. readNonNegativeInt so `0` is a
|
|
95
117
|
// valid "disabled" value, not treated as invalid.
|
|
96
|
-
computeMaxQueueDepth: readNonNegativeInt(env, 'COMPUTE_MAX_QUEUE_DEPTH', 0),
|
|
97
|
-
computeQueueWaitMs: readNonNegativeInt(env, 'COMPUTE_QUEUE_WAIT_MS', 0),
|
|
118
|
+
computeMaxQueueDepth: readNonNegativeInt(env, 'COMPUTE_MAX_QUEUE_DEPTH', 0, logger),
|
|
119
|
+
computeQueueWaitMs: readNonNegativeInt(env, 'COMPUTE_QUEUE_WAIT_MS', 0, logger),
|
|
98
120
|
// Env is MB; 0 disables. Default 256 MB holds a handful of typical
|
|
99
121
|
// definitions warm without pinning gigabytes. readNonNegativeInt so `0`
|
|
100
122
|
// (disable) is honored rather than treated as invalid.
|
|
101
|
-
computeDefinitionByteCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_BYTE_CACHE_MB', 256) * MB,
|
|
123
|
+
computeDefinitionByteCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_BYTE_CACHE_MB', 256, logger) * MB,
|
|
124
|
+
// Per-warm-client L1 response cache budget. Env is MB; 0 disables the L1.
|
|
125
|
+
// Default 256 MB: enough to keep a scrub session's recent responses warm
|
|
126
|
+
// without letting 20 × 300 MB worst-case entries pin gigabytes (audit C2).
|
|
127
|
+
computeResponseCacheBytes: readNonNegativeInt(env, 'COMPUTE_RESPONSE_CACHE_MB', 256, logger) * MB,
|
|
102
128
|
// L2 durable solve cache (H1). Default per-definition quota 0 = inherit-to-off
|
|
103
129
|
// until an operator opts in; the memory backend also only mounts under
|
|
104
130
|
// SOLVE_CACHE_PROVIDER=memory, so nothing caches by accident. Byte backstop
|
|
105
131
|
// 512 MB caps total heap use across all definitions (env is MB).
|
|
106
|
-
solveCacheDefaultMaxEntries: readNonNegativeInt(env, 'SOLVE_CACHE_DEFAULT_MAX_ENTRIES', 0),
|
|
107
|
-
solveCacheMaxTotalBytes: readNonNegativeInt(env, 'SOLVE_CACHE_MAX_TOTAL_MB', 512) * MB
|
|
132
|
+
solveCacheDefaultMaxEntries: readNonNegativeInt(env, 'SOLVE_CACHE_DEFAULT_MAX_ENTRIES', 0, logger),
|
|
133
|
+
solveCacheMaxTotalBytes: readNonNegativeInt(env, 'SOLVE_CACHE_MAX_TOTAL_MB', 512, logger) * MB
|
|
108
134
|
};
|
|
109
135
|
}
|
|
110
136
|
//# sourceMappingURL=limits.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAKvB;;;GAGG;AACH,MAAM,UAAU,eAAe,
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,20 @@
|
|
|
9
9
|
* acceptable as a first line; a distributed limiter is a follow-up. The state
|
|
10
10
|
* lives behind this factory so a shared-store implementation can slot in
|
|
11
11
|
* later without changing the call sites.
|
|
12
|
+
* - Bounded. A key is only interesting until its window expires, but nothing
|
|
13
|
+
* revisits a bucket that is never checked again — so an unswept `Map` grows
|
|
14
|
+
* with the number of distinct keys ever seen (every user, share link and
|
|
15
|
+
* login IP, forever). Two inline mechanisms keep it bounded, both driven by
|
|
16
|
+
* `check`/`peek` rather than a timer: a timer would keep the event loop alive
|
|
17
|
+
* and give this module a lifecycle its callers don't have.
|
|
18
|
+
* 1. Amortized sweep — every `sweepIntervalMs` (default: one window), drop
|
|
19
|
+
* every expired bucket. Expired buckets are semantically dead: `check`
|
|
20
|
+
* already treats them as absent, so dropping them changes no verdict.
|
|
21
|
+
* 2. Hard cap — if live buckets alone exceed `maxKeys`, evict the entries
|
|
22
|
+
* closest to resetting. Those are the ones with the least budget left to
|
|
23
|
+
* protect, so a distinct-key flood can't pin unbounded memory. Eviction
|
|
24
|
+
* forgives a key's counter, so `maxKeys` is sized to be unreachable in
|
|
25
|
+
* normal operation and only bites under a key-space attack.
|
|
12
26
|
* - Fixed window (vs sliding) — simpler, no per-request bookkeeping; the
|
|
13
27
|
* "burst at window edges" mode is fine here (share-link `maxSolves` and
|
|
14
28
|
* per-user auth already prevent unbounded abuse — this flattens short spikes).
|
|
@@ -33,7 +47,24 @@ export interface ComputeRateLimiterConfig {
|
|
|
33
47
|
windowMs: number;
|
|
34
48
|
/** Max requests admitted per key per window. */
|
|
35
49
|
maxPerWindow: number;
|
|
50
|
+
/**
|
|
51
|
+
* Max retained buckets. Once live (unexpired) buckets exceed this, the ones
|
|
52
|
+
* nearest their reset are evicted. Evicting forgives that key's counter, so
|
|
53
|
+
* this is a memory backstop against a distinct-key flood, not a tuning knob:
|
|
54
|
+
* size it well above the plausible number of concurrently-active keys.
|
|
55
|
+
* Defaults to {@link DEFAULT_MAX_KEYS}.
|
|
56
|
+
*/
|
|
57
|
+
maxKeys?: number;
|
|
58
|
+
/**
|
|
59
|
+
* How often (ms) to sweep expired buckets, amortized onto `check`/`peek`.
|
|
60
|
+
* Defaults to `windowMs` — every bucket swept is already past its reset, so
|
|
61
|
+
* sweeping more often than a window buys nothing. `0` disables the sweep,
|
|
62
|
+
* leaving only the `maxKeys` cap (test seam).
|
|
63
|
+
*/
|
|
64
|
+
sweepIntervalMs?: number;
|
|
36
65
|
}
|
|
66
|
+
/** Default {@link ComputeRateLimiterConfig.maxKeys}. */
|
|
67
|
+
export declare const DEFAULT_MAX_KEYS = 100000;
|
|
37
68
|
export interface ComputeRateLimiter {
|
|
38
69
|
/**
|
|
39
70
|
* Check + record one request against `key`. Increments the counter on allow.
|
|
@@ -48,8 +79,17 @@ export interface ComputeRateLimiter {
|
|
|
48
79
|
peek(key: string): RateLimitResult;
|
|
49
80
|
/** Drop `key`'s bucket — e.g. a successful login forgives prior failures. */
|
|
50
81
|
clear(key: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Requests recorded against `key` in the current window; `0` when the bucket
|
|
84
|
+
* is absent or expired. Observability/test seam — which key a caller charges
|
|
85
|
+
* is a security property (an anonymous share solve must not spend the link
|
|
86
|
+
* owner's budget), and a bare allow/deny verdict can't express it.
|
|
87
|
+
*/
|
|
88
|
+
count(key: string): number;
|
|
51
89
|
/** Test seam — wipes in-memory state. Production code never calls this. */
|
|
52
90
|
reset(): void;
|
|
91
|
+
/** Retained bucket count, including any not yet swept. For tests/observability. */
|
|
92
|
+
size(): number;
|
|
53
93
|
/** The resolved config, exposed for time-based test assertions. */
|
|
54
94
|
readonly config: Readonly<ComputeRateLimiterConfig>;
|
|
55
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/compute/rate-limit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/compute/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACxC,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAExC,MAAM,WAAW,kBAAkB;IAClC;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACpC;;;;OAIG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACnC,6EAA6E;IAC7E,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAC;IACd,mFAAmF;IACnF,IAAI,IAAI,MAAM,CAAC;IACf,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACpD;AAOD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CA8F7F"}
|