@voltro/client 0.8.0 → 0.10.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 +329 -7
- package/dist/index.d.ts +166 -7
- package/dist/index.js +383 -308
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -384,6 +384,14 @@ export declare interface ClientTraceEvent {
|
|
|
384
384
|
|
|
385
385
|
declare type ClientTraceListener = (event: ClientTraceEvent) => void;
|
|
386
386
|
|
|
387
|
+
/** A compensation that itself failed. Surfaced rather than swallowed: the app
|
|
388
|
+
* now has an orphan it may need to reconcile, and hiding that is how orphans
|
|
389
|
+
* become permanent. */
|
|
390
|
+
export declare interface CompensationFailure {
|
|
391
|
+
readonly step: string;
|
|
392
|
+
readonly error: unknown;
|
|
393
|
+
}
|
|
394
|
+
|
|
387
395
|
/** Pure virtualization math: scroll position → the row window to fetch +
|
|
388
396
|
* the spacer heights. `total` (when known) caps the window + sizes the bottom
|
|
389
397
|
* spacer; without it, infinite-scroll (bottomSpacer 0). */
|
|
@@ -1115,6 +1123,26 @@ export declare class RpcErrorBus {
|
|
|
1115
1123
|
|
|
1116
1124
|
export declare type RpcErrorListener = (event: RpcError) => void;
|
|
1117
1125
|
|
|
1126
|
+
/**
|
|
1127
|
+
* Run a sequence. Resolves with a discriminated result rather than rejecting —
|
|
1128
|
+
* the same "handled" semantics `useMutation`/`useAction` use, so the call site
|
|
1129
|
+
* loses its try/catch without losing the failure.
|
|
1130
|
+
*/
|
|
1131
|
+
export declare const runSequence: <Ctx>(builder: SequenceBuilder<Ctx>, options?: RunSequenceOptions) => Promise<SequenceResult<Ctx>>;
|
|
1132
|
+
|
|
1133
|
+
export declare interface RunSequenceOptions {
|
|
1134
|
+
/** Called once, with the original failure. The whole point of the primitive:
|
|
1135
|
+
* one handler for the sequence instead of a try/catch per step. */
|
|
1136
|
+
readonly onError?: (error: unknown, detail: {
|
|
1137
|
+
readonly failedStep: string;
|
|
1138
|
+
readonly compensated: ReadonlyArray<string>;
|
|
1139
|
+
readonly compensationFailures: ReadonlyArray<CompensationFailure>;
|
|
1140
|
+
}) => void;
|
|
1141
|
+
/** Skip compensation entirely. For a sequence whose steps are all idempotent
|
|
1142
|
+
* upserts, undoing is churn rather than cleanup. */
|
|
1143
|
+
readonly compensate?: boolean;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1118
1146
|
/**
|
|
1119
1147
|
* Derive table COLUMNS from a query's output `Schema` — an
|
|
1120
1148
|
* `Schema.Array(Schema.Struct({...}))`. Drills into the array's element and maps
|
|
@@ -1137,6 +1165,36 @@ export declare interface SeqElement<E> {
|
|
|
1137
1165
|
readonly event: E;
|
|
1138
1166
|
}
|
|
1139
1167
|
|
|
1168
|
+
/** Start a sequence. Pure and React-free, so it is testable on its own. */
|
|
1169
|
+
export declare const sequence: () => SequenceBuilder<Record<never, never>>;
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* A sequence under construction. Each `.step()` widens the context type, so a
|
|
1173
|
+
* later step reads earlier results by name with real types — `linkThread` can
|
|
1174
|
+
* see both `ctx.ticket` and `ctx.thread` without threading them by hand.
|
|
1175
|
+
*/
|
|
1176
|
+
export declare interface SequenceBuilder<Ctx> {
|
|
1177
|
+
step<Name extends string, Result>(name: Name, run: (ctx: Ctx) => Result | Promise<Result>, options?: StepOptions<Result>): SequenceBuilder<Ctx & {
|
|
1178
|
+
readonly [K in Name]: Result;
|
|
1179
|
+
}>;
|
|
1180
|
+
/* Excluded from this release type: steps */
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
export declare type SequenceResult<Ctx> = {
|
|
1184
|
+
readonly ok: true;
|
|
1185
|
+
readonly data: Ctx;
|
|
1186
|
+
} | {
|
|
1187
|
+
readonly ok: false;
|
|
1188
|
+
/** The step whose `run` threw. Its `undo` was NOT called — see the header. */
|
|
1189
|
+
readonly failedStep: string;
|
|
1190
|
+
/** The original failure. Never replaced by a compensation error. */
|
|
1191
|
+
readonly error: unknown;
|
|
1192
|
+
/** Names of the steps whose `undo` ran, newest first. */
|
|
1193
|
+
readonly compensated: ReadonlyArray<string>;
|
|
1194
|
+
/** Undos that threw. Non-empty means something is left over upstream. */
|
|
1195
|
+
readonly compensationFailures: ReadonlyArray<CompensationFailure>;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1140
1198
|
/** Register (or clear) the app-wide notifier that `notify:` routes to. Call once
|
|
1141
1199
|
* at boot, next to your toast provider. */
|
|
1142
1200
|
export declare const setMutationNotifier: (notifier: MutationNotifier | undefined) => void;
|
|
@@ -1191,6 +1249,22 @@ export declare const stableKey: (key: ReadonlyArray<unknown>) => string;
|
|
|
1191
1249
|
*/
|
|
1192
1250
|
export declare const startMutation: (args: Pick<MutationEvent, "id" | "apiName" | "tag" | "input">) => void;
|
|
1193
1251
|
|
|
1252
|
+
declare interface Step {
|
|
1253
|
+
readonly name: string;
|
|
1254
|
+
readonly run: (ctx: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
1255
|
+
readonly undo: StepUndo<never> | undefined;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
export declare interface StepOptions<Result> {
|
|
1259
|
+
/** How to reverse this step if a LATER step fails. Omit when the step has no
|
|
1260
|
+
* externally visible effect worth reversing. */
|
|
1261
|
+
readonly undo?: StepUndo<Result>;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/** One step's compensation. Receives the step's OWN result, which is the point:
|
|
1265
|
+
* `undo: (created) => remove.run({ id: created.id })`. */
|
|
1266
|
+
export declare type StepUndo<Result> = (result: Result) => unknown | Promise<unknown>;
|
|
1267
|
+
|
|
1194
1268
|
/** Subscribe to client errors. Returns an unsubscribe fn. */
|
|
1195
1269
|
export declare const subscribeClientErrors: (listener: ClientErrorListener) => (() => void);
|
|
1196
1270
|
|
|
@@ -1350,6 +1424,34 @@ export declare interface SubscriptionCacheOptions {
|
|
|
1350
1424
|
readonly errorBus?: RpcErrorBus;
|
|
1351
1425
|
}
|
|
1352
1426
|
|
|
1427
|
+
/**
|
|
1428
|
+
* Deliberately NOT subscribed — `skip: true`.
|
|
1429
|
+
*
|
|
1430
|
+
* This state exists because `loading` was answering two different questions
|
|
1431
|
+
* with one boolean: "waiting for the first snapshot" and "not asking at all".
|
|
1432
|
+
* A skipped subscription used to report `loading: true` forever, so the blessed
|
|
1433
|
+
* pattern
|
|
1434
|
+
*
|
|
1435
|
+
* if (loading) return <Skeleton/>
|
|
1436
|
+
*
|
|
1437
|
+
* rendered a permanent skeleton for a query the app had consciously switched
|
|
1438
|
+
* off — and `skip: !currentUser?.id` / `skip: !open` are among the most common
|
|
1439
|
+
* things anyone writes. Call sites only survived it by carrying a redundant
|
|
1440
|
+
* `if (!currentUser) return null` in front, which is the very check `loading`
|
|
1441
|
+
* was supposed to replace.
|
|
1442
|
+
*
|
|
1443
|
+
* `loading` is `false` here: nothing is in flight. `data` is `undefined`,
|
|
1444
|
+
* which is why this state is only handed to callers that actually pass a
|
|
1445
|
+
* dynamic `skip` — see the overloads. Everyone else keeps the two-state union
|
|
1446
|
+
* where `!loading` still proves `data` is present.
|
|
1447
|
+
*/
|
|
1448
|
+
export declare interface SubscriptionIdle extends SubscriptionMeta {
|
|
1449
|
+
readonly data: undefined;
|
|
1450
|
+
readonly loading: false;
|
|
1451
|
+
readonly isEmpty: false;
|
|
1452
|
+
readonly idle: true;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1353
1455
|
/** The fields that mean the same thing in every subscription state. */
|
|
1354
1456
|
export declare interface SubscriptionMeta {
|
|
1355
1457
|
/** Server revision counter; -1 before first snapshot. */
|
|
@@ -1407,6 +1509,9 @@ export declare interface SubscriptionOptions<T = unknown> {
|
|
|
1407
1509
|
export declare interface SubscriptionPending extends SubscriptionMeta {
|
|
1408
1510
|
readonly data: undefined;
|
|
1409
1511
|
readonly loading: true;
|
|
1512
|
+
/** Always `false` — this subscription IS asking, it just has no answer yet.
|
|
1513
|
+
* See {@link SubscriptionIdle} for the deliberately-not-asking case. */
|
|
1514
|
+
readonly idle: false;
|
|
1410
1515
|
/** Always `false` while pending. "Empty" is a statement about data that
|
|
1411
1516
|
* ARRIVED; before then it would be a guess, and rendering an empty state on
|
|
1412
1517
|
* that guess is the flash-of-empty-state bug. */
|
|
@@ -1417,6 +1522,8 @@ export declare interface SubscriptionPending extends SubscriptionMeta {
|
|
|
1417
1522
|
export declare interface SubscriptionSettled<T> extends SubscriptionMeta {
|
|
1418
1523
|
readonly data: T;
|
|
1419
1524
|
readonly loading: false;
|
|
1525
|
+
/** Always `false` — data arrived. */
|
|
1526
|
+
readonly idle: false;
|
|
1420
1527
|
/** The data that arrived is empty — an empty array, or a null value. */
|
|
1421
1528
|
readonly isEmpty: boolean;
|
|
1422
1529
|
}
|
|
@@ -1451,6 +1558,10 @@ export declare type SubscriptionState<T> = SubscriptionPending | SubscriptionSet
|
|
|
1451
1558
|
export declare interface SubscriptionStateWithFallback<T> extends SubscriptionMeta {
|
|
1452
1559
|
readonly data: T;
|
|
1453
1560
|
readonly loading: boolean;
|
|
1561
|
+
/** True when `skip` is on. `data` is the fallback, and no request is in
|
|
1562
|
+
* flight — distinguishes "showing the fallback because we chose not to ask"
|
|
1563
|
+
* from "…because the answer has not arrived". */
|
|
1564
|
+
readonly idle: boolean;
|
|
1454
1565
|
readonly isEmpty: boolean;
|
|
1455
1566
|
}
|
|
1456
1567
|
|
|
@@ -1854,11 +1965,54 @@ export declare interface UseResourceCanState {
|
|
|
1854
1965
|
|
|
1855
1966
|
export declare const useResumableAgentStream: <E = unknown>(apiName: string, rpcTag: string, options?: ResumableAgentStreamOptions<E>) => ResumableAgentStreamControls<E>;
|
|
1856
1967
|
|
|
1968
|
+
/**
|
|
1969
|
+
* @param options Sequence-wide defaults — most usefully `onError`, which fires
|
|
1970
|
+
* once per failed sequence instead of once per step.
|
|
1971
|
+
*
|
|
1972
|
+
* ```tsx
|
|
1973
|
+
* const seq = useSequence({ onError: (e) => notify.error(String(e)) })
|
|
1974
|
+
*
|
|
1975
|
+
* const attach = async (file: File) => {
|
|
1976
|
+
* const result = await seq.run(
|
|
1977
|
+
* sequence()
|
|
1978
|
+
* .step('upload', () => upload.run({ file }), {
|
|
1979
|
+
* undo: (r) => removeObject.run({ id: r.id }), // the created id, right here
|
|
1980
|
+
* })
|
|
1981
|
+
* .step('attach', (c) => createAttachment.run({ refId: c.upload.id })),
|
|
1982
|
+
* )
|
|
1983
|
+
* if (result.ok) setAttachmentId(result.data.attach.id)
|
|
1984
|
+
* }
|
|
1985
|
+
* ```
|
|
1986
|
+
*
|
|
1987
|
+
* Compensation is best-effort and tab-bound — see `sequence.ts`. When the
|
|
1988
|
+
* rollback has to survive a closed tab, move the sequence into a workflow.
|
|
1989
|
+
*/
|
|
1990
|
+
export declare const useSequence: (options?: RunSequenceOptions) => UseSequenceResult;
|
|
1991
|
+
|
|
1992
|
+
export declare interface UseSequenceResult {
|
|
1993
|
+
/** Run a built sequence. Resolves with the discriminated result; it does not
|
|
1994
|
+
* reject, which is what removes the try/catch. */
|
|
1995
|
+
readonly run: <Ctx>(builder: SequenceBuilder<Ctx>, options?: RunSequenceOptions) => Promise<SequenceResult<Ctx>>;
|
|
1996
|
+
/** True while a sequence is in flight. */
|
|
1997
|
+
readonly pending: boolean;
|
|
1998
|
+
/** The last sequence's original failure, or `undefined` after a success. */
|
|
1999
|
+
readonly error: unknown | undefined;
|
|
2000
|
+
/** The step that failed last time, for a targeted message ("couldn't create
|
|
2001
|
+
* the ticket") rather than a generic one. */
|
|
2002
|
+
readonly failedStep: string | undefined;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1857
2005
|
export declare function useSubscription<T = unknown>(apiName: string, rpcTag: string, input: Readonly<Record<string, unknown>>, options: SubscriptionOptions<T> & {
|
|
1858
2006
|
readonly fallback: T;
|
|
1859
2007
|
}): SubscriptionStateWithFallback<T>;
|
|
1860
2008
|
|
|
1861
|
-
export declare function useSubscription<T = unknown>(apiName: string, rpcTag: string, input?: Readonly<Record<string, unknown>>, options?: SubscriptionOptions<T>
|
|
2009
|
+
export declare function useSubscription<T = unknown>(apiName: string, rpcTag: string, input?: Readonly<Record<string, unknown>>, options?: SubscriptionOptions<T> & {
|
|
2010
|
+
readonly skip?: false;
|
|
2011
|
+
}): SubscriptionState<T>;
|
|
2012
|
+
|
|
2013
|
+
export declare function useSubscription<T = unknown>(apiName: string, rpcTag: string, input: Readonly<Record<string, unknown>>, options: SubscriptionOptions<T> & {
|
|
2014
|
+
readonly skip?: boolean;
|
|
2015
|
+
}): SubscriptionState<T> | SubscriptionIdle;
|
|
1862
2016
|
|
|
1863
2017
|
/** The column shape a `<DataTable query=…>` will render — from the query's
|
|
1864
2018
|
* output Schema. */
|
|
@@ -2012,11 +2166,11 @@ export declare interface WorkflowClientMessages {
|
|
|
2012
2166
|
}>>;
|
|
2013
2167
|
}
|
|
2014
2168
|
|
|
2015
|
-
export declare type WorkflowDomainEventsState = SubscriptionState<ReadonlyArray<WorkflowDomainEventRow>> & {
|
|
2169
|
+
export declare type WorkflowDomainEventsState = (SubscriptionState<ReadonlyArray<WorkflowDomainEventRow>> | SubscriptionIdle) & {
|
|
2016
2170
|
readonly events: ReadonlyArray<WorkflowDomainEventRow>;
|
|
2017
2171
|
};
|
|
2018
2172
|
|
|
2019
|
-
export declare type WorkflowEventDeliveriesState = SubscriptionState<ReadonlyArray<WorkflowEventDeliveryRow>> & {
|
|
2173
|
+
export declare type WorkflowEventDeliveriesState = (SubscriptionState<ReadonlyArray<WorkflowEventDeliveryRow>> | SubscriptionIdle) & {
|
|
2020
2174
|
readonly deliveries: ReadonlyArray<WorkflowEventDeliveryRow>;
|
|
2021
2175
|
};
|
|
2022
2176
|
|
|
@@ -2029,7 +2183,7 @@ export declare interface WorkflowRunError {
|
|
|
2029
2183
|
|
|
2030
2184
|
export { WorkflowRunEventRow }
|
|
2031
2185
|
|
|
2032
|
-
export declare type WorkflowRunEventsState = SubscriptionState<ReadonlyArray<WorkflowRunEventRow>> & {
|
|
2186
|
+
export declare type WorkflowRunEventsState = (SubscriptionState<ReadonlyArray<WorkflowRunEventRow>> | SubscriptionIdle) & {
|
|
2033
2187
|
readonly events: ReadonlyArray<WorkflowRunEventRow>;
|
|
2034
2188
|
};
|
|
2035
2189
|
|
|
@@ -2041,11 +2195,16 @@ export declare interface WorkflowRunsFilter {
|
|
|
2041
2195
|
readonly limit?: number;
|
|
2042
2196
|
}
|
|
2043
2197
|
|
|
2044
|
-
export declare type WorkflowRunsState = SubscriptionState<ReadonlyArray<WorkflowRunRow>> & {
|
|
2198
|
+
export declare type WorkflowRunsState = (SubscriptionState<ReadonlyArray<WorkflowRunRow>> | SubscriptionIdle) & {
|
|
2045
2199
|
readonly runs: ReadonlyArray<WorkflowRunRow>;
|
|
2046
2200
|
};
|
|
2047
2201
|
|
|
2048
|
-
|
|
2202
|
+
/** `SubscriptionIdle` is in the union because `id === undefined` skips: asking
|
|
2203
|
+
* for "the run with no id" is not a pending request, it is no request. Before
|
|
2204
|
+
* the idle state existed this reported `loading: true` forever, so a caller
|
|
2205
|
+
* following the blessed `if (loading) return <Skeleton/>` rendered a permanent
|
|
2206
|
+
* skeleton whenever nothing was selected. */
|
|
2207
|
+
export declare type WorkflowRunState = (SubscriptionState<ReadonlyArray<WorkflowRunRow>> | SubscriptionIdle) & {
|
|
2049
2208
|
readonly run: WorkflowRunRow | undefined;
|
|
2050
2209
|
};
|
|
2051
2210
|
|
|
@@ -2075,7 +2234,7 @@ export { WorkflowRunStatus }
|
|
|
2075
2234
|
|
|
2076
2235
|
export { WorkflowRunStepRow }
|
|
2077
2236
|
|
|
2078
|
-
export declare type WorkflowRunStepsState = SubscriptionState<ReadonlyArray<WorkflowRunStepRow>> & {
|
|
2237
|
+
export declare type WorkflowRunStepsState = (SubscriptionState<ReadonlyArray<WorkflowRunStepRow>> | SubscriptionIdle) & {
|
|
2079
2238
|
readonly steps: ReadonlyArray<WorkflowRunStepRow>;
|
|
2080
2239
|
};
|
|
2081
2240
|
|