@bian-womp/spark-workbench 0.2.24 → 0.2.26

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/lib/cjs/index.cjs CHANGED
@@ -572,6 +572,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
572
572
  super(registry, backend);
573
573
  this.valueCache = new Map();
574
574
  this.listenersBound = false;
575
+ this.registryFetched = false;
576
+ this.registryFetching = false;
577
+ this.MAX_REGISTRY_FETCH_ATTEMPTS = 3;
578
+ this.INITIAL_RETRY_DELAY_MS = 1000; // 1 second
575
579
  // Auto-handle registry-changed invalidations from remote
576
580
  // We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
577
581
  this.ensureRemoteRunner().then(async (runner) => {
@@ -806,39 +810,178 @@ class RemoteGraphRunner extends AbstractGraphRunner {
806
810
  super.dispose();
807
811
  this.runner = undefined;
808
812
  this.transport = undefined;
813
+ this.registryFetched = false; // Reset so registry is fetched again on reconnect
814
+ this.registryFetching = false; // Reset fetching state
809
815
  this.emit("transport", {
810
816
  state: "disconnected",
811
817
  kind: this.backend.kind,
812
818
  });
813
819
  }
820
+ /**
821
+ * Fetch full registry description from remote and register it locally.
822
+ * Called automatically on first connection with retry mechanism.
823
+ */
824
+ async fetchRegistry(runner, attempt = 1) {
825
+ if (this.registryFetching) {
826
+ // Already fetching, don't start another fetch
827
+ return;
828
+ }
829
+ this.registryFetching = true;
830
+ try {
831
+ const desc = await runner.describeRegistry();
832
+ // Register types
833
+ for (const t of desc.types) {
834
+ if (t.options) {
835
+ this.registry.registerEnum({
836
+ id: t.id,
837
+ options: t.options,
838
+ bakeTarget: t.bakeTarget,
839
+ });
840
+ }
841
+ else {
842
+ if (!this.registry.types.has(t.id)) {
843
+ this.registry.registerType({
844
+ id: t.id,
845
+ displayName: t.displayName,
846
+ validate: (_v) => true,
847
+ bakeTarget: t.bakeTarget,
848
+ });
849
+ }
850
+ }
851
+ }
852
+ // Register categories
853
+ for (const c of desc.categories || []) {
854
+ if (!this.registry.categories.has(c.id)) {
855
+ // Create placeholder category descriptor
856
+ const category = {
857
+ id: c.id,
858
+ displayName: c.displayName,
859
+ createRuntime: () => ({
860
+ async onInputsChanged() { },
861
+ }),
862
+ policy: { asyncConcurrency: "switch" },
863
+ };
864
+ this.registry.categories.register(category);
865
+ }
866
+ }
867
+ // Register coercions
868
+ for (const c of desc.coercions) {
869
+ if (c.async) {
870
+ this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
871
+ nonTransitive: c.nonTransitive,
872
+ });
873
+ }
874
+ else {
875
+ this.registry.registerCoercion(c.from, c.to, (v) => v, {
876
+ nonTransitive: c.nonTransitive,
877
+ });
878
+ }
879
+ }
880
+ // Register nodes
881
+ for (const n of desc.nodes) {
882
+ if (!this.registry.nodes.has(n.id)) {
883
+ this.registry.registerNode({
884
+ id: n.id,
885
+ categoryId: n.categoryId,
886
+ displayName: n.displayName,
887
+ inputs: n.inputs || {},
888
+ outputs: n.outputs || {},
889
+ impl: () => { },
890
+ });
891
+ }
892
+ }
893
+ this.registryFetched = true;
894
+ this.registryFetching = false;
895
+ this.emit("registry", this.registry);
896
+ }
897
+ catch (err) {
898
+ this.registryFetching = false;
899
+ const error = err instanceof Error ? err : new Error(String(err));
900
+ // Retry with exponential backoff if attempts remaining
901
+ if (attempt < this.MAX_REGISTRY_FETCH_ATTEMPTS) {
902
+ const delayMs = this.INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt - 1);
903
+ console.warn(`Failed to fetch registry (attempt ${attempt}/${this.MAX_REGISTRY_FETCH_ATTEMPTS}), retrying in ${delayMs}ms...`, error);
904
+ // Emit error event for UI feedback
905
+ this.emit("error", {
906
+ kind: "registry",
907
+ message: `Registry fetch failed (attempt ${attempt}/${this.MAX_REGISTRY_FETCH_ATTEMPTS}), retrying...`,
908
+ err: error,
909
+ attempt,
910
+ maxAttempts: this.MAX_REGISTRY_FETCH_ATTEMPTS,
911
+ });
912
+ // Retry after delay
913
+ setTimeout(() => {
914
+ this.fetchRegistry(runner, attempt + 1).catch(() => {
915
+ // Final failure handled below
916
+ });
917
+ }, delayMs);
918
+ }
919
+ else {
920
+ // Max attempts reached, emit final error
921
+ console.error(`Failed to fetch registry after ${this.MAX_REGISTRY_FETCH_ATTEMPTS} attempts:`, error);
922
+ this.emit("error", {
923
+ kind: "registry",
924
+ message: `Failed to fetch registry after ${this.MAX_REGISTRY_FETCH_ATTEMPTS} attempts. Please check your connection and try refreshing.`,
925
+ err: error,
926
+ attempt: this.MAX_REGISTRY_FETCH_ATTEMPTS,
927
+ maxAttempts: this.MAX_REGISTRY_FETCH_ATTEMPTS,
928
+ });
929
+ }
930
+ }
931
+ }
814
932
  // Ensure remote transport/runner
