@omnifyjp/omnify 5.9.0 → 5.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/omnify",
3
- "version": "5.9.0",
3
+ "version": "5.9.2",
4
4
  "description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,10 +36,10 @@
36
36
  "zod": "^3.24.0"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@omnifyjp/omnify-darwin-arm64": "5.9.0",
40
- "@omnifyjp/omnify-darwin-x64": "5.9.0",
41
- "@omnifyjp/omnify-linux-x64": "5.9.0",
42
- "@omnifyjp/omnify-linux-arm64": "5.9.0",
43
- "@omnifyjp/omnify-win32-x64": "5.9.0"
39
+ "@omnifyjp/omnify-darwin-arm64": "5.9.2",
40
+ "@omnifyjp/omnify-darwin-x64": "5.9.2",
41
+ "@omnifyjp/omnify-linux-x64": "5.9.2",
42
+ "@omnifyjp/omnify-linux-arm64": "5.9.2",
43
+ "@omnifyjp/omnify-win32-x64": "5.9.2"
44
44
  }
45
45
  }
@@ -115,7 +115,7 @@ function generateBaseModel(name, schema, reader, config) {
115
115
  // the generated base model.
116
116
  const hasAuditLog = reader.isAuditLogEnabled(name);
117
117
  const imports = buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait, needsUlidTrait, hasNestedSet, config.nestedset.namespace, hasFiles, modelNamespace, localesNamespace, traitsNamespace, sharedModelsNamespace, config, hasAuditLog);
118
- const docProperties = buildDocProperties(properties, expandedProperties, propertyOrder);
118
+ const docProperties = buildDocProperties(properties, expandedProperties, propertyOrder, reader, config);
119
119
  const baseClass = isAuthenticatable ? 'Authenticatable' : 'BaseModel';
120
120
  const implementsClause = hasTranslatable ? ' implements TranslatableContract' : '';
121
121
  const traits = buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUuidTrait, needsUlidTrait, hasNestedSet, hasFiles, hasAuditLog);
@@ -386,12 +386,14 @@ function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable
386
386
  }
387
387
  }
388
388
  if (hasAuditLog) {
389
- // HasOmnifyAuditLog is a global Omnify trait emitted by
390
- // audit-trait-generator.ts. Same global-traits placement as HasFiles
391
- // so consumers can update the trait in place without per-schema
392
- // duplication.
389
+ // HasOmnifyAuditLog is a GLOBAL Omnify trait emitted by
390
+ // audit-trait-generator.ts at `config.models.baseNamespace\Traits`
391
+ // (e.g. App\Models\Omnify\Base\Traits). The import here must resolve to
392
+ // that SAME global namespace — using the per-model (group) `baseNamespace`
393
+ // (e.g. ...\Base\Logistics) yields `Base\Logistics\Traits\HasOmnifyAuditLog`
394
+ // which does not exist → "Trait not found" fatal on every model load.
393
395
  const auditTraitsNs = config
394
- ? resolveGlobalTraitNamespace(config, baseNamespace + '\\Traits')
396
+ ? resolveGlobalTraitNamespace(config, config.models.baseNamespace + '\\Traits')
395
397
  : (traitsNamespace || baseNamespace + '\\Traits');
396
398
  lines.push(`use ${auditTraitsNs}\\HasOmnifyAuditLog;`);
397
399
  }
@@ -407,8 +409,14 @@ function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable
407
409
  }
408
410
  return lines.join('\n') + '\n';
409
411
  }
