@workglow/task-graph 0.2.17 → 0.2.18

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/dist/common.d.ts CHANGED
@@ -20,9 +20,12 @@ export * from "./task-graph/TransformRegistry";
20
20
  export * from "./task-graph/TransformTypes";
21
21
  export * from "./task-graph/transforms";
22
22
  export * from "./task-graph/autoConnect";
23
+ export { registerRefcountablePredicate, asRefcountable, _resetRefcountablePredicatesForTests, type Refcountable, } from "./refcountable";
23
24
  export * from "./task";
24
25
  export * from "./storage/TaskGraphRepository";
25
26
  export * from "./storage/TaskGraphTabularRepository";
26
27
  export * from "./storage/TaskOutputRepository";
27
28
  export * from "./storage/TaskOutputTabularRepository";
29
+ export { registerPortCodec, getPortCodec, _resetPortCodecsForTests } from "./storage/PortCodecRegistry";
30
+ export type { PortCodec } from "./storage/PortCodecRegistry";
28
31
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAE5C,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,QAAQ,CAAC;AAEvB,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAE5C,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAEzC,OAAO,EACL,6BAA6B,EAC7B,cAAc,EACd,oCAAoC,EACpC,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AAExB,cAAc,QAAQ,CAAC;AAEvB,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACxG,YAAY,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/node.js CHANGED
@@ -584,6 +584,10 @@ function computeGraphEntitlements(graph, options) {
584
584
  }
585
585
  // src/task/InputResolver.ts
586
586
  import { getInputResolvers } from "@workglow/util";
