@voltro/runtime 0.20.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ Generated from the resolved runtime dependency closure (49 packages).
9
9
 
10
10
  ---
11
11
 
12
- ## @effect/cluster@0.59.0
12
+ ## @effect/cluster@0.60.0
13
13
 
14
14
  License: MIT
15
15
 
@@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
37
  SOFTWARE.
38
38
  ```
39
39
 
40
- ## @effect/opentelemetry@0.63.0
40
+ ## @effect/opentelemetry@0.64.0
41
41
 
42
42
  License: MIT
43
43
 
@@ -65,7 +65,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
65
65
  SOFTWARE.
66
66
  ```
67
67
 
68
- ## @effect/platform-node@0.107.0
68
+ ## @effect/platform-node@0.108.0
69
69
 
70
70
  License: MIT
71
71
 
@@ -93,7 +93,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
93
93
  SOFTWARE.
94
94
  ```
95
95
 
96
- ## @effect/platform-node-shared@0.60.0
96
+ ## @effect/platform-node-shared@0.61.0
97
97
 
98
98
  License: MIT
99
99
 
@@ -121,7 +121,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
121
121
  SOFTWARE.
122
122
  ```
123
123
 
124
- ## @effect/sql@0.51.1
124
+ ## @effect/sql@0.52.0
125
125
 
126
126
  License: MIT
127
127
 
@@ -149,7 +149,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
149
149
  SOFTWARE.
150
150
  ```
151
151
 
152
- ## @effect/workflow@0.18.2
152
+ ## @effect/workflow@0.19.0
153
153
 
154
154
  License: MIT
155
155
 
@@ -414,7 +414,7 @@ Apache License
414
414
  limitations under the License.
415
415
  ```
416
416
 
417
- ## @opentelemetry/api-logs@0.220.0
417
+ ## @opentelemetry/api-logs@0.221.0
418
418
 
419
419
  License: Apache-2.0
420
420
 
@@ -622,7 +622,7 @@ Apache License
622
622
  limitations under the License.
623
623
  ```
624
624
 
625
- ## @opentelemetry/context-async-hooks@2.9.0
625
+ ## @opentelemetry/context-async-hooks@2.10.0
626
626
 
627
627
  License: Apache-2.0
628
628
 
@@ -1038,7 +1038,7 @@ Apache License
1038
1038
  limitations under the License.
1039
1039
  ```
1040
1040
 
1041
- ## @opentelemetry/exporter-metrics-otlp-http@0.220.0
1041
+ ## @opentelemetry/exporter-metrics-otlp-http@0.221.0
1042
1042
 
1043
1043
  License: Apache-2.0
1044
1044
 
@@ -1246,7 +1246,7 @@ Apache License
1246
1246
  limitations under the License.
1247
1247
  ```
1248
1248
 
1249
- ## @opentelemetry/exporter-trace-otlp-http@0.220.0
1249
+ ## @opentelemetry/exporter-trace-otlp-http@0.221.0
1250
1250
 
1251
1251
  License: Apache-2.0
1252
1252
 
@@ -1454,7 +1454,7 @@ Apache License
1454
1454
  limitations under the License.
1455
1455
  ```
1456
1456
 
1457
- ## @opentelemetry/otlp-exporter-base@0.220.0
1457
+ ## @opentelemetry/otlp-exporter-base@0.221.0
1458
1458
 
1459
1459
  License: Apache-2.0
1460
1460
 
@@ -1662,7 +1662,7 @@ Apache License
1662
1662
  limitations under the License.
1663
1663
  ```
1664
1664
 
1665
- ## @opentelemetry/otlp-transformer@0.220.0
1665
+ ## @opentelemetry/otlp-transformer@0.221.0
1666
1666
 
1667
1667
  License: Apache-2.0
1668
1668
 
@@ -1870,7 +1870,7 @@ Apache License
1870
1870
  limitations under the License.
1871
1871
  ```
1872
1872
 
1873
- ## @opentelemetry/resources@2.9.0
1873
+ ## @opentelemetry/resources@2.10.0
1874
1874
 
1875
1875
  License: Apache-2.0
1876
1876
 
@@ -2078,7 +2078,7 @@ Apache License
2078
2078
  limitations under the License.
