zomoc
Version:
A type-safe API mocking tool for frontend development, powered by axios and Zod.
435 lines (430 loc) • 12.8 kB
JavaScript
;
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
});