@omnifyjp/omnify 5.9.4 → 5.9.6

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.
@@ -538,6 +538,11 @@
538
538
  "default": true,
539
539
  "description": "Whether this field is mass assignable (Laravel)"
540
540
  },
541
+ "storedAs": {
542
+ "type": "string",
543
+ "minLength": 1,
544
+ "description": "SQL expression for a database-generated stored column. Such columns are excluded from mass assignment and request validation."
545
+ },
541
546
  "searchable": {
542
547
  "type": "boolean",
543
548
  "description": "Include in full-text search (?search=keyword)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/omnify",
3
- "version": "5.9.4",
3
+ "version": "5.9.6",
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.4",
40
- "@omnifyjp/omnify-darwin-x64": "5.9.4",
41
- "@omnifyjp/omnify-linux-x64": "5.9.4",
42
- "@omnifyjp/omnify-linux-arm64": "5.9.4",
43
- "@omnifyjp/omnify-win32-x64": "5.9.4"
39
+ "@omnifyjp/omnify-darwin-arm64": "5.9.6",
40
+ "@omnifyjp/omnify-darwin-x64": "5.9.6",
41
+ "@omnifyjp/omnify-linux-x64": "5.9.6",
42
+ "@omnifyjp/omnify-linux-arm64": "5.9.6",
43
+ "@omnifyjp/omnify-win32-x64": "5.9.6"
44
44
  }
45
45
  }
@@ -158,6 +158,10 @@ export function buildSchemaMetadata(schema, allSchemas, options, displayNames) {
158
158
  const prop = schema.properties[propName];
159
159
  if (!prop)
160
160
  continue;
161
+ // Metadata drives generic create/update forms. Stored generated columns
162
+ // remain on read models but are not caller-writable form fields.
163
+ if (typeof prop.storedAs === 'string')
164
+ continue;
161
165
  // Skip inverse associations — they don't carry a column.
162
166
  const isInverseAssoc = prop.type === 'Association' &&
163
167
  (Boolean(prop.mappedBy) ||
@@ -164,6 +164,10 @@ export function buildFormShape(schema, allSchemas, options) {
164
164
  const prop = schema.properties[propName];
165
165
  if (!prop)
166
166
  continue;
167
+ // Database-generated columns remain available on read models but are not
168
+ // form state and must never enter create/update payloads.
169
+ if (typeof prop.storedAs === 'string')
170
+ continue;
167
171
  // Skip inverse-side associations — they have no column.
168
172
  if (prop.type === 'Association' &&
169
173
  (prop.mappedBy || prop.relation === 'OneToMany' ||
@@ -494,6 +494,8 @@ function buildFillable(properties, expandedProperties, propertyOrder) {
494
494
  if (!prop)
495
495
  continue;
496
496
  const type = prop['type'] ?? 'String';
497
+ if (typeof prop['storedAs'] === 'string')
498
+ continue;
497
499
  const fillable = prop['fillable'] ?? true;
498
500
  if (!fillable)
499
501
  continue;
@@ -64,6 +64,10 @@ function generateBaseRequest(name, schema, reader, config, action) {
64
64
  const prop = properties[propName];
65
65
  if (!prop)
66
66
  continue;
67
+ // Stored generated columns are computed by the database and must never be
68
+ // accepted from create/update payloads.
69
+ if (typeof prop['storedAs'] === 'string')
70
+ continue;
67
71
  const type = prop['type'] ?? 'String';
68
72
  // Compound type: generate rules for expanded columns
69
73
  if (expandedProperties[propName]) {
@@ -307,6 +307,8 @@ export interface PropertyDefinition {
307
307
  readonly unique?: boolean;
308
308
  readonly primary?: boolean;
309
309
  readonly translatable?: boolean;
310
+ /** Database-generated stored column expression. Generated columns are read-only. */
311
+ readonly storedAs?: string;
310
312
  readonly default?: unknown;
311
313
  readonly length?: number;
312
314
  readonly minLength?: number;
@@ -437,6 +437,13 @@ export function getExcludedFields(schema) {
437
437
  createExclude.add('deleted_at');
438
438
  updateExclude.add('deleted_at');
439
439
  }
440
+ for (const [propName, prop] of Object.entries(schema.properties ?? {})) {
441
+ if (typeof prop.storedAs !== 'string')
442
+ continue;
443
+ const fieldName = toSnakeCase(propName);
444
+ createExclude.add(fieldName);
445
+ updateExclude.add(fieldName);
446
+ }
440
447
  return { create: createExclude, update: updateExclude };
441
448
  }
442
449
  /**
package/types/schema.d.ts CHANGED
@@ -201,6 +201,8 @@ export interface PropertyDefinition {
201
201
  primary?: boolean;
202
202
  hidden?: boolean;
203
203
  fillable?: boolean;
204
+ /** Database-generated stored column expression. Generated columns are read-only. */
205
+ storedAs?: string;
204
206
  /**
205
207
  * Mark field as translatable (Astrotomic/laravel-translatable).
206
208
  * When true: