@bian-womp/spark-workbench 0.2.25 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"WorkbenchStudio.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchStudio.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAgBjE,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,yBAAyB,CAAC;AASjC,OAAO,EAGL,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAGL,oBAAoB,EACpB,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACtC;AA82BD,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,cAAc,EACd,OAAO,EACP,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,aAAa,EACb,KAAK,EACL,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,oBAAoB,CAAC;IAClC,mBAAmB,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QACd,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,YAAY,CAAC;QACrB,eAAe,EAAE,CACf,GAAG,EAAE,eAAe,EACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;KACpB,KAAK,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAChB,GAAG,EAAE,eAAe,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACjD,KAAK,IAAI,CAAC;CACZ,2CAkFA"}
1
+ {"version":3,"file":"WorkbenchStudio.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchStudio.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAejE,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AASvE,OAAO,EAGL,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAGL,oBAAoB,EACpB,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACtC;AAizBD,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,cAAc,EACd,OAAO,EACP,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,aAAa,EACb,KAAK,EACL,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,oBAAoB,CAAC;IAClC,mBAAmB,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QACd,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,YAAY,CAAC;QACrB,eAAe,EAAE,CACf,GAAG,EAAE,eAAe,EACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;KACpB,KAAK,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAChB,GAAG,EAAE,eAAe,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACjD,KAAK,IAAI,CAAC;CACZ,2CAmFA"}
@@ -13,6 +13,10 @@ export declare class RemoteGraphRunner extends AbstractGraphRunner {
13
13
  runtimeTypeId?: string;
14
14
  }>;
15
15
  listenersBound: boolean;
16
+ private registryFetched;
17
+ private registryFetching;
18
+ private readonly MAX_REGISTRY_FETCH_ATTEMPTS;
19
+ private readonly INITIAL_RETRY_DELAY_MS;
16
20
  constructor(registry: Registry, backend: RemoteExecutionBackend);
17
21
  build(def: GraphDefinition): void;
18
22
  update(def: GraphDefinition): void;
@@ -31,6 +35,11 @@ export declare class RemoteGraphRunner extends AbstractGraphRunner {
31
35
  getOutputs(def: GraphDefinition): Record<string, Record<string, unknown>>;
32
36
  getInputs(def: GraphDefinition): Record<string, Record<string, unknown>>;
33
37
  dispose(): void;
38
+ /**
39
+ * Fetch full registry description from remote and register it locally.
40
+ * Called automatically on first connection with retry mechanism.
41
+ */
42
+ private fetchRegistry;
34
43
  protected ensureRemoteRunner(): Promise<RemoteRunner>;
35
44
  }
36
45
  //# sourceMappingURL=RemoteGraphRunner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteGraphRunner.d.ts","sourceRoot":"","sources":["../../../../src/runtime/RemoteGraphRunner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAU,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAEL,YAAY,EAEb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,sBAAsB,EAEtB,eAAe,EACf,aAAa,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,iBAAkB,SAAQ,mBAAmB;IACxD,SAAS,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACpC,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAChC,SAAS,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IAEtC,UAAU;YAEF,OAAO,GAAG,QAAQ;eAAS,OAAO;wBAAkB,MAAM;OAC9D;IACJ,cAAc,UAAS;gBAEX,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB;IA8D/D,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAIjC,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAWlC,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;IAyDjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,KAAK,IAAI,IAAI;IAIb,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ/C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,YAAY;IASZ,iBAAiB,CAAC,OAAO,EAAE,mBAAmB;IAKpD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAWvE,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAOrD,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAoBzE,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAmBxE,OAAO,IAAI,IAAI;cAYC,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC;CA6C5D"}
