@cosmicdrift/kumiko-renderer 0.82.0 → 0.84.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-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"description": "Platform-agnostic React renderer for Kumiko screens. Contains the shared logic — primitives-contract, hooks, KumikoScreen, navigation & SSE abstractions — that any platform-specific renderer (web, native) composes. No DOM, no EventSource, no react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
19
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
18
|
+
"@cosmicdrift/kumiko-framework": "0.84.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.84.0",
|
|
20
20
|
"react": "^19.2.6"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
AccessDeniedError,
|
|
4
|
+
ConflictError,
|
|
5
|
+
FeatureDisabledError,
|
|
6
|
+
InternalError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
RateLimitError,
|
|
9
|
+
UnconfiguredError,
|
|
10
|
+
UniqueViolationError,
|
|
11
|
+
UnprocessableError,
|
|
12
|
+
ValidationError,
|
|
13
|
+
VersionConflictError,
|
|
14
|
+
} from "@cosmicdrift/kumiko-framework/errors";
|
|
15
|
+
import { kumikoDefaultTranslations } from "../i18n-defaults";
|
|
16
|
+
|
|
17
|
+
// Keys are read off LIVE error instances, not a hardcoded list — a copied list
|
|
18
|
+
// would mirror the bundle and never catch a renamed i18nKey or a new error
|
|
19
|
+
// class shipped without a default translation. Ship an 11th error class (or
|
|
20
|
+
// rename a key) without adding the default → this fails.
|
|
21
|
+
const errorInstances = [
|
|
22
|
+
new InternalError(),
|
|
23
|
+
new AccessDeniedError(),
|
|
24
|
+
new NotFoundError("Thing"),
|
|
25
|
+
new ConflictError(),
|
|
26
|
+
new VersionConflictError({ entityId: 1, expectedVersion: 1, currentVersion: 2 }),
|
|
27
|
+
new UniqueViolationError({ entityName: "Thing" }),
|
|
28
|
+
new UnprocessableError("reason"),
|
|
29
|
+
new UnconfiguredError({ feature: "f", key: "k" }),
|
|
30
|
+
new FeatureDisabledError("f", "h"),
|
|
31
|
+
new RateLimitError({
|
|
32
|
+
bucket: "b",
|
|
33
|
+
limit: 1,
|
|
34
|
+
windowSeconds: 1,
|
|
35
|
+
remaining: 0,
|
|
36
|
+
retryAfterSeconds: 1,
|
|
37
|
+
resetAt: "1970-01-01T00:00:00Z",
|
|
38
|
+
}),
|
|
39
|
+
new ValidationError({ fields: [] }),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const de = kumikoDefaultTranslations["de"];
|
|
43
|
+
const en = kumikoDefaultTranslations["en"];
|
|
44
|
+
|
|
45
|
+
describe("kumikoDefaultTranslations covers every error i18nKey", () => {
|
|
46
|
+
for (const e of errorInstances) {
|
|
47
|
+
test(`${e.code} → ${e.i18nKey} has de+en default`, () => {
|
|
48
|
+
expect(de?.[e.i18nKey]).toBeTruthy();
|
|
49
|
+
expect(en?.[e.i18nKey]).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Client-emitted (renderer-web download helper) — not thrown by a class, but
|
|
54
|
+
// rendered through the same last-resort bundle. Gap #5 named it explicitly.
|
|
55
|
+
test("errors.download.urlMissing has de+en default", () => {
|
|
56
|
+
expect(de?.["errors.download.urlMissing"]).toBeTruthy();
|
|
57
|
+
expect(en?.["errors.download.urlMissing"]).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
});
|
package/src/i18n-defaults.ts
CHANGED
|
@@ -101,6 +101,23 @@ export const kumikoDefaultTranslations: TranslationsByLocale = {
|
|
|
101
101
|
"errors.validation.out_of_bounds": "Wert außerhalb des erlaubten Bereichs.",
|
|
102
102
|
"errors.validation.invalid_option": "Ungültige Auswahl.",
|
|
103
103
|
"errors.validation.failed": "Validierung fehlgeschlagen.",
|
|
104
|
+
|
|
105
|
+
// errors.* — Top-Level-Error-Codes (eine pro httpStatus-Klasse). Letzter
|
|
106
|
+
// Fallback wenn eine App keine eigene Übersetzung liefert; bewusst
|
|
107
|
+
// generisch (keine technischen Entity-/Feature-/Key-Namen an End-User —
|
|
108
|
+
// die stecken in `details` für Devs). Apps überschreiben pro Key.
|
|
109
|
+
"errors.feature.disabled": "Diese Funktion ist derzeit nicht verfügbar.",
|
|
110
|
+
"errors.access.denied": "Dazu hast du keine Berechtigung.",
|
|
111
|
+
"errors.notFound": "Nicht gefunden.",
|
|
112
|
+
"errors.conflict": "Konflikt — der Vorgang konnte nicht abgeschlossen werden.",
|
|
113
|
+
"errors.versionConflict":
|
|
114
|
+
"Der Datensatz wurde zwischenzeitlich geändert. Lade neu und versuche es erneut.",
|
|
115
|
+
"errors.uniqueViolation": "Dieser Eintrag existiert bereits.",
|
|
116
|
+
"errors.unprocessable": "Die Anfrage konnte nicht verarbeitet werden.",
|
|
117
|
+
"errors.unconfigured": "Diese Funktion ist noch nicht konfiguriert.",
|
|
118
|
+
"errors.internal": "Etwas ist schiefgegangen. Bitte versuche es später erneut.",
|
|
119
|
+
"errors.rate_limited": "Zu viele Anfragen. Bitte versuche es in Kürze erneut.",
|
|
120
|
+
"errors.download.urlMissing": "Download nicht verfügbar — bitte versuche es erneut.",
|
|
104
121
|
},
|
|
105
122
|
en: {
|
|
106
123
|
"kumiko.actions.save": "Save",
|
|
@@ -174,5 +191,17 @@ export const kumikoDefaultTranslations: TranslationsByLocale = {
|
|
|
174
191
|
"errors.validation.out_of_bounds": "Value out of allowed range.",
|
|
175
192
|
"errors.validation.invalid_option": "Invalid choice.",
|
|
176
193
|
"errors.validation.failed": "Validation failed.",
|
|
194
|
+
|
|
195
|
+
"errors.feature.disabled": "This feature is currently unavailable.",
|
|
196
|
+
"errors.access.denied": "You don't have permission to do this.",
|
|
197
|
+
"errors.notFound": "Not found.",
|
|
198
|
+
"errors.conflict": "Conflict — the operation could not be completed.",
|
|
199
|
+
"errors.versionConflict": "The record was modified in the meantime. Reload and try again.",
|
|
200
|
+
"errors.uniqueViolation": "This entry already exists.",
|
|
201
|
+
"errors.unprocessable": "The request could not be processed.",
|
|
202
|
+
"errors.unconfigured": "This feature isn't configured yet.",
|
|
203
|
+
"errors.internal": "Something went wrong. Please try again later.",
|
|
204
|
+
"errors.rate_limited": "Too many requests. Please try again shortly.",
|
|
205
|
+
"errors.download.urlMissing": "Download unavailable — please try again.",
|
|
177
206
|
},
|
|
178
207
|
};
|