@voltro/protocol 0.52.0 → 0.54.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
@@ -136,6 +136,8 @@ export declare interface AppliedScopes {
136
136
  readonly removed: ReadonlyArray<string>;
137
137
  }
138
138
 
139
+ export declare const applyRowPatch: (prev: ReadonlyArray<PatchRow>, patch: RowPatch, options?: ApplyRowPatchOptions) => ReadonlyArray<PatchRow>;
140
+
139
141
  /**
140
142
  * Apply a `RowPatch` to `prev`, producing `next`. Exact inverse of
141
143
  * `diffRows`: `applyRowPatch(prev, diffRows(prev, next))` deep-equals
@@ -149,7 +151,18 @@ export declare interface AppliedScopes {
149
151
  * an `add`) is skipped rather than throwing, so one dropped frame degrades
150
152
  * to a slightly stale set instead of a crash.
151
153
  */
152
- export declare const applyRowPatch: (prev: ReadonlyArray<PatchRow>, patch: RowPatch) => ReadonlyArray<PatchRow>;
154
+ export declare interface ApplyRowPatchOptions {
155
+ /**
156
+ * The CRDT downstream lane's applier half: fold a `mergeCells` update into
157
+ * the held cell value. Injected (the patch layer is CRDT-library-free);
158
+ * `@voltro/local-first` provides the real one. When absent, a `mergeCells`
159
+ * op REPLACES the cell with the update — wrong for a true incremental
160
+ * update, which is why the producer only emits `mergeCells` toward
161
+ * consumers that negotiated it; the fallback keeps an unpaired frame from
162
+ * crashing the applier.
163
+ */
164
+ readonly mergeCell?: (column: string, prevValue: unknown, update: unknown) => unknown;
165
+ }
153
166
 
154
167
  /**
155
168
  * Apply a decision to a Subject, touching nothing but `scopes`.
@@ -902,6 +915,15 @@ export declare interface ConnectionInfoValue {
902
915
  * doc comment that claims otherwise.
903
916
  */
904
917
  readonly credentialExpiresAt?: number;
918
+ /**
919
+ * The per-CALL `voltro-resume-from` request header, parsed: the last
920
+ * subscription revision the client materialised before it lost the
921
+ * connection. Read by `bindSubscription` to attempt a delta-resume — the
922
+ * server replays only the missed deltas when the retained ring still
923
+ * chains this revision, and falls back to a fresh snapshot otherwise.
924
+ * Absent when the client did not ask to resume (a fresh subscribe).
925
+ */
926
+ readonly resumeFrom?: number;
905
927
  }
906
928
 
907
929
  /** The two credential shapes a connection can hold. `oauth2` = an
@@ -1638,6 +1660,8 @@ export declare interface DeleteTarget<Input = unknown> extends NestedTargetField
1638
1660
  readonly identify?: ((input: Input) => string | ReadonlyArray<string>) | undefined;
1639
1661
  }
1640
1662
 
1663
+ export declare const diffRows: (prev: ReadonlyArray<PatchRow>, next: ReadonlyArray<PatchRow>, options?: DiffRowsOptions) => RowPatch;
1664
+
1641
1665
  /**
1642
1666
  * Compute the patch that turns `prev` into `next`, keyed by row id.
1643
1667
  *
@@ -1653,7 +1677,22 @@ export declare interface DeleteTarget<Input = unknown> extends NestedTargetField
1653
1677
  * id-keyed result sets; a result without ids must ship a full snapshot,
1654
1678
  * not a patch.
1655
1679
  */
