text-sanctifier 1.1.1 → 1.3.1

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.
@@ -1,12 +1,14 @@
1
- function n(a={}){const c=!!a.purgeInvisibleChars,d=!!a.purgeEmojis,f=!!a.nukeControls,g=!!a.keyboardOnlyFilter,l=!!a.normalizeNewlines,h=!!a.trimSpacesAroundNewlines,b=!!a.collapseNewLines,e=!!a.preserveParagraphs,k=!!a.collapseSpaces,m=!!a.finalTrim,p=Number.isFinite(a.maxLength)&&0<=a.maxLength?a.maxLength:Infinity,q="truncate"===a.g||"noop"===a.g||"throw"===a.g?a.g:"throw";return z=>r(z,c,d,f,g,l,h,b,e,k,m,p,q)}n.strict=a=>r(a,!0,!0,!0,!1,!0,!0,!0,!1,!0,!0);
2
- n.loose=a=>r(a,!1,!1,!1,!1,!0,!0,!0,!0,!0,!0);n.keyboardOnlyEmoji=a=>r(a,!1,!1,!1,!0,!0,!0,!1,!1,!1,!0);n.keyboardOnly=a=>r(a,!0,!0,!0,!0,!0,!0,!0,!1,!0,!0);
3
- function r(a,c=!1,d=!1,f=!1,g=!1,l=!1,h=!1,b=!1,e=!1,k=!1,m=!1,p=Infinity,q="throw"){if("string"!==typeof a)throw new TypeError("sanctifyText expects a string input.");"truncate"!==q&&"noop"!==q&&"throw"!==q&&(q="throw");if(a.length>p)switch(q){case "truncate":a=a.slice(0,p);break;case "noop":return a;default:throw new RangeError(`sanctifyText input length ${a.length} exceeds maxLength ${p}.`);}c&&(a=a.replace(t,""));d&&(a=a.replace(u,""));f&&(a=a.replace(v,""));g&&(a=w(a,d));l&&(a=a.replace(x,"\n"));
4
- h&&(a=a.replace(y,"$1"));b&&(c=a,a=e?c.replace(A,"\n\n"):c.replace(B,"\n"));k&&(a=a.replace(C," "));return m?a.trim():a}var t=/[\u00A0\u2000-\u200D\u202F\u2060\u3000\uFEFF\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]+/g,D=/[^\x20-\x7E\r\n]+/gu;function w(a,c=!1){a=E(a);return c?a.replace(D,""):a.replace(D,d=>{u.lastIndex=0;if(u.test(d)){u.lastIndex=0;var f="";for(const g of d.matchAll(u))f+=g[0];d=f}else d="";return d})}
5
- var F=/[\u2018\u2019\u201A\u201B\u2032\u2035]/g,G=/[\u201C\u201D\u201E\u201F\u2033\u2036\u00AB\u00BB]/g,H=/[\u2012\u2013\u2014\u2015\u2212]/g,I=/\u2026/g,J=/[\u2022\u00B7]/g,K=/[\uFF01-\uFF5E]/g;function E(a){return a.replace(F,"'").replace(G,'"').replace(H,"-").replace(I,"...").replace(J,"*").replace(K,c=>String.fromCharCode(c.charCodeAt(0)-65248))}var u;try{u=RegExp("(?:\\p{Extended_Pictographic}(?:\\uFE0F|\\uFE0E)?(?:\\u200D(?:\\p{Extended_Pictographic}|\\w)+)*)","gu")}catch{u=/[\u{2700}-\u{27BF}\u{1F300}-\u{1FAFF}\u{1F3FB}-\u{1F3FF}\u200D\uFE0F\u{1F1E6}-\u{1F1FF}]/gu}
6
- var x=/\r\n|\r|\n/g,y=/[ \t]*(\n+)[ \t]*/g,B=/\n{2,}/g,A=/\n{3,}/g,C=/ {2,}/g,v=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F\u0080-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]+/g;function L(a,c){a.lastIndex=0;return a.test(c)}
7
- function M(a){if("string"!==typeof a)throw new TypeError("inspectText expects a string input.");const c=[],d={hasControlChars:!1,hasInvisibleChars:!1,hasMixedNewlines:!1,newlineStyle:null,hasEmojis:!1,hasNonKeyboardChars:!1,summary:c};L(v,a)&&(d.hasControlChars=!0,c.push("Control characters detected."));L(t,a)&&(d.hasInvisibleChars=!0,c.push("Invisible Unicode characters detected."));L(u,a)&&(d.hasEmojis=!0,c.push("Emojis detected."));if("string"!==typeof a)throw new TypeError("getNewlineStats expects a string input.");
8
- var f=0,g=0;let l=0;for(let k=0,m=a.length;k<m;k++){const p=a.charCodeAt(k);13===p?10===a.charCodeAt(k+1)?(f++,k++):g++:10===p&&l++}f={i:f,h:g,j:l};g=[];0<f.i&&g.push("CRLF");0<f.h&&g.push("CR");0<f.j&&g.push("LF");const {l:h,types:b}={...f,types:g,l:1<g.length};d.hasMixedNewlines=h;d.newlineStyle=h?"Mixed":b[0]||null;d.newlineStyle&&c.push(h?"Mixed newline styles detected.":`Consistent newline style: ${d.newlineStyle}`);a:{a=E(a);if(L(D,a))for(e of a)if(!("\n"===e||"\r"===e||" "<=e&&"~">=e||L(u,
9
- e))){var e=!0;break a}e=!1}e&&(d.hasNonKeyboardChars=!0,c.push("Non-keyboard characters detected."));return d}
10
- function N(a,c={}){if("string"!==typeof a)throw new TypeError("inspectTextDetailed expects a string input.");c=c&&c.maxFindings;c=Infinity===c||Number.isInteger(c)&&0<=c?c:100;const d={bidi_control:0,zero_width:0,nonbreaking_space:0,bom:0,nul:0,c0_control:0,c1_control:0},f=[];let g=0,l=1,h=0;for(let e=0,k=a.length;e<k;e++){var b=a.charCodeAt(e);if(32<=b&&127>b)continue;if(10===b){l++;h=e+1;continue}if(13===b){10!==a.charCodeAt(e+1)&&(l++,h=e+1);continue}const m=128>b?0===b?"nul":32>b?9===b||10===
11
- b||13===b?null:"c0_control":127===b?"c0_control":null:159>=b?"c1_control":160===b?"nonbreaking_space":1564===b?"bidi_control":8192>b?null:8202>=b?"nonbreaking_space":8205>=b?"zero_width":8207>=b||8234<=b&&8238>=b?"bidi_control":8239===b?"nonbreaking_space":8288===b?"zero_width":8294<=b&&8297>=b?"bidi_control":12288===b?"nonbreaking_space":65279===b?"bom":null;null!==m&&(d[m]++,g++,f.length<c&&(b={offset:e,line:l,column:e-h+1,codePoint:b,category:m},"bom"===m&&(b.atStart=0===e),f.push(b)))}return{findings:f,
12
- counts:d,total:g,truncated:g>f.length}}function O(a){return{purgeInvisibleChars:a.hasInvisibleChars,purgeEmojis:a.hasEmojis,nukeControls:a.hasControlChars,keyboardOnlyFilter:a.hasNonKeyboardChars,normalizeNewlines:a.hasMixedNewlines||"CRLF"===a.newlineStyle||"CR"===a.newlineStyle}}export { n as summonSanctifier, M as inspectText, O as getRecommendedSanctifierOptions, N as inspectTextDetailed };
1
+ function n(a={}){const c=!!a.purgeInvisibleChars,d=!!a.purgeEmojis,e=!!a.nukeControls,g=!!a.keyboardOnlyFilter,f=!!a.normalizeNewlines,l=!!a.trimSpacesAroundNewlines,b=!!a.collapseNewLines,h=!!a.preserveParagraphs,k=!!a.collapseSpaces,r=!!a.finalTrim,m=Number.isFinite(a.maxLength)&&0<=a.maxLength?a.maxLength:Infinity,p="truncate"===a.g||"noop"===a.g||"throw"===a.g?a.g:"throw";return A=>q(A,c,d,e,g,f,l,b,h,k,r,m,p)}n.strict=a=>q(a,!0,!0,!0,!1,!0,!0,!0,!1,!0,!0);
2
+ n.loose=a=>q(a,!1,!1,!1,!1,!0,!0,!0,!0,!0,!0);n.keyboardOnlyEmoji=a=>q(a,!1,!1,!1,!0,!0,!0,!1,!1,!1,!0);n.keyboardOnly=a=>q(a,!0,!0,!0,!0,!0,!0,!0,!1,!0,!0);
3
+ function q(a,c=!1,d=!1,e=!1,g=!1,f=!1,l=!1,b=!1,h=!1,k=!1,r=!1,m=Infinity,p="throw"){if("string"!==typeof a)throw new TypeError("sanctifyText expects a string input.");"truncate"!==p&&"noop"!==p&&"throw"!==p&&(p="throw");if(a.length>m)switch(p){case "truncate":a=a.slice(0,m);break;case "noop":return a;default:throw new RangeError(`sanctifyText input length ${a.length} exceeds maxLength ${m}.`);}c&&(a=a.replace(t,""));d&&(a=a.replace(u,""));e&&(a=a.replace(v,""));g&&(a=w(a,d));f&&(a=a.replace(x,"\n"));
4
+ l&&(a=a.replace(y,"$1"));b&&(c=a,a=h?c.replace(z,"\n\n"):c.replace(B,"\n"));k&&(a=a.replace(C," "));return r?a.trim():a}var t=/[\u00a0\u00ad\u034f\u061c\u115f\u1160\u180e\u2000-\u200f\u202a-\u202f\u205f\u2060-\u2064\u2066-\u2069\u3000\u3164\ufe00-\ufe0f\ufeff\uffa0\u{E0000}-\u{E007F}\u{E0100}-\u{E01EF}]+/gu,D=/[^\x20-\x7E\r\n]+/gu;
5
+ function w(a,c=!1){a=E(a);return c?a.replace(D,""):a.replace(D,d=>{u.lastIndex=0;if(u.test(d)){u.lastIndex=0;var e="";for(const g of d.matchAll(u))e+=g[0];d=e}else d="";return d})}var F=/[\u2018\u2019\u201A\u201B\u2032\u2035]/g,G=/[\u201C\u201D\u201E\u201F\u2033\u2036\u00AB\u00BB]/g,H=/[\u2012\u2013\u2014\u2015\u2212]/g,I=/\u2026/g,J=/[\u2022\u00B7]/g,K=/[\uFF01-\uFF5E]/g;
6
+ function E(a){return a.replace(F,"'").replace(G,'"').replace(H,"-").replace(I,"...").replace(J,"*").replace(K,c=>String.fromCharCode(c.charCodeAt(0)-65248))}var u;try{u=RegExp("(?:\\p{Extended_Pictographic}(?:\\uFE0F|\\uFE0E)?(?:\\u200D(?:\\p{Extended_Pictographic}|\\w)+)*)","gu")}catch{u=/[\u{2700}-\u{27BF}\u{1F300}-\u{1FAFF}\u{1F3FB}-\u{1F3FF}\u200D\uFE0F\u{1F1E6}-\u{1F1FF}]/gu}var x=/\r\n|\r|\n/g,y=/[ \t]*(\n+)[ \t]*/g,B=/\n{2,}/g,z=/\n{3,}/g,C=/ {2,}/g,v=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F\u0080-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]+/g;
7
+ function L(a,c){a.lastIndex=0;return a.test(c)}
8
+ function M(a){if("string"!==typeof a)throw new TypeError("inspectText expects a string input.");const c=[],d={hasControlChars:!1,hasInvisibleChars:!1,hasMixedNewlines:!1,newlineStyle:null,hasEmojis:!1,hasNonKeyboardChars:!1,summary:c};L(v,a)&&(d.hasControlChars=!0,c.push("Control characters detected."));L(t,a)&&(d.hasInvisibleChars=!0,c.push("Invisible Unicode characters detected."));L(u,a)&&(d.hasEmojis=!0,c.push("Emojis detected."));const {mixed:e,types:g}=N(a);d.hasMixedNewlines=e;d.newlineStyle=
9
+ e?"Mixed":g[0]||null;d.newlineStyle&&c.push(e?"Mixed newline styles detected.":`Consistent newline style: ${d.newlineStyle}`);a:{a=E(a);if(L(D,a))for(f of a)if(!("\n"===f||"\r"===f||" "<=f&&"~">=f||L(u,f))){var f=!0;break a}f=!1}f&&(d.hasNonKeyboardChars=!0,c.push("Non-keyboard characters detected."));return d}
10
+ function O(a,c={}){if("string"!==typeof a)throw new TypeError("inspectTextDetailed expects a string input.");c=c&&c.maxFindings;c=Infinity===c||Number.isInteger(c)&&0<=c?c:100;const d={bidi_control:0,zero_width:0,nonbreaking_space:0,bom:0,nul:0,c0_control:0,c1_control:0,invisible_format:0,hangul_filler:0,variation_selector:0,tag:0},e=[];let g=0,f=1,l=0;for(let k=0,r=a.length;k<r;k++){var b=a.charCodeAt(k);if(32<=b&&127>b)continue;if(10===b){f++;l=k+1;continue}if(13===b){10!==a.charCodeAt(k+1)&&(f++,
11
+ l=k+1);continue}const m=k;var h=b;if(56128===b){h=a.charCodeAt(k+1);if(!(56320<=h&&57343>=h))continue;h=65536+(b-55296<<10)+(h-56320);b=917504<=h&&917631>=h?"tag":917760<=h&&917999>=h?"variation_selector":null;k++}else b=128>b?0===b?"nul":32>b?9===b||10===b||13===b?null:"c0_control":127===b?"c0_control":null:159>=b?"c1_control":160===b?"nonbreaking_space":173===b||847===b||6158===b?"invisible_format":1564===b?"bidi_control":4447===b||4448===b?"hangul_filler":8192>b?null:8202>=b?"nonbreaking_space":
12
+ 8205>=b?"zero_width":8207>=b||8234<=b&&8238>=b?"bidi_control":8239===b||8287===b?"nonbreaking_space":8288===b?"zero_width":8289<=b&&8292>=b?"invisible_format":8294<=b&&8297>=b?"bidi_control":12288===b?"nonbreaking_space":12644===b||65440===b?"hangul_filler":65024<=b&&65039>=b?"variation_selector":65279===b?"bom":null;null!==b&&(d[b]++,g++,e.length<c&&(h={offset:m,line:f,column:m-l+1,codePoint:h,category:b},"bom"===b&&(h.atStart=0===m),e.push(h)))}return{findings:e,counts:d,total:g,truncated:g>e.length}}
13
+ function N(a){if("string"!==typeof a)throw new TypeError("getNewlineStats expects a string input.");var c=0;let d=0,e=0;for(let g=0,f=a.length;g<f;g++){const l=a.charCodeAt(g);13===l?10===a.charCodeAt(g+1)?(c++,g++):d++:10===l&&e++}a={crlf:c,cr:d,lf:e};c=[];0<a.crlf&&c.push("CRLF");0<a.cr&&c.push("CR");0<a.lf&&c.push("LF");return{...a,types:c,mixed:1<c.length}}
14
+ function P(a){return{purgeInvisibleChars:a.hasInvisibleChars,purgeEmojis:a.hasEmojis,nukeControls:a.hasControlChars,keyboardOnlyFilter:a.hasNonKeyboardChars,normalizeNewlines:a.hasMixedNewlines||"CRLF"===a.newlineStyle||"CR"===a.newlineStyle}}export { n as summonSanctifier, M as inspectText, P as getRecommendedSanctifierOptions, O as inspectTextDetailed, N as getNewlineStats };
@@ -4,9 +4,11 @@ export type TrashFinding = import("./inspectText.js").TrashFinding;
4
4
  export type TrashCounts = import("./inspectText.js").TrashCounts;
