gs-tokenizer 0.1.0

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.ko.md ADDED
@@ -0,0 +1,262 @@
1
+ # gs-tokenizer
2
+
3
+ 영어, 중국어, 일본어, 한국어 등 다국어를 지원하는 강력하고 가벼운 다국어 토크나이저 라이브러리입니다.
4
+
5
+ ## 문서
6
+
7
+ - [English README](README.md)
8
+ - [中文 README](README.cn.md)
9
+ - [日本語 README](README.ja.md)
10
+ - [한국어 README](README.ko.md)
11
+
12
+ ## 기능
13
+
14
+ - **언어 지원**: 영어, 중국어, 일본어, 한국어
15
+ - **지능형 토큰화**:
16
+ - 영어: 단어 경계 기반 토큰화
17
+ - CJK(중국어, 일본어, 한국어): 브라우저의 Intl.Segmenter를 사용한 자연스러운 단어 분할
18
+ - 날짜: 날짜 패턴에 대한 특별 처리
19
+ - **사용자 정의 사전**: 우선순위와 이름이 있는 사용자 정의 단어 추가 지원
20
+ - **자동 언어 감지**: 입력 텍스트의 언어를 자동으로 감지
21
+ - **다양한 출력 형식**: 상세한 토큰 정보 또는 단어 목록만 가져오기
22
+ - **가벼움**: 최소한의 종속성, 브라우저 환경에 최적화
23
+ - **빠른 사용 API**: 쉽게 통합할 수 있는 편리한 정적 메서드
24
+
25
+ ## 설치
26
+
27
+ ```bash
28
+ yarn add gs-tokenizer
29
+ ```
30
+
31
+ ### 대체 설치 방법
32
+
33
+ ```bash
34
+ npm install gs-tokenizer
35
+ ```
36
+
37
+ ## 사용 방법
38
+
39
+ ### 기본 사용법
40
+
41
+ ```javascript
42
+ import { MultilingualTokenizer, createTokenizer } from 'gs-tokenizer';
43
+
44
+ // 토크나이저 인스턴스 생성
45
+ const tokenizer = new MultilingualTokenizer();
46
+ // 또는 팩토리 함수 사용
47
+ // const tokenizer = createTokenizer();
48
+
49
+ // 텍스트 토큰화
50
+ const text = 'Hello world! 나는 북경 천안문을 좋아합니다.';
51
+ const tokens = tokenizer.tokenize(text);
52
+ console.log(tokens);
53
+
54
+ // 단어 토큰만 가져오기
55
+ const words = tokenizer.tokenizeToText(text);
56
+ console.log(words);
57
+ ```
58
+
59
+ ### 빠른 사용 (권장)
60
+
61
+ quick 모듈은 쉽게 통합할 수 있는 편리한 정적 메서드를 제공합니다:
62
+
63
+ ```javascript
64
+ import { tokenize, tokenizeToText, addCustomDictionary } from 'gs-tokenizer';
65
+
66
+ // 인스턴스 생성 없이 직접 토큰화
67
+ const text = 'Hello world! 나는 북경 천안문을 좋아합니다.';
68
+ const tokens = tokenize(text);
69
+ const words = tokenizeToText(text);
70
+ console.log(words);
71
+
72
+ // 사용자 정의 사전 추가
73
+ addCustomDictionary(['인공지능', '기술'], 'zh', 10, 'tech');
74
+ ```
75
+
76
+ ### 고급 사용법
77
+
78
+ #### 빠른 모듈로 사용자 정의 사전 로드
79
+
80
+ ```javascript
81
+ import { tokenize, addCustomDictionary } from 'gs-tokenizer';
82
+
83
+ // 다른 언어에 대한 여러 사용자 정의 사전 로드
84
+ addCustomDictionary(['인공지능', '머신러닝'], 'zh', 10, 'tech');
85
+ addCustomDictionary(['Web3', 'Blockchain'], 'en', 10, 'crypto');
86
+ addCustomDictionary(['인공지능'], 'ko', 10, 'tech-ko');
87
+
88
+ // 사용자 정의 사전을 적용하여 토큰화
89
+ const text = '인공지능과 Web3는 미래의 중요한 기술입니다. 인공지능도 중요합니다.';
90
+ const tokens = tokenize(text);
91
+ console.log(tokens.filter(token => token.src === 'tech'));
92
+ ```
93
+
94
+ #### 기본 사전 사용 안함
95
+
96
+ ```javascript
97
+ import { MultilingualTokenizer } from 'gs-tokenizer';
98
+
99
+ // 기본 사전을 사용하지 않는 토크나이저 생성
100
+ const tokenizer = new MultilingualTokenizer({
101
+ customDictionaries: {
102
+ 'ko': [{ priority: 10, data: new Set(['사용자정의단어']), name: 'custom', lang: 'ko' }]
103
+ }
104
+ });
105
+
106
+ // 사용자 정의 사전만 사용하여 토큰화
107
+ const text = '이것은 사용자정의단어의 예입니다.';
108
+ const tokens = tokenizer.tokenize(text, 'ko');
109
+ console.log(tokens);
110
+ ```
111
+
112
+ ### 사용자 정의 사전
113
+
114
+ ```javascript
115
+ const tokenizer = new MultilingualTokenizer();
116
+
117
+ // 언어, 우선순위, 이름을 지정하여 사용자 정의 단어 추가
118
+ okenizer.addCustomDictionary(['인공지능', '기술'], 'zh', 10, 'tech');
119
+ okenizer.addCustomDictionary(['Python', 'JavaScript'], 'en', 5, 'programming');
120
+
121
+ const text = '나는 인공지능 기술과 Python 프로그래밍을 좋아합니다';
122
+ const tokens = tokenizer.tokenize(text);
123
+ const words = tokenizer.tokenizeToText(text);
124
+ console.log(words); // '인공지능', 'Python'이 포함되어야 함
125
+
126
+ // 사용자 정의 단어 삭제
127
+ okenizer.removeCustomWord('Python', 'en', 'programming');
128
+ ```
129
+
130
+ ### 고급 옵션
131
+
132
+ ```javascript
133
+ const tokenizer = createTokenizer({
134
+ defaultLanguage: 'ko',
135
+ customDictionaries: {
136
+ 'ko': [{
137
+ priority: 10,
138
+ data: new Set(['사용자정의단어']),
139
+ name: 'custom',
140
+ lang: 'ko'
141
+ }]
142
+ }
143
+ });
144
+
145
+ // 지정된 언어로 토큰화
146
+ const text = '나는 북경 천안문을 좋아합니다';
147
+ const tokens = tokenizer.tokenize(text, 'zh');
148
+ ```
149
+
150
+ ## API 참조
151
+
152
+ ### `MultilingualTokenizer`
153
+
154
+ 다국어 텍스트 처리를 담당하는 주요 토크나이저 클래스입니다.
155
+
156
+ #### 생성자
157
+
158
+ ```typescript
159
+ import { MultilingualTokenizer, TokenizerOptions } from 'gs-tokenizer';
160
+
161
+ new MultilingualTokenizer(options?: TokenizerOptions)
162
+ ```
163
+
164
+ **옵션**:
165
+ - `customDictionaries`: Record<string, LexiconEntry[]> - 각 언어에 대한 사용자 정의 사전
166
+ - `defaultLanguage`: string - 기본 언어 코드 (기본값: 'en')
167
+
168
+ #### 메서드
169
+
170
+ | 메서드 | 설명 |
171
+ |------|------|
172
+ | `tokenize(text: string, language?: string): Token[]` | 입력 텍스트를 토큰화하고 상세한 토큰 정보를 반환합니다 |
173
+ | `tokenizeToText(text: string, language?: string): string[]` | 입력 텍스트를 토큰화하고 단어 목록만 반환합니다 |
174
+ | `addCustomDictionary(words: string[], language: string, priority: number, name: string): void` | 토크나이저에 사용자 정의 단어를 추가합니다 |
175
+ | `removeCustomWord(word: string, language?: string, lexiconName?: string): void` | 토크나이저에서 사용자 정의 단어를 제거합니다 |
176
+
177
+ ### `createTokenizer(options?: TokenizerOptions): MultilingualTokenizer`
178
+
179
+ 선택적 구성으로 새로운 MultilingualTokenizer 인스턴스를 생성하는 팩토리 함수입니다.
180
+
181
+ ### 빠른 사용 API
182
+
183
+ quick 모듈은 편리한 정적 메서드를 제공합니다:
184
+
185
+ ```typescript
186
+ import { Token } from 'gs-tokenizer';
187
+
188
+ // 텍스트 토큰화
189
+ function tokenize(text: string, language?: string): Token[];
190
+
191
+ // 텍스트만 토큰화
192
+ function tokenizeToText(text: string, language?: string): string[];
193
+
194
+ // 사용자 정의 사전 추가
195
+ function addCustomDictionary(words: string[], language: string, priority: number, name: string): void;
196
+
197
+ // 사용자 정의 단어 제거
198
+ function removeCustomWord(word: string, language?: string, lexiconName?: string): void;
199
+
200
+ // 사전 로딩의 기본 언어 설정
201
+ function setDefaultLanguages(languages: string[]): void;
202
+
203
+ // 사전 로딩의 기본 타입 설정
204
+ function setDefaultTypes(types: string[]): void;
205
+ ```
206
+
207
+ ### 타입
208
+
209
+ #### `Token` 인터페이스
210
+
211
+ ```typescript
212
+ interface Token {
213
+ txt: string; // 토큰 텍스트 내용
214
+ type: 'word' | 'punctuation' | 'space' | 'other' | 'emoji' | 'date';
215
+ lang?: string; // 언어 코드
216
+ src?: string; // 소스 (예: 사용자 정의 사전 이름)
217
+ }
218
+ ```
219
+
220
+ #### `TokenizerOptions` 인터페이스
221
+
222
+ ```typescript
223
+ import { LexiconEntry } from 'gs-tokenizer';
224
+
225
+ interface TokenizerOptions {
226
+ customDictionaries?: Record<string, LexiconEntry[]>;
227
+ granularity?: 'word' | 'grapheme' | 'sentence';
228
+ defaultLanguage?: string;
229
+ }
230
+ ```
231
+
232
+ ## 브라우저 호환성
233
+
234
+ - Chrome/Edge: 87+
235
+ - Firefox: 86+
236
+ - Safari: 14.1+
237
+
238
+ 참고: CJK 언어에 `Intl.Segmenter`를 사용하므로 최신 브라우저 지원이 필요합니다.
239
+
240
+ ## 개발
241
+
242
+ ### 빌드
243
+
244
+ ```bash
245
+ npm run build
246
+ ```
247
+
248
+ ### 테스트 실행
249
+
250
+ ```bash
251
+ npm run test # 모든 테스트 실행
252
+ npm run test:base # 기본 테스트 실행
253
+ npm run test:english # 영어 특정 테스트 실행
254
+ npm run test:cjk # CJK 특정 테스트 실행
255
+ npm run test:mixed # 혼합 언어 테스트 실행
256
+ ```
257
+
258
+ ## 라이선스
259
+
260
+ MIT
261
+
262
+ [GitHub Repository](https://github.com/grain-sand/gs-tokenizer)
package/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # gs-tokenizer
2
+
3
+ A powerful and lightweight multilingual tokenizer library that provides natural language processing capabilities for multiple languages including English, Chinese, Japanese, and Korean.
4
+
5
+ ## Documentation
6
+
7
+ - [English README](README.md)
8
+ - [中文 README](README.cn.md)
9
+ - [日本語 README](README.ja.md)
10
+ - [한국어 README](README.ko.md)
11
+
12
+ ## Features
13
+
14
+ - **Language Support**: English, Chinese, Japanese, Korean
15
+ - **Intelligent Tokenization**:
16
+ - English: Word boundary-based tokenization
17
+ - CJK (Chinese, Japanese, Korean): Natural word segmentation using browser's Intl.Segmenter
18
+ - Date: Special handling for date patterns
19
+ - **Custom Dictionary**: Support for adding custom words with priority and name
20
+ - **Auto Language Detection**: Automatically detects the language of input text
21
+ - **Multiple Output Formats**: Get detailed token information or just word lists
22
+ - **Lightweight**: Minimal dependencies, designed for browser environments
23
+ - **Quick Use API**: Convenient static methods for easy integration
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ yarn add gs-tokenizer
29
+ ```
30
+
31
+ ### Alternative Installation
32
+
33
+ ```bash
34
+ npm install gs-tokenizer
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Basic Usage
40
+
41
+ ```javascript
42
+ import { MultilingualTokenizer, createTokenizer } from 'gs-tokenizer';
43
+
44
+ // Create tokenizer instance
45
+ const tokenizer = new MultilingualTokenizer();
46
+ // or using factory function
47
+ // const tokenizer = createTokenizer();
48
+
49
+ // Tokenize text
50
+ const text = 'Hello world! 我爱北京天安门。';
51
+ const tokens = tokenizer.tokenize(text);
52
+ console.log(tokens);
53
+
54
+ // Get only word tokens
55
+ const words = tokenizer.tokenizeToText(text);
56
+ console.log(words);
57
+ ```
58
+
59
+ ### Quick Use (Recommended)
60
+
61
+ The quick module provides convenient static methods for easy integration:
62
+
63
+ ```javascript
64
+ import { tokenize, tokenizeToText, addCustomDictionary } from 'gs-tokenizer';
65
+
66
+ // Direct tokenization without creating an instance
67
+ const text = 'Hello world! 我爱北京天安门。';
68
+ const tokens = tokenize(text);
69
+ const words = tokenizeToText(text);
70
+ console.log(words);
71
+
72
+ // Add custom dictionary
73
+ addCustomDictionary(['人工智能', '技术'], 'zh', 10, 'tech');
74
+ ```
75
+
76
+ ### Advanced Usage
77
+
78
+ #### Load Custom Dictionary with Quick Module
79
+
80
+ ```javascript
81
+ import { tokenize, addCustomDictionary } from 'gs-multilingual-tokenizer';
82
+
83
+ // Load multiple custom dictionaries for different languages
84
+ addCustomDictionary(['人工智能', '机器学习'], 'zh', 10, 'tech');
85
+ addCustomDictionary(['Web3', 'Blockchain'], 'en', 10, 'crypto');
86
+ addCustomDictionary(['アーティフィシャル・インテリジェンス'], 'ja', 10, 'tech-ja');
87
+
88
+ // Tokenize with custom dictionaries applied
89
+ const text = '人工智能和Web3是未来的重要技术。アーティフィシャル・インテリジェンスも重要です。';
90
+ const tokens = tokenize(text);
91
+ console.log(tokens.filter(token => token.src === 'tech'));
92
+ ```
93
+
94
+ #### Without Built-in Lexicon
95
+
96
+ ```javascript
97
+ import { MultilingualTokenizer } from 'gs-tokenizer';
98
+
99
+ // Create tokenizer without using built-in lexicon
100
+ const tokenizer = new MultilingualTokenizer({
101
+ customDictionaries: {
102
+ 'zh': [{ priority: 10, data: new Set(['自定义词']), name: 'custom', lang: 'zh' }]
103
+ }
104
+ });
105
+
106
+ // Tokenize using only custom dictionary
107
+ const text = '这是一个自定义词的示例。';
108
+ const tokens = tokenizer.tokenize(text, 'zh');
109
+ console.log(tokens);
110
+ ```
111
+
112
+ ### Custom Dictionary
113
+
114
+ ```javascript
115
+ const tokenizer = new MultilingualTokenizer();
116
+
117
+ // Add custom words with language, priority, and name
118
+ tokenizer.addCustomDictionary(['人工智能', '技术'], 'zh', 10, 'tech');
119
+ tokenizer.addCustomDictionary(['Python', 'JavaScript'], 'en', 5, 'programming');
120
+
121
+ const text = '我爱人工智能技术和Python编程';
122
+ const tokens = tokenizer.tokenize(text);
123
+ const words = tokenizer.tokenizeToText(text);
124
+ console.log(words); // Should include '人工智能', 'Python'
125
+
126
+ // Remove custom word
127
+ tokenizer.removeCustomWord('Python', 'en', 'programming');
128
+ ```
129
+
130
+ ### Advanced Options
131
+
132
+ ```javascript
133
+ const tokenizer = createTokenizer({
134
+ defaultLanguage: 'en',
135
+ customDictionaries: {
136
+ 'zh': [{
137
+ priority: 10,
138
+ data: new Set(['自定义词']),
139
+ name: 'custom',
140
+ lang: 'zh'
141
+ }]
142
+ }
143
+ });
144
+
145
+ // Tokenize with specified language
146
+ const text = '我爱北京天安门';
147
+ const tokens = tokenizer.tokenize(text, 'zh');
148
+ ```
149
+
150
+ ## API Reference
151
+
152
+ ### `MultilingualTokenizer`
153
+
154
+ Main tokenizer class that handles multilingual text processing.
155
+
156
+ #### Constructor
157
+
158
+ ```typescript
159
+ import { MultilingualTokenizer, TokenizerOptions } from 'gs-tokenizer';
160
+
161
+ new MultilingualTokenizer(options?: TokenizerOptions)
162
+ ```
163
+
164
+ **Options**:
165
+ - `customDictionaries`: Record<string, LexiconEntry[]> - Custom dictionaries for each language
166
+ - `defaultLanguage`: string - Default language code (default: 'en')
167
+
168
+ #### Methods
169
+
170
+ | Method | Description |
171
+ |--------|-------------|
172
+ | `tokenize(text: string, language?: string): Token[]` | Tokenizes the input text and returns detailed token information |
173
+ | `tokenizeToText(text: string, language?: string): string[]` | Tokenizes the input text and returns only word tokens |
174
+ | `addCustomDictionary(words: string[], language: string, priority: number, name: string): void` | Adds custom words to the tokenizer |
175
+ | `removeCustomWord(word: string, language?: string, lexiconName?: string): void` | Removes a custom word from the tokenizer |
176
+
177
+ ### `createTokenizer(options?: TokenizerOptions): MultilingualTokenizer`
178
+
179
+ Factory function to create a new MultilingualTokenizer instance with optional configuration.
180
+
181
+ ### Quick Use API
182
+
183
+ The quick module provides convenient static methods:
184
+
185
+ ```typescript
186
+ import { Token } from 'gs-tokenizer';
187
+
188
+ // Tokenize text
189
+ function tokenize(text: string, language?: string): Token[];
190
+
191
+ // Tokenize to text only
192
+ function tokenizeToText(text: string, language?: string): string[];
193
+
194
+ // Add custom dictionary
195
+ function addCustomDictionary(words: string[], language: string, priority: number, name: string): void;
196
+
197
+ // Remove custom word
198
+ function removeCustomWord(word: string, language?: string, lexiconName?: string): void;
199
+
200
+ // Set default languages for lexicon loading
201
+ function setDefaultLanguages(languages: string[]): void;
202
+
203
+ // Set default types for lexicon loading
204
+ function setDefaultTypes(types: string[]): void;
205
+ ```
206
+
207
+ ### Types
208
+
209
+ #### `Token` Interface
210
+
211
+ ```typescript
212
+ interface Token {
213
+ txt: string; // Token text content
214
+ type: 'word' | 'punctuation' | 'space' | 'other' | 'emoji' | 'date';
215
+ lang?: string; // Language code
216
+ src?: string; // Source (e.g., custom dictionary name)
217
+ }
218
+ ```
219
+
220
+ #### `TokenizerOptions` Interface
221
+
222
+ ```typescript
223
+ import { LexiconEntry } from 'gs-tokenizer';
224
+
225
+ interface TokenizerOptions {
226
+ customDictionaries?: Record<string, LexiconEntry[]>;
227
+ granularity?: 'word' | 'grapheme' | 'sentence';
228
+ defaultLanguage?: string;
229
+ }
230
+ ```
231
+
232
+ ## Browser Compatibility
233
+
234
+ - Chrome/Edge: 87+
235
+ - Firefox: 86+
236
+ - Safari: 14.1+
237
+
238
+ Note: Uses `Intl.Segmenter` for CJK languages, which requires modern browser support.
239
+
240
+ ## Development
241
+
242
+ ### Build
243
+
244
+ ```bash
245
+ npm run build
246
+ ```
247
+
248
+ ### Run Tests
249
+
250
+ ```bash
251
+ npm run test # Run all tests
252
+ npm run test:base # Run base tests
253
+ npm run test:english # Run English-specific tests
254
+ npm run test:cjk # Run CJK-specific tests
255
+ npm run test:mixed # Run mixed language tests
256
+ ```
257
+
258
+ ## License
259
+
260
+ MIT
261
+
262
+ [GitHub Repository](https://github.com/grain-sand/gs-tokenizer)
package/lib/core.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";class t{customDictionaries;constructor(t={}){this.customDictionaries=t}detectLanguage(t){return/[a-zA-Z]/.test(t)&&!/[一-鿿]/.test(t)?"en":""}tokenize(t,e){const s=[],n=t.split(/\b/);for(const t of n)t&&(t.match(/^\s+$/)?s.push({txt:t,type:"space",lang:e,src:""}):/^\p{Emoji}+$/u.test(t)&&!/[0-9]/.test(t)?s.push({txt:t,type:"emoji",lang:e,src:""}):t.match(/^[^a-zA-Z0-9]+$/)?s.push({txt:t,type:"punctuation",lang:e,src:""}):t.match(/^[a-zA-Z0-9]+$/)?s.push({txt:t,type:"word",lang:e,src:""}):s.push({txt:t,type:"other",lang:e,src:""}));return this.tagNameTokens(s,e)}tagNameTokens(t,e){const s=[];let n=0;for(;n<t.length;){if(n<t.length&&"word"===t[n].type){const i=t[n].txt;if(this.customDictionaries[e]){let t=!1;for(const r of this.customDictionaries[e].sort((t,e)=>e.priority-t.priority))if(r.data.has(i)){s.push({txt:i,type:"word",lang:e,src:r.name}),n++,t=!0;break}if(t)continue}}s.push({txt:t[n].txt,type:t[n].type,lang:e,src:t[n].src||""}),n++}return s}}class e{segmenters;customDictionaries;constructor(t={}){this.segmenters=new Map,this.customDictionaries=t}detectLanguage(t){return/[\u4e00-\u9fff]/.test(t)?"zh":/[\u3040-\u309f\u30a0-\u30ff]/.test(t)?"ja":/[\uac00-\ud7af]/.test(t)?"ko":""}tokenize(t,e){const s=[],n=this.getSegmenter(e);for(const i of n.segment(t)){const{segment:t,isWordLike:n}=i;t.match(/^\s+$/)?s.push({txt:t,type:"space",lang:e,src:""}):/^\p{Emoji}+$/u.test(t)&&!/[0-9]/.test(t)?s.push({txt:t,type:"emoji",lang:e,src:""}):t.match(/^[^\p{L}\p{N}]+$/u)?s.push({txt:t,type:"punctuation",lang:e,src:""}):n?s.push({txt:t,type:"word",lang:e,src:""}):s.push({txt:t,type:"other",lang:e,src:""})}return this.applyCustomDictionary(s,e)}getSegmenter(t,e="word"){const s=`${t}-${e}`;return this.segmenters.has(s)||this.segmenters.set(s,new Intl.Segmenter(t,{granularity:e})),this.segmenters.get(s)}applyCustomDictionary(t,e){const s=this.customDictionaries[e]||[];let n=t;if(s.length>0){const t=[];let i=0;for(;i<n.length;){let r=!1;for(let o=Math.min(5,n.length-i);o>=1;o--){if(o>1&&n.slice(i,i+o).some(t=>"word"!==t.type))continue;const a=n.slice(i,i+o).map(t=>t.txt).join("");for(const n of s.sort((t,e)=>e.priority-t.priority))if(n.data.has(a)){t.push({txt:a,type:"word",lang:e,src:""}),i+=o,r=!0;break}if(r)break}r||(t.push({...n[i],src:""}),i++)}n=t}return n}}class s{comprehensiveDatePattern=/\d{8}|\d{4}[-/.]\d{2}[-/.]\d{2}|\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}|(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+\d{1,2}(?:,\s+\d{4})?|\d{4}年\d{1,2}月(?:\d{1,2}日)?|\d{1,2}月\d{1,2}日(?:\d{4}年)?/gi;detectLanguage(t){return""}tokenize(t,e="zh"){const s=[];let n=0;const i=[];for(const e of t.matchAll(this.comprehensiveDatePattern))void 0!==e.index&&e[0]&&i.push({text:e[0],index:e.index});i.sort((t,e)=>t.index-e.index);const r=[];let o=null;for(const e of i){const s=e.index+e.text.length;o?e.index<=o.end?o={text:t.slice(o.index,Math.max(o.end,s)),index:o.index,end:Math.max(o.end,s)}:(r.push({text:o.text,index:o.index}),o={...e,end:s}):o={...e,end:s}}o&&r.push({text:o.text,index:o.index});for(const i of r){if(i.index>n){const r=t.slice(n,i.index);s.push({txt:r,type:"other",lang:e,src:""})}this.isValidDate(i.text)?s.push({txt:i.text,type:"date",lang:e,src:""}):s.push({txt:i.text,type:"other",lang:e,src:""}),n=i.index+i.text.length}if(n<t.length){const i=t.slice(n);s.push({txt:i,type:"other",lang:e,src:""})}return s}isValidDate(t){let e,s,n;if(/^\d{8}$/.test(t))e=parseInt(t.slice(0,4)),s=parseInt(t.slice(4,6)),n=parseInt(t.slice(6,8));else if(/^\d{4}[-.]\d{2}[-.]\d{2}$/.test(t)){const i=t.split(/[-.]/).map(Number);e=i[0],s=i[1],n=i[2]}else if(/^\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}$/.test(t)||/^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(t)){const i=t.split(/[-/.]/).map(Number);i[0]>=1e3?(e=i[0],s=i[1],n=i[2]):i[2]>31?(s=i[0],n=i[1],e=i[2]):(s=i[0],n=i[1],e=i[2]<50?2e3+i[2]:1900+i[2])}else if(/^(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+(\d{1,2})(?:,\s+(\d{4}))?$/i.test(t)){const i=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],r=t.match(/^(\w+)\s+(\d{1,2})(?:,\s+(\d{4}))?$/i);if(!r)return!1;s=i.findIndex(t=>t.toLowerCase()===r[1].substring(0,3).toLowerCase())+1,n=parseInt(r[2]),e=r[3]?parseInt(r[3]):(new Date).getFullYear()}else{if(!/^(\d{4})?年?(\d{1,2})月(\d{1,2})日?(\d{4})?$/.test(t))return!1;{const i=t.match(/^(\d{4})?年?(\d{1,2})月(\d{1,2})日?(\d{4})?$/);if(!i)return!1;e=parseInt(i[1]||i[4]||(new Date).getFullYear().toString()),s=parseInt(i[2]),n=parseInt(i[3])}}return this.isValidDateComponents(e,s,n)}isValidDateComponents(t,e,s){if(e<1||e>12||s<1||s>31)return!1;if(2===e){if(s>(t%4==0&&t%100!=0||t%400==0?29:28))return!1}else if(s>[31,28,31,30,31,30,31,31,30,31,30,31][e-1])return!1;return!0}}class n{static detectLanguage(t){return/[\u3040-\u309f\u30a0-\u30ff]/.test(t)?"ja":/[\uac00-\ud7af]/.test(t)?"ko":/[\u4e00-\u9fff]/.test(t)?"zh":"en"}}class i{detectLanguage(t){return"en"}tokenize(t,e){const s=[];let n=0;const i=/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g,r=/(?:https?:\/\/)?(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?|(?:localhost|127\.0\.0\.1)|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?::\d{1,5})?(?:\/[^\s]*)?/g,o=[];let a;for(;null!==(a=i.exec(t));)o.push({index:a.index,endIndex:a.index+a[0].length,type:"ip",text:a[0]});for(;null!==(a=r.exec(t));)o.push({index:a.index,endIndex:a.index+a[0].length,type:"url",text:a[0]});o.sort((t,e)=>t.index-e.index);const c=[];for(let t=0;t<o.length;t++){let e=!1;for(let s=0;s<o.length;s++)if(t!==s){const n=o[t].index===o[s].index&&o[t].endIndex===o[s].endIndex,i=o[t].index>=o[s].index&&o[t].endIndex<=o[s].endIndex;if(n){if("ip"===o[s].type){e=!0;break}}else if(i){e=!0;break}}e||c.push(o[t])}for(const i of c)i.index>n&&s.push({txt:t.substring(n,i.index),type:"other",lang:e}),s.push({txt:i.text,type:i.type,lang:"en"}),n=i.endIndex;return n<t.length&&s.push({txt:t.substring(n),type:"other",lang:e}),s}}class r{tokenizers;customDictionaries;defaultLanguage;constructor(n={}){this.customDictionaries=n.customDictionaries||{},this.defaultLanguage=n.defaultLanguage||"en",this.tokenizers=[new s,new i,new t,new e(this.customDictionaries)]}addCustomDictionary(t,e,s,n){const i=e||this.defaultLanguage;this.customDictionaries[i]||(this.customDictionaries[i]=[]);const r=this.customDictionaries[i].findIndex(t=>t.name===n&&t.lang===i&&t.priority===s);if(r>=0){const e=this.customDictionaries[i][r];t.forEach(t=>e.data.add(t))}else this.customDictionaries[i].push({priority:s,data:new Set(t),name:n,lang:i})}removeCustomWord(t,e,s){if(e){if(this.customDictionaries[e])if(s){const n=this.customDictionaries[e].find(t=>t.name===s);n&&n.data.delete(t)}else this.customDictionaries[e].forEach(e=>{e.data.delete(t)})}else Object.values(this.customDictionaries).forEach(e=>{e.forEach(e=>{e.data.has(t)&&e.data.delete(t)})})}tokenize(r,o){const a=o||n.detectLanguage(r),c=this.tokenizers.find(t=>t instanceof s);if(!c)return[];const u=c.tokenize(r,a),d=[],l=this.tokenizers.find(t=>t instanceof i);if(!l)return d;for(const s of u)if("date"===s.type)d.push(s);else{const n=l.tokenize(s.txt,a);for(const s of n)if("url"===s.type||"ip"===s.type)d.push(s);else{let n=[];if("en"===a){const e=this.tokenizers.find(e=>e instanceof t);e&&(n=e.tokenize(s.txt,a))}else if(["zh","ja","ko"].includes(a)){const t=this.tokenizers.find(t=>t instanceof e);t&&(n=t.tokenize(s.txt,a))}else{const t=this.tokenizers.find(t=>t instanceof e);t&&(n=t.tokenize(s.txt,a))}n.length>0?d.push(...n):d.push(s)}}return d}tokenizeText(t,e){const s=this.tokenize(t,e?.language),n=["punctuation","space","other",...e?.excludeTypes||[]];return s.filter(t=>!(e?.includeTypes&&e.includeTypes.length>0&&!e.includeTypes.includes(t.type))&&!n.includes(t.type)).map(t=>t.txt)}}exports.CJKTokenizer=e,exports.DateTokenizer=s,exports.EnglishTokenizer=t,exports.LanguageDetector=n,exports.MultilingualTokenizer=r,exports.createTokenizer=function(t){return new r(t)};