@kelpie/server 0.7.0 → 0.8.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 (185) hide show
  1. package/dist/lib/consumerEmailDomains.d.ts +2 -0
  2. package/dist/lib/consumerEmailDomains.d.ts.map +1 -0
  3. package/dist/lib/consumerEmailDomains.js +62 -0
  4. package/dist/lib/consumerEmailDomains.js.map +1 -0
  5. package/dist/lib/email.d.ts +7 -0
  6. package/dist/lib/email.d.ts.map +1 -1
  7. package/dist/lib/email.js.map +1 -1
  8. package/dist/lib/emailContent.d.ts +33 -0
  9. package/dist/lib/emailContent.d.ts.map +1 -0
  10. package/dist/lib/emailContent.js +54 -0
  11. package/dist/lib/emailContent.js.map +1 -0
  12. package/dist/lib/emailDomainAutoLink.d.ts +27 -0
  13. package/dist/lib/emailDomainAutoLink.d.ts.map +1 -0
  14. package/dist/lib/emailDomainAutoLink.js +119 -0
  15. package/dist/lib/emailDomainAutoLink.js.map +1 -0
  16. package/dist/lib/ids.d.ts +2 -0
  17. package/dist/lib/ids.d.ts.map +1 -1
  18. package/dist/lib/ids.js +2 -0
  19. package/dist/lib/ids.js.map +1 -1
  20. package/dist/modules/attachedRecords.d.ts.map +1 -1
  21. package/dist/modules/attachedRecords.js +4 -2
  22. package/dist/modules/attachedRecords.js.map +1 -1
  23. package/dist/modules/auth/preferences.d.ts +11 -1
  24. package/dist/modules/auth/preferences.d.ts.map +1 -1
  25. package/dist/modules/auth/preferences.js +11 -0
  26. package/dist/modules/auth/preferences.js.map +1 -1
  27. package/dist/modules/auth/routes.d.ts.map +1 -1
  28. package/dist/modules/auth/routes.js +18 -0
  29. package/dist/modules/auth/routes.js.map +1 -1
  30. package/dist/modules/auth/schema.d.ts +20 -0
  31. package/dist/modules/auth/schema.d.ts.map +1 -1
  32. package/dist/modules/auth/schema.js +11 -1
  33. package/dist/modules/auth/schema.js.map +1 -1
  34. package/dist/modules/auth/service.d.ts.map +1 -1
  35. package/dist/modules/auth/service.js +27 -3
  36. package/dist/modules/auth/service.js.map +1 -1
  37. package/dist/modules/companies/repository.d.ts +2 -0
  38. package/dist/modules/companies/repository.d.ts.map +1 -1
  39. package/dist/modules/companies/repository.js +1 -0
  40. package/dist/modules/companies/repository.js.map +1 -1
  41. package/dist/modules/companies/routes.d.ts +2 -0
  42. package/dist/modules/companies/routes.d.ts.map +1 -1
  43. package/dist/modules/companies/routes.js +11 -0
  44. package/dist/modules/companies/routes.js.map +1 -1
  45. package/dist/modules/companies/schema.d.ts +17 -0
  46. package/dist/modules/companies/schema.d.ts.map +1 -1
  47. package/dist/modules/companies/schema.js +5 -1
  48. package/dist/modules/companies/schema.js.map +1 -1
  49. package/dist/modules/companies/service.d.ts +1 -0
  50. package/dist/modules/companies/service.d.ts.map +1 -1
  51. package/dist/modules/companies/service.js +22 -0
  52. package/dist/modules/companies/service.js.map +1 -1
  53. package/dist/modules/core.d.ts.map +1 -1
  54. package/dist/modules/core.js +11 -0
  55. package/dist/modules/core.js.map +1 -1
  56. package/dist/modules/email-domain-linker/index.d.ts +12 -0
  57. package/dist/modules/email-domain-linker/index.d.ts.map +1 -0
  58. package/dist/modules/email-domain-linker/index.js +34 -0
  59. package/dist/modules/email-domain-linker/index.js.map +1 -0
  60. package/dist/modules/email-domain-linker/routes.d.ts +14 -0
  61. package/dist/modules/email-domain-linker/routes.d.ts.map +1 -0
  62. package/dist/modules/email-domain-linker/routes.js +15 -0
  63. package/dist/modules/email-domain-linker/routes.js.map +1 -0
  64. package/dist/modules/email-domain-linker/service.d.ts +37 -0
  65. package/dist/modules/email-domain-linker/service.d.ts.map +1 -0
  66. package/dist/modules/email-domain-linker/service.js +63 -0
  67. package/dist/modules/email-domain-linker/service.js.map +1 -0
  68. package/dist/modules/eventCatalogs.d.ts +1 -0
  69. package/dist/modules/eventCatalogs.d.ts.map +1 -1
  70. package/dist/modules/eventCatalogs.js +1 -0
  71. package/dist/modules/eventCatalogs.js.map +1 -1
  72. package/dist/modules/lists/events.d.ts +42 -0
  73. package/dist/modules/lists/events.d.ts.map +1 -0
  74. package/dist/modules/lists/events.js +16 -0
  75. package/dist/modules/lists/events.js.map +1 -0
  76. package/dist/modules/lists/index.d.ts +10 -0
  77. package/dist/modules/lists/index.d.ts.map +1 -0
  78. package/dist/modules/lists/index.js +35 -0
  79. package/dist/modules/lists/index.js.map +1 -0
  80. package/dist/modules/lists/repository.d.ts +69 -0
  81. package/dist/modules/lists/repository.d.ts.map +1 -0
  82. package/dist/modules/lists/repository.js +165 -0
  83. package/dist/modules/lists/repository.js.map +1 -0
  84. package/dist/modules/lists/routes.d.ts +51 -0
  85. package/dist/modules/lists/routes.d.ts.map +1 -0
  86. package/dist/modules/lists/routes.js +160 -0
  87. package/dist/modules/lists/routes.js.map +1 -0
  88. package/dist/modules/lists/schema.d.ts +264 -0
  89. package/dist/modules/lists/schema.d.ts.map +1 -0
  90. package/dist/modules/lists/schema.js +73 -0
  91. package/dist/modules/lists/schema.js.map +1 -0
  92. package/dist/modules/lists/service.d.ts +70 -0
  93. package/dist/modules/lists/service.d.ts.map +1 -0
  94. package/dist/modules/lists/service.js +214 -0
  95. package/dist/modules/lists/service.js.map +1 -0
  96. package/dist/modules/lists/tools.d.ts +4 -0
  97. package/dist/modules/lists/tools.d.ts.map +1 -0
  98. package/dist/modules/lists/tools.js +86 -0
  99. package/dist/modules/lists/tools.js.map +1 -0
  100. package/dist/modules/people/repository.d.ts +11 -0
  101. package/dist/modules/people/repository.d.ts.map +1 -1
  102. package/dist/modules/people/repository.js +16 -0
  103. package/dist/modules/people/repository.js.map +1 -1
  104. package/dist/modules/people/service.d.ts.map +1 -1
  105. package/dist/modules/people/service.js +18 -0
  106. package/dist/modules/people/service.js.map +1 -1
  107. package/dist/modules/positions/routes.d.ts.map +1 -1
  108. package/dist/modules/positions/routes.js +4 -2
  109. package/dist/modules/positions/routes.js.map +1 -1
  110. package/dist/modules/sample-data/fixture.d.ts +139 -0
  111. package/dist/modules/sample-data/fixture.d.ts.map +1 -0
  112. package/dist/modules/sample-data/fixture.js +461 -0
  113. package/dist/modules/sample-data/fixture.js.map +1 -0
  114. package/dist/modules/sample-data/index.d.ts +11 -0
  115. package/dist/modules/sample-data/index.d.ts.map +1 -0
  116. package/dist/modules/sample-data/index.js +43 -0
  117. package/dist/modules/sample-data/index.js.map +1 -0
  118. package/dist/modules/sample-data/routes.d.ts +15 -0
  119. package/dist/modules/sample-data/routes.d.ts.map +1 -0
  120. package/dist/modules/sample-data/routes.js +24 -0
  121. package/dist/modules/sample-data/routes.js.map +1 -0
  122. package/dist/modules/sample-data/service.d.ts +38 -0
  123. package/dist/modules/sample-data/service.d.ts.map +1 -0
  124. package/dist/modules/sample-data/service.js +412 -0
  125. package/dist/modules/sample-data/service.js.map +1 -0
  126. package/dist/modules/sample-data/tools.d.ts +4 -0
  127. package/dist/modules/sample-data/tools.d.ts.map +1 -0
  128. package/dist/modules/sample-data/tools.js +22 -0
  129. package/dist/modules/sample-data/tools.js.map +1 -0
  130. package/dist/modules/smtp-email/index.d.ts +1 -0
  131. package/dist/modules/smtp-email/index.d.ts.map +1 -1
  132. package/dist/modules/smtp-email/index.js +1 -0
  133. package/dist/modules/smtp-email/index.js.map +1 -1
  134. package/dist/modules/workspace/service.d.ts.map +1 -1
  135. package/dist/modules/workspace/service.js +10 -1
  136. package/dist/modules/workspace/service.js.map +1 -1
  137. package/dist/schema/index.d.ts +1 -0
  138. package/dist/schema/index.d.ts.map +1 -1
  139. package/dist/schema/index.js +1 -0
  140. package/dist/schema/index.js.map +1 -1
  141. package/migrations/0021_certain_maggott.sql +30 -0
  142. package/migrations/0022_stormy_manta.sql +1 -0
  143. package/migrations/0023_funny_maddog.sql +1 -0
  144. package/migrations/meta/0021_snapshot.json +5389 -0
  145. package/migrations/meta/0022_snapshot.json +5396 -0
  146. package/migrations/meta/0023_snapshot.json +5403 -0
  147. package/migrations/meta/_journal.json +21 -0
  148. package/package.json +3 -2
  149. package/src/lib/consumerEmailDomains.ts +62 -0
  150. package/src/lib/email.ts +7 -0
  151. package/src/lib/emailContent.ts +91 -0
  152. package/src/lib/emailDomainAutoLink.ts +165 -0
  153. package/src/lib/ids.ts +2 -0
  154. package/src/modules/attachedRecords.ts +9 -2
  155. package/src/modules/auth/preferences.ts +13 -1
  156. package/src/modules/auth/routes.ts +22 -0
  157. package/src/modules/auth/schema.ts +12 -2
  158. package/src/modules/auth/service.ts +37 -3
  159. package/src/modules/companies/repository.ts +3 -0
  160. package/src/modules/companies/routes.ts +13 -0
  161. package/src/modules/companies/schema.ts +5 -1
  162. package/src/modules/companies/service.ts +25 -0
  163. package/src/modules/core.ts +11 -0
  164. package/src/modules/email-domain-linker/index.ts +40 -0
  165. package/src/modules/email-domain-linker/routes.ts +40 -0
  166. package/src/modules/email-domain-linker/service.ts +122 -0
  167. package/src/modules/eventCatalogs.ts +1 -0
  168. package/src/modules/lists/events.ts +41 -0
  169. package/src/modules/lists/index.ts +41 -0
  170. package/src/modules/lists/repository.ts +313 -0
  171. package/src/modules/lists/routes.ts +247 -0
  172. package/src/modules/lists/schema.ts +85 -0
  173. package/src/modules/lists/service.ts +404 -0
  174. package/src/modules/lists/tools.ts +128 -0
  175. package/src/modules/people/repository.ts +26 -0
  176. package/src/modules/people/service.ts +20 -0
  177. package/src/modules/positions/routes.ts +4 -2
  178. package/src/modules/sample-data/fixture.ts +627 -0
  179. package/src/modules/sample-data/index.ts +50 -0
  180. package/src/modules/sample-data/routes.ts +49 -0
  181. package/src/modules/sample-data/service.ts +590 -0
  182. package/src/modules/sample-data/tools.ts +32 -0
  183. package/src/modules/smtp-email/index.ts +8 -1
  184. package/src/modules/workspace/service.ts +13 -1
  185. package/src/schema/index.ts +1 -0
