@voltro/client 0.51.0 → 0.53.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 +356 -0
- package/THIRD-PARTY-NOTICES.md +169 -1
- package/dist/form.d.ts +228 -0
- package/dist/form.js +2 -0
- package/dist/formData-BO7zkG_F.js +386 -0
- package/dist/index.d.ts +526 -16
- package/dist/index.js +1527 -1261
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { Effect } from 'effect';
|
|
|
6
6
|
import { EventDescriptor as EventDescriptor_2 } from '@voltro/protocol';
|
|
7
7
|
import { Fiber } from 'effect';
|
|
8
8
|
import { ManagedRuntime } from 'effect';
|
|
9
|
+
import { ParseResult } from 'effect';
|
|
10
|
+
import { ReactElement } from 'react';
|
|
9
11
|
import { ReactNode } from 'react';
|
|
10
12
|
import { Rpc } from '@effect/rpc';
|
|
11
13
|
import { RpcClient } from '@effect/rpc';
|
|
@@ -215,6 +217,23 @@ export declare interface ApiHooks<P extends ProcedureTypeMap> {
|
|
|
215
217
|
}): SubscriptionState<QueryData<P, Tag>> | SubscriptionIdle;
|
|
216
218
|
};
|
|
217
219
|
readonly useMutation: <Tag extends MutationTag<P>>(tag: Tag) => MutationBuilder<ProcedureInput<P[Tag]>, ProcedureOutput<P[Tag]>>;
|
|
220
|
+
/** Schema-driven form bound to one mutation — tag as a literal, `values` /
|
|
221
|
+
* `defaults` / the submit output all inferred from the descriptor. The
|
|
222
|
+
* input schema still resolves from the mounted client descriptor at
|
|
223
|
+
* runtime, exactly like the untyped hook.
|
|
224
|
+
*
|
|
225
|
+
* Two shapes: without `toInput` the form values ARE the mutation input.
|
|
226
|
+
* WITH `toInput` the form has its OWN `Values` shape (inferred from
|
|
227
|
+
* `defaults` / the mapper) and `toInput` must produce the mutation's
|
|
228
|
+
* input — checked by the compiler, not asserted. */
|
|
229
|
+
readonly useFormBinding: {
|
|
230
|
+
<Tag extends MutationTag<P>>(tag: Tag, options?: UseFormBindingOptions<FormInput<P, Tag>> & {
|
|
231
|
+
readonly toInput?: undefined;
|
|
232
|
+
}): FormBinding<FormInput<P, Tag>, ProcedureOutput<P[Tag]>>;
|
|
233
|
+
<Tag extends MutationTag<P>, Values extends Record<string, unknown>>(tag: Tag, options: UseFormBindingOptions<Values> & {
|
|
234
|
+
readonly toInput: (values: Partial<Values>) => ProcedureInput<P[Tag]>;
|
|
235
|
+
}): FormBinding<Values, ProcedureOutput<P[Tag]>>;
|
|
236
|
+
};
|
|
218
237
|
readonly useAction: <Tag extends ActionTag<P>>(tag: Tag) => ActionState<ProcedureInput<P[Tag]>, ProcedureOutput<P[Tag]>>;
|
|
219
238
|
}
|
|
220
239
|
|
|
@@ -751,6 +770,13 @@ export declare interface DataTableState<Row> {
|
|
|
751
770
|
*/
|
|
752
771
|
export declare const decideAccess: (guards: ReadonlyArray<ManifestGuard> | undefined, subjectScopes: ReadonlyArray<string>) => AccessDecision;
|
|
753
772
|
|
|
773
|
+
/**
|
|
774
|
+
* Walk a ParseError's issue tree into structured, PATH-PRESERVING issues.
|
|
775
|
+
* Every leaf keeps its full path (`address.city`, `entries.0.startsAt`) —
|
|
776
|
+
* nothing collapses to the top-level segment.
|
|
777
|
+
*/
|
|
778
|
+
export declare const decodeFieldIssues: (error: ParseResult.ParseError) => ReadonlyArray<FieldIssue>;
|
|
779
|
+
|
|
754
780
|
/** All defined stores, in definition order. */
|
|
755
781
|
export declare const definedStores: () => ReadonlyArray<StoreHandle<never>>;
|
|
756
782
|
|
|
@@ -792,6 +818,10 @@ export declare const defineTracking: <P = Record<string, unknown>>(name: string,
|
|
|
792
818
|
*/
|
|
793
819
|
export declare const deriveEntityAdmins: (manifest: CapabilityManifest) => ReadonlyArray<EntityAdminSpec>;
|
|
794
820
|
|
|
821
|
+
/** The document's locale per the framework contract (`<html lang>`); `'en'`
|
|
822
|
+
* outside a browser. Primary subtag only (`de-AT` → `de`). */
|
|
823
|
+
export declare const documentLocale: () => string;
|
|
824
|
+
|
|
795
825
|
export declare const enqueueEntry: (queue: Outbox, id: string, tag: string, input: unknown) => Outbox;
|
|
796
826
|
|
|
797
827
|
/**
|
|
@@ -936,17 +966,56 @@ export declare interface FieldDescriptor {
|
|
|
936
966
|
/** The resolved JSON-Schema node — carries `maxLength` / `pattern` /
|
|
937
967
|
* `minimum` / `format` / `description` for widgets + validation hints. */
|
|
938
968
|
readonly jsonSchema: Readonly<Record<string, unknown>>;
|
|
969
|
+
/** Dotted parent path for a nested-struct member (`'address'` for
|
|
970
|
+
* `address.city`) — the SECTION a renderer groups it under. Top-level
|
|
971
|
+
* fields carry none. Overridable via `formField({ section })`. */
|
|
972
|
+
readonly section?: string;
|
|
973
|
+
/** Human label for the section (humanised path tail). */
|
|
974
|
+
readonly sectionLabel?: string;
|
|
975
|
+
/** Help text — the Schema `description` annotation. */
|
|
976
|
+
readonly description?: string;
|
|
977
|
+
/** Declared ordering (`formField({ order })`); fields sort by it, stable
|
|
978
|
+
* against declaration order. */
|
|
979
|
+
readonly order?: number;
|
|
980
|
+
/** For `widget: 'array'` (array of structs): the ITEM's field descriptors,
|
|
981
|
+
* names relative to one item (`startsAt`, not `entries.0.startsAt`). */
|
|
982
|
+
readonly item?: ReadonlyArray<FieldDescriptor>;
|
|
983
|
+
/** For `widget: 'reference'`: the referenced TABLE — a renderer binds a
|
|
984
|
+
* query-backed picker to it; the VALUE stays the id (or id list). Comes
|
|
985
|
+
* from `formField({ reference: 'stores' })`. */
|
|
986
|
+
readonly reference?: string;
|
|
939
987
|
}
|
|
940
988
|
|
|
941
989
|
export declare interface FieldErrors {
|
|
942
990
|
readonly [field: string]: string;
|
|
943
991
|
}
|
|
944
992
|
|
|
993
|
+
/** One structured validation issue. `path` is the dotted field path
|
|
994
|
+
* (`'email'`, `'address.city'`, `'entries.0.startsAt'`); `id` is the stable
|
|
995
|
+
* message id (`'validation.minLength'`); `params` feed its template;
|
|
996
|
+
* `fallback` is a rendered English default so an unknown id still shows a
|
|
997
|
+
* sentence, never a bare key. */
|
|
998
|
+
export declare interface FieldIssue {
|
|
999
|
+
readonly path: string;
|
|
1000
|
+
readonly id: string;
|
|
1001
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
1002
|
+
readonly fallback: string;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
945
1005
|
export declare interface FieldOption {
|
|
946
1006
|
readonly value: string;
|
|
947
1007
|
readonly label: string;
|
|
948
1008
|
}
|
|
949
1009
|
|
|
1010
|
+
/** One display section of a form: `name` is the section path (`'address'`),
|
|
1011
|
+
* `undefined` for the top-level run of unsectioned fields. Contiguous in
|
|
1012
|
+
* display order, so a renderer can walk sections top to bottom. */
|
|
1013
|
+
export declare interface FieldSection {
|
|
1014
|
+
readonly name: string | undefined;
|
|
1015
|
+
readonly label: string | undefined;
|
|
1016
|
+
readonly fields: ReadonlyArray<FieldDescriptor>;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
950
1019
|
export declare interface FilterDescriptor {
|
|
951
1020
|
readonly name: string;
|
|
952
1021
|
readonly label: string;
|
|
@@ -961,24 +1030,220 @@ export declare type FilterKind = 'text' | 'select' | 'multi-select' | 'number-ra
|
|
|
961
1030
|
* input field → one FilterDescriptor. Pure. */
|
|
962
1031
|
export declare const filtersFromSchema: (schema: Schema.Schema.Any) => ReadonlyArray<FilterDescriptor>;
|
|
963
1032
|
|
|
1033
|
+
/** DOM id of the JSON script the 422 re-render embeds (survives the
|
|
1034
|
+
* `interactive:'none'` strip — it is neither `type="module"` nor `src`). */
|
|
1035
|
+
export declare const FORM_FLASH_SCRIPT_ID = "__voltro_form_flash__";
|
|
1036
|
+
|
|
1037
|
+
/** Hidden field carrying the form's instance key (multi-form pages). */
|
|
1038
|
+
export declare const FORM_KEY_FIELD = "__voltro_form";
|
|
1039
|
+
|
|
1040
|
+
/** URL prefix the web listener mounts the no-JS endpoint under. */
|
|
1041
|
+
export declare const FORM_PATH_PREFIX = "/form/";
|
|
1042
|
+
|
|
1043
|
+
/** Hidden field carrying the declared success-redirect path. */
|
|
1044
|
+
export declare const FORM_REDIRECT_FIELD = "__voltro_redirect";
|
|
1045
|
+
|
|
1046
|
+
/** The path a native `<AutoForm>` POST goes to. */
|
|
1047
|
+
export declare const formActionPath: (mutationTag: string) => string;
|
|
1048
|
+
|
|
1049
|
+
/** A field ARRAY (`entries[]`): items + structural operations + per-item
|
|
1050
|
+
* field handles (`array('entries').field(0, 'startsAt')`). */
|
|
1051
|
+
export declare interface FormArrayHandle {
|
|
1052
|
+
readonly items: ReadonlyArray<unknown>;
|
|
1053
|
+
readonly push: (item: unknown) => void;
|
|
1054
|
+
readonly insert: (index: number, item: unknown) => void;
|
|
1055
|
+
readonly remove: (index: number) => void;
|
|
1056
|
+
readonly move: (from: number, to: number) => void;
|
|
1057
|
+
readonly swap: (a: number, b: number) => void;
|
|
1058
|
+
readonly field: (index: number, name: string) => FormFieldHandle;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
964
1061
|
export declare interface FormBinding<Input, Output> {
|
|
965
|
-
/** Ordered, render-agnostic field list derived from the input schema
|
|
1062
|
+
/** Ordered, render-agnostic field list derived from the input schema —
|
|
1063
|
+
* nested structs flattened into sections, arrays carrying item
|
|
1064
|
+
* descriptors (see `schemaToFields`). */
|
|
966
1065
|
readonly fields: ReadonlyArray<FieldDescriptor>;
|
|
967
1066
|
readonly values: Partial<Input>;
|
|
968
|
-
/** First error per field
|
|
1067
|
+
/** First VISIBLE error per field PATH (full dotted path — `'address.city'`,
|
|
1068
|
+
* not its top segment), display text in the resolved locale: gated schema
|
|
1069
|
+
* issues, blocked async checks, and routed server field errors. */
|
|
969
1070
|
readonly errors: FieldErrors;
|
|
1071
|
+
/** Every current schema issue, structured `{ path, id, params, fallback }`
|
|
1072
|
+
* — ungated, for widget kits that translate themselves. */
|
|
1073
|
+
readonly issues: ReadonlyArray<FieldIssue>;
|
|
970
1074
|
/** True when the CURRENT values decode against the schema. */
|
|
971
1075
|
readonly isValid: boolean;
|
|
972
1076
|
readonly pending: boolean;
|
|
973
|
-
/** The mutation's typed failure, if the last submit threw
|
|
1077
|
+
/** The mutation's typed failure, if the last submit threw — MINUS anything
|
|
1078
|
+
* field-routable, which lands in `errors` instead. */
|
|
974
1079
|
readonly submitError: unknown | undefined;
|
|
1080
|
+
/** A form-level (non-field) error carried in from a failed no-JS POST —
|
|
1081
|
+
* the RPC refused after valid input. Cleared by the next submit/reset. */
|
|
1082
|
+
readonly formError: string | undefined;
|
|
975
1083
|
readonly data: Output | undefined;
|
|
976
1084
|
readonly setValue: (name: string, value: unknown) => void;
|
|
977
1085
|
readonly setValues: (patch: Partial<Input>) => void;
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1086
|
+
/** Reset to the initial defaults — or to NEW defaults (switching the edited
|
|
1087
|
+
* record without a remount). */
|
|
1088
|
+
readonly reset: (nextDefaults?: Partial<Input>) => void;
|
|
1089
|
+
/** Validate (incl. async checks); if valid, map (`toInput`), run the
|
|
1090
|
+
* mutation (or `onSubmit`), route server field errors. Returns the output,
|
|
1091
|
+
* or `undefined` when anything blocked the submit. */
|
|
981
1092
|
readonly submit: () => Promise<Output | undefined>;
|
|
1093
|
+
/** One field, bound — see {@link FormFieldHandle}. */
|
|
1094
|
+
readonly field: (path: string) => FormFieldHandle;
|
|
1095
|
+
/** A field array — see {@link FormArrayHandle}. */
|
|
1096
|
+
readonly array: (path: string) => FormArrayHandle;
|
|
1097
|
+
/** The descriptors of one section (`section('address')`). */
|
|
1098
|
+
readonly section: (name: string) => ReadonlyArray<FieldDescriptor>;
|
|
1099
|
+
readonly state: FormStateSnapshot;
|
|
1100
|
+
/** Move focus to the first field with a visible error (display order). */
|
|
1101
|
+
readonly focusFirstInvalid: () => void;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Convert posted form entries into the input object the mutation's schema
|
|
1106
|
+
* decodes — the shape `validateFields` (and the RPC payload) expects.
|
|
1107
|
+
*
|
|
1108
|
+
* Mapping rules (the documented contract):
|
|
1109
|
+
* - **checkbox/switch**: present → `true`, absent → `false`. A native POST
|
|
1110
|
+
* omits an unchecked checkbox entirely, so absence is data, not a gap.
|
|
1111
|
+
* - **number**: `Number(raw)`; `''` → the field is omitted (`undefined`), so
|
|
1112
|
+
* an optional number stays absent and a required one reports "missing"
|
|
1113
|
+
* instead of silently becoming `0`.
|
|
1114
|
+
* - **date/datetime**: the string passes through (the schema's encoded side);
|
|
1115
|
+
* `''` → omitted, same reasoning as number.
|
|
1116
|
+
* - **arrays** (multi-select): `getAll` semantics — every entry under the key,
|
|
1117
|
+
* items converted per the array's item type (number items → `Number`).
|
|
1118
|
+
* - **unknown keys are dropped**: only names the schema declares are mapped,
|
|
1119
|
+
* so protocol fields (`__voltro_form`, …) never reach the input object —
|
|
1120
|
+
* the server rejects undeclared fields, and a hidden bookkeeping field must
|
|
1121
|
+
* not turn every no-JS submit into a validation error.
|
|
1122
|
+
*/
|
|
1123
|
+
export declare const formDataToInput: (schema: Schema.Schema.Any, entries: Iterable<FormEntry>) => Record<string, unknown>;
|
|
1124
|
+
|
|
1125
|
+
/** One posted entry. File entries must be filtered out by the caller —
|
|
1126
|
+
* uploads are a declared limit of the no-JS path. */
|
|
1127
|
+
export declare type FormEntry = readonly [name: string, value: string];
|
|
1128
|
+
|
|
1129
|
+
/** UI metadata for one schema field, as an annotations object:
|
|
1130
|
+
*
|
|
1131
|
+
* Schema.String.annotations(formField({ section: 'contact', order: 2 }))
|
|
1132
|
+
*
|
|
1133
|
+
* Rides the JSON-Schema annotation (merged, not replaced — verified), so it
|
|
1134
|
+
* survives the same normalisation every other descriptor fact does. */
|
|
1135
|
+
export declare const formField: (meta: {
|
|
1136
|
+
readonly label?: string;
|
|
1137
|
+
readonly section?: string;
|
|
1138
|
+
readonly order?: number;
|
|
1139
|
+
readonly widget?: WidgetKind;
|
|
1140
|
+
/** Mark the field a REFERENCE to a table: `formField({ reference: 'stores' })`
|
|
1141
|
+
* → `widget: 'reference'` carrying the target, value = the id (or id
|
|
1142
|
+
* list, for a multi-reference feeding a target's `relations:`). */
|
|
1143
|
+
readonly reference?: string;
|
|
1144
|
+
}) => {
|
|
1145
|
+
readonly jsonSchema: {
|
|
1146
|
+
readonly "x-voltro": typeof meta;
|
|
1147
|
+
};
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
/** One field, bound: value + change/blur wiring + gated error + descriptor
|
|
1151
|
+
* facts + a11y props a widget kit spreads onto its input. */
|
|
1152
|
+
export declare interface FormFieldHandle {
|
|
1153
|
+
readonly value: unknown;
|
|
1154
|
+
readonly setValue: (value: unknown) => void;
|
|
1155
|
+
readonly onBlur: () => void;
|
|
1156
|
+
/** The VISIBLE error (display-gated schema error, server error, or async
|
|
1157
|
+
* verdict) — `undefined` while hidden or absent. */
|
|
1158
|
+
readonly error: string | undefined;
|
|
1159
|
+
/** The routed server error for this field, if any (also part of `error`). */
|
|
1160
|
+
readonly serverError: string | undefined;
|
|
1161
|
+
readonly touched: boolean;
|
|
1162
|
+
readonly blurred: boolean;
|
|
1163
|
+
readonly dirty: boolean;
|
|
1164
|
+
readonly required: boolean;
|
|
1165
|
+
readonly label: string;
|
|
1166
|
+
readonly widget: string;
|
|
1167
|
+
readonly options: ReadonlyArray<FieldOption> | undefined;
|
|
1168
|
+
readonly description: string | undefined;
|
|
1169
|
+
/** `'idle' | 'checking' | 'valid' | 'invalid'` — mirrors the entry passed
|
|
1170
|
+
* under this path in `asyncFields`. */
|
|
1171
|
+
readonly asyncStatus: AsyncValidationResult['status'];
|
|
1172
|
+
readonly a11y: {
|
|
1173
|
+
readonly id: string;
|
|
1174
|
+
readonly 'aria-invalid': true | undefined;
|
|
1175
|
+
readonly 'aria-required': true | undefined;
|
|
1176
|
+
readonly 'aria-describedby': string | undefined;
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* The error/values payload a failed no-JS form POST carries back into the
|
|
1182
|
+
* 422 re-render. Keyed by `formKey` so a page with several forms re-fills
|
|
1183
|
+
* only the one that was submitted. The SSR render passes it through the
|
|
1184
|
+
* server-request context; the client reads the same payload back off the
|
|
1185
|
+
* `#__voltro_form_flash__` JSON script, so a late-hydrating page shows the
|
|
1186
|
+
* identical state (no mismatch, no vanished errors).
|
|
1187
|
+
*/
|
|
1188
|
+
export declare interface FormFlashPayload {
|
|
1189
|
+
/** The submitted form's key — `<AutoForm formKey>` or its mutation tag. */
|
|
1190
|
+
readonly formKey: string;
|
|
1191
|
+
/** The mapped input values as posted (pre-decode), to re-fill the fields. */
|
|
1192
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
1193
|
+
/** First error per field — the `validateFields` shape. */
|
|
1194
|
+
readonly errors: Readonly<Record<string, string>>;
|
|
1195
|
+
/** A non-field error (the RPC refused after valid input: guard, server). */
|
|
1196
|
+
readonly formError?: string;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/** A mutation's input as the form binding's value shape. A struct input is its
|
|
1200
|
+
* own shape; the (theoretical) non-object input degrades to the untyped map
|
|
1201
|
+
* rather than failing the whole binding. */
|
|
1202
|
+
declare type FormInput<P extends ProcedureTypeMap, Tag extends keyof P> = ProcedureInput<P[Tag]> extends Record<string, unknown> ? ProcedureInput<P[Tag]> : Record<string, unknown>;
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Group an ordered field list into contiguous sections — the structure a
|
|
1206
|
+
* form (or its skeleton — the placeholder must have the SHAPE of the real
|
|
1207
|
+
* thing, title rows included) renders. Pure; feeds `<FormSkeleton>`,
|
|
1208
|
+
* `<AutoForm>` layouts and `form.section(...)` alike.
|
|
1209
|
+
*/
|
|
1210
|
+
export declare const formSections: (fields: ReadonlyArray<FieldDescriptor>) => ReadonlyArray<FieldSection>;
|
|
1211
|
+
|
|
1212
|
+
/** The full form state, engine + mutation, in ONE place — a save button reads
|
|
1213
|
+
* `canSubmit`/`pending`, a dialog closes on `isSubmitSuccessful`, a banner
|
|
1214
|
+
* listens to `submitError`. */
|
|
1215
|
+
export declare interface FormStateSnapshot {
|
|
1216
|
+
readonly isDirty: boolean;
|
|
1217
|
+
readonly isPristine: boolean;
|
|
1218
|
+
readonly isTouched: boolean;
|
|
1219
|
+
/** The CURRENT values decode (display gating does not apply here). */
|
|
1220
|
+
readonly isValid: boolean;
|
|
1221
|
+
/** An async field check is in flight. */
|
|
1222
|
+
readonly isValidating: boolean;
|
|
1223
|
+
readonly canSubmit: boolean;
|
|
1224
|
+
readonly isSubmitting: boolean;
|
|
1225
|
+
readonly isSubmitted: boolean;
|
|
1226
|
+
readonly isSubmitSuccessful: boolean;
|
|
1227
|
+
readonly submissionAttempts: number;
|
|
1228
|
+
/** VISIBLE errors right now (post-gating). */
|
|
1229
|
+
readonly errorCount: number;
|
|
1230
|
+
readonly firstInvalidPath: string | undefined;
|
|
1231
|
+
/** The mutation is on the wire — can outlast `isSubmitting` under
|
|
1232
|
+
* auto-optimistic. */
|
|
1233
|
+
readonly pending: boolean;
|
|
1234
|
+
readonly isLoading: boolean;
|
|
1235
|
+
readonly submitError: unknown | undefined;
|
|
1236
|
+
readonly data: unknown;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
export declare interface FormValidateOptions {
|
|
1240
|
+
/** When a field's error becomes VISIBLE while editing. `'afterTouched'`
|
|
1241
|
+
* (default): after that field blurred once, or after the first submit
|
|
1242
|
+
* attempt — a form never opens with errors. `'always'`: live from the
|
|
1243
|
+
* first keystroke. `'never'`: only after a submit attempt. */
|
|
1244
|
+
readonly onChange?: 'always' | 'afterTouched' | 'never';
|
|
1245
|
+
/** Reveal a field's error when it blurs (default true). */
|
|
1246
|
+
readonly onBlur?: boolean;
|
|
982
1247
|
}
|
|
983
1248
|
|
|
984
1249
|
export declare interface FrameworkRuntimes {
|
|
@@ -1010,6 +1275,32 @@ export declare const getMutationNotifier: () => MutationNotifier | undefined;
|
|
|
1010
1275
|
/** Read the current feed. Newest-first; up to MAX_MUTATIONS entries. */
|
|
1011
1276
|
export declare const getMutations: () => ReadonlyArray<MutationEvent>;
|
|
1012
1277
|
|
|
1278
|
+
/**
|
|
1279
|
+
* A process-wide singleton React context, pinned on the global symbol registry.
|
|
1280
|
+
*
|
|
1281
|
+
* WHY this exists: the SSG prerender (`voltro build`) loads the framework
|
|
1282
|
+
* through TWO separate module instances — the renderer comes in via Vite's
|
|
1283
|
+
* `ssrLoadModule('@voltro/web/ssr')` (Vite-transformed) while a page's own
|
|
1284
|
+
* framework import is externalised to Node — so a plain `createContext()`
|
|
1285
|
+
* in a shared module is evaluated twice and yields two DISTINCT context
|
|
1286
|
+
* objects. The `<Router>` provider then holds one while the page's
|
|
1287
|
+
* `useLocation()` reads the other, which throws "Router hooks must be used
|
|
1288
|
+
* inside <Router>." The same duplicate-instance hazard shows up wherever a
|
|
1289
|
+
* deployment ends up with two copies of a package (the classic dual-package
|
|
1290
|
+
* hazard, monorepo hoisting quirks, separate SSR vs client bundles).
|
|
1291
|
+
*
|
|
1292
|
+
* Resolving every context through `Symbol.for(...)` on `globalThis` makes all
|
|
1293
|
+
* module copies share ONE instance, so provider and consumer can never diverge.
|
|
1294
|
+
* A duplicated copy of THIS helper is harmless — both copies hit the same
|
|
1295
|
+
* global registry entry.
|
|
1296
|
+
*
|
|
1297
|
+
* Lives in @voltro/client (the lowest React-carrying package) so both
|
|
1298
|
+
* @voltro/web and @voltro/ui reach it without a cycle; the key prefix keeps
|
|
1299
|
+
* its historical spelling on purpose — a mixed dist/src world must resolve
|
|
1300
|
+
* to the same registry entries.
|
|
1301
|
+
*/
|
|
1302
|
+
export declare const globalContext: <T>(key: string, initial: T) => Context<T>;
|
|
1303
|
+
|
|
1013
1304
|
/** Is this the framework's `Unauthenticated`? Matched on `_tag`, the wire
|
|
1014
1305
|
* contract, rather than on an instance — the error crosses a package boundary
|
|
1015
1306
|
* and may be re-created by the decoder. */
|
|
@@ -1263,6 +1554,9 @@ export declare interface OutboxControls {
|
|
|
1263
1554
|
readonly online: boolean;
|
|
1264
1555
|
readonly pending: number;
|
|
1265
1556
|
readonly conflicts: ReadonlyArray<OutboxEntry>;
|
|
1557
|
+
/** Resolve a conflicted entry with a new input (see {@link resolveConflictEntry})
|
|
1558
|
+
* and replay immediately when online. */
|
|
1559
|
+
readonly resolveConflict: (id: string, input: unknown) => void;
|
|
1266
1560
|
}
|
|
1267
1561
|
|
|
1268
1562
|
export declare interface OutboxEntry {
|
|
@@ -1274,6 +1568,22 @@ export declare interface OutboxEntry {
|
|
|
1274
1568
|
readonly error?: unknown;
|
|
1275
1569
|
}
|
|
1276
1570
|
|
|
1571
|
+
/**
|
|
1572
|
+
* Durability seam for the outbox queue. `load` runs once on mount and returns
|
|
1573
|
+
* the surviving entries (oldest first); `save` is called after every queue
|
|
1574
|
+
* transition with the entries worth keeping (`sent` entries are compacted away
|
|
1575
|
+
* before the call — a delivered write needs no durability).
|
|
1576
|
+
*
|
|
1577
|
+
* The one real implementation is `outboxPersistence()` in
|
|
1578
|
+
* `@voltro/local-first`, which stores the entries through the same
|
|
1579
|
+
* `PersistenceAdapter` the sync engine uses — ONE durable queue for a device,
|
|
1580
|
+
* whichever surface enqueued the write.
|
|
1581
|
+
*/
|
|
1582
|
+
export declare interface OutboxPersistence {
|
|
1583
|
+
readonly load: () => Promise<Outbox>;
|
|
1584
|
+
readonly save: (queue: Outbox) => Promise<void>;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1277
1587
|
export declare type OutboxStatus = 'pending' | 'sent' | 'failed' | 'conflict';
|
|
1278
1588
|
|
|
1279
1589
|
/** Provide the current subject's scopes to `useCan`. Mount it once high in the
|
|
@@ -1575,6 +1885,14 @@ export declare type ResolvableHeaders = Readonly<Record<string, string>> | (() =
|
|
|
1575
1885
|
|
|
1576
1886
|
export declare const resolveByTag: (client: unknown, tag: string) => unknown;
|
|
1577
1887
|
|
|
1888
|
+
/** Resolve a conflicted entry: replace its input with the RESOLVED value and
|
|
1889
|
+
* return it to `pending`, so the next replay sends the resolution and the
|
|
1890
|
+
* entries it was blocking become replayable again. The resolved input
|
|
1891
|
+
* typically comes from a conflict policy (`@voltro/local-first`'s
|
|
1892
|
+
* `resolveWithPolicy` — CRDT fields merge, scalars resolve deterministically);
|
|
1893
|
+
* a caller may equally pass a hand-picked value. */
|
|
1894
|
+
export declare const resolveConflictEntry: (queue: Outbox, id: string, input: unknown) => Outbox;
|
|
1895
|
+
|
|
1578
1896
|
export declare interface ResolvedClient {
|
|
1579
1897
|
readonly runtime: AnyRuntime;
|
|
1580
1898
|
readonly cache: SubscriptionCache;
|
|
@@ -1587,6 +1905,13 @@ export declare const resolveStoreStorage: (area: StoreStorageArea) => StoreStora
|
|
|
1587
1905
|
/** Resolve one map entry against the props. Pure. */
|
|
1588
1906
|
export declare const resolveTrackingEvent: <P>(entry: TrackingEntry<P> | undefined, props: P) => TrackingEvent | null;
|
|
1589
1907
|
|
|
1908
|
+
/**
|
|
1909
|
+
* Render one issue id to display text: app resolver first (its `undefined`
|
|
1910
|
+
* falls through), then the built-in catalog for the locale's primary subtag,
|
|
1911
|
+
* then English, then the issue's own fallback sentence.
|
|
1912
|
+
*/
|
|
1913
|
+
export declare const resolveValidationMessage: (issue: Pick<FieldIssue, "id" | "params" | "fallback">, options?: ValidateOptions) => string;
|
|
1914
|
+
|
|
1590
1915
|
export declare interface ResourceCanInput {
|
|
1591
1916
|
/** The action being gated (e.g. `'read'`, `'write'`, `'delete'`). */
|
|
1592
1917
|
readonly action: string;
|
|
@@ -1832,6 +2157,29 @@ export declare type SequenceResult<Ctx> = {
|
|
|
1832
2157
|
}>;
|
|
1833
2158
|
};
|
|
1834
2159
|
|
|
2160
|
+
export declare const ServerRequestContext: Context<ServerRequestContextValue | null>;
|
|
2161
|
+
|
|
2162
|
+
export declare interface ServerRequestContextValue {
|
|
2163
|
+
readonly cookies: Readonly<Record<string, string>>;
|
|
2164
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
2165
|
+
/** Raw request URL as it came off the wire (path + query). Useful
|
|
2166
|
+
* for SSR pages that need to read `?q=…` style search params
|
|
2167
|
+
* without touching anything client-only. Empty string for build-
|
|
2168
|
+
* time SSG renders where there is no incoming request. */
|
|
2169
|
+
readonly url: string;
|
|
2170
|
+
/** Present ONLY on the 422 re-render of a failed no-JS form POST:
|
|
2171
|
+
* the submitted values + field errors, keyed by formKey. Travels on
|
|
2172
|
+
* the EXISTING request context deliberately — a dedicated provider
|
|
2173
|
+
* would add a fiber fork the client boot does not have, and every
|
|
2174
|
+
* ancestor arity difference shifts every useId in the app. */
|
|
2175
|
+
readonly formFlash?: FormFlashPayload;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
export declare const ServerRequestProvider: ({ value, children, }: {
|
|
2179
|
+
readonly value: ServerRequestContextValue;
|
|
2180
|
+
readonly children: ReactNode;
|
|
2181
|
+
}) => ReactNode;
|
|
2182
|
+
|
|
1835
2183
|
/** Register (or clear) the app-wide notifier that `notify:` routes to. Call once
|
|
1836
2184
|
* at boot, next to your toast provider. */
|
|
1837
2185
|
export declare const setMutationNotifier: (notifier: MutationNotifier | undefined) => void;
|
|
@@ -2168,6 +2516,8 @@ export declare class SubscriptionCache {
|
|
|
2168
2516
|
private readonly inactiveTtlMs;
|
|
2169
2517
|
private readonly onChangeHook;
|
|
2170
2518
|
private readonly errorBus;
|
|
2519
|
+
private readonly mirror;
|
|
2520
|
+
private readonly mergeCell;
|
|
2171
2521
|
constructor(options?: SubscriptionCacheOptions);
|
|
2172
2522
|
/**
|
|
2173
2523
|
* Register a subscriber. The cache forks the underlying fiber on the
|
|
@@ -2383,6 +2733,17 @@ export declare class SubscriptionCache {
|
|
|
2383
2733
|
}
|
|
2384
2734
|
|
|
2385
2735
|
export declare interface SubscriptionCacheOptions {
|
|
2736
|
+
/** Durable local mirror (see {@link SubscriptionMirror}). */
|
|
2737
|
+
readonly mirror?: SubscriptionMirror;
|
|
2738
|
+
/**
|
|
2739
|
+
* CRDT cell merger for the downstream lane (plan 18): folds a
|
|
2740
|
+
* `mergeCells` delta op's incremental update into the held cell. The one
|
|
2741
|
+
* real implementation is `crdtMergeCell` from `@voltro/local-first` (this
|
|
2742
|
+
* package stays CRDT-library-free). Absent → a mergeCells op replaces the
|
|
2743
|
+
* cell (the producer only emits them when the app declares CRDT tables,
|
|
2744
|
+
* so an app without the binding never sees one).
|
|
2745
|
+
*/
|
|
2746
|
+
readonly mergeCell?: (column: string, prevValue: unknown, update: unknown) => unknown;
|
|
2386
2747
|
/** Milliseconds to keep an entry alive after the last subscriber leaves.
|
|
2387
2748
|
* Lets back-button / quick remount reuse the warm subscription. */
|
|
2388
2749
|
readonly inactiveTtlMs?: number;
|
|
@@ -2476,6 +2837,29 @@ export declare interface SubscriptionMeta {
|
|
|
2476
2837
|
readonly pendingPatches: number;
|
|
2477
2838
|
}
|
|
2478
2839
|
|
|
2840
|
+
/**
|
|
2841
|
+
* Durable local mirror seam (plan 02 — local-first). `save` fires after every
|
|
2842
|
+
* server event that moved an entry's base (snapshot or delta) with the rows
|
|
2843
|
+
* AND the revision; `load` runs once for a COLD entry (no server data yet)
|
|
2844
|
+
* and, when it resolves first, seeds the entry so the UI renders mirrored
|
|
2845
|
+
* rows offline — and the NEXT (re)subscribe presents the mirrored revision as
|
|
2846
|
+
* `voltro-resume-from`, composing with delta-resume. The implementation
|
|
2847
|
+
* (`@voltro/local-first`'s query mirror) owns partitioning, encrypted-column
|
|
2848
|
+
* stripping and which tags mirror at all — the cache calls it for every
|
|
2849
|
+
* entry and a foreign tag is the mirror's no-op.
|
|
2850
|
+
*/
|
|
2851
|
+
export declare interface SubscriptionMirror {
|
|
2852
|
+
readonly load: (key: string) => Promise<{
|
|
2853
|
+
readonly data: unknown;
|
|
2854
|
+
readonly revision: number;
|
|
2855
|
+
} | undefined>;
|
|
2856
|
+
readonly save: (key: string, snap: {
|
|
2857
|
+
readonly tag: string | undefined;
|
|
2858
|
+
readonly data: unknown;
|
|
2859
|
+
readonly revision: number;
|
|
2860
|
+
}) => Promise<void>;
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2479
2863
|
/**
|
|
2480
2864
|
* Subscribe to a streaming rpc by tag.
|
|
2481
2865
|
*
|
|
@@ -3048,8 +3432,107 @@ export declare interface UseFormBindingOptions<Input> {
|
|
|
3048
3432
|
readonly schema?: Schema.Schema.Any;
|
|
3049
3433
|
/** Initial field values. */
|
|
3050
3434
|
readonly defaults?: Partial<Input>;
|
|
3435
|
+
/** The failed no-JS POST's payload for THIS form (`useFormFlash` from
|
|
3436
|
+
* @voltro/web resolves it, SSR and client alike). When present it seeds
|
|
3437
|
+
* the initial values + field errors, so the 422 re-render shows the
|
|
3438
|
+
* submitted state server-side and hydrates to the identical state. */
|
|
3439
|
+
readonly flash?: FormFlashPayload | undefined;
|
|
3440
|
+
/** Locale for the built-in validation messages (en/de shipped). Default:
|
|
3441
|
+
* the document's `<html lang>` — the framework's locale contract. */
|
|
3442
|
+
readonly locale?: string;
|
|
3443
|
+
/** App override for message-id resolution — wire your i18n catalog in one
|
|
3444
|
+
* line: `messages: (id, params) => t(id, params)`. Return `undefined` to
|
|
3445
|
+
* fall through to the built-in defaults. Resolves BOTH schema-validation
|
|
3446
|
+
* ids (`validation.minLength`) and server ids (`validation.emailTaken`
|
|
3447
|
+
* from `ctx.validation.fail`). */
|
|
3448
|
+
readonly messages?: ValidationMessageResolver;
|
|
3449
|
+
/** Error-visibility timing — see {@link FormValidateOptions}. */
|
|
3450
|
+
readonly validate?: FormValidateOptions;
|
|
3451
|
+
/**
|
|
3452
|
+
* Form values ≠ mutation input — the mapping seam. Runs BEFORE validation:
|
|
3453
|
+
* the INPUT is validated against the schema, and its error paths map back
|
|
3454
|
+
* to form fields via {@link UseFormBindingOptions.errorPath} (same-name
|
|
3455
|
+
* fields map automatically).
|
|
3456
|
+
*
|
|
3457
|
+
* toInput: (values) => ({ id: employee.id, ...employeePatch(values) })
|
|
3458
|
+
*/
|
|
3459
|
+
readonly toInput?: (values: Partial<Input>) => unknown;
|
|
3460
|
+
/** Input-path → form-path mapping for `toInput` error routing, e.g.
|
|
3461
|
+
* `{ firstName: 'name' }`. Same-name paths need no entry. */
|
|
3462
|
+
readonly errorPath?: Readonly<Record<string, string>>;
|
|
3463
|
+
/**
|
|
3464
|
+
* Own the save: runs INSTEAD of the plain `mutate(input)`, with the mutation
|
|
3465
|
+
* handle in hand — for composed writes (mutation + links + follow-ups).
|
|
3466
|
+
* Server field errors thrown here still route to their fields.
|
|
3467
|
+
*
|
|
3468
|
+
* onSubmit: async ({ input, mutate, values }) => {
|
|
3469
|
+
* const row = await mutate(input)
|
|
3470
|
+
* await setLinks(values.assignedStores)
|
|
3471
|
+
* return row
|
|
3472
|
+
* }
|
|
3473
|
+
*/
|
|
3474
|
+
readonly onSubmit?: (args: {
|
|
3475
|
+
readonly input: unknown;
|
|
3476
|
+
readonly values: Partial<Input>;
|
|
3477
|
+
readonly mutate: (input: unknown) => Promise<unknown>;
|
|
3478
|
+
}) => Promise<unknown>;
|
|
3479
|
+
/** Ran after a successful submit with the mutation's output. */
|
|
3480
|
+
readonly onSuccess?: (output: unknown) => void;
|
|
3481
|
+
/**
|
|
3482
|
+
* Async field checks `submit` must wait for. Pass each field's
|
|
3483
|
+
* `useAsyncValidation` result under its field path:
|
|
3484
|
+
*
|
|
3485
|
+
* const email = useAsyncValidation('app', 'users.emailAvailable', values.email ?? '', { … })
|
|
3486
|
+
* const form = useFormBinding('app', 'users.create', { asyncFields: { email } })
|
|
3487
|
+
*
|
|
3488
|
+
* On submit, in-flight checks are awaited (bounded by `asyncTimeoutMs`),
|
|
3489
|
+
* an `'invalid'` verdict blocks the submit with the error on ITS field.
|
|
3490
|
+
*/
|
|
3491
|
+
readonly asyncFields?: Readonly<Record<string, AsyncValidationResult>>;
|
|
3492
|
+
/** Upper bound for waiting on `asyncFields` (ms, default 5000). On timeout
|
|
3493
|
+
* the still-checking field blocks the submit with `validation.checking`. */
|
|
3494
|
+
readonly asyncTimeoutMs?: number;
|
|
3495
|
+
/** The defaults hang on a subscription that has not delivered yet. While
|
|
3496
|
+
* true the form suppresses `dirty` and every error — render the skeleton,
|
|
3497
|
+
* not an empty form scolding the user. */
|
|
3498
|
+
readonly isLoading?: boolean;
|
|
3499
|
+
/** Re-render scope of THIS hook. `'all'` (default): re-render on every
|
|
3500
|
+
* engine change — simplest, correct everywhere. `'fields'`: the hook only
|
|
3501
|
+
* re-renders on submission-level changes; field components subscribe
|
|
3502
|
+
* narrowly via `useFormField(form, path)` so a keystroke re-renders one
|
|
3503
|
+
* field, not the page. In that mode `values`/`errors` on the binding are
|
|
3504
|
+
* the state as of the LAST binding render. */
|
|
3505
|
+
readonly subscribe?: 'all' | 'fields';
|
|
3051
3506
|
}
|
|
3052
3507
|
|
|
3508
|
+
/**
|
|
3509
|
+
* Narrow per-field subscription — the render-performance half of the engine.
|
|
3510
|
+
*
|
|
3511
|
+
* `useFormBinding` with `subscribe: 'fields'` re-renders only on
|
|
3512
|
+
* submission-level changes; each field COMPONENT calls this with the binding
|
|
3513
|
+
* and its path, and a keystroke re-renders that field alone:
|
|
3514
|
+
*
|
|
3515
|
+
* const Field = ({ form, path }: { form: FormBinding<I, O>; path: string }) => {
|
|
3516
|
+
* const f = useFormField(form, path)
|
|
3517
|
+
* return <input id={f.a11y.id} value={String(f.value ?? '')} onChange={(e) => f.setValue(e.target.value)} onBlur={f.onBlur} />
|
|
3518
|
+
* }
|
|
3519
|
+
*/
|
|
3520
|
+
export declare const useFormField: (binding: FormBinding<never, never> | FormBinding<Record<string, unknown>, unknown>, path: string) => FormFieldHandle;
|
|
3521
|
+
|
|
3522
|
+
/**
|
|
3523
|
+
* The failed-no-JS-POST payload for ONE form, or `undefined`.
|
|
3524
|
+
*
|
|
3525
|
+
* On the server (the 422 re-render) it comes off the request context; on the
|
|
3526
|
+
* client it is read back from the `#__voltro_form_flash__` JSON script the
|
|
3527
|
+
* same response embedded. Both carry the SAME payload, so a page that loads
|
|
3528
|
+
* its bundle after a native submit hydrates to exactly the server-rendered
|
|
3529
|
+
* state — errors visible, values filled, no mismatch.
|
|
3530
|
+
*
|
|
3531
|
+
* Keyed: only the form whose `formKey` was submitted receives the payload —
|
|
3532
|
+
* two `<AutoForm>`s on one page re-fill only the one that POSTed.
|
|
3533
|
+
*/
|
|
3534
|
+
export declare const useFormFlash: (formKey: string) => FormFlashPayload | undefined;
|
|
3535
|
+
|
|
3053
3536
|
/** The field shape a `<AutoForm mutation=…>` will render — from the mutation's
|
|
3054
3537
|
* input Schema. Use it to render a matching skeleton while anything the form
|
|
3055
3538
|
* depends on is still loading. */
|
|
@@ -3074,6 +3557,8 @@ export declare const useOutbox: (options: UseOutboxOptions) => OutboxControls;
|
|
|
3074
3557
|
export declare interface UseOutboxOptions {
|
|
3075
3558
|
/** Perform one queued write (the real mutation). Throw to fail. */
|
|
3076
3559
|
readonly send: (entry: OutboxEntry) => Promise<void>;
|
|
3560
|
+
/** Durable backing (see {@link OutboxPersistence}). Omitted → in-memory only. */
|
|
3561
|
+
readonly persistence?: OutboxPersistence;
|
|
3077
3562
|
/** Online override (defaults to navigator.onLine + online/offline events). */
|
|
3078
3563
|
readonly online?: boolean;
|
|
3079
3564
|
/** Classify a send error as a conflict (blocks replay) vs a transient
|
|
@@ -3198,6 +3683,8 @@ export declare interface UseSequenceResult {
|
|
|
3198
3683
|
readonly failedStep: string | undefined;
|
|
3199
3684
|
}
|
|
3200
3685
|
|
|
3686
|
+
export declare const useServerRequest: () => ServerRequestContextValue | null;
|
|
3687
|
+
|
|
3201
3688
|
export declare function useSubscription<T = unknown>(apiName: string, rpcTag: string, input: Readonly<Record<string, unknown>>, options: SubscriptionOptions<T> & {
|
|
3202
3689
|
readonly initialSnapshot: T;
|
|
3203
3690
|
}): SubscriptionStateWithFallback<T>;
|
|
@@ -3245,6 +3732,10 @@ export declare interface UseUndoOptions {
|
|
|
3245
3732
|
*/
|
|
3246
3733
|
export declare const useUpload: (apiName: string, hookOpts?: UploadOptions) => UploadHandle;
|
|
3247
3734
|
|
|
3735
|
+
/** The app-wide resolver, if a provider is mounted. Consumed by the form
|
|
3736
|
+
* binding; exported for widget kits that resolve ids themselves. */
|
|
3737
|
+
export declare const useValidationMessages: () => ValidationMessageResolver | undefined;
|
|
3738
|
+
|
|
3248
3739
|
export declare const useWindowedSubscription: <Row = Record<string, unknown>>(apiName: string, queryTag: string, options: UseWindowedSubscriptionOptions) => WindowedSubscriptionState<Row>;
|
|
3249
3740
|
|
|
3250
3741
|
export declare interface UseWindowedSubscriptionOptions {
|
|
@@ -3288,20 +3779,39 @@ export declare const useWorkflowUpdate: (apiName: string) => WorkflowUpdateState
|
|
|
3288
3779
|
export declare const validateEventPayload: (descriptor: EventDescriptor<string, unknown>, payload: unknown) => EventValidation;
|
|
3289
3780
|
|
|
3290
3781
|
/**
|
|
3291
|
-
* Validate `values` against an input `schema
|
|
3292
|
-
*
|
|
3293
|
-
*
|
|
3294
|
-
*
|
|
3295
|
-
*
|
|
3296
|
-
*
|
|
3297
|
-
* Client-side validation is non-authoritative UX — the server re-validates the
|
|
3298
|
-
* same schema on the mutation; this just gives instant inline feedback.
|
|
3782
|
+
* Validate `values` against an input `schema`. Uses the same
|
|
3783
|
+
* `decodeUnknownEither` path the store's `.validate(...)` uses, so client and
|
|
3784
|
+
* server speak ONE schema; `{ errors: 'all' }` collects every field in one
|
|
3785
|
+
* pass. Client-side validation is non-authoritative UX — the server
|
|
3786
|
+
* re-validates the same schema on the mutation.
|
|
3299
3787
|
*/
|
|
3300
|
-
export declare const validateFields: (schema: Schema.Schema.Any, values: unknown) => ValidationResult;
|
|
3788
|
+
export declare const validateFields: (schema: Schema.Schema.Any, values: unknown, options?: ValidateOptions) => ValidationResult;
|
|
3789
|
+
|
|
3790
|
+
export declare interface ValidateOptions {
|
|
3791
|
+
/** BCP-47-ish tag; only the primary subtag is read. Default: the document's
|
|
3792
|
+
* `<html lang>`, else `'en'`. */
|
|
3793
|
+
readonly locale?: string;
|
|
3794
|
+
readonly messages?: ValidationMessageResolver;
|
|
3795
|
+
}
|
|
3796
|
+
|
|
3797
|
+
/** Resolve one message id to display text. Return `undefined` to fall through
|
|
3798
|
+
* to the built-in en/de defaults — so a resolver only has to know the ids it
|
|
3799
|
+
* overrides. Wire it to `@voltro/i18n` in one line:
|
|
3800
|
+
* `messages: (id, params) => t(id, params)`. */
|
|
3801
|
+
export declare type ValidationMessageResolver = (id: string, params: Readonly<Record<string, unknown>> | undefined, fallback: string) => string | undefined;
|
|
3802
|
+
|
|
3803
|
+
export declare const ValidationMessagesProvider: (props: {
|
|
3804
|
+
readonly messages: ValidationMessageResolver;
|
|
3805
|
+
readonly children?: ReactNode;
|
|
3806
|
+
}) => ReactElement;
|
|
3301
3807
|
|
|
3302
3808
|
export declare interface ValidationResult {
|
|
3303
3809
|
readonly valid: boolean;
|
|
3810
|
+
/** Display text per field path — what `<AutoForm>` and custom widgets
|
|
3811
|
+
* render inline. First issue per field wins. */
|
|
3304
3812
|
readonly errors: FieldErrors;
|
|
3813
|
+
/** Every issue, structured — for widget kits that translate themselves. */
|
|
3814
|
+
readonly issues: ReadonlyArray<FieldIssue>;
|
|
3305
3815
|
}
|
|
3306
3816
|
|
|
3307
3817
|
export declare type ValidationStatus = 'idle' | 'checking' | 'valid' | 'invalid';
|
|
@@ -3325,7 +3835,7 @@ export declare const validationStatus: (args: {
|
|
|
3325
3835
|
/** The default widget kinds the framework can render from a schema. `'custom'`
|
|
3326
3836
|
* means "no default widget" — the field needs a render-prop (a nested object,
|
|
3327
3837
|
* an array of objects, or a multi-branch union). Mirrors innovation/01's set. */
|
|
3328
|
-
export declare type WidgetKind = 'text' | 'textarea' | 'number' | 'checkbox' | 'switch' | 'select' | 'radio' | 'async-select' | 'multi-select' | 'date' | 'datetime' | 'daterange' | 'file' | 'hidden' | 'custom';
|
|
3838
|
+
export declare type WidgetKind = 'text' | 'textarea' | 'number' | 'checkbox' | 'switch' | 'select' | 'radio' | 'async-select' | 'multi-select' | 'date' | 'datetime' | 'daterange' | 'file' | 'hidden' | 'array' | 'reference' | 'custom';
|
|
3329
3839
|
|
|
3330
3840
|
export declare interface WindowedSubscriptionState<Row> {
|
|
3331
3841
|
/** The in-window rows (live). */
|