@cosmicdrift/kumiko-dev-server 0.198.0 → 0.199.1

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-dev-server",
3
- "version": "0.198.0",
3
+ "version": "0.199.1",
4
4
  "description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding, codegen. Not shipped into production node_modules — see @cosmicdrift/kumiko-server-runtime for the prod boot path.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -54,9 +54,9 @@
54
54
  "kumiko-schema-check": "./bin/kumiko-schema-check.ts"
55
55
  },
56
56
  "dependencies": {
57
- "@cosmicdrift/kumiko-bundled-features": "0.198.0",
58
- "@cosmicdrift/kumiko-framework": "0.198.0",
59
- "@cosmicdrift/kumiko-server-runtime": "0.198.0",
57
+ "@cosmicdrift/kumiko-bundled-features": "0.199.1",
58
+ "@cosmicdrift/kumiko-framework": "0.199.1",
59
+ "@cosmicdrift/kumiko-server-runtime": "0.199.1",
60
60
  "ts-morph": "^28.0.0"
61
61
  },
62
62
  "publishConfig": {
@@ -283,6 +283,22 @@ describe("createKumikoServer (Multi-Entry)", () => {
283
283
  )
284
284
  ).text();
285
285
  expect(adminHtml).toMatch(/__KUMIKO_SCHEMA__/);
286
+
287
+ // #2062: setupTestStack always wires an in-memory SearchAdapter
288
+ // (test-stack.ts), so the dev-server call site's
289
+ // `searchAdapterMissing: !stack.context.searchAdapter` must read false —
290
+ // proving the flag actually flows from stack.context through to the
291
+ // injected schema instead of silently staying at its own default.
292
+ const schemaMatch = adminHtml.match(/window\.__KUMIKO_SCHEMA__=(\{.*?\});<\/script>/s);
293
+ expect(schemaMatch).not.toBeNull();
294
+ const injectedSchema = JSON.parse(schemaMatch?.[1] ?? "{}");
295
+ // Guards against a regex miss silently degrading to `?? "{}"` — an empty
296
+ // features array would make `.some(...)` below vacuously false too.
297
+ expect(injectedSchema.features.length).toBeGreaterThan(0);
298
+ const anyFeatureMissingAdapter = injectedSchema.features.some(
299
+ (f: { searchAdapterMissing?: boolean }) => f.searchAdapterMissing === true,
300
+ );
301
+ expect(anyFeatureMissingAdapter).toBe(false);
286
302
  });
287
303
 
