@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
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFusedParam = useFusedParam;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const bridge_1 = require("../bridge");
|
|
6
|
+
const form_1 = require("../form");
|
|
7
|
+
const protocol_1 = require("../protocol");
|
|
5
8
|
/**
|
|
6
9
|
* Two-way bind a component to a named canvas parameter.
|
|
7
10
|
*
|
|
@@ -40,9 +43,9 @@ import { ParameterMessageType } from "../protocol";
|
|
|
40
43
|
* const { clearValue } = useFusedParam({ param: "selection", defaultValue: null });
|
|
41
44
|
* <button onClick={() => clearValue(null)}>Reset</button>
|
|
42
45
|
*/
|
|
43
|
-
|
|
44
|
-
const bridge = useFusedWidgetBridge();
|
|
45
|
-
const { configHash } = useJsonUiNode();
|
|
46
|
+
function useFusedParam({ param, debounceMs = 300, readOnly = false, defaultValue, broadcastDefaultValue = true, validate, preprocess, }) {
|
|
47
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
48
|
+
const { configHash } = (0, bridge_1.useJsonUiNode)();
|
|
46
49
|
const enabled = !!param;
|
|
47
50
|
// Stable log helper — entries appear in the workbench's runtime logs panel.
|
|
48
51
|
// configHash is passed through so nested JsonUiConfigHashOverride subtrees
|
|
@@ -61,20 +64,20 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
61
64
|
// Stable validator and preprocessor. We deliberately omit `defaultValue`
|
|
62
65
|
// from the deps so a parent re-render passing a new defaultValue literal
|
|
63
66
|
// doesn't churn these. The type semantics only depend on the original.
|
|
64
|
-
const preprocessor = useMemo(() => preprocess ?? createDefaultPreprocessor(defaultValue),
|
|
67
|
+
const preprocessor = (0, react_1.useMemo)(() => preprocess ?? createDefaultPreprocessor(defaultValue),
|
|
65
68
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
69
|
[preprocess]);
|
|
67
|
-
const validator = useMemo(() => validate ?? createDefaultValidator(defaultValue),
|
|
70
|
+
const validator = (0, react_1.useMemo)(() => validate ?? createDefaultValidator(defaultValue),
|
|
68
71
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
72
|
[validate]);
|
|
70
73
|
// Form-scoped value shadows canvas value when inside a form.
|
|
71
|
-
const paramNamesForForm = useMemo(() => (param ? [param] : []), [param]);
|
|
72
|
-
const { inForm, values: formParams } = useFormParams(paramNamesForForm);
|
|
74
|
+
const paramNamesForForm = (0, react_1.useMemo)(() => (param ? [param] : []), [param]);
|
|
75
|
+
const { inForm, values: formParams } = (0, form_1.useFormParams)(paramNamesForForm);
|
|
73
76
|
const formValue = param ? formParams[param] : undefined;
|
|
74
77
|
// ── Initial value resolution ────────────────────────────────────────────
|
|
75
78
|
// Read snapshot once to seed local state — subsequent updates come via the
|
|
76
79
|
// sync effect below. Reading the bridge here is safe (synchronous).
|
|
77
|
-
const initialCanvasValue = useMemo(() => {
|
|
80
|
+
const initialCanvasValue = (0, react_1.useMemo)(() => {
|
|
78
81
|
if (!enabled || !param)
|
|
79
82
|
return undefined;
|
|
80
83
|
return bridge.params.getSnapshot(param);
|
|
@@ -86,24 +89,24 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
86
89
|
const processed = preprocessor(raw);
|
|
87
90
|
return validator(processed) ? processed : defaultValue;
|
|
88
91
|
};
|
|
89
|
-
const [value, setValueState] = useState(computeInitialValue);
|
|
90
|
-
const valueRef = useRef(value);
|
|
92
|
+
const [value, setValueState] = (0, react_1.useState)(computeInitialValue);
|
|
93
|
+
const valueRef = (0, react_1.useRef)(value);
|
|
91
94
|
valueRef.current = value;
|
|
92
|
-
const debounceRef = useRef(null);
|
|
93
|
-
const isLocalChangeRef = useRef(false);
|
|
94
|
-
const didBroadcastOnMountRef = useRef(false);
|
|
95
|
+
const debounceRef = (0, react_1.useRef)(null);
|
|
96
|
+
const isLocalChangeRef = (0, react_1.useRef)(false);
|
|
97
|
+
const didBroadcastOnMountRef = (0, react_1.useRef)(false);
|
|
95
98
|
// Stable closure over the latest binding for the unmount cleanup.
|
|
96
99
|
// bridge is captured by ref too so the cleanup effect's deps can stay `[]`
|
|
97
100
|
// — otherwise any bridge identity flip would re-run the effect and
|
|
98
101
|
// broadcast a stray CLEAR for every bound input, cascading through the
|
|
99
102
|
// canvas-param listener fan-out.
|
|
100
|
-
const latestBindingRef = useRef({ enabled, param });
|
|
103
|
+
const latestBindingRef = (0, react_1.useRef)({ enabled, param });
|
|
101
104
|
latestBindingRef.current = { enabled, param };
|
|
102
|
-
const bridgeRef = useRef(bridge);
|
|
105
|
+
const bridgeRef = (0, react_1.useRef)(bridge);
|
|
103
106
|
bridgeRef.current = bridge;
|
|
104
107
|
// ── Clear bound param when name changes (carry-over avoidance) ──────────
|
|
105
|
-
const prevParamRef = useRef(param);
|
|
106
|
-
useEffect(() => {
|
|
108
|
+
const prevParamRef = (0, react_1.useRef)(param);
|
|
109
|
+
(0, react_1.useEffect)(() => {
|
|
107
110
|
const prev = prevParamRef.current;
|
|
108
111
|
prevParamRef.current = param;
|
|
109
112
|
if (prev && prev !== param) {
|
|
@@ -111,7 +114,7 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
111
114
|
}
|
|
112
115
|
}, [bridge, param]);
|
|
113
116
|
// ── Subscribe to canvas (and form) updates ──────────────────────────────
|
|
114
|
-
useEffect(() => {
|
|
117
|
+
(0, react_1.useEffect)(() => {
|
|
115
118
|
if (!enabled || !param)
|
|
116
119
|
return;
|
|
117
120
|
if (isLocalChangeRef.current)
|
|
@@ -131,7 +134,7 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
131
134
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
132
135
|
}, [bridge, param, enabled, inForm, formValue, preprocessor, validator]);
|
|
133
136
|
// Subscribe to bridge canvas updates so changes from other nodes flow in.
|
|
134
|
-
useEffect(() => {
|
|
137
|
+
(0, react_1.useEffect)(() => {
|
|
135
138
|
if (!enabled || !param)
|
|
136
139
|
return;
|
|
137
140
|
const unsub = bridge.params.subscribe(param, () => {
|
|
@@ -150,16 +153,16 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
150
153
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
151
154
|
}, [bridge, param, enabled, preprocessor, validator]);
|
|
152
155
|
// ── Broadcast plumbing ──────────────────────────────────────────────────
|
|
153
|
-
const broadcast = useCallback((newValue) => {
|
|
156
|
+
const broadcast = (0, react_1.useCallback)((newValue) => {
|
|
154
157
|
if (!enabled || !param)
|
|
155
158
|
return;
|
|
156
|
-
bridge.params.set(param, newValue, ParameterMessageType.PARAM);
|
|
159
|
+
bridge.params.set(param, newValue, protocol_1.ParameterMessageType.PARAM);
|
|
157
160
|
bridge.edges.stopLoading();
|
|
158
161
|
logPreview("Broadcast", newValue);
|
|
159
162
|
},
|
|
160
163
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
161
164
|
[bridge, enabled, param]);
|
|
162
|
-
const broadcastNow = useCallback(() => {
|
|
165
|
+
const broadcastNow = (0, react_1.useCallback)(() => {
|
|
163
166
|
if (!enabled || readOnly)
|
|
164
167
|
return;
|
|
165
168
|
if (debounceRef.current) {
|
|
@@ -170,7 +173,7 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
170
173
|
broadcast(valueRef.current);
|
|
171
174
|
isLocalChangeRef.current = false;
|
|
172
175
|
}, [bridge, broadcast, enabled, readOnly]);
|
|
173
|
-
const clearValue = useCallback((newValue) => {
|
|
176
|
+
const clearValue = (0, react_1.useCallback)((newValue) => {
|
|
174
177
|
if (debounceRef.current) {
|
|
175
178
|
clearTimeout(debounceRef.current);
|
|
176
179
|
debounceRef.current = null;
|
|
@@ -187,11 +190,11 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
187
190
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
188
191
|
[bridge, enabled, param, readOnly]);
|
|
189
192
|
// Reset one-time mount broadcast guard when binding changes.
|
|
190
|
-
useEffect(() => {
|
|
193
|
+
(0, react_1.useEffect)(() => {
|
|
191
194
|
didBroadcastOnMountRef.current = false;
|
|
192
195
|
}, [param, enabled]);
|
|
193
196
|
// Broadcast default value on mount when no canvas value exists.
|
|
194
|
-
useEffect(() => {
|
|
197
|
+
(0, react_1.useEffect)(() => {
|
|
195
198
|
if (!enabled || !param || readOnly)
|
|
196
199
|
return;
|
|
197
200
|
if (!broadcastDefaultValue)
|
|
@@ -211,7 +214,7 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
211
214
|
broadcast(valueRef.current);
|
|
212
215
|
didBroadcastOnMountRef.current = true;
|
|
213
216
|
}, [bridge, enabled, param, readOnly, broadcastDefaultValue, broadcast]);
|
|
214
|
-
const setValue = useCallback((newValue) => {
|
|
217
|
+
const setValue = (0, react_1.useCallback)((newValue) => {
|
|
215
218
|
setValueState(newValue);
|
|
216
219
|
if (!enabled || readOnly)
|
|
217
220
|
return;
|
|
@@ -228,7 +231,7 @@ export function useFusedParam({ param, debounceMs = 300, readOnly = false, defau
|
|
|
228
231
|
// type-swaps don't leave stale canvas state behind. Deps are `[]` so this
|
|
229
232
|
// runs on unmount only — never on bridge identity flips. bridge is read
|
|
230
233
|
// from the ref at cleanup time.
|
|
231
|
-
useEffect(() => {
|
|
234
|
+
(0, react_1.useEffect)(() => {
|
|
232
235
|
return () => {
|
|
233
236
|
if (debounceRef.current)
|
|
234
237
|
clearTimeout(debounceRef.current);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useJsonUiEdgeAnimation = useJsonUiEdgeAnimation;
|
|
4
|
+
const bridge_1 = require("../bridge");
|
|
2
5
|
/**
|
|
3
6
|
* Controls the edge-animation pellet for the current canvas node.
|
|
4
7
|
*
|
|
@@ -17,8 +20,8 @@ import { useFusedWidgetBridge } from "../bridge";
|
|
|
17
20
|
* try { await heavyCompute(); } finally { stopLoading(); }
|
|
18
21
|
* }
|
|
19
22
|
*/
|
|
20
|
-
|
|
21
|
-
const bridge = useFusedWidgetBridge();
|
|
23
|
+
function useJsonUiEdgeAnimation() {
|
|
24
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
22
25
|
return {
|
|
23
26
|
startLoading: bridge.edges.startLoading,
|
|
24
27
|
stopLoading: bridge.edges.stopLoading,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useJsonUiLog = useJsonUiLog;
|
|
4
|
+
exports.useJsonUiLogs = useJsonUiLogs;
|
|
5
|
+
exports.useJsonUiLogClear = useJsonUiLogClear;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const bridge_1 = require("../bridge");
|
|
3
8
|
const EMPTY_LOGS = Object.freeze([]);
|
|
4
9
|
/**
|
|
5
10
|
* Logging hook for json-ui components. Entries appear in the workbench's
|
|
@@ -16,22 +21,22 @@ const EMPTY_LOGS = Object.freeze([]);
|
|
|
16
21
|
* log("Selected city: " + city);
|
|
17
22
|
* log("Failed to load chart data", "error");
|
|
18
23
|
*/
|
|
19
|
-
|
|
20
|
-
const bridge = useFusedWidgetBridge();
|
|
21
|
-
const { configHash } = useJsonUiNode();
|
|
24
|
+
function useJsonUiLog() {
|
|
25
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
26
|
+
const { configHash } = (0, bridge_1.useJsonUiNode)();
|
|
22
27
|
// bridge and configHash are read from refs so the returned `log` callback
|
|
23
28
|
// identity is stable across re-renders. Downstream effects with `log` in
|
|
24
29
|
// their deps (e.g. use-duckdb-sql.ts's preprocessing effect) would
|
|
25
30
|
// otherwise re-fire on every bridge flip / configHash change and cascade
|
|
26
31
|
// setState across every SQL widget in the dashboard.
|
|
27
|
-
const bridgeRef = useRef(bridge);
|
|
32
|
+
const bridgeRef = (0, react_1.useRef)(bridge);
|
|
28
33
|
bridgeRef.current = bridge;
|
|
29
|
-
const configHashRef = useRef(configHash);
|
|
34
|
+
const configHashRef = (0, react_1.useRef)(configHash);
|
|
30
35
|
configHashRef.current = configHash;
|
|
31
|
-
const log = useCallback((message, level = "info") => {
|
|
36
|
+
const log = (0, react_1.useCallback)((message, level = "info") => {
|
|
32
37
|
bridgeRef.current.log.log(message, level, configHashRef.current);
|
|
33
38
|
}, []);
|
|
34
|
-
return useMemo(() => ({ log }), [log]);
|
|
39
|
+
return (0, react_1.useMemo)(() => ({ log }), [log]);
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* Read-only hook returning the current log entries for a given node.
|
|
@@ -41,15 +46,15 @@ export function useJsonUiLog() {
|
|
|
41
46
|
* rarely need to read logs they wrote, but the hook is exposed for
|
|
42
47
|
* completeness (e.g. building a debug view component).
|
|
43
48
|
*/
|
|
44
|
-
|
|
45
|
-
const bridge = useFusedWidgetBridge();
|
|
46
|
-
const subscribe = useCallback((cb) => {
|
|
49
|
+
function useJsonUiLogs(nodeId) {
|
|
50
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
51
|
+
const subscribe = (0, react_1.useCallback)((cb) => {
|
|
47
52
|
if (!nodeId)
|
|
48
53
|
return () => { };
|
|
49
54
|
return bridge.log.subscribeLogs(nodeId, cb);
|
|
50
55
|
}, [bridge, nodeId]);
|
|
51
|
-
const snapshotRef = useRef(EMPTY_LOGS);
|
|
52
|
-
const getSnapshot = useCallback(() => {
|
|
56
|
+
const snapshotRef = (0, react_1.useRef)(EMPTY_LOGS);
|
|
57
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
53
58
|
if (!nodeId)
|
|
54
59
|
return EMPTY_LOGS;
|
|
55
60
|
const next = bridge.log.getLogsSnapshot(nodeId);
|
|
@@ -58,15 +63,15 @@ export function useJsonUiLogs(nodeId) {
|
|
|
58
63
|
snapshotRef.current = next;
|
|
59
64
|
return next;
|
|
60
65
|
}, [bridge, nodeId]);
|
|
61
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
66
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
69
|
* Returns a stable callback that clears all log entries for the given node.
|
|
65
70
|
* Returns a no-op function when `nodeId` is undefined.
|
|
66
71
|
*/
|
|
67
|
-
|
|
68
|
-
const bridge = useFusedWidgetBridge();
|
|
69
|
-
return useCallback(() => {
|
|
72
|
+
function useJsonUiLogClear(nodeId) {
|
|
73
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
74
|
+
return (0, react_1.useCallback)(() => {
|
|
70
75
|
if (!nodeId)
|
|
71
76
|
return;
|
|
72
77
|
bridge.log.clearLogs(nodeId);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useJsonUiUdfInfo = useJsonUiUdfInfo;
|
|
4
|
+
const bridge_1 = require("../bridge");
|
|
2
5
|
/**
|
|
3
6
|
* Returns the identity of the current canvas node.
|
|
4
7
|
*
|
|
@@ -18,6 +21,6 @@ import { useJsonUiNode } from "../bridge";
|
|
|
18
21
|
* const { udfName } = useJsonUiUdfInfo();
|
|
19
22
|
* console.log("I am node:", udfName);
|
|
20
23
|
*/
|
|
21
|
-
|
|
22
|
-
return useJsonUiNode();
|
|
24
|
+
function useJsonUiUdfInfo() {
|
|
25
|
+
return (0, bridge_1.useJsonUiNode)();
|
|
23
26
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useParamSubstitution = useParamSubstitution;
|
|
1
4
|
/**
|
|
2
5
|
* Reactively resolve `$param` and `{{udf}}` placeholders in a template.
|
|
3
6
|
*
|
|
@@ -7,10 +10,10 @@
|
|
|
7
10
|
* delegate to `bridge.template.render` so the host can use its rich UDF
|
|
8
11
|
* machinery. The SDK orchestrates state, cancellation, and re-runs.
|
|
9
12
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const react_1 = require("react");
|
|
14
|
+
const bridge_1 = require("../bridge");
|
|
15
|
+
const form_1 = require("../form");
|
|
16
|
+
const use_canvas_params_1 = require("./use-canvas-params");
|
|
14
17
|
const PARAM_TOKEN_RE = /\$([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
15
18
|
const UDF_PLACEHOLDER_RE = /\{\{[\s\S]*?\}\}/;
|
|
16
19
|
// Mirrors `parseInlineUdfPlaceholders` for the purpose of finding which UDF
|
|
@@ -37,17 +40,17 @@ const UDF_NAME_EXTRACT_RE = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\b/g;
|
|
|
37
40
|
* { preserveMissingParams: true }
|
|
38
41
|
* );
|
|
39
42
|
*/
|
|
40
|
-
|
|
43
|
+
function useParamSubstitution(template, options = {}) {
|
|
41
44
|
const safeTemplate = template ?? "";
|
|
42
45
|
const preserveMissingParams = options.preserveMissingParams ?? false;
|
|
43
|
-
const bridge = useFusedWidgetBridge();
|
|
44
|
-
const paramNames = useMemo(() => extractParamNames(safeTemplate), [safeTemplate]);
|
|
45
|
-
const canvasValues = useCanvasParams(paramNames);
|
|
46
|
-
const { inForm, values: formValues } = useFormParams(paramNames);
|
|
47
|
-
const paramValues = useMemo(() => (inForm ? { ...canvasValues, ...formValues } : canvasValues), [canvasValues, formValues, inForm]);
|
|
48
|
-
const hasUdfRefs = useMemo(() => UDF_PLACEHOLDER_RE.test(safeTemplate), [safeTemplate]);
|
|
46
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
47
|
+
const paramNames = (0, react_1.useMemo)(() => extractParamNames(safeTemplate), [safeTemplate]);
|
|
48
|
+
const canvasValues = (0, use_canvas_params_1.useCanvasParams)(paramNames);
|
|
49
|
+
const { inForm, values: formValues } = (0, form_1.useFormParams)(paramNames);
|
|
50
|
+
const paramValues = (0, react_1.useMemo)(() => (inForm ? { ...canvasValues, ...formValues } : canvasValues), [canvasValues, formValues, inForm]);
|
|
51
|
+
const hasUdfRefs = (0, react_1.useMemo)(() => UDF_PLACEHOLDER_RE.test(safeTemplate), [safeTemplate]);
|
|
49
52
|
// ── Fast path: no UDF refs ─────────────────────────────────────────────
|
|
50
|
-
const pureParamValue = useMemo(() => {
|
|
53
|
+
const pureParamValue = (0, react_1.useMemo)(() => {
|
|
51
54
|
if (hasUdfRefs)
|
|
52
55
|
return "";
|
|
53
56
|
return substituteParams(safeTemplate, paramValues, preserveMissingParams);
|
|
@@ -56,7 +59,7 @@ export function useParamSubstitution(template, options = {}) {
|
|
|
56
59
|
// Extract referenced UDF names so we can subscribe to their outputs.
|
|
57
60
|
// When any of them re-executes we bump a tick and the effect below
|
|
58
61
|
// re-runs `bridge.template.render` with fresh data.
|
|
59
|
-
const referencedUdfNames = useMemo(() => {
|
|
62
|
+
const referencedUdfNames = (0, react_1.useMemo)(() => {
|
|
60
63
|
if (!hasUdfRefs)
|
|
61
64
|
return [];
|
|
62
65
|
const seen = new Set();
|
|
@@ -74,10 +77,10 @@ export function useParamSubstitution(template, options = {}) {
|
|
|
74
77
|
// Subscribe to host re-render triggers (UDF outputs + topology) so that
|
|
75
78
|
// the async render re-runs when an upstream UDF re-executes or edges shift.
|
|
76
79
|
const templateChangeKey = useTemplateBridgeChangeKey(bridge, referencedUdfNames);
|
|
77
|
-
const [resolved, setResolved] = useState(() => ({ key: "", value: "" }));
|
|
78
|
-
const [loading, setLoading] = useState(false);
|
|
80
|
+
const [resolved, setResolved] = (0, react_1.useState)(() => ({ key: "", value: "" }));
|
|
81
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
79
82
|
// Render key includes template + paramValues + the host change ticker.
|
|
80
|
-
const renderKey = useMemo(() => {
|
|
83
|
+
const renderKey = (0, react_1.useMemo)(() => {
|
|
81
84
|
return JSON.stringify({
|
|
82
85
|
template: safeTemplate,
|
|
83
86
|
paramValues,
|
|
@@ -85,7 +88,7 @@ export function useParamSubstitution(template, options = {}) {
|
|
|
85
88
|
tick: templateChangeKey,
|
|
86
89
|
});
|
|
87
90
|
}, [safeTemplate, paramValues, preserveMissingParams, templateChangeKey]);
|
|
88
|
-
useEffect(() => {
|
|
91
|
+
(0, react_1.useEffect)(() => {
|
|
89
92
|
if (!hasUdfRefs) {
|
|
90
93
|
setLoading(false);
|
|
91
94
|
return;
|
|
@@ -124,7 +127,7 @@ export function useParamSubstitution(template, options = {}) {
|
|
|
124
127
|
preserveMissingParams,
|
|
125
128
|
renderKey,
|
|
126
129
|
]);
|
|
127
|
-
const value = useMemo(() => {
|
|
130
|
+
const value = (0, react_1.useMemo)(() => {
|
|
128
131
|
if (!hasUdfRefs)
|
|
129
132
|
return pureParamValue;
|
|
130
133
|
if (resolved.key === renderKey)
|
|
@@ -187,9 +190,9 @@ function stringifyParamValue(value) {
|
|
|
187
190
|
* changes whenever a re-render could produce a different result.
|
|
188
191
|
*/
|
|
189
192
|
function useTemplateBridgeChangeKey(bridge, referencedUdfNames) {
|
|
190
|
-
const tickRef = useRef(0);
|
|
193
|
+
const tickRef = (0, react_1.useRef)(0);
|
|
191
194
|
const namesKey = referencedUdfNames.slice().sort().join("|");
|
|
192
|
-
const subscribe = useCallback((cb) => {
|
|
195
|
+
const subscribe = (0, react_1.useCallback)((cb) => {
|
|
193
196
|
const fire = () => {
|
|
194
197
|
tickRef.current += 1;
|
|
195
198
|
cb();
|
|
@@ -202,6 +205,6 @@ function useTemplateBridgeChangeKey(bridge, referencedUdfNames) {
|
|
|
202
205
|
},
|
|
203
206
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
204
207
|
[bridge, namesKey]);
|
|
205
|
-
const getSnapshot = useCallback(() => tickRef.current, []);
|
|
206
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
208
|
+
const getSnapshot = (0, react_1.useCallback)(() => tickRef.current, []);
|
|
209
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
207
210
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useUdfOutputByName = useUdfOutputByName;
|
|
4
|
+
exports.useRequestUdfReexecute = useRequestUdfReexecute;
|
|
5
|
+
exports.isUdfQuery = isUdfQuery;
|
|
6
|
+
exports.parseUdfColumnQuery = parseUdfColumnQuery;
|
|
7
|
+
exports.useUdfDataFrameSample = useUdfDataFrameSample;
|
|
8
|
+
exports.useUdfColumnValues = useUdfColumnValues;
|
|
9
|
+
exports.useUdfColumnValue = useUdfColumnValue;
|
|
10
|
+
const react_1 = require("react");
|
|
11
|
+
const bridge_1 = require("../bridge");
|
|
3
12
|
/**
|
|
4
13
|
* Subscribe to a UDF's output by name. Returns the current `UdfOutputSnapshot`
|
|
5
14
|
* (data + execution status + optional error + VFS filename) or `undefined`
|
|
@@ -14,15 +23,15 @@ import { useFusedWidgetBridge } from "../bridge";
|
|
|
14
23
|
* if (out.isExecutionInProgress) return <div>Loading…</div>;
|
|
15
24
|
* if (out.error) return <div>Error: {out.error}</div>;
|
|
16
25
|
*/
|
|
17
|
-
|
|
18
|
-
const bridge = useFusedWidgetBridge();
|
|
19
|
-
const subscribe = useCallback((cb) => {
|
|
26
|
+
function useUdfOutputByName(udfName) {
|
|
27
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
28
|
+
const subscribe = (0, react_1.useCallback)((cb) => {
|
|
20
29
|
if (!udfName)
|
|
21
30
|
return () => { };
|
|
22
31
|
return bridge.udfs.subscribeOutput(udfName, cb);
|
|
23
32
|
}, [bridge, udfName]);
|
|
24
|
-
const snapshotRef = useRef(undefined);
|
|
25
|
-
const getSnapshot = useCallback(() => {
|
|
33
|
+
const snapshotRef = (0, react_1.useRef)(undefined);
|
|
34
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
26
35
|
if (!udfName)
|
|
27
36
|
return undefined;
|
|
28
37
|
const next = bridge.udfs.getOutputSnapshot(udfName);
|
|
@@ -32,15 +41,15 @@ export function useUdfOutputByName(udfName) {
|
|
|
32
41
|
snapshotRef.current = next;
|
|
33
42
|
return next;
|
|
34
43
|
}, [bridge, udfName]);
|
|
35
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
44
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
36
45
|
}
|
|
37
46
|
/**
|
|
38
47
|
* Request the workbench to re-execute a named UDF (e.g. after the user
|
|
39
48
|
* clicks a "Refresh" button). No-op in test harnesses without re-execution.
|
|
40
49
|
*/
|
|
41
|
-
|
|
42
|
-
const bridge = useFusedWidgetBridge();
|
|
43
|
-
return useCallback((udfName) => bridge.udfs.requestReexecute(udfName), [bridge]);
|
|
50
|
+
function useRequestUdfReexecute() {
|
|
51
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
52
|
+
return (0, react_1.useCallback)((udfName) => bridge.udfs.requestReexecute(udfName), [bridge]);
|
|
44
53
|
}
|
|
45
54
|
function snapshotsEqual(a, b) {
|
|
46
55
|
if (a === b)
|
|
@@ -64,7 +73,7 @@ const UDF_QUERY_REGEX = /^\{\{(\w+)\.(\w+)(?:\[(\d+)\])?\}\}$/;
|
|
|
64
73
|
* isUdfQuery("{{my_udf.city[0]}}") // true
|
|
65
74
|
* isUdfQuery("not a query") // false
|
|
66
75
|
*/
|
|
67
|
-
|
|
76
|
+
function isUdfQuery(query) {
|
|
68
77
|
if (!query || typeof query !== "string")
|
|
69
78
|
return false;
|
|
70
79
|
return UDF_QUERY_REGEX.test(query);
|
|
@@ -76,7 +85,7 @@ export function isUdfQuery(query) {
|
|
|
76
85
|
* parseUdfColumnQuery("{{my_udf.city}}") // { udfName: "my_udf", columnName: "city" }
|
|
77
86
|
* parseUdfColumnQuery("{{my_udf.city[0]}}") // { ..., index: 0 }
|
|
78
87
|
*/
|
|
79
|
-
|
|
88
|
+
function parseUdfColumnQuery(query) {
|
|
80
89
|
if (!isUdfQuery(query))
|
|
81
90
|
return null;
|
|
82
91
|
const match = query.match(UDF_QUERY_REGEX);
|
|
@@ -96,12 +105,12 @@ function isDataSourceLike(value) {
|
|
|
96
105
|
* column names. Used by dropdowns and galleries that populate options
|
|
97
106
|
* from UDF data.
|
|
98
107
|
*/
|
|
99
|
-
|
|
108
|
+
function useUdfDataFrameSample({ udfName, sampleSize = 200, }) {
|
|
100
109
|
const snapshot = useUdfOutputByName(udfName);
|
|
101
110
|
const reexecute = useRequestUdfReexecute();
|
|
102
|
-
const [rows, setRows] = useState([]);
|
|
103
|
-
const [columns, setColumns] = useState([]);
|
|
104
|
-
useEffect(() => {
|
|
111
|
+
const [rows, setRows] = (0, react_1.useState)([]);
|
|
112
|
+
const [columns, setColumns] = (0, react_1.useState)([]);
|
|
113
|
+
(0, react_1.useEffect)(() => {
|
|
105
114
|
let cancelled = false;
|
|
106
115
|
const data = snapshot?.data;
|
|
107
116
|
if (!data || !isDataSourceLike(data)) {
|
|
@@ -139,7 +148,7 @@ export function useUdfDataFrameSample({ udfName, sampleSize = 200, }) {
|
|
|
139
148
|
cancelled = true;
|
|
140
149
|
};
|
|
141
150
|
}, [snapshot?.data, sampleSize]);
|
|
142
|
-
const requestReexecute = useCallback(() => {
|
|
151
|
+
const requestReexecute = (0, react_1.useCallback)(() => {
|
|
143
152
|
if (udfName)
|
|
144
153
|
reexecute(udfName);
|
|
145
154
|
}, [reexecute, udfName]);
|
|
@@ -159,14 +168,14 @@ export function useUdfDataFrameSample({ udfName, sampleSize = 200, }) {
|
|
|
159
168
|
* const { values, loading } = useUdfColumnValues("{{my_udf.city}}");
|
|
160
169
|
* // values = ["NYC", "LA", "SF", ...]
|
|
161
170
|
*/
|
|
162
|
-
|
|
171
|
+
function useUdfColumnValues(query, sampleSize = 200) {
|
|
163
172
|
const isValid = isUdfQuery(query);
|
|
164
|
-
const parsed = useMemo(() => (isValid ? parseUdfColumnQuery(query) : null), [isValid, query]);
|
|
173
|
+
const parsed = (0, react_1.useMemo)(() => (isValid ? parseUdfColumnQuery(query) : null), [isValid, query]);
|
|
165
174
|
const { rows, loading } = useUdfDataFrameSample({
|
|
166
175
|
udfName: parsed?.udfName,
|
|
167
176
|
sampleSize,
|
|
168
177
|
});
|
|
169
|
-
const values = useMemo(() => {
|
|
178
|
+
const values = (0, react_1.useMemo)(() => {
|
|
170
179
|
if (!parsed || !parsed.columnName)
|
|
171
180
|
return [];
|
|
172
181
|
return rows
|
|
@@ -182,14 +191,14 @@ export function useUdfColumnValues(query, sampleSize = 200) {
|
|
|
182
191
|
* const { value, loading } = useUdfColumnValue("{{my_udf.city[0]}}");
|
|
183
192
|
* // value = "NYC"
|
|
184
193
|
*/
|
|
185
|
-
|
|
194
|
+
function useUdfColumnValue(query, sampleSize = 200) {
|
|
186
195
|
const isValid = isUdfQuery(query);
|
|
187
|
-
const parsed = useMemo(() => (isValid ? parseUdfColumnQuery(query) : null), [isValid, query]);
|
|
196
|
+
const parsed = (0, react_1.useMemo)(() => (isValid ? parseUdfColumnQuery(query) : null), [isValid, query]);
|
|
188
197
|
const { rows, loading } = useUdfDataFrameSample({
|
|
189
198
|
udfName: parsed?.udfName,
|
|
190
199
|
sampleSize,
|
|
191
200
|
});
|
|
192
|
-
const value = useMemo(() => {
|
|
201
|
+
const value = (0, react_1.useMemo)(() => {
|
|
193
202
|
if (!parsed || !parsed.columnName || parsed.index === undefined)
|
|
194
203
|
return null;
|
|
195
204
|
const row = rows[parsed.index];
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useUploadAccessCheck = useUploadAccessCheck;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const bridge_1 = require("../bridge");
|
|
3
6
|
/**
|
|
4
7
|
* Check whether the current user has write access to a destination path
|
|
5
8
|
* (S3, GCS, etc.). Used by the `file-upload` widget to surface a clear
|
|
@@ -8,10 +11,10 @@ import { useFusedWidgetBridge } from "../bridge";
|
|
|
8
11
|
* Returns a state machine value `{ status: "idle" | "checking" | "allowed" |
|
|
9
12
|
* "denied" }` that you can branch on directly in render.
|
|
10
13
|
*/
|
|
11
|
-
|
|
12
|
-
const bridge = useFusedWidgetBridge();
|
|
13
|
-
const [state, setState] = useState({ status: "idle" });
|
|
14
|
-
useEffect(() => {
|
|
14
|
+
function useUploadAccessCheck(destinationPath, enabled) {
|
|
15
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
16
|
+
const [state, setState] = (0, react_1.useState)({ status: "idle" });
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
15
18
|
if (!enabled || !destinationPath?.trim()) {
|
|
16
19
|
setState({ status: "idle" });
|
|
17
20
|
return;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SIGNED_URL_SCHEMES = void 0;
|
|
4
|
+
exports.useUrlSigning = useUrlSigning;
|
|
5
|
+
exports.useMediaSrc = useMediaSrc;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const bridge_1 = require("../bridge");
|
|
8
|
+
const use_param_substitution_1 = require("./use-param-substitution");
|
|
4
9
|
/** URL schemes that require signing before fetch. */
|
|
5
|
-
|
|
10
|
+
exports.SIGNED_URL_SCHEMES = ["s3://", "gs://", "fd://"];
|
|
6
11
|
function needsSigning(url) {
|
|
7
|
-
return SIGNED_URL_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
12
|
+
return exports.SIGNED_URL_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
15
|
* Manual URL signing — returns a stable `signUrl` callback you can call
|
|
@@ -18,9 +23,9 @@ function needsSigning(url) {
|
|
|
18
23
|
* window.location.href = signed;
|
|
19
24
|
* };
|
|
20
25
|
*/
|
|
21
|
-
|
|
22
|
-
const bridge = useFusedWidgetBridge();
|
|
23
|
-
const signUrl = useCallback((url) => bridge.signUrl(url), [bridge]);
|
|
26
|
+
function useUrlSigning() {
|
|
27
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
28
|
+
const signUrl = (0, react_1.useCallback)((url) => bridge.signUrl(url), [bridge]);
|
|
24
29
|
return { signUrl };
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
@@ -33,14 +38,14 @@ export function useUrlSigning() {
|
|
|
33
38
|
* if (error) return <div>Failed: {error}</div>;
|
|
34
39
|
* return <img src={src ?? ""} alt="" />;
|
|
35
40
|
*/
|
|
36
|
-
|
|
37
|
-
const bridge = useFusedWidgetBridge();
|
|
38
|
-
const { value: resolvedSrc, loading: paramLoading } = useParamSubstitution(srcInput);
|
|
39
|
-
const [displaySrc, setDisplaySrc] = useState(null);
|
|
40
|
-
const [error, setError] = useState(null);
|
|
41
|
-
const [signing, setSigning] = useState(false);
|
|
42
|
-
const [refreshNonce, setRefreshNonce] = useState(0);
|
|
43
|
-
useEffect(() => {
|
|
41
|
+
function useMediaSrc(srcInput) {
|
|
42
|
+
const bridge = (0, bridge_1.useFusedWidgetBridge)();
|
|
43
|
+
const { value: resolvedSrc, loading: paramLoading } = (0, use_param_substitution_1.useParamSubstitution)(srcInput);
|
|
44
|
+
const [displaySrc, setDisplaySrc] = (0, react_1.useState)(null);
|
|
45
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
46
|
+
const [signing, setSigning] = (0, react_1.useState)(false);
|
|
47
|
+
const [refreshNonce, setRefreshNonce] = (0, react_1.useState)(0);
|
|
48
|
+
(0, react_1.useEffect)(() => {
|
|
44
49
|
if (paramLoading)
|
|
45
50
|
return;
|
|
46
51
|
if (!resolvedSrc) {
|
|
@@ -79,7 +84,7 @@ export function useMediaSrc(srcInput) {
|
|
|
79
84
|
cancelled = true;
|
|
80
85
|
};
|
|
81
86
|
}, [bridge, paramLoading, resolvedSrc, refreshNonce]);
|
|
82
|
-
const refreshSignedUrl = useCallback(async () => {
|
|
87
|
+
const refreshSignedUrl = (0, react_1.useCallback)(async () => {
|
|
83
88
|
if (!resolvedSrc || !needsSigning(resolvedSrc)) {
|
|
84
89
|
return resolvedSrc ?? null;
|
|
85
90
|
}
|