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