1
+ {"version":3,"file":"RemoteGraphRunner.d.ts","sourceRoot":"","sources":["../../../../src/runtime/RemoteGraphRunner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAU,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAEL,YAAY,EAEb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,sBAAsB,EAEtB,eAAe,EACf,aAAa,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,iBAAkB,SAAQ,mBAAmB;IACxD,SAAS,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACpC,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAChC,SAAS,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IAEtC,UAAU;YAEF,OAAO,GAAG,QAAQ;eAAS,OAAO;wBAAkB,MAAM;OAC9D;IACJ,cAAc,UAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAK;IACjD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAQ;gBAEnC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB;IA8D/D,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAIjC,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAWlC,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;IAyDjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,KAAK,IAAI,IAAI;IAIb,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ/C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,YAAY;IASZ,iBAAiB,CAAC,OAAO,EAAE,mBAAmB;IAKpD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAWvE,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAOrD,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAoBzE,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAmBxE,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,aAAa;cA6HX,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC;CAuD5D"}
package/lib/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { GraphBuilder, StepEngine, HybridEngine, PullEngine, BatchedEngine, PushEngine, isTypedOutput, getTypedOutputValue, getTypedOutputTypeId, isInputPrivate, getInputTypeId, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, Registry } from '@bian-womp/spark-graph';
1
+ import { GraphBuilder, StepEngine, HybridEngine, PullEngine, BatchedEngine, PushEngine, isTypedOutput, getTypedOutputValue, getTypedOutputTypeId, isInputPrivate, getInputTypeId, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
2
2
  import { HttpPollingTransport, WebSocketTransport, RemoteRunner } from '@bian-womp/spark-remote';
3
3
  import React, { useCallback, useState, useRef, useEffect, useMemo, createContext, useContext, useImperativeHandle } from 'react';
4
4
  import { Position, Handle, useUpdateNodeInternals, useReactFlow, ReactFlowProvider, ReactFlow, Background, BackgroundVariant, MiniMap, Controls } from '@xyflow/react';
@@ -570,6 +570,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
570
570
  super(registry, backend);
571
571
  this.valueCache = new Map();
572
572
  this.listenersBound = false;
573
+ this.registryFetched = false;
574
+ this.registryFetching = false;
575
+ this.MAX_REGISTRY_FETCH_ATTEMPTS = 3;
576
+ this.INITIAL_RETRY_DELAY_MS = 1000; // 1 second
573
577
  // Auto-handle registry-changed invalidations from remote
574
578
  // We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
575
579
  this.ensureRemoteRunner().then(async (runner) => {
@@ -804,11 +808,125 @@ class RemoteGraphRunner extends AbstractGraphRunner {
804
808
  super.dispose();
805
809
  this.runner = undefined;
806
810
  this.transport = undefined;
811
+ this.registryFetched = false; // Reset so registry is fetched again on reconnect
812
+ this.registryFetching = false; // Reset fetching state
807
813
  this.emit("transport", {
808
814
  state: "disconnected",
809
815
  kind: this.backend.kind,
810
816
  });
811
817
  }
818
+ /**
819
+ * Fetch full registry description from remote and register it locally.
820
+ * Called automatically on first connection with retry mechanism.
821
+ */
822
+ async fetchRegistry(runner, attempt = 1) {
823
+ if (this.registryFetching) {
824
+ // Already fetching, don't start another fetch
825
+ return;
826
+ }
827
+ this.registryFetching = true;
828
+ try {
829
+ const desc = await runner.describeRegistry();
830
+ // Register types
831
+ for (const t of desc.types) {
832
+ if (t.options) {
833
+ this.registry.registerEnum({
834
+ id: t.id,
835
+ options: t.options,
836
+ bakeTarget: t.bakeTarget,
837
+ });
838
+ }
839
+ else {
840
+ if (!this.registry.types.has(t.id)) {
841
+ this.registry.registerType({
842
+ id: t.id,
843
+ displayName: t.displayName,
844
+ validate: (_v) => true,
845
+ bakeTarget: t.bakeTarget,
846
+ });
847
+ }
848
+ }
849
+ }
850
+ // Register categories
851
+ for (const c of desc.categories || []) {
852
+ if (!this.registry.categories.has(c.id)) {
853
+ // Create placeholder category descriptor
854
+ const category = {
855
+ id: c.id,
856
+ displayName: c.displayName,
857
+ createRuntime: () => ({
858
+ async onInputsChanged() { },
859
+ }),
860
+ policy: { asyncConcurrency: "switch" },
861
+ };
862
+ this.registry.categories.register(category);
863
+ }
864
+ }
865
+ // Register coercions
866
+ for (const c of desc.coercions) {
867
+ if (c.async) {
868
+ this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
869
+ nonTransitive: c.nonTransitive,
870
+ });
871
+ }
872
+ else {
873
+ this.registry.registerCoercion(c.from, c.to, (v) => v, {
874
+ nonTransitive: c.nonTransitive,
875
+ });
876
+ }
877
+ }
878
+ // Register nodes
879
+ for (const n of desc.nodes) {
880
+ if (!this.registry.nodes.has(n.id)) {
881
+ this.registry.registerNode({
882
+ id: n.id,
883
+ categoryId: n.categoryId,
884
+ displayName: n.displayName,
885
+ inputs: n.inputs || {},
886
+ outputs: n.outputs || {},
887
+ impl: () => { },
888
+ });
889
+ }
890
+ }
891
+ this.registryFetched = true;
892
+ this.registryFetching = false;
893
+ this.emit("registry", this.registry);
894
+ }
895
+ catch (err) {
896
+ this.registryFetching = false;
897
+ const error = err instanceof Error ? err : new Error(String(err));
898
+ // Retry with exponential backoff if attempts remaining
899
+ if (attempt < this.MAX_REGISTRY_FETCH_ATTEMPTS) {
900
+ const delayMs = this.INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt - 1);
901
+ console.warn(`Failed to fetch registry (attempt ${attempt}/${this.MAX_REGISTRY_FETCH_ATTEMPTS}), retrying in ${delayMs}ms...`, error);
902
+ // Emit error event for UI feedback
903
+ this.emit("error", {
904
+ kind: "registry",
905
+ message: `Registry fetch failed (attempt ${attempt}/${this.MAX_REGISTRY_FETCH_ATTEMPTS}), retrying...`,
906
+ err: error,
907
+ attempt,
908
+ maxAttempts: this.MAX_REGISTRY_FETCH_ATTEMPTS,
909
+ });
910
+ // Retry after delay
911
+ setTimeout(() => {
912
+ this.fetchRegistry(runner, attempt + 1).catch(() => {
913
+ // Final failure handled below
914
+ });
915
+ }, delayMs);
916
+ }
917
+ else {
918
+ // Max attempts reached, emit final error
919
+ console.error(`Failed to fetch registry after ${this.MAX_REGISTRY_FETCH_ATTEMPTS} attempts:`, error);
920
+ this.emit("error", {
921
+ kind: "registry",
922
+ message: `Failed to fetch registry after ${this.MAX_REGISTRY_FETCH_ATTEMPTS} attempts. Please check your connection and try refreshing.`,
923
+ err: error,
924
+ attempt: this.MAX_REGISTRY_FETCH_ATTEMPTS,
925
+ maxAttempts: this.MAX_REGISTRY_FETCH_ATTEMPTS,
926
+ });
927
+ }
928
+ }
929
+ }
812
930
  // Ensure remote transport/runner
813
931
  async ensureRemoteRunner() {
814
932
  if (this.runner)
@@ -854,6 +972,14 @@ class RemoteGraphRunner extends AbstractGraphRunner {
854
972
  this.valueCache.clear();
855
973
  this.listenersBound = false;
856
974
  this.emit("transport", { state: "connected", kind });
975
+ // Auto-fetch registry on first connection (only once)
976
+ if (!this.registryFetched && !this.registryFetching) {
977
+ // Log loading state (UI can listen to transport status for loading indication)
978
+ console.info("Loading registry from remote...");
979
+ this.fetchRegistry(runner).catch(() => {
980
+ // Error handling is done inside fetchRegistry
981
+ });
982
+ }
857
983
  return runner;
858
984
  }
859
985
  }
@@ -2970,7 +3096,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
2970
3096
  }, 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: [jsx(Background, { id: "workbench-canvas-background", variant: BackgroundVariant.Dots, gap: 12, size: 1 }), jsx(MiniMap, {}), jsx(Controls, {}), jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: onCloseMenu }), !!nodeAtMenu && (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: onCloseNodeMenu }))] }) }) }));
