@oxyhq/core 12.5.1 → 12.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/i18n/locales/ar-SA.json +3 -1
  3. package/dist/cjs/i18n/locales/ca-ES.json +3 -1
  4. package/dist/cjs/i18n/locales/de-DE.json +3 -1
  5. package/dist/cjs/i18n/locales/en-US.json +14 -1
  6. package/dist/cjs/i18n/locales/es-ES.json +14 -1
  7. package/dist/cjs/i18n/locales/fr-FR.json +3 -1
  8. package/dist/cjs/i18n/locales/it-IT.json +3 -1
  9. package/dist/cjs/i18n/locales/ja-JP.json +3 -1
  10. package/dist/cjs/i18n/locales/ko-KR.json +3 -1
  11. package/dist/cjs/i18n/locales/locales/ar-SA.json +3 -1
  12. package/dist/cjs/i18n/locales/locales/ca-ES.json +3 -1
  13. package/dist/cjs/i18n/locales/locales/de-DE.json +3 -1
  14. package/dist/cjs/i18n/locales/locales/en-US.json +14 -1
  15. package/dist/cjs/i18n/locales/locales/es-ES.json +14 -1
  16. package/dist/cjs/i18n/locales/locales/fr-FR.json +3 -1
  17. package/dist/cjs/i18n/locales/locales/it-IT.json +3 -1
  18. package/dist/cjs/i18n/locales/locales/ja-JP.json +3 -1
  19. package/dist/cjs/i18n/locales/locales/ko-KR.json +3 -1
  20. package/dist/cjs/i18n/locales/locales/pt-PT.json +3 -1
  21. package/dist/cjs/i18n/locales/locales/zh-CN.json +3 -1
  22. package/dist/cjs/i18n/locales/pt-PT.json +3 -1
  23. package/dist/cjs/i18n/locales/zh-CN.json +3 -1
  24. package/dist/cjs/mixins/OxyServices.user.js +24 -2
  25. package/dist/cjs/utils/displayNamePolicyRanges.generated.js +35 -0
  26. package/dist/cjs/utils/textNormalization.js +9 -4
  27. package/dist/cjs/utils/validationUtils.js +49 -41
  28. package/dist/esm/.tsbuildinfo +1 -1
  29. package/dist/esm/i18n/locales/ar-SA.json +3 -1
  30. package/dist/esm/i18n/locales/ca-ES.json +3 -1
  31. package/dist/esm/i18n/locales/de-DE.json +3 -1
  32. package/dist/esm/i18n/locales/en-US.json +14 -1
  33. package/dist/esm/i18n/locales/es-ES.json +14 -1
  34. package/dist/esm/i18n/locales/fr-FR.json +3 -1
  35. package/dist/esm/i18n/locales/it-IT.json +3 -1
  36. package/dist/esm/i18n/locales/ja-JP.json +3 -1
  37. package/dist/esm/i18n/locales/ko-KR.json +3 -1
  38. package/dist/esm/i18n/locales/locales/ar-SA.json +3 -1
  39. package/dist/esm/i18n/locales/locales/ca-ES.json +3 -1
  40. package/dist/esm/i18n/locales/locales/de-DE.json +3 -1
  41. package/dist/esm/i18n/locales/locales/en-US.json +14 -1
  42. package/dist/esm/i18n/locales/locales/es-ES.json +14 -1
  43. package/dist/esm/i18n/locales/locales/fr-FR.json +3 -1
  44. package/dist/esm/i18n/locales/locales/it-IT.json +3 -1
  45. package/dist/esm/i18n/locales/locales/ja-JP.json +3 -1
  46. package/dist/esm/i18n/locales/locales/ko-KR.json +3 -1
  47. package/dist/esm/i18n/locales/locales/pt-PT.json +3 -1
  48. package/dist/esm/i18n/locales/locales/zh-CN.json +3 -1
  49. package/dist/esm/i18n/locales/pt-PT.json +3 -1
  50. package/dist/esm/i18n/locales/zh-CN.json +3 -1
  51. package/dist/esm/mixins/OxyServices.user.js +24 -2
  52. package/dist/esm/utils/displayNamePolicyRanges.generated.js +32 -0
  53. package/dist/esm/utils/textNormalization.js +9 -4
  54. package/dist/esm/utils/validationUtils.js +50 -42
  55. package/dist/types/.tsbuildinfo +1 -1
  56. package/dist/types/utils/displayNamePolicyRanges.generated.d.ts +32 -0
  57. package/dist/types/utils/validationUtils.d.ts +48 -34
  58. package/package.json +9 -4
  59. package/src/i18n/locales/ar-SA.json +3 -1
  60. package/src/i18n/locales/ca-ES.json +3 -1
  61. package/src/i18n/locales/de-DE.json +3 -1
  62. package/src/i18n/locales/en-US.json +14 -1
  63. package/src/i18n/locales/es-ES.json +14 -1
  64. package/src/i18n/locales/fr-FR.json +3 -1
  65. package/src/i18n/locales/it-IT.json +3 -1
  66. package/src/i18n/locales/ja-JP.json +3 -1
  67. package/src/i18n/locales/ko-KR.json +3 -1
  68. package/src/i18n/locales/pt-PT.json +3 -1
  69. package/src/i18n/locales/zh-CN.json +3 -1
  70. package/src/mixins/OxyServices.user.ts +24 -2
  71. package/src/mixins/__tests__/followCacheInvalidation.test.ts +5 -0
  72. package/src/mixins/__tests__/userReadCacheBypass.test.ts +8 -0
  73. package/src/utils/__tests__/validationUtils.test.ts +106 -3
  74. package/src/utils/displayNamePolicyRanges.generated.ts +40 -0
  75. package/src/utils/textNormalization.ts +9 -4
  76. package/src/utils/validationUtils.ts +55 -42
@@ -161,7 +161,7 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
161
161
  */
