3.7 KiB
registry.schemas
Purpose
Strict-object Zod schemas for every plugin kind accepted by ReactI18nRegistry. The schemas are the boundary at which a malformed plugin shape is rejected at registration time — before any callsite has a chance to consume the bad plugin and throw a less-actionable runtime error elsewhere. Each schema is .strict()-locked so extra fields on a plugin (typos, stale fields from an older registry version) trigger validation failure rather than silently passing through.
Features
- One schema per plugin kind —
DetectorPluginSchema,FormatterPluginSchema,PostProcessorPluginSchema,LocaleAdapterPluginSchema. - Non-empty id enforced (
z.string().min(1)) so every plugin has a stable lookup key. - Function-shape fields validated structurally; their semantic correctness is exercised by the tests of each consumer (e.g.
useFormattertest calls the registered plugin'sformatand asserts the output). - Schemas surface failure as a Zod
ZodError, which the registry wraps in aReactI18nError(REACT_I18N.PLUGIN_INVALID)with the issues attached tocause.
Usage: Basic Example
import { FormatterPluginSchema } from "@suaveplan/react-i18n/registry";
const result = FormatterPluginSchema.safeParse({
id: "duration",
format: (locale, args) => String(args.ms),
});
result.success; // true
Usage: Advanced Example
The schemas accept generic format / process / resolve / detect functions because their signatures cannot be expressed as a closed Zod shape. The schema verifies the function is present and is a function; the actual (locale, args) => result contract is enforced at the type-system level via FormatterPlugin<TArgs, TResult> and at the test level by exercising the plugin's behavior.
import { DetectorPluginSchema } from "@suaveplan/react-i18n/registry";
const malformed = {
id: "ab",
priority: 0,
detect: { name: "ab" /* missing environment */ },
};
const result = DetectorPluginSchema.safeParse(malformed);
result.success; // false
result.error.issues; // [{ path: ["detect", "environment"], message: "Required" }]
API Reference
DetectorPluginSchema
Validates { id: string; priority: number; detect: { name; environment; detect?; detectAsync?; cache? } }. The inner detect object is passthrough because LocaleDetector from @suaveplan/i18n-detector may carry forward-compatible fields the registry does not own.
FormatterPluginSchema
Validates { id: string; schema?: unknown; defaultOptions?: unknown; format: function }. The schema field is opaque (z.unknown()) because it is a Zod schema reference and Zod cannot meaningfully validate "is this a Zod schema."
PostProcessorPluginSchema
Validates { id: string; priority: number; process: function }.
LocaleAdapterPluginSchema
Validates { id: string; resolve: function }.
Implementation Notes
- All schemas are
.strict()— extra keys cause validation failure. This catches stale fields from older registry versions and typos in plugin objects. - The schemas are exported from a single file rather than co-located with their plugin types so a consumer importing
@suaveplan/react-i18n/registryfor runtime validation does not pay the bundle cost of the plugin function code. - Validation errors are passed through to the registry which decorates them with stable error codes; consumers should never
safeParsedirectly against these schemas — register throughReactI18nRegistry.register*to get the decorated error. - The schemas live in this package rather than in
@suaveplan/typesbecause they validate React-specific plugin shapes; theReactI18nRegistryis itself React-bound (consumed via context).