5
5
  export type DetailedTrashReport = import("./inspectText.js").DetailedTrashReport;
6
6
  export type InspectDetailedOptions = import("./inspectText.js").InspectDetailedOptions;
7
+ export type NewlineStats = import("./inspectText.js").NewlineStats;
7
8
  export type SanctifyOptions = import("./sanctifyText.js").SanctifyOptions;
8
9
  import { inspectText } from './inspectText.js';
9
10
  import { inspectTextDetailed } from './inspectText.js';
11
+ import { getNewlineStats } from './inspectText.js';
10
12
  import { getRecommendedSanctifierOptions } from './inspectText.js';
11
13
  import { summonSanctifier } from './sanctifyText.js';
12
- export { inspectText, inspectTextDetailed, getRecommendedSanctifierOptions, summonSanctifier };
14
+ export { inspectText, inspectTextDetailed, getNewlineStats, getRecommendedSanctifierOptions, summonSanctifier };
@@ -9,12 +9,17 @@ export function inspectText(text: string): UnicodeTrashReport;
9
9
  * Categories:
10
10
  * - `bidi_control`: U+061C, U+200E, U+200F, U+202A–U+202E, U+2066–U+2069
11
11
  * - `zero_width`: U+200B–U+200D, U+2060
12
- * - `nonbreaking_space`: non-breaking and typographic spaces U+00A0, U+2000–U+200A, U+202F, U+3000
12
+ * - `nonbreaking_space`: non-breaking and typographic spaces U+00A0, U+2000–U+200A, U+202F, U+205F, U+3000
13
13
  * - `bom`: U+FEFF (`atStart` says whether it sits at offset 0)
