@lunora/react 1.0.0-alpha.4 → 1.0.0-alpha.6

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/README.md CHANGED
@@ -103,6 +103,8 @@ function MessageList({ room }: { room: string }) {
103
103
  | `useInfiniteQuery(fn, args, { initialNumItems })` | `{ pages, status, hasNextPage, fetchNextPage, … }` | Page-array variant of the same paginator. |
104
104
  | `usePreloadedQuery(preloaded)` | `T` | Reads a server `Preloaded` token, then goes live. |
105
105
  | `usePresence({ heartbeat, listPresent, roomId, … })` | present members | Drives the `definePresence` heartbeat + list functions. |
106
+ | `useFlag(key, default, context?)` | flag value (live) | Live OpenFeature flag over the WS; reads `default` until the server answers. |
107
+ | `useFlags(defaults, context?)` | `{ [key]: value }` | Batch variant — one live value per key in the `defaults` map. |
106
108
  | `useStream(fn, args, options?)` | `{ chunks, status, error, cancel }` | Consumes a streamed action response chunk by chunk. |
107
109
  | `useRateLimit(config, options?)` | `{ ok, disabled, retryAfter, check, consume, reset }` | Client-side mirror of a `@lunora/ratelimit` budget for instant UX. |
108
110
  | `useConnectionStatus()` | `ConnectionStatus` | `idle` / `connecting` / `connected` / `offline`. |
package/dist/index.d.mts CHANGED
@@ -233,6 +233,38 @@ declare const useAuth: () => UseAuthResult;
233
233
  * operator can tell a healthy live channel from a silently-dropped socket.
234
234
  */
235
235
  declare const useConnectionStatus: () => ConnectionStatus;
236
+ /** A targeting context merged on top of the app's default (`defineFlags({ identify })`). */
237
+ type FlagContext = Record<string, unknown>;
238
+ /** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
239
+ type FlagValue = boolean | number | string | {
240
+ [key: string]: unknown;
241
+ } | unknown[] | null;
242
+ /**
243
+ * Subscribe to a single feature flag, live over Lunora's WebSocket.
244
+ *
245
+ * Returns `defaultValue` until the first evaluation lands, then the server's
246
+ * resolved value — re-pushed whenever the provider re-evaluates (e.g. a flag is
247
+ * toggled in Cloudflare Flagship). The flag's kind is inferred from
248
+ * `defaultValue`'s runtime type, so `useFlag("dark", false)` reads a boolean and
249
+ * `useFlag("hero", "control")` a string. `context` supplies a per-call targeting
250
+ * context merged on top of the app's default `identify` targeting key.
251
+ *
252
+ * Evaluation runs through whatever OpenFeature provider the app wired in
253
+ * `lunora/flags.ts`; the read never throws — a provider error resolves the
254
+ * default (the same fail-open contract as server-side `ctx.flags`).
255
+ */
256
+ declare const useFlag: <T extends FlagValue>(key: string, defaultValue: T, context?: FlagContext) => T;
257
+ /**
258
+ * Subscribe to several feature flags at once, live over Lunora's WebSocket.
259
+ *
260
+ * Pass a record of `key → defaultValue`; each flag's kind is inferred from its
261
+ * default, and the result is the same-shaped record with resolved values (the
262
+ * defaults until each evaluation lands). A single `context` applies to every
263
+ * flag. This is the batched form of {@link useFlag} — one effect manages one
264
+ * subscription per key, so it stays rules-of-hooks-safe even as the flag set
265
+ * changes between renders.
266
+ */
267
+ declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: FlagContext) => T;
236
268
  /** The args a paginated query exposes minus the framework-supplied page cursor. */
237
269
  type PaginatedArgs<F> = Omit<ArgsOf<F>, "paginationOpts">;
238
270
  /** The element type of the `page` array a paginated query returns. */