1656
- export declare const diffRows: (prev: ReadonlyArray<PatchRow>, next: ReadonlyArray<PatchRow>) => RowPatch;
1680
+ export declare interface DiffRowsOptions {
1681
+ /**
1682
+ * The CRDT downstream lane. `columns` names the CRDT-managed columns of
1683
+ * this row set; `incremental(column, prevValue, nextValue)` returns the
1684
+ * SMALL update a holder of prevValue needs to reach nextValue (or
1685
+ * `undefined` to fall back to a plain replace — e.g. a prev the encoder
1686
+ * cannot read). When a changed row's CRDT cells are the only change, the
1687
+ * diff emits ONE `mergeCells` op; when scalars changed too, the `replace`
1688
+ * carries the row WITHOUT its CRDT columns and the `mergeCells` op rides
1689
+ * beside it.
1690
+ */
1691
+ readonly crdt?: {
1692
+ readonly columns: ReadonlySet<string>;
1693
+ readonly incremental: (column: string, prevValue: unknown, nextValue: unknown) => unknown | undefined;
1694
+ };
1695
+ }
1657
1696
 
1658
1697
  /**
1659
1698
  * The caller's effective scope set — the merged set published by rbac if
@@ -2003,6 +2042,17 @@ export declare type ExtraErrors = ReadonlyArray<Schema.Schema.All>;
2003
2042
  /** Release a claim after the handler errored, so a retry can re-process. */
2004
2043
  export declare const failIdempotent: (store: IdempotencyStore, scope: string, key: string) => Promise<void>;
2005
2044
 
2045
+ /**
2046
+ * The field-addressed issues carried by a wire error, or `[]` when the error
2047
+ * is not field-routable. ONE reader for all three shapes — `ValidationError`,
2048
+ * `ValidationErrors`, and a `BusinessRuleViolation` whose rule pinpointed a
2049
+ * `field` — so the form binding, `<AutoForm>`, and any custom widget kit
2050
+ * cannot disagree about which errors belong on a field. A
2051
+ * `BusinessRuleViolation` WITHOUT a field is form-level, not field-level, and
2052
+ * correctly stays in `submitError`.
2053
+ */
2054
+ export declare const fieldIssuesOf: (error: unknown) => ReadonlyArray<ValidationIssueShape>;
2055
+
2006
2056
  /**
2007
2057
  * Report descriptors whose `guards:` declare a per-resource check that nothing
2008
2058
  * will enforce.
@@ -2302,6 +2352,8 @@ export declare const inputLabel: (procedure?: string, guarded?: boolean) => stri
2302
2352
  export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Record<string, unknown>> extends NestedTargetFields<Input> {
2303
2353
  readonly table: string;
2304
2354
  readonly op: 'insert';
2355
+ /** Declared junction relations — see {@link TargetRelations}. */
2356
+ readonly relations?: TargetRelations | undefined;
2305
2357
  readonly order?: 'prepend' | 'append' | undefined;
2306
2358
  /**
2307
2359
  * Build the optimistic row from the mutation input. The framework
@@ -2881,6 +2933,16 @@ export declare interface PluginEnvVar {
2881
2933
  readonly description?: string;
2882
2934
  /** Example value for `.env.example`. Never a real secret. */
2883
2935
  readonly example?: string;
2936
+ /**
2937
+ * Set ONLY when the secret is OURS to invent (a signing key, a VAPID scalar)
2938
+ * — `voltro dev` then mints a per-project value into the gitignored
2939
+ * `.env.local`, exactly like an app-declared `envVar.secret({ generate })`.
2940
+ * `p256` mints a base64url raw P-256 private scalar (web push VAPID).
2941
+ * NEVER set it for a third-party credential: an invented value merely looks
2942
+ * right, and the boot failure is the useful outcome. Minting stays dev-only —
2943
+ * in production a missing secret refuses the boot.
2944
+ */
2945
+ readonly generate?: 'base64url' | 'hex' | 'p256';
2884
2946
  }
2885
2947
 
