yomitan-core 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.
Files changed (207) hide show
  1. package/README.md +485 -0
  2. package/dist/anki-connect-BQyCGW3O.cjs +513 -0
  3. package/dist/anki-connect-BQyCGW3O.cjs.map +1 -0
  4. package/dist/anki-connect-CPPuhyiQ.js +6 -0
  5. package/dist/anki-connect-DbrQHphS.js +495 -0
  6. package/dist/anki-connect-DbrQHphS.js.map +1 -0
  7. package/dist/anki-connect-DcheJrp-.cjs +6 -0
  8. package/dist/anki.cjs +1758 -0
  9. package/dist/anki.cjs.map +1 -0
  10. package/dist/anki.d.cts +751 -0
  11. package/dist/anki.d.cts.map +1 -0
  12. package/dist/anki.d.ts +751 -0
  13. package/dist/anki.d.ts.map +1 -0
  14. package/dist/anki.js +1751 -0
  15. package/dist/anki.js.map +1 -0
  16. package/dist/audio-D9DvYyB7.d.cts +48 -0
  17. package/dist/audio-D9DvYyB7.d.cts.map +1 -0
  18. package/dist/audio-DQulUkDM.d.ts +48 -0
  19. package/dist/audio-DQulUkDM.d.ts.map +1 -0
  20. package/dist/audio-url-generator-BXvQaqUi.cjs +4 -0
  21. package/dist/audio-url-generator-Dy2hb2Mm.cjs +414 -0
  22. package/dist/audio-url-generator-Dy2hb2Mm.cjs.map +1 -0
  23. package/dist/audio-url-generator-Qi0rfzHz.js +4 -0
  24. package/dist/audio-url-generator-pFQAB5Nb.js +390 -0
  25. package/dist/audio-url-generator-pFQAB5Nb.js.map +1 -0
  26. package/dist/audio.cjs +7 -0
  27. package/dist/audio.d.cts +86 -0
  28. package/dist/audio.d.cts.map +1 -0
  29. package/dist/audio.d.ts +86 -0
  30. package/dist/audio.d.ts.map +1 -0
  31. package/dist/audio.js +4 -0
  32. package/dist/batch-processor-BR-gB3H3.js +84 -0
  33. package/dist/batch-processor-BR-gB3H3.js.map +1 -0
  34. package/dist/batch-processor-CSF1acTw.cjs +3 -0
  35. package/dist/batch-processor-DFqM_L-_.cjs +91 -0
  36. package/dist/batch-processor-DFqM_L-_.cjs.map +1 -0
  37. package/dist/batch-processor-Quo9jUyf.js +3 -0
  38. package/dist/chunk-BCwAaXi7.cjs +31 -0
  39. package/dist/cjk-util-Dp0ZU0sh.cjs +167 -0
  40. package/dist/cjk-util-Dp0ZU0sh.cjs.map +1 -0
  41. package/dist/cjk-util-DubXBGDG.js +94 -0
  42. package/dist/cjk-util-DubXBGDG.js.map +1 -0
  43. package/dist/core-BUpclilG.d.cts +102 -0
  44. package/dist/core-BUpclilG.d.cts.map +1 -0
  45. package/dist/core-DFUj5GtA.d.ts +102 -0
  46. package/dist/core-DFUj5GtA.d.ts.map +1 -0
  47. package/dist/database.cjs +7 -0
  48. package/dist/database.d.cts +4 -0
  49. package/dist/database.d.ts +4 -0
  50. package/dist/database.js +5 -0
  51. package/dist/dictionary-D7l-qFt1.d.cts +316 -0
  52. package/dist/dictionary-D7l-qFt1.d.cts.map +1 -0
  53. package/dist/dictionary-_vzfBLWi.d.ts +316 -0
  54. package/dist/dictionary-_vzfBLWi.d.ts.map +1 -0
  55. package/dist/dictionary-data-util-CHnRdYZ9.cjs +378 -0
  56. package/dist/dictionary-data-util-CHnRdYZ9.cjs.map +1 -0
  57. package/dist/dictionary-data-util-CfOLfEDE.js +323 -0
  58. package/dist/dictionary-data-util-CfOLfEDE.js.map +1 -0
  59. package/dist/dictionary-database-BDC2f9zc.d.ts +58 -0
  60. package/dist/dictionary-database-BDC2f9zc.d.ts.map +1 -0
  61. package/dist/dictionary-database-CU4TsvCC.js +393 -0
  62. package/dist/dictionary-database-CU4TsvCC.js.map +1 -0
  63. package/dist/dictionary-database-DsOi04Sg.d.cts +58 -0
  64. package/dist/dictionary-database-DsOi04Sg.d.cts.map +1 -0
  65. package/dist/dictionary-database-lvFvftnO.cjs +412 -0
  66. package/dist/dictionary-database-lvFvftnO.cjs.map +1 -0
  67. package/dist/dictionary-importer-BkQQSBhm.d.ts +237 -0
  68. package/dist/dictionary-importer-BkQQSBhm.d.ts.map +1 -0
  69. package/dist/dictionary-importer-Cen1z6co.js +1821 -0
  70. package/dist/dictionary-importer-Cen1z6co.js.map +1 -0
  71. package/dist/dictionary-importer-DYmmWmcX.cjs +8 -0
  72. package/dist/dictionary-importer-Da3AuTZw.d.cts +237 -0
  73. package/dist/dictionary-importer-Da3AuTZw.d.cts.map +1 -0
  74. package/dist/dictionary-importer-Dhn75iZ4.cjs +1834 -0
  75. package/dist/dictionary-importer-Dhn75iZ4.cjs.map +1 -0
  76. package/dist/dictionary-importer-xWkel0h-.js +8 -0
  77. package/dist/dictionary-update-checker-BNE4pGTx.js +4 -0
  78. package/dist/dictionary-update-checker-Byjvifd2.cjs +75 -0
  79. package/dist/dictionary-update-checker-Byjvifd2.cjs.map +1 -0
  80. package/dist/dictionary-update-checker-YdpalZ41.cjs +4 -0
  81. package/dist/dictionary-update-checker-kKukiovj.js +69 -0
  82. package/dist/dictionary-update-checker-kKukiovj.js.map +1 -0
  83. package/dist/display-generator-BGVWiI0t.js +746 -0
  84. package/dist/display-generator-BGVWiI0t.js.map +1 -0
  85. package/dist/display-generator-BMQmG5Ov.cjs +9 -0
  86. package/dist/display-generator-BxZ7mBjP.js +9 -0
  87. package/dist/display-generator-DyP-HNzP.cjs +758 -0
  88. package/dist/display-generator-DyP-HNzP.cjs.map +1 -0
  89. package/dist/errors-BSezaJwm.cjs +35 -0
  90. package/dist/errors-BSezaJwm.cjs.map +1 -0
  91. package/dist/errors-DuuDSO5N.js +22 -0
  92. package/dist/errors-DuuDSO5N.js.map +1 -0
  93. package/dist/frequency-ranking-BXjfhhUQ.js +3 -0
  94. package/dist/frequency-ranking-Cx1kkIrw.cjs +3 -0
  95. package/dist/frequency-ranking-DEJMTMdg.js +159 -0
  96. package/dist/frequency-ranking-DEJMTMdg.js.map +1 -0
  97. package/dist/frequency-ranking-DVYxTXN-.cjs +166 -0
  98. package/dist/frequency-ranking-DVYxTXN-.cjs.map +1 -0
  99. package/dist/furigana-5HK97CY8.js +4 -0
  100. package/dist/furigana-9bBI9-qe.d.ts +47 -0
  101. package/dist/furigana-9bBI9-qe.d.ts.map +1 -0
  102. package/dist/furigana-B3-0y231.js +471 -0
  103. package/dist/furigana-B3-0y231.js.map +1 -0
  104. package/dist/furigana-CjOhzvZt.d.cts +47 -0
  105. package/dist/furigana-CjOhzvZt.d.cts.map +1 -0
  106. package/dist/furigana-DpZLcues.cjs +609 -0
  107. package/dist/furigana-DpZLcues.cjs.map +1 -0
  108. package/dist/furigana-h3v2ub4-.cjs +4 -0
  109. package/dist/import.cjs +12 -0
  110. package/dist/import.d.cts +107 -0
  111. package/dist/import.d.cts.map +1 -0
  112. package/dist/import.d.ts +107 -0
  113. package/dist/import.d.ts.map +1 -0
  114. package/dist/import.js +9 -0
  115. package/dist/index.cjs +275 -0
  116. package/dist/index.cjs.map +1 -0
  117. package/dist/index.d.cts +211 -0
  118. package/dist/index.d.cts.map +1 -0
  119. package/dist/index.d.ts +211 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +238 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/json-DGd-cunA.js +17 -0
  124. package/dist/json-DGd-cunA.js.map +1 -0
  125. package/dist/json-DKWp-B7Y.cjs +30 -0
  126. package/dist/json-DKWp-B7Y.cjs.map +1 -0
  127. package/dist/language-KN_u-nTR.d.ts +104 -0
  128. package/dist/language-KN_u-nTR.d.ts.map +1 -0
  129. package/dist/language-xAbQxgXc.d.cts +104 -0
  130. package/dist/language-xAbQxgXc.d.cts.map +1 -0
  131. package/dist/language.cjs +15626 -0
  132. package/dist/language.cjs.map +1 -0
  133. package/dist/language.d.cts +959 -0
  134. package/dist/language.d.cts.map +1 -0
  135. package/dist/language.d.ts +959 -0
  136. package/dist/language.d.ts.map +1 -0
  137. package/dist/language.js +15522 -0
  138. package/dist/language.js.map +1 -0
  139. package/dist/log-D8KtR3aP.cjs +67 -0
  140. package/dist/log-D8KtR3aP.cjs.map +1 -0
  141. package/dist/log-hgSll-dS.js +60 -0
  142. package/dist/log-hgSll-dS.js.map +1 -0
  143. package/dist/lookup.cjs +13 -0
  144. package/dist/lookup.d.cts +161 -0
  145. package/dist/lookup.d.cts.map +1 -0
  146. package/dist/lookup.d.ts +161 -0
  147. package/dist/lookup.d.ts.map +1 -0
  148. package/dist/lookup.js +10 -0
  149. package/dist/media-loader-BABA_E4W.js +3 -0
  150. package/dist/media-loader-Ce9cuANS.cjs +21 -0
  151. package/dist/media-loader-Ce9cuANS.cjs.map +1 -0
  152. package/dist/media-loader-qRti-Q6h.js +14 -0
  153. package/dist/media-loader-qRti-Q6h.js.map +1 -0
  154. package/dist/media-loader-xlUGaJrx.cjs +3 -0
  155. package/dist/multi-language-transformer-AlxOM6b3.js +637 -0
  156. package/dist/multi-language-transformer-AlxOM6b3.js.map +1 -0
  157. package/dist/multi-language-transformer-MdbQBBOt.cjs +685 -0
  158. package/dist/multi-language-transformer-MdbQBBOt.cjs.map +1 -0
  159. package/dist/multi-language-transformer-SEhcJXEB.d.ts +63 -0
  160. package/dist/multi-language-transformer-SEhcJXEB.d.ts.map +1 -0
  161. package/dist/multi-language-transformer-Ul9mbRce.d.cts +63 -0
  162. package/dist/multi-language-transformer-Ul9mbRce.d.cts.map +1 -0
  163. package/dist/pronunciation-generator-BtBc4q_V.js +397 -0
  164. package/dist/pronunciation-generator-BtBc4q_V.js.map +1 -0
  165. package/dist/pronunciation-generator-CBYdXYou.js +4 -0
  166. package/dist/pronunciation-generator-CFbZlf5J.cjs +445 -0
  167. package/dist/pronunciation-generator-CFbZlf5J.cjs.map +1 -0
  168. package/dist/pronunciation-generator-DOz9hEuk.cjs +4 -0
  169. package/dist/render.cjs +2796 -0
  170. package/dist/render.cjs.map +1 -0
  171. package/dist/render.d.cts +424 -0
  172. package/dist/render.d.cts.map +1 -0
  173. package/dist/render.d.ts +424 -0
  174. package/dist/render.d.ts.map +1 -0
  175. package/dist/render.js +2777 -0
  176. package/dist/render.js.map +1 -0
  177. package/dist/sentence-parser-BPAJNzqW.js +126 -0
  178. package/dist/sentence-parser-BPAJNzqW.js.map +1 -0
  179. package/dist/sentence-parser-BVIOI64h.cjs +132 -0
  180. package/dist/sentence-parser-BVIOI64h.cjs.map +1 -0
  181. package/dist/sentence-parser-BoHO3cHn.js +5 -0
  182. package/dist/sentence-parser-DQVLSW0z.cjs +5 -0
  183. package/dist/structured-content-generator-BtOApkTW.cjs +4 -0
  184. package/dist/structured-content-generator-Bx62RYa8.js +4 -0
  185. package/dist/structured-content-generator-CLnybumI.js +276 -0
  186. package/dist/structured-content-generator-CLnybumI.js.map +1 -0
  187. package/dist/structured-content-generator-DrwkB0-k.cjs +282 -0
  188. package/dist/structured-content-generator-DrwkB0-k.cjs.map +1 -0
  189. package/dist/text-utilities-B7PIythe.js +8 -0
  190. package/dist/text-utilities-B7PIythe.js.map +1 -0
  191. package/dist/text-utilities-Del2Ivkg.cjs +15 -0
  192. package/dist/text-utilities-Del2Ivkg.cjs.map +1 -0
  193. package/dist/translator-CRPlPzqi.cjs +1545 -0
  194. package/dist/translator-CRPlPzqi.cjs.map +1 -0
  195. package/dist/translator-CWgG5drA.js +1539 -0
  196. package/dist/translator-CWgG5drA.js.map +1 -0
  197. package/dist/translator-CaGtJvnQ.cjs +6 -0
  198. package/dist/translator-Cc6OGxrW.d.ts +180 -0
  199. package/dist/translator-Cc6OGxrW.d.ts.map +1 -0
  200. package/dist/translator-CcA-s-W4.d.cts +180 -0
  201. package/dist/translator-CcA-s-W4.d.cts.map +1 -0
  202. package/dist/translator-CuJOTK6l.js +6 -0
  203. package/dist/utilities-C-lbZaJE.cjs +52 -0
  204. package/dist/utilities-C-lbZaJE.cjs.map +1 -0
  205. package/dist/utilities-bi3EF-q5.js +33 -0
  206. package/dist/utilities-bi3EF-q5.js.map +1 -0
  207. package/package.json +102 -0