14
14
  * - `nul`: U+0000
15
15
  * - `c0_control`: U+0001–U+001F except tab, LF and CR; and U+007F
16
16
  * - `c1_control`: U+0080–U+009F
17
+ * - `invisible_format`: soft hyphen U+00AD, U+034F, U+180E, invisible operators U+2061–U+2064
18
+ * - `hangul_filler`: U+115F, U+1160, U+3164, U+FFA0 (letters that render blank)
19
+ * - `variation_selector`: U+FE00–U+FE0F, U+E0100–U+E01EF (including VS16 in emoji)
20
+ * - `tag`: U+E0000–U+E007F (tag characters, used for "ASCII smuggling")
17
21
  *
22
+ * `codePoint` is the full code point; `offset` and `column` count UTF-16 units.
18
23
  * Every occurrence is counted; only the first `maxFindings` are listed.
19
24
  * One pass over the string, no regex, no copy.
20
25
  *
@@ -24,23 +29,19 @@ export function inspectText(text: string): UnicodeTrashReport;
24
29
  */
25
30
  export function inspectTextDetailed(text: string, options?: InspectDetailedOptions): DetailedTrashReport;
26
31
  /**
27
- * Counts the number of different newline types in a string.
32
+ * @typedef {Object} NewlineStats
33
+ * @property {number} crlf - CR LF pairs.
34
+ * @property {number} cr - CRs not followed by LF.
35
+ * @property {number} lf - LFs not preceded by CR.
36
+ * @property {('CRLF'|'CR'|'LF')[]} types - Styles present, in the order CRLF, CR, LF.
37
+ * @property {boolean} mixed - True when more than one style is present.
38
+ */
39
+ /**
40
+ * Counts each newline style in one pass, with no regex and no copy.
28
41
  * @param {string} text
29
- * @returns {{
30
- * crlf: number,
31
- * cr: number,
32
- * lf: number,
33
- * types: string[],
34
- * mixed: boolean
35
- * }}
42
+ * @returns {NewlineStats}
36
43
  */