2079
2079
  ```
2080
2080
 
2081
- ## @opentelemetry/sdk-logs@0.220.0
2081
+ ## @opentelemetry/sdk-logs@0.221.0
2082
2082
 
2083
2083
  License: Apache-2.0
2084
2084
 
@@ -2286,7 +2286,7 @@ Apache License
2286
2286
  limitations under the License.
2287
2287
  ```
2288
2288
 
2289
- ## @opentelemetry/sdk-metrics@2.9.0
2289
+ ## @opentelemetry/sdk-metrics@2.10.0
2290
2290
 
2291
2291
  License: Apache-2.0
2292
2292
 
@@ -2494,7 +2494,7 @@ Apache License
2494
2494
  limitations under the License.
2495
2495
  ```
2496
2496
 
2497
- ## @opentelemetry/sdk-trace@2.9.0
2497
+ ## @opentelemetry/sdk-trace@2.10.0
2498
2498
 
2499
2499
  License: Apache-2.0
2500
2500
 
@@ -2702,7 +2702,7 @@ Apache License
2702
2702
  limitations under the License.
2703
2703
  ```
2704
2704
 
2705
- ## @opentelemetry/sdk-trace-base@2.9.0
2705
+ ## @opentelemetry/sdk-trace-base@2.10.0
2706
2706
 
2707
2707
  License: Apache-2.0
2708
2708
 
@@ -2910,7 +2910,7 @@ Apache License
2910
2910
  limitations under the License.
2911
2911
  ```
2912
2912
 
2913
- ## @opentelemetry/sdk-trace-node@2.9.0
2913
+ ## @opentelemetry/sdk-trace-node@2.10.0
2914
2914
 
2915
2915
  License: Apache-2.0
2916
2916
 
@@ -3956,7 +3956,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3956
3956
  THE SOFTWARE.
3957
3957
  ```
3958
3958
 
3959
- ## jose@6.2.3
3959
+ ## jose@6.2.4
3960
3960
 
3961
3961
  License: MIT
3962
3962
 
@@ -4495,7 +4495,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4495
4495
  SOFTWARE.
