@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
@@ -2,7 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { access, defineEntityListHandler, } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
4
4
  // kumiko-lint-ignore cross-feature-import SystemAdmin user-list joins memberships for tenants column
5
- import { tenantMembershipsTable, tenantTable } from "../../tenant/index.js";
5
+ import { decryptTenantNames, tenantMembershipsTable, tenantTable } from "../../tenant/index.js";
6
6
  import { userEntity } from "../schema/user.js";
7
7
  const baseList = defineEntityListHandler("user", userEntity, {
8
8
  access: { roles: access.systemAdmin },
@@ -59,7 +59,7 @@ export async function attachTenantLabels(rows, db) {
59
59
  ...new Set(memberships.map((m) => String(m.tenantId ?? "")).filter((id) => id !== "")),
60
60
  ];
61
61
  const tenants = tenantIds.length > 0
62
- ? await selectMany(db, tenantTable, { id: { in: tenantIds } })
62
+ ? await decryptTenantNames(await selectMany(db, tenantTable, { id: { in: tenantIds } }), "user:list")
63
63
  : [];
64
64
  const byUser = labelsByUserId(memberships, tenantLabelById(tenants));
65
65
  return rows.map((row) => {
@@ -90,6 +90,10 @@ export const listQuery = {
90
90
  return result;
91
91
  }
92
92
  const envelope = result;
93
+ // Without the tenant feature there are no membership tables to join; with
94
+ // it, a failing lookup must surface instead of rendering a silent empty column.
95
+ if (!ctx.registry.features.has("tenant"))
96
+ return result;
93
97
  const rows = await attachTenantLabels(envelope.rows, dbForList(ctx));
94
98
  return { ...envelope, rows };
95
99
  },
@@ -49,6 +49,7 @@ export const updateWrite = defineWriteHandler({
49
49
  },
50
50
  },
51
51
  description: "Changes a user's display name, locale, timezone, email, verification flag, last active tenant or global roles against the version the caller read; callers may edit themselves, while editing someone else or granting roles needs a privileged actor.",
52
+ agent: { risk: "high" },
52
53
  escapeHatch: {
53
54
  reason: SYSTEM_ADMIN_DEMOTION_COUNT_REASON,
54
55
  },
@@ -32,6 +32,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
32
32
  readonly type: "text";
33
33
  readonly description?: string;
34
34
  readonly maxLength?: number;
35
+ readonly minLength?: number;
35
36
  readonly required?: boolean;
36
37
  readonly searchable?: boolean;
37
38
  readonly sortable?: boolean;
@@ -53,6 +54,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
53
54
  readonly type: "text";
54
55
  readonly description?: string;
55
56
  readonly maxLength?: number;
57
+ readonly minLength?: number;
56
58
  readonly required?: boolean;
57
59
  readonly searchable?: boolean;
58
60
  readonly sortable?: boolean;
@@ -74,6 +76,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
74
76
  readonly type: "text";
75
77
  readonly description?: string;
76
78
  readonly maxLength?: number;
79
+ readonly minLength?: number;
77
80
  readonly required?: boolean;
78
81
  readonly searchable?: boolean;
79
82
  readonly sortable?: boolean;
@@ -95,6 +98,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
95
98
  readonly type: "text";
96
99
  readonly description?: string;
97
100
  readonly maxLength?: number;
101
+ readonly minLength?: number;
98
102
  readonly required?: boolean;
99
103
  readonly searchable?: boolean;
100
104
  readonly sortable?: boolean;
@@ -125,6 +129,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
125
129
  readonly type: "text";
126
130
  readonly description?: string;
127
131
  readonly maxLength?: number;
132
+ readonly minLength?: number;
128
133
  readonly required?: boolean;
129
134
  readonly searchable?: boolean;
130
135
  readonly sortable?: boolean;
@@ -158,14 +163,15 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
158
163
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
159
164
  readonly display?: "radio" | "dropdown";
160
165
  readonly optionTones?: {
161
- readonly active?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
162
- readonly deleted?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
163
- readonly deletionRequested?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
164
- readonly restricted?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
166
+ readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
167
+ readonly deleted?: "bad" | "neutral" | "ok" | "warn" | undefined;
168
+ readonly deletionRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
169
+ readonly restricted?: "bad" | "neutral" | "ok" | "warn" | undefined;
165
170
  } | undefined;
166
171
  readonly optionsQuery?: string;
167
172
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
168
173
  readonly optionsAvailabilityQuery?: string;
174
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"active" | "deleted" | "deletionRequested" | "restricted">[] | undefined;
169
175
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
170
176
  required: true;
171
177
  };
@@ -188,6 +194,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
188
194
  readonly type: "text";
189
195
  readonly description?: string;
190
196
  readonly maxLength?: number;
197
+ readonly minLength?: number;
191
198
  readonly required?: boolean;
192
199
  readonly searchable?: boolean;
193
200
  readonly sortable?: boolean;
@@ -211,6 +218,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
211
218
  readonly type: "text";
212
219
  readonly description?: string;
213
220
  readonly maxLength?: number;
221
+ readonly minLength?: number;
214
222
  readonly required?: boolean;
215
223
  readonly searchable?: boolean;
216
224
  readonly sortable?: boolean;
@@ -232,6 +240,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
232
240
  readonly type: "text";
233
241
  readonly description?: string;
234
242
  readonly maxLength?: number;
243
+ readonly minLength?: number;
235
244
  readonly required?: boolean;
236
245
  readonly searchable?: boolean;
237
246
  readonly sortable?: boolean;
@@ -253,6 +262,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
253
262
  readonly type: "text";
254
263
  readonly description?: string;
255
264
  readonly maxLength?: number;
265
+ readonly minLength?: number;
256
266
  readonly required?: boolean;
257
267
  readonly searchable?: boolean;
258
268
  readonly sortable?: boolean;
@@ -274,6 +284,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
274
284
  readonly type: "text";
275
285
  readonly description?: string;
276
286
  readonly maxLength?: number;
287
+ readonly minLength?: number;
277
288
  readonly required?: boolean;
278
289
  readonly searchable?: boolean;
279
290
  readonly sortable?: boolean;
@@ -304,6 +315,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
304
315
  readonly type: "text";
305
316
  readonly description?: string;
306
317
  readonly maxLength?: number;
318
+ readonly minLength?: number;
307
319
  readonly required?: boolean;
308
320
  readonly searchable?: boolean;
309
321
  readonly sortable?: boolean;
@@ -337,14 +349,15 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
337
349
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
338
350
  readonly display?: "radio" | "dropdown";
339
351
  readonly optionTones?: {
340
- readonly active?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
341
- readonly deleted?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
342
- readonly deletionRequested?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
343
- readonly restricted?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
352
+ readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
353
+ readonly deleted?: "bad" | "neutral" | "ok" | "warn" | undefined;
354
+ readonly deletionRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
355
+ readonly restricted?: "bad" | "neutral" | "ok" | "warn" | undefined;
344
356
  } | undefined;
345
357
  readonly optionsQuery?: string;
346
358
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
347
359
  readonly optionsAvailabilityQuery?: string;
360
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"active" | "deleted" | "deletionRequested" | "restricted">[] | undefined;
348
361
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
349
362
  required: true;
350
363
  };
@@ -367,6 +380,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
367
380
  readonly type: "text";
368
381
  readonly description?: string;
369
382
  readonly maxLength?: number;
383
+ readonly minLength?: number;
370
384
  readonly required?: boolean;
371
385
  readonly searchable?: boolean;
372
386
  readonly sortable?: boolean;
@@ -31,3 +31,4 @@ export declare function seedUser(db: DbConnection, options: SeedUserOptions, hoo
31
31
  id: string;
32
32
  }>;
33
33
  export declare function reconcileSeededUserEmailVerified(db: DbConnection, existingRow: Record<string, unknown>, by: SessionUser): Promise<void>;
34
+ export declare function rewriteSeededUserEmail(db: DbConnection, existingRow: Record<string, unknown>, email: string, by: SessionUser): Promise<void>;
@@ -73,6 +73,26 @@ export async function reconcileSeededUserEmailVerified(db, existingRow, by) {
73
73
  throw new Error(`reconcileSeededUserEmailVerified failed: ${result.error.code} — ${JSON.stringify(result.error.details ?? {})}`);
74
74
  }
75
75
  }
