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