@supalive/react 0.1.1 → 0.1.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.
Files changed (42) hide show
  1. package/dist/src/index.d.ts +185 -10
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +269 -32
  4. package/dist/src/index.js.map +1 -1
  5. package/package.json +10 -5
  6. package/dist/src/context.d.ts +0 -10
  7. package/dist/src/context.d.ts.map +0 -1
  8. package/dist/src/context.js +0 -12
  9. package/dist/src/context.js.map +0 -1
  10. package/dist/src/create_action.d.ts +0 -11
  11. package/dist/src/create_action.d.ts.map +0 -1
  12. package/dist/src/create_action.js +0 -45
  13. package/dist/src/create_action.js.map +0 -1
  14. package/dist/src/create_live_query.d.ts +0 -28
  15. package/dist/src/create_live_query.d.ts.map +0 -1
  16. package/dist/src/create_live_query.js +0 -49
  17. package/dist/src/create_live_query.js.map +0 -1
  18. package/dist/src/create_mutation.d.ts +0 -20
  19. package/dist/src/create_mutation.d.ts.map +0 -1
  20. package/dist/src/create_mutation.js +0 -64
  21. package/dist/src/create_mutation.js.map +0 -1
  22. package/dist/src/create_query.d.ts +0 -10
  23. package/dist/src/create_query.d.ts.map +0 -1
  24. package/dist/src/create_query.js +0 -46
  25. package/dist/src/create_query.js.map +0 -1
  26. package/dist/src/provider.d.ts +0 -33
  27. package/dist/src/provider.d.ts.map +0 -1
  28. package/dist/src/provider.js +0 -41
  29. package/dist/src/provider.js.map +0 -1
  30. package/dist/src/types.d.ts +0 -56
  31. package/dist/src/types.d.ts.map +0 -1
  32. package/dist/src/types.js +0 -9
  33. package/dist/src/types.js.map +0 -1
  34. package/dist/src/use_connection_state.d.ts +0 -18
  35. package/dist/src/use_connection_state.d.ts.map +0 -1
  36. package/dist/src/use_connection_state.js +0 -14
  37. package/dist/src/use_connection_state.js.map +0 -1
  38. package/dist/src/use_supalive.d.ts +0 -9
  39. package/dist/src/use_supalive.d.ts.map +0 -1
  40. package/dist/src/use_supalive.js +0 -13
  41. package/dist/src/use_supalive.js.map +0 -1
  42. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,11 +1,186 @@
