release: v1.32.0 — hardening and scalability (#580)

* release: prepare v1.32.0

* test: stabilize water capture re-entry
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-07-22 04:14:15 +02:00
1 parent b01822be48
commit 62eb431d66
372 files changed
+25178 -8561

No files matched your search

@@ -0,0 +1,116 @@
import { describe, expect, it } from "vitest";
import { ApiError } from "../api-fetch";
import { localizedApiError } from "../localized-error";
import de from "../../../../messages/de.json";
import en from "../../../../messages/en.json";
import es from "../../../../messages/es.json";
import fr from "../../../../messages/fr.json";
import itCatalog from "../../../../messages/it.json";
import pl from "../../../../messages/pl.json";
type Catalog = Record<string, unknown>;
function translator(catalog: Catalog) {
return (key: string): string => {
const value = key.split(".").reduce<unknown>((node, part) => {
if (!node || typeof node !== "object") return undefined;
return (node as Record<string, unknown>)[part];
}, catalog);
return typeof value === "string" ? value : key;
};
}
const catalog = {
measurements: { saveError: "Generic measurement failure" },
mood: { saveError: "Generic mood failure" },
apiErrors: {
measurement: {
create: { invalid: "Localized invalid measurement" },
duplicate_timestamp: "Localized duplicate measurement",
},
mood: {
create: { invalid: "Localized invalid mood" },
not_found: "Localized missing mood",
},
},
};
describe("localizedApiError", () => {
it("maps a stable hierarchical error code to localized copy", () => {
const error = new ApiError("Validation failed", 422, {
errorCode: "measurement.create.invalid",
});
expect(
localizedApiError(error, translator(catalog), "measurements.saveError"),
).toBe("Localized invalid measurement");
});
it("never exposes the English server message for a handled code", () => {
const error = new ApiError(
"A mood entry with this data already exists",
409,
{ errorCode: "mood.not_found" },
);
expect(
localizedApiError(error, translator(catalog), "mood.saveError"),
).toBe("Localized missing mood");
});
it.each([
new ApiError("raw database failure", 500),
new ApiError("future server prose", 409, {
errorCode: "mood.future.unknown",
}),
new TypeError("network details"),
])("uses the safe generic fallback for %s", (error) => {
expect(
localizedApiError(error, translator(catalog), "mood.saveError"),
).toBe("Generic mood failure");
});
it.each([
["de", de],
["en", en],
["es", es],
["fr", fr],
["it", itCatalog],
["pl", pl],
])(
"resolves canonical codes in %s without exposing server prose",
(_, locale) => {
const error = new ApiError("raw English server prose", 422, {
errorCode: "measurement.create.invalid",
});
const message = localizedApiError(
error,
translator(locale as Catalog),
"measurements.saveError",
);
expect(message).not.toBe("raw English server prose");
expect(message).not.toBe("apiErrors.measurement.create.invalid");
},
);
it.each([
["de", de],
["en", en],
["es", es],
["fr", fr],
["it", itCatalog],
["pl", pl],
])("falls back safely in %s when no localized code exists", (_, locale) => {
const error = new ApiError("raw English server prose", 409, {
errorCode: "future.unknown",
});
expect(
localizedApiError(
error,
translator(locale as Catalog),
"measurements.saveError",
),
).toBe(translator(locale as Catalog)("measurements.saveError"));
});
});
+25
View File
@@ -0,0 +1,25 @@
import { ApiError } from "./api-fetch";
type Translate = (key: string) => string;
/**
* Resolve an API envelope's stable error code through the locale catalog.
* Missing/unknown codes, network failures, and non-API errors deliberately
* collapse to the caller's safe domain fallback; server prose is never shown.
*/
export function localizedApiError(
error: unknown,
t: Translate,
fallbackKey: string,
): string {
if (error instanceof ApiError) {
const errorCode = error.meta?.errorCode;
if (typeof errorCode === "string") {
const key = `apiErrors.${errorCode}`;
const translated = t(key);
if (translated !== key) return translated;
}
}
return t(fallbackKey);
}