remult 0.17.3 → 0.18.0

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 (163) hide show
  1. package/SseSubscriptionServer.d.ts +23 -0
  2. package/SseSubscriptionServer.js +143 -0
  3. package/ably.d.ts +13 -0
  4. package/ably.js +59 -0
  5. package/esm/SseSubscriptionServer.d.ts +23 -0
  6. package/esm/SseSubscriptionServer.js +102 -0
  7. package/esm/ably.d.ts +13 -0
  8. package/esm/ably.js +28 -0
  9. package/esm/index.d.ts +7 -4
  10. package/esm/index.js +4 -2
  11. package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
  12. package/esm/live-query/data-provider-live-query-storage.js +78 -0
  13. package/esm/myLog.d.ts +1 -0
  14. package/esm/myLog.js +5 -0
  15. package/esm/postgres/postgres-data-provider.d.ts +4 -2
  16. package/esm/postgres/postgres-data-provider.js +18 -6
  17. package/esm/postgres/schema-builder.d.ts +2 -1
  18. package/esm/postgres/schema-builder.js +49 -29
  19. package/esm/remult-knex/index.d.ts +10 -32
  20. package/esm/remult-knex/index.js +75 -51
  21. package/esm/remult-mongo.d.ts +9 -2
  22. package/esm/remult-mongo.js +24 -8
  23. package/esm/remult-next.d.ts +11 -0
  24. package/esm/remult-next.js +27 -0
  25. package/esm/server/core.d.ts +2 -0
  26. package/esm/server/core.js +2 -0
  27. package/esm/server/expressBridge.d.ts +19 -7
  28. package/esm/server/expressBridge.js +135 -31
  29. package/esm/server/index.d.ts +2 -1
  30. package/esm/server/index.js +2 -1
  31. package/esm/server/initDataProvider.d.ts +2 -0
  32. package/esm/server/initDataProvider.js +14 -0
  33. package/esm/src/buildRestDataProvider.d.ts +31 -0
  34. package/esm/src/buildRestDataProvider.js +110 -0
  35. package/esm/src/column-interfaces.d.ts +44 -8
  36. package/esm/src/column.d.ts +3 -3
  37. package/esm/src/context.d.ts +39 -36
  38. package/esm/src/context.js +87 -117
  39. package/esm/src/data-api.d.ts +9 -3
  40. package/esm/src/data-api.js +130 -63
  41. package/esm/src/data-interfaces.d.ts +5 -1
  42. package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
  43. package/esm/src/data-providers/array-entity-data-provider.js +2 -17
  44. package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
  45. package/esm/src/data-providers/rest-data-provider.js +64 -23
  46. package/esm/src/data-providers/sql-database.d.ts +7 -3
  47. package/esm/src/data-providers/sql-database.js +60 -17
  48. package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
  49. package/esm/src/data-providers/web-sql-data-provider.js +23 -9
  50. package/esm/src/entity.d.ts +2 -2
  51. package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  52. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
  53. package/esm/src/filter/filter-interfaces.d.ts +5 -5
  54. package/esm/src/filter/filter-interfaces.js +31 -26
  55. package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
  56. package/esm/src/live-query/LiveQueryClient.js +184 -0
  57. package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
  58. package/esm/src/live-query/SseSubscriptionClient.js +86 -0
  59. package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
  60. package/esm/src/live-query/SubscriptionChannel.js +121 -0
  61. package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
  62. package/esm/src/live-query/SubscriptionServer.js +108 -0
  63. package/esm/src/remult-proxy.d.ts +1 -1
  64. package/esm/src/remult-proxy.js +52 -7
  65. package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
  66. package/esm/src/remult3/RepositoryImplementation.js +124 -44
  67. package/esm/src/remult3/getId.d.ts +2 -0
  68. package/esm/src/remult3/getId.js +7 -0
  69. package/esm/src/remult3/remult3.d.ts +67 -10
  70. package/esm/src/remult3/remult3.js +1 -0
  71. package/esm/src/server-action.d.ts +5 -1
  72. package/esm/src/server-action.js +11 -12
  73. package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
  74. package/esm/src/shared-tests/db-tests-setup.js +14 -6
  75. package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
  76. package/esm/src/sort.d.ts +1 -0
  77. package/esm/src/sort.js +18 -0
  78. package/esm/src/sql-command.d.ts +5 -2
  79. package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
  80. package/esm/src/tests/entityForCustomFilter.js +20 -21
  81. package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
  82. package/esm/src/tests/testHelper.spec.d.ts +3 -1
  83. package/esm/src/tests/testHelper.spec.js +63 -60
  84. package/esm/src/valueConverters.js +15 -7
  85. package/index.d.ts +7 -4
  86. package/index.js +7 -3
  87. package/live-query/data-provider-live-query-storage.d.ts +23 -0
  88. package/live-query/data-provider-live-query-storage.js +236 -0
  89. package/myLog.d.ts +1 -0
  90. package/myLog.js +20 -0
  91. package/package.json +18 -1
  92. package/postgres/postgres-data-provider.d.ts +4 -2
  93. package/postgres/postgres-data-provider.js +54 -39
  94. package/postgres/schema-builder.d.ts +2 -1
  95. package/postgres/schema-builder.js +118 -70
  96. package/remult-express.js +1 -1
  97. package/remult-knex/index.d.ts +10 -32
  98. package/remult-knex/index.js +134 -87
  99. package/remult-mongo.d.ts +9 -2
  100. package/remult-mongo.js +39 -7
  101. package/remult-next.d.ts +11 -0
  102. package/remult-next.js +73 -0
  103. package/server/JsonEntityFileStorage.js +2 -2
  104. package/server/core.d.ts +2 -0
  105. package/server/core.js +5 -1
  106. package/server/expressBridge.d.ts +19 -7
  107. package/server/expressBridge.js +283 -69
  108. package/server/index.d.ts +2 -1
  109. package/server/index.js +3 -1
  110. package/server/initDataProvider.d.ts +2 -0
  111. package/server/initDataProvider.js +26 -0
  112. package/src/buildRestDataProvider.d.ts +31 -0
  113. package/src/buildRestDataProvider.js +162 -0
  114. package/src/column-interfaces.d.ts +44 -8
  115. package/src/column.d.ts +3 -3
  116. package/src/context.d.ts +39 -36
  117. package/src/context.js +171 -166
  118. package/src/data-api.d.ts +9 -3
  119. package/src/data-api.js +233 -88
  120. package/src/data-interfaces.d.ts +5 -1
  121. package/src/data-providers/array-entity-data-provider.d.ts +1 -1
  122. package/src/data-providers/array-entity-data-provider.js +2 -17
  123. package/src/data-providers/rest-data-provider.d.ts +4 -3
  124. package/src/data-providers/rest-data-provider.js +105 -39
  125. package/src/data-providers/sql-database.d.ts +7 -3
  126. package/src/data-providers/sql-database.js +89 -16
  127. package/src/data-providers/web-sql-data-provider.d.ts +3 -0
  128. package/src/data-providers/web-sql-data-provider.js +56 -11
  129. package/src/entity.d.ts +2 -2
  130. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  131. package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
  132. package/src/filter/filter-interfaces.d.ts +5 -5
  133. package/src/filter/filter-interfaces.js +31 -26
  134. package/src/live-query/LiveQueryClient.d.ts +1 -0
  135. package/src/live-query/LiveQueryClient.js +283 -0
  136. package/src/live-query/SseSubscriptionClient.d.ts +5 -0
  137. package/src/live-query/SseSubscriptionClient.js +150 -0
  138. package/src/live-query/SubscriptionChannel.d.ts +41 -0
  139. package/src/live-query/SubscriptionChannel.js +209 -0
  140. package/src/live-query/SubscriptionServer.d.ts +38 -0
  141. package/src/live-query/SubscriptionServer.js +258 -0
  142. package/src/remult-proxy.d.ts +1 -1
  143. package/src/remult-proxy.js +153 -8
  144. package/src/remult3/RepositoryImplementation.d.ts +14 -5
  145. package/src/remult3/RepositoryImplementation.js +200 -85
  146. package/src/remult3/getId.d.ts +2 -0
  147. package/src/remult3/getId.js +11 -0
  148. package/src/remult3/remult3.d.ts +67 -10
  149. package/src/remult3/remult3.js +1 -0
  150. package/src/server-action.d.ts +5 -1
  151. package/src/server-action.js +11 -11
  152. package/src/shared-tests/db-tests-setup.d.ts +12 -5
  153. package/src/shared-tests/db-tests-setup.js +17 -8
  154. package/src/shared-tests/db-tests.db-tests.js +140 -8
  155. package/src/sort.d.ts +1 -0
  156. package/src/sort.js +18 -0
  157. package/src/sql-command.d.ts +5 -2
  158. package/src/tests/entityForCustomFilter.d.ts +6 -6
  159. package/src/tests/entityForCustomFilter.js +30 -31
  160. package/src/tests/frontend-database-tests-setup.spec.js +5 -6
  161. package/src/tests/testHelper.spec.d.ts +3 -1
  162. package/src/tests/testHelper.spec.js +104 -100
  163. package/src/valueConverters.js +15 -7
