@objectstack/objectql 2.0.0 → 2.0.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/objectql@2.0.0 build /home/runner/work/spec/spec/packages/objectql
2
+ > @objectstack/objectql@2.0.1 build /home/runner/work/spec/spec/packages/objectql
3
3
  > tsup --config ../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.js 44.79 KB
14
- CJS dist/index.js.map 89.92 KB
15
- CJS ⚡️ Build success in 83ms
16
- ESM dist/index.mjs 43.37 KB
17
- ESM dist/index.mjs.map 89.20 KB
18
- ESM ⚡️ Build success in 84ms
13
+ ESM dist/index.mjs 53.23 KB
14
+ ESM dist/index.mjs.map 109.67 KB
15
+ ESM ⚡️ Build success in 79ms
16
+ CJS dist/index.js 54.65 KB
17
+ CJS dist/index.js.map 110.38 KB
18
+ CJS ⚡️ Build success in 79ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 10718ms
21
- DTS dist/index.d.mts 68.87 KB
22
- DTS dist/index.d.ts 68.87 KB
20
+ DTS ⚡️ Build success in 11177ms
21
+ DTS dist/index.d.mts 71.29 KB
22
+ DTS dist/index.d.ts 71.29 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @objectstack/objectql
2
2
 
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release for maintenance and stability improvements
8
+ - Updated dependencies
9
+ - @objectstack/spec@2.0.1
10
+ - @objectstack/core@2.0.1
11
+ - @objectstack/types@2.0.1
12
+
3
13
  ## 2.0.0
4
14
 
5
15
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1203,7 +1203,6 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1203
1203
  websockets: boolean;
1204
1204
  files: boolean;
1205
1205
  analytics: boolean;
1206
- hub: boolean;
1207
1206
  ai: boolean;
1208
1207
  workflow: boolean;
1209
1208
  notifications: boolean;
@@ -1212,7 +1211,98 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1212
1211
  endpoints: {
1213
1212
  data: string;
1214
1213
  metadata: string;
1215
- auth: string;
1214
+ analytics: string;
1215
+ };
1216
+ services: {
1217
+ metadata: {
1218
+ enabled: boolean;
1219
+ status: "degraded";
1220
+ route: string;
1221
+ provider: string;
1222
+ message: string;
1223
+ };
1224
+ data: {
1225
+ enabled: boolean;
1226
+ status: "available";
1227
+ route: string;
1228
+ provider: string;
1229
+ };
1230
+ analytics: {
1231
+ enabled: boolean;
1232
+ status: "available";
1233
+ route: string;
1234
+ provider: string;
1235
+ };
1236
+ auth: {
1237
+ enabled: boolean;
1238
+ status: "unavailable";
1239
+ message: string;
1240
+ };
1241
+ automation: {
1242
+ enabled: boolean;
1243
+ status: "unavailable";
1244
+ message: string;
1245
+ };
1246
+ cache: {
1247
+ enabled: boolean;
1248
+ status: "unavailable";
1249
+ message: string;
1250
+ };
1251
+ queue: {
1252
+ enabled: boolean;
1253
+ status: "unavailable";
1254
+ message: string;
1255
+ };
1256
+ job: {
1257
+ enabled: boolean;
1258
+ status: "unavailable";
1259
+ message: string;
1260
+ };
1261
+ ui: {
1262
+ enabled: boolean;
1263
+ status: "unavailable";
1264
+ message: string;
1265
+ };
1266
+ workflow: {
1267
+ enabled: boolean;
1268
+ status: "unavailable";
1269
+ message: string;
1270
+ };
1271
+ realtime: {
1272
+ enabled: boolean;
1273
+ status: "unavailable";
1274
+ message: string;
1275
+ };
1276
+ notification: {
1277
+ enabled: boolean;
1278
+ status: "unavailable";
1279
+ message: string;
1280
+ };
1281
+ ai: {
1282
+ enabled: boolean;
1283
+ status: "unavailable";
1284
+ message: string;
1285
+ };
1286
+ i18n: {
1287
+ enabled: boolean;
1288
+ status: "unavailable";
1289
+ message: string;
1290
+ };
1291
+ graphql: {
1292
+ enabled: boolean;
1293
+ status: "unavailable";
1294
+ message: string;
1295
+ };
1296
+ 'file-storage': {
1297
+ enabled: boolean;
1298
+ status: "unavailable";
1299
+ message: string;
1300
+ };
1301
+ search: {
1302
+ enabled: boolean;
1303
+ status: "unavailable";
1304
+ message: string;
1305
+ };
1216
1306
  };
