@omnifyjp/ts 5.9.17 → 5.9.19
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/interface-generator.d.ts +9 -0
- package/dist/interface-generator.js +13 -1
- package/dist/metadata-generator.js +2 -2
- package/dist/payload-builder-generator.js +2 -2
- package/dist/php/faker-mapper.js +2 -3
- package/dist/php/model-generator.js +7 -23
- package/dist/php/naming-helper.d.ts +9 -0
- package/dist/php/naming-helper.js +12 -0
- package/dist/php/policy-generator.js +2 -2
- package/dist/php/relation-builder.js +4 -5
- package/dist/php/request-generator.js +2 -2
- package/dist/php/resource-generator.js +11 -5
- package/dist/php/timestamps.d.ts +28 -0
- package/dist/php/timestamps.js +23 -0
- package/package.json +1 -1
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
import type { SchemaDefinition, PropertyDefinition, TSInterface, TSProperty, GeneratorOptions } from './types.js';
|
|
9
9
|
/** Convert PascalCase/camelCase to snake_case. */
|
|
10
10
|
export declare function toSnakeCase(str: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Physical FK column name for an owning-side association property.
|
|
13
|
+
*
|
|
14
|
+
* The convention is `<property_snake>_id`, but a property already named with
|
|
15
|
+
* that suffix IS the column — `table_session_id` is what Laravel's `belongsTo`
|
|
16
|
+
* convention names it and what a legacy table already has. Appending
|
|
17
|
+
* unconditionally produced `table_session_id_id` (issue #149).
|
|
18
|
+
*/
|
|
19
|
+
export declare function toFkColumnName(propertyName: string): string;
|
|
11
20
|
/** Gets TypeScript type for a property. */
|
|
12
21
|
export declare function getPropertyType(property: PropertyDefinition, allSchemas: Record<string, SchemaDefinition>): string;
|
|
13
22
|
/**
|
|
@@ -12,6 +12,18 @@ export function toSnakeCase(str) {
|
|
|
12
12
|
.replace(/^_/, '')
|
|
13
13
|
.toLowerCase();
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Physical FK column name for an owning-side association property.
|
|
17
|
+
*
|
|
18
|
+
* The convention is `<property_snake>_id`, but a property already named with
|
|
19
|
+
* that suffix IS the column — `table_session_id` is what Laravel's `belongsTo`
|
|
20
|
+
* convention names it and what a legacy table already has. Appending
|
|
21
|
+
* unconditionally produced `table_session_id_id` (issue #149).
|
|
22
|
+
*/
|
|
23
|
+
export function toFkColumnName(propertyName) {
|
|
24
|
+
const snake = toSnakeCase(propertyName);
|
|
25
|
+
return snake.endsWith('_id') ? snake : `${snake}_id`;
|
|
26
|
+
}
|
|
15
27
|
/** Maps Omnify property types to TypeScript types. */
|
|
16
28
|
const TYPE_MAP = {
|
|
17
29
|
String: 'string',
|
|
@@ -184,7 +196,7 @@ export function propertyToTSProperties(propertyName, property, schema, allSchema
|
|
|
184
196
|
}
|
|
185
197
|
return [
|
|
186
198
|
{
|
|
187
|
-
name:
|
|
199
|
+
name: toFkColumnName(propertyName),
|
|
188
200
|
type: fkType,
|
|
189
201
|
optional: isNullable,
|
|
190
202
|
nullable: isNullable,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* The metadata is a *new* artifact emitted in addition to the existing
|
|
14
14
|
* interface, zod schemas, and i18n object — none of those are modified.
|
|
15
15
|
*/
|
|
16
|
-
import { toSnakeCase } from './interface-generator.js';
|
|
16
|
+
import { toSnakeCase, toFkColumnName } from './interface-generator.js';
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// Type classification — single source of truth so #55 (payload builder)
|
|
19
19
|
// can reuse the same field-type decisions.
|
|
@@ -178,7 +178,7 @@ export function buildSchemaMetadata(schema, allSchemas, options, displayNames) {
|
|
|
178
178
|
(prop.relation === 'ManyToOne' || prop.relation === 'OneToOne') &&
|
|
179
179
|
!prop.mappedBy;
|
|
180
180
|
const fieldKey = isOwningAssoc
|
|
181
|
-
?
|
|
181
|
+
? toFkColumnName(propName)
|
|
182
182
|
: toSnakeCase(propName);
|
|
183
183
|
const fieldType = classifyFieldType(prop, allSchemas, options.customTypes.simple);
|
|
184
184
|
const required = isFieldRequired(prop);
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* codegen output root in `payload-helpers.ts` so per-model files can
|
|
21
21
|
* import it.
|
|
22
22
|
*/
|
|
23
|
-
import { toSnakeCase } from './interface-generator.js';
|
|
23
|
+
import { toSnakeCase, toFkColumnName } from './interface-generator.js';
|
|
24
24
|
import { classifyFieldType } from './metadata-generator.js';
|
|
25
25
|
import { toEnumMemberName } from './enum-generator.js';
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
@@ -186,7 +186,7 @@ export function buildFormShape(schema, allSchemas, options) {
|
|
|
186
186
|
(prop.relation === 'ManyToOne' || prop.relation === 'OneToOne') &&
|
|
187
187
|
!prop.mappedBy;
|
|
188
188
|
const fieldName = isOwningAssoc
|
|
189
|
-
?
|
|
189
|
+
? toFkColumnName(propName)
|
|
190
190
|
: toSnakeCase(propName);
|
|
191
191
|
// rules.required overrides nullable inference
|
|
192
192
|
const required = prop.rules?.required === false
|
package/dist/php/faker-mapper.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of FakerMapper.php — generates Faker expressions for factories.
|
|
3
3
|
*/
|
|
4
|
-
import { toSnakeCase } from './naming-helper.js';
|
|
4
|
+
import { toSnakeCase, toFkColumnName } from './naming-helper.js';
|
|
5
5
|
import { resolveEnumValues } from './type-mapper.js';
|
|
6
6
|
/** Generate faker expression for a property. */
|
|
7
7
|
export function toFaker(propName, property, schemas = {}) {
|
|
@@ -69,8 +69,7 @@ export function associationFaker(propName, property, modelNamespace, currentSche
|
|
|
69
69
|
const target = property['target'] ?? '';
|
|
70
70
|
if (relation !== 'ManyToOne' || !target)
|
|
71
71
|
return null;
|
|
72
|
-
const
|
|
73
|
-
const foreignKey = `${snakeName}_id`;
|
|
72
|
+
const foreignKey = toFkColumnName(propName);
|
|
74
73
|
const nullable = property['nullable'] ?? false;
|
|
75
74
|
const fake = nullable
|
|
76
75
|
? `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of ModelGenerator.php — generates Eloquent model base + user classes.
|
|
3
3
|
*/
|
|
4
|
-
import { toPascalCase, toSnakeCase, toCamelCase, pluralize } from './naming-helper.js';
|
|
4
|
+
import { toPascalCase, toSnakeCase, toCamelCase, toFkColumnName, pluralize } from './naming-helper.js';
|
|
5
|
+
import { resolveTimestamps } from './timestamps.js';
|
|
5
6
|
import { toCast, toPhpDocType, isHiddenByDefault } from './type-mapper.js';
|
|
6
7
|
import { buildRelation } from './relation-builder.js';
|
|
7
8
|
import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace, resolveSharedBaseNamespace, resolveGlobalTraitNamespace, resolveGlobalEnumNamespace, resolveBaseClass, resolveEditableClass, nestByGroup, nestEditableByGroup, } from './types.js';
|
|
@@ -97,23 +98,7 @@ function generateBaseModel(name, schema, reader, config) {
|
|
|
97
98
|
// $timestamps = true and nulls the constant for the absent column — otherwise
|
|
98
99
|
// Eloquent writes a column the table does not have and every insert dies with
|
|
99
100
|
// "Unknown column 'created_at'" (omnify-go#143).
|
|
100
|
-
const
|
|
101
|
-
const resolveTimestampColumn = (camel, snake) => {
|
|
102
|
-
if (typeof timestampsOption === 'boolean')
|
|
103
|
-
return timestampsOption;
|
|
104
|
-
if (timestampsOption && typeof timestampsOption === 'object') {
|
|
105
|
-
const map = timestampsOption;
|
|
106
|
-
if (typeof map[camel] === 'boolean')
|
|
107
|
-
return map[camel];
|
|
108
|
-
if (typeof map[snake] === 'boolean')
|
|
109
|
-
return map[snake];
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
};
|
|
114
|
-
const hasCreatedAt = resolveTimestampColumn('createdAt', 'created_at');
|
|
115
|
-
const hasUpdatedAt = resolveTimestampColumn('updatedAt', 'updated_at');
|
|
116
|
-
const hasTimestamps = hasCreatedAt || hasUpdatedAt;
|
|
101
|
+
const { createdAt: hasCreatedAt, updatedAt: hasUpdatedAt, any: hasTimestamps } = resolveTimestamps(options);
|
|
117
102
|
// NodeTrait is added to the user-editable Model, not BaseModel
|
|
118
103
|
const hasNestedSet = false;
|
|
119
104
|
const nestedSetParentColumn = null;
|
|
@@ -456,7 +441,7 @@ function buildDocProperties(properties, expandedProperties, propertyOrder, reade
|
|
|
456
441
|
if (type === 'Association') {
|
|
457
442
|
const relation = prop['relation'] ?? '';
|
|
458
443
|
if (relation === 'ManyToOne') {
|
|
459
|
-
const snakeName =
|
|
444
|
+
const snakeName = toFkColumnName(propName);
|
|
460
445
|
const nullable = prop['nullable'] ?? false;
|
|
461
446
|
const phpType = nullable ? 'int|null' : 'int';
|
|
462
447
|
lines.push(` * @property ${phpType} $${snakeName}`);
|
|
@@ -533,7 +518,7 @@ function buildFillable(properties, expandedProperties, propertyOrder) {
|
|
|
533
518
|
if (type === 'Association') {
|
|
534
519
|
const relation = prop['relation'] ?? '';
|
|
535
520
|
if (relation === 'ManyToOne') {
|
|
536
|
-
fields.push(
|
|
521
|
+
fields.push(toFkColumnName(propName));
|
|
537
522
|
}
|
|
538
523
|
else if (relation === 'MorphTo') {
|
|
539
524
|
const morphName = prop['morphName'] || toSnakeCase(propName);
|
|
@@ -641,8 +626,7 @@ function buildCasts(properties, expandedProperties, propertyOrder, reader, confi
|
|
|
641
626
|
? targetSchema.options.id
|
|
642
627
|
: 'BigInt';
|
|
643
628
|
if (targetIdType !== 'Uuid' && targetIdType !== 'Ulid') {
|
|
644
|
-
|
|
645
|
-
casts.push(`'${snakeName}_id' => 'integer',`);
|
|
629
|
+
casts.push(`'${toFkColumnName(propName)}' => 'integer',`);
|
|
646
630
|
}
|
|
647
631
|
}
|
|
648
632
|
continue;
|
|
@@ -846,7 +830,7 @@ function buildInverseRelations(parentName, modelNamespace, reader, declaredMetho
|
|
|
846
830
|
// generate a child-side accessor, not a parent-side one.
|
|
847
831
|
// Here we're emitting the *parent* accessor, so the child's ManyToOne
|
|
848
832
|
// (or owning OneToOne) is what we react to.
|
|
849
|
-
const fkColumn =
|
|
833
|
+
const fkColumn = toFkColumnName(childPropName);
|
|
850
834
|
// Issue #98 v5.8.6: nest the inverse-side FQCN by the CHILD's
|
|
851
835
|
// group / Pivot/ so the relation resolves to the child's actual
|
|
852
836
|
// file location (which lives in `<childGroup>/<Child>.php` or
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/** Convert camelCase/PascalCase to snake_case. */
|
|
5
5
|
export declare function toSnakeCase(value: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Physical FK column name for an owning-side association property.
|
|
8
|
+
*
|
|
9
|
+
* The convention is `<property_snake>_id`, but a property already named with
|
|
10
|
+
* that suffix IS the column — `table_session_id` is what Laravel's `belongsTo`
|
|
11
|
+
* convention names it and what a legacy table already has. Appending
|
|
12
|
+
* unconditionally produced `table_session_id_id` (issue #149).
|
|
13
|
+
*/
|
|
14
|
+
export declare function toFkColumnName(propertyName: string): string;
|
|
6
15
|
/** Convert any string to PascalCase. */
|
|
7
16
|
export declare function toPascalCase(value: string): string;
|
|
8
17
|
/** Convert any string to camelCase. */
|
|
@@ -8,6 +8,18 @@ export function toSnakeCase(value) {
|
|
|
8
8
|
.replace(/^_/, '')
|
|
9
9
|
.toLowerCase();
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Physical FK column name for an owning-side association property.
|
|
13
|
+
*
|
|
14
|
+
* The convention is `<property_snake>_id`, but a property already named with
|
|
15
|
+
* that suffix IS the column — `table_session_id` is what Laravel's `belongsTo`
|
|
16
|
+
* convention names it and what a legacy table already has. Appending
|
|
17
|
+
* unconditionally produced `table_session_id_id` (issue #149).
|
|
18
|
+
*/
|
|
19
|
+
export function toFkColumnName(propertyName) {
|
|
20
|
+
const snake = toSnakeCase(propertyName);
|
|
21
|
+
return snake.endsWith('_id') ? snake : `${snake}_id`;
|
|
22
|
+
}
|
|
11
23
|
/** Convert any string to PascalCase. */
|
|
12
24
|
export function toPascalCase(value) {
|
|
13
25
|
return value
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Generates Policy classes from Cedar-style ABAC policy definitions.
|
|
5
5
|
* Only generates for schemas that have `policies` defined.
|
|
6
6
|
*/
|
|
7
|
-
import { toPascalCase, toSnakeCase, toCamelCase } from './naming-helper.js';
|
|
7
|
+
import { toPascalCase, toSnakeCase, toCamelCase, toFkColumnName } from './naming-helper.js';
|
|
8
8
|
import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace, resolveBaseClass, resolveEditableClass, nestByGroup, nestEditableByGroup } from './types.js';
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// Action mapping: Omnify → Laravel
|
|
@@ -337,7 +337,7 @@ function operandToPhp(operand, schema, reader, side) {
|
|
|
337
337
|
// Check if it's an association (ManyToOne) → use _id
|
|
338
338
|
const prop = schema.properties?.[propName];
|
|
339
339
|
if (prop?.relation === 'ManyToOne') {
|
|
340
|
-
return `$record->${
|
|
340
|
+
return `$record->${toFkColumnName(propName)}`;
|
|
341
341
|
}
|
|
342
342
|
return `$record->${toSnakeCase(propName)}`;
|
|
343
343
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of RelationBuilder.php — generates Eloquent relation methods.
|
|
3
3
|
*/
|
|
4
|
-
import { toSnakeCase, toCamelCase, singularize } from './naming-helper.js';
|
|
4
|
+
import { toSnakeCase, toCamelCase, toFkColumnName, singularize } from './naming-helper.js';
|
|
5
5
|
/** Build Eloquent relation method PHP code. */
|
|
6
6
|
export function buildRelation(propName, property, modelNamespace, context) {
|
|
7
7
|
const relation = property['relation'] ?? '';
|
|
@@ -62,8 +62,7 @@ export function getReturnType(relation) {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
function belongsTo(propName, target, ns) {
|
|
65
|
-
const
|
|
66
|
-
const foreignKey = `${snakeName}_id`;
|
|
65
|
+
const foreignKey = toFkColumnName(propName);
|
|
67
66
|
const methodName = toCamelCase(propName);
|
|
68
67
|
const fqcn = `\\${ns}\\${target}`;
|
|
69
68
|
return ` /**
|
|
@@ -79,7 +78,7 @@ function oneToOne(propName, property, target, ns) {
|
|
|
79
78
|
const methodName = toCamelCase(propName);
|
|
80
79
|
const fqcn = `\\${ns}\\${target}`;
|
|
81
80
|
if (mappedBy) {
|
|
82
|
-
const foreignKey =
|
|
81
|
+
const foreignKey = toFkColumnName(mappedBy);
|
|
83
82
|
return ` /**
|
|
84
83
|
* Get the ${propName} associated with this model.
|
|
85
84
|
*/
|
|
@@ -92,7 +91,7 @@ function oneToOne(propName, property, target, ns) {
|
|
|
92
91
|
}
|
|
93
92
|
function hasMany(propName, property, target, ns) {
|
|
94
93
|
const inverse = property['mappedBy'] ?? property['inversedBy'] ?? null;
|
|
95
|
-
const foreignKey = inverse ?
|
|
94
|
+
const foreignKey = inverse ? toFkColumnName(inverse) : toFkColumnName(propName);
|
|
96
95
|
const methodName = toCamelCase(propName);
|
|
97
96
|
const fqcn = `\\${ns}\\${target}`;
|
|
98
97
|
const orderChain = formatOrderByChain(property['orderBy']);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of RequestGenerator.php — generates Store/Update request classes.
|
|
3
3
|
*/
|
|
4
|
-
import { toPascalCase, toSnakeCase, toCamelCase } from './naming-helper.js';
|
|
4
|
+
import { toPascalCase, toSnakeCase, toCamelCase, toFkColumnName } from './naming-helper.js';
|
|
5
5
|
import { toStoreRules, toUpdateRules, formatRules, hasRuleObject } from './type-mapper.js';
|
|
6
6
|
import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace, resolveBaseClass, resolveEditableClass, nestByGroup, nestEditableByGroup } from './types.js';
|
|
7
7
|
/**
|
|
@@ -106,7 +106,7 @@ function generateBaseRequest(name, schema, reader, config, action) {
|
|
|
106
106
|
if (type === 'Association') {
|
|
107
107
|
const relation = prop['relation'] ?? '';
|
|
108
108
|
if (relation === 'ManyToOne') {
|
|
109
|
-
const snakeName =
|
|
109
|
+
const snakeName = toFkColumnName(propName);
|
|
110
110
|
const target = prop['target'] ?? '';
|
|
111
111
|
const targetIdType = resolveFkRuleType(target, reader);
|
|
112
112
|
const rules = isUpdate
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of ResourceGenerator.php — generates API Resource classes.
|
|
3
3
|
*/
|
|
4
|
-
import { toPascalCase, toSnakeCase, toCamelCase } from './naming-helper.js';
|
|
4
|
+
import { toPascalCase, toSnakeCase, toCamelCase, toFkColumnName } from './naming-helper.js';
|
|
5
|
+
import { resolveTimestamps } from './timestamps.js';
|
|
5
6
|
import { isHiddenByDefault, toResourceExpression } from './type-mapper.js';
|
|
6
7
|
import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace, resolveBaseClass, resolveEditableClass, nestByGroup, nestEditableByGroup } from './types.js';
|
|
7
8
|
/** Generate Resource classes for all project-owned visible object schemas.
|
|
@@ -39,7 +40,7 @@ function generateBaseResource(name, schema, reader, config) {
|
|
|
39
40
|
const expandedProperties = reader.getExpandedProperties(name);
|
|
40
41
|
const propertyOrder = reader.getPropertyOrder(name);
|
|
41
42
|
const options = schema.options ?? {};
|
|
42
|
-
const
|
|
43
|
+
const timestamps = resolveTimestamps(options);
|
|
43
44
|
const hasSoftDelete = options.softDelete ?? false;
|
|
44
45
|
const hasId = options.id ?? true;
|
|
45
46
|
const fields = [];
|
|
@@ -87,8 +88,13 @@ function generateBaseResource(name, schema, reader, config) {
|
|
|
87
88
|
if (translatableFields.length > 0) {
|
|
88
89
|
fields.push(" 'translations' => $this->getTranslationsArray(),");
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
+
// Emit each column on its own condition — a table declaring only one of
|
|
92
|
+
// the two would otherwise expose a field the model never fills, or hide one
|
|
93
|
+
// it does (omnify-go#151).
|
|
94
|
+
if (timestamps.createdAt) {
|
|
91
95
|
fields.push(" 'created_at' => $this->created_at?->toISOString(),");
|
|
96
|
+
}
|
|
97
|
+
if (timestamps.updatedAt) {
|
|
92
98
|
fields.push(" 'updated_at' => $this->updated_at?->toISOString(),");
|
|
93
99
|
}
|
|
94
100
|
if (hasSoftDelete) {
|
|
@@ -239,7 +245,7 @@ function addAssociationFields(propName, prop, fields, resourceNamespace, modelNa
|
|
|
239
245
|
// If target is a package schema without a resource namespace, use simple whenLoaded
|
|
240
246
|
if (!targetResNs && relation !== 'MorphTo') {
|
|
241
247
|
if (relation === 'ManyToOne') {
|
|
242
|
-
const snakeName =
|
|
248
|
+
const snakeName = toFkColumnName(propName);
|
|
243
249
|
fields.push(` '${snakeName}' => $this->${snakeName},`);
|
|
244
250
|
}
|
|
245
251
|
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}'),`);
|
|
@@ -248,7 +254,7 @@ function addAssociationFields(propName, prop, fields, resourceNamespace, modelNa
|
|
|
248
254
|
const targetResource = toPascalCase(target) + 'Resource';
|
|
249
255
|
switch (relation) {
|
|
250
256
|
case 'ManyToOne': {
|
|
251
|
-
const snakeName =
|
|
257
|
+
const snakeName = toFkColumnName(propName);
|
|
252
258
|
fields.push(` '${snakeName}' => $this->${snakeName},`);
|
|
253
259
|
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => new \\${targetResNs}\\${targetResource}($this->${methodName})),`);
|
|
254
260
|
break;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp column resolution — the single source of truth for "does this
|
|
3
|
+
* table have created_at / updated_at", shared by every PHP generator.
|
|
4
|
+
*
|
|
5
|
+
* This MUST agree with the Go side (`SchemaOptions.HasCreatedAt` /
|
|
6
|
+
* `HasUpdatedAt` in internal/schema/types.go), because the Go side is what
|
|
7
|
+
* writes the migration. When the two disagree the failure is silent: the
|
|
8
|
+
* column exists in the database and the model refuses to fill it (or the
|
|
9
|
+
* reverse, and every insert dies on an unknown column).
|
|
10
|
+
*
|
|
11
|
+
* The rules, in Go's words:
|
|
12
|
+
* - option absent → BOTH columns (the long-standing default)
|
|
13
|
+
* - `timestamps: false` → neither
|
|
14
|
+
* - `timestamps: true` → both
|
|
15
|
+
* - object form → only the keys explicitly set to true
|
|
16
|
+
* (a key absent from the map means that column is absent — this is the
|
|
17
|
+
* one place where "unset" does NOT mean "default on", because naming the
|
|
18
|
+
* map at all is the author declaring the columns individually)
|
|
19
|
+
*/
|
|
20
|
+
export interface TimestampColumns {
|
|
21
|
+
createdAt: boolean;
|
|
22
|
+
updatedAt: boolean;
|
|
23
|
+
/** True when the model should carry `$timestamps = true`. */
|
|
24
|
+
any: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function resolveTimestamps(options: {
|
|
27
|
+
timestamps?: unknown;
|
|
28
|
+
} | undefined | null): TimestampColumns;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function resolveTimestamps(options) {
|
|
2
|
+
// Absent means both — matching the Go default. Reading it as `false` made
|
|
3
|
+
// every schema that never mentioned `timestamps` generate
|
|
4
|
+
// `public $timestamps = false;` against a table the migration gave both
|
|
5
|
+
// columns, so Eloquent silently stopped stamping them (omnify-go#151).
|
|
6
|
+
const raw = (options?.timestamps ?? true);
|
|
7
|
+
const column = (camel, snake) => {
|
|
8
|
+
if (typeof raw === 'boolean')
|
|
9
|
+
return raw;
|
|
10
|
+
if (raw && typeof raw === 'object') {
|
|
11
|
+
const map = raw;
|
|
12
|
+
if (typeof map[camel] === 'boolean')
|
|
13
|
+
return map[camel];
|
|
14
|
+
if (typeof map[snake] === 'boolean')
|
|
15
|
+
return map[snake];
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
const createdAt = column('createdAt', 'created_at');
|
|
21
|
+
const updatedAt = column('updatedAt', 'updated_at');
|
|
22
|
+
return { createdAt, updatedAt, any: createdAt || updatedAt };
|
|
23
|
+
}
|