@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 CHANGED
@@ -47,7 +47,7 @@ __export(index_exports, {
47
47
  createVersionStore: () => createVersionStore,
48
48
  duplicateSchemaError: () => duplicateSchemaError,
49
49
  ensureFileSchema: () => ensureFileSchema,
50
- err: () => import_omnify_types.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: () => import_omnify_types.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 import_omnify_types = require("@famgia/omnify-types");
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 && typeof data.displayName === "string") {
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 && typeof data.displayName === "string") {
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 && typeof data.description === "string") {
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 validateUnknownFields(propertyName, property, filePath) {
2298
+ function validateUnknownFieldsDetailed(propertyName, property, filePath, customTypeDefinitions) {
2271
2299
  const errors = [];
2272
- const unknownFields = property._unknownFields;
2273
- if (!unknownFields || unknownFields.length === 0) {
2274
- return errors;
2300
+ const warnings = [];
2301
+ if (!property.type) {
2302
+ return { errors, warnings };
2275
2303
  }
2276
- for (const field of unknownFields) {
2277
- let suggestion = `Valid property fields: type, displayName, nullable, default, unique, description, length, unsigned, precision, scale, enum, relation, target, onDelete, onUpdate, joinTable`;
2278
- if (field === "required") {
2279
- suggestion = `'required' is not a valid field. Properties are required by default.
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
- } else if (field === "hidden") {
2290
- suggestion = `'hidden' is not a standard field. For Laravel, hidden fields are configured in the Model class.`;
2291
- } else if (field === "foreignKey") {
2292
- suggestion = `'foreignKey' is not needed. Omnify auto-generates foreign keys from Association properties.
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 errors;
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 unknownFieldErrors = validateUnknownFields(name, property, schema.filePath);
2597
- errors.push(...unknownFieldErrors);
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,