@voltro/runtime 0.64.0 → 0.66.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/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ import { ConnectionCredential } from '@voltro/protocol';
11
11
  import { ConnectionInfo } from '@voltro/protocol';
12
12
  import { ConnectionInfoMiddleware } from '@voltro/protocol';
13
13
  import { ConnectionKind } from '@voltro/protocol';
14
- import { ConnectionState } from '@voltro/protocol';
14
+ import { ConnectionState as ConnectionState_2 } from '@voltro/protocol';
15
15
  import { ConstraintFacts } from '@voltro/database';
16
16
  import { ConstraintKind } from '@voltro/database';
17
17
  import { ConstraintViolation } from '@voltro/protocol';
@@ -57,6 +57,8 @@ import { listRetentions } from '@voltro/database';
57
57
  import { Metric } from 'effect';
58
58
  import { MetricBoundaries } from 'effect';
59
59
  import { MetricReader } from '@opentelemetry/sdk-metrics';
60
+ import { MutationIdempotencyScope } from '@voltro/protocol';
61
+ import { NoRowFound } from '@voltro/protocol';
60
62
  import { PendingApproval } from '@voltro/protocol';
61
63
  import { PluginHttpRoute } from '@voltro/protocol';
62
64
  import { PluginRefOrphanPolicy } from '@voltro/database';
@@ -71,6 +73,7 @@ import { RetentionSource } from '@voltro/database';
71
73
  import { RetentionSpec } from '@voltro/database';
72
74
  import { retentionTtlMsFromEnv } from '@voltro/database';
73
75
  import { Row } from '@voltro/database';
76
+ import { RowKey } from '@voltro/database';
74
77
  import { RowPatch } from '@voltro/protocol';
75
78
  import { Rpc } from '@effect/rpc';
76
79
  import { RpcGroup } from '@effect/rpc';
@@ -102,6 +105,7 @@ import { TenantRowNotFound } from '@voltro/protocol';
102
105
  import { TenantScopeViolation } from '@voltro/protocol';
103
106
  import { Tracer } from 'effect';
104
107
  import { Unauthenticated } from '@voltro/protocol';
108
+ import { UpsertOptions } from '@voltro/database';
105
109
  import { ValidationError } from '@voltro/protocol';
106
110
  import { ValidationErrors } from '@voltro/protocol';
107
111
  import { VersionConflict } from '@voltro/database';
@@ -1025,6 +1029,10 @@ export declare interface AppContext {
1025
1029
  */
1026
1030
  readonly storeForTenant: (tenantId: string) => FluentStore;
1027
1031
  readonly request: RuntimeContext;
1032
+ /** Ephemeral server-owned state of this RPC connection. Absent for internal
1033
+ * calls; mutation writes commit with the operation and disconnect clears it.
1034
+ * It grants no authority until the app explicitly records its own decision. */
1035
+ readonly connectionState?: ConnectionState;
1028
1036
  /** Typed authorization slice — the caller's effective scopes. Use
1029
1037
  * `ctx.access.has(scope)` / `yield* ctx.access.require(scope)` instead of
1030
1038
  * reaching into `ctx.request.subject.scopes`. Always present. */
@@ -1649,7 +1657,7 @@ export declare const bindMutation: <Input, Output, E = never>(execute: (input: I
1649
1657
  * schema keeps exactly the old behaviour rather than collapsing errors it
1650
1658
  * cannot classify.
1651
1659
  */
1652
- declaredError?: Schema.Schema.All) => Effect.Effect<Output, E, SubjectService | ConnectionInfo>;
1660
+ declaredError?: Schema.Schema.All, idempotencyScope?: MutationIdempotencyScope) => Effect.Effect<Output, E, SubjectService | ConnectionInfo>;
1653
1661
 
1654
1662
  /**
1655
1663
  * Bind a non-reactive server→client stream (A3). The executor builds a
@@ -2295,7 +2303,7 @@ export declare interface ConnectionsFacade {
2295
2303
  tryGet(connectionId: string): Promise<ResolvedConnection | null>;
2296
2304
  /** Every declared connection with the caller's status — the same projection
2297
2305
  * `__voltro.connections.list` serves, for server-side rendering. */
2298
- list(): Promise<ReadonlyArray<ConnectionState>>;
2306
+ list(): Promise<ReadonlyArray<ConnectionState_2>>;
2299
2307
  }
