@pylonts/dsl 1.1.12 → 1.1.14
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/convert.d.ts +6 -8
- package/dist/curd.js +1 -1
- package/dist/dao.d.ts +10 -7
- package/dist/dao.js +20 -7
- package/dist/dsl.d.ts +18 -1
- package/dist/dsl.js +40 -0
- package/dist/dto.d.ts +17 -8
- package/dist/dto.js +75 -13
- package/dist/entity.d.ts +4 -3
- package/dist/entity.js +1 -1
- package/dist/filter.d.ts +6 -4
- package/dist/filter.js +1 -1
- package/dist/flow-script.js +8 -2
- package/dist/flow.d.ts +10 -2
- package/dist/flow.js +44 -4
- package/dist/mermaid-driver.js +2 -2
- package/dist/service.d.ts +13 -8
- package/dist/service.js +1 -1
- package/dist/third-service.d.ts +10 -53
- package/dist/third-service.js +3 -78
- package/dist/typebox-driver.d.ts +0 -6
- package/dist/typebox-driver.js +8 -36
- package/dist/utils.d.ts +2 -2
- package/docs/curd.md +146 -146
- package/docs/dao-generation.md +477 -477
- package/docs/project.md +31 -31
- package/docs/token.md +326 -326
- package/package.json +1 -1
- package/src/action.ts +51 -51
- package/src/controller.ts +53 -53
- package/src/convert.ts +76 -78
- package/src/curd.ts +104 -104
- package/src/dao.ts +504 -485
- package/src/dsl.ts +296 -257
- package/src/dto.ts +86 -21
- package/src/entity.ts +43 -42
- package/src/expr.ts +64 -64
- package/src/filter.ts +71 -69
- package/src/flow-script.ts +702 -695
- package/src/flow.ts +1272 -1226
- package/src/index.ts +46 -46
- package/src/mermaid-driver.ts +339 -339
- package/src/mysql-driver.ts +108 -108
- package/src/project.ts +138 -138
- package/src/service.ts +112 -107
- package/src/third-service.ts +68 -191
- package/src/typebox-driver.ts +234 -268
- package/src/utils.ts +74 -74
package/dist/flow.js
CHANGED
|
@@ -20,7 +20,8 @@ export function defineSlots(slots) {
|
|
|
20
20
|
prop in target) {
|
|
21
21
|
return Reflect.get(target, prop, receiver);
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const typeObj = target.type;
|
|
24
|
+
const fields = slotFields(typeObj);
|
|
24
25
|
if (fields !== undefined && Object.prototype.hasOwnProperty.call(fields, prop)) {
|
|
25
26
|
return { slot: proxy, field: fields[prop] };
|
|
26
27
|
}
|
|
@@ -48,7 +49,7 @@ export function defineSlots(slots) {
|
|
|
48
49
|
// unreachable via dot access (slots.args.type reads the message, not the
|
|
49
50
|
// field) — warn instead of failing: the field may never be needed.
|
|
50
51
|
function warnShadowedFields(slot) {
|
|
51
|
-
const fields = slot.type
|
|
52
|
+
const fields = slotFields(slot.type);
|
|
52
53
|
if (fields === undefined)
|
|
53
54
|
return;
|
|
54
55
|
const shadowed = ['type', 'name', 'description'].filter((k) => k in fields);
|
|
@@ -56,6 +57,19 @@ function warnShadowedFields(slot) {
|
|
|
56
57
|
console.warn(`slot "${slot.name}": message fields ${shadowed.join(', ')} are shadowed by slot metadata and unreachable via field access`);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
60
|
+
/** Field map of a slot's declared type: dto/third messages carry `fields`,
|
|
61
|
+
* entity rows carry `columns` (named Fields). */
|
|
62
|
+
function slotFields(type) {
|
|
63
|
+
if (typeof type !== 'object' || type === null)
|
|
64
|
+
return undefined;
|
|
65
|
+
const t = type;
|
|
66
|
+
if (t.fields !== undefined)
|
|
67
|
+
return t.fields;
|
|
68
|
+
if (t.columns !== undefined) {
|
|
69
|
+
return Object.fromEntries(t.columns.map((c) => [c.name, c]));
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
59
73
|
export function invoke(method, options = {}) {
|
|
60
74
|
return { method, args: options.args, result: options.result };
|
|
61
75
|
}
|
|
@@ -66,7 +80,22 @@ export function isCall(m) {
|
|
|
66
80
|
export function methodOf(m) {
|
|
67
81
|
return isCall(m) ? m.method : m;
|
|
68
82
|
}
|
|
83
|
+
/** True when the condition operand is the slot itself (slot-level null check),
|
|
84
|
+
* not a field access on it. Slot metadata (name) lives on the proxy target and
|
|
85
|
+
* reads without field interception; a field access resolves to { slot, field }. */
|
|
86
|
+
export function isFlowSlot(v) {
|
|
87
|
+
return typeof v === 'object' && v !== null && typeof v.name === 'string';
|
|
88
|
+
}
|
|
69
89
|
function comparison(op, field, value) {
|
|
90
|
+
if (isFlowSlot(field)) {
|
|
91
|
+
if (op !== 'isNull' && op !== 'isNotNull') {
|
|
92
|
+
throw new Error(`${op}: a slot-level check only supports isNull/isNotNull — field comparisons need slots.args.amt`);
|
|
93
|
+
}
|
|
94
|
+
if (value !== undefined) {
|
|
95
|
+
throw new Error(`${op}: takes no value`);
|
|
96
|
+
}
|
|
97
|
+
return { kind: 'comparison', op, field, value };
|
|
98
|
+
}
|
|
70
99
|
const ref = field;
|
|
71
100
|
if (typeof ref !== 'object' || ref === null || typeof ref.slot !== 'object' || typeof ref.field !== 'object') {
|
|
72
101
|
throw new Error(`${op}: field must be a slot field access like slots.args.amt`);
|
|
@@ -471,6 +500,16 @@ function validateGuardChecks(schema) {
|
|
|
471
500
|
const COMPARE_OPS = ['lt', 'le', 'gt', 'ge', 'eq', 'ne', 'isNull', 'isNotNull'];
|
|
472
501
|
function validateCondition(where, c) {
|
|
473
502
|
if (!isCall(c)) {
|
|
503
|
+
if (isFlowSlot(c.field)) {
|
|
504
|
+
const nullOp = c.op === 'isNull' || c.op === 'isNotNull';
|
|
505
|
+
if (!nullOp) {
|
|
506
|
+
throw new Error(`${where}: a slot-level check only supports isNull/isNotNull`);
|
|
507
|
+
}
|
|
508
|
+
if (c.value !== undefined) {
|
|
509
|
+
throw new Error(`${where}: ${c.op} takes no value`);
|
|
510
|
+
}
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
474
513
|
const f = c.field;
|
|
475
514
|
if (typeof f !== 'object' || f === null || typeof f.slot !== 'object' || typeof f.field !== 'object') {
|
|
476
515
|
throw new Error(`${where}: ${c.op} field must be a slot field access like slots.args.amt`);
|
|
@@ -502,10 +541,11 @@ function validateCondition(where, c) {
|
|
|
502
541
|
throw new Error(`${where}: a predicate call cannot bind a result slot`);
|
|
503
542
|
}
|
|
504
543
|
}
|
|
505
|
-
/** Slots a condition reads: a comparison's field slot or
|
|
544
|
+
/** Slots a condition reads: a comparison's field slot (or the slot itself for
|
|
545
|
+
* slot-level checks) or a predicate call's arg slots. */
|
|
506
546
|
function conditionSlots(c) {
|
|
507
547
|
if (!isCall(c))
|
|
508
|
-
return [c.field.slot];
|
|
548
|
+
return [isFlowSlot(c.field) ? c.field : c.field.slot];
|
|
509
549
|
return c.args ?? [];
|
|
510
550
|
}
|
|
511
551
|
// ifNode rules: at least one case, every condition well-formed, and targets
|
package/dist/mermaid-driver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCall, methodOf } from './flow.js';
|
|
1
|
+
import { isCall, isFlowSlot, methodOf } from './flow.js';
|
|
2
2
|
// Mermaid driver: converts a FlowSchema into a Mermaid flowchart (TD).
|
|
3
3
|
// Node ids are hierarchical (n0, n0_0, n0_0_0, ...) so nested sub-flows stay
|
|
4
4
|
// globally unique. A node with a sub-flow renders as a subgraph block whose
|
|
@@ -215,7 +215,7 @@ function renderDataLine(n, outgoing) {
|
|
|
215
215
|
if (c === undefined)
|
|
216
216
|
return;
|
|
217
217
|
if (!isCall(c)) {
|
|
218
|
-
reads.add(c.field.slot.name);
|
|
218
|
+
reads.add(isFlowSlot(c.field) ? c.field.name : c.field.slot.name);
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
for (const t of c.args ?? [])
|
package/dist/service.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import type { SchemaBase } from './dsl.js';
|
|
1
|
+
import type { CollectionSchemaBase, SchemaBase } from './dsl.js';
|
|
2
2
|
import type { FrontAppSchema, ProjectApiSchema } from './project.js';
|
|
3
3
|
import type { DtoMessage } from './dto.js';
|
|
4
4
|
import type { ExceptionSchema } from './exception.js';
|
|
5
5
|
import type { FlowSchema } from './flow.js';
|
|
6
|
-
/** A backend service serving
|
|
7
|
-
|
|
6
|
+
/** A backend service serving one frontend app (1:1 module), or a
|
|
7
|
+
* platform-shared domain service (app unset — shared across modules). */
|
|
8
|
+
export interface ServiceSchema extends CollectionSchemaBase {
|
|
8
9
|
type: 'service';
|
|
9
10
|
/** The backend api module this service belongs to (shared instance from
|
|
10
11
|
* project.config.ts apis). Services are always backend-side, so storage is
|
|
11
|
-
* service_schema/{api.name}/{app.name}/service
|
|
12
|
+
* service_schema/{api.name}/{app.name}/service/ — app unset = the api-level
|
|
13
|
+
* common domain layer, stored at service_schema/{api.name}/common/service/. */
|
|
12
14
|
api: ProjectApiSchema;
|
|
13
|
-
/** The frontend app this service serves (shared instance from project.config).
|
|
14
|
-
|
|
15
|
+
/** The frontend app this service serves (shared instance from project.config).
|
|
16
|
+
* Unset = api-level common domain service shared by all modules of the api. */
|
|
17
|
+
app?: FrontAppSchema;
|
|
15
18
|
/** Methods keyed by name — the map key is written back as the method name. */
|
|
16
19
|
methods: Record<string, ServiceMethodSchema>;
|
|
17
20
|
}
|
|
@@ -20,11 +23,13 @@ export type ServiceMethodDef = Omit<ServiceMethodSchema, 'type' | 'schema' | 'na
|
|
|
20
23
|
export declare function defineService(options: {
|
|
21
24
|
name: string;
|
|
22
25
|
api: ProjectApiSchema;
|
|
23
|
-
app
|
|
26
|
+
app?: FrontAppSchema;
|
|
24
27
|
methods: Record<string, ServiceMethodDef>;
|
|
25
28
|
description?: string;
|
|
26
29
|
}): ServiceSchema;
|
|
27
|
-
/** A method exposed by a service
|
|
30
|
+
/** A method exposed by a business service — the contract with the calling
|
|
31
|
+
* frontend. Third-party integration methods are a separate contract
|
|
32
|
+
* (ThirdServiceMethodSchema): they can never bind a flow. */
|
|
28
33
|
export interface ServiceMethodSchema extends SchemaBase {
|
|
29
34
|
type: 'method';
|
|
30
35
|
schema: ServiceSchema;
|
package/dist/service.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exceptionEndNames } from './flow.js';
|
|
2
2
|
export function defineService(options) {
|
|
3
|
-
if (!options.api.apps.includes(options.app)) {
|
|
3
|
+
if (options.app && !options.api.apps.includes(options.app)) {
|
|
4
4
|
throw new Error(`service ${options.name}: api '${options.api.name}' does not serve app '${options.app.name}'`);
|
|
5
5
|
}
|
|
6
6
|
const schema = {
|
package/dist/third-service.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SchemaBase } from './dsl.js';
|
|
2
|
+
import type { DtoMessage } from './dto.js';
|
|
2
3
|
import type { ExceptionSchema } from './exception.js';
|
|
3
|
-
import type { FieldRuleEnd, FieldRuleSchema } from './field-rule.js';
|
|
4
4
|
import type { ThirdApiSchema } from './project.js';
|
|
5
5
|
/** A third-party integration service (e.g. tenpay wechat pay).
|
|
6
6
|
* Distinct from ServiceSchema (backend service bound to an app) and
|
|
@@ -13,65 +13,22 @@ export interface ThirdServiceSchema extends SchemaBase {
|
|
|
13
13
|
/** Methods keyed by name — the map key is written back as the method name. */
|
|
14
14
|
methods: Record<string, ThirdServiceMethodSchema>;
|
|
15
15
|
}
|
|
16
|
-
/** A method
|
|
16
|
+
/** A method of a third-party integration service. Same contract shape as
|
|
17
|
+
* ServiceMethodSchema minus flow — the implementation lives in the external
|
|
18
|
+
* system, there is nothing to model. Kept separate so a third method can
|
|
19
|
+
* never bind a flow. */
|
|
17
20
|
export interface ThirdServiceMethodSchema extends SchemaBase {
|
|
18
21
|
type: 'method';
|
|
19
22
|
schema: ThirdServiceSchema;
|
|
20
|
-
/** Input message — the Field-collection counterpart of a DtoMessage. */
|
|
21
|
-
args: ThirdMethodSchema;
|
|
22
|
-
/** Output message. */
|
|
23
|
-
results: ThirdMethodSchema;
|
|
24
|
-
/** Exceptions this method may throw (e.g. IOException, CodeException). */
|
|
25
|
-
throws?: ExceptionSchema[];
|
|
26
|
-
}
|
|
27
|
-
/** Field binding to a rule: which end the local wire field stands on.
|
|
28
|
-
* The ref always stands on the other end — from/to carry no extra information. */
|
|
29
|
-
export interface ConvertFieldSchema {
|
|
30
|
-
/** The rule binding field and ref (rule = name + two ends). */
|
|
31
|
-
rule: FieldRuleSchema;
|
|
32
|
-
/** The end instance the local wire field stands on. */
|
|
33
|
-
end: FieldRuleEnd;
|
|
34
|
-
}
|
|
35
|
-
/** Same-fact variant link: a wire field carrying the same fact as a local
|
|
36
|
-
* entity column under a different type/format (e.g. total_fee in fen vs amount in yuan). */
|
|
37
|
-
export interface ThirdFieldRef {
|
|
38
|
-
/** Wire field defined in this message (local definition). */
|
|
39
|
-
field: Field;
|
|
40
|
-
/** Field in another schema (table column or another message). */
|
|
41
|
-
ref: Field;
|
|
42
|
-
/** Optional rule binding — omitted when the fact is merely linked, not converted. */
|
|
43
|
-
convert?: ConvertFieldSchema;
|
|
44
|
-
}
|
|
45
|
-
/** A directional message of a third-party method: a Field collection mirroring
|
|
46
|
-
* TableSchema.columns, but fields hold wire-format names/types. A field may be
|
|
47
|
-
* a shared instance of a local entity column (same fact, same type) — its
|
|
48
|
-
* name/schema keep pointing at the table and the DTO projection inherits
|
|
49
|
-
* type/semantics from the entity, exactly like from(table). */
|
|
50
|
-
export interface ThirdMethodSchema extends CollectionSchemaBase {
|
|
51
|
-
type: 'thirdMethod';
|
|
52
|
-
/** The method this message belongs to (direction implied by args/results slot). */
|
|
53
|
-
schema: ThirdServiceMethodSchema;
|
|
54
|
-
/** Wire-format fields. */
|
|
55
|
-
fields: Record<string, Field>;
|
|
56
|
-
/** Same-fact variant links: wire field -> local entity column. */
|
|
57
|
-
refs?: ThirdFieldRef[];
|
|
58
|
-
}
|
|
59
|
-
/** Message input for defineThirdMethod: type/schema are set by the builder. */
|
|
60
|
-
export type ThirdMethodDef = Omit<ThirdMethodSchema, 'type' | 'schema'>;
|
|
61
|
-
/** Build a third-party method message. Writes back name/schema on own fields
|
|
62
|
-
* (top-level and nested); shared entity columns keep their table identity and
|
|
63
|
-
* must be keyed by their column name. */
|
|
64
|
-
export declare function defineThirdMethod(def: ThirdMethodDef): ThirdMethodSchema;
|
|
65
|
-
/** Method input for defineThirdService: name is written back from the methods map key. */
|
|
66
|
-
export interface ThirdServiceMethodDef {
|
|
67
23
|
/** Input message. */
|
|
68
|
-
args:
|
|
24
|
+
args: DtoMessage;
|
|
69
25
|
/** Output message. */
|
|
70
|
-
results:
|
|
26
|
+
results: DtoMessage;
|
|
71
27
|
/** Exceptions this method may throw (e.g. IOException, CodeException). */
|
|
72
28
|
throws?: ExceptionSchema[];
|
|
73
|
-
description?: string;
|
|
74
29
|
}
|
|
30
|
+
/** Method input for defineThirdService: name/schema are set by the builder. */
|
|
31
|
+
export type ThirdServiceMethodDef = Omit<ThirdServiceMethodSchema, 'type' | 'schema' | 'name'>;
|
|
75
32
|
export declare function defineThirdService(options: {
|
|
76
33
|
schema: ThirdApiSchema;
|
|
77
34
|
name: string;
|
package/dist/third-service.js
CHANGED
|
@@ -1,73 +1,3 @@
|
|
|
1
|
-
/** Build a third-party method message. Writes back name/schema on own fields
|
|
2
|
-
* (top-level and nested); shared entity columns keep their table identity and
|
|
3
|
-
* must be keyed by their column name. */
|
|
4
|
-
export function defineThirdMethod(def) {
|
|
5
|
-
const message = {
|
|
6
|
-
type: 'thirdMethod',
|
|
7
|
-
name: def.name,
|
|
8
|
-
description: def.description,
|
|
9
|
-
// Filled by defineThirdService.
|
|
10
|
-
schema: undefined,
|
|
11
|
-
fields: def.fields,
|
|
12
|
-
refs: def.refs,
|
|
13
|
-
};
|
|
14
|
-
for (const key of Object.keys(message.fields)) {
|
|
15
|
-
const field = message.fields[key];
|
|
16
|
-
if (field.schema === undefined) {
|
|
17
|
-
field.name = key;
|
|
18
|
-
field.schema = message;
|
|
19
|
-
}
|
|
20
|
-
else if (field.schema.type === 'table') {
|
|
21
|
-
// Shared entity column: from() names the projection after field.name, so
|
|
22
|
-
// a mismatched key would silently rename the wire field. Same-fact fields
|
|
23
|
-
// with different names go through refs instead.
|
|
24
|
-
if (field.name !== key) {
|
|
25
|
-
throw new Error(`thirdMethod '${message.name}': shared column key '${key}' must match the column name '${field.name}' — ` +
|
|
26
|
-
`same-fact fields with different names go through refs instead`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else if (field.schema !== message) {
|
|
30
|
-
throw new Error(`thirdMethod '${message.name}': field '${key}' already belongs to ${field.schema.type} '${field.schema.name}', cannot reuse`);
|
|
31
|
-
}
|
|
32
|
-
writeBackNested(message, field);
|
|
33
|
-
}
|
|
34
|
-
if (message.refs !== undefined) {
|
|
35
|
-
const ownFields = Object.values(message.fields);
|
|
36
|
-
for (const link of message.refs) {
|
|
37
|
-
if (!ownFields.includes(link.field)) {
|
|
38
|
-
throw new Error(`thirdMethod '${message.name}': ref field must be one of its fields`);
|
|
39
|
-
}
|
|
40
|
-
if (link.ref.schema === undefined || link.ref.schema === message) {
|
|
41
|
-
throw new Error(`thirdMethod '${message.name}': ref target '${link.ref.name}' must be defined in another schema`);
|
|
42
|
-
}
|
|
43
|
-
if (link.convert !== undefined) {
|
|
44
|
-
const ends = Object.values(link.convert.rule.ends);
|
|
45
|
-
if (!ends.includes(link.convert.end)) {
|
|
46
|
-
throw new Error(`thirdMethod '${message.name}': convert end '${link.convert.end.name}' must be one of rule '${link.convert.rule.name}' ends`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return message;
|
|
52
|
-
}
|
|
53
|
-
/** Write back name/schema on nested wire fields (array items, object properties);
|
|
54
|
-
* shared entity columns keep their table identity. */
|
|
55
|
-
function writeBackNested(message, field) {
|
|
56
|
-
if (field.type === 'array') {
|
|
57
|
-
writeBackNested(message, field.items);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (field.type !== 'object')
|
|
61
|
-
return;
|
|
62
|
-
for (const key of Object.keys(field.properties)) {
|
|
63
|
-
const child = field.properties[key];
|
|
64
|
-
if (child.schema === undefined) {
|
|
65
|
-
child.name = key;
|
|
66
|
-
child.schema = message;
|
|
67
|
-
}
|
|
68
|
-
writeBackNested(message, child);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
1
|
export function defineThirdService(options) {
|
|
72
2
|
const schema = {
|
|
73
3
|
type: 'thirdService',
|
|
@@ -78,20 +8,15 @@ export function defineThirdService(options) {
|
|
|
78
8
|
};
|
|
79
9
|
for (const key of Object.keys(options.methods)) {
|
|
80
10
|
const method = options.methods[key];
|
|
81
|
-
|
|
11
|
+
schema.methods[key] = {
|
|
82
12
|
type: 'method',
|
|
83
13
|
name: key,
|
|
84
14
|
description: method.description,
|
|
85
15
|
schema,
|
|
86
|
-
args:
|
|
87
|
-
results:
|
|
16
|
+
args: method.args,
|
|
17
|
+
results: method.results,
|
|
88
18
|
throws: method.throws,
|
|
89
19
|
};
|
|
90
|
-
methodSchema.args = defineThirdMethod(method.args);
|
|
91
|
-
methodSchema.results = defineThirdMethod(method.results);
|
|
92
|
-
methodSchema.args.schema = methodSchema;
|
|
93
|
-
methodSchema.results.schema = methodSchema;
|
|
94
|
-
schema.methods[key] = methodSchema;
|
|
95
20
|
}
|
|
96
21
|
return schema;
|
|
97
22
|
}
|
package/dist/typebox-driver.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DtoMessage, ImportBase } from './dto.js';
|
|
2
|
-
import type { ThirdMethodSchema } from './third-service.js';
|
|
3
2
|
export type EnumResolver = (enumName: string) => ImportBase | undefined;
|
|
4
3
|
/** Collect all imports needed to render a DTO: include() bases + enum references. */
|
|
5
4
|
export declare function collectDtoImports(schema: DtoMessage, resolver: EnumResolver | undefined, out: Map<string, ImportBase>): void;
|
|
@@ -11,8 +10,3 @@ export declare function renderDtoMessage(schema: DtoMessage, options?: {
|
|
|
11
10
|
resolver?: EnumResolver;
|
|
12
11
|
source?: string;
|
|
13
12
|
}): string;
|
|
14
|
-
/** Render one third-party method message export (const only — pair with
|
|
15
|
-
* renderDtoTypeExport for the Static type). */
|
|
16
|
-
export declare function renderThirdMethodExport(schema: ThirdMethodSchema, resolver: EnumResolver | undefined): string;
|
|
17
|
-
/** Collect all imports needed to render a third-party method message: enum references. */
|
|
18
|
-
export declare function collectThirdMethodImports(schema: ThirdMethodSchema, resolver: EnumResolver | undefined, out: Map<string, ImportBase>): void;
|
package/dist/typebox-driver.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isDtoField, isDtoMessage } from './dto.js';
|
|
2
|
+
import { collectEnumRefs } from './dsl.js';
|
|
1
3
|
function renderString(s) {
|
|
2
4
|
return `'${s.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
3
5
|
}
|
|
@@ -116,18 +118,6 @@ function renderValue(f, indent, resolver) {
|
|
|
116
118
|
// annotations; DB field defaults are not carried into the API contract.
|
|
117
119
|
return renderBasic(f.field, f.pattern, f.default, resolver, indent);
|
|
118
120
|
}
|
|
119
|
-
/** Structural check — DtoMessage instances may come from a different module copy, so instanceof is unreliable. */
|
|
120
|
-
function isDtoMessage(v) {
|
|
121
|
-
if (typeof v !== 'object' || v === null)
|
|
122
|
-
return false;
|
|
123
|
-
return v.type === 'dto';
|
|
124
|
-
}
|
|
125
|
-
/** Structural check — a DtoField wraps a Field in a .field property and has no .type of its own. */
|
|
126
|
-
function isDtoField(v) {
|
|
127
|
-
if (typeof v !== 'object' || v === null)
|
|
128
|
-
return false;
|
|
129
|
-
return 'field' in v && !('type' in v);
|
|
130
|
-
}
|
|
131
121
|
function collectEnumImports(f, resolver, out) {
|
|
132
122
|
if (f.field.type === 'array') {
|
|
133
123
|
const items = f.field.items;
|
|
@@ -154,23 +144,15 @@ function collectEnumImports(f, resolver, out) {
|
|
|
154
144
|
}
|
|
155
145
|
/** Enum import collection over a plain Field (wire-format nested fields). */
|
|
156
146
|
function collectFieldEnumImports(field, resolver, out) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
for (const child of Object.values(field.properties))
|
|
163
|
-
collectFieldEnumImports(child, resolver, out);
|
|
164
|
-
return;
|
|
147
|
+
for (const jsName of collectEnumRefs(field)) {
|
|
148
|
+
const ref = resolver?.(jsName);
|
|
149
|
+
if (!ref)
|
|
150
|
+
throw new Error(`enum ${jsName}: no import ref — pass an EnumResolver`);
|
|
151
|
+
out.set(`${ref.from}#${ref.name}`, ref);
|
|
165
152
|
}
|
|
166
|
-
if (field.type === 'enum')
|
|
167
|
-
collectEnumRef(field, resolver, out);
|
|
168
153
|
}
|
|
169
154
|
function collectEnumRef(field, resolver, out) {
|
|
170
|
-
|
|
171
|
-
if (!ref)
|
|
172
|
-
throw new Error(`enum field ${field.name}: no import ref for ${field.enum.jsName} — pass an EnumResolver`);
|
|
173
|
-
out.set(`${ref.from}#${ref.name}`, ref);
|
|
155
|
+
collectFieldEnumImports(field, resolver, out);
|
|
174
156
|
}
|
|
175
157
|
/** Collect all imports needed to render a DTO: include() bases + enum references. */
|
|
176
158
|
export function collectDtoImports(schema, resolver, out) {
|
|
@@ -216,13 +198,3 @@ function renderBase(base) {
|
|
|
216
198
|
const args = base.args.map((a) => (typeof a === 'string' ? a : a.name));
|
|
217
199
|
return `${base.name}(${args.join(', ')})`;
|
|
218
200
|
}
|
|
219
|
-
/** Render one third-party method message export (const only — pair with
|
|
220
|
-
* renderDtoTypeExport for the Static type). */
|
|
221
|
-
export function renderThirdMethodExport(schema, resolver) {
|
|
222
|
-
return `export const ${schema.name} = ${renderFieldObject(schema.fields, 1, resolver)};`;
|
|
223
|
-
}
|
|
224
|
-
/** Collect all imports needed to render a third-party method message: enum references. */
|
|
225
|
-
export function collectThirdMethodImports(schema, resolver, out) {
|
|
226
|
-
for (const f of Object.values(schema.fields))
|
|
227
|
-
collectFieldEnumImports(f, resolver, out);
|
|
228
|
-
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Field, SchemaBase } from './dsl.js';
|
|
1
|
+
import type { CollectionSchemaBase, Field, SchemaBase } from './dsl.js';
|
|
2
2
|
import type { FrontAppSchema, ProjectApiSchema } from './project.js';
|
|
3
3
|
/** A utility method with a full signature. */
|
|
4
4
|
export interface UtilsMethodSchema extends SchemaBase {
|
|
@@ -13,7 +13,7 @@ export interface UtilsMethodSchema extends SchemaBase {
|
|
|
13
13
|
/** Method input for defineUtils: type/schema/name are set by the builder. */
|
|
14
14
|
export type UtilsMethodDef = Omit<UtilsMethodSchema, 'type' | 'schema' | 'name'>;
|
|
15
15
|
/** A base utility module (e.g. DateTimeUtils). */
|
|
16
|
-
export interface UtilsSchema extends
|
|
16
|
+
export interface UtilsSchema extends CollectionSchemaBase {
|
|
17
17
|
type: 'utils';
|
|
18
18
|
/** Backend binding — the api module this utils belongs to (shared instance
|
|
19
19
|
* from project.config.ts apis). With `app` it serves that frontend
|