@cosmicdrift/kumiko-headless 0.13.0 → 0.15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-headless",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@cosmicdrift/kumiko-framework": "0.13.0",
31
+ "@cosmicdrift/kumiko-framework": "0.14.0",
32
32
  "zod": "^4.4.3"
33
33
  },
34
34
  "publishConfig": {
@@ -40,4 +40,4 @@
40
40
  "README.md",
41
41
  "LICENSE"
42
42
  ]
43
- }
43
+ }
@@ -1,4 +1,4 @@
1
- import { describe, expect, test, vi } from "vitest";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import type {
3
3
  AssetResolver,
4
4
  ButtonProps,
@@ -51,7 +51,7 @@ describe("Asset / Locale / Primitives contracts", () => {
51
51
  expect(resolver.translate("errors.value.minimum", { min: 3 })).toBe("≥3");
52
52
  expect(resolver.locale()).toBe("de-AT");
53
53
 
54
- const listener = vi.fn();
54
+ const listener = mock();
55
55
  const unsubscribe = resolver.subscribe(listener);
56
56
  for (const l of listeners) l();
57
57
  expect(listener).toHaveBeenCalledTimes(1);
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { createStore } from "../../store";
3
3
  import type {
4
4
  BatchResult,
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { createFormController } from "../form-controller";
4
4
 
@@ -97,13 +97,13 @@ describe("conditional fields — FieldState resolution", () => {
97
97
 
98
98
  // Force a snapshot build so the predicate runs once.
99
99
  form.getSnapshot();
100
- expect(lastCtxSeen).toBe("user");
100
+ expect(lastCtxSeen!).toBe("user");
101
101
 
102
102
  form.setCtx({ role: "admin" });
103
103
  form.getSnapshot();
104
104
 
105
105
  // The predicate must have been re-called AND observed the new ctx.
106
- expect(lastCtxSeen).toBe("admin");
106
+ expect(lastCtxSeen!).toBe("admin");
107
107
  });
108
108
 
109
109
  test("setCtx produces a new snapshot and notifies listeners", () => {
@@ -1,4 +1,4 @@
1
- import { describe, expect, test, vi } from "vitest";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import { createFormController } from "../form-controller";
3
3
 
4
4
  describe("createFormController — core state machine", () => {
@@ -31,7 +31,7 @@ describe("createFormController — core state machine", () => {
31
31
  test("setField to same value is a no-op: no new snapshot, no listener fires", () => {
32
32
  const form = createFormController({ initial: { title: "hello" } });
33
33
  const before = form.getSnapshot();
34
- const listener = vi.fn();
34
+ const listener = mock();
35
35
  form.subscribe(listener);
36
36
 
37
37
  form.setField("title", "hello");
@@ -54,7 +54,7 @@ describe("createFormController — core state machine", () => {
54
54
 
55
55
  test("setValues: bulk update fires one notify, one snapshot rebuild", () => {
56
56
  const form = createFormController({ initial: { a: 1, b: 2, c: 3 } });
57
- const listener = vi.fn();
57
+ const listener = mock();
58
58
  form.subscribe(listener);
59
59
 
60
60
  form.setValues({ a: 10, b: 20 });
@@ -66,7 +66,7 @@ describe("createFormController — core state machine", () => {
66
66
  test("setValues with no effective changes is a no-op", () => {
67
67
  const form = createFormController({ initial: { a: 1, b: 2 } });
68
68
  const before = form.getSnapshot();
69
- const listener = vi.fn();
69
+ const listener = mock();
70
70
  form.subscribe(listener);
71
71
 
72
72
  form.setValues({ a: 1 });
@@ -77,7 +77,7 @@ describe("createFormController — core state machine", () => {
77
77
 
78
78
  test("subscribe/unsubscribe: listener stops firing after unsubscribe", () => {
79
79
  const form = createFormController({ initial: { title: "hello" } });
80
- const listener = vi.fn();
80
+ const listener = mock();
81
81
  const unsubscribe = form.subscribe(listener);
82
82
 
83
83
  form.setField("title", "world");
@@ -125,7 +125,7 @@ describe("createFormController — core state machine", () => {
125
125
  test("reset is a no-op when already clean and error-free", () => {
126
126
  const form = createFormController({ initial: { title: "hello" } });
127
127
  const before = form.getSnapshot();
128
- const listener = vi.fn();
128
+ const listener = mock();
129
129
  form.subscribe(listener);
130
130
 
131
131
  form.reset();
@@ -1,4 +1,4 @@
1
- import { describe, expect, test, vi } from "vitest";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import type { Dispatcher, WriteResult } from "../../dispatcher";
4
4
  import { createStore } from "../../store";
@@ -7,9 +7,9 @@ import { createFormController } from "../form-controller";
7
7
  // Fake dispatcher scoped to this test file — same shape as contract.test.ts
8
8
  // but with an explicit spy on write() so assertions can inspect argv.
9
9
  function makeDispatcher(response?: WriteResult): Dispatcher & {
10
- readonly writeSpy: ReturnType<typeof vi.fn>;
10
+ readonly writeSpy: ReturnType<typeof mock>;
11
11
  } {
12
- const writeSpy = vi.fn(
12
+ const writeSpy = mock(
13
13
  async () => response ?? ({ isSuccess: true, data: { id: "srv-1" } } as WriteResult),
14
14
  );
15
15
  return {
@@ -261,7 +261,7 @@ describe("createFormController — submit()", () => {
261
261
  resolve = r;
262
262
  });
263
263
  const disp: Dispatcher = {
264
- write: vi.fn(async () => slow) as unknown as Dispatcher["write"],
264
+ write: mock(async () => slow) as unknown as Dispatcher["write"],
265
265
  async query<TData>() {
266
266
  return { isSuccess: true, data: null } as unknown as { isSuccess: true; data: TData };
267
267
  },
@@ -1,4 +1,4 @@
1
- import { describe, expect, test, vi } from "vitest";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { createFormController } from "../form-controller";
4
4
  import { groupIssuesByPath, zodErrorToFieldIssues } from "../zod-bridge";
@@ -63,7 +63,7 @@ describe("groupIssuesByPath", () => {
63
63
  describe("createFormController — validate()", () => {
64
64
  test("without a schema: validate() is a no-op that returns true", () => {
65
65
  const form = createFormController({ initial: { title: "" } });
66
- const listener = vi.fn();
66
+ const listener = mock();
67
67
  form.subscribe(listener);
68
68
 
69
69
  const ok = form.validate();
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import type { NavDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
2
- import { describe, expect, test } from "vitest";
3
3
  import { resolveNavigation } from "../resolve";
4
4
  import type { NavRegistrySlice } from "../types";
5
5
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, test, vi } from "vitest";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import { createStore } from "../create-store";
3
3
 
4
4
  describe("createStore — snapshot & setState", () => {
@@ -31,7 +31,7 @@ describe("createStore — snapshot & setState", () => {
31
31
  describe("createStore — subscribe & notify", () => {
32
32
  test("subscribed listener fires on setState", () => {
33
33
  const store = createStore({ count: 0 });
34
- const listener = vi.fn();
34
+ const listener = mock();
35
35
  store.subscribe(listener);
36
36
 
37
37
  store.setState({ count: 1 });
@@ -41,8 +41,8 @@ describe("createStore — subscribe & notify", () => {
41
41
 
42
42
  test("multiple listeners all fire on setState", () => {
43
43
  const store = createStore({ count: 0 });
44
- const a = vi.fn();
45
- const b = vi.fn();
44
+ const a = mock();
45
+ const b = mock();
46
46
  store.subscribe(a);
47
47
  store.subscribe(b);
48
48
 
@@ -54,7 +54,7 @@ describe("createStore — subscribe & notify", () => {
54
54
 
55
55
  test("unsubscribe stops further notifications", () => {
56
56
  const store = createStore({ count: 0 });
57
- const listener = vi.fn();
57
+ const listener = mock();
58
58
  const unsub = store.subscribe(listener);
59
59
 
60
60
  store.setState({ count: 1 });
@@ -67,14 +67,14 @@ describe("createStore — subscribe & notify", () => {
67
67
  test("Object.is-equal setState does NOT notify listeners", () => {
68
68
  const store = createStore({ count: 0 });
69
69
  const same = store.getSnapshot();
70
- const listener = vi.fn();
70
+ const listener = mock();
71
71
  store.subscribe(listener);
72
72
 
73
73
  // Same reference — gate blocks notification.
74
74
  store.setState(same);
75
75
  // Primitive-equal setState on primitive-valued store also no-ops.
76
76
  const primStore = createStore(42);
77
- const primListener = vi.fn();
77
+ const primListener = mock();
78
78
  primStore.subscribe(primListener);
79
79
  primStore.setState(42);
80
80
 
@@ -84,7 +84,7 @@ describe("createStore — subscribe & notify", () => {
84
84
 
85
85
  test("setState inside reducer that returns same ref does NOT notify", () => {
86
86
  const store = createStore({ count: 0 });
87
- const listener = vi.fn();
87
+ const listener = mock();
88
88
  store.subscribe(listener);
89
89
 
90
90
  store.setState((prev) => prev); // reducer returns same ref
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { shallowEqual } from "../equality";
3
3
 
4
4
  describe("shallowEqual — primitive cases", () => {
@@ -1,8 +1,8 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import type {
2
3
  EntityDefinition,
3
4
  EntityEditScreenDefinition,
4
5
  } from "@cosmicdrift/kumiko-framework/ui-types";
5
- import { describe, expect, test } from "vitest";
6
6
  import { computeEditViewModel } from "../edit";
7
7
 
8
8
  const orderEntity = {
@@ -1,8 +1,8 @@
1
+ import { describe, expect, mock, test } from "bun:test";
1
2
  import type {
2
3
  EntityDefinition,
3
4
  EntityListScreenDefinition,
4
5
  } from "@cosmicdrift/kumiko-framework/ui-types";
5
- import { describe, expect, test, vi } from "vitest";
6
6
  import { computeListViewModel } from "../list";
7
7
 
8
8
  // Minimal EntityDefinition-shape. ui-core's view-model only reads
@@ -112,7 +112,7 @@ describe("computeListViewModel", () => {
112
112
  });
113
113
 
114
114
  test("translate is called with the expected i18n-key per field", () => {
115
- const spy = vi.fn((key: string) => `T:${key}`);
115
+ const spy = mock((key: string) => `T:${key}`);
116
116
  computeListViewModel({
117
117
  screen: listScreen(["title", "priority"]),
118
118
  entity: taskEntity,
package/CHANGELOG.md DELETED
@@ -1,441 +0,0 @@
1
- # @cosmicdrift/kumiko-headless
2
-
3
- ## 0.13.0
4
-
5
- ### Patch Changes
6
-
7
- - Updated dependencies [7f56b2f]
8
- - @cosmicdrift/kumiko-framework@0.13.0
9
-
10
- ## 0.12.2
11
-
12
- ### Patch Changes
13
-
14
- - Updated dependencies [597de52]
15
- - @cosmicdrift/kumiko-framework@0.12.2
16
-
17
- ## 0.12.1
18
-
19
- ### Patch Changes
20
-
21
- - Updated dependencies [f2ad7c4]
22
- - @cosmicdrift/kumiko-framework@0.12.1
23
-
24
- ## 0.12.0
25
-
26
- ### Patch Changes
27
-
28
- - @cosmicdrift/kumiko-framework@0.12.0
29
-
30
- ## 0.11.2
31
-
32
- ### Patch Changes
33
-
34
- - Updated dependencies [92a84f0]
35
- - @cosmicdrift/kumiko-framework@0.11.2
36
-
37
- ## 0.11.1
38
-
39
- ### Patch Changes
40
-
41
- - @cosmicdrift/kumiko-framework@0.11.1
42
-
43
- ## 0.11.0
44
-
45
- ### Patch Changes
46
-
47
- - Updated dependencies [30ea981]
48
- - Updated dependencies [9347212]
49
- - @cosmicdrift/kumiko-framework@0.11.0
50
-
51
- ## 0.10.0
52
-
53
- ### Patch Changes
54
-
55
- - Updated dependencies [d06f029]
56
- - Updated dependencies [753d392]
57
- - @cosmicdrift/kumiko-framework@0.10.0
58
-
59
- ## 0.9.0
60
-
61
- ### Patch Changes
62
-
63
- - Updated dependencies [51e22f5]
64
- - @cosmicdrift/kumiko-framework@0.9.0
65
-
66
- ## 0.8.1
67
-
68
- ### Patch Changes
69
-
70
- - Updated dependencies [4b5f91e]
71
- - @cosmicdrift/kumiko-framework@0.8.1
72
-
73
- ## 0.8.0
74
-
75
- ### Patch Changes
76
-
77
- - Updated dependencies [f34af9a]
78
- - Updated dependencies [dff4123]
79
- - @cosmicdrift/kumiko-framework@0.8.0
80
-
81
- ## 0.7.0
82
-
83
- ### Minor Changes
84
-
85
- - bcf43b6: es-ops: `SeedMembershipRow` exposes `streamTenantId` (stream-tenant aus `kumiko_events.v1`) neben dem payload-`tenantId`. Seed-Authors müssen den `kumiko_events`-JOIN nicht mehr selbst bauen — `m.streamTenantId` ist der korrekte Wert für `systemWriteAs`'s `tenantIdOverride` wenn das Aggregate von einem fremden Executor angelegt wurde (typisches `seedTenantMembership(by=systemAdmin)`-Pattern).
86
-
87
- ### Patch Changes
88
-
89
- - Updated dependencies [bcf43b6]
90
- - @cosmicdrift/kumiko-framework@0.7.0
91
-
92
- ## 0.6.0
93
-
94
- ### Minor Changes
95
-
96
- - 8489d18: feat(es-ops): Phase 1.5 — tenantIdOverride + dry-run-validator + E2E-Test + Doku
97
-
98
- Phase 1.5 schließt die Lücken aus Phase 1 die den ersten Driver-Use-Case
99
- (publicstatus admin-roles) blockten. Siehe Retro:
100
- `kumiko-platform/docs/plans/features/es-ops-phase1-retro.md` (PR #9).
101
-
102
- **A1 — tenantIdOverride:**
103
- `SeedMigrationContext.systemWriteAs(qn, payload, tenantIdOverride?)`.
104
- Default SYSTEM_TENANT_ID (unverändert für System-scope-Aggregates wie
105
- config-values). Mit override: `createSystemUser(tenantIdOverride)` als
106
- Executor, damit der Event-Store-Executor den Aggregate-Stream im
107
- richtigen Tenant findet. Fix für die `version_conflict`-Klasse-Bug
108
- (Memory `feedback_event_store_tenant_consistency.md`).
109
-
110
- **A2 — dry-run-validator:**
111
- Runner parsed seed-files vor `migration.run()` per regex
112
- `systemWriteAs\(["']([^"']+)["']`, sammelt handler-QNs, validiert
113
- gegen `registry.getWriteHandler(qn)`. Fail-fast mit klarer Message
114
-
115
- - Datei + QN statt zur Runtime "handler not found". Catched camelCase-
116
- typos (kebab-case-vs-camelCase Drift) + andere QN-Drift zur Boot-Zeit.
117
- runProdApp reicht den richtigen Registry rein (`registry` neu in
118
- RunPendingSeedMigrationsArgs).
119
-
120
- **A3 — E2E-Test:**
121
- `packages/bundled-features/src/__tests__/es-ops-e2e.integration.ts`
122
- mit `setupTestStack`-Pattern: tenant+config Features echt geladen,
123
- echtes Membership-Aggregate via TenantHandlers.addMember im Demo-Tenant,
124
- seed-migration ruft update-member-roles mit tenantIdOverride → write
125
- geht durch, Marker landed, Event in Store, Read-Model aktualisiert.
126
- Plus typo-Test: seed mit camelCase fail-t Dry-Run mit
127
- `/dry-run found.*unknown handler-QN/`. **TDD-First**: ohne A1+A2 wäre
128
- der test rot.
129
-
130
- **A4 — Doku:**
131
- `framework/src/es-ops/README.md` erweitert um „Wann brauche ich
132
- tenantIdOverride?" + „Deployment-Anforderungen" (Docker COPY, Idempotenz,
133
- Multi-Replica) + „Lokaler Smoke vor Push". Recipe-README + seed-files
134
- auf neue API aktualisiert.
135
-
136
- **A5 — Smoke-Skript-Template:**
137
- `samples/recipes/seed-migration/scripts/smoke.ts` als copy-paste-Template
138
- für App-Authors: Bun-runnable, offline (read-only, kein DB-Write),
139
- validiert Module-Load + QN-Resolution + System-User-Access. Recipe-
140
- README dokumentiert Pflicht-Pattern.
141
-
142
- **Bonus-Fix:**
143
- `tenant:write:create`-access auf `["system", "SystemAdmin"]` erweitert
144
- (symmetrisch zu update-member-roles). Aufgedeckt durch Recipe-Smoke +
145
- initial-tenants-Seed. Pinning-Test in `tenant.integration.ts` updated.
146
-
147
- **Test-State:** 45/45 grün (Pre-Push). Typecheck clean. Biome clean.
148
- as-cast-Audit clean. Guard-silent-skip clean. Recipe-Smoke clean.
149
-
150
- **Folge-Step (separater PR):** publicstatus driver-sample reaktivieren
151
- mit lokalem Pre-Push-Smoke gegen publicstatus' echtes Feature-Set.
152
-
153
- ### Patch Changes
154
-
155
- - Updated dependencies [8489d18]
156
- - @cosmicdrift/kumiko-framework@0.6.0
157
-
158
- ## 0.5.2
159
-
160
- ### Patch Changes
161
-
162
- - 4f0d781: fix(tenant): updateMemberRoles erlaubt "system"-Rolle (symmetrisch zu create)
163
-
164
- Drift innerhalb des tenant-Features: `tenant:write:create` akzeptierte
165
- `["system", "SystemAdmin"]`, `tenant:write:update-member-roles` aber
166
- nur `["SystemAdmin"]`. Konsequenz: ops-tooling und seed-migrations
167
- (`createSystemUser` mit `roles: ["system"]`) konnten den Handler nicht
168
- aufrufen — `access_denied`.
169
-
170
- Live entdeckt beim ersten Driver-Sample der es-ops Phase 1: publicstatus
171
- seed `2026-05-20-fix-admin-roles.ts` rief `update-member-roles` via
172
- `systemWriteAs` → access_denied → Pod CrashLoopBackOff.
173
-
174
- Plus access-rule-Pinning-Test in `tenant.integration.ts`-scenario-7.
175
-
176
- - Updated dependencies [4f0d781]
177
- - @cosmicdrift/kumiko-framework@0.5.2
178
-
179
- ## 0.5.1
180
-
181
- ### Patch Changes
182
-
183
- - 0e00015: fix(es-ops): path.resolve statt path.join für seedsDir → seed-files
184
-
185
- Bun's `await import()` braucht absolute Pfade. Wenn der App-Author
186
- `runProdApp({ seedsDir: "./seeds" })` setzt (relativ), würde
187
- `path.join("./seeds", "foo.ts")` einen relativen Pfad liefern → Bun's
188
- Import-Resolver such relativ zum `runner.ts`-Modul (nicht zum
189
- `process.cwd()`) → `Cannot find module 'seeds/...' from '<runner-path>'`.
190
-
191
- `path.resolve` löst gegen `process.cwd()` auf → absolute Pfade →
192
- Import funktioniert. Aufgedeckt beim ersten Live-Boot der publicstatus-
193
- Driver-Migration (Pod CrashLoopBackOff).
194
-
195
- - Updated dependencies [0e00015]
196
- - @cosmicdrift/kumiko-framework@0.5.1
197
-
198
- ## 0.5.0
199
-
200
- ### Minor Changes
201
-
202
- - 7ff69ab: feat(es-ops): Phase 1 — file-based seed-migrations
203
-
204
- Neues first-class Operations-Pattern fürs Framework. Liefert `seed-migrations`
205
- als drizzle-migrate-equivalent für Event-Sourcing-Aggregate-Updates die
206
- idempotent-Seeder nicht erfassen können (z.B. „Member hat schon eine
207
- Rolle, aber jetzt soll noch eine dazukommen").
208
-
209
- Public-API:
210
-
211
- - `runProdApp({ seedsDir })` — Auto-apply pending Migrations beim Boot
212
- - `SeedMigration`-Interface (default-Export einer `seeds/<id>.ts`-File)
213
- - `SeedMigrationContext` mit `systemWriteAs` (ruft existing write-handler
214
- als System-User) + Read-Helpers (`findUserByEmail`,
215
- `findMembershipsOfUser`, `findTenants`)
216
- - CLI: `bunx kumiko ops seed:new|status|apply`
217
- - Tracking-Table `kumiko_es_operations` mit `operation_type`-Discriminator
218
- (vorbereitet auf Phase 2+ Operations: projection-rebuild, event-replay,
219
- stream-migration, ...)
220
- - Env-Flags: `KUMIKO_SKIP_ES_OPS=1` (alle skippen für Recovery),
221
- `KUMIKO_SKIP_ES_OPS_<ID>=1` (einzelne kaputte skippen)
222
-
223
- Garantien: single-run via tracking, atomic via per-migration-Tx,
224
- chronological order via filename-prefix, fail-stop bei Failure (kein
225
- Partial-Apply), ES-konform via Handler-Dispatch.
226
-
227
- Sub-path-Export: `@cosmicdrift/kumiko-framework/es-ops`
228
-
229
- Plan-Doc: `kumiko-platform/docs/plans/features/es-ops.md`
230
- Recipe: `samples/recipes/seed-migration/`
231
- Driver-Use-Case: publicstatus admin-roles-drift (parallel-Branch
232
- `feat/es-ops-driver-admin-roles`).
233
-
234
- Phase 2+ skizziert + offen markiert — Implementation pro Use-Case.
235
-
236
- ### Patch Changes
237
-
238
- - Updated dependencies [7ff69ab]
239
- - @cosmicdrift/kumiko-framework@0.5.0
240
-
241
- ## 0.4.1
242
-
243
- ### Patch Changes
244
-
245
- - 010b410: feat(auth-email-password): "Bestätigungs-Mail erneut senden" im LoginScreen
246
-
247
- LoginScreen bietet bei reason=email_not_verified jetzt einen Resend-Link
248
- im Fehler-Banner — der existierende `requestEmailVerification`-Endpoint
249
- wird direkt aufgerufen, der Banner wechselt nach Erfolg zum Info-Variant
250
- ("Wir haben dir eine neue Bestätigungs-Mail geschickt.").
251
-
252
- UX-Details:
253
-
254
- - Bei 429 → inline-Hint "Bitte warte kurz und versuche es erneut."
255
- - Bei Netzwerk/sonstigen Fehlern → inline-Hint "Konnte nicht senden."
256
- - Anti-Typo-Gate: ändert der User die Email-Eingabe nach dem Login-Fail,
257
- verschwindet der Resend-Link — sonst würde Resend silent-success an die
258
- geänderte (potentiell typoed) Adresse gehen ohne User-Feedback.
259
- - Andere Failure-Codes (invalid_credentials etc.) zeigen weiterhin keinen
260
- Resend-Link.
261
-
262
- i18n: 4 neue Keys (DE+EN) im `auth.login.resend*`-Namespace, additive.
263
- Apps die ihre Translations override-en müssen nichts ändern.
264
-
265
- Additive UI-Feature — keine API-Breaks, keine Schema-Migration.
266
-
267
- - Updated dependencies [010b410]
268
- - @cosmicdrift/kumiko-framework@0.4.1
269
-
270
- ## 0.4.0
271
-
272
- ### Minor Changes
273
-
274
- - 825e7d2: Visual-Tree V.1.4 → V.1.6 — Feature-complete Editor + Folder-Hierarchy + Roving-tabindex.
275
-
276
- **V.1.4** — explicit `folder?: string` Schema-Field auf text-block-entity. Slug bleibt
277
- kebab-only validiert, Folder explizit gesetzt. Tree gruppiert via `groupBlocksByFolder`
278
- (ersetzt `groupBlocksBySlugPrefix`). `Subscribe<T>` Signature um optional `emitError`
279
- erweitert für explicit async-error-Pfade. ProviderBranch zeigt Error-Banner mit
280
- Retry-Button. Drift-Test pinnt seedTextBlock-vs-set.write Slug-Validation.
281
-
282
- **V.1.4b** — URL-State-Routing für Editor-Target via `nav.searchParams`. F5 + Back-Button
283
- stellen den Editor-State wieder her. Format: `?t=text-content:edit&a_slug=...&a_lang=...`.
284
- Plus `useDispatchTarget` hook ersetzt globalen `dispatchTarget` als empfohlenen Production-
285
- Pfad (legacy bleibt für Test-Hooks).
286
-
287
- **V.1.5** — Arrow-Key-Navigation (`<aside role="tree">`, ARIA-tree-Pattern) + SSE-driven
288
- Tree-Refresh. `ClientFeatureDefinition.treeEntities?: string[]` listet Entity-Namen pro
289
- Provider; live-events triggern provider-re-mount → Stale-Tree-state="stub"→"filled"
290
- flippt nach save automatisch.
291
-
292
- **V.1.5c+d** — Active-Node-Highlight (explicit blue + 2px border-l + scrollIntoView),
293
- VS-Code-Polish (compact spacing, focus-visible, folder-icon-color text-amber, indent-
294
- guides per ancestor-depth), Folder-Wrapper für legal-pages ("📁 Legal" + slug-first
295
- Verschachtelung) und text-content ("📁 Content").
296
-
297
- **V.1.6** — Multi-level Folder-Splitting (`folder="page/marketing"` → nested folders,
298
- walk-or-create-pattern, folder/leaf-collision-tolerant). Roving-tabindex (nur focused-
299
- treeitem hat tabIndex=0, Tab cyclt aus dem Tree raus).
300
-
301
- 35/35 kumiko check PASS, 13/13 group-blocks + 22/22 text-content integration tests grün.
302
- Browser + Keyboard lokal validated.
303
-
304
- **Breaking**: `TreeContext` Type entfernt (V.1.2 SR2-Rip — war nie genutzt). Provider sind
305
- session-bound: `TreeChildrenSubscribe = () => Subscribe<T>` statt `(ctx) => Subscribe<T>`.
306
-
307
- **V.1.7-Followups**: useEffect-deps in VisualTree-focus-init (Performance), Cancellation-
308
- Token in TreeProvider's fetch (emit-after-unmount-warning), inline-rename, drag-drop,
309
- file-icons per slug-extension, parent-jump bei ArrowLeft auf collapsed-item.
310
-
311
- ### Patch Changes
312
-
313
- - Updated dependencies [825e7d2]
314
- - @cosmicdrift/kumiko-framework@0.4.0
315
-
316
- ## 0.3.0
317
-
318
- ### Minor Changes
319
-
320
- - 0.3.0 bringt zwei neue Subsysteme (Step-Engine Tier-3 + Visual-Tree) plus
321
- eine AST-Codemod-Pipeline als Vorarbeit für den L2-AI-Layer.
322
-
323
- ### Breaking Changes
324
-
325
- - `skipTransitionGuard` → `unsafeSkipTransitionGuard` (Rename in
326
- feature-ast + engine). Der `unsafe`-Prefix macht die Tragweite des
327
- Casts sichtbar und ist konsistent zur `unsafeProjectionUpsert`- und
328
- `r.rawTable`-Konvention. Migration: 1:1-Ersetzung, keine Verhaltens-Änderung.
329
-
330
- ### Features
331
-
332
- - **Step-Engine M.4 — Tier-3 Workflow-Engine.** Neue Step-Vocabulary
333
- `wait`, `waitForEvent`, `retry` ermöglicht persistierte Long-Running-Flows
334
- über Job-Boundaries hinweg. Q7 Snapshot-at-Start hängt jedem Step-Run
335
- einen SHA-256-Fingerprint des Aggregat-Zustands an, sodass Replays
336
- deterministisch gegen den ursprünglichen Eingangszustand laufen.
337
- - **Visual-Tree V.1.x — Tree-API + Editor-Panel.** Neue `VisualTree`-
338
- Component plus TreeProvider-Pattern; erste TreeProviders für
339
- `text-content` und `legal-pages` (CMS-light + Impressum/Privacy).
340
- Fundament für den späteren No-Code-Designer (~3000 LOC, 98 Tests).
341
- - **Codemod-Pipeline.** AST-basierte Patcher-Module für strukturelle
342
- Feature-Edits — wird vom kommenden L2-AI-Layer als Tool-Surface
343
- verwendet, ist aber eigenständig nutzbar für ts-morph-style Migrationen.
344
- - **user-data-rights Sample-Recipe.** DSGVO Art. 15/17/18/20 vollständig
345
- als Sample-Recipe (`samples/recipes/`) inklusive README — zeigt die
346
- Export- und Forget-Pipeline gegen den `compliance-profiles`-Default
347
- (`eu-dsgvo`).
348
-
349
- ### Fixes
350
-
351
- - `tier-engine`: auto-default-tier-Hook benutzt jetzt `ctx.db.raw` für
352
- Event-Store-Operationen (#37, vorher: stiller Bug, 22 Tage live).
353
- - `engine`: unsafe-projection-upsert nutzt `as never` statt `as any` —
354
- schmaler Cast-Surface, weniger Compiler-Knebel.
355
- - `visual-tree`: runtime-isolation marker für client-konsumierte Files,
356
- damit der Multi-Entry-Build den richtigen Bundle-Split bekommt.
357
- - `feature-ast`: vollständiger `unsafeSkipTransitionGuard`-Rename (war
358
- in zwei Modulen noch der alte Name).
359
- - `framework`: Error-Reasons + `noConsole`-Lint + No-Date-API-Guard
360
- wieder push-ready.
361
-
362
- ### Library-Updates
363
-
364
- hono 4.12, jose 6.2, stripe 22.1, meilisearch 0.58, marked 18,
365
- bun-types 1.3.13, lucide-react 1.14, bullmq 5.76, ioredis 5.10,
366
- i18next 26.0, react + radix-ui-primitives auf aktuelle Minors.
367
-
368
- ### Patch Changes
369
-
370
- - Updated dependencies
371
- - @cosmicdrift/kumiko-framework@0.3.0
372
-
373
- ## 0.2.3
374
-
375
- ### Patch Changes
376
-
377
- - @cosmicdrift/kumiko-framework@0.2.3
378
-
379
- ## 0.2.2
380
-
381
- ### Patch Changes
382
-
383
- - 7a7da3e: Re-publish 0.2.1 → 0.2.2 mit korrekt aufgelösten cross-package-Versionen.
384
- 0.2.1 hatte `workspace:*` als Wert in den dependencies (npm publish ohne
385
- yarn-pack rewrite), Konsumenten bekamen "Workspace not found".
386
-
387
- publish-with-oidc.sh nutzt jetzt `yarn pack` (rewrited workspace:\*) +
388
- `npm publish <tarball>` (OIDC + provenance).
389
-
390
- - Updated dependencies [7a7da3e]
391
- - @cosmicdrift/kumiko-framework@0.2.2
392
-
393
- ## 0.2.1
394
-
395
- ### Patch Changes
396
-
397
- - 48b7f6a: CI: switch publish to npm-CLI with OIDC Trusted Publishing + provenance.
398
- No source changes — verifies the new publish path produces a verified-
399
- provenance attestation on npmjs.com instead of token-based publish.
400
- - Updated dependencies [48b7f6a]
401
- - @cosmicdrift/kumiko-framework@0.2.1
402
-
403
- ## 0.2.0
404
-
405
- ### Minor Changes
406
-
407
- - 6c70b6f: fix(tenant): seedTenant idempotent gegen Event-Store-Projection-Drift.
408
-
409
- Verhindert version_conflict beim App-Boot wenn Aggregat existiert aber
410
- Projection-Row fehlt (rebuild-drift, async-lag, manueller DB-Eingriff).
411
-
412
- ### Patch Changes
413
-
414
- - Updated dependencies [6c70b6f]
415
- - @cosmicdrift/kumiko-framework@0.2.0
416
-
417
- ## 0.1.0
418
-
419
- ### Minor Changes
420
-
421
- - 59ba6d7: Initial public release of Kumiko — AI-native backend builder.
422
-
423
- What ships in 0.1.0:
424
-
425
- - **Engine** (`@cosmicdrift/kumiko-framework`): `defineFeature`, `r.entity`, `r.writeHandler`, `r.queryHandler`, `r.projection`, `r.multiStreamProjection`, `r.hook`, `r.translations`, `r.crud`, `r.referenceData`, `r.screen`, `r.nav`, `r.authClaims`, full lifecycle pipeline with field-level access checks
426
- - **Pipeline** (`@cosmicdrift/kumiko-framework`): `createDispatcher`, JWT auth via jose, Zod schema validation, role-based access checks, command/write/query split
427
- - **DB** (`@cosmicdrift/kumiko-framework`): Drizzle helpers (`buildDrizzleTable`, `applyCursorQuery`), CRUD executor, Postgres dialect, optimistic locking, soft delete, multi-tenant scoping
428
- - **Event sourcing** (`@cosmicdrift/kumiko-framework`): aggregate streams, single + multi-stream projections, event upcasters, asOf queries, archive support, AsyncDaemon-pattern dispatcher
429
- - **Bundled features** (`@cosmicdrift/kumiko-bundled-features`): auth-email-password, sessions, tenants, users, jobs, secrets, file-provider-s3, mail-transport-smtp/inmemory, billing-foundation, cap-counter, channel-in-app, delivery, feature-toggles, legal-pages
430
- - **Renderer** (`@cosmicdrift/kumiko-renderer`, `@cosmicdrift/kumiko-renderer-web`): schema-driven CRUD UI for React + Expo Web, override paths, list debounce, theme tokens
431
- - **Headless** (`@cosmicdrift/kumiko-headless`): view-models for list/edit screens, locale-aware
432
- - **Dev server** (`@cosmicdrift/kumiko-dev-server`): `runDevApp`, `runProdApp`, `kumiko-build` for production bundles (client + server), Docker-ready
433
- - **Realtime** (`@cosmicdrift/kumiko-dispatcher-live`): SSE broadcast across tenants, Redis Pub/Sub backend
434
- - **CLI** (`bin/kumiko.ts`): interactive dev menu, test runners, check pipeline (Biome + TypeScript + 18 guards + Vitest)
435
-
436
- This is a pre-1.0 release — APIs may change between minor versions. Breaking changes will be documented per release.
437
-
438
- ### Patch Changes
439
-
440
- - Updated dependencies [59ba6d7]
441
- - @cosmicdrift/kumiko-framework@0.1.0