@lunora/react 1.0.0-alpha.25 → 1.0.0-alpha.27

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.
Files changed (24) hide show
  1. package/dist/index.d.mts +93 -28
  2. package/dist/index.d.ts +93 -28
  3. package/dist/index.mjs +15 -13
  4. package/dist/packem_shared/{cache-D1O_xfAe.mjs → cache-JeuDAXfE.mjs} +1 -1
  5. package/dist/packem_shared/{hydratePreloaded-uMDH_xA9.mjs → hydratePreloaded-2omLtadR.mjs} +2 -2
  6. package/dist/packem_shared/{lunoraQueryOptions-C3uwOTMW.mjs → lunoraQueryOptions-CefbPBId.mjs} +1 -1
  7. package/dist/packem_shared/{query-key-BmCbgidV.mjs → query-key-LGnArBTB.mjs} +2 -2
  8. package/dist/packem_shared/{stable-key-CGp4e2Ux.mjs → stable-key-DePnevIy.mjs} +2 -2
  9. package/dist/packem_shared/{use-paginated-core-DafedU4l.mjs → use-paginated-core-xxvd1YA0.mjs} +2 -2
  10. package/dist/packem_shared/{useAgent-DwLxTc3P.mjs → useAgent-BNLlIYHz.mjs} +1 -1
  11. package/dist/packem_shared/{useAgentChat-WPm5iHgR.mjs → useAgentChat-CSjKniMO.mjs} +2 -2
  12. package/dist/packem_shared/{useAgentState-Cikdlomg.mjs → useAgentState-Cqyd3Dfy.mjs} +1 -1
  13. package/dist/packem_shared/{useAgentToolEvents-BM7ERo00.mjs → useAgentToolEvents-gF7U4_KY.mjs} +2 -2
  14. package/dist/packem_shared/useClientQuery-CN1sC3cP.mjs +56 -0
  15. package/dist/packem_shared/{useFlag-C2Pkghli.mjs → useFlag-Beyeiq2b.mjs} +1 -1
  16. package/dist/packem_shared/useHttpStream-oc4s1pMy.mjs +127 -0
  17. package/dist/packem_shared/{useInfiniteQuery-BeaehE9f.mjs → useInfiniteQuery-CJNw1XaZ.mjs} +1 -1
  18. package/dist/packem_shared/{usePaginatedQuery-_6ETVUmf.mjs → usePaginatedQuery-DWzydaVR.mjs} +1 -1
  19. package/dist/packem_shared/{useQuery-D3_ZxMYs.mjs → useQuery-CE8_QUgh.mjs} +39 -16
  20. package/dist/packem_shared/{useStream-CGC26afa.mjs → useStream-GYNgc9J5.mjs} +2 -2
  21. package/dist/packem_shared/{useSubscription-D38Jdyr8.mjs → useSubscription-DcDlvVuI.mjs} +2 -2
  22. package/dist/packem_shared/wire-key-Bg8BJ8YM.mjs +101 -0
  23. package/dist/server.mjs +2 -2
  24. package/package.json +4 -4
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, ReactElement } from 'react';
2
- import { LunoraClient, OptimisticUpdate, User, FunctionReference, ConnectionStatus, ReturnOf, ArgsOf, MutatorHandle, Preloaded } from '@lunora/client';
3
- export { type ArgsOf, type FunctionReference, type LunoraClient, type LunoraErrorCode, type MutatorHandle, type MutatorTransaction, type OptimisticLocalStore, type OptimisticUpdate, type Preloaded, type ReturnOf, type User, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
2
+ import { LunoraClient, OptimisticUpdate, User, FunctionReference, ClientQueryRef, ConnectionStatus, ArgsOf, ReturnOf, HttpStreamRef, HttpStreamArgsOf, HttpStreamChunkOf, MutatorHandle, Preloaded } from '@lunora/client';
3
+ export { type ArgsOf, type ClientQueryRef, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamRef, type LunoraClient, type LunoraErrorCode, type MutatorHandle, type MutatorTransaction, type OptimisticLocalStore, type OptimisticUpdate, type Preloaded, type ReturnOf, type User, createClientQuery, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
5
  export { type L as LunoraQueryOptions, l as lunoraQueryOptions } from "./packem_shared/query-options.d-D4okOpO8.mjs";
6
6
  import { PaginationStatus } from '@lunora/client/pagination';
@@ -615,6 +615,31 @@ declare const useAgentToolEvents: (options: UseAgentToolEventsOptions) => UseAge
615
615
  * hook with the freshly-resolved user.
616
616
  */
617
617
  declare const useAuth: () => UseAuthResult;
618
+ type Setter<T> = (value: T) => void;
619
+ /**
620
+ * Subscribe to a local-only {@link ClientQueryRef} and re-render when its
621
+ * value changes. Unlike `useQuery`, this never touches the network — the
622
+ * value lives in a reactive store on the `LunoraClient` instance and is shared
623
+ * across every consumer of the same ref.
624
+ *
625
+ * The initial render reads the store synchronously (via
626
+ * `useSyncExternalStore`), so there is never an "undefined flash" — the value
627
+ * is either the one most recently set or `ref.defaultValue`.
628
+ *
629
+ * Returns a `[value, setter]` tuple, matching the `useState` convention.
630
+ * @example
631
+ * ```tsx
632
+ * import { useClientQuery, createClientQuery } from "@lunora/react";
633
+ *
634
+ * const sidebarOpen = createClientQuery("sidebarOpen", true);
635
+ *
636
+ * function Sidebar() {
637
+ * const [open, setOpen] = useClientQuery(sidebarOpen);
638
+ * return <aside data-open={open}>…</aside>;
639
+ * }
640
+ * ```
641
+ */
642
+ declare const useClientQuery: <T extends unknown>(ref: ClientQueryRef<T>) => [T, Setter<T>];
618
643
  /**
619
644
  * Reactive view of the client's aggregate live-socket status across all shard
620
645
  * connections. Re-renders on every transition (`idle` → `connecting` →
@@ -654,6 +679,65 @@ declare const useFlag: <T extends FlagValue>(key: string, defaultValue: T, conte
654
679
  * changes between renders.
655
680
  */
656
681
  declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: FlagContext) => T;
682
+ /** The lifecycle of a stream the hook is observing. */
683
+ type UseStreamStatus = "complete" | "error" | "idle" | "streaming";
684
+ interface UseStreamResult<T> {
685
+ /** Force-cancel the stream and resolve the iterator. Safe to call multiple times. */
686
+ cancel: () => void;
687
+ /** Chunks the server has pushed so far, in arrival order. */
688
+ chunks: ReadonlyArray<T>;
689
+ error: Error | undefined;
690
+ status: UseStreamStatus;
691
+ }
692
+ interface UseStreamOptions {
693
+ /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
694
+ maxBuffer?: number;
695
+ shardKey?: string;
696
+ }
697
+ /**
698
+ * Subscribe to a streaming query. Returns the chunks pushed so far plus a
699
+ * lifecycle status and a cancel function. Changing `fn` or the serialized
700
+ * `args` resets the stream — the previous iterator is cancelled and a fresh
701
+ * one opens with empty `chunks`.
702
+ *
703
+ * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
704
+ * (mirrors `useQuery` / `useSubscription`).
705
+ */
706
+ declare const useStream: <F extends FunctionReference<"stream">>(function_: F, args: "skip" | ArgsOf<F>, options?: UseStreamOptions) => UseStreamResult<ReturnOf<F>>;
707
+ /**
708
+ * Result shape returned by {@link useHttpStream}.
709
+ * @experimental Part of the HTTP-SSE stream surface.
710
+ */
711
+ interface UseHttpStreamResult<T> {
712
+ /** Force-cancel the stream (aborts the fetch) and resolve the iterator. Safe to call multiple times. */
713
+ cancel: () => void;
714
+ /** Chunks the server has pushed so far, in arrival order. */
715
+ chunks: ReadonlyArray<T>;
716
+ error: Error | undefined;
717
+ status: UseStreamStatus;
718
+ }
719
+ /**
720
+ * Options accepted by {@link useHttpStream}.
721
+ * @experimental Part of the HTTP-SSE stream surface.
722
+ */
723
+ interface UseHttpStreamOptions {
724
+ /** Forwarded to `client.httpStream()` — caps the in-flight chunk buffer. */
725
+ maxBuffer?: number;
726
+ }
727
+ /**
728
+ * Consume an **HTTP-SSE route stream** (`httpRoute.&lt;verb>(path).stream()`) via
729
+ * `client.httpStream`. Distinct from `useStream`, which consumes the WS
730
+ * procedure stream (`kind: "stream"`). Returns the chunks received so far plus
731
+ * a lifecycle status and a cancel function. Changing the route or the
732
+ * serialized `args` resets the stream — the previous fetch is aborted (the
733
+ * server sees `request.signal`) and a fresh one opens with empty `chunks`.
734
+ * Unmount also aborts.
735
+ *
736
+ * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
737
+ * (mirrors `useQuery` / `useStream`).
738
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
739
+ */
740
+ declare const useHttpStream: <Ref extends HttpStreamRef>(route: Ref, args: "skip" | HttpStreamArgsOf<Ref>, options?: UseHttpStreamOptions) => UseHttpStreamResult<HttpStreamChunkOf<Ref>>;
657
741
  /** The args a paginated query exposes minus the framework-supplied page cursor. */
658
742
  type PaginatedArgs<F> = Omit<ArgsOf<F>, "paginationOpts">;
659
743
  /** The element type of the `page` array a paginated query returns. */
@@ -871,6 +955,12 @@ declare const usePresence: <H extends HeartbeatReference, L extends ListPresentR
871
955
  * Returns `undefined` until the first response lands. Pass `"skip"` for
872
956
  * `args` to short-circuit the query (no network call, no subscription).
873
957
  *
958
+ * When the `LunoraClient` was created with `hydrateOnStart: true` and a
959
+ * `queryCache` adapter, the first **enabled** render waits for the durable
960
+ * read cache to finish loading. If a cached value exists for this query it
961
+ * is fed as `initialData` so the user sees it immediately — no undefined
962
+ * flash before the socket round-trip.
963
+ *
874
964
  * Internally this routes through TanStack Query: the queryKey is
875
965
  * `["lunora", fn.__lunoraRef, args, shardKey]` (TanStack hashes structurally
876
966
  * so an args object built in a different key order still dedupes). The
@@ -911,31 +1001,6 @@ interface UseRateLimitResult {
911
1001
  * `useMemo`) so the `consume`/`check` callbacks keep a steady identity.
912
1002
  */
913
1003
  declare const useRateLimit: (config: RateLimitConfig, options?: UseRateLimitOptions) => UseRateLimitResult;
914
- /** The lifecycle of a stream the hook is observing. */
915
- type UseStreamStatus = "complete" | "error" | "idle" | "streaming";
916
- interface UseStreamResult<T> {
917
- /** Force-cancel the stream and resolve the iterator. Safe to call multiple times. */
918
- cancel: () => void;
919
- /** Chunks the server has pushed so far, in arrival order. */
920
- chunks: ReadonlyArray<T>;
921
- error: Error | undefined;
922
- status: UseStreamStatus;
923
- }
924
- interface UseStreamOptions {
925
- /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
926
- maxBuffer?: number;
927
- shardKey?: string;
928
- }
929
- /**
930
- * Subscribe to a streaming query. Returns the chunks pushed so far plus a
931
- * lifecycle status and a cancel function. Changing `fn` or the serialized
932
- * `args` resets the stream — the previous iterator is cancelled and a fresh
933
- * one opens with empty `chunks`.
934
- *
935
- * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
936
- * (mirrors `useQuery` / `useSubscription`).
937
- */
938
- declare const useStream: <F extends FunctionReference<"stream">>(function_: F, args: "skip" | ArgsOf<F>, options?: UseStreamOptions) => UseStreamResult<ReturnOf<F>>;
939
1004
  /**
940
1005
  * Subscribe to a real-time stream from the server. Unlike `useQuery`, this
941
1006
  * hook does not issue an initial HTTP fetch — it only delivers values that
@@ -1089,4 +1154,4 @@ interface UseVoiceAgentResult {
1089
1154
  * `createSocket`) so the hook is drivable outside a browser.
1090
1155
  */
1091
1156
  declare const useVoiceAgent: (options: UseVoiceAgentOptions) => UseVoiceAgentResult;
1092
- export { type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type MutatorHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, type UseAgentApi, type UseAgentChatApi, type UseAgentChatOptions, type UseAgentChatResult, type UseAgentOptions, type UseAgentResult, type UseAgentStateApi, type UseAgentStateOptions, type UseAgentStateResult, type UseAgentToolEventsApi, type UseAgentToolEventsOptions, type UseAgentToolEventsResult, type UseAuthResult, type UseCheckoutResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UseMutationCallOptions, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseStreamOptions, type UseStreamResult, type UseStreamStatus, type UseSubscriptionResult, type UseVoiceAgentOptions, type UseVoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, hydratePreloaded, useAgent, useAgentChat, useAgentState, useAgentToolEvents, useAuth, useAuthState, useCheckout, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription, useVoiceAgent };
1157
+ export { type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type MutatorHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, type UseAgentApi, type UseAgentChatApi, type UseAgentChatOptions, type UseAgentChatResult, type UseAgentOptions, type UseAgentResult, type UseAgentStateApi, type UseAgentStateOptions, type UseAgentStateResult, type UseAgentToolEventsApi, type UseAgentToolEventsOptions, type UseAgentToolEventsResult, type UseAuthResult, type UseCheckoutResult, type UseHttpStreamOptions, type UseHttpStreamResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UseMutationCallOptions, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseStreamOptions, type UseStreamResult, type UseStreamStatus, type UseSubscriptionResult, type UseVoiceAgentOptions, type UseVoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, hydratePreloaded, useAgent, useAgentChat, useAgentState, useAgentToolEvents, useAuth, useAuthState, useCheckout, useClientQuery, useConnectionStatus, useFlag, useFlags, useHttpStream, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription, useVoiceAgent };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, ReactElement } from 'react';
2
- import { LunoraClient, OptimisticUpdate, User, FunctionReference, ConnectionStatus, ReturnOf, ArgsOf, MutatorHandle, Preloaded } from '@lunora/client';
3
- export { type ArgsOf, type FunctionReference, type LunoraClient, type LunoraErrorCode, type MutatorHandle, type MutatorTransaction, type OptimisticLocalStore, type OptimisticUpdate, type Preloaded, type ReturnOf, type User, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
2
+ import { LunoraClient, OptimisticUpdate, User, FunctionReference, ClientQueryRef, ConnectionStatus, ArgsOf, ReturnOf, HttpStreamRef, HttpStreamArgsOf, HttpStreamChunkOf, MutatorHandle, Preloaded } from '@lunora/client';
3
+ export { type ArgsOf, type ClientQueryRef, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamRef, type LunoraClient, type LunoraErrorCode, type MutatorHandle, type MutatorTransaction, type OptimisticLocalStore, type OptimisticUpdate, type Preloaded, type ReturnOf, type User, createClientQuery, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
5
  export { type L as LunoraQueryOptions, l as lunoraQueryOptions } from "./packem_shared/query-options.d-D4okOpO8.js";
6
6
  import { PaginationStatus } from '@lunora/client/pagination';
@@ -615,6 +615,31 @@ declare const useAgentToolEvents: (options: UseAgentToolEventsOptions) => UseAge
615
615
  * hook with the freshly-resolved user.
616
616
  */
617
617
  declare const useAuth: () => UseAuthResult;
618
+ type Setter<T> = (value: T) => void;
619
+ /**
620
+ * Subscribe to a local-only {@link ClientQueryRef} and re-render when its
621
+ * value changes. Unlike `useQuery`, this never touches the network — the
622
+ * value lives in a reactive store on the `LunoraClient` instance and is shared
623
+ * across every consumer of the same ref.
624
+ *
625
+ * The initial render reads the store synchronously (via
626
+ * `useSyncExternalStore`), so there is never an "undefined flash" — the value
627
+ * is either the one most recently set or `ref.defaultValue`.
628
+ *
629
+ * Returns a `[value, setter]` tuple, matching the `useState` convention.
630
+ * @example
631
+ * ```tsx
632
+ * import { useClientQuery, createClientQuery } from "@lunora/react";
633
+ *
634
+ * const sidebarOpen = createClientQuery("sidebarOpen", true);
635
+ *
636
+ * function Sidebar() {
637
+ * const [open, setOpen] = useClientQuery(sidebarOpen);
638
+ * return <aside data-open={open}>…</aside>;
639
+ * }
640
+ * ```
641
+ */
642
+ declare const useClientQuery: <T extends unknown>(ref: ClientQueryRef<T>) => [T, Setter<T>];
618
643
  /**
619
644
  * Reactive view of the client's aggregate live-socket status across all shard
620
645
  * connections. Re-renders on every transition (`idle` → `connecting` →
@@ -654,6 +679,65 @@ declare const useFlag: <T extends FlagValue>(key: string, defaultValue: T, conte
654
679
  * changes between renders.
655
680
  */
656
681
  declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: FlagContext) => T;
682
+ /** The lifecycle of a stream the hook is observing. */
683
+ type UseStreamStatus = "complete" | "error" | "idle" | "streaming";
684
+ interface UseStreamResult<T> {
685
+ /** Force-cancel the stream and resolve the iterator. Safe to call multiple times. */
686
+ cancel: () => void;
687
+ /** Chunks the server has pushed so far, in arrival order. */
688
+ chunks: ReadonlyArray<T>;
689
+ error: Error | undefined;
690
+ status: UseStreamStatus;
691
+ }
692
+ interface UseStreamOptions {
693
+ /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
694
+ maxBuffer?: number;
695
+ shardKey?: string;
696
+ }
697
+ /**
698
+ * Subscribe to a streaming query. Returns the chunks pushed so far plus a
699
+ * lifecycle status and a cancel function. Changing `fn` or the serialized
700
+ * `args` resets the stream — the previous iterator is cancelled and a fresh
701
+ * one opens with empty `chunks`.
702
+ *
703
+ * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
704
+ * (mirrors `useQuery` / `useSubscription`).
705
+ */
706
+ declare const useStream: <F extends FunctionReference<"stream">>(function_: F, args: "skip" | ArgsOf<F>, options?: UseStreamOptions) => UseStreamResult<ReturnOf<F>>;
707
+ /**
708
+ * Result shape returned by {@link useHttpStream}.
709
+ * @experimental Part of the HTTP-SSE stream surface.
710
+ */
711
+ interface UseHttpStreamResult<T> {
712
+ /** Force-cancel the stream (aborts the fetch) and resolve the iterator. Safe to call multiple times. */
713
+ cancel: () => void;
714
+ /** Chunks the server has pushed so far, in arrival order. */
715
+ chunks: ReadonlyArray<T>;
716
+ error: Error | undefined;
717
+ status: UseStreamStatus;
718
+ }
719
+ /**
720
+ * Options accepted by {@link useHttpStream}.
721
+ * @experimental Part of the HTTP-SSE stream surface.
722
+ */
723
+ interface UseHttpStreamOptions {
724
+ /** Forwarded to `client.httpStream()` — caps the in-flight chunk buffer. */
725
+ maxBuffer?: number;
726
+ }
727
+ /**
728
+ * Consume an **HTTP-SSE route stream** (`httpRoute.&lt;verb>(path).stream()`) via
729
+ * `client.httpStream`. Distinct from `useStream`, which consumes the WS
730
+ * procedure stream (`kind: "stream"`). Returns the chunks received so far plus
731
+ * a lifecycle status and a cancel function. Changing the route or the
732
+ * serialized `args` resets the stream — the previous fetch is aborted (the
733
+ * server sees `request.signal`) and a fresh one opens with empty `chunks`.
734
+ * Unmount also aborts.
735
+ *
736
+ * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
737
+ * (mirrors `useQuery` / `useStream`).
738
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
739
+ */
740
+ declare const useHttpStream: <Ref extends HttpStreamRef>(route: Ref, args: "skip" | HttpStreamArgsOf<Ref>, options?: UseHttpStreamOptions) => UseHttpStreamResult<HttpStreamChunkOf<Ref>>;
657
741
  /** The args a paginated query exposes minus the framework-supplied page cursor. */
658
742
  type PaginatedArgs<F> = Omit<ArgsOf<F>, "paginationOpts">;
659
743
  /** The element type of the `page` array a paginated query returns. */
@@ -871,6 +955,12 @@ declare const usePresence: <H extends HeartbeatReference, L extends ListPresentR
871
955
  * Returns `undefined` until the first response lands. Pass `"skip"` for
872
956
  * `args` to short-circuit the query (no network call, no subscription).
873
957
  *
958
+ * When the `LunoraClient` was created with `hydrateOnStart: true` and a
959
+ * `queryCache` adapter, the first **enabled** render waits for the durable
960
+ * read cache to finish loading. If a cached value exists for this query it
961
+ * is fed as `initialData` so the user sees it immediately — no undefined
962
+ * flash before the socket round-trip.
963
+ *
874
964
  * Internally this routes through TanStack Query: the queryKey is
875
965
  * `["lunora", fn.__lunoraRef, args, shardKey]` (TanStack hashes structurally
876
966
  * so an args object built in a different key order still dedupes). The
@@ -911,31 +1001,6 @@ interface UseRateLimitResult {
911
1001
  * `useMemo`) so the `consume`/`check` callbacks keep a steady identity.
912
1002
  */
913
1003
  declare const useRateLimit: (config: RateLimitConfig, options?: UseRateLimitOptions) => UseRateLimitResult;
914
- /** The lifecycle of a stream the hook is observing. */
915
- type UseStreamStatus = "complete" | "error" | "idle" | "streaming";
916
- interface UseStreamResult<T> {
917
- /** Force-cancel the stream and resolve the iterator. Safe to call multiple times. */
918
- cancel: () => void;
919
- /** Chunks the server has pushed so far, in arrival order. */
920
- chunks: ReadonlyArray<T>;
921
- error: Error | undefined;
922
- status: UseStreamStatus;
923
- }
924
- interface UseStreamOptions {
925
- /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
926
- maxBuffer?: number;
927
- shardKey?: string;
928
- }
929
- /**
930
- * Subscribe to a streaming query. Returns the chunks pushed so far plus a
931
- * lifecycle status and a cancel function. Changing `fn` or the serialized
932
- * `args` resets the stream — the previous iterator is cancelled and a fresh
933
- * one opens with empty `chunks`.
934
- *
935
- * Pass `"skip"` for `args` to keep the hook mounted without opening a stream
936
- * (mirrors `useQuery` / `useSubscription`).
937
- */
938
- declare const useStream: <F extends FunctionReference<"stream">>(function_: F, args: "skip" | ArgsOf<F>, options?: UseStreamOptions) => UseStreamResult<ReturnOf<F>>;
939
1004
  /**
940
1005
  * Subscribe to a real-time stream from the server. Unlike `useQuery`, this
941
1006
  * hook does not issue an initial HTTP fetch — it only delivers values that
@@ -1089,4 +1154,4 @@ interface UseVoiceAgentResult {
1089
1154
  * `createSocket`) so the hook is drivable outside a browser.
1090
1155
  */
1091
1156
  declare const useVoiceAgent: (options: UseVoiceAgentOptions) => UseVoiceAgentResult;
1092
- export { type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type MutatorHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, type UseAgentApi, type UseAgentChatApi, type UseAgentChatOptions, type UseAgentChatResult, type UseAgentOptions, type UseAgentResult, type UseAgentStateApi, type UseAgentStateOptions, type UseAgentStateResult, type UseAgentToolEventsApi, type UseAgentToolEventsOptions, type UseAgentToolEventsResult, type UseAuthResult, type UseCheckoutResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UseMutationCallOptions, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseStreamOptions, type UseStreamResult, type UseStreamStatus, type UseSubscriptionResult, type UseVoiceAgentOptions, type UseVoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, hydratePreloaded, useAgent, useAgentChat, useAgentState, useAgentToolEvents, useAuth, useAuthState, useCheckout, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription, useVoiceAgent };
1157
+ export { type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type MutatorHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, type UseAgentApi, type UseAgentChatApi, type UseAgentChatOptions, type UseAgentChatResult, type UseAgentOptions, type UseAgentResult, type UseAgentStateApi, type UseAgentStateOptions, type UseAgentStateResult, type UseAgentToolEventsApi, type UseAgentToolEventsOptions, type UseAgentToolEventsResult, type UseAuthResult, type UseCheckoutResult, type UseHttpStreamOptions, type UseHttpStreamResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UseMutationCallOptions, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseStreamOptions, type UseStreamResult, type UseStreamStatus, type UseSubscriptionResult, type UseVoiceAgentOptions, type UseVoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, hydratePreloaded, useAgent, useAgentChat, useAgentState, useAgentToolEvents, useAuth, useAuthState, useCheckout, useClientQuery, useConnectionStatus, useFlag, useFlags, useHttpStream, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription, useVoiceAgent };
package/dist/index.mjs CHANGED
@@ -3,23 +3,25 @@ export { AuthLoading, Authenticated, Unauthenticated } from './packem_shared/Aut
3
3
  export { useAuthState } from './packem_shared/useAuthState-CyF35qZR.mjs';
4
4
  export { LunoraProvider, useLunora } from './packem_shared/LunoraProvider-BsuiW4Lk.mjs';
5
5
  export { CheckoutButton, CustomerPortalButton, useCheckout } from './packem_shared/CheckoutButton-DUite8jJ.mjs';
6
- export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-C3uwOTMW.mjs';
7
- export { useAgent } from './packem_shared/useAgent-DwLxTc3P.mjs';
8
- export { useAgentChat } from './packem_shared/useAgentChat-WPm5iHgR.mjs';
9
- export { useAgentState } from './packem_shared/useAgentState-Cikdlomg.mjs';
10
- export { useAgentToolEvents } from './packem_shared/useAgentToolEvents-BM7ERo00.mjs';
6
+ export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-CefbPBId.mjs';
7
+ export { useAgent } from './packem_shared/useAgent-BNLlIYHz.mjs';
8
+ export { useAgentChat } from './packem_shared/useAgentChat-CSjKniMO.mjs';
9
+ export { useAgentState } from './packem_shared/useAgentState-Cqyd3Dfy.mjs';
10
+ export { useAgentToolEvents } from './packem_shared/useAgentToolEvents-gF7U4_KY.mjs';
11
11
  export { default as useAuth } from './packem_shared/useAuth-BSutjJaa.mjs';
12
+ export { default as useClientQuery } from './packem_shared/useClientQuery-CN1sC3cP.mjs';
12
13
  export { default as useConnectionStatus } from './packem_shared/useConnectionStatus-PJ7vyCHy.mjs';
13
- export { useFlag, useFlags } from './packem_shared/useFlag-C2Pkghli.mjs';
14
- export { default as useInfiniteQuery } from './packem_shared/useInfiniteQuery-BeaehE9f.mjs';
14
+ export { useFlag, useFlags } from './packem_shared/useFlag-Beyeiq2b.mjs';
15
+ export { useHttpStream } from './packem_shared/useHttpStream-oc4s1pMy.mjs';
16
+ export { default as useInfiniteQuery } from './packem_shared/useInfiniteQuery-CJNw1XaZ.mjs';
15
17
  export { useMutation } from './packem_shared/useMutation-BeqeIjTr.mjs';
16
18
  export { useMutator } from './packem_shared/useMutator-IIrLtDiM.mjs';
17
- export { usePaginatedQuery } from './packem_shared/usePaginatedQuery-_6ETVUmf.mjs';
18
- export { hydratePreloaded, default as usePreloadedQuery } from './packem_shared/hydratePreloaded-uMDH_xA9.mjs';
19
+ export { usePaginatedQuery } from './packem_shared/usePaginatedQuery-DWzydaVR.mjs';
20
+ export { hydratePreloaded, default as usePreloadedQuery } from './packem_shared/hydratePreloaded-2omLtadR.mjs';
19
21
  export { usePresence } from './packem_shared/usePresence-Rnx1Fznc.mjs';
20
- export { default as useQuery } from './packem_shared/useQuery-D3_ZxMYs.mjs';
22
+ export { default as useQuery } from './packem_shared/useQuery-CE8_QUgh.mjs';
21
23
  export { useRateLimit } from './packem_shared/useRateLimit-DTEffQEi.mjs';
22
- export { useStream } from './packem_shared/useStream-CGC26afa.mjs';
23
- export { default as useSubscription } from './packem_shared/useSubscription-D38Jdyr8.mjs';
24
+ export { useStream } from './packem_shared/useStream-GYNgc9J5.mjs';
25
+ export { default as useSubscription } from './packem_shared/useSubscription-DcDlvVuI.mjs';
24
26
  export { useVoiceAgent } from './packem_shared/useVoiceAgent-sC5_ADB8.mjs';
25
- export { getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
27
+ export { createClientQuery, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from '@lunora/client';
@@ -1,4 +1,4 @@
1
- import { k as keyHash } from './query-key-BmCbgidV.mjs';
1
+ import { k as keyHash } from './query-key-LGnArBTB.mjs';
2
2
 
3
3
  class LunoraSubscriptionRegistry {
4
4
  constructor(client) {
@@ -2,9 +2,9 @@
2
2
  import { c } from 'react/compiler-runtime';
3
3
  import { useQueryClient, useQuery } from '@tanstack/react-query';
4
4
  import { useEffect } from 'react';
5
- import { g as getSubscriptionRegistry } from './cache-D1O_xfAe.mjs';
5
+ import { g as getSubscriptionRegistry } from './cache-JeuDAXfE.mjs';
6
6
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
7
- import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-BmCbgidV.mjs';
7
+ import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-LGnArBTB.mjs';
8
8
 
9
9
  const usePreloadedQuery = function(preloaded) {
10
10
  const $ = c(4);
@@ -1,4 +1,4 @@
1
- import { l as lunoraQueryKey } from './query-key-BmCbgidV.mjs';
1
+ import { l as lunoraQueryKey } from './query-key-LGnArBTB.mjs';
2
2
 
3
3
  const lunoraQueryOptions = (client, function_, args, options = {}) => {
4
4
  const argsRecord = args ?? {};
@@ -1,6 +1,6 @@
1
- import { s as stableStringify } from './stable-key-CGp4e2Ux.mjs';
1
+ import { s as stableWireKey } from './wire-key-Bg8BJ8YM.mjs';
2
2
 
3
- const keyHash = (queryKey) => stableStringify(queryKey);
3
+ const keyHash = (queryKey) => stableWireKey(queryKey);
4
4
  const lunoraQueryKey = (function_, args, shardKey) => [
5
5
  "lunora",
6
6
  function_.__lunoraRef,
@@ -9,7 +9,7 @@ const stableStringify = (value) => {
9
9
  return "null";
10
10
  }
11
11
  if (typeof value === "bigint") {
12
- throw new TypeError("stableStringify: cannot use a bigint in a cache key (query/subscription/shape args) — pass it as a string");
12
+ throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");
13
13
  }
14
14
  if (value === null || typeof value !== "object") {
15
15
  return JSON.stringify(value);
@@ -20,7 +20,7 @@ const stableStringify = (value) => {
20
20
  const proto = Object.getPrototypeOf(value);
21
21
  if (proto !== null && proto !== Object.prototype) {
22
22
  const name = value.constructor?.name ?? "value";
23
- throw new TypeError(`stableStringify: cannot use a ${name} in a cache key (query/subscription/shape args) — only plain objects, arrays, and JSON primitives are supported`);
23
+ throw new TypeError(`stableStringify: cannot use a ${name} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`);
24
24
  }
25
25
  const record = value;
26
26
  const keys = Object.keys(record).toSorted(compareKeys);
@@ -2,9 +2,9 @@
2
2
  import { initialPages, rebalance, derivePaginationStatus, applyLoadMore } from '@lunora/client/pagination';
3
3
  import { useQueryClient } from '@tanstack/react-query';
4
4
  import { useRef, useReducer, useState, useEffect, useCallback } from 'react';
5
- import { g as getSubscriptionRegistry } from './cache-D1O_xfAe.mjs';
5
+ import { g as getSubscriptionRegistry } from './cache-JeuDAXfE.mjs';
6
6
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
7
- import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-BmCbgidV.mjs';
7
+ import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-LGnArBTB.mjs';
8
8
 
9
9
  const useLazyRef = function(create) {
10
10
  const reference = useRef(void 0);
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
3
  import { useMutation } from './useMutation-BeqeIjTr.mjs';
4
- import useSubscription from './useSubscription-D38Jdyr8.mjs';
4
+ import useSubscription from './useSubscription-DcDlvVuI.mjs';
5
5
 
6
6
  const NO_MUTATION_REF = {
7
7
  __lunoraRef: ""
@@ -2,8 +2,8 @@
2
2
  import { c } from 'react/compiler-runtime';
3
3
  import { useState, useRef } from 'react';
4
4
  import { useMutation } from './useMutation-BeqeIjTr.mjs';
5
- import { useStream } from './useStream-CGC26afa.mjs';
6
- import useSubscription from './useSubscription-D38Jdyr8.mjs';
5
+ import { useStream } from './useStream-GYNgc9J5.mjs';
6
+ import useSubscription from './useSubscription-DcDlvVuI.mjs';
7
7
 
8
8
  const NO_MUTATION_REF = {
9
9
  __lunoraRef: ""
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
- import useSubscription from './useSubscription-D38Jdyr8.mjs';
3
+ import useSubscription from './useSubscription-DcDlvVuI.mjs';
4
4
 
5
5
  const useAgentState = (options) => {
6
6
  const $ = c(5);
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
- import { useStream } from './useStream-CGC26afa.mjs';
4
- import useSubscription from './useSubscription-D38Jdyr8.mjs';
3
+ import { useStream } from './useStream-GYNgc9J5.mjs';
4
+ import useSubscription from './useSubscription-DcDlvVuI.mjs';
5
5
 
6
6
  const NO_STREAM_REF = {
7
7
  __lunoraRef: ""
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { c } from 'react/compiler-runtime';
3
+ import { useSyncExternalStore } from 'react';
4
+ import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
5
+
6
+ const useClientQuery = (ref) => {
7
+ const $ = c(11);
8
+ const client = useLunora();
9
+ let t0;
10
+ let t1;
11
+ let t2;
12
+ if ($[0] !== client || $[1] !== ref) {
13
+ t0 = (onStoreChange) => {
14
+ const unsubscribe = client.subscribeClientQuery(ref, () => {
15
+ onStoreChange();
16
+ });
17
+ return unsubscribe;
18
+ };
19
+ t1 = () => client.getClientQuery(ref);
20
+ t2 = () => client.getClientQuery(ref);
21
+ $[0] = client;
22
+ $[1] = ref;
23
+ $[2] = t0;
24
+ $[3] = t1;
25
+ $[4] = t2;
26
+ } else {
27
+ t0 = $[2];
28
+ t1 = $[3];
29
+ t2 = $[4];
30
+ }
31
+ const value = useSyncExternalStore(t0, t1, t2);
32
+ let t3;
33
+ if ($[5] !== client || $[6] !== ref) {
34
+ t3 = (next) => {
35
+ client.setClientQuery(ref, next);
36
+ };
37
+ $[5] = client;
38
+ $[6] = ref;
39
+ $[7] = t3;
40
+ } else {
41
+ t3 = $[7];
42
+ }
43
+ const setter = t3;
44
+ let t4;
45
+ if ($[8] !== setter || $[9] !== value) {
46
+ t4 = [value, setter];
47
+ $[8] = setter;
48
+ $[9] = value;
49
+ $[10] = t4;
50
+ } else {
51
+ t4 = $[10];
52
+ }
53
+ return t4;
54
+ };
55
+
56
+ export { useClientQuery as default };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { useState, useRef, useEffect } from 'react';
3
3
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
4
- import { s as stableStringify } from './stable-key-CGp4e2Ux.mjs';
4
+ import { s as stableStringify } from './stable-key-DePnevIy.mjs';
5
5
 
6
6
  const FLAGS_EVAL_PATH = "__lunora_flags__:eval";
7
7
  const flagKind = (value) => {
@@ -0,0 +1,127 @@
1
+ 'use client';
2
+ import { useReducer, useRef, useEffect } from 'react';
3
+ import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
4
+ import { s as stableStringify } from './stable-key-DePnevIy.mjs';
5
+
6
+ const reducer = function(state, action) {
7
+ switch (action.type) {
8
+ case "chunk": {
9
+ return {
10
+ chunks: [...state.chunks, action.chunk],
11
+ error: void 0,
12
+ status: "streaming"
13
+ };
14
+ }
15
+ case "complete": {
16
+ return {
17
+ ...state,
18
+ status: "complete"
19
+ };
20
+ }
21
+ case "error": {
22
+ return {
23
+ ...state,
24
+ error: action.error,
25
+ status: "error"
26
+ };
27
+ }
28
+ case "reset": {
29
+ return {
30
+ chunks: [],
31
+ error: void 0,
32
+ status: "idle"
33
+ };
34
+ }
35
+ case "start": {
36
+ return {
37
+ ...state,
38
+ status: "streaming"
39
+ };
40
+ }
41
+ default: {
42
+ return state;
43
+ }
44
+ }
45
+ };
46
+ const useHttpStream = (route, args, options = {}) => {
47
+ const client = useLunora();
48
+ const [state, dispatch] = useReducer(reducer, {
49
+ chunks: [],
50
+ error: void 0,
51
+ status: "idle"
52
+ });
53
+ const skipped = args === "skip";
54
+ const serialized = skipped ? "skip" : stableStringify(args);
55
+ const cancelRef = useRef(void 0);
56
+ useEffect(() => {
57
+ if (skipped) {
58
+ dispatch({
59
+ type: "reset"
60
+ });
61
+ return () => {
62
+ };
63
+ }
64
+ dispatch({
65
+ type: "reset"
66
+ });
67
+ dispatch({
68
+ type: "start"
69
+ });
70
+ let stillMounted = true;
71
+ let cancelled = false;
72
+ const iterable = client.httpStream(route, args, {
73
+ maxBuffer: options.maxBuffer
74
+ });
75
+ const cancel = () => {
76
+ if (cancelled) {
77
+ return;
78
+ }
79
+ cancelled = true;
80
+ iterable.cancel();
81
+ };
82
+ cancelRef.current = cancel;
83
+ (async () => {
84
+ try {
85
+ for await (const chunk of iterable) {
86
+ if (!stillMounted) {
87
+ return;
88
+ }
89
+ dispatch({
90
+ chunk,
91
+ type: "chunk"
92
+ });
93
+ }
94
+ if (stillMounted) {
95
+ dispatch({
96
+ type: "complete"
97
+ });
98
+ }
99
+ } catch (error) {
100
+ if (!stillMounted) {
101
+ return;
102
+ }
103
+ const normalized = error instanceof Error ? error : new Error(String(error));
104
+ dispatch({
105
+ error: normalized,
106
+ type: "error"
107
+ });
108
+ }
109
+ })().catch(() => {
110
+ });
111
+ return () => {
112
+ stillMounted = false;
113
+ cancel();
114
+ cancelRef.current = void 0;
115
+ };
116
+ }, [client, route.method, route.path, serialized, skipped, options.maxBuffer]);
117
+ return {
118
+ cancel: () => {
119
+ cancelRef.current?.();
120
+ },
121
+ chunks: state.chunks,
122
+ error: state.error,
123
+ status: state.status
124
+ };
125
+ };
126
+
127
+ export { useHttpStream };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
3
  import { useRef, useEffect } from 'react';
4
- import { u as usePaginatedCore } from './use-paginated-core-DafedU4l.mjs';
4
+ import { u as usePaginatedCore } from './use-paginated-core-xxvd1YA0.mjs';
5
5
 
6
6
  const useInfiniteQuery = (function_, args, options) => {
7
7
  const $ = c(15);
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
- import { u as usePaginatedCore } from './use-paginated-core-DafedU4l.mjs';
3
+ import { u as usePaginatedCore } from './use-paginated-core-xxvd1YA0.mjs';
4
4
 
5
5
  const usePaginatedQuery = (function_, args, options) => {
6
6
  const $ = c(7);
@@ -1,13 +1,13 @@
1
1
  'use client';
2
2
  import { c } from 'react/compiler-runtime';
3
3
  import { useQueryClient, useQuery as useQuery$1 } from '@tanstack/react-query';
4
- import { useEffect } from 'react';
5
- import { g as getSubscriptionRegistry } from './cache-D1O_xfAe.mjs';
4
+ import { useState, useEffect } from 'react';
5
+ import { g as getSubscriptionRegistry } from './cache-JeuDAXfE.mjs';
6
6
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
7
- import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-BmCbgidV.mjs';
7
+ import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-LGnArBTB.mjs';
8
8
 
9
9
  const useQuery = (function_, args, t0) => {
10
- const $ = c(5);
10
+ const $ = c(9);
11
11
  const options = t0 === void 0 ? {} : t0;
12
12
  const client = useLunora();
13
13
  const queryClient = useQueryClient();
@@ -17,27 +17,50 @@ const useQuery = (function_, args, t0) => {
17
17
  const skipped = args === "skip";
18
18
  const argsRecord = skipped ? {} : args;
19
19
  const queryKey = lunoraQueryKey(function_, argsRecord, shardKey);
20
+ const [hydrated, setHydrated] = useState(client.isReady);
21
+ let t1;
22
+ let t2;
23
+ if ($[0] !== client || $[1] !== hydrated) {
24
+ t1 = () => {
25
+ if (!hydrated) {
26
+ client.whenReady().then(() => {
27
+ setHydrated(true);
28
+ });
29
+ }
30
+ };
31
+ t2 = [client, hydrated];
32
+ $[0] = client;
33
+ $[1] = hydrated;
34
+ $[2] = t1;
35
+ $[3] = t2;
36
+ } else {
37
+ t1 = $[2];
38
+ t2 = $[3];
39
+ }
40
+ useEffect(t1, t2);
41
+ const cachedData = skipped ? void 0 : client.peekHydratedQuery(function_.__lunoraRef, argsRecord, shardKey);
20
42
  const {
21
43
  data
22
44
  } = useQuery$1({
23
- enabled: !skipped,
45
+ enabled: !skipped && hydrated,
46
+ initialData: cachedData,
24
47
  queryFn: () => client.query(function_, argsRecord, {
25
48
  shardKey
26
49
  }),
27
50
  queryKey,
28
51
  staleTime: Number.POSITIVE_INFINITY
29
52
  });
30
- const t1 = serializeQueryKey(queryKey);
31
- let t2;
32
- if ($[0] !== client || $[1] !== queryClient || $[2] !== skipped || $[3] !== t1) {
33
- t2 = [client, queryClient, t1, skipped];
34
- $[0] = client;
35
- $[1] = queryClient;
36
- $[2] = skipped;
37
- $[3] = t1;
38
- $[4] = t2;
53
+ const t3 = serializeQueryKey(queryKey);
54
+ let t4;
55
+ if ($[4] !== client || $[5] !== queryClient || $[6] !== skipped || $[7] !== t3) {
56
+ t4 = [client, queryClient, t3, skipped];
57
+ $[4] = client;
58
+ $[5] = queryClient;
59
+ $[6] = skipped;
60
+ $[7] = t3;
61
+ $[8] = t4;
39
62
  } else {
40
- t2 = $[4];
63
+ t4 = $[8];
41
64
  }
42
65
  useEffect(() => {
43
66
  if (skipped) {
@@ -45,7 +68,7 @@ const useQuery = (function_, args, t0) => {
45
68
  }
46
69
  const registry = getSubscriptionRegistry(client);
47
70
  return registry.attach(queryClient, queryKey, function_, argsRecord, shardKey);
48
- }, t2);
71
+ }, t4);
49
72
  return skipped ? void 0 : data;
50
73
  };
51
74
  function _temp() {
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { useReducer, useRef, useEffect } from 'react';
3
3
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
4
- import { s as stableStringify } from './stable-key-CGp4e2Ux.mjs';
4
+ import { s as stableWireKey } from './wire-key-Bg8BJ8YM.mjs';
5
5
 
6
6
  const reducer = function(state, action) {
7
7
  switch (action.type) {
@@ -51,7 +51,7 @@ const useStream = (function_, args, options = {}) => {
51
51
  status: "idle"
52
52
  });
53
53
  const skipped = args === "skip";
54
- const serialized = skipped ? "skip" : stableStringify(args);
54
+ const serialized = skipped ? "skip" : stableWireKey(args);
55
55
  const cancelRef = useRef(void 0);
56
56
  useEffect(() => {
57
57
  if (skipped) {
@@ -3,7 +3,7 @@ import { c } from 'react/compiler-runtime';
3
3
  import { createQuerySubscription } from '@lunora/client/query';
4
4
  import { useState, useRef, useEffect } from 'react';
5
5
  import { useLunora } from './LunoraProvider-BsuiW4Lk.mjs';
6
- import { s as stableStringify } from './stable-key-CGp4e2Ux.mjs';
6
+ import { s as stableWireKey } from './wire-key-Bg8BJ8YM.mjs';
7
7
 
8
8
  const useSubscription = (function_, args, t0) => {
9
9
  const $ = c(22);
@@ -31,7 +31,7 @@ const useSubscription = (function_, args, t0) => {
31
31
  const skipped = args === "skip";
32
32
  let t3;
33
33
  if ($[3] !== args || $[4] !== skipped) {
34
- t3 = skipped ? "skip" : stableStringify(args);
34
+ t3 = skipped ? "skip" : stableWireKey(args);
35
35
  $[3] = args;
36
36
  $[4] = skipped;
37
37
  $[5] = t3;
@@ -0,0 +1,101 @@
1
+ import { s as stableStringify } from './stable-key-DePnevIy.mjs';
2
+
3
+ const toBase64 = (bytes) => {
4
+ let binary = "";
5
+ const chunk = 32768;
6
+ for (let index = 0; index < bytes.length; index += chunk) {
7
+ binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
8
+ }
9
+ return btoa(binary);
10
+ };
11
+
12
+ const TAG = "$lunora.wire$";
13
+ const MAX_DEPTH = 64;
14
+ const encodeWire = (value, depth = 0) => {
15
+ if (depth > MAX_DEPTH) {
16
+ throw new RangeError(`wire-codec: value nesting exceeds the ${MAX_DEPTH}-level limit`);
17
+ }
18
+ if (value === void 0) {
19
+ return [TAG, "undefined"];
20
+ }
21
+ if (value === null) {
22
+ return null;
23
+ }
24
+ const kind = typeof value;
25
+ if (kind === "bigint") {
26
+ return [TAG, "bigint", value.toString()];
27
+ }
28
+ if (kind === "number") {
29
+ const numeric = value;
30
+ if (Number.isNaN(numeric)) {
31
+ return [TAG, "nan"];
32
+ }
33
+ if (numeric === Infinity) {
34
+ return [TAG, "inf"];
35
+ }
36
+ if (numeric === -Infinity) {
37
+ return [TAG, "-inf"];
38
+ }
39
+ return numeric;
40
+ }
41
+ if (kind !== "object") {
42
+ return value;
43
+ }
44
+ if (value instanceof Date) {
45
+ return [TAG, "date", encodeWire(value.getTime(), depth + 1)];
46
+ }
47
+ if (value instanceof Error) {
48
+ const error = value;
49
+ const properties = {};
50
+ for (const key of Object.keys(error)) {
51
+ if (error[key] !== void 0) {
52
+ properties[key] = encodeWire(error[key], depth + 1);
53
+ }
54
+ }
55
+ const encodedError = [TAG, "error", error.name, error.message, properties];
56
+ if (error.cause !== void 0) {
57
+ encodedError.push(encodeWire(error.cause, depth + 1));
58
+ }
59
+ return encodedError;
60
+ }
61
+ if (value instanceof URL) {
62
+ return [TAG, "url", value.href];
63
+ }
64
+ if (value instanceof Map) {
65
+ return [TAG, "map", [...value.entries()].map(([k, v]) => [encodeWire(k, depth + 1), encodeWire(v, depth + 1)])];
66
+ }
67
+ if (value instanceof Set) {
68
+ return [TAG, "set", [...value].map((item) => encodeWire(item, depth + 1))];
69
+ }
70
+ if (value instanceof ArrayBuffer) {
71
+ return [TAG, "bytes", toBase64(new Uint8Array(value)), "ArrayBuffer"];
72
+ }
73
+ if (ArrayBuffer.isView(value)) {
74
+ const view = value;
75
+ const ctorName = view.constructor.name;
76
+ const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
77
+ return ctorName === "Uint8Array" ? [TAG, "bytes", toBase64(bytes)] : [TAG, "bytes", toBase64(bytes), ctorName];
78
+ }
79
+ if (Array.isArray(value)) {
80
+ const encoded = value.map((item) => encodeWire(item, depth + 1));
81
+ return encoded.length > 0 && encoded[0] === TAG ? [TAG, "arr", encoded] : encoded;
82
+ }
83
+ const proto = Object.getPrototypeOf(value);
84
+ if (proto !== null && proto !== Object.prototype) {
85
+ const name = value.constructor?.name ?? "value";
86
+ throw new TypeError(`wire-codec: cannot encode a ${name} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`);
87
+ }
88
+ const source = value;
89
+ const result = {};
90
+ for (const key of Object.keys(source)) {
91
+ const field = source[key];
92
+ if (field !== void 0) {
93
+ result[key] = encodeWire(field, depth + 1);
94
+ }
95
+ }
96
+ return result;
97
+ };
98
+
99
+ const stableWireKey = (value) => stableStringify(encodeWire(value));
100
+
101
+ export { stableWireKey as s };
package/dist/server.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createServerClient } from '@lunora/client/ssr';
2
2
  export { createServerClient, deserializePreloaded, getServerSession, serializePreloaded } from '@lunora/client/ssr';
3
- import { l as lunoraQueryKey } from './packem_shared/query-key-BmCbgidV.mjs';
4
- export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-C3uwOTMW.mjs';
3
+ import { l as lunoraQueryKey } from './packem_shared/query-key-LGnArBTB.mjs';
4
+ export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-CefbPBId.mjs';
5
5
  export { preloadQuery, preloadedQueryResult } from '@lunora/client';
6
6
  export { HydrationBoundary, dehydrate } from '@tanstack/react-query';
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/react",
3
- "version": "1.0.0-alpha.25",
3
+ "version": "1.0.0-alpha.27",
4
4
  "description": "React hooks for Lunora: useQuery, useMutation, useSubscription, and useAuth",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -49,9 +49,9 @@
49
49
  "access": "public"
50
50
  },
51
51
  "dependencies": {
52
- "@lunora/client": "1.0.0-alpha.21",
53
- "@lunora/errors": "1.0.0-alpha.4",
54
- "@lunora/ratelimit": "1.0.0-alpha.7"
52
+ "@lunora/client": "1.0.0-alpha.23",
53
+ "@lunora/errors": "1.0.0-alpha.5",
54
+ "@lunora/ratelimit": "1.0.0-alpha.8"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@tanstack/react-query": "^5.101.0",