@famgia/omnify-core 0.0.14 → 0.0.16
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/index.cjs +151 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -11
- package/dist/index.d.ts +17 -11
- package/dist/index.js +148 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -47,7 +47,7 @@ __export(index_exports, {
|
|
|
47
47
|
createVersionStore: () => createVersionStore,
|
|
48
48
|
duplicateSchemaError: () => duplicateSchemaError,
|
|
49
49
|
ensureFileSchema: () => ensureFileSchema,
|
|
50
|
-
err: () =>
|
|
50
|
+
err: () => import_omnify_types3.err,
|
|
51
51
|
expandProperty: () => expandProperty,
|
|
52
52
|
expandSchema: () => expandSchema,
|
|
53
53
|
expandSchemaProperties: () => expandSchemaProperties,
|
|
@@ -88,7 +88,7 @@ __export(index_exports, {
|
|
|
88
88
|
missingConfigFieldError: () => missingConfigFieldError,
|
|
89
89
|
missingFieldError: () => missingFieldError,
|
|
90
90
|
notImplementedError: () => notImplementedError,
|
|
91
|
-
ok: () =>
|
|
91
|
+
ok: () => import_omnify_types3.ok,
|
|
92
92
|
outputWriteError: () => outputWriteError,
|
|
93
93
|
parseJsonSchema: () => parseJsonSchema,
|
|
94
94
|
parseYamlSchema: () => parseYamlSchema,
|
|
@@ -110,7 +110,7 @@ __export(index_exports, {
|
|
|
110
110
|
yamlSyntaxError: () => yamlSyntaxError
|
|
111
111
|
});
|
|
112
112
|
module.exports = __toCommonJS(index_exports);
|
|
113
|
-
var
|
|
113
|
+
var import_omnify_types3 = require("@famgia/omnify-types");
|
|
114
114
|
|
|
115
115
|
// src/errors/omnify-error.ts
|
|
116
116
|
var OmnifyError = class _OmnifyError extends Error {
|
|
@@ -553,6 +553,7 @@ function notImplementedError(feature) {
|
|
|
553
553
|
var fs = __toESM(require("fs/promises"), 1);
|
|
554
554
|
var path = __toESM(require("path"), 1);
|
|
555
555
|
var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
556
|
+
var import_omnify_types = require("@famgia/omnify-types");
|
|
556
557
|
function fileNameToSchemaName(fileName) {
|
|
557
558
|
const baseName = path.basename(fileName, path.extname(fileName));
|
|
558
559
|
if (!baseName.includes("-") && !baseName.includes("_")) {
|
|
@@ -639,7 +640,7 @@ function buildSchemaDefinition(data) {
|
|
|
639
640
|
if (data.kind !== void 0) {
|
|
640
641
|
schema.kind = data.kind;
|
|
641
642
|
}
|
|
642
|
-
if (data.displayName !== void 0 &&
|
|
643
|
+
if (data.displayName !== void 0 && (0, import_omnify_types.isLocalizedString)(data.displayName)) {
|
|
643
644
|
schema.displayName = data.displayName;
|
|
644
645
|
}
|
|
645
646
|
if (data.titleIndex !== void 0 && typeof data.titleIndex === "string") {
|
|
@@ -787,7 +788,7 @@ function buildPropertyDefinition(data) {
|
|
|
787
788
|
if (unknownFields.length > 0) {
|
|
788
789
|
prop._unknownFields = unknownFields;
|
|
789
790
|
}
|
|
790
|
-
if (data.displayName !== void 0 &&
|
|
791
|
+
if (data.displayName !== void 0 && (0, import_omnify_types.isLocalizedString)(data.displayName)) {
|
|
791
792
|
prop.displayName = data.displayName;
|
|
792
793
|
}
|
|
793
794
|
if (data.nullable !== void 0 && typeof data.nullable === "boolean") {
|
|
@@ -799,7 +800,7 @@ function buildPropertyDefinition(data) {
|
|
|
799
800
|
if (data.unique !== void 0 && typeof data.unique === "boolean") {
|
|
800
801
|
prop.unique = data.unique;
|
|
801
802
|
}
|
|
802
|
-
if (data.description !== void 0 &&
|
|
803
|
+
if (data.description !== void 0 && (0, import_omnify_types.isLocalizedString)(data.description)) {
|
|
803
804
|
prop.description = data.description;
|
|
804
805
|
}
|
|
805
806
|
if (data.renamedFrom !== void 0 && typeof data.renamedFrom === "string") {
|
|
@@ -1076,6 +1077,9 @@ async function ensureFileSchema(schemas, schemasDir, autoCreate = false) {
|
|
|
1076
1077
|
};
|
|
1077
1078
|
}
|
|
1078
1079
|
|
|
1080
|
+
// src/validation/validator.ts
|
|
1081
|
+
var import_omnify_types2 = require("@famgia/omnify-types");
|
|
1082
|
+
|
|
1079
1083
|
// src/validation/types/base.ts
|
|
1080
1084
|
var BASE_PROPERTY_FIELDS = [
|
|
1081
1085
|
"type",
|
|
@@ -1169,6 +1173,30 @@ var TypeRegistry = class {
|
|
|
1169
1173
|
getAllTypeNames() {
|
|
1170
1174
|
return [...this.types.keys()];
|
|
1171
1175
|
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Get all valid fields across ALL types.
|
|
1178
|
+
* Used to determine if a field is valid for any type.
|
|
1179
|
+
*/
|
|
1180
|
+
getAllValidFields() {
|
|
1181
|
+
const allFields = /* @__PURE__ */ new Set();
|
|
1182
|
+
for (const type of this.types.values()) {
|
|
1183
|
+
for (const field of type.validFields) {
|
|
1184
|
+
allFields.add(field);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return allFields;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Check if a field is valid for any registered type.
|
|
1191
|
+
*/
|
|
1192
|
+
isFieldValidForAnyType(field) {
|
|
1193
|
+
for (const type of this.types.values()) {
|
|
1194
|
+
if (type.validFields.includes(field)) {
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1172
1200
|
/**
|
|
1173
1201
|
* Get count of registered types.
|
|
1174
1202
|
*/
|
|
@@ -2267,16 +2295,52 @@ function validateDbCompatibility(propertyName, property, filePath, databaseDrive
|
|
|
2267
2295
|
}
|
|
2268
2296
|
return { errors, warnings };
|
|
2269
2297
|
}
|
|
2270
|
-
function
|
|
2298
|
+
function validateUnknownFieldsDetailed(propertyName, property, filePath, customTypeDefinitions) {
|
|
2271
2299
|
const errors = [];
|
|
2272
|
-
const
|
|
2273
|
-
if (!
|
|
2274
|
-
return errors;
|
|
2300
|
+
const warnings = [];
|
|
2301
|
+
if (!property.type) {
|
|
2302
|
+
return { errors, warnings };
|
|
2275
2303
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2304
|
+
let validFields;
|
|
2305
|
+
const isBuiltIn = typeRegistry.has(property.type);
|
|
2306
|
+
if (isBuiltIn) {
|
|
2307
|
+
validFields = typeRegistry.getValidFields(property.type);
|
|
2308
|
+
} else {
|
|
2309
|
+
const customTypeDef = customTypeDefinitions?.get(property.type);
|
|
2310
|
+
if (customTypeDef?.validFields) {
|
|
2311
|
+
validFields = [...BASE_PROPERTY_FIELDS, ...customTypeDef.validFields];
|
|
2312
|
+
} else {
|
|
2313
|
+
validFields = [...BASE_PROPERTY_FIELDS];
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
const validFieldsSet = new Set(validFields);
|
|
2317
|
+
for (const field of Object.keys(property)) {
|
|
2318
|
+
if (field.startsWith("_")) continue;
|
|
2319
|
+
if (validFieldsSet.has(field)) continue;
|
|
2320
|
+
const isValidForOtherType = typeRegistry.isFieldValidForAnyType(field);
|
|
2321
|
+
if (isValidForOtherType) {
|
|
2322
|
+
errors.push(
|
|
2323
|
+
validationError(
|
|
2324
|
+
`Property '${propertyName}' of type '${property.type}' has invalid field '${field}'`,
|
|
2325
|
+
buildLocation7(filePath),
|
|
2326
|
+
`'${field}' is not valid for type '${property.type}'. Valid fields: ${validFields.join(", ")}`
|
|
2327
|
+
)
|
|
2328
|
+
);
|
|
2329
|
+
} else {
|
|
2330
|
+
warnings.push(
|
|
2331
|
+
validationError(
|
|
2332
|
+
`Property '${propertyName}' has unknown field '${field}'`,
|
|
2333
|
+
buildLocation7(filePath),
|
|
2334
|
+
getSuggestionForUnknownField(field)
|
|
2335
|
+
)
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
return { errors, warnings };
|
|
2340
|
+
}
|
|
2341
|
+
function getSuggestionForUnknownField(field) {
|
|
2342
|
+
if (field === "required") {
|
|
2343
|
+
return `'required' is not a valid field. Properties are required by default.
|
|
2280
2344
|
Use 'nullable: true' to make a field optional.
|
|
2281
2345
|
Example:
|
|
2282
2346
|
email:
|
|
@@ -2286,10 +2350,12 @@ Example:
|
|
|
2286
2350
|
phone:
|
|
2287
2351
|
type: String
|
|
2288
2352
|
nullable: true # optional field`;
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2353
|
+
}
|
|
2354
|
+
if (field === "hidden") {
|
|
2355
|
+
return `'hidden' is not a standard field. For Laravel, hidden fields are configured in the Model class.`;
|
|
2356
|
+
}
|
|
2357
|
+
if (field === "foreignKey") {
|
|
2358
|
+
return `'foreignKey' is not needed. Omnify auto-generates foreign keys from Association properties.
|
|
2293
2359
|
The foreign key name is derived from the property name + '_id'.
|
|
2294
2360
|
Example:
|
|
2295
2361
|
author:
|
|
@@ -2297,16 +2363,8 @@ Example:
|
|
|
2297
2363
|
relation: ManyToOne
|
|
2298
2364
|
target: User
|
|
2299
2365
|
# Creates author_id column automatically`;
|
|
2300
|
-
}
|
|
2301
|
-
errors.push(
|
|
2302
|
-
validationError(
|
|
2303
|
-
`Property '${propertyName}' has unknown field '${field}'`,
|
|
2304
|
-
buildLocation7(filePath),
|
|
2305
|
-
suggestion
|
|
2306
|
-
)
|
|
2307
|
-
);
|
|
2308
2366
|
}
|
|
2309
|
-
return
|
|
2367
|
+
return `Valid property fields: type, displayName, nullable, default, unique, description, length, unsigned, precision, scale, enum, relation, target, onDelete, onUpdate, joinTable`;
|
|
2310
2368
|
}
|
|
2311
2369
|
function validatePropertyType(propertyName, property, filePath, customTypes = []) {
|
|
2312
2370
|
const errors = [];
|
|
@@ -2562,6 +2620,62 @@ function validateEnumSchema(schema, filePath) {
|
|
|
2562
2620
|
}
|
|
2563
2621
|
return errors;
|
|
2564
2622
|
}
|
|
2623
|
+
function validateLocalizedString(fieldName, value, filePath, localeConfig, context) {
|
|
2624
|
+
const warnings = [];
|
|
2625
|
+
if (value === void 0 || !(0, import_omnify_types2.isLocaleMap)(value)) {
|
|
2626
|
+
return { warnings };
|
|
2627
|
+
}
|
|
2628
|
+
if (!localeConfig) {
|
|
2629
|
+
return { warnings };
|
|
2630
|
+
}
|
|
2631
|
+
const configuredLocales = new Set(localeConfig.locales);
|
|
2632
|
+
const usedLocales = Object.keys(value);
|
|
2633
|
+
for (const locale of usedLocales) {
|
|
2634
|
+
if (!configuredLocales.has(locale)) {
|
|
2635
|
+
const contextStr = context ? ` (property '${context}')` : "";
|
|
2636
|
+
warnings.push(
|
|
2637
|
+
validationError(
|
|
2638
|
+
`${fieldName}${contextStr} uses locale '${locale}' which is not in configured locales`,
|
|
2639
|
+
buildLocation7(filePath),
|
|
2640
|
+
`Configured locales: ${localeConfig.locales.join(", ")}. Add '${locale}' to your locale configuration or remove it from the schema.`
|
|
2641
|
+
)
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
return { warnings };
|
|
2646
|
+
}
|
|
2647
|
+
function validateLocalizedStrings(schema, localeConfig) {
|
|
2648
|
+
const warnings = [];
|
|
2649
|
+
const schemaDisplayNameResult = validateLocalizedString(
|
|
2650
|
+
"displayName",
|
|
2651
|
+
schema.displayName,
|
|
2652
|
+
schema.filePath,
|
|
2653
|
+
localeConfig
|
|
2654
|
+
);
|
|
2655
|
+
warnings.push(...schemaDisplayNameResult.warnings);
|
|
2656
|
+
if (schema.properties) {
|
|
2657
|
+
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
2658
|
+
const propDisplayNameResult = validateLocalizedString(
|
|
2659
|
+
"displayName",
|
|
2660
|
+
property.displayName,
|
|
2661
|
+
schema.filePath,
|
|
2662
|
+
localeConfig,
|
|
2663
|
+
propName
|
|
2664
|
+
);
|
|
2665
|
+
warnings.push(...propDisplayNameResult.warnings);
|
|
2666
|
+
const propDescription = property.description;
|
|
2667
|
+
const propDescriptionResult = validateLocalizedString(
|
|
2668
|
+
"description",
|
|
2669
|
+
propDescription,
|
|
2670
|
+
schema.filePath,
|
|
2671
|
+
localeConfig,
|
|
2672
|
+
propName
|
|
2673
|
+
);
|
|
2674
|
+
warnings.push(...propDescriptionResult.warnings);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
return warnings;
|
|
2678
|
+
}
|
|
2565
2679
|
function validateSchema(schema, options = {}) {
|
|
2566
2680
|
const errors = [];
|
|
2567
2681
|
const warnings = [];
|
|
@@ -2593,13 +2707,21 @@ function validateSchema(schema, options = {}) {
|
|
|
2593
2707
|
}
|
|
2594
2708
|
if (schema.properties) {
|
|
2595
2709
|
for (const [name, property] of Object.entries(schema.properties)) {
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2710
|
+
const unknownFieldResult = validateUnknownFieldsDetailed(
|
|
2711
|
+
name,
|
|
2712
|
+
property,
|
|
2713
|
+
schema.filePath,
|
|
2714
|
+
options.customTypeDefinitions
|
|
2715
|
+
);
|
|
2716
|
+
errors.push(...unknownFieldResult.errors);
|
|
2717
|
+
warnings.push(...unknownFieldResult.warnings);
|
|
2598
2718
|
const dbCompat = validateDbCompatibility(name, property, schema.filePath, options.databaseDriver);
|
|
2599
2719
|
errors.push(...dbCompat.errors);
|
|
2600
2720
|
warnings.push(...dbCompat.warnings);
|
|
2601
2721
|
}
|
|
2602
2722
|
}
|
|
2723
|
+
const localizedStringWarnings = validateLocalizedStrings(schema, options.localeConfig);
|
|
2724
|
+
warnings.push(...localizedStringWarnings);
|
|
2603
2725
|
return {
|
|
2604
2726
|
schemaName: schema.name,
|
|
2605
2727
|
valid: errors.length === 0,
|