svelte-reflector 2.1.9 → 2.5.1
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/dist/core/Reflector.js +2 -1
- package/dist/core/api/ApiClassBuilder.js +12 -12
- package/dist/core/config/ReflectorConfig.d.ts +3 -1
- package/dist/core/config/ReflectorConfig.js +1 -0
- package/dist/core/generators/ApiCallStrategy.d.ts +1 -0
- package/dist/core/generators/ApiCallStrategy.js +10 -1
- package/dist/core/generators/CallMethodGenerator.js +6 -2
- package/dist/core/generators/CallStrategy.d.ts +6 -1
- package/dist/core/generators/ModuleCallStrategy.d.ts +1 -0
- package/dist/core/generators/ModuleCallStrategy.js +10 -1
- package/dist/core/module/Module.d.ts +2 -0
- package/dist/core/module/Module.js +4 -1
- package/dist/core/module/ModuleMethodProcessor.d.ts +2 -0
- package/dist/core/module/ModuleMethodProcessor.js +3 -0
- package/dist/core/module/ModuleSchemaFileBuilder.js +15 -1
- package/dist/core/schema/DiscriminatedInterfaceRenderer.d.ts +35 -0
- package/dist/core/schema/DiscriminatedInterfaceRenderer.js +70 -0
- package/dist/core/schema/ReflectorInterface.d.ts +2 -0
- package/dist/core/schema/ReflectorInterface.js +4 -1
- package/dist/core/schema/Schema.d.ts +18 -0
- package/dist/core/schema/Schema.js +41 -0
- package/dist/core/schema/SchemaClassRenderer.d.ts +4 -0
- package/dist/core/schema/SchemaClassRenderer.js +69 -11
- package/dist/core/schema/SchemaDependencyCollector.d.ts +3 -0
- package/dist/core/schema/SchemaDependencyCollector.js +10 -1
- package/dist/core/schema/SchemaPropertyClassifier.d.ts +2 -1
- package/dist/core/schema/SchemaPropertyClassifier.js +14 -0
- package/dist/core/schema/SchemaRegistry.d.ts +2 -0
- package/dist/core/schema/SchemaRegistry.js +10 -1
- package/dist/helpers/generate-doc.helper.d.ts +1 -0
- package/dist/helpers/generate-doc.helper.js +5 -0
- package/dist/loaders/ConfigLoader.js +2 -0
- package/dist/props/array.property.d.ts +1 -0
- package/dist/props/array.property.js +6 -0
- package/dist/props/enum.property.d.ts +1 -0
- package/dist/props/enum.property.js +4 -0
- package/dist/props/object.property.d.ts +1 -0
- package/dist/props/object.property.js +13 -0
- package/dist/props/primitive.property.d.ts +3 -0
- package/dist/props/primitive.property.js +25 -6
- package/dist/props/union.property.d.ts +37 -0
- package/dist/props/union.property.js +62 -0
- package/dist/runtime/reflector.svelte.ts +229 -42
- package/dist/types/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
import { DiscriminatedInterfaceRenderer } from "./DiscriminatedInterfaceRenderer.js";
|
|
1
2
|
import { ReflectorInterface } from "./ReflectorInterface.js";
|
|
2
3
|
export class SchemaClassRenderer {
|
|
3
4
|
static render(params) {
|
|
4
|
-
const { name, primitiveProps, arrayProps, objectProps, enumProps } = params;
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const { name, primitiveProps, arrayProps, objectProps, enumProps, unionProps } = params;
|
|
6
|
+
const mode = params.mode ?? "response";
|
|
7
|
+
// A `oneOf` with a usable discriminator turns the Interface into a discriminated
|
|
8
|
+
// union type alias; the matching `discriminated()` accessor is emitted below.
|
|
9
|
+
const discriminatedUnion = unionProps.find((u) => u.hasDiscriminator);
|
|
10
|
+
const interfaceStr = discriminatedUnion
|
|
11
|
+
? DiscriminatedInterfaceRenderer.render({
|
|
12
|
+
name,
|
|
13
|
+
union: discriminatedUnion,
|
|
14
|
+
primitiveProps,
|
|
15
|
+
arrayProps,
|
|
16
|
+
objectProps,
|
|
17
|
+
enumProps,
|
|
18
|
+
otherUnionProps: unionProps.filter((u) => u !== discriminatedUnion),
|
|
19
|
+
})
|
|
20
|
+
: new ReflectorInterface({ name, arrayProps, primitiveProps, objectProps, enumProps, unionProps }).builded;
|
|
12
21
|
const constructorThis = [];
|
|
13
22
|
const keys = [];
|
|
14
23
|
const bundleParams = [];
|
|
@@ -34,9 +43,54 @@ export class SchemaClassRenderer {
|
|
|
34
43
|
keys.push(prop.classBuild());
|
|
35
44
|
bundleParams.push(prop.bundleBuild());
|
|
36
45
|
});
|
|
37
|
-
|
|
46
|
+
unionProps.forEach((prop) => {
|
|
47
|
+
constructorThis.push(prop.constructorBuild());
|
|
48
|
+
keys.push(prop.classBuild());
|
|
49
|
+
bundleParams.push(prop.bundleBuild());
|
|
50
|
+
});
|
|
51
|
+
const constructorCode = `constructor(params?: { data?: ${name}Interface | undefined, empty?: boolean }) {
|
|
38
52
|
${constructorThis.join(";\n")}
|
|
39
53
|
}`;
|
|
54
|
+
const hydrateLines = [];
|
|
55
|
+
primitiveProps.forEach((p) => hydrateLines.push(p.hydrateBuild()));
|
|
56
|
+
arrayProps.forEach((p) => hydrateLines.push(p.hydrateBuild()));
|
|
57
|
+
objectProps.forEach((p) => hydrateLines.push(p.hydrateBuild()));
|
|
58
|
+
enumProps.forEach((p) => hydrateLines.push(p.hydrateBuild()));
|
|
59
|
+
unionProps.forEach((p) => hydrateLines.push(p.hydrateBuild()));
|
|
60
|
+
const hydrateCode = `
|
|
61
|
+
hydrate(data: Partial<${name}Interface>): void {
|
|
62
|
+
${hydrateLines.join(";\n")}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
reset(): void {
|
|
66
|
+
this.hydrate(new ${name}({ empty: true }).bundle() as Partial<${name}Interface>);
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
// Live class instances can't narrow `actionMeta` by `action` (independent
|
|
70
|
+
// `$state` fields), so expose a plain discriminated snapshot for consumers
|
|
71
|
+
// that want the narrowing. `bundle()` already returns the discriminated type
|
|
72
|
+
// (see below), so this is just an ergonomic, cast-free alias.
|
|
73
|
+
const discriminatedAccessor = discriminatedUnion
|
|
74
|
+
? `discriminated(): ${name}Interface { return this.bundle(); }`
|
|
75
|
+
: "";
|
|
76
|
+
// Request DTO serializa a partir das instâncias `BuildedInput` (carregam os flags
|
|
77
|
+
// required/nullable), não do `.value` pré-extraído — bundleInputs faz a coerção
|
|
78
|
+
// nullable ''→null e cobre array/aninhado. Response fica em bundleStrict (inalterado).
|
|
79
|
+
//
|
|
80
|
+
// Para um schema com união discriminada, `bundleStrict` infere a forma plana (action
|
|
81
|
+
// = enum inteiro), que NÃO é atribuível a nenhum membro da união — o array-root wrapper
|
|
82
|
+
// (`item.bundle()` tipado como `Interface[]` discriminada) quebraria. O único `as` do
|
|
83
|
+
// feature mora aqui, fazendo `bundle()` devolver a própria união; `discriminated()` e
|
|
84
|
+
// `reset()` ficam cast-free por consequência.
|
|
85
|
+
const bundleHelper = mode === "request" ? "inputs" : "strict";
|
|
86
|
+
const responseBundle = discriminatedUnion
|
|
87
|
+
? `return bundleStrict({ ${bundleParams.join(",")} }) as ${name}Interface`
|
|
88
|
+
: `return bundleStrict({ ${bundleParams.join(",")} })`;
|
|
89
|
+
const bundleBody = mode === "request"
|
|
90
|
+
? `return bundleInputs({ ${[...primitiveProps, ...arrayProps, ...objectProps, ...enumProps, ...unionProps]
|
|
91
|
+
.map((p) => `${p.name}: this.${p.name}`)
|
|
92
|
+
.join(",")} })`
|
|
93
|
+
: responseBundle;
|
|
40
94
|
const schema = `
|
|
41
95
|
export class ${name} {
|
|
42
96
|
${keys.join(";")}
|
|
@@ -45,10 +99,14 @@ export class SchemaClassRenderer {
|
|
|
45
99
|
|
|
46
100
|
${staticMethod}
|
|
47
101
|
|
|
102
|
+
${hydrateCode}
|
|
103
|
+
|
|
48
104
|
bundle(){
|
|
49
|
-
|
|
105
|
+
${bundleBody}
|
|
50
106
|
}
|
|
107
|
+
|
|
108
|
+
${discriminatedAccessor}
|
|
51
109
|
};`;
|
|
52
|
-
return { interface:
|
|
110
|
+
return { interface: interfaceStr, schema, bundleHelper };
|
|
53
111
|
}
|
|
54
112
|
}
|
|
@@ -2,10 +2,12 @@ import type { ArrayProp } from "../../props/array.property.js";
|
|
|
2
2
|
import type { EnumProp } from "../../props/enum.property.js";
|
|
3
3
|
import type { ObjectProp } from "../../props/object.property.js";
|
|
4
4
|
import type { PrimitiveProp } from "../../props/primitive.property.js";
|
|
5
|
+
import type { UnionProp } from "../../props/union.property.js";
|
|
5
6
|
export interface SchemaDependencies {
|
|
6
7
|
schemaDeps: string[];
|
|
7
8
|
enumDeps: string[];
|
|
8
9
|
customTypeDeps: string[];
|
|
10
|
+
sanitizerDeps: string[];
|
|
9
11
|
}
|
|
10
12
|
export declare class SchemaDependencyCollector {
|
|
11
13
|
static collect(props: {
|
|
@@ -13,5 +15,6 @@ export declare class SchemaDependencyCollector {
|
|
|
13
15
|
arrayProps: ArrayProp[];
|
|
14
16
|
objectProps: ObjectProp[];
|
|
15
17
|
enumProps: EnumProp[];
|
|
18
|
+
unionProps: UnionProp[];
|
|
16
19
|
}): SchemaDependencies;
|
|
17
20
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class SchemaDependencyCollector {
|
|
2
2
|
static collect(props) {
|
|
3
|
-
const { primitiveProps, arrayProps, objectProps, enumProps } = props;
|
|
3
|
+
const { primitiveProps, arrayProps, objectProps, enumProps, unionProps } = props;
|
|
4
4
|
const schemaDepsSet = new Set();
|
|
5
5
|
for (const prop of objectProps) {
|
|
6
6
|
schemaDepsSet.add(prop.type);
|
|
@@ -10,6 +10,11 @@ export class SchemaDependencyCollector {
|
|
|
10
10
|
schemaDepsSet.add(prop.type);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
for (const prop of unionProps) {
|
|
14
|
+
for (const variant of prop.variantTypes) {
|
|
15
|
+
schemaDepsSet.add(variant);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
13
18
|
const enumDepsSet = new Set();
|
|
14
19
|
for (const prop of enumProps) {
|
|
15
20
|
if (prop.enumName)
|
|
@@ -20,14 +25,18 @@ export class SchemaDependencyCollector {
|
|
|
20
25
|
enumDepsSet.add(prop.type);
|
|
21
26
|
}
|
|
22
27
|
const customTypeDepsSet = new Set();
|
|
28
|
+
const sanitizerDepsSet = new Set();
|
|
23
29
|
for (const prop of primitiveProps) {
|
|
24
30
|
if (prop.customType)
|
|
25
31
|
customTypeDepsSet.add(prop.customType);
|
|
32
|
+
if (prop.sanitizer)
|
|
33
|
+
sanitizerDepsSet.add(prop.sanitizer);
|
|
26
34
|
}
|
|
27
35
|
return {
|
|
28
36
|
schemaDeps: [...schemaDepsSet],
|
|
29
37
|
enumDeps: [...enumDepsSet],
|
|
30
38
|
customTypeDeps: [...customTypeDepsSet],
|
|
39
|
+
sanitizerDeps: [...sanitizerDepsSet],
|
|
31
40
|
};
|
|
32
41
|
}
|
|
33
42
|
}
|
|
@@ -2,10 +2,11 @@ import { ArrayProp } from "../../props/array.property.js";
|
|
|
2
2
|
import { EnumProp } from "../../props/enum.property.js";
|
|
3
3
|
import { ObjectProp } from "../../props/object.property.js";
|
|
4
4
|
import { PrimitiveProp } from "../../props/primitive.property.js";
|
|
5
|
+
import { UnionProp } from "../../props/union.property.js";
|
|
5
6
|
import type { SchemaObject, ReferenceObject } from "../../types/open-api-spec.interface.js";
|
|
6
7
|
import type { FieldConfigs } from "../../types/types.js";
|
|
7
8
|
import type { CodegenContext } from "../CodegenContext.js";
|
|
8
|
-
export type ClassifiedProp = PrimitiveProp | ArrayProp | ObjectProp | EnumProp;
|
|
9
|
+
export type ClassifiedProp = PrimitiveProp | ArrayProp | ObjectProp | EnumProp | UnionProp;
|
|
9
10
|
export declare class SchemaPropertyClassifier {
|
|
10
11
|
static classify(params: {
|
|
11
12
|
key: string;
|
|
@@ -2,11 +2,21 @@ import { ArrayProp } from "../../props/array.property.js";
|
|
|
2
2
|
import { EnumProp } from "../../props/enum.property.js";
|
|
3
3
|
import { ObjectProp } from "../../props/object.property.js";
|
|
4
4
|
import { PrimitiveProp } from "../../props/primitive.property.js";
|
|
5
|
+
import { UnionProp } from "../../props/union.property.js";
|
|
5
6
|
import { isReferenceObject, isPrimitiveEnumValues } from "../../helpers/helpers.js";
|
|
6
7
|
export class SchemaPropertyClassifier {
|
|
7
8
|
static classify(params) {
|
|
8
9
|
const { key, value, requireds, fieldConfigs, schemaName, context } = params;
|
|
9
10
|
if (isReferenceObject(value) || !value?.type) {
|
|
11
|
+
if (!isReferenceObject(value) && value.oneOf) {
|
|
12
|
+
return new UnionProp({
|
|
13
|
+
name: key,
|
|
14
|
+
oneOf: value.oneOf,
|
|
15
|
+
discriminator: value.discriminator,
|
|
16
|
+
isRequired: requireds.includes(key),
|
|
17
|
+
isNullable: value.nullable,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
10
20
|
if (!isReferenceObject(value) && value.additionalProperties) {
|
|
11
21
|
const fakeStringSchema = { ...value, type: "string" };
|
|
12
22
|
const config = fieldConfigs.get(key);
|
|
@@ -15,6 +25,7 @@ export class SchemaPropertyClassifier {
|
|
|
15
25
|
schemaObject: fakeStringSchema,
|
|
16
26
|
required: requireds.includes(key),
|
|
17
27
|
validator: config?.validator,
|
|
28
|
+
sanitizer: config?.sanitizer,
|
|
18
29
|
customType: config?.type,
|
|
19
30
|
isParam: undefined,
|
|
20
31
|
isNullable: value.nullable,
|
|
@@ -53,6 +64,7 @@ export class SchemaPropertyClassifier {
|
|
|
53
64
|
}
|
|
54
65
|
const config = fieldConfigs.get(key);
|
|
55
66
|
const validator = config?.validator;
|
|
67
|
+
const sanitizer = config?.sanitizer;
|
|
56
68
|
const customType = config?.type;
|
|
57
69
|
const type = value.type;
|
|
58
70
|
if (type === "object") {
|
|
@@ -63,6 +75,7 @@ export class SchemaPropertyClassifier {
|
|
|
63
75
|
schemaObject: fakeStringSchema,
|
|
64
76
|
required,
|
|
65
77
|
validator,
|
|
78
|
+
sanitizer,
|
|
66
79
|
customType,
|
|
67
80
|
isParam: undefined,
|
|
68
81
|
isNullable: value.nullable,
|
|
@@ -87,6 +100,7 @@ export class SchemaPropertyClassifier {
|
|
|
87
100
|
schemaObject: value,
|
|
88
101
|
required,
|
|
89
102
|
validator,
|
|
103
|
+
sanitizer,
|
|
90
104
|
customType,
|
|
91
105
|
isParam: undefined,
|
|
92
106
|
isNullable: value.nullable,
|
|
@@ -10,6 +10,8 @@ export declare class SchemaRegistry {
|
|
|
10
10
|
components: ComponentsObject;
|
|
11
11
|
fieldConfigs: FieldConfigs;
|
|
12
12
|
context: CodegenContext;
|
|
13
|
+
/** Nomes-raiz dos request body DTOs — eles e seu fecho transitivo serializam via bundleInputs */
|
|
14
|
+
requestBodyNames?: Set<string>;
|
|
13
15
|
});
|
|
14
16
|
/** Resolve transitive schema dependencies from a list of schema class names */
|
|
15
17
|
resolveTransitiveDeps(names: string[]): Schema[];
|
|
@@ -5,7 +5,7 @@ export class SchemaRegistry {
|
|
|
5
5
|
propertiesNames = new Set();
|
|
6
6
|
schemaMap = new Map();
|
|
7
7
|
constructor(params) {
|
|
8
|
-
const { components, fieldConfigs, context } = params;
|
|
8
|
+
const { components, fieldConfigs, context, requestBodyNames } = params;
|
|
9
9
|
const componentSchemas = components.schemas;
|
|
10
10
|
if (!componentSchemas)
|
|
11
11
|
return;
|
|
@@ -32,6 +32,15 @@ export class SchemaRegistry {
|
|
|
32
32
|
for (const schema of this.schemas) {
|
|
33
33
|
this.schemaMap.set(schema.name, schema);
|
|
34
34
|
}
|
|
35
|
+
// Request body DTOs (e seu fecho transitivo de deps) serializam via bundleInputs:
|
|
36
|
+
// o bundle() lê os flags do BuildedInput em vez de só o .value extraído. Transitivo
|
|
37
|
+
// porque um DTO aninhado (ex.: createBody.role → UserRole) também é serializado no
|
|
38
|
+
// request — marcar só a raiz deixaria o nested em bundleStrict (bug nested).
|
|
39
|
+
if (requestBodyNames && requestBodyNames.size > 0) {
|
|
40
|
+
for (const schema of this.resolveTransitiveDeps([...requestBodyNames])) {
|
|
41
|
+
schema.setRequestMode();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
35
44
|
console.log(`${this.schemas.length} schemas generated successfully.`);
|
|
36
45
|
}
|
|
37
46
|
/** Resolve transitive schema dependencies from a list of schema class names */
|
|
@@ -96,12 +96,17 @@ export function parseFieldConfigsFromConfig(code) {
|
|
|
96
96
|
// extrai validator (referência sem aspas, ex: validateInputs.email)
|
|
97
97
|
const validatorMatch = block.match(/validator\s*:\s*([A-Za-z0-9_$.]+)/);
|
|
98
98
|
const validator = validatorMatch?.[1]?.trim();
|
|
99
|
+
// extrai sanitizer (referência sem aspas, ex: sanitizers.phone)
|
|
100
|
+
const sanitizerMatch = block.match(/sanitizer\s*:\s*([A-Za-z0-9_$.]+)/);
|
|
101
|
+
const sanitizer = sanitizerMatch?.[1]?.trim();
|
|
99
102
|
// extrai type (string literal com aspas, ex: 'IconName')
|
|
100
103
|
const typeMatch = block.match(/type\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
101
104
|
const type = typeMatch?.[1]?.trim();
|
|
102
105
|
const config = { fields };
|
|
103
106
|
if (validator)
|
|
104
107
|
config.validator = validator;
|
|
108
|
+
if (sanitizer)
|
|
109
|
+
config.sanitizer = sanitizer;
|
|
105
110
|
if (type)
|
|
106
111
|
config.type = type;
|
|
107
112
|
results.push(config);
|
|
@@ -96,6 +96,12 @@ export class ArrayProp {
|
|
|
96
96
|
.join(", ")}]`;
|
|
97
97
|
return `readonly ${this.name} = new EnumQueryBuilder<${this.type}>({ key: '${this.name}', defaultValues: ${literal} })`;
|
|
98
98
|
}
|
|
99
|
+
hydrateBuild() {
|
|
100
|
+
if (this.isSchemaRef) {
|
|
101
|
+
return `if (data.${this.name} !== undefined) this.${this.name} = (data.${this.name} ?? []).map((i) => new ${this.type}({ data: i as never }))`;
|
|
102
|
+
}
|
|
103
|
+
return `if (data.${this.name} !== undefined) this.${this.name} = data.${this.name} ?? []`;
|
|
104
|
+
}
|
|
99
105
|
staticBuild() {
|
|
100
106
|
const result = this._isPrimitiveType ? "obj" : `new ${this.type}({ data: obj })`;
|
|
101
107
|
const aType = this._isPrimitiveType ? this.type : `${this.type}Interface`;
|
|
@@ -39,4 +39,8 @@ export class EnumProp {
|
|
|
39
39
|
bundleBuild() {
|
|
40
40
|
return `${this.name}: this.${this.name}?.value`;
|
|
41
41
|
}
|
|
42
|
+
hydrateBuild() {
|
|
43
|
+
const opt = this.isRequired ? "" : "?";
|
|
44
|
+
return `if (data.${this.name} !== undefined) this.${this.name}${opt}.hydrate(data.${this.name} as never)`;
|
|
45
|
+
}
|
|
42
46
|
}
|
|
@@ -31,4 +31,17 @@ export class ObjectProp {
|
|
|
31
31
|
const nullable = this.isNullable ? "?? null" : "";
|
|
32
32
|
return `${this.name}: this.${this.name}?.bundle() ${nullable}`;
|
|
33
33
|
}
|
|
34
|
+
hydrateBuild() {
|
|
35
|
+
if (this.isNullable) {
|
|
36
|
+
return `if (data.${this.name} !== undefined) {
|
|
37
|
+
if (data.${this.name} === null) this.${this.name} = null;
|
|
38
|
+
else if (this.${this.name}) this.${this.name}.hydrate(data.${this.name} as never);
|
|
39
|
+
else this.${this.name} = new ${this.type}({ data: data.${this.name} as never });
|
|
40
|
+
}`;
|
|
41
|
+
}
|
|
42
|
+
return `if (data.${this.name} !== undefined) {
|
|
43
|
+
if (this.${this.name}) this.${this.name}.hydrate(data.${this.name} as never);
|
|
44
|
+
else this.${this.name} = new ${this.type}({ data: data.${this.name} as never });
|
|
45
|
+
}`;
|
|
46
|
+
}
|
|
34
47
|
}
|
|
@@ -10,6 +10,7 @@ export declare class PrimitiveProp {
|
|
|
10
10
|
private readonly isNullable;
|
|
11
11
|
readonly rawType: ReflectorParamType;
|
|
12
12
|
readonly customType: string | undefined;
|
|
13
|
+
readonly sanitizer: string | undefined;
|
|
13
14
|
private readonly buildedConst;
|
|
14
15
|
private readonly example;
|
|
15
16
|
private readonly fallbackExample;
|
|
@@ -23,6 +24,7 @@ export declare class PrimitiveProp {
|
|
|
23
24
|
schemaObject: SchemaObject;
|
|
24
25
|
required: boolean;
|
|
25
26
|
validator: string | undefined;
|
|
27
|
+
sanitizer?: string | undefined;
|
|
26
28
|
customType?: string | undefined;
|
|
27
29
|
isParam: boolean | undefined;
|
|
28
30
|
isNullable?: boolean | undefined;
|
|
@@ -38,5 +40,6 @@ export declare class PrimitiveProp {
|
|
|
38
40
|
queryBuild(): string;
|
|
39
41
|
updateQueryBuild(): string;
|
|
40
42
|
bundleBuild(): string;
|
|
43
|
+
hydrateBuild(): string;
|
|
41
44
|
}
|
|
42
45
|
export {};
|
|
@@ -9,6 +9,7 @@ export class PrimitiveProp {
|
|
|
9
9
|
isNullable;
|
|
10
10
|
rawType;
|
|
11
11
|
customType;
|
|
12
|
+
sanitizer;
|
|
12
13
|
buildedConst;
|
|
13
14
|
example;
|
|
14
15
|
fallbackExample;
|
|
@@ -22,7 +23,7 @@ export class PrimitiveProp {
|
|
|
22
23
|
return this.isNullable || !this.required;
|
|
23
24
|
}
|
|
24
25
|
constructor(params) {
|
|
25
|
-
const { name, schemaObject, required, validator, customType, isParam, isNullable } = params;
|
|
26
|
+
const { name, schemaObject, required, validator, sanitizer, customType, isParam, isNullable } = params;
|
|
26
27
|
const { type: rawType } = schemaObject;
|
|
27
28
|
this.isNullable = !!isNullable;
|
|
28
29
|
const normalizedRawType = rawType === "integer" ? "number" : rawType;
|
|
@@ -38,10 +39,13 @@ export class PrimitiveProp {
|
|
|
38
39
|
this.isSpecial = treated.isSpecial;
|
|
39
40
|
this.rawType = type ?? "any";
|
|
40
41
|
this.customType = customType;
|
|
42
|
+
// sanitizer só vale para campo string — gate aqui garante que emissão e
|
|
43
|
+
// rastreio de dependência (import) concordam.
|
|
44
|
+
this.sanitizer = type === "string" ? sanitizer : undefined;
|
|
41
45
|
this.type = `BuildedInput<${buildedType}>`;
|
|
42
46
|
this.required = required;
|
|
43
47
|
this.isParam = !!isParam;
|
|
44
|
-
this.buildedConst = this.buildConst({ example, name: this.name, required, type, validator, emptyExample: this.example });
|
|
48
|
+
this.buildedConst = this.buildConst({ example, name: this.name, required, type, validator, sanitizer: this.sanitizer, emptyExample: this.example });
|
|
45
49
|
}
|
|
46
50
|
getEmptyExample(params) {
|
|
47
51
|
const { schemaObject, type, name } = params;
|
|
@@ -83,7 +87,7 @@ export class PrimitiveProp {
|
|
|
83
87
|
};
|
|
84
88
|
}
|
|
85
89
|
buildConst(params) {
|
|
86
|
-
const { name, required, type, validator } = params;
|
|
90
|
+
const { name, required, type, validator, sanitizer } = params;
|
|
87
91
|
const getValidator = (type) => {
|
|
88
92
|
if (type === "string") {
|
|
89
93
|
return "emptyString";
|
|
@@ -114,10 +118,22 @@ export class PrimitiveProp {
|
|
|
114
118
|
else if (this.customType) {
|
|
115
119
|
typeParam = `<${this.effectiveType}>`;
|
|
116
120
|
}
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
// Monta as props num array e junta com vírgula: com dois opcionais
|
|
122
|
+
// (validator + sanitizer) o controle manual de vírgula quebrava.
|
|
123
|
+
const props = [
|
|
124
|
+
`key: ${keyExpr}`,
|
|
125
|
+
`placeholder: ${this.example}`,
|
|
126
|
+
`example: ${buildedExample}`,
|
|
127
|
+
`required: ${required}`,
|
|
128
|
+
this.isNullable ? "nullable: true" : "",
|
|
129
|
+
this.max !== undefined ? `max: ${this.max}` : "",
|
|
130
|
+
buildedValidator(),
|
|
131
|
+
sanitizer ? `sanitizer: ${sanitizer}` : "",
|
|
132
|
+
]
|
|
133
|
+
.filter(Boolean)
|
|
134
|
+
.join(", ");
|
|
119
135
|
return `
|
|
120
|
-
build${typeParam}({
|
|
136
|
+
build${typeParam}({ ${props} })
|
|
121
137
|
`;
|
|
122
138
|
}
|
|
123
139
|
thisDot() {
|
|
@@ -154,4 +170,7 @@ export class PrimitiveProp {
|
|
|
154
170
|
bundleBuild() {
|
|
155
171
|
return `${this.name}: ${this.thisDot()}${this.name}?.value`;
|
|
156
172
|
}
|
|
173
|
+
hydrateBuild() {
|
|
174
|
+
return `if (data.${this.name} !== undefined) ${this.thisDot()}${this.name}.hydrate(data.${this.name} as never)`;
|
|
175
|
+
}
|
|
157
176
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DiscriminatorObject, ReferenceObject, SchemaObject } from "../types/open-api-spec.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* Property whose OpenAPI schema is a `oneOf` — a union of `$ref`ed component
|
|
4
|
+
* schemas, optionally carrying a `discriminator`. Only `$ref` variants are
|
|
5
|
+
* supported (inline `oneOf` members aren't promoted today).
|
|
6
|
+
*
|
|
7
|
+
* Runtime model is intentionally flat/passthrough: the class field stores the
|
|
8
|
+
* raw incoming object typed as the union of the variants' `Interface`s — there
|
|
9
|
+
* is no `new` (you can't instantiate a union) and no `.bundle()` on it
|
|
10
|
+
* (`bundleStrict`/`bundleInputs` pass plain objects through unchanged). The
|
|
11
|
+
* elegant discriminated narrowing lives in the schema's `Interface` type alias
|
|
12
|
+
* (see `DiscriminatedInterfaceRenderer`), not on the live class instance.
|
|
13
|
+
*/
|
|
14
|
+
export declare class UnionProp {
|
|
15
|
+
name: string;
|
|
16
|
+
/** Variant component names extracted from each `oneOf` `$ref` (deduped, order-preserving). */
|
|
17
|
+
readonly variantTypes: string[];
|
|
18
|
+
readonly discriminator: DiscriminatorObject | undefined;
|
|
19
|
+
private readonly required;
|
|
20
|
+
private readonly isNullable;
|
|
21
|
+
constructor(params: {
|
|
22
|
+
name: string;
|
|
23
|
+
oneOf: (SchemaObject | ReferenceObject)[];
|
|
24
|
+
discriminator?: DiscriminatorObject | undefined;
|
|
25
|
+
isRequired?: boolean;
|
|
26
|
+
isNullable?: boolean | undefined;
|
|
27
|
+
});
|
|
28
|
+
/** `AInterface | BInterface | ...` — the flat union used for the class field type. */
|
|
29
|
+
flatUnion(): string;
|
|
30
|
+
/** True when this union can render as a discriminated `Interface` (needs a mapping). */
|
|
31
|
+
get hasDiscriminator(): boolean;
|
|
32
|
+
classBuild(): string;
|
|
33
|
+
constructorBuild(): string;
|
|
34
|
+
bundleBuild(): string;
|
|
35
|
+
interfaceBuild(): string;
|
|
36
|
+
hydrateBuild(): string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { isReferenceObject } from "../helpers/helpers.js";
|
|
2
|
+
/**
|
|
3
|
+
* Property whose OpenAPI schema is a `oneOf` — a union of `$ref`ed component
|
|
4
|
+
* schemas, optionally carrying a `discriminator`. Only `$ref` variants are
|
|
5
|
+
* supported (inline `oneOf` members aren't promoted today).
|
|
6
|
+
*
|
|
7
|
+
* Runtime model is intentionally flat/passthrough: the class field stores the
|
|
8
|
+
* raw incoming object typed as the union of the variants' `Interface`s — there
|
|
9
|
+
* is no `new` (you can't instantiate a union) and no `.bundle()` on it
|
|
10
|
+
* (`bundleStrict`/`bundleInputs` pass plain objects through unchanged). The
|
|
11
|
+
* elegant discriminated narrowing lives in the schema's `Interface` type alias
|
|
12
|
+
* (see `DiscriminatedInterfaceRenderer`), not on the live class instance.
|
|
13
|
+
*/
|
|
14
|
+
export class UnionProp {
|
|
15
|
+
name;
|
|
16
|
+
/** Variant component names extracted from each `oneOf` `$ref` (deduped, order-preserving). */
|
|
17
|
+
variantTypes;
|
|
18
|
+
discriminator;
|
|
19
|
+
required;
|
|
20
|
+
isNullable;
|
|
21
|
+
constructor(params) {
|
|
22
|
+
const { name, oneOf, discriminator, isRequired, isNullable } = params;
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.discriminator = discriminator;
|
|
25
|
+
this.required = isRequired ?? true;
|
|
26
|
+
this.isNullable = !!isNullable;
|
|
27
|
+
const refs = oneOf
|
|
28
|
+
.filter(isReferenceObject)
|
|
29
|
+
.map((ref) => ref.$ref.split("/").at(-1) ?? "")
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
this.variantTypes = [...new Set(refs)];
|
|
32
|
+
}
|
|
33
|
+
/** `AInterface | BInterface | ...` — the flat union used for the class field type. */
|
|
34
|
+
flatUnion() {
|
|
35
|
+
return this.variantTypes.map((t) => `${t}Interface`).join(" | ");
|
|
36
|
+
}
|
|
37
|
+
/** True when this union can render as a discriminated `Interface` (needs a mapping). */
|
|
38
|
+
get hasDiscriminator() {
|
|
39
|
+
return !!this.discriminator?.mapping && Object.keys(this.discriminator.mapping).length > 0;
|
|
40
|
+
}
|
|
41
|
+
classBuild() {
|
|
42
|
+
const req = this.required ? "" : "?";
|
|
43
|
+
const nullable = this.isNullable ? " | null" : "";
|
|
44
|
+
// No `new` for a union — `$state<T>()` (no arg) yields `T | undefined`,
|
|
45
|
+
// populated by the constructor from the incoming data.
|
|
46
|
+
return `${this.name}${req} = $state<${this.flatUnion()}${nullable}>()`;
|
|
47
|
+
}
|
|
48
|
+
constructorBuild() {
|
|
49
|
+
return `this.${this.name} = params?.data?.${this.name}`;
|
|
50
|
+
}
|
|
51
|
+
bundleBuild() {
|
|
52
|
+
return `${this.name}: this.${this.name}`;
|
|
53
|
+
}
|
|
54
|
+
interfaceBuild() {
|
|
55
|
+
const req = this.required ? "" : "?";
|
|
56
|
+
const nullable = this.isNullable ? " | null" : "";
|
|
57
|
+
return `${this.name}${req}: ${this.flatUnion()}${nullable}`;
|
|
58
|
+
}
|
|
59
|
+
hydrateBuild() {
|
|
60
|
+
return `if (data.${this.name} !== undefined) this.${this.name} = data.${this.name}`;
|
|
61
|
+
}
|
|
62
|
+
}
|