@voltro/client 0.28.0 → 0.30.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
@@ -7,6 +7,10 @@ import { EventDescriptor as EventDescriptor_2 } from '@voltro/protocol';
7
7
  import { Fiber } from 'effect';
8
8
  import { ManagedRuntime } from 'effect';
9
9
  import { ReactNode } from 'react';
10
+ import { Rpc } from '@effect/rpc';
11
+ import { RpcClient } from '@effect/rpc';
12
+ import { RpcClientError } from '@effect/rpc';
13
+ import { RpcGroup } from '@effect/rpc';
10
14
  import { Schema } from 'effect';
11
15
  import { Stream } from 'effect';
12
16
  import { SubscriptionEvent } from '@voltro/protocol';
@@ -175,6 +179,13 @@ export declare interface ApiHandle {
175
179
  readonly errorBus: RpcErrorBus;
176
180
  }
177
181
 
182
+ /**
183
+ * Apply preload seeds from the hydration payload, BEFORE the first client
184
+ * render. Idempotent (a Map upsert) so a re-hydration or a repeated key is
185
+ * harmless — last write wins, matching the store-seed rule.
186
+ */
187
+ export declare const applyPreloadSeeds: (seeds: ReadonlyArray<PreloadSeed> | undefined) => void;
188
+
178
189
  /**
179
190
  * Apply seeds from the hydration payload, BEFORE the first client render.
180
191
  *
@@ -226,6 +237,43 @@ export declare interface AutoApplyTarget {
226
237
  readonly shapeItem?: ((input: Record<string, unknown>, currentOrOptimisticId: unknown) => Record<string, unknown>) | undefined;
227
238
  }
228
239
 
240
+ /**
241
+ * Build one api's runtime + rpc client + subscription cache over a WebSocket.
242
+ *
243
+ * The returned `runtime` owns the socket; dispose it (`runtime.dispose()`, after
244
+ * `cache.destroy(runtime)`) to tear the connection down. A fresh cache is bound
245
+ * per resolved runtime — cross-runtime cache reuse is unsafe (its fibers
246
+ * reference the old runtime).
247
+ *
248
+ * Rejects if the runtime fails to resolve the client (e.g. the socket never
249
+ * opens); the caller's reconnect logic decides what to do with that.
250
+ */
251
+ export declare const buildApiRuntime: (options: BuildApiRuntimeOptions) => Promise<BuiltApiRuntime>;
252
+
253
+ export declare interface BuildApiRuntimeOptions {
254
+ /** Stable api name — used only to tag the client's Context. */
255
+ readonly name: string;
256
+ /** WebSocket URL of the api (`ws(s)://host:port/ws`). */
257
+ readonly wsUrl: string;
258
+ /** The api's rpc group (its typed procedure surface). */
259
+ readonly group: RpcGroup.RpcGroup<Rpc.Any>;
260
+ /** Per-connection auth headers (see {@link ResolvableHeaders}). */
261
+ readonly headers?: ResolvableHeaders | undefined;
262
+ /** How to construct a WebSocket. Web wraps `globalThis.WebSocket` with
263
+ * tracking + a connect-timeout; RN passes a plain `globalThis.WebSocket`.
264
+ * Injected so this module touches no environment global directly. */
265
+ readonly webSocketConstructor: (url: string, protocols?: string | ReadonlyArray<string>) => globalThis.WebSocket;
266
+ }
267
+
268
+ /** The transport-level pieces of one api's client stack. Combined with the
269
+ * api's descriptor map + inspect base URL, this is an `ApiHandle`. */
270
+ export declare interface BuiltApiRuntime {
271
+ readonly runtime: AnyRuntime;
272
+ readonly cache: SubscriptionCache;
273
+ readonly client: RpcClient.RpcClient<Rpc.Any, RpcClientError.RpcClientError>;
274
+ readonly errorBus: RpcErrorBus;
275
+ }
276
+
229
277
  declare interface CacheEntry {
230
278
  readonly key: string;
231
279
  /** First element of the keyArr — the rpc tag (e.g. 'todos.list'). Used
@@ -820,6 +868,8 @@ export declare class LoadingSubscriptionCache extends SubscriptionCache {
820
868
  };
821
869
  }
822
870
 
871
+ export declare const makePreloadSeedBag: () => PreloadSeedBag;
872
+
823
873
  export declare const makeStoreSeedBag: () => StoreSeedBag;
824
874
 
825
875
  /** Exported for tests: the inspect URL this hook fetches. */
@@ -1000,6 +1050,27 @@ export declare interface PersistOptions<S> {
1000
1050
  readonly migrate?: (stored: unknown) => Partial<S> | undefined;
1001
1051
  }
1002
1052
 
1053
+ /** Options `usePreloadedSubscription` accepts. It OWNS `initialSnapshot` (the
1054
+ * preload payload supplies it), so callers pass only `skip` — `fallback` and
1055
+ * `initialSnapshot` are its own concern. */
1056
+ export declare type PreloadedSubscriptionOptions = Pick<SubscriptionOptions<never>, 'skip'>;
1057
+
1058
+ /** One preloaded subscription snapshot, as it travels in the hydration payload. */
1059
+ export declare interface PreloadSeed {
1060
+ /** The api mount name the subscription reads from (first arg of `useSubscription`). */
1061
+ readonly api: string;
1062
+ /** `stableKey([rpcTag, input])` — identical to the SubscriptionCache's own key,
1063
+ * so the seed and the live subscription address the same entry. */
1064
+ readonly key: string;
1065
+ /** The server-fetched snapshot value. */
1066
+ readonly value: unknown;
1067
+ }
1068
+
1069
+ /** Collects preload seeds for ONE request. The server creates one per render. */
1070
+ export declare interface PreloadSeedBag {
1071
+ readonly seeds: PreloadSeed[];
1072
+ }
1073
+
1003
1074
  /** Structural mirror of @voltro/runtime's PreviewDiff (client stays decoupled). */
1004
1075
  export declare interface PreviewDiff {
1005
1076
  readonly rows: ReadonlyArray<PreviewRowDiff>;
@@ -1089,6 +1160,17 @@ export declare interface QueryFiltersState<Row> {
1089
1160
  readonly error: unknown | undefined;
1090
1161
  }
1091
1162
 
1163
+ /**
1164
+ * The preloaded snapshot for `(api, rpcTag, input)`, or `undefined` when none was
1165
+ * seeded. On the SERVER it reads the current request's bag directly, so the SSR
1166
+ * markup and the client hydration render see the SAME value (no hydration
1167
+ * flicker); on the CLIENT it reads the hydration-applied registry.
1168
+ *
1169
+ * A seed value is never `undefined` (it round-trips through JSON, so the worst
1170
+ * case is `null`), which is why `undefined` unambiguously means "not seeded".
1171
+ */
1172
+ export declare const readPreloadedSnapshot: (api: string, rpcTag: string, input: Readonly<Record<string, unknown>>) => unknown;
1173
+
1092
1174
  export declare interface RecordState<R> {
1093
1175
  readonly record: R | undefined;
1094
1176
  readonly loading: boolean;
@@ -1112,9 +1194,17 @@ export declare const reportClientError: (error: unknown, context?: Record<string
1112
1194
  * module-global flags + any pending grace-period timer). */
1113
1195
  export declare const _resetFrameworkRuntimesWarning: () => void;
1114
1196
 
1197
+ /** Drop all applied client-side seeds. Tests only. */
1198
+ export declare const resetPreloadSeeds: () => void;
1199
+
1115
1200
  /** Test helper: forget every defined store. NEVER call this in app code. */
1116
1201
  export declare const resetStoreRegistryForTests: () => void;
1117
1202
 
1203
+ /** Auth headers to attach to every rpc MESSAGE frame, resolved on EVERY
1204
+ * (re)connect so a rotating token (a `() => Promise<…>` thunk) is fetched anew
1205
+ * each connection rather than frozen at first build. */
1206
+ export declare type ResolvableHeaders = Readonly<Record<string, string>> | (() => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>);
1207
+
1118
1208
  export declare const resolveByTag: (client: unknown, tag: string) => unknown;
1119
1209
 
1120
1210
  /** Resolve one map entry against the props. Pure. */
@@ -1282,6 +1372,14 @@ export declare const schemaToColumns: (schema: Schema.Schema.Any) => ReadonlyArr
1282
1372
  */
1283
1373
  export declare const schemaToFields: (schema: Schema.Schema.Any) => ReadonlyArray<FieldDescriptor>;
1284
1374
 
1375
+ /**
1376
+ * Seed a subscription's first value for THIS request's hydration. Call it on the
1377
+ * server during a render — a loader, a layout loader — with the value a
1378
+ * `ctx.query` already fetched, and `usePreloadedSubscription(api, rpcTag, input)`
1379
+ * shows it at the first paint without any serialization step of your own.
1380
+ */
1381
+ export declare const seedPreloadedSubscription: (api: string, rpcTag: string, input: Readonly<Record<string, unknown>>, value: unknown) => void;
1382
+
1285
1383
  /**
1286
1384
  * Seed a store for THIS request's hydration. Call it anywhere on the server
1287
1385
  * during a render — a loader, a layout loader — and the value reaches the
@@ -1351,6 +1449,12 @@ export declare type SequenceResult<Ctx> = {
1351
1449
  * at boot, next to your toast provider. */
1352
1450
  export declare const setMutationNotifier: (notifier: MutationNotifier | undefined) => void;
1353
1451
 
1452
+ /**
1453
+ * Install the request-scoped bag resolver. Server-side only; pass `null` to
1454
+ * uninstall (tests). `@voltro/web/ssr` registers it, backed by its own ALS.
1455
+ */
1456
+ export declare const setPreloadSeedResolver: (resolver: (() => PreloadSeedBag | null) | null) => void;
1457
+
1354
1458
  /**
1355
1459
  * Install the request-scoped bag resolver. Server-side only; pass `null` to
1356
1460
  * uninstall (tests).
@@ -2134,13 +2238,6 @@ export declare interface UploadResult {
2134
2238
 
2135
2239
  export declare type UploadStatus = 'idle' | 'uploading' | 'success' | 'error';
2136
2240
 
2137
- /**
2138
- * Invoke a server `defineAction` procedure by tag. Unlike `useMutation`,
2139
- * an action has no optimistic / cache surface — it's a unary call whose
2140
- * effects (sends, captures, side-effecting writes the framework must NOT
2141
- * preview locally) only become visible when the server pushes a delta on
2142
- * an independent subscription.
2143
- */
2144
2241
  export declare const useAction: <Input = unknown, Output = unknown>(apiName: string, rpcTag: string) => ActionState<Input, Output>;
2145
2242
 
2146
2243
  export declare const useAgent: (apiName: string, rpcTag: string) => AgentControls;
@@ -2332,6 +2429,19 @@ export declare interface UseOutboxOptions {
2332
2429
  /** The current subject's scopes (defaults to none if no provider). */
2333
2430
  export declare const usePermissions: () => PermissionState_2;
2334
2431
 
2432
+ /**
2433
+ * Subscribe to a streaming rpc, seeding the first value from the SSR preload
2434
+ * payload when one exists.
2435
+ *
2436
+ * @param apiName The api mount name (as in `useSubscription`).
2437
+ * @param rpcTag The rpc tag (e.g. `'projects.list'`).
2438
+ * @param input The rpc payload; part of the preload lookup key AND the
2439
+ * subscription's cache key (identical `stableKey`).
2440
+ * @param options `{ skip }` — defers the subscription. `initialSnapshot`
2441
+ * comes from the preload payload, not from here.
2442
+ */
2443
+ export declare function usePreloadedSubscription<T = unknown>(apiName: string, rpcTag: string, input?: Readonly<Record<string, unknown>>, options?: PreloadedSubscriptionOptions): SubscriptionState<T> | SubscriptionIdle;
2444
+
2335
2445
  export declare const usePreview: <Input = Record<string, unknown>>(apiName: string, previewTag: string) => PreviewState<Input>;
2336
2446
 
2337
2447
  export declare const useProvenance: (apiName: string, table: string, id: string, column?: string) => ProvenanceState;