Superstruct structs with coercion, defaults and refinements: working versions (tested 2026-10-15)
Validate and coerce untrusted input with Superstruct 2: string to number coercion, defaulted fields, custom refinements, Infer types and StructError failure paths.
passed4 passed · 0 failed2026-10-15 17:11 UTC- Status
- passed
- Last passed
- 2026-10-15 17:11 UTC
- Tests
- 4 / 4
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions3
superstruct@2.0.2@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38775053802
The code
Exactly the files that passed. Your agent can pull them with get_recipe("superstruct-coercion-validation") (install), or see them on GitHub.
package.json
{
"name": "superstruct-coercion-validation",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"superstruct": "2.0.2"
},
"devDependencies": {
"@types/node": "26.6.5",
"tsx": "4.23.15"
}
}
src/order.ts
import {
array, assert, coerce, create, defaulted, enums, integer, is, min, object, optional,
refine, size, string, number, trimmed, StructError, type Infer,
} from "superstruct";
const Email = refine(trimmed(string()), "email", (v) => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v) || `"${v}" is not an email`);
const IntFromString = coerce(integer(), string(), (v) => Number.parseInt(v, 10));
const NumFromString = coerce(number(), string(), (v) => Number(v));
export const LineItem = object({
sku: size(string(), 3, 20),
qty: min(IntFromString, 1),
price: min(NumFromString, 0),
});
export const Order = object({
email: Email,
shipping: defaulted(enums(["standard", "express"]), "standard"),
coupon: optional(string()),
items: size(array(LineItem), 1, 50),
});
export type Order = Infer<typeof Order>;
export function parseOrder(input: unknown): Order {
return create(input, Order);
}
export function orderTotal(order: Order): number {
return Math.round(order.items.reduce((s, i) => s + i.qty * i.price, 0) * 100) / 100;
}
export function explain(input: unknown): string[] {
try {
create(input, Order);
return [];
} catch (e) {
if (!(e instanceof StructError)) throw e;
return e.failures().map((f) => `${f.path.join(".")}: ${f.refinement ?? f.type}`);
}
}
export { assert, is };
test/order.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { StructError } from "superstruct";
import { Order, parseOrder, orderTotal, explain, is } from "../src/order.ts";
test("coerces form-style strings and applies defaults", () => {
const order = parseOrder({ email: " [email protected] ", items: [{ sku: "BOLT-1", qty: "3", price: "2.50" }] });
assert.equal(order.email, "[email protected]");
assert.equal(order.shipping, "standard");
assert.equal(order.items[0].qty, 3);
assert.equal(orderTotal(order), 7.5);
});
test("is() checks without coercion", () => {
assert.equal(is({ email: "[email protected]", shipping: "express", items: [{ sku: "ABC", qty: 1, price: 1 }] }, Order), true);
assert.equal(is({ email: "[email protected]", shipping: "express", items: [{ sku: "ABC", qty: "1", price: 1 }] }, Order), false);
});
test("failures report path and refinement", () => {
const msgs = explain({ email: "nope", shipping: "drone", items: [{ sku: "X", qty: "0", price: "-1" }] });
assert.ok(msgs.includes("email: email"), msgs.join(", "));
assert.ok(msgs.includes("shipping: enums"), msgs.join(", "));
assert.ok(msgs.includes("items.0.sku: size"), msgs.join(", "));
assert.ok(msgs.includes("items.0.qty: min"), msgs.join(", "));
});
test("throws StructError for empty items", () => {
assert.throws(() => parseOrder({ email: "[email protected]", items: [] }), StructError);
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.