@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
@@ -1,123 +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 { describe, expect, it } from 'bun:test';
16
-
17
- import { getMigrationName, planMigrations } from '../src/migrations';
18
-
19
- const dir = '/app/src/database/migrations';
20
-
21
- // Records applied after every migration in these tests was generated.
22
- const applied = (...names: string[]) =>
23
- names.map((name) => ({ name, appliedAt: new Date('2027-01-01T00:00:00Z') }));
24
-
25
- describe('getMigrationName', () => {
26
- it('should use the file name of a flat migration', () => {
27
- expect(getMigrationName(`${dir}/0000_users.sql`)).toBe('0000_users.sql');
28
- });
29
-
30
- it('should use the folder name of a Drizzle Kit 1.0 migration', () => {
31
- expect(getMigrationName(`${dir}/20260101120000_users/migration.sql`)).toBe(
32
- '20260101120000_users'
33
- );
34
- });
35
- });
36
-
37
- describe('planMigrations', () => {
38
- it('should run every migration on an empty database, in order', () => {
39
- const files = [
40
- `${dir}/20260102000000_posts/migration.sql`,
41
- `${dir}/20260101000000_users/migration.sql`
42
- ];
43
-
44
- expect(planMigrations(files, applied())).toEqual({
45
- pending: [
46
- `${dir}/20260101000000_users/migration.sql`,
47
- `${dir}/20260102000000_posts/migration.sql`
48
- ],
49
- renames: []
50
- });
51
- });
52
-
53
- it('should skip migrations already applied under their current name', () => {
54
- const files = [`${dir}/0000_users.sql`, `${dir}/20260101000000_posts/migration.sql`];
55
-
56
- expect(planMigrations(files, applied('0000_users.sql', '20260101000000_posts'))).toEqual({
57
- pending: [],
58
- renames: []
59
- });
60
- });
61
-
62
- it('should recognise flat migrations that drizzle-kit up moved into folders', () => {
63
- const files = [
64
- `${dir}/20250301101500_users/migration.sql`,
65
- `${dir}/20250302090000_posts/migration.sql`,
66
- `${dir}/20260101000000_comments/migration.sql`
67
- ];
68
-
69
- expect(planMigrations(files, applied('0000_users.sql', '0001_posts.sql'))).toEqual({
70
- pending: [`${dir}/20260101000000_comments/migration.sql`],
71
- renames: [
72
- { from: '0000_users.sql', to: '20250301101500_users' },
73
- { from: '0001_posts.sql', to: '20250302090000_posts' }
74
- ]
75
- });
76
- });
77
-
78
- it('should match timestamp-prefixed flat migrations whose prefix differs from the folder', () => {
79
- const files = [`${dir}/20250301101501_add_users_table/migration.sql`];
80
-
81
- expect(planMigrations(files, applied('20250301101459_add_users_table.sql'))).toEqual({
82
- pending: [],
83
- renames: [
84
- { from: '20250301101459_add_users_table.sql', to: '20250301101501_add_users_table' }
85
- ]
86
- });
87
- });
88
-
89
- it('should pair repeated names in order', () => {
90
- const files = [
91
- `${dir}/20250301000000_update/migration.sql`,
92
- `${dir}/20250302000000_update/migration.sql`,
93
- `${dir}/20250303000000_update/migration.sql`
94
- ];
95
-
96
- expect(planMigrations(files, applied('0000_update.sql', '0001_update.sql'))).toEqual({
97
- pending: [`${dir}/20250303000000_update/migration.sql`],
98
- renames: [
99
- { from: '0000_update.sql', to: '20250301000000_update' },
100
- { from: '0001_update.sql', to: '20250302000000_update' }
101
- ]
102
- });
103
- });
104
-
105
- it('should not move a record whose flat file is still there', () => {
106
- const files = [`${dir}/0000_users.sql`, `${dir}/20260101000000_users/migration.sql`];
107
-
108
- expect(planMigrations(files, applied('0000_users.sql'))).toEqual({
109
- pending: [`${dir}/20260101000000_users/migration.sql`],
110
- renames: []
111
- });
112
- });
113
-
114
- it('should run a new migration that reuses the name of a deleted one', () => {
115
- const files = [`${dir}/20260101000000_update/migration.sql`];
116
- const records = [{ name: '0003_update.sql', appliedAt: new Date('2025-06-01T00:00:00Z') }];
117
-
118
- expect(planMigrations(files, records)).toEqual({
119
- pending: [`${dir}/20260101000000_update/migration.sql`],
120
- renames: []
121
- });
122
- });
123
- });
@@ -1,362 +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 { afterAll, afterEach, beforeAll, describe, expect, it, jest, mock, spyOn } from 'bun:test';
16
- import * as d from 'drizzle-orm/pg-core';
17
- import { t } from 'elysia';
18
-
19
- import { Application } from '@elysiumjs/core';
20
-
21
- import { createSchemaFromDrizzle, drizzleAdapter, DrizzleModel } from '../src/model';
22
- import { pgTable } from '../src/table';
23
- import * as Tenancy from '../src/tenancy';
24
-
25
- // ============================================================================
26
- // Test Fixtures
27
- // ============================================================================
28
-
29
- const mockColumns = {
30
- id: d.uuid().primaryKey().defaultRandom(),
31
- name: d.varchar().notNull(),
32
- age: d.integer().notNull(),
33
- email: d.varchar().notNull().unique(),
34
- is_confirmed: d.boolean().default(false)
35
- };
36
-
37
- const mockTable = d.pgTable('users', mockColumns);
38
-
39
- const enhancedMockTable = pgTable('enhanced_users', mockColumns);
40
-
41
- const mockStore = new Map([['tenant', 'test-tenant']]);
42
- const mockGetStore = mock(() => mockStore);
43
-
44
- // ============================================================================
45
- // Mock Application context
46
- // ============================================================================
47
-
48
- beforeAll(() => {
49
- Object.defineProperty(Application, 'context', {
50
- get: () => ({
51
- getStore: mockGetStore,
52
- run: mock((_: any, callback: any) => callback())
53
- }),
54
- configurable: true
55
- });
56
- });
57
-
58
- // ============================================================================
59
- // Tests
60
- // ============================================================================
61
-
62
- describe('DrizzleModel', () => {
63
- afterEach(() => {
64
- jest.clearAllMocks();
65
- });
66
-
67
- afterAll(() => {
68
- mock.restore();
69
- });
70
-
71
- describe('drizzleAdapter', () => {
72
- describe('getTableName', () => {
73
- it('should return the table name', () => {
74
- const name = drizzleAdapter.getTableName(mockTable);
75
- expect(name).toBe('users');
76
- });
77
- });
78
-
79
- describe('getColumns', () => {
80
- it('should return normalized column metadata', () => {
81
- const columns = drizzleAdapter.getColumns(mockTable);
82
-
83
- expect(columns).toBeArrayOfSize(5);
84
-
85
- const idCol = columns.find(c => c.name === 'id');
86
- expect(idCol).toBeDefined();
87
- expect(idCol!.dataType).toBe('uuid');
88
- expect(idCol!.isPrimaryKey).toBe(true);
89
- expect(idCol!.hasDefault).toBe(true);
90
- expect(idCol!.nullable).toBe(false);
91
-
92
- const nameCol = columns.find(c => c.name === 'name');
93
- expect(nameCol).toBeDefined();
94
- expect(nameCol!.dataType).toBe('string');
95
- expect(nameCol!.nullable).toBe(false);
96
- expect(nameCol!.hasDefault).toBe(false);
97
-
98
- const ageCol = columns.find(c => c.name === 'age');
99
- expect(ageCol).toBeDefined();
100
- expect(ageCol!.dataType).toBe('number');
101
-
102
- const confirmedCol = columns.find(c => c.name === 'is_confirmed');
103
- expect(confirmedCol).toBeDefined();
104
- expect(confirmedCol!.dataType).toBe('boolean');
105
- expect(confirmedCol!.hasDefault).toBe(true);
106
- });
107
- });
108
-
109
- describe('createTenantTable', () => {
110
- it('should create a tenant-scoped table when column builders are registered', () => {
111
- // Use enhanced pgTable which auto-registers builders
112
- const cols = {
113
- id: d.uuid().primaryKey().defaultRandom(),
114
- name: d.varchar().notNull()
115
- };
116
- const table = pgTable('items', cols);
117
-
118
- const pgSchemaSpy = spyOn(d, 'pgSchema');
119
-
120
- const tenantTable = drizzleAdapter.createTenantTable(table, 'tenant-abc');
121
-
122
- expect(pgSchemaSpy).toHaveBeenCalledWith('tenant-abc');
123
- expect(tenantTable).toBeDefined();
124
- });
125
-
126
- it('should create a public table for the public tenant', () => {
127
- const cols = {
128
- id: d.uuid().primaryKey().defaultRandom(),
129
- name: d.varchar().notNull()
130
- };
131
- const table = pgTable('items_public', cols);
132
-
133
- const pgTableSpy = spyOn(d, 'pgTable');
134
-
135
- const publicTable = drizzleAdapter.createTenantTable(table, 'public');
136
-
137
- expect(pgTableSpy).toHaveBeenCalledWith('items_public', cols);
138
- expect(publicTable).toBeDefined();
139
- });
140
-
141
- it('should throw if column builders are not registered', () => {
142
- const unregisteredTable = d.pgTable('orphan', {
143
- id: d.uuid().primaryKey()
144
- });
145
-
146
- expect(() => {
147
- drizzleAdapter.createTenantTable(unregisteredTable, 'some-tenant');
148
- }).toThrow('No column builders registered');
149
- });
150
- });
151
- });
152
-
153
- describe('createSchemaFromDrizzle', () => {
154
- it('should create a schema for select mode', () => {
155
- const getTableConfigSpy = spyOn(d, 'getTableConfig');
156
- const objectSpy = spyOn(t, 'Object');
157
-
158
- createSchemaFromDrizzle(mockTable, { mode: 'select' });
159
-
160
- expect(getTableConfigSpy).toHaveBeenCalledWith(mockTable);
161
- expect(objectSpy).toHaveBeenCalledWith(
162
- expect.objectContaining({
163
- id: expect.any(Object),
164
- name: expect.any(Object),
165
- email: expect.any(Object),
166
- age: expect.any(Object),
167
- is_confirmed: expect.any(Object)
168
- })
169
- );
170
-
171
- objectSpy.mockRestore();
172
- });
173
-
174
- it('should create a schema for create mode', () => {
175
- const getTableConfigSpy = spyOn(d, 'getTableConfig');
176
- const objectSpy = spyOn(t, 'Object');
177
-
178
- createSchemaFromDrizzle(mockTable, { mode: 'create' });
179
-
180
- expect(getTableConfigSpy).toHaveBeenCalledWith(mockTable);
181
-
182
- // Primary key should be excluded in create mode
183
- expect(objectSpy).toHaveBeenCalledWith(
184
- expect.objectContaining({
185
- name: expect.any(Object),
186
- email: expect.any(Object),
187
- age: expect.any(Object),
188
- is_confirmed: expect.any(Object)
189
- })
190
- );
191
-
192
- expect(objectSpy).not.toHaveBeenCalledWith(
193
- expect.objectContaining({
194
- id: expect.any(Object)
195
- })
196
- );
197
-
198
- objectSpy.mockRestore();
199
- });
200
-
201
- it('should create a schema for update mode', () => {
202
- const getTableConfigSpy = spyOn(d, 'getTableConfig');
203
- const objectSpy = spyOn(t, 'Object');
204
-
205
- createSchemaFromDrizzle(mockTable, { mode: 'update' });
206
-
207
- expect(getTableConfigSpy).toHaveBeenCalledWith(mockTable);
208
-
209
- // Primary key should be excluded in update mode
210
- expect(objectSpy).toHaveBeenCalledWith(
211
- expect.objectContaining({
212
- name: expect.any(Object),
213
- email: expect.any(Object),
214
- age: expect.any(Object),
215
- is_confirmed: expect.any(Object)
216
- })
217
- );
218
-
219
- expect(objectSpy).not.toHaveBeenCalledWith(
220
- expect.objectContaining({
221
- id: expect.any(Object)
222
- })
223
- );
224
-
225
- objectSpy.mockRestore();
226
- });
227
- });
228
-
229
- describe('DrizzleModel with pgTable', () => {
230
- describe('single signature API', () => {
231
- it('should accept a pgTable directly', () => {
232
- const cols = {
233
- id: d.uuid().primaryKey().defaultRandom(),
234
- title: d.varchar().notNull()
235
- };
236
- const posts = pgTable('posts', cols);
237
-
238
- class Post extends DrizzleModel(posts) {}
239
-
240
- expect(Post.tableName).toBe('posts');
241
- expect(Post.table).toBeDefined();
242
- expect(Post.insertSchema).toBeDefined();
243
- expect(Post.updateSchema).toBeDefined();
244
- expect(Post.selectSchema).toBeDefined();
245
- });
246
-
247
- it('should support supportTenancy option', () => {
248
- const cols = {
249
- id: d.uuid().primaryKey().defaultRandom(),
250
- name: d.varchar().notNull()
251
- };
252
- const tenants = pgTable('tenants', cols);
253
-
254
- class Tenant extends DrizzleModel(tenants, { supportTenancy: true }) {}
255
-
256
- expect(Tenant.supportTenancy).toBe(true);
257
- });
258
-
259
- it('should default supportTenancy to false', () => {
260
- const cols = {
261
- id: d.uuid().primaryKey().defaultRandom()
262
- };
263
- const items = pgTable('items', cols);
264
-
265
- class Item extends DrizzleModel(items) {}
266
-
267
- expect(Item.supportTenancy).toBe(false);
268
- });
269
-
270
- it('should infer types from the table', () => {
271
- const cols = {
272
- id: d.uuid().primaryKey().defaultRandom(),
273
- email: d.varchar().notNull(),
274
- age: d.integer()
275
- };
276
- const users = pgTable('typed_users', cols);
277
-
278
- class TypedUser extends DrizzleModel(users) {}
279
-
280
- // Type inference markers
281
- expect(TypedUser.$inferSelect).toBeUndefined();
282
- expect(TypedUser.$inferInsert).toBeUndefined();
283
- expect(TypedUser.$inferUpdate).toBeUndefined();
284
- });
285
- });
286
-
287
- describe('tenancy with pgTable', () => {
288
- it('should create tenant table when tenancy is enabled', () => {
289
- const cols = {
290
- id: d.uuid().primaryKey().defaultRandom(),
291
- data: d.varchar().notNull()
292
- };
293
- const tenantData = pgTable('tenant_data', cols);
294
-
295
- class TenantData extends DrizzleModel(tenantData, { supportTenancy: true }) {}
296
-
297
- // Should not throw when accessing table
298
- expect(TenantData.table).toBeDefined();
299
- });
300
- });
301
- });
302
-
303
- describe('Tenancy', () => {
304
- it('should register a new tenant schema', () => {
305
- const pgSchemaSpy = spyOn(d, 'pgSchema');
306
-
307
- const schema = Tenancy.registerTenantSchema('new-tenant');
308
-
309
- expect(pgSchemaSpy).toHaveBeenCalledWith('new-tenant');
310
- expect(schema).toBeDefined();
311
- });
312
-
313
- it('should wrap columns with a tenant schema', () => {
314
- const pgTableSpy = spyOn(d, 'pgTable');
315
- const pgSchemaSpy = spyOn(d, 'pgSchema');
316
-
317
- const cols = {
318
- id: d.uuid().primaryKey().defaultRandom(),
319
- name: d.varchar().notNull()
320
- };
321
-
322
- // Call the function with a non-public tenant
323
- const table = Tenancy.wrapTenantSchema('wrap-test-tenant', 'items', cols);
324
-
325
- expect(pgSchemaSpy).toHaveBeenCalledWith('wrap-test-tenant');
326
- expect(table).toBeDefined();
327
-
328
- // Call the function with the public tenant
329
- const publicTable = Tenancy.wrapTenantSchema('public', 'items', cols);
330
-
331
- expect(pgTableSpy).toHaveBeenCalledWith('items', cols);
332
- expect(publicTable).toBeDefined();
333
- });
334
-
335
- it('should reuse existing tenant schemas', () => {
336
- const pgSchemaSpy = spyOn(d, 'pgSchema');
337
-
338
- Tenancy.registerTenantSchema('existing-tenant');
339
- pgSchemaSpy.mockClear();
340
-
341
- const schema = Tenancy.registerTenantSchema('existing-tenant');
342
-
343
- expect(pgSchemaSpy).not.toHaveBeenCalled();
344
- expect(schema).toBeDefined();
345
- });
346
-
347
- it('should reuse existing tenant tables', () => {
348
- const cols = {
349
- id: d.uuid().primaryKey().defaultRandom()
350
- };
351
-
352
- const pgSchemaSpy = spyOn(d, 'pgSchema');
353
-
354
- Tenancy.wrapTenantSchema('cache-tenant', 'cached_items', cols);
355
- pgSchemaSpy.mockClear();
356
-
357
- Tenancy.wrapTenantSchema('cache-tenant', 'cached_items', cols);
358
-
359
- expect(pgSchemaSpy).not.toHaveBeenCalled();
360
- });
361
- });
362
- });
package/tests/setup.ts DELETED
File without changes
@@ -1,72 +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 { describe, expect, it } from 'bun:test';
16
- import * as d from 'drizzle-orm/pg-core';
17
-
18
- import { getTableBuilders, pgTable } from '../src/table';
19
-
20
- describe('pgTable', () => {
21
- describe('table creation', () => {
22
- it('should create a table with the same signature as drizzle pgTable', () => {
23
- const cols = {
24
- id: d.uuid().primaryKey().defaultRandom(),
25
- name: d.varchar().notNull()
26
- };
27
-
28
- const table = pgTable('users', cols);
29
-
30
- expect(table).toBeDefined();
31
- expect(table).toHaveProperty('id');
32
- expect(table).toHaveProperty('name');
33
- });
34
-
35
- it('should support extraConfig parameter', () => {
36
- const cols = {
37
- id: d.uuid().primaryKey().defaultRandom()
38
- };
39
-
40
- const table = pgTable('items', cols, (t) => ({
41
- unique: t.unique().on(t.id)
42
- }));
43
-
44
- expect(table).toBeDefined();
45
- });
46
- });
47
-
48
- describe('column builder registry', () => {
49
- it('should register column builders for created tables', () => {
50
- const cols = {
51
- id: d.uuid().primaryKey().defaultRandom(),
52
- email: d.varchar().notNull()
53
- };
54
-
55
- const table = pgTable('registered', cols);
56
- const builders = getTableBuilders(table);
57
-
58
- expect(builders).toBeDefined();
59
- expect(builders).toHaveProperty('id');
60
- expect(builders).toHaveProperty('email');
61
- });
62
-
63
- it('should return undefined for tables not created with enhanced pgTable', () => {
64
- const rawTable = d.pgTable('raw', {
65
- id: d.uuid().primaryKey()
66
- });
67
-
68
- const builders = getTableBuilders(rawTable);
69
- expect(builders).toBeUndefined();
70
- });
71
- });
72
- });