76
+ // Re-writes `email` through the executor so the PII write pipeline recomputes
77
+ // `email_bidx`. Repairs rows whose blind index is NULL or stale (written before
78
+ // the key existed, key rotated, subject-key erased), which would otherwise stay
79
+ // invisible to every `fetchOne(userTable, { email })` lookup.
80
+ export async function rewriteSeededUserEmail(db, existingRow, email, by) {
81
+ const id = existingRow["id"];
82
+ if (typeof id !== "string") {
83
+ throw new Error("rewriteSeededUserEmail: existing user row has no string id");
84
+ }
85
+ const tdb = createTenantDb(db, by.tenantId, "system");
86
+ const result = await userExecutor.update({ id, version: existingRow["version"], changes: { email } }, by, tdb);
87
+ // version_conflict: a concurrent write already changed the row — fine for a seed helper.
88
+ // unique_violation: a duplicate row already holds the blind index; the repair
89
+ // has to wait for a manual duplicate cleanup.
90
+ if (!result.isSuccess &&
91
+ result.error.code !== "version_conflict" &&
92
+ result.error.code !== "unique_violation") {
93
+ throw new Error(`rewriteSeededUserEmail failed: ${result.error.code} — ${JSON.stringify(result.error.details ?? {})}`);
94
+ }
95
+ }
76
96
  // Extrahiert die `id`-Spalte aus dem executor.create-Result. Der
