@elysiumjs/mnemosyne-drizzle 0.2.0 → 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.
Files changed (46) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/cache.d.ts +60 -0
  3. package/dist/cache.js +1 -0
  4. package/dist/commands/migration_generate.command.js +1 -1
  5. package/dist/commands/migration_run.command.d.ts +13 -0
  6. package/dist/commands/migration_run.command.js +6 -11
  7. package/dist/commands/project.d.ts +11 -0
  8. package/dist/commands/project.js +1 -0
  9. package/dist/database.d.ts +55 -23
  10. package/dist/database.js +1 -1
  11. package/dist/drizzle.module.d.ts +2 -2
  12. package/dist/drizzle.module.js +1 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +1 -1
  15. package/dist/migrations.d.ts +12 -0
  16. package/dist/migrations.js +2 -1
  17. package/dist/model.d.ts +5 -5
  18. package/dist/repository.d.ts +20 -5
  19. package/dist/repository.js +1 -1
  20. package/dist/tenancy.d.ts +25 -8
  21. package/dist/tenancy.js +1 -1
  22. package/package.json +19 -9
  23. package/.omc/state/agent-replay-3920b579-6d8e-4f5f-924a-319202936685.jsonl +0 -7
  24. package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/last-tool-error-state.json +0 -7
  25. package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/mission-state.json +0 -93
  26. package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/pre-tool-advisory-throttle.json +0 -18
  27. package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/subagent-tracking-state.json +0 -61
  28. package/.turbo/turbo-build.log +0 -3
  29. package/scripts/minify.ts +0 -10
  30. package/src/commands/migration_generate.command.ts +0 -101
  31. package/src/commands/migration_run.command.ts +0 -160
  32. package/src/database.ts +0 -205
  33. package/src/drizzle.module.ts +0 -97
  34. package/src/index.ts +0 -47
  35. package/src/migrations.ts +0 -100
  36. package/src/model.ts +0 -178
  37. package/src/repository.ts +0 -113
  38. package/src/table.ts +0 -70
  39. package/src/tenancy.ts +0 -249
  40. package/tests/drizzle.module.test.ts +0 -201
  41. package/tests/migrations.test.ts +0 -123
  42. package/tests/model.test.ts +0 -362
  43. package/tests/setup.ts +0 -0
  44. package/tests/table.test.ts +0 -72
  45. package/tests/tenancy.test.ts +0 -212
  46. package/tsconfig.json +0 -12
