@cosmicdrift/kumiko-bundled-features 0.355.0 → 0.357.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 (88) hide show
  1. package/dist/auth-mfa/i18n.js +1 -0
  2. package/dist/auth-mfa/schema/user-mfa.d.ts +6 -0
  3. package/dist/auth-mfa/screens.js +8 -1
  4. package/dist/billing-foundation/entities.d.ts +8 -0
  5. package/dist/billing-foundation/projection.d.ts +8 -0
  6. package/dist/cap-counter/entity.d.ts +1 -0
  7. package/dist/cap-counter/feature.js +7 -1
  8. package/dist/cap-overview/screens.js +3 -1
  9. package/dist/compliance-profiles/handlers/set-profile.write.js +14 -22
  10. package/dist/config/table.d.ts +6 -0
  11. package/dist/crypto-shredding/handlers/forget-subject.write.js +15 -3
  12. package/dist/custom-fields/entity.d.ts +4 -0
  13. package/dist/custom-fields/handlers/define-system-field.write.js +8 -16
  14. package/dist/custom-fields/handlers/delete-system-field.write.js +5 -2
  15. package/dist/custom-fields/lib/define-or-resurrect.d.ts +2 -1
  16. package/dist/custom-fields/lib/define-or-resurrect.js +11 -4
  17. package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -0
  18. package/dist/delivery/chat-webhook-sender.js +1 -1
  19. package/dist/delivery/constants.d.ts +1 -0
  20. package/dist/delivery/constants.js +4 -0
  21. package/dist/delivery/delivery-service.js +62 -25
  22. package/dist/delivery/feature.js +7 -1
  23. package/dist/delivery/jobs.js +25 -1
  24. package/dist/delivery/tables.d.ts +6 -0
  25. package/dist/delivery/testing.d.ts +1 -0
  26. package/dist/document-ingest-foundation/entity.d.ts +4 -0
  27. package/dist/folders/entity.d.ts +8 -0
  28. package/dist/form-draft/entity.d.ts +2 -0
  29. package/dist/inbound-mail-foundation/entities.d.ts +25 -0
  30. package/dist/inbound-mail-foundation/projection.d.ts +20 -0
  31. package/dist/jobs/job-run-table.d.ts +6 -0
  32. package/dist/ledger/entity.d.ts +13 -0
  33. package/dist/managed-pages/handlers/set.write.js +6 -17
  34. package/dist/managed-pages/table.d.ts +12 -0
  35. package/dist/notes-history/entity.d.ts +12 -0
  36. package/dist/notes-history/entity.js +7 -6
  37. package/dist/notes-history-user-data/hooks.d.ts +10 -1
  38. package/dist/notes-history-user-data/hooks.js +78 -61
  39. package/dist/notes-history-user-data/index.d.ts +1 -0
  40. package/dist/notes-history-user-data/index.js +2 -1
  41. package/dist/personal-access-tokens/schema/api-token.d.ts +6 -0
  42. package/dist/personal-access-tokens/screens.js +2 -0
  43. package/dist/secrets/table.d.ts +1 -0
  44. package/dist/sessions/schema/user-session.d.ts +4 -0
  45. package/dist/sessions/screens.js +2 -0
  46. package/dist/shared/index.d.ts +1 -0
  47. package/dist/shared/index.js +1 -0
  48. package/dist/shared/require-for-tenant.d.ts +2 -0
  49. package/dist/shared/require-for-tenant.js +9 -0
  50. package/dist/tags/entity.d.ts +9 -0
  51. package/dist/template-resolver/handlers/collection-set.write.js +4 -4
  52. package/dist/template-resolver/handlers/set.write.js +6 -7
  53. package/dist/template-resolver/handlers/upsert-system.write.js +6 -6
  54. package/dist/template-resolver/handlers/upsert-tenant.write.js +6 -4
  55. package/dist/template-resolver/table.d.ts +10 -0
  56. package/dist/template-resolver/user-content-table.d.ts +10 -0
  57. package/dist/tenant/cancel-pending-invitation.d.ts +2 -2
  58. package/dist/tenant/cancel-pending-invitation.js +2 -2
  59. package/dist/tenant/constants.d.ts +1 -0
  60. package/dist/tenant/constants.js +6 -3
  61. package/dist/tenant/feature.js +2 -0
  62. package/dist/tenant/handlers/member-role-availability.query.d.ts +7 -0
  63. package/dist/tenant/handlers/member-role-availability.query.js +18 -0
  64. package/dist/tenant/handlers/remove-member.write.js +13 -10
  65. package/dist/tenant/handlers/update-member-roles.write.js +12 -9
  66. package/dist/tenant/invitation-table.d.ts +6 -0
  67. package/dist/tenant/member-role-options.d.ts +2 -0
  68. package/dist/tenant/member-role-options.js +4 -0
  69. package/dist/tenant/membership-table.d.ts +4 -0
  70. package/dist/tenant/schema/tenant.d.ts +6 -0
  71. package/dist/tenant/screens.d.ts +4 -2
  72. package/dist/tenant/screens.js +10 -6
  73. package/dist/tier-engine/entity.d.ts +2 -0
  74. package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -18
  75. package/dist/tier-engine/tier-resolver.d.ts +2 -0
  76. package/dist/user/schema/user.d.ts +12 -0
  77. package/dist/user/screens.js +8 -1
  78. package/dist/user-data-rights/schema/download-attempt.d.ts +14 -0
  79. package/dist/user-data-rights/schema/download-token.d.ts +8 -0
  80. package/dist/user-data-rights/schema/export-job.d.ts +6 -0
  81. package/dist/user-data-rights/screens.js +8 -1
  82. package/dist/waitlist/entity.d.ts +14 -0
  83. package/dist/waitlist/screens.js +8 -1
  84. package/package.json +9 -9
  85. package/src/auth-mfa/changes.json +6 -0
  86. package/src/crypto-shredding/changes.json +6 -0
  87. package/src/delivery/changes.json +6 -0
  88. package/src/tenant/changes.json +6 -0
