@basementuniverse/kanbn 1.1.0 → 2.0.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.
Files changed (124) hide show
  1. package/README.md +2 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/coverage-summary.json +31 -0
  5. package/coverage/favicon.png +0 -0
  6. package/coverage/index.html +146 -0
  7. package/coverage/kanbn/index.html +116 -0
  8. package/coverage/kanbn/index.js.html +217 -0
  9. package/coverage/kanbn/src/board.js.html +646 -0
  10. package/coverage/kanbn/src/controller/add.js.html +1729 -0
  11. package/coverage/kanbn/src/controller/archive.js.html +277 -0
  12. package/coverage/kanbn/src/controller/board.js.html +217 -0
  13. package/coverage/kanbn/src/controller/burndown.js.html +607 -0
  14. package/coverage/kanbn/src/controller/comment.js.html +397 -0
  15. package/coverage/kanbn/src/controller/edit.js.html +2920 -0
  16. package/coverage/kanbn/src/controller/find.js.html +1597 -0
  17. package/coverage/kanbn/src/controller/gantt.js.html +1342 -0
  18. package/coverage/kanbn/src/controller/help.js.html +115 -0
  19. package/coverage/kanbn/src/controller/history.js.html +400 -0
  20. package/coverage/kanbn/src/controller/index.html +431 -0
  21. package/coverage/kanbn/src/controller/init.js.html +511 -0
  22. package/coverage/kanbn/src/controller/move.js.html +514 -0
  23. package/coverage/kanbn/src/controller/remove-all.js.html +226 -0
  24. package/coverage/kanbn/src/controller/remove.js.html +307 -0
  25. package/coverage/kanbn/src/controller/rename.js.html +397 -0
  26. package/coverage/kanbn/src/controller/restore.js.html +268 -0
  27. package/coverage/kanbn/src/controller/sort.js.html +1318 -0
  28. package/coverage/kanbn/src/controller/sprint.js.html +361 -0
  29. package/coverage/kanbn/src/controller/status.js.html +310 -0
  30. package/coverage/kanbn/src/controller/task.js.html +244 -0
  31. package/coverage/kanbn/src/controller/validate.js.html +367 -0
  32. package/coverage/kanbn/src/controller/version.js.html +100 -0
  33. package/coverage/kanbn/src/index.html +191 -0
  34. package/coverage/kanbn/src/main.js.html +9787 -0
  35. package/coverage/kanbn/src/parse-index.js.html +1063 -0
  36. package/coverage/kanbn/src/parse-markdown.js.html +202 -0
  37. package/coverage/kanbn/src/parse-task.js.html +2191 -0
  38. package/coverage/kanbn/src/utility.js.html +616 -0
  39. package/coverage/lcov.info +11837 -0
  40. package/coverage/prettify.css +1 -0
  41. package/coverage/prettify.js +2 -0
  42. package/coverage/sort-arrow-sprite.png +0 -0
  43. package/coverage/sorter.js +210 -0
  44. package/coverage/tmp/coverage-214292-1787777191526-0.json +1 -0
  45. package/coverage/tmp/coverage-214293-1787777184569-0.json +1 -0
  46. package/docs/advanced-configuration.md +2 -0
  47. package/docs/commands/add.txt +10 -0
  48. package/docs/commands/board.txt +3 -1
  49. package/docs/commands/burndown.txt +2 -4
  50. package/docs/commands/edit.txt +22 -0
  51. package/docs/commands/find.txt +45 -1
  52. package/docs/commands/help.txt +1 -0
  53. package/docs/commands/move.txt +1 -1
  54. package/docs/commands/sort.txt +13 -2
  55. package/docs/commands/validate.txt +12 -0
  56. package/docs/filtering-and-sorting.md +218 -0
  57. package/docs/index-structure.md +68 -19
  58. package/docs/index.md +26 -2
  59. package/docs/migration-2.0.md +92 -0
  60. package/docs/quick-start.md +37 -1
  61. package/docs/task-structure.md +12 -3
  62. package/docs/views.md +229 -0
  63. package/example/README.md +70 -0
  64. package/example/advanced/.kanbn/index.md +26 -0
  65. package/example/advanced/.kanbn/tasks/add-rate-limiting.md +18 -0
  66. package/example/advanced/.kanbn/tasks/add-sso-support.md +15 -0
  67. package/example/advanced/.kanbn/tasks/migrate-to-new-cdn.md +14 -0
  68. package/example/advanced/.kanbn/tasks/refresh-marketing-site.md +14 -0
  69. package/example/advanced/.kanbn/tasks/rotate-api-keys.md +15 -0
  70. package/example/advanced/.kanbn/tasks/split-billing-service.md +17 -0
  71. package/example/advanced/.kanbn/tasks/upgrade-node-runtime.md +19 -0
  72. package/example/advanced/kanbn.yml +59 -0
  73. package/example/views/.kanbn/index.md +140 -0
  74. package/example/views/.kanbn/tasks/add-audit-log-export.md +18 -0
  75. package/example/views/.kanbn/tasks/add-dark-mode-toggle.md +16 -0
  76. package/example/views/.kanbn/tasks/build-webhook-retry-queue.md +15 -0
  77. package/example/views/.kanbn/tasks/fix-avatar-upload-crash.md +13 -0
  78. package/example/views/.kanbn/tasks/fix-expired-session-redirect.md +15 -0
  79. package/example/views/.kanbn/tasks/investigate-offline-mode.md +12 -0
  80. package/example/views/.kanbn/tasks/rewrite-search-indexer.md +12 -0
  81. package/example/views/.kanbn/tasks/tidy-up-settings-copy.md +12 -0
  82. package/package.json +4 -3
  83. package/routes/edit.json +2 -1
  84. package/routes/validate.json +4 -2
  85. package/skills/kanbn-plan/references/index-structure.md +1 -0
  86. package/skills/kanbn-plan/references/task-structure.md +2 -0
  87. package/src/board.js +1 -0
  88. package/src/controller/board.js +6 -0
  89. package/src/controller/edit.js +65 -12
  90. package/src/controller/gantt.js +46 -1
  91. package/src/controller/status.js +12 -0
  92. package/src/controller/task.js +1 -1
  93. package/src/controller/validate.js +78 -15
  94. package/src/main.js +324 -152
  95. package/src/parse-index.js +3 -0
  96. package/src/parse-markdown.js +3 -1
  97. package/src/parse-task.js +28 -1
  98. package/src/utility.js +26 -1
  99. /package/example/{.kanbn → basic/.kanbn}/index.md +0 -0
  100. /package/example/{.kanbn → basic/.kanbn}/tasks/add-basic-activity-feed.md +0 -0
  101. /package/example/{.kanbn → basic/.kanbn}/tasks/add-passwordless-login-option.md +0 -0
  102. /package/example/{.kanbn → basic/.kanbn}/tasks/add-usage-alert-email-thresholds.md +0 -0
  103. /package/example/{.kanbn → basic/.kanbn}/tasks/build-email-template-system.md +0 -0
  104. /package/example/{.kanbn → basic/.kanbn}/tasks/build-invoice-download-endpoint.md +0 -0
  105. /package/example/{.kanbn → basic/.kanbn}/tasks/build-tenant-settings-page.md +0 -0
  106. /package/example/{.kanbn → basic/.kanbn}/tasks/create-organization-switcher.md +0 -0
  107. /package/example/{.kanbn → basic/.kanbn}/tasks/create-sandbox-environment-provisioner.md +0 -0
  108. /package/example/{.kanbn → basic/.kanbn}/tasks/create-self-serve-cancellation-flow.md +0 -0
  109. /package/example/{.kanbn → basic/.kanbn}/tasks/define-product-pricing-strategy.md +0 -0
  110. /package/example/{.kanbn → basic/.kanbn}/tasks/design-onboarding-checklist.md +0 -0
  111. /package/example/{.kanbn → basic/.kanbn}/tasks/design-team-invite-expiry-flow.md +0 -0
  112. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-data-retention-policy-jobs.md +0 -0
  113. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-feature-flags-foundation.md +0 -0
  114. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-project-creation-wizard.md +0 -0
  115. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-stripe-webhook-signature-check.md +0 -0
  116. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-team-permissions-ui.md +0 -0
  117. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-user-signup-and-login.md +0 -0
  118. /package/example/{.kanbn → basic/.kanbn}/tasks/integrate-crm-lead-sync.md +0 -0
  119. /package/example/{.kanbn → basic/.kanbn}/tasks/legal-review-terms-and-privacy.md +0 -0
  120. /package/example/{.kanbn → basic/.kanbn}/tasks/migrate-legacy-events-to-new-schema.md +0 -0
  121. /package/example/{.kanbn → basic/.kanbn}/tasks/optimize-dashboard-first-load.md +0 -0
  122. /package/example/{.kanbn → basic/.kanbn}/tasks/prototype-report-export-scheduler.md +0 -0
  123. /package/example/{.kanbn → basic/.kanbn}/tasks/publish-internal-qa-checklist.md +0 -0
  124. /package/example/{.kanbn → basic/.kanbn}/tasks/setup-ci-pipeline.md +0 -0