815
933
  async ensureRemoteRunner() {
816
934
  if (this.runner)
817
935
  return this.runner;
818
936
  let transport;
819
937
  const kind = this.backend.kind;
938
+ const backend = this.backend;
939
+ const connectOptions = backend.connectOptions;
820
940
  this.emit("transport", { state: "connecting", kind });
821
- if (this.backend.kind === "remote-http") {
941
+ if (backend.kind === "remote-http") {
822
942
  if (!sparkRemote.HttpPollingTransport)
823
943
  throw new Error("HttpPollingTransport not available");
824
- transport = new sparkRemote.HttpPollingTransport(this.backend.baseUrl);
825
- await transport.connect();
944
+ transport = new sparkRemote.HttpPollingTransport(backend.baseUrl);
945
+ await transport.connect(connectOptions);
826
946
  }
827
- else if (this.backend.kind === "remote-ws") {
947
+ else if (backend.kind === "remote-ws") {
828
948
  if (!sparkRemote.WebSocketTransport)
829
949
  throw new Error("WebSocketTransport not available");
830
- transport = new sparkRemote.WebSocketTransport(this.backend.url);
831
- await transport.connect();
950
+ transport = new sparkRemote.WebSocketTransport(backend.url);
951
+ await transport.connect(connectOptions);
832
952
  }
833
953
  else {
834
954
  throw new Error("Remote backend not configured");
835
955
  }
956
+ // Subscribe to custom events if handler provided
957
+ if (backend.onCustomEvent) {
958
+ transport.subscribe((event) => {
959
+ // Filter out standard runtime events, pass others to custom handler
960
+ const msg = event.message;
961
+ if (msg && typeof msg === "object" && "type" in msg) {
962
+ const type = msg.type;
963
+ // Standard runtime events: stats, value, error, invalidate
964
+ // Custom events are anything else (e.g., flow-opened, flow-latest)
965
+ if (!["stats", "value", "error", "invalidate"].includes(type)) {
966
+ backend.onCustomEvent?.(event);
967
+ }
968
+ }
969
+ });
970
+ }
836
971
  const runner = new sparkRemote.RemoteRunner(transport);
837
972
  this.runner = runner;
838
973
  this.transport = transport;
839
974
  this.valueCache.clear();
840
975
  this.listenersBound = false;
841
976
  this.emit("transport", { state: "connected", kind });
977
+ // Auto-fetch registry on first connection (only once)
978
+ if (!this.registryFetched && !this.registryFetching) {
979
+ // Log loading state (UI can listen to transport status for loading indication)
980
+ console.info("Loading registry from remote...");
981
+ this.fetchRegistry(runner).catch(() => {
982
+ // Error handling is done inside fetchRegistry
983
+ });
984
+ }
842
985
  return runner;
843
986
  }
844
987
  }
@@ -2955,7 +3098,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
2955
3098
  }, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", fitView: true, children: [jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 }), jsxRuntime.jsx(react.MiniMap, {}), jsxRuntime.jsx(react.Controls, {}), jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: onCloseMenu }), !!nodeAtMenu && (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: onCloseNodeMenu }))] }) }) }));
