@unbrained/pm-web 2026.8.10 → 2026.8.14

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 (173) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/app.d.ts +16 -0
  3. package/dist/app.js +16 -0
  4. package/dist/app.js.map +1 -1
  5. package/dist/auth.d.ts +5 -0
  6. package/dist/auth.js.map +1 -1
  7. package/dist/board.d.ts +53 -0
  8. package/dist/board.js +48 -0
  9. package/dist/board.js.map +1 -1
  10. package/dist/crypto.d.ts +23 -0
  11. package/dist/crypto.js +32 -0
  12. package/dist/crypto.js.map +1 -1
  13. package/dist/db.d.ts +19 -0
  14. package/dist/db.js +19 -0
  15. package/dist/db.js.map +1 -1
  16. package/dist/ical.d.ts +72 -0
  17. package/dist/ical.js +73 -0
  18. package/dist/ical.js.map +1 -1
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.js +52 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/middleware/auth.d.ts +8 -0
  23. package/dist/middleware/auth.js.map +1 -1
  24. package/dist/oidc.d.ts +87 -0
  25. package/dist/oidc.js +113 -0
  26. package/dist/oidc.js.map +1 -1
  27. package/dist/routes/admin.js +15 -0
  28. package/dist/routes/admin.js.map +1 -1
  29. package/dist/routes/github.js +13 -0
  30. package/dist/routes/github.js.map +1 -1
  31. package/dist/routes/oidc.d.ts +10 -0
  32. package/dist/routes/oidc.js +51 -0
  33. package/dist/routes/oidc.js.map +1 -1
  34. package/dist/routes/pm.js +132 -1
  35. package/dist/routes/pm.js.map +1 -1
  36. package/dist/routes/sharing.js +8 -0
  37. package/dist/routes/sharing.js.map +1 -1
  38. package/dist/services/mutation-event-watcher.js +13 -0
  39. package/dist/services/mutation-event-watcher.js.map +1 -1
  40. package/dist/services/pm-runner.d.ts +82 -0
  41. package/dist/services/pm-runner.js +121 -0
  42. package/dist/services/pm-runner.js.map +1 -1
  43. package/dist/services/project-watcher.d.ts +22 -0
  44. package/dist/services/project-watcher.js +51 -0
  45. package/dist/services/project-watcher.js.map +1 -1
  46. package/dist/services/realtime-bus.d.ts +46 -0
  47. package/dist/services/realtime-bus.js +59 -0
  48. package/dist/services/realtime-bus.js.map +1 -1
  49. package/dist/services/sse.d.ts +134 -0
  50. package/dist/services/sse.js +145 -0
  51. package/dist/services/sse.js.map +1 -1
  52. package/manifest.json +1 -1
  53. package/package.json +5 -3
  54. package/public/src/app.js +44 -1
  55. package/public/src/app.js.map +1 -1
  56. package/public/src/app.ts +44 -1
  57. package/public/src/components/modals.js +11 -0
  58. package/public/src/components/modals.js.map +1 -1
  59. package/public/src/components/modals.ts +11 -0
  60. package/public/src/components/toast.js +2 -0
  61. package/public/src/components/toast.js.map +1 -1
  62. package/public/src/components/toast.ts +2 -0
  63. package/public/src/constants.js +21 -4
  64. package/public/src/constants.js.map +1 -1
  65. package/public/src/constants.ts +21 -4
  66. package/public/src/filters.js +10 -7
  67. package/public/src/filters.js.map +1 -1
  68. package/public/src/filters.ts +10 -7
  69. package/public/src/i18n.js +11 -1
  70. package/public/src/i18n.js.map +1 -1
  71. package/public/src/i18n.ts +13 -1
  72. package/public/src/state.js +3 -0
  73. package/public/src/state.js.map +1 -1
  74. package/public/src/state.ts +3 -0
  75. package/public/src/sw.ts +71 -7
  76. package/public/src/theme.js +13 -1
  77. package/public/src/theme.js.map +1 -1
  78. package/public/src/theme.ts +15 -1
  79. package/public/src/types.ts +2 -0
  80. package/public/src/utils.js +26 -1
  81. package/public/src/utils.js.map +1 -1
  82. package/public/src/utils.ts +26 -1
  83. package/public/src/views/activity.js +1 -0
  84. package/public/src/views/activity.js.map +1 -1
  85. package/public/src/views/activity.ts +1 -0
  86. package/public/src/views/admin.js +134 -0
  87. package/public/src/views/admin.js.map +1 -1
  88. package/public/src/views/admin.ts +134 -0
  89. package/public/src/views/auth.js +31 -0
  90. package/public/src/views/auth.js.map +1 -1
  91. package/public/src/views/auth.ts +31 -0
  92. package/public/src/views/calendar.js +3 -0
  93. package/public/src/views/calendar.js.map +1 -1
  94. package/public/src/views/calendar.ts +3 -0
  95. package/public/src/views/comments-audit.js +1 -0
  96. package/public/src/views/comments-audit.js.map +1 -1
  97. package/public/src/views/comments-audit.ts +1 -0
  98. package/public/src/views/config.js +67 -0
  99. package/public/src/views/config.js.map +1 -1
  100. package/public/src/views/config.ts +67 -0
  101. package/public/src/views/context.js +2 -0
  102. package/public/src/views/context.js.map +1 -1
  103. package/public/src/views/context.ts +2 -0
  104. package/public/src/views/create.js +3 -0
  105. package/public/src/views/create.js.map +1 -1
  106. package/public/src/views/create.ts +3 -0
  107. package/public/src/views/dedupe.js +1 -0
  108. package/public/src/views/dedupe.js.map +1 -1
  109. package/public/src/views/dedupe.ts +1 -0
  110. package/public/src/views/export.js +6 -0
  111. package/public/src/views/export.js.map +1 -1
  112. package/public/src/views/export.ts +6 -0
  113. package/public/src/views/github.js +58 -0
  114. package/public/src/views/github.js.map +1 -1
  115. package/public/src/views/github.ts +58 -0
  116. package/public/src/views/graph-canvas.js +18 -0
  117. package/public/src/views/graph-canvas.js.map +1 -1
  118. package/public/src/views/graph-canvas.ts +20 -0
  119. package/public/src/views/graph.js +41 -0
  120. package/public/src/views/graph.js.map +1 -1
  121. package/public/src/views/graph.ts +42 -1
  122. package/public/src/views/groups.js +45 -0
  123. package/public/src/views/groups.js.map +1 -1
  124. package/public/src/views/groups.ts +45 -0
  125. package/public/src/views/guide.js +3 -0
  126. package/public/src/views/guide.js.map +1 -1
  127. package/public/src/views/guide.ts +3 -0
  128. package/public/src/views/health.js +3 -0
  129. package/public/src/views/health.js.map +1 -1
  130. package/public/src/views/health.ts +3 -0
  131. package/public/src/views/items.js +196 -6
  132. package/public/src/views/items.js.map +1 -1
  133. package/public/src/views/items.ts +196 -6
  134. package/public/src/views/normalize.js +2 -0
  135. package/public/src/views/normalize.js.map +1 -1
  136. package/public/src/views/normalize.ts +2 -0
  137. package/public/src/views/packages.js +41 -0
  138. package/public/src/views/packages.js.map +1 -1
  139. package/public/src/views/packages.ts +41 -0
  140. package/public/src/views/plan-execution.js +6 -0
  141. package/public/src/views/plan-execution.js.map +1 -1
  142. package/public/src/views/plan-execution.ts +8 -0
  143. package/public/src/views/plan.js +145 -0
  144. package/public/src/views/plan.js.map +1 -1
  145. package/public/src/views/plan.ts +146 -1
  146. package/public/src/views/projects.js +60 -0
  147. package/public/src/views/projects.js.map +1 -1
  148. package/public/src/views/projects.ts +60 -0
  149. package/public/src/views/router.js +43 -0
  150. package/public/src/views/router.js.map +1 -1
  151. package/public/src/views/router.ts +43 -0
  152. package/public/src/views/search.js +5 -0
  153. package/public/src/views/search.js.map +1 -1
  154. package/public/src/views/search.ts +5 -0
  155. package/public/src/views/settings.js +36 -0
  156. package/public/src/views/settings.js.map +1 -1
  157. package/public/src/views/settings.ts +36 -0
  158. package/public/src/views/shared.js +1 -0
  159. package/public/src/views/shared.js.map +1 -1
  160. package/public/src/views/shared.ts +1 -0
  161. package/public/src/views/sharing.js +32 -0
  162. package/public/src/views/sharing.js.map +1 -1
  163. package/public/src/views/sharing.ts +32 -0
  164. package/public/src/views/stats.js +1 -0
  165. package/public/src/views/stats.js.map +1 -1
  166. package/public/src/views/stats.ts +1 -0
  167. package/public/src/views/templates.js +4 -0
  168. package/public/src/views/templates.js.map +1 -1
  169. package/public/src/views/templates.ts +4 -0
  170. package/public/src/views/validate.js +1 -0
  171. package/public/src/views/validate.js.map +1 -1
  172. package/public/src/views/validate.ts +1 -0
  173. package/public/sw.js +60 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.8.14 - 2026-08-14