37
- export function getNewlineStats(text: string): {
38
- crlf: number;
39
- cr: number;
40
- lf: number;
41
- types: string[];
42
- mixed: boolean;
43
- };
44
+ export function getNewlineStats(text: string): NewlineStats;
44
45
  /**
45
46
  * Creates defaultOptions for summonSanctifier based on inspectText result
46
47
  * @param {!UnicodeTrashReport} report
@@ -57,10 +58,33 @@ export type UnicodeTrashReport = {
57
58
  hasNonKeyboardChars: boolean;
58
59
  summary: string[];
59
60
  };
60
- export type TrashCategory = "bidi_control" | "zero_width" | "nonbreaking_space" | "bom" | "nul" | "c0_control" | "c1_control";
61
+ export type NewlineStats = {
62
+ /**
63
+ * - CR LF pairs.
64
+ */
65
+ crlf: number;
66
+ /**
67
+ * - CRs not followed by LF.
68
+ */
69
+ cr: number;
70
+ /**
71
+ * - LFs not preceded by CR.
72
+ */
73
+ lf: number;
74
+ /**
75
+ * - Styles present, in the order CRLF, CR, LF.
76
+ */
77
+ types: ("CRLF" | "CR" | "LF")[];
78
+ /**
79
+ * - True when more than one style is present.
80
+ */
81
+ mixed: boolean;
82
+ };
83
+ export type TrashCategory = "bidi_control" | "zero_width" | "nonbreaking_space" | "bom" | "nul" | "c0_control" | "c1_control" | "invisible_format" | "hangul_filler" | "variation_selector" | "tag";
61
84
  /**
62
85
  * One located character.
63
- * - `offset`: 0-based UTF-16 index into the input (a JS string index).
86
+ * - `offset`: 0-based UTF-16 index into the input (a JS string index); for a code point
87
+ * above U+FFFF, the index of its high surrogate.
64
88
  * - `line`: 1-based; lines end at LF, CR, or CRLF (counted once).
65
89
  * - `column`: 1-based, in UTF-16 code units from the start of the line.
66
90
  * - `atStart`: present on `bom` findings only; true at offset 0, where a BOM is legitimate.
@@ -81,6 +105,10 @@ export type TrashCounts = {
81
105
  nul: number;
82
106
  c0_control: number;
83
107
  c1_control: number;
108
+ invisible_format: number;
109
+ hangul_filler: number;
110
+ variation_selector: number;
111
+ tag: number;
84
112
  };
85
113
  export type DetailedTrashReport = {
86
114
  /**
@@ -103,6 +131,7 @@ export type DetailedTrashReport = {
103
131
  export type InspectDetailedOptions = {
104
132
  /**
105
133
  * - Cap on listed findings (non-negative integer or Infinity).
134
+ * Each listed finding is an object, so memory grows with the number listed; 0 builds none (counts only).
106
135
  */