@@ -496,4 +528,4 @@ declare const useStream: <F extends FunctionReference<"stream">>(function_: F, a
496
528
  * the server pushes over the WS.
497
529
  */
498
530
  declare const useSubscription: <F extends FunctionReference>(function_: F, args: ArgsOf<F> | "skip", options?: UseQueryOptions) => UseSubscriptionResult<ReturnOf<F>>;
499
- export { AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, 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, hydratePreloaded, useAuth, useAuthState, useCheckout, useConnectionStatus, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription };
531
+ export { AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, 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, hydratePreloaded, useAuth, useAuthState, useCheckout, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription };
package/dist/index.d.ts CHANGED
@@ -233,6 +233,38 @@ declare const useAuth: () => UseAuthResult;
233
233
  * operator can tell a healthy live channel from a silently-dropped socket.
234
234
  */
235
235
  declare const useConnectionStatus: () => ConnectionStatus;
236
+ /** A targeting context merged on top of the app's default (`defineFlags({ identify })`). */
237
+ type FlagContext = Record<string, unknown>;
238
+ /** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
239
+ type FlagValue = boolean | number | string | {
240
+ [key: string]: unknown;
241
+ } | unknown[] | null;
242
+ /**
243
+ * Subscribe to a single feature flag, live over Lunora's WebSocket.
244
+ *
245
+ * Returns `defaultValue` until the first evaluation lands, then the server's
246
+ * resolved value — re-pushed whenever the provider re-evaluates (e.g. a flag is
247
+ * toggled in Cloudflare Flagship). The flag's kind is inferred from
248
+ * `defaultValue`'s runtime type, so `useFlag("dark", false)` reads a boolean and
249
+ * `useFlag("hero", "control")` a string. `context` supplies a per-call targeting
250
+ * context merged on top of the app's default `identify` targeting key.
251
+ *
252
+ * Evaluation runs through whatever OpenFeature provider the app wired in
253
+ * `lunora/flags.ts`; the read never throws — a provider error resolves the
254
+ * default (the same fail-open contract as server-side `ctx.flags`).
255
+ */
256
+ declare const useFlag: <T extends FlagValue>(key: string, defaultValue: T, context?: FlagContext) => T;
257
+ /**
258
+ * Subscribe to several feature flags at once, live over Lunora's WebSocket.
259
+ *
260
+ * Pass a record of `key → defaultValue`; each flag's kind is inferred from its
261
+ * default, and the result is the same-shaped record with resolved values (the
262
+ * defaults until each evaluation lands). A single `context` applies to every
263
+ * flag. This is the batched form of {@link useFlag} — one effect manages one
264
+ * subscription per key, so it stays rules-of-hooks-safe even as the flag set
265
+ * changes between renders.
266
+ */
267
+ declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: FlagContext) => T;
236
268
  /** The args a paginated query exposes minus the framework-supplied page cursor. */
237
269
  type PaginatedArgs<F> = Omit<ArgsOf<F>, "paginationOpts">;
238
270
  /** The element type of the `page` array a paginated query returns. */