4496
4496
  ```
4497
4497
 
4498
- ## undici@7.28.0
4498
+ ## undici@8.9.0
4499
4499
 
4500
4500
  License: MIT
4501
4501
 
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ import { DialectId } from '@voltro/database';
14
14
  import { DialectReplicationAdapter } from '@voltro/database';
15
15
  import { Duration } from 'effect';
16
16
  import { Effect } from 'effect';
17
+ import { Fiber } from 'effect';
17
18
  import { FieldCipher } from '@voltro/database';
18
19
  import * as http from 'node:http';
19
20
  import { HttpClient } from '@effect/platform';
@@ -1165,7 +1166,7 @@ resolveIndexHint?: (descriptor: QueryDescriptor) => MatcherIndexHint | undefined
1165
1166
  cache?: {
1166
1167
  readonly ttlMs: number;
1167
1168
  readonly swrMs: number | undefined;
1168
- readonly scope: "subject" | "global";
1169
+ readonly scope: "subject" | "tenant" | "global";
1169
1170
  readonly baseKey: string;
1170
1171
  },
1171
1172
  /**
@@ -1253,6 +1254,39 @@ export declare const buildTracingLayer: (options?: TracingOptions) => Promise<Tr
1253
1254
  * hit on every read. `now` injectable for tests. */
1254
1255
  export declare const cachedBackend: (backend: SecretsBackend, ttlMs?: number, now?: () => number) => SecretsBackend;
1255
1256
 
1257
+ /** One leak: a query caching ONE entry across every caller while reading a
1258
+ * table whose rows are scoped per tenant. */
1259
+ export declare interface CacheScopeLeak {
1260
+ readonly query: string;
1261
+ readonly table: string;
1262
+ }
1263
+
1264
+ /**
1265
+ * Queries that declare `cache: { scope: 'global' }` while reading a
1266
+ * `tenant()`-scoped table.
1267
+ *
1268
+ * `'global'` shares ONE cache entry across every caller. On a table whose rows
1269
+ * are filtered by the caller's tenant, that is not a cache — it is one org
1270
+ * serving another org's rows out of memory, with a TTL. The author has to be
1271
+ * wrong exactly once, in a field whose two legal values differ by one word.
1272
+ *
1273
+ * A consumer named this precisely while asking for the third option: *"'global'
1274
+ * would share across tenant boundaries. For data derived from
1275
+ * `subject.tenantId` that is not a cache, it is a leak."* They chose to
1276
+ * recompute an org-wide figure once per employee rather than write it — the
1277
+ * right call, and not one the framework should have left to judgement when it
1278
+ * already knows which tables carry `tenant()`.
1279
+ *
1280
+ * `'tenant'` is the option that makes this avoidable; this is the check that
1281
+ * makes it enforced. Both halves, or the correct choice stays a matter of
1282
+ * remembering.
1283
+ */
1284
+ export declare const cacheScopeLeaks: (query: AuditableQuery & {
1285
+ readonly cache?: {
1286
+ readonly scope?: string;
1287
+ } | undefined;
1288
+ }, tenantScopedTables: ReadonlySet<string>) => ReadonlyArray<CacheScopeLeak>;
1289
+
1256
1290
  /**
1257
1291
  * `can(subject, action, resource)` — the authorization decision. FAILS CLOSED:
1258
1292
  * - `admin:full` scope → allow (the only bypass).
@@ -1790,6 +1824,17 @@ export declare interface CrudWriteOptions {
1790
1824
  readonly redact?: ReadonlyArray<string>;
1791
1825
  }
1792
1826
 
1827
+ /**
1828
+ * The current request's loader, or `undefined` outside a request.
1829
+ *
1830
+ * `undefined` is a real answer and must stay one: a boot seed, a schedule tick
1831
+ * or a plugin's startup hook has no request and therefore no shared cache. A
1832
+ * consumer reading this must fall back to its own query rather than assume, and
1833
+ * inventing a loader here would give background code a cache with no defined
1834
+ * lifetime.
1835
+ */
1836
+ export declare const currentRequestLoader: () => DataLoader | undefined;
1837
+
1793
1838
  /** Read the active context, if any. Tests use this to validate the
1794
1839
  * propagation; production code uses it only inside this module. */
1795
1840
  export declare const currentRoutingContext: () => RoutingContext | undefined;
@@ -2105,6 +2150,28 @@ export declare interface DrainDeps {
2105
2150
  readonly attemptLogCap?: number;
2106
2151
  }
2107
2152
 
2153
+ /**
2154
+ * Run the registered hooks, drain the live launches, then `exit()` — under a
2155
+ * hard deadline.
2156
+ *
2157
+ * The deadline is the point. Interrupting a launch waits for its finalizers,
2158
+ * and a finalizer that never completes (a pool drain against a gone database, a
2159
+ * wedged plugin `onDeactivate`, a redis `quit` on a dead socket) used to mean
2160
+ * the process never exited — while ALSO having lost node's default SIGTERM
2161
+ * kill, which installing a listener removes. The result was a server that
2162
+ * ignores SIGTERM outright. Under `voltro dev` that wedged the supervisor with
2163
+ * it, and the only recovery was killing the tree by hand.
2164
+ *
2165
+ * Exported for the test: this is a race against a hang, so the assertion has to
2166
+ * inject `exit` and drive a finalizer that genuinely never finishes.
2167
+ */
2168
+ export declare const drainForShutdown: (options: {
2169
+ readonly fibers: ReadonlyArray<Fiber.RuntimeFiber<void, unknown>>;
2170
+ readonly hooks: ReadonlyArray<() => void | Promise<void>>;
2171
+ readonly deadlineMs: number;
2172
+ readonly exit: () => void;
2173
+ }) => void;
2174
+
2108
2175
  /**
2109
2176
  * One drain pass: claim due rows, run their handler, record the outcome.
2110
2177
  *
@@ -2173,6 +2240,16 @@ export declare interface EnqueueOptions {
2173
2240
  readonly delayMs?: number;
2174
2241
  }
2175
2242
 
2243
+ /**
2244
+ * Make `loader` the current request's loader for the rest of this execution
2245
+ * context.
2246
+ *
2247
+ * Called once per request, from `makeAppContextBuilder`, immediately after the
2248
+ * loader is constructed — so anything the request reaches afterwards shares the
2249
+ * cache the executor will use.
2250
+ */
2251
+ export declare const enterRequestLoader: (loader: DataLoader) => void;
2252
+
2176
2253
  /** The always-available default — reads `process.env`. Sync under the hood,
2177
2254
  * Promise-wrapped to satisfy the async contract. */
2178
2255
  export declare const envSecretsBackend: SecretsBackend;
@@ -2335,6 +2412,9 @@ export declare interface FluentStoreBackend {
2335
2412
  /** Forget the calling subject's credential. Returns whether a row was removed. */
2336
2413
  export declare const forgetCredential: (store: VaultStore, connectionId: string, subjectId: string) => Promise<boolean>;
2337
2414
 
2415
+ /** A boot-ready message for a set of cache-scope leaks (empty → `undefined`). */
2416
+ export declare const formatCacheScopeLeaks: (leaks: ReadonlyArray<CacheScopeLeak>) => string | undefined;
2417
+
2338
2418
  /** A boot-ready message for a set of leaks (empty → `undefined`, i.e. clean). */
2339
2419
  export declare const formatServerOnlyLeaks: (leaks: ReadonlyArray<ServerOnlyLeak>) => string | undefined;
2340
2420
 
@@ -3599,6 +3679,19 @@ export declare interface OneShotQueryRunnerDeps<D> extends QueryProducerDeps<D>
3599
3679
  readonly queryRows: (descriptor: D) => Promise<ReadonlyArray<unknown>>;
3600
3680
  }
3601
3681
 
3682
+ /**
3683
+ * Register teardown to run on SIGINT/SIGTERM, before the launches are drained
3684
+ * and the process exits. Returns a de-register function.
3685
+ *
3686
+ * This exists because `process.on('beforeExit')` cannot do the job and reads as
3687
+ * if it can: `beforeExit` is not emitted when something calls `process.exit()`,
3688
+ * which is exactly how a signalled process ends — so a hook registered there
3689
+ * runs on a naturally-draining event loop and NEVER on the signal. A long-lived
3690
+ * server never drains naturally, so in practice it never ran at all. Eleven of
3691
+ * `voltro dev`'s teardowns sat there, including the CDC detach.
3692
+ */
3693
+ export declare const onProcessShutdown: (hook: () => void | Promise<void>) => (() => void);
3694
+
3602
3695
  /** Thrown by `.expectVersion(n).set(...)` when the optimistic-lock guard
3603
3696
  * matched no row (the row was concurrently updated or deleted). */
3604
3697
  export declare class OptimisticLockError extends OptimisticLockError_base {
@@ -5037,6 +5130,14 @@ export declare const runWithDeadlockRetry: <T>(work: () => Promise<T>, options?:
5037
5130
  */
5038
5131
  export declare const runWithObservedProcedure: <T>(procedure: ObservedProcedure, work: () => T) => T;
5039
5132
 
5133
+ /**
5134
+ * Run `fn` with `loader` current, restoring whatever was current before.
5135
+ *
5136
+ * The wrapping form, for callers that HAVE a callback — tests, and any future
5137
+ * boundary that would rather scope explicitly than rely on `enterWith`.
5138
+ */
5139
+ export declare const runWithRequestLoader: <T>(loader: DataLoader, fn: () => T) => T;
5140
+
5040
5141
  /**
5041
5142
  * Run `work` inside a routing scope. Every `query()` call against a
5042
5143
  * `ReplicatedDataStore` from inside `work` (or its async children)
@@ -6290,6 +6391,21 @@ export declare type TupleSource = (req: {
6290
6391
  readonly subjectId: string | null;
6291
6392
  readonly resourceType: string;
6292
6393
  readonly resourceId: string;
6394
+ /**
6395
+ * The REQUEST's `DataLoader` — the same one `ctx.load` gives the executor.
6396
+ *
6397
+ * A relationship guard almost always has to read something to answer (the
6398
+ * draft whose `teamId` decides access, the membership row), and the executor
6399
+ * then reads the same row for the actual work. Loading through this makes the
6400
+ * second read free: request-scoped, batched, cached, and dropped when the
6401
+ * request ends.
6402
+ *
6403
+ * `undefined` outside a request — a boot seed, a schedule tick, a plugin's
6404
+ * startup hook. Fall back to your own query there; there is no shared cache to
6405
+ * join, and treating `undefined` as an error would break the paths that
6406
+ * legitimately have no request.
6407
+ */
6408
+ readonly load: DataLoader | undefined;
6293
6409
  }) => Promise<ReadonlyArray<RelationTuple>> | ReadonlyArray<RelationTuple>;
6294
6410
 
6295
6411
  /**