@parcae/sdk 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/index.d.ts +26 -23
- package/dist/react/index.js +67 -26
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ interface ParcaeProviderProps {
|
|
|
8
8
|
client?: ParcaeClient;
|
|
9
9
|
/** API base URL (required if no client provided). */
|
|
10
10
|
url?: string;
|
|
11
|
-
/** Bearer token or
|
|
12
|
-
apiKey?: string | null;
|
|
11
|
+
/** Bearer token, null (no session), or undefined (still loading). */
|
|
12
|
+
apiKey?: string | null | undefined;
|
|
13
13
|
/** Stable user ID — triggers re-auth when it changes. */
|
|
14
14
|
userId?: string | null;
|
|
15
15
|
/** API version. Default: "v1" */
|
|
@@ -20,26 +20,16 @@ interface ParcaeProviderProps {
|
|
|
20
20
|
onReady?: (client: ParcaeClient) => void;
|
|
21
21
|
onError?: (error: Error) => void;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
* ParcaeProvider — creates the SDK client once and re-authenticates on userId change.
|
|
25
|
-
*
|
|
26
|
-
* Usage with pre-created client:
|
|
27
|
-
* ```tsx
|
|
28
|
-
* const client = createClient({ url: "...", transport: "socket" });
|
|
29
|
-
* <ParcaeProvider client={client}><App /></ParcaeProvider>
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* Usage with inline config:
|
|
33
|
-
* ```tsx
|
|
34
|
-
* <ParcaeProvider url="http://localhost:3000" apiKey={token} userId={user.id}>
|
|
35
|
-
* <App />
|
|
36
|
-
* </ParcaeProvider>
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
23
|
declare const ParcaeProvider: React__default.FC<ParcaeProviderProps>;
|
|
40
24
|
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
type AuthState = "loading" | "authenticated" | "unauthenticated";
|
|
26
|
+
interface ParcaeContextValue {
|
|
27
|
+
client: ParcaeClient;
|
|
28
|
+
authState: AuthState;
|
|
29
|
+
authVersion: number;
|
|
30
|
+
}
|
|
31
|
+
declare const ParcaeContext: React.Context<ParcaeContextValue | null>;
|
|
32
|
+
declare function useParcae(): ParcaeContextValue;
|
|
43
33
|
|
|
44
34
|
interface QueryChain<T> {
|
|
45
35
|
find(): Promise<T[]>;
|
|
@@ -49,13 +39,21 @@ interface QueryChain<T> {
|
|
|
49
39
|
__adapter?: any;
|
|
50
40
|
__debounceMs?: number;
|
|
51
41
|
}
|
|
42
|
+
interface UseQueryOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Wait for authentication to resolve before firing the query.
|
|
45
|
+
* Default: true — queries don't fire while auth is "loading".
|
|
46
|
+
* Set to false for public/unauthenticated queries.
|
|
47
|
+
*/
|
|
48
|
+
waitForAuth?: boolean;
|
|
49
|
+
}
|
|
52
50
|
interface UseQueryResult<T> {
|
|
53
51
|
items: T[];
|
|
54
52
|
loading: boolean;
|
|
55
53
|
error: Error | null;
|
|
56
54
|
refetch: () => void;
|
|
57
55
|
}
|
|
58
|
-
declare function useQuery<T>(chain: QueryChain<T> | null | undefined): UseQueryResult<T>;
|
|
56
|
+
declare function useQuery<T>(chain: QueryChain<T> | null | undefined, options?: UseQueryOptions): UseQueryResult<T>;
|
|
59
57
|
|
|
60
58
|
declare function useApi(): {
|
|
61
59
|
get: (path: string, data?: any) => Promise<any>;
|
|
@@ -69,15 +67,20 @@ declare function useApi(): {
|
|
|
69
67
|
*/
|
|
70
68
|
declare function useSDK(): ParcaeClient;
|
|
71
69
|
/**
|
|
72
|
-
* useConnectionStatus —
|
|
70
|
+
* useConnectionStatus — connection + auth state.
|
|
73
71
|
*/
|
|
74
72
|
declare function useConnectionStatus(): {
|
|
75
73
|
isConnected: boolean;
|
|
76
74
|
isLoading: boolean;
|
|
75
|
+
authState: AuthState;
|
|
77
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* useAuthState — just the auth state.
|
|
79
|
+
*/
|
|
80
|
+
declare function useAuthState(): AuthState;
|
|
78
81
|
|
|
79
82
|
declare function useSetting<T = string>(key: string, defaultValue: T): [T, (value: T) => Promise<void>, {
|
|
80
83
|
isLoading: boolean;
|
|
81
84
|
}];
|
|
82
85
|
|
|
83
|
-
export { ParcaeContext, ParcaeProvider, type ParcaeProviderProps, useApi, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
|
|
86
|
+
export { type AuthState, ParcaeContext, type ParcaeContextValue, ParcaeProvider, type ParcaeProviderProps, useApi, useAuthState, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
|
package/dist/react/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createClient } from '../chunk-XAFYMW5P.js';
|
|
2
|
-
import { createContext, useContext, useMemo, useRef, useEffect, useCallback, useSyncExternalStore
|
|
2
|
+
import { createContext, useContext, useState, useMemo, useRef, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
var ParcaeContext = createContext(null);
|
|
6
6
|
function useParcae() {
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
7
|
+
const ctx = useContext(ParcaeContext);
|
|
8
|
+
if (!ctx) {
|
|
9
9
|
throw new Error("useParcae must be used within a <ParcaeProvider>");
|
|
10
10
|
}
|
|
11
|
-
return
|
|
11
|
+
return ctx;
|
|
12
12
|
}
|
|
13
13
|
var ParcaeProvider = ({
|
|
14
14
|
client: externalClient,
|
|
@@ -21,6 +21,13 @@ var ParcaeProvider = ({
|
|
|
21
21
|
onReady,
|
|
22
22
|
onError
|
|
23
23
|
}) => {
|
|
24
|
+
const [authState, setAuthState] = useState(
|
|
25
|
+
// If apiKey is undefined, the frontend auth provider is still loading.
|
|
26
|
+
// If apiKey is null, the user is not logged in.
|
|
27
|
+
// If apiKey is a string, we have a token but haven't verified it yet.
|
|
28
|
+
apiKey === void 0 ? "loading" : apiKey === null ? "unauthenticated" : "loading"
|
|
29
|
+
);
|
|
30
|
+
const [authVersion, setAuthVersion] = useState(0);
|
|
24
31
|
const client = useMemo(() => {
|
|
25
32
|
if (externalClient) return externalClient;
|
|
26
33
|
if (!url)
|
|
@@ -29,15 +36,31 @@ var ParcaeProvider = ({
|
|
|
29
36
|
);
|
|
30
37
|
return createClient({ url, version, transport, key: null });
|
|
31
38
|
}, [externalClient, url, version, transport]);
|
|
32
|
-
const apiKeyRef = useRef(apiKey);
|
|
33
|
-
apiKeyRef.current = apiKey;
|
|
34
39
|
const onReadyRef = useRef(onReady);
|
|
35
40
|
onReadyRef.current = onReady;
|
|
36
41
|
const onErrorRef = useRef(onError);
|
|
37
42
|
onErrorRef.current = onError;
|
|
38
43
|
useEffect(() => {
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if (apiKey === void 0) {
|
|
45
|
+
setAuthState("loading");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (apiKey === null) {
|
|
49
|
+
setAuthState("unauthenticated");
|
|
50
|
+
setAuthVersion((v) => v + 1);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
setAuthState("loading");
|
|
54
|
+
client.setKey(apiKey).then(() => {
|
|
55
|
+
setAuthState("authenticated");
|
|
56
|
+
setAuthVersion((v) => v + 1);
|
|
57
|
+
onReadyRef.current?.(client);
|
|
58
|
+
}).catch((err) => {
|
|
59
|
+
setAuthState("unauthenticated");
|
|
60
|
+
setAuthVersion((v) => v + 1);
|
|
61
|
+
onErrorRef.current?.(err);
|
|
62
|
+
});
|
|
63
|
+
}, [apiKey, userId, client]);
|
|
41
64
|
useEffect(() => {
|
|
42
65
|
const onErr = (err) => onErrorRef.current?.(err);
|
|
43
66
|
client.on("error", onErr);
|
|
@@ -45,7 +68,11 @@ var ParcaeProvider = ({
|
|
|
45
68
|
client.off("error", onErr);
|
|
46
69
|
};
|
|
47
70
|
}, [client]);
|
|
48
|
-
|
|
71
|
+
const contextValue = useMemo(
|
|
72
|
+
() => ({ client, authState, authVersion }),
|
|
73
|
+
[client, authState, authVersion]
|
|
74
|
+
);
|
|
75
|
+
return /* @__PURE__ */ jsx(ParcaeContext.Provider, { value: contextValue, children });
|
|
49
76
|
};
|
|
50
77
|
var CACHE_TIMEOUT_MS = 6e4;
|
|
51
78
|
var DEFAULT_DEBOUNCE_MS = 100;
|
|
@@ -55,11 +82,12 @@ function buildCacheKey(chain, authVersion) {
|
|
|
55
82
|
const steps = JSON.stringify(chain.__steps ?? []);
|
|
56
83
|
return `${type}:${authVersion}:${steps}`;
|
|
57
84
|
}
|
|
58
|
-
function useQuery(chain) {
|
|
59
|
-
const client = useParcae();
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
85
|
+
function useQuery(chain, options = {}) {
|
|
86
|
+
const { client, authState, authVersion } = useParcae();
|
|
87
|
+
const waitForAuth = options.waitForAuth ?? true;
|
|
88
|
+
const authReady = !waitForAuth || authState !== "loading";
|
|
89
|
+
const cacheKey = chain && authReady ? buildCacheKey(chain, authVersion) : "__null__";
|
|
90
|
+
if (!queryCache.has(cacheKey) && chain && authReady) {
|
|
63
91
|
queryCache.set(cacheKey, {
|
|
64
92
|
items: [],
|
|
65
93
|
itemMap: /* @__PURE__ */ new Map(),
|
|
@@ -104,7 +132,7 @@ function useQuery(chain) {
|
|
|
104
132
|
const getSnapshot = useCallback(() => entry?.stateVersion ?? 0, [entry]);
|
|
105
133
|
useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
106
134
|
const refetch = useCallback(() => {
|
|
107
|
-
if (!chain || !entry) return;
|
|
135
|
+
if (!chain || !entry || !authReady) return;
|
|
108
136
|
entry.loading = true;
|
|
109
137
|
notifyListeners(entry);
|
|
110
138
|
chain.find().then((items) => {
|
|
@@ -141,10 +169,14 @@ function useQuery(chain) {
|
|
|
141
169
|
client.send("unsubscribe:query", { hash: cacheKey });
|
|
142
170
|
};
|
|
143
171
|
}
|
|
144
|
-
}, [chain, entry, cacheKey, client]);
|
|
172
|
+
}, [chain, entry, cacheKey, client, authReady]);
|
|
145
173
|
useEffect(() => {
|
|
146
|
-
if (chain) refetch();
|
|
147
|
-
}, [cacheKey]);
|
|
174
|
+
if (chain && authReady) refetch();
|
|
175
|
+
}, [cacheKey, authReady]);
|
|
176
|
+
if (!authReady) {
|
|
177
|
+
return { items: [], loading: true, error: null, refetch: () => {
|
|
178
|
+
} };
|
|
179
|
+
}
|
|
148
180
|
if (!entry) {
|
|
149
181
|
return { items: [], loading: false, error: null, refetch: () => {
|
|
150
182
|
} };
|
|
@@ -189,7 +221,7 @@ function applyDiffOps(entry, chain) {
|
|
|
189
221
|
const existing = entry.itemMap.get(op.id);
|
|
190
222
|
if (existing && op.data) {
|
|
191
223
|
for (const [key, value] of Object.entries(op.data)) {
|
|
192
|
-
existing
|
|
224
|
+
existing[key] = value;
|
|
193
225
|
}
|
|
194
226
|
changed = true;
|
|
195
227
|
}
|
|
@@ -203,7 +235,7 @@ function applyDiffOps(entry, chain) {
|
|
|
203
235
|
}
|
|
204
236
|
}
|
|
205
237
|
function useApi() {
|
|
206
|
-
const client = useParcae();
|
|
238
|
+
const { client } = useParcae();
|
|
207
239
|
return useMemo(
|
|
208
240
|
() => ({
|
|
209
241
|
get: client.get.bind(client),
|
|
@@ -216,20 +248,29 @@ function useApi() {
|
|
|
216
248
|
);
|
|
217
249
|
}
|
|
218
250
|
function useSDK() {
|
|
219
|
-
return useParcae();
|
|
251
|
+
return useParcae().client;
|
|
220
252
|
}
|
|
221
253
|
function useConnectionStatus() {
|
|
222
|
-
const client = useParcae();
|
|
254
|
+
const { client, authState } = useParcae();
|
|
223
255
|
return {
|
|
224
256
|
isConnected: client.isConnected,
|
|
225
|
-
isLoading: client.isLoading
|
|
257
|
+
isLoading: client.isLoading,
|
|
258
|
+
authState
|
|
226
259
|
};
|
|
227
260
|
}
|
|
261
|
+
function useAuthState() {
|
|
262
|
+
return useParcae().authState;
|
|
263
|
+
}
|
|
228
264
|
function useSetting(key, defaultValue) {
|
|
229
|
-
const client = useParcae();
|
|
265
|
+
const { client, authState } = useParcae();
|
|
230
266
|
const [value, setValue] = useState(defaultValue);
|
|
231
267
|
const [isLoading, setIsLoading] = useState(true);
|
|
232
268
|
useEffect(() => {
|
|
269
|
+
if (authState === "loading") return;
|
|
270
|
+
if (authState === "unauthenticated") {
|
|
271
|
+
setIsLoading(false);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
233
274
|
let cancelled = false;
|
|
234
275
|
client.get(`/settings/${encodeURIComponent(key)}`).then((result) => {
|
|
235
276
|
if (!cancelled && result?.value !== void 0) {
|
|
@@ -242,7 +283,7 @@ function useSetting(key, defaultValue) {
|
|
|
242
283
|
return () => {
|
|
243
284
|
cancelled = true;
|
|
244
285
|
};
|
|
245
|
-
}, [key, client]);
|
|
286
|
+
}, [key, client, authState]);
|
|
246
287
|
const update = useCallback(
|
|
247
288
|
async (newValue) => {
|
|
248
289
|
setValue(newValue);
|
|
@@ -255,6 +296,6 @@ function useSetting(key, defaultValue) {
|
|
|
255
296
|
return [value, update, { isLoading }];
|
|
256
297
|
}
|
|
257
298
|
|
|
258
|
-
export { ParcaeContext, ParcaeProvider, useApi, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
|
|
299
|
+
export { ParcaeContext, ParcaeProvider, useApi, useAuthState, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
|
|
259
300
|
//# sourceMappingURL=index.js.map
|
|
260
301
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/context.ts","../../src/react/Provider.tsx","../../src/react/useQuery.ts","../../src/react/useApi.ts","../../src/react/useSetting.ts"],"names":["useEffect","useMemo","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ParcaeClient } from \"../client\";\n\nexport const ParcaeContext = createContext<ParcaeClient | null>(null);\n\nexport function useParcae(): ParcaeClient {\n const client = useContext(ParcaeContext);\n if (!client) {\n throw new Error(\"useParcae must be used within a <ParcaeProvider>\");\n }\n return client;\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport { createClient } from \"../client\";\nimport type { ParcaeClient, ClientConfig } from \"../client\";\nimport { ParcaeContext } from \"./context\";\n\nexport interface ParcaeProviderProps {\n /** Pre-created client instance. If provided, url/key/transport are ignored. */\n client?: ParcaeClient;\n /** API base URL (required if no client provided). */\n url?: string;\n /** Bearer token or null (pre-auth). */\n apiKey?: string | null;\n /** Stable user ID — triggers re-auth when it changes. */\n userId?: string | null;\n /** API version. Default: \"v1\" */\n version?: string;\n /** Transport type. Default: \"socket\" */\n transport?: ClientConfig[\"transport\"];\n children: React.ReactNode;\n onReady?: (client: ParcaeClient) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * ParcaeProvider — creates the SDK client once and re-authenticates on userId change.\n *\n * Usage with pre-created client:\n * ```tsx\n * const client = createClient({ url: \"...\", transport: \"socket\" });\n * <ParcaeProvider client={client}><App /></ParcaeProvider>\n * ```\n *\n * Usage with inline config:\n * ```tsx\n * <ParcaeProvider url=\"http://localhost:3000\" apiKey={token} userId={user.id}>\n * <App />\n * </ParcaeProvider>\n * ```\n */\nexport const ParcaeProvider: React.FC<ParcaeProviderProps> = ({\n client: externalClient,\n url,\n apiKey,\n userId,\n version = \"v1\",\n transport = \"socket\",\n children,\n onReady,\n onError,\n}) => {\n // Create client once per url+version+transport (or use external)\n const client = useMemo(() => {\n if (externalClient) return externalClient;\n if (!url)\n throw new Error(\n \"ParcaeProvider requires either a `client` prop or a `url` prop\",\n );\n return createClient({ url, version, transport, key: null });\n }, [externalClient, url, version, transport]);\n\n // Refs for callbacks to avoid re-running effects on unstable inline functions\n const apiKeyRef = useRef(apiKey);\n apiKeyRef.current = apiKey;\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n // Re-authenticate when userId changes\n useEffect(() => {\n client\n .setKey(apiKeyRef.current ?? null)\n .then(() => onReadyRef.current?.(client))\n .catch((err) => onErrorRef.current?.(err));\n }, [userId, client]);\n\n // Forward transport errors\n useEffect(() => {\n const onErr = (err: Error) => onErrorRef.current?.(err);\n client.on(\"error\", onErr);\n return () => {\n client.off(\"error\", onErr);\n };\n }, [client]);\n\n return (\n <ParcaeContext.Provider value={client}>{children}</ParcaeContext.Provider>\n );\n};\n\nexport default ParcaeProvider;\n","\"use client\";\n\n/**\n * useQuery — reactive data fetching with realtime subscriptions.\n *\n * Takes a query chain, returns an array of typed model instances.\n * Subscribes to realtime updates — the server diffs queries on model changes\n * and pushes surgical add/remove/update ops.\n *\n * @example\n * ```tsx\n * const { items, loading } = useQuery(Post.where({ published: true }));\n * ```\n */\n\nimport { useCallback, useEffect, useSyncExternalStore } from \"react\";\nimport { useParcae } from \"./context\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface QueryChain<T> {\n find(): Promise<T[]>;\n __steps?: any[];\n __modelType?: string;\n __modelClass?: any;\n __adapter?: any;\n __debounceMs?: number;\n}\n\ninterface UseQueryResult<T> {\n items: T[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\ninterface DiffOp {\n op: \"add\" | \"remove\" | \"update\";\n id: string;\n data?: Record<string, any>;\n}\n\n// ─── Query Cache ─────────────────────────────────────────────────────────────\n\nconst CACHE_TIMEOUT_MS = 60_000;\nconst DEFAULT_DEBOUNCE_MS = 100;\n\ninterface CacheEntry<T = any> {\n items: T[];\n itemMap: Map<string, T>;\n loading: boolean;\n error: Error | null;\n refCount: number;\n timeoutHandle: ReturnType<typeof setTimeout> | null;\n version: number;\n stateVersion: number;\n listeners: Set<() => void>;\n subscriptionHash: string | null;\n pendingOps: DiffOp[];\n debounceTimer: ReturnType<typeof setTimeout> | null;\n debounceMs: number;\n disposeSubscription: (() => void) | null;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\n\nfunction buildCacheKey(chain: QueryChain<any>, authVersion: number): string {\n const type = chain.__modelType ?? \"unknown\";\n const steps = JSON.stringify(chain.__steps ?? []);\n return `${type}:${authVersion}:${steps}`;\n}\n\n// ─── useQuery ────────────────────────────────────────────────────────────────\n\nexport function useQuery<T>(\n chain: QueryChain<T> | null | undefined,\n): UseQueryResult<T> {\n const client = useParcae();\n const authVersion = client.authVersion;\n\n const cacheKey = chain ? buildCacheKey(chain, authVersion) : \"__null__\";\n\n // ── Get or create cache entry ──────────────────────────────────────\n\n if (!queryCache.has(cacheKey) && chain) {\n queryCache.set(cacheKey, {\n items: [],\n itemMap: new Map(),\n loading: true,\n error: null,\n refCount: 0,\n timeoutHandle: null,\n version: 0,\n stateVersion: 0,\n listeners: new Set(),\n subscriptionHash: null,\n pendingOps: [],\n debounceTimer: null,\n debounceMs: chain.__debounceMs ?? DEFAULT_DEBOUNCE_MS,\n disposeSubscription: null,\n });\n }\n\n const entry = queryCache.get(cacheKey);\n\n // ── useSyncExternalStore for tear-safe rendering ───────────────────\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!entry) return () => {};\n entry.listeners.add(listener);\n entry.refCount++;\n\n // Cancel pending GC\n if (entry.timeoutHandle) {\n clearTimeout(entry.timeoutHandle);\n entry.timeoutHandle = null;\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.refCount--;\n\n // GC: if no subscribers left, schedule cleanup\n if (entry.refCount <= 0) {\n entry.timeoutHandle = setTimeout(() => {\n entry.disposeSubscription?.();\n queryCache.delete(cacheKey);\n }, CACHE_TIMEOUT_MS);\n }\n };\n },\n [entry, cacheKey],\n );\n\n const getSnapshot = useCallback(() => entry?.stateVersion ?? 0, [entry]);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n // ── Fetch + subscribe ──────────────────────────────────────────────\n\n const refetch = useCallback(() => {\n if (!chain || !entry) return;\n\n entry.loading = true;\n notifyListeners(entry);\n\n chain\n .find()\n .then((items) => {\n entry.items = items;\n entry.itemMap = new Map(items.map((item: any) => [item.id, item]));\n entry.loading = false;\n entry.error = null;\n entry.version++;\n notifyListeners(entry);\n })\n .catch((err) => {\n entry.error = err;\n entry.loading = false;\n notifyListeners(entry);\n });\n\n // Set up realtime subscription if transport supports it\n if (!entry.disposeSubscription && chain.__modelType) {\n const subEvent = `query:${cacheKey}`;\n\n // Ask server to subscribe to this query\n client.send(\"subscribe:query\", {\n hash: cacheKey,\n modelType: chain.__modelType,\n steps: chain.__steps ?? [],\n });\n\n // Listen for diff ops from the server\n const dispose = client.subscribe(subEvent, (ops: DiffOp[]) => {\n if (!entry) return;\n entry.pendingOps.push(...ops);\n\n // Debounce application of ops\n if (entry.debounceTimer) clearTimeout(entry.debounceTimer);\n entry.debounceTimer = setTimeout(() => {\n applyDiffOps(entry, chain);\n entry.debounceTimer = null;\n }, entry.debounceMs);\n });\n\n entry.subscriptionHash = cacheKey;\n entry.disposeSubscription = () => {\n dispose();\n client.send(\"unsubscribe:query\", { hash: cacheKey });\n };\n }\n }, [chain, entry, cacheKey, client]);\n\n // Initial fetch on mount / chain change\n useEffect(() => {\n if (chain) refetch();\n }, [cacheKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!entry) {\n return { items: [], loading: false, error: null, refetch: () => {} };\n }\n\n return {\n items: entry.items,\n loading: entry.loading,\n error: entry.error,\n refetch,\n };\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction notifyListeners(entry: CacheEntry): void {\n entry.stateVersion++;\n for (const listener of entry.listeners) listener();\n}\n\nfunction applyDiffOps<T>(entry: CacheEntry<T>, chain: QueryChain<T>): void {\n const ops = entry.pendingOps.splice(0);\n if (!ops.length) return;\n\n const ModelClass = chain.__modelClass;\n const adapter = chain.__adapter;\n let changed = false;\n\n for (const op of ops) {\n switch (op.op) {\n case \"add\": {\n if (!entry.itemMap.has(op.id) && op.data && ModelClass && adapter) {\n const instance = new ModelClass(adapter, op.data);\n entry.items.push(instance);\n entry.itemMap.set(op.id, instance);\n changed = true;\n }\n break;\n }\n case \"remove\": {\n if (entry.itemMap.has(op.id)) {\n entry.items = entry.items.filter((item: any) => item.id !== op.id);\n entry.itemMap.delete(op.id);\n changed = true;\n }\n break;\n }\n case \"update\": {\n const existing = entry.itemMap.get(op.id) as any;\n if (existing && op.data) {\n for (const [key, value] of Object.entries(op.data)) {\n existing.__data[key] = value;\n }\n changed = true;\n }\n break;\n }\n }\n }\n\n if (changed) {\n entry.version++;\n notifyListeners(entry);\n }\n}\n","\"use client\";\n\n/**\n * useApi — pre-bound HTTP methods from the Parcae client.\n *\n * @example\n * ```tsx\n * const { get, post } = useApi();\n * const data = await get(\"/custom-endpoint\");\n * ```\n */\n\nimport { useMemo } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useApi() {\n const client = useParcae();\n\n return useMemo(\n () => ({\n get: client.get.bind(client),\n post: client.post.bind(client),\n put: client.put.bind(client),\n patch: client.patch.bind(client),\n delete: client.delete.bind(client),\n }),\n [client],\n );\n}\n\n/**\n * useSDK — raw client instance.\n */\nexport function useSDK() {\n return useParcae();\n}\n\n/**\n * useConnectionStatus — reactive connection state.\n */\nexport function useConnectionStatus() {\n const client = useParcae();\n // This is a snapshot — for true reactivity, components should\n // listen to client.on(\"connected\"/\"disconnected\") events.\n return {\n isConnected: client.isConnected,\n isLoading: client.isLoading,\n };\n}\n","\"use client\";\n\n/**\n * useSetting — key-value user settings stored as a Setting model.\n *\n * @example\n * ```tsx\n * const [theme, setTheme, { isLoading }] = useSetting(\"theme\", \"light\");\n * ```\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useSetting<T = string>(\n key: string,\n defaultValue: T,\n): [T, (value: T) => Promise<void>, { isLoading: boolean }] {\n const client = useParcae();\n const [value, setValue] = useState<T>(defaultValue);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n\n client\n .get(`/settings/${encodeURIComponent(key)}`)\n .then((result) => {\n if (!cancelled && result?.value !== undefined) {\n setValue(result.value);\n }\n })\n .catch(() => {\n // Setting doesn't exist yet — use default\n })\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, client]);\n\n const update = useCallback(\n async (newValue: T) => {\n setValue(newValue);\n await client.put(`/settings/${encodeURIComponent(key)}`, {\n value: newValue,\n });\n },\n [key, client],\n );\n\n return [value, update, { isLoading }];\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/context.ts","../../src/react/Provider.tsx","../../src/react/useQuery.ts","../../src/react/useApi.ts","../../src/react/useSetting.ts"],"names":["useCallback","useEffect","useMemo","useState"],"mappings":";;;;AAWO,IAAM,aAAA,GAAgB,cAAyC,IAAI;AAEnE,SAAS,SAAA,GAAgC;AAC9C,EAAA,MAAM,GAAA,GAAM,WAAW,aAAa,CAAA;AACpC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,GAAA;AACT;ACaO,IAAM,iBAAgD,CAAC;AAAA,EAC5D,MAAA,EAAQ,cAAA;AAAA,EACR,GAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA,GAAU,IAAA;AAAA,EACV,SAAA,GAAY,QAAA;AAAA,EACZ,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAAM;AACJ,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA;AAAA;AAAA;AAAA;AAAA,IAIhC,MAAA,KAAW,MAAA,GACP,SAAA,GACA,MAAA,KAAW,OACT,iBAAA,GACA;AAAA,GACR;AACA,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,CAAC,CAAA;AAEhD,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAM;AAC3B,IAAA,IAAI,gBAAgB,OAAO,cAAA;AAC3B,IAAA,IAAI,CAAC,GAAA;AACH,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AACF,IAAA,OAAO,aAAa,EAAE,GAAA,EAAK,SAAS,SAAA,EAAW,GAAA,EAAK,MAAM,CAAA;AAAA,EAC5D,GAAG,CAAC,cAAA,EAAgB,GAAA,EAAK,OAAA,EAAS,SAAS,CAAC,CAAA;AAE5C,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAGrB,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,IAAI,WAAW,MAAA,EAAW;AACxB,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA,YAAA,CAAa,iBAAiB,CAAA;AAC9B,MAAA,cAAA,CAAe,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC3B,MAAA;AAAA,IACF;AAGA,IAAA,YAAA,CAAa,SAAS,CAAA;AACtB,IAAA,MAAA,CACG,MAAA,CAAO,MAAM,CAAA,CACb,IAAA,CAAK,MAAM;AACV,MAAA,YAAA,CAAa,eAAe,CAAA;AAC5B,MAAA,cAAA,CAAe,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC3B,MAAA,UAAA,CAAW,UAAU,MAAM,CAAA;AAAA,IAC7B,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,YAAA,CAAa,iBAAiB,CAAA;AAC9B,MAAA,cAAA,CAAe,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC3B,MAAA,UAAA,CAAW,UAAU,GAAG,CAAA;AAAA,IAC1B,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,CAAC,MAAA,EAAQ,MAAA,EAAQ,MAAM,CAAC,CAAA;AAE3B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAA,GAAQ,CAAC,GAAA,KAAe,UAAA,CAAW,UAAU,GAAG,CAAA;AACtD,IAAA,MAAA,CAAO,EAAA,CAAG,SAAS,KAAK,CAAA;AACxB,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,GAAA,CAAI,SAAS,KAAK,CAAA;AAAA,IAC3B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACnB,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAY,CAAA;AAAA,IACxC,CAAC,MAAA,EAAQ,SAAA,EAAW,WAAW;AAAA,GACjC;AAEA,EAAA,2BACG,aAAA,CAAc,QAAA,EAAd,EAAuB,KAAA,EAAO,cAC5B,QAAA,EACH,CAAA;AAEJ;AClEA,IAAM,gBAAA,GAAmB,GAAA;AACzB,IAAM,mBAAA,GAAsB,GAAA;AAmB5B,IAAM,UAAA,uBAAiB,GAAA,EAAwB;AAE/C,SAAS,aAAA,CAAc,OAAwB,WAAA,EAA6B;AAC1E,EAAA,MAAM,IAAA,GAAO,MAAM,WAAA,IAAe,SAAA;AAClC,EAAA,MAAM,QAAQ,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,OAAA,IAAW,EAAE,CAAA;AAChD,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,WAAW,IAAI,KAAK,CAAA,CAAA;AACxC;AAIO,SAAS,QAAA,CACd,KAAA,EACA,OAAA,GAA2B,EAAC,EACT;AACnB,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,KAAgB,SAAA,EAAU;AACrD,EAAA,MAAM,WAAA,GAAc,QAAQ,WAAA,IAAe,IAAA;AAG3C,EAAA,MAAM,SAAA,GAAY,CAAC,WAAA,IAAe,SAAA,KAAc,SAAA;AAEhD,EAAA,MAAM,WACJ,KAAA,IAAS,SAAA,GAAY,aAAA,CAAc,KAAA,EAAO,WAAW,CAAA,GAAI,UAAA;AAI3D,EAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA,IAAK,SAAS,SAAA,EAAW;AACnD,IAAA,UAAA,CAAW,IAAI,QAAA,EAAU;AAAA,MACvB,OAAO,EAAC;AAAA,MACR,OAAA,sBAAa,GAAA,EAAI;AAAA,MACjB,OAAA,EAAS,IAAA;AAAA,MACT,KAAA,EAAO,IAAA;AAAA,MACP,QAAA,EAAU,CAAA;AAAA,MACV,aAAA,EAAe,IAAA;AAAA,MACf,OAAA,EAAS,CAAA;AAAA,MACT,YAAA,EAAc,CAAA;AAAA,MACd,SAAA,sBAAe,GAAA,EAAI;AAAA,MACnB,gBAAA,EAAkB,IAAA;AAAA,MAClB,YAAY,EAAC;AAAA,MACb,aAAA,EAAe,IAAA;AAAA,MACf,UAAA,EAAY,MAAM,YAAA,IAAgB,mBAAA;AAAA,MAClC,mBAAA,EAAqB;AAAA,KACtB,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA;AAIrC,EAAA,MAAM,SAAA,GAAYA,WAAAA;AAAA,IAChB,CAAC,QAAA,KAAyB;AACxB,MAAA,IAAI,CAAC,KAAA,EAAO,OAAO,MAAM;AAAA,MAAC,CAAA;AAC1B,MAAA,KAAA,CAAM,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC5B,MAAA,KAAA,CAAM,QAAA,EAAA;AAEN,MAAA,IAAI,MAAM,aAAA,EAAe;AACvB,QAAA,YAAA,CAAa,MAAM,aAAa,CAAA;AAChC,QAAA,KAAA,CAAM,aAAA,GAAgB,IAAA;AAAA,MACxB;AAEA,MAAA,OAAO,MAAM;AACX,QAAA,KAAA,CAAM,SAAA,CAAU,OAAO,QAAQ,CAAA;AAC/B,QAAA,KAAA,CAAM,QAAA,EAAA;AAEN,QAAA,IAAI,KAAA,CAAM,YAAY,CAAA,EAAG;AACvB,UAAA,KAAA,CAAM,aAAA,GAAgB,WAAW,MAAM;AACrC,YAAA,KAAA,CAAM,mBAAA,IAAsB;AAC5B,YAAA,UAAA,CAAW,OAAO,QAAQ,CAAA;AAAA,UAC5B,GAAG,gBAAgB,CAAA;AAAA,QACrB;AAAA,MACF,CAAA;AAAA,IACF,CAAA;AAAA,IACA,CAAC,OAAO,QAAQ;AAAA,GAClB;AAEA,EAAA,MAAM,WAAA,GAAcA,YAAY,MAAM,KAAA,EAAO,gBAAgB,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEvE,EAAA,oBAAA,CAAqB,SAAA,EAAW,aAAa,WAAW,CAAA;AAIxD,EAAA,MAAM,OAAA,GAAUA,YAAY,MAAM;AAChC,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,IAAS,CAAC,SAAA,EAAW;AAEpC,IAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,IAAA,eAAA,CAAgB,KAAK,CAAA;AAErB,IAAA,KAAA,CACG,IAAA,EAAK,CACL,IAAA,CAAK,CAAC,KAAA,KAAU;AACf,MAAA,KAAA,CAAM,KAAA,GAAQ,KAAA;AACd,MAAA,KAAA,CAAM,OAAA,GAAU,IAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAc,CAAC,IAAA,CAAK,EAAA,EAAI,IAAI,CAAC,CAAC,CAAA;AACjE,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAChB,MAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AACd,MAAA,KAAA,CAAM,OAAA,EAAA;AACN,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,KAAA,CAAM,KAAA,GAAQ,GAAA;AACd,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAChB,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAGH,IAAA,IAAI,CAAC,KAAA,CAAM,mBAAA,IAAuB,KAAA,CAAM,WAAA,EAAa;AACnD,MAAA,MAAM,QAAA,GAAW,SAAS,QAAQ,CAAA,CAAA;AAElC,MAAA,MAAA,CAAO,KAAK,iBAAA,EAAmB;AAAA,QAC7B,IAAA,EAAM,QAAA;AAAA,QACN,WAAW,KAAA,CAAM,WAAA;AAAA,QACjB,KAAA,EAAO,KAAA,CAAM,OAAA,IAAW;AAAC,OAC1B,CAAA;AAED,MAAA,MAAM,OAAA,GAAU,MAAA,CAAO,SAAA,CAAU,QAAA,EAAU,CAAC,GAAA,KAAkB;AAC5D,QAAA,IAAI,CAAC,KAAA,EAAO;AACZ,QAAA,KAAA,CAAM,UAAA,CAAW,IAAA,CAAK,GAAG,GAAG,CAAA;AAE5B,QAAA,IAAI,KAAA,CAAM,aAAA,EAAe,YAAA,CAAa,KAAA,CAAM,aAAa,CAAA;AACzD,QAAA,KAAA,CAAM,aAAA,GAAgB,WAAW,MAAM;AACrC,UAAA,YAAA,CAAa,OAAO,KAAK,CAAA;AACzB,UAAA,KAAA,CAAM,aAAA,GAAgB,IAAA;AAAA,QACxB,CAAA,EAAG,MAAM,UAAU,CAAA;AAAA,MACrB,CAAC,CAAA;AAED,MAAA,KAAA,CAAM,gBAAA,GAAmB,QAAA;AACzB,MAAA,KAAA,CAAM,sBAAsB,MAAM;AAChC,QAAA,OAAA,EAAQ;AACR,QAAA,MAAA,CAAO,IAAA,CAAK,mBAAA,EAAqB,EAAE,IAAA,EAAM,UAAU,CAAA;AAAA,MACrD,CAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,KAAA,EAAO,OAAO,QAAA,EAAU,MAAA,EAAQ,SAAS,CAAC,CAAA;AAG9C,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,KAAA,IAAS,WAAW,OAAA,EAAQ;AAAA,EAClC,CAAA,EAAG,CAAC,QAAA,EAAU,SAAS,CAAC,CAAA;AAGxB,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,OAAO,EAAE,OAAO,EAAC,EAAG,SAAS,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,OAAA,EAAS,MAAM;AAAA,IAAC,CAAA,EAAE;AAAA,EACpE;AAEA,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,EAAE,OAAO,EAAC,EAAG,SAAS,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,OAAA,EAAS,MAAM;AAAA,IAAC,CAAA,EAAE;AAAA,EACrE;AAEA,EAAA,OAAO;AAAA,IACL,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,OAAO,KAAA,CAAM,KAAA;AAAA,IACb;AAAA,GACF;AACF;AAIA,SAAS,gBAAgB,KAAA,EAAyB;AAChD,EAAA,KAAA,CAAM,YAAA,EAAA;AACN,EAAA,KAAA,MAAW,QAAA,IAAY,KAAA,CAAM,SAAA,EAAW,QAAA,EAAS;AACnD;AAEA,SAAS,YAAA,CAAgB,OAAsB,KAAA,EAA4B;AACzE,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,UAAA,CAAW,MAAA,CAAO,CAAC,CAAA;AACrC,EAAA,IAAI,CAAC,IAAI,MAAA,EAAQ;AAEjB,EAAA,MAAM,aAAa,KAAA,CAAM,YAAA;AACzB,EAAA,MAAM,UAAU,KAAA,CAAM,SAAA;AACtB,EAAA,IAAI,OAAA,GAAU,KAAA;AAEd,EAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,IAAA,QAAQ,GAAG,EAAA;AAAI,MACb,KAAK,KAAA,EAAO;AACV,QAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,IAAK,EAAA,CAAG,IAAA,IAAQ,UAAA,IAAc,OAAA,EAAS;AACjE,UAAA,MAAM,QAAA,GAAW,IAAI,UAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAChD,UAAA,KAAA,CAAM,KAAA,CAAM,KAAK,QAAQ,CAAA;AACzB,UAAA,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAA,CAAG,EAAA,EAAI,QAAQ,CAAA;AACjC,UAAA,OAAA,GAAU,IAAA;AAAA,QACZ;AACA,QAAA;AAAA,MACF;AAAA,MACA,KAAK,QAAA,EAAU;AACb,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG;AAC5B,UAAA,KAAA,CAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,MAAA,CAAO,CAAC,IAAA,KAAc,IAAA,CAAK,EAAA,KAAO,EAAA,CAAG,EAAE,CAAA;AACjE,UAAA,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,EAAE,CAAA;AAC1B,UAAA,OAAA,GAAU,IAAA;AAAA,QACZ;AACA,QAAA;AAAA,MACF;AAAA,MACA,KAAK,QAAA,EAAU;AACb,QAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAG,EAAE,CAAA;AACxC,QAAA,IAAI,QAAA,IAAY,GAAG,IAAA,EAAM;AACvB,UAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,EAAA,CAAG,IAAI,CAAA,EAAG;AAClD,YAAA,QAAA,CAAS,GAAG,CAAA,GAAI,KAAA;AAAA,UAClB;AACA,UAAA,OAAA,GAAU,IAAA;AAAA,QACZ;AACA,QAAA;AAAA,MACF;AAAA;AACF,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,KAAA,CAAM,OAAA,EAAA;AACN,IAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,EACvB;AACF;AC/QO,SAAS,MAAA,GAAS;AACvB,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAE7B,EAAA,OAAOC,OAAAA;AAAA,IACL,OAAO;AAAA,MACL,GAAA,EAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AAAA,MAC3B,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA;AAAA,MAC7B,GAAA,EAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AAAA,MAC3B,KAAA,EAAO,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,MAAM,CAAA;AAAA,MAC/B,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,MAAM;AAAA,KACnC,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACF;AAKO,SAAS,MAAA,GAAS;AACvB,EAAA,OAAO,WAAU,CAAE,MAAA;AACrB;AAKO,SAAS,mBAAA,GAAsB;AACpC,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,SAAA,EAAU;AACxC,EAAA,OAAO;AAAA,IACL,aAAa,MAAA,CAAO,WAAA;AAAA,IACpB,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB;AAAA,GACF;AACF;AAKO,SAAS,YAAA,GAAe;AAC7B,EAAA,OAAO,WAAU,CAAE,SAAA;AACrB;ACvCO,SAAS,UAAA,CACd,KACA,YAAA,EAC0D;AAC1D,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,SAAA,EAAU;AACxC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,SAAY,YAAY,CAAA;AAClD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,IAAI,CAAA;AAG/C,EAAAF,UAAU,MAAM;AACd,IAAA,IAAI,cAAc,SAAA,EAAW;AAC7B,IAAA,IAAI,cAAc,iBAAA,EAAmB;AACnC,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,MAAA,CACG,GAAA,CAAI,aAAa,kBAAA,CAAmB,GAAG,CAAC,CAAA,CAAE,CAAA,CAC1C,IAAA,CAAK,CAAC,MAAA,KAAW;AAChB,MAAA,IAAI,CAAC,SAAA,IAAa,MAAA,EAAQ,KAAA,KAAU,MAAA,EAAW;AAC7C,QAAA,QAAA,CAAS,OAAO,KAAK,CAAA;AAAA,MACvB;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA,CACd,OAAA,CAAQ,MAAM;AACb,MAAA,IAAI,CAAC,SAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAAA,IACpC,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,MAAA,EAAQ,SAAS,CAAC,CAAA;AAE3B,EAAA,MAAM,MAAA,GAASD,WAAAA;AAAA,IACb,OAAO,QAAA,KAAgB;AACrB,MAAA,QAAA,CAAS,QAAQ,CAAA;AACjB,MAAA,MAAM,OAAO,GAAA,CAAI,CAAA,UAAA,EAAa,kBAAA,CAAmB,GAAG,CAAC,CAAA,CAAA,EAAI;AAAA,QACvD,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,KAAK,MAAM;AAAA,GACd;AAEA,EAAA,OAAO,CAAC,KAAA,EAAO,MAAA,EAAQ,EAAE,WAAW,CAAA;AACtC","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ParcaeClient } from \"../client\";\n\nexport type AuthState = \"loading\" | \"authenticated\" | \"unauthenticated\";\n\nexport interface ParcaeContextValue {\n client: ParcaeClient;\n authState: AuthState;\n authVersion: number;\n}\n\nexport const ParcaeContext = createContext<ParcaeContextValue | null>(null);\n\nexport function useParcae(): ParcaeContextValue {\n const ctx = useContext(ParcaeContext);\n if (!ctx) {\n throw new Error(\"useParcae must be used within a <ParcaeProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useMemo,\n useRef,\n useState,\n useCallback,\n} from \"react\";\nimport { createClient } from \"../client\";\nimport type { ParcaeClient, ClientConfig } from \"../client\";\nimport { ParcaeContext } from \"./context\";\nimport type { ParcaeContextValue, AuthState } from \"./context\";\n\nexport interface ParcaeProviderProps {\n /** Pre-created client instance. If provided, url/key/transport are ignored. */\n client?: ParcaeClient;\n /** API base URL (required if no client provided). */\n url?: string;\n /** Bearer token, null (no session), or undefined (still loading). */\n apiKey?: string | null | undefined;\n /** Stable user ID — triggers re-auth when it changes. */\n userId?: string | null;\n /** API version. Default: \"v1\" */\n version?: string;\n /** Transport type. Default: \"socket\" */\n transport?: ClientConfig[\"transport\"];\n children: React.ReactNode;\n onReady?: (client: ParcaeClient) => void;\n onError?: (error: Error) => void;\n}\n\nexport const ParcaeProvider: React.FC<ParcaeProviderProps> = ({\n client: externalClient,\n url,\n apiKey,\n userId,\n version = \"v1\",\n transport = \"socket\",\n children,\n onReady,\n onError,\n}) => {\n const [authState, setAuthState] = useState<AuthState>(\n // If apiKey is undefined, the frontend auth provider is still loading.\n // If apiKey is null, the user is not logged in.\n // If apiKey is a string, we have a token but haven't verified it yet.\n apiKey === undefined\n ? \"loading\"\n : apiKey === null\n ? \"unauthenticated\"\n : \"loading\",\n );\n const [authVersion, setAuthVersion] = useState(0);\n\n const client = useMemo(() => {\n if (externalClient) return externalClient;\n if (!url)\n throw new Error(\n \"ParcaeProvider requires either a `client` prop or a `url` prop\",\n );\n return createClient({ url, version, transport, key: null });\n }, [externalClient, url, version, transport]);\n\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n // Authenticate when apiKey changes\n useEffect(() => {\n // apiKey is undefined → frontend auth still loading, do nothing\n if (apiKey === undefined) {\n setAuthState(\"loading\");\n return;\n }\n\n // apiKey is null → user is not logged in\n if (apiKey === null) {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n return;\n }\n\n // apiKey is a string → send to backend for verification\n setAuthState(\"loading\");\n client\n .setKey(apiKey)\n .then(() => {\n setAuthState(\"authenticated\");\n setAuthVersion((v) => v + 1);\n onReadyRef.current?.(client);\n })\n .catch((err) => {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n onErrorRef.current?.(err);\n });\n }, [apiKey, userId, client]);\n\n useEffect(() => {\n const onErr = (err: Error) => onErrorRef.current?.(err);\n client.on(\"error\", onErr);\n return () => {\n client.off(\"error\", onErr);\n };\n }, [client]);\n\n const contextValue = useMemo<ParcaeContextValue>(\n () => ({ client, authState, authVersion }),\n [client, authState, authVersion],\n );\n\n return (\n <ParcaeContext.Provider value={contextValue}>\n {children}\n </ParcaeContext.Provider>\n );\n};\n\nexport default ParcaeProvider;\n","\"use client\";\n\n/**\n * useQuery — reactive data fetching with realtime subscriptions.\n *\n * @example\n * ```tsx\n * const { items, loading } = useQuery(Post.where({ published: true }));\n *\n * // Wait for auth before firing (default: true)\n * const { items } = useQuery(query, { waitForAuth: true });\n * ```\n */\n\nimport { useCallback, useEffect, useSyncExternalStore } from \"react\";\nimport { useParcae } from \"./context\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface QueryChain<T> {\n find(): Promise<T[]>;\n __steps?: any[];\n __modelType?: string;\n __modelClass?: any;\n __adapter?: any;\n __debounceMs?: number;\n}\n\ninterface UseQueryOptions {\n /**\n * Wait for authentication to resolve before firing the query.\n * Default: true — queries don't fire while auth is \"loading\".\n * Set to false for public/unauthenticated queries.\n */\n waitForAuth?: boolean;\n}\n\ninterface UseQueryResult<T> {\n items: T[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\ninterface DiffOp {\n op: \"add\" | \"remove\" | \"update\";\n id: string;\n data?: Record<string, any>;\n}\n\n// ─── Query Cache ─────────────────────────────────────────────────────────────\n\nconst CACHE_TIMEOUT_MS = 60_000;\nconst DEFAULT_DEBOUNCE_MS = 100;\n\ninterface CacheEntry<T = any> {\n items: T[];\n itemMap: Map<string, T>;\n loading: boolean;\n error: Error | null;\n refCount: number;\n timeoutHandle: ReturnType<typeof setTimeout> | null;\n version: number;\n stateVersion: number;\n listeners: Set<() => void>;\n subscriptionHash: string | null;\n pendingOps: DiffOp[];\n debounceTimer: ReturnType<typeof setTimeout> | null;\n debounceMs: number;\n disposeSubscription: (() => void) | null;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\n\nfunction buildCacheKey(chain: QueryChain<any>, authVersion: number): string {\n const type = chain.__modelType ?? \"unknown\";\n const steps = JSON.stringify(chain.__steps ?? []);\n return `${type}:${authVersion}:${steps}`;\n}\n\n// ─── useQuery ────────────────────────────────────────────────────────────────\n\nexport function useQuery<T>(\n chain: QueryChain<T> | null | undefined,\n options: UseQueryOptions = {},\n): UseQueryResult<T> {\n const { client, authState, authVersion } = useParcae();\n const waitForAuth = options.waitForAuth ?? true;\n\n // If waiting for auth and auth is still loading, return empty loading state\n const authReady = !waitForAuth || authState !== \"loading\";\n\n const cacheKey =\n chain && authReady ? buildCacheKey(chain, authVersion) : \"__null__\";\n\n // ── Get or create cache entry ──────────────────────────────────────\n\n if (!queryCache.has(cacheKey) && chain && authReady) {\n queryCache.set(cacheKey, {\n items: [],\n itemMap: new Map(),\n loading: true,\n error: null,\n refCount: 0,\n timeoutHandle: null,\n version: 0,\n stateVersion: 0,\n listeners: new Set(),\n subscriptionHash: null,\n pendingOps: [],\n debounceTimer: null,\n debounceMs: chain.__debounceMs ?? DEFAULT_DEBOUNCE_MS,\n disposeSubscription: null,\n });\n }\n\n const entry = queryCache.get(cacheKey);\n\n // ── useSyncExternalStore for tear-safe rendering ───────────────────\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!entry) return () => {};\n entry.listeners.add(listener);\n entry.refCount++;\n\n if (entry.timeoutHandle) {\n clearTimeout(entry.timeoutHandle);\n entry.timeoutHandle = null;\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.refCount--;\n\n if (entry.refCount <= 0) {\n entry.timeoutHandle = setTimeout(() => {\n entry.disposeSubscription?.();\n queryCache.delete(cacheKey);\n }, CACHE_TIMEOUT_MS);\n }\n };\n },\n [entry, cacheKey],\n );\n\n const getSnapshot = useCallback(() => entry?.stateVersion ?? 0, [entry]);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n // ── Fetch + subscribe ──────────────────────────────────────────────\n\n const refetch = useCallback(() => {\n if (!chain || !entry || !authReady) return;\n\n entry.loading = true;\n notifyListeners(entry);\n\n chain\n .find()\n .then((items) => {\n entry.items = items;\n entry.itemMap = new Map(items.map((item: any) => [item.id, item]));\n entry.loading = false;\n entry.error = null;\n entry.version++;\n notifyListeners(entry);\n })\n .catch((err) => {\n entry.error = err;\n entry.loading = false;\n notifyListeners(entry);\n });\n\n // Set up realtime subscription\n if (!entry.disposeSubscription && chain.__modelType) {\n const subEvent = `query:${cacheKey}`;\n\n client.send(\"subscribe:query\", {\n hash: cacheKey,\n modelType: chain.__modelType,\n steps: chain.__steps ?? [],\n });\n\n const dispose = client.subscribe(subEvent, (ops: DiffOp[]) => {\n if (!entry) return;\n entry.pendingOps.push(...ops);\n\n if (entry.debounceTimer) clearTimeout(entry.debounceTimer);\n entry.debounceTimer = setTimeout(() => {\n applyDiffOps(entry, chain);\n entry.debounceTimer = null;\n }, entry.debounceMs);\n });\n\n entry.subscriptionHash = cacheKey;\n entry.disposeSubscription = () => {\n dispose();\n client.send(\"unsubscribe:query\", { hash: cacheKey });\n };\n }\n }, [chain, entry, cacheKey, client, authReady]);\n\n // Fetch when auth becomes ready or cache key changes\n useEffect(() => {\n if (chain && authReady) refetch();\n }, [cacheKey, authReady]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Auth not ready — return loading\n if (!authReady) {\n return { items: [], loading: true, error: null, refetch: () => {} };\n }\n\n if (!entry) {\n return { items: [], loading: false, error: null, refetch: () => {} };\n }\n\n return {\n items: entry.items,\n loading: entry.loading,\n error: entry.error,\n refetch,\n };\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction notifyListeners(entry: CacheEntry): void {\n entry.stateVersion++;\n for (const listener of entry.listeners) listener();\n}\n\nfunction applyDiffOps<T>(entry: CacheEntry<T>, chain: QueryChain<T>): void {\n const ops = entry.pendingOps.splice(0);\n if (!ops.length) return;\n\n const ModelClass = chain.__modelClass;\n const adapter = chain.__adapter;\n let changed = false;\n\n for (const op of ops) {\n switch (op.op) {\n case \"add\": {\n if (!entry.itemMap.has(op.id) && op.data && ModelClass && adapter) {\n const instance = new ModelClass(adapter, op.data);\n entry.items.push(instance);\n entry.itemMap.set(op.id, instance);\n changed = true;\n }\n break;\n }\n case \"remove\": {\n if (entry.itemMap.has(op.id)) {\n entry.items = entry.items.filter((item: any) => item.id !== op.id);\n entry.itemMap.delete(op.id);\n changed = true;\n }\n break;\n }\n case \"update\": {\n const existing = entry.itemMap.get(op.id) as any;\n if (existing && op.data) {\n for (const [key, value] of Object.entries(op.data)) {\n existing[key] = value;\n }\n changed = true;\n }\n break;\n }\n }\n }\n\n if (changed) {\n entry.version++;\n notifyListeners(entry);\n }\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useApi() {\n const { client } = useParcae();\n\n return useMemo(\n () => ({\n get: client.get.bind(client),\n post: client.post.bind(client),\n put: client.put.bind(client),\n patch: client.patch.bind(client),\n delete: client.delete.bind(client),\n }),\n [client],\n );\n}\n\n/**\n * useSDK — raw client instance.\n */\nexport function useSDK() {\n return useParcae().client;\n}\n\n/**\n * useConnectionStatus — connection + auth state.\n */\nexport function useConnectionStatus() {\n const { client, authState } = useParcae();\n return {\n isConnected: client.isConnected,\n isLoading: client.isLoading,\n authState,\n };\n}\n\n/**\n * useAuthState — just the auth state.\n */\nexport function useAuthState() {\n return useParcae().authState;\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useSetting<T = string>(\n key: string,\n defaultValue: T,\n): [T, (value: T) => Promise<void>, { isLoading: boolean }] {\n const { client, authState } = useParcae();\n const [value, setValue] = useState<T>(defaultValue);\n const [isLoading, setIsLoading] = useState(true);\n\n // Wait for auth before fetching settings (they're user-scoped)\n useEffect(() => {\n if (authState === \"loading\") return;\n if (authState === \"unauthenticated\") {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n\n client\n .get(`/settings/${encodeURIComponent(key)}`)\n .then((result) => {\n if (!cancelled && result?.value !== undefined) {\n setValue(result.value);\n }\n })\n .catch(() => {})\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, client, authState]);\n\n const update = useCallback(\n async (newValue: T) => {\n setValue(newValue);\n await client.put(`/settings/${encodeURIComponent(key)}`, {\n value: newValue,\n });\n },\n [key, client],\n );\n\n return [value, update, { isLoading }];\n}\n"]}
|