rads-db 3.0.84 → 3.1.0
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/config.cjs +636 -0
- package/dist/config.d.ts +23 -0
- package/{integrations/lib.mjs → dist/config.mjs} +45 -3
- package/dist/index.d.ts +4 -437
- package/dist/types-7e792d1f.d.ts +449 -0
- package/integrations/cli.cjs +1 -16
- package/integrations/cli.mjs +1 -16
- package/integrations/node.cjs +54 -62
- package/integrations/node.d.ts +5 -4
- package/integrations/node.mjs +56 -52
- package/package.json +8 -2
- package/integrations/lib.cjs +0 -603
- package/integrations/lib.d.ts +0 -1
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const typescript = require('typescript');
|
|
6
|
+
const _ = require('lodash');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
9
|
+
|
|
10
|
+
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
11
|
+
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
12
|
+
const ___default = /*#__PURE__*/_interopDefaultCompat(_);
|
|
13
|
+
|
|
14
|
+
const supportedPrimitiveTypes = ["string", "number", "boolean", "Record<string, string>", "Record<string, any>"];
|
|
15
|
+
function parseSchema(typescriptFiles) {
|
|
16
|
+
const typeNodesMap = getTypeNodesMap(typescriptFiles);
|
|
17
|
+
const schema = getSchema(typeNodesMap);
|
|
18
|
+
resolveIsExtending(schema);
|
|
19
|
+
fillComputedDefinitionsForType(schema);
|
|
20
|
+
verifyDefaultValueTypes(schema, typeNodesMap);
|
|
21
|
+
verifyRelationFields(schema);
|
|
22
|
+
return schema;
|
|
23
|
+
}
|
|
24
|
+
function resolveIsExtending(schema) {
|
|
25
|
+
for (const key in schema) {
|
|
26
|
+
const isExtendingType = schema[key].isExtending;
|
|
27
|
+
if (!isExtendingType)
|
|
28
|
+
continue;
|
|
29
|
+
if (!schema[isExtendingType])
|
|
30
|
+
throw new Error(`Unknown type: "${isExtendingType}"`);
|
|
31
|
+
schema[key].fields = { ...schema[isExtendingType].fields, ...schema[key].fields };
|
|
32
|
+
schema[key].decorators = { ...schema[isExtendingType].decorators, ...schema[key].decorators };
|
|
33
|
+
if (schema[key].decorators.entity && !schema[key].fields?.id) {
|
|
34
|
+
throw new Error(`Entity "${key}" must have an id`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getSchema(typeNodesMap) {
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const key in typeNodesMap) {
|
|
41
|
+
const { node, name } = typeNodesMap[key];
|
|
42
|
+
if (![typescript.SyntaxKind.ClassDeclaration, typescript.SyntaxKind.TypeAliasDeclaration].includes(node.kind)) {
|
|
43
|
+
throw new Error(`Unexpected type kind - "${name}"`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const key in typeNodesMap) {
|
|
47
|
+
if (result[key])
|
|
48
|
+
continue;
|
|
49
|
+
const { node, sourceFile, name } = typeNodesMap[key];
|
|
50
|
+
if (node.kind !== typescript.SyntaxKind.ClassDeclaration)
|
|
51
|
+
continue;
|
|
52
|
+
const parsedClass = parseClassDeclaration(node, name, { typeNodesMap, sourceFile, result });
|
|
53
|
+
result[key] = parsedClass;
|
|
54
|
+
}
|
|
55
|
+
for (const key in typeNodesMap) {
|
|
56
|
+
if (result[key])
|
|
57
|
+
continue;
|
|
58
|
+
const { node, sourceFile, name } = typeNodesMap[key];
|
|
59
|
+
if (node.kind !== typescript.SyntaxKind.TypeAliasDeclaration)
|
|
60
|
+
continue;
|
|
61
|
+
const parsedClass = parseTypeAliasDeclaration(node, name, { typeNodesMap, sourceFile, result });
|
|
62
|
+
result[key] = parsedClass;
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function getTypeNodesMap(typescriptFiles) {
|
|
67
|
+
const typeNodesMap = {};
|
|
68
|
+
for (const key in typescriptFiles) {
|
|
69
|
+
const text = typescriptFiles[key];
|
|
70
|
+
const sourceFile = typescript.createSourceFile(`${key}.ts`, text, typescript.ScriptTarget.Latest);
|
|
71
|
+
const classDeclarations = getClassDeclarations(sourceFile);
|
|
72
|
+
for (const cd of classDeclarations) {
|
|
73
|
+
const nameNode = cd.name;
|
|
74
|
+
if (!nameNode || nameNode.kind !== typescript.SyntaxKind.Identifier)
|
|
75
|
+
throw new Error("Cannot detect class name");
|
|
76
|
+
const name = nameNode.text;
|
|
77
|
+
typeNodesMap[name] = { name, node: cd, sourceFile };
|
|
78
|
+
}
|
|
79
|
+
if (!typeNodesMap[key]) {
|
|
80
|
+
throw new Error(`File ${key}.ts must contain class declaration with name "${key}"`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return typeNodesMap;
|
|
84
|
+
}
|
|
85
|
+
function verifyDefaultValueTypes(schema, typeNodesMap) {
|
|
86
|
+
for (const key in schema) {
|
|
87
|
+
const type = schema[key];
|
|
88
|
+
const fields = type.fields;
|
|
89
|
+
if (!fields)
|
|
90
|
+
continue;
|
|
91
|
+
for (const fName in fields) {
|
|
92
|
+
const field = fields[fName];
|
|
93
|
+
if (field.defaultValue) {
|
|
94
|
+
verifyDefaultValueType(field, {
|
|
95
|
+
typeNodesMap,
|
|
96
|
+
result: schema,
|
|
97
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
for (const fName2 in fields) {
|
|
101
|
+
const field2 = fields[fName2];
|
|
102
|
+
if (field2.defaultValueCopyFrom) {
|
|
103
|
+
const sourceField = fields[field2.defaultValueCopyFrom];
|
|
104
|
+
if (!sourceField)
|
|
105
|
+
throw new Error(`Cannot find field ${key}.${field2.defaultValueCopyFrom}"`);
|
|
106
|
+
verifyDefaultValueTypeCopyFrom(field2, sourceField, {
|
|
107
|
+
typeNodesMap,
|
|
108
|
+
result: schema,
|
|
109
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function fillComputedDefinitionsForType(result) {
|
|
117
|
+
for (const key in result) {
|
|
118
|
+
const type = result[key];
|
|
119
|
+
const fields = type.fields;
|
|
120
|
+
if (!fields)
|
|
121
|
+
continue;
|
|
122
|
+
const precomputedFields = ___default.values(fields).filter((f) => !!f.decorators?.precomputed).sort((f1, f2) => getOrder(f1) - getOrder(f2)).map((f) => f.name);
|
|
123
|
+
const computedFields = ___default.values(fields).filter((f) => !!f.decorators?.computed).sort((f1, f2) => getOrder(f1) - getOrder(f2)).map((f) => f.name);
|
|
124
|
+
const nestedTypeFields = ___default.values(fields).filter((f) => result[f.type] && result[f.type].fields && !result[f.type].decorators.entity).map((f) => f.name);
|
|
125
|
+
const keepHistoryFields = ___default.values(fields).filter((f) => !!f.decorators?.keepHistory).map((f) => f.name);
|
|
126
|
+
if (precomputedFields.length)
|
|
127
|
+
type.precomputedFields = precomputedFields;
|
|
128
|
+
if (computedFields.length)
|
|
129
|
+
type.computedFields = computedFields;
|
|
130
|
+
if (nestedTypeFields.length)
|
|
131
|
+
type.nestedTypeFields = nestedTypeFields;
|
|
132
|
+
if (keepHistoryFields.length)
|
|
133
|
+
type.keepHistoryFields = keepHistoryFields;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function getOrder(f) {
|
|
137
|
+
const decorator = f.decorators.computed || f.decorators.precomputed;
|
|
138
|
+
const defaultOrders = {
|
|
139
|
+
createdAt: -1e5,
|
|
140
|
+
updatedAt: -9e4
|
|
141
|
+
};
|
|
142
|
+
return decorator.order ?? defaultOrders[decorator.preset] ?? 0;
|
|
143
|
+
}
|
|
144
|
+
function parseClassOrTypeDeclaration(typeDeclaration, typeName, ctx2) {
|
|
145
|
+
if (typeDeclaration.kind === typescript.SyntaxKind.ClassDeclaration)
|
|
146
|
+
return parseClassDeclaration(typeDeclaration, typeName, ctx2);
|
|
147
|
+
if (typeDeclaration.kind === typescript.SyntaxKind.TypeAliasDeclaration)
|
|
148
|
+
return parseTypeAliasDeclaration(typeDeclaration, typeName, ctx2);
|
|
149
|
+
throw new Error(`Unexpected type definition - ${typeName}`);
|
|
150
|
+
}
|
|
151
|
+
function parseClassDeclaration(typeDeclaration, typeName, ctx2) {
|
|
152
|
+
if (ctx2.result[typeName])
|
|
153
|
+
return ctx2.result[typeName];
|
|
154
|
+
const { modifiers } = typeDeclaration;
|
|
155
|
+
const nameNode = typeDeclaration.name;
|
|
156
|
+
if (!nameNode || nameNode.kind !== typescript.SyntaxKind.Identifier)
|
|
157
|
+
throw new Error("Cannot detect class name");
|
|
158
|
+
const name = nameNode.text;
|
|
159
|
+
const comment = getCommentFromJsdocNode(typeDeclaration.jsDoc);
|
|
160
|
+
const decorators = parseDecorators(modifiers, ctx2);
|
|
161
|
+
const classDeclaration = typeDeclaration;
|
|
162
|
+
const { members, heritageClauses } = classDeclaration;
|
|
163
|
+
const isExtendingExpr = heritageClauses?.[0]?.types?.[0]?.expression;
|
|
164
|
+
const isExtending = isExtendingExpr?.text;
|
|
165
|
+
for (const m of members) {
|
|
166
|
+
if (m.kind !== typescript.SyntaxKind.PropertyDeclaration) {
|
|
167
|
+
throw new Error(`Unexpected class member - only properties are allowed("${m.getText(ctx2.sourceFile)}")`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const fields = {};
|
|
171
|
+
for (const m of members) {
|
|
172
|
+
const field = parseClassMember(m, fields, name, ctx2);
|
|
173
|
+
fields[field.name] = field;
|
|
174
|
+
}
|
|
175
|
+
const handle = ___default.lowerFirst(name);
|
|
176
|
+
const handlePlural = `${handle}/list`;
|
|
177
|
+
const result = {
|
|
178
|
+
name,
|
|
179
|
+
handle,
|
|
180
|
+
handlePlural,
|
|
181
|
+
decorators,
|
|
182
|
+
fields,
|
|
183
|
+
isExtending,
|
|
184
|
+
comment,
|
|
185
|
+
sourceFile: ctx2.sourceFile.fileName
|
|
186
|
+
};
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
function parseTypeAliasDeclaration(typeDeclaration, typeName, ctx2) {
|
|
190
|
+
if (ctx2.result[typeName])
|
|
191
|
+
return ctx2.result[typeName];
|
|
192
|
+
const nameNode = typeDeclaration.name;
|
|
193
|
+
if (!nameNode || nameNode.kind !== typescript.SyntaxKind.Identifier)
|
|
194
|
+
throw new Error("Cannot detect class name");
|
|
195
|
+
const name = nameNode.text;
|
|
196
|
+
const comment = getCommentFromJsdocNode(typeDeclaration.jsDoc);
|
|
197
|
+
const typeAliasDeclaration = typeDeclaration;
|
|
198
|
+
const typeAliasType = typeAliasDeclaration.type;
|
|
199
|
+
if (typeAliasType.kind === typescript.SyntaxKind.UnionType) {
|
|
200
|
+
const typeAliasValue = typeAliasDeclaration.type;
|
|
201
|
+
const enumValues = getEnumValues(typeAliasValue, typeDeclaration, ctx2);
|
|
202
|
+
return { name, enumValues, comment, decorators: {}, sourceFile: ctx2.sourceFile.fileName };
|
|
203
|
+
}
|
|
204
|
+
if (typeAliasType.kind === typescript.SyntaxKind.TypeOperator && typeAliasType.operator === typescript.SyntaxKind.KeyOfKeyword) {
|
|
205
|
+
const enumValues = getEnumValuesFromKeyOf(typeAliasType, ctx2);
|
|
206
|
+
return { name, enumValues, comment, decorators: {}, sourceFile: ctx2.sourceFile.fileName };
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`Unexpected type definition - ${typeDeclaration.getText(ctx2.sourceFile)}. Did you mean 'class'?`);
|
|
209
|
+
}
|
|
210
|
+
function parseDecorators(modifiers, ctx2) {
|
|
211
|
+
if (!modifiers)
|
|
212
|
+
return {};
|
|
213
|
+
const decoratorNodes = modifiers?.filter((x) => x.kind === typescript.SyntaxKind.Decorator) || [];
|
|
214
|
+
const decoratorsArray = decoratorNodes?.map((x) => parseDecorator(x, ctx2)) || [];
|
|
215
|
+
const decorators = {};
|
|
216
|
+
for (const d of decoratorsArray) {
|
|
217
|
+
decorators[d.name] = d.args;
|
|
218
|
+
}
|
|
219
|
+
return decorators;
|
|
220
|
+
}
|
|
221
|
+
function getEnumValues(node, parentNode, ctx2) {
|
|
222
|
+
const enumValuesArray = node.types.map((node2) => {
|
|
223
|
+
if (node2.kind !== typescript.SyntaxKind.LiteralType) {
|
|
224
|
+
throw new Error(`Unexpected type definition - ${parentNode.getText(ctx2.sourceFile)}`);
|
|
225
|
+
}
|
|
226
|
+
const { literal } = node2;
|
|
227
|
+
if (literal.kind !== typescript.SyntaxKind.StringLiteral)
|
|
228
|
+
throw new Error(`Unexpected type definition - ${parentNode.getText(ctx2.sourceFile)}`);
|
|
229
|
+
return { name: literal.text };
|
|
230
|
+
});
|
|
231
|
+
return ___default.keyBy(enumValuesArray, "name");
|
|
232
|
+
}
|
|
233
|
+
function parseClassMember(node, parentFields, parentName, ctx2) {
|
|
234
|
+
const name = node.name.getText(ctx2.sourceFile);
|
|
235
|
+
const { defaultValue: defaultValue2, defaultValueCopyFrom, defaultValueClass } = parseDefaultValueExpression(node.initializer, ctx2);
|
|
236
|
+
const isRequired = !node.questionToken;
|
|
237
|
+
const comment = getCommentFromJsdocNode(node.jsDoc);
|
|
238
|
+
const decorators = parseDecorators(node.modifiers, ctx2);
|
|
239
|
+
let defaultValueType = defaultValueClass || getPrimitiveTypeFromDefaultValue(defaultValue2);
|
|
240
|
+
if (defaultValueCopyFrom) {
|
|
241
|
+
const parentField = parentFields[defaultValueCopyFrom];
|
|
242
|
+
if (!parentField)
|
|
243
|
+
throw new Error(`Cannot find field ${parentName}.${defaultValueCopyFrom}"`);
|
|
244
|
+
defaultValueType = parentField.type;
|
|
245
|
+
}
|
|
246
|
+
const parsedType = parseFieldType(ctx2, parentName, name, node, defaultValueType);
|
|
247
|
+
const result = {
|
|
248
|
+
...parsedType,
|
|
249
|
+
defaultValue: defaultValue2,
|
|
250
|
+
defaultValueCopyFrom,
|
|
251
|
+
name,
|
|
252
|
+
isRequired,
|
|
253
|
+
comment
|
|
254
|
+
};
|
|
255
|
+
if (!___default.isEmpty(decorators))
|
|
256
|
+
result.decorators = decorators;
|
|
257
|
+
if (defaultValueClass)
|
|
258
|
+
result.defaultValueClass = defaultValueClass;
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
function getCommentFromJsdocNode(jsDoc) {
|
|
262
|
+
const comment = jsDoc?.[0]?.comment;
|
|
263
|
+
const tags = jsDoc?.[0]?.tags;
|
|
264
|
+
const tagsStr = tags?.map((tag) => [`@${tag.tagName.text}`, tag.comment || ""].filter((x) => x).join(" "))?.join("\n") || "";
|
|
265
|
+
return [comment, tagsStr].filter((x) => x).join("\n") || void 0;
|
|
266
|
+
}
|
|
267
|
+
function getPrimitiveTypeFromDefaultValue(value) {
|
|
268
|
+
if (___default.isString(value))
|
|
269
|
+
return "string";
|
|
270
|
+
if (___default.isNumber(value))
|
|
271
|
+
return "number";
|
|
272
|
+
if (___default.isBoolean(value))
|
|
273
|
+
return "boolean";
|
|
274
|
+
if (___default.isArray(value))
|
|
275
|
+
return "array";
|
|
276
|
+
if (___default.isObject(value))
|
|
277
|
+
return "object";
|
|
278
|
+
return void 0;
|
|
279
|
+
}
|
|
280
|
+
function parseFieldType(ctx2, parentName, fieldName, node, defaultValueType) {
|
|
281
|
+
const parsedType = {
|
|
282
|
+
isArray: false,
|
|
283
|
+
isRelation: false,
|
|
284
|
+
isChange: false,
|
|
285
|
+
relationDenormFields: void 0,
|
|
286
|
+
type: void 0,
|
|
287
|
+
nodeType: node.type,
|
|
288
|
+
node
|
|
289
|
+
};
|
|
290
|
+
parseFieldTypeArray(parsedType, parentName);
|
|
291
|
+
parseFieldTypeRelation(parsedType, parentName, ctx2);
|
|
292
|
+
parseFieldTypeInverseRelation(parsedType, parentName, ctx2);
|
|
293
|
+
parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx2);
|
|
294
|
+
parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx2);
|
|
295
|
+
parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx2);
|
|
296
|
+
parsedType.type = parsedType.type ?? parsedType.nodeType?.getText(ctx2.sourceFile) ?? defaultValueType;
|
|
297
|
+
if (!parsedType.type)
|
|
298
|
+
throw new Error(`Cannot detect property type: '${node.getText(ctx2.sourceFile)}'`);
|
|
299
|
+
if (parsedType.type.startsWith("Change<")) {
|
|
300
|
+
parsedType.type = parsedType.type.slice(7, -1);
|
|
301
|
+
if (!ctx2.typeNodesMap[parsedType.type])
|
|
302
|
+
throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
303
|
+
parsedType.isChange = true;
|
|
304
|
+
} else {
|
|
305
|
+
if (!supportedPrimitiveTypes.includes(parsedType.type) && !ctx2.typeNodesMap[parsedType.type] && !ctx2.result[parsedType.type]) {
|
|
306
|
+
throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
isArray: parsedType.isArray || void 0,
|
|
311
|
+
isRelation: parsedType.isRelation || void 0,
|
|
312
|
+
isInverseRelation: parsedType.isInverseRelation || void 0,
|
|
313
|
+
isChange: parsedType.isChange || void 0,
|
|
314
|
+
relationDenormFields: parsedType.relationDenormFields,
|
|
315
|
+
inverseRelationField: parsedType.inverseRelationField,
|
|
316
|
+
type: parsedType.type
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx2) {
|
|
320
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.TypeReference)
|
|
321
|
+
return;
|
|
322
|
+
const nt = parsedType.nodeType;
|
|
323
|
+
if (nt.typeName.getText(ctx2.sourceFile) !== "Record")
|
|
324
|
+
return;
|
|
325
|
+
if (nt.typeArguments?.length !== 2)
|
|
326
|
+
return;
|
|
327
|
+
const keyTypeName = nt.typeArguments[0].getText(ctx2.sourceFile);
|
|
328
|
+
const valueTypeName = nt.typeArguments[1].getText(ctx2.sourceFile);
|
|
329
|
+
const keyType = ctx2.typeNodesMap[keyTypeName];
|
|
330
|
+
if (!keyType)
|
|
331
|
+
return;
|
|
332
|
+
if (!ctx2.result[keyTypeName])
|
|
333
|
+
ctx2.result[keyTypeName] = parseClassOrTypeDeclaration(keyType.node, keyTypeName, ctx2);
|
|
334
|
+
const enumValues = ctx2.result[keyTypeName].enumValues;
|
|
335
|
+
if (!enumValues)
|
|
336
|
+
throw new Error(`Unexpected type - ${keyTypeName}`);
|
|
337
|
+
const newTypeName = `${parentName}_${___default.upperFirst(fieldName)}`;
|
|
338
|
+
const fieldsArray = ___default.values(enumValues).map((v) => ({ name: v.name, type: valueTypeName, isRequired: true }));
|
|
339
|
+
ctx2.result[newTypeName] = {
|
|
340
|
+
name: newTypeName,
|
|
341
|
+
decorators: {},
|
|
342
|
+
fields: ___default.keyBy(fieldsArray, "name"),
|
|
343
|
+
sourceFile: ctx2.sourceFile.fileName
|
|
344
|
+
};
|
|
345
|
+
parsedType.type = newTypeName;
|
|
346
|
+
}
|
|
347
|
+
function parseFieldTypeArray(parsedType, parentName) {
|
|
348
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.ArrayType)
|
|
349
|
+
return;
|
|
350
|
+
parsedType.isArray = true;
|
|
351
|
+
parsedType.nodeType = parsedType.nodeType.elementType;
|
|
352
|
+
if (parsedType.nodeType?.kind === typescript.SyntaxKind.ParenthesizedType) {
|
|
353
|
+
parsedType.nodeType = parsedType.nodeType.type;
|
|
354
|
+
}
|
|
355
|
+
if (parsedType.nodeType?.kind === typescript.SyntaxKind.ArrayType) {
|
|
356
|
+
throw new Error(`Nested arrays are not supported (${parentName})`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function parseFieldTypeRelation(parsedType, parentName, ctx2) {
|
|
360
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.TypeReference)
|
|
361
|
+
return;
|
|
362
|
+
const nt = parsedType.nodeType;
|
|
363
|
+
if (nt.typeName.getText(ctx2.sourceFile) === "Relation") {
|
|
364
|
+
if (!nt.typeArguments?.length)
|
|
365
|
+
throw new Error(`Missing type argument for Relation<>: '${parentName}'`);
|
|
366
|
+
parsedType.nodeType = nt.typeArguments[0];
|
|
367
|
+
parsedType.isRelation = true;
|
|
368
|
+
if (nt.typeArguments[1]) {
|
|
369
|
+
const ta = nt.typeArguments[1];
|
|
370
|
+
parsedType.relationDenormFields = getRelationDenormFields(ctx2, ta);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function parseFieldTypeInverseRelation(parsedType, parentName, ctx2) {
|
|
375
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.TypeReference)
|
|
376
|
+
return;
|
|
377
|
+
const nt = parsedType.nodeType;
|
|
378
|
+
if (nt.typeName.getText(ctx2.sourceFile) === "InverseRelation") {
|
|
379
|
+
if (!nt.typeArguments?.length)
|
|
380
|
+
throw new Error(`Missing type argument for Relation<>: '${parentName}'`);
|
|
381
|
+
parsedType.type = parseStringField(ctx2, nt.typeArguments[0]);
|
|
382
|
+
parsedType.isInverseRelation = true;
|
|
383
|
+
if (nt.typeArguments[1]) {
|
|
384
|
+
const taNode = nt.typeArguments[1];
|
|
385
|
+
parsedType.inverseRelationField = parseStringField(ctx2, taNode);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function parseStringField(ctx2, node) {
|
|
390
|
+
if (node.kind === typescript.SyntaxKind.LiteralType) {
|
|
391
|
+
const literal = node.literal;
|
|
392
|
+
if (literal.kind === typescript.SyntaxKind.StringLiteral) {
|
|
393
|
+
return literal.text;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
throw new Error(`Unexpected type - ${node.getText(ctx2.sourceFile)}`);
|
|
397
|
+
}
|
|
398
|
+
function parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx2) {
|
|
399
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.UnionType)
|
|
400
|
+
return;
|
|
401
|
+
const nt = parsedType.nodeType;
|
|
402
|
+
const enumValues = getEnumValues(nt, parsedType.node, ctx2);
|
|
403
|
+
const newTypeName = `${parentName}_${___default.upperFirst(fieldName)}`;
|
|
404
|
+
ctx2.result[newTypeName] = { name: newTypeName, decorators: {}, enumValues, sourceFile: ctx2.sourceFile.fileName };
|
|
405
|
+
parsedType.type = newTypeName;
|
|
406
|
+
}
|
|
407
|
+
function parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx2) {
|
|
408
|
+
if (parsedType.nodeType?.kind !== typescript.SyntaxKind.TypeOperator)
|
|
409
|
+
return;
|
|
410
|
+
const nt = parsedType.nodeType;
|
|
411
|
+
if (nt.operator !== typescript.SyntaxKind.KeyOfKeyword)
|
|
412
|
+
return;
|
|
413
|
+
const enumValues = getEnumValuesFromKeyOf(nt, ctx2);
|
|
414
|
+
const newTypeName = `${parentName}_${___default.upperFirst(fieldName)}`;
|
|
415
|
+
ctx2.result[newTypeName] = { name: newTypeName, decorators: {}, enumValues, sourceFile: ctx2.sourceFile.fileName };
|
|
416
|
+
parsedType.type = newTypeName;
|
|
417
|
+
}
|
|
418
|
+
function getEnumValuesFromKeyOf(nodeType, ctx2) {
|
|
419
|
+
if (nodeType.type.kind !== typescript.SyntaxKind.TypeReference) {
|
|
420
|
+
throw new Error(`Unexpected type - ${nodeType.type.getText(ctx2.sourceFile)}`);
|
|
421
|
+
}
|
|
422
|
+
const typeReferenceNode = nodeType.type;
|
|
423
|
+
if (typeReferenceNode.typeName.kind !== typescript.SyntaxKind.Identifier) {
|
|
424
|
+
throw new Error(`Unexpected type - ${typeReferenceNode.getText(ctx2.sourceFile)}`);
|
|
425
|
+
}
|
|
426
|
+
const typeName = typeReferenceNode.typeName.text;
|
|
427
|
+
const type = ctx2.typeNodesMap[typeName];
|
|
428
|
+
if (!type) {
|
|
429
|
+
throw new Error(`Unexpected type - ${typeName}`);
|
|
430
|
+
}
|
|
431
|
+
if (!ctx2.result[typeName])
|
|
432
|
+
ctx2.result[typeName] = parseClassOrTypeDeclaration(type.node, typeName, ctx2);
|
|
433
|
+
if (!ctx2.result[typeName].fields)
|
|
434
|
+
throw new Error(`Unexpected type - ${typeName}`);
|
|
435
|
+
return ___default.mapValues(ctx2.result[typeName].fields || {}, (v) => ({
|
|
436
|
+
name: v.name,
|
|
437
|
+
comment: v.comment,
|
|
438
|
+
decorators: v.decorators
|
|
439
|
+
}));
|
|
440
|
+
}
|
|
441
|
+
function getRelationDenormFields(ctx2, node) {
|
|
442
|
+
if (node.kind === typescript.SyntaxKind.LiteralType) {
|
|
443
|
+
const literal = node.literal;
|
|
444
|
+
if (literal.kind === typescript.SyntaxKind.StringLiteral) {
|
|
445
|
+
return [literal.text];
|
|
446
|
+
}
|
|
447
|
+
throw new Error(`Unexpected type - ${literal.getText(ctx2.sourceFile)}`);
|
|
448
|
+
}
|
|
449
|
+
if (node.kind === typescript.SyntaxKind.UnionType) {
|
|
450
|
+
const union = node;
|
|
451
|
+
return union.types.flatMap((t) => getRelationDenormFields(ctx2, t));
|
|
452
|
+
}
|
|
453
|
+
throw new Error(`Unexpected type - ${node.getText(ctx2.sourceFile)}`);
|
|
454
|
+
}
|
|
455
|
+
function verifyDefaultValueType(field, ctx2) {
|
|
456
|
+
const { isArray, defaultValue: defaultValue2, type } = field;
|
|
457
|
+
if (isArray) {
|
|
458
|
+
if (!___default.isArray(defaultValue2)) {
|
|
459
|
+
throw new TypeError(`Default value type is different from field type: '${type}'`);
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
if (supportedPrimitiveTypes.includes(type) && getPrimitiveTypeFromDefaultValue(defaultValue2) !== type) {
|
|
463
|
+
throw new Error(`Default value type is different from field type: '${type}'`);
|
|
464
|
+
}
|
|
465
|
+
if (!ctx2.result[type] && ctx2.typeNodesMap[type])
|
|
466
|
+
ctx2.result[type] = parseClassOrTypeDeclaration(ctx2.typeNodesMap[type].node, type, ctx2);
|
|
467
|
+
const enumValues = ctx2.result[type]?.enumValues;
|
|
468
|
+
if (enumValues && !enumValues[defaultValue2]) {
|
|
469
|
+
const enumValuesStr = ___default.keys(enumValues).map((x) => `'x'`).join(", ");
|
|
470
|
+
throw new Error(`Default value must be one of: ${enumValuesStr}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function verifyDefaultValueTypeCopyFrom(field, sourceField, ctx2) {
|
|
475
|
+
const { isArray, type } = field;
|
|
476
|
+
if (isArray) {
|
|
477
|
+
if (!sourceField.isArray) {
|
|
478
|
+
throw new TypeError(`Default value type is not an array: '${field.name}'`);
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
if (sourceField.type !== type) {
|
|
482
|
+
throw new Error(`Default value type is different from field type: '${field.name}'`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function parseDecorator(decoratorNode, ctx2) {
|
|
487
|
+
const expr2 = decoratorNode.expression;
|
|
488
|
+
if (expr2.kind !== typescript.SyntaxKind.CallExpression)
|
|
489
|
+
throw new Error(`Unexpected decorator format: "${expr2.getText(ctx2.sourceFile)}"`);
|
|
490
|
+
const nameNode = expr2.expression;
|
|
491
|
+
if (nameNode.kind !== typescript.SyntaxKind.Identifier)
|
|
492
|
+
throw new Error(`Unexpected decorator format: "${nameNode.getText(ctx2.sourceFile)}"`);
|
|
493
|
+
return { name: nameNode.text, args: parseDecoratorArguments(expr2, ctx2) };
|
|
494
|
+
}
|
|
495
|
+
function parseDecoratorArguments(expr2, ctx2) {
|
|
496
|
+
const args = expr2.arguments;
|
|
497
|
+
if (args.length === 0)
|
|
498
|
+
return {};
|
|
499
|
+
if (args.length > 1)
|
|
500
|
+
throw new Error(`Too many arguments - one expected: "${expr2.getText(ctx2.sourceFile)}"`);
|
|
501
|
+
const arg = args[0];
|
|
502
|
+
return parseLiteralNode(arg, ctx2) ?? {};
|
|
503
|
+
}
|
|
504
|
+
function parseDefaultValueExpression(expr2, ctx2) {
|
|
505
|
+
if (!expr2)
|
|
506
|
+
return {};
|
|
507
|
+
if (expr2.kind === typescript.SyntaxKind.NewExpression) {
|
|
508
|
+
const identifier = expr2.expression;
|
|
509
|
+
const type = identifier?.text;
|
|
510
|
+
if (type) {
|
|
511
|
+
return { defaultValueClass: type, defaultValue: {} };
|
|
512
|
+
}
|
|
513
|
+
} else if (expr2.kind === typescript.SyntaxKind.PropertyAccessExpression && expr2.expression.kind === typescript.SyntaxKind.ThisKeyword) {
|
|
514
|
+
return { defaultValueCopyFrom: expr2.name?.text };
|
|
515
|
+
}
|
|
516
|
+
return { defaultValue: parseLiteralNode(expr2, ctx2) };
|
|
517
|
+
}
|
|
518
|
+
function parseLiteralNode(expr, ctx) {
|
|
519
|
+
if (!expr)
|
|
520
|
+
return void 0;
|
|
521
|
+
if (expr.kind === typescript.SyntaxKind.StringLiteral)
|
|
522
|
+
return expr.text;
|
|
523
|
+
if (expr.kind === typescript.SyntaxKind.FalseKeyword)
|
|
524
|
+
return false;
|
|
525
|
+
if (expr.kind === typescript.SyntaxKind.TrueKeyword)
|
|
526
|
+
return true;
|
|
527
|
+
if (expr.kind === typescript.SyntaxKind.NumericLiteral)
|
|
528
|
+
return Number.parseFloat(expr.text);
|
|
529
|
+
if (expr.kind === typescript.SyntaxKind.ObjectLiteralExpression)
|
|
530
|
+
return parseObjectLiteral(expr, ctx);
|
|
531
|
+
if (expr.kind === typescript.SyntaxKind.ArrayLiteralExpression) {
|
|
532
|
+
const defaultValueStr = expr.getText(ctx.sourceFile);
|
|
533
|
+
let defaultValue;
|
|
534
|
+
try {
|
|
535
|
+
defaultValue = eval(defaultValueStr);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
throw new Error("Value must be valid array");
|
|
538
|
+
}
|
|
539
|
+
if (!___default.isArray(defaultValue))
|
|
540
|
+
throw new Error("Value must be valid array");
|
|
541
|
+
return defaultValue;
|
|
542
|
+
}
|
|
543
|
+
throw new Error(`Unexpected property expression: "${expr.getText(ctx.sourceFile)}"`);
|
|
544
|
+
}
|
|
545
|
+
function parseObjectLiteral(arg, ctx2) {
|
|
546
|
+
const result = {};
|
|
547
|
+
for (const p of arg.properties) {
|
|
548
|
+
if (!p.name || ![typescript.SyntaxKind.Identifier, typescript.SyntaxKind.StringLiteral].includes(p.name.kind))
|
|
549
|
+
throw new Error(`Unexpected property name: "${p.getText(ctx2.sourceFile)}"`);
|
|
550
|
+
const nameNode = p.name;
|
|
551
|
+
const name = nameNode.text;
|
|
552
|
+
if (p.kind !== typescript.SyntaxKind.PropertyAssignment)
|
|
553
|
+
throw new Error(`Unexpected property value: "${p.getText(ctx2.sourceFile)}"`);
|
|
554
|
+
const p2 = p;
|
|
555
|
+
const valueExpression = p2.initializer;
|
|
556
|
+
const value = parseLiteralNode(valueExpression, ctx2);
|
|
557
|
+
result[name] = value;
|
|
558
|
+
}
|
|
559
|
+
return result;
|
|
560
|
+
}
|
|
561
|
+
function getClassDeclarations(sourceFile) {
|
|
562
|
+
const children = sourceFile.getChildren();
|
|
563
|
+
const syntaxListNode = children.find((c) => c.kind === typescript.SyntaxKind.SyntaxList);
|
|
564
|
+
const result = syntaxListNode?.getChildren().filter((c) => [typescript.SyntaxKind.ClassDeclaration, typescript.SyntaxKind.TypeAliasDeclaration].includes(c.kind));
|
|
565
|
+
return result || [];
|
|
566
|
+
}
|
|
567
|
+
function verifyRelationFields(result) {
|
|
568
|
+
for (const key in result) {
|
|
569
|
+
const type = result[key];
|
|
570
|
+
for (const fieldName in type.fields) {
|
|
571
|
+
const field = type.fields[fieldName];
|
|
572
|
+
if (field.isRelation && !result[field.type]?.decorators?.entity) {
|
|
573
|
+
throw new Error(`${key}.${fieldName}: Relation must point to an entity. Got "${field.type}" instead.`);
|
|
574
|
+
}
|
|
575
|
+
if (field.isInverseRelation) {
|
|
576
|
+
if (!result[field.type]?.decorators?.entity)
|
|
577
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must point to an entity. Got "${field.type}" instead.`);
|
|
578
|
+
if (field.isRequired)
|
|
579
|
+
throw new Error(`${key}.${fieldName}: InverseRelation cannot be required. Please, add "?" to the field name.`);
|
|
580
|
+
if (!field.isArray)
|
|
581
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must be an array.`);
|
|
582
|
+
if (!field.inverseRelationField)
|
|
583
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must have a field name.`);
|
|
584
|
+
if (!result[field.type]?.fields?.[field.inverseRelationField]?.isRelation)
|
|
585
|
+
throw new Error(
|
|
586
|
+
`${key}.${fieldName}: InverseRelation field must point to relation field. Got "${field.inverseRelationField}" instead.`
|
|
587
|
+
);
|
|
588
|
+
if (result[field.type]?.fields?.[field.inverseRelationField]?.type !== key)
|
|
589
|
+
throw new Error(
|
|
590
|
+
`${key}.${fieldName}: InverseRelation field must point to the current entity. Got "${field.inverseRelationField}" instead.`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function defineRadsConfig(config) {
|
|
598
|
+
return config;
|
|
599
|
+
}
|
|
600
|
+
function schemaFromFiles(entitiesDir) {
|
|
601
|
+
return async () => {
|
|
602
|
+
if (!fs__default.existsSync(entitiesDir))
|
|
603
|
+
await fs__default.promises.mkdir(entitiesDir, { recursive: true });
|
|
604
|
+
const response = await fs__default.promises.readdir(entitiesDir, { withFileTypes: true });
|
|
605
|
+
const entities = {};
|
|
606
|
+
for (const file of response) {
|
|
607
|
+
if (!file.isFile())
|
|
608
|
+
continue;
|
|
609
|
+
if (!file.name.endsWith(".ts"))
|
|
610
|
+
continue;
|
|
611
|
+
const text = await fs__default.promises.readFile(path__default.resolve(entitiesDir, file.name), "utf-8");
|
|
612
|
+
entities[file.name.slice(0, -3)] = text;
|
|
613
|
+
}
|
|
614
|
+
return { schema: parseSchema(entities), entitiesDir };
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function schemaFromRadsApi(radsApiUrl) {
|
|
618
|
+
return async () => {
|
|
619
|
+
const url = new URL("radsTunnel", radsApiUrl);
|
|
620
|
+
const fetchResponse = await fetch(url.href, {
|
|
621
|
+
method: "POST",
|
|
622
|
+
headers: { "content-type": "application/json" },
|
|
623
|
+
body: JSON.stringify({ method: "_schema" })
|
|
624
|
+
});
|
|
625
|
+
console.log("loading", url.href);
|
|
626
|
+
const schema = await fetchResponse.json();
|
|
627
|
+
if (!schema) {
|
|
628
|
+
throw new Error("Could not download schema from the server. Please check the server URL.");
|
|
629
|
+
}
|
|
630
|
+
return { schema };
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
exports.defineRadsConfig = defineRadsConfig;
|
|
635
|
+
exports.schemaFromFiles = schemaFromFiles;
|
|
636
|
+
exports.schemaFromRadsApi = schemaFromRadsApi;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { g as RadsConfig, T as TypeDefinition } from './types-7e792d1f.js';
|
|
2
|
+
import '_rads-db';
|
|
3
|
+
|
|
4
|
+
declare function defineRadsConfig(config: RadsConfig): RadsConfig;
|
|
5
|
+
/**
|
|
6
|
+
* Loads all typescript files in given folder, parses them and outputs schema.
|
|
7
|
+
* @param entitiesDir - path from project root to folder with typescript files, e.g. './entities'
|
|
8
|
+
* @returns schema object
|
|
9
|
+
*/
|
|
10
|
+
declare function schemaFromFiles(entitiesDir: string): () => Promise<{
|
|
11
|
+
schema: Record<string, TypeDefinition>;
|
|
12
|
+
entitiesDir: string;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Loads schema from the rads api.
|
|
16
|
+
* @param radsApiUrl - URL where to download schema fiels, e.g. 'https://radsjs.com/api/'
|
|
17
|
+
* @returns schema object
|
|
18
|
+
*/
|
|
19
|
+
declare function schemaFromRadsApi(radsApiUrl: string): () => Promise<{
|
|
20
|
+
schema: any;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
export { defineRadsConfig, schemaFromFiles, schemaFromRadsApi };
|