@objectstack/objectql 1.0.4 → 1.0.5

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/dist/index.js CHANGED
@@ -1,9 +1,798 @@
1
- // Export Registry
2
- export { SchemaRegistry } from './registry.js';
3
- // Export Protocol Implementation
4
- export { ObjectStackProtocolImplementation } from './protocol.js';
5
- // Export Engine
6
- export { ObjectQL } from './engine.js';
7
- // Export Plugin Shim
8
- export { ObjectQLPlugin } from './plugin.js';
9
- // Moved logic to engine.ts
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ObjectQL: () => ObjectQL,
24
+ ObjectQLPlugin: () => ObjectQLPlugin,
25
+ ObjectStackProtocolImplementation: () => ObjectStackProtocolImplementation,
26
+ SchemaRegistry: () => SchemaRegistry
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/registry.ts
31
+ var import_data = require("@objectstack/spec/data");
32
+ var import_kernel = require("@objectstack/spec/kernel");
33
+ var import_ui = require("@objectstack/spec/ui");
34
+ var SchemaRegistry = class {
35
+ /**
36
+ * Universal Register Method
37
+ * @param type The category of metadata (e.g., 'object', 'app', 'plugin')
38
+ * @param item The metadata item itself
39
+ * @param keyField The property to use as the unique key (default: 'name')
40
+ */
41
+ static registerItem(type, item, keyField = "name") {
42
+ if (!this.metadata.has(type)) {
43
+ this.metadata.set(type, /* @__PURE__ */ new Map());
44
+ }
45
+ const collection = this.metadata.get(type);
46
+ const key = String(item[keyField]);
47
+ try {
48
+ this.validate(type, item);
49
+ } catch (e) {
50
+ console.error(`[Registry] Validation failed for ${type} ${key}: ${e.message}`);
51
+ }
52
+ if (collection.has(key)) {
53
+ console.warn(`[Registry] Overwriting ${type}: ${key}`);
54
+ }
55
+ collection.set(key, item);
56
+ console.log(`[Registry] Registered ${type}: ${key}`);
57
+ }
58
+ /**
59
+ * Validate Metadata against Spec Zod Schemas
60
+ */
61
+ static validate(type, item) {
62
+ if (type === "object") {
63
+ return import_data.ObjectSchema.parse(item);
64
+ }
65
+ if (type === "app") {
66
+ return import_ui.AppSchema.parse(item);
67
+ }
68
+ if (type === "plugin") {
69
+ return import_kernel.ManifestSchema.parse(item);
70
+ }
71
+ return true;
72
+ }
73
+ /**
74
+ * Universal Unregister Method
75
+ */
76
+ static unregisterItem(type, name) {
77
+ const collection = this.metadata.get(type);
78
+ if (collection && collection.has(name)) {
79
+ collection.delete(name);
80
+ console.log(`[Registry] Unregistered ${type}: ${name}`);
81
+ } else {
82
+ console.warn(`[Registry] Attempted to unregister non-existent ${type}: ${name}`);
83
+ }
84
+ }
85
+ /**
86
+ * Universal Get Method
87
+ */
88
+ static getItem(type, name) {
89
+ return this.metadata.get(type)?.get(name);
90
+ }
91
+ /**
92
+ * Universal List Method
93
+ */
94
+ static listItems(type) {
95
+ return Array.from(this.metadata.get(type)?.values() || []);
96
+ }
97
+ /**
98
+ * Get all registered metadata types (Kinds)
99
+ */
100
+ static getRegisteredTypes() {
101
+ return Array.from(this.metadata.keys());
102
+ }
103
+ // ==========================================
104
+ // Typed Helper Methods (Shortcuts)
105
+ // ==========================================
106
+ /**
107
+ * Object Helpers
108
+ */
109
+ static registerObject(schema) {
110
+ this.registerItem("object", schema, "name");
111
+ }
112
+ static getObject(name) {
113
+ return this.getItem("object", name);
114
+ }
115
+ static getAllObjects() {
116
+ return this.listItems("object");
117
+ }
118
+ /**
119
+ * Plugin Helpers
120
+ */
121
+ static registerPlugin(manifest) {
122
+ this.registerItem("plugin", manifest, "id");
123
+ }
124
+ static getAllPlugins() {
125
+ return this.listItems("plugin");
126
+ }
127
+ /**
128
+ * Kind (Metadata Type) Helpers
129
+ */
130
+ static registerKind(kind) {
131
+ this.registerItem("kind", kind, "id");
132
+ }
133
+ static getAllKinds() {
134
+ return this.listItems("kind");
135
+ }
136
+ };
137
+ // Nested Map: Type -> Name/ID -> MetadataItem
138
+ SchemaRegistry.metadata = /* @__PURE__ */ new Map();
139
+
140
+ // src/protocol.ts
141
+ function simpleHash(str) {
142
+ let hash = 0;
143
+ for (let i = 0; i < str.length; i++) {
144
+ const char = str.charCodeAt(i);
145
+ hash = (hash << 5) - hash + char;
146
+ hash = hash & hash;
147
+ }
148
+ return Math.abs(hash).toString(16);
149
+ }
150
+ var ObjectStackProtocolImplementation = class {
151
+ constructor(engine) {
152
+ this.engine = engine;
153
+ }
154
+ async getDiscovery(_request) {
155
+ return {
156
+ version: "1.0",
157
+ apiName: "ObjectStack API",
158
+ capabilities: {
159
+ graphql: false,
160
+ search: false,
161
+ websockets: false,
162
+ files: true,
163
+ analytics: false,
164
+ hub: false
165
+ },
166
+ endpoints: {
167
+ data: "/api/data",
168
+ metadata: "/api/meta",
169
+ auth: "/api/auth"
170
+ }
171
+ };
172
+ }
173
+ async getMetaTypes(_request) {
174
+ return {
175
+ types: SchemaRegistry.getRegisteredTypes()
176
+ };
177
+ }
178
+ async getMetaItems(request) {
179
+ return {
180
+ type: request.type,
181
+ items: SchemaRegistry.listItems(request.type)
182
+ };
183
+ }
184
+ async getMetaItem(request) {
185
+ return {
186
+ type: request.type,
187
+ name: request.name,
188
+ item: SchemaRegistry.getItem(request.type, request.name)
189
+ };
190
+ }
191
+ async getUiView(request) {
192
+ const schema = SchemaRegistry.getObject(request.object);
193
+ if (!schema) throw new Error(`Object ${request.object} not found`);
194
+ let view;
195
+ if (request.type === "list") {
196
+ view = {
197
+ type: "list",
198
+ object: request.object,
199
+ columns: Object.keys(schema.fields || {}).slice(0, 5).map((f) => ({
200
+ field: f,
201
+ label: schema.fields[f].label || f
202
+ }))
203
+ };
204
+ } else {
205
+ view = {
206
+ type: "form",
207
+ object: request.object,
208
+ sections: [
209
+ {
210
+ label: "General",
211
+ fields: Object.keys(schema.fields || {}).map((f) => ({
212
+ field: f
213
+ }))
214
+ }
215
+ ]
216
+ };
217
+ }
218
+ return view;
219
+ }
220
+ async findData(request) {
221
+ const options = { ...request.query };
222
+ if (options.top) options.top = Number(options.top);
223
+ if (options.skip) options.skip = Number(options.skip);
224
+ if (options.limit) options.limit = Number(options.limit);
225
+ const records = await this.engine.find(request.object, options);
226
+ return {
227
+ object: request.object,
228
+ value: records,
229
+ // OData compaibility
230
+ records,
231
+ // Legacy
232
+ total: records.length,
233
+ hasMore: false
234
+ };
235
+ }
236
+ async getData(request) {
237
+ const result = await this.engine.findOne(request.object, {
238
+ filter: { _id: request.id }
239
+ });
240
+ if (result) {
241
+ return {
242
+ object: request.object,
243
+ id: request.id,
244
+ record: result
245
+ };
246
+ }
247
+ throw new Error(`Record ${request.id} not found in ${request.object}`);
248
+ }
249
+ async createData(request) {
250
+ const result = await this.engine.insert(request.object, request.data);
251
+ return {
252
+ object: request.object,
253
+ id: result._id || result.id,
254
+ record: result
255
+ };
256
+ }
257
+ async updateData(request) {
258
+ const result = await this.engine.update(request.object, request.data, { filter: { _id: request.id } });
259
+ return {
260
+ object: request.object,
261
+ id: request.id,
262
+ record: result
263
+ };
264
+ }
265
+ async deleteData(request) {
266
+ await this.engine.delete(request.object, { filter: { _id: request.id } });
267
+ return {
268
+ object: request.object,
269
+ id: request.id,
270
+ success: true
271
+ };
272
+ }
273
+ // ==========================================
274
+ // Metadata Caching
275
+ // ==========================================
276
+ async getMetaItemCached(request) {
277
+ try {
278
+ const item = SchemaRegistry.getItem(request.type, request.name);
279
+ if (!item) {
280
+ throw new Error(`Metadata item ${request.type}/${request.name} not found`);
281
+ }
282
+ const content = JSON.stringify(item);
283
+ const hash = simpleHash(content);
284
+ const etag = { value: hash, weak: false };
285
+ if (request.cacheRequest?.ifNoneMatch) {
286
+ const clientEtag = request.cacheRequest.ifNoneMatch.replace(/^"(.*)"$/, "$1").replace(/^W\/"(.*)"$/, "$1");
287
+ if (clientEtag === hash) {
288
+ return {
289
+ notModified: true,
290
+ etag
291
+ };
292
+ }
293
+ }
294
+ return {
295
+ data: item,
296
+ etag,
297
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
298
+ cacheControl: {
299
+ directives: ["public", "max-age"],
300
+ maxAge: 3600
301
+ // 1 hour
302
+ },
303
+ notModified: false
304
+ };
305
+ } catch (error) {
306
+ throw error;
307
+ }
308
+ }
309
+ // ==========================================
310
+ // Batch Operations
311
+ // ==========================================
312
+ async batchData(_request) {
313
+ throw new Error("Batch operations not yet fully implemented in protocol adapter");
314
+ }
315
+ async createManyData(request) {
316
+ const records = await this.engine.insert(request.object, request.records);
317
+ return {
318
+ object: request.object,
319
+ records,
320
+ count: records.length
321
+ };
322
+ }
323
+ async updateManyData(_request) {
324
+ throw new Error("updateManyData not implemented");
325
+ }
326
+ async analyticsQuery(_request) {
327
+ throw new Error("analyticsQuery not implemented");
328
+ }
329
+ async getAnalyticsMeta(_request) {
330
+ throw new Error("getAnalyticsMeta not implemented");
331
+ }
332
+ async triggerAutomation(_request) {
333
+ throw new Error("triggerAutomation not implemented");
334
+ }
335
+ async listSpaces(_request) {
336
+ throw new Error("listSpaces not implemented");
337
+ }
338
+ async createSpace(_request) {
339
+ throw new Error("createSpace not implemented");
340
+ }
341
+ async installPlugin(_request) {
342
+ throw new Error("installPlugin not implemented");
343
+ }
344
+ async deleteManyData(request) {
345
+ return this.engine.delete(request.object, {
346
+ filter: { _id: { $in: request.ids } },
347
+ ...request.options
348
+ });
349
+ }
350
+ async saveMetaItem(request) {
351
+ if (!request.item) {
352
+ throw new Error("Item data is required");
353
+ }
354
+ SchemaRegistry.registerItem(request.type, request.item, "name");
355
+ return {
356
+ success: true,
357
+ message: "Saved to memory registry"
358
+ };
359
+ }
360
+ };
361
+
362
+ // src/engine.ts
363
+ var import_core = require("@objectstack/core");
364
+ var import_system = require("@objectstack/spec/system");
365
+ var ObjectQL = class {
366
+ constructor(hostContext = {}) {
367
+ this.drivers = /* @__PURE__ */ new Map();
368
+ this.defaultDriver = null;
369
+ // Hooks Registry
370
+ this.hooks = {
371
+ "beforeFind": [],
372
+ "afterFind": [],
373
+ "beforeInsert": [],
374
+ "afterInsert": [],
375
+ "beforeUpdate": [],
376
+ "afterUpdate": [],
377
+ "beforeDelete": [],
378
+ "afterDelete": []
379
+ };
380
+ // Host provided context additions (e.g. Server router)
381
+ this.hostContext = {};
382
+ this.hostContext = hostContext;
383
+ this.logger = hostContext.logger || (0, import_core.createLogger)({ level: "info", format: "pretty" });
384
+ this.logger.info("ObjectQL Engine Instance Created");
385
+ }
386
+ /**
387
+ * Service Status Report
388
+ * Used by Kernel to verify health and capabilities.
389
+ */
390
+ getStatus() {
391
+ return {
392
+ name: import_system.CoreServiceName.enum.data,
393
+ status: "running",
394
+ version: "0.9.0",
395
+ features: ["crud", "query", "aggregate", "transactions", "metadata"]
396
+ };
397
+ }
398
+ /**
399
+ * Expose the SchemaRegistry for plugins to register metadata
400
+ */
401
+ get registry() {
402
+ return SchemaRegistry;
403
+ }
404
+ /**
405
+ * Load and Register a Plugin
406
+ */
407
+ async use(manifestPart, runtimePart) {
408
+ this.logger.debug("Loading plugin", {
409
+ hasManifest: !!manifestPart,
410
+ hasRuntime: !!runtimePart
411
+ });
412
+ if (manifestPart) {
413
+ this.registerApp(manifestPart);
414
+ }
415
+ if (runtimePart) {
416
+ const pluginDef = runtimePart.default || runtimePart;
417
+ if (pluginDef.onEnable) {
418
+ this.logger.debug("Executing plugin runtime onEnable");
419
+ const context = {
420
+ ql: this,
421
+ logger: this.logger,
422
+ // Expose the driver registry helper explicitly if needed
423
+ drivers: {
424
+ register: (driver) => this.registerDriver(driver)
425
+ },
426
+ ...this.hostContext
427
+ };
428
+ await pluginDef.onEnable(context);
429
+ this.logger.debug("Plugin runtime onEnable completed");
430
+ }
431
+ }
432
+ }
433
+ /**
434
+ * Register a hook
435
+ * @param event The event name (e.g. 'beforeFind', 'afterInsert')
436
+ * @param handler The handler function
437
+ */
438
+ registerHook(event, handler) {
439
+ if (!this.hooks[event]) {
440
+ this.hooks[event] = [];
441
+ }
442
+ this.hooks[event].push(handler);
443
+ this.logger.debug("Registered hook", { event, totalHandlers: this.hooks[event].length });
444
+ }
445
+ async triggerHooks(event, context) {
446
+ const handlers = this.hooks[event] || [];
447
+ if (handlers.length === 0) {
448
+ this.logger.debug("No hooks registered for event", { event });
449
+ return;
450
+ }
451
+ this.logger.debug("Triggering hooks", { event, count: handlers.length });
452
+ for (const handler of handlers) {
453
+ await handler(context);
454
+ }
455
+ }
456
+ /**
457
+ * Register contribution (Manifest)
458
+ */
459
+ registerApp(manifest) {
460
+ const id = manifest.id;
461
+ this.logger.debug("Registering app manifest", { id });
462
+ if (manifest.objects) {
463
+ if (Array.isArray(manifest.objects)) {
464
+ this.logger.debug("Registering objects from manifest (Array)", { id, objectCount: manifest.objects.length });
465
+ for (const objDef of manifest.objects) {
466
+ SchemaRegistry.registerObject(objDef);
467
+ this.logger.debug("Registered Object", { object: objDef.name, from: id });
468
+ }
469
+ } else {
470
+ this.logger.debug("Registering objects from manifest (Map)", { id, objectCount: Object.keys(manifest.objects).length });
471
+ for (const [name, objDef] of Object.entries(manifest.objects)) {
472
+ objDef.name = name;
473
+ SchemaRegistry.registerObject(objDef);
474
+ this.logger.debug("Registered Object", { object: name, from: id });
475
+ }
476
+ }
477
+ }
478
+ if (manifest.contributes?.kinds) {
479
+ this.logger.debug("Registering kinds from manifest", { id, kindCount: manifest.contributes.kinds.length });
480
+ for (const kind of manifest.contributes.kinds) {
481
+ SchemaRegistry.registerKind(kind);
482
+ this.logger.debug("Registered Kind", { kind: kind.name || kind.type, from: id });
483
+ }
484
+ }
485
+ }
486
+ /**
487
+ * Register a new storage driver
488
+ */
489
+ registerDriver(driver, isDefault = false) {
490
+ if (this.drivers.has(driver.name)) {
491
+ this.logger.warn("Driver already registered, skipping", { driverName: driver.name });
492
+ return;
493
+ }
494
+ this.drivers.set(driver.name, driver);
495
+ this.logger.info("Registered driver", {
496
+ driverName: driver.name,
497
+ version: driver.version
498
+ });
499
+ if (isDefault || this.drivers.size === 1) {
500
+ this.defaultDriver = driver.name;
501
+ this.logger.info("Set default driver", { driverName: driver.name });
502
+ }
503
+ }
504
+ /**
505
+ * Helper to get object definition
506
+ */
507
+ getSchema(objectName) {
508
+ return SchemaRegistry.getObject(objectName);
509
+ }
510
+ /**
511
+ * Helper to get the target driver
512
+ */
513
+ getDriver(objectName) {
514
+ const object = SchemaRegistry.getObject(objectName);
515
+ if (object) {
516
+ const datasourceName = object.datasource || "default";
517
+ if (datasourceName === "default") {
518
+ if (this.defaultDriver && this.drivers.has(this.defaultDriver)) {
519
+ return this.drivers.get(this.defaultDriver);
520
+ }
521
+ } else {
522
+ if (this.drivers.has(datasourceName)) {
523
+ return this.drivers.get(datasourceName);
524
+ }
525
+ throw new Error(`[ObjectQL] Datasource '${datasourceName}' configured for object '${objectName}' is not registered.`);
526
+ }
527
+ }
528
+ if (this.defaultDriver) {
529
+ return this.drivers.get(this.defaultDriver);
530
+ }
531
+ throw new Error(`[ObjectQL] No driver available for object '${objectName}'`);
532
+ }
533
+ /**
534
+ * Initialize the engine and all registered drivers
535
+ */
536
+ async init() {
537
+ this.logger.info("Initializing ObjectQL engine", {
538
+ driverCount: this.drivers.size,
539
+ drivers: Array.from(this.drivers.keys())
540
+ });
541
+ for (const [name, driver] of this.drivers) {
542
+ try {
543
+ await driver.connect();
544
+ this.logger.info("Driver connected successfully", { driverName: name });
545
+ } catch (e) {
546
+ this.logger.error("Failed to connect driver", e, { driverName: name });
547
+ }
548
+ }
549
+ this.logger.info("ObjectQL engine initialization complete");
550
+ }
551
+ async destroy() {
552
+ this.logger.info("Destroying ObjectQL engine", { driverCount: this.drivers.size });
553
+ for (const [name, driver] of this.drivers.entries()) {
554
+ try {
555
+ await driver.disconnect();
556
+ } catch (e) {
557
+ this.logger.error("Error disconnecting driver", e, { driverName: name });
558
+ }
559
+ }
560
+ this.logger.info("ObjectQL engine destroyed");
561
+ }
562
+ // ============================================
563
+ // Helper: Query Conversion
564
+ // ============================================
565
+ toQueryAST(object, options) {
566
+ const ast = { object };
567
+ if (!options) return ast;
568
+ if (options.filter) {
569
+ ast.where = options.filter;
570
+ }
571
+ if (options.select) {
572
+ ast.fields = options.select;
573
+ }
574
+ if (options.sort) {
575
+ if (Array.isArray(options.sort)) {
576
+ ast.orderBy = options.sort;
577
+ } else {
578
+ ast.orderBy = Object.entries(options.sort).map(([field, order]) => ({
579
+ field,
580
+ order: order === -1 || order === "desc" ? "desc" : "asc"
581
+ }));
582
+ }
583
+ }
584
+ if (options.top !== void 0) ast.limit = options.top;
585
+ else if (options.limit !== void 0) ast.limit = options.limit;
586
+ if (options.skip !== void 0) ast.offset = options.skip;
587
+ return ast;
588
+ }
589
+ // ============================================
590
+ // Data Access Methods (IDataEngine Interface)
591
+ // ============================================
592
+ async find(object, query) {
593
+ this.logger.debug("Find operation starting", { object, query });
594
+ const driver = this.getDriver(object);
595
+ const ast = this.toQueryAST(object, query);
596
+ const hookContext = {
597
+ object,
598
+ event: "beforeFind",
599
+ input: { ast, options: void 0 },
600
+ // Should map options?
601
+ ql: this
602
+ };
603
+ await this.triggerHooks("beforeFind", hookContext);
604
+ try {
605
+ const result = await driver.find(object, hookContext.input.ast, hookContext.input.options);
606
+ hookContext.event = "afterFind";
607
+ hookContext.result = result;
608
+ await this.triggerHooks("afterFind", hookContext);
609
+ return hookContext.result;
610
+ } catch (e) {
611
+ this.logger.error("Find operation failed", e, { object });
612
+ throw e;
613
+ }
614
+ }
615
+ async findOne(objectName, query) {
616
+ this.logger.debug("FindOne operation", { objectName });
617
+ const driver = this.getDriver(objectName);
618
+ const ast = this.toQueryAST(objectName, query);
619
+ ast.limit = 1;
620
+ return driver.findOne(objectName, ast);
621
+ }
622
+ async insert(object, data, options) {
623
+ this.logger.debug("Insert operation starting", { object, isBatch: Array.isArray(data) });
624
+ const driver = this.getDriver(object);
625
+ const hookContext = {
626
+ object,
627
+ event: "beforeInsert",
628
+ input: { data, options },
629
+ ql: this
630
+ };
631
+ await this.triggerHooks("beforeInsert", hookContext);
632
+ try {
633
+ let result;
634
+ if (Array.isArray(hookContext.input.data)) {
635
+ if (driver.bulkCreate) {
636
+ result = await driver.bulkCreate(object, hookContext.input.data, hookContext.input.options);
637
+ } else {
638
+ result = await Promise.all(hookContext.input.data.map((item) => driver.create(object, item, hookContext.input.options)));
639
+ }
640
+ } else {
641
+ result = await driver.create(object, hookContext.input.data, hookContext.input.options);
642
+ }
643
+ hookContext.event = "afterInsert";
644
+ hookContext.result = result;
645
+ await this.triggerHooks("afterInsert", hookContext);
646
+ return hookContext.result;
647
+ } catch (e) {
648
+ this.logger.error("Insert operation failed", e, { object });
649
+ throw e;
650
+ }
651
+ }
652
+ async update(object, data, options) {
653
+ this.logger.debug("Update operation starting", { object });
654
+ const driver = this.getDriver(object);
655
+ let id = data.id || data._id;
656
+ if (!id && options?.filter) {
657
+ if (typeof options.filter === "string") id = options.filter;
658
+ else if (options.filter._id) id = options.filter._id;
659
+ else if (options.filter.id) id = options.filter.id;
660
+ }
661
+ const hookContext = {
662
+ object,
663
+ event: "beforeUpdate",
664
+ input: { id, data, options },
665
+ ql: this
666
+ };
667
+ await this.triggerHooks("beforeUpdate", hookContext);
668
+ try {
669
+ let result;
670
+ if (hookContext.input.id) {
671
+ result = await driver.update(object, hookContext.input.id, hookContext.input.data, hookContext.input.options);
672
+ } else if (options?.multi && driver.updateMany) {
673
+ const ast = this.toQueryAST(object, { filter: options.filter });
674
+ result = await driver.updateMany(object, ast, hookContext.input.data, hookContext.input.options);
675
+ } else {
676
+ throw new Error("Update requires an ID or options.multi=true");
677
+ }
678
+ hookContext.event = "afterUpdate";
679
+ hookContext.result = result;
680
+ await this.triggerHooks("afterUpdate", hookContext);
681
+ return hookContext.result;
682
+ } catch (e) {
683
+ this.logger.error("Update operation failed", e, { object });
684
+ throw e;
685
+ }
686
+ }
687
+ async delete(object, options) {
688
+ this.logger.debug("Delete operation starting", { object });
689
+ const driver = this.getDriver(object);
690
+ let id = void 0;
691
+ if (options?.filter) {
692
+ if (typeof options.filter === "string") id = options.filter;
693
+ else if (options.filter._id) id = options.filter._id;
694
+ else if (options.filter.id) id = options.filter.id;
695
+ }
696
+ const hookContext = {
697
+ object,
698
+ event: "beforeDelete",
699
+ input: { id, options },
700
+ ql: this
701
+ };
702
+ await this.triggerHooks("beforeDelete", hookContext);
703
+ try {
704
+ let result;
705
+ if (hookContext.input.id) {
706
+ result = await driver.delete(object, hookContext.input.id, hookContext.input.options);
707
+ } else if (options?.multi && driver.deleteMany) {
708
+ const ast = this.toQueryAST(object, { filter: options.filter });
709
+ result = await driver.deleteMany(object, ast, hookContext.input.options);
710
+ } else {
711
+ throw new Error("Delete requires an ID or options.multi=true");
712
+ }
713
+ hookContext.event = "afterDelete";
714
+ hookContext.result = result;
715
+ await this.triggerHooks("afterDelete", hookContext);
716
+ return hookContext.result;
717
+ } catch (e) {
718
+ this.logger.error("Delete operation failed", e, { object });
719
+ throw e;
720
+ }
721
+ }
722
+ async count(object, query) {
723
+ const driver = this.getDriver(object);
724
+ if (driver.count) {
725
+ const ast = this.toQueryAST(object, { filter: query?.filter });
726
+ return driver.count(object, ast);
727
+ }
728
+ const res = await this.find(object, { filter: query?.filter, select: ["_id"] });
729
+ return res.length;
730
+ }
731
+ async aggregate(object, query) {
732
+ const driver = this.getDriver(object);
733
+ this.logger.debug(`Aggregate on ${object} using ${driver.name}`, query);
734
+ throw new Error("Aggregate not yet fully implemented in ObjectQL->Driver mapping");
735
+ }
736
+ async execute(command, options) {
737
+ if (options?.object) {
738
+ const driver = this.getDriver(options.object);
739
+ if (driver.execute) {
740
+ return driver.execute(command, void 0, options);
741
+ }
742
+ }
743
+ throw new Error("Execute requires options.object to select driver");
744
+ }
745
+ };
746
+
747
+ // src/plugin.ts
748
+ var ObjectQLPlugin = class {
749
+ constructor(ql, hostContext) {
750
+ this.name = "com.objectstack.engine.objectql";
751
+ this.type = "objectql";
752
+ this.version = "1.0.0";
753
+ this.init = async (ctx) => {
754
+ if (!this.ql) {
755
+ this.ql = new ObjectQL(this.hostContext);
756
+ }
757
+ ctx.registerService("objectql", this.ql);
758
+ ctx.registerService("metadata", this.ql);
759
+ ctx.registerService("data", this.ql);
760
+ ctx.registerService("auth", this.ql);
761
+ ctx.logger.info("ObjectQL engine registered as service", {
762
+ provides: ["objectql", "metadata", "data", "auth"]
763
+ });
764
+ const protocolShim = new ObjectStackProtocolImplementation(this.ql);
765
+ ctx.registerService("protocol", protocolShim);
766
+ ctx.logger.info("Protocol service registered");
767
+ };
768
+ this.start = async (ctx) => {
769
+ ctx.logger.info("ObjectQL engine initialized");
770
+ if (ctx.getServices && this.ql) {
771
+ const services = ctx.getServices();
772
+ for (const [name, service] of services.entries()) {
773
+ if (name.startsWith("driver.")) {
774
+ this.ql.registerDriver(service);
775
+ ctx.logger.debug("Discovered and registered driver service", { serviceName: name });
776
+ }
777
+ if (name.startsWith("app.")) {
778
+ this.ql.registerApp(service);
779
+ ctx.logger.debug("Discovered and registered app service", { serviceName: name });
780
+ }
781
+ }
782
+ }
783
+ };
784
+ if (ql) {
785
+ this.ql = ql;
786
+ } else {
787
+ this.hostContext = hostContext;
788
+ }
789
+ }
790
+ };
791
+ // Annotate the CommonJS export names for ESM import in node:
792
+ 0 && (module.exports = {
793
+ ObjectQL,
794
+ ObjectQLPlugin,
795
+ ObjectStackProtocolImplementation,
796
+ SchemaRegistry
797
+ });
798
+ //# sourceMappingURL=index.js.map