@voltro/runtime 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +278 -0
- package/THIRD-PARTY-NOTICES.md +87 -1
- package/dist/index.d.ts +1077 -9
- package/dist/index.js +2182 -1175
- package/package.json +7 -6
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,7 @@ import { Row } from '@voltro/database';
|
|
|
56
56
|
import { Rpc } from '@effect/rpc';
|
|
57
57
|
import { RpcGroup } from '@effect/rpc';
|
|
58
58
|
import { RpcInterceptor } from '@voltro/protocol';
|
|
59
|
+
import { RuleReadStore } from '@voltro/database';
|
|
59
60
|
import { Sampler } from '@opentelemetry/sdk-trace-base';
|
|
60
61
|
import { Schedule } from 'effect';
|
|
61
62
|
import { Schema } from 'effect';
|
|
@@ -677,6 +678,8 @@ export declare interface AnalyticsTopEntry {
|
|
|
677
678
|
|
|
678
679
|
declare type AnyRow = Record<string, unknown>;
|
|
679
680
|
|
|
681
|
+
declare type AnyRow_2 = Record<string, unknown>;
|
|
682
|
+
|
|
680
683
|
export declare const API_KEYS_TABLE = "_voltro_api_keys";
|
|
681
684
|
|
|
682
685
|
/** Admin-gated management routes (issue / list / revoke) for the built-in keys.
|
|
@@ -1018,6 +1021,17 @@ export declare const assertCan: (subject: RebacSubject, action: string, resource
|
|
|
1018
1021
|
*/
|
|
1019
1022
|
export declare const assertConnectionCipherConfigured: (definitions: ReadonlyArray<ConnectionDefinition>) => void;
|
|
1020
1023
|
|
|
1024
|
+
/**
|
|
1025
|
+
* Assign a subject to a variant — STABLE (same subject ⇒ same variant, always)
|
|
1026
|
+
* and BALANCED (proportions converge to the weights over many subjects). The
|
|
1027
|
+
* assignment is salted with the experiment `name`, so one subject buckets
|
|
1028
|
+
* independently across different experiments.
|
|
1029
|
+
*
|
|
1030
|
+
* A `holdout` fraction is carved off the top of the interval FIRST; the
|
|
1031
|
+
* remaining space is split by variant weight. Pure — a client can reproduce it.
|
|
1032
|
+
*/
|
|
1033
|
+
export declare const assignVariant: (def: ExperimentDefinition, subject: string) => string;
|
|
1034
|
+
|
|
1021
1035
|
/**
|
|
1022
1036
|
* Async cache surface exposed as `ctx.cache` to non-Effect handlers — the
|
|
1023
1037
|
* facade over the Effect-native `Cache` service from `@voltro/cache`
|
|
@@ -1095,6 +1109,82 @@ export declare interface AttachAnalyticsMirrorOptions {
|
|
|
1095
1109
|
readonly run?: (effect: Effect.Effect<void, unknown>) => Promise<unknown>;
|
|
1096
1110
|
}
|
|
1097
1111
|
|
|
1112
|
+
/**
|
|
1113
|
+
* Wire every discovered expectation onto the store's CDC channel + a clock tick,
|
|
1114
|
+
* and expose the live signal via a registry. Returns the registry impl + a
|
|
1115
|
+
* detach for graceful shutdown — the SAME shape as the aggregate runner.
|
|
1116
|
+
*
|
|
1117
|
+
* Both boot paths (`voltro dev`, `voltro serve`) must call this and provide the
|
|
1118
|
+
* resulting `ExpectationRegistry` layer, exactly like `AggregateRegistry`.
|
|
1119
|
+
*/
|
|
1120
|
+
export declare const attachExpectations: (options: AttachExpectationsOptions) => ExpectationRunnerHandle;
|
|
1121
|
+
|
|
1122
|
+
export declare interface AttachExpectationsOptions {
|
|
1123
|
+
readonly discovered: ReadonlyArray<{
|
|
1124
|
+
readonly definition: ExpectationDefinition;
|
|
1125
|
+
}>;
|
|
1126
|
+
readonly store: DataStore;
|
|
1127
|
+
readonly log: SyncLogger;
|
|
1128
|
+
/** Wall clock. Default `Date.now`. */
|
|
1129
|
+
readonly now?: () => number;
|
|
1130
|
+
/** How often the time-dependent invariants (freshness) are re-checked against
|
|
1131
|
+
* the clock. Default 30s. Ignored when no expectation is time-dependent. */
|
|
1132
|
+
readonly tickIntervalMs?: number;
|
|
1133
|
+
/** Periodic full re-seed interval (drift healing), mirroring the aggregate
|
|
1134
|
+
* runner's reconcile. Default 5m. */
|
|
1135
|
+
readonly reconcileIntervalMs?: number;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Wire every discovered experiment onto the store's CDC channel and expose the
|
|
1140
|
+
* live results via a registry. Returns the registry impl + a detach for graceful
|
|
1141
|
+
* shutdown — the SAME shape as `attachExpectations` / `attachFinops`.
|
|
1142
|
+
*
|
|
1143
|
+
* Both boot paths (`voltro dev`, `voltro serve`) must call this and provide the
|
|
1144
|
+
* resulting `ExperimentRegistry` layer, exactly like `AggregateRegistry`
|
|
1145
|
+
* (deferred cli work — see the NOTE at the bottom of this file).
|
|
1146
|
+
*/
|
|
1147
|
+
export declare const attachExperiments: (options: AttachExperimentsOptions) => ExperimentRunnerHandle;
|
|
1148
|
+
|
|
1149
|
+
export declare interface AttachExperimentsOptions {
|
|
1150
|
+
readonly discovered: ReadonlyArray<{
|
|
1151
|
+
readonly definition: ExperimentDefinition;
|
|
1152
|
+
}>;
|
|
1153
|
+
readonly store: DataStore;
|
|
1154
|
+
readonly log: SyncLogger;
|
|
1155
|
+
/** Wall clock. Default `Date.now`. */
|
|
1156
|
+
readonly now?: () => number;
|
|
1157
|
+
/** Periodic full re-seed interval (drift healing), mirroring the aggregate
|
|
1158
|
+
* runner's reconcile. Default 5m. */
|
|
1159
|
+
readonly reconcileIntervalMs?: number;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Wire every discovered cost budget into a `CostAccountant` + a clock tick, and
|
|
1164
|
+
* expose the live attribution + budget signal via a `CostRegistry`. Returns the
|
|
1165
|
+
* registry impl + the `record` ingestion entry point + a detach — the SAME shape
|
|
1166
|
+
* as `attachExpectations`.
|
|
1167
|
+
*
|
|
1168
|
+
* Both boot paths (`voltro dev`, `voltro serve`) must call this and provide the
|
|
1169
|
+
* resulting `CostRegistry` layer, exactly like `ExpectationRegistry`; the
|
|
1170
|
+
* `record` returned here is the seam the dispatcher's recompute + the query path
|
|
1171
|
+
* call to emit cost events (see the NOTE at the bottom of this file — that call-
|
|
1172
|
+
* site instrumentation + `*.budget.ts` discovery are the deferred cli work).
|
|
1173
|
+
*/
|
|
1174
|
+
export declare const attachFinops: (options: AttachFinopsOptions) => FinopsRunnerHandle;
|
|
1175
|
+
|
|
1176
|
+
export declare interface AttachFinopsOptions {
|
|
1177
|
+
readonly discovered: ReadonlyArray<{
|
|
1178
|
+
readonly definition: CostBudgetDefinition;
|
|
1179
|
+
}>;
|
|
1180
|
+
readonly log: SyncLogger;
|
|
1181
|
+
/** Wall clock. Default `Date.now`. */
|
|
1182
|
+
readonly now?: () => number;
|
|
1183
|
+
/** How often windowed budgets are re-checked against the clock for a rollover
|
|
1184
|
+
* recovery. Default 30s. Ignored when no budget is windowed. */
|
|
1185
|
+
readonly tickIntervalMs?: number;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1098
1188
|
/** The descriptor shape the audit needs — a `defineQuery` result carries it. */
|
|
1099
1189
|
export declare interface AuditableQuery {
|
|
1100
1190
|
readonly name: string;
|
|
@@ -1770,6 +1860,206 @@ export declare interface Coordinator {
|
|
|
1770
1860
|
tryClaim(scheduleName: string, scheduledAt: Date): Promise<boolean>;
|
|
1771
1861
|
}
|
|
1772
1862
|
|
|
1863
|
+
/**
|
|
1864
|
+
* The standing compute-cost accountant. Fold `CostEvent`s in with `record`;
|
|
1865
|
+
* read attribution + budget state through the getters; roll windows with `tick`.
|
|
1866
|
+
* Every method is synchronous and allocation-light — it is on the reactive
|
|
1867
|
+
* recompute hot path.
|
|
1868
|
+
*/
|
|
1869
|
+
export declare class CostAccountant {
|
|
1870
|
+
private readonly now;
|
|
1871
|
+
private readonly attribution;
|
|
1872
|
+
private readonly counters;
|
|
1873
|
+
private readonly budgetsByName;
|
|
1874
|
+
private readonly totalBudgets;
|
|
1875
|
+
private readonly unitBudgets;
|
|
1876
|
+
constructor(definitions: ReadonlyArray<CostBudgetDefinition>, deps?: CostAccountantDeps);
|
|
1877
|
+
private counterFor;
|
|
1878
|
+
/** Attribute one cost event and evaluate every budget that watches its unit.
|
|
1879
|
+
* Returns the threshold-crossing signals produced (empty in the common,
|
|
1880
|
+
* no-crossing case). */
|
|
1881
|
+
record(event: CostEvent): ReadonlyArray<CostBudgetSignal>;
|
|
1882
|
+
/** Roll every windowed budget whose window has elapsed, with no event —
|
|
1883
|
+
* a budget recovers when its window passes even if the tenant went quiet.
|
|
1884
|
+
* Returns the `recovered` signals produced. */
|
|
1885
|
+
tick(): ReadonlyArray<CostBudgetSignal>;
|
|
1886
|
+
/** Reset attribution + every budget counter for one tenant, or ALL tenants
|
|
1887
|
+
* when `tenantId` is `undefined` (the system tenant is `null`, and IS
|
|
1888
|
+
* targetable). Returns the `recovered` signals for any budget that was
|
|
1889
|
+
* breached. Use for a chargeback-period rollover the windows don't express,
|
|
1890
|
+
* or a test reset. */
|
|
1891
|
+
reset(tenantId?: string | null): ReadonlyArray<CostBudgetSignal>;
|
|
1892
|
+
private allCounterTenants;
|
|
1893
|
+
attributionSnapshot(): ReadonlyArray<TenantCostState>;
|
|
1894
|
+
tenantState(tenantId: string | null): TenantCostState | null;
|
|
1895
|
+
budgetStates(): ReadonlyArray<CostBudgetState>;
|
|
1896
|
+
budgetState(name: string, tenantId: string | null): CostBudgetState | null;
|
|
1897
|
+
breaches(): ReadonlyArray<CostBudgetState>;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
export declare interface CostAccountantDeps {
|
|
1901
|
+
/** Wall clock. Injectable for deterministic tests. Default `Date.now`. */
|
|
1902
|
+
readonly now?: () => number;
|
|
1903
|
+
readonly log?: SyncLogger;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
export declare interface CostBudgetDefinition {
|
|
1907
|
+
/** Brand marker — lets the cli discriminate default-exported budget
|
|
1908
|
+
* definitions from arbitrary objects during file discovery. */
|
|
1909
|
+
readonly _voltroCostBudget: true;
|
|
1910
|
+
readonly name: string;
|
|
1911
|
+
readonly limit: number;
|
|
1912
|
+
/** Resolved cost unit, or `null` for a total-across-all-units budget. */
|
|
1913
|
+
readonly unit: string | null;
|
|
1914
|
+
readonly warnAt: number;
|
|
1915
|
+
/** Resolved tumbling-window length in ms, or `null` for a cumulative budget. */
|
|
1916
|
+
readonly windowMs: number | null;
|
|
1917
|
+
readonly severity: CostBudgetSeverity;
|
|
1918
|
+
readonly description?: string;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
export declare interface CostBudgetDefinitionInput {
|
|
1922
|
+
/** Stable id within the app. Unique across all cost budgets. */
|
|
1923
|
+
readonly name: string;
|
|
1924
|
+
/** The per-tenant ceiling, in the budget's `unit` (or the tenant's grand
|
|
1925
|
+
* total when `unit` is omitted). A tenant crossing this is `exceeded`. */
|
|
1926
|
+
readonly limit: number;
|
|
1927
|
+
/** Which cost unit this budget meters. Omitted ⇒ the tenant's TOTAL across
|
|
1928
|
+
* every unit (the common "total compute per tenant" cap). When set, only
|
|
1929
|
+
* `CostEvent`s of this unit count toward the budget. */
|
|
1930
|
+
readonly unit?: string;
|
|
1931
|
+
/** Fraction of `limit` (0..1) at which the budget goes `warn` — the early
|
|
1932
|
+
* signal before the hard cap. Default `0.8`. Set `1` to disable the warn
|
|
1933
|
+
* band (straight ok→exceeded). */
|
|
1934
|
+
readonly warnAt?: number;
|
|
1935
|
+
/** Tumbling window as an interval string (`'1h'`, `'24h'`). The per-tenant
|
|
1936
|
+
* counter resets at each window boundary (and a breached budget RECOVERS).
|
|
1937
|
+
* Omitted ⇒ a cumulative budget that only resets on an explicit
|
|
1938
|
+
* `reset(tenantId)` — the since-boot chargeback ceiling. */
|
|
1939
|
+
readonly window?: string;
|
|
1940
|
+
/** Alerting priority carried on the breach signal. Default `'warn'`. */
|
|
1941
|
+
readonly severity?: CostBudgetSeverity;
|
|
1942
|
+
/** Human-facing note surfaced in devtools / the breach message. */
|
|
1943
|
+
readonly description?: string;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
/** How loud a budget breach is. A classification carried on the signal for
|
|
1947
|
+
* alerting priority — the framework never BLOCKS compute on a budget here
|
|
1948
|
+
* (that is a caller's choice, the way `requireAiBudget` fails a call); a cost
|
|
1949
|
+
* budget is an observability-grade signal over work that already happened. */
|
|
1950
|
+
export declare type CostBudgetSeverity = 'info' | 'warn' | 'critical';
|
|
1951
|
+
|
|
1952
|
+
/** Emitted when a budget crosses a threshold for a tenant. `warn` / `exceeded`
|
|
1953
|
+
* are upward crossings (event-driven, with a cause); `recovered` is a window
|
|
1954
|
+
* rollover / reset back to `ok` (no cause). */
|
|
1955
|
+
export declare interface CostBudgetSignal {
|
|
1956
|
+
readonly kind: 'warn' | 'exceeded' | 'recovered';
|
|
1957
|
+
readonly state: CostBudgetState;
|
|
1958
|
+
readonly cause: CostCause | null;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/** The live state of one budget for one tenant — the row an inspect endpoint /
|
|
1962
|
+
* dashboard reads. */
|
|
1963
|
+
export declare interface CostBudgetState {
|
|
1964
|
+
readonly budget: string;
|
|
1965
|
+
readonly tenantId: string | null;
|
|
1966
|
+
readonly status: CostBudgetStatus;
|
|
1967
|
+
readonly severity: CostBudgetSeverity;
|
|
1968
|
+
/** The metered cost against this budget in the current window. */
|
|
1969
|
+
readonly spent: number;
|
|
1970
|
+
/** The hard ceiling. */
|
|
1971
|
+
readonly limit: number;
|
|
1972
|
+
/** The absolute warn ceiling (`warnAt * limit`). */
|
|
1973
|
+
readonly warnThreshold: number;
|
|
1974
|
+
/** The unit this budget meters, or `null` for a total-across-units budget. */
|
|
1975
|
+
readonly unit: string | null;
|
|
1976
|
+
/** When the current window opened (windowed budgets), else `null`. */
|
|
1977
|
+
readonly windowStartedAt: number | null;
|
|
1978
|
+
/** When this budget last entered its current `status`. */
|
|
1979
|
+
readonly since: number | null;
|
|
1980
|
+
readonly lastUpdatedAt: number | null;
|
|
1981
|
+
/** The most recent breach's cause, retained across recovery for the audit
|
|
1982
|
+
* trail. `null` if it has never been breached. */
|
|
1983
|
+
readonly lastCause: CostCause | null;
|
|
1984
|
+
readonly description?: string;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
/** A budget's status for one tenant. `ok` below the warn band, `warn` at/over
|
|
1988
|
+
* `warnAt * limit`, `exceeded` at/over `limit`. Monotonic within a window;
|
|
1989
|
+
* resets to `ok` on window rollover / explicit reset. */
|
|
1990
|
+
export declare type CostBudgetStatus = 'ok' | 'warn' | 'exceeded';
|
|
1991
|
+
|
|
1992
|
+
/** What incurred the cost that tipped a budget across a threshold — resolved
|
|
1993
|
+
* from the `CostEvent`. `null` when the transition was window-driven (a budget
|
|
1994
|
+
* RECOVERING because its window rolled over, with no event). */
|
|
1995
|
+
export declare interface CostCause {
|
|
1996
|
+
readonly unit: string;
|
|
1997
|
+
readonly subscriptionId?: string | null;
|
|
1998
|
+
readonly procedure?: string | null;
|
|
1999
|
+
readonly traceId?: string | null;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* One unit of attributed compute. A compute site (a reactive recompute in the
|
|
2004
|
+
* dispatcher, a query, a fan-out delivery, or any pluggable cost source) emits
|
|
2005
|
+
* one of these tagged with the tenant/subscription that caused it, and the
|
|
2006
|
+
* accountant folds it into the standing per-tenant total AND every budget that
|
|
2007
|
+
* watches its `unit`.
|
|
2008
|
+
*
|
|
2009
|
+
* `amount` is a NORMALISED cost figure in whatever unit the caller chose — 1 per
|
|
2010
|
+
* recompute, rows scanned, milliseconds elapsed, a priced micro-USD. The
|
|
2011
|
+
* framework does not impose a cost model; `unit` names which one this event is
|
|
2012
|
+
* denominated in, and a budget either targets that named unit or the tenant's
|
|
2013
|
+
* grand total. This is the "pluggable cost unit" seam.
|
|
2014
|
+
*/
|
|
2015
|
+
export declare interface CostEvent {
|
|
2016
|
+
/** Active org id the compute is attributed to. `null` = system / untenanted
|
|
2017
|
+
* work (a schedule, a resumed workflow — `SYSTEM_SUBJECT.tenantId`). */
|
|
2018
|
+
readonly tenantId: string | null;
|
|
2019
|
+
/** The live subscription that drove this compute, when the cost came from a
|
|
2020
|
+
* reactive recompute — the per-subscription attribution the plan calls for.
|
|
2021
|
+
* Absent for non-subscription compute (a one-shot query). */
|
|
2022
|
+
readonly subscriptionId?: string | null;
|
|
2023
|
+
/** Which cost unit `amount` is denominated in — `'recompute'`, `'query'`,
|
|
2024
|
+
* `'fanout'`, or any custom unit the caller meters. */
|
|
2025
|
+
readonly unit: string;
|
|
2026
|
+
/** The normalised cost contribution in `unit`. Non-finite / negative amounts
|
|
2027
|
+
* are ignored by the accountant (a cost cannot be negative). */
|
|
2028
|
+
readonly amount: number;
|
|
2029
|
+
/** The rpc tag of the call that incurred the cost, for the "why is my bill
|
|
2030
|
+
* high" breakdown + the breach cause. */
|
|
2031
|
+
readonly procedure?: string | null;
|
|
2032
|
+
/** The trace the offending compute ran under — joins to `voltro logs --trace`. */
|
|
2033
|
+
readonly traceId?: string | null;
|
|
2034
|
+
/** Wall-clock ms the cost was incurred. Default: the accountant's clock. */
|
|
2035
|
+
readonly at?: number;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
/**
|
|
2039
|
+
* Framework-provided registry of per-tenant compute-cost attribution + every
|
|
2040
|
+
* budget the cli discovered. Handler / inspect code reads the live signal here,
|
|
2041
|
+
* exactly as it reads `ExpectationRegistry`.
|
|
2042
|
+
*/
|
|
2043
|
+
export declare class CostRegistry extends CostRegistry_base {
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
declare const CostRegistry_base: Context.TagClass<CostRegistry, "@voltro/CostRegistry", CostRegistryApi>;
|
|
2047
|
+
|
|
2048
|
+
export declare interface CostRegistryApi {
|
|
2049
|
+
/** Per-tenant compute-cost attribution — the chargeback feed. */
|
|
2050
|
+
readonly attribution: () => ReadonlyArray<TenantCostState>;
|
|
2051
|
+
/** One tenant's attribution, or `null` when nothing has been attributed. */
|
|
2052
|
+
readonly tenant: (tenantId: string | null) => TenantCostState | null;
|
|
2053
|
+
/** Every (budget, tenant) state — the full budget snapshot. */
|
|
2054
|
+
readonly budgets: () => ReadonlyArray<CostBudgetState>;
|
|
2055
|
+
/** One budget's state for one tenant, or `null` when unseen. */
|
|
2056
|
+
readonly budget: (name: string, tenantId: string | null) => CostBudgetState | null;
|
|
2057
|
+
/** Only the currently warn/exceeded budget states — the alerting view. */
|
|
2058
|
+
readonly breaches: () => ReadonlyArray<CostBudgetState>;
|
|
2059
|
+
/** Subscribe to budget threshold crossings. Returns an unsubscribe fn. */
|
|
2060
|
+
readonly subscribe: (listener: (s: CostBudgetSignal) => void) => () => void;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1773
2063
|
/** Define a cumulative counter. Increment with `incrementMetric` / `Metric.increment`. */
|
|
1774
2064
|
export declare const counter: (name: string, description?: string) => Metric.Metric.Counter<number>;
|
|
1775
2065
|
|
|
@@ -2005,6 +2295,32 @@ export declare const DEFAULT_REFRESH_SKEW_MS = 60000;
|
|
|
2005
2295
|
*/
|
|
2006
2296
|
export declare const DEFAULT_ROW_FILTER_RETRY: Schedule.Schedule<unknown, unknown>;
|
|
2007
2297
|
|
|
2298
|
+
/**
|
|
2299
|
+
* The message of an UNDECLARED throw, bounded, with nothing else attached.
|
|
2300
|
+
*
|
|
2301
|
+
* An executor that throws a plain `TypeError` produces a DEFECT, not a typed
|
|
2302
|
+
* failure — so `catchIf(isInfraError)` above never sees it (that guards the E
|
|
2303
|
+
* channel) and the rpc encoder tried to match it against the descriptor's
|
|
2304
|
+
* `error:` union. It cannot match, by definition: a defect is precisely the
|
|
2305
|
+
* thing that is not in the union. What reached the browser was the entire
|
|
2306
|
+
* decode tree — every union member, the full `ExitEncoded<…>` type, and the
|
|
2307
|
+
* real cause on the last line. ~2 KB of type names, which one consumer's
|
|
2308
|
+
* account page rendered verbatim where a reason belonged, and which every app
|
|
2309
|
+
* otherwise has to condense heuristically to avoid putting a schema on screen.
|
|
2310
|
+
*
|
|
2311
|
+
* `message` ONLY — no stack, no `cause` chain, no own fields. The same
|
|
2312
|
+
* reasoning as `wireErrorFromCause`: a nested object can carry a DSN or a
|
|
2313
|
+
* token. A message is what the server already logged and what a human needs;
|
|
2314
|
+
* the full original stays in the server log via `logHandlerFailure`.
|
|
2315
|
+
*
|
|
2316
|
+
* Note the deliberate asymmetry with `isInfraError`, which collapses to the
|
|
2317
|
+
* generic text instead: a `SqlError`'s message names internal `table.column`
|
|
2318
|
+
* detail, so its text is withheld on purpose. An arbitrary app defect has no
|
|
2319
|
+
* such known shape — withholding it too would leave the app exactly where it
|
|
2320
|
+
* started, with a reason it cannot show.
|
|
2321
|
+
*/
|
|
2322
|
+
export declare const defectMessage: (defect: unknown) => string;
|
|
2323
|
+
|
|
2008
2324
|
/**
|
|
2009
2325
|
* Construct an aggregate definition. The returned object brands itself
|
|
2010
2326
|
* so the cli's file-discovery pass picks it up from default exports.
|
|
@@ -2043,6 +2359,27 @@ export declare const defineAggregate: <Row>(input: AggregateDefinitionInput<Row>
|
|
|
2043
2359
|
*/
|
|
2044
2360
|
export declare const defineConnection: <D extends ConnectionDefinition>(definition: D) => D;
|
|
2045
2361
|
|
|
2362
|
+
/**
|
|
2363
|
+
* Declare a per-tenant compute-cost budget. Validates the shape at declaration
|
|
2364
|
+
* time so a malformed budget fails LOUD at boot rather than silently never
|
|
2365
|
+
* firing (the same discipline as `defineExpectation`).
|
|
2366
|
+
*
|
|
2367
|
+
* ```ts
|
|
2368
|
+
* // apps/api/budgets/tenantCompute.budget.ts
|
|
2369
|
+
* import { defineCostBudget } from '@voltro/runtime'
|
|
2370
|
+
*
|
|
2371
|
+
* export default defineCostBudget({
|
|
2372
|
+
* name: 'tenant-recompute-hourly',
|
|
2373
|
+
* unit: 'recompute',
|
|
2374
|
+
* limit: 100_000, // 100k recomputes per tenant per hour
|
|
2375
|
+
* warnAt: 0.8, // warn at 80k
|
|
2376
|
+
* window: '1h',
|
|
2377
|
+
* severity: 'warn',
|
|
2378
|
+
* })
|
|
2379
|
+
* ```
|
|
2380
|
+
*/
|
|
2381
|
+
export declare const defineCostBudget: (input: CostBudgetDefinitionInput) => CostBudgetDefinition;
|
|
2382
|
+
|
|
2046
2383
|
export declare const defineEventTrigger: <EventPayload = unknown, WorkflowPayload = EventPayload>(spec: Omit<WorkflowEventTriggerDefinition<EventPayload, WorkflowPayload>, "_tag"> | (Omit<WorkflowEventTriggerDefinition<EventPayload, WorkflowPayload>, "_tag" | "event"> & {
|
|
2047
2384
|
/** A declared event. Its `name` becomes the matched event. */
|
|
2048
2385
|
readonly on: {
|
|
@@ -2058,6 +2395,46 @@ export declare const defineEventTrigger: <EventPayload = unknown, WorkflowPayloa
|
|
|
2058
2395
|
*/
|
|
2059
2396
|
export declare const defineExecutor: <D extends ExecutorDescriptor, F extends (input: ExecutorInput<D>, ctx: AppContext) => ExecutorReturn<D, unknown, unknown>>(_descriptor: D, fn: F) => ((input: ExecutorInput<D>, ctx: AppContext) => ReturnType<F>);
|
|
2060
2397
|
|
|
2398
|
+
/**
|
|
2399
|
+
* Declare a data-quality expectation. Validates the shape at declaration time so
|
|
2400
|
+
* a malformed contract fails LOUD at boot rather than silently never firing.
|
|
2401
|
+
*
|
|
2402
|
+
* ```ts
|
|
2403
|
+
* // apps/api/expectations/orderFreshness.expectation.ts
|
|
2404
|
+
* import { defineExpectation } from '@voltro/runtime'
|
|
2405
|
+
*
|
|
2406
|
+
* export default defineExpectation({
|
|
2407
|
+
* name: 'orders-fresh',
|
|
2408
|
+
* on: { table: 'orders' },
|
|
2409
|
+
* invariant: { kind: 'freshness', column: 'createdAt', maxAgeMs: 10 * 60_000 },
|
|
2410
|
+
* severity: 'critical',
|
|
2411
|
+
* })
|
|
2412
|
+
* ```
|
|
2413
|
+
*/
|
|
2414
|
+
export declare const defineExpectation: (input: ExpectationDefinitionInput) => ExpectationDefinition;
|
|
2415
|
+
|
|
2416
|
+
/**
|
|
2417
|
+
* Declare an online experiment. Validates the shape at declaration time so a
|
|
2418
|
+
* malformed experiment fails LOUD at boot rather than silently never producing a
|
|
2419
|
+
* result (the same discipline as `defineExpectation` / `defineCostBudget`).
|
|
2420
|
+
*
|
|
2421
|
+
* ```ts
|
|
2422
|
+
* // apps/api/experiments/checkoutColor.experiment.ts
|
|
2423
|
+
* import { defineExperiment } from '@voltro/runtime'
|
|
2424
|
+
*
|
|
2425
|
+
* export default defineExperiment({
|
|
2426
|
+
* name: 'checkout-button-color',
|
|
2427
|
+
* on: { table: 'orders' },
|
|
2428
|
+
* subject: 'userId', // stable per-user bucketing
|
|
2429
|
+
* variants: [{ name: 'control' }, { name: 'green' }],
|
|
2430
|
+
* holdout: 0.1, // 10% see nothing, for a clean baseline
|
|
2431
|
+
* metric: { kind: 'conversionRate', column: 'completed' },
|
|
2432
|
+
* baseline: 'control',
|
|
2433
|
+
* })
|
|
2434
|
+
* ```
|
|
2435
|
+
*/
|
|
2436
|
+
export declare const defineExperiment: (input: ExperimentDefinitionInput) => ExperimentDefinition;
|
|
2437
|
+
|
|
2061
2438
|
/**
|
|
2062
2439
|
* Declare who delivers an effect. One per `*.outbox.ts` file.
|
|
2063
2440
|
*
|
|
@@ -2129,6 +2506,21 @@ export { DialectReplicationAdapter }
|
|
|
2129
2506
|
* delete → every field `value→undefined`; update → only the changed fields. */
|
|
2130
2507
|
export declare const diffChange: (c: CdcChange) => RowDiff;
|
|
2131
2508
|
|
|
2509
|
+
export declare interface DiscoveredCostBudget {
|
|
2510
|
+
readonly file: string;
|
|
2511
|
+
readonly definition: CostBudgetDefinition;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
export declare interface DiscoveredExpectation {
|
|
2515
|
+
readonly file: string;
|
|
2516
|
+
readonly definition: ExpectationDefinition;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
export declare interface DiscoveredExperiment {
|
|
2520
|
+
readonly file: string;
|
|
2521
|
+
readonly definition: ExperimentDefinition;
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2132
2524
|
export declare interface DiscoveredWorkflowLike<Context = unknown> {
|
|
2133
2525
|
readonly definition: WorkflowDefinitionLike;
|
|
2134
2526
|
readonly buildExecute: (ctx: Context) => (payload: never, executionId: string) => Effect.Effect<unknown, unknown, unknown>;
|
|
@@ -2266,6 +2658,19 @@ export declare interface DispatcherDependencies {
|
|
|
2266
2658
|
* the trace buffer so the dashboards show each data transfer's latency.
|
|
2267
2659
|
* No-op when absent (tests, embedders that don't trace). */
|
|
2268
2660
|
readonly recordDelivery?: (delivery: SubscriptionDelivery) => void;
|
|
2661
|
+
/**
|
|
2662
|
+
* Optional reactive-compute cost sink (the finops runner's `record`). Called
|
|
2663
|
+
* once per reactive RECOMPUTE delivery — a change re-ran an affected
|
|
2664
|
+
* subscription and pushed it a delta — with `unit: 'recompute', amount: 1`
|
|
2665
|
+
* attributed to the subscription's tenant. Sibling to `recordDelivery`: same
|
|
2666
|
+
* call sites, a different ledger (chargeback / budget vs latency trace).
|
|
2667
|
+
*
|
|
2668
|
+
* No-op when absent (tests, embedders, apps with no `*.budget.ts`). The hot
|
|
2669
|
+
* path pays nothing in that case — the `CostEvent` is only allocated inside
|
|
2670
|
+
* the `recordCost !== undefined` guard, never eagerly. Wired by both boot
|
|
2671
|
+
* paths ONLY when finops is attached; see `attachFinops`.
|
|
2672
|
+
*/
|
|
2673
|
+
readonly recordCost?: (event: CostEvent) => void;
|
|
2269
2674
|
/** Optional snapshot cache (Layer 3). Absent → queries are never cached;
|
|
2270
2675
|
* present → subscriptions whose descriptor opted in (a `cacheBinding`
|
|
2271
2676
|
* is passed to `subscribe`) share + cache their initial snapshot. */
|
|
@@ -2402,6 +2807,21 @@ export declare const enterRequestLoader: (loader: DataLoader) => void;
|
|
|
2402
2807
|
* Promise-wrapped to satisfy the async contract. */
|
|
2403
2808
|
export declare const envSecretsBackend: SecretsBackend;
|
|
2404
2809
|
|
|
2810
|
+
/**
|
|
2811
|
+
* Evaluate every declared rule of every touched table's rows. Throws the first
|
|
2812
|
+
* error-severity `BusinessRuleViolation` (rolling back the transaction);
|
|
2813
|
+
* warning-severity violations log and continue.
|
|
2814
|
+
*
|
|
2815
|
+
* A predicate that itself throws a `BusinessRuleViolation` (the author built one
|
|
2816
|
+
* directly) is re-thrown as-is; any OTHER thrown value propagates unchanged — a
|
|
2817
|
+
* real failure (a DB error) must not be silently reclassified as a violation.
|
|
2818
|
+
*/
|
|
2819
|
+
export declare const evaluateTouchedRules: (params: {
|
|
2820
|
+
readonly touched: ReadonlyArray<TouchedRow>;
|
|
2821
|
+
readonly store: RuleReadStore;
|
|
2822
|
+
readonly subject: unknown;
|
|
2823
|
+
}) => Promise<void>;
|
|
2824
|
+
|
|
2405
2825
|
/**
|
|
2406
2826
|
* How many deliveries one client may fall behind before the oldest are dropped.
|
|
2407
2827
|
*
|
|
@@ -2778,6 +3198,451 @@ export declare type ExecutorOutput<D extends ExecutorDescriptor> = Schema.Schema
|
|
|
2778
3198
|
*/
|
|
2779
3199
|
export declare type ExecutorReturn<D extends ExecutorDescriptor, E, R> = ExecutorOutput<D> | Promise<ExecutorOutput<D>> | Effect.Effect<ExecutorOutput<D>, E, R> | ReactiveReturn<D>;
|
|
2780
3200
|
|
|
3201
|
+
/** The write that tipped an expectation into violation, resolved from the CDC
|
|
3202
|
+
* `ChangeEvent` that caused it — the provenance link the plan calls for. `null`
|
|
3203
|
+
* when the transition was CLOCK-driven (a freshness SLA aging out with no
|
|
3204
|
+
* write) rather than write-driven: the honest answer is "no write caused this;
|
|
3205
|
+
* the ABSENCE of writes did". */
|
|
3206
|
+
export declare interface ExpectationCause {
|
|
3207
|
+
readonly table: string;
|
|
3208
|
+
readonly op: 'insert' | 'update' | 'delete';
|
|
3209
|
+
/** The trace the offending write ran under — joins to `voltro logs --trace`
|
|
3210
|
+
* and the audit sink. */
|
|
3211
|
+
readonly traceId?: string | null;
|
|
3212
|
+
/** The acting identity behind the offending write. */
|
|
3213
|
+
readonly subjectId?: string | null;
|
|
3214
|
+
/** The rpc tag of the call that made the offending write, when carried. */
|
|
3215
|
+
readonly procedure?: string | null;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
export declare interface ExpectationDefinition {
|
|
3219
|
+
/** Brand marker — lets the cli discriminate default-exported expectation
|
|
3220
|
+
* definitions from arbitrary objects during file discovery. */
|
|
3221
|
+
readonly _voltroExpectation: true;
|
|
3222
|
+
readonly name: string;
|
|
3223
|
+
readonly on: {
|
|
3224
|
+
readonly table: string;
|
|
3225
|
+
readonly where?: (row: Readonly<Record<string, unknown>>) => boolean;
|
|
3226
|
+
};
|
|
3227
|
+
readonly invariant: ExpectationInvariant;
|
|
3228
|
+
readonly severity: ExpectationSeverity;
|
|
3229
|
+
readonly description?: string;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
export declare interface ExpectationDefinitionInput {
|
|
3233
|
+
/** Stable id within the app. Unique across all expectations. */
|
|
3234
|
+
readonly name: string;
|
|
3235
|
+
/** The watched table. Its CDC deltas drive re-evaluation. An optional `where`
|
|
3236
|
+
* narrows the population the invariant is asserted over — a pure predicate
|
|
3237
|
+
* evaluated server-side per row (rows failing it are excluded from the
|
|
3238
|
+
* metric). It never leaves the server, so it can be any predicate. */
|
|
3239
|
+
readonly on: {
|
|
3240
|
+
readonly table: string;
|
|
3241
|
+
readonly where?: (row: Readonly<Record<string, unknown>>) => boolean;
|
|
3242
|
+
};
|
|
3243
|
+
readonly invariant: ExpectationInvariant;
|
|
3244
|
+
/** Alerting priority carried on the violation signal. Default `'warn'`. */
|
|
3245
|
+
readonly severity?: ExpectationSeverity;
|
|
3246
|
+
/** Human-facing note surfaced in devtools / the violation message. */
|
|
3247
|
+
readonly description?: string;
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
/**
|
|
3251
|
+
* Evaluates ONE expectation. Seed once, then feed CDC deltas via `applyChange`
|
|
3252
|
+
* and (for a time-dependent invariant) clock ticks via `tick`. Each call
|
|
3253
|
+
* re-reads the maintained metric, updates the live state, and returns a
|
|
3254
|
+
* transition when the holding/violated boundary is crossed.
|
|
3255
|
+
*/
|
|
3256
|
+
export declare class ExpectationEvaluator {
|
|
3257
|
+
private readonly def;
|
|
3258
|
+
private readonly deps;
|
|
3259
|
+
private readonly engine;
|
|
3260
|
+
private readonly now;
|
|
3261
|
+
private seeded;
|
|
3262
|
+
private status;
|
|
3263
|
+
private metric;
|
|
3264
|
+
private threshold;
|
|
3265
|
+
private since;
|
|
3266
|
+
private lastEvaluatedAt;
|
|
3267
|
+
private lastCause;
|
|
3268
|
+
constructor(def: ExpectationDefinition, deps: ExpectationEvaluatorDeps);
|
|
3269
|
+
get timeDependent(): boolean;
|
|
3270
|
+
/** Whether a row is in the asserted population (honours `on.where`). */
|
|
3271
|
+
private inScope;
|
|
3272
|
+
/** Seed the metric from the current base rows and evaluate the initial state.
|
|
3273
|
+
* Never emits a transition (there is no prior state to cross from). */
|
|
3274
|
+
seed(): Promise<void>;
|
|
3275
|
+
/**
|
|
3276
|
+
* Apply one CDC change and re-evaluate. Returns a transition if the change
|
|
3277
|
+
* (or the reshaped population) crossed the boundary. A change to a row that is
|
|
3278
|
+
* out of scope on BOTH images is ignored.
|
|
3279
|
+
*/
|
|
3280
|
+
applyChange(event: ChangeEvent): Promise<ExpectationTransition | null>;
|
|
3281
|
+
/** Re-evaluate against the current clock WITHOUT a write — for a
|
|
3282
|
+
* time-dependent invariant (freshness) whose metric ages on its own. Returns
|
|
3283
|
+
* a transition if the clock alone crossed the boundary. */
|
|
3284
|
+
tick(): ExpectationTransition | null;
|
|
3285
|
+
private commitReading;
|
|
3286
|
+
state(): ExpectationState;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
export declare interface ExpectationEvaluatorDeps {
|
|
3290
|
+
/** Read the full current base rows — used to SEED and to resolve a min/max
|
|
3291
|
+
* rescan (freshness) from the post-commit table. */
|
|
3292
|
+
readonly queryBase: () => Promise<ReadonlyArray<Row_5>>;
|
|
3293
|
+
/** Wall clock. Injectable for deterministic tests. Default `Date.now`. */
|
|
3294
|
+
readonly now?: () => number;
|
|
3295
|
+
readonly log?: SyncLogger;
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
/**
|
|
3299
|
+
* The data-quality invariant an expectation asserts. Each variant is chosen so
|
|
3300
|
+
* it reduces the watched table to a SINGLE incrementally-maintained metric that
|
|
3301
|
+
* rides the IVM engine (or a delta-driven filtered count), then compares that
|
|
3302
|
+
* metric against a threshold:
|
|
3303
|
+
*
|
|
3304
|
+
* - `freshness` — max(column) → `age = now - max`; violated when
|
|
3305
|
+
* `age > maxAgeMs`. Rides the IVM `max` maintainer (incl. its
|
|
3306
|
+
* rescan-on-extreme-delete protocol). TIME-DEPENDENT: it is
|
|
3307
|
+
* re-checked on a clock tick as well as on write, because the
|
|
3308
|
+
* metric ages with the wall clock even when no write arrives —
|
|
3309
|
+
* which is exactly when a freshness SLA matters.
|
|
3310
|
+
* - `nullRate` — fraction of rows whose `column` is null; violated when
|
|
3311
|
+
* `rate > maxRate`. Delta-maintained filtered count.
|
|
3312
|
+
* - `rowCount` — COUNT(*) of the table; violated when out of `[min, max]`.
|
|
3313
|
+
* - `valueBounds` — fraction of rows whose numeric `column` falls outside
|
|
3314
|
+
* `[min, max]` (the distribution / range check). Violated when
|
|
3315
|
+
* that fraction exceeds `maxViolationRate`, or — when no rate
|
|
3316
|
+
* is given — when ANY row is out of bounds.
|
|
3317
|
+
*/
|
|
3318
|
+
export declare type ExpectationInvariant = {
|
|
3319
|
+
readonly kind: 'freshness';
|
|
3320
|
+
readonly column: string;
|
|
3321
|
+
readonly maxAgeMs: number;
|
|
3322
|
+
} | {
|
|
3323
|
+
readonly kind: 'nullRate';
|
|
3324
|
+
readonly column: string;
|
|
3325
|
+
readonly maxRate: number;
|
|
3326
|
+
} | {
|
|
3327
|
+
readonly kind: 'rowCount';
|
|
3328
|
+
readonly min?: number;
|
|
3329
|
+
readonly max?: number;
|
|
3330
|
+
} | {
|
|
3331
|
+
readonly kind: 'valueBounds';
|
|
3332
|
+
readonly column: string;
|
|
3333
|
+
readonly min?: number;
|
|
3334
|
+
readonly max?: number;
|
|
3335
|
+
/** When set, the expectation tolerates up to this FRACTION (0..1) of
|
|
3336
|
+
* out-of-bounds rows before firing. Omitted ⇒ a single out-of-bounds row
|
|
3337
|
+
* violates. */
|
|
3338
|
+
readonly maxViolationRate?: number;
|
|
3339
|
+
};
|
|
3340
|
+
|
|
3341
|
+
/**
|
|
3342
|
+
* Framework-provided registry of every expectation the cli discovered + the
|
|
3343
|
+
* evaluator maintains. Handler / inspect code reads the live signal here.
|
|
3344
|
+
*/
|
|
3345
|
+
export declare class ExpectationRegistry extends ExpectationRegistry_base {
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
declare const ExpectationRegistry_base: Context.TagClass<ExpectationRegistry, "@voltro/ExpectationRegistry", ExpectationRegistryApi>;
|
|
3349
|
+
|
|
3350
|
+
export declare interface ExpectationRegistryApi {
|
|
3351
|
+
/** Snapshot every registered expectation's current state — the inspect feed. */
|
|
3352
|
+
readonly snapshot: () => ReadonlyArray<ExpectationState>;
|
|
3353
|
+
/** One expectation's state by name, or `null` when unknown. */
|
|
3354
|
+
readonly get: (name: string) => ExpectationState | null;
|
|
3355
|
+
/** Only the currently-violated expectations — the alerting view. */
|
|
3356
|
+
readonly violations: () => ReadonlyArray<ExpectationState>;
|
|
3357
|
+
/** Subscribe to holding/violated transitions. Returns an unsubscribe fn. */
|
|
3358
|
+
readonly subscribe: (listener: (t: ExpectationTransition) => void) => () => void;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
export declare interface ExpectationRunnerHandle {
|
|
3362
|
+
readonly registry: ExpectationRegistryApi;
|
|
3363
|
+
readonly detach: () => void;
|
|
3364
|
+
readonly registered: ReadonlyArray<string>;
|
|
3365
|
+
/** Test/debug seam: run one clock re-check of the time-dependent
|
|
3366
|
+
* expectations at `nowMs` (or the injected clock) and return the snapshot.
|
|
3367
|
+
* This is what the periodic tick timer calls. */
|
|
3368
|
+
readonly runTick: () => void;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
/** How loud a violation is. Purely a classification carried on the signal —
|
|
3372
|
+
* the framework never blocks a write on any severity (that is a business
|
|
3373
|
+
* rule's job); severity drives alerting priority downstream. */
|
|
3374
|
+
export declare type ExpectationSeverity = 'info' | 'warn' | 'critical';
|
|
3375
|
+
|
|
3376
|
+
/** The live state of one expectation — the row an inspect endpoint / dashboard
|
|
3377
|
+
* reads. */
|
|
3378
|
+
export declare interface ExpectationState {
|
|
3379
|
+
readonly name: string;
|
|
3380
|
+
readonly table: string;
|
|
3381
|
+
readonly invariant: ExpectationInvariant['kind'];
|
|
3382
|
+
readonly severity: ExpectationSeverity;
|
|
3383
|
+
readonly status: ExpectationStatus;
|
|
3384
|
+
/** The measured metric: freshness → age in ms; nullRate / valueBounds(rate) →
|
|
3385
|
+
* a fraction; rowCount → the count; valueBounds(count) → number of bad rows.
|
|
3386
|
+
* `null` when the metric is undefined over the current data. */
|
|
3387
|
+
readonly metric: number | null;
|
|
3388
|
+
/** The threshold the metric is compared against (for display). */
|
|
3389
|
+
readonly threshold: number | null;
|
|
3390
|
+
/** When the expectation entered its current `status`. */
|
|
3391
|
+
readonly since: Date | null;
|
|
3392
|
+
/** When it was last (re-)evaluated. */
|
|
3393
|
+
readonly lastEvaluatedAt: Date | null;
|
|
3394
|
+
/** The most recent violation's provenance, retained across recovery for the
|
|
3395
|
+
* audit trail. `null` if it has never been violated. */
|
|
3396
|
+
readonly lastCause: ExpectationCause | null;
|
|
3397
|
+
readonly description?: string;
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
/** Whether an expectation currently HOLDS. `unknown` = not yet seeded, or a
|
|
3401
|
+
* metric that is undefined over the current data (e.g. freshness of an empty
|
|
3402
|
+
* table — neither fresh nor stale). Transitions are emitted only into/out of
|
|
3403
|
+
* `violated`. */
|
|
3404
|
+
export declare type ExpectationStatus = 'holding' | 'violated' | 'unknown';
|
|
3405
|
+
|
|
3406
|
+
/** Emitted when an expectation crosses the holding/violated boundary. */
|
|
3407
|
+
export declare interface ExpectationTransition {
|
|
3408
|
+
readonly kind: 'violated' | 'recovered';
|
|
3409
|
+
readonly state: ExpectationState;
|
|
3410
|
+
/** The causing write (present for a write-driven violation; `null` for a
|
|
3411
|
+
* clock-driven one or a recovery). */
|
|
3412
|
+
readonly cause: ExpectationCause | null;
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
/** What caused a live recompute — resolved from the CDC `ChangeEvent`. `null`
|
|
3416
|
+
* for a non-write recompute (a reconcile re-seed). */
|
|
3417
|
+
export declare interface ExperimentCause {
|
|
3418
|
+
readonly table: string;
|
|
3419
|
+
readonly op: 'insert' | 'update' | 'delete';
|
|
3420
|
+
/** The trace the write ran under — joins to `voltro logs --trace`. */
|
|
3421
|
+
readonly traceId?: string | null;
|
|
3422
|
+
/** The acting identity behind the write. */
|
|
3423
|
+
readonly subjectId?: string | null;
|
|
3424
|
+
/** The rpc tag of the call that made the write, when carried. */
|
|
3425
|
+
readonly procedure?: string | null;
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
export declare interface ExperimentDefinition {
|
|
3429
|
+
/** Brand marker — lets the cli discriminate default-exported experiment
|
|
3430
|
+
* definitions from arbitrary objects during file discovery. */
|
|
3431
|
+
readonly _voltroExperiment: true;
|
|
3432
|
+
readonly name: string;
|
|
3433
|
+
readonly on: {
|
|
3434
|
+
readonly table: string;
|
|
3435
|
+
readonly where?: (row: Readonly<Record<string, unknown>>) => boolean;
|
|
3436
|
+
};
|
|
3437
|
+
/** Resolved to a function: reads the subject and normalises it to a non-empty
|
|
3438
|
+
* string, or `null` when the row carries no subject. */
|
|
3439
|
+
readonly subject: (row: Readonly<Record<string, unknown>>) => string | null;
|
|
3440
|
+
readonly variants: ReadonlyArray<ExperimentVariant>;
|
|
3441
|
+
/** Resolved holdout fraction in [0, 1); `0` when none. */
|
|
3442
|
+
readonly holdout: number;
|
|
3443
|
+
readonly metric: ExperimentMetric;
|
|
3444
|
+
readonly baseline: string;
|
|
3445
|
+
readonly description?: string;
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
export declare interface ExperimentDefinitionInput {
|
|
3449
|
+
/** Stable id within the app. Unique across all experiments. Also the
|
|
3450
|
+
* assignment SALT, so the same subject buckets independently per experiment. */
|
|
3451
|
+
readonly name: string;
|
|
3452
|
+
/** The watched table. Its CDC deltas drive the live recompute. An optional
|
|
3453
|
+
* `where` narrows the population the experiment observes — a pure predicate
|
|
3454
|
+
* evaluated server-side per row (rows failing it are excluded). It never
|
|
3455
|
+
* leaves the server, so it can be any predicate. */
|
|
3456
|
+
readonly on: {
|
|
3457
|
+
readonly table: string;
|
|
3458
|
+
readonly where?: (row: Readonly<Record<string, unknown>>) => boolean;
|
|
3459
|
+
};
|
|
3460
|
+
/** The stable assignment key extractor. */
|
|
3461
|
+
readonly subject: ExperimentSubject;
|
|
3462
|
+
/** The arms. At least two (a control + one treatment). */
|
|
3463
|
+
readonly variants: ReadonlyArray<ExperimentVariantSpec>;
|
|
3464
|
+
/** Fraction (0..1) of subjects held out ENTIRELY — assigned to the reserved
|
|
3465
|
+
* `holdout` variant and never to a treatment. Omitted ⇒ no holdout. */
|
|
3466
|
+
readonly holdout?: number;
|
|
3467
|
+
/** The success metric maintained per variant. */
|
|
3468
|
+
readonly metric: ExperimentMetric;
|
|
3469
|
+
/** Which variant lift/difference is computed AGAINST. Must name one of
|
|
3470
|
+
* `variants`. Default: the first variant (by convention the control). */
|
|
3471
|
+
readonly baseline?: string;
|
|
3472
|
+
/** Human-facing note surfaced in devtools / the results view. */
|
|
3473
|
+
readonly description?: string;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
/**
|
|
3477
|
+
* Evaluates ONE experiment. Seed once from the base rows, then feed CDC deltas
|
|
3478
|
+
* via `applyChange`; each call re-maintains the per-variant metric + exposure and
|
|
3479
|
+
* recomputes the live result. Returns an `ExperimentUpdate` for any relevant
|
|
3480
|
+
* write (one whose old OR new image is assigned), else `null`.
|
|
3481
|
+
*/
|
|
3482
|
+
export declare class ExperimentEvaluator {
|
|
3483
|
+
private readonly def;
|
|
3484
|
+
private readonly deps;
|
|
3485
|
+
private readonly exposure;
|
|
3486
|
+
private readonly metric;
|
|
3487
|
+
private readonly metricIsCount;
|
|
3488
|
+
private readonly now;
|
|
3489
|
+
private seeded;
|
|
3490
|
+
private lastUpdatedAt;
|
|
3491
|
+
constructor(def: ExperimentDefinition, deps: ExperimentEvaluatorDeps);
|
|
3492
|
+
/** The variant an in-scope row is assigned to, or `null` when the row is out
|
|
3493
|
+
* of the `where` population or carries no subject. */
|
|
3494
|
+
private variantOf;
|
|
3495
|
+
private inScope;
|
|
3496
|
+
/** Seed both maintainers from the current base rows. Never emits (there is no
|
|
3497
|
+
* prior result to compare). */
|
|
3498
|
+
seed(): Promise<void>;
|
|
3499
|
+
/**
|
|
3500
|
+
* Apply one CDC change and recompute. Returns an update when the change was
|
|
3501
|
+
* relevant (assigned on either image), else `null`. A change to a row that is
|
|
3502
|
+
* out of scope on BOTH images is ignored.
|
|
3503
|
+
*/
|
|
3504
|
+
applyChange(event: ChangeEvent): ExperimentUpdate | null;
|
|
3505
|
+
/** The live result — per-variant metric + lift/diff vs the baseline. */
|
|
3506
|
+
result(): ExperimentResult;
|
|
3507
|
+
/** Resolve one variant's metric value, filling the "no group yet" gap: `count`
|
|
3508
|
+
* → 0, `sum` → 0 (empty sum), `avg` / `conversionRate` → `null` (undefined
|
|
3509
|
+
* over no samples). */
|
|
3510
|
+
private metricValueFor;
|
|
3511
|
+
get isSeeded(): boolean;
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
export declare interface ExperimentEvaluatorDeps {
|
|
3515
|
+
/** Read the full current base rows — used to SEED and to reconcile. */
|
|
3516
|
+
readonly queryBase: () => Promise<ReadonlyArray<Row_6>>;
|
|
3517
|
+
/** Wall clock. Injectable for deterministic tests. Default `Date.now`. */
|
|
3518
|
+
readonly now?: () => number;
|
|
3519
|
+
readonly log?: SyncLogger;
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
/** The success metric an experiment maintains PER VARIANT. Every kind reduces to
|
|
3523
|
+
* a bounded IVM aggregate grouped by the assigned variant, so it rides the same
|
|
3524
|
+
* incremental engine as `defineAggregate` (no batch re-scan):
|
|
3525
|
+
*
|
|
3526
|
+
* - `count` — rows assigned to the variant. The metric IS the
|
|
3527
|
+
* exposure (sample size).
|
|
3528
|
+
* - `sum` — Σ of a numeric `column` over the variant's rows.
|
|
3529
|
+
* - `avg` — mean of a numeric `column` — the per-observation
|
|
3530
|
+
* average (revenue-per-order, latency, …). Comparable
|
|
3531
|
+
* across variants of unequal size.
|
|
3532
|
+
* - `conversionRate` — the FRACTION of the variant's rows that "converted":
|
|
3533
|
+
* `row[column]` is truthy, or `=== equals` when given.
|
|
3534
|
+
* Modelled as `avg` of a 0/1 projection, so it too is a
|
|
3535
|
+
* live IVM aggregate. The classic A/B success metric.
|
|
3536
|
+
*
|
|
3537
|
+
* `count` / `sum` reflect BOTH exposure and effect, so their cross-variant
|
|
3538
|
+
* comparison only means "more/less total"; `avg` / `conversionRate` are
|
|
3539
|
+
* per-subject rates and are the ones a lift reads honestly. The result always
|
|
3540
|
+
* carries each variant's `exposure`, so an unequal split is visible either way. */
|
|
3541
|
+
export declare type ExperimentMetric = {
|
|
3542
|
+
readonly kind: 'count';
|
|
3543
|
+
} | {
|
|
3544
|
+
readonly kind: 'sum';
|
|
3545
|
+
readonly column: string;
|
|
3546
|
+
} | {
|
|
3547
|
+
readonly kind: 'avg';
|
|
3548
|
+
readonly column: string;
|
|
3549
|
+
} | {
|
|
3550
|
+
readonly kind: 'conversionRate';
|
|
3551
|
+
readonly column: string;
|
|
3552
|
+
/** When set, a row converted iff `row[column] === equals`. Omitted ⇒
|
|
3553
|
+
* converted iff `row[column]` is truthy. */
|
|
3554
|
+
readonly equals?: unknown;
|
|
3555
|
+
};
|
|
3556
|
+
|
|
3557
|
+
export declare type ExperimentMetricKind = ExperimentMetric['kind'];
|
|
3558
|
+
|
|
3559
|
+
/**
|
|
3560
|
+
* Framework-provided registry of every experiment the cli discovered + the
|
|
3561
|
+
* evaluator maintains. Handler / inspect code reads the live results here,
|
|
3562
|
+
* exactly as it reads `ExpectationRegistry` / `CostRegistry`.
|
|
3563
|
+
*/
|
|
3564
|
+
export declare class ExperimentRegistry extends ExperimentRegistry_base {
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
declare const ExperimentRegistry_base: Context.TagClass<ExperimentRegistry, "@voltro/ExperimentRegistry", ExperimentRegistryApi>;
|
|
3568
|
+
|
|
3569
|
+
export declare interface ExperimentRegistryApi {
|
|
3570
|
+
/** Snapshot every registered experiment's current result — the inspect feed. */
|
|
3571
|
+
readonly snapshot: () => ReadonlyArray<ExperimentResult>;
|
|
3572
|
+
/** One experiment's result by name, or `null` when unknown. */
|
|
3573
|
+
readonly get: (name: string) => ExperimentResult | null;
|
|
3574
|
+
/** Subscribe to live result recomputes. Returns an unsubscribe fn. */
|
|
3575
|
+
readonly subscribe: (listener: (u: ExperimentUpdate) => void) => () => void;
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
/** The live state of one experiment — the row an inspect endpoint / results
|
|
3579
|
+
* dashboard reads. Recomputed on every relevant write. */
|
|
3580
|
+
export declare interface ExperimentResult {
|
|
3581
|
+
readonly name: string;
|
|
3582
|
+
readonly table: string;
|
|
3583
|
+
readonly metric: ExperimentMetricKind;
|
|
3584
|
+
readonly baseline: string;
|
|
3585
|
+
/** Rows assigned to ANY variant (Σ exposure). */
|
|
3586
|
+
readonly totalExposure: number;
|
|
3587
|
+
readonly variants: ReadonlyArray<ExperimentVariantResult>;
|
|
3588
|
+
/** When the result was last recomputed (a relevant write arrived). */
|
|
3589
|
+
readonly lastUpdatedAt: Date | null;
|
|
3590
|
+
readonly description?: string;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
export declare interface ExperimentRunnerHandle {
|
|
3594
|
+
readonly registry: ExperimentRegistryApi;
|
|
3595
|
+
readonly detach: () => void;
|
|
3596
|
+
readonly registered: ReadonlyArray<string>;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
/** How the assignment key is read from a row. A column NAME (its value is the
|
|
3600
|
+
* subject) or a function returning the subject (a composite key, a tenant id,
|
|
3601
|
+
* a hashed cookie). Returning `null` / `undefined` / an empty value ⇒ the row
|
|
3602
|
+
* is UNASSIGNED and excluded from every variant. */
|
|
3603
|
+
export declare type ExperimentSubject = string | ((row: Readonly<Record<string, unknown>>) => string | number | null | undefined);
|
|
3604
|
+
|
|
3605
|
+
/** Emitted whenever an experiment's result recomputes on a relevant write. The
|
|
3606
|
+
* results view subscribes to this to update in real time. */
|
|
3607
|
+
export declare interface ExperimentUpdate {
|
|
3608
|
+
readonly result: ExperimentResult;
|
|
3609
|
+
readonly cause: ExperimentCause | null;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
/** Resolved variant — weight defaulted. */
|
|
3613
|
+
export declare interface ExperimentVariant {
|
|
3614
|
+
readonly name: string;
|
|
3615
|
+
readonly weight: number;
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
/** One variant's live result within an experiment. */
|
|
3619
|
+
export declare interface ExperimentVariantResult {
|
|
3620
|
+
readonly variant: string;
|
|
3621
|
+
/** True for the variant lift/diff is measured against. */
|
|
3622
|
+
readonly isBaseline: boolean;
|
|
3623
|
+
/** True for the reserved holdout arm. */
|
|
3624
|
+
readonly isHoldout: boolean;
|
|
3625
|
+
/** Sample size — rows assigned to this variant (the exposure). */
|
|
3626
|
+
readonly exposure: number;
|
|
3627
|
+
/** The maintained metric value. `null` when the metric is undefined over the
|
|
3628
|
+
* variant's current data (no valid samples for an avg / rate). */
|
|
3629
|
+
readonly metric: number | null;
|
|
3630
|
+
/** Relative lift vs the baseline: `(metric − baseMetric) / baseMetric`. `null`
|
|
3631
|
+
* when either value is null or the baseline metric is 0 (undefined lift).
|
|
3632
|
+
* Always `null` on the baseline row itself. */
|
|
3633
|
+
readonly lift: number | null;
|
|
3634
|
+
/** Absolute difference vs the baseline: `metric − baseMetric`. `null` when
|
|
3635
|
+
* either value is null. Always `null` on the baseline row. */
|
|
3636
|
+
readonly diff: number | null;
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
/** A named arm of the experiment. `weight` skews the split (default 1 = equal);
|
|
3640
|
+
* a subject is assigned in proportion to its weight over the total. */
|
|
3641
|
+
export declare interface ExperimentVariantSpec {
|
|
3642
|
+
readonly name: string;
|
|
3643
|
+
readonly weight?: number;
|
|
3644
|
+
}
|
|
3645
|
+
|
|
2781
3646
|
/** Build an Effect `ExternalSpan` parent from a `traceparent` header so
|
|
2782
3647
|
* inbound HTTP work continues the caller's trace. Returns undefined
|
|
2783
3648
|
* when the header is missing/invalid (no parent → a fresh root span). */
|
|
@@ -2800,6 +3665,20 @@ export declare interface FieldChange {
|
|
|
2800
3665
|
*/
|
|
2801
3666
|
export declare const fingerprintFor: (table: string, predicate: Predicate, indexHint?: string) => string;
|
|
2802
3667
|
|
|
3668
|
+
export declare interface FinopsRunnerHandle {
|
|
3669
|
+
readonly registry: CostRegistryApi;
|
|
3670
|
+
/** The ingestion entry point compute sites call to attribute one unit of
|
|
3671
|
+
* cost. Emits any budget-crossing signals to the registry's subscribers. */
|
|
3672
|
+
readonly record: (event: CostEvent) => void;
|
|
3673
|
+
/** Reset attribution + budgets for one tenant (or all). */
|
|
3674
|
+
readonly reset: (tenantId?: string | null) => void;
|
|
3675
|
+
readonly detach: () => void;
|
|
3676
|
+
readonly registered: ReadonlyArray<string>;
|
|
3677
|
+
/** Test/debug seam: run one clock re-check of the windowed budgets. This is
|
|
3678
|
+
* what the periodic tick timer calls. */
|
|
3679
|
+
readonly runTick: () => void;
|
|
3680
|
+
}
|
|
3681
|
+
|
|
2803
3682
|
export declare interface FluentStore extends Omit<MutationStore, 'update' | 'delete' | 'query'> {
|
|
2804
3683
|
/**
|
|
2805
3684
|
* Diff-based many-to-many link writer for a junction table. `anchor` names the
|
|
@@ -2828,7 +3707,7 @@ export declare interface FluentStore extends Omit<MutationStore, 'update' | 'del
|
|
|
2828
3707
|
* SPI every dialect store implements, and it genuinely does return untyped
|
|
2829
3708
|
* rows off the wire. The type is re-applied here, at the handler boundary.
|
|
2830
3709
|
*/
|
|
2831
|
-
query<R =
|
|
3710
|
+
query<R = Row_7>(descriptor: QueryDescriptor<R>): Promise<ReadonlyArray<R>>;
|
|
2832
3711
|
/**
|
|
2833
3712
|
* Terminal: EXACTLY one row, TYPED. Fails with `NoRowFound` on zero matches
|
|
2834
3713
|
* and equally on two or more.
|
|
@@ -2849,18 +3728,18 @@ export declare interface FluentStore extends Omit<MutationStore, 'update' | 'del
|
|
|
2849
3728
|
* IS `query()` underneath — the probe just asks for one row more than it
|
|
2850
3729
|
* needs so "the first of several" cannot masquerade as "the one you meant".
|
|
2851
3730
|
*/
|
|
2852
|
-
one<R =
|
|
3731
|
+
one<R = Row_7>(query: QueryLike<R>): Promise<R>;
|
|
2853
3732
|
/** Terminal: the first matching row or `null`, TYPED. Use when "any match" is
|
|
2854
3733
|
* genuinely what you mean. */
|
|
2855
|
-
first<R =
|
|
3734
|
+
first<R = Row_7>(query: QueryLike<R>): Promise<R | null>;
|
|
2856
3735
|
/** Alias of `first` — the first matching row or `null`, TYPED. */
|
|
2857
|
-
maybeOne<R =
|
|
3736
|
+
maybeOne<R = Row_7>(query: QueryLike<R>): Promise<R | null>;
|
|
2858
3737
|
/** Fluent, scope-applying read builder: `select('notes').where(...).all()`. */
|
|
2859
3738
|
select(table: string): SelectBuilder;
|
|
2860
3739
|
/** Fluent predicate update: `update('notes').where('id', id).set({...})`. */
|
|
2861
3740
|
update(table: string): UpdateBuilder;
|
|
2862
3741
|
/** Keyed update by primary key — returns the post-image. */
|
|
2863
|
-
update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<
|
|
3742
|
+
update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<Row_7 | null>;
|
|
2864
3743
|
/** Fluent predicate delete: `delete('notes').where('id', id).soft()`. */
|
|
2865
3744
|
delete(table: string): DeleteBuilder;
|
|
2866
3745
|
/** Keyed delete by primary key. */
|
|
@@ -2885,6 +3764,31 @@ export declare interface FluentStoreBackend {
|
|
|
2885
3764
|
readonly delete: DataStore['delete'];
|
|
2886
3765
|
}
|
|
2887
3766
|
|
|
3767
|
+
/**
|
|
3768
|
+
* The columns a given SUBJECT may not see on the wire for `tableName`: the
|
|
3769
|
+
* union of the table's `.serverOnly()` columns (hidden from EVERYONE, so
|
|
3770
|
+
* subject-independent) and its `.readableBy(...)` columns whose scope set the
|
|
3771
|
+
* subject does NOT intersect (Part B — field-level read permissions).
|
|
3772
|
+
*
|
|
3773
|
+
* A subject sees a `.readableBy(...)` column iff it holds AT LEAST ONE of the
|
|
3774
|
+
* declared scopes, checked against its EFFECTIVE scope set (raw subject scopes ∪
|
|
3775
|
+
* rbac role-derived scopes) via `hasEffectiveScope` — so `admin:full` sees every
|
|
3776
|
+
* such column, exactly as it satisfies every guard.
|
|
3777
|
+
*
|
|
3778
|
+
* SUBJECT-INDEPENDENCE is deliberate and load-bearing: when the table declares
|
|
3779
|
+
* NO `.readableBy(...)` column, this returns exactly `serverOnlyColumnNames` —
|
|
3780
|
+
* the same set for every subject — so the Dispatcher's per-subscriber strip
|
|
3781
|
+
* collapses to Part A's behaviour and its read/diff memo still shares one array
|
|
3782
|
+
* across all subscribers of a change. Only a table that actually carries a
|
|
3783
|
+
* `.readableBy(...)` column pays the per-subject cost (different subjects get
|
|
3784
|
+
* different stripped arrays, the same accepted cost `.serverOnly()` already has
|
|
3785
|
+
* whenever it strips anything).
|
|
3786
|
+
*
|
|
3787
|
+
* An unknown / unregistered table forbids nothing (returns `[]`) rather than
|
|
3788
|
+
* throwing — same posture as `serverOnlyColumnNames`.
|
|
3789
|
+
*/
|
|
3790
|
+
export declare const forbiddenColumnsForSubject: (tableName: string, subject: Subject) => ReadonlyArray<string>;
|
|
3791
|
+
|
|
2888
3792
|
/** Forget the calling subject's credential. Returns whether a row was removed. */
|
|
2889
3793
|
export declare const forgetCredential: (store: VaultStore, connectionId: string, subjectId: string) => Promise<boolean>;
|
|
2890
3794
|
|
|
@@ -2988,6 +3892,14 @@ export declare interface GroupState {
|
|
|
2988
3892
|
/** What a user handler may return. */
|
|
2989
3893
|
export declare type HandlerBody = void | Promise<unknown> | Effect.Effect<unknown, unknown, never>;
|
|
2990
3894
|
|
|
3895
|
+
/**
|
|
3896
|
+
* Deterministic hash of a string into the unit interval `[0, 1)` — FNV-1a
|
|
3897
|
+
* (32-bit), no `node:crypto`, so it runs identically on server and client. The
|
|
3898
|
+
* distribution is uniform enough for balanced bucketing; it is NOT a
|
|
3899
|
+
* cryptographic hash and is not meant to be one (an assignment is not a secret).
|
|
3900
|
+
*/
|
|
3901
|
+
export declare const hashUnitInterval: (s: string) => number;
|
|
3902
|
+
|
|
2991
3903
|
/** Define a histogram. `boundaries` default to the framework duration buckets. */
|
|
2992
3904
|
export declare const histogramMetric: (name: string, boundaries?: MetricBoundaries.MetricBoundaries, description?: string) => Metric.Metric.Histogram<number>;
|
|
2993
3905
|
|
|
@@ -2998,6 +3910,12 @@ export declare const histogramMetric: (name: string, boundaries?: MetricBoundari
|
|
|
2998
3910
|
*/
|
|
2999
3911
|
export declare const historyProvenance: (history: ReadonlyArray<RowHistoryEntry>, key: ProvenanceKey) => ProvenanceResult;
|
|
3000
3912
|
|
|
3913
|
+
/** The reserved variant name a `holdout` fraction is assigned to. A holdout is
|
|
3914
|
+
* carved off the TOP of the unit interval BEFORE the weighted split, so it is
|
|
3915
|
+
* never influenced by adding/removing a treatment. No user variant may take
|
|
3916
|
+
* this name when `holdout > 0`. */
|
|
3917
|
+
export declare const HOLDOUT_VARIANT = "holdout";
|
|
3918
|
+
|
|
3001
3919
|
/** A secrets backend over a plain JSON HTTP API — no SDK. Wraps fetch + cache.
|
|
3002
3920
|
* Vault (`/v1/secret/data/...`), Doppler, and the cloud control-plane all fit. */
|
|
3003
3921
|
export declare const httpSecretsBackend: (opts: HttpSecretsOptions) => SecretsBackend;
|
|
@@ -3283,8 +4201,14 @@ export declare const invertChange: (c: ForwardChange) => InverseOp | null;
|
|
|
3283
4201
|
|
|
3284
4202
|
export declare const isAggregateDefinition: (value: unknown) => value is AggregateDefinition;
|
|
3285
4203
|
|
|
4204
|
+
export declare const isCostBudgetDefinition: (value: unknown) => value is CostBudgetDefinition;
|
|
4205
|
+
|
|
3286
4206
|
export declare const isEmptyTenantScopedRead: (descriptor: QueryDescriptor, tenantScopedTables: ReadonlySet<string>, tenantId: string | null | undefined) => boolean;
|
|
3287
4207
|
|
|
4208
|
+
export declare const isExpectationDefinition: (value: unknown) => value is ExpectationDefinition;
|
|
4209
|
+
|
|
4210
|
+
export declare const isExperimentDefinition: (value: unknown) => value is ExperimentDefinition;
|
|
4211
|
+
|
|
3288
4212
|
/** Foreign-key-violation SQLSTATE / errno across dialects (pg 23503, mysql/maria
|
|
3289
4213
|
* 1452, mssql 547, sqlite). Used to turn an opaque tenant-FK SqlError into a
|
|
3290
4214
|
* typed error. */
|
|
@@ -3828,6 +4752,18 @@ export declare const makeQuerySubscriber: <D>(deps: QuerySubscriberDeps<D>) => (
|
|
|
3828
4752
|
|
|
3829
4753
|
export declare const makeRouterActivity: () => RouterActivity;
|
|
3830
4754
|
|
|
4755
|
+
/**
|
|
4756
|
+
* Wrap a transactional store so single-row writes on USER tables are recorded
|
|
4757
|
+
* as their POST-WRITE row. Inserts and updates are captured; a delete removes
|
|
4758
|
+
* the row (there is no post-write state to validate), so — like undo's bulk
|
|
4759
|
+
* boundary — deletes are NOT re-validated in v1. Everything else delegates
|
|
4760
|
+
* untouched. Multiple writes to the same (table, id) keep the latest row.
|
|
4761
|
+
*/
|
|
4762
|
+
export declare const makeRuleCapture: (tx: unknown) => {
|
|
4763
|
+
readonly tx: unknown;
|
|
4764
|
+
drain(): ReadonlyArray<TouchedRow>;
|
|
4765
|
+
};
|
|
4766
|
+
|
|
3831
4767
|
/**
|
|
3832
4768
|
* Build the registry from a list of table descriptors. Cheap — runs once
|
|
3833
4769
|
* at boot. The returned object is read-only.
|
|
@@ -4093,6 +5029,11 @@ export declare interface MutationLike {
|
|
|
4093
5029
|
readonly name: string;
|
|
4094
5030
|
readonly source?: string | ReadonlyArray<string> | undefined;
|
|
4095
5031
|
readonly guards?: ReadonlyArray<AnyCheckSpec> | undefined;
|
|
5032
|
+
readonly target?: {
|
|
5033
|
+
readonly table: string;
|
|
5034
|
+
} | ReadonlyArray<{
|
|
5035
|
+
readonly table: string;
|
|
5036
|
+
}> | undefined;
|
|
4096
5037
|
};
|
|
4097
5038
|
executor(input: unknown, ctx: unknown): unknown;
|
|
4098
5039
|
}
|
|
@@ -4347,9 +5288,13 @@ export declare interface OneShotQueryRunnerDeps<D> extends QueryProducerDeps<D>
|
|
|
4347
5288
|
/**
|
|
4348
5289
|
* Execute a FINALIZED (tenant-scoped) descriptor → rows. On the WS path the
|
|
4349
5290
|
* dispatcher owns this step; a ONE-SHOT read (a `publicApi` REST GET, where
|
|
4350
|
-
* there is no subscription to drive) needs it inline.
|
|
5291
|
+
* there is no subscription to drive) needs it inline. The resolved request
|
|
5292
|
+
* `context` is handed alongside so the caller can apply the SAME
|
|
5293
|
+
* subject-aware wire-column strip the dispatcher does (`.serverOnly()` ∪ the
|
|
5294
|
+
* `.readableBy(...)` columns this subject lacks) — the parity that keeps the
|
|
5295
|
+
* REST projection from shipping a column the socket path would strip.
|
|
4351
5296
|
*/
|
|
4352
|
-
readonly queryRows: (descriptor: D) => Promise<ReadonlyArray<unknown>>;
|
|
5297
|
+
readonly queryRows: (descriptor: D, context: ServeRequestContext) => Promise<ReadonlyArray<unknown>>;
|
|
4353
5298
|
}
|
|
4354
5299
|
|
|
4355
5300
|
/**
|
|
@@ -4606,7 +5551,7 @@ export declare interface PreviewStore {
|
|
|
4606
5551
|
/** Project a stored row into the wire shape. Deliberately total over the
|
|
4607
5552
|
* columns the descriptor declares — and deliberately silent about the token
|
|
4608
5553
|
* columns, which have no wire representation at all. */
|
|
4609
|
-
export declare const projectConnectionState: (definition: ConnectionDefinition, row:
|
|
5554
|
+
export declare const projectConnectionState: (definition: ConnectionDefinition, row: AnyRow_2 | undefined) => ConnectionState;
|
|
4610
5555
|
|
|
4611
5556
|
/** Exact-match property filter. Values are AND'd. */
|
|
4612
5557
|
export declare type PropertyFilter = Readonly<Record<string, string | number | boolean | null>>;
|
|
@@ -4746,7 +5691,7 @@ export declare interface QueryFinalizeOptions {
|
|
|
4746
5691
|
|
|
4747
5692
|
/** A typed builder or its descriptor — the single-row terminals accept either,
|
|
4748
5693
|
* so a call site never has to reach for `.descriptor` just to use them. */
|
|
4749
|
-
export declare type QueryLike<R =
|
|
5694
|
+
export declare type QueryLike<R = Row_7> = QueryDescriptor<R> | {
|
|
4750
5695
|
readonly descriptor: QueryDescriptor<R>;
|
|
4751
5696
|
};
|
|
4752
5697
|
|
|
@@ -5124,6 +6069,8 @@ export declare interface RegistryTableLike {
|
|
|
5124
6069
|
readonly expr: string;
|
|
5125
6070
|
readonly stored: boolean;
|
|
5126
6071
|
};
|
|
6072
|
+
/** `crdtText()` column — server-merged on write (see crdtColumns above). */
|
|
6073
|
+
readonly crdtManaged?: boolean;
|
|
5127
6074
|
}>;
|
|
5128
6075
|
readonly appliedMixins?: ReadonlyArray<{
|
|
5129
6076
|
readonly id?: string;
|
|
@@ -5546,6 +6493,9 @@ export declare interface ResourcePolicy {
|
|
|
5546
6493
|
readonly implies?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
5547
6494
|
}
|
|
5548
6495
|
|
|
6496
|
+
/** The full ordered list of result variants for a definition, holdout last. */
|
|
6497
|
+
export declare const resultVariantNames: (def: ExperimentDefinition) => ReadonlyArray<string>;
|
|
6498
|
+
|
|
5549
6499
|
export { RetentionSpec }
|
|
5550
6500
|
|
|
5551
6501
|
export { retentionTtlMsFromEnv }
|
|
@@ -5605,6 +6555,10 @@ declare type Row_4 = Readonly<Record<string, unknown>>;
|
|
|
5605
6555
|
|
|
5606
6556
|
declare type Row_5 = Readonly<Record<string, unknown>>;
|
|
5607
6557
|
|
|
6558
|
+
declare type Row_6 = Readonly<Record<string, unknown>>;
|
|
6559
|
+
|
|
6560
|
+
declare type Row_7 = Readonly<Record<string, unknown>>;
|
|
6561
|
+
|
|
5608
6562
|
export declare interface RowDiff {
|
|
5609
6563
|
readonly table: string;
|
|
5610
6564
|
readonly op: 'insert' | 'update' | 'delete';
|
|
@@ -6273,6 +7227,12 @@ export declare interface SchemaInfo {
|
|
|
6273
7227
|
* them and the dialect rejects an explicit value.
|
|
6274
7228
|
*/
|
|
6275
7229
|
readonly generatedColumns: ReadonlySet<string>;
|
|
7230
|
+
/**
|
|
7231
|
+
* CRDT-managed columns (`crdtText()`). The MutationStore folds an incoming
|
|
7232
|
+
* encoded update into the stored state (`mergeCrdtStates`) before the write,
|
|
7233
|
+
* so concurrent clients converge. Empty set for the common non-CRDT table.
|
|
7234
|
+
*/
|
|
7235
|
+
readonly crdtColumns: ReadonlySet<string>;
|
|
6276
7236
|
/**
|
|
6277
7237
|
* Columns that a query predicate can hit and benefit from an index:
|
|
6278
7238
|
* - column-level `.index()`-flagged columns
|
|
@@ -6321,6 +7281,10 @@ export declare interface SchemaRegistry {
|
|
|
6321
7281
|
* optional half. */
|
|
6322
7282
|
hasExpires(table: string): boolean;
|
|
6323
7283
|
hasTenant(table: string): boolean;
|
|
7284
|
+
/** True when the table carries `localFirst()` — it is mirrored to the client,
|
|
7285
|
+
* synced bi-directionally, and its `crdtText()` fields converge via CRDT
|
|
7286
|
+
* merge. The discovery signal for the client-sync-set builder. */
|
|
7287
|
+
hasLocalFirst(table: string): boolean;
|
|
6324
7288
|
/** Field-existence check — used to skip stamping a column that the
|
|
6325
7289
|
* table doesn't actually declare (extra defensive). */
|
|
6326
7290
|
hasField(table: string, field: string): boolean;
|
|
@@ -6362,6 +7326,12 @@ export declare interface SchemaRegistry {
|
|
|
6362
7326
|
* Postgres reject an explicit value.
|
|
6363
7327
|
*/
|
|
6364
7328
|
generatedColumns(table: string): ReadonlySet<string>;
|
|
7329
|
+
/**
|
|
7330
|
+
* CRDT-managed columns (`crdtText()`) for a table. Empty set if none.
|
|
7331
|
+
* Read by the MutationStore write path to run the authoritative server-side
|
|
7332
|
+
* CRDT merge (`mergeCrdtStates`) before an INSERT/UPDATE persists.
|
|
7333
|
+
*/
|
|
7334
|
+
crdtColumns(table: string): ReadonlySet<string>;
|
|
6365
7335
|
/**
|
|
6366
7336
|
* Optional pre-INSERT schema decoder for a table. `undefined` when
|
|
6367
7337
|
* the table didn't ship `.validate(schema)`. The MutationStore runs
|
|
@@ -6468,6 +7438,13 @@ export declare interface ServeRequestContext {
|
|
|
6468
7438
|
readonly rowFilter?: RowFilterScope;
|
|
6469
7439
|
}
|
|
6470
7440
|
|
|
7441
|
+
/**
|
|
7442
|
+
* The `.serverOnly()` column names of a table by NAME, or `[]` when the table is
|
|
7443
|
+
* not registered (a computed query, a raw-SQL descriptor, an unknown source) —
|
|
7444
|
+
* an unknown table strips nothing rather than throwing.
|
|
7445
|
+
*/
|
|
7446
|
+
export declare const serverOnlyColumnNames: (tableName: string) => ReadonlyArray<string>;
|
|
7447
|
+
|
|
6471
7448
|
/** One leak: a wire query that declares a serverOnly column in its output. */
|
|
6472
7449
|
export declare interface ServerOnlyLeak {
|
|
6473
7450
|
readonly query: string;
|
|
@@ -6490,6 +7467,18 @@ export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown,
|
|
|
6490
7467
|
readonly subject: unknown;
|
|
6491
7468
|
readonly traceId: string;
|
|
6492
7469
|
readonly spanId?: string;
|
|
7470
|
+
/**
|
|
7471
|
+
* The declared write-target TABLE names of this mutation/action, derived
|
|
7472
|
+
* from the descriptor's `target:` block. Absent when the descriptor
|
|
7473
|
+
* declares no target (and always absent for queries).
|
|
7474
|
+
*
|
|
7475
|
+
* Carried so an interceptor — the audit sink is the motivating consumer —
|
|
7476
|
+
* can see WHICH tables an operation writes without reaching for the
|
|
7477
|
+
* descriptor registry. Purely additive: an interceptor that ignores it is
|
|
7478
|
+
* unaffected. It is the descriptor's DECLARED target, not the rows actually
|
|
7479
|
+
* written; a change-set carry (the rows/ids) is a separate, heavier seam.
|
|
7480
|
+
*/
|
|
7481
|
+
readonly target?: ReadonlyArray<string>;
|
|
6493
7482
|
}) => Effect.Effect<unknown, unknown, never>;
|
|
6494
7483
|
|
|
6495
7484
|
/** Register the process-wide connection resolver (or clear with `undefined`).
|
|
@@ -6780,6 +7769,45 @@ declare const StoreOperationFailed_base: Schema.TaggedErrorClass<StoreOperationF
|
|
|
6780
7769
|
cause: typeof Schema.String;
|
|
6781
7770
|
}>;
|
|
6782
7771
|
|
|
7772
|
+
/**
|
|
7773
|
+
* Drop `columns` from every row. Preserves array AND per-row object identity when
|
|
7774
|
+
* nothing is actually removed: an empty column set returns the input array
|
|
7775
|
+
* unchanged, and a row that carries none of the columns is returned as-is. That
|
|
7776
|
+
* identity preservation is load-bearing for the dispatcher's diff memo, which
|
|
7777
|
+
* keys on `prev` object identity — a gratuitous clone would defeat it.
|
|
7778
|
+
*/
|
|
7779
|
+
export declare const stripColumnsFromRows: <R extends Row>(rows: ReadonlyArray<R>, columns: ReadonlyArray<string>) => ReadonlyArray<R>;
|
|
7780
|
+
|
|
7781
|
+
/**
|
|
7782
|
+
* Strip a source table's columns this SUBJECT may not see — `.serverOnly()` ∪
|
|
7783
|
+
* the `.readableBy(...)` columns the subject lacks a scope for — from a row set
|
|
7784
|
+
* produced for the wire. The subject-aware counterpart of
|
|
7785
|
+
* {@link stripServerOnlyForWire}; the two share the same value-level
|
|
7786
|
+
* {@link stripColumnsFromRows} pass (and its array/row identity preservation),
|
|
7787
|
+
* so a table with no `.serverOnly()` and no `.readableBy(...)` column returns the
|
|
7788
|
+
* input array unchanged for every subject.
|
|
7789
|
+
*
|
|
7790
|
+
* Same ACCEPTED GAP as `stripServerOnlyForWire`: only TOP-LEVEL columns of
|
|
7791
|
+
* `tableName` are matched — an eager-loaded relation or a renamed projection
|
|
7792
|
+
* changes the shape and is not covered here.
|
|
7793
|
+
*/
|
|
7794
|
+
export declare const stripForbiddenForWire: <R extends Row>(tableName: string, rows: ReadonlyArray<R>, subject: Subject) => ReadonlyArray<R>;
|
|
7795
|
+
|
|
7796
|
+
/**
|
|
7797
|
+
* Strip a source table's `.serverOnly()` columns from a row set produced for the
|
|
7798
|
+
* wire. The runtime half of the exposure policy: declare `.serverOnly()` ONCE at
|
|
7799
|
+
* the schema and every query/subscription output respects it, regardless of
|
|
7800
|
+
* whether the handler was hand-written or a `crud.*` helper.
|
|
7801
|
+
*
|
|
7802
|
+
* ACCEPTED GAP (documented): only TOP-LEVEL columns of `tableName` are matched.
|
|
7803
|
+
* An eager-loaded relation nests another table's columns under a relation key,
|
|
7804
|
+
* and a computed/renamed projection changes the shape — neither is covered here
|
|
7805
|
+
* (mark the nested column `.serverOnly()` on ITS table and read it through a
|
|
7806
|
+
* `crud.*`/redacted path, or omit it from the output schema so the boot audit
|
|
7807
|
+
* blocks it). The marker + `crud.*` path covers the common flat-row case.
|
|
7808
|
+
*/
|
|
7809
|
+
export declare const stripServerOnlyForWire: <R extends Row>(tableName: string, rows: ReadonlyArray<R>) => ReadonlyArray<R>;
|
|
7810
|
+
|
|
6783
7811
|
export declare interface SubscribeContext {
|
|
6784
7812
|
/** Logger scoped to the subscriber file (`subscribe:<filename>`). */
|
|
6785
7813
|
readonly log: SyncLogger;
|
|
@@ -7037,6 +8065,22 @@ declare const TableValidationFailed_base: Schema.TaggedErrorClass<TableValidatio
|
|
|
7037
8065
|
}>>;
|
|
7038
8066
|
}>;
|
|
7039
8067
|
|
|
8068
|
+
/** The standing compute-cost attribution for one tenant — the chargeback /
|
|
8069
|
+
* showback row + the "why is my bill high" breakdown. Maintained incrementally:
|
|
8070
|
+
* every `CostEvent` folds into `total`, `byUnit`, and `bySubscription` in O(1).
|
|
8071
|
+
* Cumulative since boot (or since the last `reset(tenantId)`). */
|
|
8072
|
+
export declare interface TenantCostState {
|
|
8073
|
+
readonly tenantId: string | null;
|
|
8074
|
+
/** Grand total across every unit. */
|
|
8075
|
+
readonly total: number;
|
|
8076
|
+
/** Per-unit breakdown (`recompute` → n, `query` → n, …). */
|
|
8077
|
+
readonly byUnit: Readonly<Record<string, number>>;
|
|
8078
|
+
/** Per-subscription breakdown — the per-subscription attribution. Only
|
|
8079
|
+
* subscription-tagged events contribute. */
|
|
8080
|
+
readonly bySubscription: Readonly<Record<string, number>>;
|
|
8081
|
+
readonly lastUpdatedAt: number | null;
|
|
8082
|
+
}
|
|
8083
|
+
|
|
7040
8084
|
/**
|
|
7041
8085
|
* A write was attempted against a `tenant()`-scoped table, but the
|
|
7042
8086
|
* authenticated subject's `tenantId` is null — either anonymous, or
|
|
@@ -7138,6 +8182,14 @@ export declare interface TopNQuery {
|
|
|
7138
8182
|
readonly tenantId?: string | null;
|
|
7139
8183
|
}
|
|
7140
8184
|
|
|
8185
|
+
/** A row a mutation wrote, carried to rule evaluation. `row` is the POST-WRITE
|
|
8186
|
+
* state (insert result / update result). */
|
|
8187
|
+
declare interface TouchedRow {
|
|
8188
|
+
readonly table: string;
|
|
8189
|
+
readonly id: string;
|
|
8190
|
+
readonly row: AnyRow;
|
|
8191
|
+
}
|
|
8192
|
+
|
|
7141
8193
|
export declare interface TraceContext {
|
|
7142
8194
|
readonly traceId: string;
|
|
7143
8195
|
readonly spanId: string;
|
|
@@ -7472,6 +8524,18 @@ export declare const useAggregate: <Row>(def: AggregateDefinition<Row>) => Effec
|
|
|
7472
8524
|
*/
|
|
7473
8525
|
export declare const useAnalytics: () => Effect.Effect<AnalyticsSinkImpl, never, AnalyticsSink>;
|
|
7474
8526
|
|
|
8527
|
+
/** Sugar: read one budget's current state for a tenant from a handler. Returns
|
|
8528
|
+
* `null` when the budget name is not registered (none attached, or a typo). */
|
|
8529
|
+
export declare const useCostBudget: (def: CostBudgetDefinition, tenantId: string | null) => Effect.Effect<CostBudgetState | null, never, CostRegistry>;
|
|
8530
|
+
|
|
8531
|
+
/** Sugar: read one expectation's current state from a handler. Returns `null`
|
|
8532
|
+
* when the name is not registered (no expectations attached, or a typo). */
|
|
8533
|
+
export declare const useExpectation: (def: ExpectationDefinition) => Effect.Effect<ExpectationState | null, never, ExpectationRegistry>;
|
|
8534
|
+
|
|
8535
|
+
/** Sugar: read one experiment's current result from a handler. Returns `null`
|
|
8536
|
+
* when the name is not registered (none attached, or a typo). */
|
|
8537
|
+
export declare const useExperiment: (def: ExperimentDefinition) => Effect.Effect<ExperimentResult | null, never, ExperimentRegistry>;
|
|
8538
|
+
|
|
7475
8539
|
declare type VaultStore = Pick<DataStore, 'query' | 'insert' | 'update' | 'updateMany' | 'delete' | 'upsert'>;
|
|
7476
8540
|
|
|
7477
8541
|
/** The pure core of live revocation: given a subject + action + a row-set,
|
|
@@ -7496,6 +8560,10 @@ export declare const VOLTRO_AUDIT_MIXIN_ID: "voltro/audit";
|
|
|
7496
8560
|
* consumer). Kept in step by `expiresMixinId.test.ts`. */
|
|
7497
8561
|
export declare const VOLTRO_EXPIRES_MIXIN_ID: "voltro/expires";
|
|
7498
8562
|
|
|
8563
|
+
/** Re-declared here for the same reason as the ids above (no mixin import).
|
|
8564
|
+
* Kept in step by `localFirstMixinId.test.ts`. */
|
|
8565
|
+
export declare const VOLTRO_LOCAL_FIRST_MIXIN_ID: "voltro/localFirst";
|
|
8566
|
+
|
|
7499
8567
|
export declare const VOLTRO_SOFT_DELETE_MIXIN_ID: "voltro/softDelete";
|
|
7500
8568
|
|
|
7501
8569
|
export declare const VOLTRO_TENANT_MIXIN_ID: "voltro/tenant";
|