text-sanctifier 1.3.5 → 1.3.7

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.
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { CONTROL_CHARS_REGEX, INVISIBLE_TRASH_REGEX } from './categories.generated.js';
4
4
  export { CONTROL_CHARS_REGEX, INVISIBLE_TRASH_REGEX };
5
+ import { ASCII_KEYBOARD_SAFE_REGEX, EMOJI_REGEX, normalizeTypographicJank } from './textPatterns.js';
6
+ export { ASCII_KEYBOARD_SAFE_REGEX, EMOJI_REGEX, normalizeTypographicJank };
5
7
 
6
8
 
7
9
  /**
@@ -45,7 +47,7 @@ export { CONTROL_CHARS_REGEX, INVISIBLE_TRASH_REGEX };
45
47
  * @param {boolean} [defaultOptions.purgeZeroWidthSpaces]
46
48
  * @returns {(text: string) => string}
47
49
  */
48
- export function summonSanctifier(defaultOptions = {}) {
50
+ function createSanctifier(defaultOptions = {}) {
49
51
  const normalizeSpaces = !!defaultOptions.normalizeSpaces;
50
52
  const purgeZeroWidthSpaces = !!defaultOptions.purgeZeroWidthSpaces;
51
53
  const purgeInvisibleChars = !!defaultOptions.purgeInvisibleChars;
@@ -91,7 +93,10 @@ export function summonSanctifier(defaultOptions = {}) {
91
93
  );
92
94
  }
93
95
 
94
- // --- Added Presets ---
96
+ // --- Presets ---
97
+ //
98
+ // Each preset is its own export, so a bundle that imports one preset leaves out the
99
+ // others. summonSanctifier.strict and the rest are the same functions.
95
100
 
96
101
  /**
97
102
  * Strict sanitizer:
@@ -99,21 +104,24 @@ export function summonSanctifier(defaultOptions = {}) {
99
104
  * - Collapse all newlines
100
105
  * - Collapse spaces
101
106
  * - Nuke control characters
107
+ * @param {string} text
108
+ * @returns {string}
102
109
  */
103
- /** @param {string} text @returns {string} */
104
- summonSanctifier.strict = text => sanctifyText(
105
- text,
106
- true, // purgeInvisibleChars
107
- true, // purgeEmojis
108
- true, // nukeControls
109
- false, // keyboardOnlyFilter
110
- true, // normalizeNewlines
111
- true, // trimSpacesAroundNewlines
112
- true, // collapseNewLines
113
- false, // preserveParagraphs
114
- true, // collapseSpaces
115
- true // finalTrim
116
- );
110
+ export function sanctifyStrict(text) {
111
+ return sanctifyText(
112
+ text,
113
+ true, // purgeInvisibleChars
114
+ true, // purgeEmojis
115
+ true, // nukeControls
116
+ false, // keyboardOnlyFilter
117
+ true, // normalizeNewlines
118
+ true, // trimSpacesAroundNewlines
119
+ true, // collapseNewLines
120
+ false, // preserveParagraphs
121
+ true, // collapseSpaces
122
+ true // finalTrim
123
+ );
124
+ }
117
125
 
118
126
 
119
127
  /**
@@ -121,21 +129,24 @@ summonSanctifier.strict = text => sanctifyText(
121
129
  * - Collapse spaces
122
130
  * - Preserve paragraphs
123
131
  * - Normalize newlines
132
+ * @param {string} text
133
+ * @returns {string}
124
134
  */
125
- /** @param {string} text @returns {string} */
126
- summonSanctifier.loose = text => sanctifyText(
127
- text,
128
- false, // purgeInvisibleChars
129
- false, // purgeEmojis
130
- false, // nukeControls
131
- false, // keyboardOnlyFilter
132
- true, // normalizeNewlines
133
- true, // trimSpacesAroundNewlines
134
- true, // collapseNewLines
135
- true, // preserveParagraphs
136
- true, // collapseSpaces
137
- true // finalTrim
138
- );
135
+ export function sanctifyLoose(text) {
136
+ return sanctifyText(
137
+ text,
138
+ false, // purgeInvisibleChars
139
+ false, // purgeEmojis
140
+ false, // nukeControls
141
+ false, // keyboardOnlyFilter
142
+ true, // normalizeNewlines
143
+ true, // trimSpacesAroundNewlines
144
+ true, // collapseNewLines
145
+ true, // preserveParagraphs
146
+ true, // collapseSpaces
147
+ true // finalTrim
148
+ );
149
+ }
139
150
 
140
151
 
141
152
  /**
@@ -143,21 +154,24 @@ summonSanctifier.loose = text => sanctifyText(
143
154
  * - Keeps emojis and printable ASCII
144
155
  * - Strips non-standard characters
145
156
  * - Normalizes typographic trash
157
+ * @param {string} text
158
+ * @returns {string}
146
159
  */
147
- /** @param {string} text @returns {string} */
148
- summonSanctifier.keyboardOnlyEmoji = text => sanctifyText(
149
- text,
150
- false, // purgeInvisibleChars
151
- false, // purgeEmojis
152
- false, // nukeControls
153
- true, // keyboardOnlyFilter
154
- true, // normalizeNewlines
155
- true, // trimSpacesAroundNewlines
156
- false, // collapseNewLines
157
- false, // preserveParagraphs
158
- false, // collapseSpaces
159
- true // finalTrim
160
- );
160
+ export function sanctifyKeyboardOnlyEmoji(text) {
161
+ return sanctifyText(
162
+ text,
163
+ false, // purgeInvisibleChars
164
+ false, // purgeEmojis
165
+ false, // nukeControls
166
+ true, // keyboardOnlyFilter
167
+ true, // normalizeNewlines
168
+ true, // trimSpacesAroundNewlines
169
+ false, // collapseNewLines
170
+ false, // preserveParagraphs
171
+ false, // collapseSpaces
172
+ true // finalTrim
173
+ );
174
+ }
161
175
 
162
176
 
163
177
  /**
@@ -165,21 +179,24 @@ summonSanctifier.keyboardOnlyEmoji = text => sanctifyText(
165
179
  * - Removes emojis
166
180
  * - Collapses all whitespace
167
181
  * - Restricts to printable ASCII only
182
+ * @param {string} text
183
+ * @returns {string}
168
184
  */
169
- /** @param {string} text @returns {string} */
170
- summonSanctifier.keyboardOnly = text => sanctifyText(
171
- text,
172
- true, // purgeInvisibleChars
173
- true, // purgeEmojis
174
- true, // nukeControls
175
- true, // keyboardOnlyFilter
176
- true, // normalizeNewlines
177
- true, // trimSpacesAroundNewlines
178
- true, // collapseNewLines
179
- false, // preserveParagraphs
180
- true, // collapseSpaces
181
- true // finalTrim
182
- );
185
+ export function sanctifyKeyboardOnly(text) {
186
+ return sanctifyText(
187
+ text,
188
+ true, // purgeInvisibleChars
189
+ true, // purgeEmojis
190
+ true, // nukeControls
191
+ true, // keyboardOnlyFilter
192
+ true, // normalizeNewlines
193
+ true, // trimSpacesAroundNewlines
194
+ true, // collapseNewLines
195
+ false, // preserveParagraphs
196
+ true, // collapseSpaces
197
+ true // finalTrim
198
+ );
199
+ }
183
200
 
184
201
 
185
202
  /**
@@ -189,25 +206,70 @@ summonSanctifier.keyboardOnly = text => sanctifyText(
189
206
  * - Zero-width spaces, word joiners and BOMs are removed
190
207
  * - Newlines are normalized to `\n`
191
208
  * Keeps emoji joiners, variation selectors, bidi marks, indentation and line breaks.
209
+ * @param {string} text
210
+ * @returns {string}
192
211
  */
193
- /** @param {string} text @returns {string} */
194
- summonSanctifier.pasted = text => sanctifyText(
195
- text,
196
- false, // purgeInvisibleChars
197
- false, // purgeEmojis
198
- false, // nukeControls
199
- false, // keyboardOnlyFilter
200
- true, // normalizeNewlines
201
- false, // trimSpacesAroundNewlines
202
- false, // collapseNewLines
203
- false, // preserveParagraphs
204
- false, // collapseSpaces
205
- false, // finalTrim
206
- Infinity, // maxLength
207
- 'throw', // onMaxLength
208
- true, // normalizeSpaces
209
- true // purgeZeroWidthSpaces
210
- );
212
+ export function sanctifyPasted(text) {
213
+ return sanctifyText(
214
+ text,
215
+ false, // purgeInvisibleChars
216
+ false, // purgeEmojis
217
+ false, // nukeControls
218
+ false, // keyboardOnlyFilter
219
+ true, // normalizeNewlines
220
+ false, // trimSpacesAroundNewlines
221
+ false, // collapseNewLines
222
+ false, // preserveParagraphs
223
+ false, // collapseSpaces
224
+ false, // finalTrim
225
+ Infinity, // maxLength
226
+ 'throw', // onMaxLength
227
+ true, // normalizeSpaces
228
+ true // purgeZeroWidthSpaces
229
+ );
230
+ }
231
+
232
+
233
+ /**
234
+ * Summons a customized sanctifier function with pre-bound booleans.
235
+ *
236
+ * Accepts full flag names and returns a text-cleaning function.
237
+ *
238
+ * @param {Object} [defaultOptions={}]
239
+ * @param {boolean} [defaultOptions.purgeInvisibleChars]
240
+ * @param {boolean} [defaultOptions.purgeEmojis]
241
+ * @param {boolean} [defaultOptions.nukeControls]
242
+ * @param {boolean} [defaultOptions.keyboardOnlyFilter]
243
+ * @param {boolean} [defaultOptions.normalizeNewlines]
244
+ * @param {boolean} [defaultOptions.trimSpacesAroundNewlines]
245
+ * @param {boolean} [defaultOptions.collapseNewLines]
246
+ * @param {boolean} [defaultOptions.preserveParagraphs]
247
+ * @param {boolean} [defaultOptions.collapseSpaces]
248
+ * @param {boolean} [defaultOptions.finalTrim]
249
+ * @param {number} [defaultOptions.maxLength=Infinity] - Hard input length cap (UTF-16 code units).
250
+ * @param {'throw' | 'truncate' | 'noop'} [defaultOptions.onMaxLength='throw'] - Behavior when input exceeds maxLength.
251
+ * @param {boolean} [defaultOptions.normalizeSpaces]
252
+ * @param {boolean} [defaultOptions.purgeZeroWidthSpaces]
253
+ * @returns {(text: string) => string}
254
+ *
255
+ * The presets are attached as properties (`summonSanctifier.strict`, `.loose`,
256
+ * `.keyboardOnlyEmoji`, `.keyboardOnly`, `.pasted`) and are also standalone exports.
257
+ * Built in a call marked pure, so a bundle that imports only a preset leaves it out.
258
+ * @type {typeof createSanctifier & {
259
+ * strict: typeof sanctifyStrict,
260
+ * loose: typeof sanctifyLoose,
261
+ * keyboardOnlyEmoji: typeof sanctifyKeyboardOnlyEmoji,
262
+ * keyboardOnly: typeof sanctifyKeyboardOnly,
263
+ * pasted: typeof sanctifyPasted,
264
+ * }}
265
+ */
266
+ export const summonSanctifier = /* @__PURE__ */ Object.assign(createSanctifier, {
267
+ strict: sanctifyStrict,
268
+ loose: sanctifyLoose,
269
+ keyboardOnlyEmoji: sanctifyKeyboardOnlyEmoji,
270
+ keyboardOnly: sanctifyKeyboardOnly,
271
+ pasted: sanctifyPasted,
272
+ });
211
273
 
212
274
 
213
275
  /**
@@ -374,13 +436,6 @@ function extractEmojiSequences(text) {
374
436
  return out;
375
437
  }
376
438
 
377
- /**
378
- * Matches any character that is NOT:
379
- * - Printable ASCII (U+0020–U+007E)
380
- * - Newline characters: \n (LF), \r (CR)
381
- * This allows for \n, \r, and \r\n line endings.
382
- */
383
- export const ASCII_KEYBOARD_SAFE_REGEX = /[^\x20-\x7E\r\n]+/gu;
384
439
 
385
440
  /**
386
441
  * Removes all non-keyboard characters, preserving:
@@ -402,64 +457,6 @@ function purgeNonKeyboardChars(text, purgeEmojis = false) {
402
457
  }
403
458
 
404
459
 
405
- // Smart apostrophes (single quote variations)
406
- const SMART_SINGLE_QUOTES_REGEX = /[\u2018\u2019\u201A\u201B\u2032\u2035]/g;
407
-
408
- // Smart double quotes (including primes and guillemets)
409
- const SMART_DOUBLE_QUOTES_REGEX = /[\u201C\u201D\u201E\u201F\u2033\u2036\u00AB\u00BB]/g;
410
-
411
- // Dash variants: en dash, em dash, horizontal bar, figure dash, minus
412
- const UNICODE_DASHES_REGEX = /[\u2012\u2013\u2014\u2015\u2212]/g;
413
-
414
- // Ellipsis character
415
- const ELLIPSIS_REGEX = /\u2026/g;
416
-
417
- // Bullets and middle dot
418
- const BULLETS_REGEX = /[\u2022\u00B7]/g;
419
-
420
- // Full-width ASCII punctuation: U+FF01 - U+FF5E
421
- const FULLWIDTH_PUNCTUATION_REGEX = /[\uFF01-\uFF5E]/g;
422
-
423
- /**
424
- * Normalizes typographic Unicode punctuation into ASCII equivalents.
425
- * @param {string} text
426
- * @returns {string}
427
- */
428
- export function normalizeTypographicJank(text) {
429
- return text
430
- .replace(SMART_SINGLE_QUOTES_REGEX, "'")
431
- .replace(SMART_DOUBLE_QUOTES_REGEX, '"')
432
- .replace(UNICODE_DASHES_REGEX, '-')
433
- .replace(ELLIPSIS_REGEX, '...')
434
- .replace(BULLETS_REGEX, '*')
435
- .replace(FULLWIDTH_PUNCTUATION_REGEX, m =>
436
- String.fromCharCode(m.charCodeAt(0) - 0xFEE0)
437
- );
438
- }
439
-
440
-
441
- /** @type {RegExp} */
442
- export let EMOJI_REGEX;
443
-
444
- /**
445
- * Try Unicode property escape regex (preferred).
446
- * Fallback to basic emoji ranges if unsupported.
447
- */
448
- try {
449
- EMOJI_REGEX = new RegExp(
450
- '(?:\\p{Extended_Pictographic}(?:\\uFE0F|\\uFE0E)?(?:\\u200D(?:\\p{Extended_Pictographic}|\\w)+)*)',
451
- 'gu'
452
- );
453
- } catch {
454
- // Fallback: wide-range emoji component match (flags, tones, symbols)
455
- // * Covers:
456
- // * - Emoji base chars (1F300–1FAFF)
457
- // * - Dingbats (2700–27BF) like ❌, ✅, ☑️
458
- // * - Skin tones (1F3FB–1F3FF)
459
- // * - ZWJ (200D), variation selectors (FE0F)
460
- // * - Regional indicators (1F1E6–1F1FF)
461
- EMOJI_REGEX = /[\u{2700}-\u{27BF}\u{1F300}-\u{1FAFF}\u{1F3FB}-\u{1F3FF}\u200D\uFE0F\u{1F1E6}-\u{1F1FF}]/gu;
462
- }
463
460
 
464
461
  /**
465
462
  * Removes all emoji characters using Unicode property escapes.
@@ -0,0 +1,78 @@
1
+ // src/textPatterns.js
2
+ //
3
+ // Patterns shared by the inspector and the sanitizer. They live apart from
4
+ // sanctifyText.js so that importing an inspector does not pull in the sanitizer
5
+ // engine and its presets (see the size probes in scripts/check-size.js).
6
+
7
+
8
+ /**
9
+ * Matches any character that is NOT:
10
+ * - Printable ASCII (U+0020–U+007E)
11
+ * - Newline characters: \n (LF), \r (CR)
12
+ * This allows for \n, \r, and \r\n line endings.
13
+ */
14
+ export const ASCII_KEYBOARD_SAFE_REGEX = /[^\x20-\x7E\r\n]+/gu;
15
+
16
+
17
+ // Smart apostrophes (single quote variations)
18
+ const SMART_SINGLE_QUOTES_REGEX = /[\u2018\u2019\u201A\u201B\u2032\u2035]/g;
19
+
20
+ // Smart double quotes (including primes and guillemets)
21
+ const SMART_DOUBLE_QUOTES_REGEX = /[\u201C\u201D\u201E\u201F\u2033\u2036\u00AB\u00BB]/g;
22
+
23
+ // Dash variants: en dash, em dash, horizontal bar, figure dash, minus
24
+ const UNICODE_DASHES_REGEX = /[\u2012\u2013\u2014\u2015\u2212]/g;
25
+
26
+ // Ellipsis character
27
+ const ELLIPSIS_REGEX = /\u2026/g;
28
+
29
+ // Bullets and middle dot
30
+ const BULLETS_REGEX = /[\u2022\u00B7]/g;
31
+
32
+ // Full-width ASCII punctuation: U+FF01 - U+FF5E
33
+ const FULLWIDTH_PUNCTUATION_REGEX = /[\uFF01-\uFF5E]/g;
34
+
35
+ /**
36
+ * Normalizes typographic Unicode punctuation into ASCII equivalents.
37
+ * @param {string} text
38
+ * @returns {string}
39
+ */
40
+ export function normalizeTypographicJank(text) {
41
+ return text
42
+ .replace(SMART_SINGLE_QUOTES_REGEX, "'")
43
+ .replace(SMART_DOUBLE_QUOTES_REGEX, '"')
44
+ .replace(UNICODE_DASHES_REGEX, '-')
45
+ .replace(ELLIPSIS_REGEX, '...')
46
+ .replace(BULLETS_REGEX, '*')
47
+ .replace(FULLWIDTH_PUNCTUATION_REGEX, m =>
48
+ String.fromCharCode(m.charCodeAt(0) - 0xFEE0)
49
+ );
50
+ }
51
+
52
+
53
+ /**
54
+ * Try Unicode property escape regex (preferred).
55
+ * Fallback to basic emoji ranges if unsupported.
56
+ * Built in a function marked pure, so bundlers drop it when nothing uses it.
57
+ * @returns {RegExp}
58
+ */
59
+ function buildEmojiRegex() {
60
+ try {
61
+ return new RegExp(
62
+ '(?:\\p{Extended_Pictographic}(?:\\uFE0F|\\uFE0E)?(?:\\u200D(?:\\p{Extended_Pictographic}|\\w)+)*)',
63
+ 'gu'
64
+ );
65
+ } catch {
66
+ // Fallback: wide-range emoji component match (flags, tones, symbols)
67
+ // * Covers:
68
+ // * - Emoji base chars (1F300–1FAFF)
69
+ // * - Dingbats (2700–27BF) like ❌, ✅, ☑
70
+ // * - Skin tones (1F3FB–1F3FF)
71
+ // * - ZWJ (200D), variation selectors (FE0F)
72
+ // * - Regional indicators (1F1E6–1F1FF)
73
+ return /[\u{2700}-\u{27BF}\u{1F300}-\u{1FAFF}\u{1F3FB}-\u{1F3FF}\u200D\uFE0F\u{1F1E6}-\u{1F1FF}]/gu;
74
+ }
75
+ }
76
+
77
+ /** @type {RegExp} */
78
+ export const EMOJI_REGEX = /* @__PURE__ */ buildEmojiRegex();
@@ -0,0 +1,5 @@
1
+ // src/version.generated.js
2
+ // Generated from package.json by scripts/gen-version.js (`npm run gen`); do not edit.
3
+
4
+ /** The installed version of text-sanctifier, as in its package.json. */
5
+ export const VERSION = '1.3.7';