@cosmicdrift/kumiko-framework 0.147.3 → 0.148.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-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.148.0",
|
|
4
4
|
"description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
"zod": "^4.4.3"
|
|
194
194
|
},
|
|
195
195
|
"devDependencies": {
|
|
196
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
196
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.148.0",
|
|
197
197
|
"bun-types": "^1.3.13",
|
|
198
198
|
"pino-pretty": "^13.1.3"
|
|
199
199
|
},
|
|
@@ -47,6 +47,38 @@ describe("buildAppSchema", () => {
|
|
|
47
47
|
expect(fleet?.entities["vehicle"]).toBeDefined();
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
// #1059: r.translations({keys}) must flow into FeatureSchema.translations
|
|
51
|
+
// BYTE-IDENTICAL — nav/screen labels resolve these keys verbatim via
|
|
52
|
+
// t(label) client-side, so any re-prefixing (like the registry's internal
|
|
53
|
+
// mergedTranslations does for features whose keys already carry a colon)
|
|
54
|
+
// would break the lookup. Mirrors cap-counter's real shape exactly: nav
|
|
55
|
+
// label references its own already-qualified translation key.
|
|
56
|
+
test("r.translations landet verbatim in FeatureSchema.translations, ohne Re-Prefixing", () => {
|
|
57
|
+
const capCounterLikeFeature = defineFeature("cap-counter", (r) => {
|
|
58
|
+
r.nav({ id: "cap-list", label: "cap-counter:nav.cap-list" });
|
|
59
|
+
r.translations({
|
|
60
|
+
keys: {
|
|
61
|
+
"cap-counter:nav.cap-list": { de: "Limits", en: "Caps" },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const app = buildAppSchema(createRegistry([capCounterLikeFeature]));
|
|
67
|
+
const feature = app.features.find((f) => f.featureName === "cap-counter");
|
|
68
|
+
|
|
69
|
+
expect(feature?.translations).toEqual({
|
|
70
|
+
"cap-counter:nav.cap-list": { de: "Limits", en: "Caps" },
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("Feature ohne r.translations lässt das Feld weg (omit-undefined-Pattern)", () => {
|
|
75
|
+
const f = defineFeature("bare", (r) => {
|
|
76
|
+
r.nav({ id: "x", label: "X" });
|
|
77
|
+
});
|
|
78
|
+
const app = buildAppSchema(createRegistry([f]));
|
|
79
|
+
expect(app.features[0]?.translations).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
50
82
|
test("Workspaces — definition + aufgelöste navMembers landen auf AppSchema-Ebene", () => {
|
|
51
83
|
const ordersFeature = defineFeature("orders", (r) => {
|
|
52
84
|
r.nav({ id: "list", label: "List" });
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
// sind plain literals)
|
|
18
18
|
// Nav: verbatim (NavDefinition ist nur strings + literals)
|
|
19
19
|
// Workspace: verbatim definition + getWorkspaceNavs() von der Registry
|
|
20
|
+
// Translations: verbatim feature.translations (raw r.translations keys,
|
|
21
|
+
// unfiltered — see FeatureSchema.translations doc)
|
|
20
22
|
//
|
|
21
23
|
// Feature-Toggles: BISHER NICHT GEFILTERT. Wenn ein Feature über die
|
|
22
24
|
// feature-toggles-bundled-feature global deaktiviert ist, erscheint es
|
|
@@ -49,6 +51,12 @@ export function buildAppSchema(registry: Registry, options: BuildAppSchemaOption
|
|
|
49
51
|
entities: projectEntities(feature.entities ?? {}),
|
|
50
52
|
screens: Object.values(feature.screens),
|
|
51
53
|
...(navs.length > 0 && { navs }),
|
|
54
|
+
// #1059: verbatim r.translations({keys}) — see FeatureSchema.translations
|
|
55
|
+
// doc for why this must NOT go through registry.getAllTranslations()
|
|
56
|
+
// (double-prefixes features that already qualify their own keys).
|
|
57
|
+
...(Object.keys(feature.translations ?? {}).length > 0 && {
|
|
58
|
+
translations: feature.translations,
|
|
59
|
+
}),
|
|
52
60
|
};
|
|
53
61
|
features.push(featureSchema);
|
|
54
62
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// web), Runtime-Helpers (toAppSchema, isAppSchema) bleiben renderer-side
|
|
8
8
|
// weil das die Layer ist die mit den AppSchemas zur Laufzeit arbeitet.
|
|
9
9
|
|
|
10
|
+
import type { TranslationKeys } from "../engine/types/config";
|
|
10
11
|
import type { EntityDefinition } from "../engine/types/fields";
|
|
11
12
|
import type { NavDefinition } from "../engine/types/nav";
|
|
12
13
|
import type { ScreenDefinition } from "../engine/types/screen";
|
|
@@ -19,6 +20,17 @@ export type FeatureSchema = {
|
|
|
19
20
|
// Flat list; resolveNavigation builds the tree at render-time from
|
|
20
21
|
// the registry's indexes. Omitted when the app has no top-level nav.
|
|
21
22
|
readonly navs?: readonly NavDefinition[];
|
|
23
|
+
// Server-authored `r.translations({ keys })`, projected verbatim — byte-
|
|
24
|
+
// identical keys, NOT re-prefixed with featureName (unlike the registry's
|
|
25
|
+
// internal mergedTranslations, which double-prefixes features that
|
|
26
|
+
// already qualify their own keys — see registry-ingest.ts populate
|
|
27
|
+
// Translations). Nav/screen labels resolve these keys directly via
|
|
28
|
+
// `t(label)`, so shipping anything other than the raw authored string
|
|
29
|
+
// would break the lookup. Omitted when a feature declares none. #1059:
|
|
30
|
+
// without this, nav labels only resolved when an app ALSO duplicated the
|
|
31
|
+
// key in `web/i18n.ts` — most bundled features never did, so labels
|
|
32
|
+
// rendered as raw i18n keys in the shell.
|
|
33
|
+
readonly translations?: TranslationKeys;
|
|
22
34
|
// Workspaces — Legacy-Slot für single-feature-Apps. Bevorzugt liegt
|
|
23
35
|
// workspaces auf der AppSchema-Ebene weil ihre navMembers regelmäßig
|
|
24
36
|
// Cross-Feature-Navs referenzieren (siehe AppSchema-Doc). Hier als
|