remult 3.3.0-next.1 → 3.3.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.
Files changed (38) hide show
  1. package/esm/graphql.js +1 -1
  2. package/esm/remult-knex/index.js +10 -3
  3. package/esm/remult-mongo.js +7 -3
  4. package/esm/server/get-remult-admin-html.js +125 -111
  5. package/esm/server/remult-admin.js +2 -0
  6. package/esm/server/remult-api-server.js +1 -1
  7. package/esm/src/data-api.js +30 -19
  8. package/esm/src/data-providers/rest-data-provider.js +48 -37
  9. package/esm/src/data-providers/sql-database.js +14 -3
  10. package/esm/src/live-query/SubscriptionChannel.js +52 -4
  11. package/esm/src/remult-proxy.js +4 -4
  12. package/esm/src/remult3/RepositoryImplementation.js +53 -25
  13. package/esm/src/server-action.js +2 -2
  14. package/esm/src/valueConverters.js +9 -5
  15. package/graphql.js +1 -1
  16. package/index.d.ts +1 -1
  17. package/package.json +1 -1
  18. package/remult-knex/index.js +18 -9
  19. package/remult-mongo.js +6 -2
  20. package/server/get-remult-admin-html.js +1 -1
  21. package/server/remult-admin.d.ts +2 -0
  22. package/server/remult-admin.js +2 -1
  23. package/server/remult-api-server.js +1 -0
  24. package/src/data-api.d.ts +4 -4
  25. package/src/data-api.js +50 -30
  26. package/src/data-interfaces.d.ts +6 -6
  27. package/src/data-providers/rest-data-provider.d.ts +4 -4
  28. package/src/data-providers/rest-data-provider.js +48 -37
  29. package/src/data-providers/sql-database.js +14 -3
  30. package/src/live-query/SubscriptionChannel.d.ts +52 -4
  31. package/src/live-query/SubscriptionChannel.js +52 -4
  32. package/src/remult-proxy.js +4 -4
  33. package/src/remult3/RepositoryImplementation.d.ts +11 -10
  34. package/src/remult3/RepositoryImplementation.js +237 -177
  35. package/src/remult3/remult3.d.ts +25 -23
  36. package/src/server-action.js +2 -2
  37. package/src/valueConverters.d.ts +3 -1
  38. package/src/valueConverters.js +9 -6
