release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-02 23:18:51 +05:30
parent 87507edf59
commit 586a6db8ff
625 changed files with 243608 additions and 11210 deletions
@@ -0,0 +1,70 @@
import { describe, expect, test } from "bun:test";
import { ContractRegistry, checkContractCompatibility, defineEvent, v } from "../src/index.ts";
describe("boundary contract registry", () => {
test("registers typed, deterministic multi-boundary snapshots", () => {
const registry = new ContractRegistry()
.register(
defineEvent({
name: "user.created",
version: 1,
payload: v.object({ id: v.string().uuid() }),
}),
)
.register({
kind: "queue",
name: "email.send",
version: 1,
payload: v.object({ to: v.string().email() }),
});
expect(registry.snapshot().contracts.map((item) => `${item.kind}:${item.name}`)).toEqual([
"pubsub:user.created",
"queue:email.send",
]);
});
test("rejects duplicate identities", () => {
const registry = new ContractRegistry();
const contract = {
kind: "api",
name: "users",
version: 1,
payload: v.object({ id: v.string() }),
} as const;
registry.register(contract);
expect(() => registry.register(contract)).toThrow("Duplicate contract");
});
test("reports breaking fields, types, requirements, rules, and consumers", () => {
const previous = new ContractRegistry()
.register({
kind: "webhook",
name: "invoice.paid",
version: 1,
consumers: ["accounting", "email"],
payload: v.object({
email: v.string().optional(),
count: v.number().min(0),
legacy: v.string(),
}),
})
.snapshot();
const current = new ContractRegistry()
.register({
kind: "webhook",
name: "invoice.paid",
version: 1,
payload: v.object({ email: v.string(), count: v.string().min(2), added: v.string() }),
})
.snapshot();
const issues = checkContractCompatibility(previous, current);
expect(issues.map((issue) => issue.code)).toEqual([
"WRN-CONTRACT-FIELD-REQUIRED",
"WRN-CONTRACT-FIELD-TYPE",
"WRN-CONTRACT-RULE-TIGHTENED",
"WRN-CONTRACT-FIELD-REMOVED",
"WRN-CONTRACT-FIELD-REQUIRED",
]);
expect(issues.every((issue) => issue.consumers.includes("accounting"))).toBe(true);
});
});
+58
View File
@@ -0,0 +1,58 @@
import { describe, expect, test } from "bun:test";
import {
ValidationError,
firstValidationError,
parseOrThrow,
validationResponse,
validationSummary,
v,
type InferSchema,
type ObjectSchema,
} from "../src/index.ts";
describe("validation helper kit", () => {
test("throws a structured validation error", () => {
const schema = v.object({ email: v.string().email() });
expect(() => parseOrThrow(schema, { email: "invalid" })).toThrow(ValidationError);
const value = parseOrThrow(schema, { email: "user@example.com" });
const email: string = value.email;
expect(value).toEqual({ email });
});
test("preserves literal unions from oneOf", () => {
interface ContactInput {
topic: "general" | "security" | "billing" | "integration";
priority: 1 | 2 | 3;
}
const schema: ObjectSchema<ContactInput> = v.object({
topic: v.string().oneOf(["general", "security", "billing", "integration"]),
priority: v.number().oneOf([1, 2, 3]),
});
const value = parseOrThrow(schema, { topic: "security", priority: 2 });
const topic: ContactInput["topic"] = value.topic;
const priority: ContactInput["priority"] = value.priority;
expect({ topic, priority }).toEqual({ topic: "security", priority: 2 });
const _inferredSchema = v.object({
topic: v.string().oneOf(["general", "security"]),
});
type Inferred = InferSchema<typeof _inferredSchema>;
const inferredTopic: Inferred["topic"] = "general";
expect(inferredTopic).toBe("general");
const dynamicOptions: string[] = ["draft", "published"];
const dynamicSchema = v.object({ status: v.string().oneOf(dynamicOptions) });
const dynamicValue: string = parseOrThrow(dynamicSchema, {
status: "draft",
}).status;
expect(dynamicValue).toBe("draft");
});
test("normalizes error summaries and HTTP responses", async () => {
const errors = { email: "Email is invalid", password: "Password is required" };
expect(firstValidationError(errors)).toBe("Email is invalid");
expect(validationSummary(errors)).toHaveLength(2);
expect((await validationResponse({ ok: false, value: {}, errors }).json()).ok).toBe(false);
});
});
+61
View File
@@ -0,0 +1,61 @@
import { expect, test } from "bun:test";
import {
localizeDescriptor,
openApiRequestBody,
parseDescriptor,
toJsonSchema,
v,
} from "../src/index.ts";
const contact = v.object({
email: v.string().trim().email().label("Email address"),
age: v.number().integer().min(18),
role: v
.string()
.oneOf(["user", "admin"] as const)
.optional(),
});
test("exports standards-compatible JSON Schema and OpenAPI request bodies", () => {
expect(toJsonSchema(contact, { id: "urn:wrn:contact", title: "Contact" })).toEqual({
$schema: "https://json-schema.org/draft/2020-12/schema",
$id: "urn:wrn:contact",
title: "Contact",
type: "object",
properties: {
email: { type: "string", title: "Email address", "x-wrn-trim": true, format: "email" },
age: { type: "integer", minimum: 18 },
role: { type: "string", enum: ["user", "admin"] },
},
required: ["email", "age"],
additionalProperties: false,
});
const body = openApiRequestBody(contact, {
description: "Contact input",
contentTypes: ["application/json", "application/x-www-form-urlencoded"],
});
expect(body.required).toBe(true);
expect(Object.keys(body.content)).toEqual([
"application/json",
"application/x-www-form-urlencoded",
]);
});
test("localized descriptors produce the same server/browser-ready errors", () => {
const messages: Record<string, string> = {
required: "{label} आवश्यक आहे",
number: "{label} संख्या असणे आवश्यक आहे",
"rule.email": "वैध ईमेल द्या",
"rule.integer": "पूर्णांक द्या",
"rule.min": "किमान {n}",
"rule.oneOf": "परवानगी असलेले मूल्य द्या",
};
const descriptor = localizeDescriptor(contact, (key, params) =>
(messages[key] ?? key).replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? "")),
);
expect(parseDescriptor(descriptor, { email: "bad", age: "nope" }).errors).toEqual({
email: "वैध ईमेल द्या",
age: "age संख्या असणे आवश्यक आहे",
});
expect(parseDescriptor(descriptor, {}).errors.email).toBe("Email address आवश्यक आहे");
});