288
304
  test("hostDispatch redirect: liefert 302 mit Location-Header", async () => {
@@ -0,0 +1,47 @@
1
+ // Regression for #2065: scaffolded `tasks` demo feature had hardcoded nav
2
+ // labels ("Tasks", "New task") instead of i18n keys, invisible to
3
+ // guard-i18n-ui-strings because it only scans src/features/**/*.ts, not
4
+ // demo/src/... or the dev-server's own template strings.
5
+
6
+ import { describe, expect, test } from "bun:test";
7
+ import { validateBoot } from "@cosmicdrift/kumiko-framework/engine";
8
+ import {
9
+ createDemoTasksFeature,
10
+ renderDemoTasksFeatureFile,
11
+ renderDemoTasksI18n,
12
+ } from "../scaffold-demo-tasks";
13
+
14
+ describe("demo tasks feature nav labels are i18n keys (#2065)", () => {
15
+ test("boot-validates with the tasks feature registered", () => {
16
+ expect(() => validateBoot([createDemoTasksFeature()])).not.toThrow();
17
+ });
18
+
19
+ test("nav labels use tasks: i18n keys, not raw text", () => {
20
+ const feature = createDemoTasksFeature();
21
+ expect(feature.navs["tasks"]?.label).toBe("tasks:nav.tasks");
22
+ expect(feature.navs["task-new"]?.label).toBe("tasks:nav.taskNew");
23
+ expect(feature.translations?.["tasks:nav.tasks"]).toEqual({ de: "Aufgaben", en: "Tasks" });
24
+ expect(feature.translations?.["tasks:nav.taskNew"]).toEqual({
25
+ de: "Neue Aufgabe",
26
+ en: "New task",
27
+ });
28
+ });
29
+
30
+ test("renderDemoTasksFeatureFile() emits i18n keys, not raw nav labels", () => {
31
+ const rendered = renderDemoTasksFeatureFile();
32
+ expect(rendered).not.toContain('label: "Tasks"');
33
+ expect(rendered).not.toContain('label: "New task"');
34
+ expect(rendered).toContain('label: "tasks:nav.tasks"');
35
+ expect(rendered).toContain('label: "tasks:nav.taskNew"');
36
+ });
37
+
38
+ test("renderDemoTasksI18n() bundles both nav-label keys for server + client", () => {
39
+ const rendered = renderDemoTasksI18n();
40
+ expect(rendered).toContain('"tasks:nav.tasks": { de: "Aufgaben", en: "Tasks" }');
41
+ expect(rendered).toContain('"tasks:nav.taskNew": { de: "Neue Aufgabe", en: "New task" }');
42
+ expect(rendered).toContain('"tasks:nav.tasks": "Aufgaben"');
43
+ expect(rendered).toContain('"tasks:nav.taskNew": "Neue Aufgabe"');
44
+ expect(rendered).toContain('"tasks:nav.tasks": "Tasks"');
45
+ expect(rendered).toContain('"tasks:nav.taskNew": "New task"');
46
+ });
47
+ });
@@ -761,7 +761,12 @@ export async function createKumikoServer(
761
761
  // dev-server injiziert das in jede HTML-Response. Re-build NICHT
762
762
  // bei Hot-Reload weil sich Feature-Defs nur über einen restart
763
763
  // ändern.
764
- const appSchemaJson = JSON.stringify(buildAppSchema(stack.registry, { authoringWarnings: true }));
764
+ const appSchemaJson = JSON.stringify(
765
+ buildAppSchema(stack.registry, {
766
+ authoringWarnings: true,
767
+ searchAdapterMissing: !stack.context.searchAdapter,
768
+ }),
769
+ );
765
770
 
766
771
  // --- SSE reload ---
767
772
  // bootId identifiziert diese spezifische Server-Process-Instanz. Wird
@@ -52,6 +52,8 @@ const open = { access: { openToAll: true } } as const;
52
52
  const TASKS_I18N = {
53
53
  "screen:task-list.title": { de: "Aufgaben", en: "Tasks" },
54
54
  "screen:task-edit.title": { de: "Aufgabe", en: "Task" },
55
+ "tasks:nav.tasks": { de: "Aufgaben", en: "Tasks" },
56
+ "tasks:nav.taskNew": { de: "Neue Aufgabe", en: "New task" },
55
57
  "tasks:entity:task:field:title": { de: "Titel", en: "Title" },
56
58
  "tasks:entity:task:field:status": { de: "Status", en: "Status" },
57
59
  "tasks:entity:task:field:priority": { de: "Priorität", en: "Priority" },
@@ -70,10 +72,10 @@ export function createDemoTasksFeature(): FeatureDefinition {
70
72
  r.queryHandler(defineEntityDetailHandler("task", taskEntity, open));
71
73
  r.screen(listScreen);
72
74
  r.screen(editScreen);
73
- r.nav({ id: "tasks", label: "Tasks", order: 10, screen: "tasks:screen:task-list" });
75
+ r.nav({ id: "tasks", label: "tasks:nav.tasks", order: 10, screen: "tasks:screen:task-list" });
74
76
  r.nav({
75
77
  id: "task-new",
76
- label: "New task",
78
+ label: "tasks:nav.taskNew",
77
79
  parent: "tasks:nav:tasks",
78
80
  screen: "tasks:screen:task-edit",
79
81
  order: 10,
@@ -142,10 +144,10 @@ export const tasksFeature = defineFeature("tasks", (r) => {
142
144
  r.queryHandler(defineEntityDetailHandler("task", taskEntity, open));
143
145
  r.screen(listScreen);
144
146
  r.screen(editScreen);
145
- r.nav({ id: "tasks", label: "Tasks", order: 10, screen: "tasks:screen:task-list" });
147
+ r.nav({ id: "tasks", label: "tasks:nav.tasks", order: 10, screen: "tasks:screen:task-list" });
146
148
  r.nav({
147
149
  id: "task-new",
148
- label: "New task",
150
+ label: "tasks:nav.taskNew",
149
151
  parent: "tasks:nav:tasks",
150
152
  screen: "tasks:screen:task-edit",
151
153
  order: 10,
@@ -162,6 +164,8 @@ import type { TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
162
164
  export const tasksTranslationKeys = {
163
165
  "screen:task-list.title": { de: "Aufgaben", en: "Tasks" },
164
166
  "screen:task-edit.title": { de: "Aufgabe", en: "Task" },
167
+ "tasks:nav.tasks": { de: "Aufgaben", en: "Tasks" },
168
+ "tasks:nav.taskNew": { de: "Neue Aufgabe", en: "New task" },
165
169
  "tasks:entity:task:field:title": { de: "Titel", en: "Title" },
166
170
  "tasks:entity:task:field:status": { de: "Status", en: "Status" },
167
171
  "tasks:entity:task:field:priority": { de: "Priorität", en: "Priority" },
@@ -172,6 +176,8 @@ export const tasksTranslations: TranslationsByLocale = {
172
176
  de: {
173
177
  "screen:task-list.title": "Aufgaben",
174
178
  "screen:task-edit.title": "Aufgabe",
179
+ "tasks:nav.tasks": "Aufgaben",
180
+ "tasks:nav.taskNew": "Neue Aufgabe",
175
181
  "tasks:entity:task:field:title": "Titel",
176
182
  "tasks:entity:task:field:status": "Status",
177
183
  "tasks:entity:task:field:priority": "Priorität",
@@ -180,6 +186,8 @@ export const tasksTranslations: TranslationsByLocale = {
180
186
  en: {
181
187
  "screen:task-list.title": "Tasks",
182
188
  "screen:task-edit.title": "Task",
189
+ "tasks:nav.tasks": "Tasks",
190
+ "tasks:nav.taskNew": "New task",
183
191
  "tasks:entity:task:field:title": "Title",
184
192
  "tasks:entity:task:field:status": "Status",
185
193
  "tasks:entity:task:field:priority": "Priority",