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