@voltro/client 0.55.0 → 0.57.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 +691 -0
- package/dist/form.js +1 -1
- package/dist/{formData-DtlF8t8h.js → formData-He_L4ozH.js} +8 -5
- package/dist/index.d.ts +134 -3
- package/dist/index.js +866 -809
- package/package.json +2 -2
package/dist/form.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as e, S as t, _ as n, a as r, b as i, g as a, i as o, n as s, o as c, r as l, t as u, v as d, w as f, x as p, y as m } from "./formData-
|
|
1
|
+
import { C as e, S as t, _ as n, a as r, b as i, g as a, i as o, n as s, o as c, r as l, t as u, v as d, w as f, x as p, y as m } from "./formData-He_L4ozH.js";
|
|
2
2
|
export { u as FORM_FLASH_SCRIPT_ID, s as FORM_KEY_FIELD, l as FORM_PATH_PREFIX, o as FORM_REDIRECT_FIELD, a as decodeFieldIssues, n as documentLocale, r as formActionPath, c as formDataToInput, p as formField, t as formSections, d as resolveValidationMessage, m as restrictToSchema, e as schemaToColumns, f as schemaToFields, i as validateFields };
|
|
@@ -187,17 +187,20 @@ var r = (e) => ({ jsonSchema: { "x-voltro": e } }), i = "#/$defs/", a = (e) => e
|
|
|
187
187
|
"validation.invalidFileType": "Dieser Dateityp ist nicht erlaubt",
|
|
188
188
|
"validation.fileTooLarge": "Die Datei ist zu groß"
|
|
189
189
|
}
|
|
190
|
-
}, b = (e, t) =>
|
|
191
|
-
let r =
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
}, b = (e, t) => {
|
|
191
|
+
let n = !1, r = e.replace(/\{(\w+)\}/g, (e, r) => {
|
|
192
|
+
let i = t?.[r];
|
|
193
|
+
return i === void 0 ? (n = !0, e) : String(i);
|
|
194
|
+
});
|
|
195
|
+
return n ? void 0 : r;
|
|
196
|
+
}, x = () => {
|
|
194
197
|
let e = globalThis.document?.documentElement?.lang ?? "";
|
|
195
198
|
return e === "" ? "en" : e;
|
|
196
199
|
}, S = (e, t) => {
|
|
197
200
|
let n = t?.messages?.(e.id, e.params, e.fallback);
|
|
198
201
|
if (n !== void 0) return n;
|
|
199
202
|
let r = y[(t?.locale ?? x()).toLowerCase().split("-")[0] ?? "en"]?.[e.id] ?? y.en?.[e.id];
|
|
200
|
-
return r === void 0 ?
|
|
203
|
+
return (r === void 0 ? void 0 : b(r, e.params)) ?? e.fallback;
|
|
201
204
|
}, C = (e) => {
|
|
202
205
|
let t = /^([a-z][\w-]*(?:\.[\w-]+)+)\|(\{.*\})$/.exec(e);
|
|
203
206
|
if (t !== null && t[1] !== void 0 && t[2] !== void 0) try {
|
package/dist/index.d.ts
CHANGED
|
@@ -960,6 +960,15 @@ export declare interface EventValidation {
|
|
|
960
960
|
readonly errors: Readonly<Record<string, string>>;
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
+
/** One currently-failing tag. */
|
|
964
|
+
export declare interface FailedCall {
|
|
965
|
+
readonly tag: string;
|
|
966
|
+
readonly source: 'mutation' | 'action' | 'subscription';
|
|
967
|
+
readonly error: unknown;
|
|
968
|
+
/** When it failed, so a UI can say "since 12:04" rather than only "broken". */
|
|
969
|
+
readonly at: number;
|
|
970
|
+
}
|
|
971
|
+
|
|
963
972
|
export declare interface FieldDescriptor {
|
|
964
973
|
/** Property name on the input object. */
|
|
965
974
|
readonly name: string;
|
|
@@ -1283,15 +1292,40 @@ export declare interface FrameworkRuntimes {
|
|
|
1283
1292
|
readonly get: (name: string) => ApiHandle;
|
|
1284
1293
|
/** Enumerate the names of currently-installed apis (for debug). */
|
|
1285
1294
|
readonly names: ReadonlyArray<string>;
|
|
1295
|
+
/**
|
|
1296
|
+
* "Something in this tree needs `apiName`."
|
|
1297
|
+
*
|
|
1298
|
+
* Under `web.api.connect: 'lazy'` the provider does not open a socket until
|
|
1299
|
+
* this is called, so a page that reads no data never opens one. Under the
|
|
1300
|
+
* default `'eager'` it is a no-op — every api connects at mount, as before.
|
|
1301
|
+
*
|
|
1302
|
+
* It exists because the alternative was worse in a specific, measured way:
|
|
1303
|
+
* `interactive: 'full'` is the DEFAULT render mode, and the client opened a
|
|
1304
|
+
* WebSocket on every such page whether or not it subscribed. A pre-rendered
|
|
1305
|
+
* documentation page with no data at all opened one (measured in a real
|
|
1306
|
+
* browser: 0 sockets on `interactive: 'none'` / `'islands'`, one on `'full'`).
|
|
1307
|
+
* And an open socket keeps a dormancy-managed instance awake — `isIdleNow`
|
|
1308
|
+
* returns false while `connectedClients() > 0` — so one browser tab on a
|
|
1309
|
+
* pricing page prevented scale-to-zero indefinitely.
|
|
1310
|
+
*
|
|
1311
|
+
* Optional so a foreign host embedding `FrameworkRuntimesContext` by hand
|
|
1312
|
+
* keeps working; absent means "already connected", which is the safe reading.
|
|
1313
|
+
*/
|
|
1314
|
+
readonly demand?: (apiName: string) => void;
|
|
1286
1315
|
}
|
|
1287
1316
|
|
|
1288
1317
|
export declare const FrameworkRuntimesContext: Context<FrameworkRuntimes | null>;
|
|
1289
1318
|
|
|
1290
|
-
export declare const FrameworkRuntimesProvider: ({ apis, children, }: FrameworkRuntimesProviderProps) => ReactNode;
|
|
1319
|
+
export declare const FrameworkRuntimesProvider: ({ apis, children, demand, }: FrameworkRuntimesProviderProps) => ReactNode;
|
|
1291
1320
|
|
|
1292
1321
|
export declare interface FrameworkRuntimesProviderProps {
|
|
1293
1322
|
readonly apis: ReadonlyMap<string, ApiHandle>;
|
|
1294
1323
|
readonly children: ReactNode;
|
|
1324
|
+
/** Passed straight through to the context so `useFrameworkApi` can say that
|
|
1325
|
+
* something in the tree needs an api. The web boot supplies it under
|
|
1326
|
+
* `web.api.connect: 'lazy'`; a foreign host may omit it, which reads as
|
|
1327
|
+
* "already connected". */
|
|
1328
|
+
readonly demand?: (apiName: string) => void;
|
|
1295
1329
|
}
|
|
1296
1330
|
|
|
1297
1331
|
/** Browser-safe unique idempotency key: prefer `crypto.randomUUID`, fall back to
|
|
@@ -2052,7 +2086,17 @@ export declare interface ResumableAgentStreamState<E> {
|
|
|
2052
2086
|
/** The UNWRAPPED inner events received so far, in order (deduped by seq). */
|
|
2053
2087
|
readonly events: ReadonlyArray<E>;
|
|
2054
2088
|
readonly status: ResumableStreamStatus;
|
|
2055
|
-
/**
|
|
2089
|
+
/**
|
|
2090
|
+
* How many times the transport dropped + auto-reconnected this run — the
|
|
2091
|
+
* RUN TOTAL, which is NOT the number `maxReconnects` bounds.
|
|
2092
|
+
*
|
|
2093
|
+
* `maxReconnects` is a ceiling on CONSECUTIVE reconnects that made no
|
|
2094
|
+
* progress, and the counter behind it resets whenever a reconnect delivers an
|
|
2095
|
+
* event. So this total can exceed it on a long, healthy stream, and rendering
|
|
2096
|
+
* "attempt {reconnects} of {maxReconnects}" puts `5/2` on the screen — which
|
|
2097
|
+
* is what a deployment saw. The two names sit next to each other and read as a
|
|
2098
|
+
* pair; they are not one, and there is no third field that is.
|
|
2099
|
+
*/
|
|
2056
2100
|
readonly reconnects: number;
|
|
2057
2101
|
/** Set when status === 'error'. */
|
|
2058
2102
|
readonly error: unknown | undefined;
|
|
@@ -2455,10 +2499,50 @@ export declare class RpcErrorBus {
|
|
|
2455
2499
|
on(listener: RpcErrorListener): () => void;
|
|
2456
2500
|
/** Emit. Pipeline code (useMutation / subscriptionCache) calls this. */
|
|
2457
2501
|
emit(event: RpcError): void;
|
|
2502
|
+
/**
|
|
2503
|
+
* Subscribe to SUCCESSES — the channel that makes a consecutive-failure
|
|
2504
|
+
* ceiling mean what it says.
|
|
2505
|
+
*
|
|
2506
|
+
* ── Why the bus grew a second channel ──────────────────────────────────
|
|
2507
|
+
*
|
|
2508
|
+
* `wireAuthRefresh` bounds how often it may rebuild the transport on
|
|
2509
|
+
* `Unauthenticated`, and `maxConsecutive` bounds the TOTAL — after that many
|
|
2510
|
+
* refreshes *with no successful call in between*, it stops and leaves the
|
|
2511
|
+
* error to the app, because at that point the credential is not stale, it is
|
|
2512
|
+
* refused. "With no successful call in between" was carried by
|
|
2513
|
+
* `AuthRefreshHandle.noteSuccess()`, a method the HOST had to remember to
|
|
2514
|
+
* call, and the only host never called it.
|
|
2515
|
+
*
|
|
2516
|
+
* What that turns the ceiling into is the part worth writing down: not a
|
|
2517
|
+
* loop, but a LIFETIME budget. A tab that rotates its token three times over
|
|
2518
|
+
* a few hours exhausts it and then never refreshes again for as long as it
|
|
2519
|
+
* stays open — every later expiry rejects every subscription it opens, each
|
|
2520
|
+
* rejected entry is terminal for its transport, and the screen waits on a
|
|
2521
|
+
* spinner that has nothing left to wait for.
|
|
2522
|
+
*
|
|
2523
|
+
* A host that must remember something will eventually not, which is the
|
|
2524
|
+
* shape this repo keeps meeting. So the signal comes from the pipeline that
|
|
2525
|
+
* already knows — the same one that emits the errors — and no host has to
|
|
2526
|
+
* wire anything. `noteSuccess()` stays for a host with a better signal of its
|
|
2527
|
+
* own; it is no longer what the policy depends on.
|
|
2528
|
+
*/
|
|
2529
|
+
onSuccess(listener: RpcSuccessListener): () => void;
|
|
2530
|
+
/** Emit a success. Called by the same pipeline code that calls `emit`. */
|
|
2531
|
+
emitSuccess(event: RpcSuccess): void;
|
|
2458
2532
|
}
|
|
2459
2533
|
|
|
2460
2534
|
export declare type RpcErrorListener = (event: RpcError) => void;
|
|
2461
2535
|
|
|
2536
|
+
/** A call that SUCCEEDED. Carries only its source and tag — there is no payload
|
|
2537
|
+
* here on purpose: this channel exists so cross-cutting policy can tell
|
|
2538
|
+
* "working again" from "still broken", not so listeners can read data. */
|
|
2539
|
+
export declare interface RpcSuccess {
|
|
2540
|
+
readonly source: 'mutation' | 'action' | 'subscription';
|
|
2541
|
+
readonly tag: string;
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
export declare type RpcSuccessListener = (event: RpcSuccess) => void;
|
|
2545
|
+
|
|
2462
2546
|
/**
|
|
2463
2547
|
* Run a sequence. Resolves with a discriminated result rather than rejecting —
|
|
2464
2548
|
* the same "handled" semantics `useMutation`/`useAction` use, so the call site
|
|
@@ -3218,6 +3302,14 @@ export declare interface SubscriptionFailed extends SubscriptionMeta {
|
|
|
3218
3302
|
readonly error: unknown;
|
|
3219
3303
|
}
|
|
3220
3304
|
|
|
3305
|
+
export declare interface SubscriptionHealth {
|
|
3306
|
+
/** Every tag whose last outcome on this api was a failure, newest first. */
|
|
3307
|
+
readonly failed: ReadonlyArray<FailedCall>;
|
|
3308
|
+
/** `false` as soon as anything is failing — the positive form, so a component
|
|
3309
|
+
* reads `if (!healthy)` instead of `if (failed.length > 0)`. */
|
|
3310
|
+
readonly healthy: boolean;
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3221
3313
|
/**
|
|
3222
3314
|
* Deliberately NOT subscribed — `skip: true`.
|
|
3223
3315
|
*
|
|
@@ -3715,6 +3807,13 @@ export declare type UploadStatus = 'idle' | 'uploading' | 'success' | 'error';
|
|
|
3715
3807
|
* `<PermissionProvider>`. The hook form of `decideAccess`. */
|
|
3716
3808
|
export declare const useAccessDecision: (guards: ReadonlyArray<ManifestGuard> | undefined) => AccessDecision;
|
|
3717
3809
|
|
|
3810
|
+
/**
|
|
3811
|
+
* Invoke a server `defineAction` procedure by tag. Unlike `useMutation`,
|
|
3812
|
+
* an action has no optimistic / cache surface — it's a unary call whose
|
|
3813
|
+
* effects (sends, captures, side-effecting writes the framework must NOT
|
|
3814
|
+
* preview locally) only become visible when the server pushes a delta on
|
|
3815
|
+
* an independent subscription.
|
|
3816
|
+
*/
|
|
3718
3817
|
export declare const useAction: <Input = unknown, Output = unknown>(apiName: string, rpcTag: string) => ActionState<Input, Output>;
|
|
3719
3818
|
|
|
3720
3819
|
export declare const useAgent: (apiName: string, rpcTag: string) => AgentControls;
|
|
@@ -3858,7 +3957,26 @@ export declare interface UseEventOptions {
|
|
|
3858
3957
|
readonly apiName?: string;
|
|
3859
3958
|
}
|
|
3860
3959
|
|
|
3861
|
-
export declare const useFormBinding: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(apiName: string,
|
|
3960
|
+
export declare const useFormBinding: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(apiName: string,
|
|
3961
|
+
/**
|
|
3962
|
+
* The mutation to submit to — a tag, or a FUNCTION of the current values.
|
|
3963
|
+
*
|
|
3964
|
+
* The function form exists for the case where the form decides which mutation
|
|
3965
|
+
* it is: a calendar entry becomes a recurring SERIES the moment "repeats" is
|
|
3966
|
+
* ticked, and the series mutation takes eleven more fields. Deriving the tag
|
|
3967
|
+
* outside the binding is not available — the values belong to the binding and
|
|
3968
|
+
* do not exist before it — and re-mounting with a different tag resets the
|
|
3969
|
+
* engine, which throws away everything the user has typed.
|
|
3970
|
+
*
|
|
3971
|
+
* It does not: the engine is constructed once and never rebuilt, so the tag
|
|
3972
|
+
* may change on any render and the values, touched state and errors all
|
|
3973
|
+
* survive it. What changes with the tag is the SCHEMA the binding validates
|
|
3974
|
+
* against, which is the point.
|
|
3975
|
+
*
|
|
3976
|
+
* On the first render there are no values yet, so the function is called with
|
|
3977
|
+
* the seeded defaults — the same thing the user is looking at.
|
|
3978
|
+
*/
|
|
3979
|
+
mutationTag: string | ((values: Partial<Input>) => string), options: UseFormBindingOptions<Input>) => FormBinding<Input, Output>;
|
|
3862
3980
|
|
|
3863
3981
|
/** The enclosing binding, if a {@link FormBindingProvider} is mounted. */
|
|
3864
3982
|
export declare const useFormBindingContext: () => FormBinding<Record<string, unknown>, unknown> | undefined;
|
|
@@ -4153,6 +4271,19 @@ export declare function useSubscription<T = unknown>(apiName: string, rpcTag: st
|
|
|
4153
4271
|
readonly skip?: boolean;
|
|
4154
4272
|
}): SubscriptionState<T> | SubscriptionIdle;
|
|
4155
4273
|
|
|
4274
|
+
/**
|
|
4275
|
+
* The api's currently-refused calls.
|
|
4276
|
+
*
|
|
4277
|
+
* A tag enters the list when a call for it fails and LEAVES it when a call for
|
|
4278
|
+
* the same tag succeeds — keyed by tag rather than counted, because the
|
|
4279
|
+
* question a screen asks is "is this working now", and two failures of one tag
|
|
4280
|
+
* are one broken thing.
|
|
4281
|
+
*
|
|
4282
|
+
* const { healthy, failed } = useSubscriptionHealth('app')
|
|
4283
|
+
* if (!healthy) return <Banner tags={failed.map((f) => f.tag)} />
|
|
4284
|
+
*/
|
|
4285
|
+
export declare const useSubscriptionHealth: (apiName: string) => SubscriptionHealth;
|
|
4286
|
+
|
|
4156
4287
|
/** The column shape a `<DataTable query=…>` will render — from the query's
|
|
4157
4288
|
* output Schema. */
|
|
4158
4289
|
export declare const useTableSkeleton: (apiName: string, queryTag: string) => ReadonlyArray<FieldDescriptor>;
|