1217
1307
  }>;
1218
1308
  getMetaTypes(): Promise<{
@@ -1319,7 +1409,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1319
1409
  name: string;
1320
1410
  cacheRequest?: MetadataCacheRequest;
1321
1411
  }): Promise<MetadataCacheResponse>;
1322
- batchData(_request: {
1412
+ batchData(request: {
1323
1413
  object: string;
1324
1414
  request: BatchUpdateRequest;
1325
1415
  }): Promise<BatchUpdateResponse>;
@@ -1327,13 +1417,11 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1327
1417
  object: string;
1328
1418
  records: any[];
1329
1419
  }): Promise<any>;
1330
- updateManyData(_request: UpdateManyDataRequest): Promise<any>;
1331
- analyticsQuery(_request: any): Promise<any>;
1332
- getAnalyticsMeta(_request: any): Promise<any>;
1420
+ updateManyData(request: UpdateManyDataRequest): Promise<BatchUpdateResponse>;
1421
+ analyticsQuery(request: any): Promise<any>;
1422
+ getAnalyticsMeta(request: any): Promise<any>;
1423
+ private mapAnalyticsOperator;
1333
1424
  triggerAutomation(_request: any): Promise<any>;
1334
- listSpaces(_request: any): Promise<any>;
1335
- createSpace(_request: any): Promise<any>;
1336
- installPlugin(_request: any): Promise<any>;
1337
1425
  deleteManyData(request: DeleteManyDataRequest): Promise<any>;
1338
1426
  saveMetaItem(request: {
1339
1427
  type: string;
package/dist/index.d.ts CHANGED
@@ -1203,7 +1203,6 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1203
1203
  websockets: boolean;
1204
1204
  files: boolean;
1205
1205
  analytics: boolean;
1206
- hub: boolean;
1207
1206
  ai: boolean;
1208
1207
  workflow: boolean;
1209
1208
  notifications: boolean;
@@ -1212,7 +1211,98 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1212
1211
  endpoints: {
1213
1212
  data: string;
1214
1213
  metadata: string;
1215
- auth: string;
1214
+ analytics: string;
1215
+ };
1216
+ services: {
1217
+ metadata: {
1218
+ enabled: boolean;
1219
+ status: "degraded";
1220
+ route: string;
1221
+ provider: string;
1222
+ message: string;
1223
+ };
1224
+ data: {
1225
+ enabled: boolean;
1226
+ status: "available";
1227
+ route: string;
1228
+ provider: string;
1229
+ };
1230
+ analytics: {
1231
+ enabled: boolean;
1232
+ status: "available";
1233
+ route: string;
1234
+ provider: string;
1235
+ };
1236
+ auth: {
1237
+ enabled: boolean;
1238
+ status: "unavailable";
1239
+ message: string;
1240
+ };
1241
+ automation: {
1242
+ enabled: boolean;
1243
+ status: "unavailable";
1244
+ message: string;
1245
+ };
1246
+ cache: {
1247
+ enabled: boolean;
1248
+ status: "unavailable";
1249
+ message: string;
1250
+ };
1251
+ queue: {
1252
+ enabled: boolean;
1253
+ status: "unavailable";
1254
+ message: string;
1255
+ };
1256
+ job: {
1257
+ enabled: boolean;
1258
+ status: "unavailable";
1259
+ message: string;
1260
+ };
1261
+ ui: {
1262
+ enabled: boolean;
1263
+ status: "unavailable";
1264
+ message: string;
1265
+ };
1266
+ workflow: {
1267
+ enabled: boolean;
1268
+ status: "unavailable";
1269
+ message: string;
1270
+ };
1271
+ realtime: {
1272
+ enabled: boolean;
1273
+ status: "unavailable";
1274
+ message: string;
1275
+ };
1276
+ notification: {
1277
+ enabled: boolean;
1278
+ status: "unavailable";
1279
+ message: string;
1280
+ };
1281
+ ai: {
1282
+ enabled: boolean;
1283
+ status: "unavailable";
1284
+ message: string;
1285
+ };
1286
+ i18n: {
1287
+ enabled: boolean;
1288
+ status: "unavailable";
1289
+ message: string;
1290
+ };
1291
+ graphql: {
1292
+ enabled: boolean;
1293
+ status: "unavailable";
1294
+ message: string;
1295
+ };
1296
+ 'file-storage': {
1297
+ enabled: boolean;
1298
+ status: "unavailable";
1299
+ message: string;
1300
+ };
1301
+ search: {
1302
+ enabled: boolean;
1303
+ status: "unavailable";
1304
+ message: string;
1305
+ };
1216
1306
  };
1217
1307
  }>;
1218
1308
  getMetaTypes(): Promise<{
@@ -1319,7 +1409,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1319
1409
  name: string;
1320
1410
  cacheRequest?: MetadataCacheRequest;
1321
1411
  }): Promise<MetadataCacheResponse>;
