pipe-kan 0.23.3 → 0.23.5

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.
Files changed (2) hide show
  1. package/dist/pipe-kan.js +48 -17
  2. package/package.json +1 -1
package/dist/pipe-kan.js CHANGED
@@ -225,6 +225,7 @@ function mergeEpics(listed, fromBoard) {
225
225
  import { spawn } from "node:child_process";
226
226
  import { existsSync } from "node:fs";
227
227
  import { delimiter, join, resolve } from "node:path";
228
+ import { availableParallelism } from "node:os";
228
229
 
229
230
  // src/flags.ts
230
231
  var DEFAULT_FLAGS = "";
@@ -601,10 +602,10 @@ function createJiraCli(opts = {}) {
601
602
  console.log("Refresh children skipped; no valid parent keys");
602
603
  return "[]";
603
604
  }
604
- const CHUNK = 50;
605
605
  const issues = [];
606
606
  const seen = new Set;
607
607
  let anyFailed = false;
608
+ let lockedMode;
608
609
  function buildJql(chunkKeys, mode) {
609
610
  const list = chunkKeys.map((key) => `"${key}"`).join(", ");
610
611
  if (mode === "epic")
@@ -613,36 +614,66 @@ function createJiraCli(opts = {}) {
613
614
  return `parent in (${list})`;
614
615
  return `(parent in (${list}) OR "Epic Link" in (${list}))`;
615
616
  }
616
- for (let i = 0;i < validKeys.length; i += CHUNK) {
617
- const chunk = validKeys.slice(i, i + CHUNK);
618
- const modes = ["or", "epic", "parent"];
619
- let chunkIssues;
617
+ function chunkSizeFor(mode) {
618
+ return mode === "or" ? 50 : 100;
619
+ }
620
+ async function fetchChunk(chunk, chunkIndex, preferredMode) {
621
+ const modes = preferredMode ? [preferredMode, "or", "epic", "parent"].filter((m, i, arr) => arr.indexOf(m) === i) : ["or", "epic", "parent"];
620
622
  let lastError;
621
623
  for (const mode of modes) {
624
+ const size = chunkSizeFor(mode);
625
+ if (chunk.length > size)
626
+ continue;
622
627
  try {
623
- chunkIssues = JSON.parse(await listAll([
628
+ const chunkIssues = JSON.parse(await listAll([
624
629
  "issue",
625
630
  "list",
626
631
  "-q",
627
632
  buildJql(chunk, mode)
628
633
  ]));
629
- break;
634
+ return { issues: chunkIssues, used: mode };
630
635
  } catch (err) {
631
636
  lastError = err instanceof Error ? err.message : String(err);
632
637
  }
633
638
  }
634
- if (chunkIssues) {
635
- for (const issue of chunkIssues) {
636
- const key = issueKey(issue);
637
- if (key && !seen.has(key)) {
638
- seen.add(key);
639
- issues.push(issue);
640
- }
641
- }
642
- } else {
639
+ console.log(`Refresh children chunk ${chunkIndex + 1} failed; ${lastError ?? "unknown error"}`);
640
+ return { issues: [], error: lastError };
641
+ }
642
+ function childrenConcurrency() {
643
+ const env = Number(process.env.PIPE_KAN_CHILDREN_CONCURRENCY);
644
+ if (!Number.isNaN(env) && env > 0)
645
+ return env;
646
+ return Math.max(3, Math.min(10, availableParallelism()));
647
+ }
648
+ const concurrency = childrenConcurrency();
649
+ console.log(`Refresh children ${validKeys.length} epics; concurrency ${concurrency}`);
650
+ const chunks = [];
651
+ let cursor = 0;
652
+ for (let i = 0;i < validKeys.length; i += chunkSizeFor(lockedMode ?? "or")) {
653
+ const size = chunkSizeFor(lockedMode ?? "or");
654
+ const chunk = validKeys.slice(i, i + size);
655
+ chunks.push({ chunk, index: cursor++ });
656
+ }
657
+ async function processChunk({ chunk, index }) {
658
+ const preferred = lockedMode;
659
+ const result = await fetchChunk(chunk, index, preferred);
660
+ if (result.used && !lockedMode) {
661
+ lockedMode = result.used;
662
+ }
663
+ if (result.error) {
643
664
  anyFailed = true;
644
- console.log(`Refresh children chunk ${i / CHUNK + 1} failed; ${lastError ?? "unknown error"}`);
665
+ return;
645
666
  }
667
+ for (const issue of result.issues) {
668
+ const key = issueKey(issue);
669
+ if (key && !seen.has(key)) {
670
+ seen.add(key);
671
+ issues.push(issue);
672
+ }
673
+ }
674
+ }
675
+ for (let i = 0;i < chunks.length; i += concurrency) {
676
+ await Promise.all(chunks.slice(i, i + concurrency).map(processChunk));
646
677
  }
647
678
  if (anyFailed && issues.length === 0) {
648
679
  throw new Error("Epic children fetch failed for all batches");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipe-kan",
3
- "version": "0.23.3",
3
+ "version": "0.23.5",
4
4
  "description": "Local Kanban for jira-cli",
5
5
  "license": "MIT",
6
6
  "type": "module",