@dereekb/model 13.0.7 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +745 -421
- package/index.cjs.js.map +1 -1
- package/index.esm.js +730 -402
- package/index.esm.js.map +1 -1
- package/package.json +3 -6
- package/src/lib/data/address/address.d.ts +36 -15
- package/src/lib/data/website/link.d.ts +38 -9
- package/src/lib/data/website/link.file.d.ts +97 -18
- package/src/lib/data/website/link.website.d.ts +254 -3
- package/src/lib/index.d.ts +1 -0
- package/src/lib/service/permission/permission.d.ts +37 -1
- package/src/lib/service/permission/role.d.ts +71 -9
- package/src/lib/service/sync/sync.entity.d.ts +32 -3
- package/src/lib/service/sync/sync.entity.synchronizer.basic.d.ts +15 -0
- package/src/lib/service/sync/sync.entity.synchronizer.d.ts +20 -0
- package/src/lib/transform/index.d.ts +0 -2
- package/src/lib/transform/transform.d.ts +54 -25
- package/src/lib/transform/transform.function.d.ts +22 -2
- package/src/lib/transform/transform.result.d.ts +10 -2
- package/src/lib/type/index.d.ts +2 -0
- package/src/lib/type/model.d.ts +20 -0
- package/src/lib/type/type.d.ts +21 -0
- package/src/lib/validator/date.d.ts +2 -4
- package/src/lib/validator/number.d.ts +2 -4
- package/src/lib/validator/phone.d.ts +6 -14
- package/src/lib/validator/unique.d.ts +11 -4
- package/src/lib/validator/url.d.ts +4 -6
- package/src/lib/transform/type.annotation.d.ts +0 -5
- package/src/lib/transform/type.d.ts +0 -6
package/index.cjs.js
CHANGED
|
@@ -1,239 +1,217 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var util = require('@dereekb/util');
|
|
4
|
-
var
|
|
5
|
-
var classValidator = require('class-validator');
|
|
4
|
+
var arktype = require('arktype');
|
|
6
5
|
var makeError = require('make-error');
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
12
|
-
purpose with or without fee is hereby granted.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
15
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
16
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
17
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
18
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
19
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
20
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
21
|
-
***************************************************************************** */
|
|
22
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function __decorate(decorators, target, key, desc) {
|
|
26
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
27
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
28
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
29
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function __metadata(metadataKey, metadataValue) {
|
|
33
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
37
|
-
var e = new Error(message);
|
|
38
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
39
|
-
};
|
|
40
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Maximum character length for address line fields (line1, line2).
|
|
9
|
+
*/
|
|
41
10
|
const ADDRESS_LINE_MAX_LENGTH = 50;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum character length for city names.
|
|
13
|
+
*/
|
|
42
14
|
const ADDRESS_CITY_MAX_LENGTH = 80;
|
|
15
|
+
/**
|
|
16
|
+
* Maximum character length for full state names (e.g., "Texas").
|
|
17
|
+
*/
|
|
43
18
|
const ADDRESS_STATE_MAX_LENGTH = 30;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum character length for two-letter state codes (e.g., "TX").
|
|
21
|
+
*/
|
|
44
22
|
const ADDRESS_STATE_CODE_MAX_LENGTH = 2;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum character length for ZIP codes, accommodating ZIP+4 format (e.g., "77834-1234").
|
|
25
|
+
*/
|
|
45
26
|
const ADDRESS_ZIP_MAX_LENGTH = 11;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum character length for country names.
|
|
29
|
+
*/
|
|
46
30
|
const ADDRESS_COUNTRY_MAX_LENGTH = 80;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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);
|
|
82
|
-
/**
|
|
83
|
-
* UnitedStatesAddress that enforces a StateCode for the state value.
|
|
84
|
-
*/
|
|
85
|
-
class UnitedStatesAddressWithStateCodeParams extends AbstractUnitedStatesAddressWithoutStateParams {
|
|
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);
|
|
96
|
-
/**
|
|
97
|
-
* UnitedStatesAddress that enforces a State for the state value.
|
|
98
|
-
*/
|
|
99
|
-
class UnitedStatesAddressWithStateStringParams extends AbstractUnitedStatesAddressWithoutStateParams {
|
|
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);
|
|
31
|
+
/**
|
|
32
|
+
* Base ArkType schema for United States address fields without the state.
|
|
33
|
+
*/
|
|
34
|
+
const baseUnitedStatesAddressType = arktype.type({
|
|
35
|
+
line1: `0 < string <= ${ADDRESS_LINE_MAX_LENGTH}`,
|
|
36
|
+
'line2?': `string <= ${ADDRESS_LINE_MAX_LENGTH}`,
|
|
37
|
+
city: `0 < string <= ${ADDRESS_CITY_MAX_LENGTH}`,
|
|
38
|
+
zip: [/^\d{5}(-\d{4})?$/, '&', `string <= ${ADDRESS_ZIP_MAX_LENGTH}`]
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* ArkType schema for a United States address with a two-letter state code (e.g., "TX").
|
|
42
|
+
*/
|
|
43
|
+
const unitedStatesAddressWithStateCodeType = baseUnitedStatesAddressType.merge({
|
|
44
|
+
state: [util.US_STATE_CODE_STRING_REGEX, '&', `${ADDRESS_STATE_CODE_MAX_LENGTH} <= string <= ${ADDRESS_STATE_CODE_MAX_LENGTH}`]
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* ArkType schema for a United States address with a full state name (e.g., "Texas").
|
|
48
|
+
*/
|
|
49
|
+
const unitedStatesAddressWithStateStringType = baseUnitedStatesAddressType.merge({
|
|
50
|
+
state: `0 < string <= ${ADDRESS_STATE_MAX_LENGTH}`
|
|
51
|
+
});
|
|
109
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Fallback link type used when the actual type is not known.
|
|
55
|
+
*/
|
|
110
56
|
const UNKNOWN_WEBSITE_LINK_TYPE = 'u';
|
|
111
57
|
/**
|
|
112
|
-
*
|
|
58
|
+
* Maximum character length for a {@link WebsiteLinkType} string.
|
|
113
59
|
*/
|
|
114
60
|
const WEBSITE_LINK_TYPE_MAX_LENGTH = 32;
|
|
115
61
|
/**
|
|
116
|
-
*
|
|
62
|
+
* Regex pattern that validates a {@link WebsiteLinkType} as 1-32 alphanumeric characters.
|
|
117
63
|
*/
|
|
118
64
|
const WEBSITE_LINK_TYPE_REGEX = /^[a-zA-Z0-9]{1,32}$/;
|
|
65
|
+
/**
|
|
66
|
+
* Checks whether the given string is a valid {@link WebsiteLinkType}.
|
|
67
|
+
*
|
|
68
|
+
* @param input - the string to validate
|
|
69
|
+
* @returns true if it matches the alphanumeric 1-32 character pattern
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* isValidWebsiteLinkType('fb'); // true
|
|
74
|
+
* isValidWebsiteLinkType(''); // false
|
|
75
|
+
* isValidWebsiteLinkType('a-b'); // false (hyphen not allowed)
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
119
78
|
function isValidWebsiteLinkType(input) {
|
|
120
79
|
return WEBSITE_LINK_TYPE_REGEX.test(input);
|
|
121
80
|
}
|
|
122
81
|
/**
|
|
123
|
-
*
|
|
82
|
+
* Maximum character length for the encoded data string in a {@link WebsiteLink}.
|
|
124
83
|
*/
|
|
125
84
|
const WEBSITE_LINK_ENCODED_DATA_MAX_LENGTH = 1000;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
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);
|
|
85
|
+
/**
|
|
86
|
+
* ArkType schema for a {@link WebsiteLink}.
|
|
87
|
+
*/
|
|
88
|
+
const websiteLinkType = arktype.type({
|
|
89
|
+
t: [WEBSITE_LINK_TYPE_REGEX, '&', `0 < string <= ${WEBSITE_LINK_TYPE_MAX_LENGTH}`],
|
|
90
|
+
d: `0 < string <= ${WEBSITE_LINK_ENCODED_DATA_MAX_LENGTH}`
|
|
91
|
+
});
|
|
151
92
|
|
|
152
93
|
/**
|
|
153
|
-
*
|
|
94
|
+
* Maximum character length for a {@link WebsiteFileLinkType}. Matches {@link WEBSITE_LINK_TYPE_MAX_LENGTH}.
|
|
154
95
|
*/
|
|
155
96
|
const WEBSITE_FILE_LINK_TYPE_MAX_LENGTH = WEBSITE_LINK_TYPE_MAX_LENGTH;
|
|
156
97
|
/**
|
|
157
|
-
*
|
|
98
|
+
* Validation regex for {@link WebsiteFileLinkType}. Matches {@link WEBSITE_LINK_TYPE_REGEX}.
|
|
158
99
|
*/
|
|
159
100
|
const WEBSITE_FILE_LINK_TYPE_REGEX = WEBSITE_LINK_TYPE_REGEX;
|
|
160
101
|
/**
|
|
161
|
-
*
|
|
102
|
+
* Maximum character length for a {@link WebsiteFileLinkMimeType}.
|
|
162
103
|
*/
|
|
163
104
|
const WEBSITE_FILE_LINK_MIME_TYPE_MAX_LENGTH = 128;
|
|
164
105
|
/**
|
|
165
|
-
*
|
|
106
|
+
* Regex pattern that validates a MIME type string (e.g., "text/plain", "application/vnd.api+json").
|
|
166
107
|
*/
|
|
167
108
|
const WEBSITE_FILE_LINK_MIME_TYPE_REGEX = /^\w+\/[-+.\w]+$/;
|
|
168
109
|
/**
|
|
169
|
-
*
|
|
110
|
+
* Maximum character length for a {@link WebsiteFileLinkName}.
|
|
170
111
|
*/
|
|
171
112
|
const WEBSITE_FILE_LINK_NAME_MAX_LENGTH = 128;
|
|
172
113
|
/**
|
|
173
|
-
*
|
|
114
|
+
* Maximum character length for {@link WebsiteFileLinkData}, derived from the total encoded data budget
|
|
115
|
+
* minus the separator characters, type, MIME type, and name fields.
|
|
174
116
|
*/
|
|
175
117
|
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;
|
|
118
|
+
/**
|
|
119
|
+
* Regex pattern for file link data — disallows the pipe character since it is used as the encoding separator.
|
|
120
|
+
*/
|
|
176
121
|
const WEBSITE_FILE_LINK_DATA_REGEX = /^[^|]+$/;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
}
|
|
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);
|
|
122
|
+
/**
|
|
123
|
+
* ArkType schema for a {@link WebsiteFileLink}.
|
|
124
|
+
*/
|
|
125
|
+
const websiteFileLinkType = arktype.type({
|
|
126
|
+
'type?': [WEBSITE_FILE_LINK_TYPE_REGEX, '&', `string <= ${WEBSITE_LINK_TYPE_MAX_LENGTH}`],
|
|
127
|
+
'mime?': [WEBSITE_FILE_LINK_MIME_TYPE_REGEX, '&', `string <= ${WEBSITE_FILE_LINK_MIME_TYPE_MAX_LENGTH}`],
|
|
128
|
+
'name?': `string <= ${WEBSITE_FILE_LINK_NAME_MAX_LENGTH}`,
|
|
129
|
+
data: [WEBSITE_FILE_LINK_DATA_REGEX, '&', `0 < string <= ${WEBSITE_FILE_LINK_DATA_MAX_LENGTH}`]
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* The {@link WebsiteLinkType} code used to identify a {@link WebsiteLink} as a file link.
|
|
133
|
+
*/
|
|
221
134
|
const WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE = 'f';
|
|
135
|
+
/**
|
|
136
|
+
* Converts a {@link WebsiteFileLink} to a {@link WebsiteLink} by encoding its fields into the data string.
|
|
137
|
+
*
|
|
138
|
+
* @param input - the file link to convert
|
|
139
|
+
* @returns a WebsiteLink with type "f" and pipe-encoded data
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const fileLink: WebsiteFileLink = { type: 't', mime: 'text/plain', data: 'https://example.com/file.txt', name: 'file' };
|
|
144
|
+
* const link = websiteFileLinkToWebsiteLink(fileLink);
|
|
145
|
+
* // link.t === 'f'
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
222
148
|
function websiteFileLinkToWebsiteLink(input) {
|
|
223
149
|
return {
|
|
224
150
|
t: WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE,
|
|
225
151
|
d: encodeWebsiteFileLinkToWebsiteLinkEncodedData(input)
|
|
226
152
|
};
|
|
227
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Converts a {@link WebsiteLink} back to a {@link WebsiteFileLink} by decoding the pipe-separated data string.
|
|
156
|
+
*
|
|
157
|
+
* @param input - a WebsiteLink whose data field contains an encoded file link
|
|
158
|
+
* @returns the decoded file link with type, MIME, name, and data fields
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const link: WebsiteLink = { t: 'f', d: 't|text/plain|https://example.com/file.txt|file' };
|
|
163
|
+
* const fileLink = websiteLinkToWebsiteLinkFile(link);
|
|
164
|
+
* // fileLink.data === 'https://example.com/file.txt'
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
228
167
|
function websiteLinkToWebsiteLinkFile(input) {
|
|
229
168
|
const encodedData = input.d;
|
|
230
169
|
return decodeWebsiteLinkEncodedDataToWebsiteFileLink(encodedData);
|
|
231
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Separator character used when encoding/decoding file link fields into a single string.
|
|
173
|
+
*/
|
|
232
174
|
const WEBSITE_FILE_LINK_ENCODE_SEPARATOR = '|';
|
|
175
|
+
/**
|
|
176
|
+
* Encodes a {@link WebsiteFileLink} into a pipe-separated string suitable for storage in a {@link WebsiteLink}'s data field.
|
|
177
|
+
*
|
|
178
|
+
* Fields are encoded in order: type, MIME, data, name. Empty/undefined fields become empty strings.
|
|
179
|
+
*
|
|
180
|
+
* @param input - the file link to encode
|
|
181
|
+
* @returns a pipe-separated encoded string
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const encoded = encodeWebsiteFileLinkToWebsiteLinkEncodedData({
|
|
186
|
+
* type: 't',
|
|
187
|
+
* mime: 'test/test',
|
|
188
|
+
* name: 'test-name',
|
|
189
|
+
* data: 'https://example.com/'
|
|
190
|
+
* });
|
|
191
|
+
* // encoded === 't|test/test|https://example.com/|test-name'
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
233
194
|
function encodeWebsiteFileLinkToWebsiteLinkEncodedData(input) {
|
|
234
195
|
const encoded = [input.type, input.mime, input.data, input.name].map((x) => x || '').join(WEBSITE_FILE_LINK_ENCODE_SEPARATOR);
|
|
235
196
|
return encoded;
|
|
236
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Decodes a pipe-separated encoded string back into a {@link WebsiteFileLink}.
|
|
200
|
+
*
|
|
201
|
+
* Empty fields in the encoded string are omitted from the result (falsy values are filtered out).
|
|
202
|
+
*
|
|
203
|
+
* @param input - the pipe-separated encoded string
|
|
204
|
+
* @returns the decoded file link
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const fileLink = decodeWebsiteLinkEncodedDataToWebsiteFileLink('t|test/test|https://example.com/|test-name');
|
|
209
|
+
* // fileLink.type === 't'
|
|
210
|
+
* // fileLink.mime === 'test/test'
|
|
211
|
+
* // fileLink.data === 'https://example.com/'
|
|
212
|
+
* // fileLink.name === 'test-name'
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
237
215
|
function decodeWebsiteLinkEncodedDataToWebsiteFileLink(input) {
|
|
238
216
|
const [type, mime, data, name] = util.splitJoinRemainder(input, WEBSITE_FILE_LINK_ENCODE_SEPARATOR, 4);
|
|
239
217
|
return util.filterFalsyAndEmptyValues({
|
|
@@ -245,16 +223,40 @@ function decodeWebsiteLinkEncodedDataToWebsiteFileLink(input) {
|
|
|
245
223
|
}
|
|
246
224
|
|
|
247
225
|
/**
|
|
248
|
-
*
|
|
226
|
+
* Isolates a username from a URL where the username is the first path segment after the domain.
|
|
227
|
+
*
|
|
228
|
+
* Strips trailing slashes and query parameters. Used by social platforms like Facebook, Instagram, and Twitter
|
|
229
|
+
* where the profile URL pattern is `https://domain.com/{username}`.
|
|
249
230
|
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID('https://www.facebook.com/myuser');
|
|
234
|
+
* // returns 'myuser'
|
|
235
|
+
* ```
|
|
252
236
|
*/
|
|
253
237
|
const WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
254
238
|
isolatePathComponents: 0,
|
|
255
239
|
removeTrailingSlash: true,
|
|
256
240
|
removeQueryParameters: true
|
|
257
241
|
});
|
|
242
|
+
/**
|
|
243
|
+
* Extracts a username from either a raw username string or a full profile URL where the username is the first path segment.
|
|
244
|
+
*
|
|
245
|
+
* Optionally prepends a prefix (e.g., "@" for TikTok, "$" for Cash App).
|
|
246
|
+
*
|
|
247
|
+
* @param input - a username or full profile URL
|
|
248
|
+
* @param prefix - optional prefix to prepend to the extracted username
|
|
249
|
+
* @returns the isolated username, optionally prefixed
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* usernameFromUsernameOrWebsiteWithBaseUrlUsername('https://facebook.com/myuser');
|
|
254
|
+
* // returns 'myuser'
|
|
255
|
+
*
|
|
256
|
+
* usernameFromUsernameOrWebsiteWithBaseUrlUsername('myuser', '@');
|
|
257
|
+
* // returns '@myuser'
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
258
260
|
function usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, prefix) {
|
|
259
261
|
const username = util.toRelativeSlashPathStartType(WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID(usernameOrWebsiteUrlToWebsiteUrl(input)));
|
|
260
262
|
if (prefix) {
|
|
@@ -264,14 +266,46 @@ function usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, prefix) {
|
|
|
264
266
|
return username;
|
|
265
267
|
}
|
|
266
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Extracts a username from either a raw username string or a full profile URL using a custom path isolation function.
|
|
271
|
+
*
|
|
272
|
+
* Used for platforms with non-standard URL patterns (e.g., Snapchat's `/add/{username}`, YouTube's `/c/{username}`).
|
|
273
|
+
*
|
|
274
|
+
* @param input - a username or full profile URL
|
|
275
|
+
* @param isolateFn - custom function that extracts the relevant path segment from the URL
|
|
276
|
+
* @returns the isolated username
|
|
277
|
+
*/
|
|
267
278
|
function usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, isolateFn) {
|
|
268
279
|
return util.toRelativeSlashPathStartType(isolateFn(usernameOrWebsiteUrlToWebsiteUrl(input)));
|
|
269
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Normalizes a string that may be either a plain username or a full website URL into a consistent URL format.
|
|
283
|
+
*
|
|
284
|
+
* If the input has a website domain, it is returned as-is. Otherwise, it is treated as a path and converted to an absolute slash path.
|
|
285
|
+
*
|
|
286
|
+
* @param input - a username or website URL
|
|
287
|
+
* @returns a normalized website URL
|
|
288
|
+
*/
|
|
270
289
|
function usernameOrWebsiteUrlToWebsiteUrl(input) {
|
|
271
290
|
return util.hasWebsiteDomain(input) ? input : util.toAbsoluteSlashPathStartType(util.removeHttpFromUrl(input));
|
|
272
291
|
}
|
|
273
292
|
// MARK: Website
|
|
293
|
+
/**
|
|
294
|
+
* {@link WebsiteLinkType} code for generic website URLs.
|
|
295
|
+
*/
|
|
274
296
|
const WEBSITE_URL_WEBSITE_LINK_TYPE = 'w';
|
|
297
|
+
/**
|
|
298
|
+
* Converts a generic website URL into a {@link WebsiteLink}, stripping the HTTP/HTTPS protocol.
|
|
299
|
+
*
|
|
300
|
+
* @param input - the full website URL
|
|
301
|
+
* @returns a WebsiteLink with the protocol removed from the data
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```typescript
|
|
305
|
+
* const link = websiteUrlToWebsiteLink('https://example.com/page');
|
|
306
|
+
* // link.t === 'w', link.d === 'example.com/page'
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
275
309
|
function websiteUrlToWebsiteLink(input) {
|
|
276
310
|
return {
|
|
277
311
|
t: WEBSITE_URL_WEBSITE_LINK_TYPE,
|
|
@@ -279,7 +313,16 @@ function websiteUrlToWebsiteLink(input) {
|
|
|
279
313
|
};
|
|
280
314
|
}
|
|
281
315
|
// MARK: Email
|
|
316
|
+
/**
|
|
317
|
+
* {@link WebsiteLinkType} code for email addresses.
|
|
318
|
+
*/
|
|
282
319
|
const EMAIL_URL_WEBSITE_LINK_TYPE = 'e';
|
|
320
|
+
/**
|
|
321
|
+
* Converts an email address into a {@link WebsiteLink}.
|
|
322
|
+
*
|
|
323
|
+
* @param input - the email address
|
|
324
|
+
* @returns a WebsiteLink storing the email as data
|
|
325
|
+
*/
|
|
283
326
|
function emailAddressToWebsiteLink(input) {
|
|
284
327
|
return {
|
|
285
328
|
t: EMAIL_URL_WEBSITE_LINK_TYPE,
|
|
@@ -287,7 +330,16 @@ function emailAddressToWebsiteLink(input) {
|
|
|
287
330
|
};
|
|
288
331
|
}
|
|
289
332
|
// MARK: Phone
|
|
333
|
+
/**
|
|
334
|
+
* {@link WebsiteLinkType} code for phone numbers.
|
|
335
|
+
*/
|
|
290
336
|
const PHONE_URL_WEBSITE_LINK_TYPE = 'p';
|
|
337
|
+
/**
|
|
338
|
+
* Converts an E.164 phone number into a {@link WebsiteLink}.
|
|
339
|
+
*
|
|
340
|
+
* @param input - the phone number in E.164 format
|
|
341
|
+
* @returns a WebsiteLink storing the phone number as data
|
|
342
|
+
*/
|
|
291
343
|
function phoneNumberToWebsiteLink(input) {
|
|
292
344
|
return {
|
|
293
345
|
t: PHONE_URL_WEBSITE_LINK_TYPE,
|
|
@@ -295,148 +347,325 @@ function phoneNumberToWebsiteLink(input) {
|
|
|
295
347
|
};
|
|
296
348
|
}
|
|
297
349
|
// MARK: Facebook
|
|
350
|
+
/** Base URL for Facebook profiles. */
|
|
298
351
|
const FACEBOOK_BASE_URL = `https://www.facebook.com`;
|
|
352
|
+
/** {@link WebsiteLinkType} code for Facebook. */
|
|
299
353
|
const FACEBOOK_WEBSITE_LINK_TYPE = 'fb';
|
|
354
|
+
/**
|
|
355
|
+
* Converts a Facebook profile ID or URL into a {@link WebsiteLink}.
|
|
356
|
+
*
|
|
357
|
+
* Accepts either a raw username or a full Facebook profile URL.
|
|
358
|
+
*
|
|
359
|
+
* @param input - a Facebook profile ID or full profile URL
|
|
360
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* facebookProfileUrlToWebsiteLink('https://www.facebook.com/myuser');
|
|
365
|
+
* // { t: 'fb', d: 'myuser' }
|
|
366
|
+
*
|
|
367
|
+
* facebookProfileUrlToWebsiteLink('myuser');
|
|
368
|
+
* // { t: 'fb', d: 'myuser' }
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
300
371
|
function facebookProfileUrlToWebsiteLink(input) {
|
|
301
372
|
return {
|
|
302
373
|
t: FACEBOOK_WEBSITE_LINK_TYPE,
|
|
303
374
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
304
375
|
};
|
|
305
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Constructs a full Facebook profile URL from a profile ID.
|
|
379
|
+
*
|
|
380
|
+
* @param profileId - the Facebook profile ID or username
|
|
381
|
+
* @returns the full profile URL
|
|
382
|
+
*/
|
|
306
383
|
function facebookProfileUrl(profileId) {
|
|
307
384
|
return `${FACEBOOK_BASE_URL}/${profileId}`;
|
|
308
385
|
}
|
|
309
386
|
// MARK: Instagram
|
|
387
|
+
/** Base URL for Instagram profiles. */
|
|
310
388
|
const INSTAGRAM_BASE_URL = `https://www.instagram.com`;
|
|
389
|
+
/** {@link WebsiteLinkType} code for Instagram. */
|
|
311
390
|
const INSTAGRAM_WEBSITE_LINK_TYPE = 'ig';
|
|
391
|
+
/**
|
|
392
|
+
* Converts an Instagram profile ID or URL into a {@link WebsiteLink}.
|
|
393
|
+
*
|
|
394
|
+
* @param input - an Instagram username or full profile URL
|
|
395
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
396
|
+
*/
|
|
312
397
|
function instagramProfileUrlToWebsiteLink(input) {
|
|
313
398
|
return {
|
|
314
399
|
t: INSTAGRAM_WEBSITE_LINK_TYPE,
|
|
315
400
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
316
401
|
};
|
|
317
402
|
}
|
|
403
|
+
/**
|
|
404
|
+
* Constructs a full Instagram profile URL from a profile ID.
|
|
405
|
+
*
|
|
406
|
+
* @param profileId - the Instagram username
|
|
407
|
+
* @returns the full profile URL
|
|
408
|
+
*/
|
|
318
409
|
function instagramProfileUrl(profileId) {
|
|
319
410
|
return `${INSTAGRAM_BASE_URL}/${profileId}`;
|
|
320
411
|
}
|
|
321
412
|
// MARK: Twitter
|
|
413
|
+
/** Base URL for Twitter profiles. */
|
|
322
414
|
const TWITTER_BASE_URL = `https://www.twitter.com`;
|
|
415
|
+
/** {@link WebsiteLinkType} code for Twitter. */
|
|
323
416
|
const TWITTER_WEBSITE_LINK_TYPE = 'tw';
|
|
417
|
+
/**
|
|
418
|
+
* Converts a Twitter profile ID or URL into a {@link WebsiteLink}.
|
|
419
|
+
*
|
|
420
|
+
* @param input - a Twitter username or full profile URL
|
|
421
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
422
|
+
*/
|
|
324
423
|
function twitterProfileUrlToWebsiteLink(input) {
|
|
325
424
|
return {
|
|
326
425
|
t: TWITTER_WEBSITE_LINK_TYPE,
|
|
327
426
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
328
427
|
};
|
|
329
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Constructs a full Twitter profile URL from a profile ID.
|
|
431
|
+
*
|
|
432
|
+
* @param profileId - the Twitter username
|
|
433
|
+
* @returns the full profile URL
|
|
434
|
+
*/
|
|
330
435
|
function twitterProfileUrl(profileId) {
|
|
331
436
|
return `${TWITTER_BASE_URL}/${profileId}`;
|
|
332
437
|
}
|
|
333
438
|
// MARK: Tiktok
|
|
439
|
+
/** Base URL for TikTok profiles. */
|
|
334
440
|
const TIKTOK_BASE_URL = `https://tiktok.com`;
|
|
441
|
+
/** TikTok usernames are prefixed with "@" in URLs and stored data. */
|
|
335
442
|
const TIKTOK_USERNAME_PREFIX = '@';
|
|
443
|
+
/** {@link WebsiteLinkType} code for TikTok. */
|
|
336
444
|
const TIKTOK_WEBSITE_LINK_TYPE = 'tt';
|
|
445
|
+
/**
|
|
446
|
+
* Converts a TikTok profile ID or URL into a {@link WebsiteLink}.
|
|
447
|
+
*
|
|
448
|
+
* Automatically prepends the "@" prefix if not already present.
|
|
449
|
+
*
|
|
450
|
+
* @param input - a TikTok username (with or without "@") or full profile URL
|
|
451
|
+
* @returns a WebsiteLink with the "@"-prefixed username as data
|
|
452
|
+
*/
|
|
337
453
|
function tiktokProfileUrlToWebsiteLink(input) {
|
|
338
454
|
return {
|
|
339
455
|
t: TIKTOK_WEBSITE_LINK_TYPE,
|
|
340
456
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, TIKTOK_USERNAME_PREFIX)
|
|
341
457
|
};
|
|
342
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Constructs a full TikTok profile URL from a profile ID.
|
|
461
|
+
*
|
|
462
|
+
* @param profileId - the TikTok username (without "@" prefix)
|
|
463
|
+
* @returns the full profile URL with "@" prefix
|
|
464
|
+
*/
|
|
343
465
|
function tiktokProfileUrl(profileId) {
|
|
344
466
|
return `${TIKTOK_BASE_URL}/@${profileId}`;
|
|
345
467
|
}
|
|
346
468
|
// MARK: Snapchat
|
|
469
|
+
/** Base URL for Snapchat profiles. */
|
|
347
470
|
const SNAPCHAT_BASE_URL = `https://snapchat.com`;
|
|
471
|
+
/** {@link WebsiteLinkType} code for Snapchat. */
|
|
348
472
|
const SNAPCHAT_WEBSITE_LINK_TYPE = 'sc';
|
|
473
|
+
/**
|
|
474
|
+
* Isolates a Snapchat username from a URL, ignoring the `/add/` base path segment.
|
|
475
|
+
*/
|
|
349
476
|
const SNAPCHAT_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
350
477
|
ignoredBasePath: 'add',
|
|
351
478
|
isolatePathComponents: 0,
|
|
352
479
|
removeTrailingSlash: true,
|
|
353
480
|
removeQueryParameters: true
|
|
354
481
|
});
|
|
482
|
+
/**
|
|
483
|
+
* Converts a Snapchat profile ID or URL into a {@link WebsiteLink}.
|
|
484
|
+
*
|
|
485
|
+
* Handles Snapchat's `/add/{username}` URL pattern.
|
|
486
|
+
*
|
|
487
|
+
* @param input - a Snapchat username or full profile URL
|
|
488
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
489
|
+
*/
|
|
355
490
|
function snapchatProfileUrlToWebsiteLink(input) {
|
|
356
491
|
return {
|
|
357
492
|
t: SNAPCHAT_WEBSITE_LINK_TYPE,
|
|
358
493
|
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, SNAPCHAT_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
359
494
|
};
|
|
360
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Constructs a full Snapchat profile URL from a profile ID.
|
|
498
|
+
*
|
|
499
|
+
* @param profileId - the Snapchat username
|
|
500
|
+
* @returns the full profile URL with `/add/` path
|
|
501
|
+
*/
|
|
361
502
|
function snapchatProfileUrl(profileId) {
|
|
362
503
|
return `${SNAPCHAT_BASE_URL}/add/${profileId}`;
|
|
363
504
|
}
|
|
364
505
|
// MARK: YouTube
|
|
506
|
+
/** Base URL for YouTube channels. */
|
|
365
507
|
const YOUTUBE_BASE_URL = `https://youtube.com`;
|
|
508
|
+
/** {@link WebsiteLinkType} code for YouTube. */
|
|
366
509
|
const YOUTUBE_WEBSITE_LINK_TYPE = 'yt';
|
|
510
|
+
/**
|
|
511
|
+
* Isolates a YouTube channel name from a URL, ignoring the `/c/` base path segment.
|
|
512
|
+
*/
|
|
367
513
|
const YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
368
514
|
ignoredBasePath: 'c',
|
|
369
515
|
isolatePathComponents: 0,
|
|
370
516
|
removeTrailingSlash: true,
|
|
371
517
|
removeQueryParameters: true
|
|
372
518
|
});
|
|
519
|
+
/**
|
|
520
|
+
* Converts a YouTube channel ID or URL into a {@link WebsiteLink}.
|
|
521
|
+
*
|
|
522
|
+
* Handles YouTube's `/c/{channel}` URL pattern.
|
|
523
|
+
*
|
|
524
|
+
* @param input - a YouTube channel name or full channel URL
|
|
525
|
+
* @returns a WebsiteLink with the isolated channel name as data
|
|
526
|
+
*/
|
|
373
527
|
function youtubeProfileUrlToWebsiteLink(input) {
|
|
374
528
|
return {
|
|
375
529
|
t: YOUTUBE_WEBSITE_LINK_TYPE,
|
|
376
530
|
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
377
531
|
};
|
|
378
532
|
}
|
|
533
|
+
/**
|
|
534
|
+
* Constructs a full YouTube channel URL from a profile ID.
|
|
535
|
+
*
|
|
536
|
+
* @param profileId - the YouTube channel name
|
|
537
|
+
* @returns the full channel URL with `/c/` path
|
|
538
|
+
*/
|
|
379
539
|
function youtubeProfileUrl(profileId) {
|
|
380
540
|
return `${YOUTUBE_BASE_URL}/c/${profileId}`;
|
|
381
541
|
}
|
|
382
542
|
// MARK: PayPal
|
|
543
|
+
/** Base URL for PayPal.me profiles. */
|
|
383
544
|
const PAYPAL_BASE_URL = `https://paypal.me`;
|
|
545
|
+
/** {@link WebsiteLinkType} code for PayPal. */
|
|
384
546
|
const PAYPAL_WEBSITE_LINK_TYPE = 'pp';
|
|
547
|
+
/**
|
|
548
|
+
* Converts a PayPal profile ID or URL into a {@link WebsiteLink}.
|
|
549
|
+
*
|
|
550
|
+
* @param input - a PayPal username or full PayPal.me URL
|
|
551
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
552
|
+
*/
|
|
385
553
|
function paypalProfileUrlToWebsiteLink(input) {
|
|
386
554
|
return {
|
|
387
555
|
t: PAYPAL_WEBSITE_LINK_TYPE,
|
|
388
556
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input)
|
|
389
557
|
};
|
|
390
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Constructs a full PayPal.me profile URL from a profile ID.
|
|
561
|
+
*
|
|
562
|
+
* @param profileId - the PayPal username
|
|
563
|
+
* @returns the full PayPal.me URL
|
|
564
|
+
*/
|
|
391
565
|
function paypalProfileUrl(profileId) {
|
|
392
566
|
return `${PAYPAL_BASE_URL}/${profileId}`;
|
|
393
567
|
}
|
|
394
568
|
// MARK: Cashapp
|
|
569
|
+
/** Base URL for Cash App profiles. */
|
|
395
570
|
const CASHAPP_BASE_URL = `https://cash.app`;
|
|
571
|
+
/** Cash App usernames are prefixed with "$" (cashtag). */
|
|
396
572
|
const CASHAPP_USERNAME_PREFIX = '$';
|
|
573
|
+
/** {@link WebsiteLinkType} code for Cash App. */
|
|
397
574
|
const CASHAPP_WEBSITE_LINK_TYPE = 'ca';
|
|
575
|
+
/**
|
|
576
|
+
* Converts a Cash App profile ID or URL into a {@link WebsiteLink}.
|
|
577
|
+
*
|
|
578
|
+
* Automatically prepends the "$" prefix if not already present.
|
|
579
|
+
*
|
|
580
|
+
* @param input - a Cash App username (with or without "$") or full profile URL
|
|
581
|
+
* @returns a WebsiteLink with the "$"-prefixed username as data
|
|
582
|
+
*/
|
|
398
583
|
function cashappProfileUrlToWebsiteLink(input) {
|
|
399
584
|
return {
|
|
400
585
|
t: CASHAPP_WEBSITE_LINK_TYPE,
|
|
401
586
|
d: usernameFromUsernameOrWebsiteWithBaseUrlUsername(input, CASHAPP_USERNAME_PREFIX)
|
|
402
587
|
};
|
|
403
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Constructs a full Cash App profile URL from a profile ID.
|
|
591
|
+
*
|
|
592
|
+
* @param profileId - the Cash App username (without "$" prefix)
|
|
593
|
+
* @returns the full Cash App URL with "$" prefix
|
|
594
|
+
*/
|
|
404
595
|
function cashappProfileUrl(profileId) {
|
|
405
596
|
return `${CASHAPP_BASE_URL}/$${profileId}`;
|
|
406
597
|
}
|
|
407
598
|
// MARK: Venmo
|
|
599
|
+
/** Base URL for Venmo profiles. */
|
|
408
600
|
const VENMO_BASE_URL = `https://account.venmo.com`;
|
|
601
|
+
/** {@link WebsiteLinkType} code for Venmo. */
|
|
409
602
|
const VENMO_WEBSITE_LINK_TYPE = 'vn';
|
|
603
|
+
/**
|
|
604
|
+
* Isolates a Venmo username from a URL, ignoring the `/u/` base path segment.
|
|
605
|
+
*/
|
|
410
606
|
const VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
411
607
|
ignoredBasePath: 'u',
|
|
412
608
|
isolatePathComponents: 0,
|
|
413
609
|
removeTrailingSlash: true,
|
|
414
610
|
removeQueryParameters: true
|
|
415
611
|
});
|
|
612
|
+
/**
|
|
613
|
+
* Converts a Venmo profile ID or URL into a {@link WebsiteLink}.
|
|
614
|
+
*
|
|
615
|
+
* Handles Venmo's `/u/{username}` URL pattern.
|
|
616
|
+
*
|
|
617
|
+
* @param input - a Venmo username or full profile URL
|
|
618
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
619
|
+
*/
|
|
416
620
|
function venmoProfileUrlToWebsiteLink(input) {
|
|
417
621
|
return {
|
|
418
622
|
t: VENMO_WEBSITE_LINK_TYPE,
|
|
419
623
|
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
420
624
|
};
|
|
421
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Constructs a full Venmo profile URL from a profile ID.
|
|
628
|
+
*
|
|
629
|
+
* @param profileId - the Venmo username
|
|
630
|
+
* @returns the full profile URL with `/u/` path
|
|
631
|
+
*/
|
|
422
632
|
function venmoProfileUrl(profileId) {
|
|
423
633
|
return `${VENMO_BASE_URL}/u/${profileId}`;
|
|
424
634
|
}
|
|
425
635
|
// MARK: Spotify
|
|
636
|
+
/** Base URL for Spotify profiles. */
|
|
426
637
|
const SPOTIFY_BASE_URL = `https://open.spotify.com/`;
|
|
638
|
+
/** {@link WebsiteLinkType} code for Spotify. */
|
|
427
639
|
const SPOTIFY_WEBSITE_LINK_TYPE = 'sp';
|
|
640
|
+
/**
|
|
641
|
+
* Isolates a Spotify username from a URL, ignoring the `/user/` base path segment.
|
|
642
|
+
*/
|
|
428
643
|
const SPOTIFY_WEBSITE_LINK_ISOLATE_PROFILE_ID = util.isolateWebsitePathFunction({
|
|
429
644
|
ignoredBasePath: 'user',
|
|
430
645
|
isolatePathComponents: 0,
|
|
431
646
|
removeTrailingSlash: true,
|
|
432
647
|
removeQueryParameters: true
|
|
433
648
|
});
|
|
649
|
+
/**
|
|
650
|
+
* Converts a Spotify profile ID or URL into a {@link WebsiteLink}.
|
|
651
|
+
*
|
|
652
|
+
* Handles Spotify's `/user/{username}` URL pattern.
|
|
653
|
+
*
|
|
654
|
+
* @param input - a Spotify username or full profile URL
|
|
655
|
+
* @returns a WebsiteLink with the isolated username as data
|
|
656
|
+
*/
|
|
434
657
|
function spotifyProfileUrlToWebsiteLink(input) {
|
|
435
658
|
return {
|
|
436
659
|
t: SPOTIFY_WEBSITE_LINK_TYPE,
|
|
437
660
|
d: usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername(input, SPOTIFY_WEBSITE_LINK_ISOLATE_PROFILE_ID)
|
|
438
661
|
};
|
|
439
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* Constructs a full Spotify profile URL from a profile ID.
|
|
665
|
+
*
|
|
666
|
+
* @param profileId - the Spotify username
|
|
667
|
+
* @returns the full profile URL with `/user/` path
|
|
668
|
+
*/
|
|
440
669
|
function spotifyProfileUrl(profileId) {
|
|
441
670
|
return `${SPOTIFY_BASE_URL}/user/${profileId}`;
|
|
442
671
|
}
|
|
@@ -445,10 +674,17 @@ const GRANTED_SYS_ADMIN_ROLE_KEY = 'sysadmin';
|
|
|
445
674
|
const GRANTED_OWNER_ROLE_KEY = 'owner';
|
|
446
675
|
const GRANTED_ADMIN_ROLE_KEY = 'admin';
|
|
447
676
|
/**
|
|
448
|
-
*
|
|
677
|
+
* Checks whether the given role represents an admin-level permission (either "admin" or "owner").
|
|
449
678
|
*
|
|
450
|
-
* @param role
|
|
451
|
-
* @returns
|
|
679
|
+
* @param role - the role to check
|
|
680
|
+
* @returns true if the role is an admin or owner role
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```typescript
|
|
684
|
+
* isGrantedAdminLevelRole('admin'); // true
|
|
685
|
+
* isGrantedAdminLevelRole('owner'); // true
|
|
686
|
+
* isGrantedAdminLevelRole('read'); // false
|
|
687
|
+
* ```
|
|
452
688
|
*/
|
|
453
689
|
function isGrantedAdminLevelRole(role) {
|
|
454
690
|
return role === GRANTED_ADMIN_ROLE_KEY || role === GRANTED_OWNER_ROLE_KEY;
|
|
@@ -458,27 +694,75 @@ const GRANTED_UPDATE_ROLE_KEY = 'update';
|
|
|
458
694
|
const GRANTED_DELETE_ROLE_KEY = 'delete';
|
|
459
695
|
const FULL_ACCESS_ROLE_KEY = '__FULL__';
|
|
460
696
|
const NO_ACCESS_ROLE_KEY = '__EMPTY__';
|
|
697
|
+
/**
|
|
698
|
+
* Creates a {@link GrantedRoleMap} that explicitly denies all access.
|
|
699
|
+
*
|
|
700
|
+
* @returns a role map with only the no-access marker set
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```typescript
|
|
704
|
+
* const map = noAccessRoleMap();
|
|
705
|
+
* isNoAccessRoleMap(map); // true
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
461
708
|
function noAccessRoleMap() {
|
|
462
709
|
return {
|
|
463
710
|
[NO_ACCESS_ROLE_KEY]: true
|
|
464
711
|
};
|
|
465
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* Type guard that checks whether a role map is a no-access map.
|
|
715
|
+
*
|
|
716
|
+
* @param input - the role map to check
|
|
717
|
+
* @returns true if the map has the no-access marker
|
|
718
|
+
*/
|
|
466
719
|
function isNoAccessRoleMap(input) {
|
|
467
720
|
return input[NO_ACCESS_ROLE_KEY] === true;
|
|
468
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Creates a {@link GrantedRoleMap} that grants full access to all roles.
|
|
724
|
+
*
|
|
725
|
+
* @returns a role map with the full-access marker set
|
|
726
|
+
*
|
|
727
|
+
* @example
|
|
728
|
+
* ```typescript
|
|
729
|
+
* const map = fullAccessRoleMap();
|
|
730
|
+
* isFullAccessRoleMap(map); // true
|
|
731
|
+
* ```
|
|
732
|
+
*/
|
|
469
733
|
function fullAccessRoleMap() {
|
|
470
734
|
return {
|
|
471
735
|
[FULL_ACCESS_ROLE_KEY]: true
|
|
472
736
|
};
|
|
473
737
|
}
|
|
738
|
+
/**
|
|
739
|
+
* Type guard that checks whether a role map is a full-access map.
|
|
740
|
+
*
|
|
741
|
+
* @param input - the role map to check
|
|
742
|
+
* @returns true if the map has the full-access marker
|
|
743
|
+
*/
|
|
474
744
|
function isFullAccessRoleMap(input) {
|
|
475
745
|
return input[FULL_ACCESS_ROLE_KEY] === true;
|
|
476
746
|
}
|
|
477
747
|
/**
|
|
478
|
-
* Creates a GrantedRoleMapReader.
|
|
748
|
+
* Creates a {@link GrantedRoleMapReader} for querying granted roles from a role map.
|
|
479
749
|
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
750
|
+
* The reader handles full-access and no-access maps as special cases, and provides methods
|
|
751
|
+
* for checking individual roles or sets of roles.
|
|
752
|
+
*
|
|
753
|
+
* @param map - the granted role map to read from
|
|
754
|
+
* @returns a reader instance for querying the map
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* const roleMap: GrantedRoleMap = { read: true, first: true };
|
|
759
|
+
* const reader = grantedRoleMapReader(roleMap);
|
|
760
|
+
*
|
|
761
|
+
* reader.hasRole('read'); // true
|
|
762
|
+
* reader.hasRole('delete'); // false
|
|
763
|
+
* reader.containsRoles('any', ['read']); // true
|
|
764
|
+
* reader.containsRoles('all', ['read', 'delete']); // false
|
|
765
|
+
* ```
|
|
482
766
|
*/
|
|
483
767
|
function grantedRoleMapReader(map) {
|
|
484
768
|
return new GrantedRoleMapReaderInstance(map);
|
|
@@ -540,21 +824,62 @@ class GrantedRoleMapReaderInstance {
|
|
|
540
824
|
}
|
|
541
825
|
}
|
|
542
826
|
/**
|
|
543
|
-
* Converts
|
|
827
|
+
* Converts an array of role strings into a {@link GrantedRoleKeysMap} where each role is mapped to the given boolean value.
|
|
544
828
|
*
|
|
545
|
-
* @param roles
|
|
546
|
-
* @
|
|
829
|
+
* @param roles - the role strings to include
|
|
830
|
+
* @param value - the boolean value to assign to each role (defaults to true)
|
|
831
|
+
* @returns a map of roles to boolean values
|
|
832
|
+
*
|
|
833
|
+
* @example
|
|
834
|
+
* ```typescript
|
|
835
|
+
* const map = grantedRoleKeysMapFromArray(['read', 'update']);
|
|
836
|
+
* // { read: true, update: true }
|
|
837
|
+
* ```
|
|
547
838
|
*/
|
|
548
839
|
function grantedRoleKeysMapFromArray(roles, value = true) {
|
|
549
840
|
return util.arrayToObject(roles, (x) => x, () => value);
|
|
550
841
|
}
|
|
551
842
|
|
|
843
|
+
/**
|
|
844
|
+
* Creates a {@link ContextGrantedModelRoles} with a no-access role map, indicating the context has no permissions.
|
|
845
|
+
*
|
|
846
|
+
* @param context - the context that was evaluated
|
|
847
|
+
* @param data - optional model data, if it was loaded
|
|
848
|
+
* @returns a ContextGrantedModelRoles with no access
|
|
849
|
+
*
|
|
850
|
+
* @example
|
|
851
|
+
* ```typescript
|
|
852
|
+
* const result = noAccessContextGrantedModelRoles(userContext);
|
|
853
|
+
* // result.roleMap contains the no-access marker
|
|
854
|
+
* ```
|
|
855
|
+
*/
|
|
552
856
|
function noAccessContextGrantedModelRoles(context, data) {
|
|
553
857
|
return contextGrantedModelRoles(context, data, noAccessRoleMap());
|
|
554
858
|
}
|
|
859
|
+
/**
|
|
860
|
+
* Creates a {@link ContextGrantedModelRoles} with a full-access role map, granting all permissions.
|
|
861
|
+
*
|
|
862
|
+
* @param context - the context that was evaluated
|
|
863
|
+
* @param data - optional model data
|
|
864
|
+
* @returns a ContextGrantedModelRoles with full access
|
|
865
|
+
*
|
|
866
|
+
* @example
|
|
867
|
+
* ```typescript
|
|
868
|
+
* const result = fullAccessGrantedModelRoles(adminContext, modelData);
|
|
869
|
+
* // result.roleMap contains the full-access marker
|
|
870
|
+
* ```
|
|
871
|
+
*/
|
|
555
872
|
function fullAccessGrantedModelRoles(context, data) {
|
|
556
873
|
return contextGrantedModelRoles(context, data, fullAccessRoleMap());
|
|
557
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* Creates a {@link ContextGrantedModelRoles} with the given role map, data, and context.
|
|
877
|
+
*
|
|
878
|
+
* @param context - the context that was evaluated
|
|
879
|
+
* @param data - the model data, if loaded
|
|
880
|
+
* @param roles - the granted role map
|
|
881
|
+
* @returns a ContextGrantedModelRoles combining all inputs
|
|
882
|
+
*/
|
|
558
883
|
function contextGrantedModelRoles(context, data, roles) {
|
|
559
884
|
return {
|
|
560
885
|
data,
|
|
@@ -605,6 +930,22 @@ class AbstractModelPermissionService {
|
|
|
605
930
|
}
|
|
606
931
|
}
|
|
607
932
|
|
|
933
|
+
/**
|
|
934
|
+
* Creates a factory that normalizes a {@link SyncEntityCommonTypeIdPairFactoryInput} into a full {@link SyncEntityCommonTypeIdPair}.
|
|
935
|
+
*
|
|
936
|
+
* If the input is a string, it is treated as a commonId and paired with the given commonType.
|
|
937
|
+
* If the input is already a pair, it is returned as-is.
|
|
938
|
+
*
|
|
939
|
+
* @param commonType - the default common type to use when input is a plain string
|
|
940
|
+
* @returns a factory function that produces SyncEntityCommonTypeIdPair instances
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* const factory = syncEntityCommonTypeIdPairFactory('user');
|
|
945
|
+
* factory('abc123'); // { commonType: 'user', commonId: 'abc123' }
|
|
946
|
+
* factory({ commonType: 'user', commonId: 'abc123' }); // passed through as-is
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
608
949
|
function syncEntityCommonTypeIdPairFactory(commonType) {
|
|
609
950
|
return (input) => {
|
|
610
951
|
if (typeof input === 'string') {
|
|
@@ -619,10 +960,23 @@ function syncEntityCommonTypeIdPairFactory(commonType) {
|
|
|
619
960
|
};
|
|
620
961
|
}
|
|
621
962
|
/**
|
|
622
|
-
* Creates a SyncEntityFactory.
|
|
963
|
+
* Creates a {@link SyncEntityFactory} that produces {@link SyncEntity} instances from a common type/id pair.
|
|
623
964
|
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
965
|
+
* The factory attaches the configured source info and optionally transforms the commonId into an entity id
|
|
966
|
+
* using the provided idFactory (defaults to identity).
|
|
967
|
+
*
|
|
968
|
+
* @param config - source info and optional id factory
|
|
969
|
+
* @returns a factory that creates SyncEntity instances
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* ```typescript
|
|
973
|
+
* const factory = syncEntityFactory({
|
|
974
|
+
* sourceInfo: { id: 'api', name: 'External API' }
|
|
975
|
+
* });
|
|
976
|
+
*
|
|
977
|
+
* const entity = factory({ commonType: 'user', commonId: 'abc123' });
|
|
978
|
+
* // entity.id === 'abc123', entity.sourceInfo.id === 'api'
|
|
979
|
+
* ```
|
|
626
980
|
*/
|
|
627
981
|
function syncEntityFactory(config) {
|
|
628
982
|
const { idFactory: inputIdFactory, sourceInfo } = config;
|
|
@@ -682,6 +1036,26 @@ class SynchronizationFailedError extends makeError.BaseError {
|
|
|
682
1036
|
}
|
|
683
1037
|
}
|
|
684
1038
|
|
|
1039
|
+
/**
|
|
1040
|
+
* Creates a {@link SyncEntitySynchronizer} from the given configuration.
|
|
1041
|
+
*
|
|
1042
|
+
* Registers common type synchronizers and provides lookup by common type. Throws
|
|
1043
|
+
* {@link UnregisteredSyncEntityCommonTypeError} if an unregistered common type is requested.
|
|
1044
|
+
*
|
|
1045
|
+
* @param config - contains the list of common type synchronizers to register
|
|
1046
|
+
* @returns a synchronizer that delegates to the appropriate common type synchronizer
|
|
1047
|
+
* @throws {UnregisteredSyncEntityCommonTypeError} when requesting an unregistered common type
|
|
1048
|
+
*
|
|
1049
|
+
* @example
|
|
1050
|
+
* ```typescript
|
|
1051
|
+
* const synchronizer = syncEntitySynchronizer({
|
|
1052
|
+
* commonTypeSynchronizers: [userSynchronizer, orderSynchronizer]
|
|
1053
|
+
* });
|
|
1054
|
+
*
|
|
1055
|
+
* const instance = await synchronizer.synchronizeInstance({ commonType: 'user', commonId: '123' });
|
|
1056
|
+
* const result = await instance.synchronize();
|
|
1057
|
+
* ```
|
|
1058
|
+
*/
|
|
685
1059
|
function syncEntitySynchronizer(config) {
|
|
686
1060
|
const map = new Map(config.commonTypeSynchronizers.map((x) => [x.commonType, x]));
|
|
687
1061
|
const commonTypes = Array.from(map.keys());
|
|
@@ -702,6 +1076,21 @@ function syncEntitySynchronizer(config) {
|
|
|
702
1076
|
};
|
|
703
1077
|
}
|
|
704
1078
|
|
|
1079
|
+
/**
|
|
1080
|
+
* Creates a {@link BasicSyncEntityCommonTypeSynchronizer} that orchestrates synchronization across multiple sources
|
|
1081
|
+
* for a specific entity common type.
|
|
1082
|
+
*
|
|
1083
|
+
* The synchronizer follows a primary/secondary/replica flow:
|
|
1084
|
+
* 1. The primary source is synchronized first (exactly one required).
|
|
1085
|
+
* 2. Secondary sources are synchronized sequentially; if a secondary reports deletion while primary did not, the sync restarts once.
|
|
1086
|
+
* 3. Replica sources are synchronized concurrently (up to 3 in parallel).
|
|
1087
|
+
*
|
|
1088
|
+
* @param config - common type, sources, and context loader
|
|
1089
|
+
* @returns a synchronizer for the configured common type
|
|
1090
|
+
* @throws {NoPrimarySyncSourceError} when no primary source is found
|
|
1091
|
+
* @throws {MultiplePrimarySyncSourceError} when more than one primary source is found
|
|
1092
|
+
* @throws {SynchronizationFailedError} when primary or secondary sync returns failed/error
|
|
1093
|
+
*/
|
|
705
1094
|
function basicSyncEntityCommonTypeSynchronizerInstanceFactory(config) {
|
|
706
1095
|
const { commonType, sources, entitySourceContextLoader, dynamicSources = false } = config;
|
|
707
1096
|
const syncEntityCommonTypeIdPairForType = syncEntityCommonTypeIdPairFactory(commonType);
|
|
@@ -895,111 +1284,114 @@ function basicSyncEntityCommonTypeSynchronizerInstanceFactory(config) {
|
|
|
895
1284
|
return result;
|
|
896
1285
|
}
|
|
897
1286
|
|
|
898
|
-
|
|
899
|
-
function
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
function
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
const transformCommaSeparatedNumberValueToArray = transformCommaSeparatedValueToArray((x) => Number(x));
|
|
918
|
-
const transformCommaSeparatedStringValueToArray = transformCommaSeparatedValueToArray((x) => x);
|
|
919
|
-
|
|
920
|
-
// MARK: Transform Annotations
|
|
921
|
-
function TransformCommaSeparatedValueToArray(mapFn) {
|
|
922
|
-
return classTransformer.Transform(transformCommaSeparatedValueToArray(mapFn));
|
|
923
|
-
}
|
|
924
|
-
const TransformCommaSeparatedStringValueToArray = () => classTransformer.Transform(transformCommaSeparatedStringValueToArray);
|
|
925
|
-
const TransformCommaSeparatedNumberValueToArray = () => classTransformer.Transform(transformCommaSeparatedNumberValueToArray);
|
|
926
|
-
const TransformStringValueToBoolean = () => classTransformer.Transform(transformStringToBoolean());
|
|
927
|
-
|
|
1287
|
+
/**
|
|
1288
|
+
* Creates a function that validates input against an ArkType schema and then processes it.
|
|
1289
|
+
*
|
|
1290
|
+
* On validation success, calls the configured handler function. On validation failure, delegates to the error handler.
|
|
1291
|
+
*
|
|
1292
|
+
* @param config - schema, handler function, and validation error handler
|
|
1293
|
+
* @returns a function that validates and processes input objects
|
|
1294
|
+
*
|
|
1295
|
+
* @example
|
|
1296
|
+
* ```typescript
|
|
1297
|
+
* const processUser = transformAndValidateObject({
|
|
1298
|
+
* schema: userType,
|
|
1299
|
+
* fn: async (user) => ({ id: user.id }),
|
|
1300
|
+
* handleValidationError: async (errors) => { throw new Error(errors.summary); }
|
|
1301
|
+
* });
|
|
1302
|
+
*
|
|
1303
|
+
* const result = await processUser({ id: '123', name: 'John' });
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
928
1306
|
function transformAndValidateObject(config) {
|
|
929
1307
|
const transformToResult = transformAndValidateObjectResult(config);
|
|
930
1308
|
const { handleValidationError } = config;
|
|
931
|
-
return
|
|
932
|
-
const
|
|
933
|
-
let result;
|
|
1309
|
+
return async (input, context) => {
|
|
1310
|
+
const x = await transformToResult(input, context);
|
|
934
1311
|
if (x.success) {
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
else {
|
|
938
|
-
result = await handleValidationError(x.validationErrors);
|
|
1312
|
+
return { object: x.object, result: x.result };
|
|
939
1313
|
}
|
|
940
|
-
return
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
});
|
|
1314
|
+
// Error handler is expected to throw. If it doesn't, there is no validated object to return.
|
|
1315
|
+
const result = await handleValidationError(x.validationErrors);
|
|
1316
|
+
return { object: undefined, result };
|
|
1317
|
+
};
|
|
945
1318
|
}
|
|
946
1319
|
/**
|
|
947
|
-
* Creates a
|
|
1320
|
+
* Creates a reusable factory for generating transform-and-validate functions with shared defaults.
|
|
1321
|
+
*
|
|
1322
|
+
* The factory pre-configures error handling so individual function calls
|
|
1323
|
+
* only need to specify the schema and handler.
|
|
948
1324
|
*
|
|
949
|
-
* @param defaults
|
|
950
|
-
* @returns
|
|
1325
|
+
* @param defaults - default error handler
|
|
1326
|
+
* @returns a factory function that creates TransformAndValidateObjectFunction instances
|
|
951
1327
|
*/
|
|
952
1328
|
function transformAndValidateObjectFactory(defaults) {
|
|
953
|
-
const { handleValidationError: defaultHandleValidationError
|
|
954
|
-
return (
|
|
1329
|
+
const { handleValidationError: defaultHandleValidationError } = defaults;
|
|
1330
|
+
return (schema, fn, handleValidationError) => {
|
|
955
1331
|
const config = {
|
|
956
|
-
|
|
1332
|
+
schema,
|
|
957
1333
|
fn,
|
|
958
|
-
handleValidationError: handleValidationError ?? defaultHandleValidationError
|
|
959
|
-
optionsForContext,
|
|
960
|
-
defaultValidationOptions
|
|
1334
|
+
handleValidationError: handleValidationError ?? defaultHandleValidationError
|
|
961
1335
|
};
|
|
962
1336
|
return transformAndValidateObject(config);
|
|
963
1337
|
};
|
|
964
1338
|
}
|
|
965
1339
|
/**
|
|
966
|
-
*
|
|
1340
|
+
* Creates a function that validates input against an ArkType schema and returns a discriminated result.
|
|
967
1341
|
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
970
|
-
*
|
|
1342
|
+
* Returns `{ success: true, object, result }` on valid input, or `{ success: false, validationErrors }` on failure.
|
|
1343
|
+
* The caller is responsible for handling the error case.
|
|
1344
|
+
*
|
|
1345
|
+
* @param config - schema and handler function
|
|
1346
|
+
* @returns a function that returns a success/error discriminated result
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* ```typescript
|
|
1350
|
+
* const validateUser = transformAndValidateObjectResult({
|
|
1351
|
+
* schema: userType,
|
|
1352
|
+
* fn: async (user) => ({ saved: true })
|
|
1353
|
+
* });
|
|
1354
|
+
*
|
|
1355
|
+
* const result = await validateUser({ name: 'John' });
|
|
1356
|
+
* if (result.success) {
|
|
1357
|
+
* console.log(result.result);
|
|
1358
|
+
* } else {
|
|
1359
|
+
* console.log(result.validationErrors.summary);
|
|
1360
|
+
* }
|
|
1361
|
+
* ```
|
|
971
1362
|
*/
|
|
972
1363
|
function transformAndValidateObjectResult(config) {
|
|
973
|
-
const {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
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 };
|
|
1364
|
+
const { schema, fn } = config;
|
|
1365
|
+
return async (input) => {
|
|
1366
|
+
const out = schema(input);
|
|
1367
|
+
if (out instanceof arktype.type.errors) {
|
|
1368
|
+
return { validationErrors: out, success: false };
|
|
993
1369
|
}
|
|
1370
|
+
const object = out;
|
|
1371
|
+
const result = await fn(object);
|
|
1372
|
+
return { object, result, success: true };
|
|
994
1373
|
};
|
|
995
1374
|
}
|
|
996
1375
|
|
|
1376
|
+
/**
|
|
1377
|
+
* Creates a factory for transform-and-validate functions that return the result with the parsed object attached as `params`.
|
|
1378
|
+
*
|
|
1379
|
+
* @param defaults - shared error handler defaults
|
|
1380
|
+
* @returns a factory that produces functions returning {@link TransformAndValidateFunctionResult}
|
|
1381
|
+
*/
|
|
997
1382
|
function transformAndValidateFunctionResultFactory(defaults) {
|
|
998
1383
|
return toTransformAndValidateFunctionResultFactory(transformAndValidateObjectFactory(defaults));
|
|
999
1384
|
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Wraps an existing {@link TransformAndValidateObjectFactory} to produce functions that attach the parsed object
|
|
1387
|
+
* as `params` on the result.
|
|
1388
|
+
*
|
|
1389
|
+
* @param transformAndValidateObjectFactory - the base factory to wrap
|
|
1390
|
+
* @returns a factory that produces functions returning results with `params` attached
|
|
1391
|
+
*/
|
|
1000
1392
|
function toTransformAndValidateFunctionResultFactory(transformAndValidateObjectFactory) {
|
|
1001
|
-
return (
|
|
1002
|
-
const transformAndValidateObjectFn = transformAndValidateObjectFactory(
|
|
1393
|
+
return (schema, fn, handleValidationError) => {
|
|
1394
|
+
const transformAndValidateObjectFn = transformAndValidateObjectFactory(schema, fn, handleValidationError);
|
|
1003
1395
|
return (input, context) => {
|
|
1004
1396
|
return toTransformAndValidateFunctionResult(transformAndValidateObjectFn(input, context));
|
|
1005
1397
|
};
|
|
@@ -1014,10 +1406,18 @@ function toTransformAndValidateFunctionResult(objectOutput) {
|
|
|
1014
1406
|
});
|
|
1015
1407
|
}
|
|
1016
1408
|
|
|
1409
|
+
/**
|
|
1410
|
+
* Creates a factory for transform-and-validate functions that return only the result (discarding the parsed object).
|
|
1411
|
+
*
|
|
1412
|
+
* Useful when you only need the processed output and don't need access to the validated input.
|
|
1413
|
+
*
|
|
1414
|
+
* @param defaults - shared error handler defaults
|
|
1415
|
+
* @returns a factory that produces functions returning only the handler's result
|
|
1416
|
+
*/
|
|
1017
1417
|
function transformAndValidateResultFactory(defaults) {
|
|
1018
1418
|
const factory = transformAndValidateObjectFactory(defaults);
|
|
1019
|
-
return (
|
|
1020
|
-
const transformAndValidateObjectFn = factory(
|
|
1419
|
+
return (schema, fn, handleValidationError) => {
|
|
1420
|
+
const transformAndValidateObjectFn = factory(schema, fn, handleValidationError);
|
|
1021
1421
|
return async (input, context) => {
|
|
1022
1422
|
const { result } = await transformAndValidateObjectFn(input, context);
|
|
1023
1423
|
return result;
|
|
@@ -1026,152 +1426,80 @@ function transformAndValidateResultFactory(defaults) {
|
|
|
1026
1426
|
}
|
|
1027
1427
|
|
|
1028
1428
|
/**
|
|
1029
|
-
*
|
|
1030
|
-
*/
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1429
|
+
* ArkType schema for a model key (non-empty string).
|
|
1430
|
+
*/
|
|
1431
|
+
const modelKeyType = arktype.type('string > 0');
|
|
1432
|
+
/**
|
|
1433
|
+
* ArkType schema for a model id (non-empty string).
|
|
1434
|
+
*/
|
|
1435
|
+
const modelIdType = arktype.type('string > 0');
|
|
1436
|
+
/**
|
|
1437
|
+
* ArkType schema for target model params with a required `key` field.
|
|
1438
|
+
*/
|
|
1439
|
+
const targetModelParamsType = arktype.type({
|
|
1440
|
+
key: modelKeyType
|
|
1441
|
+
});
|
|
1442
|
+
/**
|
|
1443
|
+
* ArkType schema for target model id params with a required `id` field.
|
|
1444
|
+
*/
|
|
1445
|
+
const targetModelIdParamsType = arktype.type({
|
|
1446
|
+
id: modelIdType
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
function clearable(definition) {
|
|
1450
|
+
if (typeof definition === 'string') {
|
|
1451
|
+
return `${definition} | null | undefined`;
|
|
1452
|
+
}
|
|
1453
|
+
return definition.or('null').or('undefined');
|
|
1044
1454
|
}
|
|
1045
1455
|
|
|
1046
1456
|
/**
|
|
1047
|
-
*
|
|
1048
|
-
*/
|
|
1049
|
-
|
|
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
|
-
};
|
|
1062
|
-
}
|
|
1457
|
+
* ArkType schema for a valid ISO 8601 day string (e.g., "2024-01-15").
|
|
1458
|
+
*/
|
|
1459
|
+
const iso8601DayStringType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isISO8601DayString(val)) || ctx.mustBe('a valid ISO 8601 day string'));
|
|
1063
1460
|
|
|
1064
1461
|
/**
|
|
1065
|
-
*
|
|
1066
|
-
*/
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
};
|
|
1080
|
-
}
|
|
1462
|
+
* ArkType schema for a valid minute of the day (0-1439).
|
|
1463
|
+
*/
|
|
1464
|
+
const minuteOfDayType = arktype.type('number').narrow((val, ctx) => (val != null && util.isMinuteOfDay(val)) || ctx.mustBe('a valid minute of the day (0-1439)'));
|
|
1465
|
+
|
|
1081
1466
|
/**
|
|
1082
|
-
*
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
* @returns
|
|
1086
|
-
*/
|
|
1087
|
-
function IsE164PhoneNumberWithOptionalExtension(validationOptions) {
|
|
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
|
-
};
|
|
1100
|
-
}
|
|
1467
|
+
* ArkType schema for a valid E.164 phone number without an extension.
|
|
1468
|
+
*/
|
|
1469
|
+
const e164PhoneNumberType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isE164PhoneNumber(val, false)) || ctx.mustBe('a valid E.164 phone number without an extension'));
|
|
1101
1470
|
/**
|
|
1102
|
-
*
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
-
};
|
|
1120
|
-
}
|
|
1471
|
+
* ArkType schema for a valid E.164 phone number, optionally with an extension.
|
|
1472
|
+
*/
|
|
1473
|
+
const e164PhoneNumberWithOptionalExtensionType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isE164PhoneNumber(val, true)) || ctx.mustBe('a valid E.164 phone number'));
|
|
1474
|
+
/**
|
|
1475
|
+
* ArkType schema for a valid E.164 phone number that includes an extension.
|
|
1476
|
+
*/
|
|
1477
|
+
const e164PhoneNumberWithExtensionType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isE164PhoneNumberWithExtension(val)) || ctx.mustBe('a valid E.164 phone number with an extension'));
|
|
1121
1478
|
|
|
1122
1479
|
/**
|
|
1123
|
-
*
|
|
1480
|
+
* Creates an ArkType schema that validates an array has no duplicate keys.
|
|
1481
|
+
*
|
|
1482
|
+
* @param readKey - function that extracts the key from each array element
|
|
1483
|
+
* @returns an ArkType schema that narrows `T[]` to ensure uniqueness
|
|
1484
|
+
*
|
|
1485
|
+
* @example
|
|
1486
|
+
* ```typescript
|
|
1487
|
+
* const uniqueItemsType = uniqueKeyedType((item: Item) => item.id);
|
|
1488
|
+
* ```
|
|
1124
1489
|
*/
|
|
1125
|
-
function
|
|
1490
|
+
function uniqueKeyedType(readKey) {
|
|
1126
1491
|
const isUniqueKeyed = util.isUniqueKeyedFunction(readKey);
|
|
1127
|
-
return
|
|
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
|
-
};
|
|
1492
|
+
return arktype.type('unknown[]').narrow((val, ctx) => (val != null && isUniqueKeyed(val)) || ctx.mustBe('an array with unique keys'));
|
|
1139
1493
|
}
|
|
1140
1494
|
|
|
1141
1495
|
/**
|
|
1142
|
-
*
|
|
1143
|
-
*/
|
|
1144
|
-
|
|
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
|
-
};
|
|
1157
|
-
}
|
|
1496
|
+
* ArkType schema for a valid website URL (with or without protocol prefix).
|
|
1497
|
+
*/
|
|
1498
|
+
const websiteUrlType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isWebsiteUrl(val)) || ctx.mustBe('a valid website URL'));
|
|
1158
1499
|
/**
|
|
1159
|
-
*
|
|
1160
|
-
*/
|
|
1161
|
-
|
|
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
|
-
};
|
|
1174
|
-
}
|
|
1500
|
+
* ArkType schema for a valid website URL that starts with `http://` or `https://`.
|
|
1501
|
+
*/
|
|
1502
|
+
const websiteUrlWithPrefixType = arktype.type('string > 0').narrow((val, ctx) => (val != null && util.isWebsiteUrlWithPrefix(val)) || ctx.mustBe('a valid website URL starting with http:// or https://'));
|
|
1175
1503
|
|
|
1176
1504
|
exports.ADDRESS_CITY_MAX_LENGTH = ADDRESS_CITY_MAX_LENGTH;
|
|
1177
1505
|
exports.ADDRESS_COUNTRY_MAX_LENGTH = ADDRESS_COUNTRY_MAX_LENGTH;
|
|
@@ -1180,7 +1508,6 @@ exports.ADDRESS_STATE_CODE_MAX_LENGTH = ADDRESS_STATE_CODE_MAX_LENGTH;
|
|
|
1180
1508
|
exports.ADDRESS_STATE_MAX_LENGTH = ADDRESS_STATE_MAX_LENGTH;
|
|
1181
1509
|
exports.ADDRESS_ZIP_MAX_LENGTH = ADDRESS_ZIP_MAX_LENGTH;
|
|
1182
1510
|
exports.AbstractModelPermissionService = AbstractModelPermissionService;
|
|
1183
|
-
exports.AbstractUnitedStatesAddressWithoutStateParams = AbstractUnitedStatesAddressWithoutStateParams;
|
|
1184
1511
|
exports.CASHAPP_BASE_URL = CASHAPP_BASE_URL;
|
|
1185
1512
|
exports.CASHAPP_USERNAME_PREFIX = CASHAPP_USERNAME_PREFIX;
|
|
1186
1513
|
exports.CASHAPP_WEBSITE_LINK_TYPE = CASHAPP_WEBSITE_LINK_TYPE;
|
|
@@ -1197,14 +1524,6 @@ exports.GRANTED_UPDATE_ROLE_KEY = GRANTED_UPDATE_ROLE_KEY;
|
|
|
1197
1524
|
exports.GrantedRoleMapReaderInstance = GrantedRoleMapReaderInstance;
|
|
1198
1525
|
exports.INSTAGRAM_BASE_URL = INSTAGRAM_BASE_URL;
|
|
1199
1526
|
exports.INSTAGRAM_WEBSITE_LINK_TYPE = INSTAGRAM_WEBSITE_LINK_TYPE;
|
|
1200
|
-
exports.IsE164PhoneNumber = IsE164PhoneNumber;
|
|
1201
|
-
exports.IsE164PhoneNumberWithExtension = IsE164PhoneNumberWithExtension;
|
|
1202
|
-
exports.IsE164PhoneNumberWithOptionalExtension = IsE164PhoneNumberWithOptionalExtension;
|
|
1203
|
-
exports.IsISO8601DayString = IsISO8601DayString;
|
|
1204
|
-
exports.IsMinuteOfDay = IsMinuteOfDay;
|
|
1205
|
-
exports.IsUniqueKeyed = IsUniqueKeyed;
|
|
1206
|
-
exports.IsWebsiteUrl = IsWebsiteUrl;
|
|
1207
|
-
exports.IsWebsiteUrlWithPrefix = IsWebsiteUrlWithPrefix;
|
|
1208
1527
|
exports.NO_ACCESS_ROLE_KEY = NO_ACCESS_ROLE_KEY;
|
|
1209
1528
|
exports.PAYPAL_BASE_URL = PAYPAL_BASE_URL;
|
|
1210
1529
|
exports.PAYPAL_WEBSITE_LINK_TYPE = PAYPAL_WEBSITE_LINK_TYPE;
|
|
@@ -1220,13 +1539,7 @@ exports.TIKTOK_USERNAME_PREFIX = TIKTOK_USERNAME_PREFIX;
|
|
|
1220
1539
|
exports.TIKTOK_WEBSITE_LINK_TYPE = TIKTOK_WEBSITE_LINK_TYPE;
|
|
1221
1540
|
exports.TWITTER_BASE_URL = TWITTER_BASE_URL;
|
|
1222
1541
|
exports.TWITTER_WEBSITE_LINK_TYPE = TWITTER_WEBSITE_LINK_TYPE;
|
|
1223
|
-
exports.TransformCommaSeparatedNumberValueToArray = TransformCommaSeparatedNumberValueToArray;
|
|
1224
|
-
exports.TransformCommaSeparatedStringValueToArray = TransformCommaSeparatedStringValueToArray;
|
|
1225
|
-
exports.TransformCommaSeparatedValueToArray = TransformCommaSeparatedValueToArray;
|
|
1226
|
-
exports.TransformStringValueToBoolean = TransformStringValueToBoolean;
|
|
1227
1542
|
exports.UNKNOWN_WEBSITE_LINK_TYPE = UNKNOWN_WEBSITE_LINK_TYPE;
|
|
1228
|
-
exports.UnitedStatesAddressWithStateCodeParams = UnitedStatesAddressWithStateCodeParams;
|
|
1229
|
-
exports.UnitedStatesAddressWithStateStringParams = UnitedStatesAddressWithStateStringParams;
|
|
1230
1543
|
exports.VENMO_BASE_URL = VENMO_BASE_URL;
|
|
1231
1544
|
exports.VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID = VENMO_WEBSITE_LINK_ISOLATE_PROFILE_ID;
|
|
1232
1545
|
exports.VENMO_WEBSITE_LINK_TYPE = VENMO_WEBSITE_LINK_TYPE;
|
|
@@ -1244,16 +1557,18 @@ exports.WEBSITE_LINK_ISOLATE_BASE_URL_PROFILE_ID = WEBSITE_LINK_ISOLATE_BASE_URL
|
|
|
1244
1557
|
exports.WEBSITE_LINK_TYPE_MAX_LENGTH = WEBSITE_LINK_TYPE_MAX_LENGTH;
|
|
1245
1558
|
exports.WEBSITE_LINK_TYPE_REGEX = WEBSITE_LINK_TYPE_REGEX;
|
|
1246
1559
|
exports.WEBSITE_URL_WEBSITE_LINK_TYPE = WEBSITE_URL_WEBSITE_LINK_TYPE;
|
|
1247
|
-
exports.WebsiteFileLink = WebsiteFileLink;
|
|
1248
|
-
exports.WebsiteLink = WebsiteLink;
|
|
1249
1560
|
exports.YOUTUBE_BASE_URL = YOUTUBE_BASE_URL;
|
|
1250
1561
|
exports.YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID = YOUTUBE_WEBSITE_LINK_ISOLATE_PROFILE_ID;
|
|
1251
1562
|
exports.YOUTUBE_WEBSITE_LINK_TYPE = YOUTUBE_WEBSITE_LINK_TYPE;
|
|
1252
1563
|
exports.basicSyncEntityCommonTypeSynchronizerInstanceFactory = basicSyncEntityCommonTypeSynchronizerInstanceFactory;
|
|
1253
1564
|
exports.cashappProfileUrl = cashappProfileUrl;
|
|
1254
1565
|
exports.cashappProfileUrlToWebsiteLink = cashappProfileUrlToWebsiteLink;
|
|
1566
|
+
exports.clearable = clearable;
|
|
1255
1567
|
exports.contextGrantedModelRoles = contextGrantedModelRoles;
|
|
1256
1568
|
exports.decodeWebsiteLinkEncodedDataToWebsiteFileLink = decodeWebsiteLinkEncodedDataToWebsiteFileLink;
|
|
1569
|
+
exports.e164PhoneNumberType = e164PhoneNumberType;
|
|
1570
|
+
exports.e164PhoneNumberWithExtensionType = e164PhoneNumberWithExtensionType;
|
|
1571
|
+
exports.e164PhoneNumberWithOptionalExtensionType = e164PhoneNumberWithOptionalExtensionType;
|
|
1257
1572
|
exports.emailAddressToWebsiteLink = emailAddressToWebsiteLink;
|
|
1258
1573
|
exports.encodeWebsiteFileLinkToWebsiteLinkEncodedData = encodeWebsiteFileLinkToWebsiteLinkEncodedData;
|
|
1259
1574
|
exports.facebookProfileUrl = facebookProfileUrl;
|
|
@@ -1268,6 +1583,10 @@ exports.isFullAccessRoleMap = isFullAccessRoleMap;
|
|
|
1268
1583
|
exports.isGrantedAdminLevelRole = isGrantedAdminLevelRole;
|
|
1269
1584
|
exports.isNoAccessRoleMap = isNoAccessRoleMap;
|
|
1270
1585
|
exports.isValidWebsiteLinkType = isValidWebsiteLinkType;
|
|
1586
|
+
exports.iso8601DayStringType = iso8601DayStringType;
|
|
1587
|
+
exports.minuteOfDayType = minuteOfDayType;
|
|
1588
|
+
exports.modelIdType = modelIdType;
|
|
1589
|
+
exports.modelKeyType = modelKeyType;
|
|
1271
1590
|
exports.noAccessContextGrantedModelRoles = noAccessContextGrantedModelRoles;
|
|
1272
1591
|
exports.noAccessRoleMap = noAccessRoleMap;
|
|
1273
1592
|
exports.paypalProfileUrl = paypalProfileUrl;
|
|
@@ -1280,6 +1599,8 @@ exports.spotifyProfileUrlToWebsiteLink = spotifyProfileUrlToWebsiteLink;
|
|
|
1280
1599
|
exports.syncEntityCommonTypeIdPairFactory = syncEntityCommonTypeIdPairFactory;
|
|
1281
1600
|
exports.syncEntityFactory = syncEntityFactory;
|
|
1282
1601
|
exports.syncEntitySynchronizer = syncEntitySynchronizer;
|
|
1602
|
+
exports.targetModelIdParamsType = targetModelIdParamsType;
|
|
1603
|
+
exports.targetModelParamsType = targetModelParamsType;
|
|
1283
1604
|
exports.tiktokProfileUrl = tiktokProfileUrl;
|
|
1284
1605
|
exports.tiktokProfileUrlToWebsiteLink = tiktokProfileUrlToWebsiteLink;
|
|
1285
1606
|
exports.toTransformAndValidateFunctionResult = toTransformAndValidateFunctionResult;
|
|
@@ -1289,20 +1610,23 @@ exports.transformAndValidateObject = transformAndValidateObject;
|
|
|
1289
1610
|
exports.transformAndValidateObjectFactory = transformAndValidateObjectFactory;
|
|
1290
1611
|
exports.transformAndValidateObjectResult = transformAndValidateObjectResult;
|
|
1291
1612
|
exports.transformAndValidateResultFactory = transformAndValidateResultFactory;
|
|
1292
|
-
exports.transformCommaSeparatedNumberValueToArray = transformCommaSeparatedNumberValueToArray;
|
|
1293
|
-
exports.transformCommaSeparatedStringValueToArray = transformCommaSeparatedStringValueToArray;
|
|
1294
|
-
exports.transformCommaSeparatedValueToArray = transformCommaSeparatedValueToArray;
|
|
1295
|
-
exports.transformStringToBoolean = transformStringToBoolean;
|
|
1296
1613
|
exports.twitterProfileUrl = twitterProfileUrl;
|
|
1297
1614
|
exports.twitterProfileUrlToWebsiteLink = twitterProfileUrlToWebsiteLink;
|
|
1615
|
+
exports.uniqueKeyedType = uniqueKeyedType;
|
|
1616
|
+
exports.unitedStatesAddressWithStateCodeType = unitedStatesAddressWithStateCodeType;
|
|
1617
|
+
exports.unitedStatesAddressWithStateStringType = unitedStatesAddressWithStateStringType;
|
|
1298
1618
|
exports.usernameFromUsernameOrWebsiteWithBaseUrlUsername = usernameFromUsernameOrWebsiteWithBaseUrlUsername;
|
|
1299
1619
|
exports.usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername = usernameFromUsernameOrWebsiteWithOneOffBaseUrlUsername;
|
|
1300
1620
|
exports.usernameOrWebsiteUrlToWebsiteUrl = usernameOrWebsiteUrlToWebsiteUrl;
|
|
1301
1621
|
exports.venmoProfileUrl = venmoProfileUrl;
|
|
1302
1622
|
exports.venmoProfileUrlToWebsiteLink = venmoProfileUrlToWebsiteLink;
|
|
1303
1623
|
exports.websiteFileLinkToWebsiteLink = websiteFileLinkToWebsiteLink;
|
|
1624
|
+
exports.websiteFileLinkType = websiteFileLinkType;
|
|
1304
1625
|
exports.websiteLinkToWebsiteLinkFile = websiteLinkToWebsiteLinkFile;
|
|
1626
|
+
exports.websiteLinkType = websiteLinkType;
|
|
1305
1627
|
exports.websiteUrlToWebsiteLink = websiteUrlToWebsiteLink;
|
|
1628
|
+
exports.websiteUrlType = websiteUrlType;
|
|
1629
|
+
exports.websiteUrlWithPrefixType = websiteUrlWithPrefixType;
|
|
1306
1630
|
exports.youtubeProfileUrl = youtubeProfileUrl;
|
|
1307
1631
|
exports.youtubeProfileUrlToWebsiteLink = youtubeProfileUrlToWebsiteLink;
|
|
1308
1632
|
//# sourceMappingURL=index.cjs.js.map
|