@cosmicdrift/kumiko-bundled-features 0.352.0 → 0.354.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 (259) hide show
  1. package/dist/agent-tools/agent-doc-lint.d.ts +2 -0
  2. package/dist/agent-tools/agent-doc-lint.js +61 -1
  3. package/dist/agent-tools/agent-manifest.d.ts +1 -0
  4. package/dist/agent-tools/agent-manifest.js +17 -5
  5. package/dist/agent-tools/feature.js +7 -2
  6. package/dist/agent-tools/index.d.ts +1 -1
  7. package/dist/agent-tools/index.js +1 -1
  8. package/dist/agent-tools/manifest-deny-qns.d.ts +3 -0
  9. package/dist/agent-tools/manifest-deny-qns.js +17 -0
  10. package/dist/agent-tools/tool-catalog.d.ts +2 -1
  11. package/dist/agent-tools/tool-catalog.js +4 -1
  12. package/dist/agent-tools/tool-dispatch.js +1 -1
  13. package/dist/audit/handlers/list.query.js +13 -8
  14. package/dist/auth-email-password/handlers/change-password.write.js +1 -0
  15. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +2 -2
  16. package/dist/auth-email-password/handlers/invite-accept.write.js +2 -2
  17. package/dist/auth-email-password/handlers/invite-create.write.js +17 -0
  18. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +4 -1
  19. package/dist/auth-email-password/handlers/login.write.js +1 -0
  20. package/dist/auth-email-password/handlers/reset-password.write.js +1 -0
  21. package/dist/auth-email-password/handlers/signup-confirm.write.js +6 -4
  22. package/dist/auth-email-password/handlers/signup-request.write.js +51 -77
  23. package/dist/auth-email-password/handlers/token-request-handler.js +1 -0
  24. package/dist/auth-email-password/handlers/verify-email.write.js +1 -0
  25. package/dist/auth-email-password/index.d.ts +2 -0
  26. package/dist/auth-email-password/index.js +2 -0
  27. package/dist/auth-email-password/seeding.d.ts +2 -0
  28. package/dist/auth-email-password/seeding.js +30 -8
  29. package/dist/auth-email-password/signup-activation.d.ts +24 -0
  30. package/dist/auth-email-password/signup-activation.js +41 -0
  31. package/dist/auth-email-password/signup-token-store.d.ts +5 -0
  32. package/dist/auth-email-password/signup-token-store.js +8 -3
  33. package/dist/auth-email-password/web/auth-gate.d.ts +2 -2
  34. package/dist/auth-email-password/web/auth-gate.js +5 -5
  35. package/dist/auth-email-password/web/auth-redirect.d.ts +1 -0
  36. package/dist/auth-email-password/web/auth-redirect.js +14 -0
  37. package/dist/auth-email-password/web/login-screen.js +2 -2
  38. package/dist/auth-email-password/web/user-menu.js +1 -1
  39. package/dist/auth-mfa/feature.js +5 -0
  40. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -0
  41. package/dist/auth-mfa/handlers/verify.write.js +1 -0
  42. package/dist/auth-mfa/schema/user-mfa.d.ts +6 -0
  43. package/dist/auth-mfa/screens.js +2 -2
  44. package/dist/auth-mfa/web/mfa-recovery-codes-reveal.js +1 -1
  45. package/dist/auth-mfa/web/mfa-setup-preauth-screen.d.ts +1 -1
  46. package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +3 -3
  47. package/dist/auth-mfa/web/mfa-verify-screen.d.ts +1 -1
  48. package/dist/auth-mfa/web/mfa-verify-screen.js +1 -1
  49. package/dist/billing-foundation/billing-info-query.d.ts +5 -2
  50. package/dist/billing-foundation/checkout-core.d.ts +7 -0
  51. package/dist/billing-foundation/checkout-core.js +7 -2
  52. package/dist/billing-foundation/entities.d.ts +8 -0
  53. package/dist/billing-foundation/handlers/start-plan-checkout.write.js +7 -1
  54. package/dist/billing-foundation/projection.d.ts +8 -0
  55. package/dist/cap-counter/entity.d.ts +1 -0
  56. package/dist/cap-overview/access.js +2 -9
  57. package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -2
  58. package/dist/cap-overview/handlers/tenant-options.query.js +4 -5
  59. package/dist/channel-in-app/in-app-channel.js +1 -1
  60. package/dist/channel-in-app/tables.js +4 -2
  61. package/dist/compliance-profiles/schema/profile-selection.d.ts +10 -8
  62. package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +2 -4
  63. package/dist/config/handlers/values.query.d.ts +3 -1
  64. package/dist/config/handlers/values.query.js +7 -2
  65. package/dist/config/table.d.ts +6 -0
  66. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -0
  67. package/dist/crypto-shredding/handlers/forget-subject.write.js +110 -61
  68. package/dist/custom-fields/entity.d.ts +4 -0
  69. package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -0
  70. package/dist/delivery/db/queries/address-opt-outs.d.ts +3 -1
  71. package/dist/delivery/db/queries/address-opt-outs.js +12 -7
  72. package/dist/delivery/delivery-service.js +60 -12
  73. package/dist/delivery/public-names.js +1 -1
  74. package/dist/delivery/tables.d.ts +6 -0
  75. package/dist/delivery/unsubscribe.js +66 -14
  76. package/dist/document-ingest-foundation/entity.d.ts +4 -0
  77. package/dist/feature-toggles/feature.js +2 -3
  78. package/dist/feature-toggles/toggle-runtime.d.ts +5 -1
  79. package/dist/feature-toggles/toggle-runtime.js +30 -6
  80. package/dist/feature-toggles/toggle-sync-signal.d.ts +3 -2
  81. package/dist/feature-toggles/toggle-sync-signal.js +29 -10
  82. package/dist/file-derivatives/handlers/public-variant.query.d.ts +1 -0
  83. package/dist/file-derivatives/handlers/public-variant.query.js +7 -1
  84. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +5 -0
  85. package/dist/files-tenant-data/hooks.js +61 -24
  86. package/dist/folders/entity.d.ts +8 -0
  87. package/dist/folders/feature.js +2 -10
  88. package/dist/folders/handlers/delete-folder.write.js +31 -4
  89. package/dist/form-draft/db/queries/cleanup.d.ts +1 -1
  90. package/dist/form-draft/db/queries/cleanup.js +4 -3
  91. package/dist/form-draft/entity.d.ts +2 -0
  92. package/dist/form-draft/handlers/cleanup.job.d.ts +5 -1
  93. package/dist/form-draft/handlers/cleanup.job.js +11 -5
  94. package/dist/foundation-shared/index.d.ts +1 -1
  95. package/dist/foundation-shared/index.js +1 -1
  96. package/dist/foundation-shared/mail-host-policy.d.ts +4 -0
  97. package/dist/foundation-shared/mail-host-policy.js +13 -4
  98. package/dist/inbound-mail-foundation/entities.d.ts +25 -0
  99. package/dist/inbound-mail-foundation/projection.d.ts +20 -0
  100. package/dist/inbound-provider-imap/feature.js +2 -0
  101. package/dist/inbound-provider-imap/imap-client.js +3 -4
  102. package/dist/jobs/constants.d.ts +1 -0
  103. package/dist/jobs/constants.js +1 -0
  104. package/dist/jobs/db/queries/retention.d.ts +1 -0
  105. package/dist/jobs/db/queries/retention.js +5 -0
  106. package/dist/jobs/db/queries/stale-run-sweep.d.ts +2 -0
  107. package/dist/jobs/db/queries/stale-run-sweep.js +13 -2
  108. package/dist/jobs/feature.js +7 -1
  109. package/dist/jobs/handlers/list.query.d.ts +2 -1
  110. package/dist/jobs/handlers/list.query.js +40 -15
  111. package/dist/jobs/handlers/tenant-failures.query.js +1 -1
  112. package/dist/jobs/handlers/tenant-runs.query.d.ts +17 -0
  113. package/dist/jobs/handlers/tenant-runs.query.js +72 -0
  114. package/dist/jobs/i18n.js +1 -0
  115. package/dist/jobs/index.d.ts +2 -0
  116. package/dist/jobs/index.js +1 -0
  117. package/dist/jobs/job-run-logger.d.ts +2 -2
  118. package/dist/jobs/job-run-logger.js +98 -9
  119. package/dist/jobs/job-run-table.d.ts +6 -0
  120. package/dist/jobs/tenant-job-failure-table.js +15 -2
  121. package/dist/jobs/tenant-job-run-table.d.ts +4 -0
  122. package/dist/jobs/tenant-job-run-table.js +34 -0
  123. package/dist/ledger/entity.d.ts +24 -8
  124. package/dist/ledger/entity.js +17 -5
  125. package/dist/ledger/feature.js +3 -0
  126. package/dist/ledger/handlers/confirm-schedule-period.write.js +9 -3
  127. package/dist/ledger/handlers/create-transaction.write.js +1 -0
  128. package/dist/ledger/handlers/reverse-transaction.write.js +1 -0
  129. package/dist/ledger/schedule-subject-pair.d.ts +2 -0
  130. package/dist/ledger/schedule-subject-pair.js +24 -0
  131. package/dist/ledger/schemas.js +8 -2
  132. package/dist/mail-transport-smtp/feature.d.ts +3 -4
  133. package/dist/mail-transport-smtp/feature.js +4 -13
  134. package/dist/managed-pages/feature.js +28 -10
  135. package/dist/managed-pages/table.d.ts +12 -0
  136. package/dist/notes-history/entity.d.ts +12 -0
  137. package/dist/notes-history/entity.js +4 -0
  138. package/dist/notes-history/feature.js +2 -10
  139. package/dist/personal-access-tokens/handlers/create.write.js +1 -0
  140. package/dist/personal-access-tokens/schema/api-token.d.ts +6 -0
  141. package/dist/personal-access-tokens/screens.js +14 -3
  142. package/dist/secrets/table.d.ts +1 -0
  143. package/dist/seo/feature.js +28 -6
  144. package/dist/sessions/feature.d.ts +1 -0
  145. package/dist/sessions/feature.js +9 -8
  146. package/dist/sessions/handlers/detail.query.d.ts +1 -1
  147. package/dist/sessions/handlers/detail.query.js +2 -2
  148. package/dist/sessions/handlers/list.query.d.ts +1 -1
  149. package/dist/sessions/handlers/list.query.js +5 -3
  150. package/dist/sessions/schema/user-session.d.ts +4 -0
  151. package/dist/sessions/screens.d.ts +2 -2
  152. package/dist/sessions/screens.js +6 -7
  153. package/dist/shared/assert-no-where-rule-in-ownership-write.d.ts +2 -0
  154. package/dist/shared/assert-no-where-rule-in-ownership-write.js +18 -0
  155. package/dist/shared/encrypt-for-direct-write.js +10 -2
  156. package/dist/shared/index.d.ts +4 -3
  157. package/dist/shared/index.js +4 -3
  158. package/dist/shared/parent-visibility.d.ts +2 -1
  159. package/dist/shared/parent-visibility.js +21 -0
  160. package/dist/shared/rate-limited-text-response.d.ts +2 -0
  161. package/dist/shared/rate-limited-text-response.js +9 -0
  162. package/dist/shared/row-bound-grant.js +16 -2
  163. package/dist/shared/session-locale-field.d.ts +1 -0
  164. package/dist/shared/session-locale-field.js +6 -0
  165. package/dist/tags/entity.d.ts +9 -0
  166. package/dist/tags/feature.js +2 -10
  167. package/dist/template-resolver/api.d.ts +2 -2
  168. package/dist/template-resolver/handlers/list.query.d.ts +2 -0
  169. package/dist/template-resolver/handlers/list.query.js +2 -2
  170. package/dist/template-resolver/table.d.ts +48 -30
  171. package/dist/template-resolver/user-content-table.d.ts +34 -20
  172. package/dist/tenant/decrypt-tenant-names.d.ts +3 -0
  173. package/dist/tenant/decrypt-tenant-names.js +10 -0
  174. package/dist/tenant/feature.js +7 -1
  175. package/dist/tenant/handlers/me.query.js +5 -1
  176. package/dist/tenant/handlers/memberships.query.js +3 -2
  177. package/dist/tenant/handlers/tenant-directory.query.js +9 -3
  178. package/dist/tenant/handlers/toggle-enabled.write.js +2 -0
  179. package/dist/tenant/handlers/update-member-roles.write.js +1 -0
  180. package/dist/tenant/index.d.ts +1 -0
  181. package/dist/tenant/index.js +1 -0
  182. package/dist/tenant/invitation-table.d.ts +54 -8
  183. package/dist/tenant/invitation-table.js +6 -1
  184. package/dist/tenant/invited-membership.d.ts +8 -6
  185. package/dist/tenant/invited-membership.js +28 -7
  186. package/dist/tenant/membership-table.d.ts +4 -0
  187. package/dist/tenant/schema/tenant.d.ts +18 -10
  188. package/dist/tenant/schema/tenant.js +4 -6
  189. package/dist/tenant/screens.js +1 -1
  190. package/dist/tenant-handover/move-entity-graph.js +11 -18
  191. package/dist/tenant-handover/transfer-graph.d.ts +2 -21
  192. package/dist/tenant-handover/transfer-graph.js +8 -75
  193. package/dist/tenant-lifecycle/boot-checks.d.ts +1 -0
  194. package/dist/tenant-lifecycle/boot-checks.js +13 -0
  195. package/dist/tenant-lifecycle/constants.d.ts +3 -0
  196. package/dist/tenant-lifecycle/constants.js +5 -0
  197. package/dist/tenant-lifecycle/events.d.ts +5 -0
  198. package/dist/tenant-lifecycle/events.js +5 -0
  199. package/dist/tenant-lifecycle/feature.js +6 -3
  200. package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +10 -5
  201. package/dist/tenant-lifecycle/run-tenant-destroy.js +59 -17
  202. package/dist/tenant-lifecycle/stages.d.ts +8 -1
  203. package/dist/tenant-lifecycle/stages.js +33 -4
  204. package/dist/tenant-lifecycle/testing.d.ts +8 -0
  205. package/dist/tenant-lifecycle/testing.js +42 -0
  206. package/dist/tier-engine/entity.d.ts +2 -0
  207. package/dist/tier-engine/handlers/set-tenant-tier.write.js +1 -0
  208. package/dist/tier-engine/tier-resolver.d.ts +2 -0
  209. package/dist/user/handlers/detail.query.js +2 -0
  210. package/dist/user/handlers/list.query.js +6 -2
  211. package/dist/user/handlers/update.write.js +1 -0
  212. package/dist/user/schema/user.d.ts +22 -8
  213. package/dist/user/seeding.d.ts +1 -0
  214. package/dist/user/seeding.js +20 -0
  215. package/dist/user-data-rights/boot-checks.js +5 -0
  216. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +2 -2
  217. package/dist/user-data-rights/handlers/request-deletion.write.js +1 -0
  218. package/dist/user-data-rights/index.d.ts +1 -0
  219. package/dist/user-data-rights/index.js +1 -0
  220. package/dist/user-data-rights/schema/download-attempt.d.ts +14 -0
  221. package/dist/user-data-rights/schema/download-attempt.js +17 -9
  222. package/dist/user-data-rights/schema/download-token.d.ts +8 -0
  223. package/dist/user-data-rights/schema/export-job.d.ts +16 -8
  224. package/dist/user-profile/handlers/change-email.write.js +8 -2
  225. package/dist/waitlist/entity.d.ts +22 -6
  226. package/dist/workflow-runner/event-trigger.d.ts +1 -0
  227. package/dist/workflow-runner/event-trigger.js +63 -15
  228. package/dist/workflow-runner/handlers/resume-run.write.js +8 -2
  229. package/dist/workflow-runner/runner.d.ts +2 -1
  230. package/dist/workflow-runner/runner.js +5 -1
  231. package/package.json +35 -10
  232. package/src/agent-tools/changes.json +12 -0
  233. package/src/audit/changes.json +6 -0
  234. package/src/auth-email-password/changes.json +24 -0
  235. package/src/auth-mfa/changes.json +7 -0
  236. package/src/billing-foundation/changes.json +6 -0
  237. package/src/channel-in-app/changes.json +8 -1
  238. package/src/crypto-shredding/changes.json +12 -0
  239. package/src/delivery/changes.json +6 -0
  240. package/src/files/changes.json +24 -0
  241. package/src/files-tenant-data/changes.json +5 -0
  242. package/src/folders/changes.json +6 -0
  243. package/src/form-draft/changes.json +7 -1
  244. package/src/inbound-provider-imap/changes.json +6 -0
  245. package/src/jobs/changes.json +30 -0
  246. package/src/ledger/changes.json +23 -0
  247. package/src/managed-pages/changes.json +6 -0
  248. package/src/notes-history/changes.json +13 -0
  249. package/src/seo/changes.json +5 -0
  250. package/src/template-resolver/changes.json +5 -0
  251. package/src/tenant/changes.json +19 -0
  252. package/src/tenant-handover/changes.json +12 -0
  253. package/src/tenant-lifecycle/changes.json +22 -0
  254. package/src/tier-engine/changes.json +5 -0
  255. package/src/user/changes.json +15 -0
  256. package/src/user-data-rights/changes.json +6 -0
  257. package/src/workflow-runner/changes.json +5 -0
  258. package/dist/shared/has-where-rule.d.ts +0 -2
  259. package/dist/shared/has-where-rule.js +0 -10
