@voltro/client 0.24.0 → 0.26.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
@@ -3,6 +3,7 @@ import { ClipboardEvent as ClipboardEvent_2 } from 'react';
3
3
  import { Context } from 'react';
4
4
  import { DragEvent as DragEvent_2 } from 'react';
5
5
  import { Effect } from 'effect';
6
+ import { EventDescriptor as EventDescriptor_2 } from '@voltro/protocol';
6
7
  import { Fiber } from 'effect';
7
8
  import { ManagedRuntime } from 'effect';
8
9
  import { ReactNode } from 'react';
@@ -650,6 +651,27 @@ declare type EventMap<E extends ReadonlyArray<EventDescriptor<string, unknown>>>
650
651
  [D in E[number] as D['name']]: D extends EventDescriptor<string, infer A> ? A : never;
651
652
  };
652
653
 
654
+ export declare interface EventMiss {
655
+ /** How many deliveries were lost. */
656
+ readonly count: number;
657
+ readonly reason: EventMissReason;
658
+ }
659
+
660
+ /** Why a subscriber lost deliveries. Both are PROVEN counts, never guesses. */
661
+ export declare type EventMissReason = 'buffer' | 'resume';
662
+
663
+ export declare interface EventState {
664
+ /** `idle` = not subscribed (null key or `skip`); `connecting` = attaching;
665
+ * `live` = the server confirmed the subscription is attached. */
666
+ readonly status: EventStatus;
667
+ /** Total deliveries PROVEN lost on this subscription since it mounted. */
668
+ readonly missed: number;
669
+ /** The most recent loss, with its cause. */
670
+ readonly lastMiss: EventMiss | undefined;
671
+ }
672
+
673
+ export declare type EventStatus = 'idle' | 'connecting' | 'live';
674
+
653
675
  export declare interface EventValidation {
654
676
  readonly valid: boolean;
655
677
  /** `path` → message, empty when valid. */
@@ -746,6 +768,30 @@ export declare const getMutationNotifier: () => MutationNotifier | undefined;
746
768
  /** Read the current feed. Newest-first; up to MAX_MUTATIONS entries. */
747
769
  export declare const getMutations: () => ReadonlyArray<MutationEvent>;
748
770
 
771
+ /**
772
+ * Is this the loading baseline rather than a resolved api?
773
+ *
774
+ * The stub is deliberately *usable* — that is what lets a provider-less render
775
+ * proceed instead of crashing — so nothing about its shape says "not ready".
776
+ * Reads go through `LoadingSubscriptionCache`, whose `subscribe` is a
777
+ * non-fetching no-op, and that covers every hook that reads through the cache.
778
+ *
779
+ * A hook that instead forks its own fiber on `handle.runtime` has no such
780
+ * backstop, and gets a worse failure than a crash: `useEvent` retries a dropped
781
+ * subscription forever by design, so forking against the stub's client proxy —
782
+ * which throws on any call — is an unbounded retry loop, not a one-off error.
783
+ * Such a hook must gate on this instead.
784
+ *
785
+ * It asks about the CAPABILITY, not about object identity, and that is
786
+ * load-bearing rather than stylistic. `handle === ssrStubHandle` is the obvious
787
+ * spelling and it is wrong here: a host that loads a bundled copy of this
788
+ * package alongside this one — `@voltro/web`'s dist does exactly that — has its
789
+ * own `ssrStubHandle`, so the comparison answers "resolved" for a stub and the
790
+ * caller forks into the crash anyway. Asking whether the runtime can fork is
791
+ * true of every real runtime and false of every stub, in any number of copies.
792
+ */
793
+ export declare const isUnresolvedApi: (handle: ApiHandle) => boolean;
794
+
749
795
  declare type Listener = () => void;
750
796
 
751
797
  /**
@@ -2205,6 +2251,39 @@ export declare const useDebounced: <T>(value: T, ms?: number) => T;
2205
2251
  */
2206
2252
  export declare const useDerived: <S extends Record<string, unknown>, T>(sources: S, reducer: (sources: S) => T) => T;
2207
2253
 
2254
+ /**
2255
+ * Subscribe to a declared event.
2256
+ *
2257
+ * ```tsx
2258
+ * useEvent(gameStarted, arenaId ? { arenaId } : null, (payload) => {
2259
+ * scene.switchTo('running', payload.gameId)
2260
+ * })
2261
+ * ```
2262
+ *
2263
+ * `payload` is typed from the descriptor — a wrong field name is a `tsc` error
2264
+ * at this call site, and the `key` must structurally match the descriptor's key
2265
+ * schema, so a typo cannot produce a subscription that silently never fires.
2266
+ */
2267
+ export declare function useEvent<Name extends string, Key extends Schema.Schema.Any, Payload extends Schema.Schema.Any>(descriptor: EventDescriptor_2<Name, Key, Payload>, key: Schema.Schema.Type<Key> | null, handler: (payload: Schema.Schema.Type<Payload>) => void, options?: UseEventOptions): EventState;
2268
+
2269
+ export declare interface UseEventOptions {
2270
+ /**
2271
+ * Called when the server can PROVE deliveries were lost — a serial gap while
2272
+ * you were slow to read, or a reconnect older than the server's buffer.
2273
+ *
2274
+ * This is the callback an app builds recovery on: refetch the authoritative
2275
+ * state, re-run the scene, resync the display. It exists because silence is
2276
+ * the one outcome nothing can be built on — a screen cannot tell "nothing
2277
+ * happened" from "I missed the signal", and every comparable product answers
2278
+ * that question by dropping quietly.
2279
+ */
2280
+ readonly onMissed?: (miss: EventMiss) => void;
2281
+ /** Suspend the subscription without unmounting. Same meaning as
2282
+ * `useSubscription`'s `skip`; a `null` key does the same thing. */
2283
+ readonly skip?: boolean;
2284
+ readonly apiName?: string;
2285
+ }
2286
+
2208
2287
  export declare const useFormBinding: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(apiName: string, mutationTag: string, options: UseFormBindingOptions<Input>) => FormBinding<Input, Output>;
2209
2288
 
2210
2289
  export declare interface UseFormBindingOptions<Input> {