@postxl/schema 0.0.2
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/LICENSE +50 -0
- package/README.md +320 -0
- package/dist/enum/enum.brands.d.ts +11 -0
- package/dist/enum/enum.brands.js +19 -0
- package/dist/enum/enum.d.ts +49 -0
- package/dist/enum/enum.defaults.d.ts +41 -0
- package/dist/enum/enum.defaults.js +105 -0
- package/dist/enum/enum.js +6 -0
- package/dist/enum/enum.json-decoder.d.ts +61 -0
- package/dist/enum/enum.json-decoder.js +51 -0
- package/dist/enum/enum.transformer.d.ts +12 -0
- package/dist/enum/enum.transformer.js +76 -0
- package/dist/enum/enum.types.d.ts +14 -0
- package/dist/enum/enum.types.js +2 -0
- package/dist/enum/index.d.ts +6 -0
- package/dist/enum/index.js +22 -0
- package/dist/field/defaults.d.ts +12 -0
- package/dist/field/defaults.js +78 -0
- package/dist/field/discriminated-union.d.ts +125 -0
- package/dist/field/discriminated-union.js +207 -0
- package/dist/field/enum.d.ts +65 -0
- package/dist/field/enum.js +111 -0
- package/dist/field/field.d.ts +483 -0
- package/dist/field/field.js +68 -0
- package/dist/field/id.d.ts +152 -0
- package/dist/field/id.js +104 -0
- package/dist/field/index.d.ts +11 -0
- package/dist/field/index.js +27 -0
- package/dist/field/relation.d.ts +88 -0
- package/dist/field/relation.js +138 -0
- package/dist/field/scalar.d.ts +179 -0
- package/dist/field/scalar.js +197 -0
- package/dist/field/shared/brands.d.ts +38 -0
- package/dist/field/shared/brands.js +109 -0
- package/dist/field/shared/decoders.d.ts +17 -0
- package/dist/field/shared/decoders.js +88 -0
- package/dist/field/shared/seed.d.ts +32 -0
- package/dist/field/shared/seed.js +63 -0
- package/dist/field/shared/types.d.ts +73 -0
- package/dist/field/shared/types.js +2 -0
- package/dist/field/shared/utils.d.ts +26 -0
- package/dist/field/shared/utils.js +52 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +25 -0
- package/dist/model/index.d.ts +5 -0
- package/dist/model/index.js +21 -0
- package/dist/model/model.brands.d.ts +7 -0
- package/dist/model/model.brands.js +10 -0
- package/dist/model/model.defaults.d.ts +28 -0
- package/dist/model/model.defaults.js +304 -0
- package/dist/model/model.json-decoder.d.ts +94 -0
- package/dist/model/model.json-decoder.js +107 -0
- package/dist/model/model.transformer.d.ts +93 -0
- package/dist/model/model.transformer.js +183 -0
- package/dist/model/model.types.d.ts +37 -0
- package/dist/model/model.types.js +2 -0
- package/dist/project-schema/project-schema.brands.d.ts +10 -0
- package/dist/project-schema/project-schema.brands.js +17 -0
- package/dist/project-schema/project-schema.defaults.d.ts +16 -0
- package/dist/project-schema/project-schema.defaults.js +53 -0
- package/dist/project-schema/project-schema.json-decoder.d.ts +285 -0
- package/dist/project-schema/project-schema.json-decoder.js +145 -0
- package/dist/project-schema/project-schema.transformer.d.ts +284 -0
- package/dist/project-schema/project-schema.transformer.js +444 -0
- package/dist/project-schema/project-schema.types.d.ts +77 -0
- package/dist/project-schema/project-schema.types.js +2 -0
- package/dist/samples/la.schema.json +2055 -0
- package/dist/samples/mca.schema.json +830 -0
- package/dist/samples/ring.schema.json +879 -0
- package/dist/samples/sample-schemas.d.ts +1896 -0
- package/dist/samples/sample-schemas.js +20 -0
- package/dist/samples/simple.schema.json +250 -0
- package/dist/samples/subex.schema.json +1188 -0
- package/dist/samples/usp-msm.schema.json +2515 -0
- package/package.json +57 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.zProjectSchema = void 0;
|
|
37
|
+
exports.transformProjectSchemaJSON = transformProjectSchemaJSON;
|
|
38
|
+
const zod_1 = require("zod");
|
|
39
|
+
const utils_1 = require("@postxl/utils");
|
|
40
|
+
const Enum = __importStar(require("../enum"));
|
|
41
|
+
const Field = __importStar(require("../field"));
|
|
42
|
+
const Model = __importStar(require("../model"));
|
|
43
|
+
const Branded = __importStar(require("./project-schema.brands"));
|
|
44
|
+
const project_schema_defaults_1 = require("./project-schema.defaults");
|
|
45
|
+
const project_schema_json_decoder_1 = require("./project-schema.json-decoder");
|
|
46
|
+
function transformProjectSchemaJSON(input, ctx) {
|
|
47
|
+
const result = (0, utils_1.pipeZodTransformers)(input, ctx, [
|
|
48
|
+
convertRecordsToMaps,
|
|
49
|
+
addDefaults,
|
|
50
|
+
brandProjectSchema,
|
|
51
|
+
combineAndTransformEnums,
|
|
52
|
+
combineModels,
|
|
53
|
+
decodeFields,
|
|
54
|
+
transformModels,
|
|
55
|
+
advancedValidations,
|
|
56
|
+
]);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Zod decoder that converts JSON (see `ProjectSchemaJSON`) to a `ProjectSchema`
|
|
61
|
+
*/
|
|
62
|
+
exports.zProjectSchema = project_schema_json_decoder_1.zProjectSchemaJSON.transform(transformProjectSchemaJSON);
|
|
63
|
+
function convertRecordsToMaps(input) {
|
|
64
|
+
return {
|
|
65
|
+
...input,
|
|
66
|
+
models: new Map(Object.entries(input.models ?? {})),
|
|
67
|
+
enums: new Map(Object.entries(input.enums ?? {})),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function addDefaults(input) {
|
|
71
|
+
const schemas = input.schemas ?? [project_schema_defaults_1.databaseSchemaNameData, project_schema_defaults_1.databaseSchemaNameConfig];
|
|
72
|
+
const defaultSchema = getDefaultSchema(input.schemas, input.defaultSchema);
|
|
73
|
+
return {
|
|
74
|
+
name: input.name,
|
|
75
|
+
slug: input.slug,
|
|
76
|
+
description: input.description ?? '',
|
|
77
|
+
projectType: input.projectType ?? { kind: 'workspace', projectDirectory: input.slug },
|
|
78
|
+
version: input.version ?? '0.0.1',
|
|
79
|
+
schemas,
|
|
80
|
+
defaultSchema,
|
|
81
|
+
systemUser: {
|
|
82
|
+
...project_schema_defaults_1.defaultSystemUser,
|
|
83
|
+
...input.systemUser,
|
|
84
|
+
},
|
|
85
|
+
standardModels: input.standardModels ?? Array.from(Object.values(Model.defaultModelNames)),
|
|
86
|
+
models: input.models ?? new Map(),
|
|
87
|
+
standardEnums: input.standardEnums ?? Array.from(Object.values(Enum.defaultEnumNames)),
|
|
88
|
+
enums: input.enums ?? new Map(),
|
|
89
|
+
source: input.source,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function getDefaultSchema(schemas, defaultSchema) {
|
|
93
|
+
if (defaultSchema !== undefined) {
|
|
94
|
+
return defaultSchema;
|
|
95
|
+
}
|
|
96
|
+
if (schemas === undefined) {
|
|
97
|
+
return project_schema_defaults_1.databaseSchemaNameData;
|
|
98
|
+
}
|
|
99
|
+
if (schemas.length === 0) {
|
|
100
|
+
return project_schema_defaults_1.databaseSchemaNameData;
|
|
101
|
+
}
|
|
102
|
+
return schemas[0];
|
|
103
|
+
}
|
|
104
|
+
function brandProjectSchema({ name, slug, schemas, defaultSchema, standardModels, standardEnums, ...input }) {
|
|
105
|
+
return {
|
|
106
|
+
...input,
|
|
107
|
+
name: Branded.toProjectSchemaName(name),
|
|
108
|
+
slug: Branded.toProjectSlug(slug),
|
|
109
|
+
databaseSchemas: schemas.map((s) => Branded.toDatabaseSchemaName(s)),
|
|
110
|
+
defaultDatabaseSchema: Branded.toDatabaseSchemaName(defaultSchema),
|
|
111
|
+
standardModels: standardModels.map((s) => Model.toModelName(s)),
|
|
112
|
+
standardEnums: standardEnums.map((s) => Enum.toEnumName(s)),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Combines provided input enums with standard enums and transforms them to dictionary of fully typed enums
|
|
117
|
+
*/
|
|
118
|
+
function combineAndTransformEnums({ enums, standardEnums, ...projectSchema }, ctx) {
|
|
119
|
+
// All standard enums use the config database schema by default. However, if the project schema does not specify
|
|
120
|
+
// a config database schema, we use the default database schema instead.
|
|
121
|
+
const databaseSchema = projectSchema.databaseSchemas.find((s) => s === project_schema_defaults_1.databaseSchemaNameConfig) ?? projectSchema.defaultDatabaseSchema;
|
|
122
|
+
const standardEnumsSelected = [];
|
|
123
|
+
for (const standardEnum of standardEnums) {
|
|
124
|
+
if ([...enums.values()].find((e) => e.name === standardEnum)) {
|
|
125
|
+
// A more specific enum has been defined, we skip the standard enum
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const standardEnumInput = Enum.defaultEnumsJson[standardEnum];
|
|
129
|
+
if (!standardEnumInput) {
|
|
130
|
+
ctx.addIssue({
|
|
131
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
132
|
+
message: `Standard enum ${standardEnum} is not defined!`,
|
|
133
|
+
});
|
|
134
|
+
return zod_1.z.NEVER;
|
|
135
|
+
}
|
|
136
|
+
const e = { ...standardEnumInput, schema: databaseSchema };
|
|
137
|
+
standardEnumsSelected.push(e);
|
|
138
|
+
}
|
|
139
|
+
const enumsMap = new Map();
|
|
140
|
+
const enumNames = [];
|
|
141
|
+
for (const inputEnum of [...standardEnumsSelected, ...enums.values()]) {
|
|
142
|
+
const enumEnriched = Enum.enumTransformer(inputEnum, ctx);
|
|
143
|
+
const enumBranded = {
|
|
144
|
+
...enumEnriched,
|
|
145
|
+
databaseSchema: enumEnriched.databaseSchema ?? projectSchema.defaultDatabaseSchema,
|
|
146
|
+
};
|
|
147
|
+
if (enumsMap.get(enumBranded.name) !== undefined) {
|
|
148
|
+
ctx.addIssue({
|
|
149
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
150
|
+
message: `Enum ${inputEnum.name} is defined multiple times in project ${projectSchema.name}!`,
|
|
151
|
+
});
|
|
152
|
+
return zod_1.z.NEVER;
|
|
153
|
+
}
|
|
154
|
+
enumsMap.set(enumBranded.name, enumBranded);
|
|
155
|
+
enumNames.push(enumBranded.name);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
...projectSchema,
|
|
159
|
+
enums: enumsMap,
|
|
160
|
+
enumNames,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Combines provided input models with standard models. The standard models are placed first.
|
|
165
|
+
*/
|
|
166
|
+
function combineModels({ models, standardModels, ...projectSchema }, ctx) {
|
|
167
|
+
const modelsInput = new Map();
|
|
168
|
+
for (const inputModel of models.values()) {
|
|
169
|
+
if (modelsInput.get(inputModel.name) !== undefined) {
|
|
170
|
+
ctx.addIssue({
|
|
171
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
172
|
+
message: `Model ${inputModel.name} is defined multiple times in project ${projectSchema.name}!`,
|
|
173
|
+
});
|
|
174
|
+
return zod_1.z.NEVER;
|
|
175
|
+
}
|
|
176
|
+
modelsInput.set(inputModel.name, inputModel);
|
|
177
|
+
}
|
|
178
|
+
const standardModelsToInclude = [];
|
|
179
|
+
// All standard enums use the config database schema by default. However, if the project schema does not specify
|
|
180
|
+
// a config database schema, we use the default database schema instead.
|
|
181
|
+
const databaseSchema = projectSchema.databaseSchemas.find((s) => s === project_schema_defaults_1.databaseSchemaNameConfig) ?? projectSchema.defaultDatabaseSchema;
|
|
182
|
+
for (const standardModel of standardModels) {
|
|
183
|
+
if (modelsInput.get(standardModel) !== undefined) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const standardModelInput = Model.defaultModelsJson[standardModel];
|
|
187
|
+
if (!standardModelInput) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
190
|
+
message: `Standard model ${standardModel} is not defined!`,
|
|
191
|
+
});
|
|
192
|
+
return zod_1.z.NEVER;
|
|
193
|
+
}
|
|
194
|
+
const m = { ...standardModelInput, schema: databaseSchema, source: standardModelInput };
|
|
195
|
+
standardModelsToInclude.push(m);
|
|
196
|
+
}
|
|
197
|
+
const modelsArray = [];
|
|
198
|
+
const modelNames = [];
|
|
199
|
+
const modelsMap = new Map();
|
|
200
|
+
for (const modelInput of [...standardModelsToInclude, ...models.values()]) {
|
|
201
|
+
const model = Model.modelJSONTransformer(modelInput, ctx);
|
|
202
|
+
modelsArray.push(model);
|
|
203
|
+
modelNames.push(model.name);
|
|
204
|
+
modelsMap.set(model.name, model);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
...projectSchema,
|
|
208
|
+
/**
|
|
209
|
+
* Record of model names to model
|
|
210
|
+
*/
|
|
211
|
+
models: modelsMap,
|
|
212
|
+
/**
|
|
213
|
+
* We also provide an array of models for quick iterations
|
|
214
|
+
*/
|
|
215
|
+
modelsArray,
|
|
216
|
+
modelNames,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Decode fields for each model
|
|
221
|
+
*/
|
|
222
|
+
function decodeFields({ modelsArray, ...projectSchema }, ctx) {
|
|
223
|
+
const modelsWithFields = new Map();
|
|
224
|
+
const fieldDecoder = Field.createFieldJSONDecoder(projectSchema);
|
|
225
|
+
for (const inputModel of modelsArray) {
|
|
226
|
+
const fields = new Map();
|
|
227
|
+
const fieldsArray = [];
|
|
228
|
+
for (const fieldName of inputModel.fieldNames) {
|
|
229
|
+
const fieldDecoded = fieldDecoder.safeParse(inputModel.fields[fieldName]);
|
|
230
|
+
if (!fieldDecoded.success) {
|
|
231
|
+
ctx.addIssue({
|
|
232
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
233
|
+
message: `Error decoding field ${inputModel.name}.${fieldName}: ${fieldDecoded.error.message}`,
|
|
234
|
+
});
|
|
235
|
+
return zod_1.z.NEVER;
|
|
236
|
+
}
|
|
237
|
+
const field = fieldDecoded.data;
|
|
238
|
+
fields.set(field.name, field);
|
|
239
|
+
fieldsArray.push(field);
|
|
240
|
+
}
|
|
241
|
+
const modelWithFields = {
|
|
242
|
+
...inputModel,
|
|
243
|
+
fields,
|
|
244
|
+
fieldsArray,
|
|
245
|
+
};
|
|
246
|
+
modelsWithFields.set(inputModel.name, modelWithFields);
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
...projectSchema,
|
|
250
|
+
models: modelsWithFields,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Transforms the combined models to the final model type
|
|
255
|
+
*/
|
|
256
|
+
function transformModels(projectSchema, ctx) {
|
|
257
|
+
const models = new Map();
|
|
258
|
+
for (const inputModelName of projectSchema.modelNames) {
|
|
259
|
+
const model = transformModel(projectSchema.models.get(inputModelName), projectSchema, ctx);
|
|
260
|
+
models.set(model.name, model);
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
...projectSchema,
|
|
264
|
+
models,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function transformModel(model, projectSchema, ctx) {
|
|
268
|
+
const fields = transformFields(model, projectSchema, ctx);
|
|
269
|
+
validateFields(fields, ctx);
|
|
270
|
+
const idField = extractIdField(fields, model, ctx);
|
|
271
|
+
const defaultField = extractDefaultField(model, fields, ctx);
|
|
272
|
+
const labelField = extractLabelField(model, fields, ctx);
|
|
273
|
+
const { defaultFieldName, labelFieldName, ...remainingModel } = model;
|
|
274
|
+
return {
|
|
275
|
+
...remainingModel,
|
|
276
|
+
databaseSchema: model.schema ?? projectSchema.defaultDatabaseSchema,
|
|
277
|
+
fields,
|
|
278
|
+
defaultField,
|
|
279
|
+
labelField,
|
|
280
|
+
idField,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function transformFields(model, projectSchema, ctx) {
|
|
284
|
+
const fields = new Map();
|
|
285
|
+
for (const inputFieldName of model.fieldNames) {
|
|
286
|
+
const field = Field.fieldTransformer(model.fields.get(inputFieldName), model, projectSchema, ctx);
|
|
287
|
+
fields.set(field.name, field);
|
|
288
|
+
}
|
|
289
|
+
return fields;
|
|
290
|
+
}
|
|
291
|
+
function validateFields(fields, ctx) {
|
|
292
|
+
const backReferences = new Map();
|
|
293
|
+
for (const field of fields.values()) {
|
|
294
|
+
if (field.kind !== 'relation') {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const backReference = backReferences.get(field.referencedModelName) ?? new Set();
|
|
298
|
+
if (backReference.has(field.prismaRelationFieldName)) {
|
|
299
|
+
ctx.addIssue({
|
|
300
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
301
|
+
message: `Model ${field.modelName} references ${field.referencedModelName} multiple times (reference name ${field.prismaRelationFieldName}). Use 'prismaRelationFieldName' property to separate them!`,
|
|
302
|
+
});
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
backReference.add(field.prismaRelationFieldName);
|
|
306
|
+
backReferences.set(field.referencedModelName, backReference);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function advancedValidations(projectSchema, ctx) {
|
|
310
|
+
validateDatabaseSchemasMatchModelsAndEnums(projectSchema, ctx);
|
|
311
|
+
return projectSchema;
|
|
312
|
+
}
|
|
313
|
+
function validateDatabaseSchemasMatchModelsAndEnums(projectSchema, ctx) {
|
|
314
|
+
const schemasUsed = new Set();
|
|
315
|
+
const schemasProvided = new Set(projectSchema.databaseSchemas);
|
|
316
|
+
validateModelSchemas(projectSchema.models, schemasProvided, schemasUsed, ctx);
|
|
317
|
+
validateEnumSchemas(projectSchema.enums, schemasProvided, schemasUsed, ctx);
|
|
318
|
+
validateProjectSchemas(projectSchema.databaseSchemas, schemasUsed, projectSchema.defaultDatabaseSchema, ctx);
|
|
319
|
+
return projectSchema;
|
|
320
|
+
}
|
|
321
|
+
function validateModelSchemas(models, schemasProvided, schemasUsed, ctx) {
|
|
322
|
+
for (const model of models.values()) {
|
|
323
|
+
if (!schemasProvided.has(model.databaseSchema)) {
|
|
324
|
+
ctx.addIssue({
|
|
325
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
326
|
+
message: `Model "${model.name}" references database schema "${model.databaseSchema}" which is not provided.`,
|
|
327
|
+
});
|
|
328
|
+
return zod_1.z.NEVER;
|
|
329
|
+
}
|
|
330
|
+
schemasUsed.add(model.databaseSchema);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function validateEnumSchemas(enums, schemasProvided, schemasUsed, ctx) {
|
|
334
|
+
for (const e of enums.values()) {
|
|
335
|
+
if (!schemasProvided.has(e.databaseSchema)) {
|
|
336
|
+
ctx.addIssue({
|
|
337
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
338
|
+
message: `Enum "${e.name}" references database schema "${e.databaseSchema}" which is not provided.`,
|
|
339
|
+
});
|
|
340
|
+
return zod_1.z.NEVER;
|
|
341
|
+
}
|
|
342
|
+
schemasUsed.add(e.databaseSchema);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function validateProjectSchemas(schemas, schemasUsed, defaultSchema, ctx) {
|
|
346
|
+
for (const schema of schemas) {
|
|
347
|
+
if (!schemasUsed.has(schema)) {
|
|
348
|
+
// If it is the default schema and it is not used, we assume that this is a work in progress status and we do not throw an error
|
|
349
|
+
if (schema === defaultSchema) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
// For the same reason, we also skip the config and data schema
|
|
353
|
+
if (schema === project_schema_defaults_1.databaseSchemaNameConfig || schema === project_schema_defaults_1.databaseSchemaNameData) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
ctx.addIssue({
|
|
357
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
358
|
+
message: `Database schema ${schema} is not used by any model or enum!`,
|
|
359
|
+
});
|
|
360
|
+
return zod_1.z.NEVER;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function extractIdField(fields, model, ctx) {
|
|
365
|
+
for (const field of fields.values()) {
|
|
366
|
+
if (field.kind === 'id') {
|
|
367
|
+
return field;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
ctx.addIssue({
|
|
371
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
372
|
+
message: `Model ${model.name} has no id field!`,
|
|
373
|
+
});
|
|
374
|
+
return zod_1.z.NEVER;
|
|
375
|
+
}
|
|
376
|
+
function extractDefaultField({ defaultFieldName, ...model }, fields, ctx) {
|
|
377
|
+
if (defaultFieldName === undefined) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
const defaultField = fields.get(defaultFieldName);
|
|
381
|
+
if (defaultField === undefined) {
|
|
382
|
+
ctx.addIssue({
|
|
383
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
384
|
+
message: `Default field ${defaultFieldName} is not defined in model ${model.name}!`,
|
|
385
|
+
});
|
|
386
|
+
return zod_1.z.NEVER;
|
|
387
|
+
}
|
|
388
|
+
if (defaultField.kind !== 'scalar' || defaultField.type !== 'boolean') {
|
|
389
|
+
ctx.addIssue({
|
|
390
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
391
|
+
message: `Default field ${defaultFieldName} in model ${model.name} must be of type \`boolean\`, but instead is of type ${Field.getFieldType(defaultField)}!`,
|
|
392
|
+
});
|
|
393
|
+
return zod_1.z.NEVER;
|
|
394
|
+
}
|
|
395
|
+
return defaultField;
|
|
396
|
+
}
|
|
397
|
+
function extractLabelField({ labelFieldName, ...model }, fields, ctx) {
|
|
398
|
+
if (labelFieldName === undefined) {
|
|
399
|
+
return extractLabelFieldFromNameOrIdField(model, fields, ctx);
|
|
400
|
+
}
|
|
401
|
+
const labelField = fields.get(labelFieldName);
|
|
402
|
+
if (labelField === undefined) {
|
|
403
|
+
ctx.addIssue({
|
|
404
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
405
|
+
message: `Label field ${labelFieldName} is not defined in model ${model.name}!`,
|
|
406
|
+
});
|
|
407
|
+
return zod_1.z.NEVER;
|
|
408
|
+
}
|
|
409
|
+
if (labelField.kind !== 'scalar' && labelField.kind !== 'id') {
|
|
410
|
+
ctx.addIssue({
|
|
411
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
412
|
+
message: `Label field ${labelFieldName} in model ${model.name} must be of type \`string\`, but instead is of type ${Field.getFieldType(labelField)}!`,
|
|
413
|
+
});
|
|
414
|
+
return zod_1.z.NEVER;
|
|
415
|
+
}
|
|
416
|
+
const type = labelField.kind === 'scalar' ? labelField.type : labelField.unbrandedTypeName;
|
|
417
|
+
if (type !== 'string') {
|
|
418
|
+
ctx.addIssue({
|
|
419
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
420
|
+
message: `Label field ${labelFieldName} in model ${model.name} must be of type \`string\`, but instead is of type ${type}!`,
|
|
421
|
+
});
|
|
422
|
+
return zod_1.z.NEVER;
|
|
423
|
+
}
|
|
424
|
+
return labelField;
|
|
425
|
+
}
|
|
426
|
+
function extractLabelFieldFromNameOrIdField({ name }, fields, ctx) {
|
|
427
|
+
// If no label field is provided, ensure name field exists and use it
|
|
428
|
+
const fallbackField = fields.get(Field.standardFieldName_name) ?? fields.get(Field.standardFieldName_id);
|
|
429
|
+
if (fallbackField === undefined) {
|
|
430
|
+
ctx.addIssue({
|
|
431
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
432
|
+
message: `Model ${name} neither provides a \`${Field.standardFieldName_name}\` field nor has \`labelField\` property!`,
|
|
433
|
+
});
|
|
434
|
+
return zod_1.z.NEVER;
|
|
435
|
+
}
|
|
436
|
+
if (fallbackField.kind !== 'scalar' && fallbackField.kind !== 'id') {
|
|
437
|
+
ctx.addIssue({
|
|
438
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
439
|
+
message: `If label field is not provided, model ${name} must have a \`${Field.standardFieldName_name}\` field of type \`string\`, but instead is of type ${Field.getFieldType(fallbackField)}!`,
|
|
440
|
+
});
|
|
441
|
+
return zod_1.z.NEVER;
|
|
442
|
+
}
|
|
443
|
+
return fallbackField;
|
|
444
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as Enum from '../enum';
|
|
2
|
+
import * as Model from '../model';
|
|
3
|
+
import * as Branded from './project-schema.brands';
|
|
4
|
+
/**
|
|
5
|
+
* Overall project configuration.
|
|
6
|
+
*
|
|
7
|
+
* This includes the models, enums, generators, etc.
|
|
8
|
+
*/
|
|
9
|
+
export type ProjectSchema = {
|
|
10
|
+
/**
|
|
11
|
+
* The name of the project, e.g. "Client1 CRM System"
|
|
12
|
+
*/
|
|
13
|
+
name: Branded.ProjectSchemaName;
|
|
14
|
+
/**
|
|
15
|
+
* The slug of the project, e.g. "client1-crm"
|
|
16
|
+
*/
|
|
17
|
+
slug: string;
|
|
18
|
+
/**
|
|
19
|
+
* A helpful description of the project.
|
|
20
|
+
*/
|
|
21
|
+
description: string;
|
|
22
|
+
/**
|
|
23
|
+
* The type of the project. Monorepo projects need to specify their project directory.
|
|
24
|
+
*/
|
|
25
|
+
projectType: ProjectType;
|
|
26
|
+
/**
|
|
27
|
+
* The version of the project/schema. Should follow semantic versioning, see https://semver.org/.
|
|
28
|
+
*/
|
|
29
|
+
version: string;
|
|
30
|
+
/**
|
|
31
|
+
* The database schemas that can be used by models and enums.
|
|
32
|
+
*
|
|
33
|
+
* If not provided, the default schemas "PXL" and "Data" are used.
|
|
34
|
+
*/
|
|
35
|
+
databaseSchemas: Branded.DatabaseSchemaName[];
|
|
36
|
+
/**
|
|
37
|
+
* The default database schema that is used if model/enum does not specify a schema.
|
|
38
|
+
*
|
|
39
|
+
* If not provided, the default schema "Data" is used.
|
|
40
|
+
*/
|
|
41
|
+
defaultDatabaseSchema: Branded.DatabaseSchemaName;
|
|
42
|
+
/**
|
|
43
|
+
* The system user that is used for root/system tasks, e.g. data seeding.
|
|
44
|
+
*/
|
|
45
|
+
systemUser: SystemUser;
|
|
46
|
+
/**
|
|
47
|
+
* The models that are used in the project.
|
|
48
|
+
*/
|
|
49
|
+
models: Model.Models;
|
|
50
|
+
/**
|
|
51
|
+
* List of model names for easier access.
|
|
52
|
+
*/
|
|
53
|
+
modelNames: Model.ModelName[];
|
|
54
|
+
/**
|
|
55
|
+
* The enums that are used in the project.
|
|
56
|
+
*/
|
|
57
|
+
enums: Enum.Enums;
|
|
58
|
+
/**
|
|
59
|
+
* List of enum names for easier access.
|
|
60
|
+
*/
|
|
61
|
+
enumNames: Enum.EnumName[];
|
|
62
|
+
/**
|
|
63
|
+
* The source definition of the project. Provided to allow custom generators to extract/define additional information.
|
|
64
|
+
*
|
|
65
|
+
* Does not contain the models field as this is included in the individual models.
|
|
66
|
+
*/
|
|
67
|
+
source: Record<string, unknown>;
|
|
68
|
+
};
|
|
69
|
+
export type ProjectType = {
|
|
70
|
+
kind: 'standalone';
|
|
71
|
+
} | {
|
|
72
|
+
kind: 'workspace';
|
|
73
|
+
projectDirectory: string;
|
|
74
|
+
};
|
|
75
|
+
export type SystemUser = Record<string, unknown> & {
|
|
76
|
+
id: string;
|
|
77
|
+
};
|