@cosmicdrift/kumiko-bundled-features 0.165.1 → 0.165.3

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 (120) hide show
  1. package/package.json +7 -7
  2. package/src/__tests__/access-invalidation-consumer.integration.test.ts +1 -1
  3. package/src/__tests__/access-invalidation-stream.integration.test.ts +80 -78
  4. package/src/admin-shell/changes.json +8 -0
  5. package/src/agent-tools/changes.json +1 -0
  6. package/src/audit/__tests__/audit.integration.test.ts +5 -2
  7. package/src/audit/changes.json +8 -0
  8. package/src/auth-email-password/__tests__/login-gates.test.ts +39 -0
  9. package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +3 -0
  10. package/src/auth-email-password/changes.json +26 -0
  11. package/src/auth-email-password/handlers/login.write.ts +196 -163
  12. package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +3 -11
  13. package/src/auth-email-password/web/__tests__/fetch-mock.ts +14 -0
  14. package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +3 -11
  15. package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +3 -11
  16. package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +3 -11
  17. package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +3 -11
  18. package/src/auth-foundation/changes.json +8 -0
  19. package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +84 -9
  20. package/src/auth-mfa/changes.json +38 -0
  21. package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +3 -11
  22. package/src/auth-mfa-user-data/changes.json +1 -0
  23. package/src/billing-foundation/changes.json +1 -0
  24. package/src/cap-counter/changes.json +1 -0
  25. package/src/channel-email/changes.json +1 -0
  26. package/src/channel-in-app/changes.json +1 -0
  27. package/src/channel-push/changes.json +1 -0
  28. package/src/compliance-profiles/changes.json +1 -0
  29. package/src/config/changes.json +9 -0
  30. package/src/config/handlers/reencrypt.job.ts +3 -3
  31. package/src/crypto-shredding/changes.json +8 -0
  32. package/src/crypto-shredding/handlers/forget-subject.write.ts +13 -0
  33. package/src/custom-fields/changes.json +8 -0
  34. package/src/data-retention/changes.json +8 -0
  35. package/src/delivery/changes.json +8 -0
  36. package/src/document-ingest-foundation/changes.json +1 -0
  37. package/src/feature-toggles/changes.json +26 -0
  38. package/src/file-foundation/changes.json +1 -0
  39. package/src/file-provider-inmemory/changes.json +1 -0
  40. package/src/file-provider-s3/changes.json +1 -0
  41. package/src/file-provider-s3-env/changes.json +1 -0
  42. package/src/files/changes.json +1 -0
  43. package/src/files-provider-s3/changes.json +1 -0
  44. package/src/folders/changes.json +1 -0
  45. package/src/folders-user-data/changes.json +1 -0
  46. package/src/foundation-shared/changes.json +1 -0
  47. package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +3 -1
  48. package/src/inbound-mail-foundation/changes.json +8 -0
  49. package/src/inbound-mail-foundation/entities.ts +3 -3
  50. package/src/inbound-mail-foundation/watch-supervisor.ts +3 -1
  51. package/src/inbound-provider-imap/changes.json +1 -0
  52. package/src/inbound-provider-inmemory/changes.json +1 -0
  53. package/src/jobs/__tests__/jobs-catalog.integration.test.ts +159 -0
  54. package/src/jobs/__tests__/jobs-screens.boot.test.ts +1 -0
  55. package/src/jobs/__tests__/jobs-security.integration.test.ts +4 -2
  56. package/src/jobs/changes.json +8 -0
  57. package/src/jobs/constants.ts +2 -0
  58. package/src/jobs/feature.ts +3 -1
  59. package/src/jobs/handlers/catalog.query.ts +43 -0
  60. package/src/jobs/handlers/trigger.write.ts +26 -2
  61. package/src/jobs/i18n.ts +33 -0
  62. package/src/jobs/web/__tests__/job-runs-screen.test.tsx +223 -0
  63. package/src/jobs/web/job-runs-screen.tsx +218 -84
  64. package/src/ledger/changes.json +1 -0
  65. package/src/legal-pages/changes.json +1 -0
  66. package/src/mail-foundation/changes.json +1 -0
  67. package/src/mail-transport-inmemory/changes.json +1 -0
  68. package/src/mail-transport-smtp/changes.json +1 -0
  69. package/src/managed-pages/changes.json +20 -0
  70. package/src/page-render/changes.json +14 -0
  71. package/src/personal-access-tokens/changes.json +8 -0
  72. package/src/personal-access-tokens/feature.ts +2 -2
  73. package/src/personal-access-tokens/schema/api-token.ts +3 -3
  74. package/src/presets/changes.json +1 -0
  75. package/src/presets/dsgvo-self-service.ts +1 -1
  76. package/src/rate-limiting/changes.json +1 -0
  77. package/src/readiness/changes.json +1 -0
  78. package/src/renderer-foundation/changes.json +1 -0
  79. package/src/renderer-simple/changes.json +1 -0
  80. package/src/secrets/changes.json +1 -0
  81. package/src/seo/changes.json +20 -0
  82. package/src/sessions/__tests__/sessions.integration.test.ts +19 -3
  83. package/src/sessions/changes.json +8 -0
  84. package/src/sessions/feature.ts +9 -6
  85. package/src/sessions/handlers/revoke-all-for-user.write.ts +9 -1
  86. package/src/sessions/schema/user-session.ts +6 -7
  87. package/src/shared/changes.json +1 -0
  88. package/src/step-dispatcher/changes.json +1 -0
  89. package/src/subscription-mollie/changes.json +1 -0
  90. package/src/subscription-stripe/changes.json +1 -0
  91. package/src/tags/changes.json +1 -0
  92. package/src/template-resolver/changes.json +1 -0
  93. package/src/tenant/changes.json +27 -0
  94. package/src/tenant-lifecycle/changes.json +1 -0
  95. package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +46 -67
  96. package/src/tenant-settings/changes.json +1 -0
  97. package/src/text-content/changes.json +14 -0
  98. package/src/tier-engine/changes.json +1 -0
  99. package/src/user/changes.json +1 -0
  100. package/src/user/schema/user.ts +8 -5
  101. package/src/user-data-rights/__tests__/audit-log.integration.test.ts +7 -3
  102. package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +0 -1
  103. package/src/user-data-rights/__tests__/download.integration.test.ts +13 -1
  104. package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +0 -1
  105. package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +0 -1
  106. package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +0 -1
  107. package/src/user-data-rights/__tests__/request-export.integration.test.ts +0 -1
  108. package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +22 -2
  109. package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +0 -2
  110. package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +0 -1
  111. package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +0 -1
  112. package/src/user-data-rights/changes.json +21 -0
  113. package/src/user-data-rights/feature.ts +7 -6
  114. package/src/user-data-rights/handlers/lift-restriction.write.ts +6 -22
  115. package/src/user-data-rights/handlers/restrict-account.write.ts +6 -22
  116. package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -0
  117. package/src/user-data-rights/lib/deny-if-target-outside-admin-tenant.ts +32 -0
  118. package/src/user-data-rights/run-forget-cleanup.ts +23 -0
  119. package/src/user-data-rights-defaults/changes.json +8 -0
  120. package/src/user-profile/changes.json +8 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.165.1",
