@elysiumjs/mnemosyne 0.1.2 → 0.3.1
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/CHANGELOG.md +90 -0
- package/README.md +19 -16
- package/dist/cache.d.ts +134 -0
- package/dist/database.d.ts +127 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +2 -0
- package/dist/interfaces.d.ts +360 -0
- package/dist/keyv-redis.d.ts +88 -0
- package/dist/mnemosyne.module.d.ts +44 -0
- package/dist/model.d.ts +68 -0
- package/dist/repository.d.ts +96 -0
- package/dist/tenancy.d.ts +201 -0
- package/package.json +21 -12
- package/bunfig.toml +0 -19
- package/src/cache.ts +0 -218
- package/src/database.ts +0 -151
- package/src/index.ts +0 -47
- package/src/interfaces.ts +0 -449
- package/src/keyv-redis.ts +0 -313
- package/src/model.ts +0 -189
- package/src/repository.ts +0 -137
- package/src/tenancy.ts +0 -291
- package/tests/cache.test.ts +0 -464
- package/tests/database.test.ts +0 -249
- package/tests/model.test.ts +0 -234
- package/tests/repository.test.ts +0 -177
- package/tests/setup.ts +0 -26
- package/tsconfig.json +0 -12
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import type { TSchema } from 'elysia';
|
|
2
|
+
/**
|
|
3
|
+
* Contract for ORM drivers.
|
|
4
|
+
*
|
|
5
|
+
* A database driver is responsible for creating connections, managing
|
|
6
|
+
* transactions, and exposing the raw underlying client.
|
|
7
|
+
*
|
|
8
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
9
|
+
* @template TConnection The connection type managed by the driver.
|
|
10
|
+
* @template TConnectionConfig The configuration used to create a connection.
|
|
11
|
+
*/
|
|
12
|
+
export interface DatabaseDriver<TConnection = unknown, TConnectionConfig = unknown> {
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new database connection from the given configuration.
|
|
15
|
+
* @param config The connection configuration.
|
|
16
|
+
* @param name The name of the connection, when the driver scopes resources by connection.
|
|
17
|
+
* @returns A new database connection.
|
|
18
|
+
*/
|
|
19
|
+
createConnection(config: TConnectionConfig, name?: string): TConnection;
|
|
20
|
+
/**
|
|
21
|
+
* Executes a callback within a database transaction.
|
|
22
|
+
* @param connection The connection to use for the transaction.
|
|
23
|
+
* @param callback The function to execute within the transaction.
|
|
24
|
+
* @returns The return value of the callback.
|
|
25
|
+
*/
|
|
26
|
+
withTransaction<T>(connection: TConnection, callback: (tx: TConnection) => Promise<T>): Promise<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the raw underlying client from a connection.
|
|
29
|
+
* @param connection The connection to extract the raw client from.
|
|
30
|
+
* @returns The raw client instance.
|
|
31
|
+
*/
|
|
32
|
+
getRawClient(connection: TConnection): unknown;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalized column metadata for a table.
|
|
36
|
+
*
|
|
37
|
+
* Provides an ORM-agnostic description of a column's name, type,
|
|
38
|
+
* nullability, default status, and primary key status.
|
|
39
|
+
*
|
|
40
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
41
|
+
*/
|
|
42
|
+
export interface ColumnMetadata {
|
|
43
|
+
/**
|
|
44
|
+
* The column name.
|
|
45
|
+
*/
|
|
46
|
+
name: string;
|
|
47
|
+
/**
|
|
48
|
+
* The normalized data type of the column.
|
|
49
|
+
*/
|
|
50
|
+
dataType: 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'array' | 'bigint' | 'buffer' | 'uuid';
|
|
51
|
+
/**
|
|
52
|
+
* Whether the column allows `NULL` values.
|
|
53
|
+
*/
|
|
54
|
+
nullable: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the column has a default value.
|
|
57
|
+
*/
|
|
58
|
+
hasDefault: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the column is a primary key.
|
|
61
|
+
*/
|
|
62
|
+
isPrimaryKey: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Bridges ORM-specific table schemas to framework metadata.
|
|
66
|
+
*
|
|
67
|
+
* A model adapter translates between the ORM's internal table
|
|
68
|
+
* representation and the normalized metadata used by the framework.
|
|
69
|
+
*
|
|
70
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
71
|
+
* @template TTable The ORM-specific table type.
|
|
72
|
+
*/
|
|
73
|
+
export interface ModelAdapter<TTable = unknown> {
|
|
74
|
+
/**
|
|
75
|
+
* Returns the name of the given table.
|
|
76
|
+
* @param table The table to get the name of.
|
|
77
|
+
* @returns The table name.
|
|
78
|
+
*/
|
|
79
|
+
getTableName(table: TTable): string;
|
|
80
|
+
/**
|
|
81
|
+
* Returns the normalized column metadata for the given table.
|
|
82
|
+
* @param table The table to extract columns from.
|
|
83
|
+
* @returns An array of column metadata.
|
|
84
|
+
*/
|
|
85
|
+
getColumns(table: TTable): ColumnMetadata[];
|
|
86
|
+
/**
|
|
87
|
+
* Creates a tenant-scoped copy of the given table.
|
|
88
|
+
* @param table The original table.
|
|
89
|
+
* @param tenant The tenant identifier.
|
|
90
|
+
* @returns A new table scoped to the given tenant.
|
|
91
|
+
*/
|
|
92
|
+
createTenantTable(table: TTable, tenant: string): TTable;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Contract for tenancy strategies.
|
|
96
|
+
*
|
|
97
|
+
* A tenancy strategy determines how tables and connections are
|
|
98
|
+
* scoped to a specific tenant.
|
|
99
|
+
*
|
|
100
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
101
|
+
* @template TTable The ORM-specific table type.
|
|
102
|
+
* @template TConnection The connection type used by the strategy.
|
|
103
|
+
*/
|
|
104
|
+
export interface TenancyStrategy<TTable = unknown, TConnection = unknown> {
|
|
105
|
+
/**
|
|
106
|
+
* The tenancy mode identifier (e.g. `'schema'`, `'rls'`).
|
|
107
|
+
*/
|
|
108
|
+
readonly mode: string;
|
|
109
|
+
/**
|
|
110
|
+
* Resolves a table to its tenant-scoped equivalent.
|
|
111
|
+
* @param table The original table.
|
|
112
|
+
* @param tenant The tenant identifier.
|
|
113
|
+
* @returns The tenant-scoped table.
|
|
114
|
+
*/
|
|
115
|
+
resolveTable(table: TTable, tenant: string): TTable;
|
|
116
|
+
/**
|
|
117
|
+
* Executes a callback with tenant-level isolation applied to the connection.
|
|
118
|
+
* @param connection The database connection.
|
|
119
|
+
* @param tenant The tenant identifier.
|
|
120
|
+
* @param callback The function to execute within the isolated context.
|
|
121
|
+
* @returns The return value of the callback.
|
|
122
|
+
*/
|
|
123
|
+
withIsolation<T>(connection: TConnection, tenant: string, callback: () => Promise<T>): Promise<T>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Contract for database query cache strategies.
|
|
127
|
+
*
|
|
128
|
+
* Implementations are responsible for storing, retrieving, and
|
|
129
|
+
* invalidating cached query results.
|
|
130
|
+
*
|
|
131
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
132
|
+
*/
|
|
133
|
+
export interface DatabaseCacheStrategy {
|
|
134
|
+
/**
|
|
135
|
+
* Retrieves a cached query result by key.
|
|
136
|
+
* @param key The cache key.
|
|
137
|
+
* @returns The cached result, or `undefined` if not found.
|
|
138
|
+
*/
|
|
139
|
+
get(key: string): Promise<unknown[] | undefined>;
|
|
140
|
+
/**
|
|
141
|
+
* Stores a query result in the cache.
|
|
142
|
+
* @param key The cache key.
|
|
143
|
+
* @param response The query result to cache.
|
|
144
|
+
* @param tables The tables involved in the query.
|
|
145
|
+
* @param config Optional cache configuration.
|
|
146
|
+
*/
|
|
147
|
+
put(key: string, response: unknown[], tables: string[], config?: {
|
|
148
|
+
ttl?: number;
|
|
149
|
+
}): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Invalidates cached entries affected by a mutation.
|
|
152
|
+
* @param params The tables and/or tags affected by the mutation.
|
|
153
|
+
*/
|
|
154
|
+
onMutate(params: {
|
|
155
|
+
tables?: string[];
|
|
156
|
+
tags?: string[];
|
|
157
|
+
}): Promise<void>;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Configuration for database query caching.
|
|
161
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
162
|
+
*/
|
|
163
|
+
export interface DatabaseCacheConfig {
|
|
164
|
+
/**
|
|
165
|
+
* Whether caching is enabled.
|
|
166
|
+
*/
|
|
167
|
+
enabled?: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* The default TTL for cached data, in seconds. Drivers apply it to every cached result that
|
|
170
|
+
* does not set its own expiry.
|
|
171
|
+
* @default 60
|
|
172
|
+
*/
|
|
173
|
+
ttl?: number;
|
|
174
|
+
/**
|
|
175
|
+
* The caching strategy.
|
|
176
|
+
* - `'explicit'`: Only cache queries that explicitly opt in.
|
|
177
|
+
* - `'all'`: Cache all queries automatically.
|
|
178
|
+
*/
|
|
179
|
+
strategy?: 'explicit' | 'all';
|
|
180
|
+
/**
|
|
181
|
+
* The storage engine to use for the cache.
|
|
182
|
+
*/
|
|
183
|
+
storage?: 'redis' | 'memory';
|
|
184
|
+
/**
|
|
185
|
+
* A prefix for every cache key, before the connection name. Set it when several applications
|
|
186
|
+
* or environments share one cache store, so that connections with the same name but different
|
|
187
|
+
* databases do not share results.
|
|
188
|
+
* @default ''
|
|
189
|
+
*/
|
|
190
|
+
prefix?: string;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Database primary column type.
|
|
194
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
195
|
+
*/
|
|
196
|
+
export type IdType = number | string;
|
|
197
|
+
/**
|
|
198
|
+
* ORM-agnostic CRUD repository contract.
|
|
199
|
+
*
|
|
200
|
+
* Provides a standard interface for common database operations
|
|
201
|
+
* regardless of the underlying ORM implementation.
|
|
202
|
+
*
|
|
203
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
204
|
+
* @template TSelect The type returned by select queries.
|
|
205
|
+
* @template TInsert The type needed by insert queries.
|
|
206
|
+
* @template TUpdate The type needed by update queries.
|
|
207
|
+
* @template TId The primary key type.
|
|
208
|
+
*/
|
|
209
|
+
export interface RepositoryInterface<TSelect, TInsert, TUpdate, TId extends IdType = string> {
|
|
210
|
+
/**
|
|
211
|
+
* Retrieves all the records in the database.
|
|
212
|
+
* @returns All the records in the database.
|
|
213
|
+
*/
|
|
214
|
+
all(): Promise<TSelect[]>;
|
|
215
|
+
/**
|
|
216
|
+
* Retrieves a paginated subset of the records in the database.
|
|
217
|
+
* @param count The number of records per page.
|
|
218
|
+
* @param page The page number to retrieve.
|
|
219
|
+
* @returns A paginated response containing the records and the total count.
|
|
220
|
+
*/
|
|
221
|
+
paginate(count: number, page: number): Promise<{
|
|
222
|
+
page: number;
|
|
223
|
+
data: TSelect[];
|
|
224
|
+
total: number;
|
|
225
|
+
}>;
|
|
226
|
+
/**
|
|
227
|
+
* Retrieves a record by its id.
|
|
228
|
+
* @param id The id of the record to retrieve.
|
|
229
|
+
* @returns The record with the given id, or `null` if not found.
|
|
230
|
+
*/
|
|
231
|
+
find(id: TId): Promise<TSelect | null>;
|
|
232
|
+
/**
|
|
233
|
+
* Finds the first record with a column matching the given value.
|
|
234
|
+
* @param column The column to filter by.
|
|
235
|
+
* @param value The value the column should match.
|
|
236
|
+
* @returns The first record with a column matching the given value, or `null`.
|
|
237
|
+
*/
|
|
238
|
+
findBy<TKey extends keyof TSelect>(column: TKey, value: TSelect[TKey]): Promise<TSelect | null>;
|
|
239
|
+
/**
|
|
240
|
+
* Inserts a new record in the database.
|
|
241
|
+
* @param data The data to insert.
|
|
242
|
+
* @returns The inserted record.
|
|
243
|
+
*/
|
|
244
|
+
insert(data: TInsert): Promise<TSelect>;
|
|
245
|
+
/**
|
|
246
|
+
* Updates a record in the database.
|
|
247
|
+
* @param id The id of the record to update.
|
|
248
|
+
* @param data The data to update.
|
|
249
|
+
* @returns The updated record, or `null` if no record has that id.
|
|
250
|
+
*/
|
|
251
|
+
update(id: TId, data: TUpdate): Promise<TSelect | null>;
|
|
252
|
+
/**
|
|
253
|
+
* Updates all the records in the database.
|
|
254
|
+
* @param data The data to update.
|
|
255
|
+
* @returns The updated records.
|
|
256
|
+
*/
|
|
257
|
+
updateAll(data: TUpdate): Promise<TSelect[]>;
|
|
258
|
+
/**
|
|
259
|
+
* Deletes a record from the database.
|
|
260
|
+
* @param id The ID of the record to delete.
|
|
261
|
+
* @returns The deleted record, or `null` if no record has that id.
|
|
262
|
+
*/
|
|
263
|
+
delete(id: TId): Promise<TSelect | null>;
|
|
264
|
+
/**
|
|
265
|
+
* Deletes all the records from the database.
|
|
266
|
+
* @returns All the deleted records.
|
|
267
|
+
*/
|
|
268
|
+
deleteAll(): Promise<TSelect[]>;
|
|
269
|
+
/**
|
|
270
|
+
* Checks if a record exists in the database.
|
|
271
|
+
* @param id The ID of the record to check.
|
|
272
|
+
* @returns `true` if the record exists, `false` otherwise.
|
|
273
|
+
*/
|
|
274
|
+
exists(id: TId): Promise<boolean>;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Shape of a model class.
|
|
278
|
+
*
|
|
279
|
+
* Describes the static interface that a model class must expose,
|
|
280
|
+
* including inferred types, table metadata, and validation schemas.
|
|
281
|
+
*
|
|
282
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
283
|
+
* @template TSelect The type returned by select queries.
|
|
284
|
+
* @template TInsert The type needed by insert queries.
|
|
285
|
+
* @template TUpdate The type needed by update queries.
|
|
286
|
+
* @template TTable The ORM-specific table type.
|
|
287
|
+
*/
|
|
288
|
+
export type ModelClass<TSelect = unknown, TInsert = unknown, TUpdate = unknown, TTable = unknown> = {
|
|
289
|
+
/**
|
|
290
|
+
* The data type returned by select queries.
|
|
291
|
+
*/
|
|
292
|
+
readonly $inferSelect: TSelect;
|
|
293
|
+
/**
|
|
294
|
+
* The data type needed by insert queries.
|
|
295
|
+
*/
|
|
296
|
+
readonly $inferInsert: TInsert;
|
|
297
|
+
/**
|
|
298
|
+
* The data type needed by update queries.
|
|
299
|
+
*/
|
|
300
|
+
readonly $inferUpdate: TUpdate;
|
|
301
|
+
/**
|
|
302
|
+
* The ORM-specific table schema wrapped by this model.
|
|
303
|
+
*/
|
|
304
|
+
readonly table: TTable;
|
|
305
|
+
/**
|
|
306
|
+
* The name of the table wrapped by the model.
|
|
307
|
+
*/
|
|
308
|
+
readonly tableName: string;
|
|
309
|
+
/**
|
|
310
|
+
* The table columns configuration.
|
|
311
|
+
*/
|
|
312
|
+
readonly columns: Record<string, unknown>;
|
|
313
|
+
/**
|
|
314
|
+
* The validation schema for creating records.
|
|
315
|
+
*/
|
|
316
|
+
readonly insertSchema: TSchema;
|
|
317
|
+
/**
|
|
318
|
+
* The validation schema for updating records.
|
|
319
|
+
*/
|
|
320
|
+
readonly updateSchema: TSchema;
|
|
321
|
+
/**
|
|
322
|
+
* The validation schema for selecting records.
|
|
323
|
+
*/
|
|
324
|
+
readonly selectSchema: TSchema;
|
|
325
|
+
/**
|
|
326
|
+
* Whether the model supports tenancy.
|
|
327
|
+
*/
|
|
328
|
+
readonly supportTenancy: boolean;
|
|
329
|
+
/**
|
|
330
|
+
* Creates a new instance of the model.
|
|
331
|
+
* @param args The arguments to pass to the constructor.
|
|
332
|
+
* @returns A new instance of the model.
|
|
333
|
+
*/
|
|
334
|
+
new (...args: unknown[]): unknown;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Shape of a repository class.
|
|
338
|
+
*
|
|
339
|
+
* Describes the static interface that a repository class must expose,
|
|
340
|
+
* including the associated model and connection name.
|
|
341
|
+
*
|
|
342
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
343
|
+
* @template TModel The model class type wrapped by the repository.
|
|
344
|
+
*/
|
|
345
|
+
export type RepositoryClass<TModel extends ModelClass = ModelClass> = {
|
|
346
|
+
/**
|
|
347
|
+
* The model class wrapped by this repository.
|
|
348
|
+
*/
|
|
349
|
+
readonly Model: TModel;
|
|
350
|
+
/**
|
|
351
|
+
* The database connection name to use in the repository.
|
|
352
|
+
*/
|
|
353
|
+
readonly connection: string;
|
|
354
|
+
/**
|
|
355
|
+
* Creates a new instance of the repository.
|
|
356
|
+
* @param args The arguments to pass to the constructor.
|
|
357
|
+
* @returns A new repository instance.
|
|
358
|
+
*/
|
|
359
|
+
new (...args: unknown[]): RepositoryInterface<TModel['$inferSelect'], TModel['$inferInsert'], TModel['$inferUpdate']>;
|
|
360
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { KeyvStoreAdapter } from 'cacheable';
|
|
2
|
+
import type { KeyvEntry, StoredData } from 'keyv';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
/**
|
|
5
|
+
* Keyv adapter for redis. This adapter is based on the Bun's Redis client.
|
|
6
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
7
|
+
*/
|
|
8
|
+
export declare class KeyvRedis extends EventEmitter implements KeyvStoreAdapter {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(props: {
|
|
11
|
+
/**
|
|
12
|
+
* The Redis connection to use.
|
|
13
|
+
*/
|
|
14
|
+
connection: string;
|
|
15
|
+
/**
|
|
16
|
+
* Namespace for the connection.
|
|
17
|
+
*/
|
|
18
|
+
namespace?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Separator to use between namespace and key.
|
|
21
|
+
*/
|
|
22
|
+
keyPrefixSeparator?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Number of keys to delete in a single batch.
|
|
25
|
+
*/
|
|
26
|
+
clearBatchSize?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Use UNLINK instead of DEL to delete keys. UNLINK frees memory in the background (Redis 4+).
|
|
29
|
+
*/
|
|
30
|
+
useUnlink?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to allow clearing all keys when no namespace is set.
|
|
33
|
+
* If set to true and no namespace is set, iterate() will return all keys.
|
|
34
|
+
* Defaults to `false`.
|
|
35
|
+
*/
|
|
36
|
+
noNamespaceAffectsAll?: boolean;
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Resolves the Redis client on first use. The async path is always safe, even when the
|
|
40
|
+
* Redis module has not been initialised yet (lazy boot).
|
|
41
|
+
*/
|
|
42
|
+
protected getClient(): Promise<Bun.RedisClient>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the options for the adapter.
|
|
45
|
+
*/
|
|
46
|
+
get opts(): {
|
|
47
|
+
namespace: string | undefined;
|
|
48
|
+
keyPrefixSeparator: string;
|
|
49
|
+
clearBatchSize: number;
|
|
50
|
+
noNamespaceAffectsAll: boolean;
|
|
51
|
+
dialect: string;
|
|
52
|
+
url: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Gets the namespace for the adapter. If undefined, it will not use a namespace including keyPrefixing.
|
|
56
|
+
* @default undefined
|
|
57
|
+
*/
|
|
58
|
+
get namespace(): string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Sets the namespace for the adapter. If undefined, it will not use a namespace including keyPrefixing.
|
|
61
|
+
*/
|
|
62
|
+
set namespace(value: string | undefined);
|
|
63
|
+
get<Value>(key: string): Promise<StoredData<Value> | undefined>;
|
|
64
|
+
set(key: string, value: any, ttl?: number): Promise<void>;
|
|
65
|
+
setMany(values: Array<KeyvEntry>): Promise<void>;
|
|
66
|
+
delete(key: string): Promise<boolean>;
|
|
67
|
+
clear(): Promise<void>;
|
|
68
|
+
has(key: string): Promise<boolean>;
|
|
69
|
+
hasMany(keys: string[]): Promise<boolean[]>;
|
|
70
|
+
getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
|
|
71
|
+
disconnect(): Promise<void>;
|
|
72
|
+
deleteMany(keys: string[]): Promise<boolean>;
|
|
73
|
+
iterator<Value>(namespace?: string): AsyncGenerator<Array<string | Awaited<Value> | undefined>, void>;
|
|
74
|
+
/**
|
|
75
|
+
* Helper function to create a key with a namespace.
|
|
76
|
+
* @param key The key to prefix.
|
|
77
|
+
* @param namespace The namespace to prefix the key with.
|
|
78
|
+
* @returns The key with the namespace such as 'namespace::key'.
|
|
79
|
+
*/
|
|
80
|
+
createKeyPrefix(key: string, namespace?: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* Helper function to remove the namespace from a key.
|
|
83
|
+
* @param key The key to remove the namespace from.
|
|
84
|
+
* @param namespace The namespace to remove from the key.
|
|
85
|
+
* @returns The key without the namespace.
|
|
86
|
+
*/
|
|
87
|
+
getKeyWithoutPrefix(key: string, namespace?: string): string;
|
|
88
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration of the {@link MnemosyneModule}.
|
|
3
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
4
|
+
*/
|
|
5
|
+
export type MnemosyneModuleConfig = {
|
|
6
|
+
cache: {
|
|
7
|
+
/** The Redis-backed cache. Set to `false` to not provide `Cache.redis`. */
|
|
8
|
+
redis?: {
|
|
9
|
+
/** The name of the Redis connection to use. */
|
|
10
|
+
connection: string;
|
|
11
|
+
} | false;
|
|
12
|
+
/** The memory cache. Set to `false` to not provide `Cache.memory`. */
|
|
13
|
+
memory?: {
|
|
14
|
+
/** The default TTL of entries, in milliseconds. */
|
|
15
|
+
ttl: number;
|
|
16
|
+
/** The maximum number of entries kept. */
|
|
17
|
+
lruSize: number;
|
|
18
|
+
} | false;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
declare const MnemosyneModule_base: (abstract new () => {
|
|
22
|
+
readonly config: MnemosyneModuleConfig;
|
|
23
|
+
onInit(): Promise<void> | void;
|
|
24
|
+
onDestroy(): Promise<void> | void;
|
|
25
|
+
onServe(_app: import("elysia").default<any>): Promise<void> | void;
|
|
26
|
+
}) & {
|
|
27
|
+
readonly config: string;
|
|
28
|
+
with(config: Partial<MnemosyneModuleConfig> | (() => Partial<MnemosyneModuleConfig> | Promise<Partial<MnemosyneModuleConfig>>)): import("@elysiumjs/core").ConfiguredModule<MnemosyneModuleConfig>;
|
|
29
|
+
resolveConfig(input?: Partial<MnemosyneModuleConfig> | undefined): MnemosyneModuleConfig;
|
|
30
|
+
register<TConfig_1 = any>(props?: import("@elysiumjs/core").ModuleProps<TConfig_1>): (target: import("@elysiumjs/core").ModuleClass) => void;
|
|
31
|
+
configurable<TConfig extends object>(options?: {
|
|
32
|
+
defaults?: Partial<TConfig>;
|
|
33
|
+
required?: (keyof TConfig)[];
|
|
34
|
+
}): any;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Provides the caches as the global services `mnemosyne.cache.redis` and
|
|
38
|
+
* `mnemosyne.cache.memory`. Tenancy config and strategy, and database connections, are
|
|
39
|
+
* registered by the database driver modules.
|
|
40
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
41
|
+
*/
|
|
42
|
+
export declare class MnemosyneModule extends MnemosyneModule_base {
|
|
43
|
+
}
|
|
44
|
+
export {};
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ColumnMetadata, ModelAdapter } from './interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a validation schema from normalized column metadata.
|
|
4
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
5
|
+
* @param columns The column metadata to create the schema from.
|
|
6
|
+
* @param options The schema generation options.
|
|
7
|
+
* @returns A validation schema based on the given columns.
|
|
8
|
+
*/
|
|
9
|
+
export declare const createSchemaFromModel: (columns: ColumnMetadata[], { mode }?: {
|
|
10
|
+
mode?: 'create' | 'update' | 'select';
|
|
11
|
+
}) => import("@sinclair/typebox").TObject<{}>;
|
|
12
|
+
/**
|
|
13
|
+
* Mixin used to create a new abstract model class.
|
|
14
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
15
|
+
* @template TTable The ORM-specific table type.
|
|
16
|
+
* @template TColumns The table columns configuration type.
|
|
17
|
+
* @param tableName The name of the table.
|
|
18
|
+
* @param columns The table columns configuration.
|
|
19
|
+
* @param adapter The model adapter for the ORM.
|
|
20
|
+
* @param baseTable The base ORM table instance.
|
|
21
|
+
*/
|
|
22
|
+
export declare const AbstractModel: <TTable, TColumns extends Record<string, unknown>>(tableName: string, columns: TColumns, adapter: ModelAdapter<TTable>, baseTable: TTable) => {
|
|
23
|
+
new (): {};
|
|
24
|
+
/**
|
|
25
|
+
* The data type returned by the select queries.
|
|
26
|
+
*/
|
|
27
|
+
readonly $inferSelect: unknown;
|
|
28
|
+
/**
|
|
29
|
+
* The data type needed by the insert queries.
|
|
30
|
+
*/
|
|
31
|
+
readonly $inferInsert: unknown;
|
|
32
|
+
/**
|
|
33
|
+
* The data type needed by the update queries.
|
|
34
|
+
*/
|
|
35
|
+
readonly $inferUpdate: unknown;
|
|
36
|
+
/**
|
|
37
|
+
* The ORM-specific table schema wrapped by this model.
|
|
38
|
+
* Will be automatically resolved to a tenant-scoped table when tenancy is active.
|
|
39
|
+
*/
|
|
40
|
+
get table(): TTable;
|
|
41
|
+
/**
|
|
42
|
+
* The name of the table wrapped by the model.
|
|
43
|
+
*/
|
|
44
|
+
readonly tableName: string;
|
|
45
|
+
/**
|
|
46
|
+
* The table columns configuration.
|
|
47
|
+
*/
|
|
48
|
+
readonly columns: TColumns;
|
|
49
|
+
/**
|
|
50
|
+
* The validation schema for creating records.
|
|
51
|
+
*/
|
|
52
|
+
readonly insertSchema: import("@sinclair/typebox").TObject<{}>;
|
|
53
|
+
/**
|
|
54
|
+
* The validation schema for updating records.
|
|
55
|
+
*/
|
|
56
|
+
readonly updateSchema: import("@sinclair/typebox").TObject<{}>;
|
|
57
|
+
/**
|
|
58
|
+
* The validation schema for selecting records.
|
|
59
|
+
*/
|
|
60
|
+
readonly selectSchema: import("@sinclair/typebox").TObject<{}>;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the model supports tenancy.
|
|
63
|
+
*
|
|
64
|
+
* Set it to `true` if the model supports tenancy. This means that the model will be
|
|
65
|
+
* wrapped by the tenancy strategy when the current tenant is not `public`.
|
|
66
|
+
*/
|
|
67
|
+
readonly supportTenancy: boolean;
|
|
68
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { AbstractDatabase } from './database';
|
|
2
|
+
import type { IdType, ModelClass } from './interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Mixin used to create a new abstract repository class over a model.
|
|
5
|
+
*
|
|
6
|
+
* All CRUD methods are abstract and must be implemented by driver-specific
|
|
7
|
+
* subclasses.
|
|
8
|
+
*
|
|
9
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
10
|
+
* @template TModel The type of the model class wrapped by the repository.
|
|
11
|
+
* @template TConnection The database connection type.
|
|
12
|
+
* @param model The model class wrapped by the repository.
|
|
13
|
+
* @param database The database manager instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare const AbstractRepository: <TModel extends ModelClass, TConnection>(model: TModel, database: AbstractDatabase<TConnection, unknown>) => (abstract new () => {
|
|
16
|
+
/**
|
|
17
|
+
* The database connection used by this repository.
|
|
18
|
+
*/
|
|
19
|
+
get db(): TConnection;
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves all the records in the database.
|
|
22
|
+
* @returns All the records in the database.
|
|
23
|
+
*/
|
|
24
|
+
all(): Promise<TModel["$inferSelect"][]>;
|
|
25
|
+
/**
|
|
26
|
+
* Get a paginated subset of the records in the database.
|
|
27
|
+
* @param count The number of records to retrieve per page.
|
|
28
|
+
* @param page The page number to retrieve.
|
|
29
|
+
* @returns A paginated response containing the records and the total number of records.
|
|
30
|
+
*/
|
|
31
|
+
paginate(count: number, page?: number): Promise<{
|
|
32
|
+
page: number;
|
|
33
|
+
data: TModel["$inferSelect"][];
|
|
34
|
+
total: number;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves a record by its id.
|
|
38
|
+
* @param id The id of the record to retrieve.
|
|
39
|
+
* @returns The record with the given id, or `null` if not found.
|
|
40
|
+
*/
|
|
41
|
+
find(id: IdType): Promise<TModel["$inferSelect"] | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Finds the first record with a column matching the given value.
|
|
44
|
+
* @param column The column to filter by.
|
|
45
|
+
* @param value The value the column should match.
|
|
46
|
+
* @returns The first record with a column matching the given value.
|
|
47
|
+
*/
|
|
48
|
+
findBy<TKey extends keyof TModel["$inferSelect"]>(column: TKey, value: TModel["$inferSelect"][TKey]): Promise<TModel["$inferSelect"] | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Inserts a new record in the database.
|
|
51
|
+
* @param data The data to insert.
|
|
52
|
+
* @returns The inserted record.
|
|
53
|
+
*/
|
|
54
|
+
insert(data: TModel["$inferInsert"]): Promise<TModel["$inferSelect"]>;
|
|
55
|
+
/**
|
|
56
|
+
* Updates a record in the database.
|
|
57
|
+
* @param id The id of the record to update.
|
|
58
|
+
* @param data The data to update.
|
|
59
|
+
* @returns The updated record, or `null` if no record has that id.
|
|
60
|
+
*/
|
|
61
|
+
update(id: IdType, data: TModel["$inferUpdate"]): Promise<TModel["$inferSelect"] | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Updates all the records in the database.
|
|
64
|
+
* @param data The data to update.
|
|
65
|
+
* @returns The updated records.
|
|
66
|
+
*/
|
|
67
|
+
updateAll(data: TModel["$inferUpdate"]): Promise<TModel["$inferSelect"][]>;
|
|
68
|
+
/**
|
|
69
|
+
* Deletes a record from the database.
|
|
70
|
+
* @param id The ID of the record to delete.
|
|
71
|
+
* @returns The deleted record, or `null` if no record has that id.
|
|
72
|
+
*/
|
|
73
|
+
delete(id: IdType): Promise<TModel["$inferSelect"] | null>;
|
|
74
|
+
/**
|
|
75
|
+
* Deletes all the records from the database.
|
|
76
|
+
* @returns All the deleted records.
|
|
77
|
+
*/
|
|
78
|
+
deleteAll(): Promise<TModel["$inferSelect"][]>;
|
|
79
|
+
/**
|
|
80
|
+
* Checks if a record exists in the database.
|
|
81
|
+
* @param id The ID of the record to check.
|
|
82
|
+
* @returns True if the record exists, false otherwise.
|
|
83
|
+
*/
|
|
84
|
+
exists(id: IdType): Promise<boolean>;
|
|
85
|
+
}) & {
|
|
86
|
+
/**
|
|
87
|
+
* The model class wrapped by this repository.
|
|
88
|
+
*/
|
|
89
|
+
readonly Model: TModel;
|
|
90
|
+
/**
|
|
91
|
+
* The database connection name to use.
|
|
92
|
+
*
|
|
93
|
+
* Update this value to change the database connection used by this repository.
|
|
94
|
+
*/
|
|
95
|
+
readonly connection: string;
|
|
96
|
+
};
|