4
+
5
+ ### Fixed
6
+
7
+ - The pm CLI dependency was an unpinned range in a package whose production image installs without dev dependencies ([pm-web-swb5](https://github.com/unbraind/pm-web/blob/main/.agents/pm/issues/pm-web-swb5.toon))
8
+
9
+ ## 2026.8.10 - 2026-08-10
10
+
11
+ ### Fixed
12
+
13
+ - SW mutation queue: IndexedDB read failure indistinguishable from empty queue ([pm-web-zz37](https://github.com/unbraind/pm-web/blob/main/.agents/pm/bugs/pm-web-zz37.toon))
14
+
15
+ ### Other
16
+
17
+ - Adopt canonical pm-ops docstring gate and reach 100% docstring coverage ([pm-web-mglt](https://github.com/unbraind/pm-web/blob/main/.agents/pm/tasks/pm-web-mglt.toon))
18
+
3
19
  ## 2026.8.7 - 2026-08-07
4
20
 
5
21
  ### Fixed
package/dist/app.d.ts CHANGED
@@ -17,6 +17,22 @@ import { type Express } from "express";
17
17
  export declare const LEGAL_PAGES: readonly ["legal-notice", "privacy-policy", "terms", "cookie-settings"];
18
18
  /** German short-path aliases that 308-redirect to the canonical legal pages. */
19
19
  export declare const LEGAL_REDIRECTS: Record<string, string>;
20
+ /**
21
+ * Resolve the directory that serves the standalone legal-page HTML.
22
+ *
23
+ * With no `PM_WEB_LEGAL_DIR` configured (or a blank value) the bundled
24
+ * `public/` directory is returned, whose placeholder templates are English.
25
+ * When an operator overlay is set it is validated strictly before use: the path
26
+ * must be absolute, must `realpath`-resolve to a real directory, and every one
27
+ * of the {@link LEGAL_PAGES} must exist there as a regular (non-symlink) file
28
+ * whose real path stays inside the overlay root and is readable. Any failure
29
+ * throws with a specific message, so a half-configured overlay never silently
30
+ * falls back to the placeholders.
31
+ *
32
+ * @param env - Environment to read `PM_WEB_LEGAL_DIR` from; defaults to
33
+ * `process.env` so callers (and tests) can inject a controlled environment.
34
+ * @returns The real, resolved directory path holding the legal HTML files.
35
+ */
20
36
  export declare function resolveLegalPagesDir(env?: NodeJS.ProcessEnv): string;
21
37
  /**
22
38
  * Build the Express application with all middleware, static assets, legal
package/dist/app.js CHANGED
@@ -42,6 +42,22 @@ export const LEGAL_REDIRECTS = {
42
42
  "/agb": "/terms",
43
43
  "/cookies": "/cookie-settings",
44
44
  };
45
+ /**
46
+ * Resolve the directory that serves the standalone legal-page HTML.
47
+ *
48
+ * With no `PM_WEB_LEGAL_DIR` configured (or a blank value) the bundled
49
+ * `public/` directory is returned, whose placeholder templates are English.
50
+ * When an operator overlay is set it is validated strictly before use: the path
51
+ * must be absolute, must `realpath`-resolve to a real directory, and every one
52
+ * of the {@link LEGAL_PAGES} must exist there as a regular (non-symlink) file
53
+ * whose real path stays inside the overlay root and is readable. Any failure
54
+ * throws with a specific message, so a half-configured overlay never silently
55
+ * falls back to the placeholders.
56
+ *
57
+ * @param env - Environment to read `PM_WEB_LEGAL_DIR` from; defaults to
58
+ * `process.env` so callers (and tests) can inject a controlled environment.
59
+ * @returns The real, resolved directory path holding the legal HTML files.
60
+ */
45
61
  export function resolveLegalPagesDir(env = process.env) {
46
62
  const configured = env.PM_WEB_LEGAL_DIR?.trim();
47
63
  if (!configured)
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import type { Request, Response } from "express";
2
+ /**
3
+ * Claims encoded into a pm-web session JWT: the authenticated user's stable id
4
+ * and email, both set by the OIDC login flow and later restored by
5
+ * `verifyToken`.
6
+ */
2
7
  export interface JwtPayload {
3
8
  userId: string;
4
9
  email: string;
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAE/B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACpE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC3G,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;AAC/E,CAAC;AAOD,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAa,EAAE,OAAmB;IACjE,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE;QAC5B,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC7C,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;KACjC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAe,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IAC7C,IAAI,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,MAAM,GAAI,GAAsD,CAAC,OAAO,EAAE,QAAQ,CAAC;IACzF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,yEAAyE;IACzE,uEAAuE;IACvE,4EAA4E;IAC5E,MAAM,UAAU,GAAI,GAAG,CAAC,KAA6C,EAAE,CAAC,OAAO,CAAC,CAAC;IACjF,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IACpE,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAE/B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACpE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC3G,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;AAC/E,CAAC;AAYD,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAa,EAAE,OAAmB;IACjE,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE;QAC5B,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC7C,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;KACjC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAe,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IAC7C,IAAI,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,MAAM,GAAI,GAAsD,CAAC,OAAO,EAAE,QAAQ,CAAC;IACzF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,yEAAyE;IACzE,uEAAuE;IACvE,4EAA4E;IAC5E,MAAM,UAAU,GAAI,GAAG,CAAC,KAA6C,EAAE,CAAC,OAAO,CAAC,CAAC;IACjF,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IACpE,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/board.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Minimal projection of a pm item used by the pure board/search helpers: a
3
+ * stable id plus the optional text fields the kanban columns and full-text
4
+ * filter read. Callers cast richer item shapes to this narrow view.
5
+ */
1
6
  export type BoardItem = {
2
7
  id: string;
3
8
  title?: string;
@@ -6,10 +11,58 @@ export type BoardItem = {
6
11
  body?: string;
7
12
  description?: string;
8
13
  };
14
+ /**
15
+ * Coerce an arbitrary status value into a normalized board-column key.
16
+ *
17
+ * A non-empty string is trimmed and used; anything else (or a blank string)
18
+ * falls back to `"open"`. The result is then lowercased and has each run of
19
+ * whitespace or hyphens replaced with a single underscore, so `"In Progress"`,
20
+ * `"in-progress"` and `"in progress"` all map to `in_progress`.
21
+ *
22
+ * @param value - The raw `status` field from an item; type is `unknown` because
23
+ * imported items can carry non-string values.
24
+ * @returns The normalized, lowercase, underscore-separated status key.
25
+ */
9
26
  export declare function normalizeStatusKey(value: unknown): string;
27
+ /**
28
+ * Normalize an item's `tags` field into a trimmed, blank-free string array.
29
+ *
30
+ * An array has each entry stringified and trimmed; a string is split on commas;
31
+ * any other type yields an empty array. Empty fragments are dropped in every
32
+ * case, so `"a, ,b"` becomes `["a", "b"]`.
33
+ *
34
+ * @param value - The raw `tags` field from an item.
35
+ * @returns The list of non-empty tag strings.
36
+ */
10
37
  export declare function normalizeItemTags(value: unknown): string[];
38
+ /**
39
+ * Group items into board columns keyed by the workspace's actual statuses.
40
+ *
41
+ * The supplied `statuses` (from `pm contracts`) define the ordered columns; if
42
+ * none are given a default `open / in_progress / blocked / closed` set is used.
43
+ * Each status is matched case- and separator-insensitively via
44
+ * {@link normalizeStatusKey}, so items land in the intended column regardless of
45
+ * capitalization. Items whose status matches no known column are collected into
46
+ * a trailing `(other)` column so nothing is silently dropped. Columns are
47
+ * returned in declared-status order, with `(other)` appended when first needed.
48
+ *
49
+ * @param items - Items to distribute.
50
+ * @param statuses - The workspace's declared status labels.
51
+ * @returns Columns in declared-status order, each carrying its items.
52
+ */
11
53
  export declare function boardColumns<T extends BoardItem>(items: T[], statuses: string[]): Array<{
12
54
  status: string;
13
55
  items: T[];
14
56
  }>;
57
+ /**
58
+ * Case-insensitive substring filter over an item's text fields.
59
+ *
60
+ * Matches against the concatenation of id, title, tags (normalized), body and
61
+ * description, lowercased. A blank query short-circuits and returns the input
62
+ * array unchanged. The original items are returned by reference, not copied.
63
+ *
64
+ * @param items - Items to filter.
65
+ * @param query - Free-text query; surrounding whitespace is ignored.
66
+ * @returns The items whose text fields contain the query substring.
67
+ */
15
68
  export declare function filterItemsByQuery<T extends BoardItem>(items: T[], query: string): T[];
package/dist/board.js CHANGED
@@ -1,10 +1,32 @@
1
1
  // Pure, dependency-free helpers for the kanban board and search endpoints.
2
2
  // Kept in their own module (no db/neo4j imports) so they are unit-testable
3
3
  // without booting the server or a database.
4
+ /**
5
+ * Coerce an arbitrary status value into a normalized board-column key.
6
+ *
7
+ * A non-empty string is trimmed and used; anything else (or a blank string)
8
+ * falls back to `"open"`. The result is then lowercased and has each run of
9
+ * whitespace or hyphens replaced with a single underscore, so `"In Progress"`,
10
+ * `"in-progress"` and `"in progress"` all map to `in_progress`.
11
+ *
12
+ * @param value - The raw `status` field from an item; type is `unknown` because
13
+ * imported items can carry non-string values.
14
+ * @returns The normalized, lowercase, underscore-separated status key.
15
+ */
4
16
  export function normalizeStatusKey(value) {
5
17
  const raw = typeof value === "string" && value.trim() ? value.trim() : "open";
6
18
  return raw.toLowerCase().replace(/[\s-]+/g, "_");
7
19
  }
20
+ /**
21
+ * Normalize an item's `tags` field into a trimmed, blank-free string array.
22
+ *
23
+ * An array has each entry stringified and trimmed; a string is split on commas;
24
+ * any other type yields an empty array. Empty fragments are dropped in every
25
+ * case, so `"a, ,b"` becomes `["a", "b"]`.
26
+ *
27
+ * @param value - The raw `tags` field from an item.
28
+ * @returns The list of non-empty tag strings.
29
+ */
8
30
  export function normalizeItemTags(value) {
9
31
  if (Array.isArray(value))
10
32
  return value.map(String).map((tag) => tag.trim()).filter(Boolean);
@@ -16,6 +38,21 @@ export function normalizeItemTags(value) {
16
38
  // (from `pm contracts`), so the kanban board reflects whatever statuses the
17
39
  // installed pm CLI + extensions define. Items with an unlisted status fall into
18
40
  // a trailing "(other)" column so nothing is silently dropped.
41
+ /**
42
+ * Group items into board columns keyed by the workspace's actual statuses.
43
+ *
44
+ * The supplied `statuses` (from `pm contracts`) define the ordered columns; if
45
+ * none are given a default `open / in_progress / blocked / closed` set is used.
46
+ * Each status is matched case- and separator-insensitively via
47
+ * {@link normalizeStatusKey}, so items land in the intended column regardless of
48
+ * capitalization. Items whose status matches no known column are collected into
49
+ * a trailing `(other)` column so nothing is silently dropped. Columns are
50
+ * returned in declared-status order, with `(other)` appended when first needed.
51
+ *
52
+ * @param items - Items to distribute.
53
+ * @param statuses - The workspace's declared status labels.
54
+ * @returns Columns in declared-status order, each carrying its items.
55
+ */
19
56
  export function boardColumns(items, statuses) {
20
57
  const known = (statuses.length > 0 ? statuses : ["open", "in_progress", "blocked", "closed"])
21
58
  .map((status) => String(status).trim())
@@ -43,6 +80,17 @@ export function boardColumns(items, statuses) {
43
80
  return [...columns.entries()].map(([status, list]) => ({ status, items: list }));
44
81
  }
45
82
  // Case-insensitive full-text filter over id, title, tags and body.
83
+ /**
84
+ * Case-insensitive substring filter over an item's text fields.
85
+ *
86
+ * Matches against the concatenation of id, title, tags (normalized), body and
87
+ * description, lowercased. A blank query short-circuits and returns the input
88
+ * array unchanged. The original items are returned by reference, not copied.
89
+ *
90
+ * @param items - Items to filter.
91
+ * @param query - Free-text query; surrounding whitespace is ignored.
92
+ * @returns The items whose text fields contain the query substring.
93
+ */
46
94
  export function filterItemsByQuery(items, query) {
47
95
  const q = query.trim().toLowerCase();
48
96
  if (!q)
package/dist/board.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"board.js","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"board.js","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"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"}
package/dist/crypto.d.ts CHANGED
@@ -1,2 +1,25 @@
1
+ /**
2
+ * Encrypt a secret (a GitHub access token) for at-rest storage.
3
+ *
4
+ * Uses AES-256-GCM with a random 12-byte IV and a key derived as SHA-256 of
5
+ * {@link secretMaterial}. Returns a single self-describing string
6
+ * `pmweb:v1:<iv>:<authTag>:<ciphertext>`; the IV, auth tag, and ciphertext
7
+ * are base64url-encoded, while `pmweb:v1` remains a literal prefix that tags
8
+ * the format for {@link decryptSecret}.
9
+ *
10
+ * @param plainText - The plaintext secret to encrypt.
11
+ * @returns The prefixed, colon-separated token string.
12
+ */
1
13
  export declare function encryptSecret(plainText: string): string;
14
+ /**
15
+ * Decrypt a value previously produced by {@link encryptSecret}.
16
+ *
17
+ * A nullish or empty input returns `null`, and a value without the `pmweb:v1:`
18
+ * prefix is returned unchanged (a legacy plaintext token passes through).
19
+ * Otherwise the IV, auth tag and ciphertext are parsed and decrypted with
20
+ * AES-256-GCM; a malformed string or failed authenticity check throws.
21
+ *
22
+ * @param stored - The stored token string, or null/undefined when none exists.
23
+ * @returns The recovered plaintext, or `null` for a missing value.
24
+ */
2
25
  export declare function decryptSecret(stored: string | null | undefined): string | null;
package/dist/crypto.js CHANGED
@@ -1,5 +1,14 @@
1
1
  import crypto from "node:crypto";
2
2
  const TOKEN_PREFIX = "pmweb:v1";
3
+ /**
4
+ * Resolve the shared secret used to derive the GitHub-token encryption key.
5
+ *
6
+ * Reads `PM_WEB_SECRET_KEY` first, then falls back to `JWT_SECRET`. Throws when
7
+ * neither is set or the value is shorter than 32 characters, because AES-256
8
+ * under a weak secret would expose stored tokens to offline brute force.
9
+ *
10
+ * @returns The raw secret string (at least 32 characters).
11
+ */
3
12
  function secretMaterial() {
4
13
  const value = process.env.PM_WEB_SECRET_KEY || process.env.JWT_SECRET;
5
14
  if (!value || value.length < 32) {
@@ -10,6 +19,18 @@ function secretMaterial() {
10
19
  function encryptionKey() {
11
20
  return crypto.createHash("sha256").update(secretMaterial(), "utf8").digest();
12
21
  }
22
+ /**
23
+ * Encrypt a secret (a GitHub access token) for at-rest storage.
24
+ *
25
+ * Uses AES-256-GCM with a random 12-byte IV and a key derived as SHA-256 of
26
+ * {@link secretMaterial}. Returns a single self-describing string
27
+ * `pmweb:v1:<iv>:<authTag>:<ciphertext>`; the IV, auth tag, and ciphertext
28
+ * are base64url-encoded, while `pmweb:v1` remains a literal prefix that tags
29
+ * the format for {@link decryptSecret}.
30
+ *
31
+ * @param plainText - The plaintext secret to encrypt.
32
+ * @returns The prefixed, colon-separated token string.
33
+ */
13
34
  export function encryptSecret(plainText) {
14
35
  const iv = crypto.randomBytes(12);
15
36
  const cipher = crypto.createCipheriv("aes-256-gcm", encryptionKey(), iv);
@@ -22,6 +43,17 @@ export function encryptSecret(plainText) {
22
43
  encrypted.toString("base64url"),
23
44
  ].join(":");
24
45
  }
46
+ /**
47
+ * Decrypt a value previously produced by {@link encryptSecret}.
48
+ *
49
+ * A nullish or empty input returns `null`, and a value without the `pmweb:v1:`
50
+ * prefix is returned unchanged (a legacy plaintext token passes through).
51
+ * Otherwise the IV, auth tag and ciphertext are parsed and decrypted with
52
+ * AES-256-GCM; a malformed string or failed authenticity check throws.
53
+ *
54
+ * @param stored - The stored token string, or null/undefined when none exists.
55
+ * @returns The recovered plaintext, or `null` for a missing value.
56
+ */
25
57
  export function decryptSecret(stored) {
26
58
  if (!stored)
27
59
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEhC,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACtE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;IACnH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,OAAO;QACL,YAAY;QACZ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QACxB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;QACzB,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;KAChC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAiC;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,EAAE,AAAD,EAAG,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CACtC,aAAa,EACb,aAAa,EAAE,EACf,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAChC,CAAC;IACF,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QACvD,QAAQ,CAAC,KAAK,EAAE;KACjB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEhC;;;;;;;;GAQG;AACH,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACtE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;IACnH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,OAAO;QACL,YAAY;QACZ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QACxB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;QACzB,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;KAChC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,MAAiC;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,EAAE,AAAD,EAAG,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CACtC,aAAa,EACb,aAAa,EAAE,EACf,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAChC,CAAC;IACF,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QACvD,QAAQ,CAAC,KAAK,EAAE;KACjB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
package/dist/db.d.ts CHANGED
@@ -7,5 +7,24 @@
7
7
  * server fails fast with guidance instead.
8
8
  */
9
9
  export declare function assertDbConfigured(): void;
10
+ /**
11
+ * Shared PostgreSQL connection pool for all pm-web queries.
12
+ *
13
+ * Configured from {@link resolvePoolConfig} (DATABASE_URL or POSTGRES_* vars)
14
+ * with a generous default `max`; one client is permanently reserved for the
15
+ * LISTEN/NOTIFY channel, and `PM_WEB_DB_POOL_MAX` tunes capacity for larger
16
+ * multi-user deployments. Query code borrows from this pool rather than
17
+ * opening its own connections.
18
+ */
10
19
  export declare const pool: import("pg").Pool;
20
+ /**
21
+ * Create the pm-web database schema, idempotently.
22
+ *
23
+ * Issues `CREATE TABLE IF NOT EXISTS` for users, projects, groups, group
24
+ * members, project shares, external (OIDC) identities, the admin audit log,
25
+ * and GitHub item links, plus their indexes; runs idempotent `ADD COLUMN IF
26
+ * NOT EXISTS` migrations for later-added columns; and, when
27
+ * `PM_WEB_BOOTSTRAP_ADMIN_EMAIL` is set, promotes that (lower-cased) user to
28
+ * admin. Safe to call on every boot.
29
+ */
11
30
  export declare function initSchema(): Promise<void>;
package/dist/db.js CHANGED
@@ -42,6 +42,15 @@ export function assertDbConfigured() {
42
42
  " export DATABASE_URL=postgres://user:pass@localhost:5432/pmweb\n" +
43
43
  " (or provide POSTGRES_HOST + POSTGRES_DB and related POSTGRES_* vars).");
44
44
  }
45
+ /**
46
+ * Shared PostgreSQL connection pool for all pm-web queries.
47
+ *
48
+ * Configured from {@link resolvePoolConfig} (DATABASE_URL or POSTGRES_* vars)
49
+ * with a generous default `max`; one client is permanently reserved for the
50
+ * LISTEN/NOTIFY channel, and `PM_WEB_DB_POOL_MAX` tunes capacity for larger
51
+ * multi-user deployments. Query code borrows from this pool rather than
52
+ * opening its own connections.
53
+ */
45
54
  export const pool = new Pool({
46
55
  ...resolvePoolConfig(),
47
56
  // One client is permanently reserved for LISTEN/NOTIFY. Keep request/query
@@ -53,6 +62,16 @@ export const pool = new Pool({
53
62
  const bootstrapAdminEmail = (process.env.PM_WEB_BOOTSTRAP_ADMIN_EMAIL || "")
54
63
  .trim()
55
64
  .toLowerCase();
65
+ /**
66
+ * Create the pm-web database schema, idempotently.
67
+ *
68
+ * Issues `CREATE TABLE IF NOT EXISTS` for users, projects, groups, group
69
+ * members, project shares, external (OIDC) identities, the admin audit log,
70
+ * and GitHub item links, plus their indexes; runs idempotent `ADD COLUMN IF
71
+ * NOT EXISTS` migrations for later-added columns; and, when
72
+ * `PM_WEB_BOOTSTRAP_ADMIN_EMAIL` is set, promotes that (lower-cased) user to
73
+ * admin. Safe to call on every boot.
74
+ */
56
75
  export async function initSchema() {
57
76
  await pool.query(`
58
77
  CREATE TABLE IF NOT EXISTS pm_users (
package/dist/db.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AAEpB;;;;;;;GAOG;AACH,SAAS,iBAAiB;IACxB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7B,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IACxD,CAAC;IAED,oEAAoE;IACpE,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACzD,OAAO;YACL,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;YAC/B,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,EAAE,EAAE,CAAC;YACvD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;YAC/B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;YACvC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;SAClC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,UAAU,GACd,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACjC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,UAAU;QAAE,OAAO;IACvB,MAAM,IAAI,KAAK,CACb,mEAAmE;QACjE,mCAAmC;QACnC,qEAAqE;QACrE,yEAAyE,CAC5E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC;IAC3B,GAAG,iBAAiB,EAAE;IACtB,2EAA2E;IAC3E,wEAAwE;IACxE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5E,iBAAiB,EAAE,MAAM;IACzB,uBAAuB,EAAE,KAAK;CAC/B,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC;KACzE,IAAI,EAAE;KACN,WAAW,EAAE,CAAC;AAEjB,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDhB,CAAC,CAAC;IAEH,0CAA0C;IAC1C,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;GAMhB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;GAahB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd;;;;;;MAME,CACH,CAAC;IAEF,MAAM,IAAI,CAAC,KAAK,CACd,yFAAyF,CAC1F,CAAC;IAEF,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;GAUhB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd,6FAA6F,CAC9F,CAAC;IAEF,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,KAAK,CACd,wFAAwF,EACxF,CAAC,mBAAmB,CAAC,CACtB,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AAEpB;;;;;;;GAOG;AACH,SAAS,iBAAiB;IACxB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7B,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IACxD,CAAC;IAED,oEAAoE;IACpE,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACzD,OAAO;YACL,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;YAC/B,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,EAAE,EAAE,CAAC;YACvD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;YAC/B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;YACvC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;SAClC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,UAAU,GACd,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACjC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,UAAU;QAAE,OAAO;IACvB,MAAM,IAAI,KAAK,CACb,mEAAmE;QACjE,mCAAmC;QACnC,qEAAqE;QACrE,yEAAyE,CAC5E,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC;IAC3B,GAAG,iBAAiB,EAAE;IACtB,2EAA2E;IAC3E,wEAAwE;IACxE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5E,iBAAiB,EAAE,MAAM;IACzB,uBAAuB,EAAE,KAAK;CAC/B,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC;KACzE,IAAI,EAAE;KACN,WAAW,EAAE,CAAC;AAEjB;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDhB,CAAC,CAAC;IAEH,0CAA0C;IAC1C,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;GAMhB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;GAahB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd;;;;;;MAME,CACH,CAAC;IAEF,MAAM,IAAI,CAAC,KAAK,CACd,yFAAyF,CAC1F,CAAC;IAEF,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;GAUhB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd,6FAA6F,CAC9F,CAAC;IAEF,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,KAAK,CACd,wFAAwF,EACxF,CAAC,mBAAmB,CAAC,CACtB,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/ical.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Minimal projection of a pm item used for iCalendar export: a stable id plus
3
+ * the optional fields the VEVENT builder reads (title, type, status, priority,
4
+ * deadline, assignee, tags). Only `deadline` is required to produce an event.
5
+ */
1
6
  export type CalendarItem = {
2
7
  id: string;
3
8
  title?: string;
@@ -8,17 +13,84 @@ export type CalendarItem = {
8
13
  assignee?: string;
9
14
  tags?: string[];
10
15
  };
16
+ /**
17
+ * Options for {@link buildIcsCalendar}: the displayed calendar name, a stable
18
+ * UID domain suffix that keeps events from different projects from colliding
19
+ * in a multi-calendar client, and an optional fixed `now` to make output
20
+ * deterministic in tests.
21
+ */
11
22
  export type IcsOptions = {
12
23
  calendarName?: string;
13
24
  uidDomain?: string;
14
25
  now?: Date;
15
26
  };
27
+ /**
28
+ * Escape a value for an RFC 5545 (§3.3.11) TEXT property.
29
+ *
30
+ * Prefixes backslash, and escapes semicolons, commas, and any newline variant
31
+ * (CRLF/CR/LF) as the literal `\n` sequence, so the value can be embedded in a
32
+ * property without breaking line or field structure.
33
+ *
34
+ * @param value - The raw text to escape.
35
+ * @returns The escaped text, safe to place after a property name and colon.
36
+ */
16
37
  export declare function icsEscapeText(value: string): string;
38
+ /**
39
+ * Fold a content line to 75-octet segments per RFC 5545 (§3.1).
40
+ *
41
+ * Lines at or under 75 octets are returned unchanged; longer lines are split
42
+ * with the first segment taking 75 octets and each continuation taking 74 plus
43
+ * a single leading space, joined with CRLF. Folding is done on character
44
+ * boundaries, which is safe for the ASCII-dominant content pm produces.
45
+ *
46
+ * @param line - A single unfolded content line.
47
+ * @returns The line, folded into CRLF-joined 75-octet segments when needed.
48
+ */
17
49
  export declare function foldLine(line: string): string;
50
+ /**
51
+ * Format a Date as a UTC `DATE-TIME` value (`YYYYMMDDTHHMMSSZ`, RFC 5545 form
52
+ * 2). Uses the UTC components of the date so the result is timezone-stable.
53
+ *
54
+ * @param d - The instant to format.
55
+ * @returns The UTC timestamp string ending in `Z`.
56
+ */
18
57
  export declare function formatUtcTimestamp(d: Date): string;
58
+ /**
59
+ * Format a Date as an all-day `DATE` value (`YYYYMMDD`, RFC 5545 §3.3.4),
60
+ * using the UTC calendar date.
61
+ *
62
+ * @param d - The date to format.
63
+ * @returns The `YYYYMMDD` date string.
64
+ */
19
65
  export declare function formatDateValue(d: Date): string;
66
+ /**
67
+ * Build a single VEVENT (RFC 5545) for a pm item that has a usable deadline.
68
+ *
69
+ * Returns `null` when the item has no deadline or one {@link parseDeadline}
70
+ * cannot read. Otherwise emits UID/DTSTAMP, all-day or timed DTSTART/DTEND,
71
+ * an escaped SUMMARY and DESCRIPTION (status/assignee/priority/tags), an iCal
72
+ * PRIORITY mapped from pm's 0–4 scale, CATEGORIES from tags, and a STATUS of
73
+ * CANCELLED for closed/canceled items (CONFIRMED otherwise). Each line is
74
+ * folded and the event is joined with CRLF.
75
+ *
76
+ * @param item - The item to render.
77
+ * @param opts - The UID domain suffix and the shared DTSTAMP value.
78
+ * @returns The folded VEVENT text, or `null` when the item has no deadline.
79
+ */
20
80
  export declare function itemToVevent(item: CalendarItem, opts: {
21
81
  uidDomain: string;
22
82
  dtstamp: string;
23
83
  }): string | null;
84
+ /**
85
+ * Build a complete VCALENDAR document from pm items.
86
+ *
87
+ * Emits the VCALENDAR header (version, PRODID, calendar name), one VEVENT per
88
+ * item that has a usable deadline (items without one are skipped), and the
89
+ * closing tag, all CRLF-joined with a trailing CRLF per RFC 5545. The UID
90
+ * domain defaults to `pm-web` and is sanitized to hostname-safe characters.
91
+ *
92
+ * @param items - The items to export.
93
+ * @param opts - Optional calendar name, UID domain, and fixed `now` for tests.
94
+ * @returns The full iCalendar (.ics) document text.
95
+ */
24
96
  export declare function buildIcsCalendar(items: CalendarItem[], opts?: IcsOptions): string;
package/dist/ical.js CHANGED
@@ -5,6 +5,16 @@
5
5
  // the pm CLI and hands the relevant fields here.
6
6
  // Escape a text value per RFC 5545 §3.3.11 (TEXT): backslash, semicolon,
7
7
  // comma and newlines must be escaped.
8
+ /**
9
+ * Escape a value for an RFC 5545 (§3.3.11) TEXT property.
10
+ *
11
+ * Prefixes backslash, and escapes semicolons, commas, and any newline variant
12
+ * (CRLF/CR/LF) as the literal `\n` sequence, so the value can be embedded in a
13
+ * property without breaking line or field structure.
14
+ *
15
+ * @param value - The raw text to escape.
16
+ * @returns The escaped text, safe to place after a property name and colon.
17
+ */
8
18
  export function icsEscapeText(value) {
9
19
  return value
10
20
  .replace(/\\/g, "\\\\")
@@ -15,6 +25,17 @@ export function icsEscapeText(value) {
15
25
  // Fold long content lines to 75 octets per RFC 5545 §3.1. Continuation lines
16
26
  // begin with a single space. We fold on character boundaries which is safe for
17
27
  // the ASCII-dominant content pm produces.
28
+ /**
29
+ * Fold a content line to 75-octet segments per RFC 5545 (§3.1).
30
+ *
31
+ * Lines at or under 75 octets are returned unchanged; longer lines are split
32
+ * with the first segment taking 75 octets and each continuation taking 74 plus
33
+ * a single leading space, joined with CRLF. Folding is done on character
34
+ * boundaries, which is safe for the ASCII-dominant content pm produces.
35
+ *
36
+ * @param line - A single unfolded content line.
37
+ * @returns The line, folded into CRLF-joined 75-octet segments when needed.
38
+ */
18
39
  export function foldLine(line) {
19
40
  if (line.length <= 75)
20
41
  return line;
@@ -30,18 +51,44 @@ export function foldLine(line) {
30
51
  return parts.join("\r\n");
31
52
  }
32
53
  // Format a Date as a UTC timestamp: YYYYMMDDTHHMMSSZ (RFC 5545 form 2).
54
+ /**
55
+ * Format a Date as a UTC `DATE-TIME` value (`YYYYMMDDTHHMMSSZ`, RFC 5545 form
56
+ * 2). Uses the UTC components of the date so the result is timezone-stable.
57
+ *
58
+ * @param d - The instant to format.
59
+ * @returns The UTC timestamp string ending in `Z`.
60
+ */
33
61
  export function formatUtcTimestamp(d) {
34
62
  const p = (n, w = 2) => String(n).padStart(w, "0");
35
63
  return (`${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}` +
36
64
  `T${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}Z`);
37
65
  }
38
66
  // Format a Date as an all-day DATE value: YYYYMMDD (RFC 5545 §3.3.4).
67
+ /**
68
+ * Format a Date as an all-day `DATE` value (`YYYYMMDD`, RFC 5545 §3.3.4),
69
+ * using the UTC calendar date.
70
+ *
71
+ * @param d - The date to format.
72
+ * @returns The `YYYYMMDD` date string.
73
+ */
39
74
  export function formatDateValue(d) {
40
75
  const p = (n) => String(n).padStart(2, "0");
41
76
  return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}`;
42
77
  }
43
78
  // A pm deadline may be a date-only string (YYYY-MM-DD) or a full ISO datetime.
44
79
  // Returns null for anything unparseable so callers can skip the item.
80
+ /**
81
+ * Parse a pm deadline string into a date plus an all-day flag.
82
+ *
83
+ * Accepts a date-only `YYYY-MM-DD` value or a full ISO datetime; surrounding
84
+ * whitespace and empty strings yield `null`, as do values `new Date` cannot
85
+ * parse. A date-only value, or any instant that lands exactly on midnight UTC,
86
+ * is treated as an all-day event (which calendar clients render far better
87
+ * than a zero-length 00:00 event).
88
+ *
89
+ * @param raw - The raw deadline string from a pm item.
90
+ * @returns The parsed date and all-day flag, or `null` when unparseable.
91
+ */
45
92
  function parseDeadline(raw) {
46
93
  const trimmed = raw.trim();
47
94
  if (!trimmed)
@@ -62,6 +109,20 @@ function parseDeadline(raw) {
62
109
  }
63
110
  // Build a single VEVENT for a pm item with a deadline. Returns null when the
64
111
  // item has no usable deadline.
112
+ /**
113
+ * Build a single VEVENT (RFC 5545) for a pm item that has a usable deadline.
114
+ *
115
+ * Returns `null` when the item has no deadline or one {@link parseDeadline}
116
+ * cannot read. Otherwise emits UID/DTSTAMP, all-day or timed DTSTART/DTEND,
117
+ * an escaped SUMMARY and DESCRIPTION (status/assignee/priority/tags), an iCal
118
+ * PRIORITY mapped from pm's 0–4 scale, CATEGORIES from tags, and a STATUS of
119
+ * CANCELLED for closed/canceled items (CONFIRMED otherwise). Each line is
120
+ * folded and the event is joined with CRLF.
121
+ *
122
+ * @param item - The item to render.
123
+ * @param opts - The UID domain suffix and the shared DTSTAMP value.
124
+ * @returns The folded VEVENT text, or `null` when the item has no deadline.
125
+ */
65
126
  export function itemToVevent(item, opts) {
66
127
  if (!item.deadline)
67
128
  return null;
@@ -114,6 +175,18 @@ export function itemToVevent(item, opts) {
114
175
  }
115
176
  // Build a complete VCALENDAR document from pm items. Items without a usable
116
177
  // deadline are skipped. Output uses CRLF line endings per RFC 5545 §3.1.
178
+ /**
179
+ * Build a complete VCALENDAR document from pm items.
180
+ *
181
+ * Emits the VCALENDAR header (version, PRODID, calendar name), one VEVENT per
182
+ * item that has a usable deadline (items without one are skipped), and the
183
+ * closing tag, all CRLF-joined with a trailing CRLF per RFC 5545. The UID
184
+ * domain defaults to `pm-web` and is sanitized to hostname-safe characters.
185
+ *
186
+ * @param items - The items to export.
187
+ * @param opts - Optional calendar name, UID domain, and fixed `now` for tests.
188
+ * @returns The full iCalendar (.ics) document text.
189
+ */
117
190
  export function buildIcsCalendar(items, opts = {}) {
118
191
  const now = opts.now ?? new Date();
119
192
  const dtstamp = formatUtcTimestamp(now);