@objectstack/objectql 3.2.1 → 3.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/objectql",
3
- "version": "3.2.1",
3
+ "version": "3.2.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Isomorphic ObjectQL Engine for ObjectStack",
6
6
  "main": "dist/index.js",
@@ -13,9 +13,9 @@
13
13
  }
14
14
  },
15
15
  "dependencies": {
16
- "@objectstack/spec": "3.2.1",
17
- "@objectstack/core": "3.2.1",
18
- "@objectstack/types": "3.2.1"
16
+ "@objectstack/core": "3.2.3",
17
+ "@objectstack/spec": "3.2.3",
18
+ "@objectstack/types": "3.2.3"
19
19
  },
20
20
  "devDependencies": {
21
21
  "typescript": "^5.0.0",
@@ -239,20 +239,20 @@ describe('ObjectQL Engine', () => {
239
239
  // Primary find returns tasks with assignee IDs
240
240
  vi.mocked(mockDriver.find)
241
241
  .mockResolvedValueOnce([
242
- { _id: 't1', title: 'Task 1', assignee: 'u1' },
243
- { _id: 't2', title: 'Task 2', assignee: 'u2' },
242
+ { id: 't1', title: 'Task 1', assignee: 'u1' },
243
+ { id: 't2', title: 'Task 2', assignee: 'u2' },
244
244
  ])
245
245
  // Second call (expand): returns user records
246
246
  .mockResolvedValueOnce([
247
- { _id: 'u1', name: 'Alice' },
248
- { _id: 'u2', name: 'Bob' },
247
+ { id: 'u1', name: 'Alice' },
248
+ { id: 'u2', name: 'Bob' },
249
249
  ]);
250
250
 
251
251
  const result = await engine.find('task', { populate: ['assignee'] });
252
252
 
253
253
  expect(result).toHaveLength(2);
254
- expect(result[0].assignee).toEqual({ _id: 'u1', name: 'Alice' });
255
- expect(result[1].assignee).toEqual({ _id: 'u2', name: 'Bob' });
254
+ expect(result[0].assignee).toEqual({ id: 'u1', name: 'Alice' });
255
+ expect(result[1].assignee).toEqual({ id: 'u2', name: 'Bob' });
256
256
 
257
257
  // Verify the expand query used $in
258
258
  expect(mockDriver.find).toHaveBeenCalledTimes(2);
@@ -260,7 +260,7 @@ describe('ObjectQL Engine', () => {
260
260
  'user',
261
261
  expect.objectContaining({
262
262
  object: 'user',
263
- where: { _id: { $in: ['u1', 'u2'] } },
263
+ where: { id: { $in: ['u1', 'u2'] } },
264
264
  }),
265
265
  );
266
266
  });
@@ -282,14 +282,14 @@ describe('ObjectQL Engine', () => {
282
282
 
283
283
  vi.mocked(mockDriver.find)
284
284
  .mockResolvedValueOnce([
285
- { _id: 'oi1', order: 'o1' },
285
+ { id: 'oi1', order: 'o1' },
286
286
  ])
287
287
  .mockResolvedValueOnce([
288
- { _id: 'o1', total: 100 },
288
+ { id: 'o1', total: 100 },
289
289
  ]);
290
290
 
291
291
  const result = await engine.find('order_item', { populate: ['order'] });
292
- expect(result[0].order).toEqual({ _id: 'o1', total: 100 });
292
+ expect(result[0].order).toEqual({ id: 'o1', total: 100 });
293
293
  });
294
294
 
295
295
  it('should skip expand for fields without reference definition', async () => {
@@ -301,7 +301,7 @@ describe('ObjectQL Engine', () => {
301
301
  } as any);
302
302
 
303
303
  vi.mocked(mockDriver.find).mockResolvedValueOnce([
304
- { _id: 't1', title: 'Task 1' },
304
+ { id: 't1', title: 'Task 1' },
305
305
  ]);
306
306
 
307
307
  const result = await engine.find('task', { populate: ['title'] });
@@ -313,7 +313,7 @@ describe('ObjectQL Engine', () => {
313
313
  vi.mocked(SchemaRegistry.getObject).mockReturnValue(undefined);
314
314
 
315
315
  vi.mocked(mockDriver.find).mockResolvedValueOnce([
316
- { _id: 't1', assignee: 'u1' },
316
+ { id: 't1', assignee: 'u1' },
317
317
  ]);
318
318
 
319
319
  const result = await engine.find('task', { populate: ['assignee'] });
@@ -338,16 +338,16 @@ describe('ObjectQL Engine', () => {
338
338
 
339
339
  vi.mocked(mockDriver.find)
340
340
  .mockResolvedValueOnce([
341
- { _id: 't1', assignee: null },
342
- { _id: 't2', assignee: 'u1' },
341
+ { id: 't1', assignee: null },
342
+ { id: 't2', assignee: 'u1' },
343
343
  ])
344
344
  .mockResolvedValueOnce([
345
- { _id: 'u1', name: 'Alice' },
345
+ { id: 'u1', name: 'Alice' },
346
346
  ]);
347
347
 
348
348
  const result = await engine.find('task', { populate: ['assignee'] });
349
349
  expect(result[0].assignee).toBeNull();
350
- expect(result[1].assignee).toEqual({ _id: 'u1', name: 'Alice' });
350
+ expect(result[1].assignee).toEqual({ id: 'u1', name: 'Alice' });
351
351
  });
352
352
 
353
353
  it('should de-duplicate foreign key IDs in batch query', async () => {
@@ -367,13 +367,13 @@ describe('ObjectQL Engine', () => {
367
367
 
368
368
  vi.mocked(mockDriver.find)
369
369
  .mockResolvedValueOnce([
370
- { _id: 't1', assignee: 'u1' },
371
- { _id: 't2', assignee: 'u1' }, // Same user
372
- { _id: 't3', assignee: 'u2' },
370
+ { id: 't1', assignee: 'u1' },
371
+ { id: 't2', assignee: 'u1' }, // Same user
372
+ { id: 't3', assignee: 'u2' },
373
373
  ])
374
374
  .mockResolvedValueOnce([
375
- { _id: 'u1', name: 'Alice' },
376
- { _id: 'u2', name: 'Bob' },
375
+ { id: 'u1', name: 'Alice' },
376
+ { id: 'u2', name: 'Bob' },
377
377
  ]);
378
378
 
379
379
  const result = await engine.find('task', { populate: ['assignee'] });
@@ -382,11 +382,11 @@ describe('ObjectQL Engine', () => {
382
382
  expect(mockDriver.find).toHaveBeenLastCalledWith(
383
383
  'user',
384
384
  expect.objectContaining({
385
- where: { _id: { $in: ['u1', 'u2'] } },
385
+ where: { id: { $in: ['u1', 'u2'] } },
386
386
  }),
387
387
  );
388
- expect(result[0].assignee).toEqual({ _id: 'u1', name: 'Alice' });
389
- expect(result[1].assignee).toEqual({ _id: 'u1', name: 'Alice' });
388
+ expect(result[0].assignee).toEqual({ id: 'u1', name: 'Alice' });
389
+ expect(result[1].assignee).toEqual({ id: 'u1', name: 'Alice' });
390
390
  });
391
391
 
392
392
  it('should keep raw ID when referenced record not found', async () => {
@@ -406,7 +406,7 @@ describe('ObjectQL Engine', () => {
406
406
 
407
407
  vi.mocked(mockDriver.find)
408
408
  .mockResolvedValueOnce([
409
- { _id: 't1', assignee: 'u_deleted' },
409
+ { id: 't1', assignee: 'u_deleted' },
410
410
  ])
411
411
  .mockResolvedValueOnce([]); // No records found
412
412
 
@@ -436,15 +436,15 @@ describe('ObjectQL Engine', () => {
436
436
 
437
437
  vi.mocked(mockDriver.find)
438
438
  .mockResolvedValueOnce([
439
- { _id: 't1', assignee: 'u1', project: 'p1' },
439
+ { id: 't1', assignee: 'u1', project: 'p1' },
440
440
  ])
441
- .mockResolvedValueOnce([{ _id: 'u1', name: 'Alice' }])
442
- .mockResolvedValueOnce([{ _id: 'p1', name: 'Project X' }]);
441
+ .mockResolvedValueOnce([{ id: 'u1', name: 'Alice' }])
442
+ .mockResolvedValueOnce([{ id: 'p1', name: 'Project X' }]);
443
443
 
444
444
  const result = await engine.find('task', { populate: ['assignee', 'project'] });
445
445
 
446
- expect(result[0].assignee).toEqual({ _id: 'u1', name: 'Alice' });
447
- expect(result[0].project).toEqual({ _id: 'p1', name: 'Project X' });
446
+ expect(result[0].assignee).toEqual({ id: 'u1', name: 'Alice' });
447
+ expect(result[0].project).toEqual({ id: 'p1', name: 'Project X' });
448
448
  expect(mockDriver.find).toHaveBeenCalledTimes(3);
449
449
  });
450
450
 
@@ -464,15 +464,15 @@ describe('ObjectQL Engine', () => {
464
464
  });
465
465
 
466
466
  vi.mocked(mockDriver.findOne as any).mockResolvedValueOnce(
467
- { _id: 't1', title: 'Task 1', assignee: 'u1' },
467
+ { id: 't1', title: 'Task 1', assignee: 'u1' },
468
468
  );
469
469
  vi.mocked(mockDriver.find).mockResolvedValueOnce([
470
- { _id: 'u1', name: 'Alice' },
470
+ { id: 'u1', name: 'Alice' },
471
471
  ]);
472
472
 
473
473
  const result = await engine.findOne('task', { populate: ['assignee'] });
474
474
 
475
- expect(result.assignee).toEqual({ _id: 'u1', name: 'Alice' });
475
+ expect(result.assignee).toEqual({ id: 'u1', name: 'Alice' });
476
476
  });
477
477
 
478
478
  it('should handle already-expanded objects (skip re-expansion)', async () => {
@@ -492,14 +492,14 @@ describe('ObjectQL Engine', () => {
492
492
 
493
493
  // Driver returns an already-expanded object
494
494
  vi.mocked(mockDriver.find).mockResolvedValueOnce([
495
- { _id: 't1', assignee: { _id: 'u1', name: 'Alice' } },
495
+ { id: 't1', assignee: { id: 'u1', name: 'Alice' } },
496
496
  ]);
497
497
 
498
498
  const result = await engine.find('task', { populate: ['assignee'] });
499
499
 
500
500
  // No expand query should have been made — the value was already an object
501
501
  expect(mockDriver.find).toHaveBeenCalledTimes(1);
502
- expect(result[0].assignee).toEqual({ _id: 'u1', name: 'Alice' });
502
+ expect(result[0].assignee).toEqual({ id: 'u1', name: 'Alice' });
503
503
  });
504
504
 
505
505
  it('should gracefully handle expand errors and keep raw IDs', async () => {
@@ -519,7 +519,7 @@ describe('ObjectQL Engine', () => {
519
519
 
520
520
  vi.mocked(mockDriver.find)
521
521
  .mockResolvedValueOnce([
522
- { _id: 't1', assignee: 'u1' },
522
+ { id: 't1', assignee: 'u1' },
523
523
  ])
524
524
  .mockRejectedValueOnce(new Error('Driver connection failed'));
525
525
 
@@ -544,17 +544,17 @@ describe('ObjectQL Engine', () => {
544
544
 
545
545
  vi.mocked(mockDriver.find)
546
546
  .mockResolvedValueOnce([
547
- { _id: 't1', watchers: ['u1', 'u2'] },
547
+ { id: 't1', watchers: ['u1', 'u2'] },
548
548
  ])
549
549
  .mockResolvedValueOnce([
550
- { _id: 'u1', name: 'Alice' },
551
- { _id: 'u2', name: 'Bob' },
550
+ { id: 'u1', name: 'Alice' },
551
+ { id: 'u2', name: 'Bob' },
552
552
  ]);
553
553
 
554
554
  const result = await engine.find('task', { populate: ['watchers'] });
555
555
  expect(result[0].watchers).toEqual([
556
- { _id: 'u1', name: 'Alice' },
557
- { _id: 'u2', name: 'Bob' },
556
+ { id: 'u1', name: 'Alice' },
557
+ { id: 'u2', name: 'Bob' },
558
558
  ]);
559
559
  });
560
560
 
@@ -570,14 +570,14 @@ describe('ObjectQL Engine', () => {
570
570
  });
571
571
 
572
572
  vi.mocked(mockDriver.find)
573
- .mockResolvedValueOnce([{ _id: 't1', project: 'p1' }]) // find task
574
- .mockResolvedValueOnce([{ _id: 'p1', org: 'o1' }]); // expand project (depth 0)
573
+ .mockResolvedValueOnce([{ id: 't1', project: 'p1' }]) // find task
574
+ .mockResolvedValueOnce([{ id: 'p1', org: 'o1' }]); // expand project (depth 0)
575
575
  // org should NOT be expanded further — flat populate doesn't create nested expand
576
576
 
577
577
  const result = await engine.find('task', { populate: ['project'] });
578
578
 
579
579
  // Project expanded, but org inside project remains as raw ID
580
- expect(result[0].project).toEqual({ _id: 'p1', org: 'o1' });
580
+ expect(result[0].project).toEqual({ id: 'p1', org: 'o1' });
581
581
  expect(mockDriver.find).toHaveBeenCalledTimes(2); // Only primary + 1 expand query
582
582
  });
583
583
 
@@ -588,11 +588,11 @@ describe('ObjectQL Engine', () => {
588
588
  } as any);
589
589
 
590
590
  vi.mocked(mockDriver.find).mockResolvedValueOnce([
591
- { _id: 't1', title: 'Task 1' },
591
+ { id: 't1', title: 'Task 1' },
592
592
  ]);
593
593
 
594
594
  const result = await engine.find('task', {});
595
- expect(result).toEqual([{ _id: 't1', title: 'Task 1' }]);
595
+ expect(result).toEqual([{ id: 't1', title: 'Task 1' }]);
596
596
  expect(mockDriver.find).toHaveBeenCalledTimes(1);
597
597
  });
598
598
  });
package/src/engine.ts CHANGED
@@ -681,7 +681,7 @@ export class ObjectQL implements IDataEngine {
681
681
  try {
682
682
  const relatedQuery: QueryAST = {
683
683
  object: referenceObject,
684
- where: { _id: { $in: uniqueIds } },
684
+ where: { id: { $in: uniqueIds } },
685
685
  ...(nestedAST.fields ? { fields: nestedAST.fields } : {}),
686
686
  ...(nestedAST.orderBy ? { orderBy: nestedAST.orderBy } : {}),
687
687
  };
@@ -692,7 +692,7 @@ export class ObjectQL implements IDataEngine {
692
692
  // Build a lookup map: id → record
693
693
  const recordMap = new Map<string, any>();
694
694
  for (const rec of relatedRecords) {
695
- const id = rec._id ?? rec.id;
695
+ const id = rec.id;
696
696
  if (id != null) recordMap.set(String(id), rec);
697
697
  }
698
698
 
@@ -707,7 +707,7 @@ export class ObjectQL implements IDataEngine {
707
707
  // Rebuild map with expanded records
708
708
  recordMap.clear();
709
709
  for (const rec of expandedRelated) {
710
- const id = rec._id ?? rec.id;
710
+ const id = rec.id;
711
711
  if (id != null) recordMap.set(String(id), rec);
712
712
  }
713
713
  }
@@ -921,10 +921,9 @@ export class ObjectQL implements IDataEngine {
921
921
  const driver = this.getDriver(object);
922
922
 
923
923
  // 1. Extract ID from data or filter if it's a single update by ID
924
- let id = data.id || data._id;
924
+ let id = data.id;
925
925
  if (!id && options?.filter) {
926
926
  if (typeof options.filter === 'string') id = options.filter;
927
- else if (options.filter._id) id = options.filter._id;
928
927
  else if (options.filter.id) id = options.filter.id;
929
928
  }
930
929
 
@@ -980,7 +979,6 @@ export class ObjectQL implements IDataEngine {
980
979
  let id: any = undefined;
981
980
  if (options?.filter) {
982
981
  if (typeof options.filter === 'string') id = options.filter;
983
- else if (options.filter._id) id = options.filter._id;
984
982
  else if (options.filter.id) id = options.filter.id;
985
983
  }
986
984
 
@@ -1043,7 +1041,7 @@ export class ObjectQL implements IDataEngine {
1043
1041
  return driver.count(object, ast);
1044
1042
  }
1045
1043
  // Fallback to find().length
1046
- const res = await this.find(object, { filter: query?.filter, select: ['_id'] });
1044
+ const res = await this.find(object, { filter: query?.filter, select: ['id'] });
1047
1045
  return res.length;
1048
1046
  });
1049
1047
 
@@ -1335,8 +1333,8 @@ export class ObjectRepository {
1335
1333
 
1336
1334
  /** Update a single record by ID */
1337
1335
  async updateById(id: string | number, data: any): Promise<any> {
1338
- return this.engine.update(this.objectName, { ...data, _id: id }, {
1339
- filter: { _id: id },
1336
+ return this.engine.update(this.objectName, { ...data, id: id }, {
1337
+ filter: { id: id },
1340
1338
  context: this.context,
1341
1339
  });
1342
1340
  }
@@ -1351,7 +1349,7 @@ export class ObjectRepository {
1351
1349
  /** Delete a single record by ID */
1352
1350
  async deleteById(id: string | number): Promise<any> {
1353
1351
  return this.engine.delete(this.objectName, {
1354
- filter: { _id: id },
1352
+ filter: { id: id },
1355
1353
  context: this.context,
1356
1354
  });
1357
1355
  }
@@ -16,7 +16,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
16
16
  it('should register ObjectQL as metadata service provider', async () => {
17
17
  // Arrange
18
18
  const plugin = new ObjectQLPlugin();
19
- kernel.use(plugin);
19
+ await kernel.use(plugin);
20
20
 
21
21
  // Act
22
22
  await kernel.bootstrap();
@@ -24,16 +24,19 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
24
24
  // Assert
25
25
  const metadataService = kernel.getService('metadata');
26
26
  expect(metadataService).toBeDefined();
27
-
28
- // Should be the ObjectQL instance
27
+
28
+ // ObjectQL registers a MetadataFacade as the metadata service;
29
+ // it is separate from (but backed by the same registry as) the objectql service.
29
30
  const objectql = kernel.getService('objectql');
30
- expect(metadataService).toBe(objectql);
31
+ expect(objectql).toBeDefined();
32
+ // metadata and objectql are distinct service instances
33
+ expect(metadataService).not.toBe(objectql);
31
34
  });
32
35
 
33
36
  it('should serve in-memory metadata definitions', async () => {
34
37
  // Arrange
35
38
  const plugin = new ObjectQLPlugin();
36
- kernel.use(plugin);
39
+ await kernel.use(plugin);
37
40
  await kernel.bootstrap();
38
41
 
39
42
  const objectql = kernel.getService('objectql') as any;
@@ -49,17 +52,13 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
49
52
  }
50
53
  };
51
54
 
52
- // Act - Register object programmatically
53
- objectql.registry.registerObject({
54
- packageId: 'test',
55
- namespace: 'test',
56
- ownership: 'own',
57
- object: testObject
58
- });
55
+ // Act - Register object programmatically via the SchemaRegistry API
56
+ objectql.registry.registerObject(testObject, 'test', 'test');
59
57
 
60
- // Assert - Should be retrievable via registry
61
- const objects = objectql.registry.listObjects();
62
- expect(objects).toContain('test__test_object');
58
+ // Assert - Should be retrievable via registry (getAllObjects returns ServiceObject[])
59
+ const objects = objectql.registry.getAllObjects();
60
+ const fqns = objects.map((o: any) => o.name);
61
+ expect(fqns).toContain('test__test_object');
63
62
  });
64
63
  });
65
64
 
@@ -67,7 +66,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
67
66
  it('should register objectql, data, and protocol services', async () => {
68
67
  // Arrange
69
68
  const plugin = new ObjectQLPlugin();
70
- kernel.use(plugin);
69
+ await kernel.use(plugin);
71
70
 
72
71
  // Act
73
72
  await kernel.bootstrap();
@@ -88,7 +87,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
88
87
  list: async () => []
89
88
  };
90
89
 
91
- kernel.use({
90
+ await kernel.use({
92
91
  name: 'mock-metadata',
93
92
  type: 'test',
94
93
  version: '1.0.0',
@@ -98,7 +97,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
98
97
  });
99
98
 
100
99
  const plugin = new ObjectQLPlugin();
101
- kernel.use(plugin);
100
+ await kernel.use(plugin);
102
101
 
103
102
  // Act
104
103
  await kernel.bootstrap();
@@ -125,7 +124,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
125
124
  delete: async () => ({ count: 1 })
126
125
  };
127
126
 
128
- kernel.use({
127
+ await kernel.use({
129
128
  name: 'mock-driver-plugin',
130
129
  type: 'driver',
131
130
  version: '1.0.0',
@@ -135,7 +134,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
135
134
  });
136
135
 
137
136
  const plugin = new ObjectQLPlugin();
138
- kernel.use(plugin);
137
+ await kernel.use(plugin);
139
138
 
140
139
  // Act
141
140
  await kernel.bootstrap();
@@ -156,7 +155,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
156
155
  }
157
156
  };
158
157
 
159
- kernel.use({
158
+ await kernel.use({
160
159
  name: 'mock-app-plugin',
161
160
  type: 'app',
162
161
  version: '1.0.0',
@@ -166,7 +165,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
166
165
  });
167
166
 
168
167
  const plugin = new ObjectQLPlugin();
169
- kernel.use(plugin);
168
+ await kernel.use(plugin);
170
169
 
171
170
  // Act
172
171
  await kernel.bootstrap();
@@ -212,7 +211,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
212
211
  };
213
212
 
214
213
  // Register mock metadata service BEFORE ObjectQL
215
- kernel.use({
214
+ await kernel.use({
216
215
  name: 'mock-metadata',
217
216
  type: 'metadata',
218
217
  version: '1.0.0',
@@ -222,7 +221,7 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
222
221
  });
223
222
 
224
223
  const plugin = new ObjectQLPlugin();
225
- kernel.use(plugin);
224
+ await kernel.use(plugin);
226
225
 
227
226
  // Act
228
227
  await kernel.bootstrap();
package/src/plugin.ts CHANGED
@@ -116,6 +116,9 @@ export class ObjectQLPlugin implements Plugin {
116
116
  }
117
117
  }
118
118
 
119
+ // Initialize drivers (calls driver.connect() which sets up persistence)
120
+ await this.ql?.init();
121
+
119
122
  // Register built-in audit hooks
120
123
  this.registerAuditHooks(ctx);
121
124
 
@@ -129,35 +132,35 @@ export class ObjectQLPlugin implements Plugin {
129
132
  }
130
133
 
131
134
  /**
132
- * Register built-in audit hooks for auto-stamping createdBy/modifiedBy
135
+ * Register built-in audit hooks for auto-stamping created_by/updated_by
133
136
  * and fetching previousData for update/delete operations.
134
137
  */
135
138
  private registerAuditHooks(ctx: PluginContext) {
136
139
  if (!this.ql) return;
137
140
 
138
- // Auto-stamp createdBy/modifiedBy on insert
141
+ // Auto-stamp created_by/updated_by on insert
139
142
  this.ql.registerHook('beforeInsert', async (hookCtx) => {
140
143
  if (hookCtx.session?.userId && hookCtx.input?.data) {
141
144
  const data = hookCtx.input.data as Record<string, any>;
142
145
  if (typeof data === 'object' && data !== null) {
143
146
  data.created_by = data.created_by ?? hookCtx.session.userId;
144
- data.modified_by = hookCtx.session.userId;
147
+ data.updated_by = hookCtx.session.userId;
145
148
  data.created_at = data.created_at ?? new Date().toISOString();
146
- data.modified_at = new Date().toISOString();
149
+ data.updated_at = new Date().toISOString();
147
150
  if (hookCtx.session.tenantId) {
148
- data.space_id = data.space_id ?? hookCtx.session.tenantId;
151
+ data.tenant_id = data.tenant_id ?? hookCtx.session.tenantId;
149
152
  }
150
153
  }
151
154
  }
152
155
  }, { object: '*', priority: 10 });
153
156
 
154
- // Auto-stamp modifiedBy on update
157
+ // Auto-stamp updated_by on update
155
158
  this.ql.registerHook('beforeUpdate', async (hookCtx) => {
156
159
  if (hookCtx.session?.userId && hookCtx.input?.data) {
157
160
  const data = hookCtx.input.data as Record<string, any>;
158
161
  if (typeof data === 'object' && data !== null) {
159
- data.modified_by = hookCtx.session.userId;
160
- data.modified_at = new Date().toISOString();
162
+ data.updated_by = hookCtx.session.userId;
163
+ data.updated_at = new Date().toISOString();
161
164
  }
162
165
  }
163
166
  }, { object: '*', priority: 10 });
@@ -167,7 +170,7 @@ export class ObjectQLPlugin implements Plugin {
167
170
  if (hookCtx.input?.id && !hookCtx.previous) {
168
171
  try {
169
172
  const existing = await this.ql!.findOne(hookCtx.object, {
170
- filter: { _id: hookCtx.input.id }
173
+ filter: { id: hookCtx.input.id }
171
174
  });
172
175
  if (existing) {
173
176
  hookCtx.previous = existing;
@@ -183,7 +186,7 @@ export class ObjectQLPlugin implements Plugin {
183
186
  if (hookCtx.input?.id && !hookCtx.previous) {
184
187
  try {
185
188
  const existing = await this.ql!.findOne(hookCtx.object, {
186
- filter: { _id: hookCtx.input.id }
189
+ filter: { id: hookCtx.input.id }
187
190
  });
188
191
  if (existing) {
189
192
  hookCtx.previous = existing;
@@ -194,11 +197,11 @@ export class ObjectQLPlugin implements Plugin {
194
197
  }
195
198
  }, { object: '*', priority: 5 });
196
199
 
197
- ctx.logger.debug('Audit hooks registered (createdBy/modifiedBy, previousData)');
200
+ ctx.logger.debug('Audit hooks registered (created_by/updated_by, previousData)');
198
201
  }
199
202
 
200
203
  /**
201
- * Register tenant isolation middleware that auto-injects space_id filter
204
+ * Register tenant isolation middleware that auto-injects tenant_id filter
202
205
  * for multi-tenant operations.
203
206
  */
204
207
  private registerTenantMiddleware(ctx: PluginContext) {
@@ -210,10 +213,10 @@ export class ObjectQLPlugin implements Plugin {
210
213
  return next();
211
214
  }
212
215
 
213
- // Read operations: inject space_id filter into AST
216
+ // Read operations: inject tenant_id filter into AST
214
217
  if (['find', 'findOne', 'count', 'aggregate'].includes(opCtx.operation)) {
215
218
  if (opCtx.ast) {
216
- const tenantFilter = { space_id: opCtx.context.tenantId };
219
+ const tenantFilter = { tenant_id: opCtx.context.tenantId };
217
220
  if (opCtx.ast.where) {
218
221
  opCtx.ast.where = { $and: [opCtx.ast.where, tenantFilter] };
219
222
  } else {