2971
3097
  });
2972
3098
 
2973
- function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
3099
+ 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, }) {
2974
3100
  const { wb, runner, registry, def, selectedNodeId, runAutoLayout } = useWorkbenchContext();
2975
3101
  const [transportStatus, setTransportStatus] = useState({
2976
3102
  state: "local",
@@ -3019,11 +3145,14 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3019
3145
  return overrides.getExamples(defaultExamples);
3020
3146
  return defaultExamples;
3021
3147
  }, [overrides, defaultExamples]);
3022
- const [hydrated, setHydrated] = useState(false);
3023
3148
  const lastAutoLaunched = useRef(undefined);
3024
3149
  const autoLayoutRan = useRef(false);
3025
3150
  const canvasRef = useRef(null);
3026
3151
  const uploadInputRef = useRef(null);
3152
+ const [registryReady, setRegistryReady] = useState(() => {
3153
+ // For local backends, registry is always ready
3154
+ return backendKind === "local";
3155
+ });
3027
3156
  // Expose init callback with setInitialGraph helper
3028
3157
  const initCalled = useRef(false);
3029
3158
  useEffect(() => {
@@ -3082,7 +3211,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3082
3211
  const { registry: r, def } = await ex.load();
3083
3212
  // Keep registry consistent with backend:
3084
3213
  // - For local backend, allow example to provide its own registry
3085
- // - For remote backend, NEVER overwrite the hydrated remote registry
3214
+ // - For remote backend, registry is automatically managed by RemoteGraphRunner
3086
3215
  if (backendKind === "local") {
3087
3216
  if (r) {
3088
3217
  setRegistry(r);
@@ -3195,79 +3324,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3195
3324
  const triggerUpload = useCallback(() => {
3196
3325
  uploadInputRef.current?.click();
3197
3326
  }, []);
3198
- const hydrateFromBackend = useCallback(async (kind, base) => {
3199
- try {
3200
- const transport = kind === "remote-http"
3201
- ? new HttpPollingTransport(base)
3202
- : new WebSocketTransport(base);
3203
- await transport.connect();
3204
- const rr = new RemoteRunner(transport);
3205
- const desc = await rr.describeRegistry();
3206
- const r = new Registry();
3207
- // Types
3208
- for (const t of desc.types) {
3209
- if (t.options) {
3210
- r.registerEnum({
3211
- id: t.id,
3212
- options: t.options,
3213
- bakeTarget: t.bakeTarget,
3214
- });
3215
- }
3216
- else {
3217
- r.registerType({
3218
- id: t.id,
3219
- displayName: t.displayName,
3220
- validate: (_v) => true,
3221
- bakeTarget: t.bakeTarget,
3222
- });
3223
- }
3224
- }
3225
- // Categories: create placeholders for display name
3226
- for (const c of desc.categories || []) {
3227
- // If you later expose real category descriptors, register them here
3228
- // For now, rely on ComputeCategory for behavior
3229
- const category = {
3230
- id: c.id,
3231
- displayName: c.displayName,
3232
- createRuntime: () => ({
3233
- async onInputsChanged() { },
3234
- }),
3235
- policy: { asyncConcurrency: "switch" },
3236
- };
3237
- r.categories.register(category);
3238
- }
3239
- // Coercions (client-side no-op to satisfy validation) if provided
3240
- for (const c of desc.coercions) {
3241
- if (c.async) {
3242
- r.registerAsyncCoercion(c.from, c.to, async (v) => v, {
3243
- nonTransitive: c.nonTransitive,
3244
- });
3245
- }
3246
- else {
3247
- r.registerCoercion(c.from, c.to, (v) => v, {
3248
- nonTransitive: c.nonTransitive,
3249
- });
3250
- }
3251
- }
3252
- // Nodes (use no-op impl for compute)
3253
- for (const n of desc.nodes) {
3254
- r.registerNode({
3255
- id: n.id,
3256
- categoryId: n.categoryId,
3257
- displayName: n.displayName,
3258
- inputs: n.inputs || {},
3259
- outputs: n.outputs || {},
3260
- impl: () => { },
3261
- });
3262
- }
3263
- setRegistry(r);
3264
- wb.setRegistry(r);
3265
- await transport.close();
3266
- }
3267
- catch (err) {
3268
- console.error("Failed to hydrate registry from backend:", err);
3269
- }
3270
- }, [setRegistry, wb]);
3327
+ // Registry is now automatically fetched by RemoteGraphRunner on first connection
3328
+ // No need for manual hydration
3271
3329
  // Ensure initial example is loaded (and sync when example prop changes)
3272
3330
  useEffect(() => {
3273
3331
  if (!example)
@@ -3278,6 +3336,21 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3278
3336
  const off = runner.on("transport", (s) => setTransportStatus(s));
3279
3337
  return () => off();
3280
3338
  }, [runner]);
3339
+ // Track registry readiness for remote backends
3340
+ useEffect(() => {
3341
+ // For local backends, registry is always ready
3342
+ if (backendKind === "local") {
3343
+ setRegistryReady(true);
3344
+ return;
3345
+ }
3346
+ // Reset readiness when switching to remote backend
3347
+ setRegistryReady(false);
3348
+ // For remote backends, wait for registry event
3349
+ const off = runner.on("registry", () => {
3350
+ setRegistryReady(true);
3351
+ });
3352
+ return () => off();
3353
+ }, [runner, backendKind]);
3281
3354
  useEffect(() => {
3282
3355
  if (!engine)
3283
3356
  return;
@@ -3299,25 +3372,13 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3299
3372
  // ignore
3300
3373
  }
3301
3374
  }, [engine, runner, wb, backendKind]);
3302
- // When switching to remote backend, auto-hydrate registry from backend
3303
- useEffect(() => {
3304
- let hydrate;
3305
- if (backendKind === "remote-http" && httpBaseUrl) {
3306
- hydrate = hydrateFromBackend("remote-http", httpBaseUrl);
3307
- }
3308
- else if (backendKind === "remote-ws" && wsUrl) {
3309
- hydrate = hydrateFromBackend("remote-ws", wsUrl);
3310
- }
3311
- if (hydrate) {
3312
- hydrate.then(() => {
3313
- setHydrated(true);
3314
- });
3315
- }
3316
- }, [backendKind, httpBaseUrl, wsUrl, hydrateFromBackend, setHydrated]);
3375
+ // Registry is automatically fetched by RemoteGraphRunner when it connects
3376
+ // Run auto layout after registry is hydrated (for remote backends)
3317
3377
  useEffect(() => {
3318
3378
  if (autoLayoutRan.current)
3319
3379
  return;
3320
- if (backendKind !== "local" && !hydrated)
3380
+ // Wait for registry to be ready for remote backends
3381
+ if (backendKind !== "local" && !registryReady)
3321
3382
  return;
3322
3383
  const cur = wb.export();
3323
3384
  const positions = wb.getPositions();
@@ -3326,7 +3387,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3326
3387
  autoLayoutRan.current = true;
3327
3388
  runAutoLayout();
3328
3389
  }
3329
- }, [wb, runAutoLayout, backendKind, hydrated]);
3390
+ }, [wb, runAutoLayout, backendKind, registryReady, registry]);
3330
3391
  const baseSetInput = useCallback((handle, raw) => {
3331
3392
  if (!selectedNodeId)
3332
3393
  return;
@@ -3582,7 +3643,7 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
3582
3643
  if (runner.isRunning())
3583
3644
  runner.dispose();
3584
3645
  onBackendKindChange(v);
3585
- }, 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 }) }));
3646
+ }, 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 }) }));
3586
3647
  }
3587
3648
 
3588
3649
  export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, formatDataUrlAsLabel, formatDeclaredTypeSignature, getNodeBorderClassNames, preformatValueForDisplay, resolveOutputDisplay, summarizeDeep, toReactFlow, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };