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.
Files changed (45) hide show
  1. package/dist/core/Reflector.js +2 -1
  2. package/dist/core/api/ApiClassBuilder.js +12 -12
  3. package/dist/core/config/ReflectorConfig.d.ts +3 -1
  4. package/dist/core/config/ReflectorConfig.js +1 -0
  5. package/dist/core/generators/ApiCallStrategy.d.ts +1 -0
  6. package/dist/core/generators/ApiCallStrategy.js +10 -1
  7. package/dist/core/generators/CallMethodGenerator.js +6 -2
  8. package/dist/core/generators/CallStrategy.d.ts +6 -1
  9. package/dist/core/generators/ModuleCallStrategy.d.ts +1 -0
  10. package/dist/core/generators/ModuleCallStrategy.js +10 -1
  11. package/dist/core/module/Module.d.ts +2 -0
  12. package/dist/core/module/Module.js +4 -1
  13. package/dist/core/module/ModuleMethodProcessor.d.ts +2 -0
  14. package/dist/core/module/ModuleMethodProcessor.js +3 -0
  15. package/dist/core/module/ModuleSchemaFileBuilder.js +15 -1
  16. package/dist/core/schema/DiscriminatedInterfaceRenderer.d.ts +35 -0
  17. package/dist/core/schema/DiscriminatedInterfaceRenderer.js +70 -0
  18. package/dist/core/schema/ReflectorInterface.d.ts +2 -0
  19. package/dist/core/schema/ReflectorInterface.js +4 -1
  20. package/dist/core/schema/Schema.d.ts +18 -0
  21. package/dist/core/schema/Schema.js +41 -0
  22. package/dist/core/schema/SchemaClassRenderer.d.ts +4 -0
  23. package/dist/core/schema/SchemaClassRenderer.js +69 -11
  24. package/dist/core/schema/SchemaDependencyCollector.d.ts +3 -0
  25. package/dist/core/schema/SchemaDependencyCollector.js +10 -1
  26. package/dist/core/schema/SchemaPropertyClassifier.d.ts +2 -1
  27. package/dist/core/schema/SchemaPropertyClassifier.js +14 -0
  28. package/dist/core/schema/SchemaRegistry.d.ts +2 -0
  29. package/dist/core/schema/SchemaRegistry.js +10 -1
  30. package/dist/helpers/generate-doc.helper.d.ts +1 -0
  31. package/dist/helpers/generate-doc.helper.js +5 -0
  32. package/dist/loaders/ConfigLoader.js +2 -0
  33. package/dist/props/array.property.d.ts +1 -0
  34. package/dist/props/array.property.js +6 -0
  35. package/dist/props/enum.property.d.ts +1 -0
  36. package/dist/props/enum.property.js +4 -0
  37. package/dist/props/object.property.d.ts +1 -0
  38. package/dist/props/object.property.js +13 -0
  39. package/dist/props/primitive.property.d.ts +3 -0
  40. package/dist/props/primitive.property.js +25 -6
  41. package/dist/props/union.property.d.ts +37 -0
  42. package/dist/props/union.property.js +62 -0
  43. package/dist/runtime/reflector.svelte.ts +229 -42
  44. package/dist/types/types.d.ts +1 -0
  45. package/package.json +1 -1
@@ -33,7 +33,8 @@ export class Reflector {
33
33
  context: this.context,
34
34
  config: this.config,
35
35
  });
36
- this.registry = new SchemaRegistry({ components, fieldConfigs, context: this.context });
36
+ const requestBodyNames = new Set(this.modules.flatMap((m) => m.requestBodyNames));
37
+ this.registry = new SchemaRegistry({ components, fieldConfigs, context: this.context, requestBodyNames });
37
38
  this.schemas = this.registry.schemas;
38
39
  this.propertiesNames = this.registry.propertiesNames;
39
40
  }
@@ -53,17 +53,17 @@ export class ApiClassBuilder {
53
53
  this.imports.addReflectorImport("genericArrayBundler");
54
54
  }