2300
2308
 
2301
2309
  export declare interface ConnectionsFacadeDeps {
@@ -2304,6 +2312,19 @@ export declare interface ConnectionsFacadeDeps {
2304
2312
  readonly subject: Subject;
2305
2313
  }
2306
2314
 
2315
+ /** Server-local application state for ONE RPC connection. Not authentication,
2316
+ * a presence roster or durable storage. Mutation writes become visible only
2317
+ * after commit; reads always see committed state, including in the writer.
2318
+ * Values must be structured-cloneable and are copied on write and read so a
2319
+ * mutable alias cannot bypass the transaction boundary.
2320
+ * Disconnect clears everything and a reconnect starts empty. */
2321
+ export declare interface ConnectionState {
2322
+ readonly get: (key: string) => unknown;
2323
+ readonly has: (key: string) => boolean;
2324
+ readonly set: (key: string, value: unknown) => void;
2325
+ readonly delete: (key: string) => void;
2326
+ }
2327
+
2307
2328
  /** Test-only — number of streams currently registered for a client. */
2308
2329
  export declare const _connectionStreamCount: (clientId: number) => number;
2309
2330
 
@@ -3644,7 +3665,7 @@ export declare interface EffectStoreOps {
3644
3665
  readonly query: <R = Row>(descriptor: QueryDescriptor<R>) => Effect.Effect<ReadonlyArray<R>, StoreError>;
3645
3666
  readonly insert: (table: string, row: Row) => Effect.Effect<Row, StoreError>;
3646
3667
  readonly insertMany: (table: string, rows: ReadonlyArray<Row>) => Effect.Effect<ReadonlyArray<Row>, StoreError>;
3647
- readonly patchJson: (table: string, primaryKey: string, path: string, value: unknown) => Effect.Effect<Row | null, StoreError>;
3668
+ readonly patchJson: (table: string, primaryKey: string, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>) => Effect.Effect<Row | null, StoreError>;
3648
3669
  readonly update: (table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>) => Effect.Effect<Row | null, StoreError>;
3649
3670
  readonly updateMany: (table: string, patch: Readonly<Record<string, unknown>>, options: {
3650
3671
  readonly where: Predicate;
@@ -3653,13 +3674,31 @@ export declare interface EffectStoreOps {
3653
3674
  readonly deleteMany: (table: string, options: {
3654
3675
  readonly where: Predicate;
3655
3676
  }) => Effect.Effect<number, StoreError>;
3656
- readonly upsert: (table: string, row: Row, options: {
3657
- readonly conflictColumns: ReadonlyArray<string>;
3658
- readonly update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
3659
- }) => Effect.Effect<Row, StoreError>;
3677
+ readonly upsert: (table: string, row: Row, options: UpsertOptions) => Effect.Effect<Row, StoreError>;
3660
3678
  readonly insertIgnore: (table: string, row: Row, options: {
3661
3679
  readonly conflictColumns: ReadonlyArray<string>;
3662
3680
  }) => Effect.Effect<Row, StoreError>;
3681
+ /**
3682
+ * The single-row reads, on the typed channel — the same three `ctx.store`
3683
+ * offers, taking a builder or a descriptor.
3684
+ *
3685
+ * Their absence made two doctor rules mutually unsatisfiable: `row-not-found`
3686
+ * says reach for `.maybeOne(builder)`, `store-effect-lift` says reach for
3687
+ * `yield* EffectStore`, and `EffectStoreOps` had no `.maybeOne`. An app
3688
+ * following both got `Property 'maybeOne' does not exist on type
3689
+ * 'EffectStoreOps'`. Nothing was hard about it: the object this service
3690
+ * closes over has always been the full `FluentStore` — only the declared type
3691
+ * was narrower, and every call site was already casting to say so.
3692
+ *
3693
+ * `one` fails with `NoRowFound` on its own channel rather than collapsing
3694
+ * into `StoreOperationFailed`, for the reason the catch boundary gives about
3695
+ * `TenantRowNotFound`: stringifying a deliberate, catchable refusal is how it
3696
+ * stops being one. There is no `onMissing` here — `Effect.catchTag` says the
3697
+ * same thing and keeps the failure channel exact.
3698
+ */
3699
+ readonly one: <R = Row>(query: QueryLike<R>) => Effect.Effect<R, StoreError | NoRowFound>;
3700
+ readonly maybeOne: <R = Row>(query: QueryLike<R>) => Effect.Effect<R | null, StoreError>;
3701
+ readonly first: <R = Row>(query: QueryLike<R>) => Effect.Effect<R | null, StoreError>;
3663
3702
  /** Bypass soft-delete. See `MutationStore.hardDelete`. */
3664
3703
  readonly hardDelete: (table: string, primaryKey: string) => Effect.Effect<boolean, StoreError>;
3665
3704
  /** Run one Effect program atomically against the transaction-scoped store.
@@ -4804,11 +4843,11 @@ export declare interface FluentStore extends Omit<MutationStore, 'update' | 'del
4804
4843
  /** Fluent predicate update: `update('notes').where('id', id).set({...})`. */
4805
4844
  update(table: string): UpdateBuilder;
4806
4845
  /** Keyed update by primary key — returns the post-image. */
4807
- update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<Row_7 | null>;
4846
+ update(table: string, primaryKey: RowKey, patch: Readonly<Record<string, unknown>>): Promise<Row_7 | null>;
4808
4847
  /** Fluent predicate delete: `delete('notes').where('id', id).soft()`. */
4809
4848
  delete(table: string): DeleteBuilder;
4810
4849
  /** Keyed delete by primary key. */
4811
- delete(table: string, primaryKey: string): Promise<boolean>;
4850
+ delete(table: string, primaryKey: RowKey): Promise<boolean>;
4812
4851
  /**
4813
4852
  * Build an update patch from `input`, keeping ONLY the listed `keys` whose
4814
4853
  * value the caller actually PROVIDED (`!== undefined`). Collapses the
@@ -4933,6 +4972,10 @@ export declare const getResourcePolicy: (resourceType: string) => ResourcePolicy
4933
4972
  /** The registered row filter, or `undefined`. */
4934
4973
  export declare const getRowFilter: () => RowFilter<never> | undefined;
4935
4974
 
4975
+ /** Read the live server-owned connection for an opaque runtime clientId.
4976
+ * Missing for internal invocations and after transport teardown. */
4977
+ export declare const getRpcConnection: (clientId: number) => RpcConnection | undefined;
4978
+
4936
4979
  /**
4937
4980
  * Read the currently-registered process-wide handle (`undefined` when none
4938
4981
  * is registered — the state in which `runAsSystem` refuses).
@@ -5117,19 +5160,16 @@ export declare class InMemoryDataStore implements DataStore {
5117
5160
  query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
5118
5161
  insert(table: string, row: Row): Promise<Row>;
5119
5162
  insertMany(table: string, rows: ReadonlyArray<Row>): Promise<ReadonlyArray<Row>>;
5120
- patchJson(table: string, primaryKey: string, path: string, value: unknown): Promise<Row | null>;
5121
- update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<Row | null>;
5122
- delete(table: string, primaryKey: string): Promise<boolean>;
5163
+ patchJson(table: string, primaryKey: RowKey, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>): Promise<Row | null>;
5164
+ update(table: string, primaryKey: RowKey, patch: Readonly<Record<string, unknown>>): Promise<Row | null>;
5165
+ delete(table: string, primaryKey: RowKey): Promise<boolean>;
5123
5166
  updateMany(table: string, patch: Readonly<Record<string, unknown>>, options: {
5124
5167
  where: Predicate;
5125
5168
  }): Promise<number>;
5126
5169
  deleteMany(table: string, options: {
5127
5170
  where: Predicate;
5128
5171
  }): Promise<number>;
5129
- upsert(table: string, row: Row, options: {
5130
- conflictColumns: ReadonlyArray<string>;
5131
- update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
5132
- }): Promise<Row>;
5172
+ upsert(table: string, row: Row, options: UpsertOptions): Promise<Row>;
5133
5173
  insertIgnore(table: string, row: Row, options: {
5134
5174
  conflictColumns: ReadonlyArray<string>;
5135
5175
  }): Promise<Row>;
@@ -5561,7 +5601,7 @@ export declare class LinearScanMatcher implements Matcher {
5561
5601
  }
5562
5602
 
5563
5603
  /** Every connection the app declares, projected for ONE subject. */
5564
- export declare const listConnectionStates: (store: VaultStore, registry: ConnectionRegistry, subjectId: string) => Promise<ReadonlyArray<ConnectionState>>;
5604
+ export declare const listConnectionStates: (store: VaultStore, registry: ConnectionRegistry, subjectId: string) => Promise<ReadonlyArray<ConnectionState_2>>;
5565
5605
 
5566
5606
  export declare const listResourcePolicies: () => ReadonlyArray<ResourcePolicy>;
5567
5607
 
@@ -5795,6 +5835,12 @@ export declare const makeConnectionResolver: (deps: {
5795
5835
 
5796
5836
  export declare const makeConnectionsFacade: (deps: ConnectionsFacadeDeps) => ConnectionsFacade;
5797
5837
 
5838
+ /** Host seam shared by dev/serve and testing. Pass the mutation lifecycle's
5839
+ * existing afterCommit collector; rolled-back/retried bodies must discard it.
5840
+ * Outside a transaction writes are immediate. Values are server-owned; do not
5841
+ * put a client payload here as an authority claim. */
5842
+ export declare const makeConnectionState: (connection: RpcConnection, afterCommit?: (work: () => Promise<unknown>) => void) => ConnectionState;
5843
+
5798
5844
  /**
5799
5845
  * Factory the CLI binds into the plugin bind-ctx: closes over the
5800
5846
  * process-wide coordinator + replicaId so a plugin calls the ergonomic
@@ -5841,7 +5887,7 @@ export declare const makeDisconnectConnectionExecutor: (deps: ConnectionBuiltinD
5841
5887
  * dev.ts (CLI) and any standalone test setup uses this to wire up the
5842
5888
  * tag before running the user's Effect.
5843
5889
  */
5844
- export declare const makeEffectStoreLayer: (store: MutationStore) => Layer.Layer<EffectStore>;
5890
+ export declare const makeEffectStoreLayer: (store: FluentStore) => Layer.Layer<EffectStore>;
5845
5891
 
5846
5892
  export declare const makeEventPublisher: (ctx: EventPublishContext) => EventPublisher;
5847
5893
 
@@ -5869,7 +5915,7 @@ export declare const makeIdleGate: (graceMs: number, now?: () => number) => Idle
5869
5915
  export declare const makeLazyWorkflowFacade: (resolve: () => WorkflowsAppContext | undefined) => WorkflowsAppContext;
5870
5916
 
5871
5917
  /** `__voltro.connections.list` — every declared connection, for the caller. */
5872
- export declare const makeListConnectionsExecutor: (deps: ConnectionBuiltinDeps) => (_input: Record<string, never>, ctx: ConnectionExecutorCtx) => Promise<ReadonlyArray<ConnectionState>>;
5918
+ export declare const makeListConnectionsExecutor: (deps: ConnectionBuiltinDeps) => (_input: Record<string, never>, ctx: ConnectionExecutorCtx) => Promise<ReadonlyArray<ConnectionState_2>>;
5873
5919
 
5874
5920
  /**
5875
5921
  * Build the replay codec from a mutation's `descriptor.output` Schema (call it at
@@ -6467,7 +6513,7 @@ export declare interface MutationRunnerDeps {
6467
6513
  export declare interface MutationStore extends DataStore {
6468
6514
  /** Bypass auto-soft-delete. Hard-removes the row regardless of
6469
6515
  * whether the table has `softDelete()` applied. */
6470
- hardDelete(table: string, primaryKey: string): Promise<boolean>;
6516
+ hardDelete(table: string, primaryKey: RowKey): Promise<boolean>;
6471
6517
  }
6472
6518
 
6473
6519
  export declare type NegotiatedEncoding = 'br' | 'gzip';
@@ -6477,6 +6523,25 @@ export declare type NegotiatedEncoding = 'br' | 'gzip';
6477
6523
  * (a client sending both accepts both). */
6478
6524
  export declare const negotiateEncoding: (acceptEncoding: string | undefined) => NegotiatedEncoding | undefined;
6479
6525
 
6526
+ /**
6527
+ * A fresh trace id for work that has NO incoming span to continue — a webhook
6528
+ * delivery, a schedule firing, a devtools invoke, a REST or gRPC entry point.
6529
+ *
6530
+ * 128 random bits as 32 lowercase hex, the shape `TRACEPARENT_RE` above accepts
6531
+ * and the shape a collector, a log pipeline or `voltro traces` can join on.
6532
+ *
6533
+ * The reason this exists as ONE function: every entry point that minted its own
6534
+ * had drifted somewhere different, and three of the shapes were not ids at all.
6535
+ * `incoming:<webhookId>`, `reaction:<workflow>` and the literal
6536
+ * `'webhooks:trigger'` are CONSTANTS — they were the caller's `source` label
6537
+ * copied into the id field, so every delivery a process ever handled shared one
6538
+ * trace and the waterfall merged unrelated work. `public-`/`grpc-`/`rest-` plus
6539
+ * eight base36 characters carried ~41 bits, which collides long before a busy
6540
+ * day is over. The entry-point kind is not lost by fixing this: it lives in the
6541
+ * `source` field beside the id, which is where it was already being written.
6542
+ */
6543
+ export declare const newTraceId: () => string;
6544
+
6480
6545
  /**
6481
6546
  * The backoff curve, extracted so it can be asserted directly — a schedule
6482
6547
  * that backs off wrongly is otherwise only visible as a latency an integration
@@ -6583,24 +6648,7 @@ export declare const normalizeIp: (raw: string) => string | undefined;
6583
6648
  * never accidentally compare equal to a real one. */
6584
6649
  export declare const normalizeOrigin: (raw: string) => string;
6585
6650
 
6586
- /**
6587
- * `.one()` matched a number of rows other than exactly one.
6588
- *
6589
- * `found` distinguishes the two failure modes without a second error type:
6590
- * `0` is "the row you required is missing", `2` is "your filter is not as
6591
- * unique as you assumed" (the terminal probes with LIMIT 2, so `2` means
6592
- * "at least two" — it never counts the whole set just to report a number).
6593
- */
6594
- export declare class NoRowFound extends NoRowFound_base {
6595
- get message(): string;
6596
- }
6597
-
6598
- declare const NoRowFound_base: Schema.TaggedErrorClass<NoRowFound, "NoRowFound", {
6599
- readonly _tag: Schema.tag<"NoRowFound">;
6600
- } & {
6601
- table: typeof Schema.String;
6602
- found: typeof Schema.Number;
6603
- }>;
6651
+ export { NoRowFound }
6604
6652
 
6605
6653
  /** What `.one()` hands an `onMissing` factory — the two facts `NoRowFound` carries. */
6606
6654
  export declare interface NoRowFoundInfo {
@@ -7084,7 +7132,7 @@ export declare interface PreviewStore {
7084
7132
  /** Project a stored row into the wire shape. Deliberately total over the
7085
7133
  * columns the descriptor declares — and deliberately silent about the token
7086
7134
  * columns, which have no wire representation at all. */
7087
- export declare const projectConnectionState: (definition: ConnectionDefinition, row: AnyRow_2 | undefined) => ConnectionState;
7135
+ export declare const projectConnectionState: (definition: ConnectionDefinition, row: AnyRow_2 | undefined) => ConnectionState_2;
7088
7136
 
7089
7137
  /** Exact-match property filter. Values are AND'd. */
7090
7138
  export declare type PropertyFilter = Readonly<Record<string, string | number | boolean | null>>;
@@ -7641,6 +7689,17 @@ export declare interface RebacSubject {
7641
7689
 
7642
7690
  export declare const recordCoalesced: (label: string, collapsed: number) => void;
7643
7691
 
7692
+ /**
7693
+ * The verified expiry of the credential a connection authenticated with, in
7694
+ * unix SECONDS — recorded by the auth chain, read by `ConnectionInfo`.
7695
+ *
7696
+ * It lives here rather than being re-derived from headers because those are two
7697
+ * different facts once more than one auth shape exists. The header derivation
7698
+ * read the `voltro:session` cookie and nothing else, so an app on Bearer JWTs
7699
+ * had no bound at all while the docs said every realtime primitive had one.
7700
+ * The strategy that verified the token is the only place the expiry is known,
7701
+ * and this is how it reaches the consumer without a second derivation.
7702
+ */
7644
7703
  export declare const recordCredentialExpiry: (clientId: number, exp: number | undefined) => void;
7645
7704
 
7646
7705
  /** What the framework emit-seams (servePipeline / rpcServer / plugin wrappers)
@@ -7930,6 +7989,8 @@ export declare interface RegistryTableLike {
7930
7989
  * Distinct from `defaultValue` (literal landed in DDL).
7931
7990
  */
7932
7991
  readonly defaultFactory?: () => unknown;
7992
+ /** Application-side value used when an UPDATE omits this column. */
7993
+ readonly onUpdate?: 'now' | (() => unknown);
7933
7994
  /**
7934
7995
  * Computed field — re-evaluated on INSERT (AFTER defaults) and on
7935
7996
  * UPDATE (against the merged post-update row); the return value
@@ -7964,6 +8025,13 @@ export declare interface RegistryTableLike {
7964
8025
  readonly name: string;
7965
8026
  readonly fields: ReadonlyArray<string>;
7966
8027
  }>;
8028
+ /**
8029
+ * The columns of an EXPLICIT primary key (`.primaryKey([...])`). Absent or
8030
+ * empty for the ordinary table whose `id()` column carries the key. The
8031
+ * registry surfaces it so the keyed store forms can refuse a table they have
8032
+ * no single string to address — see `SchemaInfo.declaredPrimaryKey`.
8033
+ */
8034
+ readonly appliedPrimaryKey?: ReadonlyArray<string>;
7967
8035
  /**
7968
8036
  * Optional `effect/Schema` the framework decodes a row against
7969
8037
  * BEFORE every INSERT. Set via `table().validate(schema)`. The
@@ -8113,7 +8181,7 @@ export declare class ReplicatedDataStore implements DataStore {
8113
8181
  private routeWaitMode;
8114
8182
  insert(table: string, row: Row): Promise<Row>;
8115
8183
  insertMany(table: string, rows: ReadonlyArray<Row>): Promise<ReadonlyArray<Row>>;
8116
- patchJson(table: string, primaryKey: string, path: string, value: unknown): Promise<Row | null>;
8184
+ patchJson(table: string, primaryKey: string, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>): Promise<Row | null>;
8117
8185
  update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<Row | null>;
8118
8186
  delete(table: string, primaryKey: string): Promise<boolean>;
8119
8187
  updateMany(table: string, patch: Readonly<Record<string, unknown>>, options: {
@@ -8122,10 +8190,7 @@ export declare class ReplicatedDataStore implements DataStore {
8122
8190
  deleteMany(table: string, options: {
8123
8191
  where: Predicate;
8124
8192
  }): Promise<number>;
8125
- upsert(table: string, row: Row, options: {
8126
- conflictColumns: ReadonlyArray<string>;
8127
- update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
8128
- }): Promise<Row>;
8193
+ upsert(table: string, row: Row, options: UpsertOptions): Promise<Row>;
8129
8194
  insertIgnore(table: string, row: Row, options: {
8130
8195
  conflictColumns: ReadonlyArray<string>;
8131
8196
  }): Promise<Row>;
@@ -8928,6 +8993,15 @@ export declare interface RowHistoryEntry {
8928
8993
  readonly traceId?: string | null;
8929
8994
  }
8930
8995
 
8996
+ /** Server-owned lifetime, not a user identity or a client-supplied claim.
8997
+ * WebSockets retain it across calls; HTTP retains it only within one request.
8998
+ * Reconnects and server restarts receive a new id. */
8999
+ export declare interface RpcConnection {
9000
+ readonly id: string;
9001
+ readonly transport: 'websocket' | 'http';
9002
+ readonly signal: AbortSignal;
9003
+ }
9004
+
8931
9005
  export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
8932
9006
  readonly group: RpcGroup.RpcGroup<Rpcs>;
8933
9007
  readonly handlersLayer: Layer.Layer<Rpc.ToHandler<Rpcs>>;
@@ -9205,14 +9279,15 @@ export declare interface RuntimeContext {
9205
9279
  * build the outbound `traceparent` header for cross-api hops. */
9206
9280
  readonly spanId?: string;
9207
9281
  /**
9208
- * Per-connection identifier the rpc transport assigns to a
9209
- * WebSocket connection. Stable for the lifetime of that connection.
9210
- * Used by re-auth handlers (`auth.signin`) to write into the
9211
- * per-connection subject override map so subsequent calls on the
9212
- * same connection see the new subject. `undefined` for non-rpc
9213
- * call paths (workflows, smoke tests, internal invocations).
9282
+ * Opaque process-wide identifier, distinct across RPC transports/listeners.
9283
+ * Stable within a WebSocket or one HTTP request; never persist this number.
9284
+ * Re-auth handlers use it to bind a credential, not a resolved subject.
9285
+ * Undefined for non-RPC call paths (workflows, internal invocations).
9214
9286
  */
9215
9287
  readonly clientId?: number;
9288
+ /** Server-owned transport identity and close signal. Not authentication;
9289
+ * session membership remains an application's explicit access decision. */
9290
+ readonly connection?: RpcConnection;
9216
9291
  }
9217
9292
 
9218
9293
  /**
@@ -9630,6 +9705,8 @@ export declare interface SchemaInfo {
9630
9705
  * on every UPDATE against the merged post-update row.
9631
9706
  */
9632
9707
  readonly computedFields: ReadonlyMap<string, (row: Readonly<Record<string, unknown>>) => unknown>;
9708
+ /** Optional for custom registries; absent means no automatic update columns. */
9709
+ readonly onUpdateFields?: ReadonlyMap<string, 'now' | (() => unknown)>;
9633
9710
  /** The column marked `.version()`, when the table declares one. */
9634
9711
  readonly versionColumn?: string;
9635
9712
  /**
@@ -9666,11 +9743,24 @@ export declare interface SchemaInfo {
9666
9743
  readonly validatePatchSchema?: Schema.Schema.Any;
9667
9744
  /**
9668
9745
  * Resolved id-scheme for the table's primary-key column. `undefined`
9669
- * for tables that have no `id`-typed column (rare; framework tables
9670
- * always have one). MutationStore reads this to decide whether to
9671
- * auto-generate a string id or omit the column for the DB to allocate.
9746
+ * for tables that have no `id`-typed column — a natural or composite
9747
+ * primary key, see `declaredPrimaryKey`. MutationStore reads this to decide
9748
+ * whether to auto-generate a string id or omit the column for the DB to
9749
+ * allocate.
9672
9750
  */
9673
9751
  readonly idScheme?: IdScheme;
9752
+ /**
9753
+ * The columns of an EXPLICIT primary key (`.primaryKey(['userId','orgId'])`,
9754
+ * or one column for a shared key). Empty for the ordinary table whose `id()`
9755
+ * column carries the key.
9756
+ *
9757
+ * Read by the keyed store forms — `update(table, pk, patch)` and
9758
+ * `delete(table, pk)` address a row by a single string, which such a table
9759
+ * has nothing to match against. Before this was carried here they answered
9760
+ * `null` / `false`: the SAME values that mean "no such row", so a caller
9761
+ * could not tell a missing row from a call that can never find one.
9762
+ */
9763
+ readonly declaredPrimaryKey: ReadonlyArray<string>;
9674
9764
  }
9675
9765
 
9676
9766
  /**
@@ -9709,6 +9799,11 @@ export declare interface SchemaRegistry {
9709
9799
  * hook in `./storeMiddleware.ts`.
9710
9800
  */
9711
9801
  idScheme(table: string): IdScheme | undefined;
9802
+ /**
9803
+ * The columns of the table's EXPLICIT primary key, or an empty array when
9804
+ * its `id()` column carries the key. See {@link SchemaInfo.declaredPrimaryKey}.
9805
+ */
9806
+ declaredPrimaryKey(table: string): ReadonlyArray<string>;
9712
9807
  /**
9713
9808
  * Per-column app-side default factories for a table. Empty map if
9714
9809
  * none. Read by MutationStore.stampedForInsert to fill missing
@@ -9894,6 +9989,8 @@ export declare type SerializedGuard = {
9894
9989
  readonly action: string;
9895
9990
  readonly resourceType: string;
9896
9991
  readonly resource: string | null;
9992
+ } | {
9993
+ readonly kind: 'authenticated';
9897
9994
  };
9898
9995
 
9899
9996
  /**
@@ -11064,7 +11161,7 @@ export declare type TupleSource = (req: {
11064
11161
  export declare const TX_READ_VERSIONS_KEY: unique symbol;
11065
11162
 
11066
11163
  /**
11067
- * Drop everything this connection accumulated. Called by the WS-close finalizer
11164
+ * Drop everything this connection accumulated. Called by the transport finalizer
11068
11165
  * and by explicit sign-out flows. Idempotent.
11069
11166
  */
11070
11167
  export declare const unbindConnection: (clientId: number) => void;