@dereekb/model 13.0.0 → 13.0.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.
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +752 -878
- package/index.cjs.mjs +2 -0
- package/index.esm.js +752 -878
- package/package.json +12 -16
package/index.cjs.js
CHANGED
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
var util = require('@dereekb/util');
|
|
4
4
|
var classTransformer = require('class-transformer');
|
|
5
5
|
var classValidator = require('class-validator');
|
|
6
|
-
require('
|
|
7
|
-
require('core-js/modules/es.iterator.map.js');
|
|
8
|
-
require('core-js/modules/es.map.get-or-insert.js');
|
|
9
|
-
require('core-js/modules/es.map.get-or-insert-computed.js');
|
|
6
|
+
var makeError = require('make-error');
|
|
10
7
|
|
|
11
8
|
/******************************************************************************
|
|
12
9
|
Copyright (c) Microsoft Corporation.
|
|
@@ -48,37 +45,67 @@ const ADDRESS_STATE_CODE_MAX_LENGTH = 2;
|
|
|
48
45
|
const ADDRESS_ZIP_MAX_LENGTH = 11;
|
|
49
46
|
const ADDRESS_COUNTRY_MAX_LENGTH = 80;
|
|
50
47
|
class AbstractUnitedStatesAddressWithoutStateParams {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
line1;
|
|
49
|
+
line2;
|
|
50
|
+
city;
|
|
51
|
+
zip;
|
|
52
|
+
}
|
|
53
|
+
__decorate([
|
|
54
|
+
classTransformer.Expose(),
|
|
55
|
+
classValidator.IsString(),
|
|
56
|
+
classValidator.IsNotEmpty(),
|
|
57
|
+
classValidator.MaxLength(ADDRESS_LINE_MAX_LENGTH),
|
|
58
|
+
__metadata("design:type", String)
|
|
59
|
+
], AbstractUnitedStatesAddressWithoutStateParams.prototype, "line1", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
classTransformer.Expose(),
|
|
62
|
+
classValidator.IsOptional(),
|
|
63
|
+
classValidator.IsString(),
|
|
64
|
+
classValidator.MaxLength(ADDRESS_LINE_MAX_LENGTH),
|
|
65
|
+
__metadata("design:type", String)
|
|
66
|
+
], AbstractUnitedStatesAddressWithoutStateParams.prototype, "line2", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
classTransformer.Expose(),
|
|
69
|
+
classValidator.IsString(),
|
|
70
|
+
classValidator.IsNotEmpty(),
|
|
71
|
+
classValidator.MaxLength(ADDRESS_CITY_MAX_LENGTH),
|
|
72
|
+
__metadata("design:type", String)
|
|
73
|
+
], AbstractUnitedStatesAddressWithoutStateParams.prototype, "city", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
classTransformer.Expose(),
|
|
76
|
+
classValidator.IsString(),
|
|
77
|
+
classValidator.IsNotEmpty(),
|
|
78
|
+
classValidator.Matches(util.ZIP_CODE_STRING_REGEX),
|
|
79
|
+
classValidator.MaxLength(ADDRESS_ZIP_MAX_LENGTH),
|
|
80
|
+
__metadata("design:type", String)
|
|
81
|
+
], AbstractUnitedStatesAddressWithoutStateParams.prototype, "zip", void 0);
|
|
62
82
|
/**
|
|
63
83
|
* UnitedStatesAddress that enforces a StateCode for the state value.
|
|
64
84
|
*/
|
|
65
85
|
class UnitedStatesAddressWithStateCodeParams extends AbstractUnitedStatesAddressWithoutStateParams {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
state;
|
|
87
|
+
}
|
|
88
|
+
__decorate([
|
|
89
|
+
classTransformer.Expose(),
|
|
90
|
+
classValidator.IsString(),
|
|
91
|
+
classValidator.Matches(util.US_STATE_CODE_STRING_REGEX),
|
|
92
|
+
classValidator.MinLength(ADDRESS_STATE_CODE_MAX_LENGTH),
|
|
93
|
+
classValidator.MaxLength(ADDRESS_STATE_CODE_MAX_LENGTH),
|
|
94
|
+
__metadata("design:type", String)
|
|
95
|
+
], UnitedStatesAddressWithStateCodeParams.prototype, "state", void 0);
|
|
72
96
|
/**
|
|
73
97
|
* UnitedStatesAddress that enforces a State for the state value.
|
|
74
98
|
*/
|
|
75
99
|
class UnitedStatesAddressWithStateStringParams extends AbstractUnitedStatesAddressWithoutStateParams {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
state;
|
|
101
|
+
}
|
|
102
|
+
__decorate([
|
|
103
|
+
classTransformer.Expose(),
|
|
104
|
+
classValidator.IsString(),
|
|
105
|
+
classValidator.IsNotEmpty(),
|
|
106
|
+
classValidator.MaxLength(ADDRESS_STATE_MAX_LENGTH),
|
|
107
|
+
__metadata("design:type", String)
|
|
108
|
+
], UnitedStatesAddressWithStateStringParams.prototype, "state", void 0);
|
|
82
109
|
|
|
83
110
|
const UNKNOWN_WEBSITE_LINK_TYPE = 'u';
|
|
84
111
|
/**
|
|
@@ -90,24 +117,37 @@ const WEBSITE_LINK_TYPE_MAX_LENGTH = 32;
|
|
|
90
117
|
*/
|
|
91
118
|
const WEBSITE_LINK_TYPE_REGEX = /^[a-zA-Z0-9]{1,32}$/;
|
|
92
119
|
function isValidWebsiteLinkType(input) {
|
|
93
|
-
|
|
120
|
+
return WEBSITE_LINK_TYPE_REGEX.test(input);
|
|
94
121
|
}
|
|
95
122
|
/**
|
|
96
123
|
* Default max length of WebsiteLink's data string.
|
|
97
124
|
*/
|
|
98
125
|
const WEBSITE_LINK_ENCODED_DATA_MAX_LENGTH = 1000;
|
|
99
126
|
class WebsiteLink {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
t;
|
|
128
|
+
d;
|
|
129
|
+
constructor(template) {
|
|
130
|
+
if (template != null) {
|
|
131
|
+
this.t = template.t;
|
|
132
|
+
this.d = template.d;
|
|
133
|
+
}
|
|
106
134
|
}
|
|
107
|
-
}
|
|
108
135
|
}
|
|
109
|
-
__decorate([
|
|
110
|
-
|
|
136
|
+
__decorate([
|
|
137
|
+
classTransformer.Expose(),
|
|
138
|
+
classValidator.IsString(),
|
|
139
|
+
classValidator.IsNotEmpty(),
|
|
140
|
+
classValidator.Matches(WEBSITE_LINK_TYPE_REGEX),
|
|
141
|
+
classValidator.MaxLength(WEBSITE_LINK_TYPE_MAX_LENGTH),
|
|
142
|
+
__metadata("design:type", String)
|
|
143
|
+
], WebsiteLink.prototype, "t", void 0);
|
|
144
|
+
__decorate([
|
|
145
|
+
classTransformer.Expose(),
|
|
146
|
+
classValidator.IsString(),
|
|
147
|
+
classValidator.IsNotEmpty(),
|
|
148
|
+
classValidator.MaxLength(WEBSITE_LINK_ENCODED_DATA_MAX_LENGTH),
|
|
149
|
+
__metadata("design:type", String)
|
|
150
|
+
], WebsiteLink.prototype, "d", void 0);
|
|
111
151
|
|
|
112
152
|
/**
|
|
113
153
|
* Max length of WebsiteLink's data type.
|
|
@@ -135,47 +175,73 @@ const WEBSITE_FILE_LINK_NAME_MAX_LENGTH = 128;
|
|
|
135
175
|
const WEBSITE_FILE_LINK_DATA_MAX_LENGTH = WEBSITE_LINK_ENCODED_DATA_MAX_LENGTH - 3 - WEBSITE_FILE_LINK_TYPE_MAX_LENGTH - WEBSITE_FILE_LINK_MIME_TYPE_MAX_LENGTH - WEBSITE_FILE_LINK_NAME_MAX_LENGTH;
|
|
136
176
|
const WEBSITE_FILE_LINK_DATA_REGEX = /^[^|]+$/;
|
|
137
177
|
class WebsiteFileLink {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
178
|
+
type;
|
|
179
|
+
mime;
|
|
180
|
+
name;
|
|
181
|
+
data;
|
|
182
|
+
constructor(template) {
|
|
183
|
+
if (template != null) {
|
|
184
|
+
this.type = template.type;
|
|
185
|
+
this.mime = template.mime;
|
|
186
|
+
this.name = template.name;
|
|
187
|
+
this.data = template.data;
|
|
188
|
+
}
|
|
148
189
|
}
|
|
149
|
-
}
|
|
150
190
|
}
|
|
151
|
-
__decorate([
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
191
|
+
__decorate([
|
|
192
|
+
classTransformer.Expose(),
|
|
193
|
+
classValidator.IsOptional(),
|
|
194
|
+
classValidator.IsString(),
|
|
195
|
+
classValidator.Matches(WEBSITE_FILE_LINK_TYPE_REGEX),
|
|
196
|
+
classValidator.MaxLength(WEBSITE_LINK_TYPE_MAX_LENGTH),
|
|
197
|
+
__metadata("design:type", String)
|
|
198
|
+
], WebsiteFileLink.prototype, "type", void 0);
|
|
199
|
+
__decorate([
|
|
200
|
+
classTransformer.Expose(),
|
|
201
|
+
classValidator.IsOptional(),
|
|
202
|
+
classValidator.IsString(),
|
|
203
|
+
classValidator.Matches(WEBSITE_FILE_LINK_MIME_TYPE_REGEX),
|
|
204
|
+
classValidator.MaxLength(WEBSITE_FILE_LINK_MIME_TYPE_MAX_LENGTH),
|
|
205
|
+
__metadata("design:type", String)
|
|
206
|
+
], WebsiteFileLink.prototype, "mime", void 0);
|
|
207
|
+
__decorate([
|
|
208
|
+
classTransformer.Expose(),
|
|
209
|
+
classValidator.IsOptional(),
|
|
210
|
+
classValidator.IsString(),
|
|
211
|
+
classValidator.MaxLength(WEBSITE_FILE_LINK_NAME_MAX_LENGTH),
|
|
212
|
+
__metadata("design:type", String)
|
|
213
|
+
], WebsiteFileLink.prototype, "name", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
classValidator.IsString(),
|
|
216
|
+
classValidator.IsNotEmpty(),
|
|
217
|
+
classValidator.Matches(WEBSITE_FILE_LINK_DATA_REGEX),
|
|
218
|
+
classValidator.MaxLength(WEBSITE_FILE_LINK_DATA_MAX_LENGTH),
|
|
219
|
+
__metadata("design:type", String)
|
|
220
|
+
], WebsiteFileLink.prototype, "data", void 0);
|
|
155
221
|
const WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE = 'f';
|
|
156
222
|
function websiteFileLinkToWebsiteLink(input) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
223
|
+
return {
|
|
224
|
+
t: WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE,
|
|
225
|
+
d: encodeWebsiteFileLinkToWebsiteLinkEncodedData(input)
|
|
226
|
+
};
|
|
161
227
|
}
|
|
162
228
|
function websiteLinkToWebsiteLinkFile(input) {
|
|
163
|
-
|
|
164
|
-
|
|
229
|
+
const encodedData = input.d;
|
|
230
|
+
return decodeWebsiteLinkEncodedDataToWebsiteFileLink(encodedData);
|
|
165
231
|
}
|
|
166
232
|
const WEBSITE_FILE_LINK_ENCODE_SEPARATOR = '|';
|
|
167
233
|
function encodeWebsiteFileLinkToWebsiteLinkEncodedData(input) {
|
|
168
|
-
|
|
169
|
-
|
|
234
|
+
const encoded = [input.type, input.mime, input.data, input.name].map((x) => x || '').join(WEBSITE_FILE_LINK_ENCODE_SEPARATOR);
|
|
235
|
+
return encoded;
|
|
170
236
|
}
|
|
171
237
|
function decodeWebsiteLinkEncodedDataToWebsiteFileLink(input) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
238
|
+
const [type, mime, data, name] = util.splitJoinRemainder(input, WEBSITE_FILE_LINK_ENCODE_SEPARATOR, 4);
|
|
239
|
+
return util.filterFalsyAndEmptyValues({
|
|
240
|
+
type,
|
|
241
|
+
mime,
|
|
242
|
+
name,
|
|
243
|
+
data
|
|
244
|
+
});
|
|
179
245
|
}
|
|
180
246
|
|
|
181
247
|
/**
|
|
@@ -185,193 +251,194 @@ function decodeWebsiteLinkEncodedDataToWebsiteFileLink(input) {
|
|
|
185
251
|
* - https://facebook.com/facebook
|
|
186
252
|
*/
|
|
187
253
|
const WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
254
|
+
isolatePathComponents: 0,
|
|
255
|
+
removeTrailingSlash: true,
|
|
256
|
+
removeQueryParameters: true
|
|
191
257
|
});
|
|
192
258
|
function usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, prefix) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
259
|
+
const username = util.toRelativeSlashPathStartType(WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID(usernameOrWebsiteUrlToWebsiteUrl(input)));
|
|
260
|
+
if (prefix) {
|
|
261
|
+
return util.addPrefix(prefix, username);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
return username;
|
|
265
|
+
}
|
|
199
266
|
}
|
|
200
267
|
function usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, isolateFn) {
|
|
201
|
-
|
|
268
|
+
return util.toRelativeSlashPathStartType(isolateFn(usernameOrWebsiteUrlToWebsiteUrl(input)));
|
|
202
269
|
}
|
|
203
270
|
function usernameOrWebsiteUrlToWebsiteUrl(input) {
|
|
204
|
-
|
|
271
|
+
return util.hasWebsiteDomain(input) ? input : util.toAbsoluteSlashPathStartType(util.removeHttpFromUrl(input));
|
|
205
272
|
}
|
|
206
273
|
// MARK: Website
|
|
207
274
|
const WEBSITE_URL_WEBSITE_LINK_TYPE = 'w';
|
|
208
275
|
function websiteUrlToWebsiteLink(input) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
276
|
+
return {
|
|
277
|
+
t: WEBSITE_URL_WEBSITE_LINK_TYPE,
|
|
278
|
+
d: util.removeHttpFromUrl(input) // website urls are stored as-is without http/https
|
|
279
|
+
};
|
|
213
280
|
}
|
|
214
281
|
// MARK: Email
|
|
215
282
|
const EMAIL_URL_WEBSITE_LINK_TYPE = 'e';
|
|
216
283
|
function emailAddressToWebsiteLink(input) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
284
|
+
return {
|
|
285
|
+
t: EMAIL_URL_WEBSITE_LINK_TYPE,
|
|
286
|
+
d: input
|
|
287
|
+
};
|
|
221
288
|
}
|
|
222
289
|
// MARK: Phone
|
|
223
290
|
const PHONE_URL_WEBSITE_LINK_TYPE = 'p';
|
|
224
291
|
function phoneNumberToWebsiteLink(input) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
292
|
+
return {
|
|
293
|
+
t: PHONE_URL_WEBSITE_LINK_TYPE,
|
|
294
|
+
d: input
|
|
295
|
+
};
|
|
229
296
|
}
|
|
230
297
|
// MARK: Facebook
|
|
231
298
|
const FACEBOOK_BASE_URL = `https://www.facebook.com`;
|
|
232
299
|
const FACEBOOK_WEBSITE_LINK_TYPE = 'fb';
|
|
233
300
|
function facebookProfileUrlToWebsiteLink(input) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
301
|
+
return {
|
|
302
|
+
t: FACEBOOK_WEBSITE_LINK_TYPE,
|
|
303
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
304
|
+
};
|
|
238
305
|
}
|
|
239
306
|
function facebookProfileUrl(profileId) {
|
|
240
|
-
|
|
307
|
+
return `${FACEBOOK_BASE_URL}/${profileId}`;
|
|
241
308
|
}
|
|
242
309
|
// MARK: Instagram
|
|
243
310
|
const INSTAGRAM_BASE_URL = `https://www.instagram.com`;
|
|
244
311
|
const INSTAGRAM_WEBSITE_LINK_TYPE = 'ig';
|
|
245
312
|
function instagramProfileUrlToWebsiteLink(input) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
313
|
+
return {
|
|
314
|
+
t: INSTAGRAM_WEBSITE_LINK_TYPE,
|
|
315
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
316
|
+
};
|
|
250
317
|
}
|
|
251
318
|
function instagramProfileUrl(profileId) {
|
|
252
|
-
|
|
319
|
+
return `${INSTAGRAM_BASE_URL}/${profileId}`;
|
|
253
320
|
}
|
|
254
321
|
// MARK: Twitter
|
|
255
322
|
const TWITTER_BASE_URL = `https://www.twitter.com`;
|
|
256
323
|
const TWITTER_WEBSITE_LINK_TYPE = 'tw';
|
|
257
324
|
function twitterProfileUrlToWebsiteLink(input) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
325
|
+
return {
|
|
326
|
+
t: TWITTER_WEBSITE_LINK_TYPE,
|
|
327
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
328
|
+
};
|
|
262
329
|
}
|
|
263
330
|
function twitterProfileUrl(profileId) {
|
|
264
|
-
|
|
331
|
+
return `${TWITTER_BASE_URL}/${profileId}`;
|
|
265
332
|
}
|
|
266
333
|
// MARK: Tiktok
|
|
267
334
|
const TIKTOK_BASE_URL = `https://tiktok.com`;
|
|
268
335
|
const TIKTOK_USERNAME_PREFIX = '@';
|
|
269
336
|
const TIKTOK_WEBSITE_LINK_TYPE = 'tt';
|
|
270
337
|
function tiktokProfileUrlToWebsiteLink(input) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
338
|
+
return {
|
|
339
|
+
t: TIKTOK_WEBSITE_LINK_TYPE,
|
|
340
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, TIKTOK_USERNAME_PREFIX)
|
|
341
|
+
};
|
|
275
342
|
}
|
|
276
343
|
function tiktokProfileUrl(profileId) {
|
|
277
|
-
|
|
344
|
+
return `${TIKTOK_BASE_URL}/@${profileId}`;
|
|
278
345
|
}
|
|
279
346
|
// MARK: Snapchat
|
|
280
347
|
const SNAPCHAT_BASE_URL = `https://snapchat.com`;
|
|
281
348
|
const SNAPCHAT_WEBSITE_LINK_TYPE = 'sc';
|
|
282
349
|
const SNAPCHAT_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
350
|
+
ignoredBasePath: 'add',
|
|
351
|
+
isolatePathComponents: 0,
|
|
352
|
+
removeTrailingSlash: true,
|
|
353
|
+
removeQueryParameters: true
|
|
287
354
|
});
|
|
288
355
|
function snapchatProfileUrlToWebsiteLink(input) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
356
|
+
return {
|
|
357
|
+
t: SNAPCHAT_WEBSITE_LINK_TYPE,
|
|
358
|
+
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, SNAPCHAT_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
359
|
+
};
|
|
293
360
|
}
|
|
294
361
|
function snapchatProfileUrl(profileId) {
|
|
295
|
-
|
|
362
|
+
return `${SNAPCHAT_BASE_URL}/add/${profileId}`;
|
|
296
363
|
}
|
|
297
364
|
// MARK: YouTube
|
|
298
365
|
const YOUTUBE_BASE_URL = `https://youtube.com`;
|
|
299
366
|
const YOUTUBE_WEBSITE_LINK_TYPE = 'yt';
|
|
300
367
|
const YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
368
|
+
ignoredBasePath: 'c',
|
|
369
|
+
isolatePathComponents: 0,
|
|
370
|
+
removeTrailingSlash: true,
|
|
371
|
+
removeQueryParameters: true
|
|
305
372
|
});
|
|
306
373
|
function youtubeProfileUrlToWebsiteLink(input) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
374
|
+
return {
|
|
375
|
+
t: YOUTUBE_WEBSITE_LINK_TYPE,
|
|
376
|
+
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
377
|
+
};
|
|
311
378
|
}
|
|
312
379
|
function youtubeProfileUrl(profileId) {
|
|
313
|
-
|
|
380
|
+
return `${YOUTUBE_BASE_URL}/c/${profileId}`;
|
|
314
381
|
}
|
|
315
382
|
// MARK: PayPal
|
|
316
383
|
const PAYPAL_BASE_URL = `https://paypal.me`;
|
|
317
384
|
const PAYPAL_WEBSITE_LINK_TYPE = 'pp';
|
|
318
385
|
function paypalProfileUrlToWebsiteLink(input) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
386
|
+
return {
|
|
387
|
+
t: PAYPAL_WEBSITE_LINK_TYPE,
|
|
388
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
389
|
+
};
|
|
323
390
|
}
|
|
324
391
|
function paypalProfileUrl(profileId) {
|
|
325
|
-
|
|
392
|
+
return `${PAYPAL_BASE_URL}/${profileId}`;
|
|
326
393
|
}
|
|
327
394
|
// MARK: Cashapp
|
|
328
395
|
const CASHAPP_BASE_URL = `https://cash.app`;
|
|
329
396
|
const CASHAPP_USERNAME_PREFIX = '$';
|
|
330
397
|
const CASHAPP_WEBSITE_LINK_TYPE = 'ca';
|
|
331
398
|
function cashappProfileUrlToWebsiteLink(input) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
399
|
+
return {
|
|
400
|
+
t: CASHAPP_WEBSITE_LINK_TYPE,
|
|
401
|
+
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, CASHAPP_USERNAME_PREFIX)
|
|
402
|
+
};
|
|
336
403
|
}
|
|
337
404
|
function cashappProfileUrl(profileId) {
|
|
338
|
-
|
|
405
|
+
return `${CASHAPP_BASE_URL}/$${profileId}`;
|
|
339
406
|
}
|
|
340
407
|
// MARK: Venmo
|
|
341
408
|
const VENMO_BASE_URL = `https://account.venmo.com`;
|
|
342
409
|
const VENMO_WEBSITE_LINK_TYPE = 'vn';
|
|
343
410
|
const VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
411
|
+
ignoredBasePath: 'u',
|
|
412
|
+
isolatePathComponents: 0,
|
|
413
|
+
removeTrailingSlash: true,
|
|
414
|
+
removeQueryParameters: true
|
|
348
415
|
});
|
|
349
416
|
function venmoProfileUrlToWebsiteLink(input) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
417
|
+
return {
|
|
418
|
+
t: VENMO_WEBSITE_LINK_TYPE,
|
|
419
|
+
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
420
|
+
};
|
|
354
421
|
}
|
|
355
422
|
function venmoProfileUrl(profileId) {
|
|
356
|
-
|
|
423
|
+
return `${VENMO_BASE_URL}/u/${profileId}`;
|
|
357
424
|
}
|
|
358
425
|
// MARK: Spotify
|
|
359
426
|
const SPOTIFY_BASE_URL = `https://open.spotify.com/`;
|
|
360
427
|
const SPOTIFY_WEBSITE_LINK_TYPE = 'sp';
|
|
361
428
|
const SPOTIFY_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
429
|
+
ignoredBasePath: 'user',
|
|
430
|
+
isolatePathComponents: 0,
|
|
431
|
+
removeTrailingSlash: true,
|
|
432
|
+
removeQueryParameters: true
|
|
366
433
|
});
|
|
367
434
|
function spotifyProfileUrlToWebsiteLink(input) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
435
|
+
return {
|
|
436
|
+
t: SPOTIFY_WEBSITE_LINK_TYPE,
|
|
437
|
+
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, SPOTIFY_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
438
|
+
};
|
|
372
439
|
}
|
|
373
440
|
function spotifyProfileUrl(profileId) {
|
|
374
|
-
|
|
441
|
+
return `${SPOTIFY_BASE_URL}/user/${profileId}`;
|
|
375
442
|
}
|
|
376
443
|
|
|
377
444
|
const GRANTED_SYS_ADMIN_ROLE_KEY = 'sysadmin';
|
|
@@ -384,7 +451,7 @@ const GRANTED_ADMIN_ROLE_KEY = 'admin';
|
|
|
384
451
|
* @returns
|
|
385
452
|
*/
|
|
386
453
|
function isGrantedAdminLevelRole(role) {
|
|
387
|
-
|
|
454
|
+
return role === GRANTED_ADMIN_ROLE_KEY || role === GRANTED_OWNER_ROLE_KEY;
|
|
388
455
|
}
|
|
389
456
|
const GRANTED_READ_ROLE_KEY = 'read';
|
|
390
457
|
const GRANTED_UPDATE_ROLE_KEY = 'update';
|
|
@@ -392,20 +459,20 @@ const GRANTED_DELETE_ROLE_KEY = 'delete';
|
|
|
392
459
|
const FULL_ACCESS_ROLE_KEY = '__FULL__';
|
|
393
460
|
const NO_ACCESS_ROLE_KEY = '__EMPTY__';
|
|
394
461
|
function noAccessRoleMap() {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
462
|
+
return {
|
|
463
|
+
[NO_ACCESS_ROLE_KEY]: true
|
|
464
|
+
};
|
|
398
465
|
}
|
|
399
466
|
function isNoAccessRoleMap(input) {
|
|
400
|
-
|
|
467
|
+
return input[NO_ACCESS_ROLE_KEY] === true;
|
|
401
468
|
}
|
|
402
469
|
function fullAccessRoleMap() {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
470
|
+
return {
|
|
471
|
+
[FULL_ACCESS_ROLE_KEY]: true
|
|
472
|
+
};
|
|
406
473
|
}
|
|
407
474
|
function isFullAccessRoleMap(input) {
|
|
408
|
-
|
|
475
|
+
return input[FULL_ACCESS_ROLE_KEY] === true;
|
|
409
476
|
}
|
|
410
477
|
/**
|
|
411
478
|
* Creates a GrantedRoleMapReader.
|
|
@@ -414,61 +481,63 @@ function isFullAccessRoleMap(input) {
|
|
|
414
481
|
* @returns
|
|
415
482
|
*/
|
|
416
483
|
function grantedRoleMapReader(map) {
|
|
417
|
-
|
|
484
|
+
return new GrantedRoleMapReaderInstance(map);
|
|
418
485
|
}
|
|
419
486
|
class GrantedRoleMapReaderInstance {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
hasNoAccess() {
|
|
425
|
-
return this._map[NO_ACCESS_ROLE_KEY];
|
|
426
|
-
}
|
|
427
|
-
truthMap(input) {
|
|
428
|
-
const result = {};
|
|
429
|
-
util.forEachKeyValue(input, {
|
|
430
|
-
forEach: ([role, value]) => {
|
|
431
|
-
if (this.hasRole(role)) {
|
|
432
|
-
result[role] = value;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
return result;
|
|
437
|
-
}
|
|
438
|
-
hasRole(role) {
|
|
439
|
-
return this.hasRoles('any', role);
|
|
440
|
-
}
|
|
441
|
-
hasRoles(setIncludes, inputRoles) {
|
|
442
|
-
if (this._map[FULL_ACCESS_ROLE_KEY]) {
|
|
443
|
-
return true;
|
|
444
|
-
} else {
|
|
445
|
-
return this.containsRoles(setIncludes, inputRoles);
|
|
487
|
+
_map;
|
|
488
|
+
constructor(map) {
|
|
489
|
+
this._map = map;
|
|
446
490
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const roles = util.iterableToArray(inputRoles);
|
|
450
|
-
if (setIncludes === 'any') {
|
|
451
|
-
return this.containsAnyRole(roles);
|
|
452
|
-
} else {
|
|
453
|
-
return this.containsEachRole(roles);
|
|
491
|
+
hasNoAccess() {
|
|
492
|
+
return this._map[NO_ACCESS_ROLE_KEY];
|
|
454
493
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
494
|
+
truthMap(input) {
|
|
495
|
+
const result = {};
|
|
496
|
+
util.forEachKeyValue(input, {
|
|
497
|
+
forEach: ([role, value]) => {
|
|
498
|
+
if (this.hasRole(role)) {
|
|
499
|
+
result[role] = value;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
505
|
+
hasRole(role) {
|
|
506
|
+
return this.hasRoles('any', role);
|
|
507
|
+
}
|
|
508
|
+
hasRoles(setIncludes, inputRoles) {
|
|
509
|
+
if (this._map[FULL_ACCESS_ROLE_KEY]) {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
return this.containsRoles(setIncludes, inputRoles);
|
|
514
|
+
}
|
|
461
515
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
516
|
+
containsRoles(setIncludes, inputRoles) {
|
|
517
|
+
const roles = util.iterableToArray(inputRoles);
|
|
518
|
+
if (setIncludes === 'any') {
|
|
519
|
+
return this.containsAnyRole(roles);
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
return this.containsEachRole(roles);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
containsAnyRole(roles) {
|
|
526
|
+
for (const role of roles) {
|
|
527
|
+
if (this._map[role]) {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
467
531
|
return false;
|
|
468
|
-
}
|
|
469
532
|
}
|
|
470
|
-
|
|
471
|
-
|
|
533
|
+
containsEachRole(roles) {
|
|
534
|
+
for (const role of roles) {
|
|
535
|
+
if (!this._map[role]) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
472
541
|
}
|
|
473
542
|
/**
|
|
474
543
|
* Converts the input array of roles to a GrantedRoleKeysMap.
|
|
@@ -477,74 +546,77 @@ class GrantedRoleMapReaderInstance {
|
|
|
477
546
|
* @returns
|
|
478
547
|
*/
|
|
479
548
|
function grantedRoleKeysMapFromArray(roles, value = true) {
|
|
480
|
-
|
|
549
|
+
return util.arrayToObject(roles, (x) => x, () => value);
|
|
481
550
|
}
|
|
482
551
|
|
|
483
552
|
function noAccessContextGrantedModelRoles(context, data) {
|
|
484
|
-
|
|
553
|
+
return contextGrantedModelRoles(context, data, noAccessRoleMap());
|
|
485
554
|
}
|
|
486
555
|
function fullAccessGrantedModelRoles(context, data) {
|
|
487
|
-
|
|
556
|
+
return contextGrantedModelRoles(context, data, fullAccessRoleMap());
|
|
488
557
|
}
|
|
489
558
|
function contextGrantedModelRoles(context, data, roles) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
559
|
+
return {
|
|
560
|
+
data,
|
|
561
|
+
context,
|
|
562
|
+
roleMap: roles
|
|
563
|
+
};
|
|
495
564
|
}
|
|
496
565
|
|
|
497
566
|
/**
|
|
498
567
|
* Abstract ModelPermissionService implementation.
|
|
499
568
|
*/
|
|
500
569
|
class AbstractModelPermissionService {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
get modelLoader() {
|
|
506
|
-
return this._modelLoader;
|
|
507
|
-
}
|
|
508
|
-
async roleMapForKeyContext(key, context) {
|
|
509
|
-
const model = await this.modelLoader.loadModelForKey(key, context);
|
|
510
|
-
let result;
|
|
511
|
-
if (model != null) {
|
|
512
|
-
result = await this.roleMapForModelContext(model, context);
|
|
513
|
-
} else {
|
|
514
|
-
result = noAccessContextGrantedModelRoles(context);
|
|
570
|
+
_modelLoader;
|
|
571
|
+
constructor(modelLoader) {
|
|
572
|
+
this._modelLoader = modelLoader;
|
|
515
573
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
574
|
+
get modelLoader() {
|
|
575
|
+
return this._modelLoader;
|
|
576
|
+
}
|
|
577
|
+
async roleMapForKeyContext(key, context) {
|
|
578
|
+
const model = await this.modelLoader.loadModelForKey(key, context);
|
|
579
|
+
let result;
|
|
580
|
+
if (model != null) {
|
|
581
|
+
result = await this.roleMapForModelContext(model, context);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
result = noAccessContextGrantedModelRoles(context);
|
|
585
|
+
}
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
async roleMapForModelContext(model, context) {
|
|
589
|
+
const output = await this.outputForModel(model, context);
|
|
590
|
+
let result;
|
|
591
|
+
if (output != null && this.isUsableOutputForRoles(output, context)) {
|
|
592
|
+
result = await this.getRoleMapForOutput(output, context, model);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
result = noAccessContextGrantedModelRoles(context, output);
|
|
596
|
+
}
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
async getRoleMapForOutput(output, context, model) {
|
|
600
|
+
const roleMap = await this.roleMapForModel(output, context, model);
|
|
601
|
+
return contextGrantedModelRoles(context, output, roleMap);
|
|
602
|
+
}
|
|
603
|
+
isUsableOutputForRoles(output, context) {
|
|
604
|
+
return true; // can override in parent functions to further filter roles.
|
|
525
605
|
}
|
|
526
|
-
return result;
|
|
527
|
-
}
|
|
528
|
-
async getRoleMapForOutput(output, context, model) {
|
|
529
|
-
const roleMap = await this.roleMapForModel(output, context, model);
|
|
530
|
-
return contextGrantedModelRoles(context, output, roleMap);
|
|
531
|
-
}
|
|
532
|
-
isUsableOutputForRoles(output, context) {
|
|
533
|
-
return true; // can override in parent functions to further filter roles.
|
|
534
|
-
}
|
|
535
606
|
}
|
|
536
607
|
|
|
537
608
|
function syncEntityCommonTypeIdPairFactory(commonType) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
609
|
+
return (input) => {
|
|
610
|
+
if (typeof input === 'string') {
|
|
611
|
+
return {
|
|
612
|
+
commonType,
|
|
613
|
+
commonId: input
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
return input;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
548
620
|
}
|
|
549
621
|
/**
|
|
550
622
|
* Creates a SyncEntityFactory.
|
|
@@ -553,496 +625,323 @@ function syncEntityCommonTypeIdPairFactory(commonType) {
|
|
|
553
625
|
* @returns
|
|
554
626
|
*/
|
|
555
627
|
function syncEntityFactory(config) {
|
|
556
|
-
|
|
557
|
-
idFactory
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
return {
|
|
568
|
-
commonType,
|
|
569
|
-
commonId,
|
|
570
|
-
id,
|
|
571
|
-
sourceInfo
|
|
628
|
+
const { idFactory: inputIdFactory, sourceInfo } = config;
|
|
629
|
+
const idFactory = inputIdFactory ?? util.MAP_IDENTITY;
|
|
630
|
+
return (input) => {
|
|
631
|
+
const { commonType, commonId } = input;
|
|
632
|
+
const id = idFactory(commonId);
|
|
633
|
+
return {
|
|
634
|
+
commonType,
|
|
635
|
+
commonId,
|
|
636
|
+
id,
|
|
637
|
+
sourceInfo
|
|
638
|
+
};
|
|
572
639
|
};
|
|
573
|
-
};
|
|
574
640
|
}
|
|
575
641
|
|
|
576
|
-
var makeError = {exports: {}};
|
|
577
|
-
|
|
578
|
-
(function (module, exports$1) {
|
|
579
|
-
|
|
580
|
-
// ===================================================================
|
|
581
|
-
|
|
582
|
-
var construct = typeof Reflect !== "undefined" ? Reflect.construct : undefined;
|
|
583
|
-
var defineProperty = Object.defineProperty;
|
|
584
|
-
|
|
585
|
-
// -------------------------------------------------------------------
|
|
586
|
-
|
|
587
|
-
var captureStackTrace = Error.captureStackTrace;
|
|
588
|
-
if (captureStackTrace === undefined) {
|
|
589
|
-
captureStackTrace = function captureStackTrace(error) {
|
|
590
|
-
var container = new Error();
|
|
591
|
-
|
|
592
|
-
defineProperty(error, "stack", {
|
|
593
|
-
configurable: true,
|
|
594
|
-
get: function getStack() {
|
|
595
|
-
var stack = container.stack;
|
|
596
|
-
|
|
597
|
-
// Replace property with value for faster future accesses.
|
|
598
|
-
defineProperty(this, "stack", {
|
|
599
|
-
configurable: true,
|
|
600
|
-
value: stack,
|
|
601
|
-
writable: true,
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
return stack;
|
|
605
|
-
},
|
|
606
|
-
set: function setStack(stack) {
|
|
607
|
-
defineProperty(error, "stack", {
|
|
608
|
-
configurable: true,
|
|
609
|
-
value: stack,
|
|
610
|
-
writable: true,
|
|
611
|
-
});
|
|
612
|
-
},
|
|
613
|
-
});
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// -------------------------------------------------------------------
|
|
618
|
-
|
|
619
|
-
function BaseError(message) {
|
|
620
|
-
if (message !== undefined) {
|
|
621
|
-
defineProperty(this, "message", {
|
|
622
|
-
configurable: true,
|
|
623
|
-
value: message,
|
|
624
|
-
writable: true,
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
var cname = this.constructor.name;
|
|
629
|
-
if (cname !== undefined && cname !== this.name) {
|
|
630
|
-
defineProperty(this, "name", {
|
|
631
|
-
configurable: true,
|
|
632
|
-
value: cname,
|
|
633
|
-
writable: true,
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
captureStackTrace(this, this.constructor);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
BaseError.prototype = Object.create(Error.prototype, {
|
|
641
|
-
// See: https://github.com/JsCommunity/make-error/issues/4
|
|
642
|
-
constructor: {
|
|
643
|
-
configurable: true,
|
|
644
|
-
value: BaseError,
|
|
645
|
-
writable: true,
|
|
646
|
-
},
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// -------------------------------------------------------------------
|
|
650
|
-
|
|
651
|
-
// Sets the name of a function if possible (depends of the JS engine).
|
|
652
|
-
var setFunctionName = (function() {
|
|
653
|
-
function setFunctionName(fn, name) {
|
|
654
|
-
return defineProperty(fn, "name", {
|
|
655
|
-
configurable: true,
|
|
656
|
-
value: name,
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
try {
|
|
660
|
-
var f = function() {};
|
|
661
|
-
setFunctionName(f, "foo");
|
|
662
|
-
if (f.name === "foo") {
|
|
663
|
-
return setFunctionName;
|
|
664
|
-
}
|
|
665
|
-
} catch (_) {}
|
|
666
|
-
})();
|
|
667
|
-
|
|
668
|
-
// -------------------------------------------------------------------
|
|
669
|
-
|
|
670
|
-
function makeError(constructor, super_) {
|
|
671
|
-
if (super_ == null || super_ === Error) {
|
|
672
|
-
super_ = BaseError;
|
|
673
|
-
} else if (typeof super_ !== "function") {
|
|
674
|
-
throw new TypeError("super_ should be a function");
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
var name;
|
|
678
|
-
if (typeof constructor === "string") {
|
|
679
|
-
name = constructor;
|
|
680
|
-
constructor =
|
|
681
|
-
construct !== undefined
|
|
682
|
-
? function() {
|
|
683
|
-
return construct(super_, arguments, this.constructor);
|
|
684
|
-
}
|
|
685
|
-
: function() {
|
|
686
|
-
super_.apply(this, arguments);
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
// If the name can be set, do it once and for all.
|
|
690
|
-
if (setFunctionName !== undefined) {
|
|
691
|
-
setFunctionName(constructor, name);
|
|
692
|
-
name = undefined;
|
|
693
|
-
}
|
|
694
|
-
} else if (typeof constructor !== "function") {
|
|
695
|
-
throw new TypeError("constructor should be either a string or a function");
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// Also register the super constructor also as `constructor.super_` just
|
|
699
|
-
// like Node's `util.inherits()`.
|
|
700
|
-
//
|
|
701
|
-
// eslint-disable-next-line dot-notation
|
|
702
|
-
constructor.super_ = constructor["super"] = super_;
|
|
703
|
-
|
|
704
|
-
var properties = {
|
|
705
|
-
constructor: {
|
|
706
|
-
configurable: true,
|
|
707
|
-
value: constructor,
|
|
708
|
-
writable: true,
|
|
709
|
-
},
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
// If the name could not be set on the constructor, set it on the
|
|
713
|
-
// prototype.
|
|
714
|
-
if (name !== undefined) {
|
|
715
|
-
properties.name = {
|
|
716
|
-
configurable: true,
|
|
717
|
-
value: name,
|
|
718
|
-
writable: true,
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
constructor.prototype = Object.create(super_.prototype, properties);
|
|
722
|
-
|
|
723
|
-
return constructor;
|
|
724
|
-
}
|
|
725
|
-
exports$1 = module.exports = makeError;
|
|
726
|
-
exports$1.BaseError = BaseError;
|
|
727
|
-
} (makeError, makeError.exports));
|
|
728
|
-
|
|
729
|
-
var makeErrorExports = makeError.exports;
|
|
730
|
-
|
|
731
642
|
/**
|
|
732
643
|
* Error thrown when the common type is not known/registered.
|
|
733
644
|
*/
|
|
734
|
-
class UnregisteredSyncEntityCommonTypeError extends
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
645
|
+
class UnregisteredSyncEntityCommonTypeError extends makeError.BaseError {
|
|
646
|
+
commonType;
|
|
647
|
+
constructor(commonType) {
|
|
648
|
+
super(`The common type "${commonType}" is not registered.`);
|
|
649
|
+
this.commonType = commonType;
|
|
650
|
+
}
|
|
740
651
|
}
|
|
741
652
|
/**
|
|
742
653
|
* Error thrown when no primary sync source is found for an entity.
|
|
743
654
|
*/
|
|
744
|
-
class NoPrimarySyncSourceError extends
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
655
|
+
class NoPrimarySyncSourceError extends makeError.BaseError {
|
|
656
|
+
entity;
|
|
657
|
+
constructor(entity) {
|
|
658
|
+
super(`No primary sync source found for entity "${entity.commonType}:${entity.commonId}".`);
|
|
659
|
+
this.entity = entity;
|
|
660
|
+
}
|
|
750
661
|
}
|
|
751
662
|
/**
|
|
752
663
|
* Error thrown when multiple primary sync sources are found for an entity.
|
|
753
664
|
*/
|
|
754
|
-
class MultiplePrimarySyncSourceError extends
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
665
|
+
class MultiplePrimarySyncSourceError extends makeError.BaseError {
|
|
666
|
+
entity;
|
|
667
|
+
constructor(entity) {
|
|
668
|
+
super(`Multiple primary sync sources found for entity "${entity.commonType}:${entity.commonId}".`);
|
|
669
|
+
this.entity = entity;
|
|
670
|
+
}
|
|
760
671
|
}
|
|
761
672
|
/**
|
|
762
673
|
* Error thrown when a synchronization fails for an entity.
|
|
763
674
|
*/
|
|
764
|
-
class SynchronizationFailedError extends
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
675
|
+
class SynchronizationFailedError extends makeError.BaseError {
|
|
676
|
+
entity;
|
|
677
|
+
error;
|
|
678
|
+
constructor(entity, error) {
|
|
679
|
+
super(`Synchronization failed for entity "${entity.commonType}:${entity.commonId}". Error: ${error}`);
|
|
680
|
+
this.entity = entity;
|
|
681
|
+
this.error = error;
|
|
682
|
+
}
|
|
772
683
|
}
|
|
773
684
|
|
|
774
685
|
function syncEntitySynchronizer(config) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
686
|
+
const map = new Map(config.commonTypeSynchronizers.map((x) => [x.commonType, x]));
|
|
687
|
+
const commonTypes = Array.from(map.keys());
|
|
688
|
+
const commonTypeSynchronizer = (input) => {
|
|
689
|
+
const synchronizer = map.get(input);
|
|
690
|
+
if (!synchronizer) {
|
|
691
|
+
throw new UnregisteredSyncEntityCommonTypeError(input);
|
|
692
|
+
}
|
|
693
|
+
return synchronizer;
|
|
694
|
+
};
|
|
695
|
+
return {
|
|
696
|
+
commonTypes,
|
|
697
|
+
commonTypeSynchronizer,
|
|
698
|
+
synchronizeInstance: (input) => {
|
|
699
|
+
const synchronizer = commonTypeSynchronizer(input.commonType);
|
|
700
|
+
return synchronizer.synchronizeInstance(input);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
792
703
|
}
|
|
793
704
|
|
|
794
705
|
function basicSyncEntityCommonTypeSynchronizerInstanceFactory(config) {
|
|
795
|
-
|
|
796
|
-
commonType
|
|
797
|
-
sources,
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
} = config;
|
|
801
|
-
const syncEntityCommonTypeIdPairForType = syncEntityCommonTypeIdPairFactory(commonType);
|
|
802
|
-
const sourcesByContextType = util.makeValuesGroupMap(sources, x => x.contextType);
|
|
803
|
-
const allGlobalSources = sourcesByContextType.get('global') ?? [];
|
|
804
|
-
const allContextSources = sourcesByContextType.get('context') ?? [];
|
|
805
|
-
/**
|
|
806
|
-
* Loads the relevant sources for the given entity and context.
|
|
807
|
-
*
|
|
808
|
-
* @param entitySourceContext The contextual information for the entity.
|
|
809
|
-
* @returns The relevant sources for the entity.
|
|
810
|
-
*/
|
|
811
|
-
function loadSources(entityCommonTypeIdPair, entitySourceContext) {
|
|
812
|
-
const {
|
|
813
|
-
globalSources,
|
|
814
|
-
contextSources
|
|
815
|
-
} = entitySourceContext;
|
|
816
|
-
// load/filter global sources
|
|
817
|
-
const globalMap = new Map(globalSources.map(x => {
|
|
818
|
-
let sourceId;
|
|
819
|
-
let flowType;
|
|
820
|
-
if (typeof x === 'string') {
|
|
821
|
-
sourceId = x;
|
|
822
|
-
flowType = 'unset';
|
|
823
|
-
} else {
|
|
824
|
-
sourceId = x.sourceId;
|
|
825
|
-
flowType = x.flowType;
|
|
826
|
-
}
|
|
827
|
-
return [sourceId, {
|
|
828
|
-
sourceId,
|
|
829
|
-
flowType
|
|
830
|
-
}];
|
|
831
|
-
}));
|
|
832
|
-
const relevantGlobalSources = util.filterMaybeArrayValues(allGlobalSources.map(x => {
|
|
833
|
-
const sourceContext = globalMap.get(x.info.id);
|
|
834
|
-
let result;
|
|
835
|
-
if (sourceContext != null) {
|
|
836
|
-
result = {
|
|
837
|
-
entityCommonTypeIdPair,
|
|
838
|
-
flowType: sourceContext.flowType ?? x.defaultFlowType ?? 'unset',
|
|
839
|
-
source: x
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
return result;
|
|
843
|
-
}));
|
|
844
|
-
// load/filter context sources
|
|
845
|
-
const contextMap = new Map(contextSources.map(x => [x.sourceId, x]));
|
|
846
|
-
const relevantContextSources = util.filterMaybeArrayValues(allContextSources.map(x => {
|
|
847
|
-
const sourceContext = contextMap.get(x.info.id);
|
|
848
|
-
let result;
|
|
849
|
-
if (sourceContext != null) {
|
|
850
|
-
const flowType = sourceContext.flowType ?? x.defaultFlowType ?? 'unset';
|
|
851
|
-
result = {
|
|
852
|
-
entityCommonTypeIdPair,
|
|
853
|
-
flowType,
|
|
854
|
-
source: x,
|
|
855
|
-
context: sourceContext
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
return result;
|
|
859
|
-
}));
|
|
860
|
-
const allSources = [...relevantGlobalSources, ...relevantContextSources];
|
|
861
|
-
// sort by order, with primary first
|
|
862
|
-
allSources.sort(util.sortByNumberFunction(x => {
|
|
863
|
-
let result;
|
|
864
|
-
switch (x.flowType) {
|
|
865
|
-
case 'primary':
|
|
866
|
-
result = 1;
|
|
867
|
-
break;
|
|
868
|
-
case 'secondary':
|
|
869
|
-
result = 2;
|
|
870
|
-
break;
|
|
871
|
-
case 'unset':
|
|
872
|
-
default:
|
|
873
|
-
result = 3;
|
|
874
|
-
break;
|
|
875
|
-
}
|
|
876
|
-
return result;
|
|
877
|
-
}));
|
|
878
|
-
return allSources;
|
|
879
|
-
}
|
|
880
|
-
const synchronizeInstance = async input => {
|
|
881
|
-
const syncEntityCommonTypeIdPair = syncEntityCommonTypeIdPairForType(input);
|
|
882
|
-
const _loadRelevantSources = async () => {
|
|
883
|
-
const entitySourceContext = await entitySourceContextLoader(syncEntityCommonTypeIdPair);
|
|
884
|
-
const relevantSources = loadSources(syncEntityCommonTypeIdPair, entitySourceContext);
|
|
885
|
-
return relevantSources;
|
|
886
|
-
};
|
|
887
|
-
let loadRelevantSources;
|
|
888
|
-
if (dynamicSources) {
|
|
889
|
-
// if dynamic, reload each time and do not cache
|
|
890
|
-
loadRelevantSources = _loadRelevantSources;
|
|
891
|
-
} else {
|
|
892
|
-
// if not dynamic, make a cached getter
|
|
893
|
-
loadRelevantSources = util.cachedGetter(_loadRelevantSources);
|
|
894
|
-
}
|
|
706
|
+
const { commonType, sources, entitySourceContextLoader, dynamicSources = false } = config;
|
|
707
|
+
const syncEntityCommonTypeIdPairForType = syncEntityCommonTypeIdPairFactory(commonType);
|
|
708
|
+
const sourcesByContextType = util.makeValuesGroupMap(sources, (x) => x.contextType);
|
|
709
|
+
const allGlobalSources = sourcesByContextType.get('global') ?? [];
|
|
710
|
+
const allContextSources = sourcesByContextType.get('context') ?? [];
|
|
895
711
|
/**
|
|
896
|
-
*
|
|
712
|
+
* Loads the relevant sources for the given entity and context.
|
|
713
|
+
*
|
|
714
|
+
* @param entitySourceContext The contextual information for the entity.
|
|
715
|
+
* @returns The relevant sources for the entity.
|
|
897
716
|
*/
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
case 0:
|
|
908
|
-
throw new NoPrimarySyncSourceError(syncEntityCommonTypeIdPair);
|
|
909
|
-
case 1:
|
|
910
|
-
break;
|
|
911
|
-
default:
|
|
912
|
-
throw new MultiplePrimarySyncSourceError(syncEntityCommonTypeIdPair);
|
|
913
|
-
}
|
|
914
|
-
function synchronizeInstance(source, deleted) {
|
|
915
|
-
const [input, sourceInstance] = source;
|
|
916
|
-
const promise = deleted ? sourceInstance.synchronizeDelete() : sourceInstance.synchronize();
|
|
917
|
-
return promise.catch(error => {
|
|
918
|
-
const errorResult = {
|
|
919
|
-
type: 'error',
|
|
920
|
-
error,
|
|
921
|
-
entity: {
|
|
922
|
-
...syncEntityCommonTypeIdPair,
|
|
923
|
-
sourceInfo: input.source.info,
|
|
924
|
-
id: ''
|
|
717
|
+
function loadSources(entityCommonTypeIdPair, entitySourceContext) {
|
|
718
|
+
const { globalSources, contextSources } = entitySourceContext;
|
|
719
|
+
// load/filter global sources
|
|
720
|
+
const globalMap = new Map(globalSources.map((x) => {
|
|
721
|
+
let sourceId;
|
|
722
|
+
let flowType;
|
|
723
|
+
if (typeof x === 'string') {
|
|
724
|
+
sourceId = x;
|
|
725
|
+
flowType = 'unset';
|
|
925
726
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
727
|
+
else {
|
|
728
|
+
sourceId = x.sourceId;
|
|
729
|
+
flowType = x.flowType;
|
|
730
|
+
}
|
|
731
|
+
return [sourceId, { sourceId, flowType }];
|
|
732
|
+
}));
|
|
733
|
+
const relevantGlobalSources = util.filterMaybeArrayValues(allGlobalSources.map((x) => {
|
|
734
|
+
const sourceContext = globalMap.get(x.info.id);
|
|
735
|
+
let result;
|
|
736
|
+
if (sourceContext != null) {
|
|
737
|
+
result = {
|
|
738
|
+
entityCommonTypeIdPair,
|
|
739
|
+
flowType: sourceContext.flowType ?? x.defaultFlowType ?? 'unset',
|
|
740
|
+
source: x
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
return result;
|
|
744
|
+
}));
|
|
745
|
+
// load/filter context sources
|
|
746
|
+
const contextMap = new Map(contextSources.map((x) => [x.sourceId, x]));
|
|
747
|
+
const relevantContextSources = util.filterMaybeArrayValues(allContextSources.map((x) => {
|
|
748
|
+
const sourceContext = contextMap.get(x.info.id);
|
|
749
|
+
let result;
|
|
750
|
+
if (sourceContext != null) {
|
|
751
|
+
const flowType = sourceContext.flowType ?? x.defaultFlowType ?? 'unset';
|
|
752
|
+
result = {
|
|
753
|
+
entityCommonTypeIdPair,
|
|
754
|
+
flowType,
|
|
755
|
+
source: x,
|
|
756
|
+
context: sourceContext
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
return result;
|
|
760
|
+
}));
|
|
761
|
+
const allSources = [...relevantGlobalSources, ...relevantContextSources];
|
|
762
|
+
// sort by order, with primary first
|
|
763
|
+
allSources.sort(util.sortByNumberFunction((x) => {
|
|
764
|
+
let result;
|
|
765
|
+
switch (x.flowType) {
|
|
766
|
+
case 'primary':
|
|
767
|
+
result = 1;
|
|
768
|
+
break;
|
|
769
|
+
case 'secondary':
|
|
770
|
+
result = 2;
|
|
771
|
+
break;
|
|
772
|
+
case 'unset':
|
|
773
|
+
default:
|
|
774
|
+
result = 3;
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
}));
|
|
779
|
+
return allSources;
|
|
780
|
+
}
|
|
781
|
+
const synchronizeInstance = async (input) => {
|
|
782
|
+
const syncEntityCommonTypeIdPair = syncEntityCommonTypeIdPairForType(input);
|
|
783
|
+
const _loadRelevantSources = async () => {
|
|
784
|
+
const entitySourceContext = await entitySourceContextLoader(syncEntityCommonTypeIdPair);
|
|
785
|
+
const relevantSources = loadSources(syncEntityCommonTypeIdPair, entitySourceContext);
|
|
786
|
+
return relevantSources;
|
|
787
|
+
};
|
|
788
|
+
let loadRelevantSources;
|
|
789
|
+
if (dynamicSources) {
|
|
790
|
+
// if dynamic, reload each time and do not cache
|
|
791
|
+
loadRelevantSources = _loadRelevantSources;
|
|
962
792
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const replicaTaskResults = await util.performAsyncTasks(replicaSources, x => synchronizeInstance(x, primaryFlaggedDelete), {
|
|
967
|
-
sequential: false,
|
|
968
|
-
maxParallelTasks: 3
|
|
969
|
-
});
|
|
970
|
-
// add all the results
|
|
971
|
-
util.pushArrayItemsIntoArray(synchronizedEntityResults, replicaTaskResults.results.map(x => x[1]));
|
|
972
|
-
// compute final result
|
|
973
|
-
result = {
|
|
974
|
-
synchronizedEntityResults
|
|
975
|
-
};
|
|
793
|
+
else {
|
|
794
|
+
// if not dynamic, make a cached getter
|
|
795
|
+
loadRelevantSources = util.cachedGetter(_loadRelevantSources);
|
|
976
796
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
797
|
+
/**
|
|
798
|
+
* Performs the synchonization
|
|
799
|
+
*/
|
|
800
|
+
const synchronize = async (context) => {
|
|
801
|
+
const relevantSources = await loadRelevantSources();
|
|
802
|
+
const syncEntityInstances = await Promise.all(relevantSources.map((x) => x.source.syncEntityInstance(x).then((y) => [x, y])));
|
|
803
|
+
const sourcesByFlowType = util.makeValuesGroupMap(syncEntityInstances, (x) => x[0].flowType);
|
|
804
|
+
const primarySources = sourcesByFlowType.get('primary') ?? [];
|
|
805
|
+
const secondarySources = sourcesByFlowType.get('secondary') ?? [];
|
|
806
|
+
const replicaSources = sourcesByFlowType.get('replica') ?? [];
|
|
807
|
+
// assert primary sources count
|
|
808
|
+
switch (primarySources.length) {
|
|
809
|
+
case 0:
|
|
810
|
+
throw new NoPrimarySyncSourceError(syncEntityCommonTypeIdPair);
|
|
811
|
+
case 1:
|
|
812
|
+
break;
|
|
813
|
+
default:
|
|
814
|
+
throw new MultiplePrimarySyncSourceError(syncEntityCommonTypeIdPair);
|
|
815
|
+
}
|
|
816
|
+
function synchronizeInstance(source, deleted) {
|
|
817
|
+
const [input, sourceInstance] = source;
|
|
818
|
+
const promise = deleted ? sourceInstance.synchronizeDelete() : sourceInstance.synchronize();
|
|
819
|
+
return promise.catch((error) => {
|
|
820
|
+
const errorResult = {
|
|
821
|
+
type: 'error',
|
|
822
|
+
error,
|
|
823
|
+
entity: {
|
|
824
|
+
...syncEntityCommonTypeIdPair,
|
|
825
|
+
sourceInfo: input.source.info,
|
|
826
|
+
id: ''
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
return errorResult;
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
async function performSynchronizationOfSources(input) {
|
|
833
|
+
const { secondaryFlaggedDelete } = input;
|
|
834
|
+
let result;
|
|
835
|
+
// synchronize the primary source
|
|
836
|
+
const primarySource = primarySources[0];
|
|
837
|
+
const primarySyncResult = await synchronizeInstance(primarySource, secondaryFlaggedDelete ?? false);
|
|
838
|
+
const synchronizedEntityResults = [primarySyncResult];
|
|
839
|
+
let primaryFlaggedDelete = false;
|
|
840
|
+
switch (primarySyncResult.type) {
|
|
841
|
+
case 'deleted':
|
|
842
|
+
primaryFlaggedDelete = true;
|
|
843
|
+
break;
|
|
844
|
+
case 'failed':
|
|
845
|
+
case 'error':
|
|
846
|
+
throw new SynchronizationFailedError(syncEntityCommonTypeIdPair, primarySyncResult.error);
|
|
847
|
+
}
|
|
848
|
+
// synchronize all secondary sources, one after the other. If any secondary source returns deleted and the primary source was not flagged as deleted, then the synchronization will be restarted.
|
|
849
|
+
for (const secondarySource of secondarySources) {
|
|
850
|
+
const secondarySyncResult = await synchronizeInstance(secondarySource, primaryFlaggedDelete);
|
|
851
|
+
synchronizedEntityResults.push(secondarySyncResult);
|
|
852
|
+
switch (secondarySyncResult.type) {
|
|
853
|
+
case 'deleted':
|
|
854
|
+
if (primaryFlaggedDelete === false) {
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
break;
|
|
858
|
+
case 'failed':
|
|
859
|
+
case 'error':
|
|
860
|
+
throw new SynchronizationFailedError(syncEntityCommonTypeIdPair, secondarySyncResult.error);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
// if result was already set, then it was completed in a recursive result
|
|
864
|
+
if (result == null) {
|
|
865
|
+
// synchronize all replica sources concurrently
|
|
866
|
+
const replicaTaskResults = await util.performAsyncTasks(replicaSources, (x) => synchronizeInstance(x, primaryFlaggedDelete), {
|
|
867
|
+
sequential: false,
|
|
868
|
+
maxParallelTasks: 3
|
|
869
|
+
});
|
|
870
|
+
// add all the results
|
|
871
|
+
util.pushArrayItemsIntoArray(synchronizedEntityResults, replicaTaskResults.results.map((x) => x[1]));
|
|
872
|
+
// compute final result
|
|
873
|
+
result = {
|
|
874
|
+
synchronizedEntityResults
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
return result;
|
|
878
|
+
}
|
|
879
|
+
const result = await performSynchronizationOfSources({ });
|
|
880
|
+
return {
|
|
881
|
+
targetPair: syncEntityCommonTypeIdPair,
|
|
882
|
+
entitiesSynchronized: result.synchronizedEntityResults
|
|
883
|
+
};
|
|
884
|
+
};
|
|
885
|
+
const instance = {
|
|
886
|
+
entityPair: syncEntityCommonTypeIdPair,
|
|
887
|
+
synchronize
|
|
888
|
+
};
|
|
889
|
+
return instance;
|
|
985
890
|
};
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
891
|
+
const result = {
|
|
892
|
+
commonType,
|
|
893
|
+
synchronizeInstance
|
|
989
894
|
};
|
|
990
|
-
return
|
|
991
|
-
};
|
|
992
|
-
const result = {
|
|
993
|
-
commonType,
|
|
994
|
-
synchronizeInstance
|
|
995
|
-
};
|
|
996
|
-
return result;
|
|
895
|
+
return result;
|
|
997
896
|
}
|
|
998
897
|
|
|
999
898
|
// MARK: String
|
|
1000
899
|
function transformStringToBoolean(defaultValue) {
|
|
1001
|
-
|
|
900
|
+
return (params) => util.stringToBoolean(params.value, defaultValue);
|
|
1002
901
|
}
|
|
1003
902
|
// MARK: Comma Separated Values
|
|
1004
903
|
function transformCommaSeparatedValueToArray(mapFn) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
904
|
+
return (params) => {
|
|
905
|
+
let result;
|
|
906
|
+
if (params.value) {
|
|
907
|
+
if (Array.isArray(params.value)) {
|
|
908
|
+
result = params.value;
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
result = util.splitCommaSeparatedString(params.value, mapFn);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return result;
|
|
915
|
+
};
|
|
1016
916
|
}
|
|
1017
|
-
const transformCommaSeparatedNumberValueToArray = transformCommaSeparatedValueToArray(x => Number(x));
|
|
1018
|
-
const transformCommaSeparatedStringValueToArray = transformCommaSeparatedValueToArray(x => x);
|
|
917
|
+
const transformCommaSeparatedNumberValueToArray = transformCommaSeparatedValueToArray((x) => Number(x));
|
|
918
|
+
const transformCommaSeparatedStringValueToArray = transformCommaSeparatedValueToArray((x) => x);
|
|
1019
919
|
|
|
1020
920
|
// MARK: Transform Annotations
|
|
1021
921
|
function TransformCommaSeparatedValueToArray(mapFn) {
|
|
1022
|
-
|
|
922
|
+
return classTransformer.Transform(transformCommaSeparatedValueToArray(mapFn));
|
|
1023
923
|
}
|
|
1024
924
|
const TransformCommaSeparatedStringValueToArray = () => classTransformer.Transform(transformCommaSeparatedStringValueToArray);
|
|
1025
925
|
const TransformCommaSeparatedNumberValueToArray = () => classTransformer.Transform(transformCommaSeparatedNumberValueToArray);
|
|
1026
926
|
const TransformStringValueToBoolean = () => classTransformer.Transform(transformStringToBoolean());
|
|
1027
927
|
|
|
1028
928
|
function transformAndValidateObject(config) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
};
|
|
1045
|
-
});
|
|
929
|
+
const transformToResult = transformAndValidateObjectResult(config);
|
|
930
|
+
const { handleValidationError } = config;
|
|
931
|
+
return (input, context) => transformToResult(input, context).then(async (x) => {
|
|
932
|
+
const object = x.object;
|
|
933
|
+
let result;
|
|
934
|
+
if (x.success) {
|
|
935
|
+
result = x.result;
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
result = await handleValidationError(x.validationErrors);
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
object,
|
|
942
|
+
result
|
|
943
|
+
};
|
|
944
|
+
});
|
|
1046
945
|
}
|
|
1047
946
|
/**
|
|
1048
947
|
* Creates a new TransformAndValidateObjectFactory.
|
|
@@ -1051,21 +950,17 @@ function transformAndValidateObject(config) {
|
|
|
1051
950
|
* @returns
|
|
1052
951
|
*/
|
|
1053
952
|
function transformAndValidateObjectFactory(defaults) {
|
|
1054
|
-
|
|
1055
|
-
handleValidationError
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
optionsForContext,
|
|
1065
|
-
defaultValidationOptions
|
|
953
|
+
const { handleValidationError: defaultHandleValidationError, optionsForContext, defaultValidationOptions } = defaults;
|
|
954
|
+
return (classType, fn, handleValidationError) => {
|
|
955
|
+
const config = {
|
|
956
|
+
classType,
|
|
957
|
+
fn,
|
|
958
|
+
handleValidationError: handleValidationError ?? defaultHandleValidationError,
|
|
959
|
+
optionsForContext,
|
|
960
|
+
defaultValidationOptions
|
|
961
|
+
};
|
|
962
|
+
return transformAndValidateObject(config);
|
|
1066
963
|
};
|
|
1067
|
-
return transformAndValidateObject(config);
|
|
1068
|
-
};
|
|
1069
964
|
}
|
|
1070
965
|
/**
|
|
1071
966
|
* Factory function that wraps the input class type and handler function to first transform the input object to a the given class, and then validate it.
|
|
@@ -1075,134 +970,113 @@ function transformAndValidateObjectFactory(defaults) {
|
|
|
1075
970
|
* @returns
|
|
1076
971
|
*/
|
|
1077
972
|
function transformAndValidateObjectResult(config) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
});
|
|
1101
|
-
if (validationErrors.length) {
|
|
1102
|
-
return {
|
|
1103
|
-
object,
|
|
1104
|
-
validationErrors,
|
|
1105
|
-
success: false
|
|
1106
|
-
};
|
|
1107
|
-
} else {
|
|
1108
|
-
const result = await fn(object);
|
|
1109
|
-
return {
|
|
1110
|
-
object,
|
|
1111
|
-
result,
|
|
1112
|
-
success: true
|
|
1113
|
-
};
|
|
1114
|
-
}
|
|
1115
|
-
};
|
|
973
|
+
const { defaultValidationOptions, classType, fn, optionsForContext: inputOptionsForContext } = config;
|
|
974
|
+
const optionsForContext = inputOptionsForContext ?? (() => ({}));
|
|
975
|
+
return async (input, context) => {
|
|
976
|
+
const { transform: transformOptions, validate: validateOptions } = optionsForContext(context);
|
|
977
|
+
const object = classTransformer.plainToInstance(classType, input, {
|
|
978
|
+
...transformOptions,
|
|
979
|
+
// Note: Each variable on the target class must be marked with the @Expose() annotation.
|
|
980
|
+
excludeExtraneousValues: true
|
|
981
|
+
});
|
|
982
|
+
const validationErrors = await classValidator.validate(object, {
|
|
983
|
+
forbidUnknownValues: false, // allow classes without annotations by default
|
|
984
|
+
...defaultValidationOptions,
|
|
985
|
+
...validateOptions
|
|
986
|
+
});
|
|
987
|
+
if (validationErrors.length) {
|
|
988
|
+
return { object, validationErrors, success: false };
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
const result = await fn(object);
|
|
992
|
+
return { object, result, success: true };
|
|
993
|
+
}
|
|
994
|
+
};
|
|
1116
995
|
}
|
|
1117
996
|
|
|
1118
997
|
function transformAndValidateFunctionResultFactory(defaults) {
|
|
1119
|
-
|
|
998
|
+
return toTransformAndValidateFunctionResultFactory(transformAndValidateObjectFactory(defaults));
|
|
1120
999
|
}
|
|
1121
1000
|
function toTransformAndValidateFunctionResultFactory(transformAndValidateObjectFactory) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1001
|
+
return (classType, fn, handleValidationError) => {
|
|
1002
|
+
const transformAndValidateObjectFn = transformAndValidateObjectFactory(classType, fn, handleValidationError);
|
|
1003
|
+
return (input, context) => {
|
|
1004
|
+
return toTransformAndValidateFunctionResult(transformAndValidateObjectFn(input, context));
|
|
1005
|
+
};
|
|
1126
1006
|
};
|
|
1127
|
-
};
|
|
1128
1007
|
}
|
|
1129
1008
|
function toTransformAndValidateFunctionResult(objectOutput) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
fnResult.params = object;
|
|
1137
|
-
return fnResult;
|
|
1138
|
-
});
|
|
1009
|
+
return util.mapPromiseOrValue(objectOutput, (x) => {
|
|
1010
|
+
const { object, result } = x;
|
|
1011
|
+
const fnResult = result;
|
|
1012
|
+
fnResult.params = object;
|
|
1013
|
+
return fnResult;
|
|
1014
|
+
});
|
|
1139
1015
|
}
|
|
1140
1016
|
|
|
1141
1017
|
function transformAndValidateResultFactory(defaults) {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
return result;
|
|
1018
|
+
const factory = transformAndValidateObjectFactory(defaults);
|
|
1019
|
+
return (classType, fn, handleValidationError) => {
|
|
1020
|
+
const transformAndValidateObjectFn = factory(classType, fn, handleValidationError);
|
|
1021
|
+
return async (input, context) => {
|
|
1022
|
+
const { result } = await transformAndValidateObjectFn(input, context);
|
|
1023
|
+
return result;
|
|
1024
|
+
};
|
|
1150
1025
|
};
|
|
1151
|
-
};
|
|
1152
1026
|
}
|
|
1153
1027
|
|
|
1154
1028
|
/**
|
|
1155
1029
|
* isISO8601DayString validator
|
|
1156
1030
|
*/
|
|
1157
1031
|
function IsISO8601DayString(validationOptions) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1032
|
+
return function (object, propertyName) {
|
|
1033
|
+
classValidator.registerDecorator({
|
|
1034
|
+
name: 'isISO8601DayString',
|
|
1035
|
+
target: object.constructor,
|
|
1036
|
+
propertyName: propertyName,
|
|
1037
|
+
options: validationOptions,
|
|
1038
|
+
validator: {
|
|
1039
|
+
validate: util.isISO8601DayString,
|
|
1040
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a ISO8601DayString.`, validationOptions)
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
};
|
|
1170
1044
|
}
|
|
1171
1045
|
|
|
1172
1046
|
/**
|
|
1173
1047
|
* isMinuteOfDay validator
|
|
1174
1048
|
*/
|
|
1175
1049
|
function IsMinuteOfDay(validationOptions) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1050
|
+
return function (object, propertyName) {
|
|
1051
|
+
classValidator.registerDecorator({
|
|
1052
|
+
name: 'isMinuteOfDay',
|
|
1053
|
+
target: object.constructor,
|
|
1054
|
+
propertyName: propertyName,
|
|
1055
|
+
options: validationOptions,
|
|
1056
|
+
validator: {
|
|
1057
|
+
validate: util.isMinuteOfDay,
|
|
1058
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a valid minute of the day.`, validationOptions)
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
};
|
|
1188
1062
|
}
|
|
1189
1063
|
|
|
1190
1064
|
/**
|
|
1191
1065
|
* isE164PhoneNumber validator that does not allowed extensions.
|
|
1192
1066
|
*/
|
|
1193
1067
|
function IsE164PhoneNumber(validationOptions) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1068
|
+
return function (object, propertyName) {
|
|
1069
|
+
classValidator.registerDecorator({
|
|
1070
|
+
name: 'isE164PhoneNumber',
|
|
1071
|
+
target: object.constructor,
|
|
1072
|
+
propertyName: propertyName,
|
|
1073
|
+
options: validationOptions,
|
|
1074
|
+
validator: {
|
|
1075
|
+
validate: (x) => util.isE164PhoneNumber(x, false),
|
|
1076
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a E164PhoneNumber with no extension.`, validationOptions)
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
};
|
|
1206
1080
|
}
|
|
1207
1081
|
/**
|
|
1208
1082
|
* isE164PhoneNumber validator that allows extensions.
|
|
@@ -1211,18 +1085,18 @@ function IsE164PhoneNumber(validationOptions) {
|
|
|
1211
1085
|
* @returns
|
|
1212
1086
|
*/
|
|
1213
1087
|
function IsE164PhoneNumberWithOptionalExtension(validationOptions) {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1088
|
+
return function (object, propertyName) {
|
|
1089
|
+
classValidator.registerDecorator({
|
|
1090
|
+
name: 'isE164PhoneNumber',
|
|
1091
|
+
target: object.constructor,
|
|
1092
|
+
propertyName: propertyName,
|
|
1093
|
+
options: validationOptions,
|
|
1094
|
+
validator: {
|
|
1095
|
+
validate: (x) => util.isE164PhoneNumber(x, true),
|
|
1096
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not an E164PhoneNumber or has an invalid extension.`, validationOptions)
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
};
|
|
1226
1100
|
}
|
|
1227
1101
|
/**
|
|
1228
1102
|
* isE164PhoneNumberWithExtension validator
|
|
@@ -1231,72 +1105,72 @@ function IsE164PhoneNumberWithOptionalExtension(validationOptions) {
|
|
|
1231
1105
|
* @returns
|
|
1232
1106
|
*/
|
|
1233
1107
|
function IsE164PhoneNumberWithExtension(validationOptions) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1108
|
+
return function (object, propertyName) {
|
|
1109
|
+
classValidator.registerDecorator({
|
|
1110
|
+
name: 'isE164PhoneNumberWithExtension',
|
|
1111
|
+
target: object.constructor,
|
|
1112
|
+
propertyName: propertyName,
|
|
1113
|
+
options: validationOptions,
|
|
1114
|
+
validator: {
|
|
1115
|
+
validate: util.isE164PhoneNumberWithExtension,
|
|
1116
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a E164PhoneNumberWithExtension.`, validationOptions)
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1246
1120
|
}
|
|
1247
1121
|
|
|
1248
1122
|
/**
|
|
1249
1123
|
* isUniqueKeyedFunction validator
|
|
1250
1124
|
*/
|
|
1251
1125
|
function IsUniqueKeyed(readKey, validationOptions) {
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1126
|
+
const isUniqueKeyed = util.isUniqueKeyedFunction(readKey);
|
|
1127
|
+
return function (object, propertyName) {
|
|
1128
|
+
classValidator.registerDecorator({
|
|
1129
|
+
name: 'isUniqueKeyed',
|
|
1130
|
+
target: object.constructor,
|
|
1131
|
+
propertyName: propertyName,
|
|
1132
|
+
options: validationOptions,
|
|
1133
|
+
validator: {
|
|
1134
|
+
validate: isUniqueKeyed,
|
|
1135
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value has one or more values with the same key. Keys must be unique.`, validationOptions)
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
};
|
|
1265
1139
|
}
|
|
1266
1140
|
|
|
1267
1141
|
/**
|
|
1268
1142
|
* isWebsiteUrl validator
|
|
1269
1143
|
*/
|
|
1270
1144
|
function IsWebsiteUrl(validationOptions) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1145
|
+
return function (object, propertyName) {
|
|
1146
|
+
classValidator.registerDecorator({
|
|
1147
|
+
name: 'isWebsiteUrl',
|
|
1148
|
+
target: object.constructor,
|
|
1149
|
+
propertyName: propertyName,
|
|
1150
|
+
options: validationOptions,
|
|
1151
|
+
validator: {
|
|
1152
|
+
validate: util.isWebsiteUrl,
|
|
1153
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a valid website url.`, validationOptions)
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
};
|
|
1283
1157
|
}
|
|
1284
1158
|
/**
|
|
1285
1159
|
* isWebsiteUrlWithPrefix validator
|
|
1286
1160
|
*/
|
|
1287
1161
|
function IsWebsiteUrlWithPrefix(validationOptions) {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1162
|
+
return function (object, propertyName) {
|
|
1163
|
+
classValidator.registerDecorator({
|
|
1164
|
+
name: 'isWebsiteUrlWithPrefix',
|
|
1165
|
+
target: object.constructor,
|
|
1166
|
+
propertyName: propertyName,
|
|
1167
|
+
options: validationOptions,
|
|
1168
|
+
validator: {
|
|
1169
|
+
validate: util.isWebsiteUrlWithPrefix,
|
|
1170
|
+
defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args?.value}" is not a valid website url that starts with a http/https prefix.`, validationOptions)
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
};
|
|
1300
1174
|
}
|
|
1301
1175
|
|
|
1302
1176
|
exports.ADDRESS_CITY_MAX_LENGTH = ADDRESS_CITY_MAX_LENGTH;
|