55
55
  const className = capitalizeFirstLetter(method.name);
56
- const classCode = `
57
- export class ${className} {
58
- ${stateProps.join(";")}
59
- ${processedParams.paramAttributes.map((a) => `${a};`).join("\n ")}
60
-
61
- ${callMethod}
62
-
63
- reset() {
64
- ${resetLines.join(";")}
65
- }
66
- }
56
+ const classCode = `
57
+ export class ${className} {
58
+ ${stateProps.join(";")}
59
+ ${processedParams.paramAttributes.map((a) => `${a};`).join("\n ")}
60
+
61
+ ${callMethod}
62
+
63
+ reset() {
64
+ ${resetLines.join(";")}
65
+ }
66
+ }
67
67
  `;
68
68
  const paramCode = processedParams.paramClasses.join("\n");
69
69
  return { paramCode, classCode, schemaEntries };
@@ -91,7 +91,7 @@ export class ApiClassBuilder {
91
91
  const { attributeType, bodyType } = method.request;
92
92
  const lines = [];
93
93
  if (attributeType === "form" && bodyType) {
94
- lines.push(`this.form = new ${bodyType}()`);
94
+ lines.push(`this.form.reset()`);
95
95
  }
96
96
  else if (attributeType === "list") {
97
97
  lines.push("this.data = []");
@@ -6,8 +6,10 @@
6
6
  export interface ReflectorConfig {
7
7
  /** Alias that resolves to the generated reflector folder (e.g. `$reflector`). */
8
8
  reflectorAlias: string;
9
- /** Full import path to the validators/sanitizers module. */
9
+ /** Full import path to the validators module (exports `validateInputs`). */
10
10
  validatorsImport: string;
11
+ /** Full import path to the sanitizers module (input masks; exports `sanitizers`). */
12
+ sanitizersImport: string;
11
13
  /** Module path for the environment flag (e.g. `$env/static/public`). */
12
14
  environmentImport: string;
13
15
  /** Name of the exported environment flag — values other than `DEV` are treated as prod. */
@@ -1,6 +1,7 @@
1
1
  export const DEFAULT_REFLECTOR_CONFIG = {
2
2
  reflectorAlias: "$reflector",
3
3
  validatorsImport: "$lib/sanitizers/validateFormats",
4
+ sanitizersImport: "$lib/sanitizers/input-sanitizers",
4
5
  environmentImport: "$env/static/public",
5
6
  environmentFlag: "PUBLIC_ENVIRONMENT",
6
7
  toastImport: "$lib/utils/toast.svelte",
@@ -2,6 +2,7 @@ import type { CallMethodInput, CallStrategy } from "./CallStrategy.js";
2
2
  export declare class ApiCallStrategy implements CallStrategy {
3
3
  listStateAccess(_method: CallMethodInput): string;
4
4
  buildSignature(method: CallMethodInput): string;
5
+ buildLegacyWrapper(method: CallMethodInput): string;
5
6
  entityStateAccess(_method: CallMethodInput): string;
6
7
  formStateAccess(_method: CallMethodInput): string;
7
8
  private buildParamsType;
@@ -5,7 +5,16 @@ export class ApiCallStrategy {
5
5
  }
6
6
  buildSignature(method) {
7
7
  const paramsType = this.buildParamsType(method);
8
- return `async call(params?: ${paramsType})`;
8
+ return `async run(params?: ${paramsType})`;
9
+ }
10
+ buildLegacyWrapper(method) {
11
+ const paramsType = this.buildParamsType(method);
12
+ return `
13
+ /** @deprecated use \`run()\` — returns a discriminated ApiResult */
14
+ async call(params?: ${paramsType}) {
15
+ const res = await this.run(params);
16
+ return res.ok ? res.data : undefined;
17
+ }`;
9
18
  }
10
19
  entityStateAccess(_method) {
11
20
  return "this.data";
@@ -8,6 +8,7 @@ export class CallMethodGenerator {
8
8
  const { inside, outside } = this.buildApiCall(method, strategy);
9
9
  const methodReturn = this.buildMethodReturn(method, strategy);
10
10
  const signature = strategy.buildSignature(method);
11
+ const legacyWrapper = strategy.buildLegacyWrapper(method);
11
12
  return `
