@bian-womp/spark-graph 0.1.18 → 0.1.20

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
@@ -380,6 +380,16 @@ function isTypedOutput(v) {
380
380
  typeof v === "object" &&
381
381
  Object.prototype.hasOwnProperty.call(v, "__spark_type"));
382
382
  }
383
+ function getInputTypeId(inputs, handle) {
384
+ const v = inputs ? inputs[handle] : undefined;
385
+ if (!v)
386
+ return undefined;
387
+ return typeof v === "string" ? v : v.typeId;
388
+ }
389
+ function isInputPrivate(inputs, handle) {
390
+ const v = inputs ? inputs[handle] : undefined;
391
+ return !!(v && typeof v === "object" && v.private);
392
+ }
383
393
 
384
394
  class GraphRuntime {
385
395
  constructor() {
@@ -452,7 +462,7 @@ class GraphRuntime {
452
462
  if (dstNode) {
453
463
  const dstDesc = registry.nodes.get(dstNode.typeId);
454
464
  if (dstDesc) {
455
- dstDeclared = dstDesc.inputs[e.target.handle];
465
+ dstDeclared = getInputTypeId(dstDesc.inputs, e.target.handle);
456
466
  }
457
467
  }
458
468
  // Attach dynamic convert/convertAsync aware of union sources and typed outputs
@@ -1105,7 +1115,7 @@ class GraphRuntime {
1105
1115
  if (dstNode) {
1106
1116
  const dstDesc = registry.nodes.get(dstNode.typeId);
1107
1117
  if (dstDesc) {
1108
- dstDeclared = dstDesc.inputs[e.target.handle];
1118
+ dstDeclared = getInputTypeId(dstDesc.inputs, e.target.handle);
1109
1119
  }
1110
1120
  }
1111
1121
  const { convert, convertAsync } = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
@@ -1343,7 +1353,20 @@ class GraphBuilder {
1343
1353
  });
1344
1354
  }
1345
1355
  if (dstType) {
1346
- const declaredIn = dstType.inputs[e.target.handle];
1356
+ // Private inputs should not accept edges
1357
+ if (isInputPrivate(dstType.inputs, e.target.handle)) {
1358
+ issues.push({
1359
+ level: "error",
1360
+ code: "INPUT_PRIVATE",
1361
+ message: `Edge ${e.id} targets private input ${dstNode.typeId}.${e.target.handle}`,
1362
+ data: {
1363
+ edgeId: e.id,
1364
+ nodeId: dstNode.nodeId,
1365
+ input: e.target.handle,
1366
+ },
1367
+ });
1368
+ }
1369
+ const declaredIn = getInputTypeId(dstType.inputs, e.target.handle);
1347
1370
  if (declaredIn && effectiveTypeId) {
1348
1371
  // If source is a union, ensure each variant can reach declaredIn
1349
1372
  if (srcNode) {
@@ -1421,7 +1444,8 @@ class GraphBuilder {
1421
1444
  ? this.registry.nodes.get(innerNode.typeId)
1422
1445
  : undefined;
1423
1446
  const typeId = innerDesc ? innerDesc.inputs[map.handle] : undefined;
1424
- inputTypes[outerIn] = typeId ?? "untyped";
1447
+ inputTypes[outerIn] =
1448
+ typeof typeId === "string" ? typeId : typeId?.typeId ?? "untyped";
1425
1449
  }
1426
1450
  for (const [outerOut, map] of Object.entries(exposure.outputs)) {
1427
1451
  const innerNode = def.nodes.find((n) => n.nodeId === map.nodeId);
@@ -2134,6 +2158,57 @@ function setupBasicGraphRegistry() {
2134
2158
  return { Values: out };
2135
2159
  },
2136
2160
  });
2161
+ // Timer
2162
+ registry.registerNode({
2163
+ id: "base.timer",
2164
+ categoryId: "compute",
2165
+ inputs: {
2166
+ Enabled: "base.bool",
2167
+ IntervalMs: "base.float",
2168
+ Immediate: "base.bool",
2169
+ },
2170
+ outputs: { Now: "base.float", Count: "base.float" },
2171
+ inputDefaults: { Enabled: true, IntervalMs: 1000, Immediate: true },
2172
+ impl: (ins, ctx) => {
2173
+ const enabled = Boolean(ins.Enabled);
2174
+ const intervalMs = Math.max(1, Math.trunc(Number(ins.IntervalMs ?? 1000)));
2175
+ const immediate = Boolean(ins.Immediate);
2176
+ const stop = () => {
2177
+ const id = ctx.state.timerId;
2178
+ if (id !== undefined) {
2179
+ clearInterval(id);
2180
+ ctx.setState({ timerId: undefined });
2181
+ }
2182
+ };
2183
+ if (!enabled) {
2184
+ stop();
2185
+ return;
2186
+ }
2187
+ // restart timer with new settings
2188
+ stop();
2189
+ let count = 0;
2190
+ if (immediate) {
2191
+ ctx.emit("Now", Date.now());
2192
+ ctx.emit("Count", count);
2193
+ count += 1;
2194
+ }
2195
+ const id = setInterval(() => {
2196
+ ctx.emit("Now", Date.now());
2197
+ ctx.emit("Count", count);
2198
+ count += 1;
2199
+ }, intervalMs);
2200
+ ctx.setState({ timerId: id });
2201
+ },
2202
+ lifecycle: {
2203
+ dispose: (ctx) => {
2204
+ const id = ctx.state.timerId;
2205
+ if (id !== undefined) {
2206
+ clearInterval(id);
2207
+ ctx.setState({ timerId: undefined });
2208
+ }
2209
+ },
2210
+ },
2211
+ });
2137
2212
  return registry;
2138
2213
  }
2139
2214
  function registerDelayNode(registry) {
@@ -2411,6 +2486,8 @@ exports.createSimpleGraphDef = createSimpleGraphDef;
2411
2486
  exports.createSimpleGraphRegistry = createSimpleGraphRegistry;
2412
2487
  exports.createValidationGraphDef = createValidationGraphDef;
2413
2488
  exports.createValidationGraphRegistry = createValidationGraphRegistry;
2489
+ exports.getInputTypeId = getInputTypeId;
2490
+ exports.isInputPrivate = isInputPrivate;
2414
2491
  exports.isTypedOutput = isTypedOutput;
2415
2492
  exports.registerDelayNode = registerDelayNode;
2416
2493
  exports.registerProgressNodes = registerProgressNodes;