410
- function buildDocProperties(properties, expandedProperties, propertyOrder) {
412
+ function buildDocProperties(properties, expandedProperties, propertyOrder, reader, config) {
411
413
  const lines = [];
414
+ // Mirror buildCasts: EnumRef columns are cast to their generated enum class,
415
+ // so document them with that class (not `string`) — otherwise static analysis
416
+ // sees a string and rejects `$model->col->value` / enum comparisons.
417
+ const globalEnumNs = config
418
+ ? resolveGlobalEnumNamespace(config, config.models.namespace)
419
+ : '';
412
420
  for (const propName of propertyOrder) {
413
421
  const prop = properties[propName];
414
422
  if (!prop)
@@ -443,6 +451,16 @@ function buildDocProperties(properties, expandedProperties, propertyOrder) {
443
451
  }
444
452
  const nullable = prop['nullable'] ?? false;
445
453
  const snakeName = toSnakeCase(propName);
454
+ // EnumRef → document the generated enum class (fully-qualified, matching the
455
+ // cast) only when the referenced enum schema exists; else fall through.
456
+ if (type === 'EnumRef') {
457
+ const enumName = prop['enum'];
458
+ if (typeof enumName === 'string' && reader.getSchema(enumName)?.kind === 'enum' && globalEnumNs) {
459
+ const enumType = `\\${globalEnumNs}\\${enumClassName(enumName)}`;
460
+ lines.push(` * @property ${nullable ? `${enumType}|null` : enumType} $${snakeName}`);
461
+ continue;
462
+ }
463
+ }
446
464
  const phpDocType = toPhpDocType(type, nullable);
447
465
  lines.push(` * @property ${phpDocType} $${snakeName}`);
448
466
  }
@@ -195,7 +195,7 @@ namespace ${baseNamespace};
195
195
  use Illuminate\\Foundation\\Http\\FormRequest;${ruleImport}
196
196
  use ${localesClass};
197
197
 
198
- abstract class ${modelName}${action}RequestBase extends FormRequest
198
+ class ${modelName}${action}RequestBase extends FormRequest
199
199
  {
200
200
  /**
201
201
  * Determine if the user is authorized to make this request.
@@ -277,7 +277,10 @@ ${attributeKeysList}
277
277
  const nested = nestByGroup({ path: raw.path, namespace: '' }, schema.group);
278
278
  baseDescriptor = { className: raw.className, path: nested.path };
279
279
  }
280
- const finalContent = content.replace(new RegExp(`class ${modelName}${action}RequestBase\\b`, 'g'), `class ${baseDescriptor.className}`).replace(new RegExp(`abstract class ${modelName}${action}RequestBase\\b`, 'g'), `abstract class ${baseDescriptor.className}`);
280
+ // Issue #114: the request base is the class controllers inject directly
281
+ // (#101) — so it must be CONCRETE, not abstract (an abstract FormRequest is
282
+ // not container-resolvable → BindingResolutionException / HTTP 500).
283
+ const finalContent = content.replace(new RegExp(`class ${modelName}${action}RequestBase\\b`, 'g'), `class ${baseDescriptor.className}`);
281
284
  return baseFile(`${baseDescriptor.path}/${baseDescriptor.className}.php`, finalContent);
282
285
  }
283
286
  function generateUserRequest(name, schema, config, action) {
@@ -5,9 +5,12 @@ import { SchemaReader } from './schema-reader.js';
5
5
  import type { GeneratedFile, PhpConfig } from './types.js';
6
6
  /** Generate Resource classes for all project-owned visible object schemas.
7
7
  *
8
- * Issue #101 v5.8.23: only the base is emitted. Empty editable stubs
9
- * (every project's 30+ thin `class XResource extends Base {}`) are
10
- * pure scaffolding noise devs hand-create wrappers only when custom
11
- * serialization logic IS needed.
8
+ * Issue #101 v5.8.23 emitted ONLY the base but child resources embed a
9
+ * parent relation via `new \App\Http\Resources\<Parent>Resource(...)` (the
10
+ * editable sibling). With the sibling gone, any loaded relation fatals with a
11
+ * "class not found" Error (issue #114). So the editable sibling is re-emitted
12
+ * as a create-if-missing user file (`overwrite: false`): written once, never
13
+ * regenerated (so it is not the scaffolding noise #101 removed), and the
14
+ * cross-resource reference always resolves.
12
15
  */
13
16
  export declare function generateResources(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
@@ -6,10 +6,13 @@ import { isHiddenByDefault, toResourceExpression } from './type-mapper.js';
6
6
  import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace, resolveBaseClass, resolveEditableClass, nestByGroup, nestEditableByGroup } from './types.js';
7
7
  /** Generate Resource classes for all project-owned visible object schemas.
8
8
  *
9
- * Issue #101 v5.8.23: only the base is emitted. Empty editable stubs
10
- * (every project's 30+ thin `class XResource extends Base {}`) are
11
- * pure scaffolding noise devs hand-create wrappers only when custom
12
- * serialization logic IS needed.
9
+ * Issue #101 v5.8.23 emitted ONLY the base but child resources embed a
10
+ * parent relation via `new \App\Http\Resources\<Parent>Resource(...)` (the
11
+ * editable sibling). With the sibling gone, any loaded relation fatals with a
12
+ * "class not found" Error (issue #114). So the editable sibling is re-emitted
13
+ * as a create-if-missing user file (`overwrite: false`): written once, never
14
+ * regenerated (so it is not the scaffolding noise #101 removed), and the
15
+ * cross-resource reference always resolves.
13
16
  */
14
17
  export function generateResources(reader, config) {
15
18
  const files = [];
@@ -17,6 +20,7 @@ export function generateResources(reader, config) {
17
20
  if (schema.kind === 'extend')
18
21
  continue;
19
22
  files.push(generateBaseResource(name, schema, reader, config));
23
+ files.push(generateUserResource(name, schema, config));
20
24
  }
21
25
  return files;
22
26
  }