@voltro/client 0.56.0 → 0.58.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
@@ -574,9 +574,20 @@ export declare type ClientDescriptorMap = Readonly<Record<string, ClientDescript
574
574
  export declare interface ClientErrorEvent {
575
575
  /** The thrown value (Error | anything). */
576
576
  readonly error: unknown;
577
- /** Where it came from. `route` = caught by the framework's route
578
- * ErrorBoundary (render OR loader); `manual` = app called a capture helper. */
579
- readonly source: 'route' | 'manual';
577
+ /**
578
+ * Where it came from.
579
+ *
580
+ * `route` — caught by the framework's route ErrorBoundary (render OR loader).
581
+ * `manual` — app called a capture helper.
582
+ * `rpc.*` — a call that failed in the CLIENT, forwarded from the rpc error
583
+ * bus. Kept as three separate labels rather than one `rpc`, because they are
584
+ * different incidents to whoever reads the report: a failed mutation is a
585
+ * write the user watched fail, while a failed subscription is a screen that
586
+ * silently never filled — `data: undefined`, `loading: false`, an empty list
587
+ * that reads as "there is nothing here". Collapsing them makes the second
588
+ * unfindable, and the second is the expensive one.
589
+ */
590
+ readonly source: 'route' | 'manual' | 'rpc.mutation' | 'rpc.action' | 'rpc.subscription';
580
591
  /** React component stack, when the route boundary caught a render error. */
581
592
  readonly componentStack?: string;
582
593
  /** Active pathname at the time, for context. */
@@ -960,6 +971,15 @@ export declare interface EventValidation {
960
971
  readonly errors: Readonly<Record<string, string>>;
961
972
  }
962
973
 
974
+ /** One currently-failing tag. */
975
+ export declare interface FailedCall {
976
+ readonly tag: string;
977
+ readonly source: 'mutation' | 'action' | 'subscription';
978
+ readonly error: unknown;
979
+ /** When it failed, so a UI can say "since 12:04" rather than only "broken". */
980
+ readonly at: number;
981
+ }
982
+
963
983
  export declare interface FieldDescriptor {
964
984
  /** Property name on the input object. */
965
985
  readonly name: string;
@@ -2077,7 +2097,17 @@ export declare interface ResumableAgentStreamState<E> {
2077
2097
  /** The UNWRAPPED inner events received so far, in order (deduped by seq). */
2078
2098
  readonly events: ReadonlyArray<E>;
2079
2099
  readonly status: ResumableStreamStatus;
2080
- /** How many times the transport dropped + auto-reconnected this run. */
2100
+ /**
2101
+ * How many times the transport dropped + auto-reconnected this run — the
2102
+ * RUN TOTAL, which is NOT the number `maxReconnects` bounds.
2103
+ *
2104
+ * `maxReconnects` is a ceiling on CONSECUTIVE reconnects that made no
2105
+ * progress, and the counter behind it resets whenever a reconnect delivers an
2106
+ * event. So this total can exceed it on a long, healthy stream, and rendering
2107
+ * "attempt {reconnects} of {maxReconnects}" puts `5/2` on the screen — which
2108
+ * is what a deployment saw. The two names sit next to each other and read as a
2109
+ * pair; they are not one, and there is no third field that is.
2110
+ */
2081
2111
  readonly reconnects: number;
2082
2112
  /** Set when status === 'error'. */
2083
2113
  readonly error: unknown | undefined;
@@ -2480,10 +2510,50 @@ export declare class RpcErrorBus {
2480
2510
  on(listener: RpcErrorListener): () => void;
2481
2511
  /** Emit. Pipeline code (useMutation / subscriptionCache) calls this. */
2482
2512
  emit(event: RpcError): void;
2513
+ /**
2514
+ * Subscribe to SUCCESSES — the channel that makes a consecutive-failure
2515
+ * ceiling mean what it says.
2516
+ *
2517
+ * ── Why the bus grew a second channel ──────────────────────────────────
2518
+ *
2519
+ * `wireAuthRefresh` bounds how often it may rebuild the transport on
2520
+ * `Unauthenticated`, and `maxConsecutive` bounds the TOTAL — after that many
2521
+ * refreshes *with no successful call in between*, it stops and leaves the
2522
+ * error to the app, because at that point the credential is not stale, it is
2523
+ * refused. "With no successful call in between" was carried by
2524
+ * `AuthRefreshHandle.noteSuccess()`, a method the HOST had to remember to
2525
+ * call, and the only host never called it.
2526
+ *
2527
+ * What that turns the ceiling into is the part worth writing down: not a
2528
+ * loop, but a LIFETIME budget. A tab that rotates its token three times over
2529
+ * a few hours exhausts it and then never refreshes again for as long as it
2530
+ * stays open — every later expiry rejects every subscription it opens, each
2531
+ * rejected entry is terminal for its transport, and the screen waits on a
2532
+ * spinner that has nothing left to wait for.
2533
+ *
2534
+ * A host that must remember something will eventually not, which is the
2535
+ * shape this repo keeps meeting. So the signal comes from the pipeline that
2536
+ * already knows — the same one that emits the errors — and no host has to
2537
+ * wire anything. `noteSuccess()` stays for a host with a better signal of its
2538
+ * own; it is no longer what the policy depends on.
2539
+ */
2540
+ onSuccess(listener: RpcSuccessListener): () => void;
2541
+ /** Emit a success. Called by the same pipeline code that calls `emit`. */
2542
+ emitSuccess(event: RpcSuccess): void;
2483
2543
  }
2484
2544
 
2485
2545
  export declare type RpcErrorListener = (event: RpcError) => void;
2486
2546
 
2547
+ /** A call that SUCCEEDED. Carries only its source and tag — there is no payload
2548
+ * here on purpose: this channel exists so cross-cutting policy can tell
2549
+ * "working again" from "still broken", not so listeners can read data. */
2550
+ export declare interface RpcSuccess {
2551
+ readonly source: 'mutation' | 'action' | 'subscription';
2552
+ readonly tag: string;
2553
+ }
2554
+
2555
+ export declare type RpcSuccessListener = (event: RpcSuccess) => void;
2556
+
2487
2557
  /**
2488
2558
  * Run a sequence. Resolves with a discriminated result rather than rejecting —
2489
2559
  * the same "handled" semantics `useMutation`/`useAction` use, so the call site
@@ -3243,6 +3313,14 @@ export declare interface SubscriptionFailed extends SubscriptionMeta {
3243
3313
  readonly error: unknown;
3244
3314
  }
3245
3315
 
3316
+ export declare interface SubscriptionHealth {
3317
+ /** Every tag whose last outcome on this api was a failure, newest first. */
3318
+ readonly failed: ReadonlyArray<FailedCall>;
3319
+ /** `false` as soon as anything is failing — the positive form, so a component
3320
+ * reads `if (!healthy)` instead of `if (failed.length > 0)`. */
3321
+ readonly healthy: boolean;
3322
+ }
3323
+
3246
3324
  /**
3247
3325
  * Deliberately NOT subscribed — `skip: true`.
3248
3326
  *
@@ -3568,6 +3646,45 @@ export declare interface TrackingSpec<P = Record<string, unknown>> {
3568
3646
  readonly map: TrackingMap<P>;
3569
3647
  }
3570
3648
 
3649
+ /** Where the one operation is. `uploading` and `transcribing` are separate
3650
+ * because their progress means different things and only one of them is
3651
+ * measurable. */
3652
+ export declare type TranscriptionPhase = 'idle' | 'uploading' | 'transcribing' | 'done' | 'error';
3653
+
3654
+ export declare interface TranscriptionSegmentView {
3655
+ readonly start: number;
3656
+ readonly end: number;
3657
+ readonly text: string;
3658
+ readonly speaker?: string;
3659
+ }
3660
+
3661
+ export declare interface TranscriptionState {
3662
+ readonly phase: TranscriptionPhase;
3663
+ /**
3664
+ * Progress of the CURRENT phase, 0..1 — and `undefined` while transcribing.
3665
+ *
3666
+ * Not a single number across both. An upload that finishes puts a combined bar
3667
+ * at 100% while the model has not started, and a bar that says finished is
3668
+ * worse than no bar. `undefined` is what an indeterminate spinner is for.
3669
+ */
3670
+ readonly progress: number | undefined;
3671
+ readonly result: TranscriptionView | undefined;
3672
+ readonly error: unknown | undefined;
3673
+ /** Upload the file, then transcribe it. Resolves the transcript. */
3674
+ readonly transcribe: (file: File | Blob) => Promise<TranscriptionView>;
3675
+ /** Abort an upload in flight and return to idle. A transcription already
3676
+ * handed to the server is NOT cancelled — say so rather than pretend. */
3677
+ readonly cancel: () => void;
3678
+ readonly reset: () => void;
3679
+ }
3680
+
3681
+ export declare interface TranscriptionView {
3682
+ readonly text: string;
3683
+ readonly language?: string;
3684
+ readonly durationSec?: number;
3685
+ readonly segments?: ReadonlyArray<TranscriptionSegmentView>;
3686
+ }
3687
+
3571
3688
  /**
3572
3689
  * Jump one instance to the state recorded at `index` — the devtools panel's
3573
3690
  * back/forward.
@@ -3740,6 +3857,13 @@ export declare type UploadStatus = 'idle' | 'uploading' | 'success' | 'error';
3740
3857
  * `<PermissionProvider>`. The hook form of `decideAccess`. */
3741
3858
  export declare const useAccessDecision: (guards: ReadonlyArray<ManifestGuard> | undefined) => AccessDecision;
3742
3859
 
3860
+ /**
3861
+ * Invoke a server `defineAction` procedure by tag. Unlike `useMutation`,
3862
+ * an action has no optimistic / cache surface — it's a unary call whose
3863
+ * effects (sends, captures, side-effecting writes the framework must NOT
3864
+ * preview locally) only become visible when the server pushes a delta on
3865
+ * an independent subscription.
3866
+ */
3743
3867
  export declare const useAction: <Input = unknown, Output = unknown>(apiName: string, rpcTag: string) => ActionState<Input, Output>;
3744
3868
 
3745
3869
  export declare const useAgent: (apiName: string, rpcTag: string) => AgentControls;
@@ -3883,7 +4007,26 @@ export declare interface UseEventOptions {
3883
4007
  readonly apiName?: string;
3884
4008
  }
3885
4009
 
3886
- export declare const useFormBinding: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(apiName: string, mutationTag: string, options: UseFormBindingOptions<Input>) => FormBinding<Input, Output>;
4010
+ export declare const useFormBinding: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(apiName: string,
4011
+ /**
4012
+ * The mutation to submit to — a tag, or a FUNCTION of the current values.
4013
+ *
4014
+ * The function form exists for the case where the form decides which mutation
4015
+ * it is: a calendar entry becomes a recurring SERIES the moment "repeats" is
4016
+ * ticked, and the series mutation takes eleven more fields. Deriving the tag
4017
+ * outside the binding is not available — the values belong to the binding and
4018
+ * do not exist before it — and re-mounting with a different tag resets the
4019
+ * engine, which throws away everything the user has typed.
4020
+ *
4021
+ * It does not: the engine is constructed once and never rebuilt, so the tag
4022
+ * may change on any render and the values, touched state and errors all
4023
+ * survive it. What changes with the tag is the SCHEMA the binding validates
4024
+ * against, which is the point.
4025
+ *
4026
+ * On the first render there are no values yet, so the function is called with
4027
+ * the seeded defaults — the same thing the user is looking at.
4028
+ */
4029
+ mutationTag: string | ((values: Partial<Input>) => string), options: UseFormBindingOptions<Input>) => FormBinding<Input, Output>;
3887
4030
 
3888
4031
  /** The enclosing binding, if a {@link FormBindingProvider} is mounted. */
3889
4032
  export declare const useFormBindingContext: () => FormBinding<Record<string, unknown>, unknown> | undefined;
@@ -4178,6 +4321,19 @@ export declare function useSubscription<T = unknown>(apiName: string, rpcTag: st
4178
4321
  readonly skip?: boolean;
4179
4322
  }): SubscriptionState<T> | SubscriptionIdle;
4180
4323
 
4324
+ /**
4325
+ * The api's currently-refused calls.
4326
+ *
4327
+ * A tag enters the list when a call for it fails and LEAVES it when a call for
4328
+ * the same tag succeeds — keyed by tag rather than counted, because the
4329
+ * question a screen asks is "is this working now", and two failures of one tag
4330
+ * are one broken thing.
4331
+ *
4332
+ * const { healthy, failed } = useSubscriptionHealth('app')
4333
+ * if (!healthy) return <Banner tags={failed.map((f) => f.tag)} />
4334
+ */
4335
+ export declare const useSubscriptionHealth: (apiName: string) => SubscriptionHealth;
4336
+
4181
4337
  /** The column shape a `<DataTable query=…>` will render — from the query's
4182
4338
  * output Schema. */
4183
4339
  export declare const useTableSkeleton: (apiName: string, queryTag: string) => ReadonlyArray<FieldDescriptor>;
@@ -4190,6 +4346,20 @@ export declare const useTableSkeleton: (apiName: string, queryTag: string) => Re
4190
4346
  */
4191
4347
  export declare const useTracking: <P extends object>(spec: TrackingSpec<P>, props: P, sink: TrackingSink) => P;
4192
4348
 
4349
+ export declare const useTranscription: (apiName: string, options: UseTranscriptionOptions) => TranscriptionState;
4350
+
4351
+ export declare interface UseTranscriptionOptions {
4352
+ /**
4353
+ * The action that transcribes an uploaded file.
4354
+ *
4355
+ * Named rather than assumed: the app declares it, guards it, and decides which
4356
+ * model runs. Its input is `{ storageRef }` plus whatever `extra` is passed.
4357
+ */
4358
+ readonly action: string;
4359
+ /** Extra input merged into every call — a language hint, a thread id. */
4360
+ readonly extra?: Readonly<Record<string, unknown>>;
4361
+ }
4362
+
4193
4363
  export declare const useUndo: (options: UseUndoOptions) => UndoControls;
4194
4364
 
4195
4365
  export declare const useUndoLog: (apiName: string, options?: {