@pylonts/dsl 1.0.2 → 1.0.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/dictionary.d.ts +6 -1
- package/dist/dictionary.js +6 -1
- package/dist/dsl.d.ts +28 -15
- package/dist/dsl.js +44 -1
- package/dist/dto.d.ts +36 -21
- package/dist/dto.js +92 -34
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mermaid-driver.js +7 -3
- package/dist/typebox-driver.d.ts +6 -0
- package/dist/typebox-driver.js +56 -20
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +8 -0
- package/docs/dictionary.md +14 -3
- package/docs/dto.md +25 -12
- package/docs/table.md +12 -3
- package/package.json +1 -1
- package/src/dictionary.ts +6 -1
- package/src/dsl.ts +50 -19
- package/src/dto.ts +93 -48
- package/src/index.ts +1 -0
- package/src/mermaid-driver.ts +6 -3
- package/src/typebox-driver.ts +63 -20
- package/src/utils.ts +6 -0
package/src/typebox-driver.ts
CHANGED
|
@@ -17,24 +17,37 @@ function renderString(s: string): string {
|
|
|
17
17
|
return `'${s.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function
|
|
20
|
+
function renderDefault(v: unknown): string {
|
|
21
|
+
if (typeof v === 'string') return renderString(v);
|
|
22
|
+
return JSON.stringify(v);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderBasic(
|
|
26
|
+
field: Field,
|
|
27
|
+
pattern: string | undefined,
|
|
28
|
+
defaultValue: unknown,
|
|
29
|
+
resolver: EnumResolver | undefined,
|
|
30
|
+
): string {
|
|
21
31
|
if (pattern !== undefined && field.type !== 'string') {
|
|
22
32
|
throw new Error(`pattern is only supported on string fields, got ${field.type} (${field.name})`);
|
|
23
33
|
}
|
|
34
|
+
const def = defaultValue !== undefined ? `default: ${renderDefault(defaultValue)}` : undefined;
|
|
24
35
|
switch (field.type) {
|
|
25
36
|
case 'string': {
|
|
26
37
|
const opts: string[] = [];
|
|
27
38
|
if (field.minLength !== undefined) opts.push(`minLength: ${field.minLength}`);
|
|
28
39
|
if (field.maxLength !== undefined) opts.push(`maxLength: ${field.maxLength}`);
|
|
29
40
|
if (pattern !== undefined) opts.push(`pattern: ${renderString(pattern)}`);
|
|
41
|
+
if (def !== undefined) opts.push(def);
|
|
30
42
|
return opts.length > 0 ? `Type.String({ ${opts.join(', ')} })` : 'Type.String()';
|
|
31
43
|
}
|
|
32
44
|
case 'text':
|
|
33
|
-
return 'Type.String()';
|
|
45
|
+
return def !== undefined ? `Type.String({ ${def} })` : 'Type.String()';
|
|
34
46
|
case 'integer': {
|
|
35
47
|
const opts: string[] = [];
|
|
36
48
|
if (field.min !== undefined) opts.push(`minimum: ${field.min}`);
|
|
37
49
|
if (field.max !== undefined) opts.push(`maximum: ${field.max}`);
|
|
50
|
+
if (def !== undefined) opts.push(def);
|
|
38
51
|
return opts.length > 0 ? `Type.Integer({ ${opts.join(', ')} })` : 'Type.Integer()';
|
|
39
52
|
}
|
|
40
53
|
case 'bigint':
|
|
@@ -44,14 +57,23 @@ function renderBasic(field: Field, pattern: string | undefined, resolver: EnumRe
|
|
|
44
57
|
case 'datetime':
|
|
45
58
|
// Transmitted as string over HTTP: bigint/decimal keep full precision,
|
|
46
59
|
// date/time serialize to string.
|
|
47
|
-
return 'Type.String()';
|
|
60
|
+
return def !== undefined ? `Type.String({ ${def} })` : 'Type.String()';
|
|
48
61
|
case 'boolean':
|
|
49
|
-
return 'Type.Boolean()';
|
|
62
|
+
return def !== undefined ? `Type.Boolean({ ${def} })` : 'Type.Boolean()';
|
|
50
63
|
case 'json':
|
|
51
|
-
return 'Type.Unknown()';
|
|
64
|
+
return def !== undefined ? `Type.Unknown({ ${def} })` : 'Type.Unknown()';
|
|
52
65
|
case 'enum': {
|
|
53
66
|
const ref = resolver?.(field.enum.jsName);
|
|
54
67
|
if (!ref) throw new Error(`enum field ${field.name}: no import ref for ${field.enum.jsName} — pass an EnumResolver`);
|
|
68
|
+
if (def !== undefined) {
|
|
69
|
+
const member = field.enum.values.find((v) => v.value === defaultValue);
|
|
70
|
+
if (!member) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`enum field ${field.name}: default ${renderDefault(defaultValue)} is not a member of ${field.enum.jsName}`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return `Type.Enum(${ref.name}, { default: ${ref.name}.${member.symbol} })`;
|
|
76
|
+
}
|
|
55
77
|
return `Type.Enum(${ref.name})`;
|
|
56
78
|
}
|
|
57
79
|
default:
|
|
@@ -79,7 +101,10 @@ function renderValue(f: DtoField, indent: number, resolver: EnumResolver | undef
|
|
|
79
101
|
return renderObject(f.properties(), indent + 1, resolver);
|
|
80
102
|
}
|
|
81
103
|
// DtoField only wraps a database Field; array/object defs live in the subclasses.
|
|
82
|
-
|
|
104
|
+
// DTO-level default wins over the DB field default; the DB default (string)
|
|
105
|
+
// is used as a fallback so from() picks carry it into the API contract.
|
|
106
|
+
const defaultValue = f.default !== undefined ? f.default : f.field.default;
|
|
107
|
+
return renderBasic(f.field as Field, f.pattern, defaultValue, resolver);
|
|
83
108
|
}
|
|
84
109
|
|
|
85
110
|
function collectEnumImports(
|
|
@@ -102,14 +127,39 @@ function collectEnumImports(
|
|
|
102
127
|
}
|
|
103
128
|
}
|
|
104
129
|
|
|
130
|
+
/** Collect all imports needed to render a DTO: include() bases + enum references. */
|
|
131
|
+
export function collectDtoImports(
|
|
132
|
+
schema: DtoMessage,
|
|
133
|
+
resolver: EnumResolver | undefined,
|
|
134
|
+
out: Map<string, ImportRef>,
|
|
135
|
+
): void {
|
|
136
|
+
for (const base of schema.bases ?? []) out.set(`${base.from}#${base.name}`, base);
|
|
137
|
+
for (const f of Object.values(schema.fields)) collectEnumImports(f, resolver, out);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Render one DTO export (const + type) — no file header, for file-level generation. */
|
|
141
|
+
export function renderDtoExport(schema: DtoMessage, resolver: EnumResolver | undefined): string {
|
|
142
|
+
const object = renderObject(schema.fields, 1, resolver);
|
|
143
|
+
const bases = schema.bases ?? [];
|
|
144
|
+
const body =
|
|
145
|
+
bases.length > 0
|
|
146
|
+
? `Type.Intersect([${bases.map(renderBase).join(', ')}, ${object}])`
|
|
147
|
+
: object;
|
|
148
|
+
return `export const ${schema.name} = ${body};`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Render the Static type export for a DTO. */
|
|
152
|
+
export function renderDtoTypeExport(name: string): string {
|
|
153
|
+
return `export type ${name} = Static<typeof ${name}>;`;
|
|
154
|
+
}
|
|
155
|
+
|
|
105
156
|
export function renderDtoMessage(
|
|
106
157
|
schema: DtoMessage,
|
|
107
158
|
options: { resolver?: EnumResolver; source?: string } = {},
|
|
108
159
|
): string {
|
|
109
160
|
const { resolver, source } = options;
|
|
110
161
|
const imports = new Map<string, ImportRef>();
|
|
111
|
-
|
|
112
|
-
for (const f of Object.values(schema.fields)) collectEnumImports(f, resolver, imports);
|
|
162
|
+
collectDtoImports(schema, resolver, imports);
|
|
113
163
|
|
|
114
164
|
const header = [
|
|
115
165
|
'// AUTO-GENERATED by typebox-driver — DO NOT EDIT',
|
|
@@ -118,24 +168,17 @@ export function renderDtoMessage(
|
|
|
118
168
|
...[...imports.values()].map((r) => `import { ${r.name} } from '${r.from}';`),
|
|
119
169
|
];
|
|
120
170
|
|
|
121
|
-
const object = renderObject(schema.fields, 1, resolver);
|
|
122
|
-
const bases = schema.bases ?? [];
|
|
123
|
-
const body =
|
|
124
|
-
bases.length > 0
|
|
125
|
-
? `Type.Intersect([${bases.map(renderBase).join(', ')}, ${object}])`
|
|
126
|
-
: object;
|
|
127
|
-
|
|
128
171
|
return [
|
|
129
172
|
...header,
|
|
130
173
|
'',
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
renderDtoExport(schema, resolver),
|
|
175
|
+
renderDtoTypeExport(schema.name),
|
|
133
176
|
'',
|
|
134
177
|
].join('\n');
|
|
135
178
|
}
|
|
136
179
|
|
|
137
180
|
function renderBase(base: ImportRef): string {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
181
|
+
if (base.args === undefined || base.args.length === 0) return base.name;
|
|
182
|
+
const args = base.args.map((a) => (typeof a === 'string' ? a : a.name));
|
|
183
|
+
return `${base.name}(${args.join(', ')})`;
|
|
141
184
|
}
|
package/src/utils.ts
ADDED