Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions packages/apicraft/apicraft.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
// TEST CONFIG

import type { OpenApi, OpenApiOperationObject } from '@hey-api/openapi-ts';

import fs from 'node:fs';

// eslint-disable-next-line antfu/no-import-dist
import { apicraft } from './dist/esm/index.mjs';
// import { apicraft } from '@siberiacancode/apicraft';

const apicraftConfig = apicraft([
{
input: 'example-apiV1.yaml',
// input: 'example-apiv2.json',
input: () => {
const content = fs.readFileSync('example-apiv2.json', 'utf8');
const document: OpenApi.V3_0_X = JSON.parse(content);

for (const pathItem of Object.values(document.paths)) {
for (const field of Object.values(pathItem)) {
const operation = field as OpenApiOperationObject.V3_0_X;
if (!Array.isArray(operation.parameters)) continue;

operation.parameters = operation.parameters.filter(
(parameter) => parameter.name.toLowerCase() !== 'authorization'
);
}
}

return document;
},
output: 'generated/apiV1',
instance: 'fetches',
nameBy: 'path',
groupBy: 'tag',
plugins: ['tanstack']
plugins: ['tanstack'],
parser: {
filters: {
parameters: {
exclude: ['Authorization']
}
}
}
}
]);

Expand Down
4 changes: 2 additions & 2 deletions packages/apicraft/bin/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ export const generate = {
}

await createClient({
parser: { filters: option.filters },
input: option.input,
...(option.parser && { parser: option.parser as UserConfig['parser'] }),
input: typeof option.input === 'function' ? await option.input() : option.input,
output: option.output,
plugins: plugins as UserConfig['plugins']
});
Expand Down
157 changes: 97 additions & 60 deletions packages/apicraft/bin/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,80 @@ const instanceSchema = z.object({

const pathSchema = z.string().regex(/^[^/.].*[^/]$/, 'Path must be absolute');

const includeExcludeSchema = z.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
});

const filtersSchema = z.object({
deprecated: z.boolean().default(true).optional(),
operations: includeExcludeSchema.optional(),
orphans: z.boolean().default(false).optional(),
parameters: includeExcludeSchema.optional(),
preserveOrder: z.boolean().default(false).optional(),
requestBodies: includeExcludeSchema.optional(),
responses: includeExcludeSchema.optional(),
schemas: includeExcludeSchema.optional(),
tags: includeExcludeSchema.optional()
});

const enumsModeSchema = z.enum(['root', 'inline']);
const stringCaseSchema = z.enum(['camelCase', 'PascalCase', 'preserve', 'snake_case']);
const stringNameSchema = z.union([z.string(), z.function()]);

const parserTransformsSchema = z
.object({
enums: z
.union([
z.boolean(),
enumsModeSchema,
z.object({
case: stringCaseSchema.optional(),
enabled: z.boolean().optional(),
mode: enumsModeSchema.optional(),
name: stringNameSchema.optional()
})
])
.optional(),
readWrite: z
.union([
z.boolean(),
z.object({
enabled: z.boolean().optional(),
requests: z
.union([
stringNameSchema,
z.object({ case: stringCaseSchema.optional(), name: stringNameSchema.optional() })
])
.optional(),
responses: z
.union([
stringNameSchema,
z.object({ case: stringCaseSchema.optional(), name: stringNameSchema.optional() })
])
.optional()
})
])
.optional()
})
.optional();

const parserSchema = z
.object({
filters: filtersSchema.optional(),
hooks: z.record(z.string(), z.any()).optional(),
pagination: z
.object({
keywords: z.array(z.string()).readonly().optional()
})
.optional(),
patch: z.record(z.string(), z.any()).optional(),
transforms: parserTransformsSchema,
validate_EXPERIMENTAL: z.union([z.boolean(), z.enum(['strict', 'warn'])]).optional()
})
.strict()
.optional();

const pluginNameSchema = z.enum([
'@hey-api/client-angular',
'@hey-api/client-axios',
Expand Down Expand Up @@ -38,23 +112,28 @@ const pluginNameSchema = z.enum([

export const apicraftOptionSchema = z
.object({
input: pathSchema.or(
z.object({
path: pathSchema.or(z.record(z.string(), z.unknown())),
fetch: z.record(z.string(), z.any()).optional(),
watch: z
.boolean()
.or(z.number())
.or(
z.object({
enabled: z.boolean().optional(),
interval: z.number().optional(),
timeout: z.number().optional()
})
)
.optional()
})
),
input: z
.function()
.output(z.any())
.or(
pathSchema.or(
z.object({
path: pathSchema.or(z.record(z.string(), z.unknown())),
fetch: z.record(z.string(), z.any()).optional(),
watch: z
.boolean()
.or(z.number())
.or(
z.object({
enabled: z.boolean().optional(),
interval: z.number().optional(),
timeout: z.number().optional()
})
)
.optional()
})
)
),
output: pathSchema.or(
z.object({
path: pathSchema,
Expand All @@ -78,49 +157,7 @@ export const apicraftOptionSchema = z
.optional()
})
),
filters: z
.object({
deprecated: z.boolean().default(true).optional(),
operations: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional(),
orphans: z.boolean().default(false).optional(),
parameters: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional(),
preserveOrder: z.boolean().default(false).optional(),
requestBodies: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional(),
responses: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional(),
schemas: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional(),
tags: z
.object({
exclude: z.array(z.string()).readonly().optional(),
include: z.array(z.string()).readonly().optional()
})
.optional()
})
.optional(),
parser: parserSchema,
instance: z.union([instanceNameSchema, instanceSchema]).optional(),
nameBy: z.enum(['path', 'operationId']).default('operationId').optional(),
groupBy: z.enum(['path', 'tag']).default('tag').optional(),
Expand Down
46 changes: 46 additions & 0 deletions packages/apicraft/example-apiV1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ tags:
description: Example echo operations
- name: User
description: Operations about user
- name: countries v3.0
description: Countries
servers:
- url: 'http://example.com/api/v1'
- url: 'https://example.com/api/v1'
Expand Down Expand Up @@ -111,6 +113,37 @@ paths:
'404':
description: User not found

'/api/countries':
get:
tags:
- countries v3.0
summary: Получить выборку с информацией о странах.
description: Запрос используется для получения списка стран
operationId: getCountries
parameters:
- name: Authorization
in: header
required: true
description: "'Bearer {access_token}'"
schema:
type: string
pattern: '^Bearer .*$'
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/GetCountryeV30'
'400':
description: BadRequest
'401':
description: Unauthorized
'403':
description: Forbidden

# An object to hold reusable parts that can be used across the definition
components:
schemas:
Expand Down Expand Up @@ -139,6 +172,19 @@ components:
example: Smith
email:
$ref: '#/components/schemas/Email'
GetCountryeV30:
type: object
description: Country (v3.0)
properties:
id:
type: string
example: '1'
name:
type: string
example: Russia
code:
type: string
example: RU
headers:
ExpiresAfter:
description: date in UTC when token expires
Expand Down
Loading