@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.
Files changed (108) hide show
  1. package/README.md +2 -1
  2. package/dist/action.d.ts +7 -0
  3. package/dist/action.js +3 -0
  4. package/dist/asset.d.ts +48 -0
  5. package/dist/asset.js +31 -0
  6. package/dist/bases.d.ts +6 -2
  7. package/dist/bases.js +10 -6
  8. package/dist/check-inheritance.js +1 -4
  9. package/dist/component.d.ts +20 -0
  10. package/dist/component.js +1 -0
  11. package/dist/convert.d.ts +9 -0
  12. package/dist/convert.js +3 -0
  13. package/dist/curd.d.ts +62 -0
  14. package/dist/curd.js +31 -0
  15. package/dist/db-config.d.ts +8 -0
  16. package/dist/db-config.js +1 -0
  17. package/dist/db.d.ts +56 -0
  18. package/dist/db.js +100 -0
  19. package/dist/dictionary.d.ts +26 -5
  20. package/dist/dictionary.js +21 -8
  21. package/dist/dsl.d.ts +10 -49
  22. package/dist/dsl.js +12 -103
  23. package/dist/dto.d.ts +12 -9
  24. package/dist/dto.js +25 -34
  25. package/dist/enum-driver.d.ts +1 -1
  26. package/dist/enum-driver.js +1 -4
  27. package/dist/event.d.ts +8 -0
  28. package/dist/event.js +3 -0
  29. package/dist/flow.d.ts +1 -1
  30. package/dist/flow.js +3 -8
  31. package/dist/import-base.d.ts +15 -0
  32. package/dist/import-base.js +1 -0
  33. package/dist/index.d.ts +31 -17
  34. package/dist/index.js +31 -33
  35. package/dist/mermaid-driver.d.ts +2 -2
  36. package/dist/mermaid-driver.js +4 -7
  37. package/dist/mock.d.ts +12 -0
  38. package/dist/mock.js +1 -0
  39. package/dist/mysql-driver.d.ts +5 -1
  40. package/dist/mysql-driver.js +25 -10
  41. package/dist/navigation.d.ts +22 -0
  42. package/dist/navigation.js +15 -0
  43. package/dist/page-def.d.ts +40 -0
  44. package/dist/page-def.js +38 -0
  45. package/dist/page-flow.d.ts +5 -3
  46. package/dist/page-flow.js +109 -18
  47. package/dist/page.d.ts +37 -15
  48. package/dist/page.js +23 -13
  49. package/dist/pattern.js +2 -6
  50. package/dist/patterns/retry.d.ts +1 -1
  51. package/dist/patterns/retry.js +2 -6
  52. package/dist/popup.d.ts +18 -0
  53. package/dist/popup.js +8 -0
  54. package/dist/project.d.ts +25 -13
  55. package/dist/project.js +41 -6
  56. package/dist/prototype.d.ts +1 -1
  57. package/dist/prototype.js +1 -4
  58. package/dist/provider.d.ts +54 -0
  59. package/dist/provider.js +18 -0
  60. package/dist/ref.d.ts +14 -0
  61. package/dist/ref.js +6 -0
  62. package/dist/route.d.ts +8 -0
  63. package/dist/route.js +3 -0
  64. package/dist/typebox-driver.d.ts +3 -3
  65. package/dist/typebox-driver.js +24 -26
  66. package/dist/utils.d.ts +2 -0
  67. package/dist/utils.js +5 -4
  68. package/docs/curd.md +111 -0
  69. package/docs/dictionary.md +42 -31
  70. package/docs/driver.md +42 -42
  71. package/docs/dto.md +6 -6
  72. package/docs/enum.md +24 -24
  73. package/docs/project.md +2 -2
  74. package/docs/table.md +53 -16
  75. package/package.json +5 -3
  76. package/src/action.ts +11 -0
  77. package/src/asset.ts +63 -0
  78. package/src/bases.ts +30 -20
  79. package/src/component.ts +22 -0
  80. package/src/convert.ts +13 -0
  81. package/src/curd.ts +94 -0
  82. package/src/db-config.ts +8 -0
  83. package/src/db.ts +153 -0
  84. package/src/dictionary.ts +45 -19
  85. package/src/dsl.ts +13 -106
  86. package/src/dto.ts +25 -12
  87. package/src/enum-driver.ts +42 -42
  88. package/src/event.ts +12 -0
  89. package/src/flow.ts +103 -103
  90. package/src/import-base.ts +15 -0
  91. package/src/index.ts +31 -17
  92. package/src/mermaid-driver.ts +5 -4
  93. package/src/mock.ts +12 -0
  94. package/src/mysql-driver.ts +25 -6
  95. package/src/navigation.ts +29 -0
  96. package/src/page-def.ts +80 -0
  97. package/src/page-flow.ts +117 -15
  98. package/src/page.ts +57 -20
  99. package/src/patterns/retry.ts +54 -54
  100. package/src/popup.ts +25 -0
  101. package/src/project.ts +57 -14
  102. package/src/prototype.ts +29 -29
  103. package/src/provider.ts +73 -0
  104. package/src/ref.ts +19 -0
  105. package/src/route.ts +12 -0
  106. package/src/typebox-driver.ts +192 -187
  107. package/src/utils.ts +11 -6
  108. package/src/check-inheritance.ts +0 -86