@@ -19,6 +19,7 @@ export const AUTH_MFA_FEATURE_I18N = {
19
19
  "mfa.enable.reveal.qr": { en: "Scan this QR code" },
20
20
  "mfa.enable.reveal.secret": { en: "Or enter this code manually" },
21
21
  "mfa.enable.reveal.recoveryCodes": { en: "Recovery codes" },
22
+ "mfa.enable.reveal.savedCodes": { en: "I have saved my recovery codes." },
22
23
  "mfa.enable.confirm.submit": { en: "Enable" },
23
24
  "mfa.enable.confirm.done": { en: "Two-factor authentication is now enabled." },
24
25
  "screen:auth-mfa-disable.title": { en: "Disable two-factor authentication" },
@@ -4,6 +4,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -26,6 +27,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
28
29
  readonly minLength?: number;
30
+ readonly autoComplete?: string;
29
31
  readonly required?: boolean;
30
32
  readonly searchable?: boolean;
31
33
  readonly sortable?: boolean;
@@ -48,6 +50,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
48
50
  readonly description?: string;
49
51
  readonly maxLength?: number;
50
52
  readonly minLength?: number;
53
+ readonly autoComplete?: string;
51
54
  readonly required?: boolean;
52
55
  readonly searchable?: boolean;
53
56
  readonly sortable?: boolean;
@@ -102,6 +105,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
102
105
  readonly description?: string;
103
106
  readonly maxLength?: number;
104
107
  readonly minLength?: number;
108
+ readonly autoComplete?: string;
105
109
  readonly required?: boolean;
106
110
  readonly searchable?: boolean;
107
111
  readonly sortable?: boolean;
@@ -124,6 +128,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
124
128
  readonly description?: string;
125
129
  readonly maxLength?: number;
126
130
  readonly minLength?: number;
131
+ readonly autoComplete?: string;
127
132
  readonly required?: boolean;
128
133
  readonly searchable?: boolean;
129
134
  readonly sortable?: boolean;
@@ -146,6 +151,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
146
151
  readonly description?: string;
147
152
  readonly maxLength?: number;
148
153
  readonly minLength?: number;
154
+ readonly autoComplete?: string;
149
155
  readonly required?: boolean;
150
156
  readonly searchable?: boolean;
151
157
  readonly sortable?: boolean;
@@ -23,6 +23,7 @@ export const mfaEnableScreen = {
23
23
  reveal: {
24
24
  title: i18nKey("mfa.enable.reveal.title"),
25
25
  warning: i18nKey("mfa.enable.reveal.warning"),
26
+ acknowledge: i18nKey("mfa.enable.reveal.savedCodes"),
26
27
  fields: [
27
28
  {
28
29
  field: "otpauthUri",
@@ -47,7 +48,13 @@ export const mfaEnableScreen = {
47
48
  confirm: {
48
49
  handler: AuthMfaHandlers.enableConfirm,
49
50
  fields: {
50
- code: { type: "text", required: true, minLength: 6, maxLength: 6 },
51
+ code: {
52
+ type: "text",
53
+ required: true,
54
+ minLength: 6,
55
+ maxLength: 6,
56
+ autoComplete: "one-time-code",
57
+ },
51
58
  },
52
59
  layout: { sections: [{ fields: ["code"] }] },
53
60
  carry: ["setupToken"],
@@ -4,6 +4,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -26,6 +27,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
28
29
  readonly minLength?: number;
30
+ readonly autoComplete?: string;
29
31
  readonly required?: boolean;
30
32
  readonly searchable?: boolean;
31
33
  readonly sortable?: boolean;
@@ -48,6 +50,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
48
50
  readonly description?: string;
49
51
  readonly maxLength?: number;
50
52
  readonly minLength?: number;
53
+ readonly autoComplete?: string;
51
54
  readonly required?: boolean;
52
55
  readonly searchable?: boolean;
53
56
  readonly sortable?: boolean;
@@ -70,6 +73,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
70
73
  readonly description?: string;
71
74
  readonly maxLength?: number;
72
75
  readonly minLength?: number;
76
+ readonly autoComplete?: string;
73
77
  readonly required?: boolean;
74
78
  readonly searchable?: boolean;
75
79
  readonly sortable?: boolean;
@@ -92,6 +96,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
92
96
  readonly description?: string;
93
97
  readonly maxLength?: number;
94
98
  readonly minLength?: number;
99
+ readonly autoComplete?: string;
95
100
  readonly required?: boolean;
96
101
  readonly searchable?: boolean;
97
102
  readonly sortable?: boolean;
@@ -147,6 +152,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
147
152
  readonly description?: string;
148
153
  readonly maxLength?: number;
149
154
  readonly minLength?: number;
155
+ readonly autoComplete?: string;
150
156
  readonly required?: boolean;
151
157
  readonly searchable?: boolean;
152
158
  readonly sortable?: boolean;
@@ -169,6 +175,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
169
175
  readonly description?: string;
170
176
  readonly maxLength?: number;
171
177
  readonly minLength?: number;
178
+ readonly autoComplete?: string;
172
179
  readonly required?: boolean;
173
180
  readonly searchable?: boolean;
174
181
  readonly sortable?: boolean;
@@ -191,6 +198,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
191
198
  readonly description?: string;
192
199
  readonly maxLength?: number;
193
200
  readonly minLength?: number;
201
+ readonly autoComplete?: string;
194
202
  readonly required?: boolean;
195
203
  readonly searchable?: boolean;
196
204
  readonly sortable?: boolean;
@@ -4,6 +4,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -26,6 +27,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
28
29
  readonly minLength?: number;
30
+ readonly autoComplete?: string;
29
31
  readonly required?: boolean;
30
32
  readonly searchable?: boolean;
31
33
  readonly sortable?: boolean;
@@ -48,6 +50,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
48
50
  readonly description?: string;
49
51
  readonly maxLength?: number;
50
52
  readonly minLength?: number;
53
+ readonly autoComplete?: string;
51
54
  readonly required?: boolean;
52
55
  readonly searchable?: boolean;
53
56
  readonly sortable?: boolean;
@@ -70,6 +73,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
70
73
  readonly description?: string;
71
74
  readonly maxLength?: number;
72
75
  readonly minLength?: number;
76
+ readonly autoComplete?: string;
73
77
  readonly required?: boolean;
74
78
  readonly searchable?: boolean;
75
79
  readonly sortable?: boolean;
@@ -92,6 +96,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
92
96
  readonly description?: string;
93
97
  readonly maxLength?: number;
94
98
  readonly minLength?: number;
99
+ readonly autoComplete?: string;
95
100
  readonly required?: boolean;
96
101
  readonly searchable?: boolean;
97
102
  readonly sortable?: boolean;
@@ -146,6 +151,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
146
151
  readonly description?: string;
147
152
  readonly maxLength?: number;
148
153
  readonly minLength?: number;
154
+ readonly autoComplete?: string;
149
155
  readonly required?: boolean;
150
156
  readonly searchable?: boolean;
151
157
  readonly sortable?: boolean;
@@ -168,6 +174,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
168
174
  readonly description?: string;
169
175
  readonly maxLength?: number;
170
176
  readonly minLength?: number;
177
+ readonly autoComplete?: string;
171
178
  readonly required?: boolean;
172
179
  readonly searchable?: boolean;
173
180
  readonly sortable?: boolean;
@@ -190,6 +197,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
190
197
  readonly description?: string;
191
198
  readonly maxLength?: number;
192
199
  readonly minLength?: number;
200
+ readonly autoComplete?: string;
193
201
  readonly required?: boolean;
194
202
  readonly searchable?: boolean;
195
203
  readonly sortable?: boolean;
@@ -4,6 +4,7 @@ export declare const capCounterEntity: import("@cosmicdrift/kumiko-types/fields"
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -94,7 +94,13 @@ export const capCounterFeature = defineFeature(CAP_COUNTER_FEATURE, (r) => {
94
94
  id: CAP_COUNTER_LIST_SCREEN_ID,
95
95
  type: "entityList",
96
96
  entity: "cap-counter",
97
- columns: ["tenantId", "capName", "value", "periodStart", "lastSoftWarnedAt"],
97
+ columns: [
98
+ "tenantId",
99
+ "capName",
100
+ "value",
101
+ "periodStart",
102
+ { field: "lastSoftWarnedAt", hideOnNarrow: true },
103
+ ],
98
104
  defaultSort: { field: "capName", dir: "asc" },
99
105
  // Search resolves against the caller's own tenant stream, so on this
100
106
  // cross-tenant operator list it would only ever hit the SystemAdmin's tenant.
@@ -15,10 +15,12 @@ export function createTenantCapListScreen(caps, listCaps, tiers) {
15
15
  { field: "name", label: "cap-overview.list.col.name" },
16
16
  { field: "tier", label: "cap-overview.list.col.tier" },
17
17
  { field: "billing", label: "cap-overview.list.col.billing" },
18
- ...listedCaps.map((cap) => ({
18
+ // Name is the title; tier, billing and the first cap fill the card's three meta rows.
19
+ ...listedCaps.map((cap, index) => ({
19
20
  field: capFieldName(cap.id),
20
21
  label: cap.label,
21
22
  renderer: { react: { __component: CAP_USAGE_CELL_COMPONENT } },
23
+ ...(index > 0 && { hideOnNarrow: true }),
22
24
  })),
23
25
  ],
24
26
  searchable: true,
@@ -4,6 +4,7 @@ import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
4
4
  import { access, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
5
5
  import { UnprocessableError, validationErrorFromZod, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
6
6
  import * as z from "zod";
7
+ import { requireForTenant } from "../../shared/index.js";
7
8
  import { tenantComplianceProfileEntity, tenantComplianceProfileTable, } from "../schema/profile-selection.js";
8
9
  const crud = createEventStoreExecutor(tenantComplianceProfileTable, tenantComplianceProfileEntity, {
9
10
  entityName: "tenant-compliance-profile",
@@ -13,24 +14,16 @@ const crud = createEventStoreExecutor(tenantComplianceProfileTable, tenantCompli
13
14
  // nicht eine waehlbare Production-Option. Symmetrisch zu
14
15
  // SELECTABLE_PROFILE_KEYS aus der framework/compliance-Liste.
15
16
  const profileKeySchema = z.enum(SELECTABLE_PROFILE_KEYS);
16
- // Tenant-Admin setzt Profile-Key + optional Override-JSON.
17
+ // Upsert: the first call inserts, later calls update, so repeating the same values
18
+ // is idempotent apart from the extra audit events.
17
19
  //
18
- // Upsert-Verhalten: erste Wahl insert, weitere update. Idempotent —
19
- // wer mit gleichen Werten zweimal aufruft, kriegt das gleiche Ergebnis
20
- // (modulo aktualisierte Audit-Events im Event-Store).
20
+ // A SystemAdmin may target another tenant via `tenantIdOverride` (operator setup,
21
+ // onboarding migrations); the dispatcher rejects the override for anyone else. The
22
+ // executor then writes into the target tenant's stream via streamTenantId while
23
+ // the operator stays the recorded actor.
21
24
  //
22
- // Cross-Tenant-Pfad: SystemAdmin kann via `tenantIdOverride` fuer einen
23
- // anderen Tenant schreiben (Plattform-Operator-Setup, Customer-
24
- // Onboarding-Migrationen). TenantAdmin's Override-Versuch → 403.
25
- // executorUser.tenantId muss = ziel-tenant sein damit der event-store-
26
- // Stream-Lookup nicht miss → version_conflict gibt (Memory:
27
- // feedback_event_store_tenant_consistency).
28
- //
29
- // Validation:
30
- // - profileKey muss in SELECTABLE_PROFILE_KEYS sein (Zod-checked)
31
- // - override (optional) muss valides JSON-Object sein
32
- // - override Top-Level-Keys muessen in ALLOWED_OVERRIDE_KEYS sein
33
- // — verhindert Tippfehler die deepMerge stillschweigend ignoriert
25
+ // Override top-level keys are restricted to ALLOWED_OVERRIDE_KEYS because deepMerge
26
+ // would silently ignore a typo.
34
27
  export const setProfileWrite = defineWriteHandler({
35
28
  name: "set-profile",
36
29
  schema: z.object({
@@ -43,9 +36,8 @@ export const setProfileWrite = defineWriteHandler({
43
36
  access: { roles: access.admin },
44
37
  description: "Sets or replaces a tenant's compliance profile key plus an optional JSON override, for onboarding or a later region change; a system admin may target a different tenant through tenantIdOverride.",
45
38
  handler: async (event, ctx) => {
46
- const tenantOverride = event.payload.tenantIdOverride;
47
- const tenantId = (tenantOverride ?? event.user.tenantId); // @cast-boundary engine-payload
48
- const executorUser = tenantOverride !== undefined ? { ...event.user, tenantId } : event.user;
39
+ const tenantId = (event.payload.tenantIdOverride ?? event.user.tenantId); // @cast-boundary engine-payload
40
+ const { db, streamTenantId } = requireForTenant(ctx, tenantId);
49
41
  // Override-Validation: muss parseables JSON-Object sein UND dem
50
42
  // ComplianceProfileOverride-Schema entsprechen (S1.9 Z3 — strict-Zod
51
43
  // mit Top-Level + Sub-Level-Whitelist via .strict()). Tippfehler
@@ -72,7 +64,7 @@ export const setProfileWrite = defineWriteHandler({
72
64
  }
73
65
  }
74
66
  // Upsert: existierenden Eintrag suchen
75
- const existing = (await fetchOne(ctx.db, tenantComplianceProfileTable, {
67
+ const existing = (await fetchOne(db, tenantComplianceProfileTable, {
76
68
  tenantId: tenantId,
77
69
  })); // @cast-boundary db-runner
78
70
  if (existing) {
@@ -83,7 +75,7 @@ export const setProfileWrite = defineWriteHandler({
83
75
  profileKey: event.payload.profileKey,
84
76
  override: event.payload.override ?? null,
85
77
  },
86
- }, executorUser, ctx.db);
78
+ }, event.user, db, { streamTenantId });
87
79
  if (!result.isSuccess)
88
80
  return result;
89
81
  return {
@@ -95,7 +87,7 @@ export const setProfileWrite = defineWriteHandler({
95
87
  profileKey: event.payload.profileKey,
96
88
  override: event.payload.override ?? null,
97
89
  tenantId,
98
- }, executorUser, ctx.db);
90
+ }, event.user, db, { streamTenantId });
99
91
  if (!result.isSuccess)
100
92
  return result;
101
93
  return {
@@ -4,6 +4,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -26,6 +27,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
28
29
  readonly minLength?: number;
30
+ readonly autoComplete?: string;
29
31
  readonly required?: boolean;
30
32
  readonly searchable?: boolean;
31
33
  readonly sortable?: boolean;
@@ -48,6 +50,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
48
50
  readonly description?: string;
49
51
  readonly maxLength?: number;
50
52
  readonly minLength?: number;
53
+ readonly autoComplete?: string;
51
54
  readonly required?: boolean;
52
55
  readonly searchable?: boolean;
53
56
  readonly sortable?: boolean;
@@ -72,6 +75,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
72
75
  readonly description?: string;
73
76
  readonly maxLength?: number;
74
77
  readonly minLength?: number;
78
+ readonly autoComplete?: string;
75
79
  readonly required?: boolean;
76
80
  readonly searchable?: boolean;
77
81
  readonly sortable?: boolean;
@@ -94,6 +98,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
94
98
  readonly description?: string;
95
99
  readonly maxLength?: number;
96
100
  readonly minLength?: number;
101
+ readonly autoComplete?: string;
97
102
  readonly required?: boolean;
98
103
  readonly searchable?: boolean;
99
104
  readonly sortable?: boolean;
@@ -116,6 +121,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
116
121
  readonly description?: string;
117
122
  readonly maxLength?: number;
118
123
  readonly minLength?: number;
124
+ readonly autoComplete?: string;
119
125
  readonly required?: boolean;
120
126
  readonly searchable?: boolean;
121
127
  readonly sortable?: boolean;
@@ -10,6 +10,7 @@ import { purgeSearchDocumentsForSubject } from "@cosmicdrift/kumiko-framework/se
10
10
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
11
11
  import * as z from "zod";
12
12
  import { resolveRetentionPolicyForTenant } from "../../data-retention/index.js";
13
+ import { eraseNoteBodiesReachingSubject } from "../../notes-history-user-data/index.js";
13
14
  import { revokeAllPatTokensForUser } from "../../personal-access-tokens/index.js";
14
15
  import { USER_STATUS } from "../../user/index.js";
15
16
  import { denyIfTargetOutsideAdminTenant, isSystemAdminActor, updateUserLifecycle, } from "../../user-data-rights/index.js";
@@ -213,7 +214,7 @@ function sweepErrorLabel(err) {
213
214
  // The key is already erased when this runs, so a failing sweep must not skip the
214
215
  // audit event: the failure is returned as a label for the event instead of thrown.
215
216
  // The savepoint keeps a database error from aborting the handler transaction.
216
- async function sweepDerivedSubjectData(ctx, subject, subjectKey, crossTenantRunner) {
217
+ async function sweepDerivedSubjectData(ctx, subject, subjectKey, crossTenantRunner, audit) {
217
218
  try {
218
219
  await runInSavepointIfSupported(crossTenantRunner, async (crossTenantSubjectRunner) => {
219
220
  // Blind-index sweep (#818): nulls bidx columns now so the deterministic
@@ -224,6 +225,16 @@ async function sweepDerivedSubjectData(ctx, subject, subjectKey, crossTenantRunn
224
225
  if (ctx.searchAdapter) {
225
226
  await purgeSearchDocumentsForSubject(crossTenantSubjectRunner, ctx.registry.features, ctx.searchAdapter, subjectKey, subject);
226
227
  }
228
+ // Note bodies are keyed per note, so the subject's own key never reaches them.
229
+ if (ctx.registry.features.has("notes-history")) {
230
+ await eraseNoteBodiesReachingSubject({
231
+ db: crossTenantSubjectRunner,
232
+ registry: ctx.registry,
233
+ subject,
234
+ erasedBy: audit.erasedBy,
235
+ requestId: audit.requestId,
236
+ });
237
+ }
227
238
  });
228
239
  return undefined;
229
240
  }
@@ -335,12 +346,13 @@ export const forgetSubjectWrite = defineWriteHandler({
335
346
  // but no event exists — a retry is a no-op erase plus the event. The
336
347
  // reverse order could leave an audit trail claiming a shred that never
337
348
  // happened.
349
+ const requestId = requestContext.get()?.requestId ?? "crypto-shredding:forget-subject";
338
350
  await kms.eraseKey(subject, {
339
- requestId: requestContext.get()?.requestId ?? "crypto-shredding:forget-subject",
351
+ requestId,
340
352
  userId: event.user.id,
341
353
  eraseReason: event.payload.reason,
342
354
  });
343
- const cleanupError = await sweepDerivedSubjectData(ctx, subject, subjectKey, ctx.db.unsafeRaw());
355
+ const cleanupError = await sweepDerivedSubjectData(ctx, subject, subjectKey, ctx.db.unsafeRaw(), { requestId, erasedBy: event.user.id });
344
356
  // User subject: close the login door. DEK-erase makes the passwordHash
345
357
  // ciphertext unreadable, but status + PATs are standalone credentials —
346
358
  // the PAT resolver only checks revokedAt/expiresAt, NOT user.status
@@ -4,6 +4,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -26,6 +27,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
26
27
  readonly description?: string;
27
28
  readonly maxLength?: number;
28
29
  readonly minLength?: number;
30
+ readonly autoComplete?: string;
29
31
  readonly required?: boolean;
30
32
  readonly searchable?: boolean;
31
33
  readonly sortable?: boolean;
@@ -48,6 +50,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
48
50
  readonly description?: string;
49
51
  readonly maxLength?: number;
50
52
  readonly minLength?: number;
53
+ readonly autoComplete?: string;
51
54
  readonly required?: boolean;
52
55
  readonly searchable?: boolean;
53
56
  readonly sortable?: boolean;
@@ -95,6 +98,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
95
98
  readonly description?: string;
96
99
  readonly maxLength?: number;
97
100
  readonly minLength?: number;
101
+ readonly autoComplete?: string;
98
102
  readonly required?: boolean;
99
103
  readonly searchable?: boolean;
100
104
  readonly sortable?: boolean;
@@ -1,19 +1,14 @@
1
1
  import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { requireForTenant } from "../../shared/index.js";
2
3
  import { fieldDefinitionAggregateId } from "../aggregate-id.js";
3
4
  import { defineOrResurrectFieldDefinition } from "../lib/define-or-resurrect.js";
4
5
  import { buildFieldDefinitionColumns } from "../lib/field-definition-row.js";
5
6
  import { defineFieldPayloadSchema } from "../schemas.js";
6
- // define-system-field — SystemAdmin definiert eine system-weite Custom-Field-
7
- // Definition die für ALLE Tenants gilt. tenantId wird auf SYSTEM_TENANT_ID
8
- // gesetzt (NICHT vom Caller — SystemAdmin's event.user.tenantId würde sonst
9
- // auf den admin's eigenen platform-tenant zeigen).
10
- //
11
- // **Use-Case:** Vendor (cdgs) sagt "alle Hausverwaltungs-customers haben ab
12
- // heute ein `internalNumber`-Field". Tenant kann den Wert pro customer
13
- // setzen, aber die Definition nicht ändern oder löschen.
14
- //
15
- // **Same-scope-conflict** wie bei define-tenant-field via aggregate-version-
16
- // conflict (deterministische ID mit tenantId=SYSTEM_TENANT_ID).
7
+ // A system-wide field definition applies to every tenant. tenantId is forced to
8
+ // SYSTEM_TENANT_ID (never taken from the caller) and addressed via streamTenantId,
9
+ // so the SystemAdmin stays the recorded actor. Tenants can set values for the field
10
+ // but cannot change or delete the definition. A same-scope duplicate surfaces as an
11
+ // aggregate version conflict, because the ID is derived from SYSTEM_TENANT_ID.
17
12
  export const defineSystemFieldHandler = {
18
13
  name: "define-system-field",
19
14
  schema: defineFieldPayloadSchema,
@@ -22,10 +17,7 @@ export const defineSystemFieldHandler = {
22
17
  handler: async (event, ctx) => {
23
18
  const payload = event.payload; // @cast-boundary engine-payload
24
19
  const aggregateId = fieldDefinitionAggregateId(SYSTEM_TENANT_ID, payload.entityName, payload.fieldKey);
25
- // Override event.user.tenantId to SYSTEM_TENANT_ID for the system-scope
26
- // write. The framework's CrudExecutor writes the row with this tenantId
27
- // — the row lives in the system-scope-stream.
28
- const systemUser = { ...event.user, tenantId: SYSTEM_TENANT_ID };
29
- return defineOrResurrectFieldDefinition(aggregateId, buildFieldDefinitionColumns(payload), systemUser, ctx.db);
20
+ const { db, streamTenantId } = requireForTenant(ctx, SYSTEM_TENANT_ID);
21
+ return defineOrResurrectFieldDefinition(aggregateId, buildFieldDefinitionColumns(payload), event.user, db, streamTenantId);
30
22
  },
31
23
  };
@@ -1,4 +1,5 @@
1
1
  import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { requireForTenant } from "../../shared/index.js";
2
3
  import { fieldDefinitionAggregateId } from "../aggregate-id.js";
3
4
  import { FIELD_DEFINITION_AGGREGATE_TYPE } from "../constants.js";
4
5
  import { fieldDefinitionExecutor } from "../executor.js";
@@ -18,8 +19,10 @@ export const deleteSystemFieldHandler = {
18
19
  handler: async (event, ctx) => {
19
20
  const payload = event.payload; // @cast-boundary engine-payload
20
21
  const aggregateId = fieldDefinitionAggregateId(SYSTEM_TENANT_ID, payload.entityName, payload.fieldKey);
21
- const systemUser = { ...event.user, tenantId: SYSTEM_TENANT_ID };
22
- const result = await fieldDefinitionExecutor.delete({ id: aggregateId }, systemUser, ctx.db);
22
+ const { db, streamTenantId } = requireForTenant(ctx, SYSTEM_TENANT_ID);
23
+ const result = await fieldDefinitionExecutor.delete({ id: aggregateId }, event.user, db, {
24
+ streamTenantId,
25
+ });
23
26
  // Cascade-cleanup-Event — host-entity-MSPs entfernen orphan values aus
24
27
  // ihrer customFields jsonb. Im selben TX = atomic.
25
28
  if (result.isSuccess) {
@@ -1,7 +1,8 @@
1
+ import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
1
2
  import { fieldDefinitionExecutor } from "../executor.js";
2
3
  import type { FieldDefinitionColumns } from "./field-definition-row.js";
3
4
  type DefineUser = Parameters<typeof fieldDefinitionExecutor.create>[1];
4
5
  type DefineDb = Parameters<typeof fieldDefinitionExecutor.create>[2];
5
6
  type WriteResult = Awaited<ReturnType<typeof fieldDefinitionExecutor.create>>;
6
- export declare function defineOrResurrectFieldDefinition(aggregateId: string, columns: FieldDefinitionColumns, user: DefineUser, db: DefineDb): Promise<WriteResult>;
7
+ export declare function defineOrResurrectFieldDefinition(aggregateId: string, columns: FieldDefinitionColumns, user: DefineUser, db: DefineDb, streamTenantId?: TenantId): Promise<WriteResult>;
7
8
  export {};
@@ -16,12 +16,16 @@ import { fieldDefinitionExecutor } from "../executor.js";
16
16
  // fail with "current transaction is aborted". detail() (a read) + restore()
17
17
  // (which sees soft-deleted rows via a tenant-scoped fetch and only writes on
18
18
  // success) keep the tx clean until the single terminal write.
19
- export async function defineOrResurrectFieldDefinition(aggregateId, columns, user, db) {
19
+ export async function defineOrResurrectFieldDefinition(aggregateId, columns, user, db, streamTenantId) {
20
20
  const active = await fieldDefinitionExecutor.detail({ id: aggregateId }, user, db);
21
21
  if (active) {
22
- return fieldDefinitionExecutor.create({ id: aggregateId, ...columns }, user, db);
22
+ return fieldDefinitionExecutor.create({ id: aggregateId, ...columns }, user, db, {
23
+ streamTenantId,
24
+ });
23
25
  }
24
- const restored = await fieldDefinitionExecutor.restore({ id: aggregateId }, user, db);
26
+ const restored = await fieldDefinitionExecutor.restore({ id: aggregateId }, user, db, {
27
+ streamTenantId,
28
+ });
25
29
  if (restored.isSuccess) {
26
30
  // restore() just un-deleted the row in this same tx; no concurrent writer
27
31
  // exists, so skip the optimistic-lock version match (we'd otherwise have to
@@ -31,10 +35,13 @@ export async function defineOrResurrectFieldDefinition(aggregateId, columns, use
31
35
  // readonly FieldDefinitionColumns isn't assignable to it.
32
36
  return fieldDefinitionExecutor.update({ id: aggregateId, changes: { ...columns } }, user, db, {
33
37
  skipOptimisticLock: true,
38
+ streamTenantId,
34
39
  });
35
40
  }
36
41
  if (restored.error.code === "not_found") {
37
- return fieldDefinitionExecutor.create({ id: aggregateId, ...columns }, user, db);
42
+ return fieldDefinitionExecutor.create({ id: aggregateId, ...columns }, user, db, {
43
+ streamTenantId,
44
+ });
38
45
  }
39
46
  return restored;
40
47
  }
@@ -4,6 +4,7 @@ export declare const tenantRetentionOverrideEntity: import("@cosmicdrift/kumiko-
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -42,6 +43,7 @@ export declare const tenantRetentionOverrideEntity: import("@cosmicdrift/kumiko-
42
43
  readonly description?: string;
43
44
  readonly maxLength?: number;
44
45
  readonly minLength?: number;
46
+ readonly autoComplete?: string;
45
47
  readonly required?: boolean;
46
48
  readonly searchable?: boolean;
47
49
  readonly sortable?: boolean;
@@ -66,6 +68,7 @@ export declare const tenantRetentionOverrideTable: import("@cosmicdrift/kumiko-f
66
68
  readonly description?: string;
67
69
  readonly maxLength?: number;
68
70
  readonly minLength?: number;
71
+ readonly autoComplete?: string;
69
72
  readonly required?: boolean;
70
73
  readonly searchable?: boolean;
71
74
  readonly sortable?: boolean;
@@ -104,6 +107,7 @@ export declare const tenantRetentionOverrideTable: import("@cosmicdrift/kumiko-f
104
107
  readonly description?: string;
105
108
  readonly maxLength?: number;
106
109
  readonly minLength?: number;
110
+ readonly autoComplete?: string;
107
111
  readonly required?: boolean;
108
112
  readonly searchable?: boolean;
109
113
  readonly sortable?: boolean;
@@ -17,7 +17,7 @@ export const chatConnectionNameSchema = z
17
17
  function policyFor(request, target) {
18
18
  return request.requireHttps
19
19
  ? { kind: "external" }
20
- : { kind: "internal", allowHosts: [target.hostname] };
20
+ : { kind: "internal", allowOrigins: [target.origin] };
21
21
  }
22
22
  function hostMatches(hostname, allowedHost) {
23
23
  const allowed = allowedHost.toLowerCase();
@@ -1,6 +1,7 @@
1
1
  import type { NotifyPriority } from "@cosmicdrift/kumiko-framework/engine";
2
2
  export declare const DELIVERY_CHANNEL_EXTENSION: "deliveryChannel";
3
3
  export { DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT, DELIVERY_ATTEMPT_EVENT, DELIVERY_CHANNEL_CELL_COMPONENT, DELIVERY_ERROR_CELL_COMPONENT, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_RESUBSCRIBE_PATH, DELIVERY_STATUS_CELL_COMPONENT, DELIVERY_TIME_CELL_COMPONENT, DELIVERY_TYPE_CELL_COMPONENT, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobNames, DeliveryQueries, DeliveryStatus, type DeliveryStatusValue, } from "./public-names.js";
4
+ export declare function deliveryIdempotencyRedisKey(tenantId: string, key: string): string;
4
5
  export declare const deliveryPriorityRank: Record<NotifyPriority, number>;
5
6
  export declare const DeliveryJobs: {
6
7
  readonly render: string;
@@ -4,6 +4,10 @@ import { DELIVERY_FEATURE, DeliveryJobNames } from "./public-names.js";
4
4
  // channel-in-app, channel-push, ...).
5
5
  export const DELIVERY_CHANNEL_EXTENSION = "deliveryChannel";
6
6
  export { DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT, DELIVERY_ATTEMPT_EVENT, DELIVERY_CHANNEL_CELL_COMPONENT, DELIVERY_ERROR_CELL_COMPONENT, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_RESUBSCRIBE_PATH, DELIVERY_STATUS_CELL_COMPONENT, DELIVERY_TIME_CELL_COMPONENT, DELIVERY_TYPE_CELL_COMPONENT, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobNames, DeliveryQueries, DeliveryStatus, } from "./public-names.js";
7
+ // Single source for claim (delivery-service) and terminal-failure release (jobs).
8
+ export function deliveryIdempotencyRedisKey(tenantId, key) {
9
+ return `delivery:idem:${tenantId}:${key}`;
10
+ }
7
11
  // notify() priority → BullMQ job priority. Lower number = processed first; all
8
12
  // > 0 so prioritised delivery jobs never mix with BullMQ's "0 = unprioritised
9
13
  // FIFO" bucket. critical jobs jump ahead of normal/low in the worker queue.