refactor: migrate from Rollup to Bun for build configuration
feat: update adapter to use globalThis for origin resolution fix: remove unused ClientQuery export from query.ts chore: update tsconfig to include test files and set rootDir feat: add create-query functionality for dynamic API generation feat: implement QueryApi with enhanced type inference from JSON Schema test: add comprehensive API tests for QueryApi functionality test: create demo routes and schemas for testing purposes docs: add type inference demo for QueryApi usage
This commit is contained in:
30
test/api.ts
Normal file
30
test/api.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { QueryApi } from '../src/query-api.ts';
|
||||
|
||||
export const queryApi = new QueryApi();
|
||||
|
||||
export const api = {
|
||||
"test": {
|
||||
"test": {
|
||||
"path": "test",
|
||||
"key": "test",
|
||||
"id": "rWfTW4jLlwPWN_LdYXPBO",
|
||||
"description": "test route",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"a": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"description": "arg a"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Additional routes can be added here
|
||||
} as const;
|
||||
|
||||
const res = await queryApi.post(api.test.test, {
|
||||
a: 'test'
|
||||
});
|
||||
26
test/common.ts
Normal file
26
test/common.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { app } from './router.ts';
|
||||
import util from 'node:util';
|
||||
import fs from 'node:fs'
|
||||
import { createQueryByRoutes } from '../src/create-query/index.ts';
|
||||
export const showMore = (data: any) => {
|
||||
return util.inspect(data, { depth: null, colors: true });
|
||||
}
|
||||
const routes = await app.run({ path: 'router', key: 'list' })
|
||||
// console.log('rourtes', showMore(routes.data.list));
|
||||
const list = routes.data.list
|
||||
|
||||
const obj: any = {}
|
||||
|
||||
for (const route of list) {
|
||||
if (!obj[route.path]) {
|
||||
obj[route.path] = {};
|
||||
}
|
||||
obj[route.path][route.key] = route;
|
||||
}
|
||||
|
||||
// console.log('obj', showMore(obj));
|
||||
|
||||
const code = createQueryByRoutes(list);
|
||||
|
||||
fs.writeFileSync('test/query.ts', code, 'utf-8');
|
||||
|
||||
472
test/query.ts
Normal file
472
test/query.ts
Normal file
@@ -0,0 +1,472 @@
|
||||
|
||||
import { createQueryApi } from '@kevisual/query/api';
|
||||
const api = {
|
||||
"test": {
|
||||
/**
|
||||
* test route
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.a - {string} arg a
|
||||
*/
|
||||
"test": {
|
||||
"path": "test",
|
||||
"key": "test",
|
||||
"description": "test route",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"a": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "arg a",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"demo": {
|
||||
/**
|
||||
* First demo route demonstrating string and number parameters
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.username - {string (minLength: 3, maxLength: 20)} The username to be validated, must be between 3 and 20 characters
|
||||
* @param data.age - {number (min: 18, max: 100)} The age of the user, must be between 18 and 100
|
||||
* @param data.email - {string (format: email)} The email address of the user for notification purposes
|
||||
* @param data.count - {integer (max: 9007199254740991, > 0)} The number of items to process, must be a positive integer
|
||||
* @param data.name - {string} The display name of the user
|
||||
*/
|
||||
"d1": {
|
||||
"path": "demo",
|
||||
"key": "d1",
|
||||
"description": "First demo route demonstrating string and number parameters",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"username": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 20,
|
||||
"description": "The username to be validated, must be between 3 and 20 characters"
|
||||
},
|
||||
"age": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "number",
|
||||
"minimum": 18,
|
||||
"maximum": 100,
|
||||
"description": "The age of the user, must be between 18 and 100"
|
||||
},
|
||||
"email": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
|
||||
"description": "The email address of the user for notification purposes"
|
||||
},
|
||||
"count": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"description": "The number of items to process, must be a positive integer"
|
||||
},
|
||||
"name": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"description": "The display name of the user"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Second demo route for boolean and enum parameters
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.isActive - {boolean} Whether the user account is currently active and accessible
|
||||
* @param data.isAdmin - {boolean} Whether the user has administrative privileges
|
||||
* @param data.notifications - {boolean} Whether to enable email and push notifications
|
||||
* @param data.mode - {"read" | "write" | "execute"} The operation mode for the current session
|
||||
* @param data.verified - {boolean} Whether the user email has been verified
|
||||
*/
|
||||
"d2": {
|
||||
"path": "demo",
|
||||
"key": "d2",
|
||||
"description": "Second demo route for boolean and enum parameters",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"isActive": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "boolean",
|
||||
"description": "Whether the user account is currently active and accessible"
|
||||
},
|
||||
"isAdmin": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "boolean",
|
||||
"description": "Whether the user has administrative privileges"
|
||||
},
|
||||
"notifications": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "boolean",
|
||||
"description": "Whether to enable email and push notifications"
|
||||
},
|
||||
"mode": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write",
|
||||
"execute"
|
||||
],
|
||||
"description": "The operation mode for the current session"
|
||||
},
|
||||
"verified": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "boolean",
|
||||
"description": "Whether the user email has been verified"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Third demo route handling array and optional parameters
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.tags - {array} List of tags associated with the content, between 1 and 10 tags
|
||||
* @param data.categories - {array} List of category names for filtering and classification
|
||||
* @param data.ids - {array} Array of numeric identifiers for the resources
|
||||
* @param data.priority - {number (min: 1, max: 5)} Priority level from 1 to 5, defaults to 3 if not specified
|
||||
* @param data.keywords - {array} Keywords for search optimization, up to 20 keywords
|
||||
*/
|
||||
"d3": {
|
||||
"path": "demo",
|
||||
"key": "d3",
|
||||
"description": "Third demo route handling array and optional parameters",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"tags": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"minItems": 1,
|
||||
"maxItems": 10,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "List of tags associated with the content, between 1 and 10 tags"
|
||||
},
|
||||
"categories": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "List of category names for filtering and classification"
|
||||
},
|
||||
"ids": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"description": "Array of numeric identifiers for the resources"
|
||||
},
|
||||
"priority": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Priority level from 1 to 5, defaults to 3 if not specified",
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 5
|
||||
},
|
||||
"keywords": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"maxItems": 20,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Keywords for search optimization, up to 20 keywords"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fourth demo route with nested object parameters
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.user - {object} Complete user profile information
|
||||
* @param data.settings - {object} User preference settings
|
||||
* @param data.address - {object} Mailing address, optional field
|
||||
*/
|
||||
"d4": {
|
||||
"path": "demo",
|
||||
"key": "d4",
|
||||
"description": "Fourth demo route with nested object parameters",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"user": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"description": "Unique identifier for the user"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Full name of the user"
|
||||
},
|
||||
"contact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
|
||||
"description": "Primary email address"
|
||||
},
|
||||
"phone": {
|
||||
"description": "Phone number with country code",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Contact information for the user"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"name",
|
||||
"contact"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Complete user profile information"
|
||||
},
|
||||
"settings": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark",
|
||||
"auto"
|
||||
],
|
||||
"description": "UI theme preference"
|
||||
},
|
||||
"language": {
|
||||
"default": "en",
|
||||
"description": "Preferred language code",
|
||||
"type": "string"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": "Timezone identifier like America/New_York"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"theme",
|
||||
"language",
|
||||
"timezone"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "User preference settings"
|
||||
},
|
||||
"address": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Mailing address, optional field",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"street": {
|
||||
"type": "string",
|
||||
"description": "Street address line"
|
||||
},
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "City name"
|
||||
},
|
||||
"country": {
|
||||
"type": "string",
|
||||
"description": "Country code or name"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"street",
|
||||
"city",
|
||||
"country"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fifth demo route with mixed complex parameters and validation
|
||||
*
|
||||
* @param data - Request parameters
|
||||
* @param data.query - {string (minLength: 1)} Search query string, minimum 1 character required
|
||||
* @param data.filters - {object} Advanced search filters configuration
|
||||
* @param data.pagination - {object} Pagination settings for query results
|
||||
* @param data.includeMetadata - {boolean} Whether to include metadata in response
|
||||
* @param data.timeout - {number (min: 1000, max: 30000)} Request timeout in milliseconds, between 1s and 30s
|
||||
* @param data.retry - {integer (min: 0, max: 5)} Number of retry attempts on failure
|
||||
*/
|
||||
"d5": {
|
||||
"path": "demo",
|
||||
"key": "d5",
|
||||
"description": "Fifth demo route with mixed complex parameters and validation",
|
||||
"type": "route",
|
||||
"middleware": [],
|
||||
"metadata": {
|
||||
"args": {
|
||||
"query": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Search query string, minimum 1 character required"
|
||||
},
|
||||
"filters": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"image",
|
||||
"video",
|
||||
"audio",
|
||||
"document"
|
||||
],
|
||||
"description": "Content type filter"
|
||||
},
|
||||
"dateRange": {
|
||||
"description": "Date range filter, optional",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"start": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"description": "Start date in ISO 8601 format"
|
||||
},
|
||||
"end": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"description": "End date in ISO 8601 format"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"start",
|
||||
"end"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"size": {
|
||||
"description": "Size filter for media content",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"extra-large"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Advanced search filters configuration"
|
||||
},
|
||||
"pagination": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"page": {
|
||||
"default": 1,
|
||||
"description": "Page number starting from 1",
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"limit": {
|
||||
"default": 20,
|
||||
"description": "Number of items per page, max 100",
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"sort": {
|
||||
"default": "desc",
|
||||
"description": "Sort order for results",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"asc",
|
||||
"desc"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"page",
|
||||
"limit",
|
||||
"sort"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Pagination settings for query results"
|
||||
},
|
||||
"includeMetadata": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"default": false,
|
||||
"description": "Whether to include metadata in response",
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeout": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Request timeout in milliseconds, between 1s and 30s",
|
||||
"type": "number",
|
||||
"minimum": 1000,
|
||||
"maximum": 30000
|
||||
},
|
||||
"retry": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"default": 3,
|
||||
"description": "Number of retry attempts on failure",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"router": {
|
||||
/**
|
||||
* 列出当前应用下的所有的路由信息
|
||||
*/
|
||||
"list": {
|
||||
"path": "router",
|
||||
"key": "list",
|
||||
"description": "列出当前应用下的所有的路由信息",
|
||||
"type": "route",
|
||||
"middleware": []
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
const queryApi = createQueryApi({ api });
|
||||
export { queryApi };
|
||||
131
test/router.ts
Normal file
131
test/router.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { App } from '@kevisual/router';
|
||||
import { z } from 'zod';
|
||||
export const app = new App({});
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'test',
|
||||
key: 'test',
|
||||
description: 'test route',
|
||||
metadata: {
|
||||
args: {
|
||||
a: z.string().optional().describe('arg a'),
|
||||
}
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = 'test';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'demo',
|
||||
key: 'd1',
|
||||
description: 'First demo route demonstrating string and number parameters',
|
||||
metadata: {
|
||||
args: {
|
||||
username: z.string().min(3).max(20).describe('The username to be validated, must be between 3 and 20 characters'),
|
||||
age: z.number().min(18).max(100).describe('The age of the user, must be between 18 and 100'),
|
||||
email: z.email().describe('The email address of the user for notification purposes'),
|
||||
count: z.number().int().positive().describe('The number of items to process, must be a positive integer'),
|
||||
name: z.string().describe('The display name of the user'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = 'demo1';
|
||||
}).addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'demo',
|
||||
key: 'd2',
|
||||
description: 'Second demo route for boolean and enum parameters',
|
||||
metadata: {
|
||||
args: {
|
||||
isActive: z.boolean().describe('Whether the user account is currently active and accessible'),
|
||||
isAdmin: z.boolean().describe('Whether the user has administrative privileges'),
|
||||
notifications: z.boolean().describe('Whether to enable email and push notifications'),
|
||||
mode: z.enum(['read', 'write', 'execute']).describe('The operation mode for the current session'),
|
||||
verified: z.boolean().describe('Whether the user email has been verified'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = 'demo2';
|
||||
}).addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'demo',
|
||||
key: 'd3',
|
||||
description: 'Third demo route handling array and optional parameters',
|
||||
metadata: {
|
||||
args: {
|
||||
tags: z.array(z.string()).min(1).max(10).describe('List of tags associated with the content, between 1 and 10 tags'),
|
||||
categories: z.array(z.string()).describe('List of category names for filtering and classification'),
|
||||
ids: z.array(z.number().int().positive()).describe('Array of numeric identifiers for the resources'),
|
||||
priority: z.number().min(1).max(5).optional().describe('Priority level from 1 to 5, defaults to 3 if not specified'),
|
||||
keywords: z.array(z.string()).max(20).describe('Keywords for search optimization, up to 20 keywords'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = 'demo3';
|
||||
}).addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'demo',
|
||||
key: 'd4',
|
||||
description: 'Fourth demo route with nested object parameters',
|
||||
metadata: {
|
||||
args: {
|
||||
user: z.object({
|
||||
id: z.number().int().positive().describe('Unique identifier for the user'),
|
||||
name: z.string().describe('Full name of the user'),
|
||||
contact: z.object({
|
||||
email: z.email().describe('Primary email address'),
|
||||
phone: z.string().optional().describe('Phone number with country code'),
|
||||
}).describe('Contact information for the user'),
|
||||
}).describe('Complete user profile information'),
|
||||
settings: z.object({
|
||||
theme: z.enum(['light', 'dark', 'auto']).describe('UI theme preference'),
|
||||
language: z.string().default('en').describe('Preferred language code'),
|
||||
timezone: z.string().describe('Timezone identifier like America/New_York'),
|
||||
}).describe('User preference settings'),
|
||||
address: z.object({
|
||||
street: z.string().describe('Street address line'),
|
||||
city: z.string().describe('City name'),
|
||||
country: z.string().describe('Country code or name'),
|
||||
}).optional().describe('Mailing address, optional field'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = 'demo4';
|
||||
}).addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'demo',
|
||||
key: 'd5',
|
||||
description: 'Fifth demo route with mixed complex parameters and validation',
|
||||
metadata: {
|
||||
args: {
|
||||
query: z.string().min(1).describe('Search query string, minimum 1 character required'),
|
||||
filters: z.object({
|
||||
type: z.enum(['all', 'image', 'video', 'audio', 'document']).describe('Content type filter'),
|
||||
dateRange: z.object({
|
||||
start: z.iso.datetime().describe('Start date in ISO 8601 format'),
|
||||
end: z.iso.datetime().describe('End date in ISO 8601 format'),
|
||||
}).optional().describe('Date range filter, optional'),
|
||||
size: z.enum(['small', 'medium', 'large', 'extra-large']).optional().describe('Size filter for media content'),
|
||||
}).describe('Advanced search filters configuration'),
|
||||
pagination: z.object({
|
||||
page: z.number().int().positive().default(1).describe('Page number starting from 1'),
|
||||
limit: z.number().int().positive().max(100).default(20).describe('Number of items per page, max 100'),
|
||||
sort: z.enum(['asc', 'desc']).default('desc').describe('Sort order for results'),
|
||||
}).describe('Pagination settings for query results'),
|
||||
includeMetadata: z.boolean().default(false).describe('Whether to include metadata in response'),
|
||||
timeout: z.number().min(1000).max(30000).optional().describe('Request timeout in milliseconds, between 1s and 30s'),
|
||||
retry: z.number().int().min(0).max(5).default(3).describe('Number of retry attempts on failure'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = 'demo5';
|
||||
}).addTo(app);
|
||||
|
||||
app.createRouteList()
|
||||
43
test/schema.ts
Normal file
43
test/schema.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import z, { toJSONSchema } from "zod";
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().describe("The name of the person"),
|
||||
age: z.number().int().min(0).describe("The age of the person"),
|
||||
email: z.string().optional().describe("The email address of the person"),
|
||||
});
|
||||
|
||||
console.log("JSON Schema for the person object:");
|
||||
console.log(
|
||||
JSON.stringify(toJSONSchema(schema), null, 2)
|
||||
);
|
||||
const jsonSchema = toJSONSchema(schema);
|
||||
|
||||
const schema2 = z.fromJSONSchema(jsonSchema);
|
||||
|
||||
// schema2 的类型是 ZodSchema<any>,所以无法在编译时推断出具体类型
|
||||
// 这是 fromJSONSchema 的限制 - JSON Schema 转换会丢失 TypeScript 类型信息
|
||||
|
||||
schema2.parse({
|
||||
name: "John Doe",
|
||||
age: 30, // 添加必需的 age 字段
|
||||
email: "",
|
||||
})
|
||||
|
||||
type Schema2Type = z.infer<typeof schema2>;
|
||||
// Schema2Type 被推断为 any
|
||||
|
||||
// 对比:原始 schema 的类型推断是正常的
|
||||
type OriginalSchemaType = z.infer<typeof schema>;
|
||||
// OriginalSchemaType = { name: string; age: number; email?: string | undefined }
|
||||
|
||||
const v: Schema2Type = {
|
||||
name: "John Doe",
|
||||
email: ""
|
||||
}
|
||||
|
||||
// 如果使用原始 schema,类型推断会正常工作:
|
||||
const v2: OriginalSchemaType = {
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
// email 是可选的
|
||||
}
|
||||
Reference in New Issue
Block a user