1322
- batchData(_request: {
1412
+ batchData(request: {
1323
1413
  object: string;
1324
1414
  request: BatchUpdateRequest;
1325
1415
  }): Promise<BatchUpdateResponse>;
@@ -1327,13 +1417,11 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1327
1417
  object: string;
1328
1418
  records: any[];
1329
1419
  }): Promise<any>;
1330
- updateManyData(_request: UpdateManyDataRequest): Promise<any>;
1331
- analyticsQuery(_request: any): Promise<any>;
1332
- getAnalyticsMeta(_request: any): Promise<any>;
1420
+ updateManyData(request: UpdateManyDataRequest): Promise<BatchUpdateResponse>;
1421
+ analyticsQuery(request: any): Promise<any>;
1422
+ getAnalyticsMeta(request: any): Promise<any>;
1423
+ private mapAnalyticsOperator;
1333
1424
  triggerAutomation(_request: any): Promise<any>;
1334
- listSpaces(_request: any): Promise<any>;
1335
- createSpace(_request: any): Promise<any>;
1336
- installPlugin(_request: any): Promise<any>;
1337
1425
  deleteManyData(request: DeleteManyDataRequest): Promise<any>;
1338
1426
  saveMetaItem(request: {
1339
1427
  type: string;
package/dist/index.js CHANGED
@@ -546,9 +546,8 @@ var ObjectStackProtocolImplementation = class {
546
546
  graphql: false,
547
547
  search: false,
548
548
  websockets: false,
549
- files: true,
550
- analytics: false,
551
- hub: false,
549
+ files: false,
550
+ analytics: true,
552
551
  ai: false,
553
552
  workflow: false,
554
553
  notifications: false,
@@ -557,7 +556,30 @@ var ObjectStackProtocolImplementation = class {
557
556
  endpoints: {
558
557
  data: "/api/data",
559
558
  metadata: "/api/meta",
560
- auth: "/api/auth"
559
+ analytics: "/api/analytics"
560
+ },
561
+ services: {
562
+ // --- Kernel-provided (objectql is an example kernel implementation) ---
563
+ metadata: { enabled: true, status: "degraded", route: "/api/meta", provider: "objectql", message: "In-memory registry only; DB persistence not yet implemented" },
564
+ data: { enabled: true, status: "available", route: "/api/data", provider: "objectql" },
565
+ analytics: { enabled: true, status: "available", route: "/api/analytics", provider: "objectql" },
566
+ // --- Plugin-provided (kernel does NOT handle these) ---
567
+ auth: { enabled: false, status: "unavailable", message: "Install an auth plugin (e.g. plugin-auth) to enable" },
568
+ automation: { enabled: false, status: "unavailable", message: "Install an automation plugin (e.g. plugin-automation) to enable" },
569
+ // --- Core infrastructure (plugin-provided) ---
570
+ cache: { enabled: false, status: "unavailable", message: "Install a cache plugin (e.g. plugin-redis) to enable" },
571
+ queue: { enabled: false, status: "unavailable", message: "Install a queue plugin (e.g. plugin-bullmq) to enable" },
572
+ job: { enabled: false, status: "unavailable", message: "Install a job scheduler plugin to enable" },
573
+ // --- Optional services (all plugin-provided) ---
574
+ ui: { enabled: false, status: "unavailable", message: "Install a UI plugin to enable" },
575
+ workflow: { enabled: false, status: "unavailable", message: "Install a workflow plugin to enable" },
576
+ realtime: { enabled: false, status: "unavailable", message: "Install a realtime plugin to enable" },
577
+ notification: { enabled: false, status: "unavailable", message: "Install a notification plugin to enable" },
578
+ ai: { enabled: false, status: "unavailable", message: "Install an AI plugin to enable" },
579
+ i18n: { enabled: false, status: "unavailable", message: "Install an i18n plugin to enable" },
580
+ graphql: { enabled: false, status: "unavailable", message: "Install a GraphQL plugin to enable" },
581
+ "file-storage": { enabled: false, status: "unavailable", message: "Install a file-storage plugin to enable" },
582
+ search: { enabled: false, status: "unavailable", message: "Install a search plugin to enable" }
561
583
  }
562
584
  };
563
585
  }
@@ -736,8 +758,80 @@ var ObjectStackProtocolImplementation = class {
736
758
  // ==========================================
737
759
  // Batch Operations
738
760
  // ==========================================
739
- async batchData(_request) {
740
- throw new Error("Batch operations not yet fully implemented in protocol adapter");
761
+ async batchData(request) {
762
+ const { object, request: batchReq } = request;
763
+ const { operation, records, options } = batchReq;
764
+ const results = [];
765
+ let succeeded = 0;
766
+ let failed = 0;
767
+ for (const record of records) {
768
+ try {
769
+ switch (operation) {
770
+ case "create": {
771
+ const created = await this.engine.insert(object, record.data || record);
772
+ results.push({ id: created._id || created.id, success: true, record: created });
773
+ succeeded++;
774
+ break;
775
+ }
776
+ case "update": {
777
+ if (!record.id) throw new Error("Record id is required for update");
778
+ const updated = await this.engine.update(object, record.data || {}, { filter: { _id: record.id } });
779
+ results.push({ id: record.id, success: true, record: updated });
780
+ succeeded++;
781
+ break;
782
+ }
783
+ case "upsert": {
784
+ if (record.id) {
785
+ try {
786
+ const existing = await this.engine.findOne(object, { filter: { _id: record.id } });
787
+ if (existing) {
788
+ const updated = await this.engine.update(object, record.data || {}, { filter: { _id: record.id } });
789
+ results.push({ id: record.id, success: true, record: updated });
790
+ } else {
791
+ const created = await this.engine.insert(object, { _id: record.id, ...record.data || {} });
792
+ results.push({ id: created._id || created.id, success: true, record: created });
793
+ }
794
+ } catch {
795
+ const created = await this.engine.insert(object, { _id: record.id, ...record.data || {} });
796
+ results.push({ id: created._id || created.id, success: true, record: created });
797
+ }
798
+ } else {
799
+ const created = await this.engine.insert(object, record.data || record);
800
+ results.push({ id: created._id || created.id, success: true, record: created });
801
+ }
802
+ succeeded++;
803
+ break;
804
+ }
805
+ case "delete": {
806
+ if (!record.id) throw new Error("Record id is required for delete");
807
+ await this.engine.delete(object, { filter: { _id: record.id } });
808
+ results.push({ id: record.id, success: true });
809
+ succeeded++;
810
+ break;
811
+ }
812
+ default:
813
+ results.push({ id: record.id, success: false, error: `Unknown operation: ${operation}` });
814
+ failed++;
815
+ }
816
+ } catch (err) {
817
+ results.push({ id: record.id, success: false, error: err.message });
818
+ failed++;
819
+ if (options?.atomic) {
820
+ break;
821
+ }
822
+ if (!options?.continueOnError) {
823
+ break;
824
+ }
825
+ }
826
+ }
827
+ return {
828
+ success: failed === 0,
829
+ operation,
830
+ total: records.length,
831
+ succeeded,
832
+ failed,
833
+ results: options?.returnRecords !== false ? results : results.map((r) => ({ id: r.id, success: r.success, error: r.error }))
834
+ };
741
835
  }
742
836
  async createManyData(request) {
743
837
  const records = await this.engine.insert(request.object, request.records);
@@ -747,26 +841,174 @@ var ObjectStackProtocolImplementation = class {
747
841
  count: records.length
748
842
  };
749
843
  }
750
- async updateManyData(_request) {
751
- throw new Error("updateManyData not implemented");
752
- }
753
- async analyticsQuery(_request) {
754
- throw new Error("analyticsQuery not implemented");
755
- }
756
- async getAnalyticsMeta(_request) {
757
- throw new Error("getAnalyticsMeta not implemented");
844
+ async updateManyData(request) {
845
+ const { object, records, options } = request;
846
+ const results = [];
847
+ let succeeded = 0;
848
+ let failed = 0;
849
+ for (const record of records) {
850
+ try {
851
+ const updated = await this.engine.update(object, record.data, { filter: { _id: record.id } });
852
+ results.push({ id: record.id, success: true, record: updated });
853
+ succeeded++;
854
+ } catch (err) {
855
+ results.push({ id: record.id, success: false, error: err.message });
856
+ failed++;
857
+ if (!options?.continueOnError) {
858
+ break;
859
+ }
860
+ }
861
+ }
862
+ return {
863
+ success: failed === 0,
864
+ operation: "update",
865
+ total: records.length,
866
+ succeeded,
867
+ failed,
868
+ results
869
+ };
758
870
  }
759
- async triggerAutomation(_request) {
760
- throw new Error("triggerAutomation not implemented");
871
+ async analyticsQuery(request) {
872
+ const { query, cube } = request;
873
+ const object = cube;
874
+ const groupBy = query.dimensions || [];
875
+ const aggregations = [];
876
+ if (query.measures) {
877
+ for (const measure of query.measures) {
878
+ if (measure === "count" || measure === "count_all") {
879
+ aggregations.push({ field: "*", method: "count", alias: "count" });
880
+ } else if (measure.includes(".")) {
881
+ const [field, method] = measure.split(".");
882
+ aggregations.push({ field, method, alias: `${field}_${method}` });
883
+ } else {
884
+ aggregations.push({ field: measure, method: "sum", alias: measure });
885
+ }
886
+ }
887
+ }
888
+ let filter = void 0;
889
+ if (query.filters && query.filters.length > 0) {
890
+ const conditions = query.filters.map((f) => {
891
+ const op = this.mapAnalyticsOperator(f.operator);
892
+ if (f.values && f.values.length === 1) {
893
+ return { [f.member]: { [op]: f.values[0] } };
894
+ } else if (f.values && f.values.length > 1) {
895
+ return { [f.member]: { $in: f.values } };
896
+ }
897
+ return { [f.member]: { [op]: true } };
898
+ });
899
+ filter = conditions.length === 1 ? conditions[0] : { $and: conditions };
900
+ }
901
+ const rows = await this.engine.aggregate(object, {
902
+ filter,
903
+ groupBy: groupBy.length > 0 ? groupBy : void 0,
904
+ aggregations: aggregations.length > 0 ? aggregations.map((a) => ({ field: a.field, method: a.method, alias: a.alias })) : [{ field: "*", method: "count", alias: "count" }]
905
+ });
906
+ const fields = [
907
+ ...groupBy.map((d) => ({ name: d, type: "string" })),
908
+ ...aggregations.map((a) => ({ name: a.alias, type: "number" }))
909
+ ];
910
+ return {
911
+ success: true,
912
+ data: {
913
+ rows,
914
+ fields
915
+ }
916
+ };
761
917
  }
762
- async listSpaces(_request) {
763
- throw new Error("listSpaces not implemented");
918
+ async getAnalyticsMeta(request) {
919
+ const objects = SchemaRegistry.listItems("object");
920
+ const cubeFilter = request?.cube;
921
+ const cubes = [];
922
+ for (const obj of objects) {
923
+ const schema = obj;
924
+ if (cubeFilter && schema.name !== cubeFilter) continue;
925
+ const measures = {};
926
+ const dimensions = {};
927
+ const fields = schema.fields || {};
928
+ measures["count"] = {
929
+ name: "count",
930
+ label: "Count",
931
+ type: "count",
932
+ sql: "*"
933
+ };
934
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
935
+ const fd = fieldDef;
936
+ const fieldType = fd.type || "text";
937
+ if (["number", "currency", "percent"].includes(fieldType)) {
938
+ measures[`${fieldName}_sum`] = {
939
+ name: `${fieldName}_sum`,
940
+ label: `${fd.label || fieldName} (Sum)`,
941
+ type: "sum",
942
+ sql: fieldName
943
+ };
944
+ measures[`${fieldName}_avg`] = {
945
+ name: `${fieldName}_avg`,
946
+ label: `${fd.label || fieldName} (Avg)`,
947
+ type: "avg",
948
+ sql: fieldName
949
+ };
950
+ dimensions[fieldName] = {
951
+ name: fieldName,
952
+ label: fd.label || fieldName,
953
+ type: "number",
954
+ sql: fieldName
955
+ };
956
+ } else if (["date", "datetime"].includes(fieldType)) {
957
+ dimensions[fieldName] = {
958
+ name: fieldName,
959
+ label: fd.label || fieldName,
960
+ type: "time",
961
+ sql: fieldName,
962
+ granularities: ["day", "week", "month", "quarter", "year"]
963
+ };
964
+ } else if (["boolean"].includes(fieldType)) {
965
+ dimensions[fieldName] = {
966
+ name: fieldName,
967
+ label: fd.label || fieldName,
968
+ type: "boolean",
969
+ sql: fieldName
970
+ };
971
+ } else {
972
+ dimensions[fieldName] = {
973
+ name: fieldName,
974
+ label: fd.label || fieldName,
975
+ type: "string",
976
+ sql: fieldName
977
+ };
978
+ }
979
+ }
980
+ cubes.push({
981
+ name: schema.name,
982
+ title: schema.label || schema.name,
983
+ description: schema.description,
984
+ sql: schema.name,
985
+ measures,
986
+ dimensions,
987
+ public: true
988
+ });
989
+ }
990
+ return {
991
+ success: true,
992
+ data: { cubes }
993
+ };
764
994
  }
765
- async createSpace(_request) {
766
- throw new Error("createSpace not implemented");
995
+ mapAnalyticsOperator(op) {
996
+ const map = {
997
+ equals: "$eq",
998
+ notEquals: "$ne",
999
+ contains: "$contains",
1000
+ notContains: "$notContains",
1001
+ gt: "$gt",
1002
+ gte: "$gte",
1003
+ lt: "$lt",
1004
+ lte: "$lte",
1005
+ set: "$ne",
1006
+ notSet: "$eq"
1007
+ };
1008
+ return map[op] || "$eq";
767
1009
  }
768
- async installPlugin(_request) {
769
- throw new Error("installPlugin not implemented");
1010
+ async triggerAutomation(_request) {
1011
+ throw new Error('triggerAutomation requires plugin-automation service. Install and register a plugin that provides the "automation" service.');
770
1012
  }
771
1013
  async deleteManyData(request) {
772
1014
  return this.engine.delete(request.object, {
@@ -1277,7 +1519,17 @@ var ObjectQL = class {
1277
1519
  object = this.resolveObjectName(object);
1278
1520
  const driver = this.getDriver(object);
1279
1521
  this.logger.debug(`Aggregate on ${object} using ${driver.name}`, query);
1280
- throw new Error("Aggregate not yet fully implemented in ObjectQL->Driver mapping");
1522
+ const ast = {
1523
+ object,
1524
+ where: query.filter,
1525
+ groupBy: query.groupBy,
1526
+ aggregations: query.aggregations?.map((agg) => ({
1527
+ function: agg.method,
1528
+ field: agg.field,
1529
+ alias: agg.alias || `${agg.method}_${agg.field || "all"}`
1530
+ }))
1531
+ };
1532
+ return driver.find(object, ast);
1281
1533
  }
1282
1534
  async execute(command, options) {
1283
1535
  if (options?.object) {