garu-ko 0.3.1 → 0.3.3

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/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export interface AnalyzeResult {
11
11
  score: number;
12
12
  elapsed: number;
13
13
  }
14
+ export { normalizeText, splitSentences } from './normalize.js';
15
+ export type { NormalizeOptions, Segment } from './normalize.js';
14
16
  export interface AnalyzeOptions {
15
17
  topN?: number;
16
18
  }
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { normalizeText, splitSentences } from './normalize.js';
1
2
  const isNode = typeof process !== 'undefined' &&
2
3
  process.versions != null &&
3
4
  process.versions.node != null;
@@ -132,7 +133,7 @@ export class Garu {
132
133
  return {
133
134
  version: this._wasm.constructor.version(),
134
135
  size: this._modelSize,
135
- accuracy: 0.953,
136
+ accuracy: 0.908,
136
137
  };
137
138
  }
138
139
  /**
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Text normalization utilities for Garu Korean morphological analyzer.
3
+ * Pre-processes informal/non-standard Korean text before analysis.
4
+ */
5
+ export interface NormalizeOptions {
6
+ /** Collapse repeated jamo (ㅋㅋㅋ→ㅋㅋ). Default: true */
7
+ jamo?: boolean;
8
+ /** Expand 2-char jamo abbreviations (ㄱㅅ→감사). Default: false */
9
+ jamoAbbrev?: boolean;
10
+ /** Normalize common dialect stems (워디→어디). Default: false */
11
+ dialect?: boolean;
12
+ }
13
+ export declare function normalizeText(text: string, opts?: NormalizeOptions): string;
14
+ export interface Segment {
15
+ text: string;
16
+ /** Character offset of this segment in the original text */
17
+ offset: number;
18
+ }
19
+ export declare function splitSentences(text: string): Segment[];
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Text normalization utilities for Garu Korean morphological analyzer.
3
+ * Pre-processes informal/non-standard Korean text before analysis.
4
+ */
5
+ const COMPAT_JAMO_RE = /([\u3131-\u3163])\1{2,}/g;
6
+ const JAMO_ABBREV_MAP = {
7
+ 'ㄱㅅ': '감사',
8
+ 'ㅊㅋ': '축하',
9
+ 'ㄴㄴ': '노노',
10
+ 'ㅂㅂ': '바이바이',
11
+ 'ㅈㅅ': '죄송',
12
+ 'ㄱㅊ': '괜찮',
13
+ 'ㅇㅈ': '인정',
14
+ 'ㅎㅇ': '하이',
15
+ 'ㄹㅇ': '리얼',
16
+ 'ㅇㅋ': '오케이',
17
+ 'ㅁㄹ': '모름',
18
+ 'ㅅㄱ': '수고',
19
+ 'ㅊㅊ': '추천',
20
+ 'ㅍㅌ': '파이팅',
21
+ 'ㄷㄷ': '덜덜',
22
+ };
23
+ const DIALECT_MAP = {
24
+ '워디': '어디',
25
+ '와케': '왜이렇게',
26
+ '우예': '어떻게',
27
+ '긍게': '그러니까',
28
+ '워메': '어머',
29
+ '가유': '가요',
30
+ '허유': '해요',
31
+ };
32
+ export function normalizeText(text, opts = {}) {
33
+ const { jamo = true, jamoAbbrev = false, dialect = false } = opts;
34
+ let result = text;
35
+ if (jamo) {
36
+ result = result.replace(COMPAT_JAMO_RE, '$1$1');
37
+ }
38
+ if (jamoAbbrev) {
39
+ for (const [abbrev, expansion] of Object.entries(JAMO_ABBREV_MAP)) {
40
+ const re = new RegExp(`(?<![가-힣ㄱ-ㅣ])${abbrev}(?![가-힣ㄱ-ㅣ])`, 'g');
41
+ result = result.replace(re, expansion);
42
+ }
43
+ }
44
+ if (dialect) {
45
+ for (const [dialectForm, standard] of Object.entries(DIALECT_MAP)) {
46
+ result = result.replace(new RegExp(dialectForm, 'g'), standard);
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+ export function splitSentences(text) {
52
+ const segments = [];
53
+ const re = /([.!?…]+)\s+(?=[가-힣A-Z"'([{])/g;
54
+ let lastCharOffset = 0;
55
+ let lastByteIdx = 0;
56
+ let match;
57
+ while ((match = re.exec(text)) !== null) {
58
+ const segText = text.slice(lastByteIdx, match.index + match[1].length).trim();
59
+ if (segText.length > 0) {
60
+ segments.push({ text: segText, offset: lastCharOffset });
61
+ }
62
+ const newStart = match.index + match[0].length;
63
+ lastCharOffset += [...text.slice(lastByteIdx, newStart)].length;
64
+ lastByteIdx = newStart;
65
+ }
66
+ const last = text.slice(lastByteIdx).trim();
67
+ if (last.length > 0) {
68
+ segments.push({ text: last, offset: lastCharOffset });
69
+ }
70
+ return segments.length > 0 ? segments : [{ text: text.trim(), offset: 0 }];
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "garu-ko",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Ultra-lightweight Korean morphological analyzer for the web (1.7MB model, WASM 93KB, F1 95.3%)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,9 +12,10 @@
12
12
  "README.md"
13
13
  ],
14
14
  "scripts": {
15
- "build:wasm": "wasm-pack build ../crates/garu-wasm --target web --out-dir ../../js/pkg && echo '' > pkg/.gitignore",
15
+ "build:wasm": "wasm-pack build ../crates/garu-wasm --target web --out-dir ../../js/pkg && rm -f pkg/.gitignore",
16
16
  "build:js": "tsc",
17
17
  "build": "npm run build:wasm && npm run build:js",
18
+ "prepublishOnly": "rm -f pkg/.gitignore",
18
19
  "test": "vitest run"
19
20
  },
20
21
  "keywords": [
package/pkg/garu_wasm.js CHANGED
@@ -82,7 +82,7 @@ export class GaruWasm {
82
82
  }
83
83
  }
84
84
  if (Symbol.dispose) GaruWasm.prototype[Symbol.dispose] = GaruWasm.prototype.free;
85
- import * as import1 from "./snippets/garu-core-d0bd202007f7afa3/inline0.js"
85
+ import * as import1 from "./snippets/garu-core-8782e7e2eee661b3/inline0.js"
86
86
 
87
87
  function __wbg_get_imports() {
88
88
  const import0 = {
@@ -143,7 +143,7 @@ function __wbg_get_imports() {
143
143
  return {
144
144
  __proto__: null,
145
145
  "./garu_wasm_bg.js": import0,
146
- "./snippets/garu-core-d0bd202007f7afa3/inline0.js": import1,
146
+ "./snippets/garu-core-8782e7e2eee661b3/inline0.js": import1,
147
147
  };
148
148
  }
149
149
 
Binary file
package/pkg/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "garu-wasm",
3
3
  "type": "module",
4
- "version": "0.2.10",
4
+ "version": "0.3.1",
5
5
  "files": [
6
6
  "garu_wasm_bg.wasm",
7
7
  "garu_wasm.js",
@@ -0,0 +1 @@
1
+ export function performance_now() { return performance.now(); }