@@ -496,4 +528,4 @@ declare const useStream: <F extends FunctionReference<"stream">>(function_: F, a
496
528
  * the server pushes over the WS.
497
529
  */
498
530
  declare const useSubscription: <F extends FunctionReference>(function_: F, args: ArgsOf<F> | "skip", options?: UseQueryOptions) => UseSubscriptionResult<ReturnOf<F>>;
499
- export { AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, 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, hydratePreloaded, useAuth, useAuthState, useCheckout, useConnectionStatus, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription };
531
+ export { AuthLoading, type AuthState, Authenticated, CheckoutButton, type CheckoutButtonProps, CustomerPortalButton, type CustomerPortalButtonProps, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraProvider, type LunoraProviderProps, type MutationHook, type PageItemOf, type PaginatedArgs, type RedirectTarget, type RedirectTrigger, type Subscription, Unauthenticated, 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, hydratePreloaded, useAuth, useAuthState, useCheckout, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePreloadedQuery, usePresence, useQuery, useRateLimit, useStream, useSubscription };
package/dist/index.mjs CHANGED
@@ -3,15 +3,16 @@ export { AuthLoading, Authenticated, Unauthenticated } from './packem_shared/Aut
3
3
  export { useAuthState } from './packem_shared/useAuthState-BiGhtSCs.mjs';
4
4
  export { LunoraProvider, useLunora } from './packem_shared/LunoraProvider-D38Xp16l.mjs';
5
5
  export { CheckoutButton, CustomerPortalButton, useCheckout } from './packem_shared/CheckoutButton-CVSry8U1.mjs';
6
- export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-CsuWzjg1.mjs';
6
+ export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-Cij4KWBK.mjs';
7
7
  export { default as useAuth } from './packem_shared/useAuth-CNUKtOOp.mjs';
8
8
  export { default as useConnectionStatus } from './packem_shared/useConnectionStatus-DRSY9ldm.mjs';
9
- export { default as useInfiniteQuery } from './packem_shared/useInfiniteQuery-MH0x4l8h.mjs';
9
+ export { useFlag, useFlags } from './packem_shared/useFlag-Bg5DpkGv.mjs';
10
+ export { default as useInfiniteQuery } from './packem_shared/useInfiniteQuery-HjOCDd3R.mjs';
10
11
  export { useMutation } from './packem_shared/useMutation-CrvMXRsk.mjs';
11
- export { usePaginatedQuery } from './packem_shared/usePaginatedQuery-D3PTDRGS.mjs';
12
- export { hydratePreloaded, default as usePreloadedQuery } from './packem_shared/hydratePreloaded-BlFL9FGq.mjs';
12
+ export { usePaginatedQuery } from './packem_shared/usePaginatedQuery-BGYvICZU.mjs';
13
+ export { hydratePreloaded, default as usePreloadedQuery } from './packem_shared/hydratePreloaded-B6OHn0eL.mjs';
13
14
  export { usePresence } from './packem_shared/usePresence-D7jLuxj0.mjs';
14
- export { default as useQuery } from './packem_shared/useQuery-C5S0W-7K.mjs';
15
+ export { default as useQuery } from './packem_shared/useQuery-EB74gtBJ.mjs';
15
16
  export { useRateLimit } from './packem_shared/useRateLimit-DTEffQEi.mjs';
16
- export { useStream } from './packem_shared/useStream-BRY9nemd.mjs';
17
- export { default as useSubscription } from './packem_shared/useSubscription-CHMCjyQg.mjs';
17
+ export { useStream } from './packem_shared/useStream-MdYnWDL_.mjs';
18
+ export { default as useSubscription } from './packem_shared/useSubscription-DSJuryrU.mjs';
@@ -1,4 +1,4 @@
1
- import { k as keyHash } from './query-key-C5rufkEE.mjs';
1
+ import { k as keyHash } from './query-key-B5eRQCbR.mjs';
2
2
 
3
3
  class LunoraSubscriptionRegistry {
4
4
  constructor(client) {
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { useQueryClient, useQuery } from '@tanstack/react-query';
3
3
  import { useMemo, useEffect } from 'react';
4
- import { g as getSubscriptionRegistry } from './cache-CItk3fgN.mjs';
4
+ import { g as getSubscriptionRegistry } from './cache-CAp1fVNL.mjs';
5
5
  import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
6
- import { l as lunoraQueryKey, s as serializeQueryKey } from './query-key-C5rufkEE.mjs';
6
+ import { l as lunoraQueryKey, s as serializeQueryKey } from './query-key-B5eRQCbR.mjs';
7
7
 
8
8
  const usePreloadedQuery = function(preloaded) {
9
9
  const client = useLunora();
@@ -1,4 +1,4 @@
1
- import { l as lunoraQueryKey } from './query-key-C5rufkEE.mjs';
1
+ import { l as lunoraQueryKey } from './query-key-B5eRQCbR.mjs';
2
2
 
3
3
  const lunoraQueryOptions = (client, function_, args, options = {}) => {
4
4
  const argsRecord = args ?? {};
@@ -0,0 +1,13 @@
1
+ import { s as stableStringify } from './stable-key-CaKZv9lp.mjs';
2
+
3
+ const keyHash = (queryKey) => stableStringify(queryKey);
4
+ const lunoraQueryKey = (function_, args, shardKey) => [
5
+ "lunora",
6
+ function_.__lunoraRef,
7
+ args,
8
+ // eslint-disable-next-line unicorn/no-null -- this literal is part of the JSON-serialized query key TanStack hashes for dedup; `null` keeps a stable, distinct slot from an absent shardKey across renders.
9
+ shardKey ?? null
10
+ ];
11
+ const serializeQueryKey = (queryKey) => keyHash(queryKey);
12
+
13
+ export { keyHash as k, lunoraQueryKey as l, serializeQueryKey as s };
@@ -0,0 +1,30 @@
1
+ const compareKeys = (a, b) => {
2
+ if (a < b) {
3
+ return -1;
4
+ }
5
+ return a > b ? 1 : 0;
6
+ };
7
+ const stableStringify = (value) => {
8
+ if (value === void 0) {
9
+ return "null";
10
+ }
11
+ if (value === null || typeof value !== "object") {
12
+ return JSON.stringify(value);
13
+ }
14
+ if (Array.isArray(value)) {
15
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
16
+ }
17
+ const record = value;
18
+ const keys = Object.keys(record).toSorted(compareKeys);
19
+ const parts = [];
20
+ for (const key of keys) {
21
+ const raw = record[key];
22
+ if (raw === void 0) {
23
+ continue;
24
+ }
25
+ parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
26
+ }
27
+ return `{${parts.join(",")}}`;
28
+ };
29
+
30
+ export { stableStringify as s };
@@ -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-CItk3fgN.mjs';
5
+ import { g as getSubscriptionRegistry } from './cache-CAp1fVNL.mjs';
6
6
  import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
7
- import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-C5rufkEE.mjs';
7
+ import { s as serializeQueryKey, l as lunoraQueryKey } from './query-key-B5eRQCbR.mjs';
8
8
 
9
9
  const useLazyRef = function(create) {
10
10
  const reference = useRef(void 0);
@@ -0,0 +1,224 @@
1
+ 'use client';
2
+ import { c } from 'react/compiler-runtime';
3
+ import { useState, useRef, useEffect } from 'react';
4
+ import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
5
+ import { s as stableStringify } from './stable-key-CaKZv9lp.mjs';
6
+
7
+ const FLAGS_EVAL_PATH = "__lunora_flags__:eval";
8
+ const flagKind = (value) => {
9
+ const kind = typeof value;
10
+ if (kind === "boolean" || kind === "number" || kind === "string") {
11
+ return kind;
12
+ }
13
+ return "object";
14
+ };
15
+ const flagsReference = {
16
+ __lunoraRef: FLAGS_EVAL_PATH
17
+ };
18
+ const useFlag = (key, defaultValue, context) => {
19
+ const $ = c(19);
20
+ const client = useLunora();
21
+ const [value, setValue] = useState(defaultValue);
22
+ let t0;
23
+ if ($[0] !== defaultValue) {
24
+ t0 = flagKind(defaultValue);
25
+ $[0] = defaultValue;
26
+ $[1] = t0;
27
+ } else {
28
+ t0 = $[1];
29
+ }
30
+ const type = t0;
31
+ let t1;
32
+ if ($[2] !== context) {
33
+ t1 = context === void 0 ? "" : stableStringify(context);
34
+ $[2] = context;
35
+ $[3] = t1;
36
+ } else {
37
+ t1 = $[3];
38
+ }
39
+ const serializedContext = t1;
40
+ let t2;
41
+ if ($[4] !== context || $[5] !== defaultValue) {
42
+ t2 = {
43
+ context,
44
+ defaultValue
45
+ };
46
+ $[4] = context;
47
+ $[5] = defaultValue;
48
+ $[6] = t2;
49
+ } else {
50
+ t2 = $[6];
51
+ }
52
+ const latest = useRef(t2);
53
+ let t3;
54
+ if ($[7] !== context || $[8] !== defaultValue) {
55
+ t3 = () => {
56
+ latest.current = {
57
+ context,
58
+ defaultValue
59
+ };
60
+ };
61
+ $[7] = context;
62
+ $[8] = defaultValue;
63
+ $[9] = t3;
64
+ } else {
65
+ t3 = $[9];
66
+ }
67
+ useEffect(t3);
68
+ let t4;
69
+ if ($[10] !== client || $[11] !== key || $[12] !== type) {
70
+ t4 = () => {
71
+ let cancelled = false;
72
+ const {
73
+ context: currentContext,
74
+ defaultValue: currentDefault
75
+ } = latest.current;
76
+ setValue(currentDefault);
77
+ let unsubscribe;
78
+ try {
79
+ unsubscribe = client.subscribe(flagsReference, {
80
+ context: currentContext,
81
+ default: currentDefault,
82
+ key,
83
+ type
84
+ }, (next) => {
85
+ if (!cancelled) {
86
+ setValue(next);
87
+ }
88
+ });
89
+ } catch {
90
+ return () => {
91
+ cancelled = true;
92
+ };
93
+ }
94
+ return () => {
95
+ cancelled = true;
96
+ unsubscribe();
97
+ };
98
+ };
99
+ $[10] = client;
100
+ $[11] = key;
101
+ $[12] = type;
102
+ $[13] = t4;
103
+ } else {
104
+ t4 = $[13];
105
+ }
106
+ let t5;
107
+ if ($[14] !== client || $[15] !== key || $[16] !== serializedContext || $[17] !== type) {
108
+ t5 = [client, key, type, serializedContext];
109
+ $[14] = client;
110
+ $[15] = key;
111
+ $[16] = serializedContext;
112
+ $[17] = type;
113
+ $[18] = t5;
114
+ } else {
115
+ t5 = $[18];
116
+ }
117
+ useEffect(t4, t5);
118
+ return value;
119
+ };
120
+ const useFlags = (flags, context) => {
121
+ const $ = c(16);
122
+ const client = useLunora();
123
+ const [values, setValues] = useState(flags);
124
+ let t0;
125
+ if ($[0] !== flags) {
126
+ t0 = stableStringify(flags);
127
+ $[0] = flags;
128
+ $[1] = t0;
129
+ } else {
130
+ t0 = $[1];
131
+ }
132
+ const spec = t0;
133
+ let t1;
134
+ if ($[2] !== context) {
135
+ t1 = context === void 0 ? "" : stableStringify(context);
136
+ $[2] = context;
137
+ $[3] = t1;
138
+ } else {
139
+ t1 = $[3];
140
+ }
141
+ const serializedContext = t1;
142
+ let t2;
143
+ if ($[4] !== context || $[5] !== flags) {
144
+ t2 = {
145
+ context,
146
+ flags
147
+ };
148
+ $[4] = context;
149
+ $[5] = flags;
150
+ $[6] = t2;
151
+ } else {
152
+ t2 = $[6];
153
+ }
154
+ const latest = useRef(t2);
155
+ let t3;
156
+ if ($[7] !== context || $[8] !== flags) {
157
+ t3 = () => {
158
+ latest.current = {
159
+ context,
160
+ flags
161
+ };
162
+ };
163
+ $[7] = context;
164
+ $[8] = flags;
165
+ $[9] = t3;
166
+ } else {
167
+ t3 = $[9];
168
+ }
169
+ useEffect(t3);
170
+ let t4;
171
+ if ($[10] !== client) {
172
+ t4 = () => {
173
+ let cancelled = false;
174
+ const {
175
+ context: currentContext,
176
+ flags: currentFlags
177
+ } = latest.current;
178
+ setValues(currentFlags);
179
+ const unsubscribes = [];
180
+ for (const [key, defaultValue] of Object.entries(currentFlags)) {
181
+ try {
182
+ unsubscribes.push(client.subscribe(flagsReference, {
183
+ context: currentContext,
184
+ default: defaultValue,
185
+ key,
186
+ type: flagKind(defaultValue)
187
+ }, (next) => {
188
+ if (!cancelled) {
189
+ setValues((previous) => ({
190
+ ...previous,
191
+ [key]: next
192
+ }));
193
+ }
194
+ }));
195
+ } catch {
196
+ }
197
+ }
198
+ return () => {
199
+ cancelled = true;
200
+ for (const unsubscribe of unsubscribes) {
201
+ unsubscribe();
202
+ }
203
+ };
204
+ };
205
+ $[10] = client;
206
+ $[11] = t4;
207
+ } else {
208
+ t4 = $[11];
209
+ }
210
+ let t5;
211
+ if ($[12] !== client || $[13] !== serializedContext || $[14] !== spec) {
212
+ t5 = [client, spec, serializedContext];
213
+ $[12] = client;
214
+ $[13] = serializedContext;
215
+ $[14] = spec;
216
+ $[15] = t5;
217
+ } else {
218
+ t5 = $[15];
219
+ }
220
+ useEffect(t4, t5);
221
+ return values;
222
+ };
223
+
224
+ export { useFlag, useFlags };
@@ -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-CoOfcc-p.mjs';
4
+ import { u as usePaginatedCore } from './use-paginated-core-DxGvzQVR.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-CoOfcc-p.mjs';
3
+ import { u as usePaginatedCore } from './use-paginated-core-DxGvzQVR.mjs';
4
4
 
5
5
  const usePaginatedQuery = (function_, args, options) => {
6
6
  const $ = c(7);
@@ -1,9 +1,10 @@
1
1
  'use client';
2
2
  import { useQueryClient, useQuery as useQuery$1 } from '@tanstack/react-query';
3
3
  import { useMemo, useEffect } from 'react';
4
- import { g as getSubscriptionRegistry } from './cache-CItk3fgN.mjs';
4
+ import { g as getSubscriptionRegistry } from './cache-CAp1fVNL.mjs';
5
5
  import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
6
- import { l as lunoraQueryKey, a as stableStringify, s as serializeQueryKey } from './query-key-C5rufkEE.mjs';
6
+ import { l as lunoraQueryKey, s as serializeQueryKey } from './query-key-B5eRQCbR.mjs';
7
+ import { s as stableStringify } from './stable-key-CaKZv9lp.mjs';
7
8
 
8
9
  const useQuery = (function_, args, options = {}) => {
9
10
  const client = useLunora();
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { useReducer, useRef, useEffect } from 'react';
3
3
  import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
4
- import { a as stableStringify } from './query-key-C5rufkEE.mjs';
4
+ import { s as stableStringify } from './stable-key-CaKZv9lp.mjs';
5
5
 
6
6
  const reducer = function(state, action) {
7
7
  switch (action.type) {
@@ -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-D38Xp16l.mjs';
6
- import { a as stableStringify } from './query-key-C5rufkEE.mjs';
6
+ import { s as stableStringify } from './stable-key-CaKZv9lp.mjs';
7
7
 
8
8
  const useSubscription = (function_, args, t0) => {
9
9
  const $ = c(22);
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-C5rufkEE.mjs';
4
- export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-CsuWzjg1.mjs';
3
+ import { l as lunoraQueryKey } from './packem_shared/query-key-B5eRQCbR.mjs';
4
+ export { lunoraQueryOptions } from './packem_shared/lunoraQueryOptions-Cij4KWBK.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.4",
3
+ "version": "1.0.0-alpha.6",
4
4
  "description": "React hooks for Lunora: useQuery, useMutation, useSubscription, and useAuth",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -49,7 +49,7 @@
49
49
  "access": "public"
50
50
  },
51
51
  "dependencies": {
52
- "@lunora/client": "1.0.0-alpha.3",
52
+ "@lunora/client": "1.0.0-alpha.4",
53
53
  "@lunora/ratelimit": "1.0.0-alpha.3"
54
54
  },
55
55
  "peerDependencies": {
@@ -1,21 +0,0 @@
1
- const keyHash = (queryKey) => JSON.stringify(queryKey);
2
- const stableStringify = (value) => {
3
- if (value === null || typeof value !== "object") {
4
- return JSON.stringify(value);
5
- }
6
- if (Array.isArray(value)) {
7
- return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
8
- }
9
- const entries = Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b));
10
- return `{${entries.map(([key, value_]) => `${JSON.stringify(key)}:${stableStringify(value_)}`).join(",")}}`;
11
- };
12
- const lunoraQueryKey = (function_, args, shardKey) => [
13
- "lunora",
14
- function_.__lunoraRef,
15
- args,
16
- // eslint-disable-next-line unicorn/no-null -- this literal is part of the JSON-serialized query key TanStack hashes for dedup; `null` keeps a stable, distinct slot from an absent shardKey across renders.
17
- shardKey ?? null
18
- ];
19
- const serializeQueryKey = (queryKey) => keyHash(queryKey);
20
-
21
- export { stableStringify as a, keyHash as k, lunoraQueryKey as l, serializeQueryKey as s };