@selvajs/server 0.2.0-beta.2 → 0.2.0-beta.4

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 (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +32 -0
  3. package/dist/compute/client-cache.d.ts +12 -0
  4. package/dist/compute/client-cache.d.ts.map +1 -1
  5. package/dist/compute/client-cache.js +18 -3
  6. package/dist/compute/client-cache.js.map +1 -1
  7. package/dist/compute/definition-byte-cache.d.ts +85 -0
  8. package/dist/compute/definition-byte-cache.d.ts.map +1 -0
  9. package/dist/compute/definition-byte-cache.js +110 -0
  10. package/dist/compute/definition-byte-cache.js.map +1 -0
  11. package/dist/compute/index.d.ts +7 -2
  12. package/dist/compute/index.d.ts.map +1 -1
  13. package/dist/compute/index.js +6 -1
  14. package/dist/compute/index.js.map +1 -1
  15. package/dist/compute/limits.d.ts +52 -0
  16. package/dist/compute/limits.d.ts.map +1 -1
  17. package/dist/compute/limits.js +33 -1
  18. package/dist/compute/limits.js.map +1 -1
  19. package/dist/compute/memory-solve-cache.d.ts +58 -0
  20. package/dist/compute/memory-solve-cache.d.ts.map +1 -0
  21. package/dist/compute/memory-solve-cache.js +0 -0
  22. package/dist/compute/memory-solve-cache.js.map +1 -0
  23. package/dist/compute/safe-url.d.ts.map +1 -1
  24. package/dist/compute/safe-url.js +16 -0
  25. package/dist/compute/safe-url.js.map +1 -1
  26. package/dist/compute/solve-cache-envelope.d.ts +53 -0
  27. package/dist/compute/solve-cache-envelope.d.ts.map +1 -0
  28. package/dist/compute/solve-cache-envelope.js +66 -0
  29. package/dist/compute/solve-cache-envelope.js.map +1 -0
  30. package/dist/compute/solve-cache-key.d.ts +65 -0
  31. package/dist/compute/solve-cache-key.d.ts.map +1 -0
  32. package/dist/compute/solve-cache-key.js +50 -0
  33. package/dist/compute/solve-cache-key.js.map +1 -0
  34. package/dist/compute/solve-cache-single-flight.d.ts +42 -0
  35. package/dist/compute/solve-cache-single-flight.d.ts.map +1 -0
  36. package/dist/compute/solve-cache-single-flight.js +50 -0
  37. package/dist/compute/solve-cache-single-flight.js.map +1 -0
  38. package/dist/compute/solve-pipeline.d.ts +71 -8
  39. package/dist/compute/solve-pipeline.d.ts.map +1 -1
  40. package/dist/compute/solve-pipeline.js +180 -8
  41. package/dist/compute/solve-pipeline.js.map +1 -1
  42. package/dist/definitions/definition-service.d.ts +1 -1
  43. package/dist/definitions/definition-service.d.ts.map +1 -1
  44. package/dist/definitions/definition-service.js +8 -4
  45. package/dist/definitions/definition-service.js.map +1 -1
  46. package/package.json +14 -18
  47. package/dist/access/__tests__/project-access-input.test.d.ts +0 -2
  48. package/dist/access/__tests__/project-access-input.test.d.ts.map +0 -1
  49. package/dist/access/__tests__/project-access-input.test.js +0 -146
  50. package/dist/access/__tests__/project-access-input.test.js.map +0 -1
  51. package/dist/compute/__tests__/client-cache.test.d.ts +0 -11
  52. package/dist/compute/__tests__/client-cache.test.d.ts.map +0 -1
  53. package/dist/compute/__tests__/client-cache.test.js +0 -200
  54. package/dist/compute/__tests__/client-cache.test.js.map +0 -1
  55. package/dist/compute/__tests__/rate-limit.test.d.ts +0 -2
  56. package/dist/compute/__tests__/rate-limit.test.d.ts.map +0 -1
  57. package/dist/compute/__tests__/rate-limit.test.js +0 -98
  58. package/dist/compute/__tests__/rate-limit.test.js.map +0 -1
  59. package/dist/compute/__tests__/remote-definition.test.d.ts +0 -11
  60. package/dist/compute/__tests__/remote-definition.test.d.ts.map +0 -1
  61. package/dist/compute/__tests__/remote-definition.test.js +0 -114
  62. package/dist/compute/__tests__/remote-definition.test.js.map +0 -1
  63. package/dist/compute/__tests__/safe-url.test.d.ts +0 -2
  64. package/dist/compute/__tests__/safe-url.test.d.ts.map +0 -1
  65. package/dist/compute/__tests__/safe-url.test.js +0 -71
  66. package/dist/compute/__tests__/safe-url.test.js.map +0 -1
  67. package/dist/compute/__tests__/solve-pipeline.test.d.ts +0 -11
  68. package/dist/compute/__tests__/solve-pipeline.test.d.ts.map +0 -1
  69. package/dist/compute/__tests__/solve-pipeline.test.js +0 -292
  70. package/dist/compute/__tests__/solve-pipeline.test.js.map +0 -1
  71. package/dist/compute/__tests__/transform-input.test.d.ts +0 -14
  72. package/dist/compute/__tests__/transform-input.test.d.ts.map +0 -1
  73. package/dist/compute/__tests__/transform-input.test.js +0 -120
  74. package/dist/compute/__tests__/transform-input.test.js.map +0 -1
  75. package/dist/definitions/__tests__/load-for-render.test.d.ts +0 -8
  76. package/dist/definitions/__tests__/load-for-render.test.d.ts.map +0 -1
  77. package/dist/definitions/__tests__/load-for-render.test.js +0 -196
  78. package/dist/definitions/__tests__/load-for-render.test.js.map +0 -1
  79. package/dist/definitions/__tests__/schema-extraction.test.d.ts +0 -11
  80. package/dist/definitions/__tests__/schema-extraction.test.d.ts.map +0 -1
  81. package/dist/definitions/__tests__/schema-extraction.test.js +0 -115
  82. package/dist/definitions/__tests__/schema-extraction.test.js.map +0 -1
  83. package/dist/http/__tests__/body-size.test.d.ts +0 -2
  84. package/dist/http/__tests__/body-size.test.d.ts.map +0 -1
  85. package/dist/http/__tests__/body-size.test.js +0 -24
  86. package/dist/http/__tests__/body-size.test.js.map +0 -1
  87. package/dist/http/__tests__/route-classifier.test.d.ts +0 -2
  88. package/dist/http/__tests__/route-classifier.test.d.ts.map +0 -1
  89. package/dist/http/__tests__/route-classifier.test.js +0 -54
  90. package/dist/http/__tests__/route-classifier.test.js.map +0 -1
  91. package/dist/http/__tests__/safe-redirect.test.d.ts +0 -2
  92. package/dist/http/__tests__/safe-redirect.test.d.ts.map +0 -1
  93. package/dist/http/__tests__/safe-redirect.test.js +0 -44
  94. package/dist/http/__tests__/safe-redirect.test.js.map +0 -1
  95. package/dist/http/__tests__/security-headers.test.d.ts +0 -2
  96. package/dist/http/__tests__/security-headers.test.d.ts.map +0 -1
  97. package/dist/http/__tests__/security-headers.test.js +0 -24
  98. package/dist/http/__tests__/security-headers.test.js.map +0 -1
  99. package/dist/ops/__tests__/semver.test.d.ts +0 -2
  100. package/dist/ops/__tests__/semver.test.d.ts.map +0 -1
  101. package/dist/ops/__tests__/semver.test.js +0 -38
  102. package/dist/ops/__tests__/semver.test.js.map +0 -1
  103. package/dist/providers/__tests__/create-selva-providers.test.d.ts +0 -7
  104. package/dist/providers/__tests__/create-selva-providers.test.d.ts.map +0 -1
  105. package/dist/providers/__tests__/create-selva-providers.test.js +0 -135
  106. package/dist/providers/__tests__/create-selva-providers.test.js.map +0 -1
  107. package/dist/tokens/__tests__/token-codec.test.d.ts +0 -2
  108. package/dist/tokens/__tests__/token-codec.test.d.ts.map +0 -1
  109. package/dist/tokens/__tests__/token-codec.test.js +0 -50
  110. package/dist/tokens/__tests__/token-codec.test.js.map +0 -1
  111. package/src/access/__tests__/project-access-input.test.ts +0 -175
  112. package/src/access/index.ts +0 -9
  113. package/src/access/project-access-input.ts +0 -145
  114. package/src/compute/__tests__/client-cache.test.ts +0 -224
  115. package/src/compute/__tests__/rate-limit.test.ts +0 -106
  116. package/src/compute/__tests__/remote-definition.test.ts +0 -140
  117. package/src/compute/__tests__/safe-url.test.ts +0 -72
  118. package/src/compute/__tests__/solve-pipeline.test.ts +0 -341
  119. package/src/compute/__tests__/transform-input.test.ts +0 -162
  120. package/src/compute/client-cache.ts +0 -300
  121. package/src/compute/index.ts +0 -48
  122. package/src/compute/limits.ts +0 -159
  123. package/src/compute/rate-limit.ts +0 -110
  124. package/src/compute/remote-definition.ts +0 -149
  125. package/src/compute/safe-url.ts +0 -186
  126. package/src/compute/solve-pipeline.ts +0 -339
  127. package/src/compute/transform-input.ts +0 -66
  128. package/src/definitions/__tests__/load-for-render.test.ts +0 -244
  129. package/src/definitions/__tests__/schema-extraction.test.ts +0 -137
  130. package/src/definitions/definition-service.ts +0 -243
  131. package/src/definitions/index.ts +0 -22
  132. package/src/definitions/load-for-render.ts +0 -272
  133. package/src/definitions/schema-extraction.ts +0 -117
  134. package/src/errors/SentryErrorReporter.ts +0 -88
  135. package/src/errors/index.ts +0 -3
  136. package/src/http/__tests__/body-size.test.ts +0 -27
  137. package/src/http/__tests__/route-classifier.test.ts +0 -60
  138. package/src/http/__tests__/safe-redirect.test.ts +0 -47
  139. package/src/http/__tests__/security-headers.test.ts +0 -29
  140. package/src/http/body-size.ts +0 -24
  141. package/src/http/index.ts +0 -10
  142. package/src/http/redirect.ts +0 -16
  143. package/src/http/route-classifier.ts +0 -74
  144. package/src/http/security-headers.ts +0 -42
  145. package/src/index.ts +0 -27
  146. package/src/ops/__tests__/semver.test.ts +0 -42
  147. package/src/ops/index.ts +0 -3
  148. package/src/ops/semver.ts +0 -60
  149. package/src/providers/__tests__/create-selva-providers.test.ts +0 -172
  150. package/src/providers/__tests__/fixtures/override-config.mjs +0 -8
  151. package/src/providers/create-selva-providers.ts +0 -241
  152. package/src/providers/index.ts +0 -12
  153. package/src/tokens/__tests__/token-codec.test.ts +0 -63
  154. package/src/tokens/index.ts +0 -8
  155. 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,32 @@
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, and render loader. Consumed by `@selvajs/selva`; not tied to SvelteKit or any
6
+ specific HTTP framework.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @selvajs/server
12
+ ```
13
+
14
+ ## Subpath exports
15
+
16
+ | Export | Contents |
17
+ | ----------------------------- | ------------------------------------------------------- |
18
+ | `@selvajs/server` | Root — package entry point |
19
+ | `@selvajs/server/compute` | Solve pipeline, SSRF guard, Rhino.Compute client wiring |
20
+ | `@selvajs/server/definitions` | Definition service + render loader |
21
+ | `@selvajs/server/providers` | Provider composition helpers |
22
+ | `@selvajs/server/tokens` | API token (PAT) codec |
23
+ | `@selvajs/server/errors` | Shared error types |
24
+ | `@selvajs/server/http` | Rate limiting, security headers, request limits |
25
+ | `@selvajs/server/access` | Access/permission checks |
26
+ | `@selvajs/server/ops` | Operational utilities |
27
+
28
+ Requires a `@selvajs/platform` provider (e.g. `@selvajs/local-provider` or
29
+ `@selvajs/supabase-provider`) for data/storage/auth.
30
+
31
+ See the root [CLAUDE.md](../../CLAUDE.md) for how this package fits into the wider Selva
32
+ architecture.
@@ -80,6 +80,18 @@ export interface ClientCacheConfig {
80
80
  * (`ComputeLimits.computeMaxConcurrentSolves`).
81
81
  */
82
82
  maxConcurrentSolves: number;
83
+ /**
84
+ * Backpressure — max solves that may WAIT in the FIFO queue (excludes the
85
+ * in-flight `maxConcurrentSolves`). `0` = unbounded (`ComputeLimits.
86
+ * computeMaxQueueDepth`); a full queue sheds new solves with `QUEUE_FULL`.
87
+ */
88
+ maxQueueDepth: number;
89
+ /**
90
+ * Backpressure — max ms a solve may sit queued before executing; `0` = no
91
+ * deadline (`ComputeLimits.computeQueueWaitMs`). A too-long wait sheds with
92
+ * `QUEUE_TIMEOUT`.
93
+ */
94
+ queueWaitMs: number;
83
95
  /** Concise cache/timing logs. When false, `onDebugLog` is never invoked. */
84
96
  debug: boolean;
85
97
  /** 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,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,CAgKxE"}
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,CAiLxE"}
@@ -72,6 +72,11 @@ export function createClientCache(config) {
72
72
  // solve on this server through a single slot while the compute VM's
73
73
  // other compute.geometry children idle (audit B6) — always pass it.
74
74
  maxConcurrent: config.maxConcurrentSolves,
75
+ // Backpressure (audit B7). The scheduler treats undefined as
76
+ // unbounded/no-deadline, so map our `0`-means-off convention to undefined
77
+ // rather than passing 0 (which would shed EVERY queued solve).
78
+ maxQueueDepth: config.maxQueueDepth || undefined,
79
+ queueWaitMs: config.queueWaitMs || undefined,
75
80
  timeoutMs: config.maxSolveDurationMs,
76
81
  cache: { maxEntries: 20, ttlMs: 5 * 60_000 },
77
82
  // Solve large definitions by server cache-key (pointer) instead of
@@ -103,13 +108,17 @@ export function createClientCache(config) {
103
108
  ? '[Compute/def-cache] HIT — definition reused from server cache (no upload)'
104
109
  : '[Compute/def-cache] n/a — reuse disabled or non-reusable definition');
105
110
  }
106
- // Surface real GH errors when present. An errored solve is NEVER cached
107
- // server-side (cachesolve only stores error-free solves).
111
+ // Surface real GH errors when present. Whether an errored solve is cached is
112
+ // config-gated by `cacheerroredsolves` (default false → errored solves are
113
+ // re-run each time; true → the error result is cached like any other, per R2).
108
114
  if (!result.fromCache) {
109
115
  const errs = result.response?.errors;
110
116
  const errCount = Array.isArray(errs) ? errs.length : 0;
111
117
  if (errCount > 0) {
112
- debugLog(`[Compute/rhino-cache] ${errCount} GH error(s); not cacheable until fixed. First: ${JSON.stringify(errs[0])}`);
118
+ const cacheNote = config.cacheerroredsolves
119
+ ? 'cached (cacheerroredsolves on)'
120
+ : 'not cached (cacheerroredsolves off) — re-run until fixed';
121
+ debugLog(`[Compute/rhino-cache] ${errCount} GH error(s); ${cacheNote}. First: ${JSON.stringify(errs[0])}`);
113
122
  }
114
123
  }
115
124
  }
@@ -126,11 +135,15 @@ export function createClientCache(config) {
126
135
  cache.set(key, existing);
127
136
  return existing;
128
137
  }
138
+ // A build is a Rhino.Compute handshake — expensive, and repeated builds for
139
+ // the same id mean churn (LRU thrash or config-rotation evictions).
140
+ debugLog(`[Compute/client-cache] miss — building warm client for server ${key}`);
129
141
  const entry = await build(server, opts?.definitionGuid);
130
142
  // Evict the least-recently-used entry before inserting when at capacity.
131
143
  if (cache.size >= maxCachedClients) {
132
144
  const oldestKey = cache.keys().next().value;
133
145
  if (oldestKey !== undefined) {
146
+ debugLog(`[Compute/client-cache] LRU evicted warm client for server ${oldestKey} (cap ${maxCachedClients})`);
134
147
  cache.get(oldestKey)?.scheduler.dispose();
135
148
  cache.delete(oldestKey);
136
149
  }
@@ -142,11 +155,13 @@ export function createClientCache(config) {
142
155
  const key = id;
143
156
  const entry = cache.get(key);
144
157
  if (entry) {
158
+ debugLog(`[Compute/client-cache] evicted warm client for server ${key} (config change)`);
145
159
  entry.scheduler.dispose();
146
160
  cache.delete(key);
147
161
  }
148
162
  },
149
163
  disposeAll() {
164
+ debugLog(`[Compute/client-cache] disposing all ${cache.size} warm client(s)`);
150
165
  for (const entry of cache.values())
151
166
  entry.scheduler.dispose();
152
167
  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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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * In-process cache of `.gh` definition bytes, keyed by immutable **version id**.
3
+ *
4
+ * The solve path used to eagerly `storage.get(fileKey)` on every request — moving
5
+ * multi-MB definition bytes off disk/S3 into memory before the scheduler could
6
+ * decide it didn't need them (the compute server already holds the definition via
7
+ * the pointer/`reuseServerDefinitionCache` fast path). This cache turns that eager
8
+ * read into a lazy `DefinitionRef.load()`: the scheduler calls `load()` ONLY when
9
+ * an upload is unavoidable, and even then a warm entry serves the bytes without
10
+ * touching storage. A pointer-known re-solve therefore moves ZERO definition bytes.
11
+ *
12
+ * Keying discipline (the whole reason this module exists):
13
+ * - The key is the **version id**, an immutable UUID — never a `fileKey`, which
14
+ * a delete-latest-then-reupload can REUSE for different content (see the
15
+ * monotonic-version-number fix). A reused key would serve one version's bytes
16
+ * for another: silent cache poisoning with no diagnostic. Callers pass
17
+ * `version.id`, and the returned `DefinitionRef.key` is that same id, so the
18
+ * scheduler's own result/pointer cache keys on the same immutable identity.
19
+ *
20
+ * Eviction:
21
+ * - LRU by **total byte budget**, not entry count — definitions range from tens
22
+ * of KB to hundreds of MB, so an entry-count cap would either pin gigabytes or
23
+ * evict uselessly. `COMPUTE_DEFINITION_BYTE_CACHE_MB` sets the budget; `0`
24
+ * disables the cache (every `getOrLoad` calls `load` and caches nothing).
25
+ * - No TTL: version ids are immutable, so a cached entry can never go stale.
26
+ * - An entry larger than the whole budget is served but never retained (caching
27
+ * it would immediately evict everything including itself).
28
+ */
29
+ /**
30
+ * Per-`getOrLoad` outcome, observable AFTER the scheduler has (or hasn't) called
31
+ * `load()`. Lets the solve pipeline emit a `def_bytes` Server-Timing verdict:
32
+ * - `load` never invoked → the scheduler served a pointer-known solve without
33
+ * bytes (`skipped`);
34
+ * - `load` invoked, `fromCache` true → warm byte-cache entry (`hit`);
35
+ * - `load` invoked, `fromCache` false → fell through to the loader (`miss`).
36
+ */
37
+ export interface ByteRefOutcome {
38
+ /** True once `load()` has been called at least once on this ref. */
39
+ loaded: boolean;
40
+ /** When `loaded`, whether the bytes came from a warm cache entry. */
41
+ fromCache: boolean;
42
+ }
43
+ /** A definition reference shaped for `@selvajs/compute`'s `DefinitionRef`. */
44
+ export interface ByteCacheRef {
45
+ /** Immutable identity (a version UUID). Keys the scheduler's caches too. */
46
+ key: string;
47
+ /** Materialize the bytes — served from cache when warm, else via the loader. */
48
+ load: () => Promise<Uint8Array>;
49
+ /** Mutable outcome of this ref's `load()` (see {@link ByteRefOutcome}). */
50
+ outcome: ByteRefOutcome;
51
+ }
52
+ /** Hit/miss/eviction counters for observability (Server-Timing, admin debug). */
53
+ export interface ByteCacheStats {
54
+ /** `load()` served from a warm entry (no loader call). */
55
+ hits: number;
56
+ /** `load()` fell through to the loader (cold key, or caching disabled). */
57
+ misses: number;
58
+ /** Entries dropped by the byte-budget LRU. */
59
+ evictions: number;
60
+ /** Current retained entry count. */
61
+ entries: number;
62
+ /** Current retained bytes. */
63
+ bytes: number;
64
+ }
65
+ export interface DefinitionByteCache {
66
+ /**
67
+ * Return a `DefinitionRef` whose `load()` serves `versionId`'s bytes from the
68
+ * cache when warm, otherwise calls `load` (and caches the result). The
69
+ * `DefinitionRef` is cheap to build and moves no bytes until `load()` runs —
70
+ * so handing it to the scheduler for a pointer-known solve costs nothing.
71
+ */
72
+ getOrLoad(versionId: string, load: () => Promise<Uint8Array>): ByteCacheRef;
73
+ /** Snapshot of the counters (see {@link ByteCacheStats}). */
74
+ stats(): ByteCacheStats;
75
+ /** Drop everything (test seam / eviction on definition delete if ever needed). */
76
+ clear(): void;
77
+ }
78
+ /**
79
+ * Build a definition-byte cache with a total-byte LRU budget.
80
+ *
81
+ * @param maxBytes total retained-byte budget; `0` (or negative) disables caching
82
+ * entirely — every `load()` calls the loader and nothing is retained.
83
+ */
84
+ export declare function createDefinitionByteCache(maxBytes: number): DefinitionByteCache;
85
+ //# sourceMappingURL=definition-byte-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition-byte-cache.d.ts","sourceRoot":"","sources":["../../src/compute/definition-byte-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC9B,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC5B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,2EAA2E;IAC3E,OAAO,EAAE,cAAc,CAAC;CACxB;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC9B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC;IAC5E,6DAA6D;IAC7D,KAAK,IAAI,cAAc,CAAC;IACxB,kFAAkF;IAClF,KAAK,IAAI,IAAI,CAAC;CACd;AAMD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CA2E/E"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * In-process cache of `.gh` definition bytes, keyed by immutable **version id**.
3
+ *
4
+ * The solve path used to eagerly `storage.get(fileKey)` on every request — moving
5
+ * multi-MB definition bytes off disk/S3 into memory before the scheduler could
6
+ * decide it didn't need them (the compute server already holds the definition via
7
+ * the pointer/`reuseServerDefinitionCache` fast path). This cache turns that eager
8
+ * read into a lazy `DefinitionRef.load()`: the scheduler calls `load()` ONLY when
9
+ * an upload is unavoidable, and even then a warm entry serves the bytes without
10
+ * touching storage. A pointer-known re-solve therefore moves ZERO definition bytes.
11
+ *
12
+ * Keying discipline (the whole reason this module exists):
13
+ * - The key is the **version id**, an immutable UUID — never a `fileKey`, which
14
+ * a delete-latest-then-reupload can REUSE for different content (see the
15
+ * monotonic-version-number fix). A reused key would serve one version's bytes
16
+ * for another: silent cache poisoning with no diagnostic. Callers pass
17
+ * `version.id`, and the returned `DefinitionRef.key` is that same id, so the
18
+ * scheduler's own result/pointer cache keys on the same immutable identity.
19
+ *
20
+ * Eviction:
21
+ * - LRU by **total byte budget**, not entry count — definitions range from tens
22
+ * of KB to hundreds of MB, so an entry-count cap would either pin gigabytes or
23
+ * evict uselessly. `COMPUTE_DEFINITION_BYTE_CACHE_MB` sets the budget; `0`
24
+ * disables the cache (every `getOrLoad` calls `load` and caches nothing).
25
+ * - No TTL: version ids are immutable, so a cached entry can never go stale.
26
+ * - An entry larger than the whole budget is served but never retained (caching
27
+ * it would immediately evict everything including itself).
28
+ */
29
+ /**
30
+ * Build a definition-byte cache with a total-byte LRU budget.
31
+ *
32
+ * @param maxBytes total retained-byte budget; `0` (or negative) disables caching
33
+ * entirely — every `load()` calls the loader and nothing is retained.
34
+ */
35
+ export function createDefinitionByteCache(maxBytes) {
36
+ const budget = Math.max(0, Math.floor(maxBytes));
37
+ // Map preserves insertion order → iteration order IS LRU order; re-inserting
38
+ // on access moves an entry to the most-recently-used (last) position.
39
+ const cache = new Map();
40
+ let retainedBytes = 0;
41
+ const stats = { hits: 0, misses: 0, evictions: 0 };
42
+ function touch(key, entry) {
43
+ cache.delete(key);
44
+ cache.set(key, entry);
45
+ }
46
+ function evictToFit() {
47
+ // Evict least-recently-used (first in insertion order) until within budget.
48
+ while (retainedBytes > budget && cache.size > 0) {
49
+ const oldestKey = cache.keys().next().value;
50
+ const oldest = cache.get(oldestKey);
51
+ cache.delete(oldestKey);
52
+ retainedBytes -= oldest.bytes.byteLength;
53
+ stats.evictions += 1;
54
+ }
55
+ }
56
+ function store(key, bytes) {
57
+ if (budget === 0)
58
+ return;
59
+ // An entry bigger than the whole budget is never retained — caching it would
60
+ // evict everything (including itself) on the next `evictToFit`. Serve it
61
+ // through, cache nothing.
62
+ if (bytes.byteLength > budget)
63
+ return;
64
+ const existing = cache.get(key);
65
+ if (existing)
66
+ retainedBytes -= existing.bytes.byteLength;
67
+ cache.set(key, { bytes });
68
+ retainedBytes += bytes.byteLength;
69
+ evictToFit();
70
+ }
71
+ return {
72
+ getOrLoad(versionId, load) {
73
+ const outcome = { loaded: false, fromCache: false };
74
+ return {
75
+ key: versionId,
76
+ outcome,
77
+ load: async () => {
78
+ const hit = cache.get(versionId);
79
+ if (hit) {
80
+ touch(versionId, hit);
81
+ stats.hits += 1;
82
+ outcome.loaded = true;
83
+ outcome.fromCache = true;
84
+ return hit.bytes;
85
+ }
86
+ stats.misses += 1;
87
+ outcome.loaded = true;
88
+ outcome.fromCache = false;
89
+ const bytes = await load();
90
+ store(versionId, bytes);
91
+ return bytes;
92
+ }
93
+ };
94
+ },
95
+ stats() {
96
+ return {
97
+ hits: stats.hits,
98
+ misses: stats.misses,
99
+ evictions: stats.evictions,
100
+ entries: cache.size,
101
+ bytes: retainedBytes
102
+ };
103
+ },
104
+ clear() {
105
+ cache.clear();
106
+ retainedBytes = 0;
107
+ }
108
+ };
109
+ }
110
+ //# sourceMappingURL=definition-byte-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition-byte-cache.js","sourceRoot":"","sources":["../../src/compute/definition-byte-cache.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,13 @@
1
- export { resolveComputeLimits, readPositiveInt, readBool, type ComputeLimits, type EnvRecord } from './limits.js';
1
+ export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool, type ComputeLimits, type EnvRecord } from './limits.js';
2
+ export { createDefinitionByteCache, type DefinitionByteCache, type ByteCacheRef, type ByteCacheStats } from './definition-byte-cache.js';
3
+ export { createMemorySolveResultCache, type MemorySolveResultCache, type SolveCacheStats } from './memory-solve-cache.js';
4
+ export { deriveSolveCacheInputKey, type SolveCacheConfigSubset, type SolveCacheInputKey } from './solve-cache-key.js';
5
+ export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody, type EnvelopeHeader, type DecodedSolveCacheEntry } from './solve-cache-envelope.js';
6
+ export { createSolveCacheSingleFlight, type SolveCacheSingleFlight } from './solve-cache-single-flight.js';
2
7
  export { createComputeRateLimiter, type ComputeRateLimiter, type ComputeRateLimiterConfig, type RateLimitResult } from './rate-limit.js';
3
8
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
4
9
  export { transformInputParameter } from './transform-input.js';
5
10
  export { createClientCache, serverIdentity, type ClientCache, type ClientCacheConfig, type CachedClient, type ResolvedServer, type ServerIdentity } from './client-cache.js';
6
11
  export { createRemoteDefinitionFetcher, readBodyWithCap, type RemoteDefinitionFetcher, type RemoteDefinitionConfig } from './remote-definition.js';
7
- export { runSolvePipeline, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type SolvePipelineArgs, type SolveOutcome, type SolveEnvelope, type SolvePhaseMetrics, type PipelineInput } from './solve-pipeline.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';
8
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,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,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,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,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,5 +1,10 @@
1
1
  // Compute/solve server building blocks — transport-agnostic, env-injected.
2
- export { resolveComputeLimits, readPositiveInt, readBool } from './limits.js';
2
+ export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool } from './limits.js';
3
+ export { createDefinitionByteCache } from './definition-byte-cache.js';
4
+ export { createMemorySolveResultCache } from './memory-solve-cache.js';
5
+ export { deriveSolveCacheInputKey } from './solve-cache-key.js';
6
+ export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody } from './solve-cache-envelope.js';
7
+ export { createSolveCacheSingleFlight } from './solve-cache-single-flight.js';
3
8
  export { createComputeRateLimiter } from './rate-limit.js';
4
9
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
5
10
  export { transformInputParameter } from './transform-input.js';
@@ -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,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,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,EAMtB,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,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"}
@@ -24,6 +24,13 @@ export type EnvRecord = Record<string, string | undefined>;
24
24
  * + falls back when present but not a finite positive number.
25
25
  */
26
26
  export declare function readPositiveInt(env: EnvRecord, name: string, fallback: number): number;
27
+ /**
28
+ * Parse a non-negative integer env value — like {@link readPositiveInt} but `0`
29
+ * is a valid value (used by knobs where `0` means "disable", e.g. a cache
30
+ * budget). Returns `fallback` when absent, and warns + falls back when present
31
+ * but not a finite non-negative number.
32
+ */
33
+ export declare function readNonNegativeInt(env: EnvRecord, name: string, fallback: number): number;
27
34
  /**
28
35
  * Parse a boolean env flag. Accepts `true/1/yes/on` (case-insensitive) as true
29
36
  * and `false/0/no/off` as false; any other / absent value falls back.
@@ -102,6 +109,51 @@ export interface ComputeLimits {
102
109
  * app instances each apply their own cap.
103
110
  */
104
111
  computeMaxConcurrentSolves: number;
112
+ /**
113
+ * Backpressure — max solves allowed to WAIT in the per-server FIFO queue (i.e.
114
+ * excluding the `computeMaxConcurrentSolves` already in flight). A solve that
115
+ * arrives to a full queue is shed immediately (scheduler `QUEUE_FULL`, mapped
116
+ * to HTTP 503 + `Retry-After`) instead of piling up unbounded. `0` = unbounded
117
+ * (the pre-shedding default): the queue grows without limit, matching prior
118
+ * behavior. Size it to roughly 2–3× `computeMaxConcurrentSolves` once the
119
+ * Rhino pool's real capacity is known — shedding fast beats a hung request.
120
+ */
121
+ computeMaxQueueDepth: number;
122
+ /**
123
+ * Backpressure — longest (ms) a solve may sit QUEUED before it starts
124
+ * executing; still-waiting past this it's shed (scheduler `QUEUE_TIMEOUT` →
125
+ * HTTP 503 + `Retry-After`) rather than burning compute on a stale request.
126
+ * Bounds tail latency: the scheduler's `timeoutMs` clock starts at execution,
127
+ * so without this a solve's total wait is unbounded and invisible. `0` = no
128
+ * queue deadline (the default). A sensible tuned value is ≈ `maxSolveDurationMs`.
129
+ */
130
+ computeQueueWaitMs: number;
131
+ /**
132
+ * Total-byte budget (bytes) for the in-process definition-byte cache keyed on
133
+ * immutable version id. A warm entry lets a solve skip `storage.get` entirely,
134
+ * and a pointer-known solve never loads bytes at all. `0` disables the cache
135
+ * (every solve re-reads storage). Sized as a total-byte LRU because definitions
136
+ * span KB→hundreds of MB; env value is MB, converted here.
137
+ */
138
+ computeDefinitionByteCacheBytes: number;
139
+ /**
140
+ * Default per-definition entry quota for the durable L2 solve cache (H1), used
141
+ * when a definition's `solveCacheLimit` is absent (inherit). A definition may
142
+ * override this; `0` on the definition turns caching off for it. This global
143
+ * default `0` means the L2 is effectively off until an operator sets a quota —
144
+ * safe because the memory backend also ships only under
145
+ * `SOLVE_CACHE_PROVIDER=memory`. Counts, not bytes: authors think in "how many
146
+ * solves to remember"; the byte backstop below is the operator's memory guard.
147
+ */
148
+ solveCacheDefaultMaxEntries: number;
149
+ /**
150
+ * Global byte backstop (bytes) across ALL definitions' L2 entries, evicted
151
+ * global-LRU regardless of per-definition counts. Entries range KB→100s of MB,
152
+ * so a count quota alone can't bound memory. `0` disables the backstop (rely on
153
+ * per-definition counts only — not recommended in the memory backend). Env value
154
+ * is MB, converted here. On Redis this moves to `maxmemory allkeys-lru`.
155
+ */
156
+ solveCacheMaxTotalBytes: number;
105
157
  }
106
158
  /**
107
159
  * Resolve every server-side compute limit from an env map. Pure: the same env
@@ -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;;;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;CACnC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,aAAa,CAwBlE"}
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"}
@@ -33,6 +33,23 @@ export function readPositiveInt(env, name, fallback) {
33
33
  }
34
34
  return Math.floor(parsed);
35
35
  }
36
+ /**
37
+ * Parse a non-negative integer env value — like {@link readPositiveInt} but `0`
38
+ * is a valid value (used by knobs where `0` means "disable", e.g. a cache
39
+ * budget). Returns `fallback` when absent, and warns + falls back when present
40
+ * but not a finite non-negative number.
41
+ */
42
+ export function readNonNegativeInt(env, name, fallback) {
43
+ const raw = env[name];
44
+ if (!raw)
45
+ return fallback;
46
+ const parsed = Number(raw);
47
+ if (!Number.isFinite(parsed) || parsed < 0) {
48
+ console.warn(`[selva] Invalid ${name}=${raw}, falling back to ${fallback}`);
49
+ return fallback;
50
+ }
51
+ return Math.floor(parsed);
52
+ }
36
53
  /**
37
54
  * Parse a boolean env flag. Accepts `true/1/yes/on` (case-insensitive) as true
38
55
  * and `false/0/no/off` as false; any other / absent value falls back.
@@ -72,7 +89,22 @@ export function resolveComputeLimits(env) {
72
89
  computeServerCachesolve: readBool(env, 'COMPUTE_SERVER_CACHESOLVE', true),
73
90
  computeCacheErroredSolves: readBool(env, 'COMPUTE_CACHE_ERRORED_SOLVES', false),
74
91
  // 4 matches rhino.compute's default --childcount; tune to the actual VM.
75
- computeMaxConcurrentSolves: readPositiveInt(env, 'COMPUTE_MAX_CONCURRENT', 4)
92
+ computeMaxConcurrentSolves: readPositiveInt(env, 'COMPUTE_MAX_CONCURRENT', 4),
93
+ // Both 0 (unbounded / no deadline) by default — nothing sheds until an
94
+ // operator who's measured their pool opts in. readNonNegativeInt so `0` is a
95
+ // 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),
98
+ // Env is MB; 0 disables. Default 256 MB holds a handful of typical
99
+ // definitions warm without pinning gigabytes. readNonNegativeInt so `0`
100
+ // (disable) is honored rather than treated as invalid.
101
+ computeDefinitionByteCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_BYTE_CACHE_MB', 256) * MB,
102
+ // L2 durable solve cache (H1). Default per-definition quota 0 = inherit-to-off
103
+ // until an operator opts in; the memory backend also only mounts under
104
+ // SOLVE_CACHE_PROVIDER=memory, so nothing caches by accident. Byte backstop
105
+ // 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
76
108
  };
77
109
  }
78
110
  //# 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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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * In-memory `ISolveResultCache` — the first durable L2 backend (H1).
3
+ *
4
+ * Two eviction dimensions, both required (see CACHING.md H1):
5
+ *
6
+ * 1. **Per-definition count quota, LRU-within-definition.** Each definition
7
+ * gets its own budget of remembered solves (`maxEntriesForDefinition`,
8
+ * passed at write time). A slider-heavy definition churns ONLY its own
9
+ * entries — it can never evict another definition's. This is what makes a
10
+ * wide-input-space definition harmless structurally instead of via an
11
+ * off-switch.
12
+ * 2. **Global byte backstop, global-LRU.** Entries range KB→100s of MB, so a
13
+ * count quota can't bound heap. `maxTotalBytes` evicts the globally
14
+ * least-recently-used entry across every definition until total retained
15
+ * bytes fit. Authors think in counts; operators think in bytes; both hold.
16
+ *
17
+ * The key is `(orgId, definitionId, versionId, inputKey)`. `definitionId` scopes
18
+ * the count quota; the full tuple addresses the entry. Version keying means a
19
+ * publish is a fresh keyspace and there is nothing to invalidate — entries just
20
+ * age out under the two budgets.
21
+ *
22
+ * Best-effort contract: this never throws. A `get` miss and a `set` drop are both
23
+ * normal. Correctness never depends on a hit.
24
+ */
25
+ import type { ISolveResultCache } from '@selvajs/platform';
26
+ /** Observability counters for the admin/debug surface (parallels ByteCacheStats). */
27
+ export interface SolveCacheStats {
28
+ /** `get` served a live entry. */
29
+ hits: number;
30
+ /** `get` found nothing. */
31
+ misses: number;
32
+ /** `set` stored a new (or replaced) entry. */
33
+ writes: number;
34
+ /** Entries dropped by the per-definition count quota. */
35
+ quotaEvictions: number;
36
+ /** Entries dropped by the global byte backstop. */
37
+ byteEvictions: number;
38
+ /** Current retained entry count across all definitions. */
39
+ entries: number;
40
+ /** Current retained bytes across all definitions. */
41
+ bytes: number;
42
+ }
43
+ /** A memory backend with its own stats, for wiring + observability. */
44
+ export interface MemorySolveResultCache extends ISolveResultCache {
45
+ stats(): SolveCacheStats;
46
+ /** Drop everything (test seam). */
47
+ clear(): void;
48
+ }
49
+ /**
50
+ * Build an in-memory L2 solve cache.
51
+ *
52
+ * @param maxTotalBytes global retained-byte backstop; `0` (or negative) disables
53
+ * the backstop (per-definition counts are then the only bound — not recommended
54
+ * in-process). An entry larger than the whole backstop is stored-then-immediately
55
+ * evicted (served through, never retained) — same rule as the byte cache.
56
+ */
57
+ export declare function createMemorySolveResultCache(maxTotalBytes: number): MemorySolveResultCache;
58
+ //# sourceMappingURL=memory-solve-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-solve-cache.d.ts","sourceRoot":"","sources":["../../src/compute/memory-solve-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACX,iBAAiB,EAIjB,MAAM,mBAAmB,CAAC;AAE3B,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC/B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAChE,KAAK,IAAI,eAAe,CAAC;IACzB,mCAAmC;IACnC,KAAK,IAAI,IAAI,CAAC;CACd;AAOD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,sBAAsB,CAoI1F"}