3
+ "version": "0.165.3",
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>",
@@ -119,11 +119,11 @@
119
119
  "./step-dispatcher": "./src/step-dispatcher/index.ts"
120
120
  },
121
121
  "dependencies": {
122
- "@cosmicdrift/kumiko-dispatcher-live": "0.165.1",
123
- "@cosmicdrift/kumiko-framework": "0.165.1",
124
- "@cosmicdrift/kumiko-headless": "0.165.1",
125
- "@cosmicdrift/kumiko-renderer": "0.165.1",
126
- "@cosmicdrift/kumiko-renderer-web": "0.165.1",
122
+ "@cosmicdrift/kumiko-dispatcher-live": "0.165.3",
123
+ "@cosmicdrift/kumiko-framework": "0.165.3",
124
+ "@cosmicdrift/kumiko-headless": "0.165.3",
125
+ "@cosmicdrift/kumiko-renderer": "0.165.3",
126
+ "@cosmicdrift/kumiko-renderer-web": "0.165.3",
127
127
  "@mollie/api-client": "^4.5.0",
128
128
  "imapflow": "^1.3.3",
129
129
  "mailparser": "^3.9.8",
@@ -148,7 +148,7 @@
148
148
  "LICENSE"
149
149
  ],
150
150
  "peerDependencies": {
151
- "@cosmicdrift/kumiko-types": "^0.165.1"
151
+ "@cosmicdrift/kumiko-types": "^0.165.3"
152
152
  },