1
- export { createSupaliveContext, type SupaliveContext } from "./context";
2
- export { createSupaliveProvider, type SupaliveProviderProps, } from "./provider";
3
- export { createUseSupalive } from "./use_supalive";
4
- export { createUseConnectionState } from "./use_connection_state";
5
- export { createLiveQuery } from "./create_live_query";
6
- export { createQuery } from "./create_query";
7
- export { createMutation } from "./create_mutation";
8
- export { createAction } from "./create_action";
9
- export type { QueryStatus, QueryResult, LiveQueryStatus, LiveQueryResult, LiveQueryState, MutationStatus, MutationState, ActionState, QueryProcedureLike, MutationProcedureLike, ActionProcedureLike, ProcedureSelector, } from "./types";
10
- export type { ClientPublicState } from "@supalive/core/client";
1
+ import { Context, ReactNode } from "react";
2
+ import { ClientPublicState, ClientPublicState as ClientPublicState$1, LiveQueryHandle, LiveQueryState, LiveQueryState as LiveQueryState$1 } from "@supalive/core/client";
3
+
4
+ //#region src/context.d.ts
5
+ /**
6
+ * The React context object that carries the typed client. Userland creates
7
+ * one of these per app, then passes it to every hook factory so they all
8
+ * agree on the same `TClient`.
9
+ */
10
+ type SupaliveContext<TClient> = Context<TClient | null>;
11
+ declare function createSupaliveContext<TClient>(): SupaliveContext<TClient>;
12
+ //#endregion
13
+ //#region src/provider.d.ts
14
+ /**
15
+ * Minimal shape we need from any client passed through this provider. Both
16
+ * `createClient`'s output (which has `WSClientMethods`) and a hand-rolled
17
+ * client satisfy it, so the provider stays generic.
18
+ */
19
+ interface SupaliveLifecycleClient {
20
+ connect(): Promise<void>;
21
+ disconnect(): void;
22
+ }
23
+ interface SupaliveProviderProps<TClient> {
24
+ /**
25
+ * Either a factory (built each mount, lazy) or a pre-built client. A
26
+ * factory matches the Convex pattern of letting the provider own the
27
+ * client's lifetime; passing a pre-built client lets you share it with
28
+ * non-React callers (SSR, scripts).
29
+ */
30
+ client: TClient | (() => TClient);
31
+ children: ReactNode;
32
+ }
33
+ /**
34
+ * Build a typed `<SupaliveProvider>` for a given `TClient`. Pass the same
35
+ * context object to every other hook factory so they share the client.
36
+ *
37
+ * StrictMode-safe: the client lives on a ref and is never recreated. The
38
+ * cleanup schedules `disconnect()` through a 250 ms timer parked on a ref,
39
+ * so a synchronous re-mount (as StrictMode does in dev) cancels it before
40
+ * the socket is torn down. A real unmount lets the timer fire.
41
+ */
42
+ declare function createSupaliveProvider<TClient extends SupaliveLifecycleClient>(ctx: SupaliveContext<TClient>): ({
43
+ client,
44
+ children
45
+ }: SupaliveProviderProps<TClient>) => import("react/jsx-runtime").JSX.Element;
46
+ //#endregion
47
+ //#region src/use_supalive.d.ts
48
+ /**
49
+ * Returns a `useSupalive` hook that yields the typed client. Use this when
50
+ * you need to call procedures imperatively (outside the standard query/
51
+ * mutation hooks), e.g. inside an event handler that doesn't fit one of
52
+ * the higher-level helpers.
53
+ */
54
+ declare function createUseSupalive<TClient>(ctx: SupaliveContext<TClient>): () => TClient;
55
+ //#endregion
56
+ //#region src/use_connection_state.d.ts
57
+ /**
58
+ * Shape of any client that surfaces the WS connection state. Both the
59
+ * generated `createClient` proxy and a hand-rolled client satisfy it.
60
+ */
61
+ interface ClientWithState {
62
+ onState(listener: (s: ClientPublicState$1) => void): () => void;
63
+ getPublicState(): ClientPublicState$1;
64
+ }
65
+ /**
66
+ * Returns a `useSupaliveConnectionState` hook for showing connection
67
+ * banners ("Reconnecting…", "Offline", etc.) without having to subscribe
68
+ * to the client manually.
69
+ */
70
+ declare function createUseConnectionState<TClient extends ClientWithState>(ctx: SupaliveContext<TClient>): () => ClientPublicState$1;
71
+ //#endregion
72
+ //#region src/types.d.ts
73
+ /** Shape of a query procedure exposed by `ClientFromProcedures`. */
74
+ interface QueryProcedureLike<TInput, TOutput> {
75
+ query: (input: TInput) => Promise<TOutput>;
76
+ liveQuery: (input: TInput, serializedInput: string) => LiveQueryHandle<TOutput>;
77
+ }
78
+ /** Shape of a mutation procedure. */
79
+ interface MutationProcedureLike<TInput, TOutput> {
80
+ mutate: (input: TInput, opts?: {
81
+ onSend?: () => void;
82
+ }) => Promise<TOutput>;
83
+ }
84
+ /** Shape of an action procedure. */
85
+ interface ActionProcedureLike<TInput, TOutput> {
86
+ action: (input: TInput, opts?: {
87
+ onSend?: () => void;
88
+ }) => Promise<TOutput>;
89
+ }
90
+ /** Selector function used by all hooks. */
91
+ type ProcedureSelector<TClient, TProc> = (client: TClient) => TProc;
92
+ type QueryStatus = "idle" | "loading" | "success" | "error";
93
+ interface QueryResult<T> {
94
+ data: T | undefined;
95
+ status: QueryStatus;
96
+ error: Error | null;
97
+ refetch: () => void;
98
+ }
99
+ type LiveQueryStatus = LiveQueryState$1<unknown>["status"];
100
+ interface LiveQueryResult<T> {
101
+ data: T | undefined;
102
+ status: LiveQueryStatus;
103
+ error: Error | null;
104
+ refetch: () => void;
105
+ }
106
+ type MutationStatus = "idle" | "queued" | "loading" | "success" | "error";
107
+ interface MutationState<T> {
108
+ data: T | undefined;
109
+ status: MutationStatus;
110
+ error: Error | null;
111
+ reset: () => void;
112
+ }
113
+ interface ActionState<T> {
114
+ data: T | undefined;
115
+ status: MutationStatus;
116
+ error: Error | null;
117
+ reset: () => void;
118
+ }
119
+ //#endregion
120
+ //#region src/create_live_query.d.ts
121
+ /**
122
+ * Returns a typed `useLiveQuery` hook bound to a specific client type.
123
+ *
124
+ * Userland:
125
+ * const useLiveQuery = createLiveQuery<SupaliveClient>(SupaliveCtx);
126
+ * const { data, status, error } = useLiveQuery(c => c.listItems, { limit: 50 });
127
+ *
128
+ * Internals (modelled on Convex's `useQuery` + `useSubscription`, but
129
+ * using React 18's native `useSyncExternalStore` instead of the legacy
130
+ * shim Convex still bundles):
131
+ *
132
+ * - Selector is fired synchronously to get the procedure object; the
133
+ * procedure's `liveQuery(input)` returns a shared `LiveQueryHandle`
134
+ * (ref-counted in the client) so multiple components reading the same
135
+ * query share one server-side subscription.
136
+ * - `useSyncExternalStore(handle.onState, handle.getState)` gives a
137
+ * tearing-free, concurrent-safe render path.
138
+ * - Re-subscription is keyed off `stableStringify(input)` so an inline
139
+ * object literal that produces the same values doesn't churn handles.
140
+ *
141
+ * Define selectors at module scope (or with `useCallback`) so the reference
142
+ * is stable across renders; this isn't required for correctness, but it
143
+ * keeps the `useMemo` cheap.
144
+ */
145
+ declare function createLiveQuery<TClient>(ctx: SupaliveContext<TClient>): <TInput, TOutput>(selector: ProcedureSelector<TClient, QueryProcedureLike<TInput, TOutput>>, input: TInput) => LiveQueryResult<TOutput>;
146
+ //#endregion
147
+ //#region src/create_query.d.ts
148
+ /**
149
+ * One-shot query (no live updates). Re-runs when the serialised input
150
+ * changes. `refetch()` re-issues the same query immediately.
151
+ *
152
+ * const { data, status, error, refetch } = useQuery(c => c.getItem, { id });
153
+ */
154
+ declare function createQuery<TClient>(ctx: SupaliveContext<TClient>): <TInput, TOutput>(selector: ProcedureSelector<TClient, QueryProcedureLike<TInput, TOutput>>, input: TInput) => QueryResult<TOutput>;
155
+ //#endregion
156
+ //#region src/create_mutation.d.ts
157
+ /**
158
+ * Typed mutation hook factory.
159
+ *
160
+ * const [createItem, { status, error, data, reset }] =
161
+ * useMutation(c => c.createItem);
162
+ *
163
+ * Statuses:
164
+ * - "idle" — initial state, or after `reset()`
165
+ * - "queued" — call has been queued because the client wasn't ready
166
+ * (offline, reconnecting, mid-handshake); we use the
167
+ * transport's `onSend` callback to flip to "loading" once
168
+ * the request actually leaves the wire
169
+ * - "loading" — request is on the wire, awaiting response
170
+ * - "success" — server returned success
171
+ * - "error" — server returned an error or the call rejected
172
+ */
173
+ declare function createMutation<TClient>(ctx: SupaliveContext<TClient>): <TInput, TOutput>(selector: ProcedureSelector<TClient, MutationProcedureLike<TInput, TOutput>>) => [(input: TInput) => Promise<TOutput>, MutationState<TOutput>];
174
+ //#endregion
175
+ //#region src/create_action.d.ts
176
+ /**
177
+ * Typed action hook factory. Same shape as `useMutation` — actions in
178
+ * Supalive have the same client-side semantics (single-call, response,
179
+ * may be queued during disconnect).
180
+ *
181
+ * const [bulkUpdate, { status }] = useAction(c => c.bulkUpdateItems);
182
+ */
183
+ declare function createAction<TClient>(ctx: SupaliveContext<TClient>): <TInput, TOutput>(selector: ProcedureSelector<TClient, ActionProcedureLike<TInput, TOutput>>) => [(input: TInput) => Promise<TOutput>, ActionState<TOutput>];
184
+ //#endregion
185
+ export { type ActionProcedureLike, type ActionState, type ClientPublicState, type LiveQueryResult, type LiveQueryState, type LiveQueryStatus, type MutationProcedureLike, type MutationState, type MutationStatus, type ProcedureSelector, type QueryProcedureLike, type QueryResult, type QueryStatus, type SupaliveContext, type SupaliveProviderProps, createAction, createLiveQuery, createMutation, createQuery, createSupaliveContext, createSupaliveProvider, createUseConnectionState, createUseSupalive };
11
186
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EACL,sBAAsB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,YAAY,EACV,WAAW,EACX,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,cAAc,EACd,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/context.ts","../../src/provider.tsx","../../src/use_supalive.ts","../../src/use_connection_state.ts","../../src/types.ts","../../src/create_live_query.ts","../../src/create_query.ts","../../src/create_mutation.ts","../../src/create_action.ts"],"mappings":"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"}
package/dist/src/index.js CHANGED
@@ -1,33 +1,270 @@
1
- // Public surface of @supalive/react.
2
- //
3
- // Userland wiring:
4
- //
5
- // import {
6
- // createSupaliveContext,
7
- // createSupaliveProvider,
8
- // createUseSupalive,
9
- // createUseConnectionState,
10
- // createLiveQuery,
11
- // createQuery,
12
- // createMutation,
13
- // createAction,
14
- // } from "@supalive/react";
15
- //
16
- // const ctx = createSupaliveContext<SupaliveClient>();
17
- //
18
- // export const SupaliveProvider = createSupaliveProvider(ctx);
19
- // export const useSupalive = createUseSupalive(ctx);
20
- // export const useSupaliveConnectionState = createUseConnectionState(ctx);
21
- // export const useLiveQuery = createLiveQuery(ctx);
22
- // export const useQuery = createQuery(ctx);
23
- // export const useMutation = createMutation(ctx);
24
- // export const useAction = createAction(ctx);
25
- export { createSupaliveContext } from "./context";
26
- export { createSupaliveProvider, } from "./provider";
27
- export { createUseSupalive } from "./use_supalive";
28
- export { createUseConnectionState } from "./use_connection_state";
29
- export { createLiveQuery } from "./create_live_query";
30
- export { createQuery } from "./create_query";
31
- export { createMutation } from "./create_mutation";
32
- export { createAction } from "./create_action";
1
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { stableStringify } from "@supalive/core/types";
4
+ //#region src/context.ts
5
+ function createSupaliveContext() {
6
+ return createContext(null);
7
+ }
8
+ function useContextOrThrow(ctx) {
9
+ const value = useContext(ctx);
10
+ if (value === null) throw new Error("Supalive: no client found in React context. Wrap your app in <SupaliveProvider>.");
11
+ return value;
12
+ }
13
+ //#endregion
14
+ //#region src/provider.tsx
15
+ /**
16
+ * Build a typed `<SupaliveProvider>` for a given `TClient`. Pass the same
17
+ * context object to every other hook factory so they share the client.
18
+ *
19
+ * StrictMode-safe: the client lives on a ref and is never recreated. The
20
+ * cleanup schedules `disconnect()` through a 250 ms timer parked on a ref,
21
+ * so a synchronous re-mount (as StrictMode does in dev) cancels it before
22
+ * the socket is torn down. A real unmount lets the timer fire.
23
+ */
24
+ function createSupaliveProvider(ctx) {
25
+ return function SupaliveProvider({ client, children }) {
26
+ const clientRef = useRef(null);
27
+ const disconnectTimerRef = useRef(null);
28
+ if (!clientRef.current) clientRef.current = typeof client === "function" ? client() : client;
29
+ useEffect(() => {
30
+ if (disconnectTimerRef.current) {
31
+ clearTimeout(disconnectTimerRef.current);
32
+ disconnectTimerRef.current = null;
33
+ }
34
+ const c = clientRef.current;
35
+ c.connect().catch((err) => {
36
+ console.error("[Supalive] connect failed:", err);
37
+ });
38
+ return () => {
39
+ disconnectTimerRef.current = setTimeout(() => {
40
+ disconnectTimerRef.current = null;
41
+ c.disconnect();
42
+ }, 250);
43
+ };
44
+ }, []);
45
+ return /* @__PURE__ */ jsx(ctx.Provider, {
46
+ value: clientRef.current,
47
+ children
48
+ });
49
+ };
50
+ }
51
+ //#endregion
52
+ //#region src/use_supalive.ts
53
+ /**
54
+ * Returns a `useSupalive` hook that yields the typed client. Use this when
55
+ * you need to call procedures imperatively (outside the standard query/
56
+ * mutation hooks), e.g. inside an event handler that doesn't fit one of
57
+ * the higher-level helpers.
58
+ */
59
+ function createUseSupalive(ctx) {
60
+ return function useSupalive() {
61
+ return useContextOrThrow(ctx);
62
+ };
63
+ }
64
+ //#endregion
65
+ //#region src/use_connection_state.ts
66
+ /**
67
+ * Returns a `useSupaliveConnectionState` hook for showing connection
68
+ * banners ("Reconnecting…", "Offline", etc.) without having to subscribe
69
+ * to the client manually.
70
+ */
71
+ function createUseConnectionState(ctx) {
72
+ return function useSupaliveConnectionState() {
73
+ const client = useContextOrThrow(ctx);
74
+ return useSyncExternalStore((cb) => client.onState(() => cb()), () => client.getPublicState(), () => client.getPublicState());
75
+ };
76
+ }
77
+ //#endregion
78
+ //#region src/create_live_query.ts
79
+ /**
80
+ * Returns a typed `useLiveQuery` hook bound to a specific client type.
81
+ *
82
+ * Userland:
83
+ * const useLiveQuery = createLiveQuery<SupaliveClient>(SupaliveCtx);
84
+ * const { data, status, error } = useLiveQuery(c => c.listItems, { limit: 50 });
85
+ *
86
+ * Internals (modelled on Convex's `useQuery` + `useSubscription`, but
87
+ * using React 18's native `useSyncExternalStore` instead of the legacy
88
+ * shim Convex still bundles):
89
+ *
90
+ * - Selector is fired synchronously to get the procedure object; the
91
+ * procedure's `liveQuery(input)` returns a shared `LiveQueryHandle`
92
+ * (ref-counted in the client) so multiple components reading the same
93
+ * query share one server-side subscription.
94
+ * - `useSyncExternalStore(handle.onState, handle.getState)` gives a
95
+ * tearing-free, concurrent-safe render path.
96
+ * - Re-subscription is keyed off `stableStringify(input)` so an inline
97
+ * object literal that produces the same values doesn't churn handles.
98
+ *
99
+ * Define selectors at module scope (or with `useCallback`) so the reference
100
+ * is stable across renders; this isn't required for correctness, but it
101
+ * keeps the `useMemo` cheap.
102
+ */
103
+ function createLiveQuery(ctx) {
104
+ return function useLiveQuery(selector, input) {
105
+ const client = useContextOrThrow(ctx);
106
+ const selectorRef = useRef(selector);
107
+ selectorRef.current = selector;
108
+ const serializedInput = stableStringify(input);
109
+ const handle = useMemo(() => selectorRef.current(client).liveQuery(input, serializedInput), [client, serializedInput]);
110
+ const state = useSyncExternalStore(handle.onState, handle.getState, handle.getState);
111
+ const refetch = useCallback(() => handle.refetch(), [handle]);
112
+ return {
113
+ data: state.data,
114
+ status: state.status,
115
+ error: state.error,
116
+ refetch
117
+ };
118
+ };
119
+ }
120
+ //#endregion
121
+ //#region src/create_query.ts
122
+ /**
123
+ * One-shot query (no live updates). Re-runs when the serialised input
124
+ * changes. `refetch()` re-issues the same query immediately.
125
+ *
126
+ * const { data, status, error, refetch } = useQuery(c => c.getItem, { id });
127
+ */
128
+ function createQuery(ctx) {
129
+ return function useQuery(selector, input) {
130
+ const client = useContextOrThrow(ctx);
131
+ const selectorRef = useRef(selector);
132
+ selectorRef.current = selector;
133
+ const [data, setData] = useState();
134
+ const [status, setStatus] = useState("idle");
135
+ const [error, setError] = useState(null);
136
+ const [tick, setTick] = useState(0);
137
+ useEffect(() => {
138
+ let cancelled = false;
139
+ setStatus("loading");
140
+ setError(null);
141
+ selectorRef.current(client).query(input).then((result) => {
142
+ if (cancelled) return;
143
+ setData(result);
144
+ setStatus("success");
145
+ }).catch((err) => {
146
+ if (cancelled) return;
147
+ setError(err instanceof Error ? err : new Error(String(err)));
148
+ setStatus("error");
149
+ });
150
+ return () => {
151
+ cancelled = true;
152
+ };
153
+ }, [
154
+ client,
155
+ stableStringify(input),
156
+ tick
157
+ ]);
158
+ return {
159
+ data,
160
+ status,
161
+ error,
162
+ refetch: useCallback(() => setTick((t) => t + 1), [])
163
+ };
164
+ };
165
+ }
166
+ //#endregion
167
+ //#region src/create_mutation.ts
168
+ /**
169
+ * Typed mutation hook factory.
170
+ *
171
+ * const [createItem, { status, error, data, reset }] =
172
+ * useMutation(c => c.createItem);
173
+ *
174
+ * Statuses:
175
+ * - "idle" — initial state, or after `reset()`
176
+ * - "queued" — call has been queued because the client wasn't ready
177
+ * (offline, reconnecting, mid-handshake); we use the
178
+ * transport's `onSend` callback to flip to "loading" once
179
+ * the request actually leaves the wire
180
+ * - "loading" — request is on the wire, awaiting response
181
+ * - "success" — server returned success
182
+ * - "error" — server returned an error or the call rejected
183
+ */
184
+ function createMutation(ctx) {
185
+ return function useMutation(selector) {
186
+ const client = useContextOrThrow(ctx);
187
+ const selectorRef = useRef(selector);
188
+ selectorRef.current = selector;
189
+ const [data, setData] = useState(void 0);
190
+ const [status, setStatus] = useState("idle");
191
+ const [error, setError] = useState(null);
192
+ const reset = useCallback(() => {
193
+ setData(void 0);
194
+ setStatus("idle");
195
+ setError(null);
196
+ }, []);
197
+ return [useCallback(async (input) => {
198
+ setError(null);
199
+ setData(void 0);
200
+ setStatus("queued");
201
+ try {
202
+ const result = await selectorRef.current(client).mutate(input, { onSend: () => {
203
+ setStatus("loading");
204
+ } });
205
+ setData(result);
206
+ setStatus("success");
207
+ return result;
208
+ } catch (err) {
209
+ const e = err instanceof Error ? err : new Error(String(err));
210
+ setError(e);
211
+ setStatus("error");
212
+ throw e;
213
+ }
214
+ }, [client]), {
215
+ data,
216
+ status,
217
+ error,
218
+ reset
219
+ }];
220
+ };
221
+ }
222
+ //#endregion
223
+ //#region src/create_action.ts
224
+ /**
225
+ * Typed action hook factory. Same shape as `useMutation` — actions in
226
+ * Supalive have the same client-side semantics (single-call, response,
227
+ * may be queued during disconnect).
228
+ *
229
+ * const [bulkUpdate, { status }] = useAction(c => c.bulkUpdateItems);
230
+ */
231
+ function createAction(ctx) {
232
+ return function useAction(selector) {
233
+ const client = useContextOrThrow(ctx);
234
+ const selectorRef = useRef(selector);
235
+ selectorRef.current = selector;
236
+ const [data, setData] = useState(void 0);
237
+ const [status, setStatus] = useState("idle");
238
+ const [error, setError] = useState(null);
239
+ const reset = useCallback(() => {
240
+ setData(void 0);
241
+ setStatus("idle");
242
+ setError(null);
243
+ }, []);
244
+ return [useCallback(async (input) => {
245
+ setError(null);
246
+ setData(void 0);
247
+ setStatus("queued");
248
+ try {
249
+ const result = await selectorRef.current(client).action(input, { onSend: () => setStatus("loading") });
250
+ setData(result);
251
+ setStatus("success");
252
+ return result;
253
+ } catch (err) {
254
+ const e = err instanceof Error ? err : new Error(String(err));
255
+ setError(e);
256
+ setStatus("error");
257
+ throw e;
258
+ }
259
+ }, [client]), {
260
+ data,
261
+ status,
262
+ error,
263
+ reset
264
+ }];
265
+ };
266
+ }
267
+ //#endregion
268
+ export { createAction, createLiveQuery, createMutation, createQuery, createSupaliveContext, createSupaliveProvider, createUseConnectionState, createUseSupalive };
269
+
33
270
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,mBAAmB;AACnB,EAAE;AACF,aAAa;AACb,6BAA6B;AAC7B,8BAA8B;AAC9B,yBAAyB;AACzB,gCAAgC;AAChC,uBAAuB;AACvB,mBAAmB;AACnB,sBAAsB;AACtB,oBAAoB;AACpB,8BAA8B;AAC9B,EAAE;AACF,yDAAyD;AACzD,EAAE;AACF,0EAA0E;AAC1E,qEAAqE;AACrE,6EAA6E;AAC7E,mEAAmE;AACnE,+DAA+D;AAC/D,kEAAkE;AAClE,gEAAgE;AAEhE,OAAO,EAAE,qBAAqB,EAAwB,MAAM,WAAW,CAAC;AACxE,OAAO,EACL,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/context.ts","../../src/provider.tsx","../../src/use_supalive.ts","../../src/use_connection_state.ts","../../src/create_live_query.ts","../../src/create_query.ts","../../src/create_mutation.ts","../../src/create_action.ts"],"sourcesContent":["import { createContext, useContext, type Context } from \"react\";\n\n/**\n * The React context object that carries the typed client. Userland creates\n * one of these per app, then passes it to every hook factory so they all\n * agree on the same `TClient`.\n */\nexport type SupaliveContext<TClient> = Context<TClient | null>;\n\nexport function createSupaliveContext<TClient>(): SupaliveContext<TClient> {\n return createContext<TClient | null>(null);\n}\n\nexport function useContextOrThrow<TClient>(ctx: SupaliveContext<TClient>): TClient {\n const value = useContext(ctx);\n if (value === null) {\n throw new Error(\n \"Supalive: no client found in React context. Wrap your app in <SupaliveProvider>.\",\n );\n }\n return value;\n}\n","import { useEffect, useRef, type ReactNode } from \"react\";\nimport type { SupaliveContext } from \"./context\";\n\n/**\n * Minimal shape we need from any client passed through this provider. Both\n * `createClient`'s output (which has `WSClientMethods`) and a hand-rolled\n * client satisfy it, so the provider stays generic.\n */\ninterface SupaliveLifecycleClient {\n connect(): Promise<void>;\n disconnect(): void;\n}\n\nexport interface SupaliveProviderProps<TClient> {\n /**\n * Either a factory (built each mount, lazy) or a pre-built client. A\n * factory matches the Convex pattern of letting the provider own the\n * client's lifetime; passing a pre-built client lets you share it with\n * non-React callers (SSR, scripts).\n */\n client: TClient | (() => TClient);\n children: ReactNode;\n}\n\n/**\n * Build a typed `<SupaliveProvider>` for a given `TClient`. Pass the same\n * context object to every other hook factory so they share the client.\n *\n * StrictMode-safe: the client lives on a ref and is never recreated. The\n * cleanup schedules `disconnect()` through a 250 ms timer parked on a ref,\n * so a synchronous re-mount (as StrictMode does in dev) cancels it before\n * the socket is torn down. A real unmount lets the timer fire.\n */\nexport function createSupaliveProvider<TClient extends SupaliveLifecycleClient>(\n ctx: SupaliveContext<TClient>,\n) {\n return function SupaliveProvider({\n client,\n children,\n }: SupaliveProviderProps<TClient>) {\n const clientRef = useRef<TClient | null>(null);\n const disconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n if (!clientRef.current) {\n clientRef.current = typeof client === \"function\" ? (client as () => TClient)() : client;\n }\n\n useEffect(() => {\n // A pending disconnect from a prior cleanup (StrictMode) — cancel it\n // before we'd start a redundant reconnect.\n if (disconnectTimerRef.current) {\n clearTimeout(disconnectTimerRef.current);\n disconnectTimerRef.current = null;\n }\n\n const c = clientRef.current!;\n void c.connect().catch((err: unknown) => {\n console.error(\"[Supalive] connect failed:\", err);\n });\n\n return () => {\n disconnectTimerRef.current = setTimeout(() => {\n disconnectTimerRef.current = null;\n c.disconnect();\n }, 250);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return <ctx.Provider value={clientRef.current}>{children}</ctx.Provider>;\n };\n}\n","import type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\n\n/**\n * Returns a `useSupalive` hook that yields the typed client. Use this when\n * you need to call procedures imperatively (outside the standard query/\n * mutation hooks), e.g. inside an event handler that doesn't fit one of\n * the higher-level helpers.\n */\nexport function createUseSupalive<TClient>(ctx: SupaliveContext<TClient>) {\n return function useSupalive(): TClient {\n return useContextOrThrow(ctx);\n };\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { ClientPublicState } from \"@supalive/core/client\";\nimport type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\n\n/**\n * Shape of any client that surfaces the WS connection state. Both the\n * generated `createClient` proxy and a hand-rolled client satisfy it.\n */\ninterface ClientWithState {\n onState(listener: (s: ClientPublicState) => void): () => void;\n getPublicState(): ClientPublicState;\n}\n\n/**\n * Returns a `useSupaliveConnectionState` hook for showing connection\n * banners (\"Reconnecting…\", \"Offline\", etc.) without having to subscribe\n * to the client manually.\n */\nexport function createUseConnectionState<TClient extends ClientWithState>(\n ctx: SupaliveContext<TClient>,\n) {\n return function useSupaliveConnectionState(): ClientPublicState {\n const client = useContextOrThrow(ctx);\n return useSyncExternalStore(\n (cb) => client.onState(() => cb()),\n () => client.getPublicState(),\n () => client.getPublicState(),\n );\n };\n}\n","import { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport type { LiveQueryHandle } from \"@supalive/core/client\";\nimport type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\nimport type {\n LiveQueryResult,\n ProcedureSelector,\n QueryProcedureLike,\n} from \"./types\";\nimport { stableStringify } from \"@supalive/core/types\";\n\n/**\n * Returns a typed `useLiveQuery` hook bound to a specific client type.\n *\n * Userland:\n * const useLiveQuery = createLiveQuery<SupaliveClient>(SupaliveCtx);\n * const { data, status, error } = useLiveQuery(c => c.listItems, { limit: 50 });\n *\n * Internals (modelled on Convex's `useQuery` + `useSubscription`, but\n * using React 18's native `useSyncExternalStore` instead of the legacy\n * shim Convex still bundles):\n *\n * - Selector is fired synchronously to get the procedure object; the\n * procedure's `liveQuery(input)` returns a shared `LiveQueryHandle`\n * (ref-counted in the client) so multiple components reading the same\n * query share one server-side subscription.\n * - `useSyncExternalStore(handle.onState, handle.getState)` gives a\n * tearing-free, concurrent-safe render path.\n * - Re-subscription is keyed off `stableStringify(input)` so an inline\n * object literal that produces the same values doesn't churn handles.\n *\n * Define selectors at module scope (or with `useCallback`) so the reference\n * is stable across renders; this isn't required for correctness, but it\n * keeps the `useMemo` cheap.\n */\nexport function createLiveQuery<TClient>(ctx: SupaliveContext<TClient>) {\n return function useLiveQuery<TInput, TOutput>(\n selector: ProcedureSelector<TClient, QueryProcedureLike<TInput, TOutput>>,\n input: TInput,\n ): LiveQueryResult<TOutput> {\n const client = useContextOrThrow(ctx);\n\n // Keep the latest selector in a ref. We don't include it in the\n // memo deps because selectors are usually inline arrow functions;\n // we re-read it on every render but only re-acquire the handle\n // when the *input key* changes (or the client identity changes).\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n\n const serializedInput = stableStringify(input);\n const handle: LiveQueryHandle<TOutput> = useMemo(\n () => selectorRef.current(client).liveQuery(input, serializedInput),\n [client, serializedInput],\n );\n\n const state = useSyncExternalStore(\n handle.onState,\n handle.getState,\n handle.getState,\n );\n\n const refetch = useCallback(() => handle.refetch(), [handle]);\n\n return {\n data: state.data,\n status: state.status,\n error: state.error,\n refetch,\n };\n };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\nimport type {\n ProcedureSelector,\n QueryProcedureLike,\n QueryResult,\n QueryStatus,\n} from \"./types\";\nimport { stableStringify } from \"@supalive/core/types\";\n\n/**\n * One-shot query (no live updates). Re-runs when the serialised input\n * changes. `refetch()` re-issues the same query immediately.\n *\n * const { data, status, error, refetch } = useQuery(c => c.getItem, { id });\n */\nexport function createQuery<TClient>(ctx: SupaliveContext<TClient>) {\n return function useQuery<TInput, TOutput>(\n selector: ProcedureSelector<TClient, QueryProcedureLike<TInput, TOutput>>,\n input: TInput,\n ): QueryResult<TOutput> {\n const client = useContextOrThrow(ctx);\n\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n\n const [data, setData] = useState<TOutput | undefined>();\n const [status, setStatus] = useState<QueryStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [tick, setTick] = useState(0);\n const serializedInput = stableStringify(input);\n\n useEffect(() => {\n let cancelled = false;\n setStatus(\"loading\");\n setError(null);\n\n selectorRef.current(client)\n .query(input)\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setStatus(\"success\");\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus(\"error\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, serializedInput, tick]);\n\n const refetch = useCallback(() => setTick((t) => t + 1), []);\n\n return { data, status, error, refetch };\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\nimport type {\n MutationProcedureLike,\n MutationState,\n MutationStatus,\n ProcedureSelector,\n} from \"./types\";\n\n/**\n * Typed mutation hook factory.\n *\n * const [createItem, { status, error, data, reset }] =\n * useMutation(c => c.createItem);\n *\n * Statuses:\n * - \"idle\" — initial state, or after `reset()`\n * - \"queued\" — call has been queued because the client wasn't ready\n * (offline, reconnecting, mid-handshake); we use the\n * transport's `onSend` callback to flip to \"loading\" once\n * the request actually leaves the wire\n * - \"loading\" — request is on the wire, awaiting response\n * - \"success\" — server returned success\n * - \"error\" — server returned an error or the call rejected\n */\nexport function createMutation<TClient>(ctx: SupaliveContext<TClient>) {\n return function useMutation<TInput, TOutput>(\n selector: ProcedureSelector<TClient, MutationProcedureLike<TInput, TOutput>>,\n ): [\n (input: TInput) => Promise<TOutput>,\n MutationState<TOutput>,\n ] {\n const client = useContextOrThrow(ctx);\n\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n\n const [data, setData] = useState<TOutput | undefined>(undefined);\n const [status, setStatus] = useState<MutationStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const reset = useCallback(() => {\n setData(undefined);\n setStatus(\"idle\");\n setError(null);\n }, []);\n\n const mutate = useCallback(\n async (input: TInput): Promise<TOutput> => {\n setError(null);\n setData(undefined);\n // We optimistically assume the transport will queue if not ready,\n // and the transport's `onSend` will flip us to \"loading\".\n setStatus(\"queued\");\n let sent = false;\n try {\n const result = await selectorRef.current(client).mutate(input, {\n onSend: () => {\n sent = true;\n setStatus(\"loading\");\n },\n });\n // If the call was queued and dispatched in one tick, onSend may\n // have fired before this line — `sent` reflects that. Either way\n // we end up at \"success\".\n setData(result);\n setStatus(\"success\");\n return result;\n } catch (err: unknown) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus(\"error\");\n void sent;\n throw e;\n }\n },\n [client],\n );\n\n return [mutate, { data, status, error, reset }];\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { SupaliveContext } from \"./context\";\nimport { useContextOrThrow } from \"./context\";\nimport type {\n ActionProcedureLike,\n ActionState,\n MutationStatus,\n ProcedureSelector,\n} from \"./types\";\n\n/**\n * Typed action hook factory. Same shape as `useMutation` — actions in\n * Supalive have the same client-side semantics (single-call, response,\n * may be queued during disconnect).\n *\n * const [bulkUpdate, { status }] = useAction(c => c.bulkUpdateItems);\n */\nexport function createAction<TClient>(ctx: SupaliveContext<TClient>) {\n return function useAction<TInput, TOutput>(\n selector: ProcedureSelector<TClient, ActionProcedureLike<TInput, TOutput>>,\n ): [\n (input: TInput) => Promise<TOutput>,\n ActionState<TOutput>,\n ] {\n const client = useContextOrThrow(ctx);\n\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n\n const [data, setData] = useState<TOutput | undefined>(undefined);\n const [status, setStatus] = useState<MutationStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const reset = useCallback(() => {\n setData(undefined);\n setStatus(\"idle\");\n setError(null);\n }, []);\n\n const run = useCallback(\n async (input: TInput): Promise<TOutput> => {\n setError(null);\n setData(undefined);\n setStatus(\"queued\");\n try {\n const result = await selectorRef.current(client).action(input, {\n onSend: () => setStatus(\"loading\"),\n });\n setData(result);\n setStatus(\"success\");\n return result;\n } catch (err: unknown) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n },\n [client],\n );\n\n return [run, { data, status, error, reset }];\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supalive/react",
3
- "version": "0.1.1",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "React bindings for supalive",
6
6
  "license": "MIT",