package/README.md ADDED
@@ -0,0 +1,485 @@
1
+ # yomitan-core
2
+
3
+ Core dictionary lookup, language processing, and rendering engine extracted from the [Yomitan](https://github.com/louismollick/yomitan) browser extension. Use it in Node.js, Electron, or any JavaScript environment with IndexedDB.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install yomitan-core
9
+ ```
10
+
11
+ ### Optional dependencies
12
+
13
+ | Package | Purpose |
14
+ |---------|---------|
15
+ | `linkedom` | Server-side DOM for the rendering module |
16
+ | `hangul-js` | Korean Hangul disassembly/reassembly |
17
+ | `kanji-processor` | Kanji decomposition |
18
+ | `@resvg/resvg-wasm` | SVG rasterization for pitch accent images |
19
+
20
+ ## Quick start
21
+
22
+ ```typescript
23
+ import YomitanCore from 'yomitan-core';
24
+
25
+ const core = new YomitanCore();
26
+ await core.initialize();
27
+
28
+ // Import a dictionary from a .zip ArrayBuffer
29
+ const archive = await fetch('/jmdict.zip').then((r) => r.arrayBuffer());
30
+ const result = await core.importDictionary(archive, {
31
+ onProgress: (progress) => console.log(progress),
32
+ });
33
+ console.log(`Imported "${result.result.title}" with ${result.result.termCount} terms`);
34
+
35
+ // Look up a term
36
+ const { entries, originalTextLength } = await core.findTerms('食べる', {
37
+ enabledDictionaryMap: new Map([['JMdict', { index: 0, priority: 0 }]]),
38
+ });
39
+
40
+ console.log(entries[0].headwords); // [{term: '食べる', reading: 'たべる', ...}]
41
+
42
+ // Clean up
43
+ await core.dispose();
44
+ ```
45
+
46
+ ## API
47
+
48
+ ### `YomitanCore`
49
+
50
+ The main orchestrator. Manages the database, translator, and language processing subsystems.
51
+
52
+ ```typescript
53
+ const core = new YomitanCore({
54
+ databaseName: 'my-dict', // IndexedDB name (default: 'dict')
55
+ initLanguage: true, // auto-init language transformers (default: true)
56
+ });
57
+
58
+ await core.initialize();
59
+ ```
60
+
61
+ #### Dictionary management
62
+
63
+ ```typescript
64
+ // Import a dictionary zip
65
+ await core.importDictionary(archive: ArrayBuffer, options?)
66
+
67
+ // List installed dictionaries
68
+ await core.getDictionaryInfo(): Promise<Summary[]>
69
+
70
+ // Delete a dictionary by title
71
+ await core.deleteDictionary(name: string, onProgress?)
72
+
73
+ // Check for dictionary updates (fetches remote index URLs)
74
+ await core.checkForUpdates(names?: string[]): Promise<DictionaryUpdateInfo[]>
75
+ ```
76
+
77
+ #### Term lookup
78
+
79
+ ```typescript
80
+ // Look up terms with deinflection, grouping, and sorting
81
+ await core.findTerms(text, {
82
+ mode: 'group', // 'group' | 'merge' | 'split' | 'simple'
83
+ language: 'ja',
84
+ enabledDictionaryMap: new Map([['JMdict', { index: 0, priority: 0 }]]),
85
+ options: {
86
+ matchType: 'exact', // 'exact' | 'prefix' | 'suffix'
87
+ deinflect: true,
88
+ sortFrequencyDictionary: 'JPDB',
89
+ sortFrequencyDictionaryOrder: 'descending',
90
+ },
91
+ })
92
+
93
+ // Look up kanji
94
+ await core.findKanji(text, {
95
+ enabledDictionaryMap: new Map([['KANJIDIC', { index: 0, priority: 0 }]]),
96
+ })
97
+ ```
98
+
99
+ #### Sentence parsing
100
+
101
+ Sliding-window longest-match parser that splits text into segments with furigana.
102
+
103
+ ```typescript
104
+ const lines = await core.parseText('日本語を勉強する', {
105
+ enabledDictionaryMap: new Map([['JMdict', { index: 0, priority: 0 }]]),
106
+ });
107
+ // Returns ParsedLine[] with segments, readings, and furigana
108
+ ```
109
+
110
+ #### Furigana generation
111
+
112
+ ```typescript
113
+ const segments = await core.generateFurigana('食べる', 'たべる');
114
+ // [{ text: '食', reading: 'た' }, { text: 'べる', reading: '' }]
115
+ ```
116
+
117
+ #### Batch lookup
118
+
119
+ Look up multiple texts efficiently with shared caches and optional concurrency control.
120
+
121
+ ```typescript
122
+ const results = await core.batchLookup(
123
+ ['食べる', '飲む', '走る'],
124
+ {
125
+ enabledDictionaryMap: new Map([['JMdict', { index: 0, priority: 0 }]]),
126
+ concurrency: 4,
127
+ },
128
+ );
129
+ // Returns Map<string, TermLookupResult>
130
+ ```
131
+
132
+ #### Frequency ranking
133
+
134
+ ```typescript
135
+ const ranking = await core.getFrequencyRanking('食べる', ['JPDB', 'Innocent Corpus']);
136
+ // { frequencies: [...], harmonicMean: 1234 }
137
+ ```
138
+
139
+ #### Audio URLs
140
+
141
+ Generate audio source URLs for a term/reading pair across multiple providers (JapanesePod101, Jisho, Lingua Libre, Wiktionary, custom JSON).
142
+
143
+ ```typescript
144
+ const urls = await core.getAudioUrls('食べる', 'たべる', [
145
+ { type: 'jpod101', url: '', voice: '' },
146
+ ]);
147
+ ```
148
+
149
+ #### Factory methods
150
+
151
+ ```typescript
152
+ // AnkiConnect client
153
+ const anki = await core.createAnkiClient({ server: 'http://127.0.0.1:8765' });
154
+
155
+ // Rendering classes (requires DOM — use linkedom or jsdom in Node.js)
156
+ const { DisplayGenerator, StructuredContentGenerator, PronunciationGenerator } =
157
+ await core.createRenderer();
158
+
159
+ // Standalone audio URL generator
160
+ const audioGen = await core.createAudioUrlGenerator();
161
+ ```
162
+
163
+ #### Accessor properties
164
+
165
+ ```typescript
166
+ core.isReady // boolean — whether initialize() has been called
167
+ core.database // DictionaryDB — direct access to the Dexie database
168
+ core.language // { summaries, textProcessors, transformer, isTextLookupWorthy }
169
+ ```
170
+
171
+ ## Tree-shakeable submodule imports
172
+
173
+ Each submodule is a separate entry point. Import only what you need to minimize bundle size.
174
+
175
+ ```typescript
176
+ // Database layer (Dexie-based IndexedDB)
177
+ import { DictionaryDB, YomitanDatabase } from 'yomitan-core/database';
178
+
179
+ // Dictionary import and update checking
180
+ import { DictionaryImporterClass, DictionaryUpdateChecker } from 'yomitan-core/import';
181
+
182
+ // Translator, sentence parsing, batch processing, frequency ranking
183
+ import { Translator, SentenceParser, BatchProcessor, FrequencyRanker } from 'yomitan-core/lookup';
184
+
185
+ // 48 languages: transforms, text processors, CJK utils, furigana, Japanese, Korean, Chinese, ...
186
+ import {
187
+ LanguageTransformer,
188
+ MultiLanguageTransformer,
189
+ distributeFurigana,
190
+ getLanguageSummaries,
191
+ convertKatakanaToHiragana,
192
+ japaneseTransforms,
193
+ koreanTransforms,
194
+ } from 'yomitan-core/language';
195
+
196
+ // AnkiConnect client, note builder, template renderer
197
+ import { AnkiConnect, AnkiNoteBuilder, AnkiTemplateRenderer } from 'yomitan-core/anki';
198
+
199
+ // HTML display rendering (requires DOM)
200
+ import {
201
+ DisplayGenerator,
202
+ StructuredContentGenerator,
203
+ PronunciationGenerator,
204
+ HtmlTemplateCollection,
205
+ } from 'yomitan-core/render';
206
+
207
+ // Audio URL generation
208
+ import { AudioUrlGenerator } from 'yomitan-core/audio';
209
+ ```
210
+
211
+ ## Using individual classes directly
212
+
213
+ For more control, use the classes directly instead of the `YomitanCore` wrapper.
214
+
215
+ ### Database + Translator
216
+
217
+ ```typescript
218
+ import { DictionaryDB } from 'yomitan-core/database';
219
+ import { Translator } from 'yomitan-core/lookup';
220
+
221
+ const db = new DictionaryDB('my-dict');
222
+ await db.open();
223
+
224
+ const translator = new Translator(db);
225
+ // translator.prepare() loads language transformers internally
226
+
227
+ const { dictionaryEntries, originalTextLength } = await translator.findTerms(
228
+ 'group',
229
+ '食べたい',
230
+ {
231
+ matchType: 'exact',
232
+ deinflect: true,
233
+ primaryReading: '',
234
+ mainDictionary: '',
235
+ sortFrequencyDictionary: null,
236
+ sortFrequencyDictionaryOrder: 'descending',
237
+ removeNonJapaneseCharacters: false,
238
+ textReplacements: [null],
239
+ enabledDictionaryMap: new Map([['JMdict', { index: 0, priority: 0 }]]),
240
+ excludeDictionaryDefinitions: null,
241
+ searchResolution: 'letter',
242
+ language: 'ja',
243
+ },
244
+ );
245
+
246
+ db.close();
247
+ ```
248
+
249
+ ### Dictionary import
250
+
251
+ ```typescript
252
+ import { DictionaryDB } from 'yomitan-core/database';
253
+ import { DictionaryImporterClass } from 'yomitan-core/import';
254
+
255
+ const db = new DictionaryDB('my-dict');
256
+ await db.open();
257
+
258
+ const importer = new DictionaryImporterClass(
259
+ undefined, // MediaLoader (undefined = NoOpMediaLoader)
260
+ (progress) => console.log(`${progress.index}/${progress.count}`),
261
+ );
262
+
263
+ const archive = await fs.readFile('./jmdict.zip');
264
+ const result = await importer.importDictionary(db, archive.buffer, {
265
+ prefixWildcardsSupported: true,
266
+ yomitanVersion: '0.1.0',
267
+ });
268
+
269
+ console.log(result.result.title, result.result.termCount);
270
+ db.close();
271
+ ```
272
+
273
+ ### Language transforms
274
+
275
+ ```typescript
276
+ import { LanguageTransformer, japaneseTransforms } from 'yomitan-core/language';
277
+
278
+ const transformer = new LanguageTransformer();
279
+ transformer.addDescriptor(japaneseTransforms);
280
+
281
+ const deinflections = transformer.transform('食べたい');
282
+ for (const result of deinflections) {
283
+ console.log(result.text, result.trace);
284
+ }
285
+ ```
286
+
287
+ ### Furigana
288
+
289
+ ```typescript
290
+ import { distributeFurigana } from 'yomitan-core/language';
291
+
292
+ const segments = distributeFurigana('食べる', 'たべる');
293
+ // [{ text: '食', reading: 'た' }, { text: 'べる', reading: '' }]
294
+ ```
295
+
296
+ ### AnkiConnect
297
+
298
+ ```typescript
299
+ import { AnkiConnect } from 'yomitan-core/anki';
300
+
301
+ const anki = new AnkiConnect({ server: 'http://127.0.0.1:8765' });
302
+ const decks = await anki.getDeckNames();
303
+ const models = await anki.getModelNames();
304
+ const fields = await anki.getModelFieldNames('Basic');
305
+ ```
306
+
307
+ ### Server-side rendering with linkedom
308
+
309
+ ```typescript
310
+ import { parseHTML } from 'linkedom';
311
+ import {
312
+ DisplayGenerator,
313
+ HtmlTemplateCollection,
314
+ DISPLAY_TEMPLATES,
315
+ DISPLAY_CSS,
316
+ } from 'yomitan-core/render';
317
+
318
+ const { document } = parseHTML('<!DOCTYPE html><html><body></body></html>');
319
+
320
+ const templates = new HtmlTemplateCollection();
321
+ templates.loadFromString(DISPLAY_TEMPLATES, document);
322
+
323
+ const generator = new DisplayGenerator(document, templates);
324
+
325
+ // Render a term entry to DOM nodes
326
+ const node = generator.createTermEntry(dictionaryEntry);
327
+ console.log(node.outerHTML);
328
+ ```
329
+
330
+ ## Development
331
+
332
+ ### Prerequisites
333
+
334
+ - Node.js >= 18
335
+ - npm
336
+
337
+ ### Setup
338
+
339
+ ```bash
340
+ git clone https://github.com/louismollick/yomitan-core.git
341
+ cd yomitan-core
342
+ npm install
343
+ ```
344
+
345
+ ### Scripts
346
+
347
+ | Command | Description |
348
+ |---------|-------------|
349
+ | `npm run build` | Build ESM + CJS + .d.ts with tsdown |
350
+ | `npm run dev` | Watch mode build |
351
+ | `npm run typecheck` | TypeScript type checking (`tsc --noEmit`) |
352
+ | `npm run lint` | Biome lint + format check |
353
+ | `npm run lint:fix` | Auto-fix lint and formatting issues |
354
+ | `npm run format` | Format all files with Biome |
355
+ | `npm run test` | Run tests with vitest |
356
+ | `npm run test:watch` | Watch mode tests |
357
+
358
+ ### Automated versioning and releases
359
+
360
+ This repo uses semantic-release on pushes to `main`/`master` to:
361
+
362
+ - determine the next version from commit messages
363
+ - update `package.json` and `package-lock.json`
364
+ - update `CHANGELOG.md`
365
+ - publish to npm
366
+ - create a GitHub release and tag
367
+
368
+ Set the following repository secret in GitHub Actions:
369
+
370
+ - `NPM_TOKEN` (npm automation token with publish access)
371
+
372
+ Use Conventional Commits so version bumps are calculated correctly:
373
+
374
+ - `fix: ...` -> patch release (`x.y.Z`)
375
+ - `feat: ...` -> minor release (`x.Y.0`)
376
+ - `feat!: ...` or a commit body with `BREAKING CHANGE:` -> major release (`X.0.0`)
377
+
378
+ ### Testing locally from another project
379
+
380
+ There are two ways to test yomitan-core from another npm project on your machine.
381
+
382
+ #### Option A: `npm link` (recommended)
383
+
384
+ ```bash
385
+ # In the yomitan-core directory, build and create a global link
386
+ cd /path/to/yomitan-core
387
+ npm run build
388
+ npm link
389
+
390
+ # In your consuming project, link to it
391
+ cd /path/to/my-app
392
+ npm link yomitan-core
393
+ ```
394
+
395
+ You can now import from `yomitan-core` as if it were installed from the registry. Any time you rebuild yomitan-core, the changes are immediately available.
396
+
397
+ To unlink:
398
+
399
+ ```bash
400
+ cd /path/to/my-app
401
+ npm unlink yomitan-core
402
+
403
+ cd /path/to/yomitan-core
404
+ npm unlink
405
+ ```
406
+
407
+ #### Option B: `file:` dependency
408
+
409
+ In your consuming project's `package.json`:
410
+
411
+ ```json
412
+ {
413
+ "dependencies": {
414
+ "yomitan-core": "file:../yomitan-core"
415
+ }
416
+ }
417
+ ```
418
+
419
+ Then run `npm install`. This creates a symlink to the local package. You need to rebuild yomitan-core and re-run `npm install` in your project when the yomitan-core package structure changes.
420
+
421
+ #### Option C: `npm pack`
422
+
423
+ This simulates a real npm install most closely:
424
+
425
+ ```bash
426
+ # In yomitan-core
427
+ cd /path/to/yomitan-core
428
+ npm run build
429
+ npm pack
430
+ # Creates yomitan-core-0.1.0.tgz
431
+
432
+ # In your consuming project
433
+ cd /path/to/my-app
434
+ npm install /path/to/yomitan-core/yomitan-core-0.1.0.tgz
435
+ ```
436
+
437
+ #### Verifying the link works
438
+
439
+ Create a test file in your consuming project:
440
+
441
+ ```typescript
442
+ import YomitanCore from 'yomitan-core';
443
+
444
+ const core = new YomitanCore();
445
+ await core.initialize();
446
+
447
+ const info = await core.getDictionaryInfo();
448
+ console.log('Installed dictionaries:', info);
449
+
450
+ await core.dispose();
451
+ ```
452
+
453
+ Run it with a runtime that supports IndexedDB (browser, Electron) or with `fake-indexeddb` for Node.js:
454
+
455
+ ```typescript
456
+ // At the top of your Node.js entry point, before any yomitan-core imports
457
+ import 'fake-indexeddb/auto';
458
+ ```
459
+
460
+ ### Project structure
461
+
462
+ ```
463
+ yomitan-core/
464
+ src/
465
+ index.ts # YomitanCore class + barrel exports
466
+ types/ # TypeScript type definitions
467
+ util/ # Shared utilities (errors, string, regex, JSON, etc.)
468
+ database/ # Dexie-based IndexedDB dictionary storage
469
+ import/ # Dictionary .zip import + schema validation + update checking
470
+ lookup/ # Translator, sentence parser, batch processor, frequency ranker
471
+ language/ # 48 languages: transforms, text processors, CJK, furigana
472
+ ja/ # Japanese-specific (transforms, kana, wanakana, furigana)
473
+ ko/ # Korean (Hangul processing, transforms)
474
+ zh/ # Chinese (pinyin, character detection)
475
+ ar/ # Arabic
476
+ de/ en/ es/ fr/ ... # Other languages
477
+ anki/ # AnkiConnect client, note builder, template renderer
478
+ audio/ # Audio URL generation (JapanesePod101, Jisho, Wiktionary, etc.)
479
+ render/ # HTML display generation (term/kanji entries, structured content, pitch accent)
480
+ dist/ # Build output (ESM, CJS, .d.ts, sourcemaps)
481
+ ```
482
+
483
+ ## License
484
+
485
+ GPL-3.0-or-later. See [LICENSE](./LICENSE).