pipe-kan 0.11.0 → 0.12.0
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/pipe-kan.js +102 -13
- package/dist/ui/assets/index-BIl78HJ0.js +55 -0
- package/dist/ui/index.html +1 -1
- package/fixtures/issues.json +59 -0
- package/package.json +1 -1
- package/dist/ui/assets/index-CwZq9IIK.js +0 -54
package/dist/pipe-kan.js
CHANGED
|
@@ -28,7 +28,17 @@ function issueType(fields) {
|
|
|
28
28
|
return typeof name === "string" ? name : undefined;
|
|
29
29
|
}
|
|
30
30
|
function epicKey(fields) {
|
|
31
|
-
|
|
31
|
+
if (typeof fields?.parent?.key === "string")
|
|
32
|
+
return fields.parent.key;
|
|
33
|
+
const link = fields?.["Epic Link"];
|
|
34
|
+
if (typeof link === "string" && link)
|
|
35
|
+
return link;
|
|
36
|
+
if (link && typeof link === "object" && "key" in link) {
|
|
37
|
+
const key = link.key;
|
|
38
|
+
if (typeof key === "string" && key)
|
|
39
|
+
return key;
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
32
42
|
}
|
|
33
43
|
function labelName(item) {
|
|
34
44
|
if (typeof item === "string" && item)
|
|
@@ -73,7 +83,8 @@ function toEpic(face) {
|
|
|
73
83
|
...face.status ? { status: face.status } : {},
|
|
74
84
|
...face.priority ? { priority: face.priority } : {},
|
|
75
85
|
...face.assignee ? { assignee: face.assignee } : {},
|
|
76
|
-
...face.dueDate ? { dueDate: face.dueDate } : {}
|
|
86
|
+
...face.dueDate ? { dueDate: face.dueDate } : {},
|
|
87
|
+
...face.labels ? { labels: face.labels } : {}
|
|
77
88
|
};
|
|
78
89
|
}
|
|
79
90
|
function toCard(issue, key) {
|
|
@@ -252,6 +263,9 @@ function createStoreCli(store) {
|
|
|
252
263
|
const issues = store.list(`project="DEMO" AND parent="${key}"`);
|
|
253
264
|
return JSON.stringify(issues, null, 2);
|
|
254
265
|
},
|
|
266
|
+
async listChildren(keys) {
|
|
267
|
+
return JSON.stringify(store.childrenOf(keys), null, 2);
|
|
268
|
+
},
|
|
255
269
|
async move(key, status) {
|
|
256
270
|
return store.move(key, status);
|
|
257
271
|
},
|
|
@@ -342,6 +356,25 @@ function createJiraCli(opts = {}) {
|
|
|
342
356
|
}
|
|
343
357
|
return result.stdout;
|
|
344
358
|
},
|
|
359
|
+
async listChildren(keys) {
|
|
360
|
+
if (!keys.length)
|
|
361
|
+
return "[]";
|
|
362
|
+
const list = keys.map((key) => `"${key}"`).join(", ");
|
|
363
|
+
const result = await run([
|
|
364
|
+
"issue",
|
|
365
|
+
"list",
|
|
366
|
+
"-q",
|
|
367
|
+
`parent in (${list}) OR "Epic Link" in (${list})`,
|
|
368
|
+
"--raw"
|
|
369
|
+
]);
|
|
370
|
+
if (result.code !== 0) {
|
|
371
|
+
const text = result.stderr || result.stdout || "jira issue list failed";
|
|
372
|
+
if (emptyList(text))
|
|
373
|
+
return "[]";
|
|
374
|
+
throw new Error(text);
|
|
375
|
+
}
|
|
376
|
+
return result.stdout;
|
|
377
|
+
},
|
|
345
378
|
async move(key, status) {
|
|
346
379
|
const result = await run(["issue", "move", key, status]);
|
|
347
380
|
if (result.code !== 0) {
|
|
@@ -505,11 +538,35 @@ function flattenIssue(raw, url) {
|
|
|
505
538
|
}
|
|
506
539
|
|
|
507
540
|
// src/app.ts
|
|
541
|
+
function columnsOf(raw) {
|
|
542
|
+
return Object.fromEntries(issuesToBoard(raw).columns.map((column) => [column.title, column.cards]));
|
|
543
|
+
}
|
|
544
|
+
function stampMissingEpic(board, epic) {
|
|
545
|
+
for (const column of board.columns) {
|
|
546
|
+
for (const card of column.cards) {
|
|
547
|
+
if (!card.epic)
|
|
548
|
+
card.epic = epic;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return board;
|
|
552
|
+
}
|
|
508
553
|
function createApp(opts) {
|
|
509
554
|
const cli = opts.cli ?? createStoreCli(opts.store);
|
|
510
555
|
let flags = DEFAULT_FLAGS;
|
|
511
556
|
let payload = [];
|
|
512
557
|
let epicsPayload = [];
|
|
558
|
+
let childrenRaw = [];
|
|
559
|
+
let hasChildrenCache = false;
|
|
560
|
+
let childrenError;
|
|
561
|
+
function listedEpicKeys() {
|
|
562
|
+
return issuesToBoard(epicsPayload).epics.map((epic) => epic.key);
|
|
563
|
+
}
|
|
564
|
+
function cacheFromStore() {
|
|
565
|
+
epicsPayload = opts.store.list(flagsToJql("-tEpic"));
|
|
566
|
+
childrenRaw = opts.store.childrenOf(listedEpicKeys());
|
|
567
|
+
hasChildrenCache = true;
|
|
568
|
+
childrenError = undefined;
|
|
569
|
+
}
|
|
513
570
|
const app = {
|
|
514
571
|
get flags() {
|
|
515
572
|
return flags;
|
|
@@ -518,37 +575,61 @@ function createApp(opts) {
|
|
|
518
575
|
const board = issuesToBoard(payload);
|
|
519
576
|
return {
|
|
520
577
|
columns: board.columns,
|
|
521
|
-
epics: mergeEpics(issuesToBoard(epicsPayload).epics, board.epics)
|
|
578
|
+
epics: mergeEpics(issuesToBoard(epicsPayload).epics, board.epics),
|
|
579
|
+
...hasChildrenCache ? { children: columnsOf(childrenRaw) } : {},
|
|
580
|
+
...childrenError ? { error: childrenError } : {}
|
|
522
581
|
};
|
|
523
582
|
},
|
|
524
|
-
hydrate(raw) {
|
|
583
|
+
hydrate(raw, hydrateOpts) {
|
|
525
584
|
payload = Array.isArray(raw) ? raw : [];
|
|
526
585
|
epicsPayload = [];
|
|
586
|
+
childrenRaw = [];
|
|
587
|
+
hasChildrenCache = false;
|
|
588
|
+
childrenError = undefined;
|
|
589
|
+
if (hydrateOpts?.fromStore)
|
|
590
|
+
cacheFromStore();
|
|
527
591
|
return app.board();
|
|
528
592
|
},
|
|
529
593
|
async refresh(next) {
|
|
530
594
|
if (next !== undefined)
|
|
531
595
|
flags = next;
|
|
596
|
+
childrenError = undefined;
|
|
532
597
|
const [issues, epics] = await Promise.all([
|
|
533
598
|
cli.list(flags),
|
|
534
599
|
cli.listEpics()
|
|
535
600
|
]);
|
|
536
601
|
payload = JSON.parse(issues);
|
|
537
602
|
epicsPayload = JSON.parse(epics);
|
|
603
|
+
const keys = listedEpicKeys();
|
|
604
|
+
try {
|
|
605
|
+
childrenRaw = JSON.parse(await cli.listChildren(keys));
|
|
606
|
+
hasChildrenCache = true;
|
|
607
|
+
} catch (err) {
|
|
608
|
+
hasChildrenCache = false;
|
|
609
|
+
childrenRaw = [];
|
|
610
|
+
childrenError = err instanceof Error ? err.message : "Epic children list failed";
|
|
611
|
+
}
|
|
538
612
|
return app.board();
|
|
539
613
|
},
|
|
540
614
|
async children(epic) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
card
|
|
546
|
-
|
|
615
|
+
if (hasChildrenCache) {
|
|
616
|
+
const cards = Object.fromEntries(Object.entries(columnsOf(childrenRaw)).map(([title, list]) => [
|
|
617
|
+
title,
|
|
618
|
+
list.filter((card) => card.epic === epic).map((card) => ({
|
|
619
|
+
...card,
|
|
620
|
+
epic: card.epic ?? epic
|
|
621
|
+
}))
|
|
622
|
+
]));
|
|
623
|
+
return {
|
|
624
|
+
columns: Object.entries(cards).filter(([, list]) => list.length).map(([title, list]) => ({ id: title, title, cards: list })),
|
|
625
|
+
epics: []
|
|
626
|
+
};
|
|
547
627
|
}
|
|
548
|
-
return
|
|
628
|
+
return stampMissingEpic(issuesToBoard(JSON.parse(await cli.listEpic(epic))), epic);
|
|
549
629
|
},
|
|
550
630
|
async move(key, status) {
|
|
551
|
-
const
|
|
631
|
+
const fromPayload = payload.find((issue) => issue.key === key)?.fields?.status?.name;
|
|
632
|
+
const current = fromPayload ?? issuesToBoard(epicsPayload).epics.find((epic) => epic.key === key)?.status ?? app.board().epics.find((epic) => epic.key === key)?.status;
|
|
552
633
|
if (current === status) {
|
|
553
634
|
return { ok: true, noop: true, board: app.board() };
|
|
554
635
|
}
|
|
@@ -660,6 +741,14 @@ class IssueStore {
|
|
|
660
741
|
list(jql = "") {
|
|
661
742
|
return this.issues.filter((issue) => matchJql(issue, jql));
|
|
662
743
|
}
|
|
744
|
+
childrenOf(keys) {
|
|
745
|
+
const listed = new Set(keys);
|
|
746
|
+
return this.issues.filter((issue) => {
|
|
747
|
+
if (fieldValue(issue, "type").toLowerCase() === "epic")
|
|
748
|
+
return false;
|
|
749
|
+
return listed.has(fieldValue(issue, "parent"));
|
|
750
|
+
});
|
|
751
|
+
}
|
|
663
752
|
transitions(key) {
|
|
664
753
|
const issue = this.get(key);
|
|
665
754
|
if (!issue)
|
|
@@ -708,7 +797,7 @@ async function createBoardApp(opts) {
|
|
|
708
797
|
app.hydrate(opts.raw);
|
|
709
798
|
} else {
|
|
710
799
|
const local = createStoreCli(store);
|
|
711
|
-
app.hydrate(JSON.parse(await local.list(app.flags)));
|
|
800
|
+
app.hydrate(JSON.parse(await local.list(app.flags)), { fromStore: true });
|
|
712
801
|
}
|
|
713
802
|
return { app, store, kind: bin ? "jira" : "store" };
|
|
714
803
|
}
|