@pylonts/dsl 1.1.4 → 1.1.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/controller.d.ts +27 -0
- package/dist/controller.js +11 -0
- package/dist/convert.d.ts +18 -6
- package/dist/convert.js +12 -2
- package/dist/curd.d.ts +0 -2
- package/dist/curd.js +7 -0
- package/dist/dao.d.ts +111 -2
- package/dist/dao.js +28 -2
- package/dist/db.js +3 -0
- package/dist/dsl.d.ts +24 -1
- package/dist/dsl.js +16 -0
- package/dist/dto.d.ts +6 -2
- package/dist/dto.js +20 -9
- package/dist/endpoint.d.ts +15 -0
- package/dist/endpoint.js +3 -0
- package/dist/exception.d.ts +14 -0
- package/dist/exception.js +9 -0
- package/dist/field-rule.d.ts +20 -0
- package/dist/field-rule.js +19 -0
- package/dist/flow.d.ts +24 -2
- package/dist/flow.js +16 -4
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -0
- package/dist/mermaid-driver.js +32 -3
- package/dist/method.d.ts +11 -0
- package/dist/method.js +3 -0
- package/dist/mysql-driver.js +8 -1
- package/dist/provider.d.ts +6 -11
- package/dist/provider.js +2 -2
- package/dist/service.d.ts +16 -6
- package/dist/service.js +13 -2
- package/dist/third-service.d.ts +75 -0
- package/dist/third-service.js +96 -0
- package/dist/typebox-driver.d.ts +6 -0
- package/dist/typebox-driver.js +76 -14
- package/dist/utils.d.ts +25 -10
- package/dist/utils.js +28 -11
- package/docs/curd.md +110 -110
- package/docs/dto.md +9 -2
- package/docs/table.md +135 -135
- package/docs/third-service.md +122 -0
- package/package.json +4 -1
- package/src/action.ts +10 -10
- package/src/asset.ts +62 -62
- package/src/bases.ts +29 -29
- package/src/component.ts +21 -21
- package/src/controller.ts +40 -0
- package/src/convert.ts +34 -7
- package/src/curd.ts +7 -2
- package/src/dao.ts +162 -3
- package/src/db.ts +5 -0
- package/src/dsl.ts +49 -1
- package/src/dto.ts +25 -9
- package/src/endpoint.ts +18 -0
- package/src/event.ts +12 -12
- package/src/exception.ts +28 -0
- package/src/field-rule.ts +47 -0
- package/src/flow.ts +143 -103
- package/src/index.ts +11 -1
- package/src/mermaid-driver.ts +31 -3
- package/src/method.ts +20 -0
- package/src/mock.ts +12 -12
- package/src/mysql-driver.ts +8 -2
- package/src/navigation.ts +28 -28
- package/src/page-def.ts +79 -79
- package/src/page-flow.ts +153 -153
- package/src/page.ts +76 -76
- package/src/popup.ts +25 -25
- package/src/project.ts +97 -97
- package/src/provider.ts +7 -12
- package/src/ref.ts +18 -18
- package/src/route.ts +11 -11
- package/src/service.ts +28 -6
- package/src/third-service.ts +186 -0
- package/src/typebox-driver.ts +264 -192
- package/src/utils.ts +54 -17
- package/dist/check-inheritance.d.ts +0 -9
- package/dist/check-inheritance.js +0 -58
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SchemaBase } from './dsl.js';
|
|
2
|
+
import { FrontAppSchema } from './project.js';
|
|
3
|
+
import type { EndpointSchema } from './endpoint.js';
|
|
4
|
+
/** A backend RPC controller. Strong constraints:
|
|
5
|
+
* - a backend module maps 1:1 to a frontend app (they are peers);
|
|
6
|
+
* - a controller serves exactly one frontend app — no cross-module calls. */
|
|
7
|
+
export interface ControllerSchema extends SchemaBase {
|
|
8
|
+
type: 'controller';
|
|
9
|
+
/** The frontend app this controller serves (shared instance from project.config). */
|
|
10
|
+
app: FrontAppSchema;
|
|
11
|
+
/** RPC methods exposed by this controller. */
|
|
12
|
+
methods: ControllerMethodSchema[];
|
|
13
|
+
}
|
|
14
|
+
export declare function defineController(options: {
|
|
15
|
+
name: string;
|
|
16
|
+
app: FrontAppSchema;
|
|
17
|
+
/** Method declarations: type/schema are injected by this builder. */
|
|
18
|
+
methods: Array<Omit<ControllerMethodSchema, 'type' | 'schema'>>;
|
|
19
|
+
description?: string;
|
|
20
|
+
}): ControllerSchema;
|
|
21
|
+
/** An RPC method exposed by a controller. */
|
|
22
|
+
export interface ControllerMethodSchema extends SchemaBase {
|
|
23
|
+
type: 'method';
|
|
24
|
+
schema: ControllerSchema;
|
|
25
|
+
/** Shared API signature — same instance the page-side provider references. */
|
|
26
|
+
signature: EndpointSchema;
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function defineController(options) {
|
|
2
|
+
const schema = {
|
|
3
|
+
type: 'controller',
|
|
4
|
+
name: options.name,
|
|
5
|
+
description: options.description,
|
|
6
|
+
app: options.app,
|
|
7
|
+
methods: [],
|
|
8
|
+
};
|
|
9
|
+
schema.methods = options.methods.map((method) => ({ type: 'method', schema, ...method }));
|
|
10
|
+
return schema;
|
|
11
|
+
}
|
package/dist/convert.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import type { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { DtoMessage } from './dto.js';
|
|
3
|
+
import type { TableSchema } from './db.js';
|
|
4
|
+
import type { ThirdMethodSchema } from './third-service.js';
|
|
2
5
|
import type { FrontAppSchema } from './project.js';
|
|
3
|
-
/**
|
|
4
|
-
|
|
6
|
+
/** A source/target collection of a convert — dto, entity or third-party message. */
|
|
7
|
+
export type ConvertSourceSchema = DtoMessage | TableSchema | ThirdMethodSchema;
|
|
8
|
+
/** Declares a multi-source → single-target schema integration. */
|
|
5
9
|
export interface ConvertSchema extends SchemaBase {
|
|
6
10
|
type: 'convert';
|
|
7
|
-
/** The
|
|
11
|
+
/** The app (module) this convert belongs to — its artifact lands in modules/{app}/convert/. */
|
|
8
12
|
app: FrontAppSchema;
|
|
9
|
-
/**
|
|
10
|
-
|
|
13
|
+
/** Source schemas — one or more, mixed dimensions. */
|
|
14
|
+
sources: ConvertSourceSchema[];
|
|
15
|
+
/** Target schema — the single integrated collection. */
|
|
16
|
+
target: ConvertSourceSchema;
|
|
11
17
|
}
|
|
12
|
-
export declare function defineConvert(
|
|
18
|
+
export declare function defineConvert(options: {
|
|
19
|
+
name: string;
|
|
20
|
+
app: FrontAppSchema;
|
|
21
|
+
sources: ConvertSourceSchema[];
|
|
22
|
+
target: ConvertSourceSchema;
|
|
23
|
+
description?: string;
|
|
24
|
+
}): ConvertSchema;
|
package/dist/convert.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
export function defineConvert(
|
|
2
|
-
|
|
1
|
+
export function defineConvert(options) {
|
|
2
|
+
if (options.sources.length === 0) {
|
|
3
|
+
throw new Error(`convert '${options.name}': sources must not be empty`);
|
|
4
|
+
}
|
|
5
|
+
return {
|
|
6
|
+
type: 'convert',
|
|
7
|
+
name: options.name,
|
|
8
|
+
description: options.description,
|
|
9
|
+
app: options.app,
|
|
10
|
+
sources: options.sources,
|
|
11
|
+
target: options.target,
|
|
12
|
+
};
|
|
3
13
|
}
|
package/dist/curd.d.ts
CHANGED
|
@@ -7,8 +7,6 @@ export type ActionPageMode = 'modal' | 'route';
|
|
|
7
7
|
/** One CRUD action page (add / update / detail). */
|
|
8
8
|
export interface ActionPage {
|
|
9
9
|
mode: ActionPageMode;
|
|
10
|
-
/** For add/update: when true, render as modal on list page; when false/undefined, render as standalone route page. */
|
|
11
|
-
modal?: boolean;
|
|
12
10
|
/** Fields rendered on this page. Required, non-empty — every field the
|
|
13
11
|
* frontend shows must be listed explicitly. */
|
|
14
12
|
columns: Field[];
|
package/dist/curd.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toKebabCase } from '@pylonts/core';
|
|
1
2
|
function assertColumns(curd, pageName, columns) {
|
|
2
3
|
if (columns.length === 0) {
|
|
3
4
|
throw new Error(`curd ${curd.name}: ${pageName}.columns must be non-empty`);
|
|
@@ -17,6 +18,12 @@ export function defineCurd(name, schema) {
|
|
|
17
18
|
if (curd.app.type !== 'admin') {
|
|
18
19
|
throw new Error(`curd ${name}: app ${curd.app.name} must be type 'admin' (got '${curd.app.type}')`);
|
|
19
20
|
}
|
|
21
|
+
// The name is the admin route path — it must be the kebab-case table name
|
|
22
|
+
// so paths cannot drift from the table they serve.
|
|
23
|
+
const expectedName = toKebabCase(curd.table.name);
|
|
24
|
+
if (name !== expectedName) {
|
|
25
|
+
throw new Error(`curd name must be '${expectedName}' (kebab-case of table '${curd.table.name}'); got '${name}'`);
|
|
26
|
+
}
|
|
20
27
|
if (!curd.section) {
|
|
21
28
|
throw new Error(`curd ${name}: section is required (sidebar menu group, e.g. '商户管理')`);
|
|
22
29
|
}
|
package/dist/dao.d.ts
CHANGED
|
@@ -1,9 +1,118 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl.js';
|
|
1
|
+
import { SchemaBase, Field, Operator } from './dsl.js';
|
|
2
2
|
import { FrontAppSchema } from './project.js';
|
|
3
|
+
import { TableSchema } from './db.js';
|
|
3
4
|
/** A data-access layer bound to exactly one frontend app. */
|
|
4
5
|
export interface DaoSchema extends SchemaBase {
|
|
5
6
|
type: 'dao';
|
|
6
7
|
/** The frontend app this DAO belongs to (shared instance from project.config). */
|
|
7
8
|
app: FrontAppSchema;
|
|
9
|
+
/** The table this DAO operates on (single-table atomicity). */
|
|
10
|
+
table: TableSchema;
|
|
11
|
+
/** Methods keyed by name — the map key is written back as the method name. */
|
|
12
|
+
methods: Record<string, DaoMethodSchema>;
|
|
13
|
+
}
|
|
14
|
+
/** Method input for defineDao: name is written back from the methods map key. */
|
|
15
|
+
export type DaoMethodDef = Omit<FindSchema, 'schema' | 'name'> | Omit<GetSchema, 'schema' | 'name'> | Omit<InsertSchema, 'schema' | 'name'> | Omit<UpdateSchema, 'schema' | 'name'> | Omit<DeleteSchema, 'schema' | 'name'> | Omit<AggregateSchema, 'schema' | 'name'>;
|
|
16
|
+
export declare function defineDao(options: {
|
|
17
|
+
name: string;
|
|
18
|
+
app: FrontAppSchema;
|
|
19
|
+
table: TableSchema;
|
|
20
|
+
methods: Record<string, DaoMethodDef>;
|
|
21
|
+
description?: string;
|
|
22
|
+
}): DaoSchema;
|
|
23
|
+
/** One AND-combined criterion: a column plus its comparison operator. */
|
|
24
|
+
export interface QueryField {
|
|
25
|
+
/** Column to compare. */
|
|
26
|
+
field: Field;
|
|
27
|
+
/** Comparison operator; defaults to 'eq'. */
|
|
28
|
+
op?: Operator;
|
|
29
|
+
}
|
|
30
|
+
/** Query criteria for find methods. */
|
|
31
|
+
export interface QuerySchema extends SchemaBase {
|
|
32
|
+
type: 'query';
|
|
33
|
+
/** Criteria; all fields are AND-combined. */
|
|
34
|
+
fields: QueryField[];
|
|
35
|
+
}
|
|
36
|
+
/** Sort specification for find results. */
|
|
37
|
+
export interface OrderBySchema {
|
|
38
|
+
/** Column to sort by. */
|
|
39
|
+
column: Field;
|
|
40
|
+
/** Sort direction. */
|
|
41
|
+
sort: 'asc' | 'desc';
|
|
42
|
+
}
|
|
43
|
+
/** Query rows by criteria; returns a list of row objects. */
|
|
44
|
+
export interface FindSchema extends SchemaBase {
|
|
45
|
+
type: 'find';
|
|
46
|
+
schema: DaoSchema;
|
|
47
|
+
/** Query criteria; omit = all rows. */
|
|
48
|
+
args?: QuerySchema;
|
|
49
|
+
/** Pagination marker: 'page' (page/pageSize) or 'limit' (position/limit).
|
|
50
|
+
* Only presence matters — parameter shapes are a generator convention. */
|
|
51
|
+
mode?: 'page' | 'limit';
|
|
52
|
+
/** Result sort; omit = no explicit order. */
|
|
53
|
+
orderBy?: OrderBySchema | OrderBySchema[];
|
|
54
|
+
/** Row type of the result list. */
|
|
55
|
+
results: EntitySchema;
|
|
56
|
+
}
|
|
57
|
+
/** Fetch a single row by primary key. Composite PK is not supported. */
|
|
58
|
+
export interface GetSchema extends SchemaBase {
|
|
59
|
+
type: 'get';
|
|
60
|
+
schema: DaoSchema;
|
|
61
|
+
/** Single PK scalar value. */
|
|
62
|
+
args: Field;
|
|
63
|
+
/** Row type. Get may miss the row: the generated signature returns Entity | null. */
|
|
64
|
+
results: EntitySchema;
|
|
65
|
+
}
|
|
66
|
+
/** Insert one row; returns number (insert id). */
|
|
67
|
+
export interface InsertSchema extends SchemaBase {
|
|
68
|
+
type: 'insert';
|
|
69
|
+
schema: DaoSchema;
|
|
70
|
+
/** Row object. */
|
|
71
|
+
args: EntitySchema;
|
|
72
|
+
}
|
|
73
|
+
/** Update one row; returns number (affected rows). The where key is derived
|
|
74
|
+
* from the PK columns inside args. */
|
|
75
|
+
export interface UpdateSchema extends SchemaBase {
|
|
76
|
+
type: 'update';
|
|
77
|
+
schema: DaoSchema;
|
|
78
|
+
/** Row object containing the PK columns plus the columns to set. */
|
|
79
|
+
args: EntitySchema;
|
|
80
|
+
/** Additional criteria beyond the derived PK (AND-combined). */
|
|
81
|
+
where?: QuerySchema;
|
|
82
|
+
}
|
|
83
|
+
/** Delete one row by primary key; returns number (affected rows).
|
|
84
|
+
* Composite PK is not supported. */
|
|
85
|
+
export interface DeleteSchema extends SchemaBase {
|
|
86
|
+
type: 'delete';
|
|
87
|
+
schema: DaoSchema;
|
|
88
|
+
/** Single PK scalar value. */
|
|
89
|
+
args: Field;
|
|
90
|
+
}
|
|
91
|
+
export type DaoMethodSchema = FindSchema | GetSchema | InsertSchema | UpdateSchema | DeleteSchema | AggregateSchema;
|
|
92
|
+
/** Aggregate expression result for aggregate queries. */
|
|
93
|
+
export interface ComputeExpr {
|
|
94
|
+
fn: 'sum' | 'avg' | 'count';
|
|
95
|
+
/** Column the function applies to; absent for count(*). */
|
|
96
|
+
field?: Field;
|
|
97
|
+
}
|
|
98
|
+
/** Aggregate expressions: Compute.sum(col) / Compute.avg(col) / Compute.count(). */
|
|
99
|
+
export declare const Compute: {
|
|
100
|
+
sum(field: Field): ComputeExpr;
|
|
101
|
+
avg(field: Field): ComputeExpr;
|
|
102
|
+
count(): ComputeExpr;
|
|
103
|
+
};
|
|
104
|
+
/** Aggregate query (count/sum/avg): returns computed scalar values. */
|
|
105
|
+
export interface AggregateSchema extends SchemaBase {
|
|
106
|
+
type: 'aggregate';
|
|
107
|
+
schema: DaoSchema;
|
|
108
|
+
/** Criteria, same shape as find. */
|
|
109
|
+
args?: QuerySchema;
|
|
110
|
+
/** Computed results: key = result field name. */
|
|
111
|
+
results: Record<string, ComputeExpr>;
|
|
112
|
+
}
|
|
113
|
+
/** A database entity backed by a table. */
|
|
114
|
+
export interface EntitySchema extends SchemaBase {
|
|
115
|
+
type: 'entity';
|
|
116
|
+
/** The table columns of this entity. */
|
|
117
|
+
columns: Field[];
|
|
8
118
|
}
|
|
9
|
-
export declare function defineDao(name: string, app: FrontAppSchema, description?: string): DaoSchema;
|
package/dist/dao.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
-
export function defineDao(
|
|
2
|
-
|
|
1
|
+
export function defineDao(options) {
|
|
2
|
+
const schema = {
|
|
3
|
+
type: 'dao',
|
|
4
|
+
name: options.name,
|
|
5
|
+
description: options.description,
|
|
6
|
+
app: options.app,
|
|
7
|
+
table: options.table,
|
|
8
|
+
methods: {},
|
|
9
|
+
};
|
|
10
|
+
for (const key of Object.keys(options.methods)) {
|
|
11
|
+
const method = options.methods[key];
|
|
12
|
+
// Spread of a union loses discriminant correlation; the cast is safe
|
|
13
|
+
// (the builder only adds name and the back-reference field).
|
|
14
|
+
schema.methods[key] = { ...method, name: key, schema };
|
|
15
|
+
}
|
|
16
|
+
return schema;
|
|
3
17
|
}
|
|
18
|
+
/** Aggregate expressions: Compute.sum(col) / Compute.avg(col) / Compute.count(). */
|
|
19
|
+
export const Compute = {
|
|
20
|
+
sum(field) {
|
|
21
|
+
return { fn: 'sum', field };
|
|
22
|
+
},
|
|
23
|
+
avg(field) {
|
|
24
|
+
return { fn: 'avg', field };
|
|
25
|
+
},
|
|
26
|
+
count() {
|
|
27
|
+
return { fn: 'count' };
|
|
28
|
+
},
|
|
29
|
+
};
|
package/dist/db.js
CHANGED
|
@@ -66,6 +66,9 @@ export function defineTable(name, schema) {
|
|
|
66
66
|
if (field.schema && field.schema !== table) {
|
|
67
67
|
throw new Error(`field ${key}: belongs to table ${field.schema.name}, cannot reuse in table ${table.name}`);
|
|
68
68
|
}
|
|
69
|
+
if (field.type === 'array' || field.type === 'object') {
|
|
70
|
+
throw new Error(`table '${name}': column '${key}' cannot be a nested ${field.type} field — wire-format nesting is not a table column`);
|
|
71
|
+
}
|
|
69
72
|
}
|
|
70
73
|
for (const key of Object.keys(table.columns)) {
|
|
71
74
|
table.columns[key].name = key;
|
package/dist/dsl.d.ts
CHANGED
|
@@ -54,6 +54,13 @@ interface DecimalField extends BaseField {
|
|
|
54
54
|
precision: number;
|
|
55
55
|
scale: number;
|
|
56
56
|
}
|
|
57
|
+
type RateUnit = 'pct' | 'pm' | 'bp';
|
|
58
|
+
export declare function rateScale(unit: RateUnit): number;
|
|
59
|
+
interface RateField extends BaseField {
|
|
60
|
+
type: 'rate';
|
|
61
|
+
jsType: 'string';
|
|
62
|
+
unit: RateUnit;
|
|
63
|
+
}
|
|
57
64
|
interface BooleanField extends BaseField {
|
|
58
65
|
type: 'boolean';
|
|
59
66
|
jsType: 'boolean';
|
|
@@ -93,17 +100,33 @@ interface JsonField extends BaseField {
|
|
|
93
100
|
type: 'json';
|
|
94
101
|
jsType: 'object';
|
|
95
102
|
}
|
|
96
|
-
|
|
103
|
+
/** Recursive array field — wire-format nesting (third-party messages), not a table column. */
|
|
104
|
+
interface ArrayField extends BaseField {
|
|
105
|
+
type: 'array';
|
|
106
|
+
jsType: 'array';
|
|
107
|
+
/** Element type: any Field, including nested array/object. */
|
|
108
|
+
items: Field;
|
|
109
|
+
}
|
|
110
|
+
/** Recursive object field — wire-format nesting (third-party messages), not a table column. */
|
|
111
|
+
interface ObjectField extends BaseField {
|
|
112
|
+
type: 'object';
|
|
113
|
+
jsType: 'object';
|
|
114
|
+
properties: Record<string, Field>;
|
|
115
|
+
}
|
|
116
|
+
export type Field = StringField | TextField | IntField | BigintField | DecimalField | RateField | BooleanField | DateField | TimeField | DateTimeField | EnumField | JsonField | ArrayField | ObjectField;
|
|
97
117
|
type FieldExtras<T extends Field> = Omit<T, 'name' | 'type' | 'jsType'>;
|
|
98
118
|
export declare function stringField(extra?: FieldExtras<StringField>): StringField;
|
|
99
119
|
export declare function textField(extra?: FieldExtras<TextField>): TextField;
|
|
100
120
|
export declare function intField(extra?: FieldExtras<IntField>): IntField;
|
|
101
121
|
export declare function bigintField(extra?: FieldExtras<BigintField>): BigintField;
|
|
102
122
|
export declare function decimalField(extra: FieldExtras<DecimalField>): DecimalField;
|
|
123
|
+
export declare function rateField(unit: RateUnit, extra?: Omit<FieldExtras<RateField>, 'unit'>): RateField;
|
|
103
124
|
export declare function booleanField(extra?: FieldExtras<BooleanField>): BooleanField;
|
|
104
125
|
export declare function dateField(extra?: FieldExtras<DateField>): DateField;
|
|
105
126
|
export declare function timeField(extra?: FieldExtras<TimeField>): TimeField;
|
|
106
127
|
export declare function datetimeField(extra?: FieldExtras<DateTimeField>): DateTimeField;
|
|
107
128
|
export declare function jsonField(extra?: FieldExtras<JsonField>): JsonField;
|
|
129
|
+
export declare function arrayField(extra: FieldExtras<ArrayField>): ArrayField;
|
|
130
|
+
export declare function objectField(extra: FieldExtras<ObjectField>): ObjectField;
|
|
108
131
|
export declare function enumField(extra: Omit<EnumField, 'name' | 'type' | 'jsType'>): EnumField;
|
|
109
132
|
export {};
|
package/dist/dsl.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// DSL field type definitions.
|
|
2
2
|
// Shape: { type: <type name>, <extension fields> }
|
|
3
|
+
export function rateScale(unit) {
|
|
4
|
+
switch (unit) {
|
|
5
|
+
case 'pct': return 2;
|
|
6
|
+
case 'pm': return 3;
|
|
7
|
+
case 'bp': return 4;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
3
10
|
export function defineEnum(jsName, valueType, values) {
|
|
4
11
|
return { jsName, valueType, values };
|
|
5
12
|
}
|
|
@@ -18,6 +25,9 @@ export function bigintField(extra = {}) {
|
|
|
18
25
|
export function decimalField(extra) {
|
|
19
26
|
return { name: '', type: 'decimal', jsType: 'string', ...extra };
|
|
20
27
|
}
|
|
28
|
+
export function rateField(unit, extra = {}) {
|
|
29
|
+
return { name: '', type: 'rate', jsType: 'string', unit, ...extra };
|
|
30
|
+
}
|
|
21
31
|
export function booleanField(extra = {}) {
|
|
22
32
|
return { name: '', type: 'boolean', jsType: 'boolean', ...extra };
|
|
23
33
|
}
|
|
@@ -33,6 +43,12 @@ export function datetimeField(extra = {}) {
|
|
|
33
43
|
export function jsonField(extra = {}) {
|
|
34
44
|
return { name: '', type: 'json', jsType: 'object', ...extra };
|
|
35
45
|
}
|
|
46
|
+
export function arrayField(extra) {
|
|
47
|
+
return { name: '', type: 'array', jsType: 'array', ...extra };
|
|
48
|
+
}
|
|
49
|
+
export function objectField(extra) {
|
|
50
|
+
return { name: '', type: 'object', jsType: 'object', ...extra };
|
|
51
|
+
}
|
|
36
52
|
export function enumField(extra) {
|
|
37
53
|
const jsType = extra.enum.valueType === 'integer' ? 'number' : 'string';
|
|
38
54
|
return { name: '', type: 'enum', jsType, ...extra };
|
package/dist/dto.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseField, CollectionSchemaBase, Field, Operator, SchemaBase } from './dsl.js';
|
|
2
2
|
import { TableSchema } from './db.js';
|
|
3
3
|
import type { ImportBase } from './import-base.js';
|
|
4
|
+
import type { ThirdMethodSchema } from './third-service.js';
|
|
4
5
|
/** Re-export — Operator lives on the DSL level (see dsl.ts). */
|
|
5
6
|
export type { Operator } from './dsl.js';
|
|
6
7
|
/** Re-export — ImportBase lives on its own module (see import-base.ts). */
|
|
@@ -99,5 +100,8 @@ export declare function buildInput(name: string, fields: Record<string, DtoField
|
|
|
99
100
|
export declare function buildOutput(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
|
100
101
|
export declare function buildQuery(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
|
101
102
|
export declare function buildPk(name: string, fields: Record<string, DtoField>, description?: string): DtoMessage;
|
|
102
|
-
/**
|
|
103
|
-
export
|
|
103
|
+
/** Field-collection source a DTO can project from: a DB table or a third-party method message. */
|
|
104
|
+
export type DtoFieldSource = TableSchema | ThirdMethodSchema;
|
|
105
|
+
/** Project fields from a field-collection source (table or third-party method
|
|
106
|
+
* message) and wrap them as DTO fields (aligned with dto.from). */
|
|
107
|
+
export declare function from(source: DtoFieldSource, fields: Field[]): Record<string, DtoField>;
|
package/dist/dto.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toCamelCase } from '
|
|
1
|
+
import { toCamelCase } from '@pylonts/core';
|
|
2
2
|
export class DtoField {
|
|
3
3
|
/** DTO 语义字段名(接口字段名),与 field.name(数据库列名)含义不同。
|
|
4
4
|
* 构造时未知,由 buildMessage 从 map key 反写。 */
|
|
@@ -159,21 +159,32 @@ export function buildPk(name, fields, description) {
|
|
|
159
159
|
if (field.optional !== undefined)
|
|
160
160
|
continue;
|
|
161
161
|
const f = field.field;
|
|
162
|
-
const table = f.schema;
|
|
162
|
+
const table = f.schema?.type === 'table' ? f.schema : undefined;
|
|
163
163
|
field.optional = table !== undefined && table.isPk(f) ? false : true;
|
|
164
164
|
}
|
|
165
165
|
return message;
|
|
166
166
|
}
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
// Type guard instead of a plain discriminant check: TableSchema.type is declared
|
|
168
|
+
// as a class property, so TS widens it to string and cannot narrow the union.
|
|
169
|
+
function isThirdMethod(source) {
|
|
170
|
+
return source.type === 'thirdMethod';
|
|
171
|
+
}
|
|
172
|
+
function ownsField(source, field) {
|
|
173
|
+
if (isThirdMethod(source))
|
|
174
|
+
return Object.values(source.fields).includes(field);
|
|
175
|
+
return Object.values(source.columns).includes(field);
|
|
176
|
+
}
|
|
177
|
+
/** Project fields from a field-collection source (table or third-party method
|
|
178
|
+
* message) and wrap them as DTO fields (aligned with dto.from). */
|
|
179
|
+
export function from(source, fields) {
|
|
169
180
|
const out = {};
|
|
170
181
|
for (const field of fields) {
|
|
171
|
-
if (field
|
|
172
|
-
throw new Error(`dto.from(${
|
|
182
|
+
if (!ownsField(source, field)) {
|
|
183
|
+
throw new Error(`dto.from(${source.name}): field ${field.name} does not belong to this ${source.type}`);
|
|
173
184
|
}
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
out[toCamelCase(field.name)] = dtoField(field);
|
|
185
|
+
// DB columns map to camelCase interface names (mer_id → merId); wire-format
|
|
186
|
+
// names are protocol names themselves and stay untouched.
|
|
187
|
+
out[isThirdMethod(source) ? field.name : toCamelCase(field.name)] = dtoField(field);
|
|
177
188
|
}
|
|
178
189
|
return out;
|
|
179
190
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { DtoMessage } from './dto.js';
|
|
3
|
+
/** Shared API call signature: one request DTO in, one response shape out.
|
|
4
|
+
* Referenced by both backend controller methods and frontend page providers —
|
|
5
|
+
* both sides must use the exact same instance, so drift is impossible. */
|
|
6
|
+
export interface EndpointSchema extends SchemaBase {
|
|
7
|
+
type: 'endpoint';
|
|
8
|
+
args: DtoMessage;
|
|
9
|
+
results: DtoMessage | number | boolean | string;
|
|
10
|
+
}
|
|
11
|
+
export declare function defineEndpoint(name: string, options: {
|
|
12
|
+
args: DtoMessage;
|
|
13
|
+
results: DtoMessage | number | boolean | string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}): EndpointSchema;
|
package/dist/endpoint.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { ImportBase } from './import-base.js';
|
|
3
|
+
/** Describes an exception a method can throw. */
|
|
4
|
+
export interface ExceptionSchema extends SchemaBase, Omit<ImportBase, 'type'> {
|
|
5
|
+
type: 'exception';
|
|
6
|
+
/** Whether the caller may safely retry after catching this exception. */
|
|
7
|
+
retryable: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function defineException(options: {
|
|
10
|
+
name: string;
|
|
11
|
+
from: string;
|
|
12
|
+
retryable: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
}): ExceptionSchema;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SchemaBase } from './dsl.js';
|
|
2
|
+
/** One end of a field rule (e.g. 'fen' / 'yuan', 'plain' / 'cipher', 'raw' / 'masked'). */
|
|
3
|
+
export interface FieldRuleEnd {
|
|
4
|
+
/** End name — written back by defineFieldRule from the ends map key. */
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A semantic transformation rule between two field representations. */
|
|
9
|
+
export interface FieldRuleSchema extends SchemaBase {
|
|
10
|
+
type: 'fieldRule';
|
|
11
|
+
/** Rule name — the semantic uniqueness key. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** The two ends of the rule, keyed by end name. */
|
|
14
|
+
ends: Record<string, FieldRuleEnd>;
|
|
15
|
+
}
|
|
16
|
+
export declare function defineFieldRule(options: {
|
|
17
|
+
name: string;
|
|
18
|
+
ends: Record<string, Omit<FieldRuleEnd, 'name'>>;
|
|
19
|
+
description?: string;
|
|
20
|
+
}): FieldRuleSchema;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** One rule per semantic name. Defining the same name twice throws. */
|
|
2
|
+
const ruleRegistry = new Map();
|
|
3
|
+
export function defineFieldRule(options) {
|
|
4
|
+
if (ruleRegistry.has(options.name)) {
|
|
5
|
+
throw new Error(`fieldRule '${options.name}' is already defined — one rule per semantic`);
|
|
6
|
+
}
|
|
7
|
+
const ends = {};
|
|
8
|
+
for (const key of Object.keys(options.ends)) {
|
|
9
|
+
ends[key] = { name: key, description: options.ends[key].description };
|
|
10
|
+
}
|
|
11
|
+
const rule = {
|
|
12
|
+
type: 'fieldRule',
|
|
13
|
+
name: options.name,
|
|
14
|
+
description: options.description,
|
|
15
|
+
ends,
|
|
16
|
+
};
|
|
17
|
+
ruleRegistry.set(options.name, rule);
|
|
18
|
+
return rule;
|
|
19
|
+
}
|
package/dist/flow.d.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { MethodSchema } from './method.js';
|
|
3
|
+
import type { ConvertSchema } from './convert.js';
|
|
4
|
+
import type { DaoMethodSchema } from './dao.js';
|
|
5
|
+
import type { ServiceMethodSchema } from './service.js';
|
|
6
|
+
import type { ThirdServiceMethodSchema } from './third-service.js';
|
|
7
|
+
import type { UtilsMethodSchema } from './utils.js';
|
|
8
|
+
/** A method a flow node can invoke: contract references, or a pure descriptor
|
|
9
|
+
* (MethodSchema) for the period before the contract file exists. */
|
|
10
|
+
export type FlowMethodRef = MethodSchema | ConvertSchema | DaoMethodSchema | ServiceMethodSchema | ThirdServiceMethodSchema | UtilsMethodSchema;
|
|
2
11
|
export interface FlowNode extends SchemaBase {
|
|
3
12
|
/** Optional sub-flow. When present, entering this node runs the sub-flow;
|
|
4
13
|
* after the sub-flow reaches any terminal node, the outer flow continues
|
|
5
14
|
* via this node's outgoing edges. Sub-flows nest recursively. */
|
|
6
15
|
flow?: FlowSchema;
|
|
16
|
+
/** Methods this node invokes — contract references or pure descriptors
|
|
17
|
+
* (rendered under the node label). */
|
|
18
|
+
methods?: FlowMethodRef[];
|
|
7
19
|
}
|
|
8
20
|
export interface FlowEdge extends SchemaBase {
|
|
9
21
|
/** Trigger condition; undefined = default path (success/normal). */
|
|
10
22
|
when?: string;
|
|
11
23
|
start: FlowNode;
|
|
12
24
|
end: FlowNode;
|
|
25
|
+
/** Exception path (rendered dashed); defaults to normal. */
|
|
26
|
+
exception?: boolean;
|
|
13
27
|
}
|
|
14
28
|
export interface FlowSchema extends SchemaBase {
|
|
15
29
|
/** Entry node. */
|
|
@@ -19,8 +33,16 @@ export interface FlowSchema extends SchemaBase {
|
|
|
19
33
|
/** Independent edges; a node may be start of many edges, so cycles are expressible. */
|
|
20
34
|
edges: FlowEdge[];
|
|
21
35
|
}
|
|
22
|
-
export declare function node(name: string,
|
|
23
|
-
|
|
36
|
+
export declare function node(name: string, options?: {
|
|
37
|
+
flow?: FlowSchema;
|
|
38
|
+
description?: string;
|
|
39
|
+
methods?: FlowMethodRef[];
|
|
40
|
+
}): FlowNode;
|
|
41
|
+
export declare function edge(start: FlowNode, end: FlowNode, options?: {
|
|
42
|
+
when?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
exception?: boolean;
|
|
45
|
+
}): FlowEdge;
|
|
24
46
|
export declare function defineFlow(name: string, schema: {
|
|
25
47
|
start: FlowNode;
|
|
26
48
|
edges: FlowEdge[];
|
package/dist/flow.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
export function node(name,
|
|
2
|
-
return {
|
|
1
|
+
export function node(name, options = {}) {
|
|
2
|
+
return {
|
|
3
|
+
name,
|
|
4
|
+
flow: options.flow,
|
|
5
|
+
description: options.description,
|
|
6
|
+
methods: options.methods,
|
|
7
|
+
};
|
|
3
8
|
}
|
|
4
|
-
export function edge(start, end,
|
|
9
|
+
export function edge(start, end, options = {}) {
|
|
5
10
|
// Auto name for uniformity with SchemaBase; `when` stays the branch marker.
|
|
6
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
name: `${start.name}->${end.name}`,
|
|
13
|
+
start,
|
|
14
|
+
end,
|
|
15
|
+
when: options.when,
|
|
16
|
+
description: options.description,
|
|
17
|
+
exception: options.exception,
|
|
18
|
+
};
|
|
7
19
|
}
|
|
8
20
|
export function defineFlow(name, schema) {
|
|
9
21
|
const seen = new Set();
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,12 @@ export * from './convert.js';
|
|
|
22
22
|
export * from './ref.js';
|
|
23
23
|
export * from './route.js';
|
|
24
24
|
export * from './service.js';
|
|
25
|
+
export * from './controller.js';
|
|
26
|
+
export * from './endpoint.js';
|
|
25
27
|
export * from './dao.js';
|
|
28
|
+
export * from './third-service.js';
|
|
29
|
+
export * from './field-rule.js';
|
|
30
|
+
export * from './exception.js';
|
|
26
31
|
export * from './page.js';
|
|
27
32
|
export * from './curd.js';
|
|
28
33
|
export * from './page-flow.js';
|
|
@@ -31,3 +36,5 @@ export * from './page-def.js';
|
|
|
31
36
|
export * from './mermaid-driver.js';
|
|
32
37
|
export * from './navigation.js';
|
|
33
38
|
export * from './popup.js';
|
|
39
|
+
export * from './method.js';
|
|
40
|
+
export { toCamelCase, toPascalCase, toKebabCase } from '@pylonts/core';
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,12 @@ export * from './convert.js';
|
|
|
22
22
|
export * from './ref.js';
|
|
23
23
|
export * from './route.js';
|
|
24
24
|
export * from './service.js';
|
|
25
|
+
export * from './controller.js';
|
|
26
|
+
export * from './endpoint.js';
|
|
25
27
|
export * from './dao.js';
|
|
28
|
+
export * from './third-service.js';
|
|
29
|
+
export * from './field-rule.js';
|
|
30
|
+
export * from './exception.js';
|
|
26
31
|
export * from './page.js';
|
|
27
32
|
export * from './curd.js';
|
|
28
33
|
export * from './page-flow.js';
|
|
@@ -31,3 +36,7 @@ export * from './page-def.js';
|
|
|
31
36
|
export * from './mermaid-driver.js';
|
|
32
37
|
export * from './navigation.js';
|
|
33
38
|
export * from './popup.js';
|
|
39
|
+
export * from './method.js';
|
|
40
|
+
// Naming conversions moved to @pylonts/core; re-exported for compatibility
|
|
41
|
+
// with packages that import them from @pylonts/dsl.
|
|
42
|
+
export { toCamelCase, toPascalCase, toKebabCase } from '@pylonts/core';
|