@@ -3,9 +3,12 @@ import { allEntities, Remult, isBackend, queryConfig as queryConfig, setControll
3
3
  import { entityFilterToJson, Filter } from "../filter/filter-interfaces";
4
4
  import { Sort } from "../sort";
5
5
  import { v4 as uuid } from 'uuid';
6
+ import cuid from 'cuid';
6
7
  import { ValueConverters } from "../valueConverters";
7
8
  import { filterHelper } from "../filter/filter-interfaces";
8
9
  import { assign } from "../../assign";
10
+ import { remult as defaultRemult, RemultProxy } from "../remult-proxy";
11
+ import { getId } from "./getId";
9
12
  //import { remult } from "../remult-proxy";
10
13
  let classValidatorValidate = undefined;
11
14
  // import("class-validator".toString())
@@ -165,8 +168,10 @@ export class RepositoryImplementation {
165
168
  instance[field.key] = entity[field.key];
166
169
  }
167
170
  let row = new rowHelperImplementation(this._info, instance, this, this.edp, this.remult, false);
168
- if (id)
171
+ if (id) {
169
172
  row.id = id;
173
+ row.originalId = id;
174
+ }
170
175
  else
171
176
  row.id = row.getId();
172
177
  ref = row;
@@ -195,19 +200,30 @@ export class RepositoryImplementation {
195
200
  }
196
201
  }
