@zero-server/cli 0.9.2 → 0.9.5
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/README.md +2 -2
- package/index.d.ts +1 -1
- package/package.json +4 -3
- package/types/app.d.ts +223 -0
- package/types/auth.d.ts +520 -0
- package/types/body.d.ts +14 -0
- package/types/cli.d.ts +2 -0
- package/types/cluster.d.ts +75 -0
- package/types/env.d.ts +80 -0
- package/types/errors.d.ts +316 -0
- package/types/fetch.d.ts +43 -0
- package/types/grpc.d.ts +432 -0
- package/types/index.d.ts +384 -0
- package/types/lifecycle.d.ts +60 -0
- package/types/middleware.d.ts +320 -0
- package/types/observe.d.ts +304 -0
- package/types/orm.d.ts +1887 -0
- package/types/request.d.ts +109 -0
- package/types/response.d.ts +157 -0
- package/types/router.d.ts +78 -0
- package/types/sse.d.ts +78 -0
- package/types/websocket.d.ts +126 -0
package/types/orm.d.ts
ADDED
|
@@ -0,0 +1,1887 @@
|
|
|
1
|
+
// --- Schema Types ------------------------------------------------
|
|
2
|
+
|
|
3
|
+
export interface SchemaColumnDef {
|
|
4
|
+
/** Column data type. */
|
|
5
|
+
type: typeof TYPES[keyof typeof TYPES];
|
|
6
|
+
/** Field is required. */
|
|
7
|
+
required?: boolean;
|
|
8
|
+
/** Default value or factory function. */
|
|
9
|
+
default?: any | (() => any);
|
|
10
|
+
/** Allow null values. */
|
|
11
|
+
nullable?: boolean;
|
|
12
|
+
/** Is primary key. */
|
|
13
|
+
primaryKey?: boolean;
|
|
14
|
+
/** Auto-increment. */
|
|
15
|
+
autoIncrement?: boolean;
|
|
16
|
+
/** Unique constraint. */
|
|
17
|
+
unique?: boolean;
|
|
18
|
+
/** Minimum string length. */
|
|
19
|
+
minLength?: number;
|
|
20
|
+
/** Maximum string length. */
|
|
21
|
+
maxLength?: number;
|
|
22
|
+
/** Minimum numeric value. */
|
|
23
|
+
min?: number;
|
|
24
|
+
/** Maximum numeric value. */
|
|
25
|
+
max?: number;
|
|
26
|
+
/** Pattern constraint (string). */
|
|
27
|
+
match?: RegExp;
|
|
28
|
+
/** Allowed values (string/enum type). */
|
|
29
|
+
enum?: string[];
|
|
30
|
+
/** Allowed values (set type). */
|
|
31
|
+
values?: string[];
|
|
32
|
+
/** Mass-assignment protection — exclude from bulk writes. */
|
|
33
|
+
guarded?: boolean;
|
|
34
|
+
/** Precision for decimal types. */
|
|
35
|
+
precision?: number;
|
|
36
|
+
/** Scale for decimal types. */
|
|
37
|
+
scale?: number;
|
|
38
|
+
/** Length for fixed-width types (binary, varbinary, char). */
|
|
39
|
+
length?: number;
|
|
40
|
+
/** MySQL: mark column as unsigned. */
|
|
41
|
+
unsigned?: boolean;
|
|
42
|
+
/** MySQL/PG: column charset. */
|
|
43
|
+
charset?: string;
|
|
44
|
+
/** MySQL/PG: column collation. */
|
|
45
|
+
collation?: string;
|
|
46
|
+
/** MySQL/PG: column comment. */
|
|
47
|
+
comment?: string;
|
|
48
|
+
/** PG: array element type for array columns. */
|
|
49
|
+
arrayOf?: string;
|
|
50
|
+
/** PG: foreign key reference. */
|
|
51
|
+
references?: { table: string; column?: string; onDelete?: string; onUpdate?: string };
|
|
52
|
+
/** SQL CHECK constraint expression. */
|
|
53
|
+
check?: string;
|
|
54
|
+
/** Part of a composite primary key. */
|
|
55
|
+
compositeKey?: boolean;
|
|
56
|
+
/** Composite unique constraint group name (or true for default group). */
|
|
57
|
+
compositeUnique?: string | boolean;
|
|
58
|
+
/** Composite index group name (or true for default group). */
|
|
59
|
+
compositeIndex?: string | boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const TYPES: {
|
|
63
|
+
readonly STRING: 'string';
|
|
64
|
+
readonly INTEGER: 'integer';
|
|
65
|
+
readonly FLOAT: 'float';
|
|
66
|
+
readonly BOOLEAN: 'boolean';
|
|
67
|
+
readonly DATE: 'date';
|
|
68
|
+
readonly DATETIME: 'datetime';
|
|
69
|
+
readonly JSON: 'json';
|
|
70
|
+
readonly TEXT: 'text';
|
|
71
|
+
readonly BLOB: 'blob';
|
|
72
|
+
readonly UUID: 'uuid';
|
|
73
|
+
// Extended numeric
|
|
74
|
+
readonly BIGINT: 'bigint';
|
|
75
|
+
readonly SMALLINT: 'smallint';
|
|
76
|
+
readonly TINYINT: 'tinyint';
|
|
77
|
+
readonly DECIMAL: 'decimal';
|
|
78
|
+
readonly DOUBLE: 'double';
|
|
79
|
+
readonly REAL: 'real';
|
|
80
|
+
// Extended string/binary
|
|
81
|
+
readonly CHAR: 'char';
|
|
82
|
+
readonly BINARY: 'binary';
|
|
83
|
+
readonly VARBINARY: 'varbinary';
|
|
84
|
+
// Temporal
|
|
85
|
+
readonly TIMESTAMP: 'timestamp';
|
|
86
|
+
readonly TIME: 'time';
|
|
87
|
+
// MySQL-specific
|
|
88
|
+
readonly ENUM: 'enum';
|
|
89
|
+
readonly SET: 'set';
|
|
90
|
+
readonly MEDIUMTEXT: 'mediumtext';
|
|
91
|
+
readonly LONGTEXT: 'longtext';
|
|
92
|
+
readonly MEDIUMBLOB: 'mediumblob';
|
|
93
|
+
readonly LONGBLOB: 'longblob';
|
|
94
|
+
readonly YEAR: 'year';
|
|
95
|
+
// PostgreSQL-specific
|
|
96
|
+
readonly SERIAL: 'serial';
|
|
97
|
+
readonly BIGSERIAL: 'bigserial';
|
|
98
|
+
readonly JSONB: 'jsonb';
|
|
99
|
+
readonly INTERVAL: 'interval';
|
|
100
|
+
readonly INET: 'inet';
|
|
101
|
+
readonly CIDR: 'cidr';
|
|
102
|
+
readonly MACADDR: 'macaddr';
|
|
103
|
+
readonly MONEY: 'money';
|
|
104
|
+
readonly XML: 'xml';
|
|
105
|
+
readonly CITEXT: 'citext';
|
|
106
|
+
readonly ARRAY: 'array';
|
|
107
|
+
// SQLite
|
|
108
|
+
readonly NUMERIC: 'numeric';
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validate a single value against a column definition.
|
|
113
|
+
* Throws on validation failure.
|
|
114
|
+
*/
|
|
115
|
+
export function validateValue(value: any, colDef: SchemaColumnDef, colName: string): any;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validate an object against a column schema.
|
|
119
|
+
*/
|
|
120
|
+
export function validate(
|
|
121
|
+
data: object,
|
|
122
|
+
columns: Record<string, SchemaColumnDef>,
|
|
123
|
+
options?: { partial?: boolean }
|
|
124
|
+
): { valid: boolean; errors: string[]; sanitized: object };
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validate and normalise a FK action string (CASCADE, SET NULL, etc.).
|
|
128
|
+
* Throws on invalid action.
|
|
129
|
+
*/
|
|
130
|
+
export function validateFKAction(action: string): string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validate a CHECK constraint expression for dangerous SQL patterns.
|
|
134
|
+
* Throws on potentially dangerous expressions.
|
|
135
|
+
*/
|
|
136
|
+
export function validateCheck(expr: string): string;
|
|
137
|
+
|
|
138
|
+
// --- Query Builder -----------------------------------------------
|
|
139
|
+
|
|
140
|
+
export class Query {
|
|
141
|
+
constructor(model: typeof Model, adapter: any);
|
|
142
|
+
|
|
143
|
+
/** Select specific fields. */
|
|
144
|
+
select(...fields: string[]): Query;
|
|
145
|
+
/** Return distinct rows. */
|
|
146
|
+
distinct(): Query;
|
|
147
|
+
|
|
148
|
+
/** Filter by condition. */
|
|
149
|
+
where(field: string, value: any): Query;
|
|
150
|
+
where(field: string, op: string, value: any): Query;
|
|
151
|
+
where(conditions: Record<string, any>): Query;
|
|
152
|
+
/** OR filter. */
|
|
153
|
+
orWhere(field: string, value: any): Query;
|
|
154
|
+
orWhere(field: string, op: string, value: any): Query;
|
|
155
|
+
/** Filter where field IS NULL. */
|
|
156
|
+
whereNull(field: string): Query;
|
|
157
|
+
/** Filter where field IS NOT NULL. */
|
|
158
|
+
whereNotNull(field: string): Query;
|
|
159
|
+
/** Filter where field is in a set of values. */
|
|
160
|
+
whereIn(field: string, values: any[]): Query;
|
|
161
|
+
/** Filter where field is NOT in a set of values. */
|
|
162
|
+
whereNotIn(field: string, values: any[]): Query;
|
|
163
|
+
/** Filter where field is between two values. */
|
|
164
|
+
whereBetween(field: string, low: any, high: any): Query;
|
|
165
|
+
/** Filter where field is NOT between two values. */
|
|
166
|
+
whereNotBetween(field: string, low: any, high: any): Query;
|
|
167
|
+
/** Filter where field matches a LIKE pattern (% and _ wildcards). */
|
|
168
|
+
whereLike(field: string, pattern: string): Query;
|
|
169
|
+
|
|
170
|
+
/** Order results. */
|
|
171
|
+
orderBy(field: string, dir?: 'asc' | 'desc'): Query;
|
|
172
|
+
/** Limit result count. */
|
|
173
|
+
limit(n: number): Query;
|
|
174
|
+
/** Offset results. */
|
|
175
|
+
offset(n: number): Query;
|
|
176
|
+
/** Paginate results. */
|
|
177
|
+
page(page: number, perPage?: number): Query;
|
|
178
|
+
|
|
179
|
+
/** Group results by fields. */
|
|
180
|
+
groupBy(...fields: string[]): Query;
|
|
181
|
+
/** Having clause for aggregates. */
|
|
182
|
+
having(field: string, op?: string, value?: any): Query;
|
|
183
|
+
|
|
184
|
+
/** Inner join. */
|
|
185
|
+
join(table: string, localKey: string, foreignKey: string): Query;
|
|
186
|
+
/** Left join. */
|
|
187
|
+
leftJoin(table: string, localKey: string, foreignKey: string): Query;
|
|
188
|
+
/** Right join. */
|
|
189
|
+
rightJoin(table: string, localKey: string, foreignKey: string): Query;
|
|
190
|
+
|
|
191
|
+
/** Include soft-deleted records. */
|
|
192
|
+
withDeleted(): Query;
|
|
193
|
+
/** Apply a named scope from the model's static scopes. */
|
|
194
|
+
scope(name: string, ...args: any[]): Query;
|
|
195
|
+
|
|
196
|
+
/** Build the adapter-agnostic query descriptor. */
|
|
197
|
+
build(): object;
|
|
198
|
+
/** Execute the query and return model instances. */
|
|
199
|
+
exec(): Promise<Model[]>;
|
|
200
|
+
/** Execute the query and return the first result. */
|
|
201
|
+
first(): Promise<Model | null>;
|
|
202
|
+
/** Execute a count query. */
|
|
203
|
+
count(): Promise<number>;
|
|
204
|
+
/** Returns true if any matching records exist. */
|
|
205
|
+
exists(): Promise<boolean>;
|
|
206
|
+
/** Returns an array of values for a single column. */
|
|
207
|
+
pluck(field: string): Promise<any[]>;
|
|
208
|
+
/** Returns the sum of a numeric column. */
|
|
209
|
+
sum(field: string): Promise<number>;
|
|
210
|
+
/** Returns the average of a numeric column. */
|
|
211
|
+
avg(field: string): Promise<number>;
|
|
212
|
+
/** Returns the minimum value of a column. */
|
|
213
|
+
min(field: string): Promise<any>;
|
|
214
|
+
/** Returns the maximum value of a column. */
|
|
215
|
+
max(field: string): Promise<any>;
|
|
216
|
+
|
|
217
|
+
// -- Performance / Scalability (Phase 2) -----------------
|
|
218
|
+
|
|
219
|
+
/** Eager-count relationships without loading records. Adds `RelationName_count` fields. */
|
|
220
|
+
withCount(...relations: string[]): Query;
|
|
221
|
+
/** Force this query to run against a read replica if configured. */
|
|
222
|
+
onReplica(): Query;
|
|
223
|
+
/** Get the query execution plan from the adapter. */
|
|
224
|
+
explain(options?: { analyze?: boolean; buffers?: boolean; format?: string }): Promise<any>;
|
|
225
|
+
|
|
226
|
+
// -- LINQ-Inspired Methods --------------------------
|
|
227
|
+
|
|
228
|
+
/** Alias for limit (LINQ naming). */
|
|
229
|
+
take(n: number): Query;
|
|
230
|
+
/** Alias for offset (LINQ naming). */
|
|
231
|
+
skip(n: number): Query;
|
|
232
|
+
/** Alias for exec — explicitly convert to array. */
|
|
233
|
+
toArray(): Promise<Model[]>;
|
|
234
|
+
/** Shorthand for orderBy(field, 'desc'). */
|
|
235
|
+
orderByDesc(field: string): Query;
|
|
236
|
+
/** Execute and return the last result. */
|
|
237
|
+
last(): Promise<Model | null>;
|
|
238
|
+
|
|
239
|
+
/** Conditionally apply query logic when condition is truthy. */
|
|
240
|
+
when(condition: any, fn: (query: Query) => void): Query;
|
|
241
|
+
/** Conditionally apply query logic when condition is falsy. */
|
|
242
|
+
unless(condition: any, fn: (query: Query) => void): Query;
|
|
243
|
+
/** Inspect the query without breaking the chain (for debugging/logging). */
|
|
244
|
+
tap(fn: (query: Query) => void): Query;
|
|
245
|
+
|
|
246
|
+
/** Process results in batches. Calls fn(batch, batchIndex) for each chunk. */
|
|
247
|
+
chunk(size: number, fn: (batch: Model[], index: number) => void | Promise<void>): Promise<void>;
|
|
248
|
+
/** Execute and iterate each result with a callback. */
|
|
249
|
+
each(fn: (item: Model, index: number) => void | Promise<void>): Promise<void>;
|
|
250
|
+
/** Execute, transform results with a mapper, and return the mapped array. */
|
|
251
|
+
map<T>(fn: (item: Model, index: number) => T): Promise<T[]>;
|
|
252
|
+
/** Execute, filter results with a predicate, and return matches. */
|
|
253
|
+
filter(fn: (item: Model, index: number) => boolean): Promise<Model[]>;
|
|
254
|
+
/** Execute and reduce results to a single value. */
|
|
255
|
+
reduce<T>(fn: (acc: T, item: Model, index: number) => T, initial: T): Promise<T>;
|
|
256
|
+
|
|
257
|
+
/** Rich pagination with metadata: { data, total, page, perPage, pages, hasNext, hasPrev }. */
|
|
258
|
+
paginate(page: number, perPage?: number): Promise<PaginatedResult>;
|
|
259
|
+
|
|
260
|
+
/** Inject a raw WHERE clause for SQL adapters (ignored by memory/mongo). */
|
|
261
|
+
whereRaw(sql: string, ...params: any[]): Query;
|
|
262
|
+
|
|
263
|
+
/** Thenable support — `await query`. */
|
|
264
|
+
then<TResult1 = Model[], TResult2 = never>(
|
|
265
|
+
onfulfilled?: ((value: Model[]) => TResult1 | PromiseLike<TResult1>) | null,
|
|
266
|
+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
|
267
|
+
): Promise<TResult1 | TResult2>;
|
|
268
|
+
catch<TResult = never>(
|
|
269
|
+
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
|
270
|
+
): Promise<Model[] | TResult>;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// --- Pagination Result -------------------------------------------
|
|
274
|
+
|
|
275
|
+
export interface PaginatedResult {
|
|
276
|
+
data: Model[];
|
|
277
|
+
total: number;
|
|
278
|
+
page: number;
|
|
279
|
+
perPage: number;
|
|
280
|
+
pages: number;
|
|
281
|
+
hasNext: boolean;
|
|
282
|
+
hasPrev: boolean;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// --- Model -------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
export interface ModelHooks {
|
|
288
|
+
beforeCreate?: (data: object) => void | Promise<void>;
|
|
289
|
+
afterCreate?: (instance: Model) => void | Promise<void>;
|
|
290
|
+
beforeUpdate?: (instance: Model, data: object) => void | Promise<void>;
|
|
291
|
+
afterUpdate?: (instance: Model) => void | Promise<void>;
|
|
292
|
+
beforeDelete?: (instance: Model) => void | Promise<void>;
|
|
293
|
+
afterDelete?: (instance: Model) => void | Promise<void>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface FindOrCreateResult {
|
|
297
|
+
instance: Model;
|
|
298
|
+
created: boolean;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export class Model {
|
|
302
|
+
/** Table name (override in subclass). */
|
|
303
|
+
static table: string;
|
|
304
|
+
/** Column schema (override in subclass). */
|
|
305
|
+
static schema: Record<string, SchemaColumnDef>;
|
|
306
|
+
/** Enable automatic timestamps (createdAt, updatedAt). */
|
|
307
|
+
static timestamps: boolean;
|
|
308
|
+
/** Enable soft deletes (deletedAt). */
|
|
309
|
+
static softDelete: boolean;
|
|
310
|
+
/** Lifecycle hooks. */
|
|
311
|
+
static hooks: ModelHooks;
|
|
312
|
+
/** Fields excluded from toJSON() output. */
|
|
313
|
+
static hidden: string[];
|
|
314
|
+
/** Reusable named query conditions: { name: (query, ...args) => query }. */
|
|
315
|
+
static scopes: Record<string, (query: Query, ...args: any[]) => Query>;
|
|
316
|
+
|
|
317
|
+
constructor(data?: object);
|
|
318
|
+
|
|
319
|
+
/** Whether the instance has been persisted. */
|
|
320
|
+
_persisted: boolean;
|
|
321
|
+
|
|
322
|
+
/** Save the instance (insert or update dirty fields). */
|
|
323
|
+
save(): Promise<Model>;
|
|
324
|
+
/** Update fields on the instance. */
|
|
325
|
+
update(data: object): Promise<Model>;
|
|
326
|
+
/** Delete the instance (soft or hard). */
|
|
327
|
+
delete(): Promise<void>;
|
|
328
|
+
/** Restore a soft-deleted instance. */
|
|
329
|
+
restore(): Promise<Model>;
|
|
330
|
+
/** Reload the instance from the database. */
|
|
331
|
+
reload(): Promise<Model>;
|
|
332
|
+
/** Return a plain object representation (respects static hidden). */
|
|
333
|
+
toJSON(): object;
|
|
334
|
+
/** Load a named relationship. */
|
|
335
|
+
load(relationName: string): Promise<Model | Model[] | null>;
|
|
336
|
+
/** Increment a numeric field by amount (default 1). */
|
|
337
|
+
increment(field: string, by?: number): Promise<Model>;
|
|
338
|
+
/** Decrement a numeric field by amount (default 1). */
|
|
339
|
+
decrement(field: string, by?: number): Promise<Model>;
|
|
340
|
+
|
|
341
|
+
/** Create a new record. */
|
|
342
|
+
static create(data: object): Promise<Model>;
|
|
343
|
+
/** Create multiple records. */
|
|
344
|
+
static createMany(dataArray: object[]): Promise<Model[]>;
|
|
345
|
+
/** Find records matching conditions. */
|
|
346
|
+
static find(conditions?: object): Promise<Model[]>;
|
|
347
|
+
/** Find a single record matching conditions. */
|
|
348
|
+
static findOne(conditions: object): Promise<Model | null>;
|
|
349
|
+
/** Find a record by primary key. */
|
|
350
|
+
static findById(id: any): Promise<Model | null>;
|
|
351
|
+
/** Find or create a record. */
|
|
352
|
+
static findOrCreate(conditions: object, defaults?: object): Promise<FindOrCreateResult>;
|
|
353
|
+
/** Update records matching conditions. */
|
|
354
|
+
static updateWhere(conditions: object, data: object): Promise<number>;
|
|
355
|
+
/** Delete records matching conditions. */
|
|
356
|
+
static deleteWhere(conditions: object): Promise<number>;
|
|
357
|
+
/** Count records matching conditions. */
|
|
358
|
+
static count(conditions?: object): Promise<number>;
|
|
359
|
+
/** Check if any records match the conditions. */
|
|
360
|
+
static exists(conditions?: object): Promise<boolean>;
|
|
361
|
+
/** Insert or update a record. Returns { instance, created }. */
|
|
362
|
+
static upsert(conditions: object, data: object): Promise<FindOrCreateResult>;
|
|
363
|
+
/** Start a fluent query builder with a named scope applied. */
|
|
364
|
+
static scope(name: string, ...args: any[]): Query;
|
|
365
|
+
/** Start a fluent query builder. */
|
|
366
|
+
static query(): Query;
|
|
367
|
+
|
|
368
|
+
// -- LINQ-Inspired Static Shortcuts ------------------
|
|
369
|
+
|
|
370
|
+
/** Find the first record matching optional conditions. */
|
|
371
|
+
static first(conditions?: object): Promise<Model | null>;
|
|
372
|
+
/** Find the last record matching optional conditions. */
|
|
373
|
+
static last(conditions?: object): Promise<Model | null>;
|
|
374
|
+
/** Get all records (alias for find). */
|
|
375
|
+
static all(conditions?: object): Promise<Model[]>;
|
|
376
|
+
/** Rich pagination with metadata. */
|
|
377
|
+
static paginate(page: number, perPage?: number, conditions?: object): Promise<PaginatedResult>;
|
|
378
|
+
/** Process all matching records in batches. */
|
|
379
|
+
static chunk(size: number, fn: (batch: Model[], index: number) => void | Promise<void>, conditions?: object): Promise<void>;
|
|
380
|
+
/** Get a random record. */
|
|
381
|
+
static random(conditions?: object): Promise<Model | null>;
|
|
382
|
+
/** Pluck values for a single column. */
|
|
383
|
+
static pluck(field: string, conditions?: object): Promise<any[]>;
|
|
384
|
+
|
|
385
|
+
/** Define a has-many relationship. */
|
|
386
|
+
static hasMany(RelatedModel: typeof Model, foreignKey: string, localKey?: string): void;
|
|
387
|
+
/** Define a has-one relationship. */
|
|
388
|
+
static hasOne(RelatedModel: typeof Model, foreignKey: string, localKey?: string): void;
|
|
389
|
+
/** Define a belongs-to relationship. */
|
|
390
|
+
static belongsTo(RelatedModel: typeof Model, foreignKey: string, otherKey?: string): void;
|
|
391
|
+
/** Define a many-to-many relationship through a junction table. */
|
|
392
|
+
static belongsToMany(RelatedModel: typeof Model, options: {
|
|
393
|
+
through: string;
|
|
394
|
+
foreignKey: string;
|
|
395
|
+
otherKey: string;
|
|
396
|
+
localKey?: string;
|
|
397
|
+
relatedKey?: string;
|
|
398
|
+
}): void;
|
|
399
|
+
|
|
400
|
+
/** Define a polymorphic one-to-one relationship. */
|
|
401
|
+
static morphOne(RelatedModel: typeof Model, morphName: string, localKey?: string): void;
|
|
402
|
+
/** Define a polymorphic one-to-many relationship. */
|
|
403
|
+
static morphMany(RelatedModel: typeof Model, morphName: string, localKey?: string): void;
|
|
404
|
+
/** Define a has-many-through relationship (distant relation via intermediate model). */
|
|
405
|
+
static hasManyThrough(
|
|
406
|
+
RelatedModel: typeof Model,
|
|
407
|
+
ThroughModel: typeof Model,
|
|
408
|
+
firstKey: string,
|
|
409
|
+
secondKey: string,
|
|
410
|
+
localKey?: string,
|
|
411
|
+
secondLocalKey?: string,
|
|
412
|
+
): void;
|
|
413
|
+
/** Define self-referential parent/children relationships. */
|
|
414
|
+
static selfReferential(options: {
|
|
415
|
+
foreignKey: string;
|
|
416
|
+
parentName?: string;
|
|
417
|
+
childrenName?: string;
|
|
418
|
+
}): void;
|
|
419
|
+
/** Build a tree structure from self-referential records. */
|
|
420
|
+
static tree(options?: {
|
|
421
|
+
foreignKey?: string;
|
|
422
|
+
childrenKey?: string;
|
|
423
|
+
rootValue?: any;
|
|
424
|
+
}): Promise<any[]>;
|
|
425
|
+
|
|
426
|
+
/** Get all ancestors of this instance in a self-referential tree. */
|
|
427
|
+
ancestors(foreignKey?: string): Promise<Model[]>;
|
|
428
|
+
/** Get all descendants of this instance (breadth-first). */
|
|
429
|
+
descendants(foreignKey?: string): Promise<Model[]>;
|
|
430
|
+
|
|
431
|
+
// -- Computed & Virtual Columns (Phase 3) -----------------
|
|
432
|
+
|
|
433
|
+
/** Computed column definitions: { name: (instance) => value }. */
|
|
434
|
+
static computed: Record<string, (instance: Model) => any>;
|
|
435
|
+
/** Attribute casting definitions: { field: castType | { get, set } }. */
|
|
436
|
+
static casts: Record<string, string | { get?: (value: any) => any; set?: (value: any) => any }>;
|
|
437
|
+
/** Accessor transforms applied on read: { field: (value, instance) => transformedValue }. */
|
|
438
|
+
static accessors: Record<string, (value: any, instance: Model) => any>;
|
|
439
|
+
/** Mutator transforms applied on write: { field: (value, instance) => transformedValue }. */
|
|
440
|
+
static mutators: Record<string, (value: any, instance: Model) => any>;
|
|
441
|
+
|
|
442
|
+
/** Get an attribute value with accessor/cast applied. */
|
|
443
|
+
getAttribute(key: string): any;
|
|
444
|
+
/** Set an attribute value with mutator/cast applied. */
|
|
445
|
+
setAttribute(key: string, value: any): Model;
|
|
446
|
+
|
|
447
|
+
// -- Model Events (Phase 3) -------------------------------
|
|
448
|
+
|
|
449
|
+
/** Listen for a model event. */
|
|
450
|
+
static on(event: string, listener: (...args: any[]) => void): typeof Model;
|
|
451
|
+
/** Listen for a model event once. */
|
|
452
|
+
static once(event: string, listener: (...args: any[]) => void): typeof Model;
|
|
453
|
+
/** Remove a model event listener. */
|
|
454
|
+
static off(event: string, listener: (...args: any[]) => void): typeof Model;
|
|
455
|
+
/** Remove all listeners for an event, or all listeners entirely. */
|
|
456
|
+
static removeAllListeners(event?: string): typeof Model;
|
|
457
|
+
|
|
458
|
+
// -- Observers (Phase 3) ----------------------------------
|
|
459
|
+
|
|
460
|
+
/** Register an observer object with lifecycle methods. */
|
|
461
|
+
static observe(observer: Partial<ModelObserver>): typeof Model;
|
|
462
|
+
/** Unregister an observer. */
|
|
463
|
+
static unobserve(observer: Partial<ModelObserver>): typeof Model;
|
|
464
|
+
|
|
465
|
+
/** Create/sync the table in the database. */
|
|
466
|
+
static sync(): Promise<void>;
|
|
467
|
+
/** Drop the table from the database. */
|
|
468
|
+
static drop(): Promise<void>;
|
|
469
|
+
|
|
470
|
+
/** Allow index access for model fields. */
|
|
471
|
+
[key: string]: any;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// --- Model Observer --------------------------------------------------
|
|
475
|
+
|
|
476
|
+
export interface ModelObserver {
|
|
477
|
+
creating?: (data: object) => void | Promise<void>;
|
|
478
|
+
created?: (instance: Model) => void | Promise<void>;
|
|
479
|
+
updating?: (data: object) => void | Promise<void>;
|
|
480
|
+
updated?: (instance: Model) => void | Promise<void>;
|
|
481
|
+
deleting?: (instance: Model) => void | Promise<void>;
|
|
482
|
+
deleted?: (instance: Model) => void | Promise<void>;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// --- DatabaseView ----------------------------------------------------
|
|
486
|
+
|
|
487
|
+
export interface DatabaseViewOptions {
|
|
488
|
+
/** Query builder instance defining the view's SELECT. */
|
|
489
|
+
query?: Query;
|
|
490
|
+
/** Raw SQL for the view definition (SQL adapters only). */
|
|
491
|
+
sql?: string;
|
|
492
|
+
/** Model class the view is based on. */
|
|
493
|
+
model?: typeof Model;
|
|
494
|
+
/** Column schema for the view (optional; inferred from model if omitted). */
|
|
495
|
+
schema?: Record<string, SchemaColumnDef>;
|
|
496
|
+
/** Whether to create a materialized view (PostgreSQL only). */
|
|
497
|
+
materialized?: boolean;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export class DatabaseView {
|
|
501
|
+
/** View name. */
|
|
502
|
+
readonly name: string;
|
|
503
|
+
|
|
504
|
+
constructor(name: string, options: DatabaseViewOptions);
|
|
505
|
+
|
|
506
|
+
/** Create the view in the database. */
|
|
507
|
+
create(db: Database): Promise<DatabaseView>;
|
|
508
|
+
/** Drop the view from the database. */
|
|
509
|
+
drop(db?: Database): Promise<void>;
|
|
510
|
+
/** Refresh a materialized view (PostgreSQL only). */
|
|
511
|
+
refresh(db?: Database): Promise<void>;
|
|
512
|
+
/** Check whether the view exists. */
|
|
513
|
+
exists(db?: Database): Promise<boolean>;
|
|
514
|
+
/** Query all records from the view. */
|
|
515
|
+
all(): Promise<any[]>;
|
|
516
|
+
/** Find records matching conditions. */
|
|
517
|
+
find(conditions?: object): Promise<any[]>;
|
|
518
|
+
/** Find a single record. */
|
|
519
|
+
findOne(conditions?: object): Promise<any | null>;
|
|
520
|
+
/** Count records in the view. */
|
|
521
|
+
count(conditions?: object): Promise<number>;
|
|
522
|
+
/** Start a fluent query against the view. */
|
|
523
|
+
query(): Query;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// --- FullTextSearch --------------------------------------------------
|
|
527
|
+
|
|
528
|
+
export interface FullTextSearchOptions {
|
|
529
|
+
/** Column names to include in the search index. */
|
|
530
|
+
fields: string[];
|
|
531
|
+
/** Weight map for fields (e.g. { title: 'A', body: 'B' }). */
|
|
532
|
+
weights?: Record<string, string | number>;
|
|
533
|
+
/** Language for stemming/tokenisation (default: 'english'). */
|
|
534
|
+
language?: string;
|
|
535
|
+
/** Custom index name. */
|
|
536
|
+
indexName?: string;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface SearchOptions {
|
|
540
|
+
/** Include relevance ranking in results. */
|
|
541
|
+
rank?: boolean;
|
|
542
|
+
/** Maximum number of results. */
|
|
543
|
+
limit?: number;
|
|
544
|
+
/** Offset for pagination. */
|
|
545
|
+
offset?: number;
|
|
546
|
+
/** Additional WHERE conditions. */
|
|
547
|
+
where?: object;
|
|
548
|
+
/** Custom order ('rank' or a column name). */
|
|
549
|
+
orderBy?: string;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export interface SuggestOptions {
|
|
553
|
+
/** Max suggestions (default: 10). */
|
|
554
|
+
limit?: number;
|
|
555
|
+
/** Specific field to suggest from. */
|
|
556
|
+
field?: string;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export class FullTextSearch {
|
|
560
|
+
constructor(ModelClass: typeof Model, options: FullTextSearchOptions);
|
|
561
|
+
|
|
562
|
+
/** Create the full-text search index. */
|
|
563
|
+
createIndex(db: Database): Promise<FullTextSearch>;
|
|
564
|
+
/** Drop the full-text search index. */
|
|
565
|
+
dropIndex(db?: Database): Promise<void>;
|
|
566
|
+
/** Perform a full-text search. */
|
|
567
|
+
search(query: string, options?: SearchOptions): Promise<any[]>;
|
|
568
|
+
/** Search and return model instances. */
|
|
569
|
+
searchModels(query: string, options?: SearchOptions): Promise<Model[]>;
|
|
570
|
+
/** Count matching search results. */
|
|
571
|
+
count(query: string, options?: Pick<SearchOptions, 'where'>): Promise<number>;
|
|
572
|
+
/** Build search suggestions (autocomplete). */
|
|
573
|
+
suggest(prefix: string, options?: SuggestOptions): Promise<string[]>;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// --- GeoQuery --------------------------------------------------------
|
|
577
|
+
|
|
578
|
+
/** Earth's radius in kilometres. */
|
|
579
|
+
export const EARTH_RADIUS_KM: 6371;
|
|
580
|
+
/** Earth's radius in miles. */
|
|
581
|
+
export const EARTH_RADIUS_MI: 3959;
|
|
582
|
+
|
|
583
|
+
export interface GeoQueryOptions {
|
|
584
|
+
/** Column name for latitude. */
|
|
585
|
+
latField: string;
|
|
586
|
+
/** Column name for longitude. */
|
|
587
|
+
lngField: string;
|
|
588
|
+
/** Distance unit: 'km' or 'mi' (default: 'km'). */
|
|
589
|
+
unit?: 'km' | 'mi';
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export interface NearOptions {
|
|
593
|
+
/** Maximum distance (in configured unit). */
|
|
594
|
+
radius?: number;
|
|
595
|
+
/** Maximum number of results. */
|
|
596
|
+
limit?: number;
|
|
597
|
+
/** Skip N results. */
|
|
598
|
+
offset?: number;
|
|
599
|
+
/** Additional WHERE conditions. */
|
|
600
|
+
where?: object;
|
|
601
|
+
/** Override distance unit. */
|
|
602
|
+
unit?: 'km' | 'mi';
|
|
603
|
+
/** Add _distance property to results (default: true). */
|
|
604
|
+
includeDistance?: boolean;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
export interface WithinBounds {
|
|
608
|
+
north: number;
|
|
609
|
+
south: number;
|
|
610
|
+
east: number;
|
|
611
|
+
west: number;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export interface GeoJSONPoint {
|
|
615
|
+
type: 'Point';
|
|
616
|
+
coordinates: [number, number]; // [lng, lat]
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface GeoJSONFeature {
|
|
620
|
+
type: 'Feature';
|
|
621
|
+
geometry: GeoJSONPoint;
|
|
622
|
+
properties: Record<string, any>;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export interface GeoJSONFeatureCollection {
|
|
626
|
+
type: 'FeatureCollection';
|
|
627
|
+
features: GeoJSONFeature[];
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export class GeoQuery {
|
|
631
|
+
constructor(ModelClass: typeof Model, options: GeoQueryOptions);
|
|
632
|
+
|
|
633
|
+
/** Find records near a geographic point. */
|
|
634
|
+
near(lat: number, lng: number, options?: NearOptions): Promise<any[]>;
|
|
635
|
+
/** Find records within a bounding box. */
|
|
636
|
+
within(bounds: WithinBounds, options?: { limit?: number; where?: object }): Promise<any[]>;
|
|
637
|
+
/** Calculate distance between two points. */
|
|
638
|
+
distance(lat1: number, lng1: number, lat2: number, lng2: number, unit?: 'km' | 'mi'): number;
|
|
639
|
+
/** Calculate Haversine distance between two points. */
|
|
640
|
+
static haversine(lat1: number, lng1: number, lat2: number, lng2: number, unit?: 'km' | 'mi'): number;
|
|
641
|
+
/** Convert a record to GeoJSON Feature. */
|
|
642
|
+
toGeoJSON(record: any, options?: { properties?: string[] }): GeoJSONFeature;
|
|
643
|
+
/** Convert multiple records to a GeoJSON FeatureCollection. */
|
|
644
|
+
toGeoJSONCollection(records: any[], options?: { properties?: string[] }): GeoJSONFeatureCollection;
|
|
645
|
+
/** Create a model data object from a GeoJSON Feature. */
|
|
646
|
+
fromGeoJSON(feature: GeoJSONFeature): object;
|
|
647
|
+
/** Check if a point is within a given radius of a center point. */
|
|
648
|
+
isWithinRadius(lat: number, lng: number, centerLat: number, centerLng: number, radius: number, unit?: 'km' | 'mi'): boolean;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// --- SQLite Options ----------------------------------------------
|
|
652
|
+
|
|
653
|
+
export interface SqlitePragmas {
|
|
654
|
+
journal_mode?: 'WAL' | 'DELETE' | 'TRUNCATE' | 'MEMORY' | 'OFF';
|
|
655
|
+
foreign_keys?: 'ON' | 'OFF';
|
|
656
|
+
busy_timeout?: string;
|
|
657
|
+
synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
|
|
658
|
+
cache_size?: string;
|
|
659
|
+
temp_store?: 'DEFAULT' | 'FILE' | 'MEMORY';
|
|
660
|
+
mmap_size?: string;
|
|
661
|
+
page_size?: string;
|
|
662
|
+
auto_vacuum?: 'NONE' | 'FULL' | 'INCREMENTAL';
|
|
663
|
+
secure_delete?: 'ON' | 'OFF';
|
|
664
|
+
wal_autocheckpoint?: string;
|
|
665
|
+
locking_mode?: 'NORMAL' | 'EXCLUSIVE';
|
|
666
|
+
[key: string]: string | undefined;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export interface SqliteOptions {
|
|
670
|
+
filename?: string;
|
|
671
|
+
readonly?: boolean;
|
|
672
|
+
fileMustExist?: boolean;
|
|
673
|
+
verbose?: boolean;
|
|
674
|
+
createDir?: boolean;
|
|
675
|
+
pragmas?: SqlitePragmas;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export interface MySqlOptions {
|
|
679
|
+
host?: string;
|
|
680
|
+
port?: number;
|
|
681
|
+
user?: string;
|
|
682
|
+
password?: string;
|
|
683
|
+
database?: string;
|
|
684
|
+
connectionLimit?: number;
|
|
685
|
+
waitForConnections?: boolean;
|
|
686
|
+
queueLimit?: number;
|
|
687
|
+
connectTimeout?: number;
|
|
688
|
+
charset?: string;
|
|
689
|
+
timezone?: string;
|
|
690
|
+
ssl?: string | object;
|
|
691
|
+
multipleStatements?: boolean;
|
|
692
|
+
decimalNumbers?: boolean;
|
|
693
|
+
[key: string]: any;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export interface PostgresOptions {
|
|
697
|
+
host?: string;
|
|
698
|
+
port?: number;
|
|
699
|
+
user?: string;
|
|
700
|
+
password?: string;
|
|
701
|
+
database?: string;
|
|
702
|
+
connectionString?: string;
|
|
703
|
+
ssl?: boolean | object;
|
|
704
|
+
max?: number;
|
|
705
|
+
idleTimeoutMillis?: number;
|
|
706
|
+
connectionTimeoutMillis?: number;
|
|
707
|
+
application_name?: string;
|
|
708
|
+
statement_timeout?: number;
|
|
709
|
+
[key: string]: any;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export interface MongoOptions {
|
|
713
|
+
url?: string;
|
|
714
|
+
database?: string;
|
|
715
|
+
maxPoolSize?: number;
|
|
716
|
+
minPoolSize?: number;
|
|
717
|
+
connectTimeoutMS?: number;
|
|
718
|
+
socketTimeoutMS?: number;
|
|
719
|
+
serverSelectionTimeoutMS?: number;
|
|
720
|
+
retryWrites?: boolean;
|
|
721
|
+
retryReads?: boolean;
|
|
722
|
+
authSource?: string;
|
|
723
|
+
replicaSet?: string;
|
|
724
|
+
clientOptions?: object;
|
|
725
|
+
[key: string]: any;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
export interface JsonOptions {
|
|
729
|
+
dir: string;
|
|
730
|
+
pretty?: boolean;
|
|
731
|
+
flushInterval?: number;
|
|
732
|
+
autoFlush?: boolean;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export interface RedisOptions {
|
|
736
|
+
/** Redis connection URL: redis://user:pass@host:6379/0. */
|
|
737
|
+
url?: string;
|
|
738
|
+
/** Redis server hostname. Must be a non-empty string. */
|
|
739
|
+
host?: string;
|
|
740
|
+
/** Redis server port. Must be between 1 and 65535. */
|
|
741
|
+
port?: number;
|
|
742
|
+
/** Redis password (AUTH). */
|
|
743
|
+
password?: string;
|
|
744
|
+
/** Redis database index. Must be between 0 and 15. */
|
|
745
|
+
db?: number;
|
|
746
|
+
/** Key prefix for namespacing all keys. */
|
|
747
|
+
prefix?: string;
|
|
748
|
+
/** Max connection retry attempts. */
|
|
749
|
+
maxRetries?: number;
|
|
750
|
+
/** Defer connection until first operation. */
|
|
751
|
+
lazyConnect?: boolean;
|
|
752
|
+
/** Connection timeout in ms. Must be non-negative. */
|
|
753
|
+
connectTimeout?: number;
|
|
754
|
+
/** TLS options for secure connections. */
|
|
755
|
+
tls?: object;
|
|
756
|
+
[key: string]: any;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export type AdapterOptions = SqliteOptions | MySqlOptions | PostgresOptions | MongoOptions | JsonOptions | RedisOptions | object;
|
|
760
|
+
|
|
761
|
+
// --- SQLite Adapter ----------------------------------------------
|
|
762
|
+
|
|
763
|
+
export interface SqliteAdapter {
|
|
764
|
+
/** Read a single PRAGMA value. */
|
|
765
|
+
pragma(key: string): any;
|
|
766
|
+
/** Force a WAL checkpoint. */
|
|
767
|
+
checkpoint(mode?: 'PASSIVE' | 'FULL' | 'RESTART' | 'TRUNCATE'): { busy: number; log: number; checkpointed: number };
|
|
768
|
+
/** Run PRAGMA integrity_check. Returns 'ok' or a problem description. */
|
|
769
|
+
integrity(): string;
|
|
770
|
+
/** Rebuild the database file, reclaiming free pages. */
|
|
771
|
+
vacuum(): void;
|
|
772
|
+
/** Get the database file size in bytes (0 for in-memory). */
|
|
773
|
+
fileSize(): number;
|
|
774
|
+
/** List all user-created table names. */
|
|
775
|
+
tables(): string[];
|
|
776
|
+
/** Run a raw SQL SELECT query. */
|
|
777
|
+
raw(sql: string, ...params: any[]): any[];
|
|
778
|
+
/** Close the database connection. */
|
|
779
|
+
close(): void;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// --- MySQL Adapter -----------------------------------------------
|
|
783
|
+
|
|
784
|
+
export interface MySqlAdapter {
|
|
785
|
+
/** List all tables in the current database. */
|
|
786
|
+
tables(): Promise<string[]>;
|
|
787
|
+
/** Get column info for a table. */
|
|
788
|
+
columns(table: string): Promise<Array<{ Field: string; Type: string; Null: string; Key: string; Default: any; Extra: string }>>;
|
|
789
|
+
/** Get total database size in bytes. */
|
|
790
|
+
databaseSize(): Promise<number>;
|
|
791
|
+
/** Get connection pool status. */
|
|
792
|
+
poolStatus(): { total: number; idle: number; used: number; queued: number };
|
|
793
|
+
/** Get MySQL server version string. */
|
|
794
|
+
version(): Promise<string>;
|
|
795
|
+
/** Ping the server. Returns true if healthy. */
|
|
796
|
+
ping(): Promise<boolean>;
|
|
797
|
+
/** Execute a raw write/DDL statement. Returns affected rows and insert ID. */
|
|
798
|
+
exec(sql: string, ...params: any[]): Promise<{ affectedRows: number; insertId: number }>;
|
|
799
|
+
/** Run a raw SQL SELECT query. */
|
|
800
|
+
raw(sql: string, ...params: any[]): Promise<any[]>;
|
|
801
|
+
/** Run a function inside a transaction. */
|
|
802
|
+
transaction(fn: (connection: any) => Promise<void>): Promise<void>;
|
|
803
|
+
/** Close the connection pool. */
|
|
804
|
+
close(): Promise<void>;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// --- PostgreSQL Adapter ------------------------------------------
|
|
808
|
+
|
|
809
|
+
export interface PostgresAdapter {
|
|
810
|
+
/** List all tables in a schema (default: 'public'). */
|
|
811
|
+
tables(schema?: string): Promise<string[]>;
|
|
812
|
+
/** Get column info for a table. */
|
|
813
|
+
columns(table: string, schema?: string): Promise<Array<{ column_name: string; data_type: string; is_nullable: string; column_default: string | null }>>;
|
|
814
|
+
/** Get total database size in bytes. */
|
|
815
|
+
databaseSize(): Promise<number>;
|
|
816
|
+
/** Get total size of a table including indexes, in bytes. */
|
|
817
|
+
tableSize(table: string): Promise<number>;
|
|
818
|
+
/** Get connection pool status. */
|
|
819
|
+
poolStatus(): { total: number; idle: number; waiting: number };
|
|
820
|
+
/** Get PostgreSQL server version string. */
|
|
821
|
+
version(): Promise<string>;
|
|
822
|
+
/** Ping the server. Returns true if healthy. */
|
|
823
|
+
ping(): Promise<boolean>;
|
|
824
|
+
/** Execute a raw write/DDL statement. Returns row count. */
|
|
825
|
+
exec(sql: string, ...params: any[]): Promise<{ rowCount: number }>;
|
|
826
|
+
/** Subscribe to PostgreSQL LISTEN/NOTIFY channel. Returns an unlisten function. */
|
|
827
|
+
listen(channel: string, callback: (msg: { channel: string; payload?: string }) => void): Promise<() => Promise<void>>;
|
|
828
|
+
/** Run a raw SQL SELECT query. */
|
|
829
|
+
raw(sql: string, ...params: any[]): Promise<any[]>;
|
|
830
|
+
/** Run a function inside a transaction. */
|
|
831
|
+
transaction(fn: (client: any) => Promise<void>): Promise<void>;
|
|
832
|
+
/** Close the connection pool. */
|
|
833
|
+
close(): Promise<void>;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// --- MongoDB Adapter ---------------------------------------------
|
|
837
|
+
|
|
838
|
+
export interface MongoAdapter {
|
|
839
|
+
/** List all collections in the database. */
|
|
840
|
+
collections(): Promise<string[]>;
|
|
841
|
+
/** Get database-level stats. */
|
|
842
|
+
stats(): Promise<{ collections: number; objects: number; dataSize: number; storageSize: number; indexes: number; indexSize: number }>;
|
|
843
|
+
/** Get stats for a specific collection. */
|
|
844
|
+
collectionStats(name: string): Promise<{ count: number; size: number; avgObjSize: number; storageSize: number; nindexes: number }>;
|
|
845
|
+
/** Create an index on a collection. */
|
|
846
|
+
createIndex(collection: string, keys: Record<string, 1 | -1 | 'text'>, options?: { unique?: boolean; sparse?: boolean; [key: string]: any }): Promise<string>;
|
|
847
|
+
/** List all indexes on a collection. */
|
|
848
|
+
indexes(collection: string): Promise<any[]>;
|
|
849
|
+
/** Drop an index by name. */
|
|
850
|
+
dropIndex(collection: string, indexName: string): Promise<void>;
|
|
851
|
+
/** Ping the MongoDB server. Returns true if healthy. */
|
|
852
|
+
ping(): Promise<boolean>;
|
|
853
|
+
/** Get the MongoDB server version. */
|
|
854
|
+
version(): Promise<string>;
|
|
855
|
+
/** Whether the client is currently connected. */
|
|
856
|
+
readonly isConnected: boolean;
|
|
857
|
+
/** Run a raw MongoDB command. */
|
|
858
|
+
raw(command: object): Promise<any>;
|
|
859
|
+
/** Run a function inside a transaction (requires replica set). */
|
|
860
|
+
transaction(fn: (session: any) => Promise<void>): Promise<void>;
|
|
861
|
+
/** Close the connection. */
|
|
862
|
+
close(): Promise<void>;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// --- Memory Adapter ----------------------------------------------
|
|
866
|
+
|
|
867
|
+
export interface MemoryAdapter {
|
|
868
|
+
/** List all registered table names. */
|
|
869
|
+
tables(): string[];
|
|
870
|
+
/** Count all rows across all tables. */
|
|
871
|
+
totalRows(): number;
|
|
872
|
+
/** Get memory stats. */
|
|
873
|
+
stats(): { tables: number; totalRows: number; estimatedBytes: number };
|
|
874
|
+
/** Export all data as a plain object. */
|
|
875
|
+
toJSON(): Record<string, any[]>;
|
|
876
|
+
/** Import data from a plain object. */
|
|
877
|
+
fromJSON(data: Record<string, any[]>): void;
|
|
878
|
+
/** Deep-clone the entire database into a new MemoryAdapter. */
|
|
879
|
+
clone(): MemoryAdapter;
|
|
880
|
+
/** Delete all rows from all tables. */
|
|
881
|
+
clear(): void;
|
|
882
|
+
/** Run a raw query (memory adapter supports select/insert/update/delete descriptors). */
|
|
883
|
+
execute(query: object): any[];
|
|
884
|
+
/** Close (no-op for memory). */
|
|
885
|
+
close(): void;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// --- JSON Adapter ------------------------------------------------
|
|
889
|
+
|
|
890
|
+
export interface JsonAdapter extends MemoryAdapter {
|
|
891
|
+
/** The resolved directory path where JSON files are stored. */
|
|
892
|
+
readonly directory: string;
|
|
893
|
+
/** Get total size of all JSON files in bytes. */
|
|
894
|
+
fileSize(): number;
|
|
895
|
+
/** Whether there are unflushed writes. */
|
|
896
|
+
readonly hasPendingWrites: boolean;
|
|
897
|
+
/** Re-serialize and save a table's JSON file. */
|
|
898
|
+
compact(table: string): void;
|
|
899
|
+
/** Copy all JSON files to a target directory. */
|
|
900
|
+
backup(destDir: string): void;
|
|
901
|
+
/** Immediately write all pending changes to disk. */
|
|
902
|
+
flush(): Promise<void>;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// --- Redis Adapter -----------------------------------------------
|
|
906
|
+
|
|
907
|
+
export interface RedisAdapter {
|
|
908
|
+
/** Get a value by key. Auto-parses JSON. */
|
|
909
|
+
get(key: string): Promise<any>;
|
|
910
|
+
/** Set a key/value pair. Optional TTL in seconds (must be >= 0). */
|
|
911
|
+
set(key: string, value: any, ttl?: number): Promise<void>;
|
|
912
|
+
/** Delete a key. */
|
|
913
|
+
del(key: string): Promise<number>;
|
|
914
|
+
/** Check if a key exists. */
|
|
915
|
+
exists(key: string): Promise<boolean>;
|
|
916
|
+
/** Set a TTL on an existing key. Seconds must be >= 0. */
|
|
917
|
+
expire(key: string, seconds: number): Promise<boolean>;
|
|
918
|
+
/** Get remaining TTL in seconds (-1 = no expiry, -2 = missing). */
|
|
919
|
+
ttl(key: string): Promise<number>;
|
|
920
|
+
/** Increment a numeric key by 1. Returns the new value. */
|
|
921
|
+
incr(key: string): Promise<number>;
|
|
922
|
+
/** Decrement a numeric key by 1. Returns the new value. */
|
|
923
|
+
decr(key: string): Promise<number>;
|
|
924
|
+
/** Set a hash field. */
|
|
925
|
+
hset(key: string, field: string, value: any): Promise<number>;
|
|
926
|
+
/** Get a hash field value. */
|
|
927
|
+
hget(key: string, field: string): Promise<string | null>;
|
|
928
|
+
/** Get all fields and values in a hash. */
|
|
929
|
+
hgetall(key: string): Promise<Record<string, string>>;
|
|
930
|
+
/** Delete a hash field. */
|
|
931
|
+
hdel(key: string, field: string): Promise<number>;
|
|
932
|
+
/** Append values to a list (right). */
|
|
933
|
+
rpush(key: string, ...values: any[]): Promise<number>;
|
|
934
|
+
/** Prepend values to a list (left). */
|
|
935
|
+
lpush(key: string, ...values: any[]): Promise<number>;
|
|
936
|
+
/** Get a range of list elements. */
|
|
937
|
+
lrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
938
|
+
/** Remove and return the last list element. */
|
|
939
|
+
rpop(key: string): Promise<string | null>;
|
|
940
|
+
/** Remove and return the first list element. */
|
|
941
|
+
lpop(key: string): Promise<string | null>;
|
|
942
|
+
/** Get the length of a list. */
|
|
943
|
+
llen(key: string): Promise<number>;
|
|
944
|
+
/** Add members to a set. */
|
|
945
|
+
sadd(key: string, ...members: any[]): Promise<number>;
|
|
946
|
+
/** Get all members of a set. */
|
|
947
|
+
smembers(key: string): Promise<string[]>;
|
|
948
|
+
/** Check if a value is in a set. */
|
|
949
|
+
sismember(key: string, member: any): Promise<boolean>;
|
|
950
|
+
/** Remove a member from a set. */
|
|
951
|
+
srem(key: string, member: any): Promise<number>;
|
|
952
|
+
/** Get the number of members in a set. */
|
|
953
|
+
scard(key: string): Promise<number>;
|
|
954
|
+
/** Add a member to a sorted set with a score. */
|
|
955
|
+
zadd(key: string, score: number, member: any): Promise<number>;
|
|
956
|
+
/** Get members in a sorted set by index range. */
|
|
957
|
+
zrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
958
|
+
/** Get members by score range. */
|
|
959
|
+
zrangebyscore(key: string, min: number, max: number): Promise<string[]>;
|
|
960
|
+
/** Remove a member from a sorted set. */
|
|
961
|
+
zrem(key: string, member: any): Promise<number>;
|
|
962
|
+
/** Get the number of members in a sorted set. */
|
|
963
|
+
zcard(key: string): Promise<number>;
|
|
964
|
+
/** Subscribe to a pub/sub channel. callback must be a function. Returns an unsubscribe function. */
|
|
965
|
+
subscribe(channel: string, callback: (message: string) => void): Promise<() => Promise<void>>;
|
|
966
|
+
/** Publish a message to a channel. Returns number of receivers. */
|
|
967
|
+
publish(channel: string, message: string): Promise<number>;
|
|
968
|
+
/** Create a pipeline for batching commands. */
|
|
969
|
+
pipeline(): any;
|
|
970
|
+
/** Execute a raw Redis command. Command must be a non-empty string. */
|
|
971
|
+
raw(command: string, ...args: any[]): Promise<any>;
|
|
972
|
+
/** Ping the Redis server. Returns 'PONG' if healthy. */
|
|
973
|
+
ping(): Promise<string>;
|
|
974
|
+
/** Get Redis server info. Optional section filter. */
|
|
975
|
+
info(section?: string): Promise<string>;
|
|
976
|
+
/** Get the number of keys in the current database. */
|
|
977
|
+
dbsize(): Promise<number>;
|
|
978
|
+
/** Close the Redis connection. */
|
|
979
|
+
close(): Promise<void>;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// --- Database ----------------------------------------------------
|
|
983
|
+
|
|
984
|
+
export type AdapterType = 'memory' | 'json' | 'sqlite' | 'mysql' | 'postgres' | 'mongo' | 'redis';
|
|
985
|
+
|
|
986
|
+
export class Database {
|
|
987
|
+
/** The underlying adapter instance. */
|
|
988
|
+
adapter: any;
|
|
989
|
+
|
|
990
|
+
constructor(adapter: any);
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Connect to a database using the specified adapter type.
|
|
994
|
+
* Validates credentials for network adapters (mysql, postgres, mongo).
|
|
995
|
+
*/
|
|
996
|
+
static connect(type: 'sqlite', options?: SqliteOptions): Database & { adapter: SqliteAdapter };
|
|
997
|
+
static connect(type: 'mysql', options?: MySqlOptions): Database & { adapter: MySqlAdapter };
|
|
998
|
+
static connect(type: 'postgres', options?: PostgresOptions): Database & { adapter: PostgresAdapter };
|
|
999
|
+
static connect(type: 'mongo', options?: MongoOptions): Database & { adapter: MongoAdapter };
|
|
1000
|
+
static connect(type: 'redis', options?: RedisOptions): Database & { adapter: RedisAdapter };
|
|
1001
|
+
static connect(type: 'memory', options?: object): Database & { adapter: MemoryAdapter };
|
|
1002
|
+
static connect(type: 'json', options?: JsonOptions): Database & { adapter: JsonAdapter };
|
|
1003
|
+
static connect(type: AdapterType, options?: AdapterOptions): Database;
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Register a Model class with this database connection.
|
|
1007
|
+
*/
|
|
1008
|
+
register(ModelClass: typeof Model): Database;
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Register multiple Model classes.
|
|
1012
|
+
*/
|
|
1013
|
+
registerAll(...models: Array<typeof Model>): Database;
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Sync all registered models (create tables).
|
|
1017
|
+
*/
|
|
1018
|
+
sync(): Promise<void>;
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Drop all registered model tables.
|
|
1022
|
+
*/
|
|
1023
|
+
drop(): Promise<void>;
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Close the database connection.
|
|
1027
|
+
*/
|
|
1028
|
+
close(): Promise<void>;
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Get a registered model by name.
|
|
1032
|
+
*/
|
|
1033
|
+
model(name: string): typeof Model | undefined;
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Run a function inside a transaction (begin/commit/rollback if supported).
|
|
1037
|
+
*/
|
|
1038
|
+
transaction(fn: () => Promise<void>): Promise<void>;
|
|
1039
|
+
|
|
1040
|
+
// -- DDL / Migration Methods --------------------------------
|
|
1041
|
+
|
|
1042
|
+
/** Add a column to an existing table. */
|
|
1043
|
+
addColumn(table: string, column: string, definition: SchemaColumnDef): Promise<void>;
|
|
1044
|
+
/** Drop a column from a table. */
|
|
1045
|
+
dropColumn(table: string, column: string): Promise<void>;
|
|
1046
|
+
/** Rename a column. */
|
|
1047
|
+
renameColumn(table: string, oldName: string, newName: string): Promise<void>;
|
|
1048
|
+
/** Rename a table. */
|
|
1049
|
+
renameTable(oldName: string, newName: string): Promise<void>;
|
|
1050
|
+
/** Create an index on a table. */
|
|
1051
|
+
createIndex(table: string, columns: string | string[], options?: { name?: string; unique?: boolean }): Promise<void>;
|
|
1052
|
+
/** Drop an index. */
|
|
1053
|
+
dropIndex(table: string, name: string): Promise<void>;
|
|
1054
|
+
/** Check if a table exists. */
|
|
1055
|
+
hasTable(table: string): Promise<boolean>;
|
|
1056
|
+
/** Check if a column exists on a table. */
|
|
1057
|
+
hasColumn(table: string, column: string): Promise<boolean>;
|
|
1058
|
+
/** Get detailed column info for a table. */
|
|
1059
|
+
describeTable(table: string): Promise<Array<any>>;
|
|
1060
|
+
/** Add a foreign key constraint. */
|
|
1061
|
+
addForeignKey(table: string, column: string, refTable: string, refColumn: string, options?: { onDelete?: string; onUpdate?: string; name?: string }): Promise<void>;
|
|
1062
|
+
/** Drop a foreign key constraint. */
|
|
1063
|
+
dropForeignKey(table: string, constraintName: string): Promise<void>;
|
|
1064
|
+
|
|
1065
|
+
/** Ping the database to check connectivity. */
|
|
1066
|
+
ping(): Promise<boolean>;
|
|
1067
|
+
/** Retry a function with exponential backoff. */
|
|
1068
|
+
retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
1069
|
+
|
|
1070
|
+
// -- Performance & Scalability (Phase 2) -----------------
|
|
1071
|
+
|
|
1072
|
+
/** Enable query profiling on this database instance. */
|
|
1073
|
+
enableProfiling(options?: QueryProfilerOptions): QueryProfiler;
|
|
1074
|
+
/** The attached profiler (null if not enabled). */
|
|
1075
|
+
readonly profiler: QueryProfiler | null;
|
|
1076
|
+
/** The attached replica manager (null if not configured). */
|
|
1077
|
+
readonly replicas: ReplicaManager | null;
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* Connect with read replicas.
|
|
1081
|
+
* @param type - Adapter type for all connections.
|
|
1082
|
+
* @param primaryOpts - Connection options for the primary.
|
|
1083
|
+
* @param replicaConfigs - Array of connection options for each replica.
|
|
1084
|
+
* @param options - ReplicaManager options.
|
|
1085
|
+
*/
|
|
1086
|
+
static connectWithReplicas(
|
|
1087
|
+
type: AdapterType,
|
|
1088
|
+
primaryOpts: AdapterOptions,
|
|
1089
|
+
replicaConfigs: AdapterOptions[],
|
|
1090
|
+
options?: ReplicaManagerOptions,
|
|
1091
|
+
): Database;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
export interface RetryOptions {
|
|
1095
|
+
retries?: number;
|
|
1096
|
+
delay?: number;
|
|
1097
|
+
maxDelay?: number;
|
|
1098
|
+
factor?: number;
|
|
1099
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// --- Migrator -------------------------------------------------------
|
|
1103
|
+
|
|
1104
|
+
export interface MigrationDefinition {
|
|
1105
|
+
/** Unique migration name. Only letters, digits, underscores, hyphens, and dots are allowed. */
|
|
1106
|
+
name: string;
|
|
1107
|
+
up: (db: Database) => Promise<void>;
|
|
1108
|
+
down: (db: Database) => Promise<void>;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export interface MigrateResult {
|
|
1112
|
+
migrated: string[];
|
|
1113
|
+
batch: number;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
export interface RollbackResult {
|
|
1117
|
+
rolledBack: string[];
|
|
1118
|
+
batch: number;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
export interface MigrationStatus {
|
|
1122
|
+
executed: Array<{ name: string; batch: number; executedAt: string }>;
|
|
1123
|
+
pending: string[];
|
|
1124
|
+
lastBatch: number;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
export class Migrator {
|
|
1128
|
+
constructor(db: Database, options?: { table?: string });
|
|
1129
|
+
|
|
1130
|
+
/** Add a migration definition. */
|
|
1131
|
+
add(migration: MigrationDefinition): Migrator;
|
|
1132
|
+
/** Add multiple migration definitions. */
|
|
1133
|
+
addAll(migrations: MigrationDefinition[]): Migrator;
|
|
1134
|
+
/** Run all pending migrations. */
|
|
1135
|
+
migrate(): Promise<MigrateResult>;
|
|
1136
|
+
/** Rollback the last batch. */
|
|
1137
|
+
rollback(): Promise<RollbackResult>;
|
|
1138
|
+
/** Rollback all migrations. */
|
|
1139
|
+
rollbackAll(): Promise<{ rolledBack: string[] }>;
|
|
1140
|
+
/** Rollback all, then re-migrate. */
|
|
1141
|
+
reset(): Promise<MigrateResult & { rolledBack: string[] }>;
|
|
1142
|
+
/** Drop everything and re-migrate. */
|
|
1143
|
+
fresh(): Promise<MigrateResult>;
|
|
1144
|
+
/** Get current migration status. */
|
|
1145
|
+
status(): Promise<MigrationStatus>;
|
|
1146
|
+
/** Check if there are pending migrations. */
|
|
1147
|
+
hasPending(): Promise<boolean>;
|
|
1148
|
+
/** List registered migration names. */
|
|
1149
|
+
list(): string[];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Helper to create a migration definition. */
|
|
1153
|
+
export function defineMigration(
|
|
1154
|
+
name: string,
|
|
1155
|
+
up: (db: Database) => Promise<void>,
|
|
1156
|
+
down: (db: Database) => Promise<void>,
|
|
1157
|
+
): MigrationDefinition;
|
|
1158
|
+
|
|
1159
|
+
// --- QueryCache -----------------------------------------------------
|
|
1160
|
+
|
|
1161
|
+
export interface QueryCacheOptions {
|
|
1162
|
+
maxEntries?: number;
|
|
1163
|
+
defaultTTL?: number;
|
|
1164
|
+
prefix?: string;
|
|
1165
|
+
redis?: any;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
export interface CacheStats {
|
|
1169
|
+
size: number;
|
|
1170
|
+
hits: number;
|
|
1171
|
+
misses: number;
|
|
1172
|
+
hitRate: number;
|
|
1173
|
+
maxEntries: number;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
export class QueryCache {
|
|
1177
|
+
constructor(options?: QueryCacheOptions);
|
|
1178
|
+
|
|
1179
|
+
/** Generate a cache key from a query descriptor. */
|
|
1180
|
+
static keyFromDescriptor(descriptor: Record<string, any>): string;
|
|
1181
|
+
/** Get a cached value. */
|
|
1182
|
+
get(key: string): any;
|
|
1183
|
+
/** Set a cached value. */
|
|
1184
|
+
set(key: string, value: any, ttl?: number): void;
|
|
1185
|
+
/** Delete a cached entry. */
|
|
1186
|
+
delete(key: string): boolean;
|
|
1187
|
+
/** Check if a key exists and is not expired. */
|
|
1188
|
+
has(key: string): boolean;
|
|
1189
|
+
/** Invalidate all entries matching a table name. */
|
|
1190
|
+
invalidate(table: string): number;
|
|
1191
|
+
/** Clear the entire cache. */
|
|
1192
|
+
flush(): number;
|
|
1193
|
+
/** Get hit/miss statistics. */
|
|
1194
|
+
stats(): CacheStats;
|
|
1195
|
+
/** Remove expired entries. */
|
|
1196
|
+
prune(): number;
|
|
1197
|
+
/** Get or compute and cache a value. */
|
|
1198
|
+
remember<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T>;
|
|
1199
|
+
/** Wrap a query execution with caching. */
|
|
1200
|
+
wrap<T>(descriptor: Record<string, any>, executor: () => Promise<T>, ttl?: number): Promise<T>;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// --- Seeder ---------------------------------------------------------
|
|
1204
|
+
|
|
1205
|
+
/** Base Seeder class. Extend to create seeders. */
|
|
1206
|
+
export class Seeder {
|
|
1207
|
+
/** Override this method to define seeding logic. */
|
|
1208
|
+
run(db: Database): Promise<void>;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
export class SeederRunner {
|
|
1212
|
+
constructor(db: Database);
|
|
1213
|
+
|
|
1214
|
+
/** Run one or more seeder classes. */
|
|
1215
|
+
run(...seeders: Array<(new () => Seeder) | Seeder>): Promise<string[]>;
|
|
1216
|
+
/** Run a single seeder. */
|
|
1217
|
+
call(SeederClass: new () => Seeder): Promise<void>;
|
|
1218
|
+
/** Clear all data, then run seeders. */
|
|
1219
|
+
fresh(...seeders: Array<(new () => Seeder) | Seeder>): Promise<string[]>;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// --- Factory --------------------------------------------------------
|
|
1223
|
+
|
|
1224
|
+
export class Factory<T extends typeof Model = typeof Model> {
|
|
1225
|
+
constructor(ModelClass: T);
|
|
1226
|
+
|
|
1227
|
+
/** Define default field generators. */
|
|
1228
|
+
define(definition: Record<string, any | ((index: number) => any)>): Factory<T>;
|
|
1229
|
+
/** Set how many records to create. */
|
|
1230
|
+
count(n: number): Factory<T>;
|
|
1231
|
+
/** Define a named state variation. */
|
|
1232
|
+
state(name: string, overrides: Record<string, any>): Factory<T>;
|
|
1233
|
+
/** Apply a named state to the next create/make. */
|
|
1234
|
+
withState(name: string): Factory<T>;
|
|
1235
|
+
/** Register an after-create callback. */
|
|
1236
|
+
afterCreating(fn: (record: any, index: number) => Promise<void>): Factory<T>;
|
|
1237
|
+
/** Build records without persisting. */
|
|
1238
|
+
make(overrides?: Record<string, any>): any | any[];
|
|
1239
|
+
/** Create and persist records. */
|
|
1240
|
+
create(overrides?: Record<string, any>): Promise<any | any[]>;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// --- Fake -----------------------------------------------------------
|
|
1244
|
+
|
|
1245
|
+
export interface FakeNameOptions {
|
|
1246
|
+
sex?: 'male' | 'female';
|
|
1247
|
+
locale?: string;
|
|
1248
|
+
unique?: boolean;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
export interface FakeFullNameOptions extends FakeNameOptions {
|
|
1252
|
+
prefix?: boolean;
|
|
1253
|
+
middle?: boolean;
|
|
1254
|
+
suffix?: boolean;
|
|
1255
|
+
firstName?: string;
|
|
1256
|
+
lastName?: string;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
export interface FakePhoneOptions {
|
|
1260
|
+
/** ISO country code (default: 'US'). */
|
|
1261
|
+
countryCode?: string;
|
|
1262
|
+
/** Format style (default: 'human'). */
|
|
1263
|
+
format?: 'human' | 'national' | 'international';
|
|
1264
|
+
unique?: boolean;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
export interface FakeEmailOptions {
|
|
1268
|
+
firstName?: string;
|
|
1269
|
+
lastName?: string;
|
|
1270
|
+
/** Force a specific provider domain. */
|
|
1271
|
+
provider?: string;
|
|
1272
|
+
/** Use only safe example./test. domains. */
|
|
1273
|
+
safe?: boolean;
|
|
1274
|
+
locale?: string;
|
|
1275
|
+
unique?: boolean;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
export interface FakeUsernameOptions {
|
|
1279
|
+
firstName?: string;
|
|
1280
|
+
lastName?: string;
|
|
1281
|
+
/** Separator style between name parts. */
|
|
1282
|
+
style?: 'dot' | 'underscore' | 'none' | 'random';
|
|
1283
|
+
/** Append a numeric suffix (default: true). */
|
|
1284
|
+
numbers?: boolean;
|
|
1285
|
+
locale?: string;
|
|
1286
|
+
unique?: boolean;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
export interface FakeNumericStringOptions {
|
|
1290
|
+
/** Allow leading zeros (default: true). */
|
|
1291
|
+
leadingZeros?: boolean;
|
|
1292
|
+
/** Grouping separator character (e.g. '-' for credit-card style). */
|
|
1293
|
+
separator?: string;
|
|
1294
|
+
/** Width of each separated group. */
|
|
1295
|
+
groupSize?: number;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
export interface FakePasswordOptions {
|
|
1299
|
+
length?: number;
|
|
1300
|
+
uppercase?: boolean;
|
|
1301
|
+
lowercase?: boolean;
|
|
1302
|
+
digits?: boolean;
|
|
1303
|
+
special?: boolean;
|
|
1304
|
+
prefix?: string;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
export interface FakePriceOptions {
|
|
1308
|
+
min?: number;
|
|
1309
|
+
max?: number;
|
|
1310
|
+
symbol?: string;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
export interface FakeAddressOptions {
|
|
1314
|
+
countryCode?: string;
|
|
1315
|
+
/** 'string' (default) or 'object' to return address parts. */
|
|
1316
|
+
format?: 'string' | 'object';
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
export interface FakeAddressObject {
|
|
1320
|
+
street: string;
|
|
1321
|
+
city: string;
|
|
1322
|
+
state: string;
|
|
1323
|
+
zipCode: string;
|
|
1324
|
+
country: string;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
export interface FakeMacOptions {
|
|
1328
|
+
separator?: ':' | '-' | '.';
|
|
1329
|
+
realisticOUI?: boolean;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
export interface FakeUrlOptions {
|
|
1333
|
+
protocol?: string;
|
|
1334
|
+
appendSlash?: boolean;
|
|
1335
|
+
/** Suppress the word path segment. */
|
|
1336
|
+
noPath?: boolean;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
export interface FakeIpOptions {
|
|
1340
|
+
network?: 'any' | 'private-a' | 'private-b' | 'private-c' | 'loopback';
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
export interface FakeUniqueOptions {
|
|
1344
|
+
/** Namespace key for deduplication tracking. */
|
|
1345
|
+
key?: string;
|
|
1346
|
+
maxAttempts?: number;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
export class Fake {
|
|
1350
|
+
// -- RNG / Seeding ------------------------------------------------
|
|
1351
|
+
/** Set a deterministic seed (pass null to reset). */
|
|
1352
|
+
static seed(value?: number | string | null): number | null;
|
|
1353
|
+
/** Return the active seed, or null if using Math.random. */
|
|
1354
|
+
static getSeed(): number | null;
|
|
1355
|
+
/**
|
|
1356
|
+
* Generate a unique value by calling fn() until an unseen result is
|
|
1357
|
+
* returned for the given namespace key.
|
|
1358
|
+
*/
|
|
1359
|
+
static unique<T>(fn: () => T, options?: FakeUniqueOptions): T;
|
|
1360
|
+
/** Clear uniqueness tracking for a key, or all keys if omitted. */
|
|
1361
|
+
static resetUnique(key?: string): void;
|
|
1362
|
+
/** Count how many unique values have been generated for a key. */
|
|
1363
|
+
static uniqueCount(key: string): number;
|
|
1364
|
+
|
|
1365
|
+
// -- Names --------------------------------------------------------
|
|
1366
|
+
static firstName(options?: FakeNameOptions): string;
|
|
1367
|
+
static lastName(options?: Pick<FakeNameOptions, 'locale' | 'unique'>): string;
|
|
1368
|
+
/** Default (no options) returns exactly "First Last". */
|
|
1369
|
+
static fullName(options?: FakeFullNameOptions): string;
|
|
1370
|
+
static middleName(options?: FakeNameOptions): string;
|
|
1371
|
+
static namePrefix(options?: Pick<FakeNameOptions, 'sex'>): string;
|
|
1372
|
+
static nameSuffix(): string;
|
|
1373
|
+
/** List of supported locale codes. */
|
|
1374
|
+
static locales(): string[];
|
|
1375
|
+
|
|
1376
|
+
// -- Phone --------------------------------------------------------
|
|
1377
|
+
static phone(options?: FakePhoneOptions): string;
|
|
1378
|
+
/** All supported phone country codes. */
|
|
1379
|
+
static phoneCodes(): string[];
|
|
1380
|
+
|
|
1381
|
+
// -- Internet / Email ---------------------------------------------
|
|
1382
|
+
static email(options?: FakeEmailOptions): string;
|
|
1383
|
+
static username(options?: FakeUsernameOptions): string;
|
|
1384
|
+
static domainName(options?: { tld?: string }): string;
|
|
1385
|
+
static url(options?: FakeUrlOptions): string;
|
|
1386
|
+
static ip(options?: FakeIpOptions): string;
|
|
1387
|
+
static ipv6(): string;
|
|
1388
|
+
static mac(options?: FakeMacOptions): string;
|
|
1389
|
+
static port(options?: { range?: 'all' | 'registered' | 'dynamic' }): number;
|
|
1390
|
+
static httpMethod(options?: { methods?: string[] }): string;
|
|
1391
|
+
static userAgent(): string;
|
|
1392
|
+
static password(options?: FakePasswordOptions): string;
|
|
1393
|
+
|
|
1394
|
+
// -- Numbers ------------------------------------------------------
|
|
1395
|
+
static uuid(): string;
|
|
1396
|
+
static integer(min?: number, max?: number): number;
|
|
1397
|
+
static float(min?: number, max?: number, decimals?: number): number;
|
|
1398
|
+
static boolean(): boolean;
|
|
1399
|
+
/** Fixed-length numeric string (e.g. ZIP codes, PINs, credit card numbers). */
|
|
1400
|
+
static numericString(length?: number, options?: FakeNumericStringOptions): string;
|
|
1401
|
+
/** Random alphanumeric string. */
|
|
1402
|
+
static alphanumeric(length?: number, options?: { uppercase?: boolean }): string;
|
|
1403
|
+
/** Random alphabetic string. */
|
|
1404
|
+
static alpha(length?: number, options?: { uppercase?: boolean }): string;
|
|
1405
|
+
|
|
1406
|
+
// -- Dates --------------------------------------------------------
|
|
1407
|
+
static date(start?: Date, end?: Date): Date;
|
|
1408
|
+
static dateString(start?: Date, end?: Date): string;
|
|
1409
|
+
static datePast(options?: { years?: number }): Date;
|
|
1410
|
+
static dateFuture(options?: { years?: number }): Date;
|
|
1411
|
+
|
|
1412
|
+
// -- Text ---------------------------------------------------------
|
|
1413
|
+
static paragraph(sentences?: number): string;
|
|
1414
|
+
static sentence(wordCount?: number): string;
|
|
1415
|
+
static word(options?: { type?: 'lorem' | 'adjective' | 'noun' | 'verb' }): string;
|
|
1416
|
+
static words(n?: number): string;
|
|
1417
|
+
static hackerPhrase(): string;
|
|
1418
|
+
static slug(wordCount?: number): string;
|
|
1419
|
+
static hashtag(): string;
|
|
1420
|
+
|
|
1421
|
+
// -- Person -------------------------------------------------------
|
|
1422
|
+
static jobTitle(options?: { full?: boolean }): string;
|
|
1423
|
+
static jobArea(): string;
|
|
1424
|
+
static jobType(): string;
|
|
1425
|
+
static jobDescriptor(): string;
|
|
1426
|
+
static bio(options?: { style?: 'short' | 'medium' | 'long' }): string;
|
|
1427
|
+
static zodiacSign(): string;
|
|
1428
|
+
static gender(options?: { binary?: boolean }): string;
|
|
1429
|
+
static bloodType(): string;
|
|
1430
|
+
|
|
1431
|
+
// -- Location -----------------------------------------------------
|
|
1432
|
+
static city(options?: { country?: string }): string;
|
|
1433
|
+
static country(options?: { codeOnly?: boolean; full?: boolean }): string | { name: string; code: string };
|
|
1434
|
+
static state(options?: { abbr?: boolean; full?: boolean }): string | { name: string; abbr: string };
|
|
1435
|
+
static zipCode(options?: { countryCode?: string }): string;
|
|
1436
|
+
static latitude(options?: { min?: number; max?: number; decimals?: number }): number;
|
|
1437
|
+
static longitude(options?: { min?: number; max?: number; decimals?: number }): number;
|
|
1438
|
+
static coordinates(): { latitude: number; longitude: number };
|
|
1439
|
+
static timezone(): string;
|
|
1440
|
+
static streetName(): string;
|
|
1441
|
+
static address(options?: FakeAddressOptions): string | FakeAddressObject;
|
|
1442
|
+
|
|
1443
|
+
// -- Commerce -----------------------------------------------------
|
|
1444
|
+
static productName(options?: { withMaterial?: boolean }): string;
|
|
1445
|
+
static category(): string;
|
|
1446
|
+
static department(): string;
|
|
1447
|
+
static company(options?: { suffix?: boolean }): string;
|
|
1448
|
+
static price(options?: FakePriceOptions): string;
|
|
1449
|
+
static industry(): string;
|
|
1450
|
+
static catchPhrase(): string;
|
|
1451
|
+
|
|
1452
|
+
// -- Colour -------------------------------------------------------
|
|
1453
|
+
static color(): string;
|
|
1454
|
+
static rgb(options?: { format?: 'css' | 'array' | 'object' }): string | number[] | { r: number; g: number; b: number };
|
|
1455
|
+
static hsl(options?: { format?: 'css' | 'array' | 'object' }): string | number[] | { h: number; s: number; l: number };
|
|
1456
|
+
|
|
1457
|
+
// -- Helpers ------------------------------------------------------
|
|
1458
|
+
static pick<T>(arr: T[]): T;
|
|
1459
|
+
static pickMany<T>(arr: T[], n: number): T[];
|
|
1460
|
+
static shuffle<T>(arr: T[]): T[];
|
|
1461
|
+
static enumValue<T>(values: T[]): T;
|
|
1462
|
+
static json(): { key: string; value: string; count: number; active: boolean };
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// --- QueryProfiler --------------------------------------------------
|
|
1466
|
+
|
|
1467
|
+
export interface QueryProfilerOptions {
|
|
1468
|
+
/** Enable/disable profiling (default: true). */
|
|
1469
|
+
enabled?: boolean;
|
|
1470
|
+
/** Duration (ms) above which a query is "slow" (default: 100). */
|
|
1471
|
+
slowThreshold?: number;
|
|
1472
|
+
/** Maximum recorded query entries (default: 1000). */
|
|
1473
|
+
maxHistory?: number;
|
|
1474
|
+
/** Callback on slow query. */
|
|
1475
|
+
onSlow?: (entry: ProfiledQuery) => void;
|
|
1476
|
+
/** Minimum rapid same-table SELECTs to flag N+1 (default: 5). */
|
|
1477
|
+
n1Threshold?: number;
|
|
1478
|
+
/** Time window (ms) for N+1 detection (default: 100). */
|
|
1479
|
+
n1Window?: number;
|
|
1480
|
+
/** Callback on N+1 detection. */
|
|
1481
|
+
onN1?: (info: N1Detection) => void;
|
|
1482
|
+
/** Maximum N+1 detection history entries (default: 100). */
|
|
1483
|
+
maxN1History?: number;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
export interface ProfiledQuery {
|
|
1487
|
+
table: string;
|
|
1488
|
+
action: string;
|
|
1489
|
+
duration: number;
|
|
1490
|
+
timestamp: number;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
export interface N1Detection {
|
|
1494
|
+
table: string;
|
|
1495
|
+
count: number;
|
|
1496
|
+
timestamp: number;
|
|
1497
|
+
message: string;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
export interface ProfilerMetrics {
|
|
1501
|
+
totalQueries: number;
|
|
1502
|
+
totalTime: number;
|
|
1503
|
+
avgLatency: number;
|
|
1504
|
+
queriesPerSecond: number;
|
|
1505
|
+
slowQueries: number;
|
|
1506
|
+
n1Detections: number;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
export class QueryProfiler {
|
|
1510
|
+
constructor(options?: QueryProfilerOptions);
|
|
1511
|
+
|
|
1512
|
+
/** Whether profiling is currently enabled. */
|
|
1513
|
+
get enabled(): boolean;
|
|
1514
|
+
set enabled(value: boolean);
|
|
1515
|
+
|
|
1516
|
+
/** Record a query execution. */
|
|
1517
|
+
record(entry: { table: string; action: string; duration: number }): void;
|
|
1518
|
+
/** Get aggregate profiling metrics. */
|
|
1519
|
+
metrics(): ProfilerMetrics;
|
|
1520
|
+
/** Get all slow queries from history. */
|
|
1521
|
+
slowQueries(): ProfiledQuery[];
|
|
1522
|
+
/** Get all N+1 detections. */
|
|
1523
|
+
n1Detections(): N1Detection[];
|
|
1524
|
+
/** Get filtered query history. */
|
|
1525
|
+
getQueries(options?: { table?: string; action?: string; minDuration?: number }): ProfiledQuery[];
|
|
1526
|
+
/** Reset all profiling state. */
|
|
1527
|
+
reset(): void;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// --- ReplicaManager -------------------------------------------------
|
|
1531
|
+
|
|
1532
|
+
export interface ReplicaManagerOptions {
|
|
1533
|
+
/** Selection strategy: 'round-robin' | 'random' (default: 'round-robin'). */
|
|
1534
|
+
strategy?: 'round-robin' | 'random';
|
|
1535
|
+
/** Read from primary after a write for stickyWindow ms (default: true). */
|
|
1536
|
+
stickyWrite?: boolean;
|
|
1537
|
+
/** Duration (ms) to read from primary after a write (default: 1000). */
|
|
1538
|
+
stickyWindow?: number;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
export interface HealthCheckResult {
|
|
1542
|
+
healthy: boolean;
|
|
1543
|
+
lastChecked: number;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
export class ReplicaManager {
|
|
1547
|
+
constructor(options?: ReplicaManagerOptions);
|
|
1548
|
+
|
|
1549
|
+
/** Number of registered replicas. */
|
|
1550
|
+
readonly replicaCount: number;
|
|
1551
|
+
|
|
1552
|
+
/** Set the primary (read-write) adapter. */
|
|
1553
|
+
setPrimary(adapter: any): void;
|
|
1554
|
+
/** Add a read replica adapter. */
|
|
1555
|
+
addReplica(adapter: any): void;
|
|
1556
|
+
/** Get an adapter for read operations (respects strategy, health, sticky writes). */
|
|
1557
|
+
getReadAdapter(): any;
|
|
1558
|
+
/** Get the primary adapter for write operations (updates sticky window). */
|
|
1559
|
+
getWriteAdapter(): any;
|
|
1560
|
+
/** Mark a replica as unhealthy. */
|
|
1561
|
+
markUnhealthy(adapter: any): void;
|
|
1562
|
+
/** Mark a replica as healthy. */
|
|
1563
|
+
markHealthy(adapter: any): void;
|
|
1564
|
+
/** Run a health check on all replicas. */
|
|
1565
|
+
healthCheck(): Promise<HealthCheckResult[]>;
|
|
1566
|
+
/** Get all adapters (primary + replicas). */
|
|
1567
|
+
getAllAdapters(): any[];
|
|
1568
|
+
/** Close all adapters (primary + replicas). */
|
|
1569
|
+
closeAll(): Promise<void>;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// --- Multi-Tenancy (Phase 4) ----------------------------------------
|
|
1573
|
+
|
|
1574
|
+
export interface TenantManagerOptions {
|
|
1575
|
+
/** Tenancy strategy. */
|
|
1576
|
+
strategy?: 'row' | 'schema';
|
|
1577
|
+
/** Column name for row-level tenancy. */
|
|
1578
|
+
tenantColumn?: string;
|
|
1579
|
+
/** Default schema name (schema strategy). */
|
|
1580
|
+
defaultSchema?: string;
|
|
1581
|
+
/** Schema name prefix (schema strategy). */
|
|
1582
|
+
schemaPrefix?: string;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
export interface TenantMiddlewareOptions {
|
|
1586
|
+
/** Header to read tenant from. */
|
|
1587
|
+
header?: string;
|
|
1588
|
+
/** Query parameter name. */
|
|
1589
|
+
queryParam?: string;
|
|
1590
|
+
/** Custom extraction function. */
|
|
1591
|
+
extract?: (req: any) => string | undefined;
|
|
1592
|
+
/** Reject requests without tenant. */
|
|
1593
|
+
required?: boolean;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
export class TenantManager {
|
|
1597
|
+
constructor(db: Database, options?: TenantManagerOptions);
|
|
1598
|
+
|
|
1599
|
+
/** The tenancy strategy. */
|
|
1600
|
+
readonly strategy: string;
|
|
1601
|
+
/** The tenant column name (row strategy). */
|
|
1602
|
+
readonly tenantColumn: string;
|
|
1603
|
+
|
|
1604
|
+
/** Set the current tenant for subsequent queries. */
|
|
1605
|
+
setCurrentTenant(tenantId: string): TenantManager;
|
|
1606
|
+
/** Get the current tenant ID. */
|
|
1607
|
+
getCurrentTenant(): string | null;
|
|
1608
|
+
/** Clear the current tenant context. */
|
|
1609
|
+
clearTenant(): TenantManager;
|
|
1610
|
+
/** Execute a function within a specific tenant context. */
|
|
1611
|
+
withTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T>;
|
|
1612
|
+
/** Register a Model for tenant scoping. */
|
|
1613
|
+
addModel(ModelClass: typeof Model): TenantManager;
|
|
1614
|
+
/** Register multiple Models for tenant scoping. */
|
|
1615
|
+
addModels(...models: Array<typeof Model>): TenantManager;
|
|
1616
|
+
/** Create a new tenant (schema or row). */
|
|
1617
|
+
createTenant(tenantId: string): Promise<void>;
|
|
1618
|
+
/** Drop a tenant. */
|
|
1619
|
+
dropTenant(tenantId: string, options?: { cascade?: boolean }): Promise<void>;
|
|
1620
|
+
/** List all known tenant IDs. */
|
|
1621
|
+
listTenants(): string[];
|
|
1622
|
+
/** Check if a tenant exists. */
|
|
1623
|
+
hasTenant(tenantId: string): boolean;
|
|
1624
|
+
/** Returns tenant extraction middleware. */
|
|
1625
|
+
middleware(options?: TenantMiddlewareOptions): (req: any, res: any, next: () => void) => void;
|
|
1626
|
+
/** Run migrations for a specific tenant. */
|
|
1627
|
+
migrate(migrator: Migrator, tenantId: string): Promise<MigrateResult>;
|
|
1628
|
+
/** Run migrations for all known tenants. */
|
|
1629
|
+
migrateAll(migrator: Migrator): Promise<Map<string, MigrateResult>>;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// --- Audit Logging (Phase 4) ----------------------------------------
|
|
1633
|
+
|
|
1634
|
+
export interface AuditLogOptions {
|
|
1635
|
+
/** Table name for audit entries. */
|
|
1636
|
+
table?: string;
|
|
1637
|
+
/** Models to audit. */
|
|
1638
|
+
include?: Array<typeof Model>;
|
|
1639
|
+
/** Models to exclude from auditing. */
|
|
1640
|
+
exclude?: Array<typeof Model>;
|
|
1641
|
+
/** Fields to never log. */
|
|
1642
|
+
excludeFields?: string[];
|
|
1643
|
+
/** Context property for actor identifier. */
|
|
1644
|
+
actorField?: string;
|
|
1645
|
+
/** Separate database for audit storage. */
|
|
1646
|
+
storage?: Database;
|
|
1647
|
+
/** Include timestamps in entries. */
|
|
1648
|
+
timestamps?: boolean;
|
|
1649
|
+
/** Store field-level diffs for updates. */
|
|
1650
|
+
diffs?: boolean;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
export interface AuditEntry {
|
|
1654
|
+
id: number;
|
|
1655
|
+
action: 'create' | 'update' | 'delete';
|
|
1656
|
+
table_name: string;
|
|
1657
|
+
record_id: string | null;
|
|
1658
|
+
actor: string | null;
|
|
1659
|
+
old_values: Record<string, any> | null;
|
|
1660
|
+
new_values: Record<string, any> | null;
|
|
1661
|
+
diff: Array<{ field: string; from: any; to: any }> | null;
|
|
1662
|
+
timestamp: string;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
export interface AuditTrailOptions {
|
|
1666
|
+
/** Filter by table name. */
|
|
1667
|
+
table?: string;
|
|
1668
|
+
/** Filter by action. */
|
|
1669
|
+
action?: 'create' | 'update' | 'delete';
|
|
1670
|
+
/** Filter by record ID. */
|
|
1671
|
+
recordId?: string;
|
|
1672
|
+
/** Filter by actor. */
|
|
1673
|
+
actor?: string;
|
|
1674
|
+
/** ISO timestamp lower bound. */
|
|
1675
|
+
since?: string;
|
|
1676
|
+
/** ISO timestamp upper bound. */
|
|
1677
|
+
until?: string;
|
|
1678
|
+
/** Maximum entries (default 100). */
|
|
1679
|
+
limit?: number;
|
|
1680
|
+
/** Skip entries. */
|
|
1681
|
+
offset?: number;
|
|
1682
|
+
/** Sort order (default 'desc'). */
|
|
1683
|
+
order?: 'asc' | 'desc';
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
export interface AuditMiddlewareOptions {
|
|
1687
|
+
/** Custom actor extraction function. */
|
|
1688
|
+
extract?: (req: any) => string | undefined;
|
|
1689
|
+
/** Header to read actor from. */
|
|
1690
|
+
header?: string;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
export class AuditLog {
|
|
1694
|
+
constructor(db: Database, options?: AuditLogOptions);
|
|
1695
|
+
|
|
1696
|
+
/** Initialize the audit table and attach hooks. */
|
|
1697
|
+
install(): Promise<AuditLog>;
|
|
1698
|
+
/** Set the current actor. */
|
|
1699
|
+
setActor(actor: string): AuditLog;
|
|
1700
|
+
/** Get the current actor. */
|
|
1701
|
+
getActor(): string | null;
|
|
1702
|
+
/** Execute a function within a specific actor context. */
|
|
1703
|
+
withActor<T>(actor: string, fn: () => Promise<T>): Promise<T>;
|
|
1704
|
+
/** Compute a diff between two objects. */
|
|
1705
|
+
diff(oldValues: Record<string, any>, newValues: Record<string, any>): Array<{ field: string; from: any; to: any }>;
|
|
1706
|
+
/** Query the audit trail. */
|
|
1707
|
+
trail(options?: AuditTrailOptions): Promise<AuditEntry[]>;
|
|
1708
|
+
/** Get audit history for a specific record. */
|
|
1709
|
+
history(table: string, recordId: string | number, options?: AuditTrailOptions): Promise<AuditEntry[]>;
|
|
1710
|
+
/** Get audit entries grouped by actor. */
|
|
1711
|
+
byActor(options?: AuditTrailOptions): Promise<Map<string, AuditEntry[]>>;
|
|
1712
|
+
/** Count audit entries. */
|
|
1713
|
+
count(options?: AuditTrailOptions): Promise<number>;
|
|
1714
|
+
/** Purge old audit entries. */
|
|
1715
|
+
purge(options: { before?: string; table?: string; keepLast?: number }): Promise<number>;
|
|
1716
|
+
/** Returns actor extraction middleware. */
|
|
1717
|
+
middleware(options?: AuditMiddlewareOptions): (req: any, res: any, next: () => void) => void;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// --- Plugin System (Phase 4) ----------------------------------------
|
|
1721
|
+
|
|
1722
|
+
export interface PluginDefinition {
|
|
1723
|
+
/** Unique plugin name. */
|
|
1724
|
+
name: string;
|
|
1725
|
+
/** Plugin version string. */
|
|
1726
|
+
version?: string;
|
|
1727
|
+
/** Install function called on registration. */
|
|
1728
|
+
install: (manager: PluginManager, options?: Record<string, any>) => void;
|
|
1729
|
+
/** Boot function called after all plugins are registered. */
|
|
1730
|
+
boot?: (manager: PluginManager, options?: Record<string, any>) => Promise<void> | void;
|
|
1731
|
+
/** Cleanup function. */
|
|
1732
|
+
uninstall?: (manager: PluginManager) => void;
|
|
1733
|
+
/** Required plugin names. */
|
|
1734
|
+
dependencies?: string[];
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
export interface PluginInfo {
|
|
1738
|
+
name: string;
|
|
1739
|
+
version: string;
|
|
1740
|
+
hasBootFn: boolean;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
export class PluginManager {
|
|
1744
|
+
constructor(db?: Database);
|
|
1745
|
+
|
|
1746
|
+
/** Number of registered plugins. */
|
|
1747
|
+
readonly size: number;
|
|
1748
|
+
|
|
1749
|
+
/** Register a plugin. */
|
|
1750
|
+
register(plugin: PluginDefinition, options?: Record<string, any>): PluginManager;
|
|
1751
|
+
/** Register multiple plugins. */
|
|
1752
|
+
registerAll(...plugins: Array<PluginDefinition | [PluginDefinition, Record<string, any>]>): PluginManager;
|
|
1753
|
+
/** Unregister a plugin by name. */
|
|
1754
|
+
unregister(name: string): PluginManager;
|
|
1755
|
+
/** Boot all registered plugins. */
|
|
1756
|
+
boot(): Promise<PluginManager>;
|
|
1757
|
+
/** Register a hook listener. */
|
|
1758
|
+
hook(name: string, callback: (...args: any[]) => any): PluginManager;
|
|
1759
|
+
/** Remove a hook listener. */
|
|
1760
|
+
unhook(name: string, callback: Function): PluginManager;
|
|
1761
|
+
/** Execute all listeners for a hook. */
|
|
1762
|
+
runHook(name: string, ...args: any[]): Promise<any>;
|
|
1763
|
+
/** Check if listeners exist for a hook. */
|
|
1764
|
+
hasHook(name: string): boolean;
|
|
1765
|
+
/** Check if a plugin is registered. */
|
|
1766
|
+
has(name: string): boolean;
|
|
1767
|
+
/** Get a registered plugin by name. */
|
|
1768
|
+
get(name: string): PluginDefinition | undefined;
|
|
1769
|
+
/** Get options for a registered plugin. */
|
|
1770
|
+
getOptions(name: string): Record<string, any> | undefined;
|
|
1771
|
+
/** List all registered plugin names. */
|
|
1772
|
+
list(): string[];
|
|
1773
|
+
/** Get info about all registered plugins. */
|
|
1774
|
+
info(): PluginInfo[];
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// --- Stored Procedures & Functions (Phase 4) ------------------------
|
|
1778
|
+
|
|
1779
|
+
export interface ProcedureParam {
|
|
1780
|
+
/** Parameter name. */
|
|
1781
|
+
name: string;
|
|
1782
|
+
/** SQL type. */
|
|
1783
|
+
type: string;
|
|
1784
|
+
/** IN, OUT, or INOUT (procedures only). */
|
|
1785
|
+
direction?: 'IN' | 'OUT' | 'INOUT';
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
export interface StoredProcedureOptions {
|
|
1789
|
+
/** Procedure parameters. */
|
|
1790
|
+
params?: ProcedureParam[];
|
|
1791
|
+
/** Procedure body (SQL). */
|
|
1792
|
+
body: string;
|
|
1793
|
+
/** Language (sql, plpgsql). */
|
|
1794
|
+
language?: string;
|
|
1795
|
+
/** Adapter-specific options. */
|
|
1796
|
+
options?: Record<string, any>;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
export class StoredProcedure {
|
|
1800
|
+
constructor(name: string, options: StoredProcedureOptions);
|
|
1801
|
+
|
|
1802
|
+
/** Procedure name. */
|
|
1803
|
+
readonly name: string;
|
|
1804
|
+
|
|
1805
|
+
/** Create the procedure in the database. */
|
|
1806
|
+
create(db: Database): Promise<void>;
|
|
1807
|
+
/** Drop the procedure. */
|
|
1808
|
+
drop(db: Database, options?: { ifExists?: boolean }): Promise<void>;
|
|
1809
|
+
/** Execute the procedure with arguments. */
|
|
1810
|
+
execute(db: Database, args?: any[]): Promise<any>;
|
|
1811
|
+
/** Check if the procedure exists. */
|
|
1812
|
+
exists(db: Database): Promise<boolean>;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
export interface StoredFunctionOptions {
|
|
1816
|
+
/** Function parameters. */
|
|
1817
|
+
params?: Array<{ name: string; type: string }>;
|
|
1818
|
+
/** Return type. */
|
|
1819
|
+
returns: string;
|
|
1820
|
+
/** Function body (SQL). */
|
|
1821
|
+
body: string;
|
|
1822
|
+
/** Language. */
|
|
1823
|
+
language?: string;
|
|
1824
|
+
/** Whether the function is deterministic (MySQL). */
|
|
1825
|
+
deterministic?: boolean;
|
|
1826
|
+
/** PostgreSQL volatility (STABLE, VOLATILE, IMMUTABLE). */
|
|
1827
|
+
volatility?: string;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
export class StoredFunction {
|
|
1831
|
+
constructor(name: string, options: StoredFunctionOptions);
|
|
1832
|
+
|
|
1833
|
+
/** Function name. */
|
|
1834
|
+
readonly name: string;
|
|
1835
|
+
|
|
1836
|
+
/** Create the function in the database. */
|
|
1837
|
+
create(db: Database): Promise<void>;
|
|
1838
|
+
/** Drop the function. */
|
|
1839
|
+
drop(db: Database, options?: { ifExists?: boolean }): Promise<void>;
|
|
1840
|
+
/** Call the function and return its result. */
|
|
1841
|
+
call(db: Database, args?: any[]): Promise<any>;
|
|
1842
|
+
/** Check if the function exists. */
|
|
1843
|
+
exists(db: Database): Promise<boolean>;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
export interface TriggerDefinition {
|
|
1847
|
+
/** Table the trigger is on. */
|
|
1848
|
+
table: string;
|
|
1849
|
+
/** BEFORE, AFTER, or INSTEAD OF. */
|
|
1850
|
+
timing: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
|
|
1851
|
+
/** INSERT, UPDATE, or DELETE. */
|
|
1852
|
+
event: 'INSERT' | 'UPDATE' | 'DELETE';
|
|
1853
|
+
/** Trigger body (SQL). */
|
|
1854
|
+
body: string;
|
|
1855
|
+
/** ROW or STATEMENT. */
|
|
1856
|
+
forEach?: 'ROW' | 'STATEMENT';
|
|
1857
|
+
/** Optional WHEN condition. */
|
|
1858
|
+
when?: string;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
export class TriggerManager {
|
|
1862
|
+
constructor(db: Database);
|
|
1863
|
+
|
|
1864
|
+
/** Define a trigger. */
|
|
1865
|
+
define(name: string, options: TriggerDefinition): TriggerManager;
|
|
1866
|
+
/** Create a trigger in the database. */
|
|
1867
|
+
create(name: string): Promise<void>;
|
|
1868
|
+
/** Create all defined triggers. */
|
|
1869
|
+
createAll(): Promise<string[]>;
|
|
1870
|
+
/** Drop a trigger. */
|
|
1871
|
+
drop(name: string, options?: { table?: string; ifExists?: boolean }): Promise<void>;
|
|
1872
|
+
/** List all defined trigger names. */
|
|
1873
|
+
list(): string[];
|
|
1874
|
+
/** Get a trigger definition by name. */
|
|
1875
|
+
get(name: string): TriggerDefinition | undefined;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// --- CLI (Phase 4) --------------------------------------------------
|
|
1879
|
+
|
|
1880
|
+
export class CLI {
|
|
1881
|
+
constructor(argv?: string[]);
|
|
1882
|
+
/** Run the CLI command. */
|
|
1883
|
+
run(): Promise<void>;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/** Create and run the CLI. */
|
|
1887
|
+
export function runCLI(argv?: string[]): Promise<void>;
|