remult 0.22.2 → 0.22.3

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 (255) hide show
  1. package/esm/graphql.js +27 -27
  2. package/esm/postgres/schema-builder.js +4 -4
  3. package/esm/remult-knex/index.js +2 -2
  4. package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +10 -0
  5. package/esm/src/id-entity.d.ts +4 -0
  6. package/esm/src/id-entity.js +8 -0
  7. package/esm/src/remult3/RepositoryImplementation.js +7 -5
  8. package/esm/src/shared-tests/db-tests.db-tests.js +35 -0
  9. package/package.json +1 -1
  10. package/postgres/postgresColumnSyntax.d.ts +2 -0
  11. package/postgres/postgresColumnSyntax.js +34 -0
  12. package/remult-knex/index.js +2 -2
  13. package/remult-middleware.d.ts +2 -0
  14. package/remult-middleware.js +70 -0
  15. package/server/register-actions-on-server.d.ts +3 -0
  16. package/server/register-actions-on-server.js +50 -0
  17. package/server/register-entities-on-server.d.ts +3 -0
  18. package/server/register-entities-on-server.js +19 -0
  19. package/src/backend-tests/backend-database-test-setup.backend-spec.js +10 -0
  20. package/{esm/core/src → src}/backend-tests/my-test.backend-spec.d.ts +1 -1
  21. package/src/backend-tests/my-test.backend-spec.js +217 -0
  22. package/src/backend-tests/test-http-server.backend-spec.d.ts +6 -0
  23. package/src/backend-tests/test-http-server.backend-spec.js +342 -0
  24. package/src/id-entity.d.ts +4 -0
  25. package/src/id-entity.js +17 -0
  26. package/src/remult3/RepositoryImplementation.js +15 -13
  27. package/src/shared-tests/db-tests.db-tests.js +64 -0
  28. package/{esm/core/src/valueConverters.d.ts → valueConverters.d.ts} +12 -13
  29. package/valueConverters.js +168 -0
  30. package/esm/angular/interfaces/index.d.ts +0 -1
  31. package/esm/angular/interfaces/index.js +0 -1
  32. package/esm/angular/interfaces/src/column-collection.d.ts +0 -70
  33. package/esm/angular/interfaces/src/column-collection.js +0 -341
  34. package/esm/angular/interfaces/src/data-area-settings.d.ts +0 -15
  35. package/esm/angular/interfaces/src/data-area-settings.js +0 -35
  36. package/esm/angular/interfaces/src/data-control-interfaces.d.ts +0 -36
  37. package/esm/angular/interfaces/src/data-control-interfaces.js +0 -57
  38. package/esm/angular/interfaces/src/dataList.d.ts +0 -13
  39. package/esm/angular/interfaces/src/dataList.js +0 -42
  40. package/esm/angular/interfaces/src/filter-helper.d.ts +0 -13
  41. package/esm/angular/interfaces/src/filter-helper.js +0 -59
  42. package/esm/angular/interfaces/src/grid-settings.d.ts +0 -145
  43. package/esm/angular/interfaces/src/grid-settings.js +0 -387
  44. package/esm/angular/interfaces/src/public_api.d.ts +0 -4
  45. package/esm/angular/interfaces/src/public_api.js +0 -4
  46. package/esm/angular/src/angular/RemultAngularPluginsService.d.ts +0 -4
  47. package/esm/angular/src/angular/RemultAngularPluginsService.js +0 -3
  48. package/esm/angular/src/angular/data-control/data-control2.component.d.ts +0 -49
  49. package/esm/angular/src/angular/data-control/data-control2.component.js +0 -138
  50. package/esm/core/SseSubscriptionServer.d.ts +0 -33
  51. package/esm/core/SseSubscriptionServer.js +0 -107
  52. package/esm/core/assign.d.ts +0 -1
  53. package/esm/core/assign.js +0 -5
  54. package/esm/core/classType.d.ts +0 -3
  55. package/esm/core/classType.js +0 -1
  56. package/esm/core/index.d.ts +0 -27
  57. package/esm/core/index.js +0 -25
  58. package/esm/core/inputTypes.d.ts +0 -9
  59. package/esm/core/inputTypes.js +0 -9
  60. package/esm/core/live-query/data-provider-live-query-storage.d.ts +0 -22
  61. package/esm/core/live-query/data-provider-live-query-storage.js +0 -71
  62. package/esm/core/postgres/index.d.ts +0 -2
  63. package/esm/core/postgres/index.js +0 -2
  64. package/esm/core/postgres/postgres-data-provider.d.ts +0 -36
  65. package/esm/core/postgres/postgres-data-provider.js +0 -120
  66. package/esm/core/postgres/schema-builder.d.ts +0 -17
  67. package/esm/core/postgres/schema-builder.js +0 -153
  68. package/esm/core/remult-fresh.d.ts +0 -17
  69. package/esm/core/remult-fresh.js +0 -29
  70. package/esm/core/remult-knex/index.d.ts +0 -30
  71. package/esm/core/remult-knex/index.js +0 -446
  72. package/esm/core/remult-mongo.d.ts +0 -24
  73. package/esm/core/remult-mongo.js +0 -254
  74. package/esm/core/server/JsonEntityFileStorage.d.ts +0 -10
  75. package/esm/core/server/JsonEntityFileStorage.js +0 -25
  76. package/esm/core/server/core.d.ts +0 -4
  77. package/esm/core/server/core.js +0 -4
  78. package/esm/core/server/expressBridge.d.ts +0 -182
  79. package/esm/core/server/expressBridge.js +0 -938
  80. package/esm/core/server/index.d.ts +0 -6
  81. package/esm/core/server/index.js +0 -9
  82. package/esm/core/server/initAsyncHooks.d.ts +0 -1
  83. package/esm/core/server/initAsyncHooks.js +0 -9
  84. package/esm/core/server/initDataProvider.d.ts +0 -2
  85. package/esm/core/server/initDataProvider.js +0 -14
  86. package/esm/core/src/__EntityValueProvider.d.ts +0 -11
  87. package/esm/core/src/__EntityValueProvider.js +0 -1
  88. package/esm/core/src/__RowsOfDataForTesting.d.ts +0 -3
  89. package/esm/core/src/__RowsOfDataForTesting.js +0 -1
  90. package/esm/core/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +0 -6
  91. package/esm/core/src/backend-tests/backend-database-test-setup.backend-spec.js +0 -147
  92. package/esm/core/src/backend-tests/my-test.backend-spec.js +0 -197
  93. package/esm/core/src/backend-tests/test-http-server.backend-spec.d.ts +0 -1
  94. package/esm/core/src/backend-tests/test-http-server.backend-spec.js +0 -167
  95. package/esm/core/src/buildRestDataProvider.d.ts +0 -31
  96. package/esm/core/src/buildRestDataProvider.js +0 -113
  97. package/esm/core/src/column-interfaces.d.ts +0 -154
  98. package/esm/core/src/column-interfaces.js +0 -7
  99. package/esm/core/src/column.d.ts +0 -58
  100. package/esm/core/src/column.js +0 -174
  101. package/esm/core/src/context.d.ts +0 -134
  102. package/esm/core/src/context.js +0 -286
  103. package/esm/core/src/data-api.d.ts +0 -39
  104. package/esm/core/src/data-api.js +0 -279
  105. package/esm/core/src/data-interfaces.d.ts +0 -41
  106. package/esm/core/src/data-interfaces.js +0 -12
  107. package/esm/core/src/data-providers/array-entity-data-provider.d.ts +0 -22
  108. package/esm/core/src/data-providers/array-entity-data-provider.js +0 -252
  109. package/esm/core/src/data-providers/in-memory-database.d.ts +0 -9
  110. package/esm/core/src/data-providers/in-memory-database.js +0 -14
  111. package/esm/core/src/data-providers/json-data-provider.d.ts +0 -12
  112. package/esm/core/src/data-providers/json-data-provider.js +0 -55
  113. package/esm/core/src/data-providers/rest-data-provider.d.ts +0 -49
  114. package/esm/core/src/data-providers/rest-data-provider.js +0 -256
  115. package/esm/core/src/data-providers/sql-database.d.ts +0 -33
  116. package/esm/core/src/data-providers/sql-database.js +0 -384
  117. package/esm/core/src/data-providers/web-sql-data-provider.d.ts +0 -24
  118. package/esm/core/src/data-providers/web-sql-data-provider.js +0 -143
  119. package/esm/core/src/entity.d.ts +0 -91
  120. package/esm/core/src/entity.js +0 -1
  121. package/esm/core/src/filter/filter-consumer-bridge-to-sql-request.d.ts +0 -50
  122. package/esm/core/src/filter/filter-consumer-bridge-to-sql-request.js +0 -150
  123. package/esm/core/src/filter/filter-consumer-bridge-to-url-builder.d.ts +0 -1
  124. package/esm/core/src/filter/filter-consumer-bridge-to-url-builder.js +0 -1
  125. package/esm/core/src/filter/filter-interfaces.d.ts +0 -85
  126. package/esm/core/src/filter/filter-interfaces.js +0 -477
  127. package/esm/core/src/live-query/LiveQueryClient.d.ts +0 -20
  128. package/esm/core/src/live-query/LiveQueryClient.js +0 -188
  129. package/esm/core/src/live-query/SseSubscriptionClient.d.ts +0 -5
  130. package/esm/core/src/live-query/SseSubscriptionClient.js +0 -86
  131. package/esm/core/src/live-query/SubscriptionChannel.d.ts +0 -72
  132. package/esm/core/src/live-query/SubscriptionChannel.js +0 -123
  133. package/esm/core/src/live-query/SubscriptionServer.d.ts +0 -53
  134. package/esm/core/src/live-query/SubscriptionServer.js +0 -111
  135. package/esm/core/src/live-query/live-query-tests.spec.d.ts +0 -6
  136. package/esm/core/src/live-query/live-query-tests.spec.js +0 -952
  137. package/esm/core/src/remult-proxy.d.ts +0 -35
  138. package/esm/core/src/remult-proxy.js +0 -113
  139. package/esm/core/src/remult3/DecoratorReplacer.d.ts +0 -10
  140. package/esm/core/src/remult3/DecoratorReplacer.js +0 -20
  141. package/esm/core/src/remult3/RepositoryImplementation.d.ts +0 -355
  142. package/esm/core/src/remult3/RepositoryImplementation.js +0 -2109
  143. package/esm/core/src/remult3/index.d.ts +0 -2
  144. package/esm/core/src/remult3/index.js +0 -2
  145. package/esm/core/src/remult3/remult3.d.ts +0 -395
  146. package/esm/core/src/remult3/remult3.js +0 -2
  147. package/esm/core/src/server-action.d.ts +0 -81
  148. package/esm/core/src/server-action.js +0 -394
  149. package/esm/core/src/shared-tests/db-tests-setup.d.ts +0 -32
  150. package/esm/core/src/shared-tests/db-tests-setup.js +0 -78
  151. package/esm/core/src/shared-tests/db-tests.db-tests.d.ts +0 -25
  152. package/esm/core/src/shared-tests/db-tests.db-tests.js +0 -810
  153. package/esm/core/src/shared-tests/deleteAll.d.ts +0 -2
  154. package/esm/core/src/shared-tests/deleteAll.js +0 -4
  155. package/esm/core/src/shared-tests/entityWithValidations.d.ts +0 -14
  156. package/esm/core/src/shared-tests/entityWithValidations.js +0 -43
  157. package/esm/core/src/shared-tests/index.d.ts +0 -4
  158. package/esm/core/src/shared-tests/index.js +0 -4
  159. package/esm/core/src/shared-tests/test-custom-id-column.spec.d.ts +0 -1
  160. package/esm/core/src/shared-tests/test-custom-id-column.spec.js +0 -67
  161. package/esm/core/src/shared-tests/test-special-value.spec.d.ts +0 -10
  162. package/esm/core/src/shared-tests/test-special-value.spec.js +0 -73
  163. package/esm/core/src/shared-tests/test-update-with-null.spec.d.ts +0 -1
  164. package/esm/core/src/shared-tests/test-update-with-null.spec.js +0 -31
  165. package/esm/core/src/sort.d.ts +0 -16
  166. package/esm/core/src/sort.js +0 -103
  167. package/esm/core/src/sql-command.d.ts +0 -19
  168. package/esm/core/src/sql-command.js +0 -1
  169. package/esm/core/src/test.d.ts +0 -3
  170. package/esm/core/src/test.js +0 -12
  171. package/esm/core/src/tests/Done.d.ts +0 -5
  172. package/esm/core/src/tests/Done.js +0 -9
  173. package/esm/core/src/tests/RowProvider.spec.d.ts +0 -16
  174. package/esm/core/src/tests/RowProvider.spec.js +0 -631
  175. package/esm/core/src/tests/TestDataApiResponse.d.ts +0 -10
  176. package/esm/core/src/tests/TestDataApiResponse.js +0 -22
  177. package/esm/core/src/tests/basicRowFunctionality.spec.d.ts +0 -8
  178. package/esm/core/src/tests/basicRowFunctionality.spec.js +0 -2103
  179. package/esm/core/src/tests/c.d.ts +0 -8
  180. package/esm/core/src/tests/c.js +0 -13
  181. package/esm/core/src/tests/createData.d.ts +0 -7
  182. package/esm/core/src/tests/createData.js +0 -22
  183. package/esm/core/src/tests/d.d.ts +0 -6
  184. package/esm/core/src/tests/d.js +0 -11
  185. package/esm/core/src/tests/dWithPrefilter.d.ts +0 -7
  186. package/esm/core/src/tests/dWithPrefilter.js +0 -18
  187. package/esm/core/src/tests/entityForCustomFilter.d.ts +0 -16
  188. package/esm/core/src/tests/entityForCustomFilter.js +0 -33
  189. package/esm/core/src/tests/entityWithValidationsOnColumn.d.ts +0 -5
  190. package/esm/core/src/tests/entityWithValidationsOnColumn.js +0 -13
  191. package/esm/core/src/tests/frontend-database-tests-setup.spec.d.ts +0 -4
  192. package/esm/core/src/tests/frontend-database-tests-setup.spec.js +0 -37
  193. package/esm/core/src/tests/h.d.ts +0 -6
  194. package/esm/core/src/tests/h.js +0 -24
  195. package/esm/core/src/tests/remult-3-basic-tests.spec.d.ts +0 -1
  196. package/esm/core/src/tests/remult-3-basic-tests.spec.js +0 -236
  197. package/esm/core/src/tests/remult-3-entities.d.ts +0 -23
  198. package/esm/core/src/tests/remult-3-entities.js +0 -39
  199. package/esm/core/src/tests/tasks.d.ts +0 -6
  200. package/esm/core/src/tests/tasks.js +0 -10
  201. package/esm/core/src/tests/test-context-as-storage.spec.d.ts +0 -0
  202. package/esm/core/src/tests/test-context-as-storage.spec.js +0 -1
  203. package/esm/core/src/tests/test-data-api/predefined-filter.spec.d.ts +0 -1
  204. package/esm/core/src/tests/test-data-api/predefined-filter.spec.js +0 -389
  205. package/esm/core/src/tests/test-data-api/test-put-with-validation.spec.d.ts +0 -5
  206. package/esm/core/src/tests/test-data-api/test-put-with-validation.spec.js +0 -152
  207. package/esm/core/src/tests/test-decorator-precedence.spec.d.ts +0 -1
  208. package/esm/core/src/tests/test-decorator-precedence.spec.js +0 -38
  209. package/esm/core/src/tests/test-default-value.spec.d.ts +0 -1
  210. package/esm/core/src/tests/test-default-value.spec.js +0 -33
  211. package/esm/core/src/tests/test-entity-inheritance.spec.d.ts +0 -14
  212. package/esm/core/src/tests/test-entity-inheritance.spec.js +0 -82
  213. package/esm/core/src/tests/test-exceptions.spec.d.ts +0 -1
  214. package/esm/core/src/tests/test-exceptions.spec.js +0 -375
  215. package/esm/core/src/tests/test-json.spec.d.ts +0 -1
  216. package/esm/core/src/tests/test-json.spec.js +0 -115
  217. package/esm/core/src/tests/test-many-to-one-relation-in-entity.spec.d.ts +0 -9
  218. package/esm/core/src/tests/test-many-to-one-relation-in-entity.spec.js +0 -809
  219. package/esm/core/src/tests/test-non-active-record.spec.d.ts +0 -1
  220. package/esm/core/src/tests/test-non-active-record.spec.js +0 -66
  221. package/esm/core/src/tests/test-object-column.spec.d.ts +0 -1
  222. package/esm/core/src/tests/test-object-column.spec.js +0 -189
  223. package/esm/core/src/tests/test-paged-foreach.spec.d.ts +0 -1
  224. package/esm/core/src/tests/test-paged-foreach.spec.js +0 -328
  225. package/esm/core/src/tests/test-remult-angular.spec.d.ts +0 -1
  226. package/esm/core/src/tests/test-remult-angular.spec.js +0 -23
  227. package/esm/core/src/tests/test-server-controller.spec.d.ts +0 -7
  228. package/esm/core/src/tests/test-server-controller.spec.js +0 -489
  229. package/esm/core/src/tests/test-server-expressions.spec.d.ts +0 -1
  230. package/esm/core/src/tests/test-server-expressions.spec.js +0 -178
  231. package/esm/core/src/tests/test-server-method-on-entity.spec.d.ts +0 -1
  232. package/esm/core/src/tests/test-server-method-on-entity.spec.js +0 -306
  233. package/esm/core/src/tests/test-sql-expression.spec.d.ts +0 -5
  234. package/esm/core/src/tests/test-sql-expression.spec.js +0 -205
  235. package/esm/core/src/tests/test-sql.spec.d.ts +0 -1
  236. package/esm/core/src/tests/test-sql.spec.js +0 -170
  237. package/esm/core/src/tests/test-subscribe.spec.d.ts +0 -1
  238. package/esm/core/src/tests/test-subscribe.spec.js +0 -135
  239. package/esm/core/src/tests/test-type-recovery.spec.d.ts +0 -1
  240. package/esm/core/src/tests/test-type-recovery.spec.js +0 -49
  241. package/esm/core/src/tests/test-where-api.spec.d.ts +0 -1
  242. package/esm/core/src/tests/test-where-api.spec.js +0 -434
  243. package/esm/core/src/tests/testHelper.spec.d.ts +0 -36
  244. package/esm/core/src/tests/testHelper.spec.js +0 -226
  245. package/esm/core/src/tests/testModel/models.d.ts +0 -16
  246. package/esm/core/src/tests/testModel/models.js +0 -25
  247. package/esm/core/src/validators.d.ts +0 -14
  248. package/esm/core/src/validators.js +0 -33
  249. package/esm/core/src/valueConverters.js +0 -197
  250. package/esm/core/urlBuilder.d.ts +0 -6
  251. package/esm/core/urlBuilder.js +0 -20
  252. package/esm/test-servers/shared/Task.d.ts +0 -9
  253. package/esm/test-servers/shared/Task.js +0 -32
  254. package/esm/test.d.ts +0 -5
  255. package/esm/test.js +0 -6