package/src/database.ts DELETED
@@ -1,205 +0,0 @@
1
- // Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type { CacheInterface, DatabaseCacheConfig, DatabaseDriver } from '@elysiumjs/mnemosyne';
16
- import type { DrizzleConfig } from 'drizzle-orm';
17
- import type { BunSQLDatabase } from 'drizzle-orm/bun-sql/postgres';
18
- import type { CacheConfig } from 'drizzle-orm/cache/core/types';
19
-
20
- import { Service, ServiceNotReadyError } from '@elysiumjs/core';
21
- import { AbstractDatabase, createDatabaseCacheStorage } from '@elysiumjs/mnemosyne';
22
- import { getTableName, is, Table } from 'drizzle-orm';
23
- import { drizzle } from 'drizzle-orm/bun-sql';
24
- import { Cache as DrizzleCache } from 'drizzle-orm/cache/core';
25
-
26
- // ============================================================================
27
- // Types
28
- // ============================================================================
29
-
30
- /**
31
- * Connection configuration for Drizzle ORM.
32
- *
33
- * Extends `DrizzleConfig` with connection details and optional caching.
34
- *
35
- * @author Axel Nana <axel.nana@workbud.com>
36
- * @template TSchema The Drizzle schema type.
37
- */
38
- export type DrizzleConnectionProps<
39
- TSchema extends Record<string, unknown> = Record<string, never>
40
- > = Omit<DrizzleConfig<TSchema>, 'cache'> &
41
- ({ connection: string | ({ url?: string } & Bun.SQLOptions) } | { client: Bun.SQL }) & {
42
- cache?: true | DatabaseCacheConfig;
43
- };
44
-
45
- /**
46
- * A Drizzle database connection backed by Bun's SQL driver.
47
- * @author Axel Nana <axel.nana@workbud.com>
48
- */
49
- export type DrizzleConnection = BunSQLDatabase<Record<string, never>> & { $client: Bun.SQL };
50
-
51
- // ============================================================================
52
- // DrizzleDatabaseCache
53
- // ============================================================================
54
-
55
- /**
56
- * Drizzle query cache backed by the framework's cache storage.
57
- *
58
- * Extends the Drizzle `Cache` base class and implements the
59
- * framework's `DatabaseCacheStrategy` interface so that query
60
- * results can be transparently cached and invalidated.
61
- *
62
- * @author Axel Nana <axel.nana@workbud.com>
63
- */
64
- export class DrizzleDatabaseCache extends DrizzleCache {
65
- #storageEngine: 'redis' | 'memory';
66
- #cache?: Omit<CacheInterface, 'tags'>;
67
- private usedTablesPerKey: Record<string, string[]> = {};
68
-
69
- constructor(
70
- private readonly cacheStrategy: 'explicit' | 'all' = 'explicit',
71
- private readonly globalTtl: number = 1,
72
- storageEngine: 'redis' | 'memory' = 'redis'
73
- ) {
74
- super();
75
- this.#storageEngine = storageEngine;
76
- }
77
-
78
- /**
79
- * Resolves the cache storage on first use rather than at construction, so that a connection
80
- * can be built before the module providing the cache has finished initialising.
81
- */
82
- async #storage(): Promise<Omit<CacheInterface, 'tags'>> {
83
- if (this.#cache) return this.#cache;
84
- try {
85
- this.#cache = createDatabaseCacheStorage({ storage: this.#storageEngine });
86
- } catch (error) {
87
- if (!(error instanceof ServiceNotReadyError)) throw error;
88
- await Service.resolve(error.service);
89
- this.#cache = createDatabaseCacheStorage({ storage: this.#storageEngine });
90
- }
91
- return this.#cache;
92
- }
93
-
94
- public override strategy(): 'explicit' | 'all' {
95
- return this.cacheStrategy;
96
- }
97
-
98
- public override async get(key: string): Promise<any[] | undefined> {
99
- return (await (await this.#storage()).get<any[]>(key)) ?? undefined;
100
- }
101
-
102
- public override async put(
103
- key: string,
104
- response: any,
105
- tables: string[],
106
- _isTag: boolean,
107
- config?: CacheConfig
108
- ): Promise<void> {
109
- await (await this.#storage()).set(key, response, (config?.ex ?? this.globalTtl) * 1000);
110
- for (const table of tables) {
111
- const keys = this.usedTablesPerKey[table];
112
- if (keys === undefined) {
113
- this.usedTablesPerKey[table] = [key];
114
- } else {
115
- keys.push(key);
116
- }
117
- }
118
- }
119
-
120
- public override async onMutate(params: {
121
- tags?: string | string[];
122
- tables?: string | string[] | Table<any> | Table<any>[];
123
- }): Promise<void> {
124
- const tagsArray = params.tags ? (Array.isArray(params.tags) ? params.tags : [params.tags]) : [];
125
- const tablesArray = params.tables
126
- ? Array.isArray(params.tables)
127
- ? params.tables
128
- : [params.tables]
129
- : [];
130
- const keysToDelete = new Set<string>();
131
-
132
- for (const table of tablesArray) {
133
- const tableName = is(table, Table) ? getTableName(table as Table) : (table as string);
134
- const keys = this.usedTablesPerKey[tableName] ?? [];
135
- for (const key of keys) keysToDelete.add(key);
136
- }
137
-
138
- if (keysToDelete.size > 0 || tagsArray.length > 0) {
139
- const cache = await this.#storage();
140
- await cache.mdel(tagsArray);
141
- await cache.mdel([...keysToDelete]);
142
- for (const table of tablesArray) {
143
- const tableName = is(table, Table) ? getTableName(table as Table) : (table as string);
144
- this.usedTablesPerKey[tableName] = [];
145
- }
146
- }
147
- }
148
- }
149
-
150
- // ============================================================================
151
- // DrizzleDatabase
152
- // ============================================================================
153
-
154
- /**
155
- * Drizzle-specific database manager.
156
- *
157
- * Implements the abstract database class with a driver that creates
158
- * Drizzle connections using Bun's built-in SQL client.
159
- *
160
- * @author Axel Nana <axel.nana@workbud.com>
161
- */
162
- class DrizzleDatabase extends AbstractDatabase<DrizzleConnection, DrizzleConnectionProps> {
163
- /**
164
- * Creates a Drizzle connection from its properties. Used by the `DrizzleModule` to build
165
- * the `db.connection.{name}` services.
166
- * @author Axel Nana <axel.nana@workbud.com>
167
- * @param props The connection properties.
168
- * @returns A new connection.
169
- */
170
- public createConnection(props: DrizzleConnectionProps): DrizzleConnection {
171
- return this.driver.createConnection(props);
172
- }
173
-
174
- protected driver: DatabaseDriver<DrizzleConnection, DrizzleConnectionProps> = {
175
- createConnection(config) {
176
- const cache =
177
- config.cache === undefined
178
- ? undefined
179
- : config.cache === true
180
- ? new DrizzleDatabaseCache()
181
- : config.cache.enabled !== false
182
- ? new DrizzleDatabaseCache(
183
- config.cache.strategy,
184
- config.cache.ttl,
185
- config.cache.storage
186
- )
187
- : undefined;
188
- return drizzle({ ...config, cache }) as DrizzleConnection;
189
- },
190
-
191
- withTransaction(connection, callback) {
192
- return (connection as any).transaction((tx: any) => callback(tx));
193
- },
194
-
195
- getRawClient(connection) {
196
- return connection.$client;
197
- }
198
- };
199
- }
200
-
201
- /**
202
- * Singleton Drizzle database manager.
203
- * @author Axel Nana <axel.nana@workbud.com>
204
- */
205
- export const Database = new DrizzleDatabase();
@@ -1,97 +0,0 @@
1
- // Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type { TenancyConfig } from '@elysiumjs/mnemosyne';
16
- import type { DrizzleConnection, DrizzleConnectionProps } from './database';
17
-
18
- import { Module, needsDefaultAlias, runAll, Service } from '@elysiumjs/core';
19
- import {
20
- AbstractDatabase,
21
- MnemosyneModule,
22
- TENANCY_CONFIG_SERVICE,
23
- TENANCY_STRATEGY_SERVICE
24
- } from '@elysiumjs/mnemosyne';
25
-
26
- import { MigrationGenerateCommand } from './commands/migration_generate.command';
27
- import { MigrationRunCommand } from './commands/migration_run.command';
28
- import { Database } from './database';
29
- import { DrizzleRLSTenancy, DrizzleSchemaTenancy } from './tenancy';
30
-
31
- /**
32
- * Configuration of the {@link DrizzleModule}.
33
- * @author Axel Nana <axel.nana@workbud.com>
34
- */
35
- export type DrizzleModuleConfig = {
36
- /** The name of the connection that `default` resolves to. */
37
- default: string;
38
-
39
- /** The connections to declare, by name. */
40
- connections: Record<string, DrizzleConnectionProps>;
41
-
42
- /** The tenancy configuration. Defaults to schema-based tenancy. */
43
- tenancy?: TenancyConfig;
44
- };
45
-
46
- /**
47
- * Declares Drizzle connections as global services named `db.connection.{name}`, plus
48
- * `db.connection.default` aliasing the configured default, and registers the tenancy config
49
- * and strategy. Connections are created on first use. Also provides the migration commands.
50
- * @author Axel Nana <axel.nana@workbud.com>
51
- */
52
- @Module.register<DrizzleModuleConfig>({
53
- imports: [MnemosyneModule],
54
- commands: [MigrationGenerateCommand, MigrationRunCommand],
55
- services: (config) => {
56
- const alias = needsDefaultAlias('DrizzleModule', config);
57
- const tenancy = config.tenancy ?? { mode: 'schema' };
58
- return [
59
- ...Object.entries(config.connections).map(([name, props]) => ({
60
- name: AbstractDatabase.getConnectionName(name),
61
- global: true,
62
- factory: () => Database.createConnection(props)
63
- })),
64
- ...(alias
65
- ? [
66
- {
67
- name: AbstractDatabase.getConnectionName('default'),
68
- global: true,
69
- factory: (_config: unknown, get: <T>(name: string) => T) =>
70
- get<DrizzleConnection>(AbstractDatabase.getConnectionName(config.default))
71
- }
72
- ]
73
- : []),
74
- { name: TENANCY_CONFIG_SERVICE, global: true, factory: () => tenancy },
75
- {
76
- name: TENANCY_STRATEGY_SERVICE,
77
- global: true,
78
- factory: () =>
79
- tenancy.mode === 'rls' ? new DrizzleRLSTenancy() : new DrizzleSchemaTenancy()
80
- }
81
- ];
82
- }
83
- })
84
- export class DrizzleModule extends Module.configurable<DrizzleModuleConfig>({
85
- defaults: { default: 'default' },
86
- required: ['connections']
87
- }) {
88
- /** Closes the clients of the connections that were created. */
89
- public override async onDestroy(): Promise<void> {
90
- const names = Object.keys(this.config.connections).map(AbstractDatabase.getConnectionName);
91
- await runAll(
92
- names
93
- .filter((name) => Service.isConstructed(name))
94
- .map((name) => () => Service.get<DrizzleConnection>(name)?.$client.close())
95
- );
96
- }
97
- }
package/src/index.ts DELETED
@@ -1,47 +0,0 @@
1
- // Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- // Re-export drizzle-orm/pg-core with enhanced pgTable
16
- export * from 'drizzle-orm';
17
- export * from 'drizzle-orm/pg-core';
18
- export type {
19
- SelectedFields,
20
- SelectedFieldsFlat,
21
- SelectedFieldsOrdered,
22
- TableConfig
23
- } from 'drizzle-orm/pg-core';
24
- export { pgTable, getTableBuilders } from './table';
25
-
26
- // Re-exports
27
- export { Database, DrizzleDatabaseCache } from './database';
28
- export type { DrizzleConnectionProps, DrizzleConnection } from './database';
29
- export {
30
- DrizzleModel,
31
- createSchemaFromDrizzle,
32
- drizzleAdapter,
33
- type DrizzleModelOptions
34
- } from './model';
35
- export { DrizzleModule, type DrizzleModuleConfig } from './drizzle.module';
36
- export { DrizzleRepository } from './repository';
37
- export {
38
- DrizzleSchemaTenancy,
39
- DrizzleRLSTenancy,
40
- wrapTenantSchema,
41
- registerTenantSchema,
42
- getTenantSchema,
43
- withRLS,
44
- setConnectionTenant,
45
- getSessionTenant,
46
- createRLSPolicy
47
- } from './tenancy';
package/src/migrations.ts DELETED
@@ -1,100 +0,0 @@
1
- // Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { basename, dirname } from 'node:path';
16
-
17
- /** A migration recorded as applied in `elysium_migrations`. */
18
- export type AppliedMigration = {
19
- name: string;
20
- appliedAt: Date;
21
- };
22
-
23
- /** The migrations to run, and the applied records to rename to their new names. */
24
- export type MigrationPlan = {
25
- pending: string[];
26
- renames: { from: string; to: string }[];
27
- };
28
-
29
- const FOLDER_MIGRATION_FILE = 'migration.sql';
30
-
31
- /**
32
- * Gets the name under which a migration file is tracked. Drizzle Kit 1.0 stores each migration
33
- * as `<folder>/migration.sql`, in which case the folder name identifies the migration.
34
- * @author Axel Nana <axel.nana@workbud.com>
35
- * @param path The path of the migration file.
36
- */
37
- export const getMigrationName = (path: string): string =>
38
- basename(path) === FOLDER_MIGRATION_FILE ? basename(dirname(path)) : basename(path);
39
-
40
- /** Drops the `.sql` extension and the leading index or timestamp: `0001_users.sql` → `users`. */
41
- const descriptiveName = (name: string): string =>
42
- name
43
- .replace(/\.sql$/, '')
44
- .split('_')
45
- .slice(1)
46
- .join('_');
47
-
48
- /** Reads the UTC `YYYYMMDDHHmmss` timestamp that starts a Drizzle Kit 1.0 folder name. */
49
- const folderTime = (name: string): number | undefined => {
50
- const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(name);
51
- return match
52
- ? Date.UTC(+match[1]!, +match[2]! - 1, +match[3]!, +match[4]!, +match[5]!, +match[6]!)
53
- : undefined;
54
- };
55
-
56
- /**
57
- * Decides which migration files to run.
58
- *
59
- * `drizzle-kit up` moves each flat `<prefix>_<name>.sql` file into a
60
- * `<timestamp>_<name>/migration.sql` folder, where the timestamp is when the migration was
61
- * generated. An applied record whose flat file is gone is matched, in order, to a folder with
62
- * the same `<name>` generated before the record was applied; that folder is not run again and
63
- * the record is renamed. Folders generated later are new migrations and run.
64
- * @author Axel Nana <axel.nana@workbud.com>
65
- * @param files The paths of the migration files.
66
- * @param applied The applied migrations.
67
- */
68
- export const planMigrations = (files: string[], applied: AppliedMigration[]): MigrationPlan => {
69
- const appliedNames = new Set(applied.map(({ name }) => name));
70
- const fileNames = new Set(files.map(getMigrationName));
71
-
72
- const moved = new Map<string, AppliedMigration[]>();
73
- for (const record of [...applied].sort((a, b) => a.name.localeCompare(b.name))) {
74
- if (!record.name.endsWith('.sql') || fileNames.has(record.name)) continue;
75
- const key = descriptiveName(record.name);
76
- moved.set(key, [...(moved.get(key) ?? []), record]);
77
- }
78
-
79
- const plan: MigrationPlan = { pending: [], renames: [] };
80
- for (const file of [...files].sort()) {
81
- const name = getMigrationName(file);
82
- if (appliedNames.has(name)) continue;
83
-
84
- const from = basename(file) === FOLDER_MIGRATION_FILE ? takeMoved(moved, name) : undefined;
85
- if (from) plan.renames.push({ from, to: name });
86
- else plan.pending.push(file);
87
- }
88
- return plan;
89
- };
90
-
91
- /** Removes and returns the first moved record that the folder `name` can be the new home of. */
92
- const takeMoved = (moved: Map<string, AppliedMigration[]>, name: string): string | undefined => {
93
- const records = moved.get(descriptiveName(name));
94
- const generatedAt = folderTime(name);
95
- const index =
96
- records?.findIndex(
97
- ({ appliedAt }) => generatedAt === undefined || generatedAt <= appliedAt.getTime()
98
- ) ?? -1;
99
- return index < 0 ? undefined : records!.splice(index, 1)[0]!.name;
100
- };
package/src/model.ts DELETED
@@ -1,178 +0,0 @@
1
- // Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type { ColumnMetadata, ModelAdapter } from '@elysiumjs/mnemosyne';
16
- import type { PgColumn, PgTableWithColumns, TableConfig } from 'drizzle-orm/pg-core';
17
-
18
- import { AbstractModel, createSchemaFromModel } from '@elysiumjs/mnemosyne';
19
- import { getTableConfig } from 'drizzle-orm/pg-core';
20
-
21
- import { getTableBuilders } from './table';
22
- import { wrapTenantSchema } from './tenancy';
23
-
24
- // ============================================================================
25
- // Column Type Mapping
26
- // ============================================================================
27
-
28
- /**
29
- * Maps a Drizzle column to a normalized data type.
30
- * @param col The Drizzle column to inspect.
31
- * @returns A normalized data type string.
32
- */
33
- const mapDrizzleDataType = (col: PgColumn): ColumnMetadata['dataType'] => {
34
- if (col.columnType === 'PgUUID') return 'uuid';
35
- if ((col as { dimensions?: number }).dimensions) return 'array';
36
-
37
- // Drizzle 1.0 column data types are `<kind>` or `<kind> <constraint>` (e.g. `object json`).
38
- const [kind, constraint] = col.dataType.split(' ');
39
- switch (kind) {
40
- case 'number':
41
- return 'number';
42
- case 'boolean':
43
- return 'boolean';
44
- case 'array':
45
- return 'array';
46
- case 'bigint':
47
- return 'bigint';
48
- case 'object':
49
- switch (constraint) {
50
- case 'json':
51
- return 'json';
52
- case 'date':
53
- return 'date';
54
- case 'buffer':
55
- return 'buffer';
56
- default:
57
- return 'string';
58
- }
59
- default:
60
- return 'string';
61
- }
62
- };
63
-
64
- // ============================================================================
65
- // Drizzle Model Adapter
66
- // ============================================================================
67
-
68
- /**
69
- * Drizzle-specific model adapter.
70
- *
71
- * Bridges Drizzle's `PgTableWithColumns` to the framework's
72
- * normalized model metadata.
73
- *
74
- * @author Axel Nana <axel.nana@workbud.com>
75
- */
76
- export const drizzleAdapter: ModelAdapter<PgTableWithColumns<any>> = {
77
- getTableName(table) {
78
- return getTableConfig(table).name;
79
- },
80
-
81
- getColumns(table): ColumnMetadata[] {
82
- const config = getTableConfig(table);
83
- return config.columns.map((col: PgColumn) => ({
84
- name: col.name,
85
- dataType: mapDrizzleDataType(col),
86
- nullable: !col.notNull,
87
- hasDefault: col.hasDefault,
88
- isPrimaryKey:
89
- col.primary || config.primaryKeys.some((pk) => pk.columns.some((c) => c.name === col.name))
90
- }));
91
- },
92
-
93
- createTenantTable(table, tenant) {
94
- const builders = getTableBuilders(table);
95
- if (!builders) {
96
- throw new Error(
97
- `No column builders registered for table. ` +
98
- `Use pgTable() from '@elysiumjs/mnemosyne-drizzle' for tenant support.`
99
- );
100
- }
101
- const config = getTableConfig(table);
102
- return wrapTenantSchema(tenant, config.name, builders) as PgTableWithColumns<any>;
103
- }
104
- };
105
-
106
- // ============================================================================
107
- // Schema Helper
108
- // ============================================================================
109
-
110
- /**
111
- * Creates Elysia validation schemas from a Drizzle table.
112
- *
113
- * This is a convenience wrapper around `createSchemaFromModel` that
114
- * automatically extracts column metadata via the `drizzleAdapter`.
115
- *
116
- * @author Axel Nana <axel.nana@workbud.com>
117
- * @param table The Drizzle table to generate schemas for.
118
- * @param opts Schema generation options.
119
- * @returns An Elysia validation schema.
120
- */
121
- export const createSchemaFromDrizzle = (
122
- table: PgTableWithColumns<any>,
123
- opts?: { mode?: 'create' | 'update' | 'select' }
124
- ) => {
125
- return createSchemaFromModel(drizzleAdapter.getColumns(table), opts);
126
- };
127
-
128
- // ============================================================================
129
- // DrizzleModel Options
130
- // ============================================================================
131
-
132
- /**
133
- * Options for creating a DrizzleModel.
134
- */
135
- export interface DrizzleModelOptions {
136
- /**
137
- * Enable tenant schema support for this model.
138
- * @default false
139
- */
140
- supportTenancy?: boolean;
141
- }
142
-
143
- // ============================================================================
144
- // DrizzleModel Function
145
- // ============================================================================
146
-
147
- /**
148
- * Creates a model class backed by a Drizzle pgTable.
149
- *
150
- * @author Axel Nana <axel.nana@workbud.com>
151
- * @param table The Drizzle table (created with enhanced pgTable).
152
- * @param options Model configuration options.
153
- * @returns A model class with Drizzle-backed static metadata.
154
- */
155
- export const DrizzleModel = <TTable extends PgTableWithColumns<TableConfig>>(
156
- table: TTable,
157
- options?: DrizzleModelOptions
158
- ) => {
159
- const config = getTableConfig(table);
160
- const builders = getTableBuilders(table);
161
-
162
- type TSelect = TTable['$inferSelect'];
163
- type TInsert = TTable['$inferInsert'];
164
-
165
- const Base = AbstractModel(config.name, builders ?? {}, drizzleAdapter, table);
166
-
167
- // Override supportTenancy if specified
168
- if (options?.supportTenancy !== undefined) {
169
- (Base as any).supportTenancy = options.supportTenancy;
170
- }
171
-
172
- return Base as typeof Base & {
173
- readonly $inferSelect: TSelect;
174
- readonly $inferInsert: TInsert;
175
- readonly $inferUpdate: Partial<TInsert>;
176
- readonly table: TTable;
177
- };
178
- };