svelte-reflector 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +11 -0
- package/dist/file.d.ts +12 -0
- package/dist/file.js +27 -0
- package/dist/generate-doc.d.ts +4 -0
- package/dist/generate-doc.js +67 -0
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.js +35 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/input.d.ts +3 -0
- package/dist/input.js +14 -0
- package/dist/main.d.ts +25 -0
- package/dist/main.js +100 -0
- package/dist/method.d.ts +17 -0
- package/dist/method.js +128 -0
- package/dist/module.d.ts +30 -0
- package/dist/module.js +148 -0
- package/dist/property.d.ts +23 -0
- package/dist/property.js +81 -0
- package/dist/request.d.ts +31 -0
- package/dist/request.js +114 -0
- package/dist/schema.d.ts +13 -0
- package/dist/schema.js +27 -0
- package/dist/types/open-api-spec.interface.d.ts +239 -0
- package/dist/types/open-api-spec.interface.js +1 -0
- package/dist/types/types.d.ts +12 -0
- package/dist/types/types.js +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
Binary file
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { reflector } from "./generate-doc.js";
|
|
3
|
+
try {
|
|
4
|
+
await reflector(); // lê .env e roda tudo
|
|
5
|
+
console.log("[reflector] Finalizado com sucesso.");
|
|
6
|
+
process.exit(0);
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
console.error("[reflector] Falha:", err instanceof Error ? err.message : err);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
package/dist/file.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class Source {
|
|
2
|
+
path: string;
|
|
3
|
+
data: string;
|
|
4
|
+
constructor(params: {
|
|
5
|
+
path: string;
|
|
6
|
+
data?: string;
|
|
7
|
+
});
|
|
8
|
+
save(): Promise<void>;
|
|
9
|
+
safeSave(): Promise<void>;
|
|
10
|
+
changeData(data: string): Promise<void>;
|
|
11
|
+
buildPath(endpoint: string[]): string;
|
|
12
|
+
}
|
package/dist/file.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import prettier from "prettier";
|
|
3
|
+
export class Source {
|
|
4
|
+
path;
|
|
5
|
+
data;
|
|
6
|
+
constructor(params) {
|
|
7
|
+
const { path, data } = params;
|
|
8
|
+
this.path = path;
|
|
9
|
+
this.data = data ?? "";
|
|
10
|
+
}
|
|
11
|
+
async save() {
|
|
12
|
+
const options = await prettier.resolveConfig(process.cwd());
|
|
13
|
+
const formatted = await prettier.format(this.data, { ...options, filepath: this.path });
|
|
14
|
+
writeFileSync(this.path, formatted, "utf8");
|
|
15
|
+
}
|
|
16
|
+
async safeSave() {
|
|
17
|
+
writeFileSync(this.path, this.data, "utf8");
|
|
18
|
+
}
|
|
19
|
+
async changeData(data) {
|
|
20
|
+
this.data = data;
|
|
21
|
+
}
|
|
22
|
+
buildPath(endpoint) {
|
|
23
|
+
if (endpoint.length === 1)
|
|
24
|
+
return endpoint[0];
|
|
25
|
+
return endpoint.join("/");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/generate-doc.ts
|
|
2
|
+
import "dotenv/config"; // carrega .env a partir de process.cwd()
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { Reflector } from "./main.js";
|
|
7
|
+
/** ajuda a pegar a 1ª env definida dentre várias chaves possíveis */
|
|
8
|
+
function pickEnv(...keys) {
|
|
9
|
+
for (const k of keys) {
|
|
10
|
+
const v = process.env[k];
|
|
11
|
+
if (v != null && v !== "")
|
|
12
|
+
return v;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
/** normaliza URL base com / no fim */
|
|
17
|
+
function withTrailingSlash(u) {
|
|
18
|
+
if (!u)
|
|
19
|
+
return u;
|
|
20
|
+
return u.endsWith("/") ? u : `${u}/`;
|
|
21
|
+
}
|
|
22
|
+
function getParams() {
|
|
23
|
+
const BACKEND_URL_RAW = pickEnv("BACKEND_URL", "PUBLIC_BACKEND");
|
|
24
|
+
const ENVIRONMENT_RAW = pickEnv("ENVIRONMENT", "VITE_ENVIRONMENT", "VITE_ENVIROMENT", "NODE_ENV") ?? "PROD";
|
|
25
|
+
if (!BACKEND_URL_RAW)
|
|
26
|
+
throw new Error("BACKEND_URL vazio");
|
|
27
|
+
const ENVIRONMENT = ENVIRONMENT_RAW.toUpperCase();
|
|
28
|
+
const BACKEND_URL = withTrailingSlash(BACKEND_URL_RAW);
|
|
29
|
+
if (!BACKEND_URL) {
|
|
30
|
+
console.warn("[reflector] BACKEND_URL não definido (nem em params nem na .env: BACKEND_URL/PUBLIC_BACKEND).");
|
|
31
|
+
}
|
|
32
|
+
if (ENVIRONMENT === "PROD")
|
|
33
|
+
console.warn("[reflector] Ambiente não-DEV: os schemas serão atualizados automaticamente.");
|
|
34
|
+
return { BACKEND_URL, ENVIRONMENT };
|
|
35
|
+
}
|
|
36
|
+
export async function reflector() {
|
|
37
|
+
const { BACKEND_URL, ENVIRONMENT } = getParams();
|
|
38
|
+
if (ENVIRONMENT === "DEV") {
|
|
39
|
+
console.warn("[reflector] Ambiente DEV: para regerar os schemas manualmente, use: npx reflect");
|
|
40
|
+
return breakReflector();
|
|
41
|
+
}
|
|
42
|
+
const DOC_URL = `${BACKEND_URL}openapi.json`;
|
|
43
|
+
let data;
|
|
44
|
+
try {
|
|
45
|
+
const documentation = await axios.get(DOC_URL, { timeout: 15000 });
|
|
46
|
+
data = documentation.data;
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.warn(`[reflector] Não foi possível obter a documentação em ${DOC_URL}. Carregando cópia local...`);
|
|
50
|
+
const backupPath = path.resolve(process.cwd(), "src/api/backup.json");
|
|
51
|
+
data = JSON.parse(fs.readFileSync(backupPath, "utf8"));
|
|
52
|
+
}
|
|
53
|
+
const { components, paths } = data;
|
|
54
|
+
if (!components) {
|
|
55
|
+
console.warn("[reflector] OpenAPI sem components; abortando.");
|
|
56
|
+
return { name: "vite-plugin-generate-doc" };
|
|
57
|
+
}
|
|
58
|
+
const r = new Reflector({ components, paths });
|
|
59
|
+
r.build();
|
|
60
|
+
r.localSave(data);
|
|
61
|
+
breakReflector();
|
|
62
|
+
}
|
|
63
|
+
function breakReflector() {
|
|
64
|
+
return {
|
|
65
|
+
name: "vite-plugin-generate-doc",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function stripState(attr: string): string;
|
|
2
|
+
export declare function toCamelCase(str: string): string;
|
|
3
|
+
export declare function sanitizeKey(name: string): string;
|
|
4
|
+
export declare function capitalizeFirstLetter(text: string): string;
|
|
5
|
+
export declare function splitByUppercase(text: string): string[];
|
|
6
|
+
export declare function createDangerMessage(text: string): void;
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function stripState(attr) {
|
|
2
|
+
// Ex.: "form = $state(newForm(DefaultCreateUserDtoSchema))"
|
|
3
|
+
const [lhs, rhsRaw = ''] = attr.split('=');
|
|
4
|
+
const rhs = rhsRaw.trim();
|
|
5
|
+
// remove apenas UM wrapper $state( ... ) do início ao fim
|
|
6
|
+
const cleaned = rhs.startsWith('$state(') && rhs.endsWith(')') ? rhs.slice('$state('.length, -1).trim() : rhs;
|
|
7
|
+
return `${lhs.trim()} = ${cleaned}`;
|
|
8
|
+
}
|
|
9
|
+
export function toCamelCase(str) {
|
|
10
|
+
return str
|
|
11
|
+
.split('-')
|
|
12
|
+
.map((chunk, i) => (i === 0 ? chunk : chunk[0].toUpperCase() + chunk.slice(1)))
|
|
13
|
+
.join('');
|
|
14
|
+
}
|
|
15
|
+
export function sanitizeKey(name) {
|
|
16
|
+
const match = /^\[id(.+)\]$|^\{(.+)\}$/.exec(name);
|
|
17
|
+
if (match) {
|
|
18
|
+
const raw = match[1] || match[2]; // pega o conteúdo entre [] ou {}
|
|
19
|
+
const camel = toCamelCase(raw);
|
|
20
|
+
// Garante que a primeira letra fique minúscula
|
|
21
|
+
return camel.charAt(0).toLowerCase() + camel.slice(1);
|
|
22
|
+
}
|
|
23
|
+
return toCamelCase(name);
|
|
24
|
+
}
|
|
25
|
+
export function capitalizeFirstLetter(text) {
|
|
26
|
+
if (!text)
|
|
27
|
+
return '';
|
|
28
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
29
|
+
}
|
|
30
|
+
export function splitByUppercase(text) {
|
|
31
|
+
return text.split(/(?=[A-Z])/);
|
|
32
|
+
}
|
|
33
|
+
export function createDangerMessage(text) {
|
|
34
|
+
console.log('\x1b[31m%s\x1b[0m', `[!] ${text}`);
|
|
35
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { reflector } from "./generate-doc.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { reflector } from "./generate-doc.js";
|
package/dist/input.d.ts
ADDED
package/dist/input.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class ReflectorInput {
|
|
2
|
+
password = `z
|
|
3
|
+
.string()
|
|
4
|
+
.regex(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/, {
|
|
5
|
+
message: 'A senha deve conter pelo menos ',
|
|
6
|
+
})
|
|
7
|
+
.min(8, { message: '8 caracteres, ' })
|
|
8
|
+
.regex(/[A-Z]/, { message: 'uma letra maiúscula, ' })
|
|
9
|
+
.regex(/[a-z]/, { message: 'uma letra minúscula, ' })
|
|
10
|
+
.regex(/[0-9]/, { message: 'um número, ' })
|
|
11
|
+
.regex(/[^A-Za-z0-9]/, { message: 'um caracter especial.' })
|
|
12
|
+
.default('Senha123!@#')
|
|
13
|
+
`;
|
|
14
|
+
}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Source } from "./file.js";
|
|
2
|
+
import { Schema } from "./schema.js";
|
|
3
|
+
import { ComponentsObject, PathsObject, OpenAPIObject } from "./types/open-api-spec.interface.js";
|
|
4
|
+
import { Module } from "./module.js";
|
|
5
|
+
export declare class Reflector {
|
|
6
|
+
readonly components: ComponentsObject;
|
|
7
|
+
readonly paths: PathsObject;
|
|
8
|
+
readonly dir: string;
|
|
9
|
+
readonly generatedDir: string;
|
|
10
|
+
readonly localDoc: Source;
|
|
11
|
+
readonly src: Source;
|
|
12
|
+
readonly schemaFile: Source;
|
|
13
|
+
files: Source[];
|
|
14
|
+
schemas: Schema[];
|
|
15
|
+
modules: Module[];
|
|
16
|
+
constructor(params: {
|
|
17
|
+
components: ComponentsObject;
|
|
18
|
+
paths: PathsObject;
|
|
19
|
+
});
|
|
20
|
+
private getSchemas;
|
|
21
|
+
getModules(): Module[];
|
|
22
|
+
build(): {};
|
|
23
|
+
localSave(data: OpenAPIObject): void;
|
|
24
|
+
private clearSrc;
|
|
25
|
+
}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { Source } from "./file.js";
|
|
4
|
+
import { sanitizeKey, capitalizeFirstLetter } from "./helpers.js";
|
|
5
|
+
import { Schema } from "./schema.js";
|
|
6
|
+
import { Module } from "./module.js";
|
|
7
|
+
export class Reflector {
|
|
8
|
+
components;
|
|
9
|
+
paths;
|
|
10
|
+
dir = "src/api";
|
|
11
|
+
generatedDir = `${this.dir}/reflector`;
|
|
12
|
+
localDoc = new Source({ path: path.resolve(process.cwd(), `${this.dir}/backup.json`) });
|
|
13
|
+
src = new Source({ path: path.resolve(process.cwd(), this.generatedDir) });
|
|
14
|
+
schemaFile = new Source({ path: path.resolve(process.cwd(), `${this.dir}/schemas.ts`) });
|
|
15
|
+
files;
|
|
16
|
+
schemas;
|
|
17
|
+
modules;
|
|
18
|
+
constructor(params) {
|
|
19
|
+
const { components, paths } = params;
|
|
20
|
+
this.clearSrc();
|
|
21
|
+
this.components = components;
|
|
22
|
+
this.paths = paths;
|
|
23
|
+
this.files = [];
|
|
24
|
+
this.modules = this.getModules();
|
|
25
|
+
this.schemas = this.getSchemas();
|
|
26
|
+
}
|
|
27
|
+
getSchemas() {
|
|
28
|
+
const componentSchemas = this.components.schemas;
|
|
29
|
+
if (!componentSchemas)
|
|
30
|
+
return [];
|
|
31
|
+
const schemas = [];
|
|
32
|
+
Object.entries(componentSchemas).forEach(([key, object]) => {
|
|
33
|
+
if ("$ref" in object || !object.properties)
|
|
34
|
+
return;
|
|
35
|
+
const properties = object.properties;
|
|
36
|
+
schemas.push(new Schema({
|
|
37
|
+
properties,
|
|
38
|
+
name: key,
|
|
39
|
+
requireds: object.required || [],
|
|
40
|
+
}));
|
|
41
|
+
});
|
|
42
|
+
console.log(`${schemas.length} schemas gerados com sucesso.`);
|
|
43
|
+
return schemas;
|
|
44
|
+
}
|
|
45
|
+
getModules() {
|
|
46
|
+
const methodsMap = new Map();
|
|
47
|
+
const modules = [];
|
|
48
|
+
for (const [rawEndpoint, object] of Object.entries(this.paths)) {
|
|
49
|
+
const methods = ["get", "patch", "post", "put", "delete"];
|
|
50
|
+
let entity;
|
|
51
|
+
const operations = [];
|
|
52
|
+
const splittedEntitys = rawEndpoint.split("/");
|
|
53
|
+
const filteredEntitys = splittedEntitys.filter((item) => item !== "" && !item.includes("{"));
|
|
54
|
+
const moduleName = filteredEntitys.map((x) => sanitizeKey(capitalizeFirstLetter(x))).join("");
|
|
55
|
+
const endpoint = filteredEntitys.join("/");
|
|
56
|
+
for (const method of methods) {
|
|
57
|
+
if (!object[method])
|
|
58
|
+
continue;
|
|
59
|
+
operations.push({ ...object[method], apiMethod: method });
|
|
60
|
+
const tags = object[method].tags;
|
|
61
|
+
if (!entity && tags) {
|
|
62
|
+
entity = tags.join("").split("/").join("");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!entity)
|
|
66
|
+
continue;
|
|
67
|
+
const existingOps = methodsMap.get(entity);
|
|
68
|
+
if (existingOps) {
|
|
69
|
+
existingOps.operations.push(...operations);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
methodsMap.set(entity, { endpoint, operations, moduleName });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const [name, info] of methodsMap) {
|
|
76
|
+
modules.push(new Module({
|
|
77
|
+
name,
|
|
78
|
+
...info,
|
|
79
|
+
dir: this.generatedDir,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
return modules;
|
|
83
|
+
}
|
|
84
|
+
build() {
|
|
85
|
+
this.schemaFile.changeData([`import z from 'zod';`, ...this.schemas.map((s) => `${s.schema} ${s.type}`)].join("\n\n"));
|
|
86
|
+
this.schemaFile.save();
|
|
87
|
+
this.modules.forEach((module) => {
|
|
88
|
+
module.src.save();
|
|
89
|
+
});
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
localSave(data) {
|
|
93
|
+
this.localDoc.data = JSON.stringify(data);
|
|
94
|
+
this.localDoc.save();
|
|
95
|
+
}
|
|
96
|
+
clearSrc() {
|
|
97
|
+
fs.rmSync(this.src.path, { recursive: true, force: true });
|
|
98
|
+
fs.mkdirSync(this.src.path, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
package/dist/method.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Request } from "./request.js";
|
|
2
|
+
import { ZodProperty } from "./property.js";
|
|
3
|
+
import type { ReflectorOperation } from "./types/types.js";
|
|
4
|
+
export declare class Method {
|
|
5
|
+
name: string;
|
|
6
|
+
zodProperties: ZodProperty[];
|
|
7
|
+
description: string | undefined;
|
|
8
|
+
request: Request;
|
|
9
|
+
constructor(params: {
|
|
10
|
+
operation: ReflectorOperation;
|
|
11
|
+
moduleName: string;
|
|
12
|
+
});
|
|
13
|
+
private getParams;
|
|
14
|
+
private buildCallMethod;
|
|
15
|
+
private buildDescription;
|
|
16
|
+
build(): string | undefined;
|
|
17
|
+
}
|
package/dist/method.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Request } from "./request.js";
|
|
2
|
+
import { ZodProperty } from "./property.js";
|
|
3
|
+
import { createDangerMessage } from "./helpers.js";
|
|
4
|
+
export class Method {
|
|
5
|
+
name;
|
|
6
|
+
zodProperties;
|
|
7
|
+
description;
|
|
8
|
+
request;
|
|
9
|
+
constructor(params) {
|
|
10
|
+
const { operation } = params;
|
|
11
|
+
this.request = new Request(operation);
|
|
12
|
+
this.description = operation.description;
|
|
13
|
+
this.name = operation.operationId?.split("_")[1] ?? this.request.apiType;
|
|
14
|
+
const { parameters } = this.getParams(params);
|
|
15
|
+
this.zodProperties = parameters;
|
|
16
|
+
}
|
|
17
|
+
getParams(params) {
|
|
18
|
+
const { operation } = params;
|
|
19
|
+
if (!operation.parameters || operation.parameters?.length === 0) {
|
|
20
|
+
return { parameters: [] };
|
|
21
|
+
}
|
|
22
|
+
const parameters = [];
|
|
23
|
+
operation.parameters.forEach((object) => {
|
|
24
|
+
if ("$ref" in object)
|
|
25
|
+
return;
|
|
26
|
+
if (!object.schema)
|
|
27
|
+
return;
|
|
28
|
+
const { required, name, description, schema } = object;
|
|
29
|
+
if ("$ref" in schema)
|
|
30
|
+
return;
|
|
31
|
+
return parameters.push(new ZodProperty({
|
|
32
|
+
name,
|
|
33
|
+
example: schema.default,
|
|
34
|
+
schemaObject: schema,
|
|
35
|
+
type: schema.type,
|
|
36
|
+
description,
|
|
37
|
+
required: required || true,
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
return { parameters };
|
|
41
|
+
}
|
|
42
|
+
buildCallMethod() {
|
|
43
|
+
const afterResponse = [];
|
|
44
|
+
const beforeResponse = [];
|
|
45
|
+
const props = this.zodProperties.map((x) => x.name).join(",");
|
|
46
|
+
const parameters = `
|
|
47
|
+
const bundle = repo.intercept.bundle(this.parameters)
|
|
48
|
+
const {${props}} = bundle
|
|
49
|
+
`;
|
|
50
|
+
const query = `
|
|
51
|
+
queryData: {${props}}
|
|
52
|
+
`;
|
|
53
|
+
if (this.request.apiType === "get") {
|
|
54
|
+
if (this.zodProperties.length > 0) {
|
|
55
|
+
afterResponse.push(parameters);
|
|
56
|
+
beforeResponse.push(`\n`);
|
|
57
|
+
}
|
|
58
|
+
if (this.request.attributeType === "list") {
|
|
59
|
+
beforeResponse.push(`const {data, ...params} = response`);
|
|
60
|
+
beforeResponse.push("\n\n");
|
|
61
|
+
beforeResponse.push(`this.list = data`);
|
|
62
|
+
beforeResponse.push(`repo.intercept.rebuild(this.parameters, params)`);
|
|
63
|
+
}
|
|
64
|
+
else if (this.request.attributeType === "entity") {
|
|
65
|
+
beforeResponse.push(`this.entity = response`);
|
|
66
|
+
}
|
|
67
|
+
return `
|
|
68
|
+
${afterResponse.join(";")}
|
|
69
|
+
const response = await repo.api.get<${this.request.responseType}, unknown>({
|
|
70
|
+
endpoint: this.endpoint, ${query}
|
|
71
|
+
})
|
|
72
|
+
${beforeResponse.join(";")}
|
|
73
|
+
|
|
74
|
+
return response
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
else if (this.request.apiType === "post" || this.request.apiType === "put") {
|
|
78
|
+
let data = "";
|
|
79
|
+
if (this.request.bodyType) {
|
|
80
|
+
data = `const data = repo.intercept.bundle(this.forms.${this.name})`;
|
|
81
|
+
}
|
|
82
|
+
return `
|
|
83
|
+
${data}
|
|
84
|
+
|
|
85
|
+
const response = await repo.api.post<${this.request.responseType}>({
|
|
86
|
+
endpoint: this.endpoint,
|
|
87
|
+
${data ? "data" : ""}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
return response
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
else if (this.request.apiType === "delete") {
|
|
94
|
+
const props = this.zodProperties.map((x) => x.name).join(",");
|
|
95
|
+
return `
|
|
96
|
+
const {${props}} = this.parameters
|
|
97
|
+
|
|
98
|
+
const response = await repo.api.delete<${this.request.responseType}, unknown>({
|
|
99
|
+
endpoint: this.endpoint, ${query}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
this.clear()
|
|
103
|
+
return response
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
buildDescription() {
|
|
109
|
+
return `/** ${this.description ?? ""} */`;
|
|
110
|
+
}
|
|
111
|
+
build() {
|
|
112
|
+
const content = this.buildCallMethod();
|
|
113
|
+
if (!content)
|
|
114
|
+
return;
|
|
115
|
+
if (this.name === "list")
|
|
116
|
+
this.name = "listAll";
|
|
117
|
+
const hasProprierties = this.zodProperties.length > 0;
|
|
118
|
+
if (!hasProprierties && this.request.apiType === "delete") {
|
|
119
|
+
createDangerMessage(`${this.name} não vai funcionar, pois não aceita parâmetros na requisição.`);
|
|
120
|
+
}
|
|
121
|
+
return `
|
|
122
|
+
${this.buildDescription()}
|
|
123
|
+
async ${this.name}() {
|
|
124
|
+
${content}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Source } from "./file.js";
|
|
2
|
+
import { Method } from "./method.js";
|
|
3
|
+
import { ReflectorOperation } from "./types/types.js";
|
|
4
|
+
export declare class Module {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly src: Source;
|
|
7
|
+
readonly endpoint: string;
|
|
8
|
+
readonly moduleName: string;
|
|
9
|
+
imports: string[];
|
|
10
|
+
parameters: string[];
|
|
11
|
+
methods: Method[];
|
|
12
|
+
types: {
|
|
13
|
+
raw: string[];
|
|
14
|
+
constructor: string[];
|
|
15
|
+
}[];
|
|
16
|
+
constructor(params: {
|
|
17
|
+
name: string;
|
|
18
|
+
moduleName: string;
|
|
19
|
+
operations: ReflectorOperation[];
|
|
20
|
+
endpoint: string;
|
|
21
|
+
dir: string;
|
|
22
|
+
});
|
|
23
|
+
private creator;
|
|
24
|
+
private getPath;
|
|
25
|
+
private buildMethods;
|
|
26
|
+
private getParameters;
|
|
27
|
+
private buildImports;
|
|
28
|
+
private buildClass;
|
|
29
|
+
buildFile(modulesAttributes: string[], moduleTypes: string[]): string;
|
|
30
|
+
}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { Source } from "./file.js";
|
|
4
|
+
import { capitalizeFirstLetter, createDangerMessage, stripState } from "./helpers.js";
|
|
5
|
+
import { Method } from "./method.js";
|
|
6
|
+
export class Module {
|
|
7
|
+
name;
|
|
8
|
+
src;
|
|
9
|
+
endpoint;
|
|
10
|
+
moduleName;
|
|
11
|
+
imports;
|
|
12
|
+
parameters;
|
|
13
|
+
methods;
|
|
14
|
+
types;
|
|
15
|
+
constructor(params) {
|
|
16
|
+
const { name, operations, endpoint, dir, moduleName } = params;
|
|
17
|
+
this.moduleName = moduleName;
|
|
18
|
+
this.imports = ["// AUTO GERADO. QUEM ALTERAR GOSTA DE RAPAZES!\n", 'import repo from "$repository/main"'];
|
|
19
|
+
this.types = [
|
|
20
|
+
{
|
|
21
|
+
raw: [],
|
|
22
|
+
constructor: [],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
this.name = capitalizeFirstLetter(name);
|
|
26
|
+
this.endpoint = endpoint;
|
|
27
|
+
const methods = operations.map((operation) => {
|
|
28
|
+
return new Method({
|
|
29
|
+
operation,
|
|
30
|
+
moduleName: this.name,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
// não vão entrar metodos que não tiverem uma resposta tipada
|
|
34
|
+
this.methods = methods.filter((op) => {
|
|
35
|
+
if (!op.request.responseType) {
|
|
36
|
+
createDangerMessage(`Método [ ${op.name} ] do módulo [ ${this.moduleName} ] sem tipagem na resposta.`);
|
|
37
|
+
}
|
|
38
|
+
return op.request.responseType;
|
|
39
|
+
});
|
|
40
|
+
this.parameters = this.getParameters();
|
|
41
|
+
const { moduleAtributes, moduleTypes } = this.creator();
|
|
42
|
+
//sempre por último
|
|
43
|
+
this.src = new Source({
|
|
44
|
+
path: this.getPath(dir),
|
|
45
|
+
data: this.buildFile(moduleAtributes, moduleTypes),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
creator() {
|
|
49
|
+
const buildedModuleTypes = [];
|
|
50
|
+
const moduleAtributes = new Set([`endpoint = '${this.endpoint}'`]);
|
|
51
|
+
if (this.parameters.length > 0) {
|
|
52
|
+
buildedModuleTypes.push(`const ParametersSchema = z.object({${this.parameters}})`);
|
|
53
|
+
moduleAtributes.add(`parameters = $state(repo.newForm(ParametersSchema))`);
|
|
54
|
+
}
|
|
55
|
+
const form = [];
|
|
56
|
+
this.methods.forEach((method) => {
|
|
57
|
+
const { bodyType, responseType, attributeType } = method.request;
|
|
58
|
+
if (attributeType === "form" && bodyType) {
|
|
59
|
+
form.push({
|
|
60
|
+
name: method.name,
|
|
61
|
+
type: bodyType,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (attributeType === "entity") {
|
|
65
|
+
moduleAtributes.add(`list = $state<${responseType}[]>([])`);
|
|
66
|
+
moduleAtributes.add(`entity = $state<${responseType} | undefined>()`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const formSet = new Set();
|
|
70
|
+
form.forEach((f) => {
|
|
71
|
+
formSet.add(`${f.name}: repo.newForm(${f.type}Schema)`);
|
|
72
|
+
});
|
|
73
|
+
moduleAtributes.add(`
|
|
74
|
+
forms = $state({
|
|
75
|
+
${Array.from(formSet)}
|
|
76
|
+
})
|
|
77
|
+
`);
|
|
78
|
+
return {
|
|
79
|
+
moduleAtributes: Array.from(moduleAtributes),
|
|
80
|
+
moduleTypes: buildedModuleTypes,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
getPath(dir) {
|
|
84
|
+
const fileName = this.endpoint.split("/").slice(-2).join("-");
|
|
85
|
+
const inPath = path.join(dir, this.endpoint);
|
|
86
|
+
const outPath = path.join(inPath, `${fileName.toLowerCase()}.module.svelte.ts`);
|
|
87
|
+
fs.mkdirSync(inPath, { recursive: true });
|
|
88
|
+
return outPath;
|
|
89
|
+
}
|
|
90
|
+
buildMethods() {
|
|
91
|
+
return this.methods.map((method) => {
|
|
92
|
+
return method.build();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
getParameters() {
|
|
96
|
+
const set = new Set();
|
|
97
|
+
this.methods.forEach((method) => {
|
|
98
|
+
method.zodProperties.forEach((param) => set.add(param.buildedProp));
|
|
99
|
+
});
|
|
100
|
+
return Array.from(set);
|
|
101
|
+
}
|
|
102
|
+
buildImports() {
|
|
103
|
+
const entries = new Set();
|
|
104
|
+
this.methods.forEach((method) => {
|
|
105
|
+
const { bodyType, responseType, apiType } = method.request;
|
|
106
|
+
if (bodyType)
|
|
107
|
+
entries.add(`${bodyType}Schema`);
|
|
108
|
+
if (responseType) {
|
|
109
|
+
if (apiType === "delete")
|
|
110
|
+
entries.add(`${responseType}Schema`);
|
|
111
|
+
entries.add(`type ${responseType}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const cleanEntries = Array.from(entries).filter((x) => x != "type any");
|
|
115
|
+
if (cleanEntries.length === 0)
|
|
116
|
+
return "";
|
|
117
|
+
return `import { ${cleanEntries} } from '$api/schemas';`;
|
|
118
|
+
}
|
|
119
|
+
buildClass(modulesAttributes) {
|
|
120
|
+
const initAssignments = modulesAttributes.map((attr) => `this.${stripState(attr)}`).join(";");
|
|
121
|
+
return `
|
|
122
|
+
export class ${this.moduleName}Module {
|
|
123
|
+
${modulesAttributes.join(";")}
|
|
124
|
+
|
|
125
|
+
${this.buildMethods().join("\n")}
|
|
126
|
+
|
|
127
|
+
private init() {
|
|
128
|
+
${initAssignments}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
clear() {
|
|
132
|
+
this.init()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
buildFile(modulesAttributes, moduleTypes) {
|
|
138
|
+
return `
|
|
139
|
+
${this.imports.join(";")}
|
|
140
|
+
import z from "zod";
|
|
141
|
+
${this.buildImports()}
|
|
142
|
+
|
|
143
|
+
${moduleTypes.join(";")}
|
|
144
|
+
|
|
145
|
+
${this.buildClass(modulesAttributes)}
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SchemaObject } from "./types/open-api-spec.interface.js";
|
|
2
|
+
import { Example, ReflectorParamType } from "./types/types.js";
|
|
3
|
+
export declare class ZodProperty {
|
|
4
|
+
name: string;
|
|
5
|
+
example: Example;
|
|
6
|
+
type: ReflectorParamType;
|
|
7
|
+
buildedProp: string;
|
|
8
|
+
description: string;
|
|
9
|
+
required: boolean;
|
|
10
|
+
constructor(params: {
|
|
11
|
+
name: string;
|
|
12
|
+
schemaObject: SchemaObject;
|
|
13
|
+
type: ReflectorParamType;
|
|
14
|
+
example: Example | undefined;
|
|
15
|
+
required: boolean;
|
|
16
|
+
description?: string;
|
|
17
|
+
});
|
|
18
|
+
private getDtoName;
|
|
19
|
+
private getExample;
|
|
20
|
+
private deepValidator;
|
|
21
|
+
private isNullable;
|
|
22
|
+
private build;
|
|
23
|
+
}
|
package/dist/property.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ReflectorInput } from "./input.js";
|
|
2
|
+
const inputs = new ReflectorInput();
|
|
3
|
+
export class ZodProperty {
|
|
4
|
+
name;
|
|
5
|
+
example;
|
|
6
|
+
type;
|
|
7
|
+
buildedProp;
|
|
8
|
+
description = "";
|
|
9
|
+
required;
|
|
10
|
+
constructor(params) {
|
|
11
|
+
const { name, schemaObject, type, example, required } = params;
|
|
12
|
+
this.required = required;
|
|
13
|
+
this.name = name;
|
|
14
|
+
this.type = type;
|
|
15
|
+
this.example = this.getExample(example);
|
|
16
|
+
this.buildedProp = this.build(schemaObject);
|
|
17
|
+
}
|
|
18
|
+
getDtoName(ref) {
|
|
19
|
+
const cavalos = ref.split("/");
|
|
20
|
+
const dto = cavalos[cavalos.length - 1];
|
|
21
|
+
return dto;
|
|
22
|
+
}
|
|
23
|
+
getExample(example) {
|
|
24
|
+
if (example)
|
|
25
|
+
return example;
|
|
26
|
+
switch (this.type) {
|
|
27
|
+
case "number":
|
|
28
|
+
return 1;
|
|
29
|
+
case "boolean":
|
|
30
|
+
return true;
|
|
31
|
+
case "string":
|
|
32
|
+
return "lorem ipsum";
|
|
33
|
+
default:
|
|
34
|
+
return "lorem ipsum";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// fiscalNumber = cpf | cnpj | fiscalNumber
|
|
38
|
+
// cep
|
|
39
|
+
// telefone
|
|
40
|
+
// CUID
|
|
41
|
+
// url
|
|
42
|
+
deepValidator() {
|
|
43
|
+
if (this.name === "email") {
|
|
44
|
+
return `z.email().default('${this.example}')`;
|
|
45
|
+
}
|
|
46
|
+
else if (this.name === "password") {
|
|
47
|
+
return inputs.password;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
isNullable() {
|
|
54
|
+
return this.required ? "" : ".nullable()";
|
|
55
|
+
}
|
|
56
|
+
build(value) {
|
|
57
|
+
const x = `${this.name}: z.${this.type}()${this.isNullable()}`;
|
|
58
|
+
switch (this.type) {
|
|
59
|
+
case "string": {
|
|
60
|
+
const deepValidation = this.deepValidator();
|
|
61
|
+
return deepValidation ? `${this.name}: ${deepValidation}` : `${x}.default('${this.example}')`;
|
|
62
|
+
}
|
|
63
|
+
case "boolean":
|
|
64
|
+
return `${x}.default(${this.example})`;
|
|
65
|
+
case "number": {
|
|
66
|
+
const number = JSON.stringify(this.example) ?? 1;
|
|
67
|
+
return `${x}.default(${number})`;
|
|
68
|
+
}
|
|
69
|
+
case "array": {
|
|
70
|
+
if (!value.items || !("$ref" in value.items))
|
|
71
|
+
return `${x}.default([])`;
|
|
72
|
+
const dto = this.getDtoName(value.items.$ref);
|
|
73
|
+
return `${this.name}: z.array(${dto}Schema).default(Array(10).fill(${dto}Schema.parse({})))`;
|
|
74
|
+
}
|
|
75
|
+
case "object":
|
|
76
|
+
return `${this.name}: z.any()`;
|
|
77
|
+
default:
|
|
78
|
+
return `${this.name}: z.any()`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ApiType, ReflectorOperation } from "./types/types.js";
|
|
2
|
+
type ReflectorRequestType = "entity" | "list" | "pagination" | "form" | "other";
|
|
3
|
+
export declare class Request {
|
|
4
|
+
readonly attributeType: ReflectorRequestType;
|
|
5
|
+
readonly apiType: ApiType;
|
|
6
|
+
bodyType?: string;
|
|
7
|
+
responseType?: string;
|
|
8
|
+
constructor(operation: ReflectorOperation);
|
|
9
|
+
private getTypeFromRequestBody;
|
|
10
|
+
private getTypeFromResponses;
|
|
11
|
+
private inferAttributeType;
|
|
12
|
+
private componentName;
|
|
13
|
+
/**
|
|
14
|
+
* Lê o primeiro media type do ContentObject e devolve:
|
|
15
|
+
* - string com nome de componente (quando é $ref),
|
|
16
|
+
* - SchemaObject inline,
|
|
17
|
+
* - ou undefined se nada aproveitável.
|
|
18
|
+
*/
|
|
19
|
+
private getFromContent;
|
|
20
|
+
/**
|
|
21
|
+
* Converte um "schema ou nome de tipo" em string (ou undefined).
|
|
22
|
+
* Cobre: string direta, $ref, allOf, array, properties (com foco em "data"), e fallback por `type`.
|
|
23
|
+
*/
|
|
24
|
+
private typeFromSchemaOrType;
|
|
25
|
+
/**
|
|
26
|
+
* Extrai o tipo a partir de `properties`, priorizando a propriedade `data`.
|
|
27
|
+
* Se `data` não existir ou não tiver tipo, retorna undefined.
|
|
28
|
+
*/
|
|
29
|
+
private typeFromProperties;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
function isRef(v) {
|
|
2
|
+
return !!v && typeof v === "object" && "$ref" in v;
|
|
3
|
+
}
|
|
4
|
+
export class Request {
|
|
5
|
+
attributeType;
|
|
6
|
+
apiType;
|
|
7
|
+
bodyType;
|
|
8
|
+
responseType;
|
|
9
|
+
constructor(operation) {
|
|
10
|
+
this.apiType = operation.apiMethod;
|
|
11
|
+
this.bodyType = this.getTypeFromRequestBody(operation.requestBody);
|
|
12
|
+
this.responseType = this.getTypeFromResponses(operation.responses);
|
|
13
|
+
this.attributeType = this.inferAttributeType(operation) ?? "other";
|
|
14
|
+
}
|
|
15
|
+
// ============= Derivações principais =======================================
|
|
16
|
+
getTypeFromRequestBody(requestBody) {
|
|
17
|
+
if (!requestBody || isRef(requestBody))
|
|
18
|
+
return undefined;
|
|
19
|
+
const schemaOrType = this.getFromContent(requestBody.content);
|
|
20
|
+
return this.typeFromSchemaOrType(schemaOrType);
|
|
21
|
+
}
|
|
22
|
+
getTypeFromResponses(responses) {
|
|
23
|
+
for (const response of Object.values(responses)) {
|
|
24
|
+
if (!response || isRef(response))
|
|
25
|
+
continue;
|
|
26
|
+
const schemaOrType = this.getFromContent(response.content);
|
|
27
|
+
const t = this.typeFromSchemaOrType(schemaOrType);
|
|
28
|
+
if (t !== undefined)
|
|
29
|
+
return t;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
inferAttributeType(operation) {
|
|
34
|
+
const method = operation.apiMethod;
|
|
35
|
+
if (method === "post" || method === "put" || method === "patch") {
|
|
36
|
+
return "form";
|
|
37
|
+
}
|
|
38
|
+
if (method === "get") {
|
|
39
|
+
const params = operation.parameters ?? [];
|
|
40
|
+
const hasPage = params.some((p) => !isRef(p) && p.name === "page");
|
|
41
|
+
return (hasPage ? "list" : "entity");
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
// ============= Helpers de Schema/Content ===================================
|
|
46
|
+
componentName(refObj) {
|
|
47
|
+
const parts = refObj.$ref.split("/");
|
|
48
|
+
return parts[parts.length - 1];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Lê o primeiro media type do ContentObject e devolve:
|
|
52
|
+
* - string com nome de componente (quando é $ref),
|
|
53
|
+
* - SchemaObject inline,
|
|
54
|
+
* - ou undefined se nada aproveitável.
|
|
55
|
+
*/
|
|
56
|
+
getFromContent(content) {
|
|
57
|
+
if (!content || isRef(content))
|
|
58
|
+
return undefined;
|
|
59
|
+
const first = Object.values(content)[0];
|
|
60
|
+
const schema = first?.schema;
|
|
61
|
+
if (!schema)
|
|
62
|
+
return undefined;
|
|
63
|
+
if (isRef(schema))
|
|
64
|
+
return this.componentName(schema);
|
|
65
|
+
return schema;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Converte um "schema ou nome de tipo" em string (ou undefined).
|
|
69
|
+
* Cobre: string direta, $ref, allOf, array, properties (com foco em "data"), e fallback por `type`.
|
|
70
|
+
*/
|
|
71
|
+
typeFromSchemaOrType(schemaOrType) {
|
|
72
|
+
if (!schemaOrType)
|
|
73
|
+
return undefined;
|
|
74
|
+
if (typeof schemaOrType === "string")
|
|
75
|
+
return schemaOrType;
|
|
76
|
+
const schema = schemaOrType;
|
|
77
|
+
// Composição: tenta extrair de allOf[0].properties
|
|
78
|
+
const allOfFirst = schema.allOf?.[0];
|
|
79
|
+
if (allOfFirst && !isRef(allOfFirst)) {
|
|
80
|
+
const t = this.typeFromProperties(allOfFirst.properties);
|
|
81
|
+
if (t !== undefined)
|
|
82
|
+
return t;
|
|
83
|
+
}
|
|
84
|
+
// Array -> tipo do item
|
|
85
|
+
if (schema.type === "array" && schema.items) {
|
|
86
|
+
const items = schema.items;
|
|
87
|
+
if (isRef(items))
|
|
88
|
+
return this.componentName(items);
|
|
89
|
+
return items.type;
|
|
90
|
+
}
|
|
91
|
+
// Properties -> prioriza "data"
|
|
92
|
+
if (schema.properties) {
|
|
93
|
+
const t = this.typeFromProperties(schema.properties);
|
|
94
|
+
if (t !== undefined)
|
|
95
|
+
return t;
|
|
96
|
+
}
|
|
97
|
+
// Fallback: usa o próprio `type`, se houver
|
|
98
|
+
return schema.type;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Extrai o tipo a partir de `properties`, priorizando a propriedade `data`.
|
|
102
|
+
* Se `data` não existir ou não tiver tipo, retorna undefined.
|
|
103
|
+
*/
|
|
104
|
+
typeFromProperties(properties) {
|
|
105
|
+
if (!properties)
|
|
106
|
+
return undefined;
|
|
107
|
+
const data = properties.data;
|
|
108
|
+
if (isRef(data))
|
|
109
|
+
return this.componentName(data);
|
|
110
|
+
if (data.type === "any")
|
|
111
|
+
return;
|
|
112
|
+
return data.type;
|
|
113
|
+
}
|
|
114
|
+
}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ZodProperty } from "./property.js";
|
|
2
|
+
import { SchemaObject, ReferenceObject } from "./types/open-api-spec.interface.js";
|
|
3
|
+
export declare class Schema {
|
|
4
|
+
name: string;
|
|
5
|
+
properties: ZodProperty[];
|
|
6
|
+
type: string;
|
|
7
|
+
schema: string;
|
|
8
|
+
constructor(params: {
|
|
9
|
+
properties: Record<string, SchemaObject | ReferenceObject>;
|
|
10
|
+
name: string;
|
|
11
|
+
requireds: string[];
|
|
12
|
+
});
|
|
13
|
+
}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ZodProperty } from "./property.js";
|
|
2
|
+
export class Schema {
|
|
3
|
+
name;
|
|
4
|
+
properties = [];
|
|
5
|
+
type;
|
|
6
|
+
schema;
|
|
7
|
+
constructor(params) {
|
|
8
|
+
const { name, properties, requireds } = params;
|
|
9
|
+
this.name = name;
|
|
10
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
11
|
+
if ("$ref" in value || !value?.type)
|
|
12
|
+
return;
|
|
13
|
+
const required = requireds.includes(key);
|
|
14
|
+
this.properties.push(new ZodProperty({
|
|
15
|
+
name: key,
|
|
16
|
+
schemaObject: value,
|
|
17
|
+
type: value.type,
|
|
18
|
+
example: value.example,
|
|
19
|
+
required,
|
|
20
|
+
}));
|
|
21
|
+
});
|
|
22
|
+
this.type = `export type ${name} = z.infer<typeof ${name}Schema>;`;
|
|
23
|
+
this.schema = `export const ${name}Schema = z.object({
|
|
24
|
+
${this.properties.map((p) => p.buildedProp)}
|
|
25
|
+
});`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
export interface OpenAPIObject {
|
|
2
|
+
openapi: string;
|
|
3
|
+
info: InfoObject;
|
|
4
|
+
servers?: ServerObject[];
|
|
5
|
+
paths: PathsObject;
|
|
6
|
+
components?: ComponentsObject;
|
|
7
|
+
security?: SecurityRequirementObject[];
|
|
8
|
+
tags?: TagObject[];
|
|
9
|
+
externalDocs?: ExternalDocumentationObject;
|
|
10
|
+
}
|
|
11
|
+
export interface InfoObject {
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
termsOfService?: string;
|
|
15
|
+
contact?: ContactObject;
|
|
16
|
+
license?: LicenseObject;
|
|
17
|
+
version: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContactObject {
|
|
20
|
+
name?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
email?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface LicenseObject {
|
|
25
|
+
name: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ServerObject {
|
|
29
|
+
url: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
variables?: Record<string, ServerVariableObject>;
|
|
32
|
+
}
|
|
33
|
+
export interface ServerVariableObject {
|
|
34
|
+
enum?: string[] | boolean[] | number[];
|
|
35
|
+
default: string | boolean | number;
|
|
36
|
+
description?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ComponentsObject {
|
|
39
|
+
schemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
40
|
+
responses?: Record<string, ResponseObject | ReferenceObject>;
|
|
41
|
+
parameters?: Record<string, ParameterObject | ReferenceObject>;
|
|
42
|
+
examples?: Record<string, ExampleObject | ReferenceObject>;
|
|
43
|
+
requestBodies?: Record<string, RequestBodyObject | ReferenceObject>;
|
|
44
|
+
headers?: Record<string, HeaderObject | ReferenceObject>;
|
|
45
|
+
securitySchemes?: Record<string, SecuritySchemeObject | ReferenceObject>;
|
|
46
|
+
links?: Record<string, LinkObject | ReferenceObject>;
|
|
47
|
+
callbacks?: Record<string, CallbackObject | ReferenceObject>;
|
|
48
|
+
}
|
|
49
|
+
export type PathsObject = Record<string, PathItemObject>;
|
|
50
|
+
export interface PathItemObject {
|
|
51
|
+
$ref?: string;
|
|
52
|
+
summary?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
get?: OperationObject;
|
|
55
|
+
put?: OperationObject;
|
|
56
|
+
post?: OperationObject;
|
|
57
|
+
delete?: OperationObject;
|
|
58
|
+
options?: OperationObject;
|
|
59
|
+
head?: OperationObject;
|
|
60
|
+
patch?: OperationObject;
|
|
61
|
+
trace?: OperationObject;
|
|
62
|
+
servers?: ServerObject[];
|
|
63
|
+
parameters?: (ParameterObject | ReferenceObject)[];
|
|
64
|
+
}
|
|
65
|
+
export interface OperationObject {
|
|
66
|
+
tags?: string[];
|
|
67
|
+
summary?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
externalDocs?: ExternalDocumentationObject;
|
|
70
|
+
operationId?: string;
|
|
71
|
+
parameters?: (ParameterObject | ReferenceObject)[];
|
|
72
|
+
requestBody?: RequestBodyObject | ReferenceObject;
|
|
73
|
+
responses: ResponsesObject;
|
|
74
|
+
callbacks?: CallbacksObject;
|
|
75
|
+
deprecated?: boolean;
|
|
76
|
+
security?: SecurityRequirementObject[];
|
|
77
|
+
servers?: ServerObject[];
|
|
78
|
+
}
|
|
79
|
+
export interface ExternalDocumentationObject {
|
|
80
|
+
description?: string;
|
|
81
|
+
url: string;
|
|
82
|
+
}
|
|
83
|
+
export type ParameterLocation = "query" | "header" | "path" | "cookie";
|
|
84
|
+
export type ParameterStyle = "matrix" | "label" | "form" | "simple" | "spaceDelimited" | "pipeDelimited" | "deepObject";
|
|
85
|
+
export interface BaseParameterObject {
|
|
86
|
+
description?: string;
|
|
87
|
+
required?: boolean;
|
|
88
|
+
deprecated?: boolean;
|
|
89
|
+
allowEmptyValue?: boolean;
|
|
90
|
+
style?: ParameterStyle;
|
|
91
|
+
explode?: boolean;
|
|
92
|
+
allowReserved?: boolean;
|
|
93
|
+
schema?: SchemaObject | ReferenceObject;
|
|
94
|
+
examples?: Record<string, ExampleObject | ReferenceObject>;
|
|
95
|
+
example?: any;
|
|
96
|
+
content?: ContentObject;
|
|
97
|
+
}
|
|
98
|
+
export interface ParameterObject extends BaseParameterObject {
|
|
99
|
+
name: string;
|
|
100
|
+
in: ParameterLocation;
|
|
101
|
+
}
|
|
102
|
+
export interface RequestBodyObject {
|
|
103
|
+
description?: string;
|
|
104
|
+
content: ContentObject;
|
|
105
|
+
required?: boolean;
|
|
106
|
+
}
|
|
107
|
+
export type ContentObject = Record<string, MediaTypeObject>;
|
|
108
|
+
export interface MediaTypeObject {
|
|
109
|
+
schema?: SchemaObject | ReferenceObject;
|
|
110
|
+
examples?: ExamplesObject;
|
|
111
|
+
example?: any;
|
|
112
|
+
encoding?: EncodingObject;
|
|
113
|
+
}
|
|
114
|
+
export type EncodingObject = Record<string, EncodingPropertyObject>;
|
|
115
|
+
export interface EncodingPropertyObject {
|
|
116
|
+
contentType?: string;
|
|
117
|
+
headers?: Record<string, HeaderObject | ReferenceObject>;
|
|
118
|
+
style?: string;
|
|
119
|
+
explode?: boolean;
|
|
120
|
+
allowReserved?: boolean;
|
|
121
|
+
}
|
|
122
|
+
export interface ResponsesObject extends Record<string, ResponseObject | ReferenceObject | undefined> {
|
|
123
|
+
default?: ResponseObject | ReferenceObject;
|
|
124
|
+
}
|
|
125
|
+
export interface ResponseObject {
|
|
126
|
+
description: string;
|
|
127
|
+
headers?: HeadersObject;
|
|
128
|
+
content?: ContentObject;
|
|
129
|
+
links?: LinksObject;
|
|
130
|
+
}
|
|
131
|
+
export type CallbacksObject = Record<string, CallbackObject | ReferenceObject>;
|
|
132
|
+
export type CallbackObject = Record<string, PathItemObject>;
|
|
133
|
+
export type HeadersObject = Record<string, HeaderObject | ReferenceObject>;
|
|
134
|
+
export interface ExampleObject {
|
|
135
|
+
summary?: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
value?: any;
|
|
138
|
+
externalValue?: string;
|
|
139
|
+
}
|
|
140
|
+
export type LinksObject = Record<string, LinkObject | ReferenceObject>;
|
|
141
|
+
export interface LinkObject {
|
|
142
|
+
operationRef?: string;
|
|
143
|
+
operationId?: string;
|
|
144
|
+
parameters?: LinkParametersObject;
|
|
145
|
+
requestBody?: any | string;
|
|
146
|
+
description?: string;
|
|
147
|
+
server?: ServerObject;
|
|
148
|
+
}
|
|
149
|
+
export type LinkParametersObject = Record<string, any>;
|
|
150
|
+
export type HeaderObject = BaseParameterObject;
|
|
151
|
+
export interface TagObject {
|
|
152
|
+
name: string;
|
|
153
|
+
description?: string;
|
|
154
|
+
externalDocs?: ExternalDocumentationObject;
|
|
155
|
+
}
|
|
156
|
+
export type ExamplesObject = Record<string, ExampleObject | ReferenceObject>;
|
|
157
|
+
export interface ReferenceObject {
|
|
158
|
+
$ref: string;
|
|
159
|
+
}
|
|
160
|
+
export interface SchemaObject {
|
|
161
|
+
nullable?: boolean;
|
|
162
|
+
discriminator?: DiscriminatorObject;
|
|
163
|
+
readOnly?: boolean;
|
|
164
|
+
writeOnly?: boolean;
|
|
165
|
+
xml?: XmlObject;
|
|
166
|
+
externalDocs?: ExternalDocumentationObject;
|
|
167
|
+
example?: any;
|
|
168
|
+
examples?: any[] | Record<string, any>;
|
|
169
|
+
deprecated?: boolean;
|
|
170
|
+
type?: string;
|
|
171
|
+
allOf?: (SchemaObject | ReferenceObject)[];
|
|
172
|
+
oneOf?: (SchemaObject | ReferenceObject)[];
|
|
173
|
+
anyOf?: (SchemaObject | ReferenceObject)[];
|
|
174
|
+
not?: SchemaObject | ReferenceObject;
|
|
175
|
+
items?: SchemaObject | ReferenceObject;
|
|
176
|
+
properties?: Record<string, SchemaObject | ReferenceObject>;
|
|
177
|
+
additionalProperties?: SchemaObject | ReferenceObject | boolean;
|
|
178
|
+
patternProperties?: SchemaObject | ReferenceObject | any;
|
|
179
|
+
description?: string;
|
|
180
|
+
format?: string;
|
|
181
|
+
default?: any;
|
|
182
|
+
title?: string;
|
|
183
|
+
multipleOf?: number;
|
|
184
|
+
maximum?: number;
|
|
185
|
+
exclusiveMaximum?: boolean;
|
|
186
|
+
minimum?: number;
|
|
187
|
+
exclusiveMinimum?: boolean;
|
|
188
|
+
maxLength?: number;
|
|
189
|
+
minLength?: number;
|
|
190
|
+
pattern?: string;
|
|
191
|
+
maxItems?: number;
|
|
192
|
+
minItems?: number;
|
|
193
|
+
uniqueItems?: boolean;
|
|
194
|
+
maxProperties?: number;
|
|
195
|
+
minProperties?: number;
|
|
196
|
+
required?: string[];
|
|
197
|
+
enum?: any[];
|
|
198
|
+
"x-enumNames"?: string[];
|
|
199
|
+
}
|
|
200
|
+
export type SchemasObject = Record<string, SchemaObject>;
|
|
201
|
+
export interface DiscriminatorObject {
|
|
202
|
+
propertyName: string;
|
|
203
|
+
mapping?: Record<string, string>;
|
|
204
|
+
}
|
|
205
|
+
export interface XmlObject {
|
|
206
|
+
name?: string;
|
|
207
|
+
namespace?: string;
|
|
208
|
+
prefix?: string;
|
|
209
|
+
attribute?: boolean;
|
|
210
|
+
wrapped?: boolean;
|
|
211
|
+
}
|
|
212
|
+
export type SecuritySchemeType = "apiKey" | "http" | "oauth2" | "openIdConnect";
|
|
213
|
+
export interface SecuritySchemeObject {
|
|
214
|
+
type: SecuritySchemeType;
|
|
215
|
+
description?: string;
|
|
216
|
+
name?: string;
|
|
217
|
+
in?: string;
|
|
218
|
+
scheme?: string;
|
|
219
|
+
bearerFormat?: string;
|
|
220
|
+
flows?: OAuthFlowsObject;
|
|
221
|
+
openIdConnectUrl?: string;
|
|
222
|
+
"x-tokenName"?: string;
|
|
223
|
+
[extension: `x-${string}`]: any;
|
|
224
|
+
}
|
|
225
|
+
export interface OAuthFlowsObject {
|
|
226
|
+
implicit?: OAuthFlowObject;
|
|
227
|
+
password?: OAuthFlowObject;
|
|
228
|
+
clientCredentials?: OAuthFlowObject;
|
|
229
|
+
authorizationCode?: OAuthFlowObject;
|
|
230
|
+
}
|
|
231
|
+
export interface OAuthFlowObject {
|
|
232
|
+
authorizationUrl?: string;
|
|
233
|
+
tokenUrl?: string;
|
|
234
|
+
refreshUrl?: string;
|
|
235
|
+
scopes: ScopesObject;
|
|
236
|
+
}
|
|
237
|
+
export type ScopesObject = Record<string, any>;
|
|
238
|
+
export type SecurityRequirementObject = Record<string, string[]>;
|
|
239
|
+
export type ExtensionLocation = "root" | "info";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OperationObject } from "./open-api-spec.interface.js";
|
|
2
|
+
export type ReflectorParamType = "string" | "boolean" | "number" | "array" | "object";
|
|
3
|
+
export type ApiType = "get" | "post" | "delete" | "patch" | "put";
|
|
4
|
+
export type ReflectorOperation = OperationObject & {
|
|
5
|
+
apiMethod: ApiType;
|
|
6
|
+
};
|
|
7
|
+
export type Info = {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
operations: ReflectorOperation[];
|
|
10
|
+
moduleName: string;
|
|
11
|
+
};
|
|
12
|
+
export type Example = string | boolean | number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-reflector",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reflects zod types from openAPI schemas",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"reflect": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"axios": "^1.12.2",
|
|
25
|
+
"dotenv": "^16.4.5"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^24.8.1",
|
|
29
|
+
"prettier": "^3.6.2",
|
|
30
|
+
"typescript": "^5.9.3"
|
|
31
|
+
}
|
|
32
|
+
}
|