197
202
  }
203
+ liveQuery(options) {
204
+ if (!options)
205
+ options = {};
206
+ return {
207
+ subscribe: (l) => {
208
+ let listener = l;
209
+ if (typeof l === "function") {
210
+ listener = {
211
+ next: l,
212
+ complete: () => { },
213
+ error: () => { }
214
+ };
215
+ }
216
+ listener.error ??= () => { };
217
+ listener.complete ??= () => { };
218
+ return this.remult.liveQuerySubscriber.subscribe(this, options, listener);
219
+ }
220
+ };
221
+ }
198
222
  async find(options) {
199
223
  Remult.onFind(this._info, options);
200
- let opt = {};
201
224
  if (!options)
202
225
  options = {};
203
- opt = {};
204
- if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
205
- options.orderBy = this._info.entityInfo.defaultOrderBy;
206
- }
207
- opt.where = await this.translateWhereToFilter(options.where);
208
- opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
209
- opt.limit = options.limit;
210
- opt.page = options.page;
226
+ let opt = await this.buildEntityDataProviderFindOptions(options);
211
227
  let rawRows = await this.edp.find(opt);
212
228
  let loadFields = undefined;
213
229
  if (options.load)
@@ -242,6 +258,18 @@ export class RepositoryImplementation {
242
258
  }
243
259
  }
244
260
  }
261
+ async buildEntityDataProviderFindOptions(options) {
262
+ let opt = {};
263
+ opt = {};
264
+ if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
265
+ options.orderBy = this._info.entityInfo.defaultOrderBy;
266
+ }
267
+ opt.where = await this.translateWhereToFilter(options.where);
268
+ opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
269
+ opt.limit = options.limit;
270
+ opt.page = options.page;
271
+ return opt;
272
+ }
245
273
  async mapRawDataToResult(r, loadFields) {
246
274
  if (!r)
247
275
  return undefined;
@@ -355,6 +383,7 @@ export class RepositoryImplementation {
355
383
  where: this.metadata.idMetadata.getIdFilter(id),
356
384
  });
357
385
  }
386
+ /* @internal*/
358
387
  async translateWhereToFilter(where) {
359
388
  if (this.metadata.options.backendPrefilter && isBackend()) {
360
389
  let z = where;
@@ -365,7 +394,7 @@ export class RepositoryImplementation {
365
394
  };
366
395
  }
367
396
  let r = await Filter.fromEntityFilter(this.metadata, where);
368
- if (r && !this.dataProvider.supportsCustomFilter) {
397
+ if (r && !this.dataProvider.supportsRawFilter) {
369
398
  r = await Filter.translateCustomWhere(r, this.metadata, this.remult);
370
399
  }
371
400
  return r;
@@ -452,6 +481,12 @@ class rowHelperBase {
452
481
  this._isLoading = false;
453
482
  this.lookups = new Map();
454
483
  this.originalValues = {};
484
+ {
485
+ let fac = remult;
486
+ if (fac != null && fac.remultFactory) {
487
+ remult = fac.remultFactory();
488
+ }
489
+ }
455
490
  for (const col of columnsInfo) {
456
491
  let ei = getEntitySettings(col.valueType, false);
457
492
  if (ei && remult) {
@@ -622,6 +657,9 @@ class rowHelperBase {
622
657
  }
623
658
  saveOriginalData() {
624
659
  this.originalValues = this.copyDataToObject();
660
+ this.saveMoreOriginalData();
661
+ }
662
+ saveMoreOriginalData() {
625
663
  }
626
664
  async validate() {
627
665
  this.__clearErrorsAndReportChanged();
@@ -711,10 +749,10 @@ export class rowHelperImplementation extends rowHelperBase {
711
749
  get apiDeleteAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiDelete); }
712
750
  get apiInsertAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiInsert); }
713
751
  getId() {
714
- if (this.info.idMetadata.field instanceof CompoundIdField)
715
- return this.info.idMetadata.field.getId(this.instance);
716
- else
717
- return this.instance[this.info.idMetadata.field.key];
752
+ return getId(this.info, this.instance);
753
+ }
754
+ saveMoreOriginalData() {
755
+ this.originalId = this.getId();
718
756
  }
719
757
  wasDeleted() {
720
758
  this._subscribers?.reportObserved();
@@ -807,6 +845,7 @@ export class rowHelperImplementation extends rowHelperBase {
807
845
  await this.loadDataFrom(updatedRow);
808
846
  if (this.info.entityInfo.saved)
809
847
  await this.info.entityInfo.saved(this.instance);
848
+ await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }]);
810
849
  this.saveOriginalData();
811
850
  this._isNew = false;
812
851
  return this.instance;
@@ -837,6 +876,7 @@ export class rowHelperImplementation extends rowHelperBase {
837
876
  for (const listener of this.repository.listeners.filter(x => x.deleted)) {
838
877
  await listener.deleted(this.instance);
839
878
  }
879
+ await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }]);
840
880
  this._wasDeleted = true;
