metal-orm 1.0.93 → 1.0.95

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.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Base interface for country-specific identifier validators
3
+ */
4
+ export interface CountryValidator<T = string> {
5
+ /** ISO 3166-1 alpha-2 country code (e.g., 'BR', 'US') */
6
+ readonly countryCode: string;
7
+
8
+ /** Identifier type (e.g., 'cpf', 'cnpj', 'ssn', 'zip') */
9
+ readonly identifierType: string;
10
+
11
+ /** Unique validator name (e.g., 'br-cpf', 'us-ssn') */
12
+ readonly name: string;
13
+
14
+ /**
15
+ * Validates an identifier value
16
+ * @param value - The identifier value to validate
17
+ * @param options - Validation options
18
+ * @returns Validation result with success status and optional error message
19
+ */
20
+ validate(value: T, options?: ValidationOptions): ValidationResult;
21
+
22
+ /**
23
+ * Normalizes an identifier value (removes formatting, standardizes)
24
+ * @param value - The identifier value to normalize
25
+ * @returns Normalized value (digits only, uppercase, etc.)
26
+ */
27
+ normalize(value: T): string;
28
+
29
+ /**
30
+ * Formats an identifier value according to country standards
31
+ * @param value - The identifier value to format (can be normalized or formatted)
32
+ * @returns Formatted value (e.g., '123.456.789-01' for CPF)
33
+ */
34
+ format(value: T): string;
35
+
36
+ /**
37
+ * Attempts to auto-correct an invalid value
38
+ * @param value - The invalid value to correct
39
+ * @returns Auto-correction result or undefined if not possible
40
+ */
41
+ autoCorrect?(value: T): AutoCorrectionResult<T>;
42
+ }
43
+
44
+ /**
45
+ * Validation options for country identifiers
46
+ */
47
+ export interface ValidationOptions {
48
+ /** Whether to allow formatted input (e.g., '123.456.789-01') */
49
+ allowFormatted?: boolean;
50
+
51
+ /** Whether to allow normalized input (e.g., '12345678901') */
52
+ allowNormalized?: boolean;
53
+
54
+ /** Whether to perform strict validation (e.g., check for known invalid numbers) */
55
+ strict?: boolean;
56
+
57
+ /** Custom error message */
58
+ errorMessage?: string;
59
+ }
60
+
61
+ /**
62
+ * Validation result
63
+ */
64
+ export interface ValidationResult {
65
+ /** Whether validation passed */
66
+ isValid: boolean;
67
+
68
+ /** Error message if validation failed */
69
+ error?: string;
70
+
71
+ /** Normalized value (if validation passed) */
72
+ normalizedValue?: string;
73
+
74
+ /** Formatted value (if validation passed) */
75
+ formattedValue?: string;
76
+ }
77
+
78
+ /**
79
+ * Auto-correction result
80
+ */
81
+ export interface AutoCorrectionResult<T = string> {
82
+ /** Whether auto-correction was successful */
83
+ success: boolean;
84
+
85
+ /** Corrected value */
86
+ correctedValue?: T;
87
+
88
+ /** Description of what was corrected */
89
+ message?: string;
90
+ }
91
+
92
+ // Country Validator Factory
93
+ export type CountryValidatorFactory<T = string> = (options?: ValidatorFactoryOptions) => CountryValidator<T>;
94
+
95
+ // Options for creating a validator instance
96
+ export interface ValidatorFactoryOptions {
97
+ /** Custom validation rules */
98
+ customRules?: Record<string, unknown>;
99
+
100
+ /** Whether to enable strict mode by default */
101
+ strict?: boolean;
102
+
103
+ /** Custom error messages */
104
+ errorMessages?: Record<string, string>;
105
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Validation options for country identifiers
3
+ */
4
+ export interface ValidationOptions {
5
+ /** Whether to allow formatted input (e.g., '123.456.789-01') */
6
+ allowFormatted?: boolean;
7
+
8
+ /** Whether to allow normalized input (e.g., '12345678901') */
9
+ allowNormalized?: boolean;
10
+
11
+ /** Whether to perform strict validation (e.g., check for known invalid numbers) */
12
+ strict?: boolean;
13
+
14
+ /** Custom error message */
15
+ errorMessage?: string;
16
+ }
17
+
18
+ /**
19
+ * Validation result
20
+ */
21
+ export interface ValidationResult {
22
+ /** Whether validation passed */
23
+ isValid: boolean;
24
+
25
+ /** Error message if validation failed */
26
+ error?: string;
27
+
28
+ /** Normalized value (if validation passed) */
29
+ normalizedValue?: string;
30
+
31
+ /** Formatted value (if validation passed) */
32
+ formattedValue?: string;
33
+ }
34
+
35
+ /**
36
+ * Auto-correction result
37
+ */
38
+ export interface AutoCorrectionResult<T = string> {
39
+ /** Whether auto-correction was successful */
40
+ success: boolean;
41
+
42
+ /** Corrected value */
43
+ correctedValue?: T;
44
+
45
+ /** Description of what was corrected */
46
+ message?: string;
47
+ }
48
+
49
+ // Options for creating a validator instance
50
+ export interface ValidatorFactoryOptions {
51
+ /** Custom validation rules */
52
+ customRules?: Record<string, unknown>;
53
+
54
+ /** Whether to enable strict mode by default */
55
+ strict?: boolean;
56
+
57
+ /** Custom error messages */
58
+ errorMessages?: Record<string, string>;
59
+ }
@@ -26,6 +26,7 @@ export function applyNullability(
26
26
  dialect: OpenApiDialect
27
27
  ): OpenApiSchema {
28
28
  if (!isNullable) {
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
30
  const { nullable: _, ...clean } = schema;
30
31
  return clean;
31
32
  }
@@ -37,17 +38,21 @@ export function applyNullability(
37
38
  const type = schema.type;
38
39
  if (Array.isArray(type)) {
39
40
  if (!type.includes('null')) {
41
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
42
  const { nullable: _, ...clean } = schema;
41
43
  return { ...clean, type: [...type, 'null'] as OpenApiType[] };
42
44
  }
43
45
  } else if (type) {
46
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44
47
  const { nullable: _, ...clean } = schema;
45
48
  return { ...clean, type: [type, 'null'] as OpenApiType[] };
46
49
  } else {
50
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
51
  const { nullable: _, ...clean } = schema;
48
52
  return { ...clean, type: ['null'] as OpenApiType[] };
49
53
  }
50
54
 
55
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
56
  const { nullable: _, ...clean } = schema;
52
57
  return clean;
53
58
  }
package/src/index.ts CHANGED
@@ -33,9 +33,10 @@ export * from './core/functions/array.js';
33
33
  export * from './orm/als.js';
34
34
  export * from './orm/hydration.js';
35
35
  export * from './codegen/typescript.js';
36
- export * from './orm/orm-session.js';
37
- export * from './orm/orm.js';
38
- export * from './orm/entity.js';
36
+ export * from './orm/orm-session.js';
37
+ export * from './orm/orm.js';
38
+ export * from './orm/column-introspection.js';
39
+ export * from './orm/entity.js';
39
40
  export * from './orm/lazy-batch.js';
40
41
  export * from './orm/relations/has-many.js';
41
42
  export * from './orm/relations/belongs-to.js';
@@ -0,0 +1,43 @@
1
+ import { normalizeColumnType } from '../schema/column-types.js';
2
+ import type { ColumnType } from '../schema/column-types.js';
3
+ import type { TableDef } from '../schema/table.js';
4
+ import type { ColumnDefLike, EntityConstructor } from './entity-metadata.js';
5
+ import { getEntityMetadata } from './entity-metadata.js';
6
+
7
+ type ColumnTarget = TableDef | EntityConstructor;
8
+
9
+ const getColumnDefFromTarget = (
10
+ target: ColumnTarget,
11
+ column: string
12
+ ): ColumnDefLike | undefined => {
13
+ if (typeof target === 'function') {
14
+ const meta = getEntityMetadata(target);
15
+ if (!meta?.columns) return undefined;
16
+ return meta.columns[column];
17
+ }
18
+
19
+ const table = target as TableDef;
20
+ return table.columns[column];
21
+ };
22
+
23
+ export const getColumnType = (
24
+ target: ColumnTarget,
25
+ column: string
26
+ ): ColumnType | undefined => {
27
+ const col = getColumnDefFromTarget(target, column);
28
+ if (!col?.type) return undefined;
29
+ return normalizeColumnType(col.type);
30
+ };
31
+
32
+ export const getDateKind = (
33
+ target: ColumnTarget,
34
+ column: string
35
+ ): 'date' | 'date-time' | undefined => {
36
+ const type = getColumnType(target, column);
37
+ if (!type) return undefined;
38
+ if (type === 'date') return 'date';
39
+ if (type === 'datetime' || type === 'timestamp' || type === 'timestamptz') {
40
+ return 'date-time';
41
+ }
42
+ return undefined;
43
+ };
@@ -1,6 +1,7 @@
1
1
  import { ColumnDef } from '../schema/column-types.js';
2
2
  import { defineTable, TableDef, TableHooks } from '../schema/table.js';
3
3
  import { CascadeMode, RelationKinds } from '../schema/relation.js';
4
+ import type { TransformerMetadata } from '../decorators/transformers/transformer-metadata.js';
4
5
 
5
6
  /**
6
7
  * Constructor type for entities.
@@ -21,11 +22,11 @@ export type EntityOrTableTarget = EntityConstructor | TableDef;
21
22
  export type EntityOrTableTargetResolver<T extends EntityOrTableTarget = EntityOrTableTarget> =
22
23
  T | (() => T);
23
24
 
24
- /**
25
- * Simplified column definition structure used during metadata registration.
26
- * @template T - Concrete column definition type being extended
27
- */
28
- export type ColumnDefLike<T extends ColumnDef = ColumnDef> = Omit<T, 'table'>;
25
+ /**
26
+ * Simplified column definition structure used during metadata registration.
27
+ * @template T - Concrete column definition type being extended
28
+ */
29
+ export type ColumnDefLike<T extends ColumnDef = ColumnDef> = Omit<T, 'table'>;
29
30
 
30
31
  /**
31
32
  * Transforms simplified column metadata into full ColumnDef objects during table building.
@@ -53,26 +54,26 @@ interface BaseRelationMetadata {
53
54
  /**
54
55
  * Metadata for has many relations.
55
56
  */
56
- export interface HasManyRelationMetadata extends BaseRelationMetadata {
57
- /** The relation kind */
58
- kind: typeof RelationKinds.HasMany;
59
- /** The foreign key */
60
- foreignKey?: string;
61
- /** Optional local key */
62
- localKey?: string;
63
- }
57
+ export interface HasManyRelationMetadata extends BaseRelationMetadata {
58
+ /** The relation kind */
59
+ kind: typeof RelationKinds.HasMany;
60
+ /** The foreign key */
61
+ foreignKey?: string;
62
+ /** Optional local key */
63
+ localKey?: string;
64
+ }
64
65
 
65
66
  /**
66
67
  * Metadata for has one relations.
67
68
  */
68
- export interface HasOneRelationMetadata extends BaseRelationMetadata {
69
- /** The relation kind */
70
- kind: typeof RelationKinds.HasOne;
71
- /** The foreign key */
72
- foreignKey?: string;
73
- /** Optional local key */
74
- localKey?: string;
75
- }
69
+ export interface HasOneRelationMetadata extends BaseRelationMetadata {
70
+ /** The relation kind */
71
+ kind: typeof RelationKinds.HasOne;
72
+ /** The foreign key */
73
+ foreignKey?: string;
74
+ /** Optional local key */
75
+ localKey?: string;
76
+ }
76
77
 
77
78
  /**
78
79
  * Metadata for belongs to relations.
@@ -89,20 +90,20 @@ export interface BelongsToRelationMetadata extends BaseRelationMetadata {
89
90
  /**
90
91
  * Metadata for belongs to many relations.
91
92
  */
92
- export interface BelongsToManyRelationMetadata extends BaseRelationMetadata {
93
- /** The relation kind */
94
- kind: typeof RelationKinds.BelongsToMany;
95
- /** The pivot table */
96
- pivotTable: EntityOrTableTargetResolver;
97
- /** The pivot foreign key to root */
98
- pivotForeignKeyToRoot?: string;
99
- /** The pivot foreign key to target */
100
- pivotForeignKeyToTarget?: string;
101
- /** Optional local key */
102
- localKey?: string;
103
- /** Optional target key */
104
- targetKey?: string;
105
- /** Optional pivot primary key */
93
+ export interface BelongsToManyRelationMetadata extends BaseRelationMetadata {
94
+ /** The relation kind */
95
+ kind: typeof RelationKinds.BelongsToMany;
96
+ /** The pivot table */
97
+ pivotTable: EntityOrTableTargetResolver;
98
+ /** The pivot foreign key to root */
99
+ pivotForeignKeyToRoot?: string;
100
+ /** The pivot foreign key to target */
101
+ pivotForeignKeyToTarget?: string;
102
+ /** Optional local key */
103
+ localKey?: string;
104
+ /** Optional target key */
105
+ targetKey?: string;
106
+ /** Optional pivot primary key */
106
107
  pivotPrimaryKey?: string;
107
108
  /** Optional default pivot columns */
108
109
  defaultPivotColumns?: string[];
@@ -130,6 +131,8 @@ export interface EntityMetadata<TColumns extends Record<string, ColumnDefLike> =
130
131
  columns: TColumns;
131
132
  /** The relations */
132
133
  relations: Record<string, RelationMetadata>;
134
+ /** The transformers */
135
+ transformers: Record<string, TransformerMetadata>;
133
136
  /** Optional hooks */
134
137
  hooks?: TableHooks;
135
138
  /** Optional table definition */
@@ -158,7 +161,8 @@ export const ensureEntityMetadata = (target: EntityConstructor): EntityMetadata
158
161
  target,
159
162
  tableName: target.name || 'unknown',
160
163
  columns: {},
161
- relations: {}
164
+ relations: {},
165
+ transformers: {}
162
166
  };
163
167
  metadataMap.set(target, meta);
164
168
  }
@@ -219,6 +223,21 @@ export const addRelationMetadata = (
219
223
  meta.relations[propertyKey] = relation;
220
224
  };
221
225
 
226
+ /**
227
+ * Adds transformer metadata to an entity.
228
+ * @param target - The entity constructor
229
+ * @param propertyKey - The property key
230
+ * @param transformer - The transformer metadata
231
+ */
232
+ export const addTransformerMetadata = (
233
+ target: EntityConstructor,
234
+ propertyKey: string,
235
+ transformer: TransformerMetadata
236
+ ): void => {
237
+ const meta = ensureEntityMetadata(target);
238
+ meta.transformers[propertyKey] = transformer;
239
+ };
240
+
222
241
  /**
223
242
  * Sets the table name and hooks for an entity.
224
243
  * @param target - The entity constructor
@@ -250,15 +269,15 @@ export const buildTableDef = <TColumns extends Record<string, ColumnDefLike>>(me
250
269
  return meta.table;
251
270
  }
252
271
 
253
- // Build columns using a simpler approach that avoids type assertion
254
- const columns: Record<string, ColumnDef> = {};
255
- for (const [key, def] of Object.entries(meta.columns)) {
256
- columns[key] = {
257
- ...def,
258
- name: def.name ?? key,
259
- table: meta.tableName
260
- } as ColumnDef;
261
- }
272
+ // Build columns using a simpler approach that avoids type assertion
273
+ const columns: Record<string, ColumnDef> = {};
274
+ for (const [key, def] of Object.entries(meta.columns)) {
275
+ columns[key] = {
276
+ ...def,
277
+ name: def.name ?? key,
278
+ table: meta.tableName
279
+ } as ColumnDef;
280
+ }
262
281
 
263
282
  const table = defineTable(meta.tableName, columns as MaterializeColumns<TColumns>, {}, meta.hooks);
264
283
  meta.table = table;