587
+ function isPlainObject(value) {
588
+ const proto = Object.getPrototypeOf(value);
589
+ return proto === Object.prototype || proto === null;
590
+ }
587
591
  function getSchemaFormat(schema, visited = new WeakSet) {
588
592
  if (typeof schema !== "object" || schema === null)
589
593
  return;
@@ -671,6 +675,7 @@ async function resolveSchemaInputs(input, schema, config, visited = new Set) {
671
675
  for (const [key, propSchema] of Object.entries(properties)) {
672
676
  let value = resolved[key];
673
677
  const format = getSchemaFormat(propSchema);
678
+ let phase1Transformed = false;
674
679
  if (format) {
675
680
  let resolver = resolvers.get(format);
676
681
  if (!resolver) {
@@ -681,14 +686,18 @@ async function resolveSchemaInputs(input, schema, config, visited = new Set) {
681
686
  if (typeof value === "string") {
682
687
  value = await resolver(value, format, config.registry);
683
688
  resolved[key] = value;
689
+ phase1Transformed = true;
684
690
  } else if (Array.isArray(value) && value.some((item) => typeof item === "string")) {
685
691
  const results = await Promise.all(value.map((item) => typeof item === "string" ? resolver(item, format, config.registry) : item));
686
692
  value = results.filter((result) => result !== undefined);
687
693
  resolved[key] = value;
694
+ phase1Transformed = true;
688
695
  }
689
696
  }
690
697
  }
691
- if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
698
+ const hasFormatResolver = format ? !!(resolvers.get(format) ?? resolvers.get(getFormatPrefix(format))) : false;
699
+ const skipPhase2 = hasFormatResolver && !phase1Transformed;
700
+ if (!skipPhase2 && value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value) && isPlainObject(value)) {
692
701
  const objectSchema = getObjectSchema(propSchema);
693
702
  if (objectSchema && !visited.has(objectSchema)) {
694
703
  visited.add(objectSchema);
@@ -1134,6 +1143,31 @@ import {
1134
1143
  uuid4 as uuid43
1135
1144
  } from "@workglow/util";
1136
1145
 
1146
+ // src/refcountable.ts
1147
+ var GLOBAL_KEY = Symbol.for("@workglow/task-graph/refcountable.predicates");
1148
+ var _g = globalThis;
1149
+ if (!Array.isArray(_g[GLOBAL_KEY])) {
1150
+ _g[GLOBAL_KEY] = [];
1151
+ }
1152
+ var predicates = _g[GLOBAL_KEY];
1153
+ function registerRefcountablePredicate(p) {
1154
+ predicates.push(p);
1155
+ }
1156
+ function asRefcountable(v) {
1157
+ if (v === null || v === undefined)
1158
+ return null;
1159
+ if (typeof v !== "object")
1160
+ return null;
1161
+ for (const p of predicates) {
1162
+ if (p(v))
1163
+ return v;
1164
+ }
1165
+ return null;
1166
+ }
1167
+ function _resetRefcountablePredicatesForTests() {
1168
+ predicates.length = 0;
1169
+ }
1170
+
1137
1171
  // src/storage/TaskOutputRepository.ts
1138
1172
  import { createServiceToken as createServiceToken2, EventEmitter as EventEmitter2 } from "@workglow/util";
1139
1173
  var TASK_OUTPUT_REPOSITORY = createServiceToken2("taskgraph.taskOutputRepository");
@@ -1245,6 +1279,7 @@ import {
1245
1279
  globalServiceRegistry as globalServiceRegistry2,
1246
1280
  SpanStatusCode
1247
1281
  } from "@workglow/util";
1282
+ import { getPortCodec } from "@workglow/util";
1248
1283
 
1249
1284
  // src/task/StreamTypes.ts
1250
1285
  function getPortStreamMode(schema, portId) {
@@ -1348,6 +1383,42 @@ function hasStructuredOutput(schema) {
1348
1383
  }
1349
1384
 
1350
1385
  // src/task/TaskRunner.ts
1386
+ async function serializeOutputPorts(output, schema) {
1387
+ if (!schema?.properties)
1388
+ return output;
1389
+ const out = { ...output };
1390
+ for (const [key, prop] of Object.entries(schema.properties)) {
1391
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1392
+ if (codec && out[key] !== undefined) {
1393
+ out[key] = await codec.serialize(out[key]);
1394
+ }
1395
+ }
1396
+ return out;
1397
+ }
1398
+ async function deserializeOutputPorts(output, schema) {
1399
+ if (!schema?.properties)
1400
+ return output;
1401
+ const out = { ...output };
1402
+ for (const [key, prop] of Object.entries(schema.properties)) {
1403
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1404
+ if (codec && out[key] !== undefined) {
1405
+ out[key] = await codec.deserialize(out[key]);
1406
+ }
1407
+ }
1408
+ return out;
1409
+ }
1410
+ async function normalizeInputsForCacheKey(inputs, schema) {
1411
+ if (!schema?.properties)
1412
+ return inputs;
1413
+ const out = { ...inputs };
1414
+ for (const [key, prop] of Object.entries(schema.properties)) {
1415
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1416
+ if (codec && out[key] !== undefined) {
1417
+ out[key] = await codec.serialize(out[key]);
1418
+ }
1419
+ }
1420
+ return out;
1421
+ }
1351
1422
  function hasRunConfig(i) {
1352
1423
  return i !== null && typeof i === "object" && "runConfig" in i;
1353
1424
  }
@@ -1403,9 +1474,13 @@ class TaskRunner {
1403
1474
  getLogger().warn(`Task "${this.task.type}" declares streaming output (x-stream: "${streamMode}") ` + `but does not implement executeStream(). Falling back to non-streaming execute().`);
1404
1475
  }
1405
1476
  }
1477
+ const inputSchema = this.task.constructor.inputSchema();
1478
+ const outputSchema = this.task.constructor.outputSchema();
1479
+ const inputsForKey = this.outputCache ? await normalizeInputsForCacheKey(inputs, inputSchema) : inputs;
1406
1480
  if (this.task.cacheable) {
1407
- outputs = await this.outputCache?.getOutput(this.task.type, inputs);
1408
- if (outputs) {
1481
+ const cached = await this.outputCache?.getOutput(this.task.type, inputsForKey);
1482
+ if (cached !== undefined) {
1483
+ outputs = await deserializeOutputPorts(cached, outputSchema);
1409
1484
  this.telemetrySpan?.addEvent("workglow.task.cache_hit");
1410
1485
  if (isStreamable) {
1411
1486
  this.task.runOutputData = outputs;
@@ -1424,7 +1499,8 @@ class TaskRunner {
1424
1499
  outputs = await this.executeTask(inputs);
1425
1500
  }
1426
1501
  if (this.task.cacheable && outputs !== undefined) {
1427
- await this.outputCache?.saveOutput(this.task.type, inputs, outputs);
1502
+ const wireOutputs = await serializeOutputPorts(outputs, outputSchema);
1503
+ await this.outputCache?.saveOutput(this.task.type, inputsForKey, wireOutputs);
1428
1504
  }
1429
1505
  this.task.runOutputData = outputs ?? {};
1430
1506
  }
@@ -3046,6 +3122,23 @@ class TaskGraphRunner {
3046
3122
  }
3047
3123
  async pushOutputFromNodeToEdges(node, results) {
3048
3124
  const dataflows = this.graph.getTargetDataflows(node.id);
3125
+ if (Object.keys(results).length > 0) {
3126
+ const consumerCounts = new Map;
3127
+ for (const dataflow of dataflows) {
3128
+ if (dataflow.stream !== undefined)
3129
+ continue;
3130
+ const port = dataflow.sourceTaskPortId;
3131
+ consumerCounts.set(port, (consumerCounts.get(port) ?? 0) + 1);
3132
+ }
3133
+ for (const [port, count] of consumerCounts) {
3134
+ if (count <= 1)
3135
+ continue;
3136
+ const value = results[port];
3137
+ const ref = asRefcountable(value);
3138
+ if (ref)
3139
+ ref.retain(count - 1);
3140
+ }
3141
+ }
3049
3142
  for (const dataflow of dataflows) {
3050
3143
  if (dataflow.stream !== undefined)
3051
3144
  continue;
@@ -8121,6 +8214,14 @@ class TaskOutputTabularRepository extends TaskOutputRepository {
8121
8214
  this.emit("output_pruned");
8122
8215
  }
8123
8216
  }
8217
+ // src/storage/PortCodecRegistry.ts
8218
+ import {
8219
+ registerPortCodec,
8220
+ getPortCodec as getPortCodec2,
8221
+ _resetPortCodecsForTests
8222
+ } from "@workglow/util";
8223
+ // src/node.ts
8224
+ registerRefcountablePredicate((v) => !!v && typeof v === "object" && ("backend" in v) && ("retain" in v) && ("release" in v) && ("materialize" in v));
8124
8225
  export {
8125
8226
  wrapSchemaInArray,
8126
8227
  whileTaskConfigSchema,
@@ -8143,6 +8244,8 @@ export {
8143
8244
  resolveIterationBound,
8144
8245
  resetMethodNameCache,
8145
8246
  removeIterationProperties,
8247
+ registerRefcountablePredicate,
8248
+ registerPortCodec,
8146
8249
  registerJobQueueFactory,
8147
8250
  registerBuiltInTransforms,
8148
8251
  registerBaseTasks,
@@ -8178,6 +8281,7 @@ export {
8178
8281
  getSchemaFormat,
8179
8282
  getProfileGrants,
8180
8283
  getPortStreamMode,
8284
+ getPortCodec2 as getPortCodec,
8181
8285
  getOutputStreamMode,
8182
8286
  getObjectSchema,
8183
8287
  getObjectPortId,
@@ -8224,9 +8328,12 @@ export {
8224
8328
  calculateNodeDepths,
8225
8329
  buildIterationInputSchema,
8226
8330
  autoConnect,
8331
+ asRefcountable,
8227
8332
  addIterationContextToSchema,
8228
8333
  addBoundaryNodesToGraphJson,
8229
8334
  addBoundaryNodesToDependencyJson,
8335
+ _resetRefcountablePredicatesForTests,
8336
+ _resetPortCodecsForTests,
8230
8337
  WorkflowError,
8231
8338
  Workflow,
8232
8339
  WhileTaskRunner,
@@ -8300,4 +8407,4 @@ export {
8300
8407
  BROWSER_GRANTS
8301
8408
  };
8302
8409
 
8303
- //# debugId=8744F550EEB96ECE64756E2164756E21
8410
+ //# debugId=3044ECF32B8CA32464756E2164756E21