@fusedio/widget-sdk 0.1.0 → 0.1.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/bridge.js +14 -9
- package/dist/define-catalog.js +4 -1
- package/dist/define-component.js +4 -1
- package/dist/form.js +18 -12
- package/dist/hooks/use-allowed-sources.js +12 -9
- package/dist/hooks/use-allowed-udf-names.js +11 -8
- package/dist/hooks/use-canvas-params.js +12 -9
- package/dist/hooks/use-duckdb-sql.js +63 -58
- package/dist/hooks/use-fused-param.js +34 -31
- package/dist/hooks/use-json-ui-edge-animation.js +6 -3
- package/dist/hooks/use-json-ui-log.js +23 -18
- package/dist/hooks/use-json-ui-udf-info.js +6 -3
- package/dist/hooks/use-param-substitution.js +25 -22
- package/dist/hooks/use-udf-output.js +33 -24
- package/dist/hooks/use-upload-access-check.js +9 -6
- package/dist/hooks/use-url-signing.js +22 -17
- package/dist/index.js +62 -19
- package/dist/protocol.js +8 -4
- package/dist/types.js +2 -1
- package/dist/utils/sql-placeholders.js +36 -22
- package/package.json +2 -3
package/dist/bridge.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonUiNodeOverrideContext = exports.FusedWidgetBridgeContext = void 0;
|
|
4
|
+
exports.useFusedWidgetBridge = useFusedWidgetBridge;
|
|
5
|
+
exports.useJsonUiNode = useJsonUiNode;
|
|
1
6
|
/**
|
|
2
7
|
* FusedWidgetBridge — the dependency-injection interface between SDK hooks
|
|
3
8
|
* and the host environment (workbench, test harness, mobile app, etc).
|
|
@@ -12,7 +17,7 @@
|
|
|
12
17
|
* - The catalog-template test harness uses in-memory Map storage.
|
|
13
18
|
* - Any future host (mobile, embedded) can implement their own.
|
|
14
19
|
*/
|
|
15
|
-
|
|
20
|
+
const react_1 = require("react");
|
|
16
21
|
// ============================================================================
|
|
17
22
|
// React context
|
|
18
23
|
// ============================================================================
|
|
@@ -21,14 +26,14 @@ import { createContext, useContext } from "react";
|
|
|
21
26
|
* Provided by the workbench's `<JsonUiProvider>` and by the test harness.
|
|
22
27
|
* Catalog authors never interact with this directly — use the hooks.
|
|
23
28
|
*/
|
|
24
|
-
|
|
25
|
-
FusedWidgetBridgeContext.displayName = "FusedWidgetBridgeContext";
|
|
29
|
+
exports.FusedWidgetBridgeContext = (0, react_1.createContext)(null);
|
|
30
|
+
exports.FusedWidgetBridgeContext.displayName = "FusedWidgetBridgeContext";
|
|
26
31
|
/**
|
|
27
32
|
* Internal helper used by all SDK hooks. Throws if used outside a
|
|
28
33
|
* `<FusedWidgetBridgeContext.Provider>` so misconfiguration fails loudly.
|
|
29
34
|
*/
|
|
30
|
-
|
|
31
|
-
const bridge = useContext(FusedWidgetBridgeContext);
|
|
35
|
+
function useFusedWidgetBridge() {
|
|
36
|
+
const bridge = (0, react_1.useContext)(exports.FusedWidgetBridgeContext);
|
|
32
37
|
if (!bridge) {
|
|
33
38
|
throw new Error("useFusedWidgetBridge: no FusedWidgetBridgeContext provider in the tree. " +
|
|
34
39
|
"Wrap your components with the workbench's <JsonUiProvider> or the " +
|
|
@@ -36,16 +41,16 @@ export function useFusedWidgetBridge() {
|
|
|
36
41
|
}
|
|
37
42
|
return bridge;
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
JsonUiNodeOverrideContext.displayName = "JsonUiNodeOverrideContext";
|
|
44
|
+
exports.JsonUiNodeOverrideContext = (0, react_1.createContext)(null);
|
|
45
|
+
exports.JsonUiNodeOverrideContext.displayName = "JsonUiNodeOverrideContext";
|
|
41
46
|
/**
|
|
42
47
|
* Internal helper that resolves the effective node identity for SDK hooks.
|
|
43
48
|
* Reads `JsonUiNodeOverrideContext` first (per-subtree override), falls back
|
|
44
49
|
* to `bridge.node` (per-provider identity).
|
|
45
50
|
*/
|
|
46
|
-
|
|
51
|
+
function useJsonUiNode() {
|
|
47
52
|
const bridge = useFusedWidgetBridge();
|
|
48
|
-
const override = useContext(JsonUiNodeOverrideContext);
|
|
53
|
+
const override = (0, react_1.useContext)(exports.JsonUiNodeOverrideContext);
|
|
49
54
|
if (override) {
|
|
50
55
|
return {
|
|
51
56
|
udfUniqueId: override.udfUniqueId ?? bridge.node.udfUniqueId,
|
package/dist/define-catalog.js
CHANGED
package/dist/define-component.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineComponent = defineComponent;
|
|
1
4
|
/**
|
|
2
5
|
* Typed helper for registering a catalog component.
|
|
3
6
|
*
|
|
@@ -38,6 +41,6 @@
|
|
|
38
41
|
* }),
|
|
39
42
|
* };
|
|
40
43
|
*/
|
|
41
|
-
|
|
44
|
+
function defineComponent(def) {
|
|
42
45
|
return def;
|
|
43
46
|
}
|
package/dist/form.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FormContext = void 0;
|
|
4
|
+
exports.createFormParamsStore = createFormParamsStore;
|
|
5
|
+
exports.useFormContext = useFormContext;
|
|
6
|
+
exports.useFormParams = useFormParams;
|
|
1
7
|
/**
|
|
2
8
|
* Form-scoped parameter context.
|
|
3
9
|
*
|
|
@@ -10,9 +16,9 @@
|
|
|
10
16
|
* component and the catalog-template test harness both call
|
|
11
17
|
* `createFormParamsStore()` and provide it via `FormContext`.
|
|
12
18
|
*/
|
|
13
|
-
|
|
19
|
+
const react_1 = require("react");
|
|
14
20
|
/** Create a fresh form params store. Called once per Form component instance. */
|
|
15
|
-
|
|
21
|
+
function createFormParamsStore() {
|
|
16
22
|
const values = new Map();
|
|
17
23
|
const subscribers = new Set();
|
|
18
24
|
const notify = (name) => {
|
|
@@ -65,14 +71,14 @@ export function createFormParamsStore() {
|
|
|
65
71
|
* Provides form-scoped values to all descendants. Provided by the workbench's
|
|
66
72
|
* built-in Form component and by the catalog-template test harness.
|
|
67
73
|
*/
|
|
68
|
-
|
|
74
|
+
exports.FormContext = (0, react_1.createContext)({
|
|
69
75
|
store: null,
|
|
70
76
|
isInForm: false,
|
|
71
77
|
});
|
|
72
|
-
FormContext.displayName = "JsonUiFormContext";
|
|
78
|
+
exports.FormContext.displayName = "JsonUiFormContext";
|
|
73
79
|
/** Read the form context — `{ store, isInForm }`. */
|
|
74
|
-
|
|
75
|
-
return useContext(FormContext);
|
|
80
|
+
function useFormContext() {
|
|
81
|
+
return (0, react_1.useContext)(exports.FormContext);
|
|
76
82
|
}
|
|
77
83
|
// ============================================================================
|
|
78
84
|
// Hook: read form-scoped params
|
|
@@ -89,16 +95,16 @@ const EMPTY_PARAMS = Object.freeze({});
|
|
|
89
95
|
* const { inForm, values } = useFormParams(["city", "country"]);
|
|
90
96
|
* if (inForm && values.city) console.log("city:", values.city);
|
|
91
97
|
*/
|
|
92
|
-
|
|
98
|
+
function useFormParams(names) {
|
|
93
99
|
const { store, isInForm } = useFormContext();
|
|
94
100
|
const stableNames = useStableStringArray(names);
|
|
95
|
-
const subscribe = useCallback((cb) => {
|
|
101
|
+
const subscribe = (0, react_1.useCallback)((cb) => {
|
|
96
102
|
if (!store)
|
|
97
103
|
return () => { };
|
|
98
104
|
return store.subscribe(stableNames, cb);
|
|
99
105
|
}, [store, stableNames]);
|
|
100
|
-
const snapshotRef = useRef(EMPTY_PARAMS);
|
|
101
|
-
const getSnapshot = useCallback(() => {
|
|
106
|
+
const snapshotRef = (0, react_1.useRef)(EMPTY_PARAMS);
|
|
107
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
102
108
|
if (!store)
|
|
103
109
|
return EMPTY_PARAMS;
|
|
104
110
|
const next = store.getSnapshot(stableNames);
|
|
@@ -108,7 +114,7 @@ export function useFormParams(names) {
|
|
|
108
114
|
snapshotRef.current = next;
|
|
109
115
|
return next;
|
|
110
116
|
}, [store, stableNames]);
|
|
111
|
-
const values = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
117
|
+
const values = (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
112
118
|
return { inForm: isInForm, values };
|
|
113
119
|
}
|
|
114
120
|
function shallowEqualRecords(a, b) {
|
|
@@ -126,7 +132,7 @@ function shallowEqualRecords(a, b) {
|
|
|
126
132
|
}
|
|
127
133
|
/** Return a stable array reference when the string contents don't change. */
|
|
128
134
|
function useStableStringArray(names) {
|
|
129
|
-
const ref = useRef(names);
|
|
135
|
+
const ref = (0, react_1.useRef)(names);
|
|
130
136
|
const prev = ref.current;
|
|
131
137
|
if (prev !== names &&
|
|
132
138
|
(prev.length !== names.length || prev.some((n, i) => n !== names[i]))) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAllowedSources = useAllowedSources;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const bridge_1 = require("../bridge");
|
|
3
6
|
/**
|
|
4
7
|
* Returns the set of UDFs allowed to broadcast params to the current node,
|
|
5
8
|
* computed from canvas edges.
|
|
@@ -15,11 +18,11 @@ import { useFusedWidgetBridge } from "../bridge";
|
|
|
15
18
|
* const { allowedSources, isAllowedSource } = useAllowedSources();
|
|
16
19
|
* console.log("allowed:", allowedSources?.map(s => s.udfName));
|
|
17
20
|
*/
|
|
18
|
-
|
|
19
|
-
const bridge = useFusedWidgetBridge();
|
|
20
|
-
const subscribe = useCallback((cb) => bridge.routing.subscribeAllowedSources(cb), [bridge]);
|
|
21
|
-
const snapshotRef = useRef(null);
|
|
22
|
-
const getSnapshot = useCallback(() => {
|
|
21
|
+
function useAllowedSources() {
|
|
22
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
23
|
+
const subscribe = (0, react_1.useCallback)((cb) => bridge.routing.subscribeAllowedSources(cb), [bridge]);
|
|
24
|
+
const snapshotRef = (0, react_1.useRef)(null);
|
|
25
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
23
26
|
const next = bridge.routing.getAllowedSources();
|
|
24
27
|
const prev = snapshotRef.current;
|
|
25
28
|
if (allowedSourcesEqual(prev, next))
|
|
@@ -27,8 +30,8 @@ export function useAllowedSources() {
|
|
|
27
30
|
snapshotRef.current = next;
|
|
28
31
|
return next;
|
|
29
32
|
}, [bridge]);
|
|
30
|
-
const allowedSources = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
31
|
-
const isAllowedSource = useCallback((originUdfId, originUdfName) => {
|
|
33
|
+
const allowedSources = (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
34
|
+
const isAllowedSource = (0, react_1.useCallback)((originUdfId, originUdfName) => {
|
|
32
35
|
if (!allowedSources || allowedSources.length === 0)
|
|
33
36
|
return true;
|
|
34
37
|
if (!originUdfId && !originUdfName)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAllowedUdfNames = useAllowedUdfNames;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const bridge_1 = require("../bridge");
|
|
3
6
|
/**
|
|
4
7
|
* Returns the set of UDF names this node may reference (computed from
|
|
5
8
|
* incoming edges). Returns `null` when no filtering applies (e.g. node has
|
|
@@ -14,11 +17,11 @@ import { useFusedWidgetBridge } from "../bridge";
|
|
|
14
17
|
* return <div>UDF "{udfName}" is not reachable from this node.</div>;
|
|
15
18
|
* }
|
|
16
19
|
*/
|
|
17
|
-
|
|
18
|
-
const bridge = useFusedWidgetBridge();
|
|
19
|
-
const subscribe = useCallback((cb) => bridge.routing.subscribeAllowedSources(cb), [bridge]);
|
|
20
|
-
const snapshotRef = useRef(null);
|
|
21
|
-
const getSnapshot = useCallback(() => {
|
|
20
|
+
function useAllowedUdfNames() {
|
|
21
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
22
|
+
const subscribe = (0, react_1.useCallback)((cb) => bridge.routing.subscribeAllowedSources(cb), [bridge]);
|
|
23
|
+
const snapshotRef = (0, react_1.useRef)(null);
|
|
24
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
22
25
|
const next = bridge.routing.getAllowedUdfNames();
|
|
23
26
|
const prev = snapshotRef.current;
|
|
24
27
|
if (setsEqual(prev, next))
|
|
@@ -26,7 +29,7 @@ export function useAllowedUdfNames() {
|
|
|
26
29
|
snapshotRef.current = next;
|
|
27
30
|
return next;
|
|
28
31
|
}, [bridge]);
|
|
29
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
32
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
30
33
|
}
|
|
31
34
|
function setsEqual(a, b) {
|
|
32
35
|
if (a === b)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCanvasParams = useCanvasParams;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const bridge_1 = require("../bridge");
|
|
3
6
|
const EMPTY_RECORD = Object.freeze({});
|
|
4
7
|
/**
|
|
5
8
|
* Read filtered canvas parameter values for multiple param names at once.
|
|
@@ -13,16 +16,16 @@ const EMPTY_RECORD = Object.freeze({});
|
|
|
13
16
|
* const { city, year } = useCanvasParams(["city", "year"]);
|
|
14
17
|
* if (city) console.log("city is", city);
|
|
15
18
|
*/
|
|
16
|
-
|
|
17
|
-
const bridge = useFusedWidgetBridge();
|
|
19
|
+
function useCanvasParams(paramNames) {
|
|
20
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
18
21
|
const stableNames = useStableStringArray(paramNames);
|
|
19
|
-
const subscribe = useCallback((cb) => {
|
|
22
|
+
const subscribe = (0, react_1.useCallback)((cb) => {
|
|
20
23
|
if (stableNames.length === 0)
|
|
21
24
|
return () => { };
|
|
22
25
|
return bridge.params.subscribeMany(stableNames, cb);
|
|
23
26
|
}, [bridge, stableNames]);
|
|
24
|
-
const snapshotRef = useRef(EMPTY_RECORD);
|
|
25
|
-
const getSnapshot = useCallback(() => {
|
|
27
|
+
const snapshotRef = (0, react_1.useRef)(EMPTY_RECORD);
|
|
28
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
26
29
|
if (stableNames.length === 0)
|
|
27
30
|
return EMPTY_RECORD;
|
|
28
31
|
const next = bridge.params.getSnapshotMany(stableNames);
|
|
@@ -32,7 +35,7 @@ export function useCanvasParams(paramNames) {
|
|
|
32
35
|
snapshotRef.current = next;
|
|
33
36
|
return next;
|
|
34
37
|
}, [bridge, stableNames]);
|
|
35
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
38
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
36
39
|
}
|
|
37
40
|
function shallowEqualRecords(a, b) {
|
|
38
41
|
if (a === b)
|
|
@@ -48,7 +51,7 @@ function shallowEqualRecords(a, b) {
|
|
|
48
51
|
return true;
|
|
49
52
|
}
|
|
50
53
|
function useStableStringArray(names) {
|
|
51
|
-
const ref = useRef(names);
|
|
54
|
+
const ref = (0, react_1.useRef)(names);
|
|
52
55
|
const prev = ref.current;
|
|
53
56
|
if (prev !== names &&
|
|
54
57
|
(prev.length !== names.length || prev.some((n, i) => n !== names[i]))) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useDuckDbSqlQueryPreprocessing = useDuckDbSqlQueryPreprocessing;
|
|
4
|
+
exports.useDuckDbSqlQuery = useDuckDbSqlQuery;
|
|
5
|
+
exports.useVfsRegistration = useVfsRegistration;
|
|
1
6
|
/**
|
|
2
7
|
* DuckDB SQL execution against UDF Parquet outputs.
|
|
3
8
|
*
|
|
@@ -13,13 +18,13 @@
|
|
|
13
18
|
* the host side via the bridge. Both hooks work identically in the
|
|
14
19
|
* workbench, the catalog-template test harness, or any other host.
|
|
15
20
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
const bridge_1 = require("../bridge");
|
|
23
|
+
const form_1 = require("../form");
|
|
24
|
+
const use_json_ui_log_1 = require("./use-json-ui-log");
|
|
25
|
+
const use_json_ui_edge_animation_1 = require("./use-json-ui-edge-animation");
|
|
26
|
+
const use_canvas_params_1 = require("./use-canvas-params");
|
|
27
|
+
const sql_placeholders_1 = require("../utils/sql-placeholders");
|
|
23
28
|
const DEFAULT_MAX_ROWS = 500;
|
|
24
29
|
const EMPTY_ROWS = Object.freeze([]);
|
|
25
30
|
const EMPTY_COLUMNS = Object.freeze([]);
|
|
@@ -63,53 +68,53 @@ function buildProcessedSql(sql, resolved, fileNameMap, sourceOverrides, sqlParam
|
|
|
63
68
|
processedSql.slice(raw.end);
|
|
64
69
|
}
|
|
65
70
|
processedSql = appendLimitIfMissing(processedSql, maxRows);
|
|
66
|
-
return substituteSqlParams(processedSql, sqlParamValues);
|
|
71
|
+
return (0, sql_placeholders_1.substituteSqlParams)(processedSql, sqlParamValues);
|
|
67
72
|
}
|
|
68
73
|
/**
|
|
69
74
|
* Preprocess SQL: resolve placeholders, register UDFs via the bridge,
|
|
70
75
|
* substitute params, sign URLs, append LIMIT. Returns the prepared SQL.
|
|
71
76
|
*/
|
|
72
|
-
|
|
73
|
-
const bridge = useFusedWidgetBridge();
|
|
74
|
-
const { startLoading: startEdgeLoading, stopLoading: stopEdgeLoading } = useJsonUiEdgeAnimation();
|
|
75
|
-
const { log } = useJsonUiLog();
|
|
76
|
-
const [processedSql, setProcessedSql] = useState("");
|
|
77
|
-
const [loading, setLoading] = useState(false);
|
|
78
|
-
const [error, setError] = useState(null);
|
|
79
|
-
const [fetchKey, setFetchKey] = useState(0);
|
|
80
|
-
const sourcePlaceholders = useMemo(() => {
|
|
77
|
+
function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows = DEFAULT_MAX_ROWS, sourceOverrides, }) {
|
|
78
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
79
|
+
const { startLoading: startEdgeLoading, stopLoading: stopEdgeLoading } = (0, use_json_ui_edge_animation_1.useJsonUiEdgeAnimation)();
|
|
80
|
+
const { log } = (0, use_json_ui_log_1.useJsonUiLog)();
|
|
81
|
+
const [processedSql, setProcessedSql] = (0, react_1.useState)("");
|
|
82
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
83
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
84
|
+
const [fetchKey, setFetchKey] = (0, react_1.useState)(0);
|
|
85
|
+
const sourcePlaceholders = (0, react_1.useMemo)(() => {
|
|
81
86
|
if (!sql)
|
|
82
87
|
return [];
|
|
83
|
-
return parseSqlUdfPlaceholders(sql);
|
|
88
|
+
return (0, sql_placeholders_1.parseSqlUdfPlaceholders)(sql);
|
|
84
89
|
}, [sql]);
|
|
85
90
|
// Skip placeholders that have a sourceOverride active (workbench-only path).
|
|
86
|
-
const placeholdersAfterOverride = useMemo(() => {
|
|
91
|
+
const placeholdersAfterOverride = (0, react_1.useMemo)(() => {
|
|
87
92
|
if (!sourceOverrides)
|
|
88
93
|
return sourcePlaceholders;
|
|
89
94
|
return sourcePlaceholders.filter((p) => p.overrides !== null || !sourceOverrides[p.name]);
|
|
90
95
|
}, [sourcePlaceholders, sourceOverrides]);
|
|
91
96
|
// Override values may reference $params — collect those names so we can
|
|
92
97
|
// subscribe to the same canvas/form param map the SQL body uses.
|
|
93
|
-
const overrideParamNames = useMemo(() => {
|
|
98
|
+
const overrideParamNames = (0, react_1.useMemo)(() => {
|
|
94
99
|
const set = new Set();
|
|
95
100
|
for (const p of placeholdersAfterOverride) {
|
|
96
101
|
if (!p.overrides)
|
|
97
102
|
continue;
|
|
98
103
|
for (const v of Object.values(p.overrides)) {
|
|
99
|
-
const name = getDollarRefName(v);
|
|
104
|
+
const name = (0, sql_placeholders_1.getDollarRefName)(v);
|
|
100
105
|
if (name)
|
|
101
106
|
set.add(name);
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
return Array.from(set);
|
|
105
110
|
}, [placeholdersAfterOverride]);
|
|
106
|
-
const sqlParamNames = useMemo(() => {
|
|
111
|
+
const sqlParamNames = (0, react_1.useMemo)(() => {
|
|
107
112
|
if (!sql)
|
|
108
113
|
return [];
|
|
109
|
-
return extractSqlParams(sql);
|
|
114
|
+
return (0, sql_placeholders_1.extractSqlParams)(sql);
|
|
110
115
|
}, [sql]);
|
|
111
116
|
// Subscribe to canvas + form params for both the SQL body and override refs.
|
|
112
|
-
const allParamNames = useMemo(() => {
|
|
117
|
+
const allParamNames = (0, react_1.useMemo)(() => {
|
|
113
118
|
const seen = new Set();
|
|
114
119
|
const out = [];
|
|
115
120
|
for (const n of sqlParamNames) {
|
|
@@ -126,10 +131,10 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
126
131
|
}
|
|
127
132
|
return out;
|
|
128
133
|
}, [sqlParamNames, overrideParamNames]);
|
|
129
|
-
const canvasParamValues = useCanvasParams(allParamNames);
|
|
130
|
-
const { inForm, values: formParamValues } = useFormParams(allParamNames);
|
|
131
|
-
const sqlParamValues = useMemo(() => inForm ? { ...canvasParamValues, ...formParamValues } : canvasParamValues, [inForm, canvasParamValues, formParamValues]);
|
|
132
|
-
const resolvedPlaceholders = useMemo(() => {
|
|
134
|
+
const canvasParamValues = (0, use_canvas_params_1.useCanvasParams)(allParamNames);
|
|
135
|
+
const { inForm, values: formParamValues } = (0, form_1.useFormParams)(allParamNames);
|
|
136
|
+
const sqlParamValues = (0, react_1.useMemo)(() => inForm ? { ...canvasParamValues, ...formParamValues } : canvasParamValues, [inForm, canvasParamValues, formParamValues]);
|
|
137
|
+
const resolvedPlaceholders = (0, react_1.useMemo)(() => {
|
|
133
138
|
return placeholdersAfterOverride.map((p) => {
|
|
134
139
|
if (!p.overrides) {
|
|
135
140
|
return {
|
|
@@ -142,21 +147,21 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
142
147
|
const resolvedOverrides = {};
|
|
143
148
|
let unresolved = false;
|
|
144
149
|
for (const [paramKey, rawValue] of Object.entries(p.overrides)) {
|
|
145
|
-
const r = resolveOverrideValue(rawValue, sqlParamValues);
|
|
150
|
+
const r = (0, sql_placeholders_1.resolveOverrideValue)(rawValue, sqlParamValues);
|
|
146
151
|
if (r.unresolved)
|
|
147
152
|
unresolved = true;
|
|
148
153
|
resolvedOverrides[paramKey] = r.value;
|
|
149
154
|
}
|
|
150
155
|
return {
|
|
151
156
|
raw: p,
|
|
152
|
-
key: computePlaceholderKey(p.name, resolvedOverrides),
|
|
157
|
+
key: (0, sql_placeholders_1.computePlaceholderKey)(p.name, resolvedOverrides),
|
|
153
158
|
resolvedOverrides,
|
|
154
159
|
unresolved,
|
|
155
160
|
};
|
|
156
161
|
});
|
|
157
162
|
}, [placeholdersAfterOverride, sqlParamValues]);
|
|
158
163
|
// Deduplicate refs we need to pass to the bridge for VFS registration.
|
|
159
|
-
const vfsRefs = useMemo(() => {
|
|
164
|
+
const vfsRefs = (0, react_1.useMemo)(() => {
|
|
160
165
|
const seen = new Set();
|
|
161
166
|
const out = [];
|
|
162
167
|
for (const rp of resolvedPlaceholders) {
|
|
@@ -175,7 +180,7 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
175
180
|
}, [resolvedPlaceholders]);
|
|
176
181
|
// Stable key for the refs list — avoids re-running the resolve effect when
|
|
177
182
|
// the underlying refs are structurally identical.
|
|
178
|
-
const refsKey = useMemo(() => {
|
|
183
|
+
const refsKey = (0, react_1.useMemo)(() => {
|
|
179
184
|
return vfsRefs
|
|
180
185
|
.map((r) => `${r.key}|${r.name}|${r.overrides
|
|
181
186
|
? Object.entries(r.overrides)
|
|
@@ -185,12 +190,12 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
185
190
|
: ""}`)
|
|
186
191
|
.join("\n");
|
|
187
192
|
}, [vfsRefs]);
|
|
188
|
-
const refetch = useCallback(() => {
|
|
193
|
+
const refetch = (0, react_1.useCallback)(() => {
|
|
189
194
|
setFetchKey((k) => k + 1);
|
|
190
195
|
}, []);
|
|
191
196
|
// Subscribe to UDF output changes for every referenced UDF — `resolveVfsFilenames`
|
|
192
197
|
// doesn't auto-rerun when a UDF re-executes, so we trigger a refetch.
|
|
193
|
-
useEffect(() => {
|
|
198
|
+
(0, react_1.useEffect)(() => {
|
|
194
199
|
if (!enabled || vfsRefs.length === 0)
|
|
195
200
|
return;
|
|
196
201
|
const unsubs = vfsRefs.map((ref) => bridge.udfs.subscribeOutput(ref.name, () => {
|
|
@@ -201,14 +206,14 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
201
206
|
};
|
|
202
207
|
}, [bridge, enabled, vfsRefs]);
|
|
203
208
|
// Edge animation mirrors loading.
|
|
204
|
-
useEffect(() => {
|
|
209
|
+
(0, react_1.useEffect)(() => {
|
|
205
210
|
if (loading)
|
|
206
211
|
startEdgeLoading();
|
|
207
212
|
else
|
|
208
213
|
stopEdgeLoading();
|
|
209
214
|
}, [loading, startEdgeLoading, stopEdgeLoading]);
|
|
210
215
|
// Watch for sourceOverride errors / loading so the preprocessing reflects them.
|
|
211
|
-
const sourceOverrideError = useMemo(() => {
|
|
216
|
+
const sourceOverrideError = (0, react_1.useMemo)(() => {
|
|
212
217
|
if (!sourceOverrides)
|
|
213
218
|
return null;
|
|
214
219
|
for (const p of sourcePlaceholders) {
|
|
@@ -220,13 +225,13 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
220
225
|
}
|
|
221
226
|
return null;
|
|
222
227
|
}, [sourcePlaceholders, sourceOverrides]);
|
|
223
|
-
const sourceOverrideLoading = useMemo(() => {
|
|
228
|
+
const sourceOverrideLoading = (0, react_1.useMemo)(() => {
|
|
224
229
|
if (!sourceOverrides)
|
|
225
230
|
return false;
|
|
226
231
|
return sourcePlaceholders.some((p) => p.overrides === null && sourceOverrides[p.name]?.loading);
|
|
227
232
|
}, [sourcePlaceholders, sourceOverrides]);
|
|
228
233
|
const hasUnresolvedOverride = resolvedPlaceholders.some((rp) => rp.unresolved);
|
|
229
|
-
useEffect(() => {
|
|
234
|
+
(0, react_1.useEffect)(() => {
|
|
230
235
|
if (!enabled || !sql) {
|
|
231
236
|
setProcessedSql("");
|
|
232
237
|
setLoading(false);
|
|
@@ -338,7 +343,7 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
338
343
|
log(`SQL preprocessing failed: ${msg}`, "error");
|
|
339
344
|
return;
|
|
340
345
|
}
|
|
341
|
-
const urls = extractSignableUrls(nextProcessedSql);
|
|
346
|
+
const urls = (0, sql_placeholders_1.extractSignableUrls)(nextProcessedSql);
|
|
342
347
|
if (urls.length === 0) {
|
|
343
348
|
if (cancelled)
|
|
344
349
|
return;
|
|
@@ -356,7 +361,7 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
356
361
|
urls.forEach((u, i) => {
|
|
357
362
|
signedMap[u] = signed[i].signed;
|
|
358
363
|
});
|
|
359
|
-
const signedSql = rewriteSignedUrls(nextProcessedSql, signedMap);
|
|
364
|
+
const signedSql = (0, sql_placeholders_1.rewriteSignedUrls)(nextProcessedSql, signedMap);
|
|
360
365
|
setProcessedSql(signedSql);
|
|
361
366
|
setError(null);
|
|
362
367
|
setLoading(false);
|
|
@@ -402,15 +407,15 @@ export function useDuckDbSqlQueryPreprocessing({ sql, enabled = true, maxRows =
|
|
|
402
407
|
* `useDuckDbSqlQueryPreprocessing` to prepare the SQL string, then runs
|
|
403
408
|
* it via the bridge.
|
|
404
409
|
*/
|
|
405
|
-
|
|
406
|
-
const bridge = useFusedWidgetBridge();
|
|
407
|
-
const { startLoading: startEdgeLoading, stopLoading: stopEdgeLoading } = useJsonUiEdgeAnimation();
|
|
408
|
-
const { log } = useJsonUiLog();
|
|
409
|
-
const [rows, setRows] = useState(EMPTY_ROWS);
|
|
410
|
-
const [columns, setColumns] = useState(EMPTY_COLUMNS);
|
|
411
|
-
const [queryLoading, setQueryLoading] = useState(false);
|
|
412
|
-
const [error, setError] = useState(null);
|
|
413
|
-
const [fetchKey, setFetchKey] = useState(0);
|
|
410
|
+
function useDuckDbSqlQuery({ sql, enabled = true, maxRows = DEFAULT_MAX_ROWS, sourceOverrides, }) {
|
|
411
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
412
|
+
const { startLoading: startEdgeLoading, stopLoading: stopEdgeLoading } = (0, use_json_ui_edge_animation_1.useJsonUiEdgeAnimation)();
|
|
413
|
+
const { log } = (0, use_json_ui_log_1.useJsonUiLog)();
|
|
414
|
+
const [rows, setRows] = (0, react_1.useState)(EMPTY_ROWS);
|
|
415
|
+
const [columns, setColumns] = (0, react_1.useState)(EMPTY_COLUMNS);
|
|
416
|
+
const [queryLoading, setQueryLoading] = (0, react_1.useState)(false);
|
|
417
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
418
|
+
const [fetchKey, setFetchKey] = (0, react_1.useState)(0);
|
|
414
419
|
const { processedSql, loading: preprocessingLoading, error: preprocessingError, refetch: refetchPreprocessing, } = useDuckDbSqlQueryPreprocessing({
|
|
415
420
|
sql,
|
|
416
421
|
enabled,
|
|
@@ -419,7 +424,7 @@ export function useDuckDbSqlQuery({ sql, enabled = true, maxRows = DEFAULT_MAX_R
|
|
|
419
424
|
});
|
|
420
425
|
// Bridges the single-frame gap between preprocessing completing and the
|
|
421
426
|
// query effect starting — keeps `loading` true through that frame.
|
|
422
|
-
const consumedSqlRef = useRef("");
|
|
427
|
+
const consumedSqlRef = (0, react_1.useRef)("");
|
|
423
428
|
const awaitingExecution = enabled &&
|
|
424
429
|
!!sql &&
|
|
425
430
|
!!processedSql &&
|
|
@@ -427,18 +432,18 @@ export function useDuckDbSqlQuery({ sql, enabled = true, maxRows = DEFAULT_MAX_R
|
|
|
427
432
|
!preprocessingError &&
|
|
428
433
|
processedSql !== consumedSqlRef.current;
|
|
429
434
|
const loading = preprocessingLoading || queryLoading || awaitingExecution;
|
|
430
|
-
useEffect(() => {
|
|
435
|
+
(0, react_1.useEffect)(() => {
|
|
431
436
|
if (loading)
|
|
432
437
|
startEdgeLoading();
|
|
433
438
|
else
|
|
434
439
|
stopEdgeLoading();
|
|
435
440
|
}, [loading, startEdgeLoading, stopEdgeLoading]);
|
|
436
|
-
const refetch = useCallback(() => {
|
|
441
|
+
const refetch = (0, react_1.useCallback)(() => {
|
|
437
442
|
consumedSqlRef.current = "";
|
|
438
443
|
refetchPreprocessing();
|
|
439
444
|
setFetchKey((k) => k + 1);
|
|
440
445
|
}, [refetchPreprocessing]);
|
|
441
|
-
useEffect(() => {
|
|
446
|
+
(0, react_1.useEffect)(() => {
|
|
442
447
|
if (!enabled || !sql) {
|
|
443
448
|
consumedSqlRef.current = "";
|
|
444
449
|
setRows(EMPTY_ROWS);
|
|
@@ -524,11 +529,11 @@ export function useDuckDbSqlQuery({ sql, enabled = true, maxRows = DEFAULT_MAX_R
|
|
|
524
529
|
* Resolve UDF names to VFS filenames, registering them in DuckDB if needed.
|
|
525
530
|
* Exposed for advanced use cases (e.g. building your own query string).
|
|
526
531
|
*/
|
|
527
|
-
|
|
528
|
-
const bridge = useFusedWidgetBridge();
|
|
529
|
-
const [state, setState] = useState({ filenames: new Map(), loading: false });
|
|
530
|
-
const namesKey = useMemo(() => udfNames.slice().sort().join("|"), [udfNames]);
|
|
531
|
-
useEffect(() => {
|
|
532
|
+
function useVfsRegistration(udfNames, enabled = true) {
|
|
533
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
534
|
+
const [state, setState] = (0, react_1.useState)({ filenames: new Map(), loading: false });
|
|
535
|
+
const namesKey = (0, react_1.useMemo)(() => udfNames.slice().sort().join("|"), [udfNames]);
|
|
536
|
+
(0, react_1.useEffect)(() => {
|
|
532
537
|
if (!enabled || udfNames.length === 0) {
|
|
533
538
|
setState({ filenames: new Map(), loading: false });
|
|
534
539
|
return;
|