12
13
  ${description}
13
14
  ${signature} {
@@ -25,7 +26,7 @@ export class CallMethodGenerator {
25
26
  ${inside}
26
27
  await onSuccess?.(response);
27
28
 
28
- return ${methodReturn};
29
+ return { ok: true, data: ${methodReturn} };
29
30
  } catch (e) {
30
31
  let parsedError: ApiErrorResponse;
31
32
  try {
@@ -33,11 +34,14 @@ export class CallMethodGenerator {
33
34
  } catch {
34
35
  parsedError = { error: 'unknown', message: (e as Error).message ?? String(e) };
35
36
  }
36
- return await onError?.(parsedError);
37
+ await onError?.(parsedError);
38
+ return { ok: false, error: parsedError };
37
39
  } finally {
38
40
  this.loading = false;
39
41
  }
40
42
  }
43
+
44
+ ${legacyWrapper}
41
45
  `;
42
46
  }
43
47
  buildProps(method) {
@@ -17,8 +17,13 @@ export interface CallMethodInput {
17
17
  * (per-endpoint Api class vs Module `_methodName` protected method).
18
18
  */
19
19
  export interface CallStrategy {
20
- /** Full method signature incl. params e.g. `async call(params?: ...)` or `protected async _foo(params?: ...)` */
20
+ /** Full signature of the discriminated `run` method incl. params
21
+ * e.g. `async run(params?: ...)` (api) or `protected async _fooRun(params?: ...)` (module). */
21
22
  buildSignature(method: CallMethodInput): string;
23
+ /** Full `@deprecated` legacy method that delegates to the `run` variant and
24
+ * reproduces the old `Res | null | undefined` return shape. Emitted verbatim
25
+ * after the run method. Api → `call()`; module → `_foo()`. */
26
+ buildLegacyWrapper(method: CallMethodInput): string;
22
27
  /** State field holding list results — e.g. `this.list` / `this.listControllers` (module) or `this.data` (api).
23
28
  * Takes `method` so the module strategy can suffix the field when two list
24
29
  * operations collide in the same controller. */
@@ -2,6 +2,7 @@ import type { CallMethodInput, CallStrategy } from "./CallStrategy.js";
2
2
  export declare class ModuleCallStrategy implements CallStrategy {
3
3
  listStateAccess(method: CallMethodInput): string;
4
4
  buildSignature(method: CallMethodInput): string;
5
+ buildLegacyWrapper(method: CallMethodInput): string;
5
6
  entityStateAccess(method: CallMethodInput): string;
6
7
  formStateAccess(method: CallMethodInput): string;
7
8
  private buildParamsType;
@@ -6,7 +6,16 @@ export class ModuleCallStrategy {
6
6
  }
7
7
  buildSignature(method) {
8
8
  const paramsType = this.buildParamsType(method);
9
- return `protected async _${method.name}(params?: ${paramsType})`;
9
+ return `protected async _${method.name}Run(params?: ${paramsType})`;
10
+ }
11
+ buildLegacyWrapper(method) {
12
+ const paramsType = this.buildParamsType(method);
13
+ return `
14
+ /** @deprecated use \`_${method.name}Run()\` — returns a discriminated ApiResult */
15
+ protected async _${method.name}(params?: ${paramsType}) {
16
+ const res = await this._${method.name}Run(params);
17
+ return res.ok ? res.data : undefined;
18
+ }`;
10
19
  }
11
20
  entityStateAccess(method) {
12
21
  const rType = method.analyzers.request.responseType ?? "";
@@ -12,6 +12,8 @@ export declare class Module {
12
12
  readonly methods: Method[];
13
13
  /** Schema class names directly used by this module (for per-module schema generation) */
14
14
  readonly schemaClassNames: string[];
15
+ /** Nomes-raiz dos request body DTOs deste módulo (para serialização schema-aware) */
16
+ readonly requestBodyNames: string[];
15
17
  private readonly imports;
16
18
  private readonly methodProcessor;
17
19
  private readonly classBuilder;
@@ -14,6 +14,8 @@ export class Module {
14
14
  methods;
15
15
  /** Schema class names directly used by this module (for per-module schema generation) */
16
16
  schemaClassNames;
17
+ /** Nomes-raiz dos request body DTOs deste módulo (para serialização schema-aware) */
18
+ requestBodyNames;
17
19
  imports;
18
20
  methodProcessor;
19
21
  classBuilder;
@@ -55,6 +57,7 @@ export class Module {
55
57
  // Extract schema class names for per-module schema generation
56
58
  this.schemaClassNames = Array.from(processedMethods.entries)
57
59
  .filter((e) => e !== "type any" && !e.startsWith("type "));
60
+ this.requestBodyNames = Array.from(processedMethods.requestBodyNames);
58
61
  // Monta o resultado final
59
62
  const allBuilded = this.buildModuleData(processedMethods, processedParams);
60
63
  const moduleConstructor = this.constructorBuilder.build(allBuilded.form);
@@ -97,7 +100,7 @@ export class Module {
97
100
  `);