@@ -4,6 +4,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
4
4
  readonly type: "text";
5
5
  readonly description?: string;
6
6
  readonly maxLength?: number;
7
+ readonly minLength?: number;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -25,6 +26,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
25
26
  readonly type: "text";
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
29
+ readonly minLength?: number;
28
30
  readonly required?: boolean;
29
31
  readonly searchable?: boolean;
30
32
  readonly sortable?: boolean;
@@ -46,6 +48,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
46
48
  readonly type: "text";
47
49
  readonly description?: string;
48
50
  readonly maxLength?: number;
51
+ readonly minLength?: number;
49
52
  readonly required?: boolean;
50
53
  readonly searchable?: boolean;
51
54
  readonly sortable?: boolean;
@@ -67,6 +70,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
67
70
  readonly type: "text";
68
71
  readonly description?: string;
69
72
  readonly maxLength?: number;
73
+ readonly minLength?: number;
70
74
  readonly required?: boolean;
71
75
  readonly searchable?: boolean;
72
76
  readonly sortable?: boolean;
@@ -104,6 +108,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
104
108
  readonly type: "text";
105
109
  readonly description?: string;
106
110
  readonly maxLength?: number;
111
+ readonly minLength?: number;
107
112
  readonly required?: boolean;
108
113
  readonly searchable?: boolean;
