lemon-core 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +13 -21
  2. package/dist/common/test-helper.js +19 -11
  3. package/dist/common/test-helper.js.map +1 -1
  4. package/dist/controllers/dummy-controller.d.ts +1 -1
  5. package/dist/controllers/dummy-controller.js +2 -2
  6. package/dist/controllers/dummy-controller.js.map +1 -1
  7. package/dist/controllers/general-api-controller.d.ts +1 -1
  8. package/dist/controllers/general-api-controller.js +5 -5
  9. package/dist/controllers/general-api-controller.js.map +1 -1
  10. package/dist/controllers/general-controller.js +2 -1
  11. package/dist/controllers/general-controller.js.map +1 -1
  12. package/dist/controllers/index.js +6 -2
  13. package/dist/controllers/index.js.map +1 -1
  14. package/dist/cores/api/api-service.d.ts +239 -0
  15. package/dist/cores/api/api-service.js +674 -0
  16. package/dist/cores/api/api-service.js.map +1 -0
  17. package/dist/cores/api/index.d.ts +10 -0
  18. package/dist/cores/api/index.js +27 -0
  19. package/dist/cores/api/index.js.map +1 -0
  20. package/dist/cores/api-service.js +19 -18
  21. package/dist/cores/api-service.js.map +1 -1
  22. package/dist/cores/aws/aws-kms-service.d.ts +53 -2
  23. package/dist/cores/aws/aws-kms-service.js +112 -29
  24. package/dist/cores/aws/aws-kms-service.js.map +1 -1
  25. package/dist/cores/aws/aws-s3-service.js +19 -18
  26. package/dist/cores/aws/aws-s3-service.js.map +1 -1
  27. package/dist/cores/aws/aws-sns-service.js +10 -10
  28. package/dist/cores/aws/aws-sns-service.js.map +1 -1
  29. package/dist/cores/aws/aws-sqs-service.js +12 -12
  30. package/dist/cores/aws/aws-sqs-service.js.map +1 -1
  31. package/dist/cores/aws/index.js +1 -1
  32. package/dist/cores/cache/cache-service.d.ts +440 -0
  33. package/dist/cores/cache/cache-service.js +967 -0
  34. package/dist/cores/cache/cache-service.js.map +1 -0
  35. package/dist/cores/cache/index.d.ts +10 -0
  36. package/dist/cores/cache/index.js +27 -0
  37. package/dist/cores/cache/index.js.map +1 -0
  38. package/dist/cores/cache-service.d.ts +54 -18
  39. package/dist/cores/cache-service.js +56 -45
  40. package/dist/cores/cache-service.js.map +1 -1
  41. package/dist/cores/config/config-service.js +11 -10
  42. package/dist/cores/config/config-service.js.map +1 -1
  43. package/dist/cores/core-services.js +6 -2
  44. package/dist/cores/core-services.js.map +1 -1
  45. package/dist/cores/core-types.d.ts +42 -12
  46. package/dist/cores/dynamo/dynamo-query-service.d.ts +52 -0
  47. package/dist/cores/dynamo/dynamo-query-service.js +127 -0
  48. package/dist/cores/dynamo/dynamo-query-service.js.map +1 -0
  49. package/dist/cores/dynamo/dynamo-scan-service.d.ts +70 -0
  50. package/dist/cores/dynamo/dynamo-scan-service.js +164 -0
  51. package/dist/cores/dynamo/dynamo-scan-service.js.map +1 -0
  52. package/dist/cores/dynamo/dynamo-service.d.ts +192 -0
  53. package/dist/cores/dynamo/dynamo-service.js +525 -0
  54. package/dist/cores/dynamo/dynamo-service.js.map +1 -0
  55. package/dist/cores/dynamo/index.d.ts +12 -0
  56. package/dist/cores/dynamo/index.js +29 -0
  57. package/dist/cores/dynamo/index.js.map +1 -0
  58. package/dist/cores/dynamo-query-service.js +7 -7
  59. package/dist/cores/dynamo-query-service.js.map +1 -1
  60. package/dist/cores/dynamo-scan-service.js +8 -8
  61. package/dist/cores/dynamo-scan-service.js.map +1 -1
  62. package/dist/cores/dynamo-service.js +19 -19
  63. package/dist/cores/dynamo-service.js.map +1 -1
  64. package/dist/cores/elastic/elastic6-query-service.d.ts +104 -0
  65. package/dist/cores/elastic/elastic6-query-service.js +510 -0
  66. package/dist/cores/elastic/elastic6-query-service.js.map +1 -0
  67. package/dist/cores/elastic/elastic6-service.d.ts +273 -0
  68. package/dist/cores/elastic/elastic6-service.js +903 -0
  69. package/dist/cores/elastic/elastic6-service.js.map +1 -0
  70. package/dist/cores/elastic/hangul-service.d.ts +102 -0
  71. package/dist/cores/elastic/hangul-service.js +205 -0
  72. package/dist/cores/elastic/hangul-service.js.map +1 -0
  73. package/dist/cores/elastic/index.d.ts +12 -0
  74. package/dist/cores/elastic/index.js +29 -0
  75. package/dist/cores/elastic/index.js.map +1 -0
  76. package/dist/cores/elastic6-query-service.js +7 -7
  77. package/dist/cores/elastic6-query-service.js.map +1 -1
  78. package/dist/cores/elastic6-service.js +57 -65
  79. package/dist/cores/elastic6-service.js.map +1 -1
  80. package/dist/cores/hangul-service.d.ts +17 -3
  81. package/dist/cores/hangul-service.js +17 -8
  82. package/dist/cores/hangul-service.js.map +1 -1
  83. package/dist/cores/http-storage-service.js +1 -1
  84. package/dist/cores/http-storage-service.js.map +1 -1
  85. package/dist/cores/index.d.ts +9 -15
  86. package/dist/cores/index.js +14 -15
  87. package/dist/cores/index.js.map +1 -1
  88. package/dist/cores/lambda/index.js +8 -4
  89. package/dist/cores/lambda/index.js.map +1 -1
  90. package/dist/cores/lambda/lambda-cognito-handler.js +2 -2
  91. package/dist/cores/lambda/lambda-cognito-handler.js.map +1 -1
  92. package/dist/cores/lambda/lambda-cron-handler.js +2 -2
  93. package/dist/cores/lambda/lambda-cron-handler.js.map +1 -1
  94. package/dist/cores/lambda/lambda-dynamo-stream-handler.d.ts +2 -2
  95. package/dist/cores/lambda/lambda-dynamo-stream-handler.js +20 -20
  96. package/dist/cores/lambda/lambda-dynamo-stream-handler.js.map +1 -1
  97. package/dist/cores/lambda/lambda-handler.js +12 -11
  98. package/dist/cores/lambda/lambda-handler.js.map +1 -1
  99. package/dist/cores/lambda/lambda-notification-handler.js +10 -10
  100. package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
  101. package/dist/cores/lambda/lambda-sns-handler.js +13 -13
  102. package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
  103. package/dist/cores/lambda/lambda-sqs-handler.js +13 -13
  104. package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
  105. package/dist/cores/lambda/lambda-web-handler.d.ts +158 -8
  106. package/dist/cores/lambda/lambda-web-handler.js +330 -119
  107. package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
  108. package/dist/cores/lambda/lambda-wss-handler.js +16 -12
  109. package/dist/cores/lambda/lambda-wss-handler.js.map +1 -1
  110. package/dist/cores/model-manager.js +11 -11
  111. package/dist/cores/model-manager.js.map +1 -1
  112. package/dist/cores/protocol/protocol-service.d.ts +4 -0
  113. package/dist/cores/protocol/protocol-service.js +46 -40
  114. package/dist/cores/protocol/protocol-service.js.map +1 -1
  115. package/dist/cores/proxy-storage-service.js +7 -10
  116. package/dist/cores/proxy-storage-service.js.map +1 -1
  117. package/dist/cores/redis-storage-service.js +13 -13
  118. package/dist/cores/redis-storage-service.js.map +1 -1
  119. package/dist/cores/storage/http-storage-service.d.ts +22 -0
  120. package/dist/cores/storage/http-storage-service.js +129 -0
  121. package/dist/cores/storage/http-storage-service.js.map +1 -0
  122. package/dist/cores/storage/index.d.ts +14 -0
  123. package/dist/cores/storage/index.js +31 -0
  124. package/dist/cores/storage/index.js.map +1 -0
  125. package/dist/cores/storage/model-manager.d.ts +93 -0
  126. package/dist/cores/storage/model-manager.js +192 -0
  127. package/dist/cores/storage/model-manager.js.map +1 -0
  128. package/dist/cores/storage/proxy-storage-service.d.ts +573 -0
  129. package/dist/cores/storage/proxy-storage-service.js +913 -0
  130. package/dist/cores/storage/proxy-storage-service.js.map +1 -0
  131. package/dist/cores/storage/redis-storage-service.d.ts +183 -0
  132. package/dist/cores/storage/redis-storage-service.js +391 -0
  133. package/dist/cores/storage/redis-storage-service.js.map +1 -0
  134. package/dist/cores/storage/storage-service.d.ts +169 -0
  135. package/dist/cores/storage/storage-service.js +374 -0
  136. package/dist/cores/storage/storage-service.js.map +1 -0
  137. package/dist/cores/storage-service.d.ts +1 -1
  138. package/dist/cores/storage-service.js +10 -8
  139. package/dist/cores/storage-service.js.map +1 -1
  140. package/dist/engine/builder.js +27 -20
  141. package/dist/engine/builder.js.map +1 -1
  142. package/dist/engine/engine.js +53 -44
  143. package/dist/engine/engine.js.map +1 -1
  144. package/dist/engine/index.js +8 -4
  145. package/dist/engine/index.js.map +1 -1
  146. package/dist/engine/utilities.d.ts +4 -3
  147. package/dist/engine/utilities.js +32 -37
  148. package/dist/engine/utilities.js.map +1 -1
  149. package/dist/environ.d.ts +2 -2
  150. package/dist/environ.js +20 -10
  151. package/dist/environ.js.map +1 -1
  152. package/dist/exec-cli.js +26 -26
  153. package/dist/exec-cli.js.map +1 -1
  154. package/dist/extended/abstract-service.d.ts +533 -0
  155. package/dist/extended/abstract-service.js +915 -0
  156. package/dist/extended/abstract-service.js.map +1 -0
  157. package/dist/extended/index.d.ts +10 -0
  158. package/dist/extended/index.js +27 -0
  159. package/dist/extended/index.js.map +1 -0
  160. package/dist/helpers/helpers.d.ts +273 -0
  161. package/dist/helpers/helpers.js +613 -0
  162. package/dist/helpers/helpers.js.map +1 -0
  163. package/dist/helpers/index.d.ts +10 -0
  164. package/dist/helpers/index.js +27 -0
  165. package/dist/helpers/index.js.map +1 -0
  166. package/dist/index.d.ts +7 -5
  167. package/dist/index.js +11 -4
  168. package/dist/index.js.map +1 -1
  169. package/dist/lib/dynamo/expressions.js +35 -27
  170. package/dist/lib/dynamo/expressions.js.map +1 -1
  171. package/dist/lib/dynamo/query.js +24 -20
  172. package/dist/lib/dynamo/query.js.map +1 -1
  173. package/dist/lib/dynamo/scan.js +17 -13
  174. package/dist/lib/dynamo/scan.js.map +1 -1
  175. package/dist/lib/dynamo/serializer.js +11 -7
  176. package/dist/lib/dynamo/serializer.js.map +1 -1
  177. package/dist/lib/dynamo/utils.js +19 -14
  178. package/dist/lib/dynamo/utils.js.map +1 -1
  179. package/dist/lib/dynamodb-value.js +3 -3
  180. package/dist/lib/dynamodb-value.js.map +1 -1
  181. package/dist/lib/index.js +6 -2
  182. package/dist/lib/index.js.map +1 -1
  183. package/dist/tools/express.js +21 -15
  184. package/dist/tools/express.js.map +1 -1
  185. package/dist/tools/index.js +6 -2
  186. package/dist/tools/index.js.map +1 -1
  187. package/dist/tools/shared.js +12 -6
  188. package/dist/tools/shared.js.map +1 -1
  189. package/package.json +16 -15
