@voltro/runtime 0.27.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 +513 -0
- package/THIRD-PARTY-NOTICES.md +87 -1
- package/dist/index.d.ts +1121 -24
- package/dist/index.js +2192 -1178
- 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.
|
|
@@ -957,7 +960,7 @@ export declare const applyInverses: (store: UndoApplyStore, ops: ReadonlyArray<I
|
|
|
957
960
|
* finds nothing — an orphan rule firing on a row nobody references is the
|
|
958
961
|
* normal case, not an error.
|
|
959
962
|
*/
|
|
960
|
-
export declare const applyPluginRefRules: (store: PluginRefStore, rules: ReadonlyArray<PluginRefRule>, change:
|
|
963
|
+
export declare const applyPluginRefRules: (store: PluginRefStore, rules: ReadonlyArray<PluginRefRule>, change: ChangeEvent) => Promise<ReadonlyArray<{
|
|
961
964
|
readonly rule: PluginRefRule;
|
|
962
965
|
readonly affected: number;
|
|
963
966
|
}>>;
|
|
@@ -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. */
|
|
@@ -3347,7 +4271,7 @@ export declare const isScheduleDefinition: (v: unknown) => v is BrandedScheduleD
|
|
|
3347
4271
|
* The transition matters, not the value: a row that was already deleted and is
|
|
3348
4272
|
* updated again must not re-fire the rule.
|
|
3349
4273
|
*/
|
|
3350
|
-
export declare const isSoftDelete: (change:
|
|
4274
|
+
export declare const isSoftDelete: (change: ChangeEvent) => boolean;
|
|
3351
4275
|
|
|
3352
4276
|
/** A freshly issued key — `token` is shown ONCE and never stored in clear. */
|
|
3353
4277
|
export declare interface IssuedApiKey {
|
|
@@ -3782,6 +4706,10 @@ export declare const makeProcessAdapter: (supervisor: AppSupervisor) => WakeAdap
|
|
|
3782
4706
|
*/
|
|
3783
4707
|
export declare const makeQueryDescriptorProducer: <D>(deps: QueryProducerDeps<D>) => (query: MutationLike, input: unknown) => (requestContext: ServeRequestContext) => D | ComputedQuery | Effect.Effect<D | ComputedQuery, unknown, never>;
|
|
3784
4708
|
|
|
4709
|
+
export declare const makeQueryFinalizer: (options: QueryFinalizeOptions) => ((descriptor: QueryDescriptor, subject: {
|
|
4710
|
+
readonly tenantId?: string | null;
|
|
4711
|
+
}) => QueryDescriptor);
|
|
4712
|
+
|
|
3785
4713
|
/**
|
|
3786
4714
|
* Build the per-subscription authorization re-check.
|
|
3787
4715
|
*
|
|
@@ -3824,6 +4752,18 @@ export declare const makeQuerySubscriber: <D>(deps: QuerySubscriberDeps<D>) => (
|
|
|
3824
4752
|
|
|
3825
4753
|
export declare const makeRouterActivity: () => RouterActivity;
|
|
3826
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
|
+
|
|
3827
4767
|
/**
|
|
3828
4768
|
* Build the registry from a list of table descriptors. Cheap — runs once
|
|
3829
4769
|
* at boot. The returned object is read-only.
|
|
@@ -4089,6 +5029,11 @@ export declare interface MutationLike {
|
|
|
4089
5029
|
readonly name: string;
|
|
4090
5030
|
readonly source?: string | ReadonlyArray<string> | undefined;
|
|
4091
5031
|
readonly guards?: ReadonlyArray<AnyCheckSpec> | undefined;
|
|
5032
|
+
readonly target?: {
|
|
5033
|
+
readonly table: string;
|
|
5034
|
+
} | ReadonlyArray<{
|
|
5035
|
+
readonly table: string;
|
|
5036
|
+
}> | undefined;
|
|
4092
5037
|
};
|
|
4093
5038
|
executor(input: unknown, ctx: unknown): unknown;
|
|
4094
5039
|
}
|
|
@@ -4343,9 +5288,13 @@ export declare interface OneShotQueryRunnerDeps<D> extends QueryProducerDeps<D>
|
|
|
4343
5288
|
/**
|
|
4344
5289
|
* Execute a FINALIZED (tenant-scoped) descriptor → rows. On the WS path the
|
|
4345
5290
|
* dispatcher owns this step; a ONE-SHOT read (a `publicApi` REST GET, where
|
|
4346
|
-
* 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.
|
|
4347
5296
|
*/
|
|
4348
|
-
readonly queryRows: (descriptor: D) => Promise<ReadonlyArray<unknown>>;
|
|
5297
|
+
readonly queryRows: (descriptor: D, context: ServeRequestContext) => Promise<ReadonlyArray<unknown>>;
|
|
4349
5298
|
}
|
|
4350
5299
|
|
|
4351
5300
|
/**
|
|
@@ -4556,18 +5505,6 @@ export declare const payloadPropertyNames: (schema: Schema.Schema.Any) => {
|
|
|
4556
5505
|
readonly optional: ReadonlyArray<string>;
|
|
4557
5506
|
};
|
|
4558
5507
|
|
|
4559
|
-
/** A change as the post-commit channel reports it. */
|
|
4560
|
-
export declare interface PluginRefChange {
|
|
4561
|
-
readonly table: string;
|
|
4562
|
-
readonly op: 'insert' | 'update' | 'delete';
|
|
4563
|
-
readonly rowId: string;
|
|
4564
|
-
readonly tenantId?: string | null;
|
|
4565
|
-
/** The row before, when there was one — used to spot a soft delete. */
|
|
4566
|
-
readonly old?: Record<string, unknown> | null;
|
|
4567
|
-
/** The row after, when there is one. */
|
|
4568
|
-
readonly new?: Record<string, unknown> | null;
|
|
4569
|
-
}
|
|
4570
|
-
|
|
4571
5508
|
/** One resolved rule: "when <targetTable> loses a row, do <policy> to <table.column>". */
|
|
4572
5509
|
export declare interface PluginRefRule {
|
|
4573
5510
|
readonly table: string;
|
|
@@ -4614,7 +5551,7 @@ export declare interface PreviewStore {
|
|
|
4614
5551
|
/** Project a stored row into the wire shape. Deliberately total over the
|
|
4615
5552
|
* columns the descriptor declares — and deliberately silent about the token
|
|
4616
5553
|
* columns, which have no wire representation at all. */
|
|
4617
|
-
export declare const projectConnectionState: (definition: ConnectionDefinition, row:
|
|
5554
|
+
export declare const projectConnectionState: (definition: ConnectionDefinition, row: AnyRow_2 | undefined) => ConnectionState;
|
|
4618
5555
|
|
|
4619
5556
|
/** Exact-match property filter. Values are AND'd. */
|
|
4620
5557
|
export declare type PropertyFilter = Readonly<Record<string, string | number | boolean | null>>;
|
|
@@ -4715,9 +5652,46 @@ export declare const publishEvent: <Name extends string, Key extends Schema.Sche
|
|
|
4715
5652
|
readonly deferred: boolean;
|
|
4716
5653
|
}, EventPublishError>;
|
|
4717
5654
|
|
|
5655
|
+
/**
|
|
5656
|
+
* The two scopes, composed once, for every path that finalises a descriptor.
|
|
5657
|
+
*
|
|
5658
|
+
* **The primitives above were extracted so there is "no second copy that could
|
|
5659
|
+
* drift" — and then the COMPOSITION became the second copy.** `voltro dev`
|
|
5660
|
+
* wrapped them in a local helper; `serveApi` inlined the same pair in the other
|
|
5661
|
+
* spelling. They agreed, which is the dangerous state: nothing kept them
|
|
5662
|
+
* agreeing, and adding a third scoping concern would have landed in whichever
|
|
5663
|
+
* file the author had open.
|
|
5664
|
+
*
|
|
5665
|
+
* That is the shape this repo has shipped seven times and the one a
|
|
5666
|
+
* source-reading parity guard cannot see — both paths supply something, both
|
|
5667
|
+
* look right at their own call site, and they disagree about content. The fix is
|
|
5668
|
+
* not detection. It is having one function, so the disagreement has nowhere to
|
|
5669
|
+
* live.
|
|
5670
|
+
*
|
|
5671
|
+
* `observe` is where the paths are ALLOWED to differ: `voltro dev` warns about
|
|
5672
|
+
* an org-less subject, an empty tenant scope and a non-indexed predicate, and
|
|
5673
|
+
* production does not want any of that on the hot path. A hook that cannot
|
|
5674
|
+
* change the descriptor keeps the difference to the part that is genuinely
|
|
5675
|
+
* dev-only. It receives a COPY of the descriptor: a hook that could rewrite the
|
|
5676
|
+
* query would let the two paths disagree again through the one seam built to
|
|
5677
|
+
* stop them.
|
|
5678
|
+
*/
|
|
5679
|
+
export declare interface QueryFinalizeOptions {
|
|
5680
|
+
readonly tenantScopedTables: ReadonlySet<string>;
|
|
5681
|
+
readonly softDeleteScopedTables: ReadonlySet<string>;
|
|
5682
|
+
readonly observe?: {
|
|
5683
|
+
/** Before scoping — sees what the handler asked for. */
|
|
5684
|
+
readonly beforeScope?: (descriptor: QueryDescriptor, subject: {
|
|
5685
|
+
readonly tenantId?: string | null;
|
|
5686
|
+
}) => void;
|
|
5687
|
+
/** After scoping — sees what will actually run. */
|
|
5688
|
+
readonly afterScope?: (descriptor: QueryDescriptor) => void;
|
|
5689
|
+
};
|
|
5690
|
+
}
|
|
5691
|
+
|
|
4718
5692
|
/** A typed builder or its descriptor — the single-row terminals accept either,
|
|
4719
5693
|
* so a call site never has to reach for `.descriptor` just to use them. */
|
|
4720
|
-
export declare type QueryLike<R =
|
|
5694
|
+
export declare type QueryLike<R = Row_7> = QueryDescriptor<R> | {
|
|
4721
5695
|
readonly descriptor: QueryDescriptor<R>;
|
|
4722
5696
|
};
|
|
4723
5697
|
|
|
@@ -5095,6 +6069,8 @@ export declare interface RegistryTableLike {
|
|
|
5095
6069
|
readonly expr: string;
|
|
5096
6070
|
readonly stored: boolean;
|
|
5097
6071
|
};
|
|
6072
|
+
/** `crdtText()` column — server-merged on write (see crdtColumns above). */
|
|
6073
|
+
readonly crdtManaged?: boolean;
|
|
5098
6074
|
}>;
|
|
5099
6075
|
readonly appliedMixins?: ReadonlyArray<{
|
|
5100
6076
|
readonly id?: string;
|
|
@@ -5517,6 +6493,9 @@ export declare interface ResourcePolicy {
|
|
|
5517
6493
|
readonly implies?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
5518
6494
|
}
|
|
5519
6495
|
|
|
6496
|
+
/** The full ordered list of result variants for a definition, holdout last. */
|
|
6497
|
+
export declare const resultVariantNames: (def: ExperimentDefinition) => ReadonlyArray<string>;
|
|
6498
|
+
|
|
5520
6499
|
export { RetentionSpec }
|
|
5521
6500
|
|
|
5522
6501
|
export { retentionTtlMsFromEnv }
|
|
@@ -5576,6 +6555,10 @@ declare type Row_4 = Readonly<Record<string, unknown>>;
|
|
|
5576
6555
|
|
|
5577
6556
|
declare type Row_5 = Readonly<Record<string, unknown>>;
|
|
5578
6557
|
|
|
6558
|
+
declare type Row_6 = Readonly<Record<string, unknown>>;
|
|
6559
|
+
|
|
6560
|
+
declare type Row_7 = Readonly<Record<string, unknown>>;
|
|
6561
|
+
|
|
5579
6562
|
export declare interface RowDiff {
|
|
5580
6563
|
readonly table: string;
|
|
5581
6564
|
readonly op: 'insert' | 'update' | 'delete';
|
|
@@ -5771,7 +6754,7 @@ export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
|
|
|
5771
6754
|
* Exported separately from the execution so the matching is testable without a
|
|
5772
6755
|
* store: which rules fire is the part with the edge cases.
|
|
5773
6756
|
*/
|
|
5774
|
-
export declare const rulesFor: (rules: ReadonlyArray<PluginRefRule>, change:
|
|
6757
|
+
export declare const rulesFor: (rules: ReadonlyArray<PluginRefRule>, change: ChangeEvent) => ReadonlyArray<PluginRefRule>;
|
|
5775
6758
|
|
|
5776
6759
|
/**
|
|
5777
6760
|
* Run `fn` with `SubjectService` bound to a `system` subject and a
|
|
@@ -6244,6 +7227,12 @@ export declare interface SchemaInfo {
|
|
|
6244
7227
|
* them and the dialect rejects an explicit value.
|
|
6245
7228
|
*/
|
|
6246
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>;
|
|
6247
7236
|
/**
|
|
6248
7237
|
* Columns that a query predicate can hit and benefit from an index:
|
|
6249
7238
|
* - column-level `.index()`-flagged columns
|
|
@@ -6292,6 +7281,10 @@ export declare interface SchemaRegistry {
|
|
|
6292
7281
|
* optional half. */
|
|
6293
7282
|
hasExpires(table: string): boolean;
|
|
6294
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;
|
|
6295
7288
|
/** Field-existence check — used to skip stamping a column that the
|
|
6296
7289
|
* table doesn't actually declare (extra defensive). */
|
|
6297
7290
|
hasField(table: string, field: string): boolean;
|
|
@@ -6333,6 +7326,12 @@ export declare interface SchemaRegistry {
|
|
|
6333
7326
|
* Postgres reject an explicit value.
|
|
6334
7327
|
*/
|
|
6335
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>;
|
|
6336
7335
|
/**
|
|
6337
7336
|
* Optional pre-INSERT schema decoder for a table. `undefined` when
|
|
6338
7337
|
* the table didn't ship `.validate(schema)`. The MutationStore runs
|
|
@@ -6439,6 +7438,13 @@ export declare interface ServeRequestContext {
|
|
|
6439
7438
|
readonly rowFilter?: RowFilterScope;
|
|
6440
7439
|
}
|
|
6441
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
|
+
|
|
6442
7448
|
/** One leak: a wire query that declares a serverOnly column in its output. */
|
|
6443
7449
|
export declare interface ServerOnlyLeak {
|
|
6444
7450
|
readonly query: string;
|
|
@@ -6461,6 +7467,18 @@ export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown,
|
|
|
6461
7467
|
readonly subject: unknown;
|
|
6462
7468
|
readonly traceId: string;
|
|
6463
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>;
|
|
6464
7482
|
}) => Effect.Effect<unknown, unknown, never>;
|
|
6465
7483
|
|
|
6466
7484
|
/** Register the process-wide connection resolver (or clear with `undefined`).
|
|
@@ -6751,6 +7769,45 @@ declare const StoreOperationFailed_base: Schema.TaggedErrorClass<StoreOperationF
|
|
|
6751
7769
|
cause: typeof Schema.String;
|
|
6752
7770
|
}>;
|
|
6753
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
|
+
|
|
6754
7811
|
export declare interface SubscribeContext {
|
|
6755
7812
|
/** Logger scoped to the subscriber file (`subscribe:<filename>`). */
|
|
6756
7813
|
readonly log: SyncLogger;
|
|
@@ -7008,6 +8065,22 @@ declare const TableValidationFailed_base: Schema.TaggedErrorClass<TableValidatio
|
|
|
7008
8065
|
}>>;
|
|
7009
8066
|
}>;
|
|
7010
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
|
+
|
|
7011
8084
|
/**
|
|
7012
8085
|
* A write was attempted against a `tenant()`-scoped table, but the
|
|
7013
8086
|
* authenticated subject's `tenantId` is null — either anonymous, or
|
|
@@ -7109,6 +8182,14 @@ export declare interface TopNQuery {
|
|
|
7109
8182
|
readonly tenantId?: string | null;
|
|
7110
8183
|
}
|
|
7111
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
|
+
|
|
7112
8193
|
export declare interface TraceContext {
|
|
7113
8194
|
readonly traceId: string;
|
|
7114
8195
|
readonly spanId: string;
|
|
@@ -7443,6 +8524,18 @@ export declare const useAggregate: <Row>(def: AggregateDefinition<Row>) => Effec
|
|
|
7443
8524
|
*/
|
|
7444
8525
|
export declare const useAnalytics: () => Effect.Effect<AnalyticsSinkImpl, never, AnalyticsSink>;
|
|
7445
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
|
+
|
|
7446
8539
|
declare type VaultStore = Pick<DataStore, 'query' | 'insert' | 'update' | 'updateMany' | 'delete' | 'upsert'>;
|
|
7447
8540
|
|
|
7448
8541
|
/** The pure core of live revocation: given a subject + action + a row-set,
|
|
@@ -7467,6 +8560,10 @@ export declare const VOLTRO_AUDIT_MIXIN_ID: "voltro/audit";
|
|
|
7467
8560
|
* consumer). Kept in step by `expiresMixinId.test.ts`. */
|
|
7468
8561
|
export declare const VOLTRO_EXPIRES_MIXIN_ID: "voltro/expires";
|
|
7469
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
|
+
|
|
7470
8567
|
export declare const VOLTRO_SOFT_DELETE_MIXIN_ID: "voltro/softDelete";
|
|
7471
8568
|
|
|
7472
8569
|
export declare const VOLTRO_TENANT_MIXIN_ID: "voltro/tenant";
|