2956
3099
  });
2957
3100
 
2958
- function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
3101
+ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, backendOptions, overrides, onInit, onChange, }) {
2959
3102
  const { wb, runner, registry, def, selectedNodeId, runAutoLayout } = useWorkbenchContext();
2960
3103
  const [transportStatus, setTransportStatus] = React.useState({
2961
3104
  state: "local",
@@ -3004,11 +3147,14 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3004
3147
  return overrides.getExamples(defaultExamples);
3005
3148
  return defaultExamples;
3006
3149
  }, [overrides, defaultExamples]);
3007
- const [hydrated, setHydrated] = React.useState(false);
3008
3150
  const lastAutoLaunched = React.useRef(undefined);
3009
3151
  const autoLayoutRan = React.useRef(false);
3010
3152
  const canvasRef = React.useRef(null);
3011
3153
  const uploadInputRef = React.useRef(null);
3154
+ const [registryReady, setRegistryReady] = React.useState(() => {
3155
+ // For local backends, registry is always ready
3156
+ return backendKind === "local";
3157
+ });
3012
3158
  // Expose init callback with setInitialGraph helper
3013
3159
  const initCalled = React.useRef(false);
3014
3160
  React.useEffect(() => {
@@ -3067,7 +3213,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3067
3213
  const { registry: r, def } = await ex.load();
3068
3214
  // Keep registry consistent with backend:
3069
3215
  // - For local backend, allow example to provide its own registry
3070
- // - For remote backend, NEVER overwrite the hydrated remote registry
3216
+ // - For remote backend, registry is automatically managed by RemoteGraphRunner
3071
3217
  if (backendKind === "local") {
3072
3218
  if (r) {
3073
3219
  setRegistry(r);
@@ -3180,79 +3326,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3180
3326
  const triggerUpload = React.useCallback(() => {
3181
3327
  uploadInputRef.current?.click();
3182
3328
  }, []);
3183
- const hydrateFromBackend = React.useCallback(async (kind, base) => {
3184
- try {
3185
- const transport = kind === "remote-http"
3186
- ? new sparkRemote.HttpPollingTransport(base)
3187
- : new sparkRemote.WebSocketTransport(base);
3188
- await transport.connect();
3189
- const rr = new sparkRemote.RemoteRunner(transport);
3190
- const desc = await rr.describeRegistry();
3191
- const r = new sparkGraph.Registry();
3192
- // Types
3193
- for (const t of desc.types) {
3194
- if (t.options) {
3195
- r.registerEnum({
3196
- id: t.id,
3197
- options: t.options,
3198
- bakeTarget: t.bakeTarget,
3199
- });
3200
- }
3201
- else {
3202
- r.registerType({
3203
- id: t.id,
3204
- displayName: t.displayName,
3205
- validate: (_v) => true,
3206
- bakeTarget: t.bakeTarget,
3207
- });
3208
- }
3209
- }
3210
- // Categories: create placeholders for display name
3211
- for (const c of desc.categories || []) {
3212
- // If you later expose real category descriptors, register them here
3213
- // For now, rely on ComputeCategory for behavior
3214
- const category = {
3215
- id: c.id,
3216
- displayName: c.displayName,
3217
- createRuntime: () => ({
3218
- async onInputsChanged() { },
3219
- }),
3220
- policy: { asyncConcurrency: "switch" },
3221
- };
3222
- r.categories.register(category);
3223
- }
3224
- // Coercions (client-side no-op to satisfy validation) if provided
3225
- for (const c of desc.coercions) {
3226
- if (c.async) {
3227
- r.registerAsyncCoercion(c.from, c.to, async (v) => v, {
3228
- nonTransitive: c.nonTransitive,
3229
- });
3230
- }
3231
- else {
3232
- r.registerCoercion(c.from, c.to, (v) => v, {
3233
- nonTransitive: c.nonTransitive,
3234
- });
3235
- }
3236
- }
3237
- // Nodes (use no-op impl for compute)
3238
- for (const n of desc.nodes) {
3239
- r.registerNode({
3240
- id: n.id,
3241
- categoryId: n.categoryId,
3242
- displayName: n.displayName,
3243
- inputs: n.inputs || {},
3244
- outputs: n.outputs || {},
3245
- impl: () => { },
3246
- });
3247
- }
3248
- setRegistry(r);
3249
- wb.setRegistry(r);
3250
- await transport.close();
3251
- }
3252
- catch (err) {
3253
- console.error("Failed to hydrate registry from backend:", err);
3254
- }
3255
- }, [setRegistry, wb]);
3329
+ // Registry is now automatically fetched by RemoteGraphRunner on first connection
3330
+ // No need for manual hydration
3256
3331
  // Ensure initial example is loaded (and sync when example prop changes)
3257
3332
  React.useEffect(() => {
3258
3333
  if (!example)
@@ -3263,6 +3338,21 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3263
3338
  const off = runner.on("transport", (s) => setTransportStatus(s));
3264
3339
  return () => off();
3265
3340
  }, [runner]);
3341
+ // Track registry readiness for remote backends
3342
+ React.useEffect(() => {
3343
+ // For local backends, registry is always ready
3344
+ if (backendKind === "local") {
3345
+ setRegistryReady(true);
3346
+ return;
3347
+ }
3348
+ // Reset readiness when switching to remote backend
3349
+ setRegistryReady(false);
3350
+ // For remote backends, wait for registry event
3351
+ const off = runner.on("registry", () => {
3352
+ setRegistryReady(true);
3353
+ });
3354
+ return () => off();
3355
+ }, [runner, backendKind]);
3266
3356
  React.useEffect(() => {
3267
3357
  if (!engine)
3268
3358
  return;
@@ -3284,25 +3374,13 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3284
3374
  // ignore
3285
3375
  }
3286
3376
  }, [engine, runner, wb, backendKind]);
3287
- // When switching to remote backend, auto-hydrate registry from backend
3288
- React.useEffect(() => {
3289
- let hydrate;
3290
- if (backendKind === "remote-http" && httpBaseUrl) {
3291
- hydrate = hydrateFromBackend("remote-http", httpBaseUrl);
3292
- }
3293
- else if (backendKind === "remote-ws" && wsUrl) {
3294
- hydrate = hydrateFromBackend("remote-ws", wsUrl);
3295
- }
3296
- if (hydrate) {
3297
- hydrate.then(() => {
3298
- setHydrated(true);
3299
- });
3300
- }
3301
- }, [backendKind, httpBaseUrl, wsUrl, hydrateFromBackend, setHydrated]);
3377
+ // Registry is automatically fetched by RemoteGraphRunner when it connects
3378
+ // Run auto layout after registry is hydrated (for remote backends)
3302
3379
  React.useEffect(() => {
3303
3380
  if (autoLayoutRan.current)
3304
3381
  return;
3305
- if (backendKind !== "local" && !hydrated)
3382
+ // Wait for registry to be ready for remote backends
3383
+ if (backendKind !== "local" && !registryReady)
3306
3384
  return;
3307
3385
  const cur = wb.export();
3308
3386
  const positions = wb.getPositions();
@@ -3311,7 +3389,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3311
3389
  autoLayoutRan.current = true;
3312
3390
  runAutoLayout();
3313
3391
  }
3314
- }, [wb, runAutoLayout, backendKind, hydrated]);
3392
+ }, [wb, runAutoLayout, backendKind, registryReady, registry]);
3315
3393
  const baseSetInput = React.useCallback((handle, raw) => {
3316
3394
  if (!selectedNodeId)
3317
3395
  return;
@@ -3421,7 +3499,9 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3421
3499
  value !== null &&
3422
3500
  "url" in value &&
3423
3501
  typeof value.url === "string") {
3424
- const title = ("title" in value && typeof value.title === "string") ? value.title : "";
3502
+ const title = "title" in value && typeof value.title === "string"
3503
+ ? value.title
3504
+ : "";
3425
3505
  const url = String(value.url || "");
3426
3506
  // value.ts handles data URL formatting
3427
3507
  return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
@@ -3434,7 +3514,11 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3434
3514
  const round4 = (n) => Math.round(Number(n) * 10000) / 10000;
3435
3515
  if (typeId === "base.vec3" && Array.isArray(value)) {
3436
3516
  const a = value;
3437
- return [round4(Number(a[0] ?? 0)), round4(Number(a[1] ?? 0)), round4(Number(a[2] ?? 0))].join(",");
3517
+ return [
3518
+ round4(Number(a[0] ?? 0)),
3519
+ round4(Number(a[1] ?? 0)),
3520
+ round4(Number(a[2] ?? 0)),
3521
+ ].join(",");
3438
3522
  }
3439
3523
  const stringifyRounded = (v) => {
3440
3524
  try {
@@ -3519,21 +3603,38 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3519
3603
  }
3520
3604
  }, children: "Download Snapshot" }), jsxRuntime.jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: triggerUpload, children: "Upload Graph/Snapshot" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement, contextPanel: overrides?.contextPanel })] })] }));