77
97
  // Executor liefert ein Record<string, unknown> (die Projection-Row), in
78
98
  // das die DB die Aggregat-id reinschreibt — wir prüfen runtime statt
@@ -20,6 +20,11 @@ export function validateGdprHookCompleteness(features) {
20
20
  continue;
21
21
  const hasExport = typeof usage.options?.["export"] === "function";
22
22
  const hasDelete = typeof usage.options?.["delete"] === "function";
23
+ // The compile-time "at least one hook" type is bypassed by string-typed
24
+ // extension names and casts; without this an empty bag registers nothing.
25
+ if (!hasExport && !hasDelete) {
26
+ throw new Error(`[kumiko:boot] Feature "${feature.name}" registers EXT_USER_DATA for entity "${usage.entityName}" with neither an export nor a delete hook — the entity silently stays out of the Art.15/17/20 pipeline. Register at least one hook.`);
27
+ }
23
28
  if (hasExport && !hasDelete) {
24
29
  throw new Error(`[kumiko:boot] Feature "${feature.name}" exports entity "${usage.entityName}" via EXT_USER_DATA but registers no delete hook — data is included in Art.20 exports but never erased on forget (Art.17 violation). Add a delete hook. If erasure is intentionally handled elsewhere (e.g. crypto-shredding key-erase, parent cascade), register a no-op delete: async () => {} with a comment explaining why.`);
25
30
  }
@@ -28,7 +28,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
28
28
  }
29
29
  // Anonymous apex flow step 2: verify-link target. Verifies the HMAC token,
30
30
  // extracts the userId, and flips the grace period through the shared logic —
31
- // in ONE atomic step with the anchor spend (#3024): commitDeletion carries
31
+ // in ONE atomic step with the anchor spend: commitDeletion carries
32
32
  // the actual grace-period transition, so a crash after the spend can no
33
33
  // longer lose the write step.
34
34
  //
@@ -38,7 +38,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
38
38
  // a cancel-deletion (status → Active, pendingDeletionRequestId → null), a
39
39
  // replayed token fails against the nulled/renewed requestId — no re-arm.
40
40
  //
41
- // Concurrency (#3024): two simultaneous confirms of the same token both read
41
+ // Concurrency: two simultaneous confirms of the same token both read
42
42
  // the same requestId and both verify the HMAC — commitDeletion spends the
