@pylonts/dsl 1.0.6 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/action.d.ts +7 -0
- package/dist/action.js +3 -0
- package/dist/asset.d.ts +48 -0
- package/dist/asset.js +31 -0
- package/dist/bases.d.ts +6 -2
- package/dist/bases.js +10 -6
- package/dist/check-inheritance.js +1 -4
- package/dist/component.d.ts +20 -0
- package/dist/component.js +1 -0
- package/dist/convert.d.ts +9 -0
- package/dist/convert.js +3 -0
- package/dist/curd.d.ts +62 -0
- package/dist/curd.js +31 -0
- package/dist/db-config.d.ts +8 -0
- package/dist/db-config.js +1 -0
- package/dist/db.d.ts +56 -0
- package/dist/db.js +100 -0
- package/dist/dictionary.d.ts +26 -5
- package/dist/dictionary.js +21 -8
- package/dist/dsl.d.ts +10 -49
- package/dist/dsl.js +12 -103
- package/dist/dto.d.ts +12 -9
- package/dist/dto.js +25 -34
- package/dist/enum-driver.d.ts +1 -1
- package/dist/enum-driver.js +1 -4
- package/dist/event.d.ts +8 -0
- package/dist/event.js +3 -0
- package/dist/flow.d.ts +1 -1
- package/dist/flow.js +3 -8
- package/dist/import-base.d.ts +15 -0
- package/dist/import-base.js +1 -0
- package/dist/index.d.ts +31 -17
- package/dist/index.js +31 -33
- package/dist/mermaid-driver.d.ts +2 -2
- package/dist/mermaid-driver.js +4 -7
- package/dist/mock.d.ts +12 -0
- package/dist/mock.js +1 -0
- package/dist/mysql-driver.d.ts +5 -1
- package/dist/mysql-driver.js +25 -10
- package/dist/navigation.d.ts +22 -0
- package/dist/navigation.js +15 -0
- package/dist/page-def.d.ts +40 -0
- package/dist/page-def.js +38 -0
- package/dist/page-flow.d.ts +5 -3
- package/dist/page-flow.js +109 -18
- package/dist/page.d.ts +37 -15
- package/dist/page.js +23 -13
- package/dist/pattern.js +2 -6
- package/dist/patterns/retry.d.ts +1 -1
- package/dist/patterns/retry.js +2 -6
- package/dist/popup.d.ts +18 -0
- package/dist/popup.js +8 -0
- package/dist/project.d.ts +25 -13
- package/dist/project.js +41 -6
- package/dist/prototype.d.ts +1 -1
- package/dist/prototype.js +1 -4
- package/dist/provider.d.ts +54 -0
- package/dist/provider.js +18 -0
- package/dist/ref.d.ts +14 -0
- package/dist/ref.js +6 -0
- package/dist/route.d.ts +8 -0
- package/dist/route.js +3 -0
- package/dist/typebox-driver.d.ts +3 -3
- package/dist/typebox-driver.js +24 -26
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +5 -4
- package/docs/curd.md +111 -0
- package/docs/dictionary.md +42 -31
- package/docs/driver.md +42 -42
- package/docs/dto.md +6 -6
- package/docs/enum.md +24 -24
- package/docs/project.md +2 -2
- package/docs/table.md +53 -16
- package/package.json +5 -3
- package/src/action.ts +11 -0
- package/src/asset.ts +63 -0
- package/src/bases.ts +30 -20
- package/src/component.ts +22 -0
- package/src/convert.ts +13 -0
- package/src/curd.ts +94 -0
- package/src/db-config.ts +8 -0
- package/src/db.ts +153 -0
- package/src/dictionary.ts +45 -19
- package/src/dsl.ts +13 -106
- package/src/dto.ts +25 -12
- package/src/enum-driver.ts +42 -42
- package/src/event.ts +12 -0
- package/src/flow.ts +103 -103
- package/src/import-base.ts +15 -0
- package/src/index.ts +31 -17
- package/src/mermaid-driver.ts +5 -4
- package/src/mock.ts +12 -0
- package/src/mysql-driver.ts +25 -6
- package/src/navigation.ts +29 -0
- package/src/page-def.ts +80 -0
- package/src/page-flow.ts +117 -15
- package/src/page.ts +57 -20
- package/src/patterns/retry.ts +54 -54
- package/src/popup.ts +25 -0
- package/src/project.ts +57 -14
- package/src/prototype.ts +29 -29
- package/src/provider.ts +73 -0
- package/src/ref.ts +19 -0
- package/src/route.ts +12 -0
- package/src/typebox-driver.ts +192 -187
- package/src/utils.ts +11 -6
- package/src/check-inheritance.ts +0 -86
package/dist/prototype.d.ts
CHANGED
package/dist/prototype.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.definePrototype = definePrototype;
|
|
4
1
|
/**
|
|
5
2
|
* Defines a page prototype. The field keys become the names referenced by
|
|
6
3
|
* later DTO/table definitions; here they only carry label/description.
|
|
7
4
|
*/
|
|
8
|
-
function definePrototype(name, schema) {
|
|
5
|
+
export function definePrototype(name, schema) {
|
|
9
6
|
return { name, ...schema };
|
|
10
7
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ImportBase } from './import-base.js';
|
|
2
|
+
import type { ImportableSchemaBase } from './dsl.js';
|
|
3
|
+
import type { DtoField, DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
|
|
4
|
+
import type { ActionSchema } from './action.js';
|
|
5
|
+
import type { RefSchema } from './ref.js';
|
|
6
|
+
import type { ConvertSchema } from './convert.js';
|
|
7
|
+
/** Parameter data source for a call argument. */
|
|
8
|
+
export type DataRef = {
|
|
9
|
+
type: 'route';
|
|
10
|
+
key: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'data';
|
|
13
|
+
key: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'value';
|
|
16
|
+
value: unknown;
|
|
17
|
+
};
|
|
18
|
+
/** Create a route-parameter reference. */
|
|
19
|
+
export declare function route(key: string): DataRef;
|
|
20
|
+
/** Create a page-data reference. */
|
|
21
|
+
export declare function data(key: string): DataRef;
|
|
22
|
+
export interface CallAction extends ActionSchema {
|
|
23
|
+
type: 'call';
|
|
24
|
+
func: ProviderSchema;
|
|
25
|
+
args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>;
|
|
26
|
+
}
|
|
27
|
+
export declare function call(func: ProviderSchema, args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>): CallAction;
|
|
28
|
+
/** Provider function signature. The generated API client exposes one function
|
|
29
|
+
* per endpoint; ProviderSchema gives that function a name and types. */
|
|
30
|
+
export interface ProviderSchema extends ImportableSchemaBase {
|
|
31
|
+
isAsync: boolean;
|
|
32
|
+
/** Input DTO (buildInput / buildQuery / buildPk result). */
|
|
33
|
+
args: DtoMessage;
|
|
34
|
+
/** Output DTO (buildOutput result) or primitive. */
|
|
35
|
+
results: DtoMessage | number | boolean | string;
|
|
36
|
+
}
|
|
37
|
+
/** Define a provider function. */
|
|
38
|
+
export declare function defineProvider(name: string, schema: {
|
|
39
|
+
isAsync: boolean;
|
|
40
|
+
args: DtoMessage;
|
|
41
|
+
results: DtoMessage | number | boolean | string;
|
|
42
|
+
description?: string;
|
|
43
|
+
importRef?: ImportBase;
|
|
44
|
+
}): ProviderSchema;
|
|
45
|
+
/** Assign a call's result to a page data field.
|
|
46
|
+
* React: setState({ [field]: await ... }). Mini-program: this.setData({ [field]: ... }). */
|
|
47
|
+
export interface SetDataAction extends ActionSchema {
|
|
48
|
+
type: 'setData';
|
|
49
|
+
call: CallAction;
|
|
50
|
+
field: DtoField;
|
|
51
|
+
/** Optional field-level transform before assignment. */
|
|
52
|
+
convert?: ConvertSchema;
|
|
53
|
+
}
|
|
54
|
+
export declare function setData(call: CallAction, field: DtoField, convert?: ConvertSchema): SetDataAction;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Create a route-parameter reference. */
|
|
2
|
+
export function route(key) {
|
|
3
|
+
return { type: 'route', key };
|
|
4
|
+
}
|
|
5
|
+
/** Create a page-data reference. */
|
|
6
|
+
export function data(key) {
|
|
7
|
+
return { type: 'data', key };
|
|
8
|
+
}
|
|
9
|
+
export function call(func, args) {
|
|
10
|
+
return { name: func.name, type: 'call', func, args };
|
|
11
|
+
}
|
|
12
|
+
/** Define a provider function. */
|
|
13
|
+
export function defineProvider(name, schema) {
|
|
14
|
+
return { name, ...schema };
|
|
15
|
+
}
|
|
16
|
+
export function setData(call, field, convert) {
|
|
17
|
+
return { name: 'setData', type: 'setData', call, field, convert };
|
|
18
|
+
}
|
package/dist/ref.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CollectionSchemaBase } from './dsl.js';
|
|
2
|
+
import type { DtoField } from './dto.js';
|
|
3
|
+
/** A reference to a field within another schema. Carries a .schema back-reference
|
|
4
|
+
* so the driver knows where the data comes from (route params, page data, etc.). */
|
|
5
|
+
export interface RefSchema {
|
|
6
|
+
name: string;
|
|
7
|
+
/** Schema back-reference — tells driver the data source. */
|
|
8
|
+
schema: CollectionSchemaBase;
|
|
9
|
+
/** The referenced field instance. */
|
|
10
|
+
field: DtoField;
|
|
11
|
+
}
|
|
12
|
+
/** Create a reference to a field in a source schema.
|
|
13
|
+
* Writes back field.schema to the source so the driver can trace origin. */
|
|
14
|
+
export declare function defineRef(source: CollectionSchemaBase, field: DtoField): RefSchema;
|
package/dist/ref.js
ADDED
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CollectionSchemaBase } from './dsl.js';
|
|
2
|
+
import type { DtoField } from './dto.js';
|
|
3
|
+
/** Route params: data that arrives from the navigation URL / route. */
|
|
4
|
+
export interface RouteDataSchema extends CollectionSchemaBase {
|
|
5
|
+
type: 'route';
|
|
6
|
+
fields: Record<string, DtoField>;
|
|
7
|
+
}
|
|
8
|
+
export declare function defineRouteData(name: string, fields: RouteDataSchema['fields']): RouteDataSchema;
|
package/dist/route.js
ADDED
package/dist/typebox-driver.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DtoMessage,
|
|
2
|
-
export type EnumResolver = (enumName: string) =>
|
|
1
|
+
import { DtoMessage, ImportBase } from './dto.js';
|
|
2
|
+
export type EnumResolver = (enumName: string) => ImportBase | undefined;
|
|
3
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,
|
|
4
|
+
export declare function collectDtoImports(schema: DtoMessage, resolver: EnumResolver | undefined, out: Map<string, ImportBase>): void;
|
|
5
5
|
/** Render one DTO export (const + type) — no file header, for file-level generation. */
|
|
6
6
|
export declare function renderDtoExport(schema: DtoMessage, resolver: EnumResolver | undefined): string;
|
|
7
7
|
/** Render the Static type export for a DTO. */
|
package/dist/typebox-driver.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.collectDtoImports = collectDtoImports;
|
|
4
|
-
exports.renderDtoExport = renderDtoExport;
|
|
5
|
-
exports.renderDtoTypeExport = renderDtoTypeExport;
|
|
6
|
-
exports.renderDtoMessage = renderDtoMessage;
|
|
7
|
-
const dto_1 = require("./dto");
|
|
8
1
|
function renderString(s) {
|
|
9
2
|
return `'${s.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
10
3
|
}
|
|
@@ -83,31 +76,36 @@ function renderField(f, indent, resolver) {
|
|
|
83
76
|
return f.isOptional() ? `Type.Optional(${base})` : base;
|
|
84
77
|
}
|
|
85
78
|
function renderValue(f, indent, resolver) {
|
|
86
|
-
if (f
|
|
87
|
-
const items = f.items
|
|
79
|
+
if (f.field.type === 'array') {
|
|
80
|
+
const items = f.field.items;
|
|
88
81
|
// Referenced DTO element — render by name (same-file export), not expanded.
|
|
89
|
-
if (items
|
|
82
|
+
if (isDtoMessage(items))
|
|
90
83
|
return `Type.Array(${items.name})`;
|
|
91
84
|
return `Type.Array(${renderField(items, indent + 1, resolver)})`;
|
|
92
85
|
}
|
|
93
|
-
if (f
|
|
94
|
-
return renderObject(f.properties
|
|
86
|
+
if (f.field.type === 'object') {
|
|
87
|
+
return renderObject(f.field.properties, indent + 1, resolver);
|
|
95
88
|
}
|
|
96
89
|
// DtoField only wraps a database Field; array/object defs live in the subclasses.
|
|
97
|
-
// DTO-level
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
// Only DTO-level defaults (setDefault) are emitted as TypeBox default
|
|
91
|
+
// annotations; DB field defaults are not carried into the API contract.
|
|
92
|
+
return renderBasic(f.field, f.pattern, f.default, resolver);
|
|
93
|
+
}
|
|
94
|
+
/** Structural check — DtoMessage instances may come from a different module copy, so instanceof is unreliable. */
|
|
95
|
+
function isDtoMessage(v) {
|
|
96
|
+
if (typeof v !== 'object' || v === null)
|
|
97
|
+
return false;
|
|
98
|
+
return v.type === 'dto';
|
|
101
99
|
}
|
|
102
100
|
function collectEnumImports(f, resolver, out) {
|
|
103
|
-
if (f
|
|
104
|
-
const items = f.items
|
|
105
|
-
if (!(items
|
|
101
|
+
if (f.field.type === 'array') {
|
|
102
|
+
const items = f.field.items;
|
|
103
|
+
if (!isDtoMessage(items))
|
|
106
104
|
collectEnumImports(items, resolver, out);
|
|
107
105
|
return;
|
|
108
106
|
}
|
|
109
|
-
if (f
|
|
110
|
-
for (const child of Object.values(f.properties
|
|
107
|
+
if (f.field.type === 'object') {
|
|
108
|
+
for (const child of Object.values(f.field.properties))
|
|
111
109
|
collectEnumImports(child, resolver, out);
|
|
112
110
|
return;
|
|
113
111
|
}
|
|
@@ -119,14 +117,14 @@ function collectEnumImports(f, resolver, out) {
|
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
/** Collect all imports needed to render a DTO: include() bases + enum references. */
|
|
122
|
-
function collectDtoImports(schema, resolver, out) {
|
|
120
|
+
export function collectDtoImports(schema, resolver, out) {
|
|
123
121
|
for (const base of schema.bases ?? [])
|
|
124
122
|
out.set(`${base.from}#${base.name}`, base);
|
|
125
123
|
for (const f of Object.values(schema.fields))
|
|
126
124
|
collectEnumImports(f, resolver, out);
|
|
127
125
|
}
|
|
128
126
|
/** Render one DTO export (const + type) — no file header, for file-level generation. */
|
|
129
|
-
function renderDtoExport(schema, resolver) {
|
|
127
|
+
export function renderDtoExport(schema, resolver) {
|
|
130
128
|
const object = renderObject(schema.fields, 1, resolver);
|
|
131
129
|
const bases = schema.bases ?? [];
|
|
132
130
|
const body = bases.length > 0
|
|
@@ -135,10 +133,10 @@ function renderDtoExport(schema, resolver) {
|
|
|
135
133
|
return `export const ${schema.name} = ${body};`;
|
|
136
134
|
}
|
|
137
135
|
/** Render the Static type export for a DTO. */
|
|
138
|
-
function renderDtoTypeExport(name) {
|
|
136
|
+
export function renderDtoTypeExport(name) {
|
|
139
137
|
return `export type ${name} = Static<typeof ${name}>;`;
|
|
140
138
|
}
|
|
141
|
-
function renderDtoMessage(schema, options = {}) {
|
|
139
|
+
export function renderDtoMessage(schema, options = {}) {
|
|
142
140
|
const { resolver, source } = options;
|
|
143
141
|
const imports = new Map();
|
|
144
142
|
collectDtoImports(schema, resolver, imports);
|
|
@@ -146,7 +144,7 @@ function renderDtoMessage(schema, options = {}) {
|
|
|
146
144
|
'// AUTO-GENERATED by typebox-driver — DO NOT EDIT',
|
|
147
145
|
...(source !== undefined ? [`// Source: ${source}`] : []),
|
|
148
146
|
"import { Type, Static } from '@sinclair/typebox';",
|
|
149
|
-
...[...imports.values()].map((r) => `import { ${r.name} } from '${r.from}';`),
|
|
147
|
+
...[...imports.values()].map((r) => `import${r.type ? ' type' : ''} { ${r.name} } from '${r.from}';`),
|
|
150
148
|
];
|
|
151
149
|
return [
|
|
152
150
|
...header,
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// ── naming conversions ──
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.toCamelCase = toCamelCase;
|
|
5
2
|
/** snake_case → camelCase: mer_id → merId; names without underscores are unchanged */
|
|
6
|
-
function toCamelCase(name) {
|
|
3
|
+
export function toCamelCase(name) {
|
|
7
4
|
return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
8
5
|
}
|
|
6
|
+
/** snake_case → PascalCase: mer_id → MerId */
|
|
7
|
+
export function toPascalCase(snake) {
|
|
8
|
+
return snake.replace(/(^|_)([a-z])/g, (_m, _p, c) => c.toUpperCase());
|
|
9
|
+
}
|
package/docs/curd.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# 管理端 CRUD 页面标准(CurdSchema)
|
|
2
|
+
|
|
3
|
+
CurdSchema 是**管理端专用**(`FrontAppSchema.type === 'admin'`)的 CRUD 页面标准:绑定一张实体表 + 一个管理端 app,描述列表页与新增/编辑/详情动作页生成所需的全部页面语义。一条 CurdSchema = 列表页(+ 动作页)的生成规格。
|
|
4
|
+
|
|
5
|
+
页面定义文件按实体组织:`{project}/pages/{entity}.curd.ts`(与 `schema/*.table.ts` 平级)。
|
|
6
|
+
|
|
7
|
+
**CurdSchema 只依赖 table schema(`Field` 实例),不挂钩 DTO(`DtoMessage`)**——DTO 由生成器按标准从 `columns` 推导。
|
|
8
|
+
|
|
9
|
+
## 定义
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defineCurd } from '@pylonts/dsl';
|
|
13
|
+
|
|
14
|
+
export const orderCurd = defineCurd('order-curd', {
|
|
15
|
+
description: '订单管理',
|
|
16
|
+
app: webAdmin, // 所属管理端(project.config.ts 的 FrontAppSchema 共享实例)
|
|
17
|
+
table: order, // 绑定实体表(共享实例)
|
|
18
|
+
title: '订单管理',
|
|
19
|
+
section: '订单管理', // 必填:sidebar 分组名
|
|
20
|
+
actions: [defineAction('EXPORT', '导出订单')], // 额外操作按钮
|
|
21
|
+
actionPages: {
|
|
22
|
+
add: { mode: 'modal', columns: [order.columns.order_no, order.columns.mer_id] },
|
|
23
|
+
update: { mode: 'modal', columns: [order.columns.id, order.columns.order_no] },
|
|
24
|
+
detail: { mode: 'route', columns: [order.columns.id, order.columns.order_no, order.columns.amount] },
|
|
25
|
+
},
|
|
26
|
+
list: {
|
|
27
|
+
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 可跨表
|
|
28
|
+
keyword: { columns: [order.columns.order_no] }, // 模糊搜索(本表字段)
|
|
29
|
+
orderBy: { column: order.columns.id, direction: 'desc' },
|
|
30
|
+
searchFields: [{ field: order.columns.mer_id }, { field: order.columns.order_no, op: 'like' }],
|
|
31
|
+
columnTitles: { order_no: '订单号', name: '商户名称' }, // Field.name → 文案
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 字段
|
|
37
|
+
|
|
38
|
+
| 字段 | 类型 | 说明 |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `app` | `FrontAppSchema` | 所属管理端(共享实例,`type` 必须为 `'admin'`) |
|
|
41
|
+
| `table` | `TableSchema` | 绑定实体表(共享实例) |
|
|
42
|
+
| `title` | `string` | 列表页中文标题 |
|
|
43
|
+
| `section` | `string` | **必填**:sidebar 分组名(`pylonts gen curd` 据此生成路由注册的 `section` 字段) |
|
|
44
|
+
| `actions?` | `ActionSchema[]` | 页面额外可执行动作(标准 CRUD 之外,如导出、审核) |
|
|
45
|
+
| `actionPages?` | `{ add? / update? / detail? }` | 动作页:`{ mode: 'modal' \| 'route'; columns: Field[] }` |
|
|
46
|
+
| `list` | `CurdListConfig` | 列表页配置(必填) |
|
|
47
|
+
|
|
48
|
+
### ActionPage
|
|
49
|
+
|
|
50
|
+
| 字段 | 类型 | 说明 |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `mode` | `'modal' \| 'route'` | 弹窗或独立路由 |
|
|
53
|
+
| `columns` | `Field[]` | 该页面渲染的字段,**必填非空**——前端要显示的字段必须全部显式列出 |
|
|
54
|
+
|
|
55
|
+
### CurdListConfig
|
|
56
|
+
|
|
57
|
+
| 字段 | 类型 | 说明 |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `columns` | `Field[]` | 列表列,**必填非空**——前端要显示的字段必须全部显式列出;可含跨表字段(见下) |
|
|
60
|
+
| `keyword?` | `{ columns: Field[] }` | 模糊搜索,columns 必须是**本表字段实例** |
|
|
61
|
+
| `orderBy` | `{ column: Field; direction: 'asc' \| 'desc' }` | 默认排序,**必填**,column 与 direction 都必填;column 必须是**本表字段实例** |
|
|
62
|
+
| `searchFields?` | `{ field: Field; op?: Operator }[]` | 搜索条件字段,op 默认 `'eq'`,可选 `eq/gt/gte/lt/lte/like/ne` |
|
|
63
|
+
| `columnTitles?` | `Record<string, string>` | 列标题覆盖:`Field.name` → 中文文案 |
|
|
64
|
+
|
|
65
|
+
## 跨表字段
|
|
66
|
+
|
|
67
|
+
`columns` / `searchFields` 里的 `Field` 实例可指向**本表或其他表**的列——列表列与搜索条件因此可以显示关联表字段(如订单列表显示商户名称):
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
list: {
|
|
71
|
+
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 跨表:字段指向 merchant.name
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 默认与校验
|
|
76
|
+
|
|
77
|
+
- `list.columns` / `actionPages.*.columns` **必填非空**(不允许省略、不允许空数组)——前端显示什么必须显式定义
|
|
78
|
+
- `list.orderBy` **必填**,`column` 与 `direction` 都必填(规格:默认主键 desc 由定义方显式写出)
|
|
79
|
+
- 运行时校验(`defineCurd`,仿 `defineTable` 强校验风格):
|
|
80
|
+
- `app.type` 必须为 `'admin'`,否则抛错
|
|
81
|
+
- 所有 `columns` 非空,否则抛错
|
|
82
|
+
- `list.keyword.columns` / `list.orderBy.column` 必须属于 `table`,否则抛错
|
|
83
|
+
- `list.columns` / `list.searchFields` 允许跨表,**不校验归属**
|
|
84
|
+
- 生成时校验(curd 生成器,`DtoSchemaGen.add` / `update`):
|
|
85
|
+
- 表配置 `autoIncrement` 或 `generator`(主键由服务端生成)时,`actionPages.add.columns` **不允许包含主键字段**,否则抛错——AddRequest 不携带服务端生成的主键
|
|
86
|
+
- `actionPages.update.columns` **必须包含主键字段**,否则抛错——UpdateRequest 靠主键定位记录
|
|
87
|
+
|
|
88
|
+
## DTO 推导(生成器约定)
|
|
89
|
+
|
|
90
|
+
DTO 由 curd 生成器从 `CurdSchema` 按标准命名推导,页面语义不持有 DTO 实例:
|
|
91
|
+
|
|
92
|
+
| DTO | 命名 | 字段来源 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| Row | `{Pascal}Row` | `list.columns` |
|
|
95
|
+
| AddRequest | `{Pascal}AddRequest` | `actionPages.add.columns` |
|
|
96
|
+
| UpdateRequest | `{Pascal}UpdateRequest` | `actionPages.update.columns` |
|
|
97
|
+
| DetailRequest | `{Pascal}DetailRequest` | 主键 |
|
|
98
|
+
| DetailResponse | `{Pascal}DetailResponse` | `actionPages.detail.columns` |
|
|
99
|
+
|
|
100
|
+
## 与旧 PageConfig 的差异
|
|
101
|
+
|
|
102
|
+
| PageConfig(旧方案,已废弃) | CurdSchema |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `module: string` | 由 `app` 推导(后端模块 == app 1:1) |
|
|
105
|
+
| `schema: 'bd'` 字符串 | `table: TableSchema` 实例(类型安全) |
|
|
106
|
+
| `operations: { label, action }` | `actions: ActionSchema[]` |
|
|
107
|
+
| `detail.mode` 单例 | `actionPages.detail.mode` |
|
|
108
|
+
| `forms.add / forms.update` | `actionPages.add / actionPages.update` |
|
|
109
|
+
| `keyword` / `orderBy` / `columnTitles` | `list.keyword` / `list.orderBy` / `list.columnTitles`(列改字段实例引用) |
|
|
110
|
+
| `naming` | 去掉(DTO 命名是生成器约定,非页面语义) |
|
|
111
|
+
| DTO 引用(`request` / `fields` / `DtoFields`) | 去掉(DTO 由生成器推导,页面只依赖 table) |
|
package/docs/dictionary.md
CHANGED
|
@@ -1,31 +1,42 @@
|
|
|
1
|
-
# 短语词典 (Dictionary)
|
|
2
|
-
|
|
3
|
-
词典是与团队达成共识的基础知识库:**某词代表什么**(语义/定义层面),不是物理形式。短语定了,字段命名、外键命名就都有依据——全项目只说同一种话。
|
|
4
|
-
|
|
5
|
-
- 是基础知识库,很少变更。
|
|
6
|
-
- **能引用就引用**:魔法字符串只在首次出现时使用,之后一律引用词典条目。
|
|
7
|
-
|
|
8
|
-
## 规范位置:schema/_dictionary.ts
|
|
9
|
-
|
|
10
|
-
**所有短语统一定义在 `schema/_dictionary.ts`**,一个文件一处定义;`*.table.ts` 从该文件 import 短语,禁止在表文件里内联定义短语。
|
|
11
|
-
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
# 短语词典 (Dictionary)
|
|
2
|
+
|
|
3
|
+
词典是与团队达成共识的基础知识库:**某词代表什么**(语义/定义层面),不是物理形式。短语定了,字段命名、外键命名就都有依据——全项目只说同一种话。
|
|
4
|
+
|
|
5
|
+
- 是基础知识库,很少变更。
|
|
6
|
+
- **能引用就引用**:魔法字符串只在首次出现时使用,之后一律引用词典条目。
|
|
7
|
+
|
|
8
|
+
## 规范位置:schema/_dictionary.ts
|
|
9
|
+
|
|
10
|
+
**所有短语统一定义在 `schema/_dictionary.ts`**,一个文件一处定义;`*.table.ts` 从该文件 import 短语,禁止在表文件里内联定义短语。
|
|
11
|
+
|
|
12
|
+
## 两种短语,两种命名规则
|
|
13
|
+
|
|
14
|
+
短语分两类,参与不同的字段命名校验:
|
|
15
|
+
|
|
16
|
+
| 类型 | 定义函数 | 含义 | 命名规则 | 例子 |
|
|
17
|
+
|---|---|---|---|---|
|
|
18
|
+
| `entity` | `defineEntityPhrase` | 实体缩写 | 字段名**首段**(实体领先) | `mer_id`、`bd_rate` |
|
|
19
|
+
| `business` | `defineBusinessPhrase` | 实体的属性 | 字段名**末段**(属性收尾) | `bd_rate`、`acquiring_rate` |
|
|
20
|
+
|
|
21
|
+
## 口径:name 即短语
|
|
22
|
+
|
|
23
|
+
两个定义函数返回的条目**就是短语本身**,不是"全名 + 缩写"两套——`name` 即短语词干(列名前缀),`label`/`description` 解释语义。**变量名与 name 一致**(小写)。短语要短(mer / bd / amt 三字母左右),**不要用长语**:引用商户实体的字段叫 `mer_id`,不叫 `merchant_id`。
|
|
24
|
+
|
|
25
|
+
## 定义
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// schema/_dictionary.ts
|
|
29
|
+
import { defineEntityPhrase, defineBusinessPhrase } from '@pylonts/dsl';
|
|
30
|
+
|
|
31
|
+
const bd = defineEntityPhrase({ name: 'bd', label: 'BD推广员', description: '线下拓展商户、辅助入驻的推广人员' });
|
|
32
|
+
const mer = defineEntityPhrase({ name: 'mer', label: '商户', description: '入驻平台的商户' });
|
|
33
|
+
const amt = defineBusinessPhrase({ name: 'amt', label: '金额', description: '交易金额,单位分' });
|
|
34
|
+
const rate = defineBusinessPhrase({ name: 'rate', label: '费率', description: '结算费率' });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 使用
|
|
38
|
+
|
|
39
|
+
- **表链接实体**:`TableSchema.phrase` 引用**实体短语**条目,声明本表归属哪个实体(见 [table.md](./table.md) 的外键检查链)。关联表等多实体场景不需要。
|
|
40
|
+
- 业务短语供字段命名/文档使用,跨团队对齐。
|
|
41
|
+
- 未收录短语的实体保留全名作词干(不臆造缩写),评审时再裁决收录。
|
|
42
|
+
- 字段命名校验按类型区分:实体短语必须首段、业务短语必须末段(见 [field-check.md](../../lint/docs/field-check.md))。
|
package/docs/driver.md
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
# Driver 模式与产物生成
|
|
2
|
-
|
|
3
|
-
DSL 定义元数据,driver 翻译成目标语言产物。产物与定义解耦,同一份定义可生成不同目标:
|
|
4
|
-
|
|
5
|
-
| 定义 | Driver | 产物 | 消费方 |
|
|
6
|
-
|---|---|---|---|
|
|
7
|
-
| `TableSchema` | mysql-driver | `CREATE TABLE` | MySQL |
|
|
8
|
-
| `EnumDef` | enum-driver | `export enum Xxx { … }` + `XXX_LABEL` | 业务代码 |
|
|
9
|
-
| `DtoMessage` | typebox-driver | `Type.Object({…})` + `Static` 推导 | fastify v5 参数校验 |
|
|
10
|
-
|
|
11
|
-
## 生成 SQL
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { buildCreateTableSql } from '@pylonts/dsl';
|
|
15
|
-
|
|
16
|
-
buildCreateTableSql(order); // "CREATE TABLE `order` (\n ..."
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
默认不生成外键约束;需要时:
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
buildCreateTableSql(order, { generateForeignKeys: true });
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 生成枚举源码
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { renderEnum } from '@pylonts/dsl';
|
|
29
|
-
|
|
30
|
-
renderEnum(AcquiringType); // TS enum 源码
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## 生成 TypeBox 源码
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
import { renderDtoMessage } from '@pylonts/dsl';
|
|
37
|
-
|
|
38
|
-
renderDtoMessage(orderPageQuery, {
|
|
39
|
-
source: 'dto_schema/order/order.
|
|
40
|
-
resolver: (name) => ({ from: '@mall/enums/user', name }), // 枚举引用解析
|
|
41
|
-
});
|
|
42
|
-
```
|
|
1
|
+
# Driver 模式与产物生成
|
|
2
|
+
|
|
3
|
+
DSL 定义元数据,driver 翻译成目标语言产物。产物与定义解耦,同一份定义可生成不同目标:
|
|
4
|
+
|
|
5
|
+
| 定义 | Driver | 产物 | 消费方 |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| `TableSchema` | mysql-driver | `CREATE TABLE` | MySQL |
|
|
8
|
+
| `EnumDef` | enum-driver | `export enum Xxx { … }` + `XXX_LABEL` | 业务代码 |
|
|
9
|
+
| `DtoMessage` | typebox-driver | `Type.Object({…})` + `Static` 推导 | fastify v5 参数校验 |
|
|
10
|
+
|
|
11
|
+
## 生成 SQL
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { buildCreateTableSql } from '@pylonts/dsl';
|
|
15
|
+
|
|
16
|
+
buildCreateTableSql(order); // "CREATE TABLE `order` (\n ..."
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
默认不生成外键约束;需要时:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
buildCreateTableSql(order, { generateForeignKeys: true });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 生成枚举源码
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { renderEnum } from '@pylonts/dsl';
|
|
29
|
+
|
|
30
|
+
renderEnum(AcquiringType); // TS enum 源码
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 生成 TypeBox 源码
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { renderDtoMessage } from '@pylonts/dsl';
|
|
37
|
+
|
|
38
|
+
renderDtoMessage(orderPageQuery, {
|
|
39
|
+
source: 'dto_schema/order/order.dto.ts',
|
|
40
|
+
resolver: (name) => ({ from: '@mall/enums/user', name }), // 枚举引用解析
|
|
41
|
+
});
|
|
42
|
+
```
|
package/docs/dto.md
CHANGED
|
@@ -4,7 +4,7 @@ DTO 描述接口出入参。方向决定可选性规则,由各方向工厂设
|
|
|
4
4
|
|
|
5
5
|
| 构建器 | 方向 | 可选性规则 |
|
|
6
6
|
|---|---|---|
|
|
7
|
-
| `buildInput` | input | 按 DB
|
|
7
|
+
| `buildInput` | input | 按 DB 列规则:主键 → 必填;可空(未写 `optional` 或 `optional: true`)/ 有默认 → 可选;`optional: false` 无默认 → 必填 |
|
|
8
8
|
| `buildOutput` | output | 不设置(保持字段构建器/作者设置) |
|
|
9
9
|
| `buildQuery` | query | 全部可选 |
|
|
10
10
|
| `buildPk` | pk | 主键字段必填,其他字段可选 |
|
|
@@ -15,19 +15,19 @@ DTO 描述接口出入参。方向决定可选性规则,由各方向工厂设
|
|
|
15
15
|
import { buildInput, buildQuery, dtoField, from } from '@pylonts/dsl';
|
|
16
16
|
|
|
17
17
|
// 输入:新增订单
|
|
18
|
-
buildInput('OrderAddRequest', { ...from(order, [order.
|
|
18
|
+
buildInput('OrderAddRequest', { ...from(order, [order.columns.mer_id, order.columns.amount]) });
|
|
19
19
|
|
|
20
20
|
// 输出:订单行
|
|
21
|
-
buildOutput('OrderRow', from(order, [order.
|
|
21
|
+
buildOutput('OrderRow', from(order, [order.columns.id, order.columns.order_no]));
|
|
22
22
|
|
|
23
23
|
// 查询:分页 + 过滤(query 字段恒为可选,.op() 声明比较操作符)
|
|
24
24
|
buildQuery('OrderPageQuery', {
|
|
25
25
|
keyword: dtoField(stringField({ maxLength: 32 })).setOperator('like'),
|
|
26
|
-
...from(order, [order.
|
|
26
|
+
...from(order, [order.columns.mer_id]),
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
// 主键:按 id 取详情
|
|
30
|
-
buildPk('OrderDetailRequest', from(order, [order.
|
|
30
|
+
buildPk('OrderDetailRequest', from(order, [order.columns.id]));
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
`from(table, fields)` 提取表字段包装为 DTO 字段,字段实例与表共享,`name/schema` 保持指向表。**DTO 字段名转 camelCase**(`mer_id` → `merId`),与 DB 列名(snake_case)分离。`from()` 本身不做任何可选性推断——推断在各方向工厂。
|
|
@@ -63,5 +63,5 @@ buildQuery('OrderPageQuery', { ... })
|
|
|
63
63
|
## 文件组织
|
|
64
64
|
|
|
65
65
|
- `dto_schema/{module}/*.dto.ts`:DTO 源文件,一个文件可导出多个 DtoMessage;`module` = `project.config.ts` 的 `apps.name`,另加 `common/` 放跨 app 共享 DTO。
|
|
66
|
-
- `gen
|
|
66
|
+
- `pylonts gen dto` 按 module 逐个扫描:`dto_schema/{module}/*.dto.ts` → 生成 `dto/{module}/{Name}.dto.ts`。
|
|
67
67
|
- 枚举字段引用 `enums/` 下生成的枚举源文件,DTO 文件通过 `../../enums/{JsName}.enum` 导入。
|
package/docs/enum.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# 定义枚举(可跨表复用)
|
|
2
|
-
|
|
3
|
-
枚举定义与字段分离:`defineEnum` 产生共享定义(纯值对象),`enumField` 引用它。同一枚举可被多张表 / 多个 DTO 复用,只生成一次。
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
import { defineEnum, enumField } from '@pylonts/dsl';
|
|
7
|
-
|
|
8
|
-
// 共享定义(_common.ts 等公共文件)
|
|
9
|
-
export const AcquiringType = defineEnum('AcquiringType', 'string', [
|
|
10
|
-
{ symbol: 'WECHAT', value: 'wechat', label: '微信' },
|
|
11
|
-
{ symbol: 'UNIONPAY', value: 'unionpay', label: '银联商务' },
|
|
12
|
-
]);
|
|
13
|
-
|
|
14
|
-
// 字段引用(每表独立实例)
|
|
15
|
-
buildTable('merchant', {
|
|
16
|
-
buildTable('order', {
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
- 字段实例每表独立(列名、可选性随表),枚举定义全局共享。
|
|
20
|
-
- 枚举由 enum-driver 生成独立文件;typebox-driver 只渲染 `Type.Enum(名称)` + import 引用,不内联。
|
|
21
|
-
|
|
22
|
-
## 生成枚举源码
|
|
23
|
-
|
|
24
|
-
见 [driver.md](./driver.md)。
|
|
1
|
+
# 定义枚举(可跨表复用)
|
|
2
|
+
|
|
3
|
+
枚举定义与字段分离:`defineEnum` 产生共享定义(纯值对象),`enumField` 引用它。同一枚举可被多张表 / 多个 DTO 复用,只生成一次。
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { defineEnum, enumField } from '@pylonts/dsl';
|
|
7
|
+
|
|
8
|
+
// 共享定义(_common.ts 等公共文件)
|
|
9
|
+
export const AcquiringType = defineEnum('AcquiringType', 'string', [
|
|
10
|
+
{ symbol: 'WECHAT', value: 'wechat', label: '微信' },
|
|
11
|
+
{ symbol: 'UNIONPAY', value: 'unionpay', label: '银联商务' },
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
// 字段引用(每表独立实例)
|
|
15
|
+
buildTable('merchant', { columns: { acquiring_type: enumField({ enum: AcquiringType }) }, ... });
|
|
16
|
+
buildTable('order', { columns: { acquiring_type: enumField({ enum: AcquiringType }) }, ... });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- 字段实例每表独立(列名、可选性随表),枚举定义全局共享。
|
|
20
|
+
- 枚举由 enum-driver 生成独立文件;typebox-driver 只渲染 `Type.Enum(名称)` + import 引用,不内联。
|
|
21
|
+
|
|
22
|
+
## 生成枚举源码
|
|
23
|
+
|
|
24
|
+
见 [driver.md](./driver.md)。
|
package/docs/project.md
CHANGED
|
@@ -18,7 +18,7 @@ export const mall = defineProject('mall', {
|
|
|
18
18
|
});
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
21
|
+
- `FrontAppSchema`:`name` / `description` / `type`(admin | wxmini)/ `dir`(相对仓库根目录的源码目录)。
|
|
22
|
+
- `ProjectApiSchema`:`name` / `description` / `dir` / `apps`(直接引用共享的 FrontAppSchema 实例——一个 app 被多个 API 服务就定义一次、引用多次)/ `contextPath`(API 基础 URL 前缀,如 `/mall`,空串表示无前缀)。
|
|
23
23
|
- **直接对象引用优先**:`api.apps` 与 `project.apps` 指向同一实例,不写字符串。
|
|
24
24
|
- **contextPath 解析**:前端 app 的 API 前缀由服务它的 api 决定——`api.apps` 必须恰好包含该 app(零个或多个都报错),app 本身不声明 contextPath。
|