-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodel.ts
More file actions
217 lines (197 loc) · 6.36 KB
/
Copy pathmodel.ts
File metadata and controls
217 lines (197 loc) · 6.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* @file model.ts
* @description Provides a programmatic API for defining models and a metadata storage system.
* @author ElectronSz
*/
import type { QueryBuilder } from "./query-builder";
import { DataTypes, RelationType, type DefaultExpression } from "./types";
// Interface for column configuration
export interface ColumnConfig {
name?: string;
type: DataTypes;
length?: number;
precision?: number;
scale?: number;
required?: boolean;
unique?: boolean;
defaultValue?: any;
defaultExpression?: DefaultExpression;
index?: string;
softDelete?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
customValidator?: (val: any) => boolean | string;
encrypted?: boolean;
optimisticLock?: boolean;
}
// Interface for relationship configuration
export interface RelationConfig {
type: RelationType;
target: () => any; // Reference to another model
property: string; // Property name in the model
foreignKey?: string;
inverseKey?: string;
joinTable?: string;
}
export interface TimestampsConfig {
createdAt?: string;
updatedAt?: string;
}
// Interface for model configuration
export interface ModelConfig {
tableName: string;
versioned?: boolean;
softDelete?: boolean;
columns: Record<string, ColumnConfig>;
relations?: RelationConfig[];
scopes?: Record<
string,
(qb: QueryBuilder<any>, ...args: any[]) => QueryBuilder<any>
>; // Custom query scopes
timestamps?: TimestampsConfig; // Auto-managed timestamp columns
}
/**
* Metadata storage for models.
* Stores and retrieves model configuration such as columns, relations, scopes, etc.
*/
export class MetadataStorage {
private static models: Map<Function, ModelConfig> = new Map();
/**
* Associates model metadata with a class constructor.
* @param model - The class constructor for the model.
* @param config - The model configuration object.
*/
static setModelMetadata(model: Function, config: ModelConfig) {
this.models.set(model, config);
}
/**
* Retrieves the model configuration for a given model class.
* @param model - The class constructor for the model.
* @returns The model configuration or undefined if not found.
*/
static getModelMetadata(model: Function): ModelConfig | undefined {
return this.models.get(model);
}
/**
* Gets the table name for a given model class.
* @param model - The class constructor for the model.
* @returns The table name or an empty string if not found.
*/
static getTableName(model: Function): string {
return this.getModelMetadata(model)?.tableName || "";
}
/**
* Gets the column configuration for a given model class.
* @param model - The class constructor for the model.
* @returns Record of column names to their configuration.
*/
static getColumns(model: Function): Record<string, ColumnConfig> {
return this.getModelMetadata(model)?.columns || {};
}
/**
* Collects validation rules for each column of a given model.
* @param model - The class constructor for the model.
* @returns An object mapping column names to an array of validation rule names.
*/
static getValidators(model: Function): Record<string, string[]> {
const columns = this.getModelMetadata(model)?.columns || {};
const validators: Record<string, string[]> = {};
for (const [key, col] of Object.entries(columns)) {
const rules: string[] = [];
if (col.required) rules.push("required");
if (col.unique) rules.push("unique");
validators[key] = rules;
}
return validators;
}
/**
* Gets the relationship configuration for a given model class.
* @param model - The class constructor for the model.
* @returns Record of property names to their relation configuration.
*/
static getRelations(model: Function): Record<string, RelationConfig> {
const relations = this.getModelMetadata(model)?.relations || [];
const result: Record<string, RelationConfig> = {};
for (const rel of relations) {
result[rel.property] = rel;
}
return result;
}
/**
* Finds the soft delete field, if any, for a given model class.
* @param model - The class constructor for the model.
* @returns The key of the soft delete field, or null if not found.
*/
static getSoftDeleteField(model: Function): string | null {
const columns = this.getModelMetadata(model)?.columns || {};
for (const [key, col] of Object.entries(columns)) {
if (col.softDelete) return key;
}
return null;
}
/**
* Checks if the model is versioned.
* @param model - The class constructor for the model.
* @returns True if versioned, false otherwise.
*/
static isVersioned(model: Function): boolean {
return !!this.getModelMetadata(model)?.versioned;
}
/**
* Gets custom query scopes for a given model class.
* @param model - The class constructor for the model.
* @returns Record of scope names to scope functions.
*/
static getScopes(
model: Function,
): Record<
string,
(qb: QueryBuilder<any>, ...args: any[]) => QueryBuilder<any>
> {
return this.getModelMetadata(model)?.scopes || {};
}
/**
* Finds the model constructor by table name.
* @param tableName - The table name to search for.
* @returns The model constructor or undefined if not found.
*/
static getModelByTableName(tableName: string): Function | undefined {
for (const [model, config] of this.models) {
if (config.tableName === tableName) {
return model;
}
}
return undefined;
}
static getTimestamps(model: Function): TimestampsConfig {
return this.getModelMetadata(model)?.timestamps || {};
}
}
/**
* Programmatically defines a model and stores its metadata.
* @param config - The model configuration object.
* @returns The dynamically created model class.
*/
export function defineModel(config: ModelConfig) {
class Model {
/**
* Constructs a model instance from plain data.
* @param data - The plain object to assign properties from.
*/
constructor(data: any) {
Object.assign(this, data);
}
}
// Store metadata
MetadataStorage.setModelMetadata(Model, {
tableName: config.tableName,
versioned: config.versioned || false,
softDelete: config.softDelete || false,
columns: config.columns,
relations: config.relations || [],
scopes: config.scopes || {},
timestamps: config.timestamps || {},
});
return Model;
}