@famgia/omnify-core 0.0.13 → 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 +207 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -12
- package/dist/index.d.ts +19 -12
- package/dist/index.js +204 -34
- 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") {
|
|
@@ -665,10 +666,13 @@ function buildSchemaDefinition(data) {
|
|
|
665
666
|
}
|
|
666
667
|
}
|
|
667
668
|
if (data.properties !== void 0 && typeof data.properties === "object") {
|
|
668
|
-
const properties = buildProperties(data.properties);
|
|
669
|
+
const { properties, invalidProperties } = buildProperties(data.properties);
|
|
669
670
|
if (Object.keys(properties).length > 0) {
|
|
670
671
|
schema.properties = properties;
|
|
671
672
|
}
|
|
673
|
+
if (invalidProperties.length > 0) {
|
|
674
|
+
schema._invalidProperties = invalidProperties;
|
|
675
|
+
}
|
|
672
676
|
}
|
|
673
677
|
if (data.values !== void 0 && Array.isArray(data.values)) {
|
|
674
678
|
schema.values = data.values;
|
|
@@ -717,12 +721,25 @@ function buildSchemaOptions(data) {
|
|
|
717
721
|
}
|
|
718
722
|
function buildProperties(data) {
|
|
719
723
|
const properties = {};
|
|
724
|
+
const invalidProperties = [];
|
|
720
725
|
for (const [name, value] of Object.entries(data)) {
|
|
721
|
-
if (value
|
|
726
|
+
if (value === void 0 || value === null) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (typeof value === "object") {
|
|
722
730
|
properties[name] = buildPropertyDefinition(value);
|
|
731
|
+
} else {
|
|
732
|
+
invalidProperties.push({
|
|
733
|
+
propertyName: name,
|
|
734
|
+
value,
|
|
735
|
+
expectedFormat: `Property '${name}' must be an object with 'type' field. Example:
|
|
736
|
+
${name}:
|
|
737
|
+
type: String
|
|
738
|
+
nullable: true`
|
|
739
|
+
});
|
|
723
740
|
}
|
|
724
741
|
}
|
|
725
|
-
return properties;
|
|
742
|
+
return { properties, invalidProperties };
|
|
726
743
|
}
|
|
727
744
|
var VALID_PROPERTY_FIELDS = /* @__PURE__ */ new Set([
|
|
728
745
|
"type",
|
|
@@ -771,7 +788,7 @@ function buildPropertyDefinition(data) {
|
|
|
771
788
|
if (unknownFields.length > 0) {
|
|
772
789
|
prop._unknownFields = unknownFields;
|
|
773
790
|
}
|
|
774
|
-
if (data.displayName !== void 0 &&
|
|
791
|
+
if (data.displayName !== void 0 && (0, import_omnify_types.isLocalizedString)(data.displayName)) {
|
|
775
792
|
prop.displayName = data.displayName;
|
|
776
793
|
}
|
|
777
794
|
if (data.nullable !== void 0 && typeof data.nullable === "boolean") {
|
|
@@ -783,7 +800,7 @@ function buildPropertyDefinition(data) {
|
|
|
783
800
|
if (data.unique !== void 0 && typeof data.unique === "boolean") {
|
|
784
801
|
prop.unique = data.unique;
|
|
785
802
|
}
|
|
786
|
-
if (data.description !== void 0 &&
|
|
803
|
+
if (data.description !== void 0 && (0, import_omnify_types.isLocalizedString)(data.description)) {
|
|
787
804
|
prop.description = data.description;
|
|
788
805
|
}
|
|
789
806
|
if (data.renamedFrom !== void 0 && typeof data.renamedFrom === "string") {
|
|
@@ -1060,6 +1077,9 @@ async function ensureFileSchema(schemas, schemasDir, autoCreate = false) {
|
|
|
1060
1077
|
};
|
|
1061
1078
|
}
|
|
1062
1079
|
|
|
1080
|
+
// src/validation/validator.ts
|
|
1081
|
+
var import_omnify_types2 = require("@famgia/omnify-types");
|
|
1082
|
+
|
|
1063
1083
|
// src/validation/types/base.ts
|
|
1064
1084
|
var BASE_PROPERTY_FIELDS = [
|
|
1065
1085
|
"type",
|
|
@@ -1153,6 +1173,30 @@ var TypeRegistry = class {
|
|
|
1153
1173
|
getAllTypeNames() {
|
|
1154
1174
|
return [...this.types.keys()];
|
|
1155
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
|
+
}
|
|
1156
1200
|
/**
|
|
1157
1201
|
* Get count of registered types.
|
|
1158
1202
|
*/
|
|
@@ -2206,6 +2250,25 @@ function validateUnknownOptionsFields(schema, filePath) {
|
|
|
2206
2250
|
}
|
|
2207
2251
|
return errors;
|
|
2208
2252
|
}
|
|
2253
|
+
function validatePropertyFormat(schema, filePath) {
|
|
2254
|
+
const errors = [];
|
|
2255
|
+
const invalidProperties = schema._invalidProperties;
|
|
2256
|
+
if (!invalidProperties || invalidProperties.length === 0) {
|
|
2257
|
+
return errors;
|
|
2258
|
+
}
|
|
2259
|
+
for (const invalid of invalidProperties) {
|
|
2260
|
+
const valueType = typeof invalid.value;
|
|
2261
|
+
const valuePreview = valueType === "string" ? `"${invalid.value}"` : String(invalid.value);
|
|
2262
|
+
errors.push(
|
|
2263
|
+
validationError(
|
|
2264
|
+
`Property '${invalid.propertyName}' has invalid format: got ${valueType} (${valuePreview})`,
|
|
2265
|
+
buildLocation7(filePath),
|
|
2266
|
+
invalid.expectedFormat
|
|
2267
|
+
)
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
return errors;
|
|
2271
|
+
}
|
|
2209
2272
|
function validateDbCompatibility(propertyName, property, filePath, databaseDriver) {
|
|
2210
2273
|
const errors = [];
|
|
2211
2274
|
const warnings = [];
|
|
@@ -2232,16 +2295,52 @@ function validateDbCompatibility(propertyName, property, filePath, databaseDrive
|
|
|
2232
2295
|
}
|
|
2233
2296
|
return { errors, warnings };
|
|
2234
2297
|
}
|
|
2235
|
-
function
|
|
2298
|
+
function validateUnknownFieldsDetailed(propertyName, property, filePath, customTypeDefinitions) {
|
|
2236
2299
|
const errors = [];
|
|
2237
|
-
const
|
|
2238
|
-
if (!
|
|
2239
|
-
return errors;
|
|
2300
|
+
const warnings = [];
|
|
2301
|
+
if (!property.type) {
|
|
2302
|
+
return { errors, warnings };
|
|
2240
2303
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
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.
|
|
2245
2344
|
Use 'nullable: true' to make a field optional.
|
|
2246
2345
|
Example:
|
|
2247
2346
|
email:
|
|
@@ -2251,10 +2350,12 @@ Example:
|
|
|
2251
2350
|
phone:
|
|
2252
2351
|
type: String
|
|
2253
2352
|
nullable: true # optional field`;
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
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.
|
|
2258
2359
|
The foreign key name is derived from the property name + '_id'.
|
|
2259
2360
|
Example:
|
|
2260
2361
|
author:
|
|
@@ -2262,16 +2363,8 @@ Example:
|
|
|
2262
2363
|
relation: ManyToOne
|
|
2263
2364
|
target: User
|
|
2264
2365
|
# Creates author_id column automatically`;
|
|
2265
|
-
}
|
|
2266
|
-
errors.push(
|
|
2267
|
-
validationError(
|
|
2268
|
-
`Property '${propertyName}' has unknown field '${field}'`,
|
|
2269
|
-
buildLocation7(filePath),
|
|
2270
|
-
suggestion
|
|
2271
|
-
)
|
|
2272
|
-
);
|
|
2273
2366
|
}
|
|
2274
|
-
return
|
|
2367
|
+
return `Valid property fields: type, displayName, nullable, default, unique, description, length, unsigned, precision, scale, enum, relation, target, onDelete, onUpdate, joinTable`;
|
|
2275
2368
|
}
|
|
2276
2369
|
function validatePropertyType(propertyName, property, filePath, customTypes = []) {
|
|
2277
2370
|
const errors = [];
|
|
@@ -2527,6 +2620,62 @@ function validateEnumSchema(schema, filePath) {
|
|
|
2527
2620
|
}
|
|
2528
2621
|
return errors;
|
|
2529
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
|
+
}
|
|
2530
2679
|
function validateSchema(schema, options = {}) {
|
|
2531
2680
|
const errors = [];
|
|
2532
2681
|
const warnings = [];
|
|
@@ -2535,6 +2684,8 @@ function validateSchema(schema, options = {}) {
|
|
|
2535
2684
|
errors.push(...unknownSchemaFieldErrors);
|
|
2536
2685
|
const unknownOptionsErrors = validateUnknownOptionsFields(schema, schema.filePath);
|
|
2537
2686
|
errors.push(...unknownOptionsErrors);
|
|
2687
|
+
const propertyFormatErrors = validatePropertyFormat(schema, schema.filePath);
|
|
2688
|
+
errors.push(...propertyFormatErrors);
|
|
2538
2689
|
const propErrors = validateProperties(schema, schema.filePath, customTypes);
|
|
2539
2690
|
errors.push(...propErrors);
|
|
2540
2691
|
const optErrors = validateOptions(schema, schema.filePath);
|
|
@@ -2556,13 +2707,21 @@ function validateSchema(schema, options = {}) {
|
|
|
2556
2707
|
}
|
|
2557
2708
|
if (schema.properties) {
|
|
2558
2709
|
for (const [name, property] of Object.entries(schema.properties)) {
|
|
2559
|
-
const
|
|
2560
|
-
|
|
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);
|
|
2561
2718
|
const dbCompat = validateDbCompatibility(name, property, schema.filePath, options.databaseDriver);
|
|
2562
2719
|
errors.push(...dbCompat.errors);
|
|
2563
2720
|
warnings.push(...dbCompat.warnings);
|
|
2564
2721
|
}
|
|
2565
2722
|
}
|
|
2723
|
+
const localizedStringWarnings = validateLocalizedStrings(schema, options.localeConfig);
|
|
2724
|
+
warnings.push(...localizedStringWarnings);
|
|
2566
2725
|
return {
|
|
2567
2726
|
schemaName: schema.name,
|
|
2568
2727
|
valid: errors.length === 0,
|
|
@@ -2652,8 +2811,10 @@ var GeneratorRunner = class {
|
|
|
2652
2811
|
}
|
|
2653
2812
|
/**
|
|
2654
2813
|
* Run all generators in topological order.
|
|
2814
|
+
* @param schemas - The schemas to generate from
|
|
2815
|
+
* @param changes - Schema changes detected from lock file comparison
|
|
2655
2816
|
*/
|
|
2656
|
-
async runAll(schemas) {
|
|
2817
|
+
async runAll(schemas, changes) {
|
|
2657
2818
|
const errors = [];
|
|
2658
2819
|
const outputs = [];
|
|
2659
2820
|
const outputsByGenerator = /* @__PURE__ */ new Map();
|
|
@@ -2681,10 +2842,12 @@ var GeneratorRunner = class {
|
|
|
2681
2842
|
this.options.logger.debug(`Running generator: ${name}`);
|
|
2682
2843
|
const ctx = {
|
|
2683
2844
|
schemas,
|
|
2845
|
+
changes,
|
|
2684
2846
|
pluginConfig,
|
|
2685
2847
|
cwd: this.options.cwd,
|
|
2686
2848
|
logger: this.options.logger,
|
|
2687
|
-
previousOutputs
|
|
2849
|
+
previousOutputs,
|
|
2850
|
+
customTypes: this.options.customTypes ?? /* @__PURE__ */ new Map()
|
|
2688
2851
|
};
|
|
2689
2852
|
const result = await definition.generate(ctx);
|
|
2690
2853
|
const generatorOutputs = Array.isArray(result) ? result : [result];
|
|
@@ -3036,17 +3199,24 @@ var PluginManager = class {
|
|
|
3036
3199
|
/**
|
|
3037
3200
|
* Runs all registered generators in topological order.
|
|
3038
3201
|
* @param schemas - The schemas to generate from
|
|
3202
|
+
* @param changes - Schema changes detected from lock file comparison
|
|
3039
3203
|
* @returns Result with all generated outputs
|
|
3040
3204
|
*/
|
|
3041
|
-
async runGenerators(schemas) {
|
|
3205
|
+
async runGenerators(schemas, changes) {
|
|
3206
|
+
const customTypes = /* @__PURE__ */ new Map();
|
|
3207
|
+
for (const [name, registeredType] of this._types) {
|
|
3208
|
+
const { pluginName, pluginVersion, ...typeDefinition } = registeredType;
|
|
3209
|
+
customTypes.set(name, typeDefinition);
|
|
3210
|
+
}
|
|
3042
3211
|
const runner = new GeneratorRunner({
|
|
3043
3212
|
cwd: this._cwd,
|
|
3044
|
-
logger: this._logger
|
|
3213
|
+
logger: this._logger,
|
|
3214
|
+
customTypes
|
|
3045
3215
|
});
|
|
3046
3216
|
for (const gen of this._generators.values()) {
|
|
3047
3217
|
runner.register(gen);
|
|
3048
3218
|
}
|
|
3049
|
-
return runner.runAll(schemas);
|
|
3219
|
+
return runner.runAll(schemas, changes);
|
|
3050
3220
|
}
|
|
3051
3221
|
/**
|
|
3052
3222
|
* Clears all registered plugins, types, and generators.
|