remult 3.4.0-next.5 → 3.4.0-next.7

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/internals.d.ts CHANGED
@@ -14,6 +14,8 @@ export declare const actionInfo: {
14
14
  };
15
15
  export { isOfType } from './src/isOfType.js';
16
16
  export type { ClassType } from './classType.js';
17
+ export type { SubscriptionServerRouteApi, SubscriptionServerRouteHandlerArgs, } from './server/remult-api-server.js';
18
+ export type { DataApiResponse } from './src/data-api.js';
17
19
  export { flags } from './src/remult3/remult3.js';
18
20
  export { SseSubscriptionClient } from './src/live-query/SseSubscriptionClient.js';
19
21
  export { DataProviderPromiseWrapper } from './src/data-interfaces.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.4.0-next.5",
3
+ "version": "3.4.0-next.7",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
package/remult-mongo.js CHANGED
@@ -228,6 +228,8 @@ var MongoEntityDataProvider = /** @class */ (function () {
228
228
  try {
229
229
  for (var _b = tslib_1.__values(this.entity.fields), _c = _b.next(); !_c.done; _c = _b.next()) {
230
230
  var col = _c.value;
231
+ if (col.dbReadOnly || col.isServerExpression)
232
+ continue;
231
233
  var val = toDb(col, row[col.key]);
232
234
  if (val === null)
233
235
  val = NULL;
@@ -494,7 +496,7 @@ var MongoEntityDataProvider = /** @class */ (function () {
494
496
  f_1 = _c.value;
495
497
  if (!f_1.dbReadOnly && !f_1.isServerExpression) {
496
498
  if (keys.includes(f_1.key)) {
497
- newR[f_1.key] = toDb(f_1, data[f_1.key]);
499
+ newR[e.$dbNameOf(f_1)] = toDb(f_1, data[f_1.key]);
498
500
  }
499
501
  }
500
502
  }
@@ -797,27 +799,32 @@ var FilterConsumerBridgeToMongo = /** @class */ (function () {
797
799
  this.add(col, val, '$lt');
798
800
  };
799
801
  FilterConsumerBridgeToMongo.prototype.containsCaseInsensitive = function (col, val) {
800
- this.add(col, val, '$regex', { $options: 'i' });
802
+ this.addRegex(col, val);
801
803
  };
802
804
  FilterConsumerBridgeToMongo.prototype.notContainsCaseInsensitive = function (col, val) {
803
- var _this = this;
804
- this.result.push(function () {
805
- var _a;
806
- return (_a = {},
807
- _a[_this.nameProvider.$dbNameOf(col)] = {
808
- $not: {
809
- $regex: isNull(val) ? val : toDb(col, val),
810
- $options: 'i',
811
- },
812
- },
813
- _a);
814
- });
805
+ this.addRegex(col, val, '', '', true);
815
806
  };
816
807
  FilterConsumerBridgeToMongo.prototype.startsWithCaseInsensitive = function (col, val) {
817
- this.add(col, "^".concat(val), '$regex', { $options: 'i' });
808
+ this.addRegex(col, val, '^');
818
809
  };
819
810
  FilterConsumerBridgeToMongo.prototype.endsWithCaseInsensitive = function (col, val) {
820
- this.add(col, "".concat(val, "$"), '$regex', { $options: 'i' });
811
+ this.addRegex(col, val, '', '$');
812
+ };
813
+ FilterConsumerBridgeToMongo.prototype.addRegex = function (col, val, start, end, negate) {
814
+ var _this = this;
815
+ if (start === void 0) { start = ''; }
816
+ if (end === void 0) { end = ''; }
817
+ if (negate === void 0) { negate = false; }
818
+ this.result.push(function () {
819
+ var _a;
820
+ var regex = {
821
+ $regex: start + toDb(col, val).replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + end,
822
+ $options: 'i',
823
+ };
824
+ return _a = {},
825
+ _a[_this.nameProvider.$dbNameOf(col)] = negate ? { $not: regex } : regex,
826
+ _a;
827
+ });
821
828
  };
822
829
  FilterConsumerBridgeToMongo.prototype.add = function (col, val, operator, moreOptions) {
823
830
  var _this = this;
@@ -1,6 +1,7 @@
1
1
  import type { ClassType } from '../classType.js';
2
2
  import type { Allowed, UserInfo } from '../src/context.js';
3
3
  import { Remult } from '../src/context.js';
4
+ import type { DataApiResponse } from '../src/data-api.js';
4
5
  import type { DataProvider } from '../src/data-interfaces.js';
5
6
  import type { LiveQueryStorage, SubscriptionServer } from '../src/live-query/SubscriptionServer.js';
6
7
  import { IdEntity } from '../src/remult3/IdEntity.js';
@@ -167,6 +168,17 @@ export interface GenericResponse {
167
168
  status(statusCode: number): GenericResponse;
168
169
  end(): void;
169
170
  }
171
+ export interface SubscriptionServerRouteApi {
172
+ rootPath: string;
173
+ addRoute(relativePath: string, method: 'get' | 'post', handler: (args: SubscriptionServerRouteHandlerArgs) => Promise<void>): void;
174
+ }
175
+ export interface SubscriptionServerRouteHandlerArgs {
176
+ remult: Remult;
177
+ res: DataApiResponse;
178
+ req: GenericRequestInfo;
179
+ origRes: GenericResponse;
180
+ getBody(): Promise<any>;
181
+ }
170
182
  export interface queuedJobInfo {
171
183
  info: queuedJobInfoResponse;
172
184
  userId: string;
@@ -441,45 +441,25 @@ var RemultServerImplementation = /** @class */ (function () {
441
441
  r.route(this.options.rootPath + '/me').get(this.process(function (remult, req, res) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var _a; return tslib_1.__generator(this, function (_b) {
442
442
  return [2 /*return*/, res.success((_a = remult.user) !== null && _a !== void 0 ? _a : null)];
443
443
  }); }); }));
444
- if (this.options.subscriptionServer instanceof SseSubscriptionServer_js_1.SseSubscriptionServer) {
445
- var streamPath = this.options.rootPath + '/' + SubscriptionChannel_js_1.streamUrl;
446
- r.route(streamPath).get(this.process(function (remult, req, res, origReq, origRes) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
447
- return tslib_1.__generator(this, function (_a) {
448
- ;
449
- remult.subscriptionServer.openHttpServerStream(origReq, origRes);
450
- return [2 /*return*/];
451
- });
452
- }); }));
453
- r.route(streamPath + '/subscribe').post(this.process(function (remult, _2, res, _, _1, origReq) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
454
- var body, _a, _b;
455
- return tslib_1.__generator(this, function (_c) {
456
- switch (_c.label) {
457
- case 0:
458
- _b = (_a = remult.subscriptionServer).subscribeToChannel;
459
- return [4 /*yield*/, this.coreOptions.getRequestBody(origReq)];
460
- case 1:
461
- body = _b.apply(_a, [_c.sent(), res,
462
- remult]);
463
- return [2 /*return*/];
464
- }
465
- });
466
- }); }));
467
- r.route(streamPath + '/unsubscribe').post(this.process(function (remult, req, res, reqInfo, origRes, origReq) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
468
- var _a, _b;
469
- return tslib_1.__generator(this, function (_c) {
470
- switch (_c.label) {
471
- case 0:
472
- ;
473
- _b = (_a = remult.subscriptionServer).subscribeToChannel;
474
- return [4 /*yield*/, this.coreOptions.getRequestBody(origReq)];
475
- case 1:
476
- _b.apply(_a, [_c.sent(), res,
477
- remult,
478
- true]);
479
- return [2 /*return*/];
480
- }
481
- });
482
- }); }));
444
+ var subscriptionServer = this.options.subscriptionServer;
445
+ if (subscriptionServer === null || subscriptionServer === void 0 ? void 0 : subscriptionServer.initApiServer) {
446
+ subscriptionServer.initApiServer({
447
+ rootPath: this.options.rootPath,
448
+ addRoute: function (relativePath, method, handler) {
449
+ r.route(_this.options.rootPath + relativePath)[method](_this.process(function (remult, _, res, req, origRes, origReq) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
450
+ var _this = this;
451
+ return tslib_1.__generator(this, function (_a) {
452
+ return [2 /*return*/, handler({
453
+ remult: remult,
454
+ res: res,
455
+ req: req,
456
+ origRes: origRes,
457
+ getBody: function () { return _this.coreOptions.getRequestBody(origReq); },
458
+ })];
459
+ });
460
+ }); }));
461
+ },
462
+ });
483
463
  }