@@ -51,6 +51,7 @@ export function buildEntityInfo(options) {
51
51
  if (relRepo.metadata.apiReadAllowed) {
52
52
  relation = {
53
53
  ...relInfo,
54
+ allowNull: x.allowNull,
54
55
  where,
55
56
  entityKey: relRepo.metadata.key,
56
57
  idField,
@@ -81,6 +82,7 @@ export function buildEntityInfo(options) {
81
82
  caption: x.label,
82
83
  relationToOne: relation,
83
84
  inputType: x.inputType,
85
+ allowNull: x.allowNull,
84
86
  type: x.inputType === InputTypes.json
85
87
  ? 'json'
86
88
  : x.valueType === Number
@@ -316,7 +316,7 @@ export class RemultServerImplementation {
316
316
  //@ts-ignore
317
317
  .get(this.process(async (c, req, res, orig) => dataApiFactory(c).get(res, orig.params.id)))
318
318
  //@ts-ignore
319
- .put(this.process(async (c, req, res, reqInfo, _, orig) => dataApiFactory(c).put(res, reqInfo.params.id, await this.coreOptions.getRequestBody(orig))))
319
+ .put(this.process(async (c, req, res, reqInfo, _, orig) => dataApiFactory(c).put(res, req, reqInfo.params.id, await this.coreOptions.getRequestBody(orig))))
320
320
  //@ts-ignore
321
321
  .delete(this.process(async (c, req, res, orig) => dataApiFactory(c).delete(res, orig.params.id)));
322
322
  }
@@ -72,7 +72,7 @@ export class DataApi {
72
72
  case 'query':
73
73
  return res.success(await this.query(res, req, body));
74
74
  default:
75
- return res.created(await this.post(body));
75
+ return res.created(await this.post(body, req));
76
76
  }
77
77
  }
78
78
  catch (err) {
@@ -93,11 +93,11 @@ export class DataApi {
93
93
  include: this.includeNone(),
94
94
  });
95
95
  if (r.length == 0) {
96
- result.push(await this.post({ ...item.where, ...item.set }));
96
+ result.push(await this.post({ ...item.where, ...item.set }, request));
97
97
  }
98
98
  else {
99
99
  if (item.set !== undefined) {
100
- result.push((await this.actualUpdate(r[0], item.set)).toApiJson());
100
+ result.push(await this.actualUpdate(r[0], item.set, request));
101
101
  }
102
102
  else
103
103
  result.push(this.repository.getEntityRef(r[0]).toApiJson());
@@ -154,8 +154,7 @@ export class DataApi {
154
154
  async deleteMany(response, request, body) {
155
155
  try {
156
156
  let deleted = 0;
157
- let where = await this.buildWhere(request, body);
158
- Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
157
+ let where = await this.prepareWhereForManyOperation(body, request);
159
158
  return await doTransaction(this.remult, async () => {
160
159
  for await (const x of this.repository.query({
161
160
  where,
@@ -172,6 +171,14 @@ export class DataApi {
172
171
  response.error(err, this.repository.metadata);
173
172
  }
174
173
  }
174
+ async prepareWhereForManyOperation(body, request) {
175
+ let where = undefined;
176
+ if (body?.where !== 'all') {
177
+ where = await this.buildWhere(request, body);
178
+ Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
179
+ }
180
+ return where;
181
+ }
175
182
  async groupBy(request, body) {
176
183
  if (!this.repository.metadata.apiReadAllowed) {
177
184
  throw new ForbiddenError();
@@ -412,7 +419,7 @@ export class DataApi {
412
419
  async updateManyThroughPutRequest(response, request, body) {
413
420
  const action = request?.get('__action');
414
421
  if (action == 'emptyId') {
415
- return this.put(response, '', body);
422
+ return this.put(response, request, '', body);
416
423
  }
417
424
  return this.updateManyImplementation(response, request, {
418
425
  where: undefined,
@@ -421,8 +428,7 @@ export class DataApi {
421
428
  }
422
429
  async updateManyImplementation(response, request, body) {
423
430
  try {
424
- let where = await this.buildWhere(request, body);
425
- Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
431
+ let where = await this.prepareWhereForManyOperation(body, request);
426
432
  return await doTransaction(this.remult, async () => {
427
433
  let updated = 0;
428
434
  for await (const x of this.repository.query({
@@ -430,7 +436,7 @@ export class DataApi {
430
436
  include: this.includeNone(),
431
437
  aggregate: undefined,
432
438
  })) {
433
- await this.actualUpdate(x, body.set);
439
+ await this.actualUpdate(x, body.set, request);
434
440
  updated++;
435
441
  }
436
442
  response.success({ updated });
@@ -440,20 +446,22 @@ export class DataApi {
440
446
  response.error(err, this.repository.metadata);
441
447
  }
442
448
  }
443
- async actualUpdate(row, body) {
449
+ async actualUpdate(row, body, req, doNotSelect) {
444
450
  let ref = this.repository.getEntityRef(row);
445
451
  await ref._updateEntityBasedOnApi(body);
446
452
  if (!ref.apiUpdateAllowed) {
447
453
  throw new ForbiddenError();
448
454
  }
449
- await ref.save();
450
- return ref;
455
+ let options = req.get('_select') === 'none' || doNotSelect
456
+ ? { select: 'none' }
457
+ : undefined;
458
+ await ref.save(options);
459
+ if (options?.select === 'none')
460
+ return undefined;
461
+ return ref.toApiJson();
451
462
  }
452
- async put(response, id, body) {
453
- await this.doOnId(response, id, async (row) => {
454
- const ref = await this.actualUpdate(row, body);
455
- response.success(ref.toApiJson());
456
- });
463
+ async put(response, req, id, body) {
464
+ await this.doOnId(response, id, async (row) => response.success(await this.actualUpdate(row, body, req)));
457
465
  }
458
466
  async actualDelete(row) {
459
467
  if (!this.repository.getEntityRef(row).apiDeleteAllowed) {
@@ -467,14 +475,17 @@ export class DataApi {
467
475
  response.deleted();
468
476
  });
469
477
  }
470
- async post(body) {
478
+ async post(body, req) {
479
+ let options = req.get('_select') === 'none' ? { select: 'none' } : undefined;
471
480
  const insert = async (what) => {
472
481
  let newr = this.repository.create();
473
482
  await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(what);
474
483
  if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
475
484
  throw new ForbiddenError();
476
485
  }
477
- await this.repository.getEntityRef(newr).save();
486
+ await this.repository.getEntityRef(newr).save(options);
487
+ if (options?.select === 'none')
488
+ return undefined;
478
489
  return this.repository.getEntityRef(newr).toApiJson();
479
490
  };
480
491
  if (Array.isArray(body)) {
@@ -199,37 +199,42 @@ export class RestEntityDataProvider {
199
199
  let url = new UrlBuilder(this.url());
200
200
  let filterObject;
201
201
  if (options) {
202
- if (options.where) {
203
- filterObject = options.where.toJson(); // options.where.__applyToConsumer(new FilterConsumnerBridgeToUrlBuilder(url));
204
- if (options.args === undefined)
205
- if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
206
- filterObject = undefined;
202
+ if (options.where === 'all') {
203
+ filterObject = 'all';
207
204
  }
208
- if (options.orderBy && options.orderBy.Segments) {
209
- let sort = '';
210
- let order = '';
211
- let hasDescending = false;
212
- options.orderBy.Segments.forEach((c) => {
213
- if (sort.length > 0) {
214
- sort += ',';
215
- order += ',';
216
- }
217
- sort += c.field.key;
218
- order += c.isDescending ? 'desc' : 'asc';
219
- if (c.isDescending)
220
- hasDescending = true;
221
- });
222
- if (sort)
223
- url.add('_sort', sort);
224
- if (hasDescending)
225
- url.add('_order', order);
226
- }
227
- if (options.limit)
228
- url.add('_limit', options.limit);
229
- if (options.page)
230
- url.add('_page', options.page);
231
- if (options.select) {
232
- url.add('_select', options.select.join(','));
205
+ else {
206
+ if (options.where) {
207
+ filterObject = options.where.toJson();
208
+ if (options.args === undefined)
209
+ if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
210
+ filterObject = undefined;
211
+ }
212
+ if (options.orderBy && options.orderBy.Segments) {
213
+ let sort = '';
214
+ let order = '';
215
+ let hasDescending = false;
216
+ options.orderBy.Segments.forEach((c) => {
217
+ if (sort.length > 0) {
218
+ sort += ',';
219
+ order += ',';
220
+ }
221
+ sort += c.field.key;
222
+ order += c.isDescending ? 'desc' : 'asc';
223
+ if (c.isDescending)
224
+ hasDescending = true;
225
+ });
226
+ if (sort)
227
+ url.add('_sort', sort);
228
+ if (hasDescending)
229
+ url.add('_order', order);
230
+ }
231
+ if (options.limit)
232
+ url.add('_limit', options.limit);
233
+ if (options.page)
234
+ url.add('_page', options.page);
235
+ if (options.select) {
236
+ url.add('_select', options.select.join(','));
237
+ }
233
238
  }
234
239
  }
235
240
  const run = (action, body) => {
@@ -246,7 +251,7 @@ export class RestEntityDataProvider {
246
251
  else
247
252
  body = { ...body, where: filterObject };
248
253
  }
249
- if (options?.args)
254
+ if (options && 'args' in options)
250
255
  body.args = options.args;
251
256
  if (body && method != 'put')
252
257
  return this.http().post(u.url, body);
@@ -269,10 +274,16 @@ export class RestEntityDataProvider {
269
274
  },
270
275
  };
271
276
  }
272
- update(id, data) {
277
+ update(id, data, options) {
278
+ const urlArgs = [];
279
+ if (id == '')
280
+ urlArgs.push('__action=emptyId');
281
+ if (options?.select === 'none')
282
+ urlArgs.push('_select=$none');
273
283
  return this.http()
274
284
  .put(this.url() +
275
- (id != '' ? '/' + encodeURIComponent(id) : '?__action=emptyId'), this.toJsonOfIncludedKeys(data))
285
+ (id != '' ? '/' + encodeURIComponent(id) : '') +
286
+ (urlArgs.length > 0 ? '?' + urlArgs.join('&') : ''), this.toJsonOfIncludedKeys(data))
276
287
  .then((y) => this.translateFromJson(y));
277
288
  }
278
289
  toJsonOfIncludedKeys(data) {
@@ -290,14 +301,14 @@ export class RestEntityDataProvider {
290
301
  else
291
302
  return this.http().delete(this.url() + '/' + encodeURIComponent(id));
292
303
  }
293
- insert(data) {
304
+ insert(data, options) {
294
305
  return this.http()
295
- .post(this.url(), this.translateToJson(data))
306
+ .post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), this.translateToJson(data))
296
307
  .then((y) => this.translateFromJson(y));
297
308
  }
298
- insertMany(data) {
309
+ insertMany(data, options) {
299
310
  return this.http()
300
- .post(this.url(), data.map((data) => this.translateToJson(data)))
311
+ .post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), data.map((data) => this.translateToJson(data)))
301
312
  .then((y) => y.map((y) => this.translateFromJson(y)));
302
313
  }
303
314
  }
@@ -392,7 +392,7 @@ class ActualSQLEntityDataProvider {
392
392
  }
393
393
  return { colKeys, select };
394
394
  }
395
- async update(id, data) {
395
+ async update(id, data, options) {
396
396
  let e = await this.init();
397
397
  let r = this.sql.createCommand();
398
398
  let statement = 'update ' + e.$entityName + ' set ';
@@ -419,6 +419,8 @@ class ActualSQLEntityDataProvider {
419
419
  let returning = true;
420
420
  if (this.sql._getSourceSql().doesNotSupportReturningSyntax)
421
421
  returning = false;
422
+ if (options?.select === 'none')
423
+ returning = false;
422
424
  if (returning &&
423
425
  this.sql._getSourceSql().doesNotSupportReturningSyntaxOnlyForUpdate)
424
426
  returning = false;
@@ -427,6 +429,8 @@ class ActualSQLEntityDataProvider {
427
429
  return r.execute(statement).then((sqlResult) => {
428
430
  this.sql._getSourceSql().afterMutation?.();
429
431
  if (!returning) {
432
+ if (options?.select === 'none')
433
+ return undefined;
430
434
  return getRowAfterUpdate(this.entity, this, data, id, 'update');
431
435
  }
432
436
  if (sqlResult.rows.length != 1)
@@ -448,7 +452,7 @@ class ActualSQLEntityDataProvider {
448
452
  this.sql._getSourceSql().afterMutation?.();
449
453
  });
450
454
  }
451
- async insert(data) {
455
+ async insert(data, options) {
452
456
  let e = await this.init();
453
457
  let r = this.sql.createCommand();
454
458
  let cols = '';
@@ -473,7 +477,8 @@ class ActualSQLEntityDataProvider {
473
477
  }
474
478
  let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
475
479
  let { colKeys, select } = await this.buildSelect(e, r, undefined, undefined);
476
- if (!this.sql._getSourceSql().doesNotSupportReturningSyntax)
480
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax &&
481
+ !(options?.select === 'none'))
477
482
  statement += ' returning ' + select;
478
483
  return await r.execute(statement).then((sql) => {
479
484
  this.sql._getSourceSql().afterMutation?.();
@@ -483,14 +488,20 @@ class ActualSQLEntityDataProvider {
483
488
  if (typeof id !== 'number')
484
489
  throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
485
490
  JSON.stringify(id));
491
+ if (options?.select === 'none')
492
+ return undefined;
486
493
  return this.find({
487
494
  where: new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, id)),
488
495
  }).then((r) => r[0]);
489
496
  }
490
497
  else {
498
+ if (options?.select === 'none')
499
+ return undefined;
491
500
  return getRowAfterUpdate(this.entity, this, data, undefined, 'insert');
492
501
  }
493
502
  }
503
+ if (options?.select === 'none')
504
+ return undefined;
494
505
  return this.buildResultRow(colKeys, sql.rows[0], sql);
495
506
  });
496
507
  }
@@ -106,18 +106,66 @@ export const liveQueryKeepAliveRoute = '_liveQueryKeepAlive';
106
106
  *
107
107
  * @template messageType The type of the message that the channel will handle.
108
108
  * @example
109
+ * ```ts
109
110
  * // Defined in code that is shared between the frontend and the backend
110
- * const statusChange = new SubscriptionChannel<{ oldStatus: number, newStatus: number }>("statusChange");
111
+ * export const notif = new SubscriptionChannel<{ coolInfo: string }>("notif");
111
112
  *
112
113
  * // Backend: Publishing a message
113
- * statusChange.publish({ oldStatus: 1, newStatus: 2 });
114
+ * notif.publish({ coolInfo: "Message coming from the backend" });
114
115
  *
115
116
  * // Frontend: Subscribing to messages
116
- * statusChange.subscribe((message) => {
117
- * console.log(`Status changed from ${message.oldStatus} to ${message.newStatus}`);
117
+ * notif.subscribe((message) => {
118
+ * console.log(`Something exciting happened: ${message.coolInfo}`);
118
119
  * });
119
120
  *
120
121
  * // Note: If you want to publish from the frontend, use a BackendMethod for that.
122
+ * ```
123
+ * ---
124
+ *
125
+ * ### Use cases:
126
+ *
127
+ * #### A user _(Paul)_ wants to notify everyone _(Eric & Simona)_
128
+ * ```ts
129
+ * // shared code between the frontend and the backend
130
+ * export const notif = new SubscriptionChannel<{ from: UserInfo, msg: string }>("notif");
131
+ *
132
+ * // Frontend, Paul can call a BackendMethod to send his message
133
+ * NotificationContoller.send("Hello everyone!")
134
+ *
135
+ * // Backend
136
+ * export class NotificationContoller {
137
+ * @BackendMethod({ allowed: true })
138
+ * static async send(message: string) {
139
+ * notif.publish({ from: remult.user, msg: message });
140
+ * }
141
+ * }
142
+ *
143
+ * // Frontend: Eric & Simona can subscribe to messages
144
+ * notif.subscribe((message) => {
145
+ * if (message.from === remult.user) return;
146
+ * console.log(`Message from ${message.from.name}: ${message.msg}`);
147
+ * });
148
+ * ```
149
+ *
150
+ * #### A complexe chart that needs to be updated on multiple changes
151
+ * ```ts
152
+ * // shared code between the frontend and the backend
153
+ * export const chart = new SubscriptionChannel<{ kind: "pie" | "bar" | "line", data: number[] }>("chart");
154
+ *
155
+ * // Entity
156
+ * @Entity<Task>("tasks", {
157
+ * saved: async (e) => {
158
+ * if (e.completed) chart.publish({ kind: "pie", data: [1, 2, 3] });
159
+ * }
160
+ * })
161
+ * export class Task { ... }
162
+ *
163
+ * // Frontend: in the chart component, we can subscribe to messages
164
+ * chart.subscribe((message) => {
165
+ * chartData = message.data;
166
+ * });
167
+ * ```
168
+ *
121
169
  */
122
170
  export class SubscriptionChannel {
123
171
  channelKey;
@@ -156,10 +156,10 @@ export class RemultProxy {
156
156
  .remultFactory()
157
157
  .repo(...args)
158
158
  .getEntityRef(...args2),
159
- insert: (args2) => self
159
+ insert: (args2, args3) => self
160
160
  .remultFactory()
161
161
  .repo(...args)
162
- .insert(args2),
162
+ .insert(args2, args3),
163
163
  liveQuery: (...args2) => self
164
164
  .remultFactory()
165
165
  .repo(...args)
@@ -179,10 +179,10 @@ export class RemultProxy {
179
179
  .remultFactory()
180
180
  .repo(...args)
181
181
  .upsert(args2),
182
- update: (a, b) => self
182
+ update: (a, b, c) => self
183
183
  .remultFactory()
184
184
  .repo(...args)
185
- .update(a, b),
185
+ .update(a, b, c),
186
186
  };
187
187
  entityCache.set(args[1], result);
188
188
  return result;
@@ -317,7 +317,7 @@ export class RepositoryImplementation {
317
317
  for (const field of this.fields) {
318
318
  }
319
319
  }
320
- async insert(entity) {
320
+ async insert(entity, options) {
321
321
  if (Array.isArray(entity)) {
322
322
  if (this._dataProvider.isProxy) {
323
323
  let refs = [];
@@ -335,27 +335,36 @@ export class RepositoryImplementation {
335
335
  refs.push(ref);
336
336
  raw.push(await ref.buildDtoForInsert());
337
337
  }
338
- return promiseAll(await this._edp.insertMany(raw), (item, i) => refs[i].processInsertResponseDto(item));
338
+ const inserted = await this._edp.insertMany(raw, options);
339
+ if (options?.select === 'none')
340
+ return undefined;
341
+ return promiseAll(inserted, (item, i) => refs[i].processInsertResponseDto(item));
339
342
  }
340
343
  else {
341
344
  let r = [];
342
345
  for (const item of entity) {
343
- r.push(await this.insert(item));
346
+ r.push(await this.insert(item, options));
344
347
  }
348
+ if (options?.select === 'none')
349
+ return undefined;
345
350
  return r;
346
351
  }
347
352
  }
348
353
  else {
349
354
  let ref = getEntityRef(entity, false);
355
+ let result = undefined;
350
356
  if (ref) {
351
357
  if (!ref.isNew())
352
358
  throw 'Item is not new';
353
- return await ref.save();
359
+ result = await ref.save(options);
354
360
  }
355
361
  else {
356
362
  this.__cleanupPartialObject(entity);
357
- return await this.getEntityRef(this.create(entity)).save();
363
+ result = await this.getEntityRef(this.create(entity)).save(options);
358
364
  }
365
+ if (options?.select === 'none')
366
+ return undefined;
367
+ return result;
359
368
  }
360
369
  }
361
370
  get fields() {
@@ -399,29 +408,36 @@ export class RepositoryImplementation {
399
408
  this.__cleanupPartialObject(set);
400
409
  Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
401
410
  if (this._dataProvider.isProxy) {
402
- return this._edp.updateMany(await this._translateWhereToFilter(where), this.__createDto({ ...set }));
411
+ return this._edp.updateMany(where === 'all' ? 'all' : await this._translateWhereToFilter(where), this.__createDto({ ...set }));
403
412
  }
404
413
  else {
405
414
  let updated = 0;
415
+ if (where === 'all')
416
+ where = undefined;
406
417
  for await (const item of this.query({ where, aggregate: undefined })) {
407
418
  assign(item, set);
408
- await getEntityRef(item).save();
419
+ await getEntityRef(item).save({ select: 'none' });
409
420
  updated++;
410
421
  }
411
422
  return updated;
412
423
  }
413
424
  }
414
- async update(id, entity) {
425
+ async update(id, entity, options) {
426
+ function returnResult(result) {
427
+ if (options?.select === 'none')
428
+ return undefined;
429
+ return result;
430
+ }
415
431
  {
416
432
  let ref = getEntityRef(entity, false);
417
433
  if (ref)
418
- return (await ref.save());
434
+ return returnResult((await ref.save(options)));
419
435
  }
420
436
  {
421
437
  let ref = getEntityRef(id, false);
422
438
  if (ref) {
423
439
  assign(id, entity);
424
- return ref.save();
440
+ return returnResult(await ref.save(options));
425
441
  }
426
442
  }
427
443
  this.__cleanupPartialObject(entity);
@@ -433,7 +449,7 @@ export class RepositoryImplementation {
433
449
  else
434
450
  ref = this._getRefForExistingRow(entity, id);
435
451
  if (this._dataProvider.isProxy) {
436
- return await ref.save(Object.keys(entity));
452
+ return returnResult(await ref.save(options, Object.keys(entity)));
437
453
  }
438
454
  else {
439
455
  const r = await ref.reload();
@@ -450,7 +466,7 @@ export class RepositoryImplementation {
450
466
  }
451
467
  }
452
468
  await this._fixTypes(r);
453
- return await ref.save();
469
+ return returnResult(await ref.save(options));
454
470
  }
455
471
  }
456
472
  async upsert(options) {
@@ -518,22 +534,22 @@ export class RepositoryImplementation {
518
534
  }
519
535
  return ref;
520
536
  }
521
- async save(entity) {
537
+ async save(entity, options) {
522
538
  if (Array.isArray(entity)) {
523
539
  return promiseAll(entity, (x) => this.save(x));
524
540
  }
525
541
  else {
526
542
  let ref = getEntityRef(entity, false);
527
543
  if (ref)
528
- return await ref.save();
544
+ return await ref.save(options);
529
545
  else if (entity instanceof EntityBase) {
530
- return await this.getEntityRef(entity).save();
546
+ return await this.getEntityRef(entity).save(options);
531
547
  }
532
548
  else {
533
549
  let id = this.metadata.idMetadata.getId(entity);
534
550
  if (id === undefined)
535
- return this.insert(entity);
536
- return this.update(id, entity);
551
+ return this.insert(entity, options);
552
+ return this.update(id, entity, options);
537
553
  }
538
554
  }
539
555
  }
@@ -855,10 +871,12 @@ export class RepositoryImplementation {
855
871
  async deleteMany({ where, }) {
856
872
  Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
857
873
  if (this._dataProvider.isProxy) {
858
- return this._edp.deleteMany(await this._translateWhereToFilter(where));
874
+ return this._edp.deleteMany(where === 'all' ? 'all' : await this._translateWhereToFilter(where));
859
875
  }
860
876
  else {
861
877
  let deleted = 0;
878
+ if (where === 'all')
879
+ where = undefined;
862
880
  for await (const item of this.query({ where, aggregate: undefined })) {
863
881
  await getEntityRef(item).delete();
864
882
  deleted++;
@@ -1374,6 +1392,15 @@ export class rowHelperImplementation extends rowHelperBase {
1374
1392
  repo;
1375
1393
  edp;
1376
1394
  _isNew;
1395
+ async _updateResultsFromServerAction(rowInfo) {
1396
+ await this._updateEntityBasedOnApi(rowInfo.data, true);
1397
+ if (this._isNew != rowInfo.isNewRow) {
1398
+ this._isNew = rowInfo.isNewRow;
1399
+ this.originalId = rowInfo.id;
1400
+ }
1401
+ if (!rowInfo.wasChanged)
1402
+ this.originalValues = this.copyDataToObject();
1403
+ }
1377
1404
  constructor(info, instance, repo, edp, remult, _isNew, excludeRelationMembers) {
1378
1405
  super(info.fieldsMetadata, instance, remult, _isNew, excludeRelationMembers);
1379
1406
  this.info = info;
@@ -1474,7 +1501,7 @@ export class rowHelperImplementation extends rowHelperBase {
1474
1501
  return this._columns;
1475
1502
  }
1476
1503
  _saving = false;
1477
- async save(onlyTheseFieldsSentOnlyInTheCaseOfProxySaveWithPartialObject) {
1504
+ async save(options, onlyTheseFieldsSentOnlyInTheCaseOfProxySaveWithPartialObject) {
1478
1505
  try {
1479
1506
  if (this._saving)
1480
1507
  throw new Error('cannot save while entity is already saving');
@@ -1517,13 +1544,13 @@ export class rowHelperImplementation extends rowHelperBase {
1517
1544
  try {
1518
1545
  this._subscribers?.reportChanged();
1519
1546
  if (this.isNew()) {
1520
- if (doNotSave) {
1547
+ if (doNotSave && options?.select !== 'none') {
1521
1548
  updatedRow = (updatedRow = await this.edp.find({
1522
1549
  where: await this.getIdFilter(),
1523
1550
  }))[0];
1524
1551
  }
1525
1552
  else
1526
- updatedRow = await this.edp.insert(d);
1553
+ updatedRow = await this.edp.insert(d, options);
1527
1554
  }
1528
1555
  else {
1529
1556
  let changesOnly = {};
@@ -1542,12 +1569,13 @@ export class rowHelperImplementation extends rowHelperBase {
1542
1569
  if (!wasChanged)
1543
1570
  return this.instance;
1544
1571
  if (doNotSave) {
1545
- updatedRow = (await this.edp.find({ where: await this.getIdFilter() }))[0];
1572
+ if (options?.select !== 'none')
1573
+ updatedRow = (await this.edp.find({ where: await this.getIdFilter() }))[0];
1546
1574
  }
1547
1575
  else {
1548
1576
  if (this.id === undefined)
1549
1577
  throw new Error('Invalid operation, id is undefined');
1550
- updatedRow = await this.edp.update(this.id, changesOnly);
1578
+ updatedRow = await this.edp.update(this.id, changesOnly, options);
1551
1579
  }
1552
1580
  }
1553
1581
  if (updatedRow)
@@ -2500,8 +2528,8 @@ export class EntityBase {
2500
2528
  get _() {
2501
2529
  return getEntityRef(this);
2502
2530
  }
2503
- save() {
2504
- return getEntityRef(this).save();
2531
+ save(options) {
2532
+ return getEntityRef(this).save(options);
2505
2533
  }
2506
2534
  assign(values) {
2507
2535
  assign(this, values);