2886
2948
  /**
@@ -3737,6 +3799,10 @@ export declare const queryToRpc: <Name extends string, Input extends Schema.Sche
3737
3799
  }>, Schema.Struct<{
3738
3800
  op: Schema.Literal<["remove"]>;
3739
3801
  path: typeof Schema.String;
3802
+ }>, Schema.Struct<{
3803
+ op: Schema.Literal<["mergeCells"]>;
3804
+ path: typeof Schema.String;
3805
+ value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
3740
3806
  }>]>>;
3741
3807
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
3742
3808
  }>;
@@ -3876,6 +3942,19 @@ export declare type RowPatchOp = {
3876
3942
  } | {
3877
3943
  readonly op: 'remove';
3878
3944
  readonly path: string;
3945
+ }
3946
+ /** The CRDT downstream lane (plan 18): cell-level MERGE instead of
3947
+ * replace. `value` maps column → an INCREMENTAL encoded update (what the
3948
+ * subscriber is missing relative to the previous delivery, not the full
3949
+ * document), and the applier folds it into the held cell with the
3950
+ * injected merger. This is what keeps a 1-character edit ≤1KB on the
3951
+ * subscription wire regardless of document size. A `mergeCells` op may
3952
+ * accompany a `replace` on the same row — the replace then carries the
3953
+ * row WITHOUT its CRDT columns. */
3954
+ | {
3955
+ readonly op: 'mergeCells';
3956
+ readonly path: string;
3957
+ readonly value: Readonly<Record<string, unknown>>;
3879
3958
  };
3880
3959
 
3881
3960
  export declare const rowPatchOpSchema: Schema.Union<[Schema.Struct<{
@@ -3905,6 +3984,10 @@ export declare const rowPatchOpSchema: Schema.Union<[Schema.Struct<{
3905
3984
  }>, Schema.Struct<{
3906
3985
  op: Schema.Literal<["remove"]>;
3907
3986
  path: typeof Schema.String;
3987
+ }>, Schema.Struct<{
3988
+ op: Schema.Literal<["mergeCells"]>;
3989
+ path: typeof Schema.String;
3990
+ value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
3908
3991
  }>]>;
3909
3992
 
3910
3993
  export declare const rowPatchSchema: Schema.Struct<{
@@ -3935,6 +4018,10 @@ export declare const rowPatchSchema: Schema.Struct<{
3935
4018
  }>, Schema.Struct<{
3936
4019
  op: Schema.Literal<["remove"]>;
3937
4020
  path: typeof Schema.String;
4021
+ }>, Schema.Struct<{
4022
+ op: Schema.Literal<["mergeCells"]>;
4023
+ path: typeof Schema.String;
4024
+ value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
3938
4025
  }>]>>;
3939
4026
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
3940
4027
  }>;
@@ -4523,6 +4610,10 @@ export declare const subscriptionEvent: <D extends Schema.Schema.Any>(data: D) =
4523
4610
  }>, Schema.Struct<{
4524
4611
  op: Schema.Literal<["remove"]>;
4525
4612
  path: typeof Schema.String;
4613
+ }>, Schema.Struct<{
4614
+ op: Schema.Literal<["mergeCells"]>;
4615
+ path: typeof Schema.String;
4616
+ value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
4526
4617
  }>]>>;
4527
4618
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
4528
4619
  }>;
@@ -4613,6 +4704,22 @@ declare const TableValidationFailed_base: Schema.TaggedErrorClass<TableValidatio
4613
4704
 
4614
4705
  export declare type Target<Input = unknown, Row = unknown> = TargetSpec<Input, Row> | ReadonlyArray<TargetSpec<Input, Row>>;
4615
4706
 
4707
+ /**
4708
+ * Declared many-to-many RELATIONS of a write target: `{ inputField:
4709
+ * junctionTable }`. After the executor succeeds — inside the SAME
4710
+ * transaction — the framework reconciles the junction's links for the
4711
+ * written row against `input[inputField]` (an array of target ids) via the
4712
+ * diff-based `store.relationLinks`, so a form's multi-reference field saves
4713
+ * in one mutation with no hand-written junction code. The anchor column is
4714
+ * derived from the junction's `reference()` targets (a self-junction is
4715
+ * refused, never guessed).
4716
+ *
4717
+ * An ABSENT input field leaves the links untouched (absent ≠ empty — an
4718
+ * empty array is the explicit "clear them all"). The row id is the
4719
+ * executor's `output.id`, falling back to `input.id`.
4720
+ */
4721
+ export declare type TargetRelations = Readonly<Record<string, string>>;
4722
+
4616
4723
  export declare type TargetSpec<Input = unknown, Row = unknown> = InsertTarget<Input, Row> | UpdateTarget<Input, Row> | DeleteTarget<Input>;
