UNPKG

zomoc

Version:

A type-safe API mocking tool for frontend development, powered by axios and Zod.

435 lines (430 loc) 12.8 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/cli.ts var cli_exports = {}; __export(cli_exports, { createGenerator: () => createGenerator, setupMockingInterceptor: () => setupMockingInterceptor }); module.exports = __toCommonJS(cli_exports); // src/generator.ts var import_zod = require("zod"); var getRandomString = (key) => { if (key.toLowerCase().includes("password")) return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; if (key.toLowerCase().includes("url")) return `https://example.com/mock-path/${Math.random().toString(36).substring(7)}`; return `temp text: ${Math.random().toString(36).substring(7)}`; }; var getRandomNumber = () => Math.floor(1e3 + Math.random() * 9e3); var getRandomBoolean = () => Math.random() > 0.5; var getRandomDateTime = () => (/* @__PURE__ */ new Date()).toISOString(); function createMockDataFromZodSchema(schema, key = "", customGenerators, repeatCount, seed = 0, strategy = "random") { const def = schema._def ?? schema._zod?.def; if (schema instanceof import_zod.ZodOptional || schema instanceof import_zod.ZodNullable) { return createMockDataFromZodSchema( def.innerType, key, customGenerators, repeatCount, seed, strategy ); } if ("schema" in def) { return createMockDataFromZodSchema( def.schema, key, customGenerators, repeatCount, seed, strategy ); } if (schema instanceof import_zod.ZodArray) { const itemSchema = schema.element; const itemCount = typeof repeatCount === "number" ? repeatCount : Math.floor(Math.random() * 3) + 1; return Array.from( { length: itemCount }, (_, i) => createMockDataFromZodSchema( itemSchema, `${key}[${i}]`, customGenerators, void 0, seed * itemCount + i, strategy ) ); } if (schema instanceof import_zod.ZodObject) { const shape = schema.shape; const mockData = {}; for (const field in shape) { mockData[field] = createMockDataFromZodSchema( shape[field], field, customGenerators, repeatCount, seed, strategy ); } return mockData; } if (schema instanceof import_zod.ZodRecord) { return { key1: createMockDataFromZodSchema( def.valueType, "key1", customGenerators, repeatCount, seed, strategy ), key2: createMockDataFromZodSchema( def.valueType, "key2", customGenerators, repeatCount, seed, strategy ) }; } if (schema instanceof import_zod.ZodString) { if (customGenerators?.string) { return customGenerators.string(key); } return getRandomString(key); } if (schema instanceof import_zod.ZodNumber) { return customGenerators?.number ? customGenerators.number() : getRandomNumber(); } if (schema instanceof import_zod.ZodBoolean) { return customGenerators?.boolean ? customGenerators.boolean() : getRandomBoolean(); } if (schema instanceof import_zod.ZodDate) { return customGenerators?.dateTime ? customGenerators.dateTime() : getRandomDateTime(); } if (def.typeName === "ZodNativeEnum") { const values = schema.getValues ? schema.getValues() : def.values; if (strategy === "fixed") { return values[0]; } const randomIndex = Math.floor(Math.random() * values.length); return values[randomIndex]; } if (schema instanceof import_zod.ZodTuple) { return def.items.map( (item, i) => createMockDataFromZodSchema( item, `${key}[${i}]`, customGenerators, repeatCount, seed, strategy ) ); } if (schema instanceof import_zod.ZodEnum) { if (strategy === "fixed") { return schema.options[0]; } const options = schema.options; return options[Math.floor(Math.random() * options.length)]; } if (schema instanceof import_zod.ZodLiteral) { return schema.value; } if (schema instanceof import_zod.ZodUnion) { const options = schema.options; if (strategy === "fixed") { return createMockDataFromZodSchema( options[0], key, customGenerators, repeatCount, seed, strategy ); } const randomOption = options[Math.floor(Math.random() * options.length)]; return createMockDataFromZodSchema( randomOption, key, customGenerators, repeatCount, seed, strategy ); } if (schema instanceof import_zod.ZodDiscriminatedUnion) { const options = Array.from(schema.options.values()); if (options.length === 0) { return "unhandled zod type"; } if (strategy === "fixed") { return createMockDataFromZodSchema( options[0], key, customGenerators, repeatCount, seed, strategy ); } const randomIndex = Math.floor(Math.random() * options.length); return createMockDataFromZodSchema( options[randomIndex], key, customGenerators, repeatCount, seed, strategy ); } if (schema instanceof import_zod.ZodIntersection) { const mockLeft = createMockDataFromZodSchema( def.left, key, customGenerators, repeatCount, seed, strategy ); const mockRight = createMockDataFromZodSchema( def.right, key, customGenerators, repeatCount, seed, strategy ); return { ...mockLeft, ...mockRight }; } return "unhandled zod type"; } // src/interceptor.ts var import_path_to_regexp = require("path-to-regexp"); var STATUS_TEXTS = { 200: "OK", 201: "Created", 204: "No Content", 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 500: "Internal Server Error" }; function setupMockingInterceptor(instance, options) { const { enabled = true, registry, debug = false, customGenerators } = options; if (!enabled) { return; } const onFulfilled = async (config) => { const { url, method } = config; if (!url || !method) return config; const requestMethod = method.toUpperCase(); for (const registeredPattern in registry) { const parts = registeredPattern.split(" "); if (parts.length < 2) continue; const mockMethod = parts[0].toUpperCase(); const mockUrlPattern = parts.slice(1).join(" "); if (requestMethod !== mockMethod) { continue; } const matchFn = (0, import_path_to_regexp.match)(mockUrlPattern, { decode: decodeURIComponent }); const matchResult = matchFn(url); if (matchResult) { const { schema, responseBody, status, pagination, strategy, repeatCount } = registry[registeredPattern]; let mockData; if (responseBody !== void 0) { mockData = responseBody; } else if (schema) { if (pagination) { const pageKey = pagination.pageKey ?? "page"; const sizeKey = pagination.sizeKey ?? "size"; const page = config.params?.[pageKey] ?? config.data?.[pageKey] ?? 1; const size = config.params?.[sizeKey] ?? config.data?.[sizeKey] ?? 10; const itemSchema = schema.shape[pagination.itemsKey]; const pageData = createMockDataFromZodSchema( itemSchema, "", customGenerators, size, page, strategy ); mockData = { [pagination.itemsKey]: pageData, [pagination.totalKey]: 100, // Dummy total [pageKey]: page }; } else { mockData = createMockDataFromZodSchema( schema, "", customGenerators, repeatCount, 0, strategy ); } } else { mockData = { message: `[Zomoc] No responseBody or schema found for this route. Defaulting response.` }; } if (debug) { const isServer = typeof window === "undefined"; if (isServer) { console.log(`[Zomoc] Mocked Request: ${requestMethod} ${url}`); console.log(` - Pattern: ${registeredPattern}`); if (Object.keys(matchResult.params).length > 0) { console.log(" - Matched Params:", matchResult.params); } console.log( " - Generated Mock Data:", JSON.stringify(mockData, null, 2) ); } else { console.groupCollapsed( `%c[Zomoc] Mocked Request: %c${requestMethod} ${url}`, "color: #6e28d9; font-weight: bold;", "color: #10b981;" ); console.log( "%cURL Pattern:", "font-weight: bold;", registeredPattern ); if (Object.keys(matchResult.params).length > 0) { console.log( "%cMatched Params:", "font-weight: bold;", matchResult.params ); } console.log( "%cGenerated Mock Data:", "font-weight: bold;", mockData ); console.groupEnd(); } } config.adapter = (config2) => { return new Promise((resolve, reject) => { const res = { data: mockData, status, statusText: STATUS_TEXTS[status] ?? "Unknown Status", headers: { "x-zomoc-mocked": "true" }, config: config2, request: {} }; if (status >= 400) { const error = { isAxiosError: true, response: res, config: config2, toJSON: () => ({ message: `Request failed with status code ${status}`, name: "AxiosError", config: config2, response: { data: res.data, status: res.status, headers: res.headers } }) }; reject(error); } else { resolve(res); } }); }; break; } } return config; }; const onRejected = (error) => { return Promise.reject(error); }; instance.interceptors.request.use(onFulfilled, onRejected); } // src/public.ts function createGenerator(typeRegistry, customGenerators) { function getZomocGenerator(typeName, options = {}) { const schema = typeRegistry[typeName]; if (!schema) { throw new Error( `[Zomoc] The type "${String( typeName )}" is not registered. Please check your mock.json files.` ); } const { pagination, page = 1, size = 10, repeatCount, strategy } = options; let mockData; if (pagination) { const itemSchema = schema.shape[pagination.itemsKey]; const pageData = createMockDataFromZodSchema( itemSchema, String(typeName), customGenerators, size, page, strategy ); mockData = { [pagination.itemsKey]: pageData, [pagination.totalKey]: 100, // Dummy total [pagination.pageKey ?? "page"]: page, [pagination.sizeKey ?? "size"]: size }; } else { mockData = createMockDataFromZodSchema( schema, String(typeName), customGenerators, repeatCount, 0, // page is not relevant here strategy ); } return mockData; } return getZomocGenerator; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createGenerator, setupMockingInterceptor });