43
43
  // anchor AND writes the transition atomically (expect: status===Active &&
44
44
  // pendingDeletionRequestId===requestId), so exactly one wins. The loser gets
@@ -21,6 +21,7 @@ export function createRequestDeletionHandler(opts = {}) {
21
21
  },
22
22
  },
23
23
  escapeHatch: {
24
+ grants: ["systemIdentity", "unsafeRaw"],
24
25
  reason: "resolveGracePeriod reads the tenant compliance profile via ctx.queryAs(SYSTEM, " +
25
26
  "...) to compute the grace period end — the calling user's own identity has no read " +
26
27
  "access to that tenant-config projection. startDeletionGracePeriod also appends the " +
@@ -1,3 +1,4 @@
1
+ export { TENANT_MODEL_CONFIG_KEY } from "./constants.js";
1
2
  export { createUserDataRightsFeature, type UserDataRightsOptions } from "./feature.js";
2
3
  export type { SendDeletionVerificationEmailFn } from "./handlers/request-deletion-by-email.write.js";
3
4
  export { denyIfTargetOutsideAdminTenant, TARGET_USER_NOT_IN_ADMIN_TENANT, } from "./lib/deny-if-target-outside-admin-tenant.js";
@@ -1,3 +1,4 @@
1
+ export { TENANT_MODEL_CONFIG_KEY } from "./constants.js";
1
2
  export { createUserDataRightsFeature } from "./feature.js";
2
3
  export { denyIfTargetOutsideAdminTenant, TARGET_USER_NOT_IN_ADMIN_TENANT, } from "./lib/deny-if-target-outside-admin-tenant.js";
3
4
  export { isSystemAdminActor } from "./lib/is-admin-actor.js";
@@ -3,6 +3,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
3
3
  readonly type: "text";
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
+ readonly minLength?: number;
6
7
  readonly required?: boolean;
7
8
  readonly searchable?: boolean;
8
9
  readonly sortable?: boolean;
@@ -24,6 +25,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
24
25
  readonly type: "text";
25
26
  readonly description?: string;
26
27
  readonly maxLength?: number;
28
+ readonly minLength?: number;
27
29
  readonly required?: boolean;
28
30
  readonly searchable?: boolean;
29
31
  readonly sortable?: boolean;
@@ -45,6 +47,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
45
47
  readonly type: "text";
46
48
  readonly description?: string;
47
49
  readonly maxLength?: number;
50
+ readonly minLength?: number;
48
51
  readonly required?: boolean;
49
52
  readonly searchable?: boolean;
50
53
  readonly sortable?: boolean;
@@ -66,6 +69,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
66
69
  readonly type: "text";
67
70
  readonly description?: string;
68
71
  readonly maxLength?: number;
72
+ readonly minLength?: number;
69
73
  readonly required?: boolean;
70
74
  readonly searchable?: boolean;
71
75
  readonly sortable?: boolean;
@@ -87,6 +91,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
87
91
  readonly type: "text";
88
92
  readonly description?: string;
89
93
  readonly maxLength?: number;
94
+ readonly minLength?: number;
90
95
  readonly required?: boolean;
91
96
  readonly searchable?: boolean;
92
97
  readonly sortable?: boolean;
@@ -108,6 +113,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
108
113
  readonly type: "text";
109
114
  readonly description?: string;
110
115
  readonly maxLength?: number;
116
+ readonly minLength?: number;
111
117
  readonly required?: boolean;
112
118
  readonly searchable?: boolean;
113
119
  readonly sortable?: boolean;
@@ -129,6 +135,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
129
135
  readonly type: "text";
130
136
  readonly description?: string;
131
137
  readonly maxLength?: number;
138
+ readonly minLength?: number;
132
139
  readonly required?: boolean;
133
140
  readonly searchable?: boolean;
134
141
  readonly sortable?: boolean;
@@ -167,6 +174,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
167
174
  readonly type: "text";
168
175
  readonly description?: string;
169
176
  readonly maxLength?: number;
177
+ readonly minLength?: number;
170
178
  readonly required?: boolean;
171
179
  readonly searchable?: boolean;
172
180
  readonly sortable?: boolean;
@@ -188,6 +196,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
188
196
  readonly type: "text";
189
197
  readonly description?: string;
190
198
  readonly maxLength?: number;
199
+ readonly minLength?: number;
191
200
  readonly required?: boolean;
192
201
  readonly searchable?: boolean;
193
202
  readonly sortable?: boolean;
@@ -209,6 +218,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
209
218
  readonly type: "text";
210
219
  readonly description?: string;
211
220
  readonly maxLength?: number;
221
+ readonly minLength?: number;
212
222
  readonly required?: boolean;
213
223
  readonly searchable?: boolean;
214
224
  readonly sortable?: boolean;
@@ -230,6 +240,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
230
240
  readonly type: "text";
231
241
  readonly description?: string;
232
242
  readonly maxLength?: number;
243
+ readonly minLength?: number;
233
244
  readonly required?: boolean;
234
245
  readonly searchable?: boolean;
235
246
  readonly sortable?: boolean;
@@ -251,6 +262,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
251
262
  readonly type: "text";
252
263
  readonly description?: string;
253
264
  readonly maxLength?: number;
265
+ readonly minLength?: number;
254
266
  readonly required?: boolean;
255
267
  readonly searchable?: boolean;
256
268
  readonly sortable?: boolean;
@@ -272,6 +284,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
272
284
  readonly type: "text";
273
285
  readonly description?: string;
274
286
  readonly maxLength?: number;
287
+ readonly minLength?: number;
275
288
  readonly required?: boolean;
276
289
  readonly searchable?: boolean;
277
290
  readonly sortable?: boolean;
@@ -293,6 +306,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
293
306
  readonly type: "text";
294
307
  readonly description?: string;
295
308
  readonly maxLength?: number;
309
+ readonly minLength?: number;
296
310
  readonly required?: boolean;
297
311
  readonly searchable?: boolean;
298
312
  readonly sortable?: boolean;
@@ -29,16 +29,24 @@ export const downloadAttemptEntity = createEntity({
29
29
  jobId: createTextField({ personal: false, reason: "technical_reference" }),
30
30
  // User-ID wenn auth-Pfad (job). NULL bei anonymous (token-Pfad).
31
31
  attemptedByUserId: createTextField({ personal: false, reason: "pseudonymous_fk" }),
32
- // Same reasoning as download-token.ts's lastUsedFromIp/lastUsedUserAgent
33
- // (sibling entity, same feature): DPO brute-force-audit data belongs to
34
- // the tenant operator, not the (often anonymous) requester. Also avoids
35
- // a real hazard on this buildEntityTable-managed entity: attemptedByUserId
36
- // is null-by-design for the anonymous token path, so a subject annotation
37
- // (`personal: { of: "attemptedByUserId" }`) would throw
38
- // SubjectResolutionError on every such row once a KMS is configured
32
+ // No subject annotation on this buildEntityTable-managed entity:
33
+ // attemptedByUserId is null-by-design for the anonymous token path, so
34
+ // `personal: { of: "attemptedByUserId" }` would throw SubjectResolutionError
35
+ // on every such row once a KMS is configured
39
36
  // (crypto/subject-resolver.ts resolveUserOwnedSubject).
40
- ip: createTextField({ maxLength: 64, personal: false, reason: "is_business_data" }),
41
- userAgent: createTextField({ maxLength: 256, personal: false, reason: "is_business_data" }),
37
+ // The job path knows the requester and an IP is personal data, so this is a
38
+ // deliberate waiver, not business data: a user forget does not shred these
39
+ // values, the 90d hardDelete below is what removes them.
40
+ ip: createTextField({
41
+ maxLength: 64,
42
+ personal: false,
43
+ reason: "security_audit_retention_bounded",
44
+ }),
45
+ userAgent: createTextField({
46
+ maxLength: 256,
47
+ personal: false,
48
+ reason: "security_audit_retention_bounded",
49
+ }),
42
50
  attemptedAt: createTimestampField({ required: true }),
43
51
  },
44
52
  // 90d hardDelete: unbounded growth = disk-bomb genau gegen das System
@@ -3,6 +3,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
3
3
  readonly type: "text";
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
+ readonly minLength?: number;
6
7
  readonly required?: boolean;
7
8
  readonly searchable?: boolean;
8
9
  readonly sortable?: boolean;
@@ -24,6 +25,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
24
25
  readonly type: "text";
25
26
  readonly description?: string;
26
27
  readonly maxLength?: number;
28
+ readonly minLength?: number;
27
29
  readonly required?: boolean;
28
30
  readonly searchable?: boolean;
29
31
  readonly sortable?: boolean;
@@ -102,6 +104,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
102
104
  readonly type: "text";
103
105
  readonly description?: string;
104
106
  readonly maxLength?: number;
107
+ readonly minLength?: number;
105
108
  readonly required?: boolean;
106
109
  readonly searchable?: boolean;
107
110
  readonly sortable?: boolean;
@@ -123,6 +126,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
123
126
  readonly type: "text";
124
127
  readonly description?: string;
125
128
  readonly maxLength?: number;
129
+ readonly minLength?: number;
126
130
  readonly required?: boolean;
127
131
  readonly searchable?: boolean;
128
132
  readonly sortable?: boolean;
@@ -146,6 +150,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
146
150
  readonly type: "text";
147
151
  readonly description?: string;
148
152
  readonly maxLength?: number;
153
+ readonly minLength?: number;
149
154
  readonly required?: boolean;
150
155
  readonly searchable?: boolean;
151
156
  readonly sortable?: boolean;
@@ -167,6 +172,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
167
172
  readonly type: "text";
168
173
  readonly description?: string;
169
174
  readonly maxLength?: number;
175
+ readonly minLength?: number;
170
176
  readonly required?: boolean;
171
177
  readonly searchable?: boolean;
172
178
  readonly sortable?: boolean;
@@ -245,6 +251,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
245
251
  readonly type: "text";
246
252
  readonly description?: string;
247
253
  readonly maxLength?: number;
254
+ readonly minLength?: number;
248
255
  readonly required?: boolean;
249
256
  readonly searchable?: boolean;
250
257
  readonly sortable?: boolean;
@@ -266,6 +273,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
266
273
  readonly type: "text";
267
274
  readonly description?: string;
268
275
  readonly maxLength?: number;
276
+ readonly minLength?: number;
269
277
  readonly required?: boolean;
270
278
  readonly searchable?: boolean;
271
279
  readonly sortable?: boolean;
@@ -17,6 +17,7 @@ export declare const exportJobEntity: import("@cosmicdrift/kumiko-types/fields")
17
17
  readonly type: "text";
18
18
  readonly description?: string;
19
19
  readonly maxLength?: number;
20
+ readonly minLength?: number;
20
21
  readonly required?: boolean;
21
22
  readonly searchable?: boolean;
22
23
  readonly sortable?: boolean;
@@ -38,6 +39,7 @@ export declare const exportJobEntity: import("@cosmicdrift/kumiko-types/fields")
38
39
  readonly type: "text";
39
40
  readonly description?: string;
40
41
  readonly maxLength?: number;
42
+ readonly minLength?: number;
41
43
  readonly required?: boolean;
42
44
  readonly searchable?: boolean;
43
45
  readonly sortable?: boolean;
@@ -67,14 +69,15 @@ export declare const exportJobEntity: import("@cosmicdrift/kumiko-types/fields")
67
69
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
68
70
  readonly display?: "radio" | "dropdown";
69
71
  readonly optionTones?: {
70
- readonly done?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
71
- readonly failed?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
72
- readonly pending?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
73
- readonly running?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
72
+ readonly done?: "bad" | "neutral" | "ok" | "warn" | undefined;
73
+ readonly failed?: "bad" | "neutral" | "ok" | "warn" | undefined;
74
+ readonly pending?: "bad" | "neutral" | "ok" | "warn" | undefined;
75
+ readonly running?: "bad" | "neutral" | "ok" | "warn" | undefined;
74
76
  } | undefined;
75
77
  readonly optionsQuery?: string;
76
78
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
77
79
  readonly optionsAvailabilityQuery?: string;
80
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"done" | "failed" | "pending" | "running">[] | undefined;
78
81
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
79
82
  required: true;
80
83
  };
@@ -127,6 +130,7 @@ export declare const exportJobEntity: import("@cosmicdrift/kumiko-types/fields")
127
130
  readonly type: "text";
128
131
  readonly description?: string;
129
132
  readonly maxLength?: number;
133
+ readonly minLength?: number;
130
134
  readonly required?: boolean;
131
135
  readonly searchable?: boolean;
132
136
  readonly sortable?: boolean;
@@ -193,6 +197,7 @@ export declare const exportJobsTable: import("@cosmicdrift/kumiko-framework/db")
193
197
  readonly type: "text";
194
198
  readonly description?: string;
195
199
  readonly maxLength?: number;
200
+ readonly minLength?: number;
196
201
  readonly required?: boolean;
197
202
  readonly searchable?: boolean;
198
203
  readonly sortable?: boolean;
@@ -214,6 +219,7 @@ export declare const exportJobsTable: import("@cosmicdrift/kumiko-framework/db")
214
219
  readonly type: "text";
215
220
  readonly description?: string;
216
221
  readonly maxLength?: number;
222
+ readonly minLength?: number;
217
223
  readonly required?: boolean;
218
224
  readonly searchable?: boolean;
219
225
  readonly sortable?: boolean;
@@ -243,14 +249,15 @@ export declare const exportJobsTable: import("@cosmicdrift/kumiko-framework/db")
243
249
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
244
250
  readonly display?: "radio" | "dropdown";
245
251
  readonly optionTones?: {
246
- readonly done?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
247
- readonly failed?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
248
- readonly pending?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
249
- readonly running?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
252
+ readonly done?: "bad" | "neutral" | "ok" | "warn" | undefined;
253
+ readonly failed?: "bad" | "neutral" | "ok" | "warn" | undefined;
254
+ readonly pending?: "bad" | "neutral" | "ok" | "warn" | undefined;
255
+ readonly running?: "bad" | "neutral" | "ok" | "warn" | undefined;
250
256
  } | undefined;
251
257
  readonly optionsQuery?: string;
252
258
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
253
259
  readonly optionsAvailabilityQuery?: string;
260
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"done" | "failed" | "pending" | "running">[] | undefined;
254
261
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
255
262
  required: true;
256
263
  };
