@objectstack/objectql 4.0.2 → 4.0.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +14 -0
- package/dist/index.d.mts +38 -6
- package/dist/index.d.ts +38 -6
- package/dist/index.js +292 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +292 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/engine.ts +116 -10
- package/src/plugin.integration.test.ts +170 -0
- package/src/plugin.ts +149 -1
- package/src/protocol.ts +100 -19
- package/src/registry.test.ts +11 -11
- package/src/registry.ts +4 -4
package/src/protocol.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { ObjectStackProtocol } from '@objectstack/spec/api';
|
|
4
4
|
import { IDataEngine } from '@objectstack/core';
|
|
5
|
-
import type {
|
|
6
|
-
BatchUpdateRequest,
|
|
7
|
-
BatchUpdateResponse,
|
|
5
|
+
import type {
|
|
6
|
+
BatchUpdateRequest,
|
|
7
|
+
BatchUpdateResponse,
|
|
8
8
|
UpdateManyDataRequest,
|
|
9
9
|
DeleteManyDataRequest
|
|
10
10
|
} from '@objectstack/spec/api';
|
|
11
11
|
import type { MetadataCacheRequest, MetadataCacheResponse, ServiceInfo, ApiRoutes, WellKnownCapabilities } from '@objectstack/spec/api';
|
|
12
12
|
import type { IFeedService } from '@objectstack/spec/contracts';
|
|
13
13
|
import { parseFilterAST, isFilterAST } from '@objectstack/spec/data';
|
|
14
|
+
import { PLURAL_TO_SINGULAR, SINGULAR_TO_PLURAL } from '@objectstack/spec/shared';
|
|
14
15
|
|
|
15
16
|
// We import SchemaRegistry directly since this class lives in the same package
|
|
16
17
|
import { SchemaRegistry } from './registry.js';
|
|
@@ -180,18 +181,31 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
async getMetaTypes() {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
const schemaTypes = SchemaRegistry.getRegisteredTypes();
|
|
185
|
+
|
|
186
|
+
// Also include types from MetadataService (runtime-registered: agent, tool, etc.)
|
|
187
|
+
let runtimeTypes: string[] = [];
|
|
188
|
+
try {
|
|
189
|
+
const services = this.getServicesRegistry?.();
|
|
190
|
+
const metadataService = services?.get('metadata');
|
|
191
|
+
if (metadataService && typeof metadataService.getRegisteredTypes === 'function') {
|
|
192
|
+
runtimeTypes = await metadataService.getRegisteredTypes();
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
// MetadataService not available
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const allTypes = Array.from(new Set([...schemaTypes, ...runtimeTypes]));
|
|
199
|
+
return { types: allTypes };
|
|
186
200
|
}
|
|
187
201
|
|
|
188
202
|
async getMetaItems(request: { type: string; packageId?: string }) {
|
|
189
203
|
const { packageId } = request;
|
|
190
204
|
let items = SchemaRegistry.listItems(request.type, packageId);
|
|
191
|
-
// Normalize singular/plural
|
|
205
|
+
// Normalize singular/plural using explicit mapping
|
|
192
206
|
if (items.length === 0) {
|
|
193
|
-
const alt = request.type
|
|
194
|
-
items = SchemaRegistry.listItems(alt, packageId);
|
|
207
|
+
const alt = PLURAL_TO_SINGULAR[request.type] ?? SINGULAR_TO_PLURAL[request.type];
|
|
208
|
+
if (alt) items = SchemaRegistry.listItems(alt, packageId);
|
|
195
209
|
}
|
|
196
210
|
|
|
197
211
|
// Fallback to database if registry is empty for this type
|
|
@@ -212,8 +226,9 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
212
226
|
return data;
|
|
213
227
|
});
|
|
214
228
|
} else {
|
|
215
|
-
// Try alternate type name in DB
|
|
216
|
-
const alt = request.type
|
|
229
|
+
// Try alternate type name in DB using explicit mapping
|
|
230
|
+
const alt = PLURAL_TO_SINGULAR[request.type] ?? SINGULAR_TO_PLURAL[request.type];
|
|
231
|
+
if (alt) {
|
|
217
232
|
const altRecords = await this.engine.find('sys_metadata', {
|
|
218
233
|
where: { type: alt, state: 'active' }
|
|
219
234
|
});
|
|
@@ -226,12 +241,41 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
226
241
|
return data;
|
|
227
242
|
});
|
|
228
243
|
}
|
|
244
|
+
}
|
|
229
245
|
}
|
|
230
246
|
} catch {
|
|
231
247
|
// DB not available, return registry results (empty)
|
|
232
248
|
}
|
|
233
249
|
}
|
|
234
250
|
|
|
251
|
+
// Merge with MetadataService (runtime-registered items: agents, tools, etc.)
|
|
252
|
+
try {
|
|
253
|
+
const services = this.getServicesRegistry?.();
|
|
254
|
+
const metadataService = services?.get('metadata');
|
|
255
|
+
if (metadataService && typeof metadataService.list === 'function') {
|
|
256
|
+
const runtimeItems = await metadataService.list(request.type);
|
|
257
|
+
if (runtimeItems && runtimeItems.length > 0) {
|
|
258
|
+
// Merge, avoiding duplicates by name
|
|
259
|
+
const itemMap = new Map<string, any>();
|
|
260
|
+
for (const item of items) {
|
|
261
|
+
const entry = item as any;
|
|
262
|
+
if (entry && typeof entry === 'object' && 'name' in entry) {
|
|
263
|
+
itemMap.set(entry.name, entry);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (const item of runtimeItems) {
|
|
267
|
+
const entry = item as any;
|
|
268
|
+
if (entry && typeof entry === 'object' && 'name' in entry) {
|
|
269
|
+
itemMap.set(entry.name, entry);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
items = Array.from(itemMap.values());
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} catch {
|
|
276
|
+
// MetadataService not available or doesn't support this type
|
|
277
|
+
}
|
|
278
|
+
|
|
235
279
|
return {
|
|
236
280
|
type: request.type,
|
|
237
281
|
items
|
|
@@ -240,10 +284,10 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
240
284
|
|
|
241
285
|
async getMetaItem(request: { type: string, name: string, packageId?: string }) {
|
|
242
286
|
let item = SchemaRegistry.getItem(request.type, request.name);
|
|
243
|
-
// Normalize singular/plural
|
|
287
|
+
// Normalize singular/plural using explicit mapping
|
|
244
288
|
if (item === undefined) {
|
|
245
|
-
const alt = request.type
|
|
246
|
-
item = SchemaRegistry.getItem(alt, request.name);
|
|
289
|
+
const alt = PLURAL_TO_SINGULAR[request.type] ?? SINGULAR_TO_PLURAL[request.type];
|
|
290
|
+
if (alt) item = SchemaRegistry.getItem(alt, request.name);
|
|
247
291
|
}
|
|
248
292
|
|
|
249
293
|
// Fallback to database if not in registry
|
|
@@ -259,8 +303,9 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
259
303
|
// Hydrate back into registry for next time
|
|
260
304
|
SchemaRegistry.registerItem(request.type, item, 'name' as any);
|
|
261
305
|
} else {
|
|
262
|
-
// Try alternate type name
|
|
263
|
-
const alt = request.type
|
|
306
|
+
// Try alternate type name using explicit mapping
|
|
307
|
+
const alt = PLURAL_TO_SINGULAR[request.type] ?? SINGULAR_TO_PLURAL[request.type];
|
|
308
|
+
if (alt) {
|
|
264
309
|
const altRecord = await this.engine.findOne('sys_metadata', {
|
|
265
310
|
where: { type: alt, name: request.name, state: 'active' }
|
|
266
311
|
});
|
|
@@ -271,12 +316,26 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
271
316
|
// Hydrate back into registry for next time
|
|
272
317
|
SchemaRegistry.registerItem(request.type, item, 'name' as any);
|
|
273
318
|
}
|
|
319
|
+
}
|
|
274
320
|
}
|
|
275
321
|
} catch {
|
|
276
322
|
// DB not available, return undefined
|
|
277
323
|
}
|
|
278
324
|
}
|
|
279
325
|
|
|
326
|
+
// Fallback to MetadataService for runtime-registered items (agents, tools, etc.)
|
|
327
|
+
if (item === undefined) {
|
|
328
|
+
try {
|
|
329
|
+
const services = this.getServicesRegistry?.();
|
|
330
|
+
const metadataService = services?.get('metadata');
|
|
331
|
+
if (metadataService && typeof metadataService.get === 'function') {
|
|
332
|
+
item = await metadataService.get(request.type, request.name);
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
// MetadataService not available
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
280
339
|
return {
|
|
281
340
|
type: request.type,
|
|
282
341
|
name: request.name,
|
|
@@ -563,7 +622,27 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
563
622
|
|
|
564
623
|
async getMetaItemCached(request: { type: string, name: string, cacheRequest?: MetadataCacheRequest }): Promise<MetadataCacheResponse> {
|
|
565
624
|
try {
|
|
566
|
-
|
|
625
|
+
let item = SchemaRegistry.getItem(request.type, request.name);
|
|
626
|
+
|
|
627
|
+
// Normalize singular/plural using explicit mapping
|
|
628
|
+
if (!item) {
|
|
629
|
+
const alt = PLURAL_TO_SINGULAR[request.type] ?? SINGULAR_TO_PLURAL[request.type];
|
|
630
|
+
if (alt) item = SchemaRegistry.getItem(alt, request.name);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Fallback to MetadataService (e.g. agents, tools registered in MetadataManager)
|
|
634
|
+
if (!item) {
|
|
635
|
+
try {
|
|
636
|
+
const services = this.getServicesRegistry?.();
|
|
637
|
+
const metadataService = services?.get('metadata');
|
|
638
|
+
if (metadataService && typeof metadataService.get === 'function') {
|
|
639
|
+
item = await metadataService.get(request.type, request.name);
|
|
640
|
+
}
|
|
641
|
+
} catch {
|
|
642
|
+
// MetadataService not available
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
567
646
|
if (!item) {
|
|
568
647
|
throw new Error(`Metadata item ${request.type}/${request.name} not found`);
|
|
569
648
|
}
|
|
@@ -984,10 +1063,12 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
984
1063
|
const data = typeof record.metadata === 'string'
|
|
985
1064
|
? JSON.parse(record.metadata)
|
|
986
1065
|
: record.metadata;
|
|
987
|
-
|
|
1066
|
+
// Normalize DB type to singular (DB may store legacy plural forms)
|
|
1067
|
+
const normalizedType = PLURAL_TO_SINGULAR[record.type] ?? record.type;
|
|
1068
|
+
if (normalizedType === 'object') {
|
|
988
1069
|
SchemaRegistry.registerObject(data as any, record.packageId || 'sys_metadata');
|
|
989
1070
|
} else {
|
|
990
|
-
SchemaRegistry.registerItem(
|
|
1071
|
+
SchemaRegistry.registerItem(normalizedType, data, 'name' as any);
|
|
991
1072
|
}
|
|
992
1073
|
loaded++;
|
|
993
1074
|
} catch (e) {
|
package/src/registry.test.ts
CHANGED
|
@@ -353,17 +353,17 @@ describe('SchemaRegistry', () => {
|
|
|
353
353
|
describe('Generic Metadata', () => {
|
|
354
354
|
it('should register and retrieve generic items', () => {
|
|
355
355
|
const item = { name: 'test_action', type: 'custom' };
|
|
356
|
-
SchemaRegistry.registerItem('
|
|
357
|
-
|
|
358
|
-
const retrieved = SchemaRegistry.getItem('
|
|
356
|
+
SchemaRegistry.registerItem('action', item, 'name', 'com.pkg');
|
|
357
|
+
|
|
358
|
+
const retrieved = SchemaRegistry.getItem('action', 'test_action');
|
|
359
359
|
expect(retrieved).toEqual(item);
|
|
360
360
|
});
|
|
361
361
|
|
|
362
362
|
it('should list items by type with package filter', () => {
|
|
363
|
-
SchemaRegistry.registerItem('
|
|
364
|
-
SchemaRegistry.registerItem('
|
|
365
|
-
|
|
366
|
-
const filtered = SchemaRegistry.listItems('
|
|
363
|
+
SchemaRegistry.registerItem('action', { name: 'a1' }, 'name', 'com.pkg1');
|
|
364
|
+
SchemaRegistry.registerItem('action', { name: 'a2' }, 'name', 'com.pkg2');
|
|
365
|
+
|
|
366
|
+
const filtered = SchemaRegistry.listItems('action', 'com.pkg1');
|
|
367
367
|
expect(filtered).toHaveLength(1);
|
|
368
368
|
});
|
|
369
369
|
});
|
|
@@ -396,12 +396,12 @@ describe('SchemaRegistry', () => {
|
|
|
396
396
|
describe('Reset', () => {
|
|
397
397
|
it('should clear all state', () => {
|
|
398
398
|
SchemaRegistry.registerObject({ name: 'obj', fields: {} } as any, 'com.pkg', 'pkg', 'own');
|
|
399
|
-
SchemaRegistry.registerItem('
|
|
400
|
-
|
|
399
|
+
SchemaRegistry.registerItem('action', { name: 'act' }, 'name');
|
|
400
|
+
|
|
401
401
|
SchemaRegistry.reset();
|
|
402
|
-
|
|
402
|
+
|
|
403
403
|
expect(SchemaRegistry.getAllObjects()).toHaveLength(0);
|
|
404
|
-
expect(SchemaRegistry.listItems('
|
|
404
|
+
expect(SchemaRegistry.listItems('action')).toHaveLength(0);
|
|
405
405
|
});
|
|
406
406
|
});
|
|
407
407
|
|
package/src/registry.ts
CHANGED
|
@@ -485,7 +485,7 @@ export class SchemaRegistry {
|
|
|
485
485
|
if (type === 'object') {
|
|
486
486
|
return ObjectSchema.parse(item);
|
|
487
487
|
}
|
|
488
|
-
if (type === '
|
|
488
|
+
if (type === 'app') {
|
|
489
489
|
return AppSchema.parse(item);
|
|
490
490
|
}
|
|
491
491
|
if (type === 'package') {
|
|
@@ -664,15 +664,15 @@ export class SchemaRegistry {
|
|
|
664
664
|
// ==========================================
|
|
665
665
|
|
|
666
666
|
static registerApp(app: any, packageId?: string) {
|
|
667
|
-
this.registerItem('
|
|
667
|
+
this.registerItem('app', app, 'name', packageId);
|
|
668
668
|
}
|
|
669
669
|
|
|
670
670
|
static getApp(name: string): any {
|
|
671
|
-
return this.getItem('
|
|
671
|
+
return this.getItem('app', name);
|
|
672
672
|
}
|
|
673
673
|
|
|
674
674
|
static getAllApps(): any[] {
|
|
675
|
-
return this.listItems('
|
|
675
|
+
return this.listItems('app');
|
|
676
676
|
}
|
|
677
677
|
|
|
678
678
|
// ==========================================
|