@prompd/core 0.5.0-beta.19 → 0.5.1

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/index.cjs CHANGED
@@ -3726,8 +3726,9 @@ function validateNode(node, workflow) {
3726
3726
  break;
3727
3727
  }
3728
3728
  if (["loop", "parallel", "tool-call-router", "chat-agent"].includes(node.type)) {
3729
+ const isForkParallel = node.type === "parallel" && node.data.mode === "fork";
3729
3730
  const children = workflow.nodes.filter((n) => n.parentId === node.id);
3730
- if (children.length === 0 && node.type !== "chat-agent") {
3731
+ if (children.length === 0 && node.type !== "chat-agent" && !isForkParallel) {
3731
3732
  const containerTypeHelp = {
3732
3733
  "loop": "Drag nodes inside this loop container to define the loop body.",
3733
3734
  "parallel": "Drag nodes inside this parallel container to run them concurrently.",
@@ -4923,6 +4924,166 @@ function getExecutionOrder(workflow) {
4923
4924
  return order;
4924
4925
  }
4925
4926
 
4927
+ // src/lib/parallel.ts
4928
+ var DEFAULT_MAX_CONCURRENCY = 4;
4929
+ var errMsg = (e) => e instanceof Error ? e.message : String(e);
4930
+ function runBranches(branches, run, opts) {
4931
+ const n = branches.length;
4932
+ if (n === 0) return Promise.resolve({ ok: true, results: [], selected: [] });
4933
+ const now = opts.now ?? (() => Date.now());
4934
+ const need = opts.waitFor === "quorum" ? Math.max(1, Math.min(n, Math.floor(opts.quorumCount ?? 1))) : opts.waitFor === "all" ? n : 1;
4935
+ const limit = Math.max(1, Math.floor(opts.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY));
4936
+ const timeoutMs = opts.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : 0;
4937
+ const results = new Array(n);
4938
+ const controllers = new Array(n);
4939
+ const timers = new Array(n);
4940
+ const startedAt = new Array(n).fill(0);
4941
+ const order = [];
4942
+ return new Promise((resolve) => {
4943
+ let nextIdx = 0;
4944
+ let running = 0;
4945
+ let okCount = 0;
4946
+ let failCount = 0;
4947
+ let done = false;
4948
+ const finish = (ok, error) => {
4949
+ if (done) return;
4950
+ done = true;
4951
+ opts.signal?.removeEventListener("abort", onParentAbort);
4952
+ const t = now();
4953
+ for (let i = 0; i < n; i++) {
4954
+ if (timers[i]) clearTimeout(timers[i]);
4955
+ if (results[i]) continue;
4956
+ controllers[i]?.abort();
4957
+ results[i] = { key: branches[i].key, label: branches[i].label, status: "cancelled", ms: controllers[i] ? t - startedAt[i] : 0 };
4958
+ }
4959
+ const all = results;
4960
+ let selected;
4961
+ if (opts.waitFor === "all") selected = all;
4962
+ else if (opts.waitFor === "race") selected = order.length ? [all[order[0]]] : [];
4963
+ else if (opts.waitFor === "any") {
4964
+ const i = order.find((k) => all[k].status === "ok");
4965
+ selected = i === void 0 ? [] : [all[i]];
4966
+ } else selected = ok ? all.filter((r) => r.status === "ok") : [];
4967
+ resolve({ ok, results: all, selected, ...error ? { error } : {} });
4968
+ };
4969
+ const onParentAbort = () => finish(false, "Aborted");
4970
+ const decide = () => {
4971
+ if (opts.waitFor === "race") {
4972
+ const w = results[order[0]];
4973
+ finish(w.status === "ok", w.status === "ok" ? void 0 : `First branch to finish (${w.label}) ended with ${w.status}${w.error ? `: ${w.error}` : ""}`);
4974
+ return;
4975
+ }
4976
+ if (opts.waitFor === "all") {
4977
+ if (order.length === n) finish(okCount > 0, okCount > 0 ? void 0 : "All branches failed");
4978
+ return;
4979
+ }
4980
+ if (okCount >= need) finish(true);
4981
+ else if (failCount > n - need) {
4982
+ finish(false, opts.waitFor === "any" ? "All branches failed" : `Quorum not reached: ${okCount} of ${need} required branches succeeded`);
4983
+ }
4984
+ };
4985
+ const settle = (i, part) => {
4986
+ if (done || results[i]) return;
4987
+ if (timers[i]) clearTimeout(timers[i]);
4988
+ results[i] = { key: branches[i].key, label: branches[i].label, ...part, ms: now() - startedAt[i] };
4989
+ running--;
4990
+ order.push(i);
4991
+ if (part.status === "ok") okCount++;
4992
+ else failCount++;
4993
+ decide();
4994
+ pump();
4995
+ };
4996
+ const start = (i) => {
4997
+ const ac = new AbortController();
4998
+ controllers[i] = ac;
4999
+ startedAt[i] = now();
5000
+ running++;
5001
+ if (timeoutMs) {
5002
+ timers[i] = setTimeout(() => {
5003
+ ac.abort();
5004
+ settle(i, { status: "timeout", error: `timed out after ${timeoutMs}ms` });
5005
+ }, timeoutMs);
5006
+ }
5007
+ let p;
5008
+ try {
5009
+ p = run(branches[i], ac.signal);
5010
+ } catch (e) {
5011
+ p = Promise.reject(e);
5012
+ }
5013
+ p.then(
5014
+ (output) => settle(i, { status: "ok", output }),
5015
+ (e) => settle(i, { status: "error", error: errMsg(e) })
5016
+ );
5017
+ };
5018
+ function pump() {
5019
+ while (!done && running < limit && nextIdx < n) start(nextIdx++);
5020
+ }
5021
+ if (opts.signal?.aborted) {
5022
+ finish(false, "Aborted");
5023
+ return;
5024
+ }
5025
+ opts.signal?.addEventListener("abort", onParentAbort, { once: true });
5026
+ pump();
5027
+ });
5028
+ }
5029
+ function mergeBranchResults(outcome, strategy) {
5030
+ const sel = outcome.selected;
5031
+ if (strategy === "object") {
5032
+ const merged = {};
5033
+ for (const r of sel) merged[r.key] = r.status === "ok" ? r.output : null;
5034
+ return merged;
5035
+ }
5036
+ if (strategy === "first") {
5037
+ const first = sel.find((r) => r.status === "ok");
5038
+ return first ? { result: first.output } : { error: "All branches failed" };
5039
+ }
5040
+ return sel.map((r) => r.status === "ok" ? r.output : null);
5041
+ }
5042
+ var EVENT_HANDLES = ["onError", "onCheckpoint", "onProgress", "toolResult"];
5043
+ var forkIndex = (handle) => {
5044
+ const k = parseInt((handle ?? "").replace("fork-", ""), 10);
5045
+ return Number.isFinite(k) ? k : 0;
5046
+ };
5047
+ function traceForkBranches(file, parallelId) {
5048
+ const node = file.nodes.find((n) => n.id === parallelId);
5049
+ if (!node || node.type !== "parallel") return [];
5050
+ const data = node.data;
5051
+ const mergeIds = new Set(file.nodes.filter((n) => n.type === "merge").map((n) => n.id));
5052
+ const forkEdges = file.edges.filter((e) => e.source === parallelId && e.sourceHandle?.startsWith("fork-")).sort((a, b) => forkIndex(a.sourceHandle) - forkIndex(b.sourceHandle));
5053
+ const labelUses = /* @__PURE__ */ new Map();
5054
+ return forkEdges.map((fe) => {
5055
+ const idx = forkIndex(fe.sourceHandle);
5056
+ const nodeIds = [];
5057
+ if (!fe.targetHandle?.startsWith("input-")) {
5058
+ const visited = /* @__PURE__ */ new Set();
5059
+ const queue = [fe.target];
5060
+ while (queue.length > 0) {
5061
+ const id = queue.shift();
5062
+ if (visited.has(id) || mergeIds.has(id)) continue;
5063
+ visited.add(id);
5064
+ nodeIds.push(id);
5065
+ for (const e of file.edges) {
5066
+ if (e.source !== id) continue;
5067
+ if (e.sourceHandle === "loop-end" || e.sourceHandle === "parallel-end") continue;
5068
+ if (e.targetHandle?.startsWith("fork-")) continue;
5069
+ if (e.sourceHandle && EVENT_HANDLES.includes(e.sourceHandle)) continue;
5070
+ if (e.targetHandle?.startsWith("input-")) continue;
5071
+ if (!visited.has(e.target) && !mergeIds.has(e.target)) queue.push(e.target);
5072
+ }
5073
+ }
5074
+ }
5075
+ const base = data.forkLabels?.[idx]?.trim() || `Branch ${idx + 1}`;
5076
+ const uses = labelUses.get(base) ?? 0;
5077
+ labelUses.set(base, uses + 1);
5078
+ const label = uses === 0 ? base : `${base} (${uses + 1})`;
5079
+ return { key: label, label, nodeIds };
5080
+ });
5081
+ }
5082
+ function forkJoinId(file, parallelId) {
5083
+ const members = /* @__PURE__ */ new Set([parallelId, ...traceForkBranches(file, parallelId).flatMap((b) => b.nodeIds)]);
5084
+ return file.edges.find((e) => members.has(e.source) && e.targetHandle?.startsWith("input-"))?.target;
5085
+ }
5086
+
4926
5087
  exports.AnthropicFormatter = AnthropicFormatter;
4927
5088
  exports.BUILTIN_COMMAND_EXECUTABLES = BUILTIN_COMMAND_EXECUTABLES;
4928
5089
  exports.CODE_EXTENSIONS = CODE_EXTENSIONS;
@@ -4932,6 +5093,7 @@ exports.CompilationContext = CompilationContext;
4932
5093
  exports.CompilationError = CompilationError;
4933
5094
  exports.CompilationStage = CompilationStage;
4934
5095
  exports.CompilerPipeline = CompilerPipeline;
5096
+ exports.DEFAULT_MAX_CONCURRENCY = DEFAULT_MAX_CONCURRENCY;
4935
5097
  exports.DEFAULT_SECURITY_CONFIG = DEFAULT_SECURITY_CONFIG;
4936
5098
  exports.DOCKABLE_HANDLES = DOCKABLE_HANDLES;
4937
5099
  exports.DOCKABLE_NODE_TYPES = DOCKABLE_NODE_TYPES;
@@ -4968,6 +5130,7 @@ exports.createWorkflowNode = createWorkflowNode;
4968
5130
  exports.dirnamePosix = dirnamePosix;
4969
5131
  exports.extname = extname;
4970
5132
  exports.extractPdpkg = extractPdpkg;
5133
+ exports.forkJoinId = forkJoinId;
4971
5134
  exports.getContentType = getContentType;
4972
5135
  exports.getExecutionOrder = getExecutionOrder;
4973
5136
  exports.getInstallDirForType = getInstallDirForType;
@@ -4979,6 +5142,7 @@ exports.isPrompdFile = isPrompdFile;
4979
5142
  exports.isValidPackageReference = isValidPackageReference;
4980
5143
  exports.isValidPackageType = isValidPackageType;
4981
5144
  exports.joinPosix = joinPosix;
5145
+ exports.mergeBranchResults = mergeBranchResults;
4982
5146
  exports.needsFrontmatterProtection = needsFrontmatterProtection;
4983
5147
  exports.normalizePosix = normalizePosix;
4984
5148
  exports.parsePackageReference = parsePackageReference;
@@ -4986,8 +5150,10 @@ exports.parsePackageReferenceWithPath = parsePackageReferenceWithPath;
4986
5150
  exports.parseWorkflow = parseWorkflow;
4987
5151
  exports.resolvePackageFile = resolvePackageFile;
4988
5152
  exports.resolvePosix = resolvePosix;
5153
+ exports.runBranches = runBranches;
4989
5154
  exports.serializeWorkflow = serializeWorkflow;
4990
5155
  exports.stripFilePath = stripFilePath;
5156
+ exports.traceForkBranches = traceForkBranches;
4991
5157
  exports.uninstallPackage = uninstallPackage;
4992
5158
  exports.validateWorkflow = validateWorkflow;
4993
5159
  exports.validateWorkflowQuick = validateWorkflowQuick;