@stll/text-normalize 0.0.1-placeholder.0 → 0.1.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.
- package/README.md +81 -1
- package/dist/arabic.d.ts +67 -0
- package/dist/arabic.js +156 -0
- package/dist/arabic.js.map +1 -0
- package/dist/ascii-fold-table.d.ts +29 -0
- package/dist/ascii-fold-table.js +513 -0
- package/dist/ascii-fold-table.js.map +1 -0
- package/dist/ascii-fold.d.ts +29 -0
- package/dist/ascii-fold.js +37 -0
- package/dist/ascii-fold.js.map +1 -0
- package/dist/diacritics.d.ts +32 -0
- package/dist/diacritics.js +34 -0
- package/dist/diacritics.js.map +1 -0
- package/dist/escape-regexp.d.ts +10 -0
- package/dist/escape-regexp.js +11 -0
- package/dist/escape-regexp.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize.d.ts +16 -0
- package/dist/normalize.js +32 -0
- package/dist/normalize.js.map +1 -0
- package/dist/search-match.d.ts +38 -0
- package/dist/search-match.js +118 -0
- package/dist/search-match.js.map +1 -0
- package/dist/slug.d.ts +45 -0
- package/dist/slug.js +53 -0
- package/dist/slug.js.map +1 -0
- package/dist/spaced-letters.d.ts +57 -0
- package/dist/spaced-letters.js +101 -0
- package/dist/spaced-letters.js.map +1 -0
- package/package.json +54 -5
package/README.md
CHANGED
|
@@ -1 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
# @stll/text-normalize
|
|
2
|
+
|
|
3
|
+
Search match-key text normalization. Folds orthographic variants so a
|
|
4
|
+
search query matches text regardless of how it was typed.
|
|
5
|
+
|
|
6
|
+
The normalized output is a **match key**: it is lossy and intended only
|
|
7
|
+
for indexing and query comparison. Never store or display it in place of
|
|
8
|
+
the original text.
|
|
9
|
+
|
|
10
|
+
## Arabic
|
|
11
|
+
|
|
12
|
+
`arabicNormalize` folds the orthographic variants that make Arabic
|
|
13
|
+
search miss otherwise-identical words:
|
|
14
|
+
|
|
15
|
+
- alef variants and alef-wasla (`أ إ آ ٱ`) to bare alef `ا`
|
|
16
|
+
- waw-hamza `ؤ` to `و`, yeh-hamza `ئ` to `ي`, standalone hamza `ء` dropped
|
|
17
|
+
- teh marbuta `ة` to heh `ه`
|
|
18
|
+
- alef maksura `ى` to yeh `ي`
|
|
19
|
+
- tashkeel (harakat), superscript alef, and tatweel removed
|
|
20
|
+
- Arabic-Indic and Extended Arabic-Indic digits to ASCII
|
|
21
|
+
|
|
22
|
+
It also runs NFKC (folding presentation forms), locale-stable ASCII case
|
|
23
|
+
folding, and whitespace collapse, so it is a safe pass-through for
|
|
24
|
+
non-Arabic scripts.
|
|
25
|
+
|
|
26
|
+
The fold tables are vendored from Lucene's `ArabicNormalizer` (Apache-2.0)
|
|
27
|
+
and cross-checked against CAMeL Tools (MIT), extended for the classes
|
|
28
|
+
Lucene omits.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { arabicNormalize } from "@stll/text-normalize";
|
|
32
|
+
|
|
33
|
+
arabicNormalize("أحمد") === arabicNormalize("احمد"); // true
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The same fold must be reproduced by the PostgreSQL `arabic_normalize()`
|
|
37
|
+
function used in contacts trigram expression indexes and search predicates;
|
|
38
|
+
the golden vectors in `src/normalize.test.ts` pin the shared contract.
|
|
39
|
+
|
|
40
|
+
## Diacritics
|
|
41
|
+
|
|
42
|
+
`stripDiacritics` decomposes with NFD and removes every combining mark
|
|
43
|
+
(`\p{Diacritic}`, so marks beyond the U+0300–U+036F block are covered
|
|
44
|
+
too). Use it for accent-insensitive search keys.
|
|
45
|
+
|
|
46
|
+
`foldToAscii` keeps that strip as its base and adds the folds a
|
|
47
|
+
decomposition cannot express, so it matches PostgreSQL `unaccent()` for
|
|
48
|
+
every Latin-script character for which the extension declares a rule:
|
|
49
|
+
`ł` to `l`, `đ` to `d`, `ø` to `o`, `ß` to `ss`, `æ` to `ae`, and the
|
|
50
|
+
rest of the table in `src/ascii-fold-table.ts`. Use it wherever the
|
|
51
|
+
counterpart is an `unaccent`-folded index (or a Tantivy `ascii_folding`
|
|
52
|
+
one); a plain mark strip leaves `ł` standing and under-matches the
|
|
53
|
+
lexemes the index actually stored. Non-Latin scripts are out of contract
|
|
54
|
+
and pass through with their marks stripped.
|
|
55
|
+
|
|
56
|
+
`stripDiacriticsForSlug` is the same strip but decomposes with NFKD, so
|
|
57
|
+
compatibility characters (ligatures, full-width forms, superscripts) fold
|
|
58
|
+
to their base before a `[a-z0-9]` slug filter runs. Slugs are persisted,
|
|
59
|
+
public URL segments; this variant pins the exact form existing slugs were
|
|
60
|
+
generated with.
|
|
61
|
+
|
|
62
|
+
## Substring matching with highlight offsets
|
|
63
|
+
|
|
64
|
+
`foldSearchMatchText` is the client-side match key for interactive
|
|
65
|
+
filtering: NFKD, the `unaccent()`-parity ASCII folds, mark strip, Arabic
|
|
66
|
+
folds, lowercase — `capek` matches `Čapek` and `wroclaw` matches
|
|
67
|
+
`Wrocław`, both ways. `findSearchMatchRanges` finds a folded query in
|
|
68
|
+
folded content and returns ranges into the **original** string, so
|
|
69
|
+
highlights wrap the text the user sees even where folding changes string
|
|
70
|
+
length; it accepts content pre-folded with `foldSearchMatchTextWithOffsets`
|
|
71
|
+
so repeated queries against the same text fold it once.
|
|
72
|
+
|
|
73
|
+
## Spaced letters
|
|
74
|
+
|
|
75
|
+
`collapseSpacedLetters` joins letter-spaced court headings
|
|
76
|
+
(`r o z h o d o l :` to `rozhodol:`) back into searchable words. It
|
|
77
|
+
requires at least four spaced letters, so Czech/Slovak single-letter word
|
|
78
|
+
lists (`a b c`) are left intact. `spacedLetterRunRegex()` returns the
|
|
79
|
+
underlying matcher for callers that need per-match offsets (find-in-page
|
|
80
|
+
highlighting). Index-time and query-time collapse share this one
|
|
81
|
+
threshold so a heading collapses identically on both sides.
|
package/dist/arabic.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arabic orthographic fold tables for SEARCH match-key normalization.
|
|
3
|
+
*
|
|
4
|
+
* Vendored from Lucene's ArabicNormalizer (Apache-2.0) and cross-checked
|
|
5
|
+
* against CAMeL Tools (MIT), then extended for the classes Lucene omits:
|
|
6
|
+
* alef-wasla, waw/yeh/standalone hamza, superscript alef, and Arabic-Indic
|
|
7
|
+
* digits. Fold directions (alef variants to bare alef, teh-marbuta to heh,
|
|
8
|
+
* alef-maksura to yeh) are the settled Lucene/CAMeL consensus.
|
|
9
|
+
*
|
|
10
|
+
* These folds are lossy match-key transforms: never apply them to stored
|
|
11
|
+
* or displayed text, only to search keys.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ARABIC_LETTER_FOLDS: Readonly<Record<string, string>>;
|
|
14
|
+
export declare const ARABIC_REMOVED: readonly string[];
|
|
15
|
+
export declare const ARABIC_DIGIT_FOLDS: Readonly<Record<string, string>>;
|
|
16
|
+
/**
|
|
17
|
+
* Apply the Arabic letter, digit, and removal folds codepoint by
|
|
18
|
+
* codepoint. Non-Arabic characters pass through unchanged.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately does not normalize. Unicode normalization is whole-string
|
|
21
|
+
* work — NFKC composes across codepoint boundaries — so it belongs to
|
|
22
|
+
* the caller, before the fold: `arabicNormalize` applies NFKC to the
|
|
23
|
+
* whole string and then calls this. Folding is a per-codepoint table
|
|
24
|
+
* lookup and stays that way.
|
|
25
|
+
*/
|
|
26
|
+
export declare const applyArabicFolds: (text: string) => string;
|
|
27
|
+
export type FoldedText = {
|
|
28
|
+
sourceEndIndex: number[];
|
|
29
|
+
text: string;
|
|
30
|
+
truncated: boolean;
|
|
31
|
+
sourceIndex: number[];
|
|
32
|
+
};
|
|
33
|
+
type ApplyArabicFoldsWithOffsetsOptions = {
|
|
34
|
+
maxFoldedUnits?: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Fold Arabic variants and return an offset map, so a caller matching
|
|
38
|
+
* against the folded text (find-in-page, search previews) can slice the
|
|
39
|
+
* original at the right positions.
|
|
40
|
+
*
|
|
41
|
+
* Not simply `applyArabicFolds` plus offsets. This variant additionally
|
|
42
|
+
* normalizes NFKC one character at a time, because it is meant for raw
|
|
43
|
+
* document text: Arabic PDFs carry presentation forms (`ﺍﺣﻤﺪ`) and
|
|
44
|
+
* ligatures (`ﷲ`, `ﷺ`) that must expand to canonical letters to be
|
|
45
|
+
* searchable at all. Per character, not whole string, is what keeps
|
|
46
|
+
* every unit of an expansion pointing back at the one source character
|
|
47
|
+
* it came from — whole-string NFKC composes across boundaries and would
|
|
48
|
+
* describe positions the map no longer has. The `maxFoldedUnits` budget
|
|
49
|
+
* exists for the same reason: one ligature can expand to eighteen
|
|
50
|
+
* characters.
|
|
51
|
+
*
|
|
52
|
+
* The consequence is a deliberate asymmetry with `applyArabicFolds`,
|
|
53
|
+
* which never normalizes. On a compatibility character the two disagree:
|
|
54
|
+
*
|
|
55
|
+
* applyArabicFolds("fi") === "fi"
|
|
56
|
+
* applyArabicFoldsWithOffsets("fi") === "fi"
|
|
57
|
+
*
|
|
58
|
+
* They agree on NFKC-normalized input, and that is the state both sides
|
|
59
|
+
* of a comparison are in wherever the two are used together: the API's
|
|
60
|
+
* search highlighter folds its source through `normalizeSourceWithMappings`
|
|
61
|
+
* and each candidate through `arabicNormalize`, and both apply
|
|
62
|
+
* whole-string NFKC before either fold runs. Feeding raw text to one side
|
|
63
|
+
* and normalized text to the other is what would misplace a highlight, so
|
|
64
|
+
* normalize both or neither.
|
|
65
|
+
*/
|
|
66
|
+
export declare const applyArabicFoldsWithOffsets: (input: string, options?: ApplyArabicFoldsWithOffsetsOptions) => FoldedText;
|
|
67
|
+
export {};
|
package/dist/arabic.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arabic orthographic fold tables for SEARCH match-key normalization.
|
|
3
|
+
*
|
|
4
|
+
* Vendored from Lucene's ArabicNormalizer (Apache-2.0) and cross-checked
|
|
5
|
+
* against CAMeL Tools (MIT), then extended for the classes Lucene omits:
|
|
6
|
+
* alef-wasla, waw/yeh/standalone hamza, superscript alef, and Arabic-Indic
|
|
7
|
+
* digits. Fold directions (alef variants to bare alef, teh-marbuta to heh,
|
|
8
|
+
* alef-maksura to yeh) are the settled Lucene/CAMeL consensus.
|
|
9
|
+
*
|
|
10
|
+
* These folds are lossy match-key transforms: never apply them to stored
|
|
11
|
+
* or displayed text, only to search keys.
|
|
12
|
+
*/
|
|
13
|
+
// One-to-one letter folds (source codepoint to target codepoint).
|
|
14
|
+
export const ARABIC_LETTER_FOLDS = {
|
|
15
|
+
آ: "ا", // آ alef madda -> ا alef
|
|
16
|
+
أ: "ا", // أ alef hamza above -> ا alef
|
|
17
|
+
إ: "ا", // إ alef hamza below -> ا alef
|
|
18
|
+
ٱ: "ا", // ٱ alef wasla -> ا alef
|
|
19
|
+
ؤ: "و", // ؤ waw hamza -> و waw
|
|
20
|
+
ئ: "ي", // ئ yeh hamza -> ي yeh
|
|
21
|
+
ة: "ه", // ة teh marbuta -> ه heh
|
|
22
|
+
ى: "ي", // ى alef maksura -> ي yeh
|
|
23
|
+
};
|
|
24
|
+
// Codepoints folded to nothing (removed). Tatweel, the eight harakat
|
|
25
|
+
// (U+064B–U+0652), decomposed hamza/madda marks, superscript alef, and
|
|
26
|
+
// standalone hamza.
|
|
27
|
+
export const ARABIC_REMOVED = [
|
|
28
|
+
"ء", // ء standalone hamza
|
|
29
|
+
"ـ", // ـ tatweel / kashida
|
|
30
|
+
"ً", // fathatan
|
|
31
|
+
"ٌ", // dammatan
|
|
32
|
+
"ٍ", // kasratan
|
|
33
|
+
"َ", // fatha
|
|
34
|
+
"ُ", // damma
|
|
35
|
+
"ِ", // kasra
|
|
36
|
+
"ّ", // shadda
|
|
37
|
+
"ْ", // sukun
|
|
38
|
+
"ٓ", // madda above
|
|
39
|
+
"ٔ", // hamza above
|
|
40
|
+
"ٕ", // hamza below
|
|
41
|
+
"ٰ", // superscript alef
|
|
42
|
+
];
|
|
43
|
+
// Arabic-Indic (U+0660–0669) and Extended Arabic-Indic (U+06F0–06F9)
|
|
44
|
+
// digits to ASCII. Same mapping as @stll/stdnum's internal digit table;
|
|
45
|
+
// inlined here to avoid depending on another package's private util.
|
|
46
|
+
export const ARABIC_DIGIT_FOLDS = {
|
|
47
|
+
"٠": "0",
|
|
48
|
+
"١": "1",
|
|
49
|
+
"٢": "2",
|
|
50
|
+
"٣": "3",
|
|
51
|
+
"٤": "4",
|
|
52
|
+
"٥": "5",
|
|
53
|
+
"٦": "6",
|
|
54
|
+
"٧": "7",
|
|
55
|
+
"٨": "8",
|
|
56
|
+
"٩": "9",
|
|
57
|
+
"۰": "0",
|
|
58
|
+
"۱": "1",
|
|
59
|
+
"۲": "2",
|
|
60
|
+
"۳": "3",
|
|
61
|
+
"۴": "4",
|
|
62
|
+
"۵": "5",
|
|
63
|
+
"۶": "6",
|
|
64
|
+
"۷": "7",
|
|
65
|
+
"۸": "8",
|
|
66
|
+
"۹": "9",
|
|
67
|
+
};
|
|
68
|
+
const FOLD_MAP = new Map([
|
|
69
|
+
...Object.entries(ARABIC_LETTER_FOLDS),
|
|
70
|
+
...Object.entries(ARABIC_DIGIT_FOLDS),
|
|
71
|
+
...ARABIC_REMOVED.map((char) => [char, ""]),
|
|
72
|
+
]);
|
|
73
|
+
/**
|
|
74
|
+
* Apply the Arabic letter, digit, and removal folds codepoint by
|
|
75
|
+
* codepoint. Non-Arabic characters pass through unchanged.
|
|
76
|
+
*
|
|
77
|
+
* Deliberately does not normalize. Unicode normalization is whole-string
|
|
78
|
+
* work — NFKC composes across codepoint boundaries — so it belongs to
|
|
79
|
+
* the caller, before the fold: `arabicNormalize` applies NFKC to the
|
|
80
|
+
* whole string and then calls this. Folding is a per-codepoint table
|
|
81
|
+
* lookup and stays that way.
|
|
82
|
+
*/
|
|
83
|
+
export const applyArabicFolds = (text) => {
|
|
84
|
+
// Fast path for the single-code-unit calls made character-by-character
|
|
85
|
+
// when building offset maps; avoids an array allocation per character.
|
|
86
|
+
if (text.length === 1) {
|
|
87
|
+
return FOLD_MAP.get(text) ?? text;
|
|
88
|
+
}
|
|
89
|
+
const out = [];
|
|
90
|
+
for (const char of text) {
|
|
91
|
+
out.push(FOLD_MAP.get(char) ?? char);
|
|
92
|
+
}
|
|
93
|
+
return out.join("");
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Fold Arabic variants and return an offset map, so a caller matching
|
|
97
|
+
* against the folded text (find-in-page, search previews) can slice the
|
|
98
|
+
* original at the right positions.
|
|
99
|
+
*
|
|
100
|
+
* Not simply `applyArabicFolds` plus offsets. This variant additionally
|
|
101
|
+
* normalizes NFKC one character at a time, because it is meant for raw
|
|
102
|
+
* document text: Arabic PDFs carry presentation forms (`ﺍﺣﻤﺪ`) and
|
|
103
|
+
* ligatures (`ﷲ`, `ﷺ`) that must expand to canonical letters to be
|
|
104
|
+
* searchable at all. Per character, not whole string, is what keeps
|
|
105
|
+
* every unit of an expansion pointing back at the one source character
|
|
106
|
+
* it came from — whole-string NFKC composes across boundaries and would
|
|
107
|
+
* describe positions the map no longer has. The `maxFoldedUnits` budget
|
|
108
|
+
* exists for the same reason: one ligature can expand to eighteen
|
|
109
|
+
* characters.
|
|
110
|
+
*
|
|
111
|
+
* The consequence is a deliberate asymmetry with `applyArabicFolds`,
|
|
112
|
+
* which never normalizes. On a compatibility character the two disagree:
|
|
113
|
+
*
|
|
114
|
+
* applyArabicFolds("fi") === "fi"
|
|
115
|
+
* applyArabicFoldsWithOffsets("fi") === "fi"
|
|
116
|
+
*
|
|
117
|
+
* They agree on NFKC-normalized input, and that is the state both sides
|
|
118
|
+
* of a comparison are in wherever the two are used together: the API's
|
|
119
|
+
* search highlighter folds its source through `normalizeSourceWithMappings`
|
|
120
|
+
* and each candidate through `arabicNormalize`, and both apply
|
|
121
|
+
* whole-string NFKC before either fold runs. Feeding raw text to one side
|
|
122
|
+
* and normalized text to the other is what would misplace a highlight, so
|
|
123
|
+
* normalize both or neither.
|
|
124
|
+
*/
|
|
125
|
+
export const applyArabicFoldsWithOffsets = (input, options = {}) => {
|
|
126
|
+
const parts = [];
|
|
127
|
+
const sourceEndIndex = [];
|
|
128
|
+
const sourceIndex = [];
|
|
129
|
+
const maxFoldedUnits = options.maxFoldedUnits ?? Number.POSITIVE_INFINITY;
|
|
130
|
+
let foldedUnits = 0;
|
|
131
|
+
let originalUnit = 0;
|
|
132
|
+
let truncated = false;
|
|
133
|
+
for (const char of input) {
|
|
134
|
+
const replacement = applyArabicFolds(char.normalize("NFKC"));
|
|
135
|
+
if (foldedUnits + replacement.length > maxFoldedUnits) {
|
|
136
|
+
truncated = true;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
const originalEnd = originalUnit + char.length;
|
|
140
|
+
parts.push(replacement);
|
|
141
|
+
// One offset entry per UTF-16 code unit of the replacement; folds are
|
|
142
|
+
// BMP, but an unfolded astral passthrough spans two code units.
|
|
143
|
+
let unit = 0;
|
|
144
|
+
while (unit < replacement.length) {
|
|
145
|
+
sourceIndex.push(originalUnit);
|
|
146
|
+
sourceEndIndex.push(originalEnd);
|
|
147
|
+
unit += 1;
|
|
148
|
+
}
|
|
149
|
+
foldedUnits += replacement.length;
|
|
150
|
+
originalUnit = originalEnd;
|
|
151
|
+
}
|
|
152
|
+
sourceIndex.push(originalUnit);
|
|
153
|
+
sourceEndIndex.push(originalUnit);
|
|
154
|
+
return { sourceEndIndex, text: parts.join(""), truncated, sourceIndex };
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=arabic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arabic.js","sourceRoot":"","sources":["../src/arabic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Latin-script fold table: the characters PostgreSQL `unaccent()` folds that
|
|
3
|
+
* NFD plus combining-mark removal cannot reach, either because the letter has
|
|
4
|
+
* no canonical decomposition (`ł`, `đ`, `ø`, `ß`) or because Unicode
|
|
5
|
+
* classifies it as a diacritic and a mark strip deletes it outright (`ʰ`, `ᴬ`).
|
|
6
|
+
*
|
|
7
|
+
* Generated from the extension's own rules, so the two sides cannot drift by
|
|
8
|
+
* hand:
|
|
9
|
+
*
|
|
10
|
+
* ```sql
|
|
11
|
+
* SELECT cp, unaccent(chr(cp))
|
|
12
|
+
* FROM generate_series(1, 196607) AS cp
|
|
13
|
+
* WHERE NOT (cp BETWEEN 55296 AND 57343)
|
|
14
|
+
* AND unaccent(chr(cp)) IS DISTINCT FROM chr(cp);
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* A row becomes an entry below when its character is `\p{Script=Latin}` and its
|
|
18
|
+
* `unaccent` output differs from NFD plus mark removal. Non-Latin scripts stay
|
|
19
|
+
* out of the contract and pass through unchanged; see `foldToAscii`.
|
|
20
|
+
*
|
|
21
|
+
* `apps/api/src/lib/search/ascii-fold-unaccent-parity.db.test.ts` re-runs the
|
|
22
|
+
* query against a live PostgreSQL and asserts both directions, so an extension
|
|
23
|
+
* upgrade that adds, drops, or retargets a rule fails CI instead of silently
|
|
24
|
+
* splitting the query-side fold from the indexed lexemes.
|
|
25
|
+
*
|
|
26
|
+
* Keys are single code points; values are ASCII, which is what makes
|
|
27
|
+
* `foldToAscii` idempotent.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ASCII_FOLD_TABLE: Readonly<Record<string, string>>;
|