162
162
  async getProfileByUsername(username: string, options?: { cache?: boolean }): Promise<User> {
163
163
  try {
164
- const user = await this.makeRequest<User>('GET', `/profiles/username/${username}`, undefined, {
164
+ const user = await this.makeRequest<User>('GET', `/profiles/username/${encodeURIComponent(username)}`, undefined, {
165
165
  cache: options?.cache ?? true,
166
166
  cacheTTL: 5 * 60 * 1000, // 5 minutes cache for profiles
167
167
  });
@@ -244,7 +244,15 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
244
244
  handle,
245
245
  }, {
246
246
  cache: true,
247
- cacheTTL: 24 * 60 * 60 * 1000, // 24h cache — matches server-side staleness window
247
+ // 5 min matches the sibling profile fetches (getUserById /
248
+ // getProfileByUsername). /profiles/resolve now carries the viewer-relative
249
+ // `relationship` (isFollowing / followsYou); `followsYou` is target→viewer,
250
+ // so the viewer can't self-refresh it via a follow action. The GET cache is
251
+ // identity-scoped (keyed by the caller's access-token identity tag → no
252
+ // cross-viewer poison), so a short TTL keeps "Follows you" reasonably fresh
253
+ // without a per-call bypass. The identity fields alone tolerate a longer
254
+ // window, but a 24h stale relationship is not acceptable.
255
+ cacheTTL: 5 * 60 * 1000,
248
256
  });
249
257
  return normalizeUserIdentityOrNull(result);
250
258
  } catch (error: unknown) {
@@ -725,6 +733,11 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
725
733
  try {
726
734
  const result = await this.makeRequest<FollowMutationResult>('POST', `/users/${userId}/follow`, undefined, { cache: false });
727
735
  this.clearCacheEntry(`GET:/users/${userId}/follow-status`);
736
+ // Profile fetches embed viewer-relative `relationship` — bust so a
737
+ // remount doesn't serve a stale isFollowing for up to 5 minutes.
738
+ this.clearCacheEntry(`GET:/users/${userId}`);
739
+ this.clearCacheByPrefix('GET:/profiles/username/');
740
+ this.clearCacheByPrefix('GET:/profiles/resolve');
728
741
  // The follow changed the viewer's graph — bust the cached consolidated
729
742
  // `GET /users/me/graph` so the next read reflects the new following/
730
743
  // mutual set instead of the stale pre-write snapshot.
@@ -752,7 +765,10 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
752
765
  // Bust each affected user's cached follow-status (see `followUser`).
753
766
  for (const id of userIds) {
754
767
  this.clearCacheEntry(`GET:/users/${id}/follow-status`);
768
+ this.clearCacheEntry(`GET:/users/${id}`);
755
769
  }
770
+ this.clearCacheByPrefix('GET:/profiles/username/');
771
+ this.clearCacheByPrefix('GET:/profiles/resolve');
756
772
  // The batch changed the viewer's graph — bust the consolidated cache.
757
773
  this.clearCacheEntry('GET:/users/me/graph');
758
774
  return result;
@@ -778,7 +794,10 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
778
794
  // Bust each affected user's cached follow-status (see `followUser`).
779
795
  for (const id of userIds) {
780
796
  this.clearCacheEntry(`GET:/users/${id}/follow-status`);
797
+ this.clearCacheEntry(`GET:/users/${id}`);
781
798
  }
799
+ this.clearCacheByPrefix('GET:/profiles/username/');
800
+ this.clearCacheByPrefix('GET:/profiles/resolve');
782
801
  // The batch changed the viewer's graph — bust the consolidated cache.
783
802
  this.clearCacheEntry('GET:/users/me/graph');
784
803
  return result;
@@ -795,6 +814,9 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
795
814
  const result = await this.makeRequest<FollowMutationResult>('DELETE', `/users/${userId}/follow`, undefined, { cache: false });
796
815
  // Bust the cached follow-status so a remount reads fresh truth (see `followUser`).
797
816
  this.clearCacheEntry(`GET:/users/${userId}/follow-status`);
817
+ this.clearCacheEntry(`GET:/users/${userId}`);
818
+ this.clearCacheByPrefix('GET:/profiles/username/');
819
+ this.clearCacheByPrefix('GET:/profiles/resolve');
798
820
  // The unfollow changed the viewer's graph — bust the consolidated cache.
799
821
  this.clearCacheEntry('GET:/users/me/graph');
800
822
  return result;
@@ -147,12 +147,17 @@ describe('follow-status cache invalidation', () => {
147
147
 
148
148
  it('invalidates the exact logical follow-status key on a single follow', async () => {
149
149
  const clearSpy = jest.spyOn(oxy, 'clearCacheEntry');
150
+ const clearPrefixSpy = jest.spyOn(oxy, 'clearCacheByPrefix');
150
151
  fetchMock.mockResolvedValueOnce(jsonResponse({ success: true, message: 'ok' }));
151
152
 
152
153
  await oxy.followUser('target-3');
153
154
 
154
155
  expect(clearSpy).toHaveBeenCalledWith('GET:/users/target-3/follow-status');
156
+ expect(clearSpy).toHaveBeenCalledWith('GET:/users/target-3');
157
+ expect(clearPrefixSpy).toHaveBeenCalledWith('GET:/profiles/username/');
158
+ expect(clearPrefixSpy).toHaveBeenCalledWith('GET:/profiles/resolve');
155
159
  clearSpy.mockRestore();
160
+ clearPrefixSpy.mockRestore();
156
161
  });
157
162
 
158
163
  it('does not invalidate or call the network when bulk follow gets an empty list', async () => {
@@ -117,5 +117,13 @@ describe('getUserById / getProfileByUsername cache bypass', () => {
117
117
  expect((fresh as unknown as { bio: string }).bio).toBe('new');
118
118
  expect(fetchMock).toHaveBeenCalledTimes(2);
119
119
  });
120
+
121
+ it('URL-encodes the username in the request path', async () => {
122
+ fetchMock.mockResolvedValueOnce(jsonResponse({ id: 'fed-1', username: 'alice@mastodon.social' }));
123
+ await oxy.getProfileByUsername('alice@mastodon.social');
124
+ expect(fetchMock).toHaveBeenCalledTimes(1);
125
+ const [url] = fetchMock.mock.calls[0] as [string];
126
+ expect(url).toContain('/profiles/username/alice%40mastodon.social');
127
+ });
120
128
  });
121
129
  });
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
1
3
  import {
2
4
  isRequiredString,
3
5
  isRequiredNumber,
@@ -8,6 +10,9 @@ import {
8
10
  isValidUsername,
9
11
  isValidPassword,
10
12
  isValidDisplayName,
13
+ DISPLAY_NAME_ALLOWED_SCRIPTS,
14
+ DISPLAY_NAME_DISALLOWED_SOURCE,
15
+ DISPLAY_NAME_ORPHANED_MARK_SOURCE,
11
16
  isValidUUID,
12
17
  isValidDate,
13
18
  isValidFileSize,
@@ -176,15 +181,15 @@ describe('Validation Utils', () => {
176
181
  ['ᚠ', 'Runic'],
177
182
  ['Miguel de Icaza ᯅ', 'a Latin name with a trailing Batak letter'],
178
183
  ])('should return false for non-allowlisted-script letter %p (%s)', (name) => {
179
- // These characters are General_Category Lo (`\p{L}`), so the old
184
+ // These characters are General_Category Lo (letters), so the old
180
185
  // all-scripts policy accepted them; the curated script allowlist rejects
181
186
  // decorative / limited-use scripts a real name never uses.
182
187
  expect(isValidDisplayName(name)).toBe(false);
183
188
  });
184
189
 
185
190
  it('should return false for control whitespace (tab/newline/CR)', () => {
186
- // \p{Zs} (space separators only) rejects layout-breaking / multi-line
187
- // spoofing whitespace that \s would have admitted.
191
+ // Space separators only (General_Category Zs) rejects layout-breaking /
192
+ // multi-line spoofing whitespace that \s would have admitted.
188
193
  expect(isValidDisplayName('Ada\tLovelace')).toBe(false);
189
194
  expect(isValidDisplayName('Ada\nLovelace')).toBe(false);
190
195
  expect(isValidDisplayName('Ada\rLovelace')).toBe(false);
@@ -196,6 +201,104 @@ describe('Validation Utils', () => {
196
201
  });
197
202
  });
198
203
 
204
+ // Hermes (React Native) has Unicode property escapes compiled OUT: any such
205
+ // escape (Script_Extensions "scx=…" or General_Category classes) in a `u`-flag
206
+ // regex throws "Invalid RegExp: Invalid property name" at module load and
207
+ // crashes every Oxy RN/Expo app at boot. The display-name policy therefore
208
+ // ships explicit code-point RANGES instead. This block guards that invariant
209
+ // AND proves the range regexes behave identically.
210
+ describe('display-name policy is property-escape-free (Hermes safety)', () => {
211
+ const PROPERTY_ESCAPE = /\\[pP]\{/;
212
+
213
+ it('exposes runtime regex sources that contain NO Unicode property escape', () => {
214
+ expect(PROPERTY_ESCAPE.test(DISPLAY_NAME_ALLOWED_SCRIPTS)).toBe(false);
215
+ expect(PROPERTY_ESCAPE.test(DISPLAY_NAME_DISALLOWED_SOURCE)).toBe(false);
216
+ expect(PROPERTY_ESCAPE.test(DISPLAY_NAME_ORPHANED_MARK_SOURCE)).toBe(false);
217
+ });
218
+
219
+ it('only uses code-point escapes (\\x / \\u) in the class bodies', () => {
220
+ // regexpu-core lowers the property escapes to `\x…` / `\u…` / `\u{…}`
221
+ // code-point escapes. Every backslash-escape in the runtime sources must
222
+ // be one of those forms — never a property escape.
223
+ const escapeLeads = new Set<string>();
224
+ for (const src of [DISPLAY_NAME_DISALLOWED_SOURCE, DISPLAY_NAME_ORPHANED_MARK_SOURCE]) {
225
+ for (const [, lead] of src.matchAll(/\\(.)/g)) {
226
+ escapeLeads.add(lead);
227
+ }
228
+ }
229
+ expect([...escapeLeads].sort()).toEqual(['u', 'x']);
230
+ });
231
+
232
+ it('the shipped (non-test) policy source files contain NO property escape', () => {
233
+ const utilsDir = join(__dirname, '..');
234
+ for (const file of [
235
+ 'validationUtils.ts',
236
+ 'displayNamePolicyRanges.generated.ts',
237
+ 'textNormalization.ts',
238
+ ]) {
239
+ const contents = readFileSync(join(utilsDir, file), 'utf8');
240
+ expect(PROPERTY_ESCAPE.test(contents)).toBe(false);
241
+ }
242
+ });
243
+
244
+ // Build the actual regexes exactly as production does (range-only sources +
245
+ // `u` flag + lookbehind — the shape Hermes must accept) and assert the full
246
+ // policy behaves as before across scripts, marks, and rejections.
247
+ const disallowed = new RegExp(DISPLAY_NAME_DISALLOWED_SOURCE, 'u');
248
+ const orphaned = new RegExp(DISPLAY_NAME_ORPHANED_MARK_SOURCE, 'u');
249
+ const passesPolicy = (raw: string) =>
250
+ !disallowed.test(raw) && !orphaned.test(raw.normalize('NFC'));
251
+
252
+ it('constructs the `u`-flag regexes without throwing', () => {
253
+ expect(() => new RegExp(DISPLAY_NAME_DISALLOWED_SOURCE, 'u')).not.toThrow();
254
+ expect(() => new RegExp(DISPLAY_NAME_ORPHANED_MARK_SOURCE, 'u')).not.toThrow();
255
+ // Global variants are what @oxyhq/api compiles for the strip path.
256
+ expect(() => new RegExp(DISPLAY_NAME_DISALLOWED_SOURCE, 'gu')).not.toThrow();
257
+ expect(() => new RegExp(DISPLAY_NAME_ORPHANED_MARK_SOURCE, 'gu')).not.toThrow();
258
+ });
259
+
260
+ it.each([
261
+ ["Renée O'Brien", 'Latin with decomposed accent + apostrophe'],
262
+ ['Ada Lovelace', 'ASCII Latin'],
263
+ ['山田太郎', 'Han'],
264
+ ['田中\u{20000}', 'Han incl. astral CJK Extension B'],
265
+ ['Владимир', 'Cyrillic'],
266
+ ['مُحَمَد', 'Arabic with harakat (combining marks on base letters)'],
267
+ ['נתן', 'Hebrew'],
268
+ ['नमस्ते', 'Devanagari'],
269
+ ['김철수', 'Hangul'],
270
+ ['Αριστοτέλης', 'Greek'],
271
+ ['ᏔᎳ', 'Cherokee'],
272
+ ['ᠮᠣᠩᠭᠣᠯ', 'Mongolian'],
273
+ ])('range regexes ACCEPT allowlisted %p (%s)', (name) => {
274
+ expect(passesPolicy(name)).toBe(true);
275
+ // Parity with the public predicate.
276
+ expect(isValidDisplayName(name)).toBe(true);
277
+ });
278
+
279
+ it.each([
280
+ ['nixCraft \u{1f427}', 'emoji (astral)'],
281
+ ['Agent007', 'digit'],
282
+ ['Jean-Luc', 'hyphen'],
283
+ ['J.R.', 'dot'],
284
+ ['ᯅ', 'Batak (non-allowlisted script)'],
285
+ ['ᚠ', 'Runic (non-allowlisted script)'],
286
+ ['Ada\tLovelace', 'tab (control whitespace)'],
287
+ ['Ada\nLovelace', 'newline (control whitespace)'],
288
+ ['Ada\rLovelace', 'carriage return (control whitespace)'],
289
+ ])('range regexes REJECT %p (%s)', (name) => {
290
+ expect(passesPolicy(name)).toBe(false);
291
+ expect(isValidDisplayName(name)).toBe(false);
292
+ });
293
+
294
+ it('rejects an orphaned combining mark not riding a base letter', () => {
295
+ expect(passesPolicy('༘⋆')).toBe(false); // lone Tibetan mark + star
296
+ expect(orphaned.test('༘')).toBe(true); // bare mark at string start
297
+ // A decomposed accent recomposes under NFC and rides its base letter.
298
+ expect(orphaned.test('é'.normalize('NFC'))).toBe(false);
299
+ });
300
+ });
301
+
199
302
  describe('isValidUUID', () => {
200
303
  it('should return true for valid UUIDs', () => {
201
304
  expect(isValidUUID('123e4567-e89b-12d3-a456-426614174000')).toBe(true);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * GENERATED FILE — DO NOT EDIT BY HAND.
3
+ *
4
+ * Explicit Unicode code-point ranges for the display-name character policy,
5
+ * transpiled from the readable Script_Extensions / General_Category allowlist in
6
+ * `scripts/generateDisplayNamePolicyRanges.mjs` with regexpu-core (the same
7
+ * transform Babel uses to lower Unicode property escapes for Hermes targets).
8
+ *
9
+ * Each export is a character-class BODY (no surrounding `[]`) using only
10
+ * `\x…`/`\u…`/`\u{…}` code-point escapes — and ZERO Unicode property
11
+ * escapes — so the regexes built from them in `validationUtils.ts` run
12
+ * identically on V8 (web) and Hermes (React Native), whose engine has Unicode
13
+ * property escapes compiled OUT. A bare property escape at module load throws
14
+ * "Invalid RegExp: Invalid property name" and crashes every Oxy RN/Expo app at
15
+ * boot. The transpiled ranges are the exact same match set as the property
16
+ * escapes, verified across the full code-point space.
17
+ *
18
+ * Classes captured (regexpu-core, u-mode):
19
+ * - DISPLAY_NAME_ALLOWED_SCRIPTS_RANGES: the 30-script Script_Extensions
20
+ * allowlist (scx=Latin, scx=Greek, … scx=Han).
21
+ * - DISPLAY_NAME_COMBINING_MARKS_RANGES: General_Category M (combining marks).
22
+ * - DISPLAY_NAME_SPACE_SEPARATORS_RANGES: General_Category Zs (space
23
+ * separators).
24
+ * - DISPLAY_NAME_LETTERS_RANGES: General_Category L (letters of any script;
25
+ * used only in the orphaned-combining-mark lookbehind).
26
+ *
27
+ * REGENERATE: cd packages/core && bun run generate:display-name-policy
28
+ */
29
+
30
+ export const DISPLAY_NAME_ALLOWED_SCRIPTS_RANGES =
31
+ 'A-Za-z\\xAA\\xB7\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02B8\\u02BC\\u02C7\\u02C9-\\u02CB\\u02CD\\u02D7\\u02D9\\u02E0-\\u02E4\\u02EA\\u02EB\\u0300-\\u030E\\u0310\\u0311\\u0313\\u0323-\\u0325\\u032D\\u032E\\u0330\\u0331\\u0342\\u0345\\u0358\\u035E\\u0363-\\u0377\\u037A-\\u037D\\u037F\\u0384\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03E1\\u03F0-\\u052F\\u0531-\\u0556\\u0559-\\u058A\\u058D-\\u058F\\u0591-\\u05C7\\u05D0-\\u05EA\\u05EF-\\u05F4\\u0600-\\u0604\\u0606-\\u06DC\\u06DE-\\u06FF\\u0750-\\u07B1\\u0870-\\u0891\\u0897-\\u08E1\\u08E3-\\u0952\\u0955-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09FE\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A76\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AF1\\u0AF9-\\u0AFF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B55-\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B77\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BFA\\u0C00-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3C-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58-\\u0C5A\\u0C5C\\u0C5D\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C77-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDC-\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1-\\u0CF3\\u0D00-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4F\\u0D54-\\u0D63\\u0D66-\\u0D7F\\u0D81-\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2-\\u0DF4\\u0E01-\\u0E3A\\u0E40-\\u0E5B\\u0E81\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECE\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F97\\u0F99-\\u0FBC\\u0FBE-\\u0FCC\\u0FCE-\\u0FD4\\u0FD9\\u0FDA\\u1000-\\u10C5\\u10C7\\u10CD\\u10D0-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u137C\\u1380-\\u1399\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1780-\\u17DD\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1800-\\u1819\\u1820-\\u1878\\u1880-\\u18AA\\u19E0-\\u19FF\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CD0-\\u1CF9\\u1D00-\\u1DC1\\u1DF8\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FC4\\u1FC6-\\u1FD3\\u1FD6-\\u1FDB\\u1FDD-\\u1FEF\\u1FF2-\\u1FF4\\u1FF6-\\u1FFE\\u202F\\u204F\\u205A\\u205D\\u2071\\u207F\\u2090-\\u209C\\u20F0\\u2126\\u212A\\u212B\\u2132\\u214E\\u2160-\\u2188\\u2C60-\\u2C7F\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E17\\u2E31\\u2E41\\u2E43\\u2E80-\\u2E99\\u2E9B-\\u2EF3\\u2F00-\\u2FD5\\u2FF0-\\u2FFF\\u3001-\\u3003\\u3005-\\u3011\\u3013-\\u301F\\u302A-\\u3035\\u3021-\\u302D\\u3037-\\u303F\\u3041-\\u3096\\u3099-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u3190-\\u31E5\\u31EF-\\u321E\\u3220-\\u3247\\u3260-\\u327E\\u3280-\\u32B0\\u32C0-\\u32CB\\u32D0-\\u3370\\u337B-\\u337F\\u33E0-\\u33FE\\u3400-\\u4DBF\\u4E00-\\u9FFF\\uA640-\\uA69F\\uA700-\\uA707\\uA722-\\uA787\\uA78B-\\uA7DC\\uA7F1-\\uA7FF\\uA830-\\uA839\\uA8E0-\\uA8FF\\uA92E\\uA960-\\uA97C\\uA9E0-\\uA9FE\\uAA60-\\uAA7F\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABBF\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFDCF\\uFDF0-\\uFDFF\\uFE2E\\uFE2F\\uFE45\\uFE46\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF61-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10140}-\\u{1018E}\\u{101A0}\\u{102E0}-\\u{102FB}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10E60}-\\u{10E7E}\\u{10EC2}-\\u{10EC7}\\u{10ED0}-\\u{10ED8}\\u{10EFA}-\\u{10EFF}\\u{111E1}-\\u{111F4}\\u{11301}\\u{11303}\\u{1133B}\\u{1133C}\\u{11660}-\\u{1166C}\\u{116D0}-\\u{116E3}\\u{11B00}-\\u{11B09}\\u{11FC0}-\\u{11FF1}\\u{11FFF}\\u{16FE2}\\u{16FE3}\\u{16FF0}-\\u{16FF6}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1D200}-\\u{1D245}\\u{1D360}-\\u{1D371}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E08F}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{1EEF0}\\u{1EEF1}\\u{1F200}\\u{1F250}\\u{1F251}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}';
32
+
33
+ export const DISPLAY_NAME_COMBINING_MARKS_RANGES =
34
+ '\\u0300-\\u036F\\u0483-\\u0489\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065F\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u07FD\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0859-\\u085B\\u0897-\\u089F\\u08CA-\\u08E1\\u08E3-\\u0903\\u093A-\\u093C\\u093E-\\u094F\\u0951-\\u0957\\u0962\\u0963\\u0981-\\u0983\\u09BC\\u09BE-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CD\\u09D7\\u09E2\\u09E3\\u09FE\\u0A01-\\u0A03\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81-\\u0A83\\u0ABC\\u0ABE-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AE2\\u0AE3\\u0AFA-\\u0AFF\\u0B01-\\u0B03\\u0B3C\\u0B3E-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B55-\\u0B57\\u0B62\\u0B63\\u0B82\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD7\\u0C00-\\u0C04\\u0C3C\\u0C3E-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0C81-\\u0C83\\u0CBC\\u0CBE-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CE2\\u0CE3\\u0CF3\\u0D00-\\u0D03\\u0D3B\\u0D3C\\u0D3E-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4D\\u0D57\\u0D62\\u0D63\\u0D81-\\u0D83\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EBC\\u0EC8-\\u0ECE\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F3E\\u0F3F\\u0F71-\\u0F84\\u0F86\\u0F87\\u0F8D-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102B-\\u103E\\u1056-\\u1059\\u105E-\\u1060\\u1062-\\u1064\\u1067-\\u106D\\u1071-\\u1074\\u1082-\\u108D\\u108F\\u109A-\\u109D\\u135D-\\u135F\\u1712-\\u1715\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B4-\\u17D3\\u17DD\\u180B-\\u180D\\u180F\\u1885\\u1886\\u18A9\\u1920-\\u192B\\u1930-\\u193B\\u1A17-\\u1A1B\\u1A55-\\u1A5E\\u1A60-\\u1A7C\\u1A7F\\u1AB0-\\u1ADD\\u1AE0-\\u1AEB\\u1B00-\\u1B04\\u1B34-\\u1B44\\u1B6B-\\u1B73\\u1B80-\\u1B82\\u1BA1-\\u1BAD\\u1BE6-\\u1BF3\\u1C24-\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE8\\u1CED\\u1CF4\\u1CF7-\\u1CF9\\u1DC0-\\u1DFF\\u20D0-\\u20F0\\u2CEF-\\u2CF1\\u2D7F\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F-\\uA672\\uA674-\\uA67D\\uA69E\\uA69F\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA823-\\uA827\\uA82C\\uA880\\uA881\\uA8B4-\\uA8C5\\uA8E0-\\uA8F1\\uA8FF\\uA926-\\uA92D\\uA947-\\uA953\\uA980-\\uA983\\uA9B3-\\uA9C0\\uA9E5\\uAA29-\\uAA36\\uAA43\\uAA4C\\uAA4D\\uAA7B-\\uAA7D\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uAAEB-\\uAAEF\\uAAF5\\uAAF6\\uABE3-\\uABEA\\uABEC\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE2F\\u{101FD}\\u{102E0}\\u{10376}-\\u{1037A}\\u{10A01}-\\u{10A03}\\u{10A05}\\u{10A06}\\u{10A0C}-\\u{10A0F}\\u{10A38}-\\u{10A3A}\\u{10A3F}\\u{10AE5}\\u{10AE6}\\u{10D24}-\\u{10D27}\\u{10D69}-\\u{10D6D}\\u{10EAB}\\u{10EAC}\\u{10EFA}-\\u{10EFF}\\u{10F46}-\\u{10F50}\\u{10F82}-\\u{10F85}\\u{11000}-\\u{11002}\\u{11038}-\\u{11046}\\u{11070}\\u{11073}\\u{11074}\\u{1107F}-\\u{11082}\\u{110B0}-\\u{110BA}\\u{110C2}\\u{11100}-\\u{11102}\\u{11127}-\\u{11134}\\u{11145}\\u{11146}\\u{11173}\\u{11180}-\\u{11182}\\u{111B3}-\\u{111C0}\\u{111C9}-\\u{111CC}\\u{111CE}\\u{111CF}\\u{1122C}-\\u{11237}\\u{1123E}\\u{11241}\\u{112DF}-\\u{112EA}\\u{11300}-\\u{11303}\\u{1133B}\\u{1133C}\\u{1133E}-\\u{11344}\\u{11347}\\u{11348}\\u{1134B}-\\u{1134D}\\u{11357}\\u{11362}\\u{11363}\\u{11366}-\\u{1136C}\\u{11370}-\\u{11374}\\u{113B8}-\\u{113C0}\\u{113C2}\\u{113C5}\\u{113C7}-\\u{113CA}\\u{113CC}-\\u{113D0}\\u{113D2}\\u{113E1}\\u{113E2}\\u{11435}-\\u{11446}\\u{1145E}\\u{114B0}-\\u{114C3}\\u{115AF}-\\u{115B5}\\u{115B8}-\\u{115C0}\\u{115DC}\\u{115DD}\\u{11630}-\\u{11640}\\u{116AB}-\\u{116B7}\\u{1171D}-\\u{1172B}\\u{1182C}-\\u{1183A}\\u{11930}-\\u{11935}\\u{11937}\\u{11938}\\u{1193B}-\\u{1193E}\\u{11940}\\u{11942}\\u{11943}\\u{119D1}-\\u{119D7}\\u{119DA}-\\u{119E0}\\u{119E4}\\u{11A01}-\\u{11A0A}\\u{11A33}-\\u{11A39}\\u{11A3B}-\\u{11A3E}\\u{11A47}\\u{11A51}-\\u{11A5B}\\u{11A8A}-\\u{11A99}\\u{11B60}-\\u{11B67}\\u{11C2F}-\\u{11C36}\\u{11C38}-\\u{11C3F}\\u{11C92}-\\u{11CA7}\\u{11CA9}-\\u{11CB6}\\u{11D31}-\\u{11D36}\\u{11D3A}\\u{11D3C}\\u{11D3D}\\u{11D3F}-\\u{11D45}\\u{11D47}\\u{11D8A}-\\u{11D8E}\\u{11D90}\\u{11D91}\\u{11D93}-\\u{11D97}\\u{11EF3}-\\u{11EF6}\\u{11F00}\\u{11F01}\\u{11F03}\\u{11F34}-\\u{11F3A}\\u{11F3E}-\\u{11F42}\\u{11F5A}\\u{13440}\\u{13447}-\\u{13455}\\u{1611E}-\\u{1612F}\\u{16AF0}-\\u{16AF4}\\u{16B30}-\\u{16B36}\\u{16F4F}\\u{16F51}-\\u{16F87}\\u{16F8F}-\\u{16F92}\\u{16FE4}\\u{16FF0}\\u{16FF1}\\u{1BC9D}\\u{1BC9E}\\u{1CF00}-\\u{1CF2D}\\u{1CF30}-\\u{1CF46}\\u{1D165}-\\u{1D169}\\u{1D16D}-\\u{1D172}\\u{1D17B}-\\u{1D182}\\u{1D185}-\\u{1D18B}\\u{1D1AA}-\\u{1D1AD}\\u{1D242}-\\u{1D244}\\u{1DA00}-\\u{1DA36}\\u{1DA3B}-\\u{1DA6C}\\u{1DA75}\\u{1DA84}\\u{1DA9B}-\\u{1DA9F}\\u{1DAA1}-\\u{1DAAF}\\u{1E000}-\\u{1E006}\\u{1E008}-\\u{1E018}\\u{1E01B}-\\u{1E021}\\u{1E023}\\u{1E024}\\u{1E026}-\\u{1E02A}\\u{1E08F}\\u{1E130}-\\u{1E136}\\u{1E2AE}\\u{1E2EC}-\\u{1E2EF}\\u{1E4EC}-\\u{1E4EF}\\u{1E5EE}\\u{1E5EF}\\u{1E6E3}\\u{1E6E6}\\u{1E6EE}\\u{1E6EF}\\u{1E6F5}\\u{1E8D0}-\\u{1E8D6}\\u{1E944}-\\u{1E94A}\\u{E0100}-\\u{E01EF}';
35
+
36
+ export const DISPLAY_NAME_SPACE_SEPARATORS_RANGES =
37
+ ' \\xA0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000';
38
+
39
+ export const DISPLAY_NAME_LETTERS_RANGES =
40
+ 'A-Za-z\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C\\u0C5D\\u0C60\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}\\u{16FE1}\\u{16FE3}\\u{16FF2}\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}\\u{1D49F}\\u{1D4A2}\\u{1D4A5}\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}';
@@ -52,11 +52,16 @@ const ANY_WHITESPACE_RUN = /\s+/g;
52
52
  * SEPARATOR (U+2028) / PARAGRAPH SEPARATOR (U+2029), which are mandatory breaks
53
53
  * in Unicode and a well-known hazard in JSON/JS payloads. CRLF must be matched
54
54
  * before the lone `\r` alternative or it would yield two breaks. The separators
55
- * are matched by Unicode property (`\p{Zl}` = U+2028, `\p{Zp}` = U+2029) rather
56
- * than as literals: a literal U+2028/U+2029 is a LineTerminator and cannot appear
57
- * inside a regex literal at all.
55
+ * are matched by their code-point ESCAPES (`\u2028` = LINE SEPARATOR, `\u2029` =
56
+ * PARAGRAPH SEPARATOR) rather than by their Unicode Line_Separator /
57
+ * Paragraph_Separator property escapes: React Native's Hermes engine ships with
58
+ * Unicode property escapes compiled OUT and rejects those subcategory property
59
+ * names with "Invalid RegExp: Invalid property name", whereas the `\uXXXX`
60
+ * escapes are exactly equivalent (each property covers exactly its one code
61
+ * point), engine-portable, and drop the now-unneeded `u` flag. (A literal
62
+ * U+2028/U+2029 is itself a LineTerminator and cannot appear in a regex literal.)
58
63
  */
59
- const LINE_BREAK_FORMS = /\r\n|\r|\p{Zl}|\p{Zp}/gu;
64
+ const LINE_BREAK_FORMS = /\r\n|\r|\u2028|\u2029/g;
60
65
 
61
66
  /**
62
67
  * A run of HORIZONTAL whitespace: any whitespace that is not a line break.
@@ -2,6 +2,13 @@
2
2
  * Validation utilities for common data validation patterns
3
3
  */
4
4
 
5
+ import {
6
+ DISPLAY_NAME_ALLOWED_SCRIPTS_RANGES,
7
+ DISPLAY_NAME_COMBINING_MARKS_RANGES,
8
+ DISPLAY_NAME_SPACE_SEPARATORS_RANGES,
9
+ DISPLAY_NAME_LETTERS_RANGES,
10
+ } from './displayNamePolicyRanges.generated';
11
+
5
12
  /**
6
13
  * Email validation regex
7
14
  */
@@ -44,15 +51,16 @@ export function isValidPassword(password: string): boolean {
44
51
  *
45
52
  * A clean display name is composed ONLY of:
46
53
  * - letters from a curated ALLOWLIST of scripts that real names use
47
- * ({@link DISPLAY_NAME_ALLOWED_SCRIPTS}) — NOT `\p{L}` (letters of ANY
48
- * script), which admits decorative / historic / limited-use scripts whose
49
- * characters are `\p{L}` yet never appear in a real name (e.g. `ᯅ` U+1BC5
50
- * Batak, Runic, Deseret, dingbat letters),
51
- * - combining marks / accents (`\p{M}`, e.g. the acute accent in a decomposed
52
- * "é"),
53
- * - Unicode space separators (`\p{Zs}`: the ASCII space, NBSP, ideographic
54
- * space, …) — but NOT control whitespace such as tab, newline, or carriage
55
- * return, which would break layout or enable multi-line spoofing,
54
+ * ({@link DISPLAY_NAME_ALLOWED_SCRIPTS}) — NOT every letter (General_Category
55
+ * L of ANY script), which admits decorative / historic / limited-use scripts
56
+ * whose characters are letters yet never appear in a real name (e.g. `ᯅ`
57
+ * U+1BC5 Batak, Runic, Deseret, dingbat letters),
58
+ * - combining marks / accents (General_Category M, e.g. the acute accent in a
59
+ * decomposed "é"),
60
+ * - Unicode space separators (General_Category Zs: the ASCII space, NBSP,
61
+ * ideographic space, …) — but NOT control whitespace such as tab, newline,
62
+ * or carriage return, which would break layout or enable multi-line
63
+ * spoofing,
56
64
  * - the straight apostrophe (`'`, e.g. "O'Brien").
57
65
  *
58
66
  * Everything else is rejected: emoji (🐧), symbols (⁂ ⏚), `:emoji:` shortcodes,
@@ -61,15 +69,26 @@ export function isValidPassword(password: string): boolean {
61
69
  * includes `<`, `>`, `&`, or `"`, so a value that passes this predicate can
62
70
  * never contain an HTML/XSS vector.
63
71
  *
64
- * The allowlist is expressed with Unicode Script_Extensions (`\p{scx=…}`)
65
- * escapes so a letter shared by several scripts (e.g. a Han ideograph used in
66
- * both Chinese and Japanese) still matches. It is the set of scripts Unicode
67
- * UTS #39 marks "Recommended" for general interchange / identifiers, plus
68
- * Cherokee and Mongolian (both in real modern name use). "Common" script is
69
- * deliberately EXCLUDED — that is where ASCII digits and general punctuation
70
- * live, and this policy excludes those; the space separators, combining marks,
71
- * and apostrophe a name needs are added back explicitly. Limited-use / excluded
72
- * / historic scripts (Batak, Runic, Deseret, Adlam, …) are simply absent.
72
+ * The allowlist is expressed with Unicode Script_Extensions (scx=…) so a letter
73
+ * shared by several scripts (e.g. a Han ideograph used in both Chinese and
74
+ * Japanese) still matches. It is the set of scripts Unicode UTS #39 marks
75
+ * "Recommended" for general interchange / identifiers, plus Cherokee and
76
+ * Mongolian (both in real modern name use). "Common" script is deliberately
77
+ * EXCLUDED — that is where ASCII digits and general punctuation live, and this
78
+ * policy excludes those; the space separators, combining marks, and apostrophe a
79
+ * name needs are added back explicitly. Limited-use / excluded / historic
80
+ * scripts (Batak, Runic, Deseret, Adlam, …) are simply absent.
81
+ *
82
+ * HERMES / RANGES: the class bodies below are built from explicit Unicode
83
+ * code-point RANGES ({@link DISPLAY_NAME_ALLOWED_SCRIPTS_RANGES} et al. from
84
+ * `./displayNamePolicyRanges.generated`), NOT from `scx`/General_Category
85
+ * property escapes. React Native's Hermes engine ships with Unicode property
86
+ * escapes compiled OUT and throws "Invalid RegExp: Invalid property name" for
87
+ * any such escape at module load, crashing every Oxy RN/Expo app at boot. The
88
+ * ranges are the compressed union of exactly those properties (generated on
89
+ * Node/V8, which supports them — see `scripts/generateDisplayNamePolicyRanges.mjs`),
90
+ * so behavior is IDENTICAL on V8 (web) and Hermes (native) with the same `u`
91
+ * flag and lookbehind, but with zero property escapes in the shipped regex.
73
92
  *
74
93
  * This is the SINGLE definition of the policy: the character-class sources below
75
94
  * are the ONE source of truth, shared between the API strip/gate
@@ -81,40 +100,34 @@ export function isValidPassword(password: string): boolean {
81
100
 
82
101
  /**
83
102
  * The curated allowlist of Unicode scripts permitted in a display name, as a
84
- * character-class body of Script_Extensions (`scx`) property escapes. Ordered by
85
- * rough script family for readability; order has no semantic effect.
103
+ * character-class body of explicit code-point ranges (the compressed union of
104
+ * the 30 allowlisted Script_Extensions, generated on V8). Interpolated into the
105
+ * negated class below.
86
106
  */
87
- export const DISPLAY_NAME_ALLOWED_SCRIPTS =
88
- '\\p{scx=Latin}\\p{scx=Greek}\\p{scx=Cyrillic}\\p{scx=Armenian}' +
89
- '\\p{scx=Hebrew}\\p{scx=Arabic}\\p{scx=Thaana}\\p{scx=Devanagari}' +
90
- '\\p{scx=Bengali}\\p{scx=Gurmukhi}\\p{scx=Gujarati}\\p{scx=Oriya}' +
91
- '\\p{scx=Tamil}\\p{scx=Telugu}\\p{scx=Kannada}\\p{scx=Malayalam}' +
92
- '\\p{scx=Sinhala}\\p{scx=Thai}\\p{scx=Lao}\\p{scx=Tibetan}' +
93
- '\\p{scx=Myanmar}\\p{scx=Georgian}\\p{scx=Hangul}\\p{scx=Ethiopic}' +
94
- '\\p{scx=Cherokee}\\p{scx=Khmer}\\p{scx=Mongolian}\\p{scx=Hiragana}' +
95
- '\\p{scx=Katakana}\\p{scx=Bopomofo}\\p{scx=Han}';
107
+ export const DISPLAY_NAME_ALLOWED_SCRIPTS = DISPLAY_NAME_ALLOWED_SCRIPTS_RANGES;
96
108
 
97
109
  /**
98
110
  * Source of the disallowed-character pattern: the negation of the full allowed
99
- * set (allowlisted scripts + combining marks `\p{M}` + space separators `\p{Zs}`
100
- * + the straight apostrophe). Consumers compile this with the `u` flag (and `g`
101
- * for a global strip). The whitespace class is `\p{Zs}` (space separators only),
111
+ * set (allowlisted scripts + combining marks + space separators + the straight
112
+ * apostrophe). Consumers compile this with the `u` flag (and `g` for a global
113
+ * strip). The whitespace class is space separators only (General_Category Zs),
102
114
  * NOT `\s` — the latter would admit tab/newline/carriage return, which break
103
115
  * layout and enable multi-line spoofing.
104
116
  */
105
- export const DISPLAY_NAME_DISALLOWED_SOURCE = `[^${DISPLAY_NAME_ALLOWED_SCRIPTS}\\p{M}\\p{Zs}']`;
117
+ export const DISPLAY_NAME_DISALLOWED_SOURCE = `[^${DISPLAY_NAME_ALLOWED_SCRIPTS}${DISPLAY_NAME_COMBINING_MARKS_RANGES}${DISPLAY_NAME_SPACE_SEPARATORS_RANGES}']`;
106
118
 
107
119
  /**
108
- * Source of the orphaned combining-mark pattern: a run of `\p{M}` NOT attached
109
- * to a base letter (preceded by string start, whitespace, the apostrophe, or a
110
- * position vacated by a stripped character). A mark preceded by `\p{L}` (a base
111
- * letter, e.g. the decomposed accent in "Renée") or by another `\p{M}` (a
112
- * multi-mark cluster) is NOT matched because the negative lookbehind fails at its
113
- * position. Used both as a non-global probe (`.test`) and, with the `g` flag, to
114
- * strip whole orphaned runs. The lookbehind intentionally still uses the broad
115
- * `\p{L}` so that a mark riding on an allowlisted base letter is preserved.
120
+ * Source of the orphaned combining-mark pattern: a run of combining marks NOT
121
+ * attached to a base letter (preceded by string start, whitespace, the
122
+ * apostrophe, or a position vacated by a stripped character). A mark preceded by
123
+ * a base letter (e.g. the decomposed accent in "Renée") or by another combining
124
+ * mark (a multi-mark cluster) is NOT matched because the negative lookbehind
125
+ * fails at its position. Used both as a non-global probe (`.test`) and, with the
126
+ * `g` flag, to strip whole orphaned runs. The lookbehind intentionally uses the
127
+ * BROAD letters set (General_Category L of any script) so that a mark riding on
128
+ * an allowlisted base letter is preserved.
116
129
  */
117
- export const DISPLAY_NAME_ORPHANED_MARK_SOURCE = '(?<![\\p{L}\\p{M}])\\p{M}+';
130
+ export const DISPLAY_NAME_ORPHANED_MARK_SOURCE = `(?<![${DISPLAY_NAME_LETTERS_RANGES}${DISPLAY_NAME_COMBINING_MARKS_RANGES}])[${DISPLAY_NAME_COMBINING_MARKS_RANGES}]+`;
118
131
 
119
132
  /** Non-global probe for the presence of a disallowed character. */
120
133
  const DISALLOWED_PROBE = new RegExp(DISPLAY_NAME_DISALLOWED_SOURCE, 'u');