153
153
  "devDependencies": {
154
154
  "@testing-library/user-event": "^14.6.1",
@@ -72,7 +72,7 @@ let invalidated: string[];
72
72
  let unsubscribe: (() => void) | undefined;
73
73
 
74
74
  function trackInvalidation(userId: string): void {
75
- unsubscribe = stack.sseBroker.subscribeAccessInvalidation(userId, () => {
75
+ unsubscribe = stack.sseBroker.subscribeAccessInvalidation?.(userId, () => {
76
76
  invalidated.push(userId);
77
77
  });
78
78
  }
@@ -53,25 +53,28 @@ const callbacks = createLateBoundHolder<SessionCallbacks>("session-callbacks");
53
53
  const encryptionKey = randomBytes(32).toString("base64");
54
54
  const TENANT = testTenantId(1);
55
55
 
56
- // Gate between chunk 0 and chunk 1 so the test can revoke + drain the event
57
- // consumer before the stream's next pull otherwise an eager pump would
58
- // race past the invalidation window.
59
- let releaseNextChunk: (() => void) | undefined;
60
- let nextChunkGate: Promise<void>;
61
-
62
- function resetGate(): void {
63
- nextChunkGate = new Promise<void>((resolve) => {
64
- releaseNextChunk = resolve;
56
+ // Per-stream gate (gateId in payload) so parallel/second streams don't share
57
+ // one module-level Promise Map keyed by the id the test picks.
58
+ const chunkGates = new Map<string, { promise: Promise<void>; release: () => void }>();
59
+
60
+ function createGate(gateId: string): () => void {
61
+ let release!: () => void;
62
+ const promise = new Promise<void>((resolve) => {
63
+ release = resolve;
65
64
  });
65
+ chunkGates.set(gateId, { promise, release });
66
+ return release;
66
67
  }
67
68
 
68
69
  const streamProbeFeature = defineFeature("stream-probe", (r) => {
69
70
  r.streamHandler(
70
71
  "probe:tail",
71
- z.object({}).optional(),
72
- async function* () {
72
+ z.object({ gateId: z.string().min(1) }),
73
+ async function* (event) {
73
74
  yield { phase: "open" as const };
74
- await nextChunkGate;
75
+ const gate = chunkGates.get(event.payload.gateId);
76
+ if (!gate) throw new Error(`missing gate ${event.payload.gateId}`);
77
+ await gate.promise;
75
78
  // Reached only if mid-stream access check failed to cut the generator.
76
79
  yield { phase: "should-not-reach-client" as const };
77
80
  },
@@ -144,74 +147,73 @@ beforeEach(async () => {
144
147
  userSessionTable,
145
148
  eventsTable,
146
149
  ]);
147
- resetGate();
150
+ chunkGates.clear();
148
151
  });
149
152
 
150
- describe("access-invalidation mid-stream SSE teardown (#1561)", () => {
151
- test("session revoke mid-stream terminates the open HTTP SSE stream", async () => {
152
- await h.seedUser("stream-revoke@example.com", "pw-long-enough");
153
- const { token, sid } = await h.login("stream-revoke@example.com", "pw-long-enough");
154
-
155
- const { fetch, csrfToken } = buildLiveFetch(token);
156
- const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
157
-
158
- const iter = dispatcher
159
- .stream<{ phase: string }>("stream-probe:stream:probe:tail", {})
160
- [Symbol.asyncIterator]();
161
-
162
- const first = await iter.next();
163
- expect(first.done).toBe(false);
164
- expect(first.value).toEqual({ phase: "open" });
153
+ async function openProbeStream(token: string): Promise<{
154
+ iter: AsyncIterator<{ phase: string }>;
155
+ release: () => void;
156
+ }> {
157
+ const gateId = generateToken();
158
+ const release = createGate(gateId);
159
+ const { fetch, csrfToken } = buildLiveFetch(token);
160
+ const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
161
+ const iter = dispatcher
162
+ .stream<{ phase: string }>("stream-probe:stream:probe:tail", { gateId })
163
+ [Symbol.asyncIterator]();
164
+ const first = await iter.next();
165
+ expect(first.done).toBe(false);
166
+ expect(first.value).toEqual({ phase: "open" });
167
+ return { iter, release };
168
+ }
165
169
 
166
- const revokeRes = await h.authedPost("/api/write", token, {
167
- type: SessionHandlers.revoke,
168
- payload: { id: sid },
170
+ describe("access-invalidation mid-stream SSE teardown (#1561)", () => {
171
+ const cases = [
172
+ {
173
+ name: "session revoke mid-stream terminates the open HTTP SSE stream",
174
+ setup: async () => {
175
+ await h.seedUser("stream-revoke@example.com", "pw-long-enough");
176
+ const { token, sid } = await h.login("stream-revoke@example.com", "pw-long-enough");
177
+ return {
178
+ token,
179
+ revoke: async () => {
180
+ const revokeRes = await h.authedPost("/api/write", token, {
181
+ type: SessionHandlers.revoke,
182
+ payload: { id: sid },
183
+ });
184
+ expect(revokeRes.status).toBe(200);
185
+ },
186
+ };
187
+ },
188
+ },
189
+ {
190
+ name: "tenant role strip mid-stream terminates the open HTTP SSE stream",
191
+ setup: async () => {
192
+ const { userId } = await h.seedUser("stream-roles@example.com", "pw-long-enough");
193
+ const { token } = await h.login("stream-roles@example.com", "pw-long-enough");
194
+ return {
195
+ token,
196
+ revoke: async () => {
197
+ const admin = await withMintedSession(sessionCreator, TestUsers.systemAdmin);
198
+ await stack.http.writeOk(
199
+ TenantHandlers.updateMemberRoles,
200
+ { userId, tenantId: TENANT, roles: ["Admin"] },
201
+ admin,
202
+ );
203
+ },
204
+ };
205
+ },
206
+ },
207
+ ] as const;
208
+
209
+ for (const c of cases) {
210
+ test(c.name, async () => {
211
+ const { token, revoke } = await c.setup();
212
+ const { iter, release } = await openProbeStream(token);
213
+ await revoke();
214
+ await stack.eventDispatcher?.runOnce();
215
+ release();
216
+ await expect(iter.next()).rejects.toMatchObject({ code: "access_denied" });
169
217
  });
170
- expect(revokeRes.status).toBe(200);
171
- await stack.eventDispatcher?.runOnce();
172
-
173
- releaseNextChunk?.();
174
-
175
- let thrown: unknown;
176
- try {
177
- await iter.next();
178
- } catch (e) {
179
- thrown = e;
180
- }
181
- expect(thrown).toMatchObject({ code: "access_denied" });
182
- });
183
-
184
- test("tenant role strip mid-stream terminates the open HTTP SSE stream", async () => {
185
- const { userId } = await h.seedUser("stream-roles@example.com", "pw-long-enough");
186
- const { token } = await h.login("stream-roles@example.com", "pw-long-enough");
187
-
188
- const { fetch, csrfToken } = buildLiveFetch(token);
189
- const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
190
-
191
- const iter = dispatcher
192
- .stream<{ phase: string }>("stream-probe:stream:probe:tail", {})
193
- [Symbol.asyncIterator]();
194
-
195
- const first = await iter.next();
196
- expect(first.done).toBe(false);
197
- expect(first.value).toEqual({ phase: "open" });
198
-
199
- const admin = await withMintedSession(sessionCreator, TestUsers.systemAdmin);
200
- await stack.http.writeOk(
201
- TenantHandlers.updateMemberRoles,
202
- { userId, tenantId: TENANT, roles: ["Admin"] },
203
- admin,
204
- );
205
- await stack.eventDispatcher?.runOnce();
206
-
207
- releaseNextChunk?.();
208
-
209
- let thrown: unknown;
210
- try {
211
- await iter.next();
212
- } catch (e) {
213
- thrown = e;
214
- }
215
- expect(thrown).toMatchObject({ code: "access_denied" });
216
- });
218
+ }
217
219
  });
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "version": "0.125.0",
4
+ "type": "improvement",
5
+ "title": "admin-shell: neues bundled-feature für rollen-gated Tenant- und Platform-Workspaces mit O…",
6
+ "detail": "admin-shell: neues bundled-feature für rollen-gated Tenant- und Platform-Workspaces mit Overview-Home-Screens, Nav-Icons und Server-i18n. Komponiert Screens aus `tenant`, `audit`, `jobs` und optional `tier-engine` — mount nach diesen Features. Overview-Queries laufen über eine fest kodierte Allowlist pro Workspace, um versehentliche Cross-Workspace-Datenzugriffe zu verhindern."
7
+ }
8
+ ]
@@ -0,0 +1 @@
1
+ []
@@ -212,7 +212,7 @@ describe("audit: list query", () => {
212
212
  ];
213
213
  for (const [name, createdAt] of stamps) {
214
214
  const id = byName.get(name);
215
- expect(id).toBeDefined();
215
+ if (!id) throw new Error(`missing event for ${name}`);
216
216
  await asRawClient(stack.db).unsafe(
217
217
  `UPDATE kumiko_events SET created_at = $1::timestamptz WHERE id = $2::bigint`,
218
218
  [createdAt, id],
@@ -244,7 +244,10 @@ describe("audit: list query", () => {
244
244
  admin,
245
245
  );
246
246
  expect(untilBefore.rows).toHaveLength(1);
247
- expect((untilBefore.rows[0]?.payload as { name?: string }).name).toBe("before-window");
247
+ const untilRow = untilBefore.rows[0];
248
+ expect(untilRow).toBeDefined();
249
+ if (!untilRow) throw new Error("expected untilBefore row");
250
+ expect((untilRow.payload as { name?: string }).name).toBe("before-window");
248
251
  });
249
252
 
250
253
  test("rejects inverted from/to range with validation_error", async () => {
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "version": "0.127.0",
4
+ "type": "improvement",
5
+ "title": "Harden admin operator UI: stricter boot i18n/entityList validation, job run logger wiring…",
6
+ "detail": "Harden admin operator UI: stricter boot i18n/entityList validation, job run logger wiring, audit/job filters, shell breadcrumbs, and bundled entityList/i18n standards."
7
+ }
8
+ ]
@@ -0,0 +1,39 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { USER_STATUS } from "../../user";
3
+ import type { AuthUserRow } from "../auth-user-row";
4
+ import { gateEnforceAccountStatus, gateEnforceEmailVerified } from "../handlers/login.write";
5
+
6
+ function row(over: Partial<AuthUserRow> = {}): AuthUserRow {
7
+ return { id: "00000000-0000-4000-8000-000000000001", passwordHash: "x", ...over };
8
+ }
9
+
10
+ describe("login.write gates (fw#1284)", () => {
11
+ test("gateEnforceEmailVerified rejects when strict and unverified", () => {
12
+ const g = gateEnforceEmailVerified(row({ emailVerified: false }), true);
13
+ expect(g.ok).toBe(false);
14
+ if (!g.ok) expect(g.result.isSuccess).toBe(false);
15
+ });
16
+
17
+ test("gateEnforceEmailVerified passes when not strict", () => {
18
+ expect(gateEnforceEmailVerified(row({ emailVerified: false }), false).ok).toBe(true);
19
+ });
20
+
21
+ test("gateEnforceAccountStatus rejects restricted", () => {
22
+ const g = gateEnforceAccountStatus(row({ status: USER_STATUS.Restricted }));
23
+ expect(g.ok).toBe(false);
24
+ });
25
+
26
+ test("gateEnforceAccountStatus rejects deletion_requested as invalid_creds shape", () => {
27
+ const g = gateEnforceAccountStatus(row({ status: USER_STATUS.DeletionRequested }));
28
+ expect(g.ok).toBe(false);
29
+ if (!g.ok) {
30
+ expect(g.result.isSuccess).toBe(false);
31
+ // anti-enumeration — same family as invalid credentials
32
+ expect(g.result).toMatchObject({ isSuccess: false });
33
+ }
34
+ });
35
+
36
+ test("gateEnforceAccountStatus passes active", () => {
37
+ expect(gateEnforceAccountStatus(row({ status: USER_STATUS.Active })).ok).toBe(true);
38
+ });
39
+ });
@@ -133,6 +133,9 @@ beforeAll(async () => {
133
133
  password: "warmup-password-not-asserted-on",
134
134
  tenants: [],
135
135
  });
136
+ // Pins the authHeader sid-cache key: one create for the seed actor, then
137
+ // we reset. If the cache key drifts, this fails instead of leaking into tests.
138
+ expect(store.created).toHaveLength(1);
136
139
  store.live.clear();
137
140
  store.created.length = 0;
138
141
  store.revoked.length = 0;
@@ -0,0 +1,26 @@
1
+ [
2
+ {
3
+ "version": "0.162.0",
4
+ "type": "improvement",
5
+ "title": "auth-email-password: runtime toggle for self-registration.",
6
+ "detail": "auth-email-password: runtime toggle for self-registration. A new handler-less companion feature (`auth-self-registration`, toggleable, default on) lets an operator flip self-signup off at runtime via feature-toggles without redeploying. `signup-request` silently no-ops when off (matching its always-200 anti-enumeration contract), and a new anonymous-readable query (`auth-email-password:query:signup-registration-status`) lets the public signup page hide its own link/form."
7
+ },
8
+ {
9
+ "version": "0.154.1",
10
+ "type": "fix",
11
+ "title": "Export `LoginScreen`/`LoginScreenProps`/`AuthLegalLink` from `@cosmicdrift/kumiko-bundled…",
12
+ "detail": "Export `LoginScreen`/`LoginScreenProps`/`AuthLegalLink` from `@cosmicdrift/kumiko-bundled-features/auth-email-password/web`. Every other auth screen (ForgotPasswordScreen, SignupScreen, ResetPasswordScreen, …) was already exported from the barrel with its props type; `LoginScreen` was missed. `makeAuthGate`'s second parameter is typed `LoginScreenProps`, so consumers passing a typed `loginScreenProps` override (e.g. `@cosmicdriftgamestudio/kumiko-designer`) couldn't import the type at all. - @cosmicdrift/kumiko-framework@0.154.1 - @cosmicdrift/kumiko-dispatcher-live@0.154.1 - @cosmicdrift/kumiko-headless@0.154.1 - @cosmicdrift/kumiko-renderer@0.154.1 - @cosmicdrift/kumiko-renderer-web@0.154.1"
13
+ },
14
+ {
15
+ "version": "0.151.0",
16
+ "type": "improvement",
17
+ "title": "`auth-email-password/web` no longer exports `LoginScreen`/`LoginScreenProps` directly — u…",
18
+ "detail": "`auth-email-password/web` no longer exports `LoginScreen`/`LoginScreenProps` directly — use the new `createLoginRoute({ loginScreenProps, mfaVerifyScreen, unaffected (they already build on the same logic internally now). Why: a raw `<LoginScreen />` render has no MFA-challenge handling unless the caller remembers to hand-wire `onMfaChallenge` + swap in a verify screen itself — exactly how kumiko-framework#266's login-time MFA step went missing in a real app's standalone apex/marketing login route (it renders outside `emailPasswordClient`'s own gate, which already handled this correctly). `createLoginRoute` is the one place this logic lives now, for both the gated and standalone cases — there's no lower-level piece left to misuse. The `apex-surface-auth` recipe is updated to match."
19
+ },
20
+ {
21
+ "version": "0.131.0",
22
+ "type": "improvement",
23
+ "title": "App-Mounting 2.0 Säule A: Mid-Level-Widget-Kit in renderer-web (StatCard, MiniStat, Secti…",
24
+ "detail": "App-Mounting 2.0 Säule A: Mid-Level-Widget-Kit in renderer-web (StatCard, MiniStat, SectionCard, StatusBadge, ProgressBar, CollapsibleSection, DetailList, ModeSwitch, StatusBarChart, TimeseriesChart, EmptyState/LoadingState/ErrorState, QueryTable) + Status-Farb-Tokens (--color-status-\\*). Neue Hooks useMutation + useDisclosure. Neues Core-Primitive Link (default/button/muted), Button-Variant \"link\", Text-Variant \"muted\"; auth-email-password nutzt sie (authButtonClass/authMutedLinkClass entfernt)."
25
+ }
26
+ ]