@bian-womp/spark-workbench 0.3.78 → 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 +111 -143
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.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 +111 -143
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.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
|
@@ -1425,10 +1425,7 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1425
1425
|
const snapshot = await this.snapshotFull();
|
|
1426
1426
|
const converter = sparkGraph.buildValueConverter(converterConfig);
|
|
1427
1427
|
const converted = sparkGraph.convertSnapshot(snapshot, converter);
|
|
1428
|
-
await this.applySnapshotFull(converted, {
|
|
1429
|
-
skipBuild: true,
|
|
1430
|
-
dry: options?.dry,
|
|
1431
|
-
});
|
|
1428
|
+
await this.applySnapshotFull(converted, { skipBuild: true, dry: options?.dry });
|
|
1432
1429
|
return converted;
|
|
1433
1430
|
}
|
|
1434
1431
|
async pause() {
|
|
@@ -1506,139 +1503,101 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1506
1503
|
getCacheKey(nodeId, handle, io) {
|
|
1507
1504
|
return `${nodeId}.${io}.${handle}`;
|
|
1508
1505
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
// If already fetching, wait for that fetch to complete
|
|
1519
|
-
if (this.registryFetchPromise) {
|
|
1520
|
-
return this.registryFetchPromise;
|
|
1521
|
-
}
|
|
1522
|
-
// Create promise wrapper and assign atomically to prevent race conditions
|
|
1523
|
-
// This ensures concurrent calls will see the promise and wait for it
|
|
1524
|
-
let promise;
|
|
1525
|
-
this.registryFetchPromise = promise = (async () => {
|
|
1526
|
-
try {
|
|
1527
|
-
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
|
+
});
|
|
1528
1514
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
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
|
+
});
|
|
1532
1522
|
}
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
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,
|
|
1547
1541
|
});
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
timeoutId = undefined;
|
|
1553
|
-
}
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
this.registry.registerCoercion(c.from, c.to, (v) => v, {
|
|
1545
|
+
nonTransitive: c.nonTransitive,
|
|
1554
1546
|
});
|
|
1555
|
-
const desc = await Promise.race([fetchPromise, timeoutPromise]);
|
|
1556
|
-
// Register types
|
|
1557
|
-
for (const t of desc.types) {
|
|
1558
|
-
if (t.options) {
|
|
1559
|
-
this.registry.registerEnum({
|
|
1560
|
-
id: t.id,
|
|
1561
|
-
options: t.options,
|
|
1562
|
-
bakeTarget: t.bakeTarget,
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1565
|
-
else {
|
|
1566
|
-
if (!this.registry.types.has(t.id)) {
|
|
1567
|
-
this.registry.registerType({
|
|
1568
|
-
id: t.id,
|
|
1569
|
-
displayName: t.displayName,
|
|
1570
|
-
validate: (_v) => true,
|
|
1571
|
-
bakeTarget: t.bakeTarget,
|
|
1572
|
-
});
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
// Register categories
|
|
1577
|
-
for (const c of desc.categories || []) {
|
|
1578
|
-
if (!this.registry.categories.has(c.id)) {
|
|
1579
|
-
// Create placeholder category descriptor
|
|
1580
|
-
const category = {
|
|
1581
|
-
id: c.id,
|
|
1582
|
-
displayName: c.displayName,
|
|
1583
|
-
createRuntime: () => ({
|
|
1584
|
-
async onInputsChanged() { },
|
|
1585
|
-
}),
|
|
1586
|
-
policy: { asyncConcurrency: "switch" },
|
|
1587
|
-
};
|
|
1588
|
-
this.registry.categories.register(category);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
// Register coercions
|
|
1592
|
-
for (const c of desc.coercions) {
|
|
1593
|
-
if (c.async) {
|
|
1594
|
-
this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
|
|
1595
|
-
nonTransitive: c.nonTransitive,
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
else {
|
|
1599
|
-
this.registry.registerCoercion(c.from, c.to, (v) => v, {
|
|
1600
|
-
nonTransitive: c.nonTransitive,
|
|
1601
|
-
});
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
// Register nodes
|
|
1605
|
-
for (const n of desc.nodes) {
|
|
1606
|
-
if (!this.registry.nodes.has(n.id)) {
|
|
1607
|
-
this.registry.registerNode({
|
|
1608
|
-
id: n.id,
|
|
1609
|
-
categoryId: n.categoryId,
|
|
1610
|
-
displayName: n.displayName,
|
|
1611
|
-
inputs: n.inputs || {},
|
|
1612
|
-
outputs: n.outputs || {},
|
|
1613
|
-
policy: n.policy || {},
|
|
1614
|
-
impl: () => { },
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
this.registryFetched = true;
|
|
1619
|
-
this.emit("registry", this.registry);
|
|
1620
|
-
return;
|
|
1621
1547
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
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
|
+
});
|
|
1630
1560
|
}
|
|
1631
1561
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
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;
|
|
1640
1599
|
});
|
|
1641
|
-
|
|
1600
|
+
return this.registryBootstrapPromise;
|
|
1642
1601
|
}
|
|
1643
1602
|
/**
|
|
1644
1603
|
* Build RemoteRuntimeClient config from RemoteExecutionBackend config.
|
|
@@ -1700,13 +1659,27 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1700
1659
|
const clientConfig = this.buildClientConfig(backend);
|
|
1701
1660
|
// Wrap custom event handler to intercept viewport events and emit viewport event
|
|
1702
1661
|
const wrappedOnCustomEvent = (event) => {
|
|
1703
|
-
const msg = event
|
|
1704
|
-
if (msg &&
|
|
1705
|
-
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;
|
|
1706
1665
|
if (isValidViewport(viewport)) {
|
|
1707
1666
|
this.emit("viewport", { viewport });
|
|
1708
1667
|
}
|
|
1709
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
|
+
}
|
|
1710
1683
|
// Call original handler if provided
|
|
1711
1684
|
if (backend.onCustomEvent) {
|
|
1712
1685
|
backend.onCustomEvent(event);
|
|
@@ -1724,12 +1697,11 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1724
1697
|
this.client = client;
|
|
1725
1698
|
this.valueCache.clear();
|
|
1726
1699
|
this.listenersBound = false;
|
|
1727
|
-
//
|
|
1728
|
-
// Only fetch if not already fetched (handles concurrent calls)
|
|
1700
|
+
// Wait for registry descriptor pushed by backend on flow-open.
|
|
1729
1701
|
if (!this.registryFetched) {
|
|
1730
|
-
console.info("[RemoteGraphRunner]
|
|
1731
|
-
await this.
|
|
1732
|
-
console.info("[RemoteGraphRunner]
|
|
1702
|
+
console.info("[RemoteGraphRunner] Waiting for registry bootstrap event...");
|
|
1703
|
+
await this.waitForRegistryBootstrap();
|
|
1704
|
+
console.info("[RemoteGraphRunner] Received registry bootstrap event");
|
|
1733
1705
|
}
|
|
1734
1706
|
// Clear promise on success
|
|
1735
1707
|
this.clientPromise = undefined;
|
|
@@ -1743,9 +1715,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1743
1715
|
this.valueCache = new Map();
|
|
1744
1716
|
this.listenersBound = false;
|
|
1745
1717
|
this.registryFetched = false;
|
|
1746
|
-
this.
|
|
1747
|
-
this.INITIAL_RETRY_DELAY_MS = 1000; // 1 second
|
|
1748
|
-
this.REGISTRY_FETCH_TIMEOUT_MS = 3000; // 3 seconds
|
|
1718
|
+
this.REGISTRY_BOOTSTRAP_TIMEOUT_MS = 15000; // 15 seconds
|
|
1749
1719
|
// Generate readable ID for this runner instance (e.g., remote-001, remote-002)
|
|
1750
1720
|
remoteRunnerCounter++;
|
|
1751
1721
|
this.runnerId = `remote-${String(remoteRunnerCounter).padStart(3, "0")}`;
|
|
@@ -2032,9 +2002,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
2032
2002
|
this.hydrateValueCache(payload, { dry: options?.dry });
|
|
2033
2003
|
// Then sync with backend
|
|
2034
2004
|
const client = await this.ensureClient();
|
|
2035
|
-
await client.api.applySnapshotFull(payload, {
|
|
2036
|
-
skipBuild: options?.skipBuild,
|
|
2037
|
-
});
|
|
2005
|
+
await client.api.applySnapshotFull(payload, { skipBuild: options?.skipBuild });
|
|
2038
2006
|
}
|
|
2039
2007
|
async convertSnapshot(converterConfig, options) {
|
|
2040
2008
|
const client = await this.ensureClient();
|