@@ -303,6 +310,7 @@ export declare const exportJobsTable: import("@cosmicdrift/kumiko-framework/db")
303
310
  readonly type: "text";
304
311
  readonly description?: string;
305
312
  readonly maxLength?: number;
313
+ readonly minLength?: number;
306
314
  readonly required?: boolean;
307
315
  readonly searchable?: boolean;
308
316
  readonly sortable?: boolean;
@@ -1,4 +1,4 @@
1
- import { access, createSystemUser, defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
1
+ import { createSystemUser, defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
3
3
  import * as z from "zod";
4
4
  import { AuthErrors } from "../../auth-email-password/index.js";
@@ -27,8 +27,14 @@ export const changeEmailWrite = defineWriteHandler({
27
27
  currentPassword: z.string().min(1),
28
28
  newEmail: z.email(),
29
29
  }),
30
- access: { roles: access.authenticated },
30
+ access: {
31
+ openToAll: {
32
+ reason: "self-service for any signed-in user, including tenant members whose only role is " +
33
+ "TenantAdmin; the handler only touches event.user.id and re-checks the caller's password",
34
+ },
35
+ },
31
36
  escapeHatch: {
37
+ grants: ["systemIdentity"],
32
38
  reason: "Reads the caller's own passwordHash via ctx.queryAs(SYSTEM, user:findForAuth), checks " +
33
39
  "email uniqueness the same way, and writes the new email via ctx.writeAs(SYSTEM, " +
34
40
  "user:update) — field-access on passwordHash/email is privileged-only.",