@@ -1,2109 +0,0 @@
1
- import { CompoundIdField, LookupColumn, makeTitle } from '../column';
2
- import { allEntities, Remult, isBackend, queryConfig as queryConfig, setControllerSettings } from "../context";
3
- import { entityFilterToJson, Filter } from "../filter/filter-interfaces";
4
- import { Sort } from "../sort";
5
- import { v4 as uuid } from 'uuid';
6
- import { createId } from '@paralleldrive/cuid2';
7
- import { ValueConverters } from "../valueConverters";
8
- import { filterHelper } from "../filter/filter-interfaces";
9
- import { assign } from "../../assign";
10
- import { remult as defaultRemult } from "../remult-proxy";
11
- //import { remult } from "../remult-proxy";
12
- let classValidatorValidate = undefined;
13
- // import("class-validator".toString())
14
- // .then((v) => {
15
- // classValidatorValidate = (item, ref) => {
16
- // return v.validate(item).then(errors => {
17
- // for (const err of errors) {
18
- // for (const key in err.constraints) {
19
- // if (Object.prototype.hasOwnProperty.call(err.constraints, key)) {
20
- // const element = err.constraints[key];
21
- // ref.fields.find(err.property).error = element;
22
- // }
23
- // }
24
- // }
25
- // });
26
- // }
27
- // })
28
- // .catch(() => {
29
- // });
30
- export class RepositoryImplementation {
31
- entity;
32
- remult;
33
- dataProvider;
34
- async createAfterFilter(orderBy, lastRow) {
35
- let values = new Map();
36
- for (const s of Sort.translateOrderByToSort(this.metadata, orderBy).Segments) {
37
- let existingVal = lastRow[s.field.key];
38
- // if (typeof existingVal !== "string" && typeof existingVal !== "number") {
39
- // }
40
- // else {
41
- // let ei = getEntitySettings(s.field.valueType, false);
42
- // if (ei) {
43
- // existingVal = await this.remult.repo(s.field.valueType).findId(existingVal);
44
- // }
45
- // }
46
- values.set(s.field.key, existingVal);
47
- }
48
- let r = { $or: [] };
49
- let equalToColumn = [];
50
- for (const s of Sort.translateOrderByToSort(this.metadata, orderBy).Segments) {
51
- let ff = new filterHelper(s.field);
52
- let f = {};
53
- for (const c of equalToColumn) {
54
- f[c.key] = values.get(c.key);
55
- }
56
- equalToColumn.push(s.field);
57
- if (s.isDescending) {
58
- f[s.field.key] = { $lt: values.get(s.field.key) };
59
- }
60
- else
61
- f[s.field.key] = { $gt: values.get(s.field.key) };
62
- r.$or.push(f);
63
- }
64
- return r;
65
- }
66
- _info;
67
- __edp;
68
- get edp() {
69
- return this.__edp ? this.__edp : this.__edp = this.dataProvider.getEntityDataProvider(this.metadata);
70
- }
71
- constructor(entity, remult, dataProvider) {
72
- this.entity = entity;
73
- this.remult = remult;
74
- this.dataProvider = dataProvider;
75
- this._info = createOldEntity(entity, remult);
76
- }
77
- idCache = new Map();
78
- getCachedById(id) {
79
- id = id + '';
80
- this.getCachedByIdAsync(id);
81
- let r = this.idCache.get(id);
82
- if (r instanceof Promise)
83
- return undefined;
84
- return r;
85
- }
86
- async getCachedByIdAsync(id) {
87
- id = id + '';
88
- let r = this.idCache.get(id);
89
- if (r instanceof Promise)
90
- return await r;
91
- if (this.idCache.has(id)) {
92
- return r;
93
- }
94
- this.idCache.set(id, undefined);
95
- let row = this.findId(id).then(row => {
96
- if (row === undefined) {
97
- r = null;
98
- }
99
- else
100
- r = row;
101
- this.idCache.set(id, r);
102
- return r;
103
- });
104
- this.idCache.set(id, row);
105
- return await row;
106
- }
107
- //TODO2 - Used to prevent unnecessary many to one relations
108
- addToCache(item) {
109
- if (item)
110
- this.idCache.set(this.getEntityRef(item).getId() + '', item);
111
- }
112
- get metadata() { return this._info; }
113
- ;
114
- listeners;
115
- addEventListener(listener) {
116
- if (!this.listeners)
117
- this.listeners = [];
118
- this.listeners.push(listener);
119
- return () => {
120
- this.listeners.splice(this.listeners.indexOf(listener), 1);
121
- };
122
- }
123
- query(options) {
124
- return new QueryResultImpl(options, this);
125
- }
126
- getEntityRef(entity) {
127
- let x = entity[entityMember];
128
- if (!x) {
129
- this.fixTypes(entity);
130
- x = new rowHelperImplementation(this._info, entity, this, this.edp, this.remult, true);
131
- Object.defineProperty(entity, entityMember, {
132
- get: () => x
133
- });
134
- x.saveOriginalData();
135
- }
136
- return x;
137
- }
138
- async delete(item) {
139
- if (typeof item === "string" || typeof item === "number")
140
- return this.edp.delete(item);
141
- else {
142
- return this.getRefForExistingRow(item, undefined).delete();
143
- }
144
- }
145
- async insert(entity) {
146
- if (Array.isArray(entity)) {
147
- let r = [];
148
- for (const item of entity) {
149
- r.push(await this.insert(item));
150
- }
151
- return r;
152
- }
153
- else {
154
- let ref = getEntityRef(entity, false);
155
- if (ref) {
156
- if (!ref.isNew())
157
- throw "Item is not new";
158
- return await ref.save();
159
- }
160
- else {
161
- return await this.getEntityRef(this.create(entity)).save();
162
- }
163
- }
164
- }
165
- get fields() {
166
- return this.metadata.fields;
167
- }
168
- async validate(entity, ...fields) {
169
- {
170
- let ref = getEntityRef(entity, false);
171
- if (!ref)
172
- ref = this.getEntityRef({ ...entity });
173
- if (!fields || fields.length === 0) {
174
- return await ref.validate();
175
- }
176
- else {
177
- ref.__clearErrorsAndReportChanged();
178
- let hasError = false;
179
- for (const f of fields) {
180
- if (!await ref.fields.find(f).validate())
181
- hasError = true;
182
- }
183
- if (!hasError)
184
- return undefined;
185
- return ref.buildErrorInfoObject();
186
- }
187
- }
188
- }
189
- async update(id, entity) {
190
- let ref = this.getRefForExistingRow(entity, id);
191
- return (await ref.save());
192
- }
193
- getRefForExistingRow(entity, id) {
194
- let ref = getEntityRef(entity, false);
195
- if (!ref) {
196
- const instance = new this.entity(this.remult);
197
- for (const field of this.fieldsOf(entity)) {
198
- instance[field.key] = entity[field.key];
199
- }
200
- this.fixTypes(instance);
201
- let row = new rowHelperImplementation(this._info, instance, this, this.edp, this.remult, false);
202
- if (typeof (id) === "object")
203
- id = this.metadata.idMetadata.getId(id);
204
- if (id) {
205
- row.id = id;
206
- row.originalId = id;
207
- }
208
- else
209
- row.id = row.getId();
210
- ref = row;
211
- Object.defineProperty(instance, entityMember, {
212
- get: () => row
213
- });
214
- }
215
- return ref;
216
- }
217
- async save(entity) {
218
- if (Array.isArray(entity)) {
219
- return Promise.all(entity.map(x => this.save(x)));
220
- }
221
- else {
222
- let ref = getEntityRef(entity, false);
223
- if (ref)
224
- return await ref.save();
225
- else if (entity instanceof EntityBase) {
226
- return await this.getEntityRef(entity).save();
227
- }
228
- else {
229
- let id = this.metadata.idMetadata.getId(entity);
230
- if (id === undefined)
231
- return this.insert(entity);
232
- return this.update(id, entity);
233
- }
234
- }
235
- }
236
- liveQuery(options) {
237
- if (!options)
238
- options = {};
239
- return {
240
- subscribe: (l) => {
241
- let listener = l;
242
- if (typeof l === "function") {
243
- listener = {
244
- next: l,
245
- complete: () => { },
246
- error: () => { }
247
- };
248
- }
249
- listener.error ??= () => { };
250
- listener.complete ??= () => { };
251
- return this.remult.liveQuerySubscriber.subscribe(this, options, listener);
252
- }
253
- };
254
- }
255
- async find(options, skipOrderByAndLimit = false) {
256
- Remult.onFind(this._info, options);
257
- if (!options)
258
- options = {};
259
- let opt = await this.buildEntityDataProviderFindOptions(options);
260
- if (skipOrderByAndLimit) {
261
- delete opt.orderBy;
262
- delete opt.limit;
263
- }
264
- let rawRows = await this.edp.find(opt);
265
- let result = await this.loadManyToOneForManyRows(rawRows, options.load);
266
- return result;
267
- }
268
- async buildEntityDataProviderFindOptions(options) {
269
- let opt = {};
270
- opt = {};
271
- if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
272
- options.orderBy = this._info.entityInfo.defaultOrderBy;
273
- }
274
- opt.where = await this.translateWhereToFilter(options.where);
275
- opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
276
- opt.limit = options.limit;
277
- opt.page = options.page;
278
- return opt;
279
- }
280
- async fromJsonArray(jsonItems, load) {
281
- return this.loadManyToOneForManyRows(jsonItems.map(row => {
282
- let result = {};
283
- for (const col of this.metadata.fields.toArray()) {
284
- result[col.key] = col.valueConverter.fromJson(row[col.key]);
285
- }
286
- return result;
287
- }), load);
288
- }
289
- async loadManyToOneForManyRows(rawRows, load) {
290
- let loadFields = undefined;
291
- if (load)
292
- loadFields = load(this.metadata.fields);
293
- for (const col of this.metadata.fields) {
294
- let ei = getEntitySettings(col.valueType, false);
295
- if (ei) {
296
- let load = !col.options.lazy;
297
- if (loadFields !== undefined)
298
- load = loadFields.includes(col);
299
- if (load) {
300
- let repo = this.remult.repo(col.valueType);
301
- let toLoad = [];
302
- for (const r of rawRows) {
303
- let val = r[col.key];
304
- if (val !== undefined && val !== null && !toLoad.includes(val) && !repo.idCache.has(val)) {
305
- toLoad.push(val);
306
- }
307
- }
308
- if (toLoad.length > 0) {
309
- await loadManyToOne(repo, toLoad);
310
- }
311
- }
312
- }
313
- }
314
- async function loadManyToOne(repo, toLoad) {
315
- let rows = await repo.find({ where: repo.metadata.idMetadata.getIdFilter(...toLoad) }, true);
316
- for (const r of rows) {
317
- repo.addToCache(r);
318
- }
319
- }
320
- let result = await Promise.all(rawRows.map(async (r) => await this.mapRawDataToResult(r, loadFields)));
321
- return result;
322
- }
323
- async mapRawDataToResult(r, loadFields) {
324
- if (!r)
325
- return undefined;
326
- let x = new this.entity(this.remult);
327
- let helper = new rowHelperImplementation(this._info, x, this, this.edp, this.remult, false);
328
- Object.defineProperty(x, entityMember, {
329
- get: () => helper
330
- });
331
- await helper.loadDataFrom(r, loadFields);
332
- helper.saveOriginalData();
333
- return x;
334
- }
335
- toJson(item) {
336
- if (Array.isArray(item))
337
- return item.map(x => this.toJson(x));
338
- return this.getEntityRef(item).toApiJson(true);
339
- }
340
- fromJson(json, newRow) {
341
- if (json === null || json === undefined)
342
- return json;
343
- if (Array.isArray(json))
344
- return json.map(item => this.fromJson(item));
345
- let result = new this.entity(this.remult);
346
- for (const col of this.fieldsOf(json)) {
347
- let ei = getEntitySettings(col.valueType, false);
348
- if (ei) {
349
- result[col.key] = this.remult.repo(col.valueType).fromJson(json[col.key]);
350
- }
351
- else {
352
- if (json[col.key] !== undefined) {
353
- result[col.key] = col.valueConverter.fromJson(json[col.key]);
354
- }
355
- }
356
- }
357
- this.fixTypes(result);
358
- if (newRow) {
359
- return this.create(result);
360
- }
361
- else {
362
- let row = new rowHelperImplementation(this._info, result, this, this.edp, this.remult, false);
363
- Object.defineProperty(result, entityMember, {
364
- get: () => row
365
- });
366
- row.saveOriginalData();
367
- return result;
368
- }
369
- }
370
- async count(where) {
371
- return this.edp.count(await this.translateWhereToFilter(where));
372
- }
373
- cache = new Map();
374
- async findFirst(where, options, skipOrderByAndLimit = false) {
375
- if (!options)
376
- options = {};
377
- if (where) {
378
- if (options.where) {
379
- let w = options.where;
380
- options.where = {
381
- $and: [
382
- w,
383
- where
384
- ]
385
- };
386
- }
387
- else
388
- options.where = where;
389
- }
390
- let r;
391
- let cacheInfo;
392
- if (options.useCache) {
393
- let f = await entityFilterToJson(this.metadata, options.where);
394
- let key = JSON.stringify(f);
395
- cacheInfo = this.cache.get(key);
396
- if (cacheInfo !== undefined) {
397
- if (cacheInfo.value && this.getEntityRef(cacheInfo.value).wasDeleted()) {
398
- cacheInfo = undefined;
399
- this.cache.delete(key);
400
- }
401
- else
402
- return this.cache.get(key).promise;
403
- }
404
- else {
405
- cacheInfo = {
406
- value: undefined,
407
- promise: undefined
408
- };
409
- this.cache.set(key, cacheInfo);
410
- }
411
- }
412
- r = this.find({ ...options, limit: 1 }, skipOrderByAndLimit).then(async (items) => {
413
- let r = undefined;
414
- if (items.length > 0)
415
- r = items[0];
416
- if (!r && options.createIfNotFound) {
417
- r = this.create();
418
- if (options.where) {
419
- await __updateEntityBasedOnWhere(this.metadata, options.where, r);
420
- }
421
- }
422
- return r;
423
- });
424
- if (cacheInfo) {
425
- cacheInfo.promise = r = r.then(r => {
426
- cacheInfo.value = r;
427
- return r;
428
- });
429
- }
430
- return r;
431
- }
432
- fieldsOf(item) {
433
- let keys = Object.keys(item);
434
- return this.metadata.fields.toArray().filter(x => keys.includes(x.key));
435
- }
436
- create(item) {
437
- let r = new this.entity(this.remult);
438
- if (item) {
439
- for (const field of this.fieldsOf(item)) {
440
- r[field.key] = item[field.key];
441
- }
442
- this.fixTypes(r);
443
- }
444
- let z = this.getEntityRef(r);
445
- return r;
446
- }
447
- async fixTypes(item) {
448
- for (const field of this.fieldsOf(item)) {
449
- const val = item[field.key];
450
- if (val !== null && val !== undefined) {
451
- if (field.valueType === Date && !(val instanceof Date))
452
- item[field.key] = field.valueConverter.fromJson(field.valueConverter.toJson(val));
453
- else
454
- for (const [type, typeName] of [[String, "string"], [Number, "number"], [Boolean, "boolean"]]) {
455
- if (field.valueType === type && typeof val !== typeName)
456
- item[field.key] = field.valueConverter.fromJson(field.valueConverter.toJson(val));
457
- }
458
- }
459
- }
460
- return item;
461
- }
462
- findId(id, options) {
463
- if (id === null || id === undefined)
464
- return null;
465
- if (typeof id !== "string" && typeof id !== "number")
466
- throw new Error("id can be either number or string, but got: " + typeof (id));
467
- return this.findFirst({}, {
468
- useCache: true,
469
- ...options,
470
- where: this.metadata.idMetadata.getIdFilter(id),
471
- }, true);
472
- }
473
- /* @internal*/
474
- async translateWhereToFilter(where) {
475
- if (this.metadata.options.backendPrefilter && isBackend()) {
476
- let z = where;
477
- where = {
478
- $and: [
479
- z, await Filter.resolve(this.metadata.options.backendPrefilter)
480
- ]
481
- };
482
- }
483
- let r = await Filter.fromEntityFilter(this.metadata, where);
484
- if (r && !this.dataProvider.isProxy) {
485
- r = await Filter.translateCustomWhere(r, this.metadata, this.remult);
486
- }
487
- return r;
488
- }
489
- }
490
- export function __updateEntityBasedOnWhere(entityDefs, where, r) {
491
- let w = Filter.fromEntityFilter(entityDefs, where);
492
- const emptyFunction = () => { };
493
- if (w) {
494
- w.__applyToConsumer({
495
- custom: emptyFunction,
496
- databaseCustom: emptyFunction,
497
- containsCaseInsensitive: emptyFunction,
498
- isDifferentFrom: emptyFunction,
499
- isEqualTo: (col, val) => {
500
- r[col.key] = val;
501
- },
502
- isGreaterOrEqualTo: emptyFunction,
503
- isGreaterThan: emptyFunction,
504
- isIn: emptyFunction,
505
- isLessOrEqualTo: emptyFunction,
506
- isLessThan: emptyFunction,
507
- isNotNull: emptyFunction,
508
- isNull: emptyFunction,
509
- or: emptyFunction
510
- });
511
- }
512
- }
513
- export const entityInfo = Symbol("entityInfo");
514
- export const entityInfo_key = Symbol("entityInfo_key");
515
- const entityMember = Symbol("entityMember");
516
- export function getEntitySettings(entity, throwError = true) {
517
- if (entity === undefined)
518
- if (throwError) {
519
- throw new Error("Undefined is not an entity :)");
520
- }
521
- else
522
- return undefined;
523
- let info = Reflect.getMetadata(entityInfo, entity);
524
- if (!info && throwError)
525
- throw new Error(entity.prototype.constructor.name + " is not a known entity, did you forget to set @Entity() or did you forget to add the '@' before the call to Entity?");
526
- return info;
527
- }
528
- export function getEntityKey(entity) {
529
- return Reflect.getMetadata(entityInfo_key, entity);
530
- }
531
- export const columnsOfType = new Map();
532
- export function createOldEntity(entity, remult) {
533
- let r = columnsOfType.get(entity);
534
- if (!r)
535
- columnsOfType.set(entity, r = []);
536
- let info = getEntitySettings(entity)(remult);
537
- let key = getEntityKey(entity);
538
- let base = Object.getPrototypeOf(entity);
539
- while (base != null) {
540
- let baseCols = columnsOfType.get(base);
541
- if (baseCols) {
542
- r.unshift(...baseCols.filter(x => !r.find(y => y.key == x.key)));
543
- }
544
- let baseSettingsFactory = getEntitySettings(base, false);
545
- if (baseSettingsFactory) {
546
- let baseSettings = baseSettingsFactory(remult);
547
- info = { ...baseSettings, ...info };
548
- let functions = ["saving", "saved", "deleting", "deleted", "validation"];
549
- for (const key of functions) {
550
- if (baseSettings[key] && baseSettings[key] !== info[key]) {
551
- let x = info[key];
552
- info[key] = async (a, b) => {
553
- await x(a, b);
554
- await baseSettings[key](a, b);
555
- };
556
- }
557
- }
558
- }
559
- base = Object.getPrototypeOf(base);
560
- }
561
- return new EntityFullInfo(prepareColumnInfo(r, remult), info, remult, entity, key);
562
- }
563
- class rowHelperBase {
564
- columnsInfo;
565
- instance;
566
- remult;
567
- _error;
568
- get error() {
569
- this._subscribers?.reportObserved();
570
- return this._error;
571
- }
572
- set error(val) {
573
- this._error = val;
574
- this._subscribers?.reportChanged();
575
- }
576
- constructor(columnsInfo, instance, remult) {
577
- this.columnsInfo = columnsInfo;
578
- this.instance = instance;
579
- this.remult = remult;
580
- {
581
- let fac = remult;
582
- if (fac != null && fac.remultFactory) {
583
- remult = fac.remultFactory();
584
- }
585
- }
586
- for (const col of columnsInfo) {
587
- let ei = getEntitySettings(col.valueType, false);
588
- if (ei && remult) {
589
- let lookup = new LookupColumn(remult.repo(col.valueType), undefined);
590
- this.lookups.set(col.key, lookup);
591
- let val = instance[col.key];
592
- let refImpl;
593
- Object.defineProperty(instance, col.key, {
594
- get: () => {
595
- if (this._subscribers) {
596
- this._subscribers.reportObserved();
597
- if (!refImpl) {
598
- refImpl = this.fields.find(col.key);
599
- if (!refImpl._subscribers) {
600
- refImpl._subscribers = new SubscribableImp();
601
- }
602
- }
603
- refImpl._subscribers.reportObserved();
604
- }
605
- return lookup.item;
606
- },
607
- set: (val) => {
608
- lookup.set(val);
609
- this._subscribers?.reportChanged();
610
- if (!refImpl) {
611
- refImpl = this.fields.find(col.key);
612
- if (!refImpl._subscribers) {
613
- refImpl._subscribers = new SubscribableImp();
614
- }
615
- }
616
- refImpl._subscribers.reportChanged();
617
- },
618
- enumerable: true
619
- });
620
- lookup.set(val);
621
- }
622
- else {
623
- }
624
- }
625
- }
626
- _subscribers;
627
- subscribe(listener) {
628
- this.initSubscribers();
629
- return this._subscribers.subscribe(listener);
630
- }
631
- _isLoading = false;
632
- initSubscribers() {
633
- if (!this._subscribers) {
634
- this._subscribers = new SubscribableImp();
635
- ;
636
- for (const col of this.columnsInfo) {
637
- let ei = getEntitySettings(col.valueType, false);
638
- let refImpl = this.fields.find(col.key);
639
- refImpl._subscribers = new SubscribableImp();
640
- if (ei && this.remult) {
641
- }
642
- else {
643
- let val = this.instance[col.key];
644
- Object.defineProperty(this.instance, col.key, {
645
- get: () => {
646
- this._subscribers.reportObserved();
647
- refImpl._subscribers.reportObserved();
648
- return val;
649
- },
650
- set: (value) => {
651
- val = value;
652
- this._subscribers.reportChanged();
653
- refImpl._subscribers.reportChanged();
654
- },
655
- enumerable: true
656
- });
657
- }
658
- }
659
- }
660
- }
661
- get isLoading() {
662
- this._subscribers?.reportObserved();
663
- return this._isLoading;
664
- }
665
- set isLoading(val) {
666
- this._isLoading = val;
667
- this._subscribers?.reportChanged();
668
- }
669
- lookups = new Map();
670
- async waitLoad() {
671
- await Promise.all([...this.lookups.values()].map(x => x.waitLoad()));
672
- }
673
- errors;
674
- __assertValidity() {
675
- if (!this.hasErrors())
676
- throw this.buildErrorInfoObject();
677
- }
678
- buildErrorInfoObject() {
679
- let error = {
680
- modelState: Object.assign({}, this.errors),
681
- message: this.error
682
- };
683
- if (!error.message) {
684
- for (const col of this.columnsInfo) {
685
- if (this.errors[col.key]) {
686
- error.message = this.fields[col.key].metadata.caption + ": " + this.errors[col.key];
687
- this.error = error.message;
688
- break;
689
- }
690
- }
691
- }
692
- return error;
693
- }
694
- catchSaveErrors(err) {
695
- let e = err;
696
- if (e instanceof Promise) {
697
- return e.then(x => this.catchSaveErrors(x));
698
- }
699
- if (e.error) {
700
- e = e.error;
701
- }
702
- if (e.message)
703
- this.error = e.message;
704
- else if (e.Message)
705
- this.error = e.Message;
706
- else
707
- this.error = e;
708
- let s = e.modelState;
709
- if (!s)
710
- s = e.ModelState;
711
- if (s) {
712
- this.errors = s;
713
- }
714
- throw err;
715
- }
716
- __clearErrorsAndReportChanged() {
717
- this.errors = undefined;
718
- this.error = undefined;
719
- this._reportChangedToEntityAndFields();
720
- }
721
- _reportChangedToEntityAndFields() {
722
- if (this._subscribers) {
723
- this._subscribers.reportChanged();
724
- for (const field of this.fields) {
725
- let ref = field;
726
- ref._subscribers.reportChanged();
727
- }
728
- }
729
- }
730
- hasErrors() {
731
- this._subscribers?.reportObserved();
732
- return !!!this.error && this.errors == undefined;
733
- }
734
- copyDataToObject() {
735
- let d = {};
736
- for (const col of this.columnsInfo) {
737
- let lu = this.lookups.get(col.key);
738
- let val = undefined;
739
- if (lu)
740
- val = lu.id;
741
- else
742
- val = this.instance[col.key];
743
- if (val !== undefined) {
744
- val = col.valueConverter.toJson(val);
745
- if (val !== undefined && val !== null)
746
- val = col.valueConverter.fromJson(JSON.parse(JSON.stringify(val)));
747
- }
748
- d[col.key] = val;
749
- }
750
- return d;
751
- }
752
- originalValues = {};
753
- saveOriginalData() {
754
- this.originalValues = this.copyDataToObject();
755
- this.saveMoreOriginalData();
756
- }
757
- saveMoreOriginalData() {
758
- }
759
- async validate() {
760
- this.__clearErrorsAndReportChanged();
761
- if (classValidatorValidate)
762
- await classValidatorValidate(this.instance, this);
763
- await this.__performColumnAndEntityValidations();
764
- let r = this.hasErrors();
765
- if (!this.hasErrors())
766
- return this.buildErrorInfoObject();
767
- else
768
- return undefined;
769
- }
770
- async __validateEntity() {
771
- this.__clearErrorsAndReportChanged();
772
- if (classValidatorValidate)
773
- await classValidatorValidate(this.instance, this);
774
- await this.__performColumnAndEntityValidations();
775
- this.__assertValidity();
776
- }
777
- async __performColumnAndEntityValidations() {
778
- }
779
- toApiJson(includeRelatedEntities = false) {
780
- let result = {};
781
- for (const col of this.columnsInfo) {
782
- if (!this.remult || col.includeInApi === undefined || this.remult.isAllowed(col.includeInApi)) {
783
- let val;
784
- let lu = this.lookups.get(col.key);
785
- if (lu)
786
- if (includeRelatedEntities)
787
- val = lu.toJson();
788
- else
789
- val = lu.id;
790
- else {
791
- val = this.instance[col.key];
792
- if (!this.remult) {
793
- if (val) {
794
- let eo = getEntitySettings(val.constructor, false);
795
- if (eo) {
796
- val = getEntityRef(val).getId();
797
- }
798
- }
799
- }
800
- }
801
- result[col.key] = col.valueConverter.toJson(val);
802
- }
803
- }
804
- return result;
805
- }
806
- async _updateEntityBasedOnApi(body) {
807
- let keys = Object.keys(body);
808
- for (const col of this.columnsInfo) {
809
- if (keys.includes(col.key))
810
- if (col.includeInApi === undefined || this.remult.isAllowed(col.includeInApi)) {
811
- if (!this.remult || col.allowApiUpdate === undefined || this.remult.isAllowedForInstance(this.instance, col.allowApiUpdate)) {
812
- let lu = this.lookups.get(col.key);
813
- if (lu)
814
- lu.id = body[col.key];
815
- else
816
- this.instance[col.key] = col.valueConverter.fromJson(body[col.key]);
817
- }
818
- }
819
- }
820
- await Promise.all([...this.fields].map(x => x.load()));
821
- }
822
- }
823
- export class rowHelperImplementation extends rowHelperBase {
824
- info;
825
- repository;
826
- edp;
827
- _isNew;
828
- constructor(info, instance, repository, edp, remult, _isNew) {
829
- super(info.columnsInfo, instance, remult);
830
- this.info = info;
831
- this.repository = repository;
832
- this.edp = edp;
833
- this._isNew = _isNew;
834
- this.metadata = info;
835
- if (_isNew) {
836
- for (const col of info.columnsInfo) {
837
- if (col.defaultValue && instance[col.key] === undefined) {
838
- if (typeof col.defaultValue === "function") {
839
- instance[col.key] = col.defaultValue(instance);
840
- }
841
- else if (!instance[col.key])
842
- instance[col.key] = col.defaultValue;
843
- }
844
- }
845
- }
846
- if (this.info.options.entityRefInit)
847
- this.info.options.entityRefInit(this, instance);
848
- if (Remult.entityRefInit)
849
- Remult.entityRefInit(this, instance);
850
- }
851
- get apiUpdateAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiUpdate); }
852
- get apiDeleteAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiDelete); }
853
- get apiInsertAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiInsert); }
854
- metadata;
855
- getId() {
856
- return this.info.idMetadata.getId(this.instance);
857
- }
858
- saveMoreOriginalData() {
859
- this.originalId = this.getId();
860
- }
861
- _wasDeleted = false;
862
- wasDeleted() {
863
- this._subscribers?.reportObserved();
864
- return this._wasDeleted;
865
- }
866
- undoChanges() {
867
- this.loadDataFrom(this.originalValues);
868
- this.__clearErrorsAndReportChanged();
869
- }
870
- async reload() {
871
- await this.edp.find({ where: this.getIdFilter() }).then(async (newData) => {
872
- await this.loadDataFrom(newData[0]);
873
- this.saveOriginalData();
874
- });
875
- this._reportChangedToEntityAndFields();
876
- return this.instance;
877
- }
878
- _columns;
879
- get fields() {
880
- if (!this._columns) {
881
- let _items = [];
882
- let r = {
883
- find: (c) => r[typeof c === "string" ? c : c.key],
884
- [Symbol.iterator]: () => _items[Symbol.iterator](),
885
- toArray: () => _items
886
- };
887
- for (const c of this.info.columnsInfo) {
888
- _items.push(r[c.key] = new FieldRefImplementation(c, this.info.fields[c.key], this.instance, this, this));
889
- }
890
- this._columns = r;
891
- }
892
- return this._columns;
893
- }
894
- _saving = false;
895
- async save() {
896
- try {
897
- if (this._saving)
898
- throw new Error("cannot save while entity is already saving");
899
- this._saving = true;
900
- if (this.wasDeleted())
901
- throw new Error("cannot save a deleted row");
902
- this.isLoading = true;
903
- await this.__validateEntity();
904
- let doNotSave = false;
905
- for (const col of this.fields) {
906
- if (col.metadata.options.saving)
907
- await col.metadata.options.saving(this.instance, col);
908
- }
909
- if (this.info.entityInfo.saving) {
910
- await this.info.entityInfo.saving(this.instance, () => doNotSave = true);
911
- }
912
- this.__assertValidity();
913
- let d = this.copyDataToObject();
914
- let ignoreKeys = [];
915
- for (const field of this.metadata.fields) {
916
- if (field.dbReadOnly) {
917
- d[field.key] = undefined;
918
- ignoreKeys.push(field.key);
919
- let f = this.fields.find(field);
920
- f.value = f.originalValue;
921
- }
922
- }
923
- if (this.info.idMetadata.field instanceof CompoundIdField)
924
- delete (d.id);
925
- let updatedRow;
926
- let isNew = this.isNew();
927
- try {
928
- this._subscribers?.reportChanged();
929
- if (this.isNew()) {
930
- updatedRow = await this.edp.insert(d);
931
- }
932
- else {
933
- let changesOnly = {};
934
- let wasChanged = false;
935
- for (const key in d) {
936
- if (Object.prototype.hasOwnProperty.call(d, key)) {
937
- const element = d[key];
938
- if (this.fields.find(key).valueChanged() && !ignoreKeys.includes(key)) {
939
- changesOnly[key] = element;
940
- wasChanged = true;
941
- }
942
- }
943
- }
944
- if (!wasChanged)
945
- return this.instance;
946
- if (doNotSave) {
947
- updatedRow = (await this.edp.find({ where: this.getIdFilter() }))[0];
948
- }
949
- else {
950
- updatedRow = await this.edp.update(this.id, changesOnly);
951
- }
952
- }
953
- await this.loadDataFrom(updatedRow);
954
- if (this.info.entityInfo.saved)
955
- await this.info.entityInfo.saved(this.instance);
956
- if (this.repository.listeners)
957
- for (const listener of this.repository.listeners.filter(x => x.saved)) {
958
- await listener.saved(this.instance, isNew);
959
- }
960
- await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }]);
961
- this.saveOriginalData();
962
- this._isNew = false;
963
- return this.instance;
964
- }
965
- catch (err) {
966
- await this.catchSaveErrors(err);
967
- }
968
- }
969
- finally {
970
- this.isLoading = false;
971
- this._reportChangedToEntityAndFields();
972
- this._saving = false;
973
- }
974
- }
975
- getIdFilter() {
976
- return Filter.fromEntityFilter(this.metadata, this.repository.metadata.idMetadata.getIdFilter(this.id));
977
- }
978
- async delete() {
979
- this.__clearErrorsAndReportChanged();
980
- if (this.info.entityInfo.deleting)
981
- await this.info.entityInfo.deleting(this.instance);
982
- this.__assertValidity();
983
- try {
984
- await this.edp.delete(this.id);
985
- if (this.info.entityInfo.deleted)
986
- await this.info.entityInfo.deleted(this.instance);
987
- if (this.repository.listeners)
988
- for (const listener of this.repository.listeners.filter(x => x.deleted)) {
989
- await listener.deleted(this.instance);
990
- }
991
- await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }]);
992
- this._wasDeleted = true;
993
- }
994
- catch (err) {
995
- await this.catchSaveErrors(err);
996
- }
997
- }
998
- async loadDataFrom(data, loadItems) {
999
- for (const col of this.info.fields) {
1000
- let lu = this.lookups.get(col.key);
1001
- if (lu) {
1002
- lu.id = data[col.key];
1003
- if (loadItems === undefined) {
1004
- if (!col.options.lazy)
1005
- await lu.waitLoad();
1006
- }
1007
- else {
1008
- if (loadItems.includes(col))
1009
- await lu.waitLoad();
1010
- }
1011
- }
1012
- else
1013
- this.instance[col.key] = data[col.key];
1014
- }
1015
- await this.calcServerExpression();
1016
- this.id = this.getId();
1017
- }
1018
- id;
1019
- originalId;
1020
- getOriginalId() {
1021
- return this.originalId;
1022
- }
1023
- async calcServerExpression() {
1024
- if (isBackend())
1025
- for (const col of this.info.columnsInfo) {
1026
- if (col.serverExpression) {
1027
- this.instance[col.key] = await col.serverExpression(this.instance);
1028
- }
1029
- }
1030
- }
1031
- isNew() {
1032
- this._subscribers?.reportObserved();
1033
- return this._isNew;
1034
- }
1035
- wasChanged() {
1036
- this._subscribers?.reportObserved();
1037
- for (const col of this.fields) {
1038
- if (col.valueChanged())
1039
- return true;
1040
- }
1041
- return false;
1042
- }
1043
- async __performColumnAndEntityValidations() {
1044
- for (const c of this.columnsInfo) {
1045
- if (c.validate) {
1046
- let col = new FieldRefImplementation(c, this.info.fields[c.key], this.instance, this, this);
1047
- await col.__performValidation();
1048
- }
1049
- }
1050
- if (this.info.entityInfo.validation)
1051
- await this.info.entityInfo.validation(this.instance, this);
1052
- if (this.repository.listeners)
1053
- for (const listener of this.repository.listeners.filter(x => x.validating)) {
1054
- await listener.validating(this.instance);
1055
- }
1056
- }
1057
- }
1058
- const controllerColumns = Symbol("controllerColumns");
1059
- function prepareColumnInfo(r, remult) {
1060
- return r.map(x => decorateColumnSettings(x.settings(remult), remult));
1061
- }
1062
- export function getFields(container, remult) {
1063
- return getControllerRef(container, remult).fields;
1064
- }
1065
- export function getControllerRef(container, remultArg) {
1066
- const remultVar = remultArg || defaultRemult;
1067
- let result = container[controllerColumns];
1068
- if (!result)
1069
- result = container[entityMember];
1070
- if (!result) {
1071
- let columnSettings = columnsOfType.get(container.constructor);
1072
- if (!columnSettings)
1073
- columnsOfType.set(container.constructor, columnSettings = []);
1074
- let base = Object.getPrototypeOf(container.constructor);
1075
- while (base != null) {
1076
- let baseCols = columnsOfType.get(base);
1077
- if (baseCols) {
1078
- columnSettings.unshift(...baseCols.filter(x => !columnSettings.find(y => y.key == x.key)));
1079
- }
1080
- base = Object.getPrototypeOf(base);
1081
- }
1082
- container[controllerColumns] = result = new controllerRefImpl(prepareColumnInfo(columnSettings, remultVar), container, remultVar);
1083
- }
1084
- return result;
1085
- }
1086
- export class controllerRefImpl extends rowHelperBase {
1087
- constructor(columnsInfo, instance, remult) {
1088
- super(columnsInfo, instance, remult);
1089
- let _items = [];
1090
- let r = {
1091
- find: (c) => r[typeof c === "string" ? c : c.key],
1092
- [Symbol.iterator]: () => _items[Symbol.iterator](),
1093
- toArray: () => _items
1094
- };
1095
- for (const col of columnsInfo) {
1096
- _items.push(r[col.key] = new FieldRefImplementation(col, new columnDefsImpl(col, undefined, remult), instance, undefined, this));
1097
- }
1098
- this.fields = r;
1099
- }
1100
- async __performColumnAndEntityValidations() {
1101
- for (const col of this.fields) {
1102
- if (col instanceof FieldRefImplementation) {
1103
- await col.__performValidation();
1104
- }
1105
- }
1106
- }
1107
- fields;
1108
- }
1109
- export class FieldRefImplementation {
1110
- settings;
1111
- metadata;
1112
- container;
1113
- helper;
1114
- rowBase;
1115
- constructor(settings, metadata, container, helper, rowBase) {
1116
- this.settings = settings;
1117
- this.metadata = metadata;
1118
- this.container = container;
1119
- this.helper = helper;
1120
- this.rowBase = rowBase;
1121
- this.target = this.settings.target;
1122
- this.entityRef = this.helper;
1123
- }
1124
- _subscribers;
1125
- subscribe(listener) {
1126
- if (!this._subscribers) {
1127
- this.rowBase.initSubscribers();
1128
- }
1129
- return this._subscribers.subscribe(listener);
1130
- }
1131
- valueIsNull() {
1132
- this.reportObserved();
1133
- let lu = this.rowBase.lookups.get(this.metadata.key);
1134
- if (lu) {
1135
- return lu.id === undefined || lu.id === null;
1136
- }
1137
- return this.value === null;
1138
- }
1139
- originalValueIsNull() {
1140
- this.reportObserved();
1141
- let lu = this.rowBase.lookups.get(this.metadata.key);
1142
- return this.rawOriginalValue() === null;
1143
- }
1144
- async load() {
1145
- let lu = this.rowBase.lookups.get(this.metadata.key);
1146
- if (lu) {
1147
- if (this.valueChanged()) {
1148
- await lu.waitLoadOf(this.rawOriginalValue());
1149
- }
1150
- return await lu.waitLoad();
1151
- }
1152
- return this.value;
1153
- }
1154
- target;
1155
- reportObserved() {
1156
- this._subscribers?.reportObserved();
1157
- this.rowBase._subscribers?.reportObserved();
1158
- }
1159
- reportChanged() {
1160
- this._subscribers?.reportChanged();
1161
- this.rowBase._subscribers?.reportChanged();
1162
- }
1163
- get error() {
1164
- this.reportObserved();
1165
- if (!this.rowBase.errors)
1166
- return undefined;
1167
- return this.rowBase.errors[this.metadata.key];
1168
- }
1169
- set error(error) {
1170
- if (!this.rowBase.errors)
1171
- this.rowBase.errors = {};
1172
- this.rowBase.errors[this.metadata.key] = error;
1173
- this.reportChanged();
1174
- }
1175
- get displayValue() {
1176
- this.reportObserved();
1177
- if (this.value != undefined) {
1178
- if (this.settings.displayValue)
1179
- return this.settings.displayValue(this.container, this.value);
1180
- else if (this.metadata.valueConverter.displayValue)
1181
- return this.metadata.valueConverter.displayValue(this.value);
1182
- else
1183
- return this.value.toString();
1184
- }
1185
- return "";
1186
- }
1187
- ;
1188
- get value() { return this.container[this.metadata.key]; }
1189
- ;
1190
- set value(value) { this.container[this.metadata.key] = value; }
1191
- ;
1192
- get originalValue() {
1193
- this.reportObserved();
1194
- let lu = this.rowBase.lookups.get(this.metadata.key);
1195
- if (lu)
1196
- return lu.get(this.rawOriginalValue());
1197
- return this.rowBase.originalValues[this.metadata.key];
1198
- }
1199
- ;
1200
- rawOriginalValue() {
1201
- return this.rowBase.originalValues[this.metadata.key];
1202
- }
1203
- setId(id) {
1204
- this.value = id;
1205
- }
1206
- getId() {
1207
- let lu = this.rowBase.lookups.get(this.metadata.key);
1208
- if (lu)
1209
- return lu.id != undefined ? lu.id : null;
1210
- return this.value;
1211
- }
1212
- get inputValue() {
1213
- this.reportObserved();
1214
- let lu = this.rowBase.lookups.get(this.metadata.key);
1215
- if (lu)
1216
- return lu.id != undefined ? lu.id.toString() : null;
1217
- return this.metadata.valueConverter.toInput(this.value, this.settings.inputType);
1218
- }
1219
- set inputValue(val) {
1220
- let lu = this.rowBase.lookups.get(this.metadata.key);
1221
- if (lu) {
1222
- lu.setId(val);
1223
- }
1224
- else
1225
- this.value = this.metadata.valueConverter.fromInput(val, this.settings.inputType);
1226
- }
1227
- ;
1228
- valueChanged() {
1229
- this.reportObserved();
1230
- let val = this.value;
1231
- let lu = this.rowBase.lookups.get(this.metadata.key);
1232
- if (lu) {
1233
- val = lu.id;
1234
- }
1235
- return JSON.stringify(this.metadata.valueConverter.toJson(this.rowBase.originalValues[this.metadata.key])) != JSON.stringify(this.metadata.valueConverter.toJson(val));
1236
- }
1237
- entityRef;
1238
- async __performValidation() {
1239
- try {
1240
- let x = typeof (this.settings.validate);
1241
- if (Array.isArray(this.settings.validate)) {
1242
- for (const v of this.settings.validate) {
1243
- await v(this.container, this);
1244
- }
1245
- }
1246
- else if (typeof this.settings.validate === 'function')
1247
- await this.settings.validate(this.container, this);
1248
- }
1249
- catch (error) {
1250
- if (typeof error === "string")
1251
- this.error = error;
1252
- else
1253
- this.error = error.message;
1254
- }
1255
- }
1256
- async validate() {
1257
- await this.__performValidation();
1258
- return !!!this.error;
1259
- }
1260
- }
1261
- export function getEntityRef(entity, throwException = true) {
1262
- let x = entity[entityMember];
1263
- if (!x && throwException)
1264
- throw new Error("item " + entity.constructor.name + " was not initialized using a context");
1265
- return x;
1266
- }
1267
- export const CaptionTransformer = {
1268
- transformCaption: (remult, key, caption) => caption
1269
- };
1270
- export function buildCaption(caption, key, remult) {
1271
- let result;
1272
- if (typeof (caption) === "function") {
1273
- if (remult)
1274
- result = caption(remult);
1275
- }
1276
- else if (caption)
1277
- result = caption;
1278
- result = CaptionTransformer.transformCaption(remult, key, result);
1279
- if (result)
1280
- return result;
1281
- if (key)
1282
- return makeTitle(key);
1283
- return '';
1284
- }
1285
- export class columnDefsImpl {
1286
- settings;
1287
- entityDefs;
1288
- remult;
1289
- constructor(settings, entityDefs, remult) {
1290
- this.settings = settings;
1291
- this.entityDefs = entityDefs;
1292
- this.remult = remult;
1293
- this.options = this.settings;
1294
- this.target = this.settings.target;
1295
- this.valueConverter = this.settings.valueConverter;
1296
- this.allowNull = !!this.settings.allowNull;
1297
- this.valueType = this.settings.valueType;
1298
- this.key = this.settings.key;
1299
- this.inputType = this.settings.inputType;
1300
- if (settings.serverExpression)
1301
- this.isServerExpression = true;
1302
- if (typeof (this.settings.allowApiUpdate) === "boolean")
1303
- this.readonly = this.settings.allowApiUpdate;
1304
- if (!this.inputType)
1305
- this.inputType = this.valueConverter.inputType;
1306
- this.caption = buildCaption(settings.caption, settings.key, remult);
1307
- }
1308
- apiUpdateAllowed(item) {
1309
- if (this.options.allowApiUpdate === undefined)
1310
- return true;
1311
- return this.remult.isAllowedForInstance(item, this.options.allowApiUpdate);
1312
- }
1313
- displayValue(item) {
1314
- return this.entityDefs.getEntityMetadataWithoutBreakingTheEntity(item).fields.find(this.key).displayValue;
1315
- }
1316
- get includedInApi() {
1317
- if (this.options.includeInApi === undefined)
1318
- return true;
1319
- return this.remult.isAllowed(this.options.includeInApi); //TODO 2- consolidate other code paths to go through here
1320
- }
1321
- toInput(value, inputType) {
1322
- return this.valueConverter.toInput(value, inputType);
1323
- }
1324
- fromInput(inputValue, inputType) {
1325
- return this.valueConverter.fromInput(inputValue, inputType);
1326
- }
1327
- async getDbName() {
1328
- try {
1329
- if (this.settings.sqlExpression) {
1330
- let result;
1331
- if (typeof this.settings.sqlExpression === "function") {
1332
- result = await this.settings.sqlExpression(this.entityDefs);
1333
- }
1334
- else
1335
- result = this.settings.sqlExpression;
1336
- if (!result)
1337
- return this.settings.dbName;
1338
- return result;
1339
- }
1340
- return this.settings.dbName;
1341
- }
1342
- finally {
1343
- }
1344
- }
1345
- options;
1346
- target;
1347
- readonly;
1348
- valueConverter;
1349
- allowNull;
1350
- caption;
1351
- get dbName() {
1352
- let result;
1353
- if (this.settings.sqlExpression) {
1354
- if (typeof this.settings.sqlExpression === "function") {
1355
- result = this.settings.sqlExpression(this.entityDefs);
1356
- }
1357
- else
1358
- result = this.settings.sqlExpression;
1359
- }
1360
- if (result)
1361
- return result;
1362
- return this.settings.dbName;
1363
- }
1364
- inputType;
1365
- key;
1366
- get dbReadOnly() {
1367
- return this.settings.dbReadOnly;
1368
- }
1369
- ;
1370
- isServerExpression;
1371
- valueType;
1372
- }
1373
- class EntityFullInfo {
1374
- columnsInfo;
1375
- entityInfo;
1376
- remult;
1377
- entityType;
1378
- key;
1379
- options;
1380
- constructor(columnsInfo, entityInfo, remult, entityType, key) {
1381
- this.columnsInfo = columnsInfo;
1382
- this.entityInfo = entityInfo;
1383
- this.remult = remult;
1384
- this.entityType = entityType;
1385
- this.key = key;
1386
- this.options = entityInfo;
1387
- if (this.options.allowApiCrud !== undefined) {
1388
- let crud;
1389
- if ((typeof this.options.allowApiCrud) === "function")
1390
- crud = (_, remult) => this.options.allowApiCrud(remult);
1391
- else
1392
- crud = this.options.allowApiCrud;
1393
- if (this.options.allowApiDelete === undefined)
1394
- this.options.allowApiDelete = crud;
1395
- if (this.options.allowApiInsert === undefined)
1396
- this.options.allowApiInsert = crud;
1397
- if (this.options.allowApiUpdate === undefined)
1398
- this.options.allowApiUpdate = crud;
1399
- if (this.options.allowApiRead === undefined)
1400
- this.options.allowApiRead = this.options.allowApiCrud;
1401
- }
1402
- if (this.options.allowApiRead === undefined)
1403
- this.options.allowApiRead = true;
1404
- if (!this.key)
1405
- this.key = entityType.name;
1406
- if (!entityInfo.dbName)
1407
- entityInfo.dbName = this.key;
1408
- let _items = [];
1409
- let r = {
1410
- find: (c) => r[typeof c === "string" ? c : c.key],
1411
- [Symbol.iterator]: () => _items[Symbol.iterator](),
1412
- toArray: () => _items
1413
- };
1414
- for (const x of columnsInfo) {
1415
- _items.push(r[x.key] = new columnDefsImpl(x, this, remult));
1416
- }
1417
- this.fields = r;
1418
- this.caption = buildCaption(entityInfo.caption, this.key, remult);
1419
- if (entityInfo.id) {
1420
- let r = entityInfo.id(this.fields);
1421
- if (Array.isArray(r)) {
1422
- if (r.length > 1)
1423
- this.idMetadata.field = new CompoundIdField(...r);
1424
- else if (r.length == 1)
1425
- this.idMetadata.field = r[0];
1426
- }
1427
- else
1428
- this.idMetadata.field = r;
1429
- }
1430
- else {
1431
- if (this.fields["id"])
1432
- this.idMetadata.field = this.fields["id"];
1433
- else
1434
- this.idMetadata.field = [...this.fields][0];
1435
- }
1436
- }
1437
- apiUpdateAllowed(item) {
1438
- if (this.options.allowApiUpdate === undefined)
1439
- return false;
1440
- return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiUpdate) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiUpdateAllowed;
1441
- }
1442
- get apiReadAllowed() {
1443
- if (this.options.allowApiRead === undefined)
1444
- return true; //TODO2 - consider that this default may be confusing, since it's different from all others.
1445
- return this.remult.isAllowed(this.options.allowApiRead);
1446
- }
1447
- apiDeleteAllowed(item) {
1448
- if (this.options.allowApiDelete === undefined)
1449
- return false;
1450
- return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiDelete) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiDeleteAllowed;
1451
- }
1452
- apiInsertAllowed(item) {
1453
- if (this.options.allowApiUpdate === undefined)
1454
- return false;
1455
- return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiInsert) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiInsertAllowed;
1456
- }
1457
- getEntityMetadataWithoutBreakingTheEntity(item) {
1458
- let result = getEntityRef(item, false);
1459
- if (result)
1460
- return result;
1461
- return this.remult.repo(this.entityType).getEntityRef({ ...item });
1462
- }
1463
- dbNamePromise;
1464
- getDbName() {
1465
- if (this.dbNamePromise)
1466
- return this.dbNamePromise;
1467
- if (!this.options.sqlExpression) {
1468
- this.dbNamePromise = Promise.resolve(this.options.dbName);
1469
- }
1470
- if (typeof this.options.sqlExpression === "string")
1471
- this.dbNamePromise = Promise.resolve(this.options.sqlExpression);
1472
- else if (typeof this.options.sqlExpression === "function") {
1473
- let r = this.options.sqlExpression(this);
1474
- if (r instanceof Promise)
1475
- this.dbNamePromise = r;
1476
- else if (r)
1477
- this.dbNamePromise = Promise.resolve(r);
1478
- }
1479
- this.dbNamePromise = this.dbNamePromise.then(x => {
1480
- if (!x)
1481
- return this.options.dbName;
1482
- return x;
1483
- });
1484
- return this.dbNamePromise;
1485
- }
1486
- idMetadata = {
1487
- getId: item => {
1488
- if (this.idMetadata.field instanceof CompoundIdField)
1489
- return this.idMetadata.field.getId(item);
1490
- else
1491
- return item[this.idMetadata.field.key];
1492
- },
1493
- field: undefined,
1494
- createIdInFilter: (items) => {
1495
- if (items.length > 0)
1496
- return {
1497
- $or: items.map(x => this.idMetadata.getIdFilter(getEntityRef(x).getId()))
1498
- };
1499
- },
1500
- isIdField: (col) => {
1501
- return col.key == this.idMetadata.field.key;
1502
- },
1503
- getIdFilter: (...ids) => {
1504
- if (this.idMetadata.field instanceof CompoundIdField) {
1505
- let field = this.idMetadata.field;
1506
- return {
1507
- $or: ids.map(x => field.isEqualTo(x))
1508
- };
1509
- }
1510
- if (ids.length == 1)
1511
- return {
1512
- [this.idMetadata.field.key]: ids[0]
1513
- };
1514
- else
1515
- return {
1516
- [this.idMetadata.field.key]: ids
1517
- };
1518
- }
1519
- };
1520
- fields;
1521
- dbName;
1522
- caption;
1523
- }
1524
- export function FieldType(...options) {
1525
- return (target, context) => {
1526
- if (!options) {
1527
- options = [];
1528
- }
1529
- options.splice(0, 0, { valueType: target });
1530
- Reflect.defineMetadata(storableMember, options, target);
1531
- return target;
1532
- };
1533
- }
1534
- export class Fields {
1535
- /**
1536
- * Stored as a JSON.stringify - to store as json use Fields.json
1537
- */
1538
- static object(...options) {
1539
- return Field(undefined, ...options);
1540
- }
1541
- static json(...options) {
1542
- return Field(undefined, {
1543
- valueConverter: {
1544
- fieldTypeInDb: "json"
1545
- }
1546
- }, ...options);
1547
- }
1548
- static dateOnly(...options) {
1549
- return Field(() => Date, {
1550
- valueConverter: ValueConverters.DateOnly
1551
- }, ...options);
1552
- }
1553
- static date(...options) {
1554
- return Field(() => Date, ...options);
1555
- }
1556
- static integer(...options) {
1557
- return Field(() => Number, {
1558
- valueConverter: ValueConverters.Integer
1559
- }, ...options);
1560
- }
1561
- static autoIncrement(...options) {
1562
- return Field(() => Number, {
1563
- allowApiUpdate: false,
1564
- dbReadOnly: true,
1565
- valueConverter: { ...ValueConverters.Integer, fieldTypeInDb: 'autoincrement' }
1566
- }, ...options);
1567
- }
1568
- static number(...options) {
1569
- return Field(() => Number, ...options);
1570
- }
1571
- static createdAt(...options) {
1572
- return Field(() => Date, {
1573
- allowApiUpdate: false,
1574
- saving: (_, ref) => {
1575
- if (getEntityRef(_).isNew())
1576
- ref.value = new Date();
1577
- }
1578
- }, ...options);
1579
- }
1580
- static updatedAt(...options) {
1581
- return Field(() => Date, {
1582
- allowApiUpdate: false,
1583
- saving: (_, ref) => {
1584
- ref.value = new Date();
1585
- }
1586
- }, ...options);
1587
- }
1588
- static uuid(...options) {
1589
- return Field(() => String, {
1590
- allowApiUpdate: false,
1591
- defaultValue: () => uuid(),
1592
- saving: (_, r) => {
1593
- if (!r.value)
1594
- r.value = uuid();
1595
- }
1596
- }, ...options);
1597
- }
1598
- static cuid(...options) {
1599
- return Field(() => String, {
1600
- allowApiUpdate: false,
1601
- defaultValue: () => createId(),
1602
- saving: (_, r) => {
1603
- if (!r.value)
1604
- r.value = createId();
1605
- }
1606
- }, ...options);
1607
- }
1608
- static string(...options) {
1609
- return Field(() => String, ...options);
1610
- }
1611
- static boolean(...options) {
1612
- return Field(() => Boolean, ...options);
1613
- }
1614
- }
1615
- export function isAutoIncrement(f) {
1616
- return f.options?.valueConverter?.fieldTypeInDb === 'autoincrement';
1617
- }
1618
- export function ValueListFieldType(...options) {
1619
- return (type, context) => {
1620
- FieldType(o => {
1621
- o.valueConverter = ValueListInfo.get(type),
1622
- o.displayValue = (item, val) => val?.caption;
1623
- }, ...options)(type, context);
1624
- };
1625
- }
1626
- export class ValueListInfo {
1627
- valueListType;
1628
- static get(type) {
1629
- let r = typeCache.get(type);
1630
- if (!r) {
1631
- r = new ValueListInfo(type);
1632
- typeCache.set(type, r);
1633
- }
1634
- return r;
1635
- }
1636
- byIdMap = new Map();
1637
- values = [];
1638
- isNumeric = false;
1639
- constructor(valueListType) {
1640
- this.valueListType = valueListType;
1641
- for (let member in this.valueListType) {
1642
- let s = this.valueListType[member];
1643
- if (s instanceof this.valueListType) {
1644
- if (s.id === undefined)
1645
- s.id = member;
1646
- if (typeof s.id === 'number')
1647
- this.isNumeric = true;
1648
- if (s.caption === undefined)
1649
- s.caption = makeTitle(s.id !== undefined ? s.id.toString() : member);
1650
- this.byIdMap.set(s.id, s);
1651
- this.values.push(s);
1652
- }
1653
- }
1654
- if (this.isNumeric) {
1655
- this.fieldTypeInDb = 'integer';
1656
- }
1657
- var options = Reflect.getMetadata(storableMember, this.valueListType);
1658
- if (options) {
1659
- for (const op of options) {
1660
- if (op?.getValues) {
1661
- this.values.splice(0, this.values.length, ...op.getValues());
1662
- this.byIdMap.clear();
1663
- this.values.forEach(s => {
1664
- if (s.caption === undefined && s.id !== undefined)
1665
- s.caption = makeTitle(s.id);
1666
- this.byIdMap.set(s.id, s);
1667
- });
1668
- }
1669
- }
1670
- if (this.values.find(s => s.id === undefined))
1671
- throw new Error(`ValueType ${this.valueListType} has values without an id`);
1672
- }
1673
- else
1674
- throw new Error(`ValueType not yet initialized, did you forget to call @ValueListFieldType on ` + valueListType);
1675
- }
1676
- getValues() {
1677
- return this.values;
1678
- }
1679
- byId(key) {
1680
- if (this.isNumeric)
1681
- key = +key;
1682
- return this.byIdMap.get(key);
1683
- }
1684
- fromJson(val) {
1685
- return this.byId(val);
1686
- }
1687
- toJson(val) {
1688
- if (!val)
1689
- return undefined;
1690
- return val.id;
1691
- }
1692
- fromDb(val) {
1693
- return this.fromJson(val);
1694
- }
1695
- toDb(val) {
1696
- return this.toJson(val);
1697
- }
1698
- toInput(val, inputType) {
1699
- return this.toJson(val);
1700
- }
1701
- fromInput(val, inputType) {
1702
- return this.fromJson(val);
1703
- }
1704
- displayValue(val) {
1705
- if (!val)
1706
- return '';
1707
- return val.caption;
1708
- }
1709
- fieldTypeInDb;
1710
- inputType;
1711
- }
1712
- const typeCache = new Map();
1713
- export function getValueList(type) {
1714
- {
1715
- const fr = type?.metadata?.valueType;
1716
- if (fr)
1717
- return ValueListInfo.get(fr).getValues();
1718
- }
1719
- {
1720
- const fr = type?.valueType;
1721
- if (fr)
1722
- return ValueListInfo.get(fr).getValues();
1723
- }
1724
- return ValueListInfo.get(type).getValues();
1725
- }
1726
- /**Decorates fields that should be used as fields.
1727
- * for more info see: [Field Types](https://remult.dev/docs/field-types.html)
1728
- *
1729
- * FieldOptions can be set in two ways:
1730
- * @example
1731
- * // as an object
1732
- * @Fields.string({ includeInApi:false })
1733
- * title='';
1734
- * @example
1735
- * // as an arrow function that receives `remult` as a parameter
1736
- * @Fields.string((options,remult) => options.includeInApi = true)
1737
- * title='';
1738
- */
1739
- export function Field(valueType, ...options) {
1740
- return (target,
1741
- //@ts-ignore
1742
- context, c) => {
1743
- const key = typeof context === "string" ? context : context.name.toString();
1744
- let factory = (remult) => {
1745
- let r = buildOptions(options, remult);
1746
- if (!r.valueType && valueType) {
1747
- r.valueType = valueType();
1748
- }
1749
- if (!r.key) {
1750
- r.key = key;
1751
- }
1752
- if (!r.dbName)
1753
- r.dbName = r.key;
1754
- let type = r.valueType;
1755
- if (!type) {
1756
- type = Reflect.getMetadata("design:type", target, key);
1757
- r.valueType = type;
1758
- }
1759
- if (!r.target)
1760
- r.target = target;
1761
- return r;
1762
- };
1763
- let names = columnsOfType.get(target.constructor);
1764
- if (!names) {
1765
- names = [];
1766
- columnsOfType.set(target.constructor, names);
1767
- }
1768
- let set = names.find(x => x.key == key);
1769
- if (!set)
1770
- names.push({
1771
- key,
1772
- settings: factory,
1773
- });
1774
- else {
1775
- let prev = set.settings;
1776
- set.settings = (c) => {
1777
- let prevO = prev(c);
1778
- let curr = factory(c);
1779
- return Object.assign(prevO, curr);
1780
- };
1781
- }
1782
- };
1783
- }
1784
- export const storableMember = Symbol("storableMember");
1785
- function buildOptions(options, remult) {
1786
- let r = {};
1787
- for (const o of options) {
1788
- if (o) {
1789
- if (typeof o === "function")
1790
- o(r, remult);
1791
- else
1792
- Object.assign(r, o);
1793
- }
1794
- }
1795
- return r;
1796
- }
1797
- export function decorateColumnSettings(settings, remult) {
1798
- if (settings.valueType) {
1799
- let settingsOnTypeLevel = Reflect.getMetadata(storableMember, settings.valueType);
1800
- if (settingsOnTypeLevel) {
1801
- settings = {
1802
- ...buildOptions(settingsOnTypeLevel, remult),
1803
- ...settings
1804
- };
1805
- }
1806
- }
1807
- if (settings.valueType == String) {
1808
- let x = settings;
1809
- if (!settings.valueConverter)
1810
- x.valueConverter = ValueConverters.String;
1811
- }
1812
- if (settings.valueType == Number) {
1813
- let x = settings;
1814
- if (!settings.valueConverter)
1815
- x.valueConverter = ValueConverters.Number;
1816
- }
1817
- if (settings.valueType == Date) {
1818
- let x = settings;
1819
- if (!settings.valueConverter) {
1820
- x.valueConverter = ValueConverters.Date;
1821
- }
1822
- }
1823
- if (settings.valueType == Boolean) {
1824
- let x = settings;
1825
- if (!x.valueConverter)
1826
- x.valueConverter = ValueConverters.Boolean;
1827
- }
1828
- if (!settings.valueConverter) {
1829
- let ei = getEntitySettings(settings.valueType, false);
1830
- if (ei) {
1831
- settings.valueConverter = {
1832
- toDb: x => x,
1833
- fromDb: x => x
1834
- };
1835
- }
1836
- else
1837
- settings.valueConverter = ValueConverters.Default;
1838
- }
1839
- if (!settings.valueConverter.toJson) {
1840
- settings.valueConverter.toJson = x => x;
1841
- }
1842
- if (!settings.valueConverter.fromJson) {
1843
- settings.valueConverter.fromJson = x => x;
1844
- }
1845
- if (!settings.valueConverter.toDb) {
1846
- settings.valueConverter.toDb = x => settings.valueConverter.toJson(x);
1847
- }
1848
- if (!settings.valueConverter.fromDb) {
1849
- settings.valueConverter.fromDb = x => settings.valueConverter.fromJson(x);
1850
- }
1851
- if (!settings.valueConverter.toInput) {
1852
- settings.valueConverter.toInput = x => settings.valueConverter.toJson(x);
1853
- }
1854
- if (!settings.valueConverter.fromInput) {
1855
- settings.valueConverter.fromInput = x => settings.valueConverter.fromJson(x);
1856
- }
1857
- return settings;
1858
- }
1859
- //[ ] YONI - in Typescript 5 I do get the name of the class, what would we like to change because of that?
1860
- /**Decorates classes that should be used as entities.
1861
- * Receives a key and an array of EntityOptions.
1862
- * @example
1863
- * import { Entity, Fields } from "remult";
1864
- * @Entity("tasks", {
1865
- * allowApiCrud: true
1866
- * })
1867
- * export class Task {
1868
- * @Fields.uuid()
1869
- * id!: string;
1870
- * @Fields.string()
1871
- * title = '';
1872
- * @Fields.boolean()
1873
- * completed = false;
1874
- * }
1875
- * @note
1876
- * EntityOptions can be set in two ways:
1877
- * @example
1878
- * // as an object
1879
- * @Entity("tasks",{ allowApiCrud:true })
1880
- * @example
1881
- * // as an arrow function that receives `remult` as a parameter
1882
- * @Entity("tasks", (options,remult) => options.allowApiCrud = true)
1883
- */
1884
- export function Entity(key, ...options) {
1885
- return (target,
1886
- //@ts-ignore
1887
- info) => {
1888
- for (const rawFilterMember in target) {
1889
- if (Object.prototype.hasOwnProperty.call(target, rawFilterMember)) {
1890
- const element = target[rawFilterMember];
1891
- if (element?.rawFilterInfo?.rawFilterTranslator) {
1892
- if (!element.rawFilterInfo.key)
1893
- element.rawFilterInfo.key = rawFilterMember;
1894
- }
1895
- }
1896
- }
1897
- let factory = remult => {
1898
- let r = {};
1899
- for (const o of options) {
1900
- if (o) {
1901
- if (typeof o === "function")
1902
- o(r, remult);
1903
- else
1904
- Object.assign(r, o);
1905
- }
1906
- }
1907
- let base = Object.getPrototypeOf(target);
1908
- if (base) {
1909
- let baseFactory = getEntitySettings(base, false);
1910
- if (baseFactory) {
1911
- let opt = baseFactory(remult);
1912
- if (opt) {
1913
- r = {
1914
- ...opt,
1915
- ...r
1916
- };
1917
- }
1918
- }
1919
- }
1920
- return r;
1921
- };
1922
- allEntities.push(target);
1923
- setControllerSettings(target, { key });
1924
- Reflect.defineMetadata(entityInfo, factory, target);
1925
- Reflect.defineMetadata(entityInfo_key, key, target);
1926
- return target;
1927
- };
1928
- }
1929
- export class EntityBase {
1930
- get _() { return getEntityRef(this); }
1931
- save() { return this._.save(); }
1932
- assign(values) {
1933
- assign(this, values);
1934
- return this;
1935
- }
1936
- delete() { return this._.delete(); }
1937
- isNew() { return this._.isNew(); }
1938
- get $() { return this._.fields; }
1939
- }
1940
- export class IdEntity extends EntityBase {
1941
- @Fields.uuid()
1942
- id;
1943
- }
1944
- export class ControllerBase {
1945
- remult;
1946
- constructor(remult) {
1947
- this.remult = remult || defaultRemult;
1948
- }
1949
- assign(values) {
1950
- assign(this, values);
1951
- return this;
1952
- }
1953
- get $() { return getFields(this, this.remult); }
1954
- get _() { return getControllerRef(this, this.remult); }
1955
- }
1956
- class QueryResultImpl {
1957
- options;
1958
- repo;
1959
- constructor(options, repo) {
1960
- this.options = options;
1961
- this.repo = repo;
1962
- if (!this.options)
1963
- this.options = {};
1964
- if (!this.options.pageSize) {
1965
- this.options.pageSize = queryConfig.defaultPageSize;
1966
- }
1967
- }
1968
- _count = undefined;
1969
- async getPage(page) {
1970
- if (page < 1)
1971
- page = 1;
1972
- return this.repo.find({
1973
- where: this.options.where,
1974
- orderBy: this.options.orderBy,
1975
- limit: this.options.pageSize,
1976
- page: page,
1977
- load: this.options.load
1978
- });
1979
- }
1980
- async count() {
1981
- if (this._count === undefined)
1982
- this._count = await this.repo.count(this.options.where);
1983
- return this._count;
1984
- }
1985
- async forEach(what) {
1986
- let i = 0;
1987
- for await (const x of this) {
1988
- await what(x);
1989
- i++;
1990
- }
1991
- return i;
1992
- }
1993
- async paginator(pNextPageFilter) {
1994
- this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, this.options.orderBy);
1995
- let items = await this.repo.find({
1996
- where: { $and: [this.options.where, pNextPageFilter] },
1997
- orderBy: this.options.orderBy,
1998
- limit: this.options.pageSize,
1999
- load: this.options.load
2000
- });
2001
- let nextPage = undefined;
2002
- let hasNextPage = items.length == this.options.pageSize;
2003
- if (hasNextPage) {
2004
- let nextPageFilter = await this.repo.createAfterFilter(this.options.orderBy, items[items.length - 1]);
2005
- nextPage = () => this.paginator(nextPageFilter);
2006
- }
2007
- return {
2008
- count: () => this.count(),
2009
- hasNextPage,
2010
- items,
2011
- nextPage
2012
- };
2013
- }
2014
- [Symbol.asyncIterator]() {
2015
- if (!this.options.where) {
2016
- this.options.where = {};
2017
- }
2018
- let ob = this.options.orderBy;
2019
- this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, ob);
2020
- let itemIndex = -1;
2021
- let currentPage = undefined;
2022
- let itStrategy;
2023
- let j = 0;
2024
- itStrategy = async () => {
2025
- if (this.options.progress) {
2026
- this.options.progress.progress(j++ / await this.count());
2027
- }
2028
- if (currentPage === undefined || itemIndex == currentPage.items.length) {
2029
- if (currentPage && !currentPage.hasNextPage)
2030
- return { value: undefined, done: true };
2031
- let prev = currentPage;
2032
- if (currentPage)
2033
- currentPage = await currentPage.nextPage();
2034
- else
2035
- currentPage = await this.paginator();
2036
- itemIndex = 0;
2037
- if (currentPage.items.length == 0) {
2038
- return { value: undefined, done: true };
2039
- }
2040
- else {
2041
- if (prev?.items.length > 0) {
2042
- if (this.repo.getEntityRef(prev.items[0]).getId() == this.repo.getEntityRef(currentPage.items[0]).getId())
2043
- throw new Error("pagination failure, returned same first row");
2044
- }
2045
- }
2046
- }
2047
- if (itemIndex < currentPage.items.length)
2048
- return { value: currentPage.items[itemIndex++], done: false };
2049
- };
2050
- return {
2051
- next: async () => {
2052
- let r = itStrategy();
2053
- return r;
2054
- }
2055
- };
2056
- }
2057
- }
2058
- class cacheEntityInfo {
2059
- value = {};
2060
- promise;
2061
- }
2062
- class SubscribableImp {
2063
- reportChanged() {
2064
- if (this._subscribers)
2065
- this._subscribers.forEach(x => x.reportChanged());
2066
- }
2067
- reportObserved() {
2068
- if (this._subscribers)
2069
- this._subscribers.forEach(x => x.reportObserved());
2070
- }
2071
- _subscribers;
2072
- subscribe(listener) {
2073
- let list;
2074
- if (typeof listener === "function")
2075
- list = {
2076
- reportChanged: () => listener(),
2077
- reportObserved: () => { }
2078
- };
2079
- else
2080
- list = listener;
2081
- if (!this._subscribers) {
2082
- this._subscribers = [];
2083
- }
2084
- this._subscribers.push(list);
2085
- return () => this._subscribers = this._subscribers.filter(x => x != list);
2086
- }
2087
- }
2088
- export function getEntityMetadata(entity) {
2089
- if (entity.metadata)
2090
- return entity.metadata;
2091
- const settings = getEntitySettings(entity, false);
2092
- if (settings) {
2093
- return defaultRemult.repo(entity).metadata;
2094
- }
2095
- return entity;
2096
- }
2097
- export function getRepository(entity) {
2098
- const settings = getEntitySettings(entity, false);
2099
- if (settings) {
2100
- return defaultRemult.repo(entity);
2101
- }
2102
- return entity;
2103
- }
2104
- export function Field1(valueType) {
2105
- return (target, context) => { };
2106
- }
2107
- export function createdAt() {
2108
- return Field1(() => Date);
2109
- }