109
114
  readonly sortable?: boolean;
@@ -133,13 +138,14 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
133
138
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
134
139
  readonly display?: "radio" | "dropdown";
135
140
  readonly optionTones?: {
136
- readonly invited?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
137
- readonly pending?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
138
- readonly rejected?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
141
+ readonly invited?: "bad" | "neutral" | "ok" | "warn" | undefined;
142
+ readonly pending?: "bad" | "neutral" | "ok" | "warn" | undefined;
143
+ readonly rejected?: "bad" | "neutral" | "ok" | "warn" | undefined;
139
144
  } | undefined;
140
145
  readonly optionsQuery?: string;
141
146
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
142
147
  readonly optionsAvailabilityQuery?: string;
148
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"invited" | "pending" | "rejected">[] | undefined;
143
149
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
144
150
  required: true;
145
151
  };
@@ -177,6 +183,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
177
183
  readonly type: "text";
178
184
  readonly description?: string;
179
185
  readonly maxLength?: number;
186
+ readonly minLength?: number;
180
187
  readonly required?: boolean;
181
188
  readonly searchable?: boolean;
182
189
  readonly sortable?: boolean;
@@ -198,6 +205,7 @@ export declare const waitlistEntryEntity: import("@cosmicdrift/kumiko-types/fiel
198
205
  readonly type: "text";
199
206
  readonly description?: string;
200
207
  readonly maxLength?: number;
208
+ readonly minLength?: number;
201
209
  readonly required?: boolean;
202
210
  readonly searchable?: boolean;
203
211
  readonly sortable?: boolean;
@@ -221,6 +229,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
221
229
  readonly type: "text";
222
230
  readonly description?: string;
223
231
  readonly maxLength?: number;
232
+ readonly minLength?: number;
224
233
  readonly required?: boolean;
225
234
  readonly searchable?: boolean;
226
235
  readonly sortable?: boolean;
@@ -242,6 +251,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
242
251
  readonly type: "text";
243
252
  readonly description?: string;
244
253
  readonly maxLength?: number;
254
+ readonly minLength?: number;
245
255
  readonly required?: boolean;
246
256
  readonly searchable?: boolean;
247
257
  readonly sortable?: boolean;
@@ -263,6 +273,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
263
273
  readonly type: "text";
264
274
  readonly description?: string;
265
275
  readonly maxLength?: number;
276
+ readonly minLength?: number;
266
277
  readonly required?: boolean;
267
278
  readonly searchable?: boolean;
268
279
  readonly sortable?: boolean;
@@ -284,6 +295,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
284
295
  readonly type: "text";
285
296
  readonly description?: string;
286
297
  readonly maxLength?: number;
298
+ readonly minLength?: number;
287
299
  readonly required?: boolean;
288
300
  readonly searchable?: boolean;
289
301
  readonly sortable?: boolean;
@@ -321,6 +333,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
321
333
  readonly type: "text";
322
334
  readonly description?: string;
323
335
  readonly maxLength?: number;
336
+ readonly minLength?: number;
324
337
  readonly required?: boolean;
325
338
  readonly searchable?: boolean;
326
339
  readonly sortable?: boolean;
@@ -350,13 +363,14 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
350
363
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
351
364
  readonly display?: "radio" | "dropdown";
352
365
  readonly optionTones?: {
353
- readonly invited?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
354
- readonly pending?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
355
- readonly rejected?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
366
+ readonly invited?: "bad" | "neutral" | "ok" | "warn" | undefined;
367
+ readonly pending?: "bad" | "neutral" | "ok" | "warn" | undefined;
368
+ readonly rejected?: "bad" | "neutral" | "ok" | "warn" | undefined;
356
369
  } | undefined;
357
370
  readonly optionsQuery?: string;
358
371
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
359
372
  readonly optionsAvailabilityQuery?: string;
373
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"invited" | "pending" | "rejected">[] | undefined;
360
374
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
361
375
  required: true;
362
376
  };