107
136
  maxFindings?: number;
108
137
  };
@@ -91,7 +91,7 @@ export namespace summonSanctifier {
91
91
  * ⚠️ Note: This is plain-text normalization/filtering — not an HTML/XSS sanitizer.
92
92
  *
93
93
  * @param {string} text
94
- * @param {boolean} [purgeInvisibleChars=false] - Remove ZWSP, NBSP, bidi, etc.
94
+ * @param {boolean} [purgeInvisibleChars=false] - Remove ZWSP, NBSP, bidi, tags, variation selectors, etc.
95
95
  * @param {boolean} [purgeEmojis=false] - Remove emoji characters entirely.
96
96
  * @param {boolean} [nukeControls=false] - Remove non-whitespace control characters.
97
97
  * @param {boolean} [keyboardOnlyFilter=false] - Keep printable ASCII + full emoji sequences only (drops other Unicode).
@@ -121,9 +121,13 @@ export function normalizeTypographicJank(text: string): string;
121
121
  * Purges invisible Unicode "trash" characters and removes them.
122
122
  *
123
123
  * Targets:
124
- * - Non-breaking spaces (\u00A0)
125
- * - Zero-width spaces and miscellaneous Unicode spaces (\u2000–\u200D, \u202F, \u2060, \u3000, \uFEFF)
126
- * - Bidi controls: marks, embeddings/overrides and isolates (\u061C, \u200E, \u200F, \u202A–\u202E, \u2066–\u2069)
124
+ * - Non-breaking spaces ( )
125
+ * - Zero-width spaces and miscellaneous Unicode spaces ( –‍,  ,  , ⁠,  , )
126
+ * - Bidi controls: marks, embeddings/overrides and isolates (؜, ‎, ‏, ‪–‮, ⁦–⁩)
127
+ * - Invisible format characters: soft hyphen, CGJ, Mongolian vowel separator, invisible operators (­, ͏, ᠎, ⁡–⁤)
128
+ * - Hangul fillers (ᅟ, ᅠ, ㅤ, ᅠ)
129
+ * - Variation selectors (︀–️, \u{E0100}–\u{E01EF}), including VS16 in emoji
130
+ * - Tag characters (\u{E0000}–\u{E007F}), used for "ASCII smuggling"
127
131
  *
128
132
  * @param {string} text
129
133
  * @returns {string}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "text-sanctifier",
3
- "version": "1.1.1",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "description": "A brutal text normalizer and invisible trash scrubber for modern web projects.",
6
6
  "engines": {
@@ -9,8 +9,12 @@
9
9
  "main": "./src/index.js",
10
10
  "module": "./src/index.js",
11
11
  "browser": "./dist/text-sanctifier.min.js",
12
+ "bin": {
13
+ "text-sanctifier": "bin/text-sanctifier.js"
14
+ },
12
15
  "files": [
13
16
  "src",
17
+ "bin",
14
18
  "dist/text-sanctifier.min.js",
15
19
  "dist/types",
16
20
  "LICENSE",
@@ -34,7 +38,7 @@
34
38
  "build": "node scripts/buildc.js",
35
39
  "build:types": "tsc -p tsconfig.types.json",
36
40
  "test:types": "tsc -p tests/types/tsconfig.json",
37
- "test": "node tests/sanctifyText.test.js && node tests/inspectTextDetailed.test.js",
41
+ "test": "node tests/sanctifyText.test.js && node tests/inspectTextDetailed.test.js && node tests/cli.test.js",
38
42
  "test-min": "node tests/sanctifyText.test.min.js",
39
43
  "ci": "npm run test && npm run build && npm run build:types && npm run test:types && npm run test-min"
40
44
  },
package/src/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { inspectText } from './inspectText.js';
2
2
  import { inspectTextDetailed } from './inspectText.js';
3
+ import { getNewlineStats } from './inspectText.js';
3
4
  import { getRecommendedSanctifierOptions } from './inspectText.js';
4
5
  import { summonSanctifier } from './sanctifyText.js';
5
- export { inspectText, inspectTextDetailed, getRecommendedSanctifierOptions, summonSanctifier };
6
+ export { inspectText, inspectTextDetailed, getNewlineStats, getRecommendedSanctifierOptions, summonSanctifier };
6
7
  export type UnicodeTrashReport = import('./inspectText.js').UnicodeTrashReport;
7
8
  export type TrashCategory = import('./inspectText.js').TrashCategory;
8
9
  export type TrashFinding = import('./inspectText.js').TrashFinding;
9
10
  export type TrashCounts = import('./inspectText.js').TrashCounts;
10
11
  export type DetailedTrashReport = import('./inspectText.js').DetailedTrashReport;
11
12
  export type InspectDetailedOptions = import('./inspectText.js').InspectDetailedOptions;
13
+ export type NewlineStats = import('./inspectText.js').NewlineStats;
12
14
  export type SanctifyOptions = import('./sanctifyText.js').SanctifyOptions;
package/src/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/index.js
2
2
 
3
3
 
4
- import { inspectText, inspectTextDetailed, getRecommendedSanctifierOptions } from './inspectText.js';
5
- export { inspectText, inspectTextDetailed, getRecommendedSanctifierOptions };
4
+ import { inspectText, inspectTextDetailed, getNewlineStats, getRecommendedSanctifierOptions } from './inspectText.js';
5
+ export { inspectText, inspectTextDetailed, getNewlineStats, getRecommendedSanctifierOptions };
6
6
 
7
7
  import { summonSanctifier } from './sanctifyText.js';
8
8
  export { summonSanctifier };
@@ -14,6 +14,7 @@ export { summonSanctifier };
14
14
  /** @typedef {import('./inspectText.js').TrashCounts} TrashCounts */
15
15
  /** @typedef {import('./inspectText.js').DetailedTrashReport} DetailedTrashReport */
16
16
  /** @typedef {import('./inspectText.js').InspectDetailedOptions} InspectDetailedOptions */
17
+ /** @typedef {import('./inspectText.js').NewlineStats} NewlineStats */
17
18
  /** @typedef {import('./sanctifyText.js').SanctifyOptions} SanctifyOptions */
18
19
 
19
20