@@ -0,0 +1,2 @@
1
+ export declare function isConsumerEmailDomain(domain: string): boolean;
2
+ //# sourceMappingURL=consumerEmailDomains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumerEmailDomains.d.ts","sourceRoot":"","sources":["../../src/lib/consumerEmailDomains.ts"],"names":[],"mappings":"AA2DA,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Consumer email hosts, checked case-insensitively.
3
+ *
4
+ * The email-domain listener refuses to auto-link a Person to a Company on any
5
+ * domain in this list. A consumer address belongs to no one company, and a
6
+ * Company row entered by mistake against, say, `gmail.com` would otherwise
7
+ * auto-attach every consumer address after.
8
+ *
9
+ * A starter list of the common providers. Add locale variants and less common
10
+ * hosts as the need arrives.
11
+ */
12
+ const CONSUMER_EMAIL_DOMAINS = new Set([
13
+ 'gmail.com',
14
+ 'googlemail.com',
15
+ 'yahoo.com',
16
+ 'yahoo.co.uk',
17
+ 'yahoo.co.jp',
18
+ 'yahoo.fr',
19
+ 'yahoo.de',
20
+ 'yahoo.ca',
21
+ 'yahoo.com.au',
22
+ 'ymail.com',
23
+ 'rocketmail.com',
24
+ 'hotmail.com',
25
+ 'hotmail.co.uk',
26
+ 'hotmail.fr',
27
+ 'hotmail.de',
28
+ 'hotmail.it',
29
+ 'hotmail.es',
30
+ 'outlook.com',
31
+ 'outlook.co.uk',
32
+ 'live.com',
33
+ 'live.co.uk',
34
+ 'msn.com',
35
+ 'aol.com',
36
+ 'aim.com',
37
+ 'icloud.com',
38
+ 'me.com',
39
+ 'mac.com',
40
+ 'protonmail.com',
41
+ 'proton.me',
42
+ 'pm.me',
43
+ 'gmx.com',
44
+ 'gmx.net',
45
+ 'gmx.de',
46
+ 'mail.com',
47
+ 'fastmail.com',
48
+ 'fastmail.fm',
49
+ 'zoho.com',
50
+ 'yandex.com',
51
+ 'yandex.ru',
52
+ 'tutanota.com',
53
+ 'tuta.io',
54
+ 'qq.com',
55
+ '163.com',
56
+ '126.com',
57
+ 'naver.com',
58
+ ]);
59
+ export function isConsumerEmailDomain(domain) {
60
+ return CONSUMER_EMAIL_DOMAINS.has(domain.trim().toLowerCase());
61
+ }
62
+ //# sourceMappingURL=consumerEmailDomains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumerEmailDomains.js","sourceRoot":"","sources":["../../src/lib/consumerEmailDomains.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IAC1D,WAAW;IACX,gBAAgB;IAChB,WAAW;IACX,aAAa;IACb,aAAa;IACb,UAAU;IACV,UAAU;IACV,UAAU;IACV,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,aAAa;IACb,eAAe;IACf,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,eAAe;IACf,UAAU;IACV,YAAY;IACZ,SAAS;IACT,SAAS;IACT,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,OAAO;IACP,SAAS;IACT,SAAS;IACT,QAAQ;IACR,UAAU;IACV,cAAc;IACd,aAAa;IACb,UAAU;IACV,YAAY;IACZ,WAAW;IACX,cAAc;IACd,SAAS;IACT,QAAQ;IACR,SAAS;IACT,SAAS;IACT,WAAW;CACZ,CAAC,CAAA;AAEF,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,OAAO,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;AAChE,CAAC"}
@@ -18,7 +18,14 @@ import type { Logger } from './logger.ts';
18
18
  export interface EmailMessage {
19
19
  readonly to: string;
20
20
  readonly subject: string;
21
+ /** The plaintext part. Always present: text-only clients and logs read it. */
21
22
  readonly body: string;
23
+ /**
24
+ * The HTML alternative, rendered by `lib/emailContent.ts`. Optional so
25
+ * provider modules written against the older port keep compiling and a
26
+ * plain-text message stays expressible.
27
+ */
28
+ readonly html?: string;
22
29
  }