@@ -394,6 +408,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
394
408
  readonly type: "text";
395
409
  readonly description?: string;
396
410
  readonly maxLength?: number;
411
+ readonly minLength?: number;
397
412
  readonly required?: boolean;
398
413
  readonly searchable?: boolean;
399
414
  readonly sortable?: boolean;
@@ -415,6 +430,7 @@ export declare const waitlistEntryTable: import("@cosmicdrift/kumiko-framework/d
415
430
  readonly type: "text";
416
431
  readonly description?: string;
417
432
  readonly maxLength?: number;
433
+ readonly minLength?: number;
418
434
  readonly required?: boolean;
419
435
  readonly searchable?: boolean;
420
436
  readonly sortable?: boolean;
@@ -1,2 +1,3 @@
1
1
  import type { FeatureRegistrar, WorkflowDefinition } from "@cosmicdrift/kumiko-framework/engine";
2
+ export declare function assertEventTriggerStepsSupported(workflow: WorkflowDefinition): void;
2
3
  export declare function registerEventTrigger(r: FeatureRegistrar, workflow: WorkflowDefinition): void;
@@ -8,13 +8,62 @@
8
8
  // wiring uses the unbound pool), so `workflow.run-started` and the rest of
9
9
  // the pipeline are not atomic. A throw from startAndRunWorkflow is recorded
10
10
  // as `workflow.run-failed` and swallowed, so the dispatcher moves on.
11
- import { describeWorkflowStepError, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RUN_FAILED_TYPE, } from "@cosmicdrift/kumiko-framework/engine";
11
+ import { buildPipelineSteps, createSystemUser, describeWorkflowStepError, getStep, SYSTEM_TENANT_ID, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RUN_FAILED_TYPE, WorkflowStepError, } from "@cosmicdrift/kumiko-framework/engine";
12
12
  import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
13
13
  import { workflowRunAggregateId } from "./aggregate-id.js";
14
14
  import { registerEventWakeup } from "./event-subscriber.js";
15
- import { startAndRunWorkflow } from "./runner.js";
15
+ import { startAndRunWorkflow, WorkflowSuspensionUnsupportedError, } from "./runner.js";
16
16
  import { registerWorkflow } from "./workflow-registry.js";
17
17
  const log = createFallbackLogger("workflow-runner");
18
+ // The MSP apply context only offers unsafeAppendEvent/loadAggregate; these steps reach for
19
+ // ctx.db/write/writeAs and would crash on the first run instead of at registration.
20
+ const STEP_KINDS_NEEDING_HANDLER_CONTEXT = new Set([
21
+ "aggregate.create",
22
+ "aggregate.update",
23
+ "callFeature",
24
+ "read.findOne",
25
+ "read.findMany",
26
+ "unsafeProjectionUpsert",
27
+ "unsafeProjectionDelete",
28
+ ]);
29
+ function* walkStepInstances(steps) {
30
+ for (const step of steps) {
31
+ yield step;
32
+ const { args } = step;
33
+ if (typeof args !== "object" || args === null)
34
+ continue;
35
+ for (const path of getStep(step.kind)?.subPaths ?? []) {
36
+ const nested = path in args ? Reflect.get(args, path) : undefined;
37
+ if (Array.isArray(nested))
38
+ yield* walkStepInstances(nested);
39
+ }
40
+ }
41
+ }
42
+ export function assertEventTriggerStepsSupported(workflow) {
43
+ let steps;
44
+ try {
45
+ // Same contract as the boot validator for handler pipelines: the closure must build its
46
+ // step list from the step builder alone, so an empty event is enough to enumerate kinds.
47
+ steps = buildPipelineSteps(workflow.pipelineDef, {
48
+ type: "workflow-registration-probe",
49
+ payload: {},
50
+ user: createSystemUser(SYSTEM_TENANT_ID),
51
+ });
52
+ }
53
+ catch (error) {
54
+ throw new Error(`Workflow "${workflow.name}" pipeline closure threw at registration: ${String(error)}. ` +
55
+ "Build the step list without reading event payload fields; read them inside step resolvers.", { cause: error });
56
+ }
57
+ for (const step of walkStepInstances(steps)) {
58
+ if (getStep(step.kind) === undefined) {
59
+ throw new Error(`Workflow "${workflow.name}" uses unknown step kind "${step.kind}"`);
60
+ }
61
+ if (STEP_KINDS_NEEDING_HANDLER_CONTEXT.has(step.kind)) {
62
+ throw new Error(`Workflow "${workflow.name}" is event-triggered but uses step "${step.kind}", which needs a ` +
63
+ "handler context (db/write) the event-trigger projection does not provide.");
64
+ }
65
+ }
66
+ }
18
67
  export function registerEventTrigger(r, workflow) {
19
68
  // Populate the workflow-registry unconditionally, before the event-trigger
20
69
  // guard below — resume-run (framework#2513 Phase 2) looks workflows up by
@@ -28,6 +77,7 @@ export function registerEventTrigger(r, workflow) {
28
77
  // need a scheduler, not an MSP, so there is nothing to register here.
29
78
  if (workflow.trigger.kind !== "event")
30
79
  return;
80
+ assertEventTriggerStepsSupported(workflow);
31
81
  const eventType = workflow.trigger.eventType;
32
82
  r.multiStreamProjection({
33
83
  name: `workflow-${workflow.name}`,
@@ -41,19 +91,16 @@ export function registerEventTrigger(r, workflow) {
41
91
  // `trigger.filter` below typed without a cast.
42
92
  if (workflow.trigger.kind !== "event")
43
93
  return;
44
- if (workflow.trigger.filter) {
45
- const matches = workflow.trigger.filter(event);
46
- // skip: the workflow's own trigger filter rejected this event — not
47
- // this run's concern, so no run is started and nothing is recorded.
48
- if (!matches)
49
- return;
50
- }
51
94
  // @cast-boundary msp-to-write-event — the MSP delivers a StoredEvent
52
95
  // (event-store shape); the workflow runner expects a WriteEvent
53
96
  // (handler shape). The fields workflow steps read (type, payload)
54
97
  // overlap exactly — the missing `.user` field is acceptable because
55
98
  // workflow triggers run system-level, not user-scoped.
56
99
  const triggerEvent = event;
100
+ // skip: the workflow's own trigger filter rejected this event — not this run's
101
+ // concern, so no run is started and nothing is recorded.
102
+ if (workflow.trigger.filter && !workflow.trigger.filter(triggerEvent))
103
+ return;
57
104
  let idempotencyKey;
58
105
  if (typeof workflow.idempotencyKey === "function") {
59
106
  idempotencyKey = workflow.idempotencyKey(triggerEvent);
@@ -75,23 +122,24 @@ export function registerEventTrigger(r, workflow) {
75
122
  version: event.version,
76
123
  },
77
124
  ...(idempotencyKey && { idempotencyKey }),
78
- // @cast-boundary msp-to-handler-ctx — MultiStreamApplyContext only
79
- // exposes unsafeAppendEvent/loadAggregate, a subset of
80
- // HandlerContext. The step vocabulary this runner drives (wait,
81
- // compute, return) only ever calls unsafeAppendEvent on it.
82
125
  handlerCtx: ctx,
83
126
  });
84
127
  }
85
128
  catch (error) {
129
+ // Failures outside the step loop (run-started append) have no step yet, so 0.
130
+ const stepIndex = error instanceof WorkflowStepError ||
131
+ error instanceof WorkflowSuspensionUnsupportedError
132
+ ? error.stepIndex
133
+ : 0;
86
134
  log.warn("workflow run failed", {
87
135
  runId,
88
136
  workflowName: workflow.name,
89
- stepIndex: 0,
137
+ stepIndex,
90
138
  error: String(error),
91
139
  });
92
140
  const failedPayload = {
93
141
  workflowName: workflow.name,
94
- stepIndex: 0,
142
+ stepIndex,
95
143
  error: describeWorkflowStepError(error),
96
144
  };
97
145
  await ctx.unsafeAppendEvent({
@@ -30,7 +30,7 @@
30
30
  // resultKey on steps/wait-for-event.ts) — pre-seeded into stepsAcc below so
31
31
  // a subsequent step's resolver can read `ctx.steps[awaits.someKey]`.
32
32
  import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
33
- import { buildPipelineSteps, computeDefinitionFingerprint, describeWorkflowStepError, getStep, runStepList, SYSTEM_ROLE, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RESUMED_TYPE, WORKFLOW_RETRY_SCHEDULED_TYPE, WORKFLOW_RUN_COMPLETED_TYPE, WORKFLOW_RUN_FAILED_TYPE, WORKFLOW_RUN_STARTED_TYPE, WORKFLOW_WAITING_FOR_EVENT_TYPE, } from "@cosmicdrift/kumiko-framework/engine";
33
+ import { buildPipelineSteps, computeDefinitionFingerprint, describeWorkflowStepError, getStep, runStepList, SYSTEM_ROLE, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RESUMED_TYPE, WORKFLOW_RETRY_SCHEDULED_TYPE, WORKFLOW_RUN_COMPLETED_TYPE, WORKFLOW_RUN_FAILED_TYPE, WORKFLOW_RUN_STARTED_TYPE, WORKFLOW_WAITING_FOR_EVENT_TYPE, WorkflowStepError, } from "@cosmicdrift/kumiko-framework/engine";
34
34
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
35
35
  import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
36
36
  import * as z from "zod";
@@ -52,6 +52,9 @@ async function appendRunFailed(ctx, runId, failedPayload) {
52
52
  }
53
53
  function checkQ7Fingerprint(workflow, workflowName, runId, stepIndex, storedFingerprint) {
54
54
  const currentFingerprint = computeDefinitionFingerprint(workflow);
55
+ if (storedFingerprint === null) {
56
+ log.warn(`Run ${runId} of workflow "${workflowName}" has no stored definition fingerprint — resuming against the current definition unchecked.`);
57
+ }
55
58
  const fingerprintChanged = storedFingerprint !== null && currentFingerprint !== storedFingerprint;
56
59
  if (!fingerprintChanged) {
57
60
  return null;
@@ -235,9 +238,12 @@ async function runResumedPipeline(ctx, input) {
235
238
  }
236
239
  catch (error) {
237
240
  log.warn("workflow run failed", { runId, workflowName, stepIndex, error: String(error) });
241
+ // The input stepIndex is the suspended step; the failure happened at the step the resumed pass reached.
238
242
  const failedPayload = {
239
243
  workflowName,
240
- stepIndex,
244
+ stepIndex: error instanceof WorkflowStepError || error instanceof WorkflowSuspensionUnsupportedError
245
+ ? error.stepIndex
246
+ : stepIndex,
241
247
  error: describeWorkflowStepError(error),
242
248
  };
243
249
  await appendRunFailed(ctx, runId, failedPayload);
@@ -22,6 +22,7 @@ export type WorkflowRunFailedPayload = {
22
22
  readonly error: string;
23
23
  readonly reason?: string;
24
24
  };
25
+ export type WorkflowRunContext = Pick<HandlerContext, "unsafeAppendEvent">;
25
26
  export declare class WorkflowSuspensionUnsupportedError extends Error {
26
27
  readonly workflowName: string;
27
28
  readonly stepIndex: number;
@@ -42,7 +43,7 @@ export declare function startAndRunWorkflow(args: {
42
43
  readonly triggerEvent: WriteEvent;
43
44
  readonly triggerEventRef: TriggerEventRef;
44
45
  readonly idempotencyKey?: string;
45
- readonly handlerCtx: HandlerContext;
46
+ readonly handlerCtx: WorkflowRunContext;
46
47
  }): Promise<{
47
48
  readonly outcome: "completed" | "suspended";
48
49
  }>;
@@ -53,7 +53,11 @@ export async function startAndRunWorkflow(args) {
53
53
  payload: startedPayload,
54
54
  });
55
55
  const steps = buildPipelineSteps(args.workflow.pipelineDef, args.triggerEvent);
56
- const outcome = await runStepList(steps, args.triggerEvent, args.handlerCtx, {}, {}, {
56
+ // @cast-boundary msp-to-handler-ctx — event-trigger hands over the narrower
57
+ // MultiStreamApplyContext (no query/write/writeAs). The step vocabulary this
58
+ // runner drives (wait, compute, return) only ever calls unsafeAppendEvent.
59
+ const stepCtx = args.handlerCtx;
60
+ const outcome = await runStepList(steps, args.triggerEvent, stepCtx, {}, {}, {
57
61
  runId: args.runId,
58
62
  workflowName: args.workflow.name,
59
63
  stepIndex: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.352.0",
3
+ "version": "0.354.0",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -17,7 +17,8 @@
17
17
  "kumiko": {
18
18
  "runtime": "runtime",
19
19
  "bunOnlyExports": [
20
- "./template-resolver/testing"
20
+ "./template-resolver/testing",
21
+ "./tenant-lifecycle/testing"
21
22
  ]
22
23
  },
23
24
  "exports": {
@@ -257,6 +258,10 @@
257
258
  "types": "./dist/user-data-rights/index.d.ts",
258
259
  "default": "./dist/user-data-rights/index.js"
259
260
  },
261
+ "./user-data-rights/email-templates": {
262
+ "types": "./dist/user-data-rights/email-templates.d.ts",
263
+ "default": "./dist/user-data-rights/email-templates.js"
264
+ },
260
265
  "./user-data-rights/web": {
261
266
  "types": "./dist/user-data-rights/web/index.d.ts",
262
267
  "default": "./dist/user-data-rights/web/index.js"
@@ -285,6 +290,10 @@
285
290
  "types": "./dist/tenant-lifecycle/index.d.ts",
286
291
  "default": "./dist/tenant-lifecycle/index.js"
287
292
  },
293
+ "./tenant-lifecycle/testing": {
294
+ "types": "./dist/tenant-lifecycle/testing.d.ts",
295
+ "default": "./dist/tenant-lifecycle/testing.js"
296
+ },
288
297
  "./tenant-handover": {
289
298
  "types": "./dist/tenant-handover/index.d.ts",
290
299
  "default": "./dist/tenant-handover/index.js"
@@ -317,6 +326,10 @@
317
326
  "types": "./dist/auth-email-password/index.d.ts",
318
327
  "default": "./dist/auth-email-password/index.js"
319
328
  },
329
+ "./auth-email-password/email-templates": {
330
+ "types": "./dist/auth-email-password/email-templates.d.ts",
331
+ "default": "./dist/auth-email-password/email-templates.js"
332
+ },
320
333
  "./auth-email-password/constants": {
321
334
  "types": "./dist/auth-email-password/constants.d.ts",
322
335
  "default": "./dist/auth-email-password/constants.js"
@@ -503,12 +516,12 @@
503
516
  }
504
517
  },
505
518
  "dependencies": {
506
- "@cosmicdrift/kumiko-dispatcher-live": "0.352.0",
507
- "@cosmicdrift/kumiko-framework": "0.352.0",
508
- "@cosmicdrift/kumiko-headless": "0.352.0",
509
- "@cosmicdrift/kumiko-renderer": "0.352.0",
510
- "@cosmicdrift/kumiko-renderer-web": "0.352.0",
511
- "@cosmicdrift/kumiko-types": "0.352.0",
519
+ "@cosmicdrift/kumiko-dispatcher-live": "0.354.0",
520
+ "@cosmicdrift/kumiko-framework": "0.354.0",
521
+ "@cosmicdrift/kumiko-headless": "0.354.0",
522
+ "@cosmicdrift/kumiko-renderer": "0.354.0",
523
+ "@cosmicdrift/kumiko-renderer-web": "0.354.0",
524
+ "@cosmicdrift/kumiko-types": "0.354.0",
512
525
  "@mollie/api-client": "^4.5.0",
513
526
  "@node-rs/argon2": "^2.0.2",
514
527
  "@types/mailparser": "^3.4.6",
@@ -771,6 +784,10 @@
771
784
  "types": "./dist/user-data-rights/index.d.ts",
772
785
  "default": "./dist/user-data-rights/index.js"
773
786
  },
787
+ "./user-data-rights/email-templates": {
788
+ "types": "./dist/user-data-rights/email-templates.d.ts",
789
+ "default": "./dist/user-data-rights/email-templates.js"
790
+ },
774
791
  "./user-data-rights/web": {
775
792
  "types": "./dist/user-data-rights/web/index.d.ts",
776
793
  "default": "./dist/user-data-rights/web/index.js"
@@ -799,6 +816,10 @@
799
816
  "types": "./dist/tenant-lifecycle/index.d.ts",
800
817
  "default": "./dist/tenant-lifecycle/index.js"
801
818
  },
819
+ "./tenant-lifecycle/testing": {
820
+ "types": "./dist/tenant-lifecycle/testing.d.ts",
821
+ "default": "./dist/tenant-lifecycle/testing.js"
822
+ },
802
823
  "./tenant-handover": {
803
824
  "types": "./dist/tenant-handover/index.d.ts",
804
825
  "default": "./dist/tenant-handover/index.js"
@@ -831,6 +852,10 @@
831
852
  "types": "./dist/auth-email-password/index.d.ts",
832
853
  "default": "./dist/auth-email-password/index.js"
833
854
  },
855
+ "./auth-email-password/email-templates": {
856
+ "types": "./dist/auth-email-password/email-templates.d.ts",
857
+ "default": "./dist/auth-email-password/email-templates.js"
858
+ },
834
859
  "./auth-email-password/constants": {
835
860
  "types": "./dist/auth-email-password/constants.d.ts",
836
861
  "default": "./dist/auth-email-password/constants.js"
@@ -1025,8 +1050,8 @@
1025
1050
  ],
1026
1051
  "devDependencies": {
1027
1052
  "@testing-library/user-event": "^14.6.1",
1028
- "@cosmicdrift/kumiko-locale-de": "0.352.0",
1029
- "@cosmicdrift/kumiko-locale-es": "0.352.0",
1053
+ "@cosmicdrift/kumiko-locale-de": "0.354.0",
1054
+ "@cosmicdrift/kumiko-locale-es": "0.354.0",
1030
1055
  "jsqr": "^1.4.0"
1031
1056
  }
1032
1057
  }
@@ -1,4 +1,16 @@
1
1
  [
2
+ {
3
+ "version": "0.353.0",
4
+ "type": "fix",
5
+ "title": "buildToolCatalog throws when the manifest was built with denyQns the catalog options lack",
6
+ "migration": "Pass the same denyQns list to buildAgentManifest and buildToolCatalog."
7
+ },
8
+ {
9
+ "version": "0.353.0",
10
+ "type": "fix",
11
+ "title": "Agent manifest screen titles resolve per feature instead of by mount order",
12
+ "detail": "Agent manifest screen titles no longer depend on feature mount order\nTwo features with a screen of the same short id used to get the title of whichever feature was mounted first. The manifest now looks up the screen's own feature-prefixed title key first and falls back to the unprefixed key."
13
+ },
2
14
  {
3
15
  "version": "0.338.0",
4
16
  "type": "improvement",
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.353.0",
4
+ "type": "fix",
5
+ "title": "Audit list paging no longer repeats rows with ascending sort",
6
+ "detail": "Audit list pages forward without repeats when sorted ascending\n`audit:query:list` with `sortDirection: \"asc\"` returned the same rows again on the next page because the cursor always meant \"older than\". The cursor now follows the sort direction, and ties are broken by event id. Sorting by `type` returns a single page without `nextCursor`, since an event id cannot resume a type-ordered listing."
7
+ },
2
8
  {
3
9
  "version": "0.346.0",
4
10
  "type": "improvement",
@@ -1,4 +1,28 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "breaking",
5
+ "title": "Invitations pin the invitee's membership version; superseded accept no longer relies on timestamps",
6
+ "migration": "New nullable column read_tenant_invitations.membership_version: run `kumiko schema generate` and apply the migration before deploying. Invitations issued before the migration keep the timestamp comparison."
7
+ },
8
+ {
9
+ "version": "0.354.0",
10
+ "type": "fix",
11
+ "title": "Login, MFA verify and MFA preauth setup follow the server landingPath when no valid next parameter is present",
12
+ "detail": "AuthGate onSuccess receives the landing path. A validated next parameter still wins; a landing path equal to the current path is not followed, so the screen does not reload itself."
13
+ },
14
+ {
15
+ "version": "0.354.0",
16
+ "type": "fix",
17
+ "title": "Signup-confirm and invite-signup-complete store the request locale on the new user and in the session"
18
+ },
19
+ {
20
+ "version": "0.354.0",
21
+ "type": "fix",
22
+ "title": "INITIAL_SIGNUP_ROLES stays [\"TenantAdmin\", \"Admin\"] as a transition",
23
+ "detail": "New self-signup owners hold TenantAdmin plus the legacy Admin literal, because handlers that gate on the Admin literal would otherwise lock them out. Both roles show in Team, Members until those handlers gate on access.admin. This supersedes the earlier note that the owner role is only TenantAdmin.",
24
+ "migration": "Move your own handlers that gate on the Admin literal to access.admin; after that the Admin entry can be dropped from the constant."
25
+ },
2
26
  {
3
27
  "version": "0.348.0",
4
28
  "type": "fix",
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "breaking",
5
+ "title": "auth-mfa declares the master-key env slots, so composeEnvSchema requires KUMIKO_SECRETS_MASTER_KEY_V1 when auth-mfa is mounted",
6
+ "detail": "Review batch H1: framework parts of consumer-app findings.\n- `auth-email-password` exports `issueSignupActivation` (invalidate, mint, store and mail an activation link; the signup-request handler now runs on it), `SIGNUP_ACTIVATION_NOTIFICATION_TYPE`, `storeSignupToken`, `invalidateExistingSignupToken`, `normalizeEmail` and `SIGNUP_TOKEN_KEY_PREFIXES`.\n- `auth-mfa` declares the master-key env slots (`KUMIKO_SECRETS_MASTER_KEY_V1`, `_CURRENT_VERSION`, the `_CIPHERTEXT` twin) as a shared fragment with `secrets`, so a rotated `V<n>` slot is unpacked even without `secrets` mounted.\n- `user-data-rights` exports `TENANT_MODEL_CONFIG_KEY`; `createTemplateResolverApi` takes a `DbRunner`.\n- The user menu's logout item carries `data-testid=\"user-menu-logout\"`.\n- renderer-web: a `Field` description is linked to its text input via `aria-describedby`; the facet filter dropdown is capped to the available viewport height and scrolls; `createBrowserLocaleResolver` takes `normalizeLocale` and the default storage key is exported as `BROWSER_LOCALE_STORAGE_KEY`.\n- testing: `loginViaApi(request, credentials, { bucketKey })`, `CLIENT_IP_HEADER` from `@cosmicdrift/kumiko-testing/e2e`, and a Playwright-free `@cosmicdrift/kumiko-testing/e2e/constants` subpath (`KUMIKO_SECRETS_MASTER_KEY_V1`).",
7
+ "migration": "Apps that bring their own masterKey provider instead of the env KEK add \"auth-mfa\" to composeEnvSchema's optionalFeatures, like they already do for \"secrets\". Required wins as soon as auth-mfa is not in optionalFeatures, even when `secrets` is listed there."
8
+ },
2
9
  {
3
10
  "version": "0.341.0",
4
11
  "type": "breaking",
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "breaking",
5
+ "title": "BillingInfo.prices and getBillingPrices are keyed by the tier union",
6
+ "migration": "A getBillingPrices that returns keys outside your tier union no longer compiles; remove or rename those keys."
7
+ },
2
8
  {
3
9
  "version": "0.346.0",
4
10
  "type": "fix",
@@ -1 +1,8 @@
1
- []
1
+ [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "fix",
5
+ "title": "in_app_messages gets a tenant, user, created_at index",
6
+ "migration": "Run `kumiko schema generate`; it emits an in-place CREATE INDEX with no DROP and no rebuild."
7
+ }
8
+ ]
@@ -1,4 +1,16 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "fix",
5
+ "title": "forget-subject: the SUBJECT_FORGOTTEN audit now commits before the handler tx does and records cleanupError when a derived-data sweep fails",
6
+ "detail": "The SUBJECT_FORGOTTEN audit event is appended through the outside-transaction db, so it commits before the handler transaction does and survives a later rollback. If the blind-index sweep or search purge fails, the event carries cleanupError (error code or name, never the message) and the handler then fails; retry to finish the cleanup. The user-lifecycle step runs in a savepoint and no longer aborts the handler transaction."
7
+ },
8
+ {
9
+ "version": "0.354.0",
10
+ "type": "fix",
11
+ "title": "Writing a personal field of an erased data subject answers 409 subject_erased instead of 500",
12
+ "detail": "Writing PII to an erased data subject answers 409 `subject_erased`, rebinding a hook's unsafeRaw grant no longer inherits db.global() writes, jobs wait for boot gates before consuming\nA write that has to encrypt a personal field for a subject whose key was erased (crypto-shredding) now fails with a `ConflictError` (409, reason `subject_erased`, i18n in en and de) instead of a 500, on the executor write path and in `encryptForDirectWrite`. A hook's own `unsafeRaw` grant (`withUnsafeRawGrant`) no longer carries the handler's `globalWrites` grant. The job runner starts its BullMQ worker only after the boot gates passed, so a job queued by a previous run cannot execute against an unmet boot precondition. `buildServer` warns at boot when `r.systemScope()` features exist but no RateLimitResolver is configured. The auth, MFA, PAT, change-email and request-deletion handlers declare only the escape-hatch grants they use."
13
+ },
2
14
  {
3
15
  "version": "0.338.0",
4
16
  "type": "improvement",
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "breaking",
5
+ "title": "POST /resubscribe requires the undo token from the POST /unsubscribe response",
6
+ "migration": "Clients that called the resubscribe route with the unsubscribe token must send the undo token instead: read it from the hidden token field of the page POST /unsubscribe returns (aud kumiko:resubscribe, valid for 1 hour). The unsubscribe token is rejected on /resubscribe with 400, and the undo token is rejected on /unsubscribe."
7
+ },
2
8
  {
3
9
  "version": "0.352.0",
4
10
  "type": "breaking",
@@ -1,4 +1,28 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "breaking",
5
+ "title": "FileHandle.derive throws for suffixes outside <name>-<16 hex>",
6
+ "detail": "Final review batch D.\n- `FileHandle.derive(suffix)` now throws unless the suffix is `<name>-<16 hex>` (build it with `variantSuffix(name, spec)`). Free-form suffixes such as `derive(\"thumb\")` wrote keys the forget/tenant-destroy sweep could not recognize, so those binaries survived erasure. Callers passing a bare name must switch to `variantSuffix`.\n- `resolveKmsWiring` / `requireKmsWiring` throw when a `*_CIPHERTEXT` slot is set without its plaintext, pointing at the async entry points, instead of a misleading \"trio required\" error or a silent plaintext-PII fallback.\n- Event-PII owner/`whenAbsent` resolution now also runs without a configured KMS, so a missing owner fails in dev like in prod.\n- PII event backfill queues rebuilds under a per-run migration id, so a peer replica's re-queue is no longer cleared by another run.\n- `seedAdminGuarded` re-writes the canonical admin's email so the blind index is repaired, and skips undecryptable foreign user rows instead of aborting the boot seed.\n- A tenant-bound `FileContext.list` refuses prefixes outside the tenant's key space.\n- An entityList whose create screen was dropped for the caller's roles now carries `createUnavailable`, so the renderer no longer falls back to a generic create form.\n- Boot validation rejects an object-form `redirect` with `idFrom` whose same-feature target screen carries no id.\n- `defineFeature` throws when `dedupeOptions` holds a nested object (it can never compare equal across two mounts).\n- `FileContext.ref` and `list` (tenant-bound) refuse keys outside `${tenantId}/`; list columns accept `virtual: true`; `httpRoute` session-only routes now pass the global IP rate limit; tenant timezone cache TTL is 30 s; text fields without a personal stance warn at boot; the orphan-derivative sweep keeps derivatives whose original still exists; `piiFields` owner fields that can never yield a string throw unless `whenAbsent` is set.",
7
+ "migration": "Build the suffix with variantSuffix(name, spec) instead of passing a free-form string."
8
+ },
9
+ {
10
+ "version": "0.354.0",
11
+ "type": "breaking",
12
+ "title": "A tenant-bound FileContext.list throws for prefixes outside the tenant's key space",
13
+ "migration": "Pass a prefix that starts with `${tenantId}/`."
14
+ },
15
+ {
16
+ "version": "0.354.0",
17
+ "type": "breaking",
18
+ "title": "A tenant-bound FileContext.ref throws for keys outside the tenant's key space",
19
+ "migration": "Use keys that start with `${tenantId}/`."
20
+ },
21
+ {
22
+ "version": "0.354.0",
23
+ "type": "fix",
24
+ "title": "The signed download URL carries a neutral file name instead of the personal fileName"
25
+ },
2
26
  {
3
27
  "version": "0.341.0",
4
28
  "type": "improvement",
@@ -1,4 +1,9 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "fix",
5
+ "title": "The orphan-derivative sweep keeps derivatives whose original still exists in storage"
6
+ },
2
7
  {
3
8
  "version": "0.275.0",
4
9
  "type": "breaking",
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.354.0",
4
+ "type": "fix",
5
+ "title": "folder:delete removes assignments whose host entity is gone instead of refusing forever",
6
+ "detail": "An assignment pointing at a hard-deleted, soft-deleted or unregistered host blocked folder:delete permanently with folder_has_assignments, because clear-folder needs a visible host and could not remove it. folder:delete now removes such assignments through the event-store executor (normal delete events) before the check, without the caller's read gate. Assignments of live hosts, including ones the caller cannot see, still block with folder_has_assignments. A refused delete removes nothing. The cleanup runs lazily at folder:delete, not in a host-delete hook, so restoring a soft-deleted host before then keeps its folder."
7
+ },
2
8
  {
3
9
  "version": "0.246.0",
4
10
  "type": "improvement",