@@ -0,0 +1,913 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UniqueFieldManager = exports.ModelUtil = exports.TypedStorageService = exports.ProxyStorageService = exports.GeneralModelFilter = exports.GeneralKeyMaker = exports.CORE_FIELDS = void 0;
13
+ /**
14
+ * `proxy-storage-service.js`
15
+ * - common service for `accounts`
16
+ *
17
+ *
18
+ * @author Steve Jung <steve@lemoncloud.io>
19
+ * @date 2019-12-27 initial service skeleton.
20
+ *
21
+ * @copyright (C) 2019 LemonCloud Co Ltd. - All Rights Reserved.
22
+ */
23
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
+ const engine_1 = require("../../engine/");
25
+ const storage_service_1 = require("./storage-service");
26
+ const test_helper_1 = require("../../common/test-helper");
27
+ const general_api_controller_1 = require("../../controllers/general-api-controller");
28
+ const NS = engine_1.$U.NS('PSTR', 'blue'); // NAMESPACE TO BE PRINTED.
29
+ //NOTE! - BE WARE TO USE `ts-transformer-keys` DUE TO MISSING `ttypescript`
30
+ // export const CORE_FIELDS: string[] = keys<CoreModel>().filter(_ => !_.startsWith('_'));
31
+ // _inf(NS, '! CORE_FIELDS =', CORE_FIELDS.join(', ')); // for debugging.
32
+ exports.CORE_FIELDS = 'ns,type,sid,uid,gid,lock,next,meta,createdAt,updatedAt,deletedAt'.split(',');
33
+ /**
34
+ * class: `GeneralKeyMaker`
35
+ * - use ':' as delimiter to join [ns, type, id]
36
+ */
37
+ class GeneralKeyMaker {
38
+ constructor(ns = '', delimiter = ':') {
39
+ this.NS = ns;
40
+ this.DELIMITER = delimiter;
41
+ }
42
+ asKey$(type, id) {
43
+ if (!id)
44
+ throw new Error('@id (model-id) is required!');
45
+ const ns = `${this.NS || ''}`;
46
+ const _id = [ns, `${type || ''}`, id].map(_ => _.replace(/[:]/gi, '-')).join(this.DELIMITER);
47
+ const res = { ns, id, type, _id };
48
+ return res;
49
+ }
50
+ }
51
+ exports.GeneralKeyMaker = GeneralKeyMaker;
52
+ /**
53
+ * class: `GeneralModelFilter`
54
+ * - general model-filter with differential update.
55
+ * - to customize, override this class.
56
+ */
57
+ // eslint-disable-next-line prettier/prettier
58
+ class GeneralModelFilter {
59
+ /**
60
+ * default constructor
61
+ */
62
+ constructor(fields) {
63
+ this.FIELDS = fields;
64
+ }
65
+ /**
66
+ * parse `.meta` to json
67
+ * @param model the current model
68
+ * @param origin the origin model
69
+ */
70
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
+ afterRead(model, origin) {
72
+ // _log(NS, `filter.afterRead(${model._id || ''})....`);
73
+ if (!model.meta)
74
+ return model;
75
+ const meta = model.meta;
76
+ model.meta = meta && typeof meta == 'string' && meta.startsWith('{') ? JSON.parse(meta) : meta;
77
+ return model;
78
+ }
79
+ /**
80
+ * filter for before saving.
81
+ * - make sure data conversion
82
+ * - move the unknown fields to `.meta`.
83
+ *
84
+ * @param model the current model
85
+ * @param origin the origin model
86
+ */
87
+ beforeSave(model, origin) {
88
+ // _log(NS, `filter.beforeSave(${model._id})....`);
89
+ origin = origin || {};
90
+ const FIELDS = this.FIELDS && this.FIELDS.length ? this.FIELDS : null;
91
+ //! call service.onBeforeSave().
92
+ model = this.onBeforeSave(model, origin);
93
+ //TODO - accept only primitive types of field @191228.
94
+ //NOTE! - should not update the core field in save()
95
+ delete model.lock;
96
+ delete model.next;
97
+ //! load the meta data...
98
+ const $meta = (() => {
99
+ if (model.meta !== undefined && !model.meta)
100
+ return {};
101
+ const meta = model.meta || origin.meta || {}; // 만일, 파라미터에 meta 가 있다면, 클라이언트에서 직접 처리함.
102
+ return meta && typeof meta == 'string' ? JSON.parse(meta) : meta;
103
+ })();
104
+ //! move all fields to meta which is not defined in FIELDS.
105
+ model = Object.keys(model).reduce((N, key) => {
106
+ if (key.startsWith('_') || key.startsWith('$'))
107
+ return N;
108
+ if (key == 'createdAt' || key == 'updatedAt' || key == 'deletedAt')
109
+ return N;
110
+ if (/^[A-Z][A-Za-z0-9]*$/.test(key) && !/^[A-Z_]+$/.test(key))
111
+ return N; // ABC_DE 는 상수이며 OK, 다만, AbcDe 는 내부 오브젝트 이므로 무시!!!
112
+ if (key == 'meta')
113
+ return N; // meta itself.
114
+ if (FIELDS && FIELDS.indexOf(key) < 0 && FIELDS.indexOf('*' + key) < 0) {
115
+ $meta[key] = model[key];
116
+ }
117
+ else {
118
+ N[key] = model[key];
119
+ }
120
+ return N;
121
+ }, {});
122
+ model.meta = Object.keys($meta).length ? engine_1.$U.json($meta) : '';
123
+ //! handle for meta.
124
+ if (model.meta === '')
125
+ model.meta = null;
126
+ else if (typeof origin.meta == 'string' && model.meta == origin.meta)
127
+ delete model.meta;
128
+ else if (typeof origin.meta == 'object' && model.meta == engine_1.$U.json(origin.meta))
129
+ delete model.meta;
130
+ else if (!origin.meta && !model.meta)
131
+ model.meta = origin.meta;
132
+ //! filter out only the updated fields.
133
+ const res = Object.keys(model).reduce((N, key) => {
134
+ if (key.startsWith('_') || key.startsWith('$'))
135
+ return N; // ignore.
136
+ const org = origin[key];
137
+ const val = N[key];
138
+ if (!org && val)
139
+ return N;
140
+ else if (org && !val)
141
+ return N;
142
+ else if (org && typeof org === 'object') {
143
+ const org2 = engine_1.$U.json(org);
144
+ const val2 = typeof val === 'object' ? engine_1.$U.json(val) : val;
145
+ if (org2 == val2) {
146
+ delete N[key];
147
+ }
148
+ }
149
+ else if ((val === '' || val === null) && (org === null || org === undefined)) {
150
+ //NOTE! - dynamo saves null for '' string.
151
+ delete N[key];
152
+ }
153
+ else if (val === org) {
154
+ delete N[key];
155
+ }
156
+ return N;
157
+ }, model);
158
+ //! if nothing to update, then returns null.
159
+ const keys = Object.keys(model).filter(_ => !_.startsWith('_') && !_.startsWith('$'));
160
+ if (keys.length <= 0)
161
+ return null;
162
+ //! returns the filtered node.
163
+ return res;
164
+ }
165
+ /**
166
+ * called after saving the model.
167
+ * - parse `.meta` back to json object.
168
+ *
169
+ * @param model the saved model
170
+ * @param origin the origin model.
171
+ */
172
+ afterSave(model, origin) {
173
+ return this.afterRead(model, origin);
174
+ }
175
+ /**
176
+ * called before updating the model.
177
+ * @param model the updated model
178
+ * @param incrementals (optional) incremental fields.
179
+ */
180
+ beforeUpdate(model, incrementals) {
181
+ return model;
182
+ }
183
+ /**
184
+ * called after updating the model.
185
+ * @param model the updated model
186
+ */
187
+ afterUpdate(model) {
188
+ return this.afterRead(model, null);
189
+ }
190
+ /**
191
+ * override this `onBeforeSave()` in sub-class.
192
+ * @param model the current model
193
+ * @param origin (optional) the origin model
194
+ */
195
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
196
+ onBeforeSave(model, origin) {
197
+ //TODO - override this function.
198
+ //! conversion data-type.
199
+ // if (model.count !== undefined) model.count = $U.N(model.count, 0);
200
+ return model;
201
+ }
202
+ }
203
+ exports.GeneralModelFilter = GeneralModelFilter;
204
+ /**
205
+ * class: `ProxyStorageService`
206
+ * - support `nextSeq()`, `doLock()`, `doRelease()`
207
+ * - proxed storage-service to wrap the parent storage-service w/ more features.
208
+ * - table is supposed to have internal-key as `_id` string.
209
+ *
210
+ * **Usage**
211
+ * ```js
212
+ * type MyType = '' | 'test';
213
+ * interface MyModel extends CoreModel<MyType>{
214
+ * name?: string;
215
+ * }
216
+ * const storage = new ProxyStorageService<MyModel, MyType>(this, 'TestTable', ['id','name']);
217
+ * const $test = storage.makeTypedStorageService('test');
218
+ * ```
219
+ */
220
+ // eslint-disable-next-line prettier/prettier
221
+ class ProxyStorageService {
222
+ /**
223
+ * create proxed storage-service.
224
+ *
225
+ * @param service service to support `CoreKeyMakeable`
226
+ * @param storage table-name or the parent storage-service
227
+ * @param fields list of fields.
228
+ * @param filters filters of `CoreModelFilterable`
229
+ * @param idName (optional) internal partition-key (default as '_id')
230
+ */
231
+ constructor(service, storage, fields, filters, idName) {
232
+ /**
233
+ * say hello()
234
+ */
235
+ this.hello = () => `proxy-storage-service:${this.storage.hello()}`;
236
+ /**
237
+ * read by _id
238
+ */
239
+ this.read = (_id) => this.storage.read(_id);
240
+ /**
241
+ * read or create by _id
242
+ */
243
+ this.readOrCreate = (_id, model) => this.storage.readOrCreate(_id, model);
244
+ /**
245
+ * save by _id
246
+ */
247
+ this.save = (_id, model) => this.storage.save(_id, model);
248
+ /**
249
+ * update by _id
250
+ */
251
+ this.update = (_id, model, incrementals) => this.storage.update(_id, model, incrementals);
252
+ /**
253
+ * increment by _id
254
+ */
255
+ this.increment = (_id, model, $update) => this.storage.increment(_id, model, $update);
256
+ /**
257
+ * delete by _id
258
+ */
259
+ this.delete = (_id) => this.storage.delete(_id);
260
+ /**
261
+ * get key-id by type+id
262
+ */
263
+ this.asKey = (type, id) => {
264
+ if (typeof this.service.asKey == 'function') {
265
+ return this.service.asKey(type, `${id}`);
266
+ }
267
+ const $key = this.service.asKey$(type, `${id}`);
268
+ return $key[this.idName];
269
+ };
270
+ /**
271
+ * timer to generate the current-time (msec)
272
+ */
273
+ this.$timer = null;
274
+ this.setTimer = (timer) => {
275
+ const previous = this.$timer;
276
+ this.$timer = timer;
277
+ return previous;
278
+ };
279
+ this.getTime = () => {
280
+ if (this.$timer)
281
+ return this.$timer();
282
+ return new Date().getTime();
283
+ };
284
+ this.idName = idName === undefined ? '_id' : `${idName || ''}`;
285
+ this.service = service;
286
+ this.storage =
287
+ typeof storage == 'string' ? ProxyStorageService.makeStorageService(storage, fields, idName) : storage;
288
+ this.filters = filters || new GeneralModelFilter(fields);
289
+ }
290
+ /**
291
+ * factory function to create this `proxy-storage-service`
292
+ * @param service key-makeable
293
+ * @param table table-name
294
+ * @param fields list of fields.
295
+ * @param filters model filter.
296
+ * @param idName (optional) internal partition-key (default as '_id')
297
+ */
298
+ static create(service, table, fields, filters, idName) {
299
+ const storage = ProxyStorageService.makeStorageService(table, fields, idName);
300
+ const res = new ProxyStorageService(service, storage, fields, filters, idName);
301
+ return res;
302
+ }
303
+ /**
304
+ * get next auto-sequence number.
305
+ *
306
+ * @param type type of seqeunce.
307
+ * @param initNext initial next value if not exist.
308
+ */
309
+ nextSeq(type, initNext) {
310
+ return __awaiter(this, void 0, void 0, function* () {
311
+ (0, engine_1._log)(NS, `nextSeq(${type})..`);
312
+ const { createdAt, updatedAt } = this.asTime();
313
+ const _id = this.asKey(ProxyStorageService.TYPE_SEQUENCE, `${type}`);
314
+ let res = yield this.storage.increment(_id, { next: 1 }, { updatedAt }); // it will create new row if not exists. (like upset)
315
+ if (res.next == 1) {
316
+ const $key = this.service.asKey$(ProxyStorageService.TYPE_SEQUENCE, `${type}`);
317
+ initNext = initNext === undefined ? ProxyStorageService.AUTO_SEQUENCE : initNext;
318
+ const $upd = { next: initNext };
319
+ const $inc = Object.assign(Object.assign({}, $key), { createdAt, updatedAt });
320
+ res = yield this.storage.increment(_id, $upd, $inc); //! increment w/ update-set
321
+ }
322
+ return res.next;
323
+ });
324
+ }
325
+ /**
326
+ * get uuid by type.
327
+ * @param type
328
+ */
329
+ nextUuid(type) {
330
+ return __awaiter(this, void 0, void 0, function* () {
331
+ (0, engine_1._log)(NS, `nextUuid(${type})..`);
332
+ return engine_1.$U.uuid();
333
+ });
334
+ }
335
+ /**
336
+ * get time-stamp as now.
337
+ */
338
+ asTime(currentTime) {
339
+ currentTime = currentTime || this.getTime();
340
+ const createdAt = currentTime;
341
+ const updatedAt = currentTime;
342
+ const deletedAt = currentTime;
343
+ return { createdAt, updatedAt, deletedAt };
344
+ }
345
+ /**
346
+ * delete sequence-key.
347
+ * @param type type of seqeunce.
348
+ */
349
+ clearSeq(type) {
350
+ return __awaiter(this, void 0, void 0, function* () {
351
+ (0, engine_1._log)(NS, `nextSeq(${type})..`);
352
+ const _id = this.asKey(ProxyStorageService.TYPE_SEQUENCE, `${type}`);
353
+ yield this.storage.delete(_id);
354
+ });
355
+ }
356
+ /**
357
+ * read model by key + id with optional auto creation.
358
+ *
359
+ * @param type model-type
360
+ * @param id node-id
361
+ * @param $create (optional) initial model if not exist. (or throw 404 error)
362
+ */
363
+ doRead(type, id, $create) {
364
+ return __awaiter(this, void 0, void 0, function* () {
365
+ const $key = this.service.asKey$(type, id);
366
+ const _id = this.asKey(type, id);
367
+ const model = yield this.storage.read(_id).catch((e) => {
368
+ if (`${e.message}`.startsWith('404 NOT FOUND') && $create) {
369
+ const { createdAt, updatedAt } = this.asTime();
370
+ return this.storage.update(_id, Object.assign(Object.assign(Object.assign({}, $create), $key), { createdAt, updatedAt, deletedAt: 0 }));
371
+ }
372
+ throw e;
373
+ });
374
+ //! make sure it has `_id`
375
+ model[this.idName] = _id;
376
+ const res = this.filters.afterRead(model);
377
+ return res;
378
+ });
379
+ }
380
+ /**
381
+ * delete model by id.
382
+ *
383
+ * @param type model-type
384
+ * @param id node-id
385
+ * @param destroy flag to destroy (real delete)
386
+ */
387
+ doDelete(type, id, destroy = true) {
388
+ return __awaiter(this, void 0, void 0, function* () {
389
+ const _id = this.asKey(type, id);
390
+ if (destroy === undefined || destroy === true)
391
+ return this.storage.delete(_id);
392
+ const { createdAt, updatedAt, deletedAt } = this.asTime();
393
+ const $up = { updatedAt, deletedAt };
394
+ const $org = yield this.read(_id); //! it will make 404 if not found.
395
+ if (!$org.createdAt)
396
+ $up.createdAt = createdAt;
397
+ return this.update(_id, $up);
398
+ });
399
+ }
400
+ /**
401
+ * update model (or it will create automatically)
402
+ *
403
+ * @param type model-type
404
+ * @param id node-id
405
+ * @param node model
406
+ * @param incrementals (optional) fields to increment
407
+ */
408
+ doUpdate(type, id, node, incrementals) {
409
+ return __awaiter(this, void 0, void 0, function* () {
410
+ const $inc = Object.assign({}, incrementals); //! make copy.
411
+ const _id = this.asKey(type, id);
412
+ // const $key = this.service.asKey$(type, id);
413
+ const node2 = this.filters.beforeUpdate(Object.assign(Object.assign({}, node), { [this.idName]: _id }), $inc);
414
+ delete node2['_id'];
415
+ const { updatedAt } = this.asTime();
416
+ const model = yield this.update(_id, Object.assign(Object.assign({}, node2), { updatedAt }), $inc);
417
+ //! make sure it has `_id`
418
+ model[this.idName] = _id;
419
+ return this.filters.afterUpdate(model);
420
+ });
421
+ }
422
+ /**
423
+ * update model (or it will create automatically)
424
+ *
425
+ * @param type model-type
426
+ * @param id node-id
427
+ */
428
+ doIncrement(type, id, $inc, $up) {
429
+ return __awaiter(this, void 0, void 0, function* () {
430
+ const _id = this.asKey(type, id);
431
+ const { updatedAt } = this.asTime();
432
+ const model = yield this.increment(_id, Object.assign({}, $inc), Object.assign(Object.assign({}, $up), { updatedAt }));
433
+ //! make sure it has `_id`
434
+ model[this.idName] = _id;
435
+ return this.filters.afterUpdate(model);
436
+ });
437
+ }
438
+ /**
439
+ * save model by checking origin node.
440
+ * - use `doSave()` rather than `doUpdate()` for both create & update.
441
+ * - if `$create` is null, throw 404 error it if not found.
442
+ *
443
+ * @param type model-type
444
+ * @param id node-id
445
+ * @param node node to save (or update)
446
+ * @param $create (optional) initial creation model if not found.
447
+ */
448
+ doSave(type, id, node, $create) {
449
+ return __awaiter(this, void 0, void 0, function* () {
450
+ //! read origin model w/o error.
451
+ const $org = (yield this.doRead(type, id, null).catch(e => {
452
+ if (`${e.message}`.startsWith('404 NOT FOUND'))
453
+ return null; // mark null to create later.
454
+ throw e;
455
+ }));
456
+ //! if `$create` is undefined, create it with default $key.
457
+ const _id = this.asKey(type, id);
458
+ const model = Object.assign({}, node); // copy from param.
459
+ model[this.idName] = _id; //! make sure the internal id
460
+ //! apply filter.
461
+ const $ups = this.filters.beforeSave(model, $org); //! `$org` should be null if create.
462
+ (0, engine_1._log)(NS, `> ${type}[${id}].update =`, engine_1.$U.json($ups));
463
+ //! if null, then nothing to update.
464
+ if (!$ups) {
465
+ const res = { [this.idName]: _id };
466
+ return res;
467
+ }
468
+ //! determine of create or update.
469
+ const { createdAt, updatedAt } = this.asTime();
470
+ if ($org) {
471
+ const $save = Object.assign(Object.assign({}, $ups), { updatedAt });
472
+ const res = yield this.doUpdate(type, id, $save);
473
+ return this.filters.afterSave(res, $org); //! `$org` should be valid if update.
474
+ }
475
+ else {
476
+ const $key = this.service.asKey$(type, id);
477
+ const $save = Object.assign(Object.assign(Object.assign(Object.assign({}, $ups), $create), $key), { createdAt, updatedAt: createdAt, deletedAt: 0 });
478
+ const res = yield this.storage.save(_id, $save);
479
+ return this.filters.afterSave(res, null); //! `$org` should be null if create.
480
+ }
481
+ });
482
+ }
483
+ /**
484
+ * lock data-entry by type+id w/ limited time tick
485
+ * - WARN! must release lock by `doRelease()`
486
+ *
487
+ * `total-waited-time = tick * interval (msec)`
488
+ *
489
+ * @param type model-type
490
+ * @param id model-id
491
+ * @param tick tick count to wait.
492
+ * @param interval timeout interval per each tick (in msec, default 1000 = 1sec)
493
+ */
494
+ doLock(type, id, tick, interval) {
495
+ return __awaiter(this, void 0, void 0, function* () {
496
+ tick = engine_1.$U.N(tick, 30);
497
+ interval = engine_1.$U.N(interval, 1000);
498
+ if (typeof tick != 'number' || tick < 0)
499
+ throw new Error(`@tick (${tick}) is not valid!`);
500
+ if (typeof interval != 'number' || interval < 1)
501
+ throw new Error(`@interval (${interval}) is not valid!`);
502
+ const _id = this.asKey(type, id);
503
+ //! WARN! DO NOT MAKE ANY MODEL CREATION IN HERE.
504
+ // const $org = await this.storage.readOrCreate(_id, { lock: 0, ...$key } as any);
505
+ // _log(NS, `> $org[${type}/${id}].lock =`, $org.lock);
506
+ const thiz = this;
507
+ //! wait some time.
508
+ const wait = (timeout) => __awaiter(this, void 0, void 0, function* () {
509
+ return new Promise(resolve => {
510
+ setTimeout(() => {
511
+ resolve(timeout);
512
+ }, timeout);
513
+ });
514
+ });
515
+ const incLock = (_id, lock) => __awaiter(this, void 0, void 0, function* () {
516
+ const $up = {};
517
+ const $in = { lock };
518
+ return thiz.storage.update(_id, $up, $in).then($t2 => {
519
+ return engine_1.$U.N($t2.lock, 1);
520
+ });
521
+ });
522
+ //! recursive to wait lock()
523
+ const waitLock = (_id, ttl, int) => __awaiter(this, void 0, void 0, function* () {
524
+ //! try to check the current value.....
525
+ const lock = yield incLock(_id, 0).then(n => {
526
+ if (n > 1)
527
+ return n;
528
+ //! then, try to increment the lock
529
+ return incLock(_id, ttl > 0 ? 1 : 0);
530
+ });
531
+ (0, engine_1._log)(NS, `! waitLock(${_id}, ${ttl}). lock =`, lock);
532
+ if (lock == 1 || lock == 0) {
533
+ return true;
534
+ }
535
+ else if (ttl > 0 && lock > 1) {
536
+ return wait(int).then(() => waitLock(_id, ttl - 1, int));
537
+ }
538
+ else {
539
+ throw new Error(`400 TIMEOUT - model[${_id}].lock = ${lock}`);
540
+ }
541
+ });
542
+ return waitLock(_id, tick, interval);
543
+ });
544
+ }
545
+ /**
546
+ * release lock by resetting lock = 0.
547
+ *
548
+ * @param type model-type
549
+ * @param id model-id
550
+ */
551
+ doRelease(type, id) {
552
+ return __awaiter(this, void 0, void 0, function* () {
553
+ (0, engine_1._log)(NS, `doRelease(${type}, ${id})... `);
554
+ const _id = this.asKey(type, id);
555
+ const $up = { lock: 0 };
556
+ const node = yield this.storage.update(_id, $up).catch(() => ({ lock: 0 }));
557
+ const lock = engine_1.$U.N(node.lock, 1);
558
+ return lock === 0 ? true : false;
559
+ });
560
+ }
561
+ /**
562
+ * create storage-service w/ fields list.
563
+ * - idName should be `_id`
564
+ *
565
+ * @param table table-name or dummy file name (ex: `dummy-data.yml`).
566
+ * @param fields required for dynamo table.
567
+ * @param idName internal partition-key name (default '_id')
568
+ */
569
+ static makeStorageService(table, fields, idName) {
570
+ if (!table)
571
+ throw new Error(`@table (table-name) is required!`);
572
+ idName = idName === undefined ? '_id' : `${idName || ''}`;
573
+ //! clear the duplicated string
574
+ const clearDuplicated = (arr) => arr.sort().reduce((L, val) => {
575
+ if (val && L.indexOf(val) < 0)
576
+ L.push(val);
577
+ return L;
578
+ }, []);
579
+ //! make internal storage-service by table
580
+ if (table.endsWith('.yml')) {
581
+ return new storage_service_1.DummyStorageService(table, table.split('.')[0], idName);
582
+ }
583
+ else {
584
+ if (!fields)
585
+ throw new Error(`@fields (list of field) is required!`);
586
+ fields = clearDuplicated(exports.CORE_FIELDS.concat(fields));
587
+ return new storage_service_1.DynamoStorageService(table, fields, idName);
588
+ }
589
+ }
590
+ /**
591
+ * create proxy-storage-service by type
592
+ * @param type model-type
593
+ */
594
+ makeTypedStorageService(type) {
595
+ if (!type)
596
+ throw new Error(`@type (model-type) is required!`);
597
+ // if (!fields) throw new Error(`@fields[${type}] (list of field) is required!`);
598
+ const res = new TypedStorageService(this, type);
599
+ return res;
600
+ }
601
+ }
602
+ exports.ProxyStorageService = ProxyStorageService;
603
+ ProxyStorageService.AUTO_SEQUENCE = 1000000;
604
+ ProxyStorageService.TYPE_SEQUENCE = 'sequence';
605
+ /**
606
+ * class: `TypedStorageService`
607
+ * - wrap id with type + id.
608
+ */
609
+ // eslint-disable-next-line prettier/prettier
610
+ class TypedStorageService {
611
+ constructor(service, type) {
612
+ /**
613
+ * show self service name
614
+ */
615
+ this.hello = () => `typed-storage-service:${this.type}/${this.storage.hello()}`;
616
+ /**
617
+ * get next auto-sequence id in number like `1000003`.
618
+ */
619
+ this.nextId = () => this.storage.nextSeq(this.type);
620
+ /**
621
+ * get uuid like `d01764cd-9ef2-41e2-9e88-68e79555c979`
622
+ */
623
+ this.nextUuid = () => this.storage.nextUuid(this.type);
624
+ /**
625
+ * read model by key + id with optional auto creation.
626
+ * - throws '404 NOT FOUND' if not found.
627
+ *
628
+ * @param id node-id
629
+ */
630
+ this.read = (id) => this.storage.doRead(this.type, `${id || ''}`);
631
+ /**
632
+ * read model by key + id with optional auto creation.
633
+ *
634
+ * @param id node-id
635
+ * @param model initial model if not exist. (or throw 404 error)
636
+ */
637
+ this.readOrCreate = (id, model) => this.storage.doRead(this.type, `${id || ''}`, model);
638
+ /**
639
+ * update model (or it will create automatically)
640
+ *
641
+ * @param id node-id
642
+ * @param model model to update
643
+ * @param incrementals (optional) fields to increment.
644
+ */
645
+ this.update = (id, model, incrementals) => this.storage.doUpdate(this.type, `${id || ''}`, model, incrementals);
646
+ /**
647
+ * insert model w/ auto generated id
648
+ *
649
+ * @param model model to insert
650
+ */
651
+ this.insert = (node) => __awaiter(this, void 0, void 0, function* () {
652
+ return this.nextId().then(_ => {
653
+ const id = `${_}`;
654
+ (0, engine_1._log)(NS, `> next-id[${this.type}] =`, id);
655
+ return this.readOrCreate(id, Object.assign(Object.assign({}, node), { id }));
656
+ });
657
+ });
658
+ /**
659
+ * update model (or it will create automatically)
660
+ *
661
+ * ```ts
662
+ * //before: { count: 1 };
663
+ * const res = await storage.increment(1, { count: 2 }, { total: 2 });
664
+ * //after : { count: 3, total: 2 }
665
+ * ```
666
+ *
667
+ * @param id node-id
668
+ * @param $increments model only with numbers
669
+ */
670
+ this.increment = (id, $increments, $update) => this.storage.doIncrement(this.type, `${id || ''}`, $increments, $update);
671
+ /**
672
+ * delete model by id.
673
+ *
674
+ * @param id node-id
675
+ * @param destroy flag to destroy (real delete)
676
+ */
677
+ this.delete = (id, destroy) => this.storage.doDelete(this.type, `${id || ''}`, destroy === undefined ? true : destroy);
678
+ /**
679
+ * save model by checking origin node.
680
+ * - use `doSave()` rather than `doUpdate()` for both create & update.
681
+ * - if `$create` is null, throw 404 error it if not found.
682
+ *
683
+ * @param id node-id
684
+ * @param node node to save (or update)
685
+ * @param $create (optional) initial creation model.
686
+ */
687
+ this.save = (id, model, $create) => this.storage.doSave(this.type, `${id || ''}`, model, $create);
688
+ /**
689
+ * lock data-entry by type+id w/ limited time tick
690
+ * - WARN! must release lock by `release(id)`
691
+ *
692
+ * `total-waited-time = tick * interval (msec)`
693
+ *
694
+ * **[UPDATES]**
695
+ * 1. read original node (or, throw 404 error)
696
+ * 2. use internal lock.
697
+ *
698
+ * @param id model-id to lock
699
+ * @param tick tick count to wait.
700
+ * @param interval timeout interval per each tick (in msec, default 1000 = 1sec)
701
+ */
702
+ this.lock = (id, tick, interval) => this.storage
703
+ .doRead(this.type, `${id || ''}`, null)
704
+ .then(node => this.storage.doLock(this.type, node.id, tick, interval));
705
+ /**
706
+ * release lock by resetting lock = 0.
707
+ * @param id model-id
708
+ */
709
+ this.release = (id) => this.storage.doRelease(this.type, `${id || ''}`);
710
+ /**
711
+ * using `lock()`, guard func with auto lock & release.
712
+ *
713
+ * ```ts
714
+ * const res = await storage.guard(async ()=>{
715
+ * return 'abc';
716
+ * });
717
+ * // res === 'abc'
718
+ * ```
719
+ */
720
+ this.guard = (id, handler, tick, interval) => __awaiter(this, void 0, void 0, function* () {
721
+ let locked = false;
722
+ return this.lock(id, tick, interval)
723
+ .then((_) => {
724
+ locked = _;
725
+ try {
726
+ return handler();
727
+ }
728
+ catch (e) {
729
+ return Promise.reject(e);
730
+ }
731
+ })
732
+ .then((_) => {
733
+ if (locked)
734
+ return this.release(id).then(() => _);
735
+ return _;
736
+ })
737
+ .catch((e) => {
738
+ if (locked)
739
+ return this.release(id).then(() => Promise.reject(e));
740
+ // throw e;
741
+ return Promise.reject(e);
742
+ });
743
+ });
744
+ /**
745
+ * make `UniqueFieldManager` for field.
746
+ */
747
+ this.makeUniqueFieldManager = (field) => new UniqueFieldManager(this, field);
748
+ /**
749
+ * make `GeneralAPIController` for REST API w/ supporting basic CRUD
750
+ */
751
+ this.makeGeneralAPIController = (search, uniqueField) => new general_api_controller_1.GeneralAPIController(this.type, this, search, uniqueField);
752
+ this.storage = service;
753
+ this.type = type;
754
+ }
755
+ }
756
+ exports.TypedStorageService = TypedStorageService;
757
+ /**
758
+ * class: `ModelUtil`
759
+ * - Helper functions for model.
760
+ */
761
+ class ModelUtil {
762
+ }
763
+ exports.ModelUtil = ModelUtil;
764
+ ModelUtil.selfRead = (self, key, defValue) => {
765
+ const value = self[key];
766
+ return value === undefined ? defValue : value;
767
+ };
768
+ ModelUtil.selfPop = (self, key, defValue) => {
769
+ const value = ModelUtil.selfRead(self, key, defValue);
770
+ delete self[key];
771
+ return value;
772
+ };
773
+ /**
774
+ * attach `.pop()` method to object.
775
+ *
776
+ * ```js
777
+ * const data = CoreModelUtil.buildPop({'a':1});
778
+ * assert( 1 === data.pop('a) );
779
+ * const final = data.pop();
780
+ * assert( final == data );
781
+ */
782
+ ModelUtil.buildPop = (thiz, popName = 'pop') => {
783
+ if (!thiz)
784
+ throw new Error('@thiz (object) is required!');
785
+ if (typeof thiz[popName] != 'undefined')
786
+ throw new Error(`.[${popName}] is duplicated!`);
787
+ thiz[popName] = function (key, defValue) {
788
+ if (!key) {
789
+ //! clear pop() if key is null.
790
+ delete this[popName];
791
+ return this;
792
+ }
793
+ else {
794
+ return ModelUtil.selfPop(this, key, defValue);
795
+ }
796
+ };
797
+ return thiz;
798
+ };
799
+ /**
800
+ * class: `UniqueFieldManager`
801
+ * - support `.{field}` is unique in typed-storage-service.
802
+ * - make lookup data entry to save the reverse mapping to origin id.
803
+ * - set `.stereo` as '#' to mark as lookup. (to filter out from Elastic.search())
804
+ * - set `.id` as `#{field}/{name}` or `#{name}`.
805
+ * - set `.meta` as origin id.
806
+ */
807
+ class UniqueFieldManager {
808
+ constructor(storage, field = 'name') {
809
+ this.hello = () => `unique-field-manager:${this.type}/${this.field}:${this.storage.hello()}`;
810
+ this.type = storage.type;
811
+ this.storage = storage;
812
+ this.field = field;
813
+ }
814
+ /**
815
+ * validate value format
816
+ * - just check empty string.
817
+ * @param value unique value in same type+field.
818
+ */
819
+ validate(value) {
820
+ const name2 = `${value || ''}`.trim();
821
+ return name2 && value == name2 ? true : false;
822
+ }
823
+ /**
824
+ * convert to internal id by value
825
+ * @param value unique value in same type group.
826
+ */
827
+ asLookupId(value) {
828
+ return `#${this.field || ''}/${value || ''}`;
829
+ }
830
+ /**
831
+ * lookup model by value
832
+ * - use `.meta` property to link with the origin.
833
+ * - mark `.stereo` as to '#' to distinguish normal.
834
+ *
835
+ * @param value unique value in same type group.
836
+ * @param $creates (optional) create-set if not found.
837
+ */
838
+ findOrCreate(value, $creates) {
839
+ return __awaiter(this, void 0, void 0, function* () {
840
+ if (!value || typeof value != 'string')
841
+ throw new Error(`@${this.field} (string) is required!`);
842
+ if (!this.validate(value))
843
+ throw new Error(`@${this.field} (${value || ''}) is not valid!`);
844
+ const ID = this.asLookupId(value);
845
+ const field = `${this.field}`;
846
+ if (!$creates) {
847
+ // STEP.1 read the origin name map
848
+ const $map = yield this.storage.read(ID).catch(test_helper_1.NUL404);
849
+ const rid = $map && $map.meta;
850
+ if (!rid)
851
+ throw new Error(`404 NOT FOUND - ${this.type}:${field}/${value}`);
852
+ // STEP.2 read the target node by stereo key.
853
+ const model = yield this.storage.read(rid);
854
+ return model;
855
+ }
856
+ else {
857
+ // STEP.0 validate if value is same
858
+ const $any = $creates || {};
859
+ if ($any[field] !== undefined && $any[field] !== value)
860
+ throw new Error(`@${this.field} (${value}) is not same as (${$any[field]})!`);
861
+ // STEP.1 read the origin value map
862
+ const $new = { stereo: '#', meta: `${$creates.id || ''}`, [field]: value };
863
+ const $map = yield this.storage.readOrCreate(ID, $new);
864
+ const rid = ($map && $map.meta) || $creates.id;
865
+ //! check if already saved, and id is differ.
866
+ if ($any['id'] && $any['id'] != rid)
867
+ throw new Error(`@id (${rid}) is not same as (${$any['id']})`);
868
+ // STEP.2 read the target node or create.
869
+ const $temp = Object.assign(Object.assign({}, $creates), { [field]: value });
870
+ const model = rid ? yield this.storage.readOrCreate(rid, $temp) : yield this.storage.insert($temp);
871
+ model[field] = value;
872
+ // STEP.3 update lookup key.
873
+ const newId = `${rid || model.id || ''}`;
874
+ if ($map.meta != newId) {
875
+ const $upt = { meta: newId };
876
+ yield this.storage.update(ID, $upt);
877
+ $map.meta = newId;
878
+ }
879
+ //! returns.
880
+ return model;
881
+ }
882
+ });
883
+ }
884
+ /**
885
+ * update lookup table (or create)
886
+ *
887
+ * @param model target model
888
+ * @param value (optional) new value of model.
889
+ */
890
+ updateLookup(model, value) {
891
+ return __awaiter(this, void 0, void 0, function* () {
892
+ value = value || model[this.field];
893
+ if (!this.validate(value))
894
+ throw new Error(`@${this.field} (${value || ''}) is not valid!`);
895
+ const ID = this.asLookupId(value);
896
+ const field = `${this.field}`;
897
+ // STEP.0 validate if value has changed
898
+ const $any = model;
899
+ if ($any[field] && $any[field] !== value)
900
+ throw new Error(`@${this.field} (${value}) is not same as (${$any[field]})!`);
901
+ // STEP.1 check if value is duplicated.
902
+ const $org = yield this.storage.read(ID).catch(test_helper_1.NUL404);
903
+ const rid = $org && $org.meta;
904
+ if ($org && rid != model.id)
905
+ throw new Error(`400 DUPLICATED NAME - ${field}[${value}] is duplicated to ${this.type}[${rid}]`);
906
+ // STEP.2 save the name mapping.
907
+ const $new = Object.assign(Object.assign({}, model), { [field]: value, id: model.id });
908
+ return yield this.findOrCreate(value, $new);
909
+ });
910
+ }
911
+ }
912
+ exports.UniqueFieldManager = UniqueFieldManager;
913
+ //# sourceMappingURL=proxy-storage-service.js.map