@@ -19,13 +19,18 @@
19
19
  "default": "./dist/src/index.js"
20
20
  }
21
21
  },
22
- "files": ["dist", "README.md"],
23
- "publishConfig": { "access": "public" },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
24
29
  "scripts": {
25
- "build": "tsc -p ."
30
+ "build": "tsdown"
26
31
  },
27
32
  "dependencies": {
28
- "@supalive/core": "^0.1.4"
33
+ "@supalive/core": "workspace:*"
29
34
  },
30
35
  "peerDependencies": {
31
36
  "react": ">=18.0.0"
@@ -1,10 +0,0 @@
1
- import { type Context } from "react";
2
- /**
3
- * The React context object that carries the typed client. Userland creates
4
- * one of these per app, then passes it to every hook factory so they all
5
- * agree on the same `TClient`.
6
- */
7
- export type SupaliveContext<TClient> = Context<TClient | null>;
8
- export declare function createSupaliveContext<TClient>(): SupaliveContext<TClient>;
9
- export declare function useContextOrThrow<TClient>(ctx: SupaliveContext<TClient>): TClient;
10
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhE;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CAAC,OAAO,KAAK,eAAe,CAAC,OAAO,CAAC,CAEzE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,OAAO,CAQjF"}
@@ -1,12 +0,0 @@
1
- import { createContext, useContext } from "react";
2
- export function createSupaliveContext() {
3
- return createContext(null);
4
- }
5
- export function useContextOrThrow(ctx) {
6
- const value = useContext(ctx);
7
- if (value === null) {
8
- throw new Error("Supalive: no client found in React context. Wrap your app in <SupaliveProvider>.");
9
- }
10
- return value;
11
- }
12
- //# sourceMappingURL=context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,OAAO,CAAC;AAShE,MAAM,UAAU,qBAAqB;IACnC,OAAO,aAAa,CAAiB,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAU,GAA6B;IACtE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,11 +0,0 @@
1
- import type { SupaliveContext } from "./context";
2
- import type { ActionProcedureLike, ActionState, ProcedureSelector } from "./types";
3
- /**
4
- * Typed action hook factory. Same shape as `useMutation` — actions in
5
- * Supalive have the same client-side semantics (single-call, response,
6
- * may be queued during disconnect).
7
- *
8
- * const [bulkUpdate, { status }] = useAction(c => c.bulkUpdateItems);
9
- */
10
- export declare function createAction<TClient>(ctx: SupaliveContext<TClient>): <TInput, TOutput>(selector: ProcedureSelector<TClient, ActionProcedureLike<TInput, TOutput>>) => [(input: TInput) => Promise<TOutput>, ActionState<TOutput>];
11
- //# sourceMappingURL=create_action.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create_action.d.ts","sourceRoot":"","sources":["../../src/create_action.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EAEX,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,IACvC,MAAM,EAAE,OAAO,EACvC,UAAU,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KACzE,CACD,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACnC,WAAW,CAAC,OAAO,CAAC,CACrB,CAwCF"}