package/README.md CHANGED
@@ -21,6 +21,7 @@ project(地图:应用与 API 拓扑)
21
21
  - [prototype.md](./docs/prototype.md) — 页面原型(概要设计)
22
22
  - [table.md](./docs/table.md) — 定义表(详细设计)
23
23
  - [dto.md](./docs/dto.md) — 定义 DTO
24
+ - [curd.md](./docs/curd.md) — 管理端 CRUD 页面标准(CurdSchema)
24
25
  - [enum.md](./docs/enum.md) — 定义枚举
25
26
  - [driver.md](./docs/driver.md) — driver 模式与产物生成
26
- - [mysql-connection.md](./docs/mysql-connection.md) — MySQL 连接说明
27
+ - [mysql-connection.md](./docs/mysql-connection.md) — MySQL 连接说明
@@ -0,0 +1,7 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ /** An action a user can perform on a page (e.g. submit, approve, reject).
3
+ * Subclasses use `type` as the discriminator. */
4
+ export interface ActionSchema extends SchemaBase {
5
+ type: string;
6
+ }
7
+ export declare function defineAction(name: string, description?: string): ActionSchema;
package/dist/action.js ADDED
@@ -0,0 +1,3 @@
1
+ export function defineAction(name, description) {
2
+ return { name, description, type: 'gesture' };
3
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * defineAsset — registry for reusable project assets (utils, components, flows, pages, hooks).
3
+ *
4
+ * Each asset declares its name, category, import path, tags, and optional usage example.
5
+ * CLI scans all asset declarations, supports query (by tag/category) and generate (import links).
6
+ *
7
+ * // assets/utils.assets.ts
8
+ * import { defineAsset } from '@pylonts/dsl';
9
+ * export const formatAmt = defineAsset({
10
+ * name: 'formatAmt',
11
+ * category: 'util',
12
+ * tags: ['amount', 'format'],
13
+ * import: { name: 'formatAmt', from: '@/utils/amount' },
14
+ * example: 'formatAmt(12345) => "12,345.00"',
15
+ * });
16
+ *
17
+ * // CLI:
18
+ * // pylonts gen asset list --tag form → all form-related assets
19
+ * // pylonts gen asset import formatAmt → import { formatAmt } from '@/utils/amount';
20
+ */
21
+ export type AssetCategory = 'util' | 'component' | 'flow' | 'page' | 'hook';
22
+ export interface AssetImport {
23
+ /** Named export, e.g. 'formatAmt' */
24
+ name: string;
25
+ /** Module path, e.g. '@/utils/amount' */
26
+ from: string;
27
+ }
28
+ export interface AssetConfig {
29
+ name: string;
30
+ category: AssetCategory;
31
+ tags: string[];
32
+ import: AssetImport;
33
+ description?: string;
34
+ /** One-liner usage example */
35
+ example?: string;
36
+ /** Link to detailed docs */
37
+ see?: string;
38
+ }
39
+ export interface AssetDef {
40
+ name: string;
41
+ category: AssetCategory;
42
+ tags: string[];
43
+ import: AssetImport;
44
+ description?: string;
45
+ example?: string;
46
+ see?: string;
47
+ }
48
+ export declare function defineAsset(config: AssetConfig): AssetDef;
package/dist/asset.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * defineAsset — registry for reusable project assets (utils, components, flows, pages, hooks).
3
+ *
4
+ * Each asset declares its name, category, import path, tags, and optional usage example.
5
+ * CLI scans all asset declarations, supports query (by tag/category) and generate (import links).
6
+ *
7
+ * // assets/utils.assets.ts
8
+ * import { defineAsset } from '@pylonts/dsl';
9
+ * export const formatAmt = defineAsset({
10
+ * name: 'formatAmt',
11
+ * category: 'util',
12
+ * tags: ['amount', 'format'],
13
+ * import: { name: 'formatAmt', from: '@/utils/amount' },
14
+ * example: 'formatAmt(12345) => "12,345.00"',
15
+ * });
16
+ *
17
+ * // CLI:
18
+ * // pylonts gen asset list --tag form → all form-related assets
19
+ * // pylonts gen asset import formatAmt → import { formatAmt } from '@/utils/amount';
20
+ */
21
+ export function defineAsset(config) {
22
+ return {
23
+ name: config.name,
24
+ category: config.category,
25
+ tags: config.tags,
26
+ import: config.import,
27
+ description: config.description,
28
+ example: config.example,
29
+ see: config.see,
30
+ };
31
+ }
package/dist/bases.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- import type { DtoMessage, ImportRef } from './dto';
1
+ import type { DtoMessage, ImportBase, ImportRef } from './dto.js';
2
2
  /** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
3
- export declare const PageRequest: ImportRef;
3
+ export declare const PageRequest: ImportBase;
4
4
  /** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
5
5
  export declare const PageResult: (row: DtoMessage) => ImportRef;
6
+ /** Paged rows type base without generic args — renders `import { PagedRows } from '@pylonts/core'` */
7
+ export declare const PagedRows: ImportBase;
8
+ /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
9
+ export declare const PageRows: (row: DtoMessage) => ImportRef;
package/dist/bases.js CHANGED
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PageResult = exports.PageRequest = void 0;
4
1
  // Named base-schema references for common protocol DTOs.
5
2
  //
6
3
  // These are ImportRef metadata (not re-exports of the actual TypeBox schemas):
@@ -10,11 +7,18 @@ exports.PageResult = exports.PageRequest = void 0;
10
7
  // `import { PageRequest } from '@pylonts/dsl'` therefore gives .include() an
11
8
  // already-resolved reference — no static analysis or name lookup needed.
12
9
  /** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
13
- exports.PageRequest = { from: '@pylonts/core', name: 'PageRequest' };
10
+ export const PageRequest = { from: '@pylonts/core', name: 'PageRequest' };
14
11
  /** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
15
- const PageResult = (row) => ({
12
+ export const PageResult = (row) => ({
16
13
  from: '@pylonts/core',
17
14
  name: 'PageResult',
18
15
  args: [row],
19
16
  });
20
- exports.PageResult = PageResult;
17
+ /** Paged rows type base without generic args — renders `import { PagedRows } from '@pylonts/core'` */
18
+ export const PagedRows = { from: '@pylonts/core', name: 'PagedRows' };
19
+ /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
20
+ export const PageRows = (row) => ({
21
+ from: '@pylonts/core',
22
+ name: 'PagedRows',
23
+ args: [row],
24
+ });
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkInheritance = checkInheritance;
4
1
  /** snake_case → camelCase: order_no → orderNo; names without underscores are unchanged */
5
2
  function toCamelCase(name) {
6
3
  return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
@@ -24,7 +21,7 @@ function collectFields(mod) {
24
21
  return out;
25
22
  }
26
23
  /** Check one loaded DSL module (its exports) for inheritance gaps. */
27
- function checkInheritance(mod, file) {
24
+ export function checkInheritance(mod, file) {
28
25
  const fields = collectFields(mod);
29
26
  if (fields.length === 0)
30
27
  return [];
@@ -0,0 +1,20 @@
1
+ import type { SchemaBase } from './dsl.js';
2
+ import type { RefSchema } from './ref.js';
3
+ import type { ActionSchema } from './action.js';
4
+ import type { EventDataSchema } from './event.js';
5
+ /** A component event trigger declaration. */
6
+ export interface TriggerSchema extends SchemaBase {
7
+ /** Data the event carries (e.g. e.detail). */
8
+ eventData?: EventDataSchema;
9
+ /** Actions that fire when the event occurs. */
10
+ actions?: ActionSchema[];
11
+ }
12
+ /** A UI component declaration — a virtual schema that describes props and
13
+ * event triggers, not a real renderable component.
14
+ *
15
+ * properties: data bindings via RefSchema (or literal values).
16
+ * triggers: event name → TriggerSchema bindings. */
17
+ export interface ComponentSchema extends SchemaBase {
18
+ properties: Record<string, RefSchema | string | number | boolean>;
19
+ triggers: Record<string, TriggerSchema>;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { SchemaBase } from './dsl.js';
2
+ /** Declares post-call result → page data field mapping.
3
+ * Driver generates per-item transform (e.g. .map()) before setData. */
4
+ export interface ConvertSchema extends SchemaBase {
5
+ type: 'convert';
6
+ /** { targetField: sourceField } — renames or copies fields from call result. */
7
+ fields: Record<string, string>;
8
+ }
9
+ export declare function defineConvert(name: string, fields: Record<string, string>): ConvertSchema;
@@ -0,0 +1,3 @@
1
+ export function defineConvert(name, fields) {
2
+ return { name, type: 'convert', fields };
3
+ }
package/dist/curd.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { SchemaBase, Field, Operator } from './dsl.js';
2
+ import { TableSchema } from './db.js';
3
+ import { FrontAppSchema } from './project.js';
4
+ import { ActionSchema } from './action.js';
5
+ /** Mode of an action page: modal dialog or standalone route. */
6
+ export type ActionPageMode = 'modal' | 'route';
7
+ /** One CRUD action page (add / update / detail). */
8
+ export interface ActionPage {
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
+ /** Fields rendered on this page. Required, non-empty — every field the
13
+ * frontend shows must be listed explicitly. */
14
+ columns: Field[];
15
+ }
16
+ /** List page configuration. */
17
+ export interface CurdListConfig {
18
+ /** List columns; required, non-empty. Every field the list shows must be
19
+ * listed explicitly. May include cross-table fields via foreign refs. */
20
+ columns: Field[];
21
+ /** Fuzzy keyword search on this table's columns. */
22
+ keyword?: {
23
+ columns: Field[];
24
+ };
25
+ /** Default sort. Required — column and direction are both mandatory. */
26
+ orderBy: {
27
+ column: Field;
28
+ direction: 'asc' | 'desc';
29
+ };
30
+ /** Search condition fields; op defaults to 'eq'. */
31
+ searchFields?: {
32
+ field: Field;
33
+ op?: Operator;
34
+ }[];
35
+ /** Column header text overrides: Field.name → header text. */
36
+ columnTitles?: Record<string, string>;
37
+ }
38
+ /** Admin-only CRUD page standard: binds one entity table to a frontend
39
+ * admin app. Drives generation of the list page plus optional
40
+ * add/update/detail pages. */
41
+ export interface CurdSchema extends SchemaBase {
42
+ /** The admin frontend app this CRUD belongs to (shared instance, type must be 'admin'). */
43
+ app: FrontAppSchema;
44
+ /** The bound entity table (shared instance). */
45
+ table: TableSchema;
46
+ /** List page Chinese title. */
47
+ title: string;
48
+ /** Sidebar menu section (group) this CRUD page belongs to. */
49
+ section: string;
50
+ /** Extra user actions on this page (beyond the standard CRUD). */
51
+ actions?: ActionSchema[];
52
+ /** Add/update/detail action pages. */
53
+ actionPages?: {
54
+ add?: ActionPage;
55
+ update?: ActionPage;
56
+ detail?: ActionPage;
57
+ };
58
+ list: CurdListConfig;
59
+ }
60
+ /** Defines an admin CRUD page standard. Runtime-validates admin app binding,
61
+ * non-empty columns and table field ownership (same style as defineTable). */
62
+ export declare function defineCurd(name: string, schema: Omit<CurdSchema, 'name'>): CurdSchema;
package/dist/curd.js ADDED
@@ -0,0 +1,31 @@
1
+ function assertColumns(curd, pageName, columns) {
2
+ if (columns.length === 0) {
3
+ throw new Error(`curd ${curd.name}: ${pageName}.columns must be non-empty`);
4
+ }
5
+ }
6
+ function assertFieldsOwnTable(curd, label, fields) {
7
+ for (const f of fields) {
8
+ if (f.schema !== curd.table) {
9
+ throw new Error(`curd ${curd.name}: ${label} field ${f.name} does not belong to table ${curd.table.name}`);
10
+ }
11
+ }
12
+ }
13
+ /** Defines an admin CRUD page standard. Runtime-validates admin app binding,
14
+ * non-empty columns and table field ownership (same style as defineTable). */
15
+ export function defineCurd(name, schema) {
16
+ const curd = { name, ...schema };
17
+ if (curd.app.type !== 'admin') {
18
+ throw new Error(`curd ${name}: app ${curd.app.name} must be type 'admin' (got '${curd.app.type}')`);
19
+ }
20
+ if (!curd.section) {
21
+ throw new Error(`curd ${name}: section is required (sidebar menu group, e.g. '商户管理')`);
22
+ }
23
+ assertColumns(curd, 'list', curd.list.columns);
24
+ for (const [pageName, page] of Object.entries(curd.actionPages ?? {})) {
25
+ if (page)
26
+ assertColumns(curd, `actionPages.${pageName}`, page.columns);
27
+ }
28
+ assertFieldsOwnTable(curd, 'keyword', curd.list.keyword?.columns ?? []);
29
+ assertFieldsOwnTable(curd, 'orderBy', [curd.list.orderBy.column]);
30
+ return curd;
31
+ }
@@ -0,0 +1,8 @@
1
+ /** MySQL connection settings, loaded from mysql.config.ts at the project root. */
2
+ export interface DbConfig {
3
+ host: string;
4
+ port: number;
5
+ user: string;
6
+ password: string;
7
+ database: string;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/db.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { EntityPhrase } from './dictionary.js';
2
+ import type { CollectionSchemaBase, EnumDef, Field } from './dsl.js';
3
+ export type Index = {
4
+ name?: string;
5
+ columns: Field | Field[];
6
+ unique?: boolean;
7
+ };
8
+ export type ForeignKey = {
9
+ columns: Field | Field[];
10
+ references: Field | Field[];
11
+ };
12
+ export interface TableSchemaOptions<N extends string = string, C extends Record<string, Field> = Record<string, Field>, E extends Record<string, EnumDef> = Record<string, EnumDef>> {
13
+ description?: string;
14
+ paginated?: boolean;
15
+ actor?: boolean;
16
+ generator?: string;
17
+ autoIncrement?: Field;
18
+ /** 本表用于关联显示的名称字段(如 name / username)。被外键引用时,自动用该字段做 label 展示。 */
19
+ label?: Field;
20
+ primaryKey?: Field | Field[];
21
+ indexes?: Index[];
22
+ foreignKeys?: Record<string, ForeignKey>;
23
+ /** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
24
+ phrase?: EntityPhrase;
25
+ /** 本表引用的所有枚举定义(map,key 为枚举标识),显式声明供 gen-enums 收集 */
26
+ enums: E;
27
+ columns: C;
28
+ }
29
+ export declare class TableSchema<N extends string = string, C extends Record<string, Field> = Record<string, Field>, E extends Record<string, EnumDef> = Record<string, EnumDef>> implements CollectionSchemaBase {
30
+ type: string;
31
+ name: N;
32
+ description?: string;
33
+ /** 分页 */
34
+ paginated?: boolean;
35
+ /** 系统操作者(如小程序为 C 端用户,管理端为运营) */
36
+ actor?: boolean;
37
+ /** id 生成器 */
38
+ generator?: string;
39
+ /** 自增主键字段 */
40
+ autoIncrement?: Field;
41
+ primaryKey?: Field | Field[];
42
+ indexes?: Index[];
43
+ /** 外键,引用其他表的字段 */
44
+ foreignKeys?: Record<string, ForeignKey>;
45
+ /** 本表用于关联显示的名称字段(如 name / username)。被外键引用时,自动用该字段做 label 展示。 */
46
+ label?: Field;
47
+ /** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
48
+ phrase?: EntityPhrase;
49
+ /** 本表引用的所有枚举定义(map,key 为枚举标识),显式声明供 gen-enums 收集 */
50
+ enums: E;
51
+ columns: C;
52
+ constructor(name: N, options: TableSchemaOptions<N, C, E>);
53
+ /** True when the field is part of this table's primary key */
54
+ isPk(fieldRef: Field): boolean;
55
+ }
56
+ export declare function defineTable<N extends string, C extends Record<string, Field>, E extends Record<string, EnumDef> = Record<string, EnumDef>>(name: N, schema: TableSchemaOptions<N, C, E>): TableSchema<N, C, E>;
package/dist/db.js ADDED
@@ -0,0 +1,100 @@
1
+ // DB table definitions.
2
+ // Shape: { type: 'table', <extension fields> }
3
+ export class TableSchema {
4
+ type = 'table';
5
+ name;
6
+ description;
7
+ /** 分页 */
8
+ paginated;
9
+ /** 系统操作者(如小程序为 C 端用户,管理端为运营) */
10
+ actor;
11
+ /** id 生成器 */
12
+ generator;
13
+ /** 自增主键字段 */
14
+ autoIncrement;
15
+ primaryKey;
16
+ indexes;
17
+ /** 外键,引用其他表的字段 */
18
+ foreignKeys;
19
+ /** 本表用于关联显示的名称字段(如 name / username)。被外键引用时,自动用该字段做 label 展示。 */
20
+ label;
21
+ /** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
22
+ phrase;
23
+ /** 本表引用的所有枚举定义(map,key 为枚举标识),显式声明供 gen-enums 收集 */
24
+ enums;
25
+ columns;
26
+ constructor(name, options) {
27
+ if (!options.enums) {
28
+ throw new Error(`table '${name}': enums is required — declare enums: {} when the table has no enums`);
29
+ }
30
+ this.name = name;
31
+ this.description = options.description;
32
+ this.paginated = options.paginated;
33
+ this.actor = options.actor;
34
+ this.generator = options.generator;
35
+ this.autoIncrement = options.autoIncrement;
36
+ this.primaryKey = options.primaryKey;
37
+ this.indexes = options.indexes;
38
+ this.foreignKeys = options.foreignKeys;
39
+ this.label = options.label;
40
+ this.phrase = options.phrase;
41
+ this.enums = options.enums;
42
+ this.columns = options.columns;
43
+ }
44
+ /** True when the field is part of this table's primary key */
45
+ isPk(fieldRef) {
46
+ if (this.primaryKey === undefined)
47
+ return false;
48
+ return Array.isArray(this.primaryKey)
49
+ ? this.primaryKey.includes(fieldRef)
50
+ : this.primaryKey === fieldRef;
51
+ }
52
+ }
53
+ export function defineTable(name, schema) {
54
+ const table = new TableSchema(name, schema);
55
+ if (table.generator && table.autoIncrement) {
56
+ throw new Error(`table '${name}': generator and autoIncrement are mutually exclusive`);
57
+ }
58
+ if (table.label && !Object.values(table.columns).includes(table.label)) {
59
+ throw new Error(`table '${name}': label field '${table.label.name}' must be one of the table's columns`);
60
+ }
61
+ for (const key of Object.keys(table.columns)) {
62
+ const field = table.columns[key];
63
+ if (field.schema && field.schema !== table) {
64
+ throw new Error(`field ${key}: belongs to table ${field.schema.name}, cannot reuse in table ${table.name}`);
65
+ }
66
+ }
67
+ for (const key of Object.keys(table.columns)) {
68
+ table.columns[key].name = key;
69
+ table.columns[key].schema = table;
70
+ }
71
+ for (const [fkName, fk] of Object.entries(table.foreignKeys ?? {})) {
72
+ const refs = Array.isArray(fk.references) ? fk.references : [fk.references];
73
+ const fields = Array.isArray(fk.columns) ? fk.columns : [fk.columns];
74
+ for (let i = 0; i < refs.length; i++) {
75
+ const ref = refs[i];
76
+ if (!ref.schema)
77
+ throw new Error(`foreign key ${fkName}: references field has no schema`);
78
+ if (ref.schema === table)
79
+ throw new Error(`foreign key ${fkName}: cannot reference own table ${table.name}`);
80
+ const phrase = ref.schema.phrase;
81
+ if (!phrase)
82
+ throw new Error(`foreign key ${fkName}: referenced table ${ref.schema.name} has no phrase, cannot check field naming`);
83
+ const expected = `${phrase.name}_${ref.name}`;
84
+ const fkField = fields[i];
85
+ if (fkField.name !== expected) {
86
+ throw new Error(`foreign key ${fkName}: field must be named ${expected} (phrase ${phrase.name} + ${ref.name}), got ${fkField.name}`);
87
+ }
88
+ }
89
+ }
90
+ for (const [idxKey, index] of Object.entries(table.indexes ?? {})) {
91
+ const cols = Array.isArray(index.columns) ? index.columns : [index.columns];
92
+ for (const col of cols) {
93
+ if (typeof col !== 'object' || col === null || typeof col.type !== 'string') {
94
+ const idxName = index.name ?? idxKey;
95
+ throw new Error(`index ${idxName}: columns must be Field instances (stringField()/intField()/...), got ${JSON.stringify(col)}`);
96
+ }
97
+ }
98
+ }
99
+ return table;
100
+ }
@@ -1,13 +1,34 @@
1
- import { SchemaBase } from './dsl';
1
+ import { SchemaBase } from './dsl.js';
2
+ /** Kind of a dictionary entry: which naming rule the phrase participates in. */
3
+ export declare enum DictionaryEntryType {
4
+ Entity = "entity",
5
+ Business = "business"
6
+ }
2
7
  /** A vocabulary entry. */
3
8
  export interface DictionaryEntry extends SchemaBase {
9
+ /**
10
+ * Entry kind. Entity phrases (abbreviation of an entity, e.g. mer) must be
11
+ * the FIRST segment of a field name; business phrases (attribute of an
12
+ * entity, e.g. rate) must be the LAST segment.
13
+ */
14
+ type: DictionaryEntryType;
4
15
  /** Display label (Chinese) for the term. */
5
16
  label?: string;
6
17
  }
18
+ /** An entity phrase entry — type narrowed to Entity. */
19
+ export interface EntityPhrase extends DictionaryEntry {
20
+ type: DictionaryEntryType.Entity;
21
+ }
7
22
  /**
8
- * Explains a phrase — the returned entry IS the phrase. `name` is the phrase
9
- * itself (column-name stem, e.g. mer / bd / amt), not a full entity name;
23
+ * Explains an entity phrase — the returned entry IS the phrase. `name` is the
24
+ * phrase itself (column-name stem, e.g. mer / bd), not a full entity name;
10
25
  * label/description explain what it means. Convention: short phrase, not long
11
- * name.
26
+ * name. Entity phrases appear as the first segment of a field name (mer_id).
27
+ */
28
+ export declare function defineEntityPhrase(extra: Omit<DictionaryEntry, 'type'>): EntityPhrase;
29
+ /**
30
+ * Explains a business phrase (an attribute of an entity) — the returned entry
31
+ * IS the phrase. `name` is the phrase itself (column-name stem, e.g. amt /
32
+ * rate). Business phrases appear as the last segment of a field name (bd_rate).
12
33
  */
13
- export declare function definePhrase(extra: DictionaryEntry): DictionaryEntry;
34
+ export declare function defineBusinessPhrase(extra: Omit<DictionaryEntry, 'type'>): DictionaryEntry;
@@ -1,12 +1,25 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.definePhrase = definePhrase;
1
+ // Dictionary definitions: vocabulary shared across the team — what a term
2
+ // means and what it is called.
3
+ /** Kind of a dictionary entry: which naming rule the phrase participates in. */
4
+ export var DictionaryEntryType;
5
+ (function (DictionaryEntryType) {
6
+ DictionaryEntryType["Entity"] = "entity";
7
+ DictionaryEntryType["Business"] = "business";
8
+ })(DictionaryEntryType || (DictionaryEntryType = {}));
4
9
  /**
5
- * Explains a phrase — the returned entry IS the phrase. `name` is the phrase
6
- * itself (column-name stem, e.g. mer / bd / amt), not a full entity name;
10
+ * Explains an entity phrase — the returned entry IS the phrase. `name` is the
11
+ * phrase itself (column-name stem, e.g. mer / bd), not a full entity name;
7
12
  * label/description explain what it means. Convention: short phrase, not long
8
- * name.
13
+ * name. Entity phrases appear as the first segment of a field name (mer_id).
9
14
  */
10
- function definePhrase(extra) {
11
- return extra;
15
+ export function defineEntityPhrase(extra) {
16
+ return { type: DictionaryEntryType.Entity, ...extra };
17
+ }
18
+ /**
19
+ * Explains a business phrase (an attribute of an entity) — the returned entry
20
+ * IS the phrase. `name` is the phrase itself (column-name stem, e.g. amt /
21
+ * rate). Business phrases appear as the last segment of a field name (bd_rate).
22
+ */
23
+ export function defineBusinessPhrase(extra) {
24
+ return { type: DictionaryEntryType.Business, ...extra };
12
25
  }
package/dist/dsl.d.ts CHANGED
@@ -1,8 +1,15 @@
1
- import type { DictionaryEntry } from './dictionary';
1
+ import type { ImportBase } from './import-base.js';
2
+ import type { MockDescriptor } from './mock.js';
3
+ /** Field query comparison operators (search field semantics). */
4
+ export type Operator = 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ne';
2
5
  export interface SchemaBase {
3
6
  name: string;
4
7
  description?: string;
5
8
  }
9
+ /** Schema base for cross-file entities. importRef locates the generating module; inline schemas omit it. */
10
+ export interface ImportableSchemaBase extends SchemaBase {
11
+ importRef?: ImportBase;
12
+ }
6
13
  /** Field collection schemas (DB table vs DTO message); `type` is the discriminator */
7
14
  export interface CollectionSchemaBase extends SchemaBase {
8
15
  type: string;
@@ -13,13 +20,13 @@ export interface BaseField {
13
20
  label?: string;
14
21
  /** 字段描述 */
15
22
  description?: string;
16
- /** 业务语义码(如 'merchant_name'),驱动 mock 生成等下游消费 */
17
- semantic?: string;
18
23
  optional?: boolean;
19
24
  readOnly?: boolean;
20
25
  default?: string;
21
26
  /** 所属 schema(db 或 dto) */
22
27
  schema?: CollectionSchemaBase;
28
+ /** Pure-data mock descriptor for test data generation */
29
+ mock?: MockDescriptor;
23
30
  }
24
31
  interface StringField extends BaseField {
25
32
  type: 'string';
@@ -87,51 +94,6 @@ interface JsonField extends BaseField {
87
94
  jsType: 'object';
88
95
  }
89
96
  export type Field = StringField | TextField | IntField | BigintField | DecimalField | BooleanField | DateField | TimeField | DateTimeField | EnumField | JsonField;
90
- export type Index = {
91
- name?: string;
92
- fields: Field | Field[];
93
- unique?: boolean;
94
- };
95
- export type ForeignKey = {
96
- fields: Field | Field[];
97
- references: Field | Field[];
98
- };
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;
116
- /** 分页 */
117
- paginated?: boolean;
118
- /** 系统操作者(如小程序为 C 端用户,管理端为运营) */
119
- actor?: boolean;
120
- /** id 生成器 */
121
- generator?: string;
122
- /** 自增主键字段 */
123
- autoIncrement?: Field;
124
- primaryKey?: Field | Field[];
125
- indexes?: Index[];
126
- /** 外键,引用其他表的字段 */
127
- foreignKeys?: Record<string, ForeignKey>;
128
- /** 引用的实体短语(词典条目):本表归属的实体;关联表等多实体场景不需要 */
129
- phrase?: DictionaryEntry;
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;
134
- }
135
97
  type FieldExtras<T extends Field> = Omit<T, 'name' | 'type' | 'jsType'>;
136
98
  export declare function stringField(extra?: FieldExtras<StringField>): StringField;
137
99
  export declare function textField(extra?: FieldExtras<TextField>): TextField;
@@ -144,5 +106,4 @@ export declare function timeField(extra?: FieldExtras<TimeField>): TimeField;
144
106
  export declare function datetimeField(extra?: FieldExtras<DateTimeField>): DateTimeField;
145
107
  export declare function jsonField(extra?: FieldExtras<JsonField>): JsonField;
146
108
  export declare function enumField(extra: Omit<EnumField, 'name' | 'type' | 'jsType'>): EnumField;
147
- export declare function defineTable(name: string, schema: TableSchemaOptions): TableSchema;
148
109
  export {};