Skip to main content
Glama
codec-examples.test.ts17.4 kB
import { expect, test } from "vitest"; import * as z from "zod/v4"; // ============================================================================ // Number/BigInt Codecs // ============================================================================ const stringToNumber = () => z.codec(z.string(), z.number(), { decode: (str) => Number.parseFloat(str), encode: (num) => num.toString(), }); const stringToInt = () => z.codec(z.string(), z.int(), { decode: (str) => Number.parseInt(str, 10), encode: (num) => num.toString(), }); const stringToBigInt = () => z.codec(z.string(), z.bigint(), { decode: (str) => BigInt(str), encode: (bigint) => bigint.toString(), }); const numberToBigInt = () => z.codec(z.int(), z.bigint(), { decode: (num) => BigInt(num), encode: (bigint) => Number(bigint), }); // ============================================================================ // Date/Duration Codecs // ============================================================================ const isoDatetimeToDate = () => z.codec(z.iso.datetime(), z.date(), { decode: (isoString) => new Date(isoString), encode: (date) => date.toISOString(), }); const epochSecondsToDate = () => z.codec(z.int().min(0), z.date(), { decode: (seconds) => new Date(seconds * 1000), encode: (date) => Math.floor(date.getTime() / 1000), }); const epochMillisToDate = () => z.codec(z.int().min(0), z.date(), { decode: (millis) => new Date(millis), encode: (date) => date.getTime(), }); // ============================================================================ // JSON Codec // ============================================================================ const json = <T extends z.ZodTypeAny>(schema: T) => z.codec(z.string(), schema, { decode: (jsonString) => JSON.parse(jsonString), encode: (value) => JSON.stringify(value), }); // ============================================================================ // Text/Bytes Codecs // ============================================================================ const utf8ToBytes = () => z.codec(z.string(), z.instanceof(Uint8Array), { decode: (str) => new TextEncoder().encode(str), encode: (bytes) => new TextDecoder().decode(bytes), }); const bytesToUtf8 = () => z.codec(z.instanceof(Uint8Array), z.string(), { decode: (bytes) => new TextDecoder().decode(bytes), encode: (str) => new TextEncoder().encode(str), }); // ============================================================================ // Binary-to-text Codecs // ============================================================================ // Using utility functions from z.core.util const base64 = () => z.codec(z.base64(), z.instanceof(Uint8Array), { decode: (base64String) => z.core.util.base64ToUint8Array(base64String), encode: (bytes) => z.core.util.uint8ArrayToBase64(bytes), }); const base64urlToBytes = () => z.codec(z.base64url(), z.instanceof(Uint8Array), { decode: (base64urlString) => z.core.util.base64urlToUint8Array(base64urlString), encode: (bytes) => z.core.util.uint8ArrayToBase64url(bytes), }); const hexToBytes = () => z.codec(z.hex(), z.instanceof(Uint8Array), { decode: (hexString) => z.core.util.hexToUint8Array(hexString), encode: (bytes) => z.core.util.uint8ArrayToHex(bytes), }); // ============================================================================ // URL Codecs // ============================================================================ const stringToURL = () => z.codec(z.url(), z.instanceof(URL), { decode: (urlString) => new URL(urlString), encode: (url) => url.href, }); const stringToHttpURL = () => z.codec(z.httpUrl(), z.instanceof(URL), { decode: (urlString) => new URL(urlString), encode: (url) => url.href, }); const uriComponent = () => z.codec(z.string(), z.string(), { decode: (encodedString) => decodeURIComponent(encodedString), encode: (decodedString) => encodeURIComponent(decodedString), }); // ============================================================================ // Boolean Codec // ============================================================================ const stringToBoolean = (options?: { truthy?: string[]; falsy?: string[] }) => z.stringbool(options); // ============================================================================ // Tests // ============================================================================ test("stringToNumber codec", () => { const codec = stringToNumber(); // Test decode expect(z.decode(codec, "42.5")).toBe(42.5); expect(z.decode(codec, "0")).toBe(0); expect(z.decode(codec, "-123.456")).toBe(-123.456); // Test encode expect(z.encode(codec, 42.5)).toBe("42.5"); expect(z.encode(codec, 0)).toBe("0"); expect(z.encode(codec, -123.456)).toBe("-123.456"); // Test round trip const original = "3.14159"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("3.14159"); }); test("stringToInt codec", () => { const codec = stringToInt(); // Test decode expect(z.decode(codec, "42")).toBe(42); expect(z.decode(codec, "0")).toBe(0); expect(z.decode(codec, "-123")).toBe(-123); // Test encode expect(z.encode(codec, 42)).toBe("42"); expect(z.encode(codec, 0)).toBe("0"); expect(z.encode(codec, -123)).toBe("-123"); // Test round trip const original = "999"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("999"); }); test("stringToBigInt codec", () => { const codec = stringToBigInt(); // Test decode expect(z.decode(codec, "123456789012345678901234567890")).toBe(123456789012345678901234567890n); expect(z.decode(codec, "0")).toBe(0n); expect(z.decode(codec, "-999")).toBe(-999n); // Test encode expect(z.encode(codec, 123456789012345678901234567890n)).toBe("123456789012345678901234567890"); expect(z.encode(codec, 0n)).toBe("0"); expect(z.encode(codec, -999n)).toBe("-999"); // Test round trip const original = "987654321098765432109876543210"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("987654321098765432109876543210"); }); test("numberToBigInt codec", () => { const codec = numberToBigInt(); // Test decode expect(z.decode(codec, 42)).toBe(42n); expect(z.decode(codec, 0)).toBe(0n); expect(z.decode(codec, -123)).toBe(-123n); // Test encode expect(z.encode(codec, 42n)).toBe(42); expect(z.encode(codec, 0n)).toBe(0); expect(z.encode(codec, -123n)).toBe(-123); // Test round trip const original = 999; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(999); }); test("isoDatetimeToDate codec", () => { const codec = isoDatetimeToDate(); // Test decode const decoded = z.decode(codec, "2024-01-15T10:30:00.000Z"); expect(decoded).toBeInstanceOf(Date); expect(decoded.getTime()).toBe(1705314600000); // Test encode const date = new Date("2024-01-15T10:30:00.000Z"); expect(z.encode(codec, date)).toBe("2024-01-15T10:30:00.000Z"); // Test round trip const original = "2024-12-25T15:45:30.123Z"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("2024-12-25T15:45:30.123Z"); }); test("epochSecondsToDate codec", () => { const codec = epochSecondsToDate(); // Test decode const decoded = z.decode(codec, 1705314600); expect(decoded).toBeInstanceOf(Date); expect(decoded.getTime()).toBe(1705314600000); // Test encode const date = new Date(1705314600000); expect(z.encode(codec, date)).toBe(1705314600); // Test round trip const original = 1640995200; // 2022-01-01 00:00:00 UTC const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(1640995200); }); test("epochMillisToDate codec", () => { const codec = epochMillisToDate(); // Test decode const decoded = z.decode(codec, 1705314600000); expect(decoded).toBeInstanceOf(Date); expect(decoded.getTime()).toBe(1705314600000); // Test encode const date = new Date(1705314600000); expect(z.encode(codec, date)).toBe(1705314600000); // Test round trip const original = 1640995200123; // 2022-01-01 00:00:00.123 UTC const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(1640995200123); }); test("json codec", () => { const codec = json(z.object({ name: z.string(), age: z.number() })); // Test decode const decoded = z.decode(codec, '{"name":"Alice","age":30}'); expect(decoded).toEqual({ name: "Alice", age: 30 }); // Test encode const encoded = z.encode(codec, { name: "Bob", age: 25 }); expect(encoded).toBe('{"name":"Bob","age":25}'); // Test round trip const original = '{"name":"Charlie","age":35}'; const parsed = z.decode(codec, original); const roundTrip = z.encode(codec, parsed); expect(JSON.parse(roundTrip)).toEqual(JSON.parse(original)); }); test("utf8ToBytes codec", () => { const codec = utf8ToBytes(); // Test decode const decoded = z.decode(codec, "Hello, 世界!"); expect(decoded).toBeInstanceOf(Uint8Array); expect(Array.from(decoded)).toEqual([72, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140, 33]); // Test encode const bytes = new Uint8Array([72, 101, 108, 108, 111]); expect(z.encode(codec, bytes)).toBe("Hello"); // Test round trip const original = "Hello, 世界! 🚀"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(original); }); test("bytesToUtf8 codec", () => { const codec = bytesToUtf8(); // Test decode const bytes = new Uint8Array([72, 101, 108, 108, 111]); const decoded = z.decode(codec, bytes); expect(decoded).toBe("Hello"); // Test encode const encoded = z.encode(codec, "Hello, 世界!"); expect(encoded).toBeInstanceOf(Uint8Array); expect(Array.from(encoded)).toEqual([72, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140, 33]); // Test round trip const original = new Uint8Array([72, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140, 33]); const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toEqual(original); }); test("base64 codec", () => { const codec = base64(); // Test decode const decoded = z.decode(codec, "SGVsbG8="); expect(decoded).toBeInstanceOf(Uint8Array); expect(Array.from(decoded)).toEqual([72, 101, 108, 108, 111]); // Test encode const bytes = new Uint8Array([72, 101, 108, 108, 111]); expect(z.encode(codec, bytes)).toBe("SGVsbG8="); // Test round trip const original = "SGVsbG8gV29ybGQh"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(original); }); test("base64urlToBytes codec", () => { const codec = base64urlToBytes(); // Test decode const decoded = z.decode(codec, "SGVsbG8"); expect(decoded).toBeInstanceOf(Uint8Array); expect(Array.from(decoded)).toEqual([72, 101, 108, 108, 111]); // Test encode const bytes = new Uint8Array([72, 101, 108, 108, 111]); expect(z.encode(codec, bytes)).toBe("SGVsbG8"); // Test round trip with padding case const original = "SGVsbG9Xb3JsZA"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(original); }); test("hexToBytes codec", () => { const codec = hexToBytes(); // Test decode const decoded = z.decode(codec, "48656c6c6f"); expect(decoded).toBeInstanceOf(Uint8Array); expect(Array.from(decoded)).toEqual([72, 101, 108, 108, 111]); // Note: z.hex() doesn't accept 0x prefix, but our utility function can handle it // const decodedWithPrefix = z.decode(codec, "0x48656c6c6f"); // expect(Array.from(decodedWithPrefix)).toEqual([72, 101, 108, 108, 111]); // Test encode const bytes = new Uint8Array([72, 101, 108, 108, 111]); expect(z.encode(codec, bytes)).toBe("48656c6c6f"); // Test round trip const original = "deadbeef"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("deadbeef"); }); test("stringToURL codec", () => { const codec = stringToURL(); // Test decode const decoded = z.decode(codec, "https://example.com/path?query=value"); expect(decoded).toBeInstanceOf(URL); expect(decoded.hostname).toBe("example.com"); expect(decoded.pathname).toBe("/path"); expect(decoded.search).toBe("?query=value"); // Test encode const url = new URL("https://example.com/path?query=value"); expect(z.encode(codec, url)).toBe("https://example.com/path?query=value"); // Test round trip const original = "https://test.com/api/v1?foo=bar&baz=qux"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(original); }); test("stringToHttpURL codec", () => { const codec = stringToHttpURL(); // Test decode HTTPS const decodedHttps = z.decode(codec, "https://example.com/path"); expect(decodedHttps).toBeInstanceOf(URL); expect(decodedHttps.protocol).toBe("https:"); // Test decode HTTP const decodedHttp = z.decode(codec, "http://example.com/path"); expect(decodedHttp).toBeInstanceOf(URL); expect(decodedHttp.protocol).toBe("http:"); // Test encode const url = new URL("https://example.com/path"); expect(z.encode(codec, url)).toBe("https://example.com/path"); // Test round trip const original = "http://api.example.com/v1/users"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe(original); }); test("uriComponent codec", () => { const codec = uriComponent(); // Test decode const decoded = z.decode(codec, "Hello%20World%21"); expect(decoded).toBe("Hello World!"); // Test encode const encoded = z.encode(codec, "Hello World!"); expect(encoded).toBe("Hello%20World!"); // Test round trip const original = "Hello%20World%21%20%26%20More"; const roundTrip = z.encode(codec, z.decode(codec, original)); expect(roundTrip).toBe("Hello%20World!%20%26%20More"); // Test complex characters const complex = "café & résumé"; const encodedComplex = z.encode(codec, complex); const decodedComplex = z.decode(codec, encodedComplex); expect(decodedComplex).toBe(complex); }); test("stringToBoolean codec", () => { const codec = stringToBoolean(); // Test decode - default truthy values expect(z.decode(codec, "true")).toBe(true); expect(z.decode(codec, "yes")).toBe(true); expect(z.decode(codec, "1")).toBe(true); // Test decode - default falsy values expect(z.decode(codec, "false")).toBe(false); expect(z.decode(codec, "no")).toBe(false); expect(z.decode(codec, "0")).toBe(false); // Test encode - default behavior expect(z.encode(codec, true)).toBe("true"); expect(z.encode(codec, false)).toBe("false"); // Test custom options const customCodec = stringToBoolean({ truthy: ["yes", "y"], falsy: ["no", "n"] }); expect(z.decode(customCodec, "yes")).toBe(true); expect(z.decode(customCodec, "y")).toBe(true); expect(z.decode(customCodec, "no")).toBe(false); expect(z.decode(customCodec, "n")).toBe(false); expect(z.encode(customCodec, true)).toBe("yes"); expect(z.encode(customCodec, false)).toBe("no"); }); // Test error cases - these test input validation, not transform errors test("codec input validation", () => { // Test invalid base64 format const base64Codec = base64(); const invalidBase64Result = z.safeDecode(base64Codec, "invalid!@#"); expect(invalidBase64Result.success).toBe(false); // Test invalid hex format const hexCodec = hexToBytes(); const invalidHexResult = z.safeDecode(hexCodec, "gg"); expect(invalidHexResult.success).toBe(false); // Test invalid URL format const urlCodec = stringToURL(); const invalidUrlResult = z.safeDecode(urlCodec, "not a url"); expect(invalidUrlResult.success).toBe(false); // Test invalid HTTP URL format const httpUrlCodec = stringToHttpURL(); const invalidHttpResult = z.safeDecode(httpUrlCodec, "ftp://example.com"); expect(invalidHttpResult.success).toBe(false); }); // Test transform errors - these test errors added by transform functions test("codec transform error handling", () => { // JSON codec that can fail during transform const jsonCodec = z.codec(z.string(), z.json(), { decode: (jsonString, ctx) => { try { return JSON.parse(jsonString); } catch (err: any) { ctx.issues.push({ code: "invalid_format", format: "json", input: jsonString, message: err.message, }); return z.NEVER; } }, encode: (value) => JSON.stringify(value), }); // Test successful JSON parsing const validResult = z.safeDecode(jsonCodec, '{"valid": "json"}'); expect(validResult.success).toBe(true); if (validResult.success) { expect(validResult.data).toEqual({ valid: "json" }); } // Test invalid JSON that should create a single "invalid_format" issue // Verifies that the transform error aborts before reaching the output schema const invalidResult = z.safeDecode(jsonCodec, '{"invalid":,}'); expect(invalidResult.success).toBe(false); if (!invalidResult.success) { expect(invalidResult.error.issues).toMatchInlineSnapshot(` [ { "code": "invalid_format", "format": "json", "message": "Unexpected token ',', "{"invalid":,}" is not valid JSON", "path": [], }, ] `); } });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/narayana-reddy-circles/mcp-git-status'

If you have feedback or need assistance with the MCP directory API, please join our Discord server