text-sanctifier 1.3.12 → 1.3.13
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/README.md +2 -1
- package/dist/text-sanctifier.min.js +1 -1
- package/dist/types/hiddenScan.d.ts +1 -0
- package/dist/types/hiddenText.d.ts +12 -0
- package/dist/types/version.generated.d.ts +1 -1
- package/package.json +1 -1
- package/src/hiddenScan.js +30 -0
- package/src/hiddenText.js +6 -0
- package/src/version.generated.js +1 -1
package/README.md
CHANGED
|
@@ -261,6 +261,7 @@ const { runs, benign, counts, total, truncated } = findHiddenRuns(text, { maxRun
|
|
|
261
261
|
|
|
262
262
|
- `kind` is `tag` (U+E0000–U+E007F), `variation_selector` (U+FE00–U+FE0F, U+E0100–U+E01EF, U+180B–U+180D, U+180F) `invisible_operator` (U+2061–U+2064) or `reserved_tag_plane` (U+E0080–U+E00FF, U+E01F0–U+E0FFF: unassigned, default ignorable, so they render as nothing and can carry data). Consecutive characters of one kind form one run.
|
|
263
263
|
- `offset` and `end` are UTF-16 indexes, `[offset, end)`; `count` is in code points. `line` and `column` are counted exactly as in `inspectTextDetailed` (1-based, columns in UTF-16 units, CRLF is one line break). Only tag runs have `decoded` and `decodedTruncated`.
|
|
264
|
+
- `sequence` marks a lone selector in a form `text-sanctifier/context` calls benign: `'ideographic'` for U+E0100–U+E01EF after an ideograph, `'mongolian'` for U+180B–U+180D or U+180F after a Mongolian letter. These stay in `runs` because they can carry data, up to a byte per ideograph, so a run with `sequence` is usually ordinary text, but many of them can be a message. U+FE0E and U+FE0F have no such room and go under `benign`.
|
|
264
265
|
- `maxRuns` caps the `runs` list and, separately, the `benign` list; `counts`, `total` and `benignTotal` count everything, and `truncated` is true when either list was cut. `maxDecodedLength` caps each `decoded` string in UTF-16 units and never cuts a `<U+XXXX>` marker. Both default to no cap. Only non-negative integers and `Infinity` are valid; invalid values (negative, fractional, a string, `NaN` or `null`) also mean no cap, so set them explicitly for untrusted input. With both set, memory stays flat on any input: on 16 M characters (32 MB) of hidden text the heap grows by under 1 MB, against hundreds of MB with no caps.
|
|
265
266
|
- `decodeTagText` turns U+E0020–U+E007E into the ASCII they shadow and writes any other character as `<U+XXXX>`.
|
|
266
267
|
- Zero-width, bidi and other invisible characters are not runs; use `inspectTextDetailed` for those.
|
|
@@ -365,7 +366,7 @@ The repository publishes a [pre-commit](https://pre-commit.com) hook manifest, s
|
|
|
365
366
|
# .pre-commit-config.yaml
|
|
366
367
|
repos:
|
|
367
368
|
- repo: https://github.com/iWhatty/text-sanctifier
|
|
368
|
-
rev: v1.3.
|
|
369
|
+
rev: v1.3.13 # a release tag
|
|
369
370
|
hooks:
|
|
370
371
|
- id: text-sanctifier # every staged text file
|
|
371
372
|
- id: text-sanctifier-ai-config # agent instruction files, with --ai-config
|
|
@@ -3,4 +3,4 @@ var y=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u061C\u200E\u200F\u2
|
|
|
3
3
|
`){return e.replace(re,r)}var te=/[ \t]*(\n+)[ \t]*|([ \t]+)/g;function ie(e){return e.replace(te,"$1$2")}var oe=/\n{2,}/g,ue=/\n{3,}/g;function se(e,r){return r?e.replace(ue,`
|
|
4
4
|
|
|
5
5
|
`):e.replace(oe,`
|
|
6
|
-
`)}var le=/ {2,}/g;function ae(e){return e.replace(le," ")}function fe(e){return e.replace(y,"")}function ce(e,r={}){if(typeof e!="string")throw new TypeError("revealInvisible expects a string input.");let n=r&&typeof r.format=="function"?r.format:i=>"<U+"+i.toString(16).toUpperCase().padStart(4,"0")+">",u="",o=0;for(let i of A(e,{maxFindings:1/0}).findings)u+=e.slice(o,i.offset)+n(i.codePoint,i.category),o=i.offset+(i.codePoint>65535?2:1);return u+e.slice(o)}var Ee="1.3.
|
|
6
|
+
`)}var le=/ {2,}/g;function ae(e){return e.replace(le," ")}function fe(e){return e.replace(y,"")}function ce(e,r={}){if(typeof e!="string")throw new TypeError("revealInvisible expects a string input.");let n=r&&typeof r.format=="function"?r.format:i=>"<U+"+i.toString(16).toUpperCase().padStart(4,"0")+">",u="",o=0;for(let i of A(e,{maxFindings:1/0}).findings)u+=e.slice(o,i.offset)+n(i.codePoint,i.category),o=i.offset+(i.codePoint>65535?2:1);return u+e.slice(o)}var Ee="1.3.13";export{Ee as VERSION,R as getNewlineStats,H as getRecommendedSanctifierOptions,j as inspectText,A as inspectTextDetailed,ce as revealInvisible,L as sanctifyKeyboardOnly,T as sanctifyKeyboardOnlyEmoji,N as sanctifyLoose,D as sanctifyPasted,w as sanctifyStrict,J as summonSanctifier};
|
|
@@ -25,6 +25,7 @@ export function scanHiddenSpans(text: string, options?: HiddenScanOptions, onSpa
|
|
|
25
25
|
}) => void): HiddenTextScan;
|
|
26
26
|
export type HiddenRunKind = import("./hiddenText.js").HiddenRunKind;
|
|
27
27
|
export type HiddenRun = import("./hiddenText.js").HiddenRun;
|
|
28
|
+
export type HiddenRunSequence = import("./hiddenText.js").HiddenRunSequence;
|
|
28
29
|
export type HiddenRunCounts = import("./hiddenText.js").HiddenRunCounts;
|
|
29
30
|
export type HiddenTextScan = import("./hiddenText.js").HiddenTextScan;
|
|
30
31
|
export type HiddenScanOptions = import("./hiddenText.js").HiddenScanOptions;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @typedef {'tag' | 'variation_selector' | 'invisible_operator' | 'reserved_tag_plane'} HiddenRunKind */
|
|
2
|
+
/** @typedef {'ideographic' | 'mongolian'} HiddenRunSequence */
|
|
2
3
|
/**
|
|
3
4
|
* @typedef {Object} HiddenRun
|
|
4
5
|
* @property {HiddenRunKind} kind
|
|
@@ -10,6 +11,9 @@
|
|
|
10
11
|
* @property {string} [decoded] - tag runs only: the ASCII the tags spell (see decodeTagText),
|
|
11
12
|
* at most `maxDecodedLength` UTF-16 units
|
|
12
13
|
* @property {boolean} [decodedTruncated] - tag runs only: true when `decoded` stops short of the run
|
|
14
|
+
* @property {HiddenRunSequence} [sequence] - a lone selector in a form text-sanctifier/context calls
|
|
15
|
+
* benign: U+E0100–U+E01EF after an ideograph, or U+180B–U+180D or U+180F after a Mongolian letter.
|
|
16
|
+
* Still a run, because these can carry data: up to a byte per ideograph.
|
|
13
17
|
*/
|
|
14
18
|
/**
|
|
15
19
|
* @typedef {Object} HiddenRunCounts
|
|
@@ -57,6 +61,7 @@ export function decodeTagText(text: string): string;
|
|
|
57
61
|
* Extended_Pictographic character, or between #, * or a digit and U+20E3 (a keycap),
|
|
58
62
|
* the same rule as text-sanctifier/context. After a letter it is a run: one selector
|
|
59
63
|
* per letter can carry a bit each.
|
|
64
|
+
* Lone ideographic and Mongolian selectors stay runs, marked with `sequence`.
|
|
60
65
|
*
|
|
61
66
|
* `line` and `column` are counted as in inspectTextDetailed. Every run and benign
|
|
62
67
|
* span is counted; only the first `maxRuns` of each are listed. With the caps set,
|
|
@@ -70,6 +75,7 @@ export function decodeTagText(text: string): string;
|
|
|
70
75
|
export function findHiddenRuns(text: string, options?: HiddenScanOptions): HiddenTextScan;
|
|
71
76
|
export { VERSION } from "./version.generated.js";
|
|
72
77
|
export type HiddenRunKind = "tag" | "variation_selector" | "invisible_operator" | "reserved_tag_plane";
|
|
78
|
+
export type HiddenRunSequence = "ideographic" | "mongolian";
|
|
73
79
|
export type HiddenRun = {
|
|
74
80
|
kind: HiddenRunKind;
|
|
75
81
|
/**
|
|
@@ -101,6 +107,12 @@ export type HiddenRun = {
|
|
|
101
107
|
* - tag runs only: true when `decoded` stops short of the run
|
|
102
108
|
*/
|
|
103
109
|
decodedTruncated?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* - a lone selector in a form text-sanctifier/context calls
|
|
112
|
+
* benign: U+E0100–U+E01EF after an ideograph, or U+180B–U+180D or U+180F after a Mongolian letter.
|
|
113
|
+
* Still a run, because these can carry data: up to a byte per ideograph.
|
|
114
|
+
*/
|
|
115
|
+
sequence?: HiddenRunSequence;
|
|
104
116
|
};
|
|
105
117
|
export type HiddenRunCounts = {
|
|
106
118
|
tag: number;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The installed version of text-sanctifier, as in its package.json. */
|
|
2
|
-
export const VERSION: "1.3.
|
|
2
|
+
export const VERSION: "1.3.13";
|
package/package.json
CHANGED
package/src/hiddenScan.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {import('./hiddenText.js').HiddenRunKind} HiddenRunKind
|
|
5
5
|
* @typedef {import('./hiddenText.js').HiddenRun} HiddenRun
|
|
6
|
+
* @typedef {import('./hiddenText.js').HiddenRunSequence} HiddenRunSequence
|
|
6
7
|
* @typedef {import('./hiddenText.js').HiddenRunCounts} HiddenRunCounts
|
|
7
8
|
* @typedef {import('./hiddenText.js').HiddenTextScan} HiddenTextScan
|
|
8
9
|
* @typedef {import('./hiddenText.js').HiddenScanOptions} HiddenScanOptions
|
|
@@ -76,6 +77,32 @@ function isPresentationBase(text, index) {
|
|
|
76
77
|
return PICTOGRAPHIC_AT.test(text);
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
const IDEOGRAPHIC_AT = /\p{Ideographic}/yu;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The legitimate sequence a lone selector at `index` could belong to, by the same rules
|
|
84
|
+
* as text-sanctifier/context. It stays a run: unlike U+FE0E and U+FE0F, these selectors
|
|
85
|
+
* have enough values to spell data, up to a byte per ideograph.
|
|
86
|
+
* @param {string} text
|
|
87
|
+
* @param {number} index
|
|
88
|
+
* @param {number} cp - the selector
|
|
89
|
+
* @returns {HiddenRunSequence | null}
|
|
90
|
+
*/
|
|
91
|
+
function sequenceOf(text, index, cp) {
|
|
92
|
+
if (cp >= 0xE0100 && cp <= 0xE01EF) {
|
|
93
|
+
if (index === 0) return null;
|
|
94
|
+
const low = text.charCodeAt(index - 1);
|
|
95
|
+
IDEOGRAPHIC_AT.lastIndex = low >= 0xDC00 && low <= 0xDFFF && index >= 2 &&
|
|
96
|
+
(text.charCodeAt(index - 2) & 0xFC00) === 0xD800 ? index - 2 : index - 1;
|
|
97
|
+
return IDEOGRAPHIC_AT.test(text) ? 'ideographic' : null;
|
|
98
|
+
}
|
|
99
|
+
if (cp > 0x180F) return null;
|
|
100
|
+
const c = text.charCodeAt(index - 1);
|
|
101
|
+
return (c >= 0x1820 && c <= 0x1878) || (c >= 0x1880 && c <= 0x1884) || (c >= 0x1887 && c <= 0x18A8) || c === 0x18AA
|
|
102
|
+
? 'mongolian'
|
|
103
|
+
: null;
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
/**
|
|
80
107
|
* A cap option: a non-negative integer or Infinity; anything else means no cap.
|
|
81
108
|
* @param {unknown} value
|
|
@@ -163,6 +190,9 @@ export function scanHiddenSpans(text, options = {}, onSpan) {
|
|
|
163
190
|
const { decoded, truncated } = decodeRange(text, offset, end, maxDecodedLength);
|
|
164
191
|
run.decoded = decoded;
|
|
165
192
|
run.decodedTruncated = truncated;
|
|
193
|
+
} else if (kind === 'variation_selector' && count === 1) {
|
|
194
|
+
const sequence = sequenceOf(text, offset, cp);
|
|
195
|
+
if (sequence) run.sequence = sequence;
|
|
166
196
|
}
|
|
167
197
|
runs.push(run);
|
|
168
198
|
}
|
package/src/hiddenText.js
CHANGED
|
@@ -15,6 +15,8 @@ export { VERSION } from './version.generated.js';
|
|
|
15
15
|
|
|
16
16
|
/** @typedef {'tag' | 'variation_selector' | 'invisible_operator' | 'reserved_tag_plane'} HiddenRunKind */
|
|
17
17
|
|
|
18
|
+
/** @typedef {'ideographic' | 'mongolian'} HiddenRunSequence */
|
|
19
|
+
|
|
18
20
|
/**
|
|
19
21
|
* @typedef {Object} HiddenRun
|
|
20
22
|
* @property {HiddenRunKind} kind
|
|
@@ -26,6 +28,9 @@ export { VERSION } from './version.generated.js';
|
|
|
26
28
|
* @property {string} [decoded] - tag runs only: the ASCII the tags spell (see decodeTagText),
|
|
27
29
|
* at most `maxDecodedLength` UTF-16 units
|
|
28
30
|
* @property {boolean} [decodedTruncated] - tag runs only: true when `decoded` stops short of the run
|
|
31
|
+
* @property {HiddenRunSequence} [sequence] - a lone selector in a form text-sanctifier/context calls
|
|
32
|
+
* benign: U+E0100–U+E01EF after an ideograph, or U+180B–U+180D or U+180F after a Mongolian letter.
|
|
33
|
+
* Still a run, because these can carry data: up to a byte per ideograph.
|
|
29
34
|
*/
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -81,6 +86,7 @@ export function decodeTagText(text) {
|
|
|
81
86
|
* Extended_Pictographic character, or between #, * or a digit and U+20E3 (a keycap),
|
|
82
87
|
* the same rule as text-sanctifier/context. After a letter it is a run: one selector
|
|
83
88
|
* per letter can carry a bit each.
|
|
89
|
+
* Lone ideographic and Mongolian selectors stay runs, marked with `sequence`.
|
|
84
90
|
*
|
|
85
91
|
* `line` and `column` are counted as in inspectTextDetailed. Every run and benign
|
|
86
92
|
* span is counted; only the first `maxRuns` of each are listed. With the caps set,
|
package/src/version.generated.js
CHANGED