@pylonts/dsl 1.0.4 → 1.0.6
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/bases.d.ts +5 -0
- package/dist/bases.js +20 -0
- package/dist/check-inheritance.d.ts +9 -0
- package/dist/check-inheritance.js +61 -0
- package/dist/dto.d.ts +6 -2
- package/dist/dto.js +8 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/typebox-driver.js +8 -2
- package/package.json +1 -1
- package/src/bases.ts +20 -0
- package/src/check-inheritance.ts +86 -0
- package/src/dto.ts +12 -7
- package/src/index.ts +2 -0
- package/src/typebox-driver.ts +6 -2
package/dist/bases.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DtoMessage, ImportRef } from './dto';
|
|
2
|
+
/** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
|
|
3
|
+
export declare const PageRequest: ImportRef;
|
|
4
|
+
/** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
|
|
5
|
+
export declare const PageResult: (row: DtoMessage) => ImportRef;
|
package/dist/bases.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PageResult = exports.PageRequest = void 0;
|
|
4
|
+
// Named base-schema references for common protocol DTOs.
|
|
5
|
+
//
|
|
6
|
+
// These are ImportRef metadata (not re-exports of the actual TypeBox schemas):
|
|
7
|
+
// the DSL stores { from, name } so the generator can emit the import line and
|
|
8
|
+
// the identifier — the runtime schema object itself is never loaded by the DSL.
|
|
9
|
+
//
|
|
10
|
+
// `import { PageRequest } from '@pylonts/dsl'` therefore gives .include() an
|
|
11
|
+
// already-resolved reference — no static analysis or name lookup needed.
|
|
12
|
+
/** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
|
|
13
|
+
exports.PageRequest = { from: '@pylonts/core', name: 'PageRequest' };
|
|
14
|
+
/** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
|
|
15
|
+
const PageResult = (row) => ({
|
|
16
|
+
from: '@pylonts/core',
|
|
17
|
+
name: 'PageResult',
|
|
18
|
+
args: [row],
|
|
19
|
+
});
|
|
20
|
+
exports.PageResult = PageResult;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface InheritanceIssue {
|
|
2
|
+
file: string;
|
|
3
|
+
container: string;
|
|
4
|
+
field: string;
|
|
5
|
+
/** e.g. ["t_order.order_no"] or ["t_order.id", "t_merchant.id"] when several inferred tables share the name */
|
|
6
|
+
candidates: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Check one loaded DSL module (its exports) for inheritance gaps. */
|
|
9
|
+
export declare function checkInheritance(mod: Record<string, unknown>, file: string): InheritanceIssue[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkInheritance = checkInheritance;
|
|
4
|
+
/** snake_case → camelCase: order_no → orderNo; names without underscores are unchanged */
|
|
5
|
+
function toCamelCase(name) {
|
|
6
|
+
return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
7
|
+
}
|
|
8
|
+
function isDtoMessage(v) {
|
|
9
|
+
if (typeof v !== 'object' || v === null)
|
|
10
|
+
return false;
|
|
11
|
+
const o = v;
|
|
12
|
+
return o.type === 'dto' && typeof o.name === 'string' && typeof o.fields === 'object' && o.fields !== null;
|
|
13
|
+
}
|
|
14
|
+
function collectFields(mod) {
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const [name, v] of Object.entries(mod)) {
|
|
17
|
+
if (isDtoMessage(v)) {
|
|
18
|
+
const container = v;
|
|
19
|
+
for (const [fname, f] of Object.entries(container.fields)) {
|
|
20
|
+
out.push({ name: fname, field: f.field, container: name });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/** Check one loaded DSL module (its exports) for inheritance gaps. */
|
|
27
|
+
function checkInheritance(mod, file) {
|
|
28
|
+
const fields = collectFields(mod);
|
|
29
|
+
if (fields.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
// Infer the tables this file is about from the referenced fields' schema identity.
|
|
32
|
+
const tables = new Map(); // table -> camelCol -> origCol
|
|
33
|
+
for (const f of fields) {
|
|
34
|
+
const schema = f.field.schema;
|
|
35
|
+
if (schema?.type !== 'table')
|
|
36
|
+
continue;
|
|
37
|
+
const table = schema;
|
|
38
|
+
if (!tables.has(table.name))
|
|
39
|
+
tables.set(table.name, new Map());
|
|
40
|
+
tables.get(table.name).set(toCamelCase(f.name), f.name);
|
|
41
|
+
}
|
|
42
|
+
if (tables.size === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const issues = [];
|
|
45
|
+
for (const f of fields) {
|
|
46
|
+
if (f.field.schema?.type === 'table')
|
|
47
|
+
continue;
|
|
48
|
+
if (f.field.semantic !== undefined || f.field.type === 'enum')
|
|
49
|
+
continue;
|
|
50
|
+
const candidates = [];
|
|
51
|
+
for (const [t, cols] of tables) {
|
|
52
|
+
const orig = cols.get(f.name);
|
|
53
|
+
if (orig !== undefined)
|
|
54
|
+
candidates.push(`${t}.${orig}`);
|
|
55
|
+
}
|
|
56
|
+
if (candidates.length > 0) {
|
|
57
|
+
issues.push({ file, container: f.container, field: f.name, candidates });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return issues;
|
|
61
|
+
}
|
package/dist/dto.d.ts
CHANGED
|
@@ -21,7 +21,8 @@ export interface ImportRef {
|
|
|
21
21
|
export type DtoArrayFieldDef = BaseField & {
|
|
22
22
|
type: 'array';
|
|
23
23
|
jsType: 'array';
|
|
24
|
-
|
|
24
|
+
/** Element type: inline DtoField, or a reference to an existing DTO (rendered by name) */
|
|
25
|
+
items: DtoField | DtoMessage;
|
|
25
26
|
};
|
|
26
27
|
export type DtoObjectFieldDef = BaseField & {
|
|
27
28
|
type: 'object';
|
|
@@ -46,6 +47,8 @@ export declare class DtoField implements SchemaBase {
|
|
|
46
47
|
setPattern(value: string): this;
|
|
47
48
|
setDescription(value: string): this;
|
|
48
49
|
getDescription(): string | undefined;
|
|
50
|
+
/** True when this field wraps a DB column (picked via from()); false for inline fields. */
|
|
51
|
+
isColumn(): boolean;
|
|
49
52
|
setOptional(value: boolean): this;
|
|
50
53
|
/** Set a default value — emitted as a TypeBox schema default annotation */
|
|
51
54
|
setDefault(value: unknown): this;
|
|
@@ -56,7 +59,8 @@ export declare class DtoField implements SchemaBase {
|
|
|
56
59
|
}
|
|
57
60
|
export declare class DtoArrayField extends DtoField {
|
|
58
61
|
field: DtoArrayFieldDef;
|
|
59
|
-
|
|
62
|
+
/** Element type: inline DtoField or a referenced DtoMessage (rendered by name). */
|
|
63
|
+
items(): DtoField | DtoMessage;
|
|
60
64
|
}
|
|
61
65
|
export declare class DtoObjectField extends DtoField {
|
|
62
66
|
field: DtoObjectFieldDef;
|
package/dist/dto.js
CHANGED
|
@@ -39,6 +39,10 @@ class DtoField {
|
|
|
39
39
|
getDescription() {
|
|
40
40
|
return this.description;
|
|
41
41
|
}
|
|
42
|
+
/** True when this field wraps a DB column (picked via from()); false for inline fields. */
|
|
43
|
+
isColumn() {
|
|
44
|
+
return this.field.schema?.type === 'table';
|
|
45
|
+
}
|
|
42
46
|
setOptional(value) {
|
|
43
47
|
this.optional = value;
|
|
44
48
|
return this;
|
|
@@ -62,6 +66,7 @@ class DtoField {
|
|
|
62
66
|
}
|
|
63
67
|
exports.DtoField = DtoField;
|
|
64
68
|
class DtoArrayField extends DtoField {
|
|
69
|
+
/** Element type: inline DtoField or a referenced DtoMessage (rendered by name). */
|
|
65
70
|
items() {
|
|
66
71
|
return this.field.items;
|
|
67
72
|
}
|
|
@@ -106,11 +111,9 @@ function dtoField(field) {
|
|
|
106
111
|
return new DtoField(field);
|
|
107
112
|
}
|
|
108
113
|
function dtoArrayField(def) {
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
: def.items;
|
|
113
|
-
return new DtoArrayField({ name: '', type: 'array', jsType: 'array', ...def, items });
|
|
114
|
+
// Items stay as-is: an inline DtoField is rendered inline, a DtoMessage is
|
|
115
|
+
// referenced by name (the driver renders Type.Array(<DtoName>)).
|
|
116
|
+
return new DtoArrayField({ name: '', type: 'array', jsType: 'array', ...def });
|
|
114
117
|
}
|
|
115
118
|
function dtoObjectField(def) {
|
|
116
119
|
return new DtoObjectField({ name: '', type: 'object', jsType: 'object', ...def });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './dsl';
|
|
2
2
|
export * from './dto';
|
|
3
|
+
export * from './bases';
|
|
3
4
|
export * from './utils';
|
|
4
5
|
export * from './project';
|
|
5
6
|
export * from './prototype';
|
|
@@ -7,6 +8,7 @@ export * from './dictionary';
|
|
|
7
8
|
export * from './mysql-driver';
|
|
8
9
|
export * from './enum-driver';
|
|
9
10
|
export * from './typebox-driver';
|
|
11
|
+
export * from './check-inheritance';
|
|
10
12
|
export * from './pattern';
|
|
11
13
|
export * from './patterns/retry';
|
|
12
14
|
export * from './flow';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./dsl"), exports);
|
|
18
18
|
__exportStar(require("./dto"), exports);
|
|
19
|
+
__exportStar(require("./bases"), exports);
|
|
19
20
|
__exportStar(require("./utils"), exports);
|
|
20
21
|
__exportStar(require("./project"), exports);
|
|
21
22
|
__exportStar(require("./prototype"), exports);
|
|
@@ -23,6 +24,7 @@ __exportStar(require("./dictionary"), exports);
|
|
|
23
24
|
__exportStar(require("./mysql-driver"), exports);
|
|
24
25
|
__exportStar(require("./enum-driver"), exports);
|
|
25
26
|
__exportStar(require("./typebox-driver"), exports);
|
|
27
|
+
__exportStar(require("./check-inheritance"), exports);
|
|
26
28
|
__exportStar(require("./pattern"), exports);
|
|
27
29
|
__exportStar(require("./patterns/retry"), exports);
|
|
28
30
|
__exportStar(require("./flow"), exports);
|
package/dist/typebox-driver.js
CHANGED
|
@@ -84,7 +84,11 @@ function renderField(f, indent, resolver) {
|
|
|
84
84
|
}
|
|
85
85
|
function renderValue(f, indent, resolver) {
|
|
86
86
|
if (f instanceof dto_1.DtoArrayField) {
|
|
87
|
-
|
|
87
|
+
const items = f.items();
|
|
88
|
+
// Referenced DTO element — render by name (same-file export), not expanded.
|
|
89
|
+
if (items instanceof dto_1.DtoMessage)
|
|
90
|
+
return `Type.Array(${items.name})`;
|
|
91
|
+
return `Type.Array(${renderField(items, indent + 1, resolver)})`;
|
|
88
92
|
}
|
|
89
93
|
if (f instanceof dto_1.DtoObjectField) {
|
|
90
94
|
return renderObject(f.properties(), indent + 1, resolver);
|
|
@@ -97,7 +101,9 @@ function renderValue(f, indent, resolver) {
|
|
|
97
101
|
}
|
|
98
102
|
function collectEnumImports(f, resolver, out) {
|
|
99
103
|
if (f instanceof dto_1.DtoArrayField) {
|
|
100
|
-
|
|
104
|
+
const items = f.items();
|
|
105
|
+
if (!(items instanceof dto_1.DtoMessage))
|
|
106
|
+
collectEnumImports(items, resolver, out);
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
103
109
|
if (f instanceof dto_1.DtoObjectField) {
|
package/package.json
CHANGED
package/src/bases.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DtoMessage, ImportRef } from './dto';
|
|
2
|
+
|
|
3
|
+
// Named base-schema references for common protocol DTOs.
|
|
4
|
+
//
|
|
5
|
+
// These are ImportRef metadata (not re-exports of the actual TypeBox schemas):
|
|
6
|
+
// the DSL stores { from, name } so the generator can emit the import line and
|
|
7
|
+
// the identifier — the runtime schema object itself is never loaded by the DSL.
|
|
8
|
+
//
|
|
9
|
+
// `import { PageRequest } from '@pylonts/dsl'` therefore gives .include() an
|
|
10
|
+
// already-resolved reference — no static analysis or name lookup needed.
|
|
11
|
+
|
|
12
|
+
/** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
|
|
13
|
+
export const PageRequest: ImportRef = { from: '@pylonts/core', name: 'PageRequest' };
|
|
14
|
+
|
|
15
|
+
/** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
|
|
16
|
+
export const PageResult = (row: DtoMessage): ImportRef => ({
|
|
17
|
+
from: '@pylonts/core',
|
|
18
|
+
name: 'PageResult',
|
|
19
|
+
args: [row],
|
|
20
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { DtoMessage } from './dto';
|
|
2
|
+
import type { TableSchema } from './dsl';
|
|
3
|
+
|
|
4
|
+
// Inheritance check — find DTO fields that should inherit from a DB column
|
|
5
|
+
// (via from()) but were written by hand, so they miss the column's
|
|
6
|
+
// type / semantic / optionality backfill.
|
|
7
|
+
//
|
|
8
|
+
// Inference: the tables a DSL file is about are inferred from the referenced
|
|
9
|
+
// fields in the same file (each Field carries schema identity via .schema).
|
|
10
|
+
// A field without a table ref whose name matches a column of an inferred table
|
|
11
|
+
// is a candidate for inheritance. Fields with an explicit semantic or enum type
|
|
12
|
+
// are treated as author-intent and skipped.
|
|
13
|
+
//
|
|
14
|
+
// False-positive boundary: when a DSL file has zero referenced fields, no table
|
|
15
|
+
// can be inferred and nothing is reported (never guess which table a hand-written
|
|
16
|
+
// field belongs to).
|
|
17
|
+
|
|
18
|
+
export interface InheritanceIssue {
|
|
19
|
+
file: string;
|
|
20
|
+
container: string;
|
|
21
|
+
field: string;
|
|
22
|
+
/** e.g. ["t_order.order_no"] or ["t_order.id", "t_merchant.id"] when several inferred tables share the name */
|
|
23
|
+
candidates: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** snake_case → camelCase: order_no → orderNo; names without underscores are unchanged */
|
|
27
|
+
function toCamelCase(name: string): string {
|
|
28
|
+
return name.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isDtoMessage(v: unknown): v is DtoMessage {
|
|
32
|
+
if (typeof v !== 'object' || v === null) return false;
|
|
33
|
+
const o = v as Record<string, unknown>;
|
|
34
|
+
return o.type === 'dto' && typeof o.name === 'string' && typeof o.fields === 'object' && o.fields !== null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface FieldEntry {
|
|
38
|
+
name: string;
|
|
39
|
+
field: { schema?: { type?: string }; semantic?: string; type?: string };
|
|
40
|
+
container: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function collectFields(mod: Record<string, unknown>): FieldEntry[] {
|
|
44
|
+
const out: FieldEntry[] = [];
|
|
45
|
+
for (const [name, v] of Object.entries(mod)) {
|
|
46
|
+
if (isDtoMessage(v)) {
|
|
47
|
+
const container = v as { fields: Record<string, { field: FieldEntry['field'] }> };
|
|
48
|
+
for (const [fname, f] of Object.entries(container.fields)) {
|
|
49
|
+
out.push({ name: fname, field: f.field, container: name });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Check one loaded DSL module (its exports) for inheritance gaps. */
|
|
57
|
+
export function checkInheritance(mod: Record<string, unknown>, file: string): InheritanceIssue[] {
|
|
58
|
+
const fields = collectFields(mod);
|
|
59
|
+
if (fields.length === 0) return [];
|
|
60
|
+
|
|
61
|
+
// Infer the tables this file is about from the referenced fields' schema identity.
|
|
62
|
+
const tables = new Map<string, Map<string, string>>(); // table -> camelCol -> origCol
|
|
63
|
+
for (const f of fields) {
|
|
64
|
+
const schema = f.field.schema;
|
|
65
|
+
if (schema?.type !== 'table') continue;
|
|
66
|
+
const table = schema as TableSchema;
|
|
67
|
+
if (!tables.has(table.name)) tables.set(table.name, new Map());
|
|
68
|
+
tables.get(table.name)!.set(toCamelCase(f.name), f.name);
|
|
69
|
+
}
|
|
70
|
+
if (tables.size === 0) return [];
|
|
71
|
+
|
|
72
|
+
const issues: InheritanceIssue[] = [];
|
|
73
|
+
for (const f of fields) {
|
|
74
|
+
if (f.field.schema?.type === 'table') continue;
|
|
75
|
+
if (f.field.semantic !== undefined || f.field.type === 'enum') continue;
|
|
76
|
+
const candidates: string[] = [];
|
|
77
|
+
for (const [t, cols] of tables) {
|
|
78
|
+
const orig = cols.get(f.name);
|
|
79
|
+
if (orig !== undefined) candidates.push(`${t}.${orig}`);
|
|
80
|
+
}
|
|
81
|
+
if (candidates.length > 0) {
|
|
82
|
+
issues.push({ file, container: f.container, field: f.name, candidates });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return issues;
|
|
86
|
+
}
|
package/src/dto.ts
CHANGED
|
@@ -29,7 +29,8 @@ export interface ImportRef {
|
|
|
29
29
|
export type DtoArrayFieldDef = BaseField & {
|
|
30
30
|
type: 'array';
|
|
31
31
|
jsType: 'array';
|
|
32
|
-
|
|
32
|
+
/** Element type: inline DtoField, or a reference to an existing DTO (rendered by name) */
|
|
33
|
+
items: DtoField | DtoMessage;
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export type DtoObjectFieldDef = BaseField & {
|
|
@@ -72,6 +73,11 @@ export class DtoField implements SchemaBase {
|
|
|
72
73
|
return this.description;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/** True when this field wraps a DB column (picked via from()); false for inline fields. */
|
|
77
|
+
isColumn(): boolean {
|
|
78
|
+
return this.field.schema?.type === 'table';
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
setOptional(value: boolean): this {
|
|
76
82
|
this.optional = value;
|
|
77
83
|
return this;
|
|
@@ -99,7 +105,8 @@ export class DtoField implements SchemaBase {
|
|
|
99
105
|
export class DtoArrayField extends DtoField {
|
|
100
106
|
declare field: DtoArrayFieldDef;
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
/** Element type: inline DtoField or a referenced DtoMessage (rendered by name). */
|
|
109
|
+
items(): DtoField | DtoMessage {
|
|
103
110
|
return this.field.items;
|
|
104
111
|
}
|
|
105
112
|
}
|
|
@@ -148,11 +155,9 @@ export function dtoField(field: Field): DtoField {
|
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
export function dtoArrayField(def: { items: DtoField | DtoMessage } & Omit<BaseField, 'name'>): DtoArrayField {
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
: def.items;
|
|
155
|
-
return new DtoArrayField({ name: '', type: 'array', jsType: 'array', ...def, items });
|
|
158
|
+
// Items stay as-is: an inline DtoField is rendered inline, a DtoMessage is
|
|
159
|
+
// referenced by name (the driver renders Type.Array(<DtoName>)).
|
|
160
|
+
return new DtoArrayField({ name: '', type: 'array', jsType: 'array', ...def });
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
export function dtoObjectField(def: { properties: Record<string, DtoField> } & Omit<BaseField, 'name'>): DtoObjectField {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './dsl';
|
|
2
2
|
export * from './dto';
|
|
3
|
+
export * from './bases';
|
|
3
4
|
export * from './utils';
|
|
4
5
|
export * from './project';
|
|
5
6
|
export * from './prototype';
|
|
@@ -7,6 +8,7 @@ export * from './dictionary';
|
|
|
7
8
|
export * from './mysql-driver';
|
|
8
9
|
export * from './enum-driver';
|
|
9
10
|
export * from './typebox-driver';
|
|
11
|
+
export * from './check-inheritance';
|
|
10
12
|
export * from './pattern';
|
|
11
13
|
export * from './patterns/retry';
|
|
12
14
|
export * from './flow';
|
package/src/typebox-driver.ts
CHANGED
|
@@ -95,7 +95,10 @@ function renderField(f: DtoField, indent: number, resolver: EnumResolver | undef
|
|
|
95
95
|
|
|
96
96
|
function renderValue(f: DtoField, indent: number, resolver: EnumResolver | undefined): string {
|
|
97
97
|
if (f instanceof DtoArrayField) {
|
|
98
|
-
|
|
98
|
+
const items = f.items();
|
|
99
|
+
// Referenced DTO element — render by name (same-file export), not expanded.
|
|
100
|
+
if (items instanceof DtoMessage) return `Type.Array(${items.name})`;
|
|
101
|
+
return `Type.Array(${renderField(items, indent + 1, resolver)})`;
|
|
99
102
|
}
|
|
100
103
|
if (f instanceof DtoObjectField) {
|
|
101
104
|
return renderObject(f.properties(), indent + 1, resolver);
|
|
@@ -113,7 +116,8 @@ function collectEnumImports(
|
|
|
113
116
|
out: Map<string, ImportRef>,
|
|
114
117
|
): void {
|
|
115
118
|
if (f instanceof DtoArrayField) {
|
|
116
|
-
|
|
119
|
+
const items = f.items();
|
|
120
|
+
if (!(items instanceof DtoMessage)) collectEnumImports(items, resolver, out);
|
|
117
121
|
return;
|
|
118
122
|
}
|
|
119
123
|
if (f instanceof DtoObjectField) {
|