23
30
  export interface EmailSender {
24
31
  send(message: EmailMessage): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/lib/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3C;AAED,eAAO,MAAM,iBAAiB;;;iBAS5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAa9E"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/lib/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3C;AAED,eAAO,MAAM,iBAAiB;;;iBAS5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAa9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/lib/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8BvB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC;;;;;OAKG;IACH,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAA;AAIF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,IAAY;IAC/D,OAAO;QACL,IAAI,CAAC,OAAO;YACV,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE;gBAC3D,IAAI;gBACJ,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAA;YAEF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/lib/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAqCvB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC;;;;;OAKG;IACH,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAA;AAIF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,IAAY;IAC/D,OAAO;QACL,IAAI,CAAC,OAAO;YACV,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE;gBAC3D,IAAI;gBACJ,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAA;YAEF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Renders transactional mail bodies: a plaintext part and an HTML part from
3
+ * one description of the message.
4
+ *
5
+ * Email clients need table-based, inline-styled HTML; mailgen owns that
6
+ * problem so Kelpie does not. The plaintext part is built here by hand rather
7
+ * than by mailgen's plaintext generator, because that generator pads lines
8
+ * with trailing whitespace and the plaintext body is the part tests and
9
+ * text-only clients read token links out of. The wording of the two parts
10
+ * comes from the same fields, so they cannot drift.
11
+ */
12
+ /** A link the message asks the reader to follow, rendered as a button in HTML. */
13
+ export interface EmailAction {
14
+ readonly instructions: string;
15
+ readonly buttonText: string;
16
+ readonly link: string;
17
+ }
18
+ export interface EmailContent {
19
+ readonly intro?: string;
20
+ readonly action?: EmailAction;
21
+ readonly outro?: string;
22
+ }
23
+ export interface RenderedEmail {
24
+ readonly text: string;
25
+ readonly html: string;
26
+ }
27
+ /**
28
+ * Renders one transactional message. `appBaseUrl` becomes the link on the
29
+ * product name in the header and footer, so every part of the mail points at
30
+ * the deployment that sent it.
31
+ */
32
+ export declare function renderEmail(content: EmailContent, appBaseUrl: string): RenderedEmail;
33
+ //# sourceMappingURL=emailContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emailContent.d.ts","sourceRoot":"","sources":["../../src/lib/emailContent.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AASD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,CA8CpF"}
@@ -0,0 +1,54 @@
1
+ import Mailgen from 'mailgen';
2
+ /**
3
+ * Fixed so rendering is deterministic. Mailgen's default footer stamps the
4
+ * current year, which the sender has no claim to: the workspace, not Kelpie,
5
+ * owns the relationship with the recipient.
6
+ */
7
+ const FOOTER_LINE = 'Sent by Kelpie.';
8
+ /**
9
+ * Renders one transactional message. `appBaseUrl` becomes the link on the
10
+ * product name in the header and footer, so every part of the mail points at
11
+ * the deployment that sent it.
12
+ */
13
+ export function renderEmail(content, appBaseUrl) {
14
+ const generator = new Mailgen({
15
+ theme: 'default',
16
+ product: {
17
+ name: 'Kelpie',
18
+ link: appBaseUrl,
19
+ copyright: FOOTER_LINE,
20
+ },
21
+ });
22
+ const html = generator.generate({
23
+ body: {
24
+ greeting: false,
25
+ signature: false,
26
+ ...(content.intro === undefined ? {} : { intro: content.intro }),
27
+ ...(content.action === undefined
28
+ ? {}
29
+ : {
30
+ action: {
31
+ instructions: content.action.instructions,
32
+ button: {
33
+ text: content.action.buttonText,
34
+ link: content.action.link,
35
+ fallback: true,
36
+ },
37
+ },
38
+ }),
39
+ ...(content.outro === undefined ? {} : { outro: content.outro }),
40
+ },
41
+ });
42
+ const paragraphs = [];
43
+ if (content.intro !== undefined) {
44
+ paragraphs.push(content.intro);
45
+ }
46
+ if (content.action !== undefined) {
47
+ paragraphs.push(`${content.action.instructions}\n\n${content.action.link}`);
48
+ }
49
+ if (content.outro !== undefined) {
50
+ paragraphs.push(content.outro);
51
+ }
52
+ return { text: paragraphs.join('\n\n'), html };
53
+ }
54
+ //# sourceMappingURL=emailContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emailContent.js","sourceRoot":"","sources":["../../src/lib/emailContent.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAgC7B;;;;GAIG;AACH,MAAM,WAAW,GAAG,iBAAiB,CAAA;AAErC;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAAqB,EAAE,UAAkB;IACnE,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC;QAC5B,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,WAAW;SACvB;KACF,CAAC,CAAA;IAEF,MAAM,IAAI,GAAW,SAAS,CAAC,QAAQ,CAAC;QACtC,IAAI,EAAE;YACJ,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,KAAK;YAChB,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;YAChE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS;gBAC9B,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,MAAM,EAAE;wBACN,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY;wBACzC,MAAM,EAAE;4BACN,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU;4BAC/B,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;4BACzB,QAAQ,EAAE,IAAI;yBACf;qBACF;iBACF,CAAC;YACN,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;SACjE;KACF,CAAC,CAAA;IAEF,MAAM,UAAU,GAAa,EAAE,CAAA;IAE/B,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,UAAU,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7E,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAA;AAChD,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { IdFactory } from './ids.ts';
2
+ import type { BufferedEvents, Transaction } from '../runtime/transaction.ts';
3
+ import type { ActivityRecorder } from '../modules/activities/recorder.ts';
4
+ import type { CompanyRecord } from '../modules/companies/repository.ts';
5
+ import type { PersonRecord } from '../modules/people/repository.ts';
6
+ import '../modules/positions/events.ts';
7
+ export interface AutoLinkDependencies {
8
+ readonly createId: IdFactory;
9
+ readonly now: () => Date;
10
+ readonly recordActivity: ActivityRecorder;
11
+ }
12
+ /**
13
+ * Considers `person` for a matching Company in `workspaceId` and inserts a
14
+ * titleless Position if none links them yet.
15
+ *
16
+ * @returns The new Position's id, or `undefined` if nothing was written.
17
+ */
18
+ export declare function autoLinkPersonByEmailDomain(tx: Transaction, events: BufferedEvents, workspaceId: string, person: PersonRecord, dependencies: AutoLinkDependencies): Promise<string | undefined>;
19
+ /**
20
+ * Sweeps every Person in `workspaceId` whose email domain matches `company`'s
21
+ * domain and stubs a Position where none exists yet. Called when a Company is
22
+ * created or its domain is set.
23
+ *
24
+ * @returns The ids of every Position this call inserted.
25
+ */
26
+ export declare function autoLinkCompanyByDomain(tx: Transaction, events: BufferedEvents, workspaceId: string, company: CompanyRecord, dependencies: AutoLinkDependencies): Promise<readonly string[]>;
27
+ //# sourceMappingURL=emailDomainAutoLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emailDomainAutoLink.d.ts","sourceRoot":"","sources":["../../src/lib/emailDomainAutoLink.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,mCAAmC,CAAA;AAGtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,gCAAgC,CAAA;AAwBvC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,CAAA;IACxB,QAAQ,CAAC,cAAc,EAAE,gBAAgB,CAAA;CAC1C;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GACjC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA0B7B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,EACtB,YAAY,EAAE,oBAAoB,GACjC,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAyB5B"}
@@ -0,0 +1,119 @@
1
+ import { UNIQUE_VIOLATION, postgresErrorCode } from './database.js';
2
+ import { isConsumerEmailDomain } from './consumerEmailDomains.js';
3
+ import { describeLink } from '../modules/activities/wording.js';
4
+ import * as companyRepository from '../modules/companies/repository.js';
5
+ import * as peopleRepository from '../modules/people/repository.js';
6
+ import '../modules/positions/events.js';
7
+ import * as positionRepository from '../modules/positions/repository.js';
8
+ /**
9
+ * Auto-link a Person to a workspace Company whose domain matches the person's
10
+ * email. Runs inside the caller's transaction, so the new Position is present
11
+ * in the response the API returns for the same request.
12
+ *
13
+ * Skips: no email, consumer email host (`isConsumerEmailDomain`), no Company
14
+ * on that domain in the workspace, or a Position already linking the two. A
15
+ * unique-key race with a concurrent linker is absorbed.
16
+ *
17
+ * The created Position carries an empty title. `positions.title` is `NOT NULL`,
18
+ * and empty reads honestly as "the domain matched; no title yet".
19
+ *
20
+ * Split from the people service so the layering (people below positions) is
21
+ * carried by imports only: this helper depends on both repositories, and the
22
+ * people service depends on this helper. The `positions` module still owns the
23
+ * event catalogue for the emitted event.
24
+ */
25
+ /** What the timeline calls this side effect. */
26
+ const LINKER_ACTOR = { kind: 'system', label: 'Email domain match' };
27
+ /**
28
+ * Considers `person` for a matching Company in `workspaceId` and inserts a
29
+ * titleless Position if none links them yet.
30
+ *
31
+ * @returns The new Position's id, or `undefined` if nothing was written.
32
+ */
33
+ export async function autoLinkPersonByEmailDomain(tx, events, workspaceId, person, dependencies) {
34
+ if (person.email === null) {
35
+ return undefined;
36
+ }
37
+ const at = person.email.indexOf('@');
38
+ if (at < 0) {
39
+ return undefined;
40
+ }
41
+ const domain = person.email.slice(at + 1);
42
+ if (domain.length === 0 || isConsumerEmailDomain(domain)) {
43
+ return undefined;
44
+ }
45
+ const company = await companyRepository.findCompanyByDomain(tx, workspaceId, domain);
46
+ if (company === undefined) {
47
+ return undefined;
48
+ }
49
+ const held = await positionRepository.listPositionsAt(tx, workspaceId, person.id, company.id);
50
+ if (held.length > 0) {
51
+ return undefined;
52
+ }
53
+ return insertStubPosition(tx, events, workspaceId, person, company, dependencies);
54
+ }
55
+ /**
56
+ * Sweeps every Person in `workspaceId` whose email domain matches `company`'s
57
+ * domain and stubs a Position where none exists yet. Called when a Company is
58
+ * created or its domain is set.
59
+ *
60
+ * @returns The ids of every Position this call inserted.
61
+ */
62
+ export async function autoLinkCompanyByDomain(tx, events, workspaceId, company, dependencies) {
63
+ if (company.domain === null || company.domain.length === 0) {
64
+ return [];
65
+ }
66
+ if (isConsumerEmailDomain(company.domain)) {
67
+ return [];
68
+ }
69
+ const matches = await peopleRepository.findPeopleByEmailDomain(tx, workspaceId, company.domain);
70
+ const created = [];
71
+ for (const person of matches) {
72
+ const held = await positionRepository.listPositionsAt(tx, workspaceId, person.id, company.id);
73
+ if (held.length > 0) {
74
+ continue;
75
+ }
76
+ const id = await insertStubPosition(tx, events, workspaceId, person, company, dependencies);
77
+ if (id !== undefined) {
78
+ created.push(id);
79
+ }
80
+ }
81
+ return created;
82
+ }
83
+ /**
84
+ * The write half. Wraps the unique-constraint race the two entry points share:
85
+ * a concurrent linker that beat us to `(personId, companyId, '')` is treated
86
+ * as success (the link is there, either way).
87
+ */
88
+ async function insertStubPosition(tx, events, workspaceId, person, company, dependencies) {
89
+ try {
90
+ const created = await positionRepository.insertPosition(tx, {
91
+ id: dependencies.createId('position'),
92
+ workspaceId,
93
+ personId: person.id,
94
+ companyId: company.id,
95
+ title: '',
96
+ });
97
+ await dependencies.recordActivity(tx, workspaceId, LINKER_ACTOR, {
98
+ targetType: 'person',
99
+ targetId: person.id,
100
+ kind: 'linked',
101
+ ...describeLink('company', company.name),
102
+ });
103
+ await dependencies.recordActivity(tx, workspaceId, LINKER_ACTOR, {
104
+ targetType: 'company',
105
+ targetId: company.id,
106
+ kind: 'linked',
107
+ ...describeLink('person', person.name),
108
+ });
109
+ events.emit('positions.position.created', { type: 'position', id: created.id }, {});
110
+ return created.id;
111
+ }
112
+ catch (error) {
113
+ if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
114
+ return undefined;
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ //# sourceMappingURL=emailDomainAutoLink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emailDomainAutoLink.js","sourceRoot":"","sources":["../../src/lib/emailDomainAutoLink.ts"],"names":[],"mappings":"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"}
package/dist/lib/ids.d.ts CHANGED
@@ -26,6 +26,8 @@ export declare const idPrefixes: {
26
26
  readonly decision: 'dec';
27
27
  readonly note: 'note';
28
28
  readonly activity: 'act';
29
+ readonly list: 'list';
30
+ readonly listMember: 'lmem';
29
31
  readonly handbookPage: 'hb';
30
32
  readonly form: 'form';
31
33
  readonly formField: 'ff';
@@ -1 +1 @@
1
- {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/lib/ids.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,UAAU;aACrB,SAAS,EAAE,IAAI;aACf,IAAI,EAAE,KAAK;aACX,OAAO,EAAE,KAAK;aACd,kBAAkB,EAAE,KAAK;IACzB,wFAAwF;aACxF,sBAAsB,EAAE,KAAK;aAC7B,UAAU,EAAE,KAAK;aACjB,MAAM,EAAE,KAAK;aACb,MAAM,EAAE,KAAK;aACb,MAAM,EAAE,KAAK;aACb,OAAO,EAAE,KAAK;aACd,QAAQ,EAAE,KAAK;aACf,aAAa,EAAE,OAAO;aACtB,IAAI,EAAE,MAAM;aACZ,WAAW,EAAE,KAAK;aAClB,WAAW,EAAE,KAAK;aAClB,KAAK,EAAE,KAAK;aACZ,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM;aACjB,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,KAAK;aACf,IAAI,EAAE,MAAM;aACZ,QAAQ,EAAE,KAAK;aACf,YAAY,EAAE,IAAI;aAClB,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,IAAI;aACf,cAAc,EAAE,KAAK;aACrB,OAAO,EAAE,IAAI;IACb,8EAA8E;aAC9E,eAAe,EAAE,KAAK;aACtB,iBAAiB,EAAE,IAAI;aACvB,QAAQ,EAAE,KAAK;aACf,SAAS,EAAE,KAAK;IAChB,6FAA6F;aAC7F,aAAa,EAAE,MAAM;IACrB;;;;;OAKG;aACH,cAAc,EAAE,MAAM;IACtB;;;OAGG;aACH,eAAe,EAAE,IAAI;IACrB;;;;OAIG;aACH,KAAK,EAAE,IAAI;CACH,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,UAAU,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,YAAY,GAAE,MAAM,MAAa,GAAG,SAAS,CAE5E"}
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/lib/ids.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,UAAU;aACrB,SAAS,EAAE,IAAI;aACf,IAAI,EAAE,KAAK;aACX,OAAO,EAAE,KAAK;aACd,kBAAkB,EAAE,KAAK;IACzB,wFAAwF;aACxF,sBAAsB,EAAE,KAAK;aAC7B,UAAU,EAAE,KAAK;aACjB,MAAM,EAAE,KAAK;aACb,MAAM,EAAE,KAAK;aACb,MAAM,EAAE,KAAK;aACb,OAAO,EAAE,KAAK;aACd,QAAQ,EAAE,KAAK;aACf,aAAa,EAAE,OAAO;aACtB,IAAI,EAAE,MAAM;aACZ,WAAW,EAAE,KAAK;aAClB,WAAW,EAAE,KAAK;aAClB,KAAK,EAAE,KAAK;aACZ,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM;aACjB,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,KAAK;aACf,IAAI,EAAE,MAAM;aACZ,QAAQ,EAAE,KAAK;aACf,IAAI,EAAE,MAAM;aACZ,UAAU,EAAE,MAAM;aAClB,YAAY,EAAE,IAAI;aAClB,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,IAAI;aACf,cAAc,EAAE,KAAK;aACrB,OAAO,EAAE,IAAI;IACb,8EAA8E;aAC9E,eAAe,EAAE,KAAK;aACtB,iBAAiB,EAAE,IAAI;aACvB,QAAQ,EAAE,KAAK;aACf,SAAS,EAAE,KAAK;IAChB,6FAA6F;aAC7F,aAAa,EAAE,MAAM;IACrB;;;;;OAKG;aACH,cAAc,EAAE,MAAM;IACtB;;;OAGG;aACH,eAAe,EAAE,IAAI;IACrB;;;;OAIG;aACH,KAAK,EAAE,IAAI;CACH,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,UAAU,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,YAAY,GAAE,MAAM,MAAa,GAAG,SAAS,CAE5E"}
package/dist/lib/ids.js CHANGED
@@ -27,6 +27,8 @@ export const idPrefixes = {
27
27
  decision: 'dec',
28
28
  note: 'note',
29
29
  activity: 'act',
30
+ list: 'list',
31
+ listMember: 'lmem',
30
32
  handbookPage: 'hb',
31
33
  form: 'form',
32
34
  formField: 'ff',
@@ -1 +1 @@
1
- {"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/lib/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,SAAS,EAAE,IAAI;IACf,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,KAAK;IACd,kBAAkB,EAAE,KAAK;IACzB,wFAAwF;IACxF,sBAAsB,EAAE,KAAK;IAC7B,UAAU,EAAE,KAAK;IACjB,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,KAAK;IACf,aAAa,EAAE,OAAO;IACtB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,KAAK;IAClB,WAAW,EAAE,KAAK;IAClB,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,MAAM;IACjB,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,KAAK;IACf,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,KAAK;IACf,YAAY,EAAE,IAAI;IAClB,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,IAAI;IACb,8EAA8E;IAC9E,eAAe,EAAE,KAAK;IACtB,iBAAiB,EAAE,IAAI;IACvB,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,KAAK;IAChB,6FAA6F;IAC7F,aAAa,EAAE,MAAM;IACrB;;;;;OAKG;IACH,cAAc,EAAE,MAAM;IACtB;;;OAGG;IACH,eAAe,EAAE,IAAI;IACrB;;;;OAIG;IACH,KAAK,EAAE,IAAI;CACH,CAAA;AAMV;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,YAAY,GAAiB,IAAI;IAC/D,OAAO,CAAC,IAAgB,EAAU,EAAE,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,EAAE,CAAA;AAC9E,CAAC"}
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/lib/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,SAAS,EAAE,IAAI;IACf,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,KAAK;IACd,kBAAkB,EAAE,KAAK;IACzB,wFAAwF;IACxF,sBAAsB,EAAE,KAAK;IAC7B,UAAU,EAAE,KAAK;IACjB,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,KAAK;IACf,aAAa,EAAE,OAAO;IACtB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,KAAK;IAClB,WAAW,EAAE,KAAK;IAClB,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,MAAM;IACjB,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,KAAK;IACf,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,KAAK;IACf,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,MAAM;IAClB,YAAY,EAAE,IAAI;IAClB,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,IAAI;IACb,8EAA8E;IAC9E,eAAe,EAAE,KAAK;IACtB,iBAAiB,EAAE,IAAI;IACvB,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,KAAK;IAChB,6FAA6F;IAC7F,aAAa,EAAE,MAAM;IACrB;;;;;OAKG;IACH,cAAc,EAAE,MAAM;IACtB;;;OAGG;IACH,eAAe,EAAE,IAAI;IACrB;;;;OAIG;IACH,KAAK,EAAE,IAAI;CACH,CAAA;AAMV;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,YAAY,GAAiB,IAAI;IAC/D,OAAO,CAAC,IAAgB,EAAU,EAAE,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,EAAE,CAAA;AAC9E,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"attachedRecords.d.ts","sourceRoot":"","sources":["../../src/modules/attachedRecords.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAGnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAO1D;;;;;;;;;;GAUG;AAEH,uDAAuD;AACvD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAA;AAIpF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,oBAAoB,EAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oCAAoC,CACxD,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAajB"}
1
+ {"version":3,"file":"attachedRecords.d.ts","sourceRoot":"","sources":["../../src/modules/attachedRecords.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAGnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAQ1D;;;;;;;;;;GAUG;AAEH,uDAAuD;AACvD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAA;AAIpF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,oBAAoB,EAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oCAAoC,CACxD,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAajB"}
@@ -3,6 +3,7 @@ import { and, eq } from 'drizzle-orm';
3
3
  import * as activityRepository from './activities/repository.js';
4
4
  import * as decisionRepository from './decisions/repository.js';
5
5
  import { candidates } from './hiring/schema.js';
6
+ import * as listRepository from './lists/repository.js';
6
7
  import * as noteRepository from './notes/repository.js';
7
8
  import * as planRepository from './plans/repository.js';
8
9
  const PLAN_TARGET_TYPES = new Set(PIPELINE_KINDS);
@@ -12,7 +13,7 @@ const PLAN_TARGET_TYPES = new Set(PIPELINE_KINDS);
12
13
  * @returns How many rows were removed, for the caller's activity trail.
13
14
  */
14
15
  export async function deleteRecordsAttachedTo(db, workspaceId, targetType, targetId) {
15
- // Sequential, not concurrent: a transaction is one connection, and four
16
+ // Sequential, not concurrent: a transaction is one connection, and five
16
17
  // statements racing down it is not something to rely on for a cheap delete.
17
18
  const notes = await noteRepository.deleteForTarget(db, workspaceId, targetType, targetId);
18
19
  const activities = await activityRepository.deleteForTarget(db, workspaceId, targetType, targetId);
@@ -20,7 +21,8 @@ export async function deleteRecordsAttachedTo(db, workspaceId, targetType, targe
20
21
  const plans = PLAN_TARGET_TYPES.has(targetType)
21
22
  ? await planRepository.deleteForTarget(db, workspaceId, targetType, targetId)
22
23
  : 0;
23
- return notes + activities + decisions + plans;
24
+ const memberships = await listRepository.deleteMembershipsForTarget(db, workspaceId, targetType, targetId);
25
+ return notes + activities + decisions + plans + memberships;
24
26
  }
25
27
  /**
26
28
  * The same cleanup for the candidacies a person's delete takes with them.
@@ -1 +1 @@
1
- {"version":3,"file":"attachedRecords.js","sourceRoot":"","sources":["../../src/modules/attachedRecords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGrC,OAAO,KAAK,kBAAkB,MAAM,4BAA4B,CAAA;AAChE,OAAO,KAAK,kBAAkB,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AAiBvD,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;AAEtE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAa,EACb,WAAmB,EACnB,UAAgC,EAChC,QAAgB;IAEhB,wEAAwE;IACxE,4EAA4E;IAC5E,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IACzF,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IAClG,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IACjG,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC;QAC7C,CAAC,CAAC,MAAM,cAAc,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC;QAC7E,CAAC,CAAC,CAAC,CAAA;IAEL,OAAO,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,KAAK,CAAA;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,oCAAoC,CACxD,EAAa,EACb,WAAmB,EACnB,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;SAC7B,IAAI,CAAC,UAAU,CAAC;SAChB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IAEzF,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,OAAO,IAAI,MAAM,uBAAuB,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IACtF,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"attachedRecords.js","sourceRoot":"","sources":["../../src/modules/attachedRecords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGrC,OAAO,KAAK,kBAAkB,MAAM,4BAA4B,CAAA;AAChE,OAAO,KAAK,kBAAkB,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AAiBvD,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;AAEtE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAa,EACb,WAAmB,EACnB,UAAgC,EAChC,QAAgB;IAEhB,wEAAwE;IACxE,4EAA4E;IAC5E,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IACzF,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IAClG,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IACjG,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC;QAC7C,CAAC,CAAC,MAAM,cAAc,CAAC,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC;QAC7E,CAAC,CAAC,CAAC,CAAA;IACL,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,0BAA0B,CACjE,EAAE,EACF,WAAW,EACX,UAAU,EACV,QAAQ,CACT,CAAA;IAED,OAAO,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,WAAW,CAAA;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,oCAAoC,CACxD,EAAa,EACb,WAAmB,EACnB,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;SAC7B,IAAI,CAAC,UAAU,CAAC;SAChB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IAEzF,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,OAAO,IAAI,MAAM,uBAAuB,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IACtF,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ThemePreference } from '@kelpie/schemas';
1
+ import type { ListViewPreference, ThemePreference } from '@kelpie/schemas';
2
2
  /**
3
3
  * What an account's preferences are before anyone has saved any, and how a
4
4
  * partial change becomes the whole row.
@@ -15,11 +15,16 @@ export interface PreferenceValues {
15
15
  readonly emailDigest: boolean;
16
16
  readonly mentionEmails: boolean;
17
17
  readonly productUpdates: boolean;
18
+ readonly listViews: Readonly<Record<string, ListViewPreference>>;
18
19
  }
19
20
  /**
20
21
  * UTC, not the workspace's zone. An account is global and may belong to several
21
22
  * workspaces in different zones, so deriving from one of them would be picking
22
23
  * arbitrarily and calling it the person's answer.
24
+ *
25
+ * `listViews` starts empty. Each list page falls back to its own default when
26
+ * the map holds no entry for it, so the person sees the page's built-in shape
27
+ * until they change it.
23
28
  */
24
29
  export declare const DEFAULT_PREFERENCES: PreferenceValues;
25
30
  export type PreferenceChanges = Partial<PreferenceValues>;
@@ -29,6 +34,11 @@ export type PreferenceChanges = Partial<PreferenceValues>;
29
34
  * The result is always a complete row, which is what makes the write an upsert
30
35
  * with no read-modify-write hazard in the caller: the same request applied twice
31
36
  * produces the same row.
37
+ *
38
+ * `listViews` is treated as one field, so the caller replaces the whole map
39
+ * rather than merging by key. The UI already holds the current map in the
40
+ * account-preferences query, so a per-view change becomes a PATCH of the merged
41
+ * map — the merge lives where the choice was made.
32
42
  */
33
43
  export declare function applyPreferenceChanges(stored: PreferenceValues | undefined, changes: PreferenceChanges): PreferenceValues;
34
44
  //# sourceMappingURL=preferences.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preferences.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/preferences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD;;;;;;;;;GASG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,gBAMjC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAEzD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,OAAO,EAAE,iBAAiB,GACzB,gBAAgB,CAUlB"}
1
+ {"version":3,"file":"preferences.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/preferences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE1E;;;;;;;;;GASG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAA;CACjE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,gBAOjC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAEzD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,OAAO,EAAE,iBAAiB,GACzB,gBAAgB,CAWlB"}
@@ -2,6 +2,10 @@
2
2
  * UTC, not the workspace's zone. An account is global and may belong to several
3
3
  * workspaces in different zones, so deriving from one of them would be picking
4
4
  * arbitrarily and calling it the person's answer.
5
+ *
6
+ * `listViews` starts empty. Each list page falls back to its own default when
7
+ * the map holds no entry for it, so the person sees the page's built-in shape
8
+ * until they change it.
5
9
  */
6
10
  export const DEFAULT_PREFERENCES = {
7
11
  timezone: 'UTC',
@@ -9,6 +13,7 @@ export const DEFAULT_PREFERENCES = {
9
13
  emailDigest: true,
10
14
  mentionEmails: true,
11
15
  productUpdates: false,
16
+ listViews: {},
12
17
  };
13
18
  /**
14
19
  * Folds a `PATCH` body onto what is stored, or onto the defaults when nothing is.
@@ -16,6 +21,11 @@ export const DEFAULT_PREFERENCES = {
16
21
  * The result is always a complete row, which is what makes the write an upsert
17
22
  * with no read-modify-write hazard in the caller: the same request applied twice
18
23
  * produces the same row.
24
+ *
25
+ * `listViews` is treated as one field, so the caller replaces the whole map
26
+ * rather than merging by key. The UI already holds the current map in the
27
+ * account-preferences query, so a per-view change becomes a PATCH of the merged
28
+ * map — the merge lives where the choice was made.
19
29
  */
20
30
  export function applyPreferenceChanges(stored, changes) {
21
31
  const current = stored ?? DEFAULT_PREFERENCES;
@@ -25,6 +35,7 @@ export function applyPreferenceChanges(stored, changes) {
25
35
  emailDigest: changes.emailDigest ?? current.emailDigest,
26
36
  mentionEmails: changes.mentionEmails ?? current.mentionEmails,
27
37
  productUpdates: changes.productUpdates ?? current.productUpdates,
38
+ listViews: changes.listViews ?? current.listViews,
28
39
  };
29
40
  }
30
41
  //# sourceMappingURL=preferences.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preferences.js","sourceRoot":"","sources":["../../../src/modules/auth/preferences.ts"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqB;IACnD,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,KAAK;CACtB,CAAA;AAID;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAoC,EACpC,OAA0B;IAE1B,MAAM,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAAA;IAE7C,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ;QAC9C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QACrC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW;QACvD,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,aAAa;QAC7D,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,cAAc;KACjE,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"preferences.js","sourceRoot":"","sources":["../../../src/modules/auth/preferences.ts"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqB;IACnD,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,KAAK;IACrB,SAAS,EAAE,EAAE;CACd,CAAA;AAID;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAoC,EACpC,OAA0B;IAE1B,MAAM,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAAA;IAE7C,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ;QAC9C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QACrC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW;QACvD,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,aAAa;QAC7D,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,cAAc;QAChE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS;KAClD,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAA;AAQzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAE9D,OAAO,KAAK,EAAe,WAAW,EAA8B,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AA0DxD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;CACtC;AA2DD,wBAAgB,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,sBAAsB,GAAG,IAAI,CA6IxF"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/routes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAA;AAQzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAE9D,OAAO,KAAK,EAAe,WAAW,EAA8B,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AA6ExD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;CACtC;AA4DD,wBAAgB,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,sBAAsB,GAAG,IAAI,CA8IxF"}
@@ -43,6 +43,21 @@ const updateAccountBody = z
43
43
  message: 'Current password is required to change email',
44
44
  path: ['current_password'],
45
45
  });
46
+ const listViewBody = z
47
+ .strictObject({
48
+ columns: z.array(z.string()).optional(),
49
+ mode: z.enum(['list', 'columns']).optional(),
50
+ grouping: z.string().optional(),
51
+ scope: z.string().optional(),
52
+ sort: z.string().optional(),
53
+ })
54
+ .transform((wire) => ({
55
+ ...(wire.columns === undefined ? {} : { columns: wire.columns }),
56
+ ...(wire.mode === undefined ? {} : { mode: wire.mode }),
57
+ ...(wire.grouping === undefined ? {} : { grouping: wire.grouping }),
58
+ ...(wire.scope === undefined ? {} : { scope: wire.scope }),
59
+ ...(wire.sort === undefined ? {} : { sort: wire.sort }),
60
+ }));
46
61
  const updatePreferencesBody = z
47
62
  .strictObject({
48
63
  timezone: timezoneSchema,
@@ -50,6 +65,7 @@ const updatePreferencesBody = z
50
65
  email_digest: z.boolean(),
51
66
  mention_emails: z.boolean(),
52
67
  product_updates: z.boolean(),
68
+ list_views: z.record(z.string(), listViewBody),
53
69
  })
54
70
  .partial();
55
71
  /** Parses a body into `T` or throws the `422` `api.md` describes. */
@@ -89,6 +105,7 @@ function preferencesResponse(preferences) {
89
105
  email_digest: preferences.emailDigest,
90
106
  mention_emails: preferences.mentionEmails,
91
107
  product_updates: preferences.productUpdates,
108
+ list_views: preferences.listViews,
92
109
  };
93
110
  }
94
111
  function sessionResponse(session) {
@@ -160,6 +177,7 @@ export function mountAuthRoutes(router, dependencies) {
160
177
  ...(body.email_digest === undefined ? {} : { emailDigest: body.email_digest }),
161
178
  ...(body.mention_emails === undefined ? {} : { mentionEmails: body.mention_emails }),
162
179
  ...(body.product_updates === undefined ? {} : { productUpdates: body.product_updates }),
180
+ ...(body.list_views === undefined ? {} : { listViews: body.list_views }),
163
181
  });
164
182
  return context.json(preferencesResponse(preferences));
165
183
  });