@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.
Files changed (188) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +40 -0
  3. package/dist/compute/client-cache.d.ts +7 -0
  4. package/dist/compute/client-cache.d.ts.map +1 -1
  5. package/dist/compute/client-cache.js +12 -1
  6. package/dist/compute/client-cache.js.map +1 -1
  7. package/dist/compute/index.d.ts +2 -2
  8. package/dist/compute/index.d.ts.map +1 -1
  9. package/dist/compute/index.js +2 -2
  10. package/dist/compute/index.js.map +1 -1
  11. package/dist/compute/limits.d.ts +26 -5
  12. package/dist/compute/limits.d.ts.map +1 -1
  13. package/dist/compute/limits.js +51 -25
  14. package/dist/compute/limits.js.map +1 -1
  15. package/dist/compute/rate-limit.d.ts +40 -0
  16. package/dist/compute/rate-limit.d.ts.map +1 -1
  17. package/dist/compute/rate-limit.js +72 -1
  18. package/dist/compute/rate-limit.js.map +1 -1
  19. package/dist/compute/remote-definition.d.ts +7 -0
  20. package/dist/compute/remote-definition.d.ts.map +1 -1
  21. package/dist/compute/remote-definition.js +3 -1
  22. package/dist/compute/remote-definition.js.map +1 -1
  23. package/dist/compute/safe-url.d.ts +2 -1
  24. package/dist/compute/safe-url.d.ts.map +1 -1
  25. package/dist/compute/safe-url.js +31 -1
  26. package/dist/compute/safe-url.js.map +1 -1
  27. package/dist/compute/solve-cache-key.d.ts +5 -6
  28. package/dist/compute/solve-cache-key.d.ts.map +1 -1
  29. package/dist/compute/solve-cache-key.js +4 -3
  30. package/dist/compute/solve-cache-key.js.map +1 -1
  31. package/dist/compute/solve-cache-single-flight.d.ts +8 -1
  32. package/dist/compute/solve-cache-single-flight.d.ts.map +1 -1
  33. package/dist/compute/solve-cache-single-flight.js +4 -2
  34. package/dist/compute/solve-cache-single-flight.js.map +1 -1
  35. package/dist/compute/solve-pipeline.d.ts +31 -0
  36. package/dist/compute/solve-pipeline.d.ts.map +1 -1
  37. package/dist/compute/solve-pipeline.js +42 -1
  38. package/dist/compute/solve-pipeline.js.map +1 -1
  39. package/dist/definitions/load-for-render.d.ts +10 -1
  40. package/dist/definitions/load-for-render.d.ts.map +1 -1
  41. package/dist/definitions/load-for-render.js +33 -6
  42. package/dist/definitions/load-for-render.js.map +1 -1
  43. package/dist/logging/PinoLogger.d.ts +39 -0
  44. package/dist/logging/PinoLogger.d.ts.map +1 -0
  45. package/dist/logging/PinoLogger.js +189 -0
  46. package/dist/logging/PinoLogger.js.map +1 -0
  47. package/dist/logging/index.d.ts +3 -0
  48. package/dist/logging/index.d.ts.map +1 -0
  49. package/dist/logging/index.js +5 -0
  50. package/dist/logging/index.js.map +1 -0
  51. package/dist/logging/requestId.d.ts +45 -0
  52. package/dist/logging/requestId.d.ts.map +1 -0
  53. package/dist/logging/requestId.js +82 -0
  54. package/dist/logging/requestId.js.map +1 -0
  55. package/dist/providers/create-selva-providers.d.ts +11 -1
  56. package/dist/providers/create-selva-providers.d.ts.map +1 -1
  57. package/dist/providers/create-selva-providers.js +21 -8
  58. package/dist/providers/create-selva-providers.js.map +1 -1
  59. package/package.json +23 -19
  60. package/dist/access/__tests__/project-access-input.test.d.ts +0 -2
  61. package/dist/access/__tests__/project-access-input.test.d.ts.map +0 -1
  62. package/dist/access/__tests__/project-access-input.test.js +0 -146
  63. package/dist/access/__tests__/project-access-input.test.js.map +0 -1
  64. package/dist/compute/__tests__/client-cache.test.d.ts +0 -11
  65. package/dist/compute/__tests__/client-cache.test.d.ts.map +0 -1
  66. package/dist/compute/__tests__/client-cache.test.js +0 -218
  67. package/dist/compute/__tests__/client-cache.test.js.map +0 -1
  68. package/dist/compute/__tests__/definition-byte-cache.test.d.ts +0 -2
  69. package/dist/compute/__tests__/definition-byte-cache.test.d.ts.map +0 -1
  70. package/dist/compute/__tests__/definition-byte-cache.test.js +0 -107
  71. package/dist/compute/__tests__/definition-byte-cache.test.js.map +0 -1
  72. package/dist/compute/__tests__/rate-limit.test.d.ts +0 -2
  73. package/dist/compute/__tests__/rate-limit.test.d.ts.map +0 -1
  74. package/dist/compute/__tests__/rate-limit.test.js +0 -98
  75. package/dist/compute/__tests__/rate-limit.test.js.map +0 -1
  76. package/dist/compute/__tests__/remote-definition.test.d.ts +0 -11
  77. package/dist/compute/__tests__/remote-definition.test.d.ts.map +0 -1
  78. package/dist/compute/__tests__/remote-definition.test.js +0 -114
  79. package/dist/compute/__tests__/remote-definition.test.js.map +0 -1
  80. package/dist/compute/__tests__/safe-url.test.d.ts +0 -2
  81. package/dist/compute/__tests__/safe-url.test.d.ts.map +0 -1
  82. package/dist/compute/__tests__/safe-url.test.js +0 -71
  83. package/dist/compute/__tests__/safe-url.test.js.map +0 -1
  84. package/dist/compute/__tests__/solve-cache.test.d.ts +0 -13
  85. package/dist/compute/__tests__/solve-cache.test.d.ts.map +0 -1
  86. package/dist/compute/__tests__/solve-cache.test.js +0 -234
  87. package/dist/compute/__tests__/solve-cache.test.js.map +0 -1
  88. package/dist/compute/__tests__/solve-pipeline.test.d.ts +0 -11
  89. package/dist/compute/__tests__/solve-pipeline.test.d.ts.map +0 -1
  90. package/dist/compute/__tests__/solve-pipeline.test.js +0 -557
  91. package/dist/compute/__tests__/solve-pipeline.test.js.map +0 -1
  92. package/dist/compute/__tests__/transform-input.test.d.ts +0 -14
  93. package/dist/compute/__tests__/transform-input.test.d.ts.map +0 -1
  94. package/dist/compute/__tests__/transform-input.test.js +0 -120
  95. package/dist/compute/__tests__/transform-input.test.js.map +0 -1
  96. package/dist/definitions/__tests__/definition-service.test.d.ts +0 -14
  97. package/dist/definitions/__tests__/definition-service.test.d.ts.map +0 -1
  98. package/dist/definitions/__tests__/definition-service.test.js +0 -95
  99. package/dist/definitions/__tests__/definition-service.test.js.map +0 -1
  100. package/dist/definitions/__tests__/load-for-render.test.d.ts +0 -8
  101. package/dist/definitions/__tests__/load-for-render.test.d.ts.map +0 -1
  102. package/dist/definitions/__tests__/load-for-render.test.js +0 -196
  103. package/dist/definitions/__tests__/load-for-render.test.js.map +0 -1
  104. package/dist/definitions/__tests__/schema-extraction.test.d.ts +0 -11
  105. package/dist/definitions/__tests__/schema-extraction.test.d.ts.map +0 -1
  106. package/dist/definitions/__tests__/schema-extraction.test.js +0 -115
  107. package/dist/definitions/__tests__/schema-extraction.test.js.map +0 -1
  108. package/dist/http/__tests__/body-size.test.d.ts +0 -2
  109. package/dist/http/__tests__/body-size.test.d.ts.map +0 -1
  110. package/dist/http/__tests__/body-size.test.js +0 -24
  111. package/dist/http/__tests__/body-size.test.js.map +0 -1
  112. package/dist/http/__tests__/route-classifier.test.d.ts +0 -2
  113. package/dist/http/__tests__/route-classifier.test.d.ts.map +0 -1
  114. package/dist/http/__tests__/route-classifier.test.js +0 -54
  115. package/dist/http/__tests__/route-classifier.test.js.map +0 -1
  116. package/dist/http/__tests__/safe-redirect.test.d.ts +0 -2
  117. package/dist/http/__tests__/safe-redirect.test.d.ts.map +0 -1
  118. package/dist/http/__tests__/safe-redirect.test.js +0 -44
  119. package/dist/http/__tests__/safe-redirect.test.js.map +0 -1
  120. package/dist/http/__tests__/security-headers.test.d.ts +0 -2
  121. package/dist/http/__tests__/security-headers.test.d.ts.map +0 -1
  122. package/dist/http/__tests__/security-headers.test.js +0 -24
  123. package/dist/http/__tests__/security-headers.test.js.map +0 -1
  124. package/dist/ops/__tests__/semver.test.d.ts +0 -2
  125. package/dist/ops/__tests__/semver.test.d.ts.map +0 -1
  126. package/dist/ops/__tests__/semver.test.js +0 -38
  127. package/dist/ops/__tests__/semver.test.js.map +0 -1
  128. package/dist/providers/__tests__/create-selva-providers.test.d.ts +0 -7
  129. package/dist/providers/__tests__/create-selva-providers.test.d.ts.map +0 -1
  130. package/dist/providers/__tests__/create-selva-providers.test.js +0 -135
  131. package/dist/providers/__tests__/create-selva-providers.test.js.map +0 -1
  132. package/dist/tokens/__tests__/token-codec.test.d.ts +0 -2
  133. package/dist/tokens/__tests__/token-codec.test.d.ts.map +0 -1
  134. package/dist/tokens/__tests__/token-codec.test.js +0 -50
  135. package/dist/tokens/__tests__/token-codec.test.js.map +0 -1
  136. package/src/access/__tests__/project-access-input.test.ts +0 -175
  137. package/src/access/index.ts +0 -9
  138. package/src/access/project-access-input.ts +0 -145
  139. package/src/compute/__tests__/client-cache.test.ts +0 -244
  140. package/src/compute/__tests__/definition-byte-cache.test.ts +0 -129
  141. package/src/compute/__tests__/rate-limit.test.ts +0 -106
  142. package/src/compute/__tests__/remote-definition.test.ts +0 -140
  143. package/src/compute/__tests__/safe-url.test.ts +0 -72
  144. package/src/compute/__tests__/solve-cache.test.ts +0 -268
  145. package/src/compute/__tests__/solve-pipeline.test.ts +0 -660
  146. package/src/compute/__tests__/transform-input.test.ts +0 -162
  147. package/src/compute/client-cache.ts +0 -321
  148. package/src/compute/definition-byte-cache.ts +0 -168
  149. package/src/compute/index.ts +0 -82
  150. package/src/compute/limits.ts +0 -237
  151. package/src/compute/memory-solve-cache.ts +0 -0
  152. package/src/compute/rate-limit.ts +0 -110
  153. package/src/compute/remote-definition.ts +0 -149
  154. package/src/compute/safe-url.ts +0 -186
  155. package/src/compute/solve-cache-envelope.ts +0 -98
  156. package/src/compute/solve-cache-key.ts +0 -85
  157. package/src/compute/solve-cache-single-flight.ts +0 -59
  158. package/src/compute/solve-pipeline.ts +0 -603
  159. package/src/compute/transform-input.ts +0 -66
  160. package/src/definitions/__tests__/definition-service.test.ts +0 -131
  161. package/src/definitions/__tests__/load-for-render.test.ts +0 -244
  162. package/src/definitions/__tests__/schema-extraction.test.ts +0 -137
  163. package/src/definitions/definition-service.ts +0 -247
  164. package/src/definitions/index.ts +0 -22
  165. package/src/definitions/load-for-render.ts +0 -272
  166. package/src/definitions/schema-extraction.ts +0 -117
  167. package/src/errors/SentryErrorReporter.ts +0 -88
  168. package/src/errors/index.ts +0 -3
  169. package/src/http/__tests__/body-size.test.ts +0 -27
  170. package/src/http/__tests__/route-classifier.test.ts +0 -60
  171. package/src/http/__tests__/safe-redirect.test.ts +0 -47
  172. package/src/http/__tests__/security-headers.test.ts +0 -29
  173. package/src/http/body-size.ts +0 -24
  174. package/src/http/index.ts +0 -10
  175. package/src/http/redirect.ts +0 -16
  176. package/src/http/route-classifier.ts +0 -74
  177. package/src/http/security-headers.ts +0 -42
  178. package/src/index.ts +0 -27
  179. package/src/ops/__tests__/semver.test.ts +0 -42
  180. package/src/ops/index.ts +0 -3
  181. package/src/ops/semver.ts +0 -60
  182. package/src/providers/__tests__/create-selva-providers.test.ts +0 -172
  183. package/src/providers/__tests__/fixtures/override-config.mjs +0 -8
  184. package/src/providers/create-selva-providers.ts +0 -241
  185. package/src/providers/index.ts +0 -12
  186. package/src/tokens/__tests__/token-codec.test.ts +0 -63
  187. package/src/tokens/index.ts +0 -8
  188. package/src/tokens/token-codec.ts +0 -86
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Selva FelixBrunold VektorNode
3
+ Copyright (c) 2025 Selva VektorNode AG
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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,CAyKxE"}
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: { maxEntries: 20, ttlMs: 5 * 60_000 },
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":"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"}
1
+ {"version":3,"file":"client-cache.js","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"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"}
@@ -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"}
@@ -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,EAIxB,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,wBAAwB,EACxB,sBAAsB,EAOtB,MAAM,qBAAqB,CAAC"}
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"}
@@ -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;AAIH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAQjF;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;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;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;;;;;;;;OAQG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,aAAa,CAwClE"}
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"}
@@ -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
- console.warn(`[selva] Invalid ${name}=${raw}, falling back to ${fallback}`);
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
- console.warn(`[selva] Invalid ${name}=${raw}, falling back to ${fallback}`);
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
- console.warn(`[selva] Invalid ${name}=${raw}, falling back to ${fallback}`);
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', 300 * MB);
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', 300 * MB),
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":"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"}
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;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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;CACrB;AAED,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,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACpD;AAOD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CAyC7F"}
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"}