3521
3605
  }
3522
- function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, }) {
3606
+ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
3523
3607
  const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
3524
3608
  const [wb] = React.useState(() => new InMemoryWorkbench({ ui: new DefaultUIExtensionRegistry() }));
3525
3609
  const runner = React.useMemo(() => {
3526
3610
  if (backendKind === "remote-http") {
3527
- return new RemoteGraphRunner(registry, {
3611
+ const backend = {
3528
3612
  kind: "remote-http",
3529
3613
  baseUrl: httpBaseUrl,
3530
- });
3614
+ ...(backendOptions?.connectOptions && {
3615
+ connectOptions: backendOptions.connectOptions,
3616
+ }),
3617
+ ...(backendOptions?.onCustomEvent && {
3618
+ onCustomEvent: backendOptions.onCustomEvent,
3619
+ }),
3620
+ };
3621
+ return new RemoteGraphRunner(registry, backend);
3531
3622
  }
3532
3623
  if (backendKind === "remote-ws") {
3533
- return new RemoteGraphRunner(registry, { kind: "remote-ws", url: wsUrl });
3624
+ const backend = {
3625
+ kind: "remote-ws",
3626
+ url: wsUrl,
3627
+ ...(backendOptions?.connectOptions && {
3628
+ connectOptions: backendOptions.connectOptions,
3629
+ }),
3630
+ ...(backendOptions?.onCustomEvent && {
3631
+ onCustomEvent: backendOptions.onCustomEvent,
3632
+ }),
3633
+ };
3634
+ return new RemoteGraphRunner(registry, backend);
3534
3635
  }
3535
3636
  return new LocalGraphRunner(registry);
3536
- }, [registry, backendKind, httpBaseUrl, wsUrl]);
3637
+ }, [registry, backendKind, httpBaseUrl, wsUrl, backendOptions]);
3537
3638
  // Allow external UI registration (e.g., node renderers) with access to wb
3538
3639
  React.useEffect(() => {
3539
3640
  const baseRegisterUI = (_wb) => { };
@@ -3544,7 +3645,7 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
3544
3645
  if (runner.isRunning())
3545
3646
  runner.dispose();
3546
3647
  onBackendKindChange(v);
3547
- }, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, overrides: overrides, onInit: onInit, onChange: onChange }) }));
3648
+ }, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, backendOptions: backendOptions, overrides: overrides, onInit: onInit, onChange: onChange }) }));
3548
3649
  }
3549
3650
 
3550
3651
  exports.AbstractWorkbench = AbstractWorkbench;