841
881
  }
842
882
  catch (err) {
@@ -861,14 +901,10 @@ export class rowHelperImplementation extends rowHelperBase {
861
901
  this.instance[col.key] = data[col.key];
862
902
  }
863
903
  await this.calcServerExpression();
864
- if (this.repository.metadata.idMetadata.field instanceof CompoundIdField) {
865
- this.id = this.repository.metadata.idMetadata.field.getId(this.instance);
866
- }
867
- else
868
- this.id = data[this.repository.metadata.idMetadata.field.key];
904
+ this.id = getId(this.info, this.instance);
869
905
  }
870
906
  getOriginalId() {
871
- return this.id;
907
+ return this.originalId;
872
908
  }
873
909
  async calcServerExpression() {
874
910
  if (isBackend())
@@ -913,7 +949,7 @@ export function getFields(container, remult) {
913
949
  return getControllerRef(container, remult).fields;
914
950
  }
915
951
  export function getControllerRef(container, remultArg) {
916
- const remultVar = remultArg || RepositoryImplementation.defaultRemult;
952
+ const remultVar = remultArg || defaultRemult;
917
953
  let result = container[controllerColumns];
918
954
  if (!result)
919
955
  result = container[entityMember];
@@ -1142,24 +1178,23 @@ export class columnDefsImpl {
1142
1178
  this.inputType = this.valueConverter.inputType;
1143
1179
  this.caption = buildCaption(settings.caption, settings.key, remult);
1144
1180
  }
1145
- getDbName() {
1146
- if (this.dbNamePromise)
1147
- return this.dbNamePromise;
1148
- this.dbNamePromise = (async () => {
1181
+ async getDbName() {
1182
+ try {
1149
1183
  if (this.settings.sqlExpression) {
1184
+ let result;
1150
1185
  if (typeof this.settings.sqlExpression === "function") {
1151
- return this.settings.sqlExpression(this.entityDefs);
1186
+ result = await this.settings.sqlExpression(this.entityDefs);
1152
1187
  }
1153
1188
  else
1154
- return this.settings.sqlExpression;
1189
+ result = this.settings.sqlExpression;
1190
+ if (!result)
1191
+ return this.settings.dbName;
1192
+ return result;
1155
1193
  }
1156
1194
  return this.settings.dbName;
1157
- })().then(x => {
1158
- if (x)
1159
- return x;
1160
- return this.settings.dbName;
1161
- });
1162
- return this.dbNamePromise;
1195
+ }
1196
+ finally {
1197
+ }
1163
1198
  }
1164
1199
  get dbName() {
1165
1200
  let result;
@@ -1273,7 +1308,7 @@ class EntityFullInfo {
1273
1308
  if (typeof this.options.sqlExpression === "string")
1274
1309
  this.dbNamePromise = Promise.resolve(this.options.sqlExpression);
1275
1310
  else if (typeof this.options.sqlExpression === "function") {
1276
- let r = this.options.sqlExpression(this.fields);
1311
+ let r = this.options.sqlExpression(this);
1277
1312
  if (r instanceof Promise)
1278
1313
  this.dbNamePromise = r;
1279
1314
  else if (r)
@@ -1324,10 +1359,39 @@ export class Fields {
1324
1359
  static number(...options) {
1325
1360
  return Field(() => Number, ...options);
1326
1361
  }
1362
+ static createdAt(...options) {
1363
+ return Field(() => Date, {
1364
+ saving: (_, ref) => {
1365
+ if (getEntityRef(_).isNew())
1366
+ ref.value = new Date();
1367
+ }
1368
+ }, ...options);
1369
+ }
1370
+ static updatedAt(...options) {
1371
+ return Field(() => Date, {
1372
+ saving: (_, ref) => {
1373
+ ref.value = new Date();
1374
+ }
1375
+ }, ...options);
1376
+ }
1327
1377
  static uuid(...options) {
1328
1378
  return Field(() => String, {
1329
1379
  allowApiUpdate: false,
1330
- defaultValue: () => uuid()
1380
+ defaultValue: () => uuid(),
1381
+ saving: (_, r) => {
1382
+ if (!r.value)
1383
+ r.value = uuid();
1384
+ }
1385
+ }, ...options);
1386
+ }
1387
+ static cuid(...options) {
1388
+ return Field(() => String, {
1389
+ allowApiUpdate: false,
1390
+ defaultValue: () => cuid(),
1391
+ saving: (_, r) => {
1392
+ if (!r.value)
1393
+ r.value = cuid();
1394
+ }
1331
1395
  }, ...options);
1332
1396
  }
1333
1397
  static string(...options) {
@@ -1344,7 +1408,7 @@ export function ValueListFieldType(...options) {
1344
1408
  return (type) => {
1345
1409
  FieldType(o => {
1346
1410
  o.valueConverter = ValueListInfo.get(type),
1347
- o.displayValue = (item, val) => val.caption;
1411
+ o.displayValue = (item, val) => val?.caption;
1348
1412
  }, ...options)(type);
1349
1413
  };
1350
1414
  }
@@ -1604,12 +1668,12 @@ export function decorateColumnSettings(settings, remult) {
1604
1668
  */
1605
1669
  export function Entity(key, ...options) {
1606
1670
  return target => {
1607
- for (const customFilterMember in target) {
1608
- if (Object.prototype.hasOwnProperty.call(target, customFilterMember)) {
1609
- const element = target[customFilterMember];
1610
- if (element?.customFilterInfo?.customFilterTranslator) {
1611
- if (!element.customFilterInfo.key)
1612
- element.customFilterInfo.key = customFilterMember;
1671
+ for (const rawFilterMember in target) {
1672
+ if (Object.prototype.hasOwnProperty.call(target, rawFilterMember)) {
1673
+ const element = target[rawFilterMember];
1674
+ if (element?.rawFilterInfo?.rawFilterTranslator) {
1675
+ if (!element.rawFilterInfo.key)
1676
+ element.rawFilterInfo.key = rawFilterMember;
1613
1677
  }
1614
1678
  }
1615
1679
  }
@@ -1658,7 +1722,7 @@ export class EntityBase {
1658
1722
  }
1659
1723
  export class ControllerBase {
1660
1724
  constructor(remult) {
1661
- this.remult = remult || RepositoryImplementation.defaultRemult;
1725
+ this.remult = remult || defaultRemult;
1662
1726
  }
1663
1727
  assign(values) {
1664
1728
  assign(this, values);
@@ -1797,3 +1861,19 @@ class SubscribableImp {
1797
1861
  return () => this._subscribers = this._subscribers.filter(x => x != list);
1798
1862
  }
1799
1863
  }
1864
+ export function getEntityMetadata(entity) {
1865
+ if (entity.metadata)
1866
+ return entity.metadata;
1867
+ const settings = getEntitySettings(entity, false);
1868
+ if (settings) {
1869
+ return RemultProxy.defaultRemult.repo(entity).metadata;
1870
+ }
1871
+ return entity;
1872
+ }
1873
+ export function getRepository(entity) {
1874
+ const settings = getEntitySettings(entity, false);
1875
+ if (settings) {
1876
+ return RemultProxy.defaultRemult.repo(entity);
1877
+ }
1878
+ return entity;
1879
+ }
@@ -0,0 +1,2 @@
1
+ import type { EntityMetadata } from "./remult3";
2
+ export declare function getId<entityType>(meta: EntityMetadata<entityType>, instance: entityType): any;
@@ -0,0 +1,7 @@
1
+ import { CompoundIdField } from '../column';
2
+ export function getId(meta, instance) {
3
+ if (meta.idMetadata.field instanceof CompoundIdField)
4
+ return meta.idMetadata.field.getId(instance);
5
+ else
6
+ return instance[meta.idMetadata.field.key];
7
+ }
@@ -1,7 +1,8 @@
1
1
  import { ClassType } from "../../classType";
2
2
  import { FieldMetadata } from "../column-interfaces";
3
3
  import { Unobserve } from "../context";
4
- import { EntityOptions as EntityOptions } from "../entity";
4
+ import { LiveQueryChange, SubscriptionListener, Unsubscribe } from "../live-query/SubscriptionChannel";
5
+ import { EntityOptions } from "../entity";
5
6
  import { SortSegment } from "../sort";
6
7
  import { entityEventListener } from "../__EntityValueProvider";
7
8
  export interface EntityRef<entityType> extends Subscribable {
@@ -15,7 +16,8 @@ export interface EntityRef<entityType> extends Subscribable {
15
16
  wasDeleted(): boolean;
16
17
  fields: FieldsRef<entityType>;
17
18
  error: string;
18
- getId(): any;
19
+ getId(): idType<entityType>;
20
+ getOriginalId(): idType<entityType>;
19
21
  repository: Repository<entityType>;
20
22
  metadata: EntityMetadata<entityType>;
21
23
  toApiJson(): any;
@@ -94,24 +96,70 @@ export interface IdMetadata<entityType = any> {
94
96
  isIdField(col: FieldMetadata): boolean;
95
97
  createIdInFilter(items: entityType[]): EntityFilter<entityType>;
96
98
  }
99
+ /** Metadata for an `Entity`, this metadata can be used in the user interface to provide a richer UI experience */
97
100
  export interface EntityMetadata<entityType = any> {
98
- readonly idMetadata: IdMetadata<entityType>;
101
+ /** The Entity's key also used as it's url */
99
102
  readonly key: string;
103
+ /** Metadata for the Entity's fields */
100
104
  readonly fields: FieldsMetadata<entityType>;
105
+ /** A human readable caption for the entity. Can be used to achieve a consistent caption for a field throughout the app
106
+ * @example
107
+ * <h1>Create a new item in {taskRepo.metadata.caption}</h1>
108
+ */
101
109
  readonly caption: string;
110
+ /** The options send to the `Entity`'s decorator */
102
111
  readonly options: EntityOptions;
112
+ /** The class type of the entity */
103
113
  readonly entityType: ClassType<entityType>;
114
+ /** true if the current user is allowed to update an entity instance
115
+ * @example
116
+ * const taskRepo = remult.repo(Task);
117
+ * if (taskRepo.metadata.apiUpdateAllowed){
118
+ * //Allow user to edit the entity
119
+ * }
120
+ */
104
121
  readonly apiUpdateAllowed: boolean;
122
+ /** true if the current user is allowed to read from entity
123
+ * @example
124
+ * const taskRepo = remult.repo(Task);
125
+ * if (taskRepo.metadata.apiReadAllowed){
126
+ * taskRepo.find()
127
+ * }
128
+ */
105
129
  readonly apiReadAllowed: boolean;
130
+ /** true if the current user is allowed to delete an entity instance
131
+ * @example
132
+ * const taskRepo = remult.repo(Task);
133
+ * if (taskRepo.metadata.apiDeleteAllowed){
134
+ * //display delete button
135
+ * }
136
+ */
106
137
  readonly apiDeleteAllowed: boolean;
138
+ /** true if the current user is allowed to create an entity instance
139
+ * @example
140
+ * const taskRepo = remult.repo(Task);
141
+ * if (taskRepo.metadata.apiInsertAllowed){
142
+ * //display insert button
143
+ * }
144
+ */
107
145
  readonly apiInsertAllowed: boolean;
146
+ /** Returns the dbName - based on it's `dbName` option and it's `sqlExpression` option */
108
147
  getDbName(): Promise<string>;
148
+ /** Metadata for the Entity's id */
149
+ readonly idMetadata: IdMetadata<entityType>;
109
150
  }
110
151
  export declare type OmitEB<T> = Omit<T, keyof import('./RepositoryImplementation').EntityBase>;
152
+ export declare type idType<entityType> = entityType extends {
153
+ id?: number;
154
+ } ? number : entityType extends {
155
+ id?: string;
156
+ } ? string : (string | number);
111
157
  /**used to perform CRUD operations on an `entityType` */
112
158
  export interface Repository<entityType> {
113
159
  /** returns a result array based on the provided options */
114
160
  find(options?: FindOptions<entityType>): Promise<entityType[]>;
161
+ /** returns a result array based on the provided options */
162
+ liveQuery(options?: FindOptions<entityType>): LiveQuery<entityType>;
115
163
  /** returns the first item that matchers the `where` condition
116
164
  * @example
117
165
  * await taskRepo.findFirst({ completed:false })
@@ -120,16 +168,12 @@ export interface Repository<entityType> {
120
168
  * */
121
169
  findFirst(where?: EntityFilter<entityType>, options?: FindFirstOptions<entityType>): Promise<entityType>;
122
170
  /** returns the items that matches the idm the result is cached unless specified differently in the `options` parameter */
123
- findId(id: entityType extends {
124
- id?: number;
125
- } ? number : entityType extends {
126
- id?: string;
127
- } ? string : (string | number), options?: FindFirstOptionsBase<entityType>): Promise<entityType>;
171
+ findId(id: idType<entityType>, options?: FindFirstOptionsBase<entityType>): Promise<entityType>;
128
172
  /** An alternative form of fetching data from the API server, which is intended for operating on large numbers of entity objects.
129
173
  *
130
174
  * It also has it's own paging mechanism that can be used n paging scenarios.
131
175
  *
132
- * The `query` method doesn't return an array (as the `find` method) and instead returns an `iteratable` `QueryResult` object
176
+ * The `query` method doesn't return an array (as the `find` method) and instead returns an `iterable` `QueryResult` object
133
177
  * which supports iterations using the JavaScript `for await` statement.
134
178
  * @example
135
179
  * for await (const task of taskRepo.query()) {
@@ -179,10 +223,21 @@ export interface Repository<entityType> {
179
223
  fromJson(x: any, isNew?: boolean): Promise<entityType>;
180
224
  /** returns an `entityRef` for an item returned by `create`, `find` etc... */
181
225
  getEntityRef(item: entityType): EntityRef<entityType>;
182
- /**The metadata for the `entity` */
226
+ /**The metadata for the `entity`
227
+ * @See [EntityMetadata](https://remult.dev/docs/ref_entitymetadata.html)
228
+ */
183
229
  metadata: EntityMetadata<entityType>;
184
230
  addEventListener(listener: entityEventListener<entityType>): Unobserve;
185
231
  }
232
+ export interface LiveQuery<entityType> {
233
+ subscribe(next: (info: LiveQueryChangeInfo<entityType>) => void): Unsubscribe;
234
+ subscribe(listener: Partial<SubscriptionListener<LiveQueryChangeInfo<entityType>>>): Unsubscribe;
235
+ }
236
+ export interface LiveQueryChangeInfo<entityType> {
237
+ items: entityType[];
238
+ changes: LiveQueryChange[];
239
+ applyChanges(prevState: entityType[]): entityType[];
240
+ }
186
241
  export interface FindOptions<entityType> extends FindOptionsBase<entityType> {
187
242
  /** Determines the number of rows returned by the request, on the browser the default is 100 rows
188
243
  * @example
@@ -224,6 +279,8 @@ export declare type EntityFilter<entityType> = {
224
279
  export declare type ValueFilter<valueType> = valueType | valueType[] | {
225
280
  $ne?: valueType | valueType[];
226
281
  "!="?: valueType | valueType[];
282
+ $in?: valueType[];
283
+ $nin?: valueType[];
227
284
  };
228
285
  export declare type ComparisonValueFilter<valueType> = ValueFilter<valueType> & {
229
286
  $gt?: valueType;
@@ -1 +1,2 @@
1
+ ;
1
2
  export {};
@@ -19,6 +19,10 @@ export declare abstract class Action<inParam, outParam> implements ActionInterfa
19
19
  protected abstract execute(info: inParam, req: Remult, res: DataApiResponse): Promise<outParam>;
20
20
  __register(reg: (url: string, queue: boolean, allowed: AllowedForInstance<any>, what: ((data: any, req: Remult, res: DataApiResponse) => void)) => void): void;
21
21
  }
22
+ export declare class ForbiddenError extends Error {
23
+ constructor();
24
+ isForbiddenError: true;
25
+ }
22
26
  export declare class myServerAction extends Action<inArgs, result> {
23
27
  private types;
24
28
  private options;
@@ -38,7 +42,7 @@ export interface BackendMethodOptions<type> {
38
42
  export declare const actionInfo: {
39
43
  allActions: any[];
40
44
  runningOnServer: boolean;
41
- runActionWithoutBlockingUI: (what: () => Promise<any>) => Promise<any>;
45
+ runActionWithoutBlockingUI: <T>(what: () => Promise<T>) => Promise<T>;
42
46
  startBusyWithProgress: () => {
43
47
  progress: (percent: number) => void;
44
48
  close: () => void;
@@ -1,7 +1,9 @@
1
1
  import 'reflect-metadata';
2
- import { Remult, allEntities, classHelpers, ClassHelper, setControllerSettings, buildRestDataProvider } from './context';
2
+ import { Remult, allEntities, classHelpers, ClassHelper, setControllerSettings, doTransaction } from './context';
3
+ import { buildRestDataProvider } from "./buildRestDataProvider";
3
4
  import { SqlDatabase } from './data-providers/sql-database';
4
- import { getEntityRef, decorateColumnSettings, getEntitySettings, getControllerRef, RepositoryImplementation } from './remult3';
5
+ import { getEntityRef, decorateColumnSettings, getEntitySettings, getControllerRef } from './remult3';
6
+ import { remult } from './remult-proxy';
5
7
  export class Action {
6
8
  constructor(actionUrl, queue, allowed) {
7
9
  this.actionUrl = actionUrl;
@@ -10,9 +12,9 @@ export class Action {
10
12
  }
11
13
  async run(pIn, baseUrl, http) {
12
14
  if (baseUrl === undefined)
13
- baseUrl = RepositoryImplementation.defaultRemult.apiClient.url;
15
+ baseUrl = remult.apiClient.url;
14
16
  if (!http)
15
- http = buildRestDataProvider(RepositoryImplementation.defaultRemult.apiClient.httpClient);
17
+ http = buildRestDataProvider(remult.apiClient.httpClient);
16
18
  let r = await http.post(baseUrl + '/' + this.actionUrl, pIn);
17
19
  let p = r;
18
20
  if (p && p.queuedJobId) {
@@ -59,7 +61,7 @@ export class Action {
59
61
  }
60
62
  }
61
63
  Action.apiUrlForJobStatus = 'jobStatusInQueue';
62
- class ForbiddenError extends Error {
64
+ export class ForbiddenError extends Error {
63
65
  constructor() {
64
66
  super("Forbidden");
65
67
  this.isForbiddenError = true;
@@ -75,8 +77,7 @@ export class myServerAction extends Action {
75
77
  async execute(info, remult, res) {
76
78
  let result = { data: {} };
77
79
  let ds = remult.dataProvider;
78
- await ds.transaction(async (ds) => {
79
- remult.dataProvider = (ds);
80
+ await doTransaction(remult, async () => {
80
81
  if (!remult.isAllowedForInstance(undefined, this.options.allowed))
81
82
  throw new ForbiddenError();
82
83
  info.args = await prepareReceivedArgs(this.types, info.args, remult, ds, res);
@@ -160,11 +161,9 @@ export function BackendMethod(options) {
160
161
  let allowed = options.allowed;
161
162
  try {
162
163
  let remult = req;
163
- let ds = remult.dataProvider;
164
164
  let r;
165
- await ds.transaction(async (ds) => {
166
- remult.dataProvider = (ds);
167
- d.args = await prepareReceivedArgs(types, d.args, remult, ds, res);
165
+ await doTransaction(remult, async () => {
166
+ d.args = await prepareReceivedArgs(types, d.args, remult, remult.dataProvider, res);
168
167
  if (allEntities.includes(constructor)) {
169
168
  let repo = remult.repo(constructor);
170
169
  let y;
@@ -205,7 +204,7 @@ export function BackendMethod(options) {
205
204
  }
206
205
  }
207
206
  else {
208
- let y = new constructor(remult, ds);
207
+ let y = new constructor(remult, remult.dataProvider);
209
208
  let controllerRef = getControllerRef(y, remult);
210
209
  await controllerRef._updateEntityBasedOnApi(d.fields);
211
210
  if (!remult.isAllowedForInstance(y, allowed))
@@ -5,15 +5,22 @@ import { Repository } from "../remult3";
5
5
  import { CategoriesForTesting } from "../tests/remult-3-entities";
6
6
  import { Status } from "../tests/testModel/models";
7
7
  export declare function itWithFocus(key: string, what: () => Promise<void>, focus?: boolean): void;
8
- export declare function testAll(key: string, what: dbTestWhatSignature, focus?: boolean): void;
9
- export declare function addDatabaseToTest(tester: dbTestMethodSignature): dbTestMethodSignature;
8
+ export declare function testAll(key: string, what: dbTestWhatSignature, focus?: boolean, options?: {
9
+ exclude?: string[];
10
+ }): void;
11
+ export declare function addDatabaseToTest(tester: dbTestMethodSignature, key?: string): dbTestMethodSignature;
10
12
  export declare function testInMemory(key: string, what: dbTestWhatSignature, focus?: boolean): void;
11
- export declare type dbTestWhatSignature = (db: {
13
+ export declare const TestDbs: {
14
+ restDataProvider: string;
15
+ };
16
+ export declare type dbTestWhatSignature = ((db: {
12
17
  db: DataProvider;
13
18
  remult: Remult;
14
19
  createEntity<entityType>(entity: ClassType<entityType>): Promise<Repository<entityType>>;
15
- }) => Promise<void>;
16
- export declare type dbTestMethodSignature = (key: string, what: dbTestWhatSignature, focus: boolean) => void;
20
+ }) => Promise<void>);
21
+ export declare type dbTestMethodSignature = ((key: string, what: dbTestWhatSignature, focus: boolean) => void) & {
22
+ key?: string;
23
+ };
17
24
  export declare function testAllDbs<T extends CategoriesForTesting>(key: string, doTest: (helper: {
18
25
  remult: Remult;
19
26
  createData: (doInsert?: (insert: (id: number, name: string, description?: string, status?: Status) => Promise<void>) => Promise<void>, entity?: {
@@ -8,17 +8,23 @@ export function itWithFocus(key, what, focus = false) {
8
8
  else
9
9
  it(key, what);
10
10
  }
11
- export function testAll(key, what, focus = false) {
11
+ export function testAll(key, what, focus = false, options) {
12
+ function runTest(m) {
13
+ return !options?.exclude?.includes(m.key);
14
+ }
12
15
  for (const test of databasesTesters) {
13
- test(key, what, focus);
16
+ if (runTest(test))
17
+ test(key, what, focus);
14
18
  }
15
19
  loadedTests.push(x => {
16
- x(key, what, focus);
20
+ if (runTest(x))
21
+ x(key, what, focus);
17
22
  });
18
23
  }
19
24
  const databasesTesters = [];
20
25
  const loadedTests = [];
21
- export function addDatabaseToTest(tester) {
26
+ export function addDatabaseToTest(tester, key) {
27
+ tester.key = key;
22
28
  for (const test of loadedTests) {
23
29
  test(tester);
24
30
  }
@@ -27,12 +33,14 @@ export function addDatabaseToTest(tester) {
27
33
  }
28
34
  export function testInMemory(key, what, focus = false) {
29
35
  itWithFocus(key + " - in memory", async () => {
30
- let remult = new Remult();
31
36
  let db = new InMemoryDataProvider();
32
- remult.dataProvider = (db);
37
+ let remult = new Remult(db);
33
38
  await what({ db, remult, createEntity: async (x) => remult.repo(x) });
34
39
  }, focus);
35
40
  }
41
+ export const TestDbs = {
42
+ restDataProvider: "restDataProvider"
43
+ };
36
44
  databasesTesters.push(testInMemory);
37
45
  export async function testAllDbs(key, doTest, focus = false) {
38
46
  testAll(key, async ({ remult, createEntity }) => {