@pylonts/dsl 1.0.6 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- 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/curd.d.ts +60 -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 +52 -0
- package/dist/db.js +91 -0
- package/dist/dictionary.d.ts +26 -5
- package/dist/dictionary.js +21 -8
- package/dist/dsl.d.ts +5 -49
- package/dist/dsl.js +12 -103
- package/dist/dto.d.ts +10 -7
- package/dist/dto.js +24 -33
- package/dist/enum-driver.d.ts +1 -1
- package/dist/enum-driver.js +1 -4
- 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 +21 -17
- package/dist/index.js +21 -33
- package/dist/mermaid-driver.d.ts +2 -2
- package/dist/mermaid-driver.js +2 -6
- package/dist/mock.d.ts +30 -0
- package/dist/mock.js +18 -0
- package/dist/mysql-driver.d.ts +1 -1
- package/dist/mysql-driver.js +20 -10
- package/dist/page-flow.d.ts +2 -2
- package/dist/page-flow.js +2 -6
- package/dist/page.d.ts +6 -6
- package/dist/page.js +3 -8
- package/dist/pattern.js +2 -6
- package/dist/patterns/retry.d.ts +1 -1
- package/dist/patterns/retry.js +2 -6
- package/dist/project.d.ts +18 -13
- package/dist/project.js +41 -6
- package/dist/prototype.d.ts +1 -1
- package/dist/prototype.js +1 -4
- 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 +66 -66
- package/docs/enum.md +24 -24
- package/docs/project.md +2 -2
- package/docs/table.md +50 -15
- package/package.json +6 -4
- package/src/asset.ts +63 -0
- package/src/bases.ts +12 -2
- package/src/curd.ts +92 -0
- package/src/db-config.ts +8 -0
- package/src/db.ts +142 -0
- package/src/dictionary.ts +45 -19
- package/src/dsl.ts +182 -281
- package/src/dto.ts +247 -234
- package/src/enum-driver.ts +1 -1
- package/src/flow.ts +1 -1
- package/src/import-base.ts +15 -0
- package/src/index.ts +21 -17
- package/src/mermaid-driver.ts +3 -3
- package/src/mock.ts +45 -0
- package/src/mysql-driver.ts +19 -6
- package/src/page-flow.ts +3 -3
- package/src/page.ts +7 -7
- package/src/patterns/retry.ts +1 -1
- package/src/project.ts +90 -54
- package/src/prototype.ts +1 -1
- package/src/typebox-driver.ts +192 -187
- package/src/utils.ts +5 -0
- package/src/check-inheritance.ts +0 -86
package/src/mysql-driver.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Field
|
|
1
|
+
import { Field } from './dsl.js';
|
|
2
|
+
import { ForeignKey, Index, TableSchema } from './db.js';
|
|
2
3
|
|
|
3
4
|
// MySQL driver: converts a TableSchema into a CREATE TABLE statement.
|
|
4
5
|
|
|
@@ -31,11 +32,23 @@ function columnType(field: Field): string {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function renderDefault(field: Field): string {
|
|
36
|
+
if (field.default === undefined) return '';
|
|
37
|
+
// CURRENT_TIMESTAMP is a MySQL function keyword, not a string literal.
|
|
38
|
+
if (field.default === 'CURRENT_TIMESTAMP') return ' DEFAULT CURRENT_TIMESTAMP';
|
|
39
|
+
// Numeric columns take a bare literal, not a quoted one.
|
|
40
|
+
if (field.type === 'integer' || field.type === 'bigint' || field.type === 'decimal' || field.type === 'boolean') {
|
|
41
|
+
return ` DEFAULT ${field.default}`;
|
|
42
|
+
}
|
|
43
|
+
return ` DEFAULT '${field.default}'`;
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
function columnDef(field: Field, autoIncrement?: Field): string {
|
|
35
47
|
const parts = [field.name, columnType(field)];
|
|
36
48
|
if (field.optional === false) parts.push('NOT NULL');
|
|
37
|
-
|
|
49
|
+
parts.push(renderDefault(field));
|
|
38
50
|
if (field === autoIncrement) parts.push('AUTO_INCREMENT');
|
|
51
|
+
if (field.description) parts.push(`COMMENT '${field.description.replace(/'/g, "\\'")}'`);
|
|
39
52
|
return parts.join(' ');
|
|
40
53
|
}
|
|
41
54
|
|
|
@@ -46,14 +59,14 @@ function primaryKeyClause(schema: TableSchema): string | null {
|
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
function indexClause(index: Index): string {
|
|
49
|
-
const fields = Array.isArray(index.
|
|
62
|
+
const fields = Array.isArray(index.columns) ? index.columns : [index.columns];
|
|
50
63
|
const kind = index.unique ? 'UNIQUE KEY' : 'KEY';
|
|
51
64
|
const name = index.name ?? fields.map((f) => f.name).join('_');
|
|
52
65
|
return `${kind} ${name} (${fields.map((f) => f.name).join(', ')})`;
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
function foreignKeyClause(name: string, fk: ForeignKey): string {
|
|
56
|
-
const fields = Array.isArray(fk.
|
|
69
|
+
const fields = Array.isArray(fk.columns) ? fk.columns : [fk.columns];
|
|
57
70
|
const refs = Array.isArray(fk.references) ? fk.references : [fk.references];
|
|
58
71
|
const refTable = refs[0].schema?.name;
|
|
59
72
|
if (!refTable) throw new Error(`foreign key ${name}: references field has no schema`);
|
|
@@ -68,7 +81,7 @@ export interface BuildCreateTableSqlOptions {
|
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
export function buildCreateTableSql(schema: TableSchema, options: BuildCreateTableSqlOptions = {}): string {
|
|
71
|
-
const lines = Object.values(schema.
|
|
84
|
+
const lines = Object.values(schema.columns).map((field) => columnDef(field, schema.autoIncrement));
|
|
72
85
|
const pk = primaryKeyClause(schema);
|
|
73
86
|
if (pk) lines.push(pk);
|
|
74
87
|
for (const index of schema.indexes ?? []) lines.push(indexClause(index));
|
|
@@ -77,5 +90,5 @@ export function buildCreateTableSql(schema: TableSchema, options: BuildCreateTab
|
|
|
77
90
|
lines.push(foreignKeyClause(name, fk));
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
|
-
return `CREATE TABLE \`${schema.name}\` (\n ${lines.join(',\n ')}\n);`;
|
|
93
|
+
return `CREATE TABLE IF NOT EXISTS \`${schema.name}\` (\n ${lines.join(',\n ')}\n);`;
|
|
81
94
|
}
|
package/src/page-flow.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl';
|
|
2
|
-
import { ActionSchema, Page } from './page';
|
|
1
|
+
import { SchemaBase } from './dsl.js';
|
|
2
|
+
import { ActionSchema, Page } from './page.js';
|
|
3
3
|
|
|
4
4
|
// Page-driven flow: every node is a page, and a page belongs to an app.
|
|
5
5
|
// Leaf nodes are pages too — a journey starts at a page and ends at a page.
|
|
@@ -49,4 +49,4 @@ export function definePageFlow(
|
|
|
49
49
|
pages.push(schema.start);
|
|
50
50
|
}
|
|
51
51
|
return { name, description: schema.description, start: schema.start, pages, edges: schema.edges };
|
|
52
|
-
}
|
|
52
|
+
}
|
package/src/page.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl';
|
|
2
|
-
import {
|
|
1
|
+
import { SchemaBase } from './dsl.js';
|
|
2
|
+
import { FrontAppSchema } from './project.js';
|
|
3
3
|
|
|
4
4
|
// Page definitions: standalone page schemas and the page node type used by
|
|
5
5
|
// page-driven flows. Kept separate from page-flow.ts (the flow graph itself).
|
|
@@ -8,7 +8,7 @@ import { FrontApp } from './project';
|
|
|
8
8
|
* the actions a user can perform, and belongs to exactly one frontend app. */
|
|
9
9
|
export interface PageSchema extends SchemaBase {
|
|
10
10
|
/** The frontend app this page belongs to (shared instance from project.config). */
|
|
11
|
-
app:
|
|
11
|
+
app: FrontAppSchema;
|
|
12
12
|
/** Actions a user can perform on this page (e.g. submit, approve, reject). */
|
|
13
13
|
actions: ActionSchema[];
|
|
14
14
|
}
|
|
@@ -23,7 +23,7 @@ export function defineAction(name: string, description?: string): ActionSchema {
|
|
|
23
23
|
export function definePage(schema: {
|
|
24
24
|
name: string;
|
|
25
25
|
description?: string;
|
|
26
|
-
app:
|
|
26
|
+
app: FrontAppSchema;
|
|
27
27
|
actions: ActionSchema[];
|
|
28
28
|
}): PageSchema {
|
|
29
29
|
return { ...schema };
|
|
@@ -32,9 +32,9 @@ export function definePage(schema: {
|
|
|
32
32
|
/** A page node in a page-driven flow: every node is a page, and a page belongs to an app. */
|
|
33
33
|
export interface Page extends SchemaBase {
|
|
34
34
|
/** The frontend app this page belongs to (shared instance from project.config). */
|
|
35
|
-
app:
|
|
35
|
+
app: FrontAppSchema;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function page(app:
|
|
38
|
+
export function page(app: FrontAppSchema, name: string, description?: string): Page {
|
|
39
39
|
return { name, app, description };
|
|
40
|
-
}
|
|
40
|
+
}
|
package/src/patterns/retry.ts
CHANGED
package/src/project.ts
CHANGED
|
@@ -1,55 +1,91 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl';
|
|
2
|
-
|
|
3
|
-
// Project topology definitions: describe the applications (frontends) and
|
|
4
|
-
// backend APIs of a repository, and which frontends each API serves.
|
|
5
|
-
|
|
6
|
-
/** Frontend form factor. Closed enum, extend when new form factors appear. */
|
|
7
|
-
export type FrontType = 'admin' | 'wxmini';
|
|
8
|
-
|
|
9
|
-
/** A frontend application (e.g. admin console, wechat mini program). */
|
|
10
|
-
export interface
|
|
11
|
-
type: FrontType;
|
|
12
|
-
/** Source directory relative to project root, e.g. 'web-admin/'. */
|
|
13
|
-
dir: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** A backend API service. apps references shared
|
|
17
|
-
export interface
|
|
18
|
-
/** Source directory relative to project root, e.g. 'api/'. */
|
|
19
|
-
dir: string;
|
|
20
|
-
/** Frontends this API serves. Direct instance references (see defineProject). */
|
|
21
|
-
apps:
|
|
22
|
-
/** API base URL prefix shared by all apps it serves, e.g. '/mall'. '' = no prefix. */
|
|
23
|
-
contextPath?: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import { SchemaBase } from './dsl.js';
|
|
2
|
+
|
|
3
|
+
// Project topology definitions: describe the applications (frontends) and
|
|
4
|
+
// backend APIs of a repository, and which frontends each API serves.
|
|
5
|
+
|
|
6
|
+
/** Frontend form factor. Closed enum, extend when new form factors appear. */
|
|
7
|
+
export type FrontType = 'admin' | 'wxmini';
|
|
8
|
+
|
|
9
|
+
/** A frontend application (e.g. admin console, wechat mini program). */
|
|
10
|
+
export interface FrontAppSchema extends SchemaBase {
|
|
11
|
+
type: FrontType;
|
|
12
|
+
/** Source directory relative to project root, e.g. 'web-admin/'. */
|
|
13
|
+
dir: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A backend API service. apps references shared FrontAppSchema instances. */
|
|
17
|
+
export interface ProjectApiSchema extends SchemaBase {
|
|
18
|
+
/** Source directory relative to project root, e.g. 'api/'. */
|
|
19
|
+
dir: string;
|
|
20
|
+
/** Frontends this API serves. Direct instance references (see defineProject). */
|
|
21
|
+
apps: FrontAppSchema[];
|
|
22
|
+
/** API base URL prefix shared by all apps it serves, e.g. '/mall'. '' = no prefix. */
|
|
23
|
+
contextPath?: string;
|
|
24
|
+
/** API service base URL for node clients, e.g. 'http://127.0.0.1:3000'. */
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A third-party system (e.g. wechat pay, unionpay). Owns its own
|
|
29
|
+
* implementation dir and contract (controller_types), just like an API,
|
|
30
|
+
* but is not part of this repo's served surface. */
|
|
31
|
+
export interface ThirdApiSchema extends SchemaBase {
|
|
32
|
+
/** Source directory relative to project root, e.g. 'wechat/'. */
|
|
33
|
+
dir: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ProjectSchema extends SchemaBase {
|
|
37
|
+
apps: FrontAppSchema[];
|
|
38
|
+
apis: ProjectApiSchema[];
|
|
39
|
+
thirdApis: ThirdApiSchema[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Defines the project topology. FrontAppSchema instances are shared value objects:
|
|
44
|
+
* api.apps references the same instances from project.apps, so an app served
|
|
45
|
+
* by multiple APIs is defined once and referenced many times.
|
|
46
|
+
*
|
|
47
|
+
* Runtime-validates app type whitelist, unique names and api.apps reference
|
|
48
|
+
* integrity (same style as defineTable/defineCurd).
|
|
49
|
+
*/
|
|
50
|
+
export function defineProject(
|
|
51
|
+
name: string,
|
|
52
|
+
schema: {
|
|
53
|
+
description?: string;
|
|
54
|
+
apps: FrontAppSchema[];
|
|
55
|
+
apis: ProjectApiSchema[];
|
|
56
|
+
thirdApis?: ThirdApiSchema[];
|
|
57
|
+
},
|
|
58
|
+
): ProjectSchema {
|
|
59
|
+
const project: ProjectSchema = { name, ...schema, thirdApis: schema.thirdApis ?? [] };
|
|
60
|
+
|
|
61
|
+
const appNames = new Set<string>();
|
|
62
|
+
for (const app of project.apps) {
|
|
63
|
+
if (!app.name) throw new Error(`project ${name}: app name is required`);
|
|
64
|
+
if (appNames.has(app.name)) throw new Error(`project ${name}: duplicate app name '${app.name}'`);
|
|
65
|
+
appNames.add(app.name);
|
|
66
|
+
if (app.type !== 'admin' && app.type !== 'wxmini') {
|
|
67
|
+
throw new Error(`project ${name}: app '${app.name}' must be type 'admin' or 'wxmini' (got '${app.type}')`);
|
|
68
|
+
}
|
|
69
|
+
if (!app.dir) throw new Error(`project ${name}: app '${app.name}' dir is required`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const apiNames = new Set<string>();
|
|
73
|
+
for (const api of project.apis) {
|
|
74
|
+
if (!api.name) throw new Error(`project ${name}: api name is required`);
|
|
75
|
+
if (apiNames.has(api.name)) throw new Error(`project ${name}: duplicate api name '${api.name}'`);
|
|
76
|
+
apiNames.add(api.name);
|
|
77
|
+
if (!api.dir) throw new Error(`project ${name}: api '${api.name}' dir is required`);
|
|
78
|
+
for (const ref of api.apps) {
|
|
79
|
+
if (!project.apps.includes(ref)) {
|
|
80
|
+
throw new Error(`project ${name}: api '${api.name}' references app '${ref.name}' that is not a shared instance in project.apps (define once and reference it)`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const third of project.thirdApis) {
|
|
86
|
+
if (!third.name) throw new Error(`project ${name}: thirdApi name is required`);
|
|
87
|
+
if (!third.dir) throw new Error(`project ${name}: thirdApi '${third.name}' dir is required`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return project;
|
|
55
91
|
}
|
package/src/prototype.ts
CHANGED