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:
2026-02-17 21:39:41 +08:00
parent 7adedc0552
commit ecb69ba326
18 changed files with 1106 additions and 591 deletions

131
test/router.ts Normal file
View 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()