@pylonts/dsl 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bases.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { DtoMessage, ImportBase, ImportRef } from './dto.js';
3
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'` */
6
+ /** Paged rows type base without generic args — renders `import type { PagedRows } from '@pylonts/core'` */
7
7
  export declare const PagedRows: ImportBase;
8
8
  /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
9
9
  export declare const PageRows: (row: DtoMessage) => ImportRef;
package/dist/bases.js CHANGED
@@ -14,8 +14,8 @@ export const PageResult = (row) => ({
14
14
  name: 'PageResult',
15
15
  args: [row],
16
16
  });
17
- /** Paged rows type base without generic args — renders `import { PagedRows } from '@pylonts/core'` */
18
- export const PagedRows = { from: '@pylonts/core', name: 'PagedRows' };
17
+ /** Paged rows type base without generic args — renders `import type { PagedRows } from '@pylonts/core'` */
18
+ export const PagedRows = { from: '@pylonts/core', name: 'PagedRows', type: true };
19
19
  /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
20
20
  export const PageRows = (row) => ({
21
21
  from: '@pylonts/core',
package/dist/convert.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import type { SchemaBase } from './dsl.js';
2
+ import type { FrontAppSchema } from './project.js';
2
3
  /** Declares post-call result → page data field mapping.
3
4
  * Driver generates per-item transform (e.g. .map()) before setData. */
4
5
  export interface ConvertSchema extends SchemaBase {
5
6
  type: 'convert';
7
+ /** The frontend app this convert belongs to (shared instance from project.config). */
8
+ app: FrontAppSchema;
6
9
  /** { targetField: sourceField } — renames or copies fields from call result. */
7
10
  fields: Record<string, string>;
8
11
  }
9
- export declare function defineConvert(name: string, fields: Record<string, string>): ConvertSchema;
12
+ export declare function defineConvert(name: string, app: FrontAppSchema, fields: Record<string, string>): ConvertSchema;
package/dist/convert.js CHANGED
@@ -1,3 +1,3 @@
1
- export function defineConvert(name, fields) {
2
- return { name, type: 'convert', fields };
1
+ export function defineConvert(name, app, fields) {
2
+ return { name, type: 'convert', app, fields };
3
3
  }
package/dist/dao.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import { FrontAppSchema } from './project.js';
3
+ /** A data-access layer bound to exactly one frontend app. */
4
+ export interface DaoSchema extends SchemaBase {
5
+ type: 'dao';
6
+ /** The frontend app this DAO belongs to (shared instance from project.config). */
7
+ app: FrontAppSchema;
8
+ }
9
+ export declare function defineDao(name: string, app: FrontAppSchema, description?: string): DaoSchema;
package/dist/dao.js ADDED
@@ -0,0 +1,3 @@
1
+ export function defineDao(name, app, description) {
2
+ return { name, type: 'dao', app, description };
3
+ }
package/dist/db.d.ts CHANGED
@@ -11,7 +11,7 @@ export type ForeignKey = {
11
11
  };
12
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
13
  description?: string;
14
- paginated?: boolean;
14
+ paginated: boolean;
15
15
  actor?: boolean;
16
16
  generator?: string;
17
17
  autoIncrement?: Field;
@@ -31,7 +31,7 @@ export declare class TableSchema<N extends string = string, C extends Record<str
31
31
  name: N;
32
32
  description?: string;
33
33
  /** 分页 */
34
- paginated?: boolean;
34
+ paginated: boolean;
35
35
  /** 系统操作者(如小程序为 C 端用户,管理端为运营) */
36
36
  actor?: boolean;
37
37
  /** id 生成器 */
package/dist/db.js CHANGED
@@ -27,6 +27,9 @@ export class TableSchema {
27
27
  if (!options.enums) {
28
28
  throw new Error(`table '${name}': enums is required — declare enums: {} when the table has no enums`);
29
29
  }
30
+ if (options.paginated === undefined || options.paginated === null) {
31
+ throw new Error(`table '${name}': paginated is required — discuss with user whether this table needs pagination, then set paginated: true or paginated: false`);
32
+ }
30
33
  this.name = name;
31
34
  this.description = options.description;
32
35
  this.paginated = options.paginated;
@@ -68,9 +71,24 @@ export function defineTable(name, schema) {
68
71
  table.columns[key].name = key;
69
72
  table.columns[key].schema = table;
70
73
  }
74
+ if (table.primaryKey) {
75
+ const pkFields = Array.isArray(table.primaryKey) ? table.primaryKey : [table.primaryKey];
76
+ const colRefs = Object.values(table.columns);
77
+ for (const pk of pkFields) {
78
+ if (!colRefs.includes(pk)) {
79
+ throw new Error(`table '${name}': primaryKey field '${pk.name}' must be the exact same object as the corresponding column (extract it to a const and reuse)`);
80
+ }
81
+ }
82
+ }
71
83
  for (const [fkName, fk] of Object.entries(table.foreignKeys ?? {})) {
72
84
  const refs = Array.isArray(fk.references) ? fk.references : [fk.references];
73
85
  const fields = Array.isArray(fk.columns) ? fk.columns : [fk.columns];
86
+ const colRefs = Object.values(table.columns);
87
+ for (const fkField of fields) {
88
+ if (!colRefs.includes(fkField)) {
89
+ throw new Error(`foreign key ${fkName}: column '${fkField.name}' must be the exact same object as the corresponding column in the table (extract it to a const and reuse)`);
90
+ }
91
+ }
74
92
  for (let i = 0; i < refs.length; i++) {
75
93
  const ref = refs[i];
76
94
  if (!ref.schema)
@@ -89,11 +107,16 @@ export function defineTable(name, schema) {
89
107
  }
90
108
  for (const [idxKey, index] of Object.entries(table.indexes ?? {})) {
91
109
  const cols = Array.isArray(index.columns) ? index.columns : [index.columns];
110
+ const colRefs = Object.values(table.columns);
92
111
  for (const col of cols) {
93
112
  if (typeof col !== 'object' || col === null || typeof col.type !== 'string') {
94
113
  const idxName = index.name ?? idxKey;
95
114
  throw new Error(`index ${idxName}: columns must be Field instances (stringField()/intField()/...), got ${JSON.stringify(col)}`);
96
115
  }
116
+ if (!colRefs.includes(col)) {
117
+ const idxName = index.name ?? idxKey;
118
+ throw new Error(`index ${idxName}: column '${col.name}' must be the exact same object as the corresponding column in the table (extract it to a const and reuse)`);
119
+ }
97
120
  }
98
121
  }
99
122
  return table;
package/dist/index.d.ts CHANGED
@@ -21,6 +21,8 @@ export * from './component.js';
21
21
  export * from './convert.js';
22
22
  export * from './ref.js';
23
23
  export * from './route.js';
24
+ export * from './service.js';
25
+ export * from './dao.js';
24
26
  export * from './page.js';
25
27
  export * from './curd.js';
26
28
  export * from './page-flow.js';
package/dist/index.js CHANGED
@@ -21,6 +21,8 @@ export * from './component.js';
21
21
  export * from './convert.js';
22
22
  export * from './ref.js';
23
23
  export * from './route.js';
24
+ export * from './service.js';
25
+ export * from './dao.js';
24
26
  export * from './page.js';
25
27
  export * from './curd.js';
26
28
  export * from './page-flow.js';
@@ -0,0 +1,16 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import { FrontAppSchema } from './project.js';
3
+ import type { DtoArrayField, DtoField, DtoMessage, DtoObjectField } from './dto.js';
4
+ /** A frontend service bound to exactly one frontend app. */
5
+ export interface ServiceSchema extends SchemaBase {
6
+ type: 'service';
7
+ /** The frontend app this service belongs to (shared instance from project.config). */
8
+ app: FrontAppSchema;
9
+ }
10
+ export declare function defineService(name: string, app: FrontAppSchema, description?: string): ServiceSchema;
11
+ /** A method exposed by a service. */
12
+ export interface ServiceMethodSchema extends SchemaBase {
13
+ type: 'method';
14
+ name: string;
15
+ fields: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField>;
16
+ }
@@ -0,0 +1,3 @@
1
+ export function defineService(name, app, description) {
2
+ return { name, type: 'service', app, description };
3
+ }
package/dist/utils.d.ts CHANGED
@@ -2,3 +2,12 @@
2
2
  export declare function toCamelCase(name: string): string;
3
3
  /** snake_case → PascalCase: mer_id → MerId */
4
4
  export declare function toPascalCase(snake: string): string;
5
+ import { SchemaBase } from './dsl.js';
6
+ import { FrontAppSchema } from './project.js';
7
+ /** A utility module bound to exactly one frontend app. */
8
+ export interface UtilsSchema extends SchemaBase {
9
+ type: 'utils';
10
+ /** The frontend app this utility module belongs to (shared instance from project.config). */
11
+ app: FrontAppSchema;
12
+ }
13
+ export declare function defineUtils(name: string, app: FrontAppSchema, description?: string): UtilsSchema;
package/dist/utils.js CHANGED
@@ -7,3 +7,6 @@ export function toCamelCase(name) {
7
7
  export function toPascalCase(snake) {
8
8
  return snake.replace(/(^|_)([a-z])/g, (_m, _p, c) => c.toUpperCase());
9
9
  }
10
+ export function defineUtils(name, app, description) {
11
+ return { name, type: 'utils', app, description };
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonts/dsl",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Schema definition DSL with drivers: MySQL DDL, TS enum, TypeBox schema codegen.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/bases.ts CHANGED
@@ -1,30 +1,30 @@
1
- import type { DtoMessage, ImportBase, ImportRef } from './dto.js';
2
-
3
- // Named base-schema references for common protocol DTOs.
4
- //
5
- // These are ImportRef metadata (not re-exports of the actual TypeBox schemas):
6
- // the DSL stores { from, name } so the generator can emit the import line and
7
- // the identifier — the runtime schema object itself is never loaded by the DSL.
8
- //
9
- // `import { PageRequest } from '@pylonts/dsl'` therefore gives .include() an
10
- // already-resolved reference — no static analysis or name lookup needed.
11
-
12
- /** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
13
- export const PageRequest: ImportBase = { from: '@pylonts/core', name: 'PageRequest' };
14
-
15
- /** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
16
- export const PageResult = (row: DtoMessage): ImportRef => ({
17
- from: '@pylonts/core',
18
- name: 'PageResult',
19
- args: [row],
20
- });
21
-
22
- /** Paged rows type base without generic args — renders `import { PagedRows } from '@pylonts/core'` */
23
- export const PagedRows: ImportBase = { from: '@pylonts/core', name: 'PagedRows' };
24
-
25
- /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
26
- export const PageRows = (row: DtoMessage): ImportRef => ({
27
- from: '@pylonts/core',
28
- name: 'PagedRows',
29
- args: [row],
1
+ import type { DtoMessage, ImportBase, ImportRef } from './dto.js';
2
+
3
+ // Named base-schema references for common protocol DTOs.
4
+ //
5
+ // These are ImportRef metadata (not re-exports of the actual TypeBox schemas):
6
+ // the DSL stores { from, name } so the generator can emit the import line and
7
+ // the identifier — the runtime schema object itself is never loaded by the DSL.
8
+ //
9
+ // `import { PageRequest } from '@pylonts/dsl'` therefore gives .include() an
10
+ // already-resolved reference — no static analysis or name lookup needed.
11
+
12
+ /** Paginated query request base — renders `import { PageRequest } from '@pylonts/core'` + Intersect */
13
+ export const PageRequest: ImportBase = { from: '@pylonts/core', name: 'PageRequest' };
14
+
15
+ /** Paginated list response base — renders `import { PageResult } from '@pylonts/core'` + `PageResult(<row>)` */
16
+ export const PageResult = (row: DtoMessage): ImportRef => ({
17
+ from: '@pylonts/core',
18
+ name: 'PageResult',
19
+ args: [row],
20
+ });
21
+
22
+ /** Paged rows type base without generic args — renders `import type { PagedRows } from '@pylonts/core'` */
23
+ export const PagedRows: ImportBase = { from: '@pylonts/core', name: 'PagedRows', type: true };
24
+
25
+ /** Paged rows type base — renders `import { PagedRows } from '@pylonts/core'` + `PagedRows(<row>)` */
26
+ export const PageRows = (row: DtoMessage): ImportRef => ({
27
+ from: '@pylonts/core',
28
+ name: 'PagedRows',
29
+ args: [row],
30
30
  });
package/src/convert.ts CHANGED
@@ -1,13 +1,16 @@
1
1
  import type { SchemaBase } from './dsl.js';
2
+ import type { FrontAppSchema } from './project.js';
2
3
 
3
4
  /** Declares post-call result → page data field mapping.
4
5
  * Driver generates per-item transform (e.g. .map()) before setData. */
5
6
  export interface ConvertSchema extends SchemaBase {
6
7
  type: 'convert';
8
+ /** The frontend app this convert belongs to (shared instance from project.config). */
9
+ app: FrontAppSchema;
7
10
  /** { targetField: sourceField } — renames or copies fields from call result. */
8
11
  fields: Record<string, string>;
9
12
  }
10
13
 
11
- export function defineConvert(name: string, fields: Record<string, string>): ConvertSchema {
12
- return { name, type: 'convert', fields };
13
- }
14
+ export function defineConvert(name: string, app: FrontAppSchema, fields: Record<string, string>): ConvertSchema {
15
+ return { name, type: 'convert', app, fields };
16
+ }
package/src/dao.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import { FrontAppSchema } from './project.js';
3
+
4
+ /** A data-access layer bound to exactly one frontend app. */
5
+ export interface DaoSchema extends SchemaBase {
6
+ type: 'dao';
7
+ /** The frontend app this DAO belongs to (shared instance from project.config). */
8
+ app: FrontAppSchema;
9
+ }
10
+
11
+ export function defineDao(name: string, app: FrontAppSchema, description?: string): DaoSchema {
12
+ return { name, type: 'dao', app, description };
13
+ }
package/src/db.ts CHANGED
@@ -21,7 +21,7 @@ export interface TableSchemaOptions<
21
21
  E extends Record<string, EnumDef> = Record<string, EnumDef>
22
22
  > {
23
23
  description?: string;
24
- paginated?: boolean;
24
+ paginated: boolean;
25
25
  actor?: boolean;
26
26
  generator?: string;
27
27
  autoIncrement?: Field;
@@ -46,7 +46,7 @@ export class TableSchema<
46
46
  name: N;
47
47
  description?: string;
48
48
  /** 分页 */
49
- paginated?: boolean;
49
+ paginated: boolean;
50
50
  /** 系统操作者(如小程序为 C 端用户,管理端为运营) */
51
51
  actor?: boolean;
52
52
  /** id 生成器 */
@@ -69,6 +69,9 @@ export class TableSchema<
69
69
  if (!options.enums) {
70
70
  throw new Error(`table '${name}': enums is required — declare enums: {} when the table has no enums`);
71
71
  }
72
+ if (options.paginated === undefined || options.paginated === null) {
73
+ throw new Error(`table '${name}': paginated is required — discuss with user whether this table needs pagination, then set paginated: true or paginated: false`);
74
+ }
72
75
  this.name = name;
73
76
  this.description = options.description;
74
77
  this.paginated = options.paginated;
@@ -120,9 +123,28 @@ export function defineTable<
120
123
  table.columns[key].name = key;
121
124
  table.columns[key].schema = table;
122
125
  }
126
+ if (table.primaryKey) {
127
+ const pkFields = Array.isArray(table.primaryKey) ? table.primaryKey : [table.primaryKey];
128
+ const colRefs = Object.values(table.columns);
129
+ for (const pk of pkFields) {
130
+ if (!colRefs.includes(pk)) {
131
+ throw new Error(
132
+ `table '${name}': primaryKey field '${pk.name}' must be the exact same object as the corresponding column (extract it to a const and reuse)`,
133
+ );
134
+ }
135
+ }
136
+ }
123
137
  for (const [fkName, fk] of Object.entries(table.foreignKeys ?? {})) {
124
138
  const refs = Array.isArray(fk.references) ? fk.references : [fk.references];
125
139
  const fields = Array.isArray(fk.columns) ? fk.columns : [fk.columns];
140
+ const colRefs = Object.values(table.columns);
141
+ for (const fkField of fields) {
142
+ if (!colRefs.includes(fkField)) {
143
+ throw new Error(
144
+ `foreign key ${fkName}: column '${fkField.name}' must be the exact same object as the corresponding column in the table (extract it to a const and reuse)`,
145
+ );
146
+ }
147
+ }
126
148
  for (let i = 0; i < refs.length; i++) {
127
149
  const ref = refs[i];
128
150
  if (!ref.schema) throw new Error(`foreign key ${fkName}: references field has no schema`);
@@ -140,6 +162,7 @@ export function defineTable<
140
162
  }
141
163
  for (const [idxKey, index] of Object.entries(table.indexes ?? {})) {
142
164
  const cols = Array.isArray(index.columns) ? index.columns : [index.columns];
165
+ const colRefs = Object.values(table.columns);
143
166
  for (const col of cols) {
144
167
  if (typeof col !== 'object' || col === null || typeof (col as Field).type !== 'string') {
145
168
  const idxName = index.name ?? idxKey;
@@ -147,6 +170,12 @@ export function defineTable<
147
170
  `index ${idxName}: columns must be Field instances (stringField()/intField()/...), got ${JSON.stringify(col)}`,
148
171
  );
149
172
  }
173
+ if (!colRefs.includes(col)) {
174
+ const idxName = index.name ?? idxKey;
175
+ throw new Error(
176
+ `index ${idxName}: column '${col.name}' must be the exact same object as the corresponding column in the table (extract it to a const and reuse)`,
177
+ );
178
+ }
150
179
  }
151
180
  }
152
181
  return table;
package/src/index.ts CHANGED
@@ -21,6 +21,8 @@ export * from './component.js';
21
21
  export * from './convert.js';
22
22
  export * from './ref.js';
23
23
  export * from './route.js';
24
+ export * from './service.js';
25
+ export * from './dao.js';
24
26
  export * from './page.js';
25
27
  export * from './curd.js';
26
28
  export * from './page-flow.js';
package/src/service.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import { FrontAppSchema } from './project.js';
3
+ import type { DtoArrayField, DtoField, DtoMessage, DtoObjectField } from './dto.js';
4
+
5
+ /** A frontend service bound to exactly one frontend app. */
6
+ export interface ServiceSchema extends SchemaBase {
7
+ type: 'service';
8
+ /** The frontend app this service belongs to (shared instance from project.config). */
9
+ app: FrontAppSchema;
10
+ }
11
+
12
+ export function defineService(name: string, app: FrontAppSchema, description?: string): ServiceSchema {
13
+ return { name, type: 'service', app, description };
14
+ }
15
+
16
+ /** A method exposed by a service. */
17
+ export interface ServiceMethodSchema extends SchemaBase {
18
+ type: 'method';
19
+ name: string;
20
+ fields: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField>;
21
+ }
package/src/utils.ts CHANGED
@@ -1,11 +1,27 @@
1
- // ── naming conversions ──
2
-
3
- /** snake_case → camelCase: mer_id → merId; names without underscores are unchanged */
4
- export function toCamelCase(name: string): string {
5
- return name.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());
6
- }
7
-
8
- /** snake_case → PascalCase: mer_id → MerId */
9
- export function toPascalCase(snake: string): string {
10
- return snake.replace(/(^|_)([a-z])/g, (_m, _p, c: string) => c.toUpperCase());
1
+ // ── naming conversions ──
2
+
3
+ /** snake_case → camelCase: mer_id → merId; names without underscores are unchanged */
4
+ export function toCamelCase(name: string): string {
5
+ return name.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());
6
+ }
7
+
8
+ /** snake_case → PascalCase: mer_id → MerId */
9
+ export function toPascalCase(snake: string): string {
10
+ return snake.replace(/(^|_)([a-z])/g, (_m, _p, c: string) => c.toUpperCase());
11
+ }
12
+
13
+ // ── schema ──
14
+
15
+ import { SchemaBase } from './dsl.js';
16
+ import { FrontAppSchema } from './project.js';
17
+
18
+ /** A utility module bound to exactly one frontend app. */
19
+ export interface UtilsSchema extends SchemaBase {
20
+ type: 'utils';
21
+ /** The frontend app this utility module belongs to (shared instance from project.config). */
22
+ app: FrontAppSchema;
23
+ }
24
+
25
+ export function defineUtils(name: string, app: FrontAppSchema, description?: string): UtilsSchema {
26
+ return { name, type: 'utils', app, description };
11
27
  }