@@ -24,6 +24,9 @@ function validateOptions(options) {
24
24
  type: 'array',
25
25
  items: { type: 'string' }
26
26
  },
27
+ 'startedField': { type: 'string' },
28
+ 'completedField': { type: 'string' },
29
+ 'verbose': { type: 'boolean' },
27
30
  'sprints': {
28
31
  type: 'array',
29
32
  items: {
@@ -26,7 +26,9 @@ module.exports = function parseMarkdown(markdown) {
26
26
  parsed[headings[i].title] = {
27
27
  heading: headings[i].heading,
28
28
  content: markdown.slice(
29
- headings[i].index + headings[i].heading.length + 1,
29
+ // The + 1 skips the newline that ends the heading line; the synthetic 'raw' entry for
30
+ // content before the first heading has no heading line, so it starts at index 0
31
+ headings[i].index + headings[i].heading.length + (headings[i].heading ? 1 : 0),
30
32
  i < headings.length - 1
31
33
  ? headings[i + 1].index
32
34
  : undefined
package/src/parse-task.js CHANGED
@@ -239,6 +239,11 @@ function validateHistoryFromJSON(history) {
239
239
 
240
240
  /**
241
241
  * Validate required fields in a history event
242
+ *
243
+ * An event with an unrecognised type is left alone rather than rejected: a task file written by a
244
+ * newer version of kanbn may contain event types this version doesn't know about, and refusing to
245
+ * parse the whole task would make the file unreadable. Unknown events are preserved on write and
246
+ * ignored when history is replayed.
242
247
  * @param {object} historyEvent
243
248
  */
244
249
  function validateHistoryEvent(historyEvent) {
@@ -275,10 +280,24 @@ function validateHistoryEvent(historyEvent) {
275
280
  }
276
281
  break;
277
282
  default:
278
- throw new Error(`unsupported history event type "${historyEvent.type}"`);
283
+ break;
279
284
  }
280
285
  }
281
286
 
287
+ /**
288
+ * History event keys that this version understands, in the order they should be written
289
+ */
290
+ const KNOWN_HISTORY_EVENT_KEYS = [
291
+ 'type',
292
+ 'date',
293
+ 'column',
294
+ 'fromColumn',
295
+ 'toColumn',
296
+ 'fromProgress',
297
+ 'toProgress',
298
+ 'author'
299
+ ];
300
+
282
301
  module.exports = {
283
302
 
284
303
  /**
@@ -660,6 +679,14 @@ module.exports = {
660
679
  if ('author' in historyEvent && historyEvent.author) {
661
680
  historyEventOutput.push(`author: ${historyEvent.author}`);
662
681
  }
682
+
683
+ // Write back any keys this version doesn't recognise, so that a file written by a
684
+ // newer version of kanbn survives a read/write cycle here without losing data
685
+ for (const key of Object.keys(historyEvent)) {
686
+ if (KNOWN_HISTORY_EVENT_KEYS.indexOf(key) === -1 && historyEvent[key] !== undefined) {
687
+ historyEventOutput.push(`${key}: ${historyEvent[key]}`);
688
+ }
689
+ }
663
690
  return `- ${historyEventOutput.map((v, i) => i > 0 ? ` ${v}` : v).join('\n')}`;
664
691
  }).join('\n')
665
692
  );
package/src/utility.js CHANGED
@@ -19,6 +19,26 @@ module.exports = (() => {
19
19
  !dontExit && process.env.KANBN_ENV !== 'test' && process.exit(1);
20
20
  },
21
21
 
22
+ /**
23
+ * Show a hint about tasks whose dates don't match the column they're in. Hints are suppressed
24
+ * for machine-readable output and when the board sets verbose to false
25
+ * @param {object[]} drift Drift entries from kanbn.getDateDrift()
26
+ * @param {boolean} show False to suppress the hint
27
+ */
28
+ showDateDriftHint(drift, show = true) {
29
+ if (!show || !drift.length) {
30
+ return;
31
+ }
32
+ const fixable = drift.filter(d => d.fixable).length;
33
+ const summary =
34
+ `${drift.length} ${drift.length === 1 ? 'task has' : 'tasks have'} dates that don't match ` +
35
+ `${drift.length === 1 ? 'its' : 'their'} column`;
36
+ console.log(this.replaceTags(
37
+ `\n{d}${summary}${fixable ? ' - run{d} {b}kanbn validate --fix{b}' : '{d}'}` +
38
+ (fixable ? ` {d}to fill in the missing ${fixable === 1 ? 'date' : 'dates'}{d}` : '')
39
+ ));
40
+ },
41
+
22
42
  /**
23
43
  * Convert a string to simplified paramcase, e.g:
24
44
  * PascalCase -> pascalcase
@@ -34,7 +54,12 @@ module.exports = (() => {
34
54
  )
35
55
  .split(/[\s!?.,@:;|\\/"'`£$%\^&*{}[\]()<>~#+\-=_¬]+/g)
36
56
  .join('-')
37
- .replace(/(^-|-$)/g, '');
57
+ .replace(/(^-|-$)/g, '')
58
+ // The word-boundary replacement above only matches an uppercase run that is followed by
59
+ // another character, so a trailing capital ("Task A" -> "task-A") and any non-ASCII capital
60
+ // ("Ünïcodé" -> "Ünïcodé") would otherwise survive. Board slugs use this function, so ids
61
+ // must be lowercase throughout.
62
+ .toLowerCase();
38
63
  },
39
64
 
40
65
  /**
File without changes