4617
4724
 
4618
4725
  /**
@@ -4725,6 +4832,10 @@ export declare const toRpc: <Name extends string, Input extends Schema.Schema.An
4725
4832
  }>, Schema.Struct<{
4726
4833
  op: Schema.Literal<["remove"]>;
4727
4834
  path: typeof Schema.String;
4835
+ }>, Schema.Struct<{
4836
+ op: Schema.Literal<["mergeCells"]>;
4837
+ path: typeof Schema.String;
4838
+ value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
4728
4839
  }>]>>;
4729
4840
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
4730
4841
  }>;
@@ -4876,6 +4987,8 @@ export declare const undoRedoDescriptor: MutationProcedureDescriptor<"__voltro.u
4876
4987
  export declare interface UpdateTarget<Input = unknown, Row = unknown, Item = Record<string, unknown>> extends NestedTargetFields<Input> {
4877
4988
  readonly table: string;
4878
4989
  readonly op: 'update';
4990
+ /** Declared junction relations — see {@link TargetRelations}. */
4991
+ readonly relations?: TargetRelations | undefined;
4879
4992
  /** Identify the row(s) to patch. Default: `input.id`. Return an ARRAY to patch
4880
4993
  * MANY rows/items in one mutation (a bulk edit — where the per-item
4881
4994
  * parallel-write race lived). */
@@ -4888,6 +5001,52 @@ export declare interface UpdateTarget<Input = unknown, Row = unknown, Item = Rec
4888
5001
  readonly shapeItem?: ((input: Input, current: Item) => Item) | undefined;
4889
5002
  }
4890
5003
 
5004
+ /**
5005
+ * A server-side validation failure pinned to ONE field.
5006
+ *
5007
+ * return yield* ctx.validation.fail('email', 'validation.emailTaken')
5008
+ *
5009
+ * Routed by the form binding to `errors.email`; the mutation's transaction is
5010
+ * rolled back (or, thrown before any write, never opened).
5011
+ */
5012
+ export declare class ValidationError extends ValidationError_base {
5013
+ }
5014
+
5015
+ declare const ValidationError_base: Schema.TaggedErrorClass<ValidationError, "ValidationError", {
5016
+ readonly _tag: Schema.tag<"ValidationError">;
5017
+ } & {
5018
+ field: typeof Schema.String;
5019
+ message: typeof Schema.String;
5020
+ params: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
5021
+ }>;
5022
+
5023
+ /** Several field-addressed failures at once — one round trip, every field
5024
+ * marked. `issues` is non-empty by construction (`ctx.validation.failAll`
5025
+ * refuses an empty list rather than shipping a failure with nothing to show). */
5026
+ export declare class ValidationErrors extends ValidationErrors_base {
5027
+ }
5028
+
5029
+ declare const ValidationErrors_base: Schema.TaggedErrorClass<ValidationErrors, "ValidationErrors", {
5030
+ readonly _tag: Schema.tag<"ValidationErrors">;
5031
+ } & {
5032
+ issues: Schema.filter<Schema.Array$<Schema.Struct<{
5033
+ field: typeof Schema.String;
5034
+ message: typeof Schema.String;
5035
+ params: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
5036
+ }>>>;
5037
+ }>;
5038
+
5039
+ /** One field-addressed validation issue: `field` is the form path
5040
+ * (`'email'`, `'address.city'`, `'entries.0.startsAt'`), `message` is a
5041
+ * message id (or verbatim text), `params` feed the message template. */
5042
+ export declare const ValidationIssue: Schema.Struct<{
5043
+ field: typeof Schema.String;
5044
+ message: typeof Schema.String;
5045
+ params: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
5046
+ }>;
5047
+
5048
+ export declare type ValidationIssueShape = Schema.Schema.Type<typeof ValidationIssue>;
5049
+
4891
5050
  export declare interface VoltroPlugin {
4892
5051
  /**
4893
5052
  * Plugin identifier — printed in `voltro dev` boot logs + surfaced in