@workglow/task-graph 0.2.17 → 0.2.19

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/bun.js CHANGED
@@ -585,6 +585,10 @@ function computeGraphEntitlements(graph, options) {
585
585
  }
586
586
  // src/task/InputResolver.ts
587
587
  import { getInputResolvers } from "@workglow/util";
588
+ function isPlainObject(value) {
589
+ const proto = Object.getPrototypeOf(value);
590
+ return proto === Object.prototype || proto === null;
591
+ }
588
592
  function getSchemaFormat(schema, visited = new WeakSet) {
589
593
  if (typeof schema !== "object" || schema === null)
590
594
  return;
@@ -672,6 +676,7 @@ async function resolveSchemaInputs(input, schema, config, visited = new Set) {
672
676
  for (const [key, propSchema] of Object.entries(properties)) {
673
677
  let value = resolved[key];
674
678
  const format = getSchemaFormat(propSchema);
679
+ let phase1Transformed = false;
675
680
  if (format) {
676
681
  let resolver = resolvers.get(format);
677
682
  if (!resolver) {
@@ -682,14 +687,18 @@ async function resolveSchemaInputs(input, schema, config, visited = new Set) {
682
687
  if (typeof value === "string") {
683
688
  value = await resolver(value, format, config.registry);
684
689
  resolved[key] = value;
690
+ phase1Transformed = true;
685
691
  } else if (Array.isArray(value) && value.some((item) => typeof item === "string")) {
686
692
  const results = await Promise.all(value.map((item) => typeof item === "string" ? resolver(item, format, config.registry) : item));
687
693
  value = results.filter((result) => result !== undefined);
688
694
  resolved[key] = value;
695
+ phase1Transformed = true;
689
696
  }
690
697
  }
691
698
  }
692
- if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
699
+ const hasFormatResolver = format ? !!(resolvers.get(format) ?? resolvers.get(getFormatPrefix(format))) : false;
700
+ const skipPhase2 = hasFormatResolver && !phase1Transformed;
701
+ if (!skipPhase2 && value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value) && isPlainObject(value)) {
693
702
  const objectSchema = getObjectSchema(propSchema);
694
703
  if (objectSchema && !visited.has(objectSchema)) {
695
704
  visited.add(objectSchema);
@@ -1135,6 +1144,31 @@ import {
1135
1144
  uuid4 as uuid43
1136
1145
  } from "@workglow/util";
1137
1146
 
1147
+ // src/refcountable.ts
1148
+ var GLOBAL_KEY = Symbol.for("@workglow/task-graph/refcountable.predicates");
1149
+ var _g = globalThis;
1150
+ if (!Array.isArray(_g[GLOBAL_KEY])) {
1151
+ _g[GLOBAL_KEY] = [];
1152
+ }
1153
+ var predicates = _g[GLOBAL_KEY];
1154
+ function registerRefcountablePredicate(p) {
1155
+ predicates.push(p);
1156
+ }
1157
+ function asRefcountable(v) {
1158
+ if (v === null || v === undefined)
1159
+ return null;
1160
+ if (typeof v !== "object")
1161
+ return null;
1162
+ for (const p of predicates) {
1163
+ if (p(v))
1164
+ return v;
1165
+ }
1166
+ return null;
1167
+ }
1168
+ function _resetRefcountablePredicatesForTests() {
1169
+ predicates.length = 0;
1170
+ }
1171
+
1138
1172
  // src/storage/TaskOutputRepository.ts
1139
1173
  import { createServiceToken as createServiceToken2, EventEmitter as EventEmitter2 } from "@workglow/util";
1140
1174
  var TASK_OUTPUT_REPOSITORY = createServiceToken2("taskgraph.taskOutputRepository");
@@ -1246,6 +1280,7 @@ import {
1246
1280
  globalServiceRegistry as globalServiceRegistry2,
1247
1281
  SpanStatusCode
1248
1282
  } from "@workglow/util";
1283
+ import { getPortCodec } from "@workglow/util";
1249
1284
 
1250
1285
  // src/task/StreamTypes.ts
1251
1286
  function getPortStreamMode(schema, portId) {
@@ -1349,6 +1384,42 @@ function hasStructuredOutput(schema) {
1349
1384
  }
1350
1385
 
1351
1386
  // src/task/TaskRunner.ts
1387
+ async function serializeOutputPorts(output, schema) {
1388
+ if (!schema?.properties)
1389
+ return output;
1390
+ const out = { ...output };
1391
+ for (const [key, prop] of Object.entries(schema.properties)) {
1392
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1393
+ if (codec && out[key] !== undefined) {
1394
+ out[key] = await codec.serialize(out[key]);
1395
+ }
1396
+ }
1397
+ return out;
1398
+ }
1399
+ async function deserializeOutputPorts(output, schema) {
1400
+ if (!schema?.properties)
1401
+ return output;
1402
+ const out = { ...output };
1403
+ for (const [key, prop] of Object.entries(schema.properties)) {
1404
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1405
+ if (codec && out[key] !== undefined) {
1406
+ out[key] = await codec.deserialize(out[key]);
1407
+ }
1408
+ }
1409
+ return out;
1410
+ }
1411
+ async function normalizeInputsForCacheKey(inputs, schema) {
1412
+ if (!schema?.properties)
1413
+ return inputs;
1414
+ const out = { ...inputs };
1415
+ for (const [key, prop] of Object.entries(schema.properties)) {
1416
+ const codec = prop.format ? getPortCodec(prop.format) : undefined;
1417
+ if (codec && out[key] !== undefined) {
1418
+ out[key] = await codec.serialize(out[key]);
1419
+ }
1420
+ }
1421
+ return out;
1422
+ }
1352
1423
  function hasRunConfig(i) {
1353
1424
  return i !== null && typeof i === "object" && "runConfig" in i;
1354
1425
  }
@@ -1365,6 +1436,7 @@ class TaskRunner {
1365
1436
  timeoutTimer;
1366
1437
  pendingTimeoutError;
1367
1438
  shouldAccumulate = true;
1439
+ runWithPreviews = false;
1368
1440
  telemetrySpan;
1369
1441
  constructor(task) {
1370
1442
  this.task = task;
@@ -1404,9 +1476,13 @@ class TaskRunner {
1404
1476
  getLogger().warn(`Task "${this.task.type}" declares streaming output (x-stream: "${streamMode}") ` + `but does not implement executeStream(). Falling back to non-streaming execute().`);
1405
1477
  }
1406
1478
  }
1479
+ const inputSchema = this.task.constructor.inputSchema();
1480
+ const outputSchema = this.task.constructor.outputSchema();
1481
+ const inputsForKey = this.outputCache ? await normalizeInputsForCacheKey(inputs, inputSchema) : inputs;
1407
1482
  if (this.task.cacheable) {
1408
- outputs = await this.outputCache?.getOutput(this.task.type, inputs);
1409
- if (outputs) {
1483
+ const cached = await this.outputCache?.getOutput(this.task.type, inputsForKey);
1484
+ if (cached !== undefined) {
1485
+ outputs = await deserializeOutputPorts(cached, outputSchema);
1410
1486
  this.telemetrySpan?.addEvent("workglow.task.cache_hit");
1411
1487
  if (isStreamable) {
1412
1488
  this.task.runOutputData = outputs;
@@ -1425,7 +1501,8 @@ class TaskRunner {
1425
1501
  outputs = await this.executeTask(inputs);
1426
1502
  }
1427
1503
  if (this.task.cacheable && outputs !== undefined) {
1428
- await this.outputCache?.saveOutput(this.task.type, inputs, outputs);
1504
+ const wireOutputs = await serializeOutputPorts(outputs, outputSchema);
1505
+ await this.outputCache?.saveOutput(this.task.type, inputsForKey, wireOutputs);
1429
1506
  }
1430
1507
  this.task.runOutputData = outputs ?? {};
1431
1508
  }
@@ -1642,6 +1719,7 @@ class TaskRunner {
1642
1719
  this.outputCache = cache;
1643
1720
  }
1644
1721
  this.shouldAccumulate = config.shouldAccumulate !== false;
1722
+ this.runWithPreviews = config.runWithPreviews === true;
1645
1723
  if (config.updateProgress) {
1646
1724
  this.updateProgress = config.updateProgress;
1647
1725
  }
@@ -2813,6 +2891,7 @@ class TaskGraphRunner {
2813
2891
  graph;
2814
2892
  outputCache;
2815
2893
  accumulateLeafOutputs = true;
2894
+ runWithPreviews = false;
2816
2895
  registry = globalServiceRegistry3;
2817
2896
  resourceScope;
2818
2897
  abortController;
@@ -3047,6 +3126,24 @@ class TaskGraphRunner {
3047
3126
  }
3048
3127
  async pushOutputFromNodeToEdges(node, results) {
3049
3128
  const dataflows = this.graph.getTargetDataflows(node.id);
3129
+ if (Object.keys(results).length > 0) {
3130
+ const consumerCounts = new Map;
3131
+ for (const dataflow of dataflows) {
3132
+ if (dataflow.stream !== undefined)
3133
+ continue;
3134
+ const port = dataflow.sourceTaskPortId;
3135
+ consumerCounts.set(port, (consumerCounts.get(port) ?? 0) + 1);
3136
+ }
3137
+ for (const [port, count] of consumerCounts) {
3138
+ const extra = this.runWithPreviews ? count : count - 1;
3139
+ if (extra <= 0)
3140
+ continue;
3141
+ const value = results[port];
3142
+ const ref = asRefcountable(value);
3143
+ if (ref)
3144
+ ref.retain(extra);
3145
+ }
3146
+ }
3050
3147
  for (const dataflow of dataflows) {
3051
3148
  if (dataflow.stream !== undefined)
3052
3149
  continue;
@@ -3216,7 +3313,8 @@ class TaskGraphRunner {
3216
3313
  outputCache: this.outputCache ?? false,
3217
3314
  updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
3218
3315
  registry: this.registry,
3219
- resourceScope: this.resourceScope
3316
+ resourceScope: this.resourceScope,
3317
+ runWithPreviews: this.runWithPreviews
3220
3318
  });
3221
3319
  await this.pushOutputFromNodeToEdges(task, results);
3222
3320
  return {
@@ -3266,7 +3364,8 @@ class TaskGraphRunner {
3266
3364
  shouldAccumulate,
3267
3365
  updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
3268
3366
  registry: this.registry,
3269
- resourceScope: this.resourceScope
3367
+ resourceScope: this.resourceScope,
3368
+ runWithPreviews: this.runWithPreviews
3270
3369
  });
3271
3370
  await this.pushOutputFromNodeToEdges(task, results);
3272
3371
  return {
@@ -3341,6 +3440,17 @@ class TaskGraphRunner {
3341
3440
  }
3342
3441
  }
3343
3442
  resetTask(graph, task, runId) {
3443
+ const previous = task.runOutputData;
3444
+ if (previous) {
3445
+ for (const port of Object.keys(previous)) {
3446
+ const ref = asRefcountable(previous[port]);
3447
+ if (!ref)
3448
+ continue;
3449
+ try {
3450
+ ref.release();
3451
+ } catch {}
3452
+ }
3453
+ }
3344
3454
  task.status = TaskStatus.PENDING;
3345
3455
  task.resetInputData();
3346
3456
  task.runOutputData = {};
@@ -3374,6 +3484,7 @@ class TaskGraphRunner {
3374
3484
  this.resourceScope = config.resourceScope;
3375
3485
  }
3376
3486
  this.accumulateLeafOutputs = config?.accumulateLeafOutputs !== false;
3487
+ this.runWithPreviews = config?.runWithPreviews === true;
3377
3488
  if (config?.outputCache !== undefined) {
3378
3489
  if (typeof config.outputCache === "boolean") {
3379
3490
  if (config.outputCache === true) {
@@ -3473,6 +3584,7 @@ class TaskGraphRunner {
3473
3584
  throw new TaskConfigurationError(`Graph has ${taskCount} tasks, exceeding the limit of ${config.maxTasks}`);
3474
3585
  }
3475
3586
  }
3587
+ this.runWithPreviews = false;
3476
3588
  this.previewScheduler.reset();
3477
3589
  this.previewRunning = true;
3478
3590
  }
@@ -3572,7 +3684,8 @@ class GraphAsTaskRunner extends TaskRunner {
3572
3684
  parentSignal: this.abortController?.signal,
3573
3685
  outputCache: this.outputCache,
3574
3686
  registry: this.registry,
3575
- resourceScope: this.resourceScope
3687
+ resourceScope: this.resourceScope,
3688
+ runWithPreviews: this.runWithPreviews
3576
3689
  });
3577
3690
  unsubscribe();
3578
3691
  return results;
@@ -4035,7 +4148,8 @@ class TaskGraph {
4035
4148
  registry: config?.registry,
4036
4149
  timeout: config?.timeout,
4037
4150
  maxTasks: config?.maxTasks,
4038
- resourceScope: config?.resourceScope
4151
+ resourceScope: config?.resourceScope,
4152
+ runWithPreviews: config?.runWithPreviews
4039
4153
  });
4040
4154
  }
4041
4155
  runPreview(input = {}, config = {}) {
@@ -4888,7 +5002,8 @@ class Workflow {
4888
5002
  parentSignal: this._abortController.signal,
4889
5003
  outputCache: this._outputCache,
4890
5004
  registry: config?.registry ?? this._registry,
4891
- resourceScope: config?.resourceScope
5005
+ resourceScope: config?.resourceScope,
5006
+ runWithPreviews: config?.runWithPreviews
4892
5007
  });
4893
5008
  const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
4894
5009
  this.events.emit("complete");
@@ -8122,6 +8237,14 @@ class TaskOutputTabularRepository extends TaskOutputRepository {
8122
8237
  this.emit("output_pruned");
8123
8238
  }
8124
8239
  }
8240
+ // src/storage/PortCodecRegistry.ts
8241
+ import {
8242
+ registerPortCodec,
8243
+ getPortCodec as getPortCodec2,
8244
+ _resetPortCodecsForTests
8245
+ } from "@workglow/util";
8246
+ // src/bun.ts
8247
+ registerRefcountablePredicate((v) => !!v && typeof v === "object" && ("backend" in v) && ("retain" in v) && ("release" in v) && ("materialize" in v));
8125
8248
  export {
8126
8249
  wrapSchemaInArray,
8127
8250
  whileTaskConfigSchema,
@@ -8144,6 +8267,8 @@ export {
8144
8267
  resolveIterationBound,
8145
8268
  resetMethodNameCache,
8146
8269
  removeIterationProperties,
8270
+ registerRefcountablePredicate,
8271
+ registerPortCodec,
8147
8272
  registerJobQueueFactory,
8148
8273
  registerBuiltInTransforms,
8149
8274
  registerBaseTasks,
@@ -8179,6 +8304,7 @@ export {
8179
8304
  getSchemaFormat,
8180
8305
  getProfileGrants,
8181
8306
  getPortStreamMode,
8307
+ getPortCodec2 as getPortCodec,
8182
8308
  getOutputStreamMode,
8183
8309
  getObjectSchema,
8184
8310
  getObjectPortId,
@@ -8225,9 +8351,12 @@ export {
8225
8351
  calculateNodeDepths,
8226
8352
  buildIterationInputSchema,
8227
8353
  autoConnect,
8354
+ asRefcountable,
8228
8355
  addIterationContextToSchema,
8229
8356
  addBoundaryNodesToGraphJson,
8230
8357
  addBoundaryNodesToDependencyJson,
8358
+ _resetRefcountablePredicatesForTests,
8359
+ _resetPortCodecsForTests,
8231
8360
  WorkflowError,
8232
8361
  Workflow,
8233
8362
  WhileTaskRunner,
@@ -8301,4 +8430,4 @@ export {
8301
8430
  BROWSER_GRANTS
8302
8431
  };
8303
8432
 
8304
- //# debugId=6F0C663FBE6F199E64756E2164756E21
8433
+ //# debugId=AA782C3ACDE0213064756E2164756E21