@bian-womp/spark-workbench 0.3.79 → 0.3.80
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 +109 -136
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +8 -14
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +109 -136
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +8 -14
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +5 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -1503,139 +1503,101 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1503
1503
|
getCacheKey(nodeId, handle, io) {
|
|
1504
1504
|
return `${nodeId}.${io}.${handle}`;
|
|
1505
1505
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
}
|
|
1515
|
-
// If already fetching, wait for that fetch to complete
|
|
1516
|
-
if (this.registryFetchPromise) {
|
|
1517
|
-
return this.registryFetchPromise;
|
|
1518
|
-
}
|
|
1519
|
-
// Create promise wrapper and assign atomically to prevent race conditions
|
|
1520
|
-
// This ensures concurrent calls will see the promise and wait for it
|
|
1521
|
-
let promise;
|
|
1522
|
-
this.registryFetchPromise = promise = (async () => {
|
|
1523
|
-
try {
|
|
1524
|
-
await this._doFetchRegistry(client);
|
|
1506
|
+
applyRegistryDescriptor(desc) {
|
|
1507
|
+
for (const t of desc.types) {
|
|
1508
|
+
if (t.options) {
|
|
1509
|
+
this.registry.registerEnum({
|
|
1510
|
+
id: t.id,
|
|
1511
|
+
options: t.options,
|
|
1512
|
+
bakeTarget: t.bakeTarget,
|
|
1513
|
+
});
|
|
1525
1514
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1515
|
+
else if (!this.registry.types.has(t.id)) {
|
|
1516
|
+
this.registry.registerType({
|
|
1517
|
+
id: t.id,
|
|
1518
|
+
displayName: t.displayName,
|
|
1519
|
+
validate: (_v) => true,
|
|
1520
|
+
bakeTarget: t.bakeTarget,
|
|
1521
|
+
});
|
|
1529
1522
|
}
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1523
|
+
}
|
|
1524
|
+
for (const c of desc.categories || []) {
|
|
1525
|
+
if (!this.registry.categories.has(c.id)) {
|
|
1526
|
+
const category = {
|
|
1527
|
+
id: c.id,
|
|
1528
|
+
displayName: c.displayName,
|
|
1529
|
+
createRuntime: () => ({
|
|
1530
|
+
async onInputsChanged() { },
|
|
1531
|
+
}),
|
|
1532
|
+
policy: { asyncConcurrency: "switch" },
|
|
1533
|
+
};
|
|
1534
|
+
this.registry.categories.register(category);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
for (const c of desc.coercions) {
|
|
1538
|
+
if (c.async) {
|
|
1539
|
+
this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
|
|
1540
|
+
nonTransitive: c.nonTransitive,
|
|
1544
1541
|
});
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
timeoutId = undefined;
|
|
1550
|
-
}
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
this.registry.registerCoercion(c.from, c.to, (v) => v, {
|
|
1545
|
+
nonTransitive: c.nonTransitive,
|
|
1551
1546
|
});
|
|
1552
|
-
const desc = await Promise.race([fetchPromise, timeoutPromise]);
|
|
1553
|
-
// Register types
|
|
1554
|
-
for (const t of desc.types) {
|
|
1555
|
-
if (t.options) {
|
|
1556
|
-
this.registry.registerEnum({
|
|
1557
|
-
id: t.id,
|
|
1558
|
-
options: t.options,
|
|
1559
|
-
bakeTarget: t.bakeTarget,
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
else {
|
|
1563
|
-
if (!this.registry.types.has(t.id)) {
|
|
1564
|
-
this.registry.registerType({
|
|
1565
|
-
id: t.id,
|
|
1566
|
-
displayName: t.displayName,
|
|
1567
|
-
validate: (_v) => true,
|
|
1568
|
-
bakeTarget: t.bakeTarget,
|
|
1569
|
-
});
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
// Register categories
|
|
1574
|
-
for (const c of desc.categories || []) {
|
|
1575
|
-
if (!this.registry.categories.has(c.id)) {
|
|
1576
|
-
// Create placeholder category descriptor
|
|
1577
|
-
const category = {
|
|
1578
|
-
id: c.id,
|
|
1579
|
-
displayName: c.displayName,
|
|
1580
|
-
createRuntime: () => ({
|
|
1581
|
-
async onInputsChanged() { },
|
|
1582
|
-
}),
|
|
1583
|
-
policy: { asyncConcurrency: "switch" },
|
|
1584
|
-
};
|
|
1585
|
-
this.registry.categories.register(category);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
// Register coercions
|
|
1589
|
-
for (const c of desc.coercions) {
|
|
1590
|
-
if (c.async) {
|
|
1591
|
-
this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
|
|
1592
|
-
nonTransitive: c.nonTransitive,
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
|
-
else {
|
|
1596
|
-
this.registry.registerCoercion(c.from, c.to, (v) => v, {
|
|
1597
|
-
nonTransitive: c.nonTransitive,
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
// Register nodes
|
|
1602
|
-
for (const n of desc.nodes) {
|
|
1603
|
-
if (!this.registry.nodes.has(n.id)) {
|
|
1604
|
-
this.registry.registerNode({
|
|
1605
|
-
id: n.id,
|
|
1606
|
-
categoryId: n.categoryId,
|
|
1607
|
-
displayName: n.displayName,
|
|
1608
|
-
inputs: n.inputs || {},
|
|
1609
|
-
outputs: n.outputs || {},
|
|
1610
|
-
policy: n.policy || {},
|
|
1611
|
-
impl: () => { },
|
|
1612
|
-
});
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
this.registryFetched = true;
|
|
1616
|
-
this.emit("registry", this.registry);
|
|
1617
|
-
return;
|
|
1618
1547
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1548
|
+
}
|
|
1549
|
+
for (const n of desc.nodes) {
|
|
1550
|
+
if (!this.registry.nodes.has(n.id)) {
|
|
1551
|
+
this.registry.registerNode({
|
|
1552
|
+
id: n.id,
|
|
1553
|
+
categoryId: n.categoryId,
|
|
1554
|
+
displayName: n.displayName,
|
|
1555
|
+
inputs: n.inputs || {},
|
|
1556
|
+
outputs: n.outputs || {},
|
|
1557
|
+
policy: n.policy || {},
|
|
1558
|
+
impl: () => { },
|
|
1559
|
+
});
|
|
1627
1560
|
}
|
|
1628
1561
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1562
|
+
this.registryFetched = true;
|
|
1563
|
+
if (this.registryBootstrapResolve) {
|
|
1564
|
+
this.registryBootstrapResolve();
|
|
1565
|
+
this.registryBootstrapResolve = undefined;
|
|
1566
|
+
this.registryBootstrapReject = undefined;
|
|
1567
|
+
}
|
|
1568
|
+
this.emit("registry", this.registry);
|
|
1569
|
+
}
|
|
1570
|
+
isRecord(value) {
|
|
1571
|
+
return typeof value === "object" && value !== null;
|
|
1572
|
+
}
|
|
1573
|
+
isRegistryDescriptorEvent(message) {
|
|
1574
|
+
if (!this.isRecord(message))
|
|
1575
|
+
return false;
|
|
1576
|
+
if (message.type !== "registry-descriptor")
|
|
1577
|
+
return false;
|
|
1578
|
+
if (!this.isRecord(message.payload))
|
|
1579
|
+
return false;
|
|
1580
|
+
return "registry" in message.payload;
|
|
1581
|
+
}
|
|
1582
|
+
waitForRegistryBootstrap() {
|
|
1583
|
+
if (this.registryFetched)
|
|
1584
|
+
return Promise.resolve();
|
|
1585
|
+
if (this.registryBootstrapPromise)
|
|
1586
|
+
return this.registryBootstrapPromise;
|
|
1587
|
+
this.registryBootstrapPromise = new Promise((resolve, reject) => {
|
|
1588
|
+
this.registryBootstrapResolve = resolve;
|
|
1589
|
+
this.registryBootstrapReject = reject;
|
|
1590
|
+
setTimeout(() => {
|
|
1591
|
+
if (!this.registryFetched && this.registryBootstrapReject) {
|
|
1592
|
+
this.registryBootstrapReject(new Error(`Registry bootstrap timeout (${this.REGISTRY_BOOTSTRAP_TIMEOUT_MS}ms exceeded)`));
|
|
1593
|
+
this.registryBootstrapResolve = undefined;
|
|
1594
|
+
this.registryBootstrapReject = undefined;
|
|
1595
|
+
}
|
|
1596
|
+
}, this.REGISTRY_BOOTSTRAP_TIMEOUT_MS);
|
|
1597
|
+
}).finally(() => {
|
|
1598
|
+
this.registryBootstrapPromise = undefined;
|
|
1637
1599
|
});
|
|
1638
|
-
|
|
1600
|
+
return this.registryBootstrapPromise;
|
|
1639
1601
|
}
|
|
1640
1602
|
/**
|
|
1641
1603
|
* Build RemoteRuntimeClient config from RemoteExecutionBackend config.
|
|
@@ -1697,13 +1659,27 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1697
1659
|
const clientConfig = this.buildClientConfig(backend);
|
|
1698
1660
|
// Wrap custom event handler to intercept viewport events and emit viewport event
|
|
1699
1661
|
const wrappedOnCustomEvent = (event) => {
|
|
1700
|
-
const msg = event
|
|
1701
|
-
if (msg &&
|
|
1702
|
-
const viewport = msg.payload
|
|
1662
|
+
const msg = event.message;
|
|
1663
|
+
if (this.isRecord(msg) && msg.type === "viewport" && this.isRecord(msg.payload)) {
|
|
1664
|
+
const viewport = msg.payload.viewport;
|
|
1703
1665
|
if (isValidViewport(viewport)) {
|
|
1704
1666
|
this.emit("viewport", { viewport });
|
|
1705
1667
|
}
|
|
1706
1668
|
}
|
|
1669
|
+
else if (this.isRecord(msg) && msg.type === "flow-opened" && this.isRecord(msg.payload)) {
|
|
1670
|
+
const sessionId = msg.payload.sessionId;
|
|
1671
|
+
const resumed = msg.payload.resumed;
|
|
1672
|
+
if (typeof sessionId === "number") {
|
|
1673
|
+
console.info(`[RemoteGraphRunner] Flow opened (runner=${this.runnerId}, sessionId=${sessionId}, resumed=${Boolean(resumed)})`);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
else if (this.isRecord(msg) && msg.type === "flow-closed" && this.isRecord(msg.payload)) {
|
|
1677
|
+
const reason = msg.payload.reason;
|
|
1678
|
+
console.warn(`[RemoteGraphRunner] Flow closed (runner=${this.runnerId}, reason=${typeof reason === "string" ? reason : "unknown"})`);
|
|
1679
|
+
}
|
|
1680
|
+
else if (this.isRegistryDescriptorEvent(msg)) {
|
|
1681
|
+
this.applyRegistryDescriptor(msg.payload.registry);
|
|
1682
|
+
}
|
|
1707
1683
|
// Call original handler if provided
|
|
1708
1684
|
if (backend.onCustomEvent) {
|
|
1709
1685
|
backend.onCustomEvent(event);
|
|
@@ -1721,12 +1697,11 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1721
1697
|
this.client = client;
|
|
1722
1698
|
this.valueCache.clear();
|
|
1723
1699
|
this.listenersBound = false;
|
|
1724
|
-
//
|
|
1725
|
-
// Only fetch if not already fetched (handles concurrent calls)
|
|
1700
|
+
// Wait for registry descriptor pushed by backend on flow-open.
|
|
1726
1701
|
if (!this.registryFetched) {
|
|
1727
|
-
console.info("[RemoteGraphRunner]
|
|
1728
|
-
await this.
|
|
1729
|
-
console.info("[RemoteGraphRunner]
|
|
1702
|
+
console.info("[RemoteGraphRunner] Waiting for registry bootstrap event...");
|
|
1703
|
+
await this.waitForRegistryBootstrap();
|
|
1704
|
+
console.info("[RemoteGraphRunner] Received registry bootstrap event");
|
|
1730
1705
|
}
|
|
1731
1706
|
// Clear promise on success
|
|
1732
1707
|
this.clientPromise = undefined;
|
|
@@ -1740,9 +1715,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1740
1715
|
this.valueCache = new Map();
|
|
1741
1716
|
this.listenersBound = false;
|
|
1742
1717
|
this.registryFetched = false;
|
|
1743
|
-
this.
|
|
1744
|
-
this.INITIAL_RETRY_DELAY_MS = 1000; // 1 second
|
|
1745
|
-
this.REGISTRY_FETCH_TIMEOUT_MS = 3000; // 3 seconds
|
|
1718
|
+
this.REGISTRY_BOOTSTRAP_TIMEOUT_MS = 15000; // 15 seconds
|
|
1746
1719
|
// Generate readable ID for this runner instance (e.g., remote-001, remote-002)
|
|
1747
1720
|
remoteRunnerCounter++;
|
|
1748
1721
|
this.runnerId = `remote-${String(remoteRunnerCounter).padStart(3, "0")}`;
|