@pylonts/dsl 1.1.6 → 1.1.12
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 +4 -0
- package/dist/action.d.ts +32 -0
- package/dist/action.js +14 -0
- package/dist/aggregate.d.ts +38 -0
- package/dist/aggregate.js +46 -0
- package/dist/business-flow.d.ts +9 -0
- package/dist/business-flow.js +72 -0
- package/dist/controller.d.ts +17 -9
- package/dist/controller.js +8 -2
- package/dist/convert.d.ts +28 -10
- package/dist/convert.js +16 -5
- package/dist/curd.d.ts +7 -10
- package/dist/curd.js +3 -1
- package/dist/dao.d.ts +81 -53
- package/dist/dao.js +291 -12
- package/dist/db.d.ts +6 -0
- package/dist/db.js +10 -0
- package/dist/domain-event.d.ts +48 -0
- package/dist/domain-event.js +24 -0
- package/dist/dsl.d.ts +17 -2
- package/dist/dsl.js +7 -0
- package/dist/dto.d.ts +6 -4
- package/dist/dto.js +5 -4
- package/dist/entity.d.ts +29 -0
- package/dist/entity.js +13 -0
- package/dist/exception.d.ts +9 -3
- package/dist/exception.js +25 -1
- package/dist/expr.d.ts +45 -0
- package/dist/expr.js +32 -0
- package/dist/filter.d.ts +45 -0
- package/dist/filter.js +21 -0
- package/dist/flow-script.d.ts +108 -0
- package/dist/flow-script.js +505 -0
- package/dist/flow.d.ts +294 -17
- package/dist/flow.js +803 -18
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/mermaid-driver.js +264 -24
- package/dist/mysql-driver.js +3 -0
- package/dist/project.d.ts +10 -6
- package/dist/project.js +35 -4
- package/dist/repository.d.ts +26 -0
- package/dist/repository.js +8 -0
- package/dist/service.d.ts +14 -2
- package/dist/service.js +49 -0
- package/dist/third-service.d.ts +5 -0
- package/dist/third-service.js +1 -0
- package/dist/typebox-driver.js +4 -0
- package/dist/utils.d.ts +9 -2
- package/dist/utils.js +4 -0
- package/docs/aggregate.md +110 -0
- package/docs/curd.md +146 -111
- package/docs/dao-generation.md +478 -0
- package/docs/ddd-principles.md +75 -0
- package/docs/domain-event.md +137 -0
- package/docs/keyword-matcher.md +182 -0
- package/docs/project.md +17 -9
- package/docs/token.md +327 -0
- package/docs/trans-reentrant.md +85 -0
- package/package.json +25 -6
- package/src/action.ts +51 -10
- package/src/aggregate.ts +104 -0
- package/src/business-flow.ts +80 -0
- package/src/controller.ts +25 -11
- package/src/convert.ts +51 -15
- package/src/curd.ts +12 -6
- package/src/dao.ts +377 -63
- package/src/db.ts +13 -0
- package/src/domain-event.ts +74 -0
- package/src/dsl.ts +23 -2
- package/src/dto.ts +9 -6
- package/src/entity.ts +43 -0
- package/src/exception.ts +30 -5
- package/src/expr.ts +65 -0
- package/src/filter.ts +70 -0
- package/src/flow-script.ts +696 -0
- package/src/flow.ts +1129 -46
- package/src/index.ts +6 -2
- package/src/mermaid-driver.ts +256 -29
- package/src/mysql-driver.ts +3 -0
- package/src/project.ts +138 -97
- package/src/repository.ts +35 -0
- package/src/service.ts +68 -3
- package/src/third-service.ts +6 -0
- package/src/typebox-driver.ts +4 -0
- package/src/utils.ts +13 -2
- package/src/endpoint.ts +0 -18
- package/src/provider.ts +0 -68
package/src/service.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl.js';
|
|
2
|
-
import { FrontAppSchema } from './project.js';
|
|
1
|
+
import type { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { FrontAppSchema, ProjectApiSchema } from './project.js';
|
|
3
3
|
import type { DtoArrayField, DtoField, DtoMessage, DtoObjectField } from './dto.js';
|
|
4
|
+
import type { ExceptionSchema } from './exception.js';
|
|
5
|
+
import type { FlowSchema } from './flow.js';
|
|
6
|
+
import { exceptionEndNames } from './flow.js';
|
|
4
7
|
|
|
5
8
|
/** A backend service serving exactly one frontend app (1:1 module). */
|
|
6
9
|
export interface ServiceSchema extends SchemaBase {
|
|
7
10
|
type: 'service';
|
|
11
|
+
/** The backend api module this service belongs to (shared instance from
|
|
12
|
+
* project.config.ts apis). Services are always backend-side, so storage is
|
|
13
|
+
* service_schema/{api.name}/{app.name}/service/. */
|
|
14
|
+
api: ProjectApiSchema;
|
|
8
15
|
/** The frontend app this service serves (shared instance from project.config). */
|
|
9
16
|
app: FrontAppSchema;
|
|
10
17
|
/** Methods keyed by name — the map key is written back as the method name. */
|
|
@@ -16,28 +23,86 @@ export type ServiceMethodDef = Omit<ServiceMethodSchema, 'type' | 'schema' | 'na
|
|
|
16
23
|
|
|
17
24
|
export function defineService(options: {
|
|
18
25
|
name: string;
|
|
26
|
+
api: ProjectApiSchema;
|
|
19
27
|
app: FrontAppSchema;
|
|
20
28
|
methods: Record<string, ServiceMethodDef>;
|
|
21
29
|
description?: string;
|
|
22
30
|
}): ServiceSchema {
|
|
31
|
+
if (!options.api.apps.includes(options.app)) {
|
|
32
|
+
throw new Error(`service ${options.name}: api '${options.api.name}' does not serve app '${options.app.name}'`);
|
|
33
|
+
}
|
|
23
34
|
const schema: ServiceSchema = {
|
|
24
35
|
type: 'service',
|
|
25
36
|
name: options.name,
|
|
26
37
|
description: options.description,
|
|
38
|
+
api: options.api,
|
|
27
39
|
app: options.app,
|
|
28
40
|
methods: {},
|
|
29
41
|
};
|
|
30
42
|
for (const key of Object.keys(options.methods)) {
|
|
31
|
-
const method = options.methods[key]
|
|
43
|
+
const method = options.methods[key];
|
|
32
44
|
schema.methods[key] = { type: 'method', schema, ...method, name: key };
|
|
33
45
|
}
|
|
46
|
+
validateFlowBindings(schema);
|
|
34
47
|
return schema;
|
|
35
48
|
}
|
|
36
49
|
|
|
50
|
+
// A method bound to a flow: the flow's escape set (its exception ends) must
|
|
51
|
+
// equal the method's declared throws, the flow's own args/results (when
|
|
52
|
+
// present) must be the same objects as the method's, and one flow may serve
|
|
53
|
+
// only one method.
|
|
54
|
+
//
|
|
55
|
+
// Notes: (1) the unique-binding check is per defineService call — a flow
|
|
56
|
+
// shared across two service schemas is not detected; (2) escape-set equality
|
|
57
|
+
// means exceptions swallowed by an internal catch (or routed through a
|
|
58
|
+
// catch-all end) disappear from the contract, so the method must not declare
|
|
59
|
+
// them.
|
|
60
|
+
function validateFlowBindings(schema: ServiceSchema): void {
|
|
61
|
+
const seenFlows = new Set<FlowSchema>();
|
|
62
|
+
for (const key of Object.keys(schema.methods)) {
|
|
63
|
+
const m = schema.methods[key];
|
|
64
|
+
if (!m.flow) continue;
|
|
65
|
+
if (seenFlows.has(m.flow)) {
|
|
66
|
+
throw new Error(`service ${schema.name}: flow "${m.flow.name}" is bound to more than one method`);
|
|
67
|
+
}
|
|
68
|
+
seenFlows.add(m.flow);
|
|
69
|
+
if (m.flow.args !== undefined && m.flow.args !== m.args) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`service ${schema.name}: method "${key}" args and its flow "${m.flow.name}" args must be the same object`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (m.flow.results !== undefined && m.flow.results !== m.results) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`service ${schema.name}: method "${key}" results and its flow "${m.flow.name}" results must be the same object`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const escaped = exceptionEndNames(m.flow);
|
|
80
|
+
const declared = new Set((m.throws ?? []).map((t) => t.name));
|
|
81
|
+
for (const e of escaped) {
|
|
82
|
+
if (!declared.has(e)) {
|
|
83
|
+
throw new Error(`service ${schema.name}: method "${key}" flow escapes ${e} but the method does not declare it`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const d of declared) {
|
|
87
|
+
if (!escaped.has(d)) {
|
|
88
|
+
throw new Error(`service ${schema.name}: method "${key}" declares throw ${d} but its flow never escapes it`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (m.flow.name !== key) {
|
|
92
|
+
console.warn(`service ${schema.name}: method "${key}" flow name "${m.flow.name}" differs from the method key`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
37
97
|
/** A method exposed by a service. */
|
|
38
98
|
export interface ServiceMethodSchema extends SchemaBase {
|
|
39
99
|
type: 'method';
|
|
40
100
|
schema : ServiceSchema;
|
|
41
101
|
args: DtoMessage,
|
|
42
102
|
results : DtoMessage
|
|
103
|
+
/** Exceptions this method may throw (e.g. IOException, CodeException). */
|
|
104
|
+
throws?: ExceptionSchema[];
|
|
105
|
+
/** Implementation flow — the method's logic graph. The method carries the
|
|
106
|
+
* contract (args/results/throws), the flow carries the structure. */
|
|
107
|
+
flow?: FlowSchema;
|
|
43
108
|
}
|
package/src/third-service.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CollectionSchemaBase, Field, SchemaBase } from './dsl.js';
|
|
2
|
+
import type { ExceptionSchema } from './exception.js';
|
|
2
3
|
import type { FieldRuleEnd, FieldRuleSchema } from './field-rule.js';
|
|
3
4
|
import type { ThirdApiSchema } from './project.js';
|
|
4
5
|
|
|
@@ -29,6 +30,8 @@ export interface ThirdServiceMethodSchema extends SchemaBase {
|
|
|
29
30
|
args: ThirdMethodSchema;
|
|
30
31
|
/** Output message. */
|
|
31
32
|
results: ThirdMethodSchema;
|
|
33
|
+
/** Exceptions this method may throw (e.g. IOException, CodeException). */
|
|
34
|
+
throws?: ExceptionSchema[];
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/** Field binding to a rule: which end the local wire field stands on.
|
|
@@ -150,6 +153,8 @@ export interface ThirdServiceMethodDef {
|
|
|
150
153
|
args: ThirdMethodDef;
|
|
151
154
|
/** Output message. */
|
|
152
155
|
results: ThirdMethodDef;
|
|
156
|
+
/** Exceptions this method may throw (e.g. IOException, CodeException). */
|
|
157
|
+
throws?: ExceptionSchema[];
|
|
153
158
|
description?: string;
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -175,6 +180,7 @@ export function defineThirdService(options: {
|
|
|
175
180
|
schema,
|
|
176
181
|
args: undefined as unknown as ThirdMethodSchema,
|
|
177
182
|
results: undefined as unknown as ThirdMethodSchema,
|
|
183
|
+
throws: method.throws,
|
|
178
184
|
};
|
|
179
185
|
methodSchema.args = defineThirdMethod(method.args);
|
|
180
186
|
methodSchema.results = defineThirdMethod(method.results);
|
package/src/typebox-driver.ts
CHANGED
|
@@ -79,6 +79,10 @@ function renderBasic(
|
|
|
79
79
|
}
|
|
80
80
|
return `Type.Enum(${ref.name})`;
|
|
81
81
|
}
|
|
82
|
+
case 'aggregate':
|
|
83
|
+
// Aggregate query outputs: count/sum(int) are numbers, everything else
|
|
84
|
+
// arrives as a precision string.
|
|
85
|
+
return field.jsType === 'number' ? 'Type.Number()' : 'Type.String()';
|
|
82
86
|
case 'array':
|
|
83
87
|
return `Type.Array(${renderFieldValue(field.items, indent, resolver)})`;
|
|
84
88
|
case 'object':
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Field, SchemaBase } from './dsl.js';
|
|
2
|
-
import type { FrontAppSchema } from './project.js';
|
|
2
|
+
import type { FrontAppSchema, ProjectApiSchema } from './project.js';
|
|
3
3
|
|
|
4
4
|
// Base utility modules — business-agnostic helpers with full method
|
|
5
5
|
// signatures (e.g. DateTimeUtils.format). Called at the service layer;
|
|
@@ -22,7 +22,13 @@ export type UtilsMethodDef = Omit<UtilsMethodSchema, 'type' | 'schema' | 'name'>
|
|
|
22
22
|
/** A base utility module (e.g. DateTimeUtils). */
|
|
23
23
|
export interface UtilsSchema extends SchemaBase {
|
|
24
24
|
type: 'utils';
|
|
25
|
-
/**
|
|
25
|
+
/** Backend binding — the api module this utils belongs to (shared instance
|
|
26
|
+
* from project.config.ts apis). With `app` it serves that frontend
|
|
27
|
+
* ({api}/{app}/utils/); alone it is the api's public module
|
|
28
|
+
* ({api}/common/utils/). Unset = not backend-side. */
|
|
29
|
+
api?: ProjectApiSchema;
|
|
30
|
+
/** Optional binding — the frontend app this utils serves (shared instance
|
|
31
|
+
* from project.config.ts apps). Empty means a shared public module. */
|
|
26
32
|
app?: FrontAppSchema;
|
|
27
33
|
/** Methods keyed by name — the map key is written back as the method name. */
|
|
28
34
|
methods: Record<string, UtilsMethodSchema>;
|
|
@@ -30,14 +36,19 @@ export interface UtilsSchema extends SchemaBase {
|
|
|
30
36
|
|
|
31
37
|
export function defineUtils(options: {
|
|
32
38
|
name: string;
|
|
39
|
+
api?: ProjectApiSchema;
|
|
33
40
|
app?: FrontAppSchema;
|
|
34
41
|
methods: Record<string, UtilsMethodDef>;
|
|
35
42
|
description?: string;
|
|
36
43
|
}): UtilsSchema {
|
|
44
|
+
if (options.api !== undefined && options.app !== undefined && !options.api.apps.includes(options.app)) {
|
|
45
|
+
throw new Error(`utils ${options.name}: api '${options.api.name}' does not serve app '${options.app.name}'`);
|
|
46
|
+
}
|
|
37
47
|
const schema: UtilsSchema = {
|
|
38
48
|
type: 'utils',
|
|
39
49
|
name: options.name,
|
|
40
50
|
description: options.description,
|
|
51
|
+
api: options.api,
|
|
41
52
|
app: options.app,
|
|
42
53
|
methods: {},
|
|
43
54
|
};
|
package/src/endpoint.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { SchemaBase } from './dsl.js';
|
|
2
|
-
import type { DtoMessage } from './dto.js';
|
|
3
|
-
|
|
4
|
-
/** Shared API call signature: one request DTO in, one response shape out.
|
|
5
|
-
* Referenced by both backend controller methods and frontend page providers —
|
|
6
|
-
* both sides must use the exact same instance, so drift is impossible. */
|
|
7
|
-
export interface EndpointSchema extends SchemaBase {
|
|
8
|
-
type: 'endpoint';
|
|
9
|
-
args: DtoMessage;
|
|
10
|
-
results: DtoMessage | number | boolean | string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function defineEndpoint(
|
|
14
|
-
name: string,
|
|
15
|
-
options: { args: DtoMessage; results: DtoMessage | number | boolean | string; description?: string },
|
|
16
|
-
): EndpointSchema {
|
|
17
|
-
return { type: 'endpoint', name, args: options.args, results: options.results, description: options.description };
|
|
18
|
-
}
|
package/src/provider.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type { ImportBase } from './import-base.js';
|
|
2
|
-
import type { ImportableSchemaBase } from './dsl.js';
|
|
3
|
-
import type { DtoField, DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
|
|
4
|
-
import type { ActionSchema } from './action.js';
|
|
5
|
-
import type { RefSchema } from './ref.js';
|
|
6
|
-
import type { EndpointSchema } from './endpoint.js';
|
|
7
|
-
|
|
8
|
-
// ProviderSchema: an API function signature (args DTO + results DTO).
|
|
9
|
-
|
|
10
|
-
/** Parameter data source for a call argument. */
|
|
11
|
-
export type DataRef =
|
|
12
|
-
| { type: 'route'; key: string }
|
|
13
|
-
| { type: 'data'; key: string }
|
|
14
|
-
| { type: 'value'; value: unknown };
|
|
15
|
-
|
|
16
|
-
/** Create a route-parameter reference. */
|
|
17
|
-
export function route(key: string): DataRef {
|
|
18
|
-
return { type: 'route', key };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Create a page-data reference. */
|
|
22
|
-
export function data(key: string): DataRef {
|
|
23
|
-
return { type: 'data', key };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface CallAction extends ActionSchema {
|
|
27
|
-
type: 'call';
|
|
28
|
-
func: ProviderSchema;
|
|
29
|
-
args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export function call(func: ProviderSchema, args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>): CallAction {
|
|
34
|
-
return { name: func.name, type: 'call', func, args };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Provider function signature. The generated API client exposes one function
|
|
38
|
-
* per endpoint; ProviderSchema gives that function a name and a shared signature. */
|
|
39
|
-
export interface ProviderSchema extends ImportableSchemaBase {
|
|
40
|
-
isAsync: boolean;
|
|
41
|
-
/** Shared API signature — same instance the backend controller method references. */
|
|
42
|
-
signature: EndpointSchema;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Define a provider function. */
|
|
46
|
-
export function defineProvider(
|
|
47
|
-
name: string,
|
|
48
|
-
schema: {
|
|
49
|
-
isAsync: boolean;
|
|
50
|
-
signature: EndpointSchema;
|
|
51
|
-
description?: string;
|
|
52
|
-
importRef?: ImportBase;
|
|
53
|
-
},
|
|
54
|
-
): ProviderSchema {
|
|
55
|
-
return { name, ...schema };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Assign a call's result to a page data field.
|
|
59
|
-
* React: setState({ [field]: await ... }). Mini-program: this.setData({ [field]: ... }). */
|
|
60
|
-
export interface SetDataAction extends ActionSchema {
|
|
61
|
-
type: 'setData';
|
|
62
|
-
call: CallAction;
|
|
63
|
-
field: DtoField;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function setData(call: CallAction, field: DtoField): SetDataAction {
|
|
67
|
-
return { name: 'setData', type: 'setData', call, field };
|
|
68
|
-
}
|