98
101
  moduleInit.add("this.clearForms()");
99
102
  moduleClear.add(`
100
- protected clearForms() { this.forms = this.buildForms(true) }
103
+ protected clearForms() { ${form.map((f) => `this.forms.${f.name}.reset()`).join("; ")} }
101
104
  `);
102
105
  }
103
106
  return {
@@ -6,6 +6,8 @@ import type { Form } from "./ModuleConstructorBuilder.js";
6
6
  export interface ProcessedMethods {
7
7
  buildedMethods: string[];
8
8
  entries: Set<string>;
9
+ /** Nomes-raiz dos request body DTOs (para serialização schema-aware via bundleInputs) */
10
+ requestBodyNames: Set<string>;
9
11
  form: Form[];
10
12
  formSet: Set<string>;
11
13
  methodsAttributes: string[];
@@ -13,6 +13,7 @@ export class ModuleMethodProcessor {
13
13
  const form = [];
14
14
  const formSet = new Set();
15
15
  const entries = new Set();
16
+ const requestBodyNames = new Set();
16
17
  const buildedMethods = [];
17
18
  const queryMap = new Map();
18
19
  const headerMap = new Map();
@@ -54,6 +55,7 @@ export class ModuleMethodProcessor {
54
55
  }
55
56
  if (bodyType) {
56
57
  entries.add(bodyType);
58
+ requestBodyNames.add(bodyType);
57
59
  }
58
60
  if (responseType && responseType !== "response" && !isPrimitiveResponse) {
59
61
  entries.add(`type ${responseType}Interface`);
@@ -64,6 +66,7 @@ export class ModuleMethodProcessor {
64
66
  form,
65
67
  formSet,
66
68
  entries,
69
+ requestBodyNames,
67
70
  buildedMethods,
68
71
  methodsAttributes: Array.from(methodsAttributes),
69
72
  methodsInit: Array.from(methodsInit),
@@ -16,11 +16,25 @@ export class ModuleSchemaFileBuilder {
16
16
  customTypeDeps.add(t);
17
17
  }
18
18
  }
19
+ // só presença importa (o import é o objeto `sanitizers` inteiro), não os refs
20
+ const usesSanitizers = schemas.some((s) => s.sanitizerDeps.length > 0);
19
21
  const treatedSchemas = schemas.map((s) => `${s.interface};\n${s.schema};`);
22
+ // import preciso: response usa bundleStrict, request usa bundleInputs, array-root
23
+ // não usa nenhum. Evita import morto (svelte-check/eslint do consumer reclama).
24
+ const needsStrict = schemas.some((s) => s.bundleHelper === "strict");
25
+ const needsInputs = schemas.some((s) => s.bundleHelper === "inputs");
26
+ const reflectorImports = ["build", "BuildedInput"];
27
+ if (needsStrict)
28
+ reflectorImports.push("bundleStrict");
29
+ if (needsInputs)
30
+ reflectorImports.push("bundleInputs");
20
31
  const imports = [
21
- `import { build, BuildedInput, bundleStrict } from "${config.reflectorAlias}/reflector.svelte";`,
32
+ `import { ${reflectorImports.join(", ")} } from "${config.reflectorAlias}/reflector.svelte";`,
22
33
  `import { validateInputs } from "${config.validatorsImport}";`,
23
34
  ];
35
+ if (usesSanitizers) {
36
+ imports.push(`import { sanitizers } from "${config.sanitizersImport}";`);
37
+ }
24
38
  if (enumDeps.size > 0) {
25
39
  imports.push(`import type { ${[...enumDeps].join(", ")} } from "${config.reflectorAlias}/enums"`);
26
40
  }
@@ -0,0 +1,35 @@
1
+ import type { ArrayProp } from "../../props/array.property.js";
2
+ import type { EnumProp } from "../../props/enum.property.js";
3
+ import type { ObjectProp } from "../../props/object.property.js";
4
+ import type { PrimitiveProp } from "../../props/primitive.property.js";
5
+ import type { UnionProp } from "../../props/union.property.js";
6
+ /**
7
+ * Renders a schema's `Interface` as a *discriminated union* type alias when one
8
+ * of its properties is a `oneOf` carrying a `discriminator` whose `propertyName`
9
+ * points at a sibling enum property (e.g. `NotificationData.actionMeta` keyed by
10
+ * `action`). Each `discriminator.mapping` entry (action value → variant schema)
11
+ * becomes a union member that pins `action` to its literal(s) and `actionMeta`
12
+ * to the matching variant `Interface`:
13
+ *
14
+ * ```ts
15
+ * export type NotificationDataInterface =
16
+ * | { ...commons; action: "STOCK_LOW" | "STOCK_OUT"; actionMeta: ProductStockMetaInterface }
17
+ * | { ...commons; action: "ANNOUNCEMENT"; actionMeta: UrlMetaInterface };
18
+ * ```
19
+ *
20
+ * This is what makes `if (n.action === "STOCK_LOW") n.actionMeta.productId`
21
+ * narrow on plain `Interface`-typed values (raw response data, `.bundle()`,
22
+ * `.discriminated()`). It never narrows on the live `$state` class instance —
23
+ * TS can't correlate two independent mutable fields.
24
+ */
25
+ export declare class DiscriminatedInterfaceRenderer {
26
+ static render(params: {
27
+ name: string;
28
+ union: UnionProp;
29
+ primitiveProps: PrimitiveProp[];
30
+ arrayProps: ArrayProp[];
31
+ objectProps: ObjectProp[];
32
+ enumProps: EnumProp[];
33
+ otherUnionProps: UnionProp[];
34
+ }): string;
35
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Renders a schema's `Interface` as a *discriminated union* type alias when one
3
+ * of its properties is a `oneOf` carrying a `discriminator` whose `propertyName`
4
+ * points at a sibling enum property (e.g. `NotificationData.actionMeta` keyed by
5
+ * `action`). Each `discriminator.mapping` entry (action value → variant schema)
6
+ * becomes a union member that pins `action` to its literal(s) and `actionMeta`
7
+ * to the matching variant `Interface`:
8
+ *
9
+ * ```ts
10
+ * export type NotificationDataInterface =
11
+ * | { ...commons; action: "STOCK_LOW" | "STOCK_OUT"; actionMeta: ProductStockMetaInterface }
12
+ * | { ...commons; action: "ANNOUNCEMENT"; actionMeta: UrlMetaInterface };
13
+ * ```
14
+ *
15
+ * This is what makes `if (n.action === "STOCK_LOW") n.actionMeta.productId`
16
+ * narrow on plain `Interface`-typed values (raw response data, `.bundle()`,
17
+ * `.discriminated()`). It never narrows on the live `$state` class instance —
18
+ * TS can't correlate two independent mutable fields.
19
+ */
20
+ export class DiscriminatedInterfaceRenderer {
21
+ static render(params) {
22
+ const { name, union, primitiveProps, arrayProps, objectProps, enumProps, otherUnionProps } = params;
23
+ const discProp = union.discriminator.propertyName;
24
+ const mapping = union.discriminator.mapping ?? {};
25
+ // Commons = every property except the discriminant and the discriminated union itself.
26
+ const allProps = [
27
+ ...primitiveProps,
28
+ ...arrayProps,
29
+ ...objectProps,
30
+ ...enumProps,
31
+ ...otherUnionProps,
32
+ ];
33
+ const commons = allProps
34
+ .filter((p) => p.name !== discProp && p.name !== union.name)
35
+ .map((p) => p.interfaceBuild());
36
+ // Invert the mapping: variant schema name → the action literals that select it,
37
+ // preserving first-seen order so output stays deterministic.
38
+ const byVariant = new Map();
39
+ for (const [actionValue, ref] of Object.entries(mapping)) {
40
+ const variant = ref.split("/").at(-1) ?? "";
41
+ if (!variant)
42
+ continue;
43
+ const list = byVariant.get(variant) ?? [];
44
+ list.push(actionValue);
45
+ byVariant.set(variant, list);
46
+ }
47
+ // The discriminated part: only the discriminant + the union field vary per variant.
48
+ // Commons are factored into a single object type and intersected with the union —
49
+ // narrowing on `action` still works through the intersection.
50
+ const members = [...byVariant.entries()].map(([variant, actions]) => {
51
+ const literals = actions.map((a) => `"${a}"`).join(" | ");
52
+ return `{ ${discProp}: ${literals}; ${union.name}: ${variant}Interface }`;
53
+ });
54
+ const variantUnion = members.map((m) => `| ${m}`).join("\n");
55
+ // No commons → emit the bare union (avoid a pointless `{} & (...)`).
56
+ if (commons.length === 0) {
57
+ return `
58
+ export type ${name}Interface =
59
+ ${variantUnion};
60
+ `;
61
+ }
62
+ return `
63
+ export type ${name}Interface = {
64
+ ${commons.join(";\n")}
65
+ } & (
66
+ ${variantUnion}
67
+ );
68
+ `;
69
+ }
70
+ }
@@ -2,6 +2,7 @@ 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 declare class ReflectorInterface {
6
7
  builded: string;
7
8
  constructor(params: {
@@ -10,5 +11,6 @@ export declare class ReflectorInterface {
10
11
  name: string;
11
12
  objectProps: ObjectProp[];
12
13
  enumProps: EnumProp[];
14
+ unionProps: UnionProp[];
13
15
  });
14
16
  }
@@ -1,7 +1,7 @@
1
1
  export class ReflectorInterface {
2
2
  builded;
3
3
  constructor(params) {
4
- const { name, arrayProps, primitiveProps, objectProps, enumProps } = params;
4
+ const { name, arrayProps, primitiveProps, objectProps, enumProps, unionProps } = params;
5
5
  const buildedProps = [];
6
6
  primitiveProps.forEach((prop) => {
7
7
  buildedProps.push(prop.interfaceBuild());
@@ -15,6 +15,9 @@ export class ReflectorInterface {
15
15
  enumProps.forEach((prop) => {
16
16
  buildedProps.push(prop.interfaceBuild());
17
17
  });
18
+ unionProps.forEach((prop) => {
19
+ buildedProps.push(prop.interfaceBuild());
20
+ });
18
21
  this.builded = `
19
22
  export interface ${name}Interface {
20
23
  ${buildedProps}
@@ -2,6 +2,7 @@ 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";
@@ -11,14 +12,23 @@ export declare class Schema {
11
12
  arrayProps: ArrayProp[];
12
13
  objectProps: ObjectProp[];
13
14
  enumProps: EnumProp[];
15
+ unionProps: UnionProp[];
14
16
  /** Other schema class names this schema depends on (via ObjectProp/$ref arrays) */
15
17
  readonly schemaDeps: string[];
16
18
  /** Enum type names used by this schema */
17
19
  readonly enumDeps: string[];
18
20
  /** Custom type names used by this schema (from fieldConfigs) */
19
21
  readonly customTypeDeps: string[];
22
+ /** Sanitizer refs used by this schema (from fieldConfigs) — gates the `sanitizers` import */
23
+ readonly sanitizerDeps: string[];
20
24
  schema: string;
21
25
  interface: string;
26
+ /**
27
+ * Qual helper de runtime o `bundle()` renderizado referencia — controla os imports
28
+ * do schema file. `'strict'` = response (bundleStrict), `'inputs'` = request
29
+ * (bundleInputs), `null` = array-root (mapeia `item.bundle()`, não usa nenhum).
30
+ */
31
+ bundleHelper: "strict" | "inputs" | null;
22
32
  /**
23
33
  * Builds a Schema for an array-root component (top-level `type: array`), e.g.
24
34
  * a promoted `data: array` response envelope. Renders a wrapper class whose
@@ -48,4 +58,12 @@ export declare class Schema {
48
58
  fieldConfigs: FieldConfigs;
49
59
  context: CodegenContext;
50
60
  });
61
+ /**
62
+ * Re-renderiza este schema como request DTO: `bundle()` passa a serializar a partir
63
+ * das instâncias `BuildedInput` via `bundleInputs` (em vez de `bundleStrict` sobre os
64
+ * `.value` extraídos). Idempotente. Schemas array-root não têm props → no-op
65
+ * (continuam mapeando `item.bundle()`). Chamado pelo `SchemaRegistry` para o fecho
66
+ * transitivo dos request bodies.
67
+ */
68
+ setRequestMode(): void;
51
69
  }
@@ -2,6 +2,7 @@ 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 } from "../../helpers/helpers.js";
6
7
  import { SchemaPropertyClassifier } from "./SchemaPropertyClassifier.js";
7
8
  import { SchemaDependencyCollector } from "./SchemaDependencyCollector.js";
@@ -13,14 +14,23 @@ export class Schema {
13
14
  arrayProps = [];
14
15
  objectProps = [];
15
16
  enumProps = [];
17
+ unionProps = [];
16
18
  /** Other schema class names this schema depends on (via ObjectProp/$ref arrays) */
17
19
  schemaDeps;
18
20
  /** Enum type names used by this schema */
19
21
  enumDeps;
20
22
  /** Custom type names used by this schema (from fieldConfigs) */
21
23
  customTypeDeps;
24
+ /** Sanitizer refs used by this schema (from fieldConfigs) — gates the `sanitizers` import */
25
+ sanitizerDeps;
22
26
  schema;
23
27
  interface;
28
+ /**
29
+ * Qual helper de runtime o `bundle()` renderizado referencia — controla os imports
30
+ * do schema file. `'strict'` = response (bundleStrict), `'inputs'` = request
31
+ * (bundleInputs), `null` = array-root (mapeia `item.bundle()`, não usa nenhum).
32
+ */
33
+ bundleHelper;
24
34
  /**
25
35
  * Builds a Schema for an array-root component (top-level `type: array`), e.g.
26
36
  * a promoted `data: array` response envelope. Renders a wrapper class whose
@@ -37,9 +47,11 @@ export class Schema {
37
47
  schema.arrayProps = [];
38
48
  schema.objectProps = [];
39
49
  schema.enumProps = [];
50
+ schema.unionProps = [];
40
51
  schema.schemaDeps = element.kind === "ref" ? [element.type] : [];
41
52
  schema.enumDeps = element.kind === "enum" ? [element.type] : [];
42
53
  schema.customTypeDeps = [];
54
+ schema.sanitizerDeps = [];
43
55
  const rendered = ArraySchemaRenderer.render({
44
56
  name,
45
57
  elementType: element.type,
@@ -47,6 +59,7 @@ export class Schema {
47
59
  });
48
60
  schema.interface = rendered.interface;
49
61
  schema.schema = rendered.schema;
62
+ schema.bundleHelper = null;
50
63
  return schema;
51
64
  }
52
65
  /**
@@ -100,24 +113,52 @@ export class Schema {
100
113
  this.objectProps.push(prop);
101
114
  else if (prop instanceof EnumProp)
102
115
  this.enumProps.push(prop);
116
+ else if (prop instanceof UnionProp)
117
+ this.unionProps.push(prop);
103
118
  }
104
119
  const deps = SchemaDependencyCollector.collect({
105
120
  primitiveProps: this.primitiveProps,
106
121
  arrayProps: this.arrayProps,
107
122
  objectProps: this.objectProps,
108
123
  enumProps: this.enumProps,
124
+ unionProps: this.unionProps,
109
125
  });
110
126
  this.schemaDeps = deps.schemaDeps;
111
127
  this.enumDeps = deps.enumDeps;
112
128
  this.customTypeDeps = deps.customTypeDeps;
129
+ this.sanitizerDeps = deps.sanitizerDeps;
113
130
  const rendered = SchemaClassRenderer.render({
114
131
  name: this.name,
115
132
  primitiveProps: this.primitiveProps,
116
133
  arrayProps: this.arrayProps,
117
134
  objectProps: this.objectProps,
118
135
  enumProps: this.enumProps,
136
+ unionProps: this.unionProps,
119
137
  });
120
138
  this.interface = rendered.interface;
121
139
  this.schema = rendered.schema;
140
+ this.bundleHelper = rendered.bundleHelper;
141
+ }
142
+ /**
143
+ * Re-renderiza este schema como request DTO: `bundle()` passa a serializar a partir
144
+ * das instâncias `BuildedInput` via `bundleInputs` (em vez de `bundleStrict` sobre os
145
+ * `.value` extraídos). Idempotente. Schemas array-root não têm props → no-op
146
+ * (continuam mapeando `item.bundle()`). Chamado pelo `SchemaRegistry` para o fecho
147
+ * transitivo dos request bodies.
148
+ */
149
+ setRequestMode() {
150
+ if (this.bundleHelper !== "strict")
151
+ return;
152
+ const rendered = SchemaClassRenderer.render({
153
+ name: this.name,
154
+ primitiveProps: this.primitiveProps,
155
+ arrayProps: this.arrayProps,
156
+ objectProps: this.objectProps,
157
+ enumProps: this.enumProps,
158
+ unionProps: this.unionProps,
159
+ mode: "request",
160
+ });
161
+ this.schema = rendered.schema;
162
+ this.bundleHelper = rendered.bundleHelper;
122
163
  }
123
164
  }
@@ -2,6 +2,7 @@ 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 declare class SchemaClassRenderer {
6
7
  static render(params: {
7
8
  name: string;
@@ -9,8 +10,11 @@ export declare class SchemaClassRenderer {
9
10
  arrayProps: ArrayProp[];
10
11
  objectProps: ObjectProp[];
11
12
  enumProps: EnumProp[];
13
+ unionProps: UnionProp[];
14
+ mode?: "request" | "response";
12
15
  }): {
13
16
  interface: string;
14
17
  schema: string;
18
+ bundleHelper: "strict" | "inputs";
15
19
  };
16
20
  }