@pylonts/dsl 1.0.3 → 1.0.5
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/dsl.d.ts +28 -15
- package/dist/dsl.js +44 -1
- package/dist/dto.d.ts +38 -21
- package/dist/dto.js +96 -34
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/mermaid-driver.js +7 -3
- package/dist/typebox-driver.d.ts +6 -0
- package/dist/typebox-driver.js +56 -20
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +8 -0
- package/docs/dto.md +17 -10
- package/package.json +1 -1
- package/src/bases.ts +20 -0
- package/src/check-inheritance.ts +86 -0
- package/src/dsl.ts +50 -19
- package/src/dto.ts +98 -48
- package/src/index.ts +3 -0
- package/src/mermaid-driver.ts +6 -3
- package/src/typebox-driver.ts +183 -140
- package/src/utils.ts +6 -0
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/dsl.d.ts
CHANGED
|
@@ -3,17 +3,23 @@ export interface SchemaBase {
|
|
|
3
3
|
name: string;
|
|
4
4
|
description?: string;
|
|
5
5
|
}
|
|
6
|
+
/** Field collection schemas (DB table vs DTO message); `type` is the discriminator */
|
|
7
|
+
export interface CollectionSchemaBase extends SchemaBase {
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
6
10
|
export interface BaseField {
|
|
7
11
|
name: string;
|
|
8
12
|
/** 显示名称(中文标签) */
|
|
9
13
|
label?: string;
|
|
10
14
|
/** 字段描述 */
|
|
11
15
|
description?: string;
|
|
16
|
+
/** 业务语义码(如 'merchant_name'),驱动 mock 生成等下游消费 */
|
|
17
|
+
semantic?: string;
|
|
12
18
|
optional?: boolean;
|
|
13
19
|
readOnly?: boolean;
|
|
14
20
|
default?: string;
|
|
15
21
|
/** 所属 schema(db 或 dto) */
|
|
16
|
-
schema?:
|
|
22
|
+
schema?: CollectionSchemaBase;
|
|
17
23
|
}
|
|
18
24
|
interface StringField extends BaseField {
|
|
19
25
|
type: 'string';
|
|
@@ -90,7 +96,23 @@ export type ForeignKey = {
|
|
|
90
96
|
fields: Field | Field[];
|
|
91
97
|
references: Field | Field[];
|
|
92
98
|
};
|
|
93
|
-
export interface
|
|
99
|
+
export interface TableSchemaOptions {
|
|
100
|
+
description?: string;
|
|
101
|
+
paginated?: boolean;
|
|
102
|
+
actor?: boolean;
|
|
103
|
+
generator?: string;
|
|
104
|
+
autoIncrement?: Field;
|
|
105
|
+
primaryKey?: Field | Field[];
|
|
106
|
+
indexes?: Index[];
|
|
107
|
+
foreignKeys?: Record<string, ForeignKey>;
|
|
108
|
+
/** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
|
|
109
|
+
phrase?: DictionaryEntry;
|
|
110
|
+
fields: Record<string, Field>;
|
|
111
|
+
}
|
|
112
|
+
export declare class TableSchema implements CollectionSchemaBase {
|
|
113
|
+
type: string;
|
|
114
|
+
name: string;
|
|
115
|
+
description?: string;
|
|
94
116
|
/** 分页 */
|
|
95
117
|
paginated?: boolean;
|
|
96
118
|
/** 系统操作者(如小程序为 C 端用户,管理端为运营) */
|
|
@@ -106,6 +128,9 @@ export interface TableSchema extends SchemaBase {
|
|
|
106
128
|
/** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
|
|
107
129
|
phrase?: DictionaryEntry;
|
|
108
130
|
fields: Record<string, Field>;
|
|
131
|
+
constructor(name: string, options: TableSchemaOptions);
|
|
132
|
+
/** True when the field is part of this table's primary key */
|
|
133
|
+
isPk(fieldRef: Field): boolean;
|
|
109
134
|
}
|
|
110
135
|
type FieldExtras<T extends Field> = Omit<T, 'name' | 'type' | 'jsType'>;
|
|
111
136
|
export declare function stringField(extra?: FieldExtras<StringField>): StringField;
|
|
@@ -119,17 +144,5 @@ export declare function timeField(extra?: FieldExtras<TimeField>): TimeField;
|
|
|
119
144
|
export declare function datetimeField(extra?: FieldExtras<DateTimeField>): DateTimeField;
|
|
120
145
|
export declare function jsonField(extra?: FieldExtras<JsonField>): JsonField;
|
|
121
146
|
export declare function enumField(extra: Omit<EnumField, 'name' | 'type' | 'jsType'>): EnumField;
|
|
122
|
-
export declare function defineTable(name: string, schema:
|
|
123
|
-
description?: string;
|
|
124
|
-
paginated?: boolean;
|
|
125
|
-
actor?: boolean;
|
|
126
|
-
generator?: string;
|
|
127
|
-
autoIncrement?: Field;
|
|
128
|
-
primaryKey?: Field | Field[];
|
|
129
|
-
indexes?: Index[];
|
|
130
|
-
foreignKeys?: Record<string, ForeignKey>;
|
|
131
|
-
/** 引用的实体短语(词典条目) */
|
|
132
|
-
phrase?: DictionaryEntry;
|
|
133
|
-
fields: Record<string, Field>;
|
|
134
|
-
}): TableSchema;
|
|
147
|
+
export declare function defineTable(name: string, schema: TableSchemaOptions): TableSchema;
|
|
135
148
|
export {};
|
package/dist/dsl.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// DSL field type definitions.
|
|
3
3
|
// Shape: { type: <type name>, <extension fields> }
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.TableSchema = void 0;
|
|
5
6
|
exports.defineEnum = defineEnum;
|
|
6
7
|
exports.stringField = stringField;
|
|
7
8
|
exports.textField = textField;
|
|
@@ -18,6 +19,48 @@ exports.defineTable = defineTable;
|
|
|
18
19
|
function defineEnum(jsName, valueType, values) {
|
|
19
20
|
return { jsName, valueType, values };
|
|
20
21
|
}
|
|
22
|
+
class TableSchema {
|
|
23
|
+
type = 'table';
|
|
24
|
+
name;
|
|
25
|
+
description;
|
|
26
|
+
/** 分页 */
|
|
27
|
+
paginated;
|
|
28
|
+
/** 系统操作者(如小程序为 C 端用户,管理端为运营) */
|
|
29
|
+
actor;
|
|
30
|
+
/** id 生成器 */
|
|
31
|
+
generator;
|
|
32
|
+
/** 自增主键字段 */
|
|
33
|
+
autoIncrement;
|
|
34
|
+
primaryKey;
|
|
35
|
+
indexes;
|
|
36
|
+
/** 外键,引用其他表的字段 */
|
|
37
|
+
foreignKeys;
|
|
38
|
+
/** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
|
|
39
|
+
phrase;
|
|
40
|
+
fields;
|
|
41
|
+
constructor(name, options) {
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.description = options.description;
|
|
44
|
+
this.paginated = options.paginated;
|
|
45
|
+
this.actor = options.actor;
|
|
46
|
+
this.generator = options.generator;
|
|
47
|
+
this.autoIncrement = options.autoIncrement;
|
|
48
|
+
this.primaryKey = options.primaryKey;
|
|
49
|
+
this.indexes = options.indexes;
|
|
50
|
+
this.foreignKeys = options.foreignKeys;
|
|
51
|
+
this.phrase = options.phrase;
|
|
52
|
+
this.fields = options.fields;
|
|
53
|
+
}
|
|
54
|
+
/** True when the field is part of this table's primary key */
|
|
55
|
+
isPk(fieldRef) {
|
|
56
|
+
if (this.primaryKey === undefined)
|
|
57
|
+
return false;
|
|
58
|
+
return Array.isArray(this.primaryKey)
|
|
59
|
+
? this.primaryKey.includes(fieldRef)
|
|
60
|
+
: this.primaryKey === fieldRef;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.TableSchema = TableSchema;
|
|
21
64
|
function stringField(extra = {}) {
|
|
22
65
|
return { name: '', type: 'string', jsType: 'string', ...extra };
|
|
23
66
|
}
|
|
@@ -53,7 +96,7 @@ function enumField(extra) {
|
|
|
53
96
|
return { name: '', type: 'enum', jsType, ...extra };
|
|
54
97
|
}
|
|
55
98
|
function defineTable(name, schema) {
|
|
56
|
-
const table =
|
|
99
|
+
const table = new TableSchema(name, schema);
|
|
57
100
|
for (const key of Object.keys(table.fields)) {
|
|
58
101
|
const field = table.fields[key];
|
|
59
102
|
if (field.schema && field.schema !== table) {
|
package/dist/dto.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseField, Field, SchemaBase, TableSchema } from './dsl';
|
|
1
|
+
import { BaseField, CollectionSchemaBase, Field, SchemaBase, TableSchema } from './dsl';
|
|
2
2
|
export type Operator = 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ne';
|
|
3
3
|
/**
|
|
4
4
|
* Reference to an existing TypeBox base schema by its import location.
|
|
@@ -10,16 +10,14 @@ export interface ImportRef {
|
|
|
10
10
|
from: string;
|
|
11
11
|
/** Named export, e.g. 'PageRequest' */
|
|
12
12
|
name: string;
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Generic type arguments for the base schema (e.g. PageResult(OrderRow)).
|
|
15
|
+
* Two forms, both local DTOs:
|
|
16
|
+
* string — the DTO export name
|
|
17
|
+
* DtoMessage — the DTO instance itself; the driver resolves it to its name
|
|
18
|
+
*/
|
|
19
|
+
args?: (string | DtoMessage)[];
|
|
15
20
|
}
|
|
16
|
-
export type DtoExtras = {
|
|
17
|
-
pattern?: string;
|
|
18
|
-
/** 联合判断是否可选,定义后优先级高于 field.optional */
|
|
19
|
-
optional?: boolean;
|
|
20
|
-
/** 查询比较操作符(query 方向字段) */
|
|
21
|
-
operator?: Operator;
|
|
22
|
-
};
|
|
23
21
|
export type DtoArrayFieldDef = BaseField & {
|
|
24
22
|
type: 'array';
|
|
25
23
|
jsType: 'array';
|
|
@@ -30,21 +28,31 @@ export type DtoObjectFieldDef = BaseField & {
|
|
|
30
28
|
jsType: 'object';
|
|
31
29
|
properties: Record<string, DtoField>;
|
|
32
30
|
};
|
|
33
|
-
export declare class DtoField {
|
|
31
|
+
export declare class DtoField implements SchemaBase {
|
|
34
32
|
/** DTO 语义字段名(接口字段名),与 field.name(数据库列名)含义不同。
|
|
35
33
|
* 构造时未知,由 buildMessage 从 map key 反写。 */
|
|
36
34
|
name: string;
|
|
35
|
+
/** 字段描述 */
|
|
36
|
+
description?: string;
|
|
37
37
|
/** 所属 DTO 容器(buildMessage 反写) */
|
|
38
38
|
schema?: DtoMessage;
|
|
39
39
|
field: Field | DtoArrayFieldDef | DtoObjectFieldDef;
|
|
40
40
|
pattern?: string;
|
|
41
41
|
optional?: boolean;
|
|
42
42
|
operator?: Operator;
|
|
43
|
-
|
|
43
|
+
/** TypeBox default annotation (API contract level); falls back to field.default (DB default) */
|
|
44
|
+
default?: unknown;
|
|
45
|
+
constructor(field: Field | DtoArrayFieldDef | DtoObjectFieldDef);
|
|
44
46
|
setPattern(value: string): this;
|
|
47
|
+
setDescription(value: string): this;
|
|
48
|
+
getDescription(): string | undefined;
|
|
49
|
+
/** True when this field wraps a DB column (picked via from()); false for inline fields. */
|
|
50
|
+
isColumn(): boolean;
|
|
45
51
|
setOptional(value: boolean): this;
|
|
52
|
+
/** Set a default value — emitted as a TypeBox schema default annotation */
|
|
53
|
+
setDefault(value: unknown): this;
|
|
46
54
|
/** 查询比较操作符(query 方向字段)。Rule B: 查询字段恒为可选 */
|
|
47
|
-
|
|
55
|
+
setOperator(value: Operator): this;
|
|
48
56
|
/** optional 优先于 field.optional */
|
|
49
57
|
isOptional(): boolean;
|
|
50
58
|
}
|
|
@@ -56,23 +64,32 @@ export declare class DtoObjectField extends DtoField {
|
|
|
56
64
|
field: DtoObjectFieldDef;
|
|
57
65
|
properties(): Record<string, DtoField>;
|
|
58
66
|
}
|
|
59
|
-
export
|
|
60
|
-
|
|
67
|
+
export declare enum DtoDirection {
|
|
68
|
+
Input = "input",
|
|
69
|
+
Output = "output",
|
|
70
|
+
Query = "query",
|
|
71
|
+
Pk = "pk"
|
|
72
|
+
}
|
|
73
|
+
export declare class DtoMessage implements CollectionSchemaBase {
|
|
74
|
+
type: string;
|
|
75
|
+
name: string;
|
|
76
|
+
description?: string;
|
|
61
77
|
/** 方向:输入或输出 */
|
|
62
78
|
direction: DtoDirection;
|
|
63
79
|
fields: Record<string, DtoField>;
|
|
64
80
|
/** TypeBox base schemas to intersect with at generation time (e.g. PageRequest) */
|
|
65
|
-
bases
|
|
81
|
+
bases: ImportRef[];
|
|
82
|
+
constructor(name: string, direction: DtoDirection, fields: Record<string, DtoField>, description?: string);
|
|
66
83
|
/** 引用已存在的 TypeBox base schema,例如 include({ from: '@pylonts/core', name: 'PageRequest' }) */
|
|
67
|
-
include(...refs: ImportRef[]):
|
|
84
|
+
include(...refs: ImportRef[]): this;
|
|
68
85
|
}
|
|
69
|
-
export declare function dtoField(field: Field
|
|
86
|
+
export declare function dtoField(field: Field): DtoField;
|
|
70
87
|
export declare function dtoArrayField(def: {
|
|
71
|
-
items: DtoField;
|
|
72
|
-
} & Omit<BaseField, 'name'
|
|
88
|
+
items: DtoField | DtoMessage;
|
|
89
|
+
} & Omit<BaseField, 'name'>): DtoArrayField;
|
|
73
90
|
export declare function dtoObjectField(def: {
|
|
74
91
|
properties: Record<string, DtoField>;
|
|
75
|
-
} & Omit<BaseField, 'name'
|
|
92
|
+
} & Omit<BaseField, 'name'>): DtoObjectField;
|
|
76
93
|
export declare function buildInput(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
|
77
94
|
export declare function buildOutput(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
|
78
95
|
export declare function buildQuery(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
package/dist/dto.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DtoObjectField = exports.DtoArrayField = exports.DtoField = void 0;
|
|
3
|
+
exports.DtoMessage = exports.DtoDirection = exports.DtoObjectField = exports.DtoArrayField = exports.DtoField = void 0;
|
|
4
4
|
exports.dtoField = dtoField;
|
|
5
5
|
exports.dtoArrayField = dtoArrayField;
|
|
6
6
|
exports.dtoObjectField = dtoObjectField;
|
|
@@ -9,35 +9,52 @@ exports.buildOutput = buildOutput;
|
|
|
9
9
|
exports.buildQuery = buildQuery;
|
|
10
10
|
exports.buildPk = buildPk;
|
|
11
11
|
exports.from = from;
|
|
12
|
+
const utils_1 = require("./utils");
|
|
12
13
|
class DtoField {
|
|
13
14
|
/** DTO 语义字段名(接口字段名),与 field.name(数据库列名)含义不同。
|
|
14
15
|
* 构造时未知,由 buildMessage 从 map key 反写。 */
|
|
15
16
|
name;
|
|
17
|
+
/** 字段描述 */
|
|
18
|
+
description;
|
|
16
19
|
/** 所属 DTO 容器(buildMessage 反写) */
|
|
17
20
|
schema;
|
|
18
21
|
field;
|
|
19
22
|
pattern;
|
|
20
23
|
optional;
|
|
21
24
|
operator;
|
|
22
|
-
|
|
25
|
+
/** TypeBox default annotation (API contract level); falls back to field.default (DB default) */
|
|
26
|
+
default;
|
|
27
|
+
constructor(field) {
|
|
23
28
|
this.name = '';
|
|
24
29
|
this.field = field;
|
|
25
|
-
this.pattern = extra.pattern;
|
|
26
|
-
this.optional = extra.optional;
|
|
27
|
-
this.operator = extra.operator;
|
|
28
30
|
}
|
|
29
31
|
setPattern(value) {
|
|
30
32
|
this.pattern = value;
|
|
31
33
|
return this;
|
|
32
34
|
}
|
|
35
|
+
setDescription(value) {
|
|
36
|
+
this.description = value;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
getDescription() {
|
|
40
|
+
return this.description;
|
|
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
|
+
}
|
|
33
46
|
setOptional(value) {
|
|
34
47
|
this.optional = value;
|
|
35
48
|
return this;
|
|
36
49
|
}
|
|
50
|
+
/** Set a default value — emitted as a TypeBox schema default annotation */
|
|
51
|
+
setDefault(value) {
|
|
52
|
+
this.default = value;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
37
55
|
/** 查询比较操作符(query 方向字段)。Rule B: 查询字段恒为可选 */
|
|
38
|
-
|
|
56
|
+
setOperator(value) {
|
|
39
57
|
this.operator = value;
|
|
40
|
-
this.optional = true;
|
|
41
58
|
return this;
|
|
42
59
|
}
|
|
43
60
|
/** optional 优先于 field.optional */
|
|
@@ -60,32 +77,50 @@ class DtoObjectField extends DtoField {
|
|
|
60
77
|
}
|
|
61
78
|
}
|
|
62
79
|
exports.DtoObjectField = DtoObjectField;
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
var DtoDirection;
|
|
81
|
+
(function (DtoDirection) {
|
|
82
|
+
DtoDirection["Input"] = "input";
|
|
83
|
+
DtoDirection["Output"] = "output";
|
|
84
|
+
DtoDirection["Query"] = "query";
|
|
85
|
+
DtoDirection["Pk"] = "pk";
|
|
86
|
+
})(DtoDirection || (exports.DtoDirection = DtoDirection = {}));
|
|
87
|
+
class DtoMessage {
|
|
88
|
+
type = 'dto';
|
|
89
|
+
name;
|
|
90
|
+
description;
|
|
91
|
+
/** 方向:输入或输出 */
|
|
92
|
+
direction;
|
|
93
|
+
fields;
|
|
94
|
+
/** TypeBox base schemas to intersect with at generation time (e.g. PageRequest) */
|
|
95
|
+
bases = [];
|
|
96
|
+
constructor(name, direction, fields, description) {
|
|
97
|
+
this.name = name;
|
|
98
|
+
this.direction = direction;
|
|
99
|
+
this.fields = fields;
|
|
100
|
+
this.description = description;
|
|
101
|
+
}
|
|
102
|
+
/** 引用已存在的 TypeBox base schema,例如 include({ from: '@pylonts/core', name: 'PageRequest' }) */
|
|
103
|
+
include(...refs) {
|
|
104
|
+
this.bases.push(...refs);
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.DtoMessage = DtoMessage;
|
|
109
|
+
function dtoField(field) {
|
|
110
|
+
return new DtoField(field);
|
|
65
111
|
}
|
|
66
|
-
function dtoArrayField(def
|
|
67
|
-
|
|
112
|
+
function dtoArrayField(def) {
|
|
113
|
+
// Reuse an existing DTO as the array element: expand its fields into an object.
|
|
114
|
+
const items = def.items instanceof DtoMessage
|
|
115
|
+
? new DtoObjectField({ name: '', type: 'object', jsType: 'object', properties: def.items.fields })
|
|
116
|
+
: def.items;
|
|
117
|
+
return new DtoArrayField({ name: '', type: 'array', jsType: 'array', ...def, items });
|
|
68
118
|
}
|
|
69
|
-
function dtoObjectField(def
|
|
70
|
-
return new DtoObjectField({ name: '', type: 'object', jsType: 'object', ...def }
|
|
119
|
+
function dtoObjectField(def) {
|
|
120
|
+
return new DtoObjectField({ name: '', type: 'object', jsType: 'object', ...def });
|
|
71
121
|
}
|
|
72
122
|
function buildMessage(name, direction, fields, description) {
|
|
73
|
-
const message =
|
|
74
|
-
name,
|
|
75
|
-
direction,
|
|
76
|
-
description,
|
|
77
|
-
fields,
|
|
78
|
-
bases: [],
|
|
79
|
-
include(...refs) {
|
|
80
|
-
this.bases.push(...refs);
|
|
81
|
-
return this;
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
if (direction === 'query') {
|
|
85
|
-
// Rule B: query/search fields are always optional.
|
|
86
|
-
for (const field of Object.values(message.fields))
|
|
87
|
-
field.optional = true;
|
|
88
|
-
}
|
|
123
|
+
const message = new DtoMessage(name, direction, fields, description);
|
|
89
124
|
// Write back the DTO field name from the map key (safe: DtoField instances
|
|
90
125
|
// are created per DTO, never shared).
|
|
91
126
|
for (const key of Object.keys(message.fields)) {
|
|
@@ -103,16 +138,41 @@ function buildMessage(name, direction, fields, description) {
|
|
|
103
138
|
return message;
|
|
104
139
|
}
|
|
105
140
|
function buildInput(name, fields, description) {
|
|
106
|
-
|
|
141
|
+
const message = buildMessage(name, DtoDirection.Input, fields, description);
|
|
142
|
+
// Rule A — set optionality from the DB column rule (skips fields the author
|
|
143
|
+
// already set): nullable / default / auto-increment → optional, else required.
|
|
144
|
+
for (const field of Object.values(message.fields)) {
|
|
145
|
+
if (field.optional !== undefined)
|
|
146
|
+
continue;
|
|
147
|
+
const f = field.field;
|
|
148
|
+
if (f.schema?.type !== 'table')
|
|
149
|
+
continue;
|
|
150
|
+
const table = f.schema;
|
|
151
|
+
field.optional = f.optional !== false || f.default !== undefined || table.autoIncrement === f;
|
|
152
|
+
}
|
|
153
|
+
return message;
|
|
107
154
|
}
|
|
108
155
|
function buildOutput(name, fields, description) {
|
|
109
|
-
return buildMessage(name,
|
|
156
|
+
return buildMessage(name, DtoDirection.Output, fields, description);
|
|
110
157
|
}
|
|
111
158
|
function buildQuery(name, fields, description) {
|
|
112
|
-
|
|
159
|
+
const message = buildMessage(name, DtoDirection.Query, fields, description);
|
|
160
|
+
// Rule B: query/search fields are always optional.
|
|
161
|
+
for (const field of Object.values(message.fields))
|
|
162
|
+
field.optional = true;
|
|
163
|
+
return message;
|
|
113
164
|
}
|
|
114
165
|
function buildPk(name, fields, description) {
|
|
115
|
-
|
|
166
|
+
const message = buildMessage(name, DtoDirection.Pk, fields, description);
|
|
167
|
+
// Rule P: PK locator fields are required, other fields are optional.
|
|
168
|
+
for (const field of Object.values(message.fields)) {
|
|
169
|
+
if (field.optional !== undefined)
|
|
170
|
+
continue;
|
|
171
|
+
const f = field.field;
|
|
172
|
+
const table = f.schema;
|
|
173
|
+
field.optional = table !== undefined && table.isPk(f) ? false : true;
|
|
174
|
+
}
|
|
175
|
+
return message;
|
|
116
176
|
}
|
|
117
177
|
/** Pick columns from a built table and wrap them as DTO fields (aligned with dto.from). */
|
|
118
178
|
function from(table, fields) {
|
|
@@ -121,7 +181,9 @@ function from(table, fields) {
|
|
|
121
181
|
if (field.schema !== table) {
|
|
122
182
|
throw new Error(`dto.from(${table.name}): field ${field.name} does not belong to this table`);
|
|
123
183
|
}
|
|
124
|
-
|
|
184
|
+
// DTO field name is camelCase (mer_id → merId); the underlying field.name
|
|
185
|
+
// stays snake_case (DB column).
|
|
186
|
+
out[(0, utils_1.toCamelCase)(field.name)] = dtoField(field);
|
|
125
187
|
}
|
|
126
188
|
return out;
|
|
127
189
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export * from './dsl';
|
|
2
2
|
export * from './dto';
|
|
3
|
+
export * from './bases';
|
|
4
|
+
export * from './utils';
|
|
3
5
|
export * from './project';
|
|
4
6
|
export * from './prototype';
|
|
5
7
|
export * from './dictionary';
|
|
6
8
|
export * from './mysql-driver';
|
|
7
9
|
export * from './enum-driver';
|
|
8
10
|
export * from './typebox-driver';
|
|
11
|
+
export * from './check-inheritance';
|
|
9
12
|
export * from './pattern';
|
|
10
13
|
export * from './patterns/retry';
|
|
11
14
|
export * from './flow';
|
package/dist/index.js
CHANGED
|
@@ -16,12 +16,15 @@ 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);
|
|
20
|
+
__exportStar(require("./utils"), exports);
|
|
19
21
|
__exportStar(require("./project"), exports);
|
|
20
22
|
__exportStar(require("./prototype"), exports);
|
|
21
23
|
__exportStar(require("./dictionary"), exports);
|
|
22
24
|
__exportStar(require("./mysql-driver"), exports);
|
|
23
25
|
__exportStar(require("./enum-driver"), exports);
|
|
24
26
|
__exportStar(require("./typebox-driver"), exports);
|
|
27
|
+
__exportStar(require("./check-inheritance"), exports);
|
|
25
28
|
__exportStar(require("./pattern"), exports);
|
|
26
29
|
__exportStar(require("./patterns/retry"), exports);
|
|
27
30
|
__exportStar(require("./flow"), exports);
|
package/dist/mermaid-driver.js
CHANGED
|
@@ -47,9 +47,13 @@ function renderPageFlowMermaid(schema) {
|
|
|
47
47
|
const ids = new Map();
|
|
48
48
|
const byApp = new Map();
|
|
49
49
|
for (const p of schema.pages) {
|
|
50
|
-
const list = byApp.get(p.app.name)
|
|
51
|
-
list
|
|
52
|
-
|
|
50
|
+
const list = byApp.get(p.app.name);
|
|
51
|
+
if (!list) {
|
|
52
|
+
byApp.set(p.app.name, [p]);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
list.push(p);
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
let appIdx = 0;
|
|
55
59
|
for (const [appName, pages] of byApp) {
|
package/dist/typebox-driver.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { DtoMessage, ImportRef } from './dto';
|
|
2
2
|
export type EnumResolver = (enumName: string) => ImportRef | undefined;
|
|
3
|
+
/** Collect all imports needed to render a DTO: include() bases + enum references. */
|
|
4
|
+
export declare function collectDtoImports(schema: DtoMessage, resolver: EnumResolver | undefined, out: Map<string, ImportRef>): void;
|
|
5
|
+
/** Render one DTO export (const + type) — no file header, for file-level generation. */
|
|
6
|
+
export declare function renderDtoExport(schema: DtoMessage, resolver: EnumResolver | undefined): string;
|
|
7
|
+
/** Render the Static type export for a DTO. */
|
|
8
|
+
export declare function renderDtoTypeExport(name: string): string;
|
|
3
9
|
export declare function renderDtoMessage(schema: DtoMessage, options?: {
|
|
4
10
|
resolver?: EnumResolver;
|
|
5
11
|
source?: string;
|