@reifydb/react 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1010 +1,103 @@
1
1
  // src/index.ts
2
2
  export * from "@reifydb/core";
3
3
  export * from "@reifydb/client";
4
+ export * from "@reifydb/store";
4
5
 
5
- // src/connection/connection.ts
6
- import { Client } from "@reifydb/client";
7
- var DEFAULT_CONFIG = {
8
- url: "ws://127.0.0.1:8090",
9
- options: { timeoutMs: 3e4 }
10
- };
11
- var DEFAULT_URL = "ws://127.0.0.1:8090";
12
- var Connection = class {
13
- constructor(config = DEFAULT_CONFIG) {
14
- this.state = {
15
- client: null,
16
- isConnected: false,
17
- isConnecting: false,
18
- connectionError: null,
19
- listeners: /* @__PURE__ */ new Set()
20
- };
21
- this.connectPromise = null;
22
- this.config = { ...DEFAULT_CONFIG, ...config };
23
- }
24
- setConfig(config) {
25
- this.config = config;
26
- }
27
- getConfig() {
28
- return this.config;
29
- }
30
- async connect(url, options) {
31
- if (this.state.isConnected) {
32
- return;
33
- }
34
- if (this.connectPromise) {
35
- return this.connectPromise;
36
- }
37
- const connectUrl = url || this.config.url || DEFAULT_CONFIG.url;
38
- const connectOptions = { token: this.config.token, ...this.config.options, ...options };
39
- this.updateState({
40
- isConnecting: true,
41
- connectionError: null
42
- });
43
- this.connectPromise = (async () => {
44
- try {
45
- const isHttp = connectUrl.startsWith("http://") || connectUrl.startsWith("https://");
46
- const isJson = this.config.format === "json";
47
- let client;
48
- if (isHttp) {
49
- client = isJson ? Client.connectJsonHttp(connectUrl, connectOptions) : Client.connectHttp(connectUrl, connectOptions);
50
- } else {
51
- client = isJson ? await Client.connectJsonWs(connectUrl, connectOptions) : await Client.connectWs(connectUrl, connectOptions);
52
- }
53
- this.updateState({
54
- client,
55
- isConnected: true,
56
- isConnecting: false,
57
- connectionError: null
58
- });
59
- } catch (err) {
60
- const errorMessage = err instanceof Error ? err.message : "Failed to connect to ReifyDB";
61
- console.error("[Connection] Connection failed:", errorMessage, err);
62
- this.updateState({
63
- client: null,
64
- isConnected: false,
65
- isConnecting: false,
66
- connectionError: errorMessage
67
- });
68
- throw err;
69
- } finally {
70
- this.connectPromise = null;
71
- }
72
- })();
73
- return this.connectPromise;
74
- }
75
- async disconnect() {
76
- this.connectPromise = null;
77
- if (this.state.client) {
78
- try {
79
- if ("disconnect" in this.state.client) {
80
- await this.state.client.disconnect();
81
- }
82
- } catch (err) {
83
- console.error("Error disconnecting:", err);
84
- }
85
- }
86
- this.updateState({
87
- client: null,
88
- isConnected: false,
89
- isConnecting: false,
90
- connectionError: null
91
- });
92
- }
93
- async reconnect(url, options) {
94
- await this.disconnect();
95
- await this.connect(url, options);
96
- }
97
- getClient() {
98
- return this.state.client;
99
- }
100
- isConnected() {
101
- return this.state.isConnected;
102
- }
103
- isConnecting() {
104
- return this.state.isConnecting;
105
- }
106
- getConnectionError() {
107
- return this.state.connectionError;
6
+ // src/provider.tsx
7
+ import { createContext, useContext } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var StoreContext = createContext(void 0);
10
+ function StoreProvider({ store, children }) {
11
+ return /* @__PURE__ */ jsx(StoreContext.Provider, { value: store, children });
12
+ }
13
+ function useStore() {
14
+ const store = useContext(StoreContext);
15
+ if (store === void 0) {
16
+ throw new Error("useStore must be used inside a StoreProvider");
108
17
  }
109
- getState() {
110
- const { listeners, ...state } = this.state;
111
- return state;
112
- }
113
- // Subscribe to state changes
114
- subscribe(listener) {
115
- this.state.listeners.add(listener);
116
- return () => {
117
- this.state.listeners.delete(listener);
118
- };
119
- }
120
- updateState(updates) {
121
- this.state = {
122
- ...this.state,
123
- ...updates
124
- };
125
- this.state.listeners.forEach((listener) => {
126
- listener(this.state);
127
- });
128
- }
129
- };
130
-
131
- // src/connection/connection-pool.ts
132
- var defaultConnection = null;
133
- function getConnection(config) {
134
- if (!defaultConnection) {
135
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
136
- defaultConnection = new Connection(mergedConfig);
137
- defaultConnection.connect().catch((err) => {
138
- console.error("[ConnectionPool] Eager connect failed:", err);
139
- });
140
- } else if (config) {
141
- defaultConnection.setConfig({ ...DEFAULT_CONFIG, ...config });
142
- }
143
- return defaultConnection;
144
- }
145
- async function clearConnection() {
146
- if (defaultConnection) {
147
- await defaultConnection.disconnect();
148
- defaultConnection = null;
149
- }
150
- }
151
-
152
- // src/connection/connection-context.tsx
153
- import React, { createContext, useEffect, useRef } from "react";
154
- var ConnectionContext = createContext(null);
155
- function ConnectionProvider({ config, children }) {
156
- const connection = getConnection(config);
157
- const prevConfigRef = useRef(void 0);
158
- const currentConfigStr = JSON.stringify(config);
159
- useEffect(() => {
160
- const configChanged = prevConfigRef.current !== void 0 && prevConfigRef.current !== currentConfigStr;
161
- if (configChanged && connection.isConnected()) {
162
- connection.reconnect().catch((err) => {
163
- console.error("[ConnectionProvider] Failed to reconnect:", err);
164
- });
165
- } else if (!connection.isConnected() && !connection.isConnecting()) {
166
- connection.connect().catch((err) => {
167
- console.error("[ConnectionProvider] Failed to connect:", err);
168
- });
169
- }
170
- prevConfigRef.current = currentConfigStr;
171
- }, [currentConfigStr, connection]);
172
- return /* @__PURE__ */ React.createElement(ConnectionContext.Provider, { value: connection }, children);
173
- }
174
-
175
- // src/hooks/use-connection.ts
176
- import { useContext, useEffect as useEffect2, useState } from "react";
177
- function useConnection(overrideConfig) {
178
- const contextConnection = useContext(ConnectionContext);
179
- const [connection] = useState(() => {
180
- if (overrideConfig) {
181
- return getConnection(overrideConfig);
182
- }
183
- if (contextConnection) {
184
- return contextConnection;
185
- }
186
- return getConnection();
187
- });
188
- const [state, setState] = useState(() => connection.getState());
189
- useEffect2(() => {
190
- const currentState = connection.getState();
191
- setState(currentState);
192
- const unsubscribe = connection.subscribe((newState) => {
193
- setState({
194
- client: newState.client,
195
- isConnected: newState.isConnected,
196
- isConnecting: newState.isConnecting,
197
- connectionError: newState.connectionError
198
- });
199
- });
200
- return unsubscribe;
201
- }, [connection]);
202
- return {
203
- ...state,
204
- connect: () => connection.connect(),
205
- disconnect: () => connection.disconnect(),
206
- reconnect: () => connection.reconnect()
207
- };
18
+ return store;
208
19
  }
209
20
 
210
- // src/hooks/use-query-executor.ts
211
- import { useState as useState2, useCallback, useRef as useRef2, useEffect as useEffect3 } from "react";
212
- function useQueryExecutor(options) {
213
- const { client } = useConnection(options?.connectionConfig);
214
- const [state, setState] = useState2({
215
- isExecuting: false,
216
- results: void 0,
217
- error: void 0,
218
- executionTime: void 0
219
- });
220
- const clientRef = useRef2(client);
221
- clientRef.current = client;
222
- const isMountedRef = useRef2(true);
223
- useEffect3(() => {
224
- return () => {
225
- isMountedRef.current = false;
226
- };
227
- }, []);
228
- const executionIdRef = useRef2(0);
229
- const pendingRef = useRef2(null);
230
- const query = useCallback(
231
- (rql, params, shapes) => {
232
- const currentClient = clientRef.current;
233
- if (!currentClient) {
234
- pendingRef.current = { rql, params, shapes };
235
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
236
- return Promise.resolve();
237
- }
238
- pendingRef.current = null;
239
- const thisExecution = ++executionIdRef.current;
240
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
241
- const startTime = Date.now();
242
- return (async () => {
243
- try {
244
- const frameResults = await currentClient.query(rql, params || null, shapes || []) || [];
245
- if (executionIdRef.current !== thisExecution) return;
246
- const executionTime = Date.now() - startTime;
247
- const results = frameResults.map((frame) => {
248
- if (Array.isArray(frame) && frame.length > 0) {
249
- const firstRow = frame[0];
250
- let columns = [];
251
- const hasValueObjects = firstRow && typeof firstRow === "object" && Object.values(firstRow).some((v) => v && typeof v === "object" && "type" in v);
252
- if (hasValueObjects) {
253
- columns = Object.keys(firstRow).map((key) => {
254
- const value = firstRow[key];
255
- const dataType = value?.type || "Utf8";
256
- return {
257
- name: key,
258
- type: dataType,
259
- payload: []
260
- };
261
- });
262
- } else {
263
- columns = Object.keys(firstRow).map((key) => ({
264
- name: key,
265
- type: "Utf8",
266
- payload: []
267
- }));
268
- }
269
- return {
270
- columns,
271
- rows: frame,
272
- executionTimeMs: executionTime
273
- };
274
- } else {
275
- return {
276
- columns: [],
277
- rows: [],
278
- executionTimeMs: executionTime
279
- };
280
- }
281
- });
282
- if (!isMountedRef.current) return;
283
- setState({
284
- isExecuting: false,
285
- results,
286
- error: void 0,
287
- executionTime
288
- });
289
- } catch (err) {
290
- if (executionIdRef.current !== thisExecution) return;
291
- const executionTime = Date.now() - startTime;
292
- let errorMessage = "Query execution failed";
293
- if (err instanceof Error) {
294
- errorMessage = err.message;
295
- } else if (typeof err === "string") {
296
- errorMessage = err;
297
- } else if (err && typeof err === "object" && "message" in err) {
298
- errorMessage = err.message;
299
- }
300
- console.error("Query execution failed:", errorMessage);
301
- if (!isMountedRef.current) return;
302
- setState((prev) => ({
303
- ...prev,
304
- isExecuting: false,
305
- error: errorMessage,
306
- executionTime
307
- }));
308
- }
309
- })();
310
- },
311
- []
312
- );
313
- useEffect3(() => {
314
- if (client && pendingRef.current) {
315
- const { rql, params, shapes } = pendingRef.current;
316
- query(rql, params, shapes);
317
- }
318
- }, [client, query]);
319
- const cancelQuery = useCallback(() => {
320
- executionIdRef.current++;
321
- setState((prev) => ({
322
- ...prev,
323
- isExecuting: false,
324
- error: "Query cancelled"
325
- }));
326
- }, []);
327
- return {
328
- isExecuting: state.isExecuting,
329
- results: state.results,
330
- error: state.error,
331
- executionTime: state.executionTime,
332
- query,
333
- cancelQuery
334
- };
335
- }
21
+ // src/use-subscription.ts
22
+ import { useEffect } from "react";
23
+ import { entryKey } from "@reifydb/store";
336
24
 
337
- // src/hooks/use-query.ts
338
- import { useEffect as useEffect4, useMemo } from "react";
339
- function useQueryOne(rql, params, shape, options) {
340
- const {
341
- isExecuting,
342
- results,
343
- error,
344
- query
345
- } = useQueryExecutor(options);
346
- useEffect4(() => {
347
- const shapes = shape ? [shape] : void 0;
348
- query(rql, params, shapes);
349
- }, [rql, params, query]);
350
- const result = useMemo(() => {
351
- return results && results.length > 0 ? results[0] : void 0;
352
- }, [results]);
353
- return { isExecuting, result, error };
354
- }
355
- function useQueryMany(rql, params, shapes, options) {
356
- const {
357
- isExecuting,
358
- results,
359
- error,
360
- query
361
- } = useQueryExecutor(options);
362
- useEffect4(() => {
363
- query(rql, params, shapes);
364
- }, [rql, params, query]);
365
- return { isExecuting, results, error };
25
+ // src/use-entry.ts
26
+ import { useCallback, useSyncExternalStore } from "react";
27
+ function useEntry(store, rql, params, shape) {
28
+ const subscribe = useCallback((listener) => store.subscribeState(listener), [store]);
29
+ return useSyncExternalStore(subscribe, () => store.getEntry(rql, params, shape));
366
30
  }
367
31
 
368
- // src/hooks/use-command-executor.ts
369
- import { useState as useState3, useCallback as useCallback2, useRef as useRef3, useEffect as useEffect5 } from "react";
370
- function useCommandExecutor(options) {
371
- const { client } = useConnection(options?.connectionConfig);
372
- const [state, setState] = useState3({
373
- isExecuting: false,
374
- results: void 0,
375
- error: void 0,
376
- executionTime: void 0
377
- });
378
- const clientRef = useRef3(client);
379
- clientRef.current = client;
380
- const isMountedRef = useRef3(false);
381
- useEffect5(() => {
382
- isMountedRef.current = true;
383
- return () => {
384
- isMountedRef.current = false;
385
- };
386
- }, []);
387
- const executionIdRef = useRef3(0);
388
- const pendingRef = useRef3(null);
389
- const command = useCallback2(
390
- (rql, params, shapes) => {
391
- const currentClient = clientRef.current;
392
- if (!currentClient) {
393
- pendingRef.current = { rql, params, shapes };
394
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
395
- return Promise.resolve();
396
- }
397
- pendingRef.current = null;
398
- const thisExecution = ++executionIdRef.current;
399
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
400
- const startTime = Date.now();
401
- return (async () => {
402
- try {
403
- const frameResults = await currentClient.command(rql, params || null, shapes || []) || [];
404
- if (executionIdRef.current !== thisExecution) return;
405
- const executionTime = Date.now() - startTime;
406
- const results = frameResults.map((frame) => {
407
- if (Array.isArray(frame) && frame.length > 0) {
408
- const firstRow = frame[0];
409
- let columns = [];
410
- const hasValueObjects = firstRow && typeof firstRow === "object" && Object.values(firstRow).some((v) => v && typeof v === "object" && "type" in v);
411
- if (hasValueObjects) {
412
- columns = Object.keys(firstRow).map((key) => {
413
- const value = firstRow[key];
414
- const dataType = value?.type || "Utf8";
415
- return {
416
- name: key,
417
- type: dataType,
418
- payload: []
419
- };
420
- });
421
- } else {
422
- columns = Object.keys(firstRow).map((key) => ({
423
- name: key,
424
- type: "Utf8",
425
- payload: []
426
- }));
427
- }
428
- return {
429
- columns,
430
- rows: frame,
431
- executionTimeMs: executionTime
432
- };
433
- } else {
434
- return {
435
- columns: [],
436
- rows: [],
437
- executionTimeMs: executionTime,
438
- rowsAffected: typeof frame === "number" ? frame : void 0
439
- };
440
- }
441
- });
442
- if (!isMountedRef.current) return;
443
- setState({
444
- isExecuting: false,
445
- results,
446
- error: void 0,
447
- executionTime
448
- });
449
- } catch (err) {
450
- if (executionIdRef.current !== thisExecution) return;
451
- const executionTime = Date.now() - startTime;
452
- let errorMessage = "Command execution failed";
453
- if (err instanceof Error) {
454
- errorMessage = err.message;
455
- } else if (typeof err === "string") {
456
- errorMessage = err;
457
- } else if (err && typeof err === "object" && "message" in err) {
458
- errorMessage = err.message;
459
- }
460
- console.error("Command execution failed:", errorMessage);
461
- if (!isMountedRef.current) return;
462
- setState((prev) => ({
463
- ...prev,
464
- isExecuting: false,
465
- error: errorMessage,
466
- executionTime
467
- }));
468
- }
469
- })();
470
- },
471
- []
472
- );
473
- useEffect5(() => {
474
- if (client && pendingRef.current) {
475
- const { rql, params, shapes } = pendingRef.current;
476
- command(rql, params, shapes);
32
+ // src/use-subscription.ts
33
+ function useSubscription(rql, params, shape, options = {}) {
34
+ const store = useStore();
35
+ const { config, enabled = true } = options;
36
+ const key = entryKey(rql, params, shape);
37
+ useEffect(() => {
38
+ if (!enabled) {
39
+ return;
477
40
  }
478
- }, [client, command]);
479
- const cancelCommand = useCallback2(() => {
480
- executionIdRef.current++;
481
- setState((prev) => ({
482
- ...prev,
483
- isExecuting: false,
484
- error: "Command cancelled"
485
- }));
486
- }, []);
487
- return {
488
- isExecuting: state.isExecuting,
489
- results: state.results,
490
- error: state.error,
491
- executionTime: state.executionTime,
492
- command,
493
- cancelCommand
494
- };
41
+ return store.subscribe(rql, params, shape, config);
42
+ }, [store, key, enabled]);
43
+ return useEntry(store, rql, params, shape);
495
44
  }
496
45
 
497
- // src/hooks/use-command.ts
498
- import { useEffect as useEffect6, useMemo as useMemo2 } from "react";
499
- function useCommandOne(rql, params, shape, options) {
500
- const {
501
- isExecuting,
502
- results,
503
- error,
504
- command
505
- } = useCommandExecutor(options);
506
- useEffect6(() => {
507
- const shapes = shape ? [shape] : void 0;
508
- command(rql, params, shapes);
509
- }, [rql, params, command]);
510
- const result = useMemo2(() => {
511
- return results && results.length > 0 ? results[0] : void 0;
512
- }, [results]);
513
- return { isExecuting, result, error };
514
- }
515
- function useCommandMany(rql, params, shapes, options) {
516
- const {
517
- isExecuting,
518
- results,
519
- error,
520
- command
521
- } = useCommandExecutor(options);
522
- useEffect6(() => {
523
- command(rql, params, shapes);
524
- }, [rql, params, command]);
525
- return { isExecuting, results, error };
526
- }
527
-
528
- // src/hooks/use-admin-executor.ts
529
- import { useState as useState4, useCallback as useCallback3, useRef as useRef4, useEffect as useEffect7 } from "react";
530
- function useAdminExecutor(options) {
531
- const { client } = useConnection(options?.connectionConfig);
532
- const [state, setState] = useState4({
533
- isExecuting: false,
534
- results: void 0,
535
- error: void 0,
536
- executionTime: void 0
537
- });
538
- const clientRef = useRef4(client);
539
- clientRef.current = client;
540
- const isMountedRef = useRef4(false);
541
- useEffect7(() => {
542
- isMountedRef.current = true;
543
- return () => {
544
- isMountedRef.current = false;
545
- };
546
- }, []);
547
- const executionIdRef = useRef4(0);
548
- const pendingRef = useRef4(null);
549
- const admin = useCallback3(
550
- (rql, params, shapes) => {
551
- const currentClient = clientRef.current;
552
- if (!currentClient) {
553
- pendingRef.current = { rql, params, shapes };
554
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
555
- return Promise.resolve();
556
- }
557
- pendingRef.current = null;
558
- const thisExecution = ++executionIdRef.current;
559
- setState((prev) => ({ ...prev, isExecuting: true, error: void 0 }));
560
- const startTime = Date.now();
561
- return (async () => {
562
- try {
563
- const frameResults = await currentClient.admin(rql, params || null, shapes || []) || [];
564
- if (executionIdRef.current !== thisExecution) return;
565
- const executionTime = Date.now() - startTime;
566
- const results = frameResults.map((frame) => {
567
- if (Array.isArray(frame) && frame.length > 0) {
568
- const firstRow = frame[0];
569
- let columns = [];
570
- const hasValueObjects = firstRow && typeof firstRow === "object" && Object.values(firstRow).some((v) => v && typeof v === "object" && "type" in v);
571
- if (hasValueObjects) {
572
- columns = Object.keys(firstRow).map((key) => {
573
- const value = firstRow[key];
574
- const dataType = value?.type || "Utf8";
575
- return {
576
- name: key,
577
- type: dataType,
578
- payload: []
579
- };
580
- });
581
- } else {
582
- columns = Object.keys(firstRow).map((key) => ({
583
- name: key,
584
- type: "Utf8",
585
- payload: []
586
- }));
587
- }
588
- return {
589
- columns,
590
- rows: frame,
591
- executionTimeMs: executionTime
592
- };
593
- } else {
594
- return {
595
- columns: [],
596
- rows: [],
597
- executionTimeMs: executionTime,
598
- rowsAffected: typeof frame === "number" ? frame : void 0
599
- };
600
- }
601
- });
602
- if (!isMountedRef.current) return;
603
- setState({
604
- isExecuting: false,
605
- results,
606
- error: void 0,
607
- executionTime
608
- });
609
- } catch (err) {
610
- if (executionIdRef.current !== thisExecution) return;
611
- const executionTime = Date.now() - startTime;
612
- let errorMessage = "Admin execution failed";
613
- if (err instanceof Error) {
614
- errorMessage = err.message;
615
- } else if (typeof err === "string") {
616
- errorMessage = err;
617
- } else if (err && typeof err === "object" && "message" in err) {
618
- errorMessage = err.message;
619
- }
620
- console.error("Admin execution failed:", errorMessage);
621
- if (!isMountedRef.current) return;
622
- setState((prev) => ({
623
- ...prev,
624
- isExecuting: false,
625
- error: errorMessage,
626
- executionTime
627
- }));
628
- }
629
- })();
630
- },
631
- []
632
- // stable — never recreates
633
- );
634
- useEffect7(() => {
635
- if (client && pendingRef.current) {
636
- const { rql, params, shapes } = pendingRef.current;
637
- admin(rql, params, shapes);
46
+ // src/use-query.ts
47
+ import { useEffect as useEffect2 } from "react";
48
+ import { entryKey as entryKey2 } from "@reifydb/store";
49
+ function useQuery(rql, params, shape, options = {}) {
50
+ const store = useStore();
51
+ const { enabled = true } = options;
52
+ const key = entryKey2(rql, params, shape);
53
+ useEffect2(() => {
54
+ if (enabled) {
55
+ store.query(rql, params, shape).catch(() => void 0);
638
56
  }
639
- }, [client, admin]);
640
- const cancelAdmin = useCallback3(() => {
641
- executionIdRef.current++;
642
- setState((prev) => ({
643
- ...prev,
644
- isExecuting: false,
645
- error: "Admin cancelled"
646
- }));
647
- }, []);
648
- return {
649
- isExecuting: state.isExecuting,
650
- results: state.results,
651
- error: state.error,
652
- executionTime: state.executionTime,
653
- admin,
654
- cancelAdmin
655
- };
57
+ }, [store, key, enabled]);
58
+ return useEntry(store, rql, params, shape);
656
59
  }
657
60
 
658
- // src/hooks/use-admin.ts
659
- import { useEffect as useEffect8, useMemo as useMemo3 } from "react";
660
- function useAdminOne(rql, params, shape, options) {
661
- const {
662
- isExecuting,
663
- results,
664
- error,
665
- admin
666
- } = useAdminExecutor(options);
667
- useEffect8(() => {
668
- const shapes = shape ? [shape] : void 0;
669
- admin(rql, params, shapes);
670
- }, [rql, params, admin]);
671
- const result = useMemo3(() => {
672
- return results && results.length > 0 ? results[0] : void 0;
673
- }, [results]);
674
- return { isExecuting, result, error };
675
- }
676
- function useAdminMany(rql, params, shapes, options) {
677
- const {
678
- isExecuting,
679
- results,
680
- error,
681
- admin
682
- } = useAdminExecutor(options);
683
- useEffect8(() => {
684
- admin(rql, params, shapes);
685
- }, [rql, params, admin]);
686
- return { isExecuting, results, error };
687
- }
688
-
689
- // src/hooks/use-subscription-executor.ts
690
- import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect9 } from "react";
691
- function useSubscriptionExecutor(options) {
692
- const { client } = useConnection(options?.connectionConfig);
693
- const [state, setState] = useState5({
694
- data: [],
695
- changes: [],
696
- isSubscribed: false,
697
- isSubscribing: false,
698
- error: void 0,
699
- subscriptionId: void 0
700
- });
701
- const clientRef = useRef5(client);
702
- const subscriptionIdRef = useRef5(void 0);
703
- const rqlRef = useRef5(void 0);
704
- const paramsRef = useRef5(void 0);
705
- const shapeRef = useRef5(void 0);
706
- const configRef = useRef5(void 0);
707
- useEffect9(() => {
708
- clientRef.current = client;
709
- }, [client]);
710
- const addChangeEvent = useCallback4((operation, rows) => {
711
- setState((prev) => {
712
- const newChange = {
713
- operation,
714
- rows,
715
- timestamp: Date.now()
716
- };
717
- const newChanges = [...prev.changes, newChange];
718
- return {
719
- ...prev,
720
- data: prev.data,
721
- changes: newChanges
722
- };
723
- });
724
- }, []);
725
- const handleInsert = useCallback4((rows) => {
726
- addChangeEvent("INSERT", rows);
727
- }, [addChangeEvent]);
728
- const handleUpdate = useCallback4((rows) => {
729
- addChangeEvent("UPDATE", rows);
730
- }, [addChangeEvent]);
731
- const handleRemove = useCallback4((rows) => {
732
- addChangeEvent("REMOVE", rows);
733
- }, [addChangeEvent]);
734
- const subscribe = useCallback4(async (rql, params, shape, config) => {
735
- const currentClient = clientRef.current;
736
- if (!currentClient) {
737
- setState((prev) => ({ ...prev, error: "Client not connected" }));
738
- return;
739
- }
740
- if (!("subscribe" in currentClient)) {
741
- setState((prev) => ({ ...prev, error: "Subscriptions require a WebSocket connection" }));
742
- return;
743
- }
744
- rqlRef.current = rql;
745
- paramsRef.current = params;
746
- shapeRef.current = shape;
747
- configRef.current = config;
748
- setState((prev) => ({
749
- ...prev,
750
- isSubscribing: true,
751
- error: void 0
752
- }));
61
+ // src/use-runner.ts
62
+ import { useCallback as useCallback2, useState } from "react";
63
+ function toError(error) {
64
+ return error instanceof Error ? error : new Error(String(error));
65
+ }
66
+ function useRunner(kind, shapes) {
67
+ const store = useStore();
68
+ const [pending, setPending] = useState(0);
69
+ const [error, setError] = useState(void 0);
70
+ const run = useCallback2(async (rql, params = null) => {
71
+ setPending((count) => count + 1);
753
72
  try {
754
- const subId = await currentClient.subscribe(rql, params, shape, {
755
- onInsert: handleInsert,
756
- onUpdate: handleUpdate,
757
- onRemove: handleRemove
758
- }, config);
759
- subscriptionIdRef.current = subId;
760
- setState((prev) => ({
761
- ...prev,
762
- isSubscribing: false,
763
- isSubscribed: true,
764
- subscriptionId: subId
765
- }));
766
- } catch (err) {
767
- setState((prev) => ({
768
- ...prev,
769
- isSubscribing: false,
770
- error: err.message || "Subscription failed"
771
- }));
73
+ const result = await store[kind](rql, params, shapes);
74
+ setError(void 0);
75
+ return result;
76
+ } catch (cause) {
77
+ setError(toError(cause));
78
+ throw cause;
79
+ } finally {
80
+ setPending((count) => count - 1);
772
81
  }
773
- }, [handleInsert, handleUpdate, handleRemove]);
774
- const unsubscribe = useCallback4(async () => {
775
- const currentClient = clientRef.current;
776
- if (!currentClient || !subscriptionIdRef.current) return;
777
- if (!("unsubscribe" in currentClient)) return;
778
- try {
779
- await currentClient.unsubscribe(subscriptionIdRef.current);
780
- subscriptionIdRef.current = void 0;
781
- rqlRef.current = void 0;
782
- paramsRef.current = void 0;
783
- shapeRef.current = void 0;
784
- configRef.current = void 0;
785
- setState((prev) => ({
786
- ...prev,
787
- isSubscribed: false,
788
- subscriptionId: void 0
789
- }));
790
- } catch (err) {
791
- setState((prev) => ({
792
- ...prev,
793
- error: err.message || "Unsubscribe failed"
794
- }));
795
- }
796
- }, []);
797
- const clearChanges = useCallback4(() => {
798
- setState((prev) => ({ ...prev, changes: [] }));
799
- }, []);
800
- const clearData = useCallback4(() => {
801
- setState((prev) => ({ ...prev, data: [] }));
802
- }, []);
803
- useEffect9(() => {
804
- return () => {
805
- if (subscriptionIdRef.current && clientRef.current && "unsubscribe" in clientRef.current) {
806
- clientRef.current.unsubscribe(subscriptionIdRef.current).catch(console.error);
807
- }
808
- };
809
- }, []);
810
- return {
811
- state,
812
- subscribe,
813
- unsubscribe,
814
- clearChanges,
815
- clearData
816
- };
82
+ }, [store, kind, shapes]);
83
+ return { run, isPending: pending > 0, error };
817
84
  }
818
85
 
819
- // src/hooks/use-subscription.ts
820
- import { useEffect as useEffect10, useMemo as useMemo4 } from "react";
821
- function useSubscription(rql, params, shape, options) {
822
- const {
823
- state,
824
- subscribe,
825
- unsubscribe
826
- } = useSubscriptionExecutor(options);
827
- const paramsKey = useMemo4(() => JSON.stringify(params), [params]);
828
- const configKey = useMemo4(() => JSON.stringify(options?.config), [options?.config]);
829
- useEffect10(() => {
830
- if (options?.enabled === false) return;
831
- subscribe(rql, params, shape, options?.config);
832
- return () => {
833
- unsubscribe();
834
- };
835
- }, [rql, paramsKey, shape, configKey, options?.enabled, subscribe, unsubscribe]);
836
- return {
837
- data: state.data,
838
- changes: state.changes,
839
- isSubscribed: state.isSubscribed,
840
- isSubscribing: state.isSubscribing,
841
- error: state.error,
842
- subscriptionId: state.subscriptionId
843
- };
86
+ // src/use-command.ts
87
+ function useCommand(shapes) {
88
+ return useRunner("command", shapes);
844
89
  }
845
90
 
846
- // src/hooks/use-catalog.ts
847
- import { useEffect as useEffect11, useState as useState6 } from "react";
848
- import { Shape } from "@reifydb/core";
849
- import { typeNameFromCode } from "@reifydb/client";
850
- var namespaceShape = Shape.object({
851
- id: Shape.number(),
852
- name: Shape.string()
853
- });
854
- var tableShape = Shape.object({
855
- id: Shape.number(),
856
- namespace_id: Shape.number(),
857
- name: Shape.string(),
858
- primary_key_id: Shape.number()
859
- });
860
- var viewShape = Shape.object({
861
- id: Shape.number(),
862
- namespace_id: Shape.number(),
863
- name: Shape.string()
864
- });
865
- var columnShape = Shape.object({
866
- id: Shape.number(),
867
- object_id: Shape.number(),
868
- object_type: Shape.number(),
869
- name: Shape.string(),
870
- type: Shape.number(),
871
- position: Shape.number(),
872
- auto_increment: Shape.boolean()
873
- });
874
- function toNumber(value) {
875
- if (value === void 0 || value === null) return void 0;
876
- const n = Number(value);
877
- return Number.isNaN(n) ? void 0 : n;
878
- }
879
- function useCatalog() {
880
- const { isExecuting, results, error, query } = useQueryExecutor();
881
- const [shape, setShape] = useState6([]);
882
- const [isLoading, setIsLoading] = useState6(true);
883
- useEffect11(() => {
884
- if (!query) return;
885
- const fetchShape = async () => {
886
- setIsLoading(true);
887
- try {
888
- await query(
889
- `OUTPUT FROM system::namespaces; OUTPUT FROM system::tables; OUTPUT FROM system::views; OUTPUT FROM system::columns;`,
890
- void 0,
891
- [namespaceShape, tableShape, viewShape, columnShape]
892
- );
893
- } catch (err) {
894
- console.error("Failed to fetch shape:", err);
895
- }
896
- };
897
- fetchShape();
898
- }, [query]);
899
- useEffect11(() => {
900
- if (!results || results.length < 4) {
901
- setIsLoading(isExecuting);
902
- return;
903
- }
904
- const tablesResult = results[1];
905
- const viewsResult = results[2];
906
- const columnsResult = results[3];
907
- if (!tablesResult?.rows || !viewsResult?.rows || !columnsResult?.rows) {
908
- setIsLoading(false);
909
- return;
910
- }
911
- const namespacesResult = results[0];
912
- const namespaces = namespacesResult.rows;
913
- const tables = tablesResult.rows;
914
- const views = viewsResult.rows;
915
- const columns = columnsResult.rows;
916
- const namespaceMap = /* @__PURE__ */ new Map();
917
- namespaces.forEach((ns) => {
918
- const id = toNumber(ns.id);
919
- const name = ns.name?.valueOf();
920
- if (id !== void 0 && name) {
921
- namespaceMap.set(id, name);
922
- }
923
- });
924
- const tableInfoMap = /* @__PURE__ */ new Map();
925
- tables.forEach((table) => {
926
- const tableId = toNumber(table.id);
927
- const namespaceId = toNumber(table.namespaceId);
928
- const tableName = table.name?.valueOf();
929
- if (tableId === void 0 || !tableName || namespaceId === void 0) return;
930
- const namespace = namespaceMap.get(namespaceId);
931
- if (!namespace) return;
932
- const fullTableName = `${namespace}::${tableName}`;
933
- tableInfoMap.set(tableId, {
934
- name: fullTableName,
935
- columns: []
936
- });
937
- });
938
- views.forEach((view) => {
939
- const viewId = toNumber(view.id);
940
- const namespaceId = toNumber(view.namespaceId);
941
- const viewName = view.name?.valueOf();
942
- if (viewId === void 0 || !viewName || namespaceId === void 0) return;
943
- const namespace = namespaceMap.get(namespaceId);
944
- if (!namespace) return;
945
- const fullViewName = `${namespace}::${viewName}`;
946
- tableInfoMap.set(viewId, {
947
- name: fullViewName,
948
- columns: []
949
- });
950
- });
951
- const tableColumnsMap = /* @__PURE__ */ new Map();
952
- columns.forEach((column) => {
953
- const objectId = toNumber(column.objectId);
954
- const objectType = toNumber(column.objectType);
955
- const columnName = column.name?.valueOf();
956
- const typeId = toNumber(column.type);
957
- const position = toNumber(column.position);
958
- if (objectId === void 0 || !columnName || typeId === void 0) return;
959
- if (objectType !== 0 && objectType !== 1) return;
960
- if (!tableColumnsMap.has(objectId)) {
961
- tableColumnsMap.set(objectId, []);
962
- }
963
- let dataType;
964
- try {
965
- dataType = typeNameFromCode(typeId);
966
- } catch {
967
- dataType = `Unknown(${typeId})`;
968
- }
969
- tableColumnsMap.get(objectId).push({
970
- name: columnName,
971
- dataType,
972
- position: position ?? 0
973
- });
974
- });
975
- tableColumnsMap.forEach((cols, objectId) => {
976
- const tableInfo = tableInfoMap.get(objectId);
977
- if (tableInfo) {
978
- cols.sort((a, b) => a.position - b.position);
979
- tableInfo.columns = cols.map((c) => ({ name: c.name, dataType: c.dataType }));
980
- }
981
- });
982
- const shapeArray = Array.from(tableInfoMap.values()).filter((table) => table.name !== "reifydb::flows").sort((a, b) => a.name.localeCompare(b.name));
983
- setShape(shapeArray);
984
- setIsLoading(false);
985
- }, [results, isExecuting]);
986
- return [isLoading, shape, error];
91
+ // src/use-admin.ts
92
+ function useAdmin(shapes) {
93
+ return useRunner("admin", shapes);
987
94
  }
988
95
  export {
989
- Connection,
990
- ConnectionContext,
991
- ConnectionProvider,
992
- DEFAULT_CONFIG,
993
- DEFAULT_URL,
994
- clearConnection,
995
- getConnection,
996
- useAdminExecutor,
997
- useAdminMany,
998
- useAdminOne,
999
- useCatalog,
1000
- useCommandExecutor,
1001
- useCommandMany,
1002
- useCommandOne,
1003
- useConnection,
1004
- useQueryExecutor,
1005
- useQueryMany,
1006
- useQueryOne,
1007
- useSubscription,
1008
- useSubscriptionExecutor
96
+ StoreProvider,
97
+ useAdmin,
98
+ useCommand,
99
+ useQuery,
100
+ useStore,
101
+ useSubscription
1009
102
  };
1010
103
  //# sourceMappingURL=index.js.map