484
464
  r.route(this.options.rootPath + '/' + SubscriptionChannel_js_1.liveQueryKeepAliveRoute).post(this.process(function (remult, req, res, reqInfo, origRes, origReq) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
485
465
  var body, queryIds, result;
@@ -58,7 +58,11 @@ export interface FieldOptions<entityType = unknown, valueType = unknown> {
58
58
  * @type {AllowedForInstance<entityType>}
59
59
  */
60
60
  allowApiUpdate?: AllowedForInstance<entityType>;
61
- /** An arrow function that'll be used to perform validations on it
61
+ /** An arrow function that'll be used to perform validations on it.
62
+ *
63
+ * `validate` from several option objects is additive: each one is appended.
64
+ * A function option is the only way to replace validators already set, including a built-in one:
65
+ * `(options) => { options.validate = ... }`.
62
66
  * @example
63
67
  * @Fields.string({
64
68
  * validate: Validators.required
package/src/data-api.d.ts CHANGED
@@ -5,13 +5,10 @@ export declare class DataApi<T = unknown> {
5
5
  private repository;
6
6
  private remult;
7
7
  constructor(repository: Repository<T>, remult: Remult);
8
- httpGet(res: DataApiResponse, req: DataApiRequest, serializeContext: () => Promise<any>): void | Promise<void>;
8
+ httpGet(res: DataApiResponse, req: DataApiRequest, serializeContext: () => Promise<any>): Promise<void>;
9
9
  httpPost(res: DataApiResponse, req: DataApiRequest, body: any, serializeContext: () => Promise<any>): Promise<void>;
10
10
  upsertMany(response: DataApiResponse, request: DataApiRequest, body: any): Promise<void>;
11
- query(response: DataApiResponse, request: DataApiRequest, body: any): Promise<{
12
- items: any[];
13
- aggregates: import("./remult3/remult3.js").GroupByResult<T, any[], any[], any[], any[], any[], any[]>;
14
- } | undefined>;
11
+ query(response: DataApiResponse, request: DataApiRequest, body: any): Promise<void>;
15
12
  static defaultGetLimit: number;
16
13
  get(response: DataApiResponse, id: any): Promise<void>;
17
14
  count(response: DataApiResponse, request: DataApiRequest, body?: any): Promise<void>;
package/src/data-api.js CHANGED
@@ -17,25 +17,39 @@ var DataApi = /** @class */ (function () {
17
17
  this.remult = remult;
18
18
  }
19
19
  DataApi.prototype.httpGet = function (res, req, serializeContext) {
20
- try {
21
- var action = req === null || req === void 0 ? void 0 : req.get('__action');
22
- if (action === null || action === void 0 ? void 0 : action.startsWith(rest_data_provider_js_1.liveQueryAction))
23
- return this.liveQuery(res, req, undefined, serializeContext, action.substring(rest_data_provider_js_1.liveQueryAction.length));
24
- switch (action) {
25
- case 'get':
26
- case 'count':
27
- return this.count(res, req, undefined);
28
- case 'groupBy':
29
- return res.success(this.groupBy(req, undefined));
30
- }
31
- return this.getArray(res, req, undefined);
32
- }
33
- catch (err) {
34
- if (err.isForbiddenError)
35
- res.forbidden(err);
36
- else
37
- res.error(err, this.repository.metadata);
38
- }
20
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
21
+ var action, _a, _b, _c, err_1;
22
+ return tslib_1.__generator(this, function (_d) {
23
+ switch (_d.label) {
24
+ case 0:
25
+ _d.trys.push([0, 5, , 6]);
26
+ action = req === null || req === void 0 ? void 0 : req.get('__action');
27
+ if (action === null || action === void 0 ? void 0 : action.startsWith(rest_data_provider_js_1.liveQueryAction))
28
+ return [2 /*return*/, this.liveQuery(res, req, undefined, serializeContext, action.substring(rest_data_provider_js_1.liveQueryAction.length))];
29
+ _a = action;
30
+ switch (_a) {
31
+ case 'get': return [3 /*break*/, 1];
32
+ case 'count': return [3 /*break*/, 1];
33
+ case 'groupBy': return [3 /*break*/, 2];
34
+ }
35
+ return [3 /*break*/, 4];
36
+ case 1: return [2 /*return*/, this.count(res, req, undefined)];
37
+ case 2:
38
+ _c = (_b = res).success;
39
+ return [4 /*yield*/, this.groupBy(req, undefined)];
40
+ case 3: return [2 /*return*/, _c.apply(_b, [_d.sent()])];
41
+ case 4: return [2 /*return*/, this.getArray(res, req, undefined)];
42
+ case 5:
43
+ err_1 = _d.sent();
44
+ if (err_1.isForbiddenError)
45
+ res.forbidden(err_1.message);
46
+ else
47
+ res.error(err_1, this.repository.metadata);
48
+ return [3 /*break*/, 6];
49
+ case 6: return [2 /*return*/];
50
+ }
51
+ });
52
+ });
39
53
  };
40
54
  DataApi.prototype.httpPost = function (res, req, body, serializeContext) {
41
55
  return tslib_1.__awaiter(this, void 0, void 0, function () {
@@ -47,14 +61,14 @@ var DataApi = /** @class */ (function () {
47
61
  };
48
62
  }
49
63
  }
50
- var action, _a, _b, _c, _d, _e, _f, _g, err_1;
51
- return tslib_1.__generator(this, function (_h) {
52
- switch (_h.label) {
64
+ var action, _a, _b, _c, _d, _e, err_2;
65
+ return tslib_1.__generator(this, function (_f) {
66
+ switch (_f.label) {
53
67
  case 0:
54
68
  action = req === null || req === void 0 ? void 0 : req.get('__action');
55
- _h.label = 1;
69
+ _f.label = 1;
56
70
  case 1:
57
- _h.trys.push([1, 16, , 17]);
71
+ _f.trys.push([1, 15, , 16]);
58
72
  if (action === null || action === void 0 ? void 0 : action.startsWith(rest_data_provider_js_1.liveQueryAction)) {
59
73
  validateWhereInBody();
60
74
  return [2 /*return*/, this.liveQuery(res, req, body, serializeContext, action.substring(rest_data_provider_js_1.liveQueryAction.length))];
@@ -70,7 +84,7 @@ var DataApi = /** @class */ (function () {
70
84
  case 'endLiveQuery': return [3 /*break*/, 9];
71
85
  case 'query': return [3 /*break*/, 11];
72
86
  }
73
- return [3 /*break*/, 13];
87
+ return [3 /*break*/, 12];
74
88
  case 2:
75
89
  validateWhereInBody();
76
90
  return [2 /*return*/, this.getArray(res, req, body)];
@@ -80,7 +94,7 @@ var DataApi = /** @class */ (function () {
80
94
  case 4:
81
95
  _c = (_b = res).success;
82
96
  return [4 /*yield*/, this.groupBy(req, body)];
83
- case 5: return [2 /*return*/, _c.apply(_b, [_h.sent()])];
97
+ case 5: return [2 /*return*/, _c.apply(_b, [_f.sent()])];
84
98
  case 6:
85
99
  validateWhereInBody();
86
100
  return [2 /*return*/, this.deleteMany(res, req, body)];
@@ -90,26 +104,23 @@ var DataApi = /** @class */ (function () {
90
104
  case 8: return [2 /*return*/, this.upsertMany(res, req, body)];
91
105
  case 9: return [4 /*yield*/, this.remult.liveQueryStorage.remove(body.id)];
92
106
  case 10:
93
- _h.sent();
107
+ _f.sent();
94
108
  res.success('ok');
95
109
  return [2 /*return*/];
96
- case 11:
97
- _e = (_d = res).success;
98
- return [4 /*yield*/, this.query(res, req, body)];
99
- case 12: return [2 /*return*/, _e.apply(_d, [_h.sent()])];
100
- case 13:
101
- _g = (_f = res).created;
110
+ case 11: return [2 /*return*/, this.query(res, req, body)];
111
+ case 12:
112
+ _e = (_d = res).created;
102
113
  return [4 /*yield*/, this.post(body, req)];
103
- case 14: return [2 /*return*/, _g.apply(_f, [_h.sent()])];
104
- case 15: return [3 /*break*/, 17];
105
- case 16:
106
- err_1 = _h.sent();
107
- if (err_1.isForbiddenError)
108
- res.forbidden(err_1.message);
114
+ case 13: return [2 /*return*/, _e.apply(_d, [_f.sent()])];
115
+ case 14: return [3 /*break*/, 16];
116
+ case 15:
117
+ err_2 = _f.sent();
118
+ if (err_2.isForbiddenError)
119
+ res.forbidden(err_2.message);
109
120
  else
110
- res.error(err_1, this.repository.metadata);
111
- return [3 /*break*/, 17];
112
- case 17: return [2 /*return*/];
121
+ res.error(err_2, this.repository.metadata);
122
+ return [3 /*break*/, 16];
123
+ case 16: return [2 /*return*/];
113
124
  }
114
125
  });
115
126
  });
@@ -187,7 +198,7 @@ var DataApi = /** @class */ (function () {
187
198
  };
188
199
  DataApi.prototype.query = function (response, request, body) {
189
200
  return tslib_1.__awaiter(this, void 0, void 0, function () {
190
- var aggregate, rest, aggregateRequest, _a, r, _b, aggregates, err_2;
201
+ var aggregate, rest, aggregateRequest, _a, r, _b, aggregates, err_3;
191
202
  return tslib_1.__generator(this, function (_c) {
192
203
  switch (_c.label) {
193
204
  case 0:
@@ -215,16 +226,16 @@ var DataApi = /** @class */ (function () {
215
226
  ])];
216
227
  case 2:
217
228
  _a = tslib_1.__read.apply(void 0, [_c.sent(), 2]), r = _a[0].r, _b = tslib_1.__read(_a[1], 1), aggregates = _b[0];
218
- return [2 /*return*/, {
229
+ return [2 /*return*/, response.success({
219
230
  items: r,
220
231
  aggregates: aggregates,
221
- }];
232
+ })];
222
233
  case 3:
223
- err_2 = _c.sent();
224
- if (err_2.isForbiddenError)
234
+ err_3 = _c.sent();
235
+ if (err_3.isForbiddenError)
225
236
  response.forbidden();
226
237
  else
227
- response.error(err_2, this.repository.metadata);
238
+ response.error(err_3, this.repository.metadata);
228
239
  return [3 /*break*/, 4];
229
240
  case 4: return [2 /*return*/];
230
241
  }
@@ -253,7 +264,7 @@ var DataApi = /** @class */ (function () {
253
264
  };
254
265
  DataApi.prototype.count = function (response, request, body) {
255
266
  return tslib_1.__awaiter(this, void 0, void 0, function () {
256
- var _a, _b, _c, _d, err_3;
267
+ var _a, _b, _c, _d, err_4;
257
268
  var _e;
258
269
  return tslib_1.__generator(this, function (_f) {
259
270
  switch (_f.label) {
@@ -275,8 +286,8 @@ var DataApi = /** @class */ (function () {
275
286
  _e)]);
276
287
  return [3 /*break*/, 5];
277
288
  case 4:
278
- err_3 = _f.sent();
279
- response.error(err_3, this.repository.metadata);
289
+ err_4 = _f.sent();
290
+ response.error(err_4, this.repository.metadata);
280
291
  return [3 /*break*/, 5];
281
292
  case 5: return [2 /*return*/];
282
293
  }
@@ -285,7 +296,7 @@ var DataApi = /** @class */ (function () {
285
296
  };
286
297
  DataApi.prototype.deleteMany = function (response, request, body) {
287
298
  return tslib_1.__awaiter(this, void 0, void 0, function () {
288
- var deleted_1, where_1, err_4;
299
+ var deleted_1, where_1, err_5;
289
300
  var _this = this;
290
301
  return tslib_1.__generator(this, function (_a) {
291
302
  switch (_a.label) {
@@ -347,8 +358,8 @@ var DataApi = /** @class */ (function () {
347
358
  }); })];
348
359
  case 2: return [2 /*return*/, _a.sent()];
349
360
  case 3:
350
- err_4 = _a.sent();
351
- response.error(err_4, this.repository.metadata);
361
+ err_5 = _a.sent();
362
+ response.error(err_5, this.repository.metadata);
352
363
  return [3 /*break*/, 4];
353
364
  case 4: return [2 /*return*/];
354
365
  }
@@ -635,7 +646,7 @@ var DataApi = /** @class */ (function () {
635
646
  };
636
647
  DataApi.prototype.getArray = function (response, request, body) {
637
648
  return tslib_1.__awaiter(this, void 0, void 0, function () {
638
- var r, err_5;
649
+ var r, err_6;
639
650
  return tslib_1.__generator(this, function (_a) {
640
651
  switch (_a.label) {
641
652
  case 0:
@@ -652,11 +663,11 @@ var DataApi = /** @class */ (function () {
652
663
  response.success(r);
653
664
  return [3 /*break*/, 4];
654
665
  case 3:
655
- err_5 = _a.sent();
656
- if (err_5.isForbiddenError)
657
- response.forbidden(err_5.message);
666
+ err_6 = _a.sent();
667
+ if (err_6.isForbiddenError)
668
+ response.forbidden(err_6.message);
658
669
  else
659
- response.error(err_5, this.repository.metadata);
670
+ response.error(err_6, this.repository.metadata);
660
671
  return [3 /*break*/, 4];
661
672
  case 4: return [2 /*return*/];
662
673
  }
@@ -665,7 +676,7 @@ var DataApi = /** @class */ (function () {
665
676
  };
666
677
  DataApi.prototype.liveQuery = function (response, request, body, serializeContext, queryChannel) {
667
678
  return tslib_1.__awaiter(this, void 0, void 0, function () {
668
- var r, data, err_6;
679
+ var r, data, err_7;
669
680
  var _a;
670
681
  var _this = this;
671
682
  return tslib_1.__generator(this, function (_b) {
@@ -699,11 +710,11 @@ var DataApi = /** @class */ (function () {
699
710
  response.success(r.r);
700
711
  return [3 /*break*/, 6];
701
712
  case 5:
702
- err_6 = _b.sent();
703
- if (err_6.isForbiddenError)
713
+ err_7 = _b.sent();
714
+ if (err_7.isForbiddenError)
704
715
  response.forbidden();
705
716
  else
706
- response.error(err_6, this.repository.metadata);
717
+ response.error(err_7, this.repository.metadata);
707
718
  return [3 /*break*/, 6];
708
719
  case 6: return [2 /*return*/];
709
720
  }
@@ -766,7 +777,7 @@ var DataApi = /** @class */ (function () {
766
777
  };
767
778
  DataApi.prototype.doOnId = function (response, id, what) {
768
779
  return tslib_1.__awaiter(this, void 0, void 0, function () {
769
- var where, _a, _b, err_7;
780
+ var where, _a, _b, err_8;
770
781
  var _this = this;
771
782
  return tslib_1.__generator(this, function (_c) {
772
783
  switch (_c.label) {
@@ -816,8 +827,8 @@ var DataApi = /** @class */ (function () {
816
827
  _c.sent();
817
828
  return [3 /*break*/, 6];
818
829
  case 5:
819
- err_7 = _c.sent();
820
- response.error(err_7, this.repository.metadata);
830
+ err_8 = _c.sent();
831
+ response.error(err_8, this.repository.metadata);
821
832
  return [3 /*break*/, 6];
822
833
  case 6: return [2 /*return*/];
823
834
  }
@@ -841,7 +852,7 @@ var DataApi = /** @class */ (function () {
841
852
  };
842
853
  DataApi.prototype.updateManyImplementation = function (response, request, body) {
843
854
  return tslib_1.__awaiter(this, void 0, void 0, function () {
844
- var where_2, err_8;
855
+ var where_2, err_9;
845
856
  var _this = this;
846
857
  return tslib_1.__generator(this, function (_a) {
847
858
  switch (_a.label) {
@@ -905,8 +916,8 @@ var DataApi = /** @class */ (function () {
905
916
  }); })];
906
917
  case 2: return [2 /*return*/, _a.sent()];
907
918
  case 3:
908
- err_8 = _a.sent();
909
- response.error(err_8, this.repository.metadata);
919
+ err_9 = _a.sent();
920
+ response.error(err_9, this.repository.metadata);
910
921
  return [3 /*break*/, 4];
911
922
  case 4: return [2 /*return*/];
912
923
  }
@@ -1,7 +1,8 @@
1
- import type { DataProvider, EntityDataProvider, EntityDataProviderGroupByOptions } from '../data-interfaces.js';
1
+ import type { DataProvider, EntityDataProvider, EntityDataProviderGroupByOptions, EntityDataProviderFindOptions } from '../data-interfaces.js';
2
2
  import type { HasWrapIdentifier, SqlCommand, SqlCommandFactory, SqlCommandWithParameters, SqlImplementation, SqlResult } from '../sql-command.js';
3
+ import type { FieldMetadata } from '../column-interfaces.js';
3
4
  import type { CustomSqlFilterBuilderFunction, EntityDbNamesBase } from '../filter/filter-consumer-bridge-to-sql-request.js';
4
- import { type EntityFilter, type EntityMetadata } from '../remult3/remult3.js';
5
+ import { type EntityFilter, type EntityMetadata, type FindOptions, type GroupByOptions, type GroupByResult, type MembersOnly, type NumericKeys } from '../remult3/remult3.js';
5
6
  import type { RepositoryOverloads } from '../remult3/RepositoryImplementation.js';
6
7
  import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from '../../migrations/migration-types.js';
7
8
  /**
@@ -67,6 +68,24 @@ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, Can
67
68
 
68
69
  */
69
70
  static filterToRaw<entityType>(repo: RepositoryOverloads<entityType>, condition: EntityFilter<entityType>, sqlCommand?: SqlCommandWithParameters, dbNames?: EntityDbNamesBase, wrapIdentifier?: (name: string) => string): Promise<string>;
71
+ /**
72
+ * Builds the select that `repo.groupBy` would run, without running it - for a statement remult cannot run on its own, such as one `UNION ALL` over several databases.
73
+ * @returns `sql`, and `toResult`, which maps a result row (read by column alias) to the shape `groupBy` returns.
74
+ * @see [Building a select with remult](https://remult.dev/docs/running-sql-on-the-server#building-a-select-with-remult)
75
+ */
76
+ static groupByToRaw<entityType, groupByFields extends (keyof MembersOnly<entityType>)[] | undefined = undefined, sumFields extends NumericKeys<entityType>[] | undefined = undefined, averageFields extends NumericKeys<entityType>[] | undefined = undefined, minFields extends (keyof MembersOnly<entityType>)[] | undefined = undefined, maxFields extends (keyof MembersOnly<entityType>)[] | undefined = undefined, distinctCountFields extends (keyof MembersOnly<entityType>)[] | undefined = undefined>(repo: RepositoryOverloads<entityType>, options: GroupByOptions<entityType, groupByFields extends undefined ? never : groupByFields, sumFields extends undefined ? never : sumFields, averageFields extends undefined ? never : averageFields, minFields extends undefined ? never : minFields, maxFields extends undefined ? never : maxFields, distinctCountFields extends undefined ? never : distinctCountFields>, sql?: RawSelectOptions): Promise<{
77
+ sql: string;
78
+ toResult: (row: Record<string, any>) => GroupByResult<entityType, groupByFields extends undefined ? never : groupByFields, sumFields extends undefined ? never : sumFields, averageFields extends undefined ? never : averageFields, minFields extends undefined ? never : minFields, maxFields extends undefined ? never : maxFields, distinctCountFields extends undefined ? never : distinctCountFields>;
79
+ }>;
80
+ /**
81
+ * Builds the select that `repo.find` would run, without running it. Columns are aliased by field key; no default order is added, so the sql can be a branch of a `UNION`.
82
+ * @returns `sql`, and `toResult`, which maps a result row (read by column alias) to the fields' values.
83
+ * @see [Building a select with remult](https://remult.dev/docs/running-sql-on-the-server#building-a-select-with-remult)
84
+ */
85
+ static selectToRaw<entityType>(repo: RepositoryOverloads<entityType>, options?: FindOptions<entityType>, sql?: RawSelectOptions): Promise<{
86
+ sql: string;
87
+ toResult: (row: Record<string, any>) => Partial<MembersOnly<entityType>>;
88
+ }>;
70
89
  /**
71
90
  * `false` _(default)_ - No logging
72
91
  *
@@ -94,5 +113,30 @@ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, Can
94
113
  private createdEntities;
95
114
  end: () => Promise<void>;
96
115
  }
116
+ /** Where a raw select is built: the command that collects its parameters, the names it addresses the table and columns by, and the dialect's paging syntax. */
117
+ export interface RawSelectOptions {
118
+ sqlCommand?: SqlCommandWithParameters;
119
+ /** Replace `$entityName` here to address another database or an alias: `{ ...await dbNamesOf(Task), $entityName: '[other].dbo.tasks' }`. */
120
+ dbNames?: EntityDbNamesBase;
121
+ wrapIdentifier?: (name: string) => string;
122
+ /** Required only with `limit`: `(limit, offset) => 'limit 10 offset 20'`. */
123
+ limitSyntax?: (limit: number, offset: number) => string;
124
+ }
97
125
  export declare function getRowAfterUpdate<entityType>(meta: EntityMetadata<entityType>, dataProvider: EntityDataProvider, data: any, id: any, operation: string): any;
126
+ /** The select that `find` runs, and the mapping of one result row to field values. With `aliasColumns`, every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
127
+ export declare function buildFindSql(entity: EntityMetadata, options: EntityDataProviderFindOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullsFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, { aliasColumns, defaultOrderBy }?: {
128
+ aliasColumns?: boolean | undefined;
129
+ defaultOrderBy?: boolean | undefined;
130
+ }): Promise<{
131
+ select: string;
132
+ colKeys: FieldMetadata<unknown, unknown>[];
133
+ /** `valueAt` reads one column of a result row, by its position in the select or by its alias (the field key). */
134
+ toResult(valueAt: (indexInSelect: number, alias: string) => any): any;
135
+ }>;
98
136
  export declare function groupByImpl(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommand, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<any[]>;
137
+ /** The select that `groupBy` runs, and the mapping of one result row back to a `GroupByResult`. Every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
138
+ export declare function buildGroupBySql(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<{
139
+ select: string;
140
+ /** `valueAt` reads one column of a result row, by its position in the select or by its alias. */
141
+ toResult(valueAt: (indexInSelect: number, alias: string) => any): any;
142
+ }>;