henkan 0.6.1 → 0.7.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 (61) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs.js +53 -34
  3. package/dist/index.cjs.js.map +3 -3
  4. package/dist/index.mjs +53 -36
  5. package/dist/index.mjs.map +2 -2
  6. package/dist/types/types.d.ts +21 -0
  7. package/dist/types/types.d.ts.map +1 -1
  8. package/dist/types/utils.d.ts +9 -6
  9. package/dist/types/utils.d.ts.map +1 -1
  10. package/docs/api/README.md +1 -0
  11. package/docs/api/functions/capitalizeString.md +1 -1
  12. package/docs/api/functions/convertJMdict.md +1 -1
  13. package/docs/api/functions/convertKanjiDic.md +1 -1
  14. package/docs/api/functions/convertKradFile.md +1 -1
  15. package/docs/api/functions/convertRadkFile.md +1 -1
  16. package/docs/api/functions/convertTanakaCorpus.md +1 -1
  17. package/docs/api/functions/generateAnkiNote.md +1 -1
  18. package/docs/api/functions/generateAnkiNotesFile.md +1 -1
  19. package/docs/api/functions/getKanji.md +1 -1
  20. package/docs/api/functions/getKanjiExtended.md +1 -1
  21. package/docs/api/functions/getWord.md +1 -1
  22. package/docs/api/functions/isStringArray.md +1 -1
  23. package/docs/api/functions/isValidArray.md +1 -1
  24. package/docs/api/functions/isValidArrayWithFirstElement.md +1 -1
  25. package/docs/api/functions/makeSSML.md +1 -1
  26. package/docs/api/functions/shuffleArray.md +1 -1
  27. package/docs/api/functions/synthesizeSpeech.md +25 -13
  28. package/docs/api/interfaces/DictKanji.md +5 -5
  29. package/docs/api/interfaces/DictKanjiForm.md +4 -4
  30. package/docs/api/interfaces/DictKanjiMisc.md +5 -5
  31. package/docs/api/interfaces/DictKanjiReading.md +3 -3
  32. package/docs/api/interfaces/DictKanjiReadingMeaning.md +3 -3
  33. package/docs/api/interfaces/DictKanjiReadingMeaningGroup.md +3 -3
  34. package/docs/api/interfaces/DictKanjiWithRadicals.md +3 -3
  35. package/docs/api/interfaces/DictMeaning.md +11 -11
  36. package/docs/api/interfaces/DictRadical.md +4 -4
  37. package/docs/api/interfaces/DictReading.md +5 -5
  38. package/docs/api/interfaces/DictWord.md +8 -8
  39. package/docs/api/interfaces/ExamplePart.md +7 -7
  40. package/docs/api/interfaces/GlossSpecificNumber.md +31 -0
  41. package/docs/api/interfaces/Grammar.md +15 -15
  42. package/docs/api/interfaces/GrammarMeaning.md +3 -3
  43. package/docs/api/interfaces/Kana.md +11 -11
  44. package/docs/api/interfaces/Kanji.md +22 -22
  45. package/docs/api/interfaces/KanjiComponent.md +3 -3
  46. package/docs/api/interfaces/KanjiForm.md +4 -4
  47. package/docs/api/interfaces/NoteAndTag.md +3 -3
  48. package/docs/api/interfaces/Phrase.md +16 -4
  49. package/docs/api/interfaces/Radical.md +16 -16
  50. package/docs/api/interfaces/Reading.md +5 -5
  51. package/docs/api/interfaces/ResultEntry.md +7 -7
  52. package/docs/api/interfaces/TanakaExample.md +16 -6
  53. package/docs/api/interfaces/Translation.md +3 -3
  54. package/docs/api/interfaces/UsefulRegExps.md +9 -9
  55. package/docs/api/interfaces/Word.md +14 -14
  56. package/docs/api/type-aliases/Dict.md +1 -1
  57. package/docs/api/type-aliases/DictName.md +1 -1
  58. package/docs/api/type-aliases/EntryType.md +1 -1
  59. package/docs/api/type-aliases/JLPT.md +1 -1
  60. package/docs/api/type-aliases/Result.md +1 -1
  61. package/package.json +5 -5
package/dist/index.mjs CHANGED
@@ -1131,9 +1131,7 @@ var noteMap = /* @__PURE__ */ new Map([
1131
1131
  import libxml from "libxmljs2";
1132
1132
  import xml from "xml2js";
1133
1133
  import iconv from "iconv-lite";
1134
- import {
1135
- SynthesizeSpeechCommand
1136
- } from "@aws-sdk/client-polly";
1134
+ import fetch from "node-fetch";
1137
1135
  var Kuroshiro = __require("kuroshiro");
1138
1136
  var KuromojiAnalyzer = __require("kuroshiro-analyzer-kuromoji");
1139
1137
  function capitalizeString(value) {
@@ -1279,11 +1277,9 @@ function convertJMdict(xmlString, examples) {
1279
1277
  ).map((reading) => reading.reading)
1280
1278
  );
1281
1279
  const kanjiForms2 = entryObj.kanjiForms ? new Set(
1282
- entryObj.kanjiForms.filter(
1283
- (kanjiForm) => (!kanjiForm.notes || !kanjiForm.notes.some(
1284
- (note) => notSearchedForms.has(note)
1285
- )) && (entryObj.isCommon === void 0 || kanjiForm.commonness && kanjiForm.commonness.length > 0)
1286
- ).map((kanjiForm) => kanjiForm.form)
1280
+ entryObj.kanjiForms.map(
1281
+ (kanjiForm) => kanjiForm.form
1282
+ )
1287
1283
  ) : void 0;
1288
1284
  let existsExample = false;
1289
1285
  if (kanjiForms2 && kanjiForms2.size > 0 && tanakaParts) {
@@ -1465,9 +1461,9 @@ function convertRadkFile(radkBuffer, kanjiDic) {
1465
1461
  try {
1466
1462
  const fileParsed = iconv.decode(radkBuffer, "euc-jp").split("\n").filter((line) => !line.startsWith("#"));
1467
1463
  const radicals = [];
1468
- for (let i = 0; i <= fileParsed.length; i++) {
1464
+ for (let i = 0; i < fileParsed.length; i++) {
1469
1465
  const line = fileParsed[i];
1470
- if (!line) throw new Error("Invalid radkfile2 buffer");
1466
+ if (!line) continue;
1471
1467
  if (line.startsWith("$ ")) {
1472
1468
  const radical = {
1473
1469
  radical: line.charAt(2).trim(),
@@ -1475,7 +1471,7 @@ function convertRadkFile(radkBuffer, kanjiDic) {
1475
1471
  };
1476
1472
  let j = i + 1;
1477
1473
  let kanjiLine = fileParsed[j];
1478
- if (!kanjiLine) throw new Error("Invalid radkfile2 buffer");
1474
+ if (!kanjiLine) continue;
1479
1475
  const kanjiList = [];
1480
1476
  while (kanjiLine && !kanjiLine.startsWith("$ ")) {
1481
1477
  const kanjis = kanjiLine.split("");
@@ -1511,8 +1507,7 @@ function convertKradFile(kradBuffer, kanjiDic, katakanaList) {
1511
1507
  const split = line.split(" : ");
1512
1508
  const kanjiChar = split[0];
1513
1509
  const radicalsRow = split[1];
1514
- if (!kanjiChar || !radicalsRow)
1515
- throw new Error("Invalid kradfile2 buffer");
1510
+ if (!kanjiChar || !radicalsRow) continue;
1516
1511
  const kanji = {
1517
1512
  ...kanjiChar && radicalsRow && kanjiChar.length === 1 && radicalsRow.length > 0 ? { kanji: kanjiChar } : { kanji: "" },
1518
1513
  radicals: []
@@ -1723,11 +1718,9 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1723
1718
  ).map((reading) => reading.reading)
1724
1719
  );
1725
1720
  const kanjiForms = word.kanjiForms ? new Set(
1726
- word.kanjiForms.filter(
1727
- (kanjiForm) => (!kanjiForm.notes || !kanjiForm.notes.some(
1728
- (note) => notSearchedForms.has(note)
1729
- )) && (word.common === void 0 || kanjiForm.common === true)
1730
- ).map((kanjiForm) => kanjiForm.kanjiForm)
1721
+ word.kanjiForms.map(
1722
+ (kanjiForm) => kanjiForm.kanjiForm
1723
+ )
1731
1724
  ) : void 0;
1732
1725
  const kanjiFormExamples = [];
1733
1726
  const readingMatchingKanjiFormExamples = [];
@@ -1735,7 +1728,7 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1735
1728
  const partParts = /* @__PURE__ */ new Set();
1736
1729
  for (const example of examples)
1737
1730
  for (const part of example.parts) {
1738
- const readingAsReadingMatch = part.reading !== void 0 && readings.has(part.reading);
1731
+ const readingAsReadingMatch = part.reading !== void 0 && readings.has(part.reading) || part.inflectedForm !== void 0 && readings.has(part.inflectedForm);
1739
1732
  if (kanjiForms && kanjiForms.size > 0 && kanjiForms.has(part.baseForm)) {
1740
1733
  if (readingAsReadingMatch) {
1741
1734
  readingMatchingKanjiFormExamples.push(example);
@@ -1748,17 +1741,20 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1748
1741
  }
1749
1742
  const readingAsBaseFormMatch = readings.has(part.baseForm);
1750
1743
  const referenceIDMatch = part.referenceID !== void 0 && word.id !== void 0 && part.referenceID === word.id;
1751
- if (readingAsReadingMatch || readingAsBaseFormMatch || referenceIDMatch) {
1744
+ if (readingAsBaseFormMatch || referenceIDMatch) {
1752
1745
  readingExamples.push(example);
1753
- if (readingAsReadingMatch) partParts.add(part.reading);
1754
1746
  if (readingAsBaseFormMatch) partParts.add(part.baseForm);
1755
1747
  if (referenceIDMatch) partParts.add(part.referenceID);
1756
1748
  break;
1757
1749
  }
1758
1750
  }
1759
1751
  const exampleSize = readingMatchingKanjiFormExamples.length + kanjiFormExamples.length + readingExamples.length;
1760
- const includeKanjiFormExamples = readingMatchingKanjiFormExamples.length < Math.max(2, Math.round(exampleSize * 0.05));
1761
- const includeReadingExamples = word.usuallyInKana === void 0 && includeKanjiFormExamples && readingExamples.length >= Math.max(10, Math.round(exampleSize * 0.15)) || word.usuallyInKana === true && readingExamples.length >= Math.max(2, Math.round(exampleSize * 0.5));
1752
+ const includeReadingThreshold = Math.max(
1753
+ 10,
1754
+ Math.round(exampleSize * 0.5)
1755
+ );
1756
+ const includeKanjiFormExamples = word.kanjiForms !== void 0;
1757
+ const includeReadingExamples = readingExamples.length >= includeReadingThreshold && readingExamples.length >= readingMatchingKanjiFormExamples.length && readingExamples.length >= kanjiFormExamples.length || readingExamples.length >= includeReadingThreshold && word.usuallyInKana === true || word.kanjiForms === void 0;
1762
1758
  let wordExamples = [
1763
1759
  ...readingMatchingKanjiFormExamples,
1764
1760
  ...includeKanjiFormExamples ? kanjiFormExamples : [],
@@ -1770,7 +1766,11 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1770
1766
  outer: for (const example of wordExamples) {
1771
1767
  if (seenPhrases.has(example.phrase)) continue;
1772
1768
  for (const part of example.parts)
1773
- if (part.glossNumber === i + 1 && (partParts.has(part.baseForm) || part.reading && partParts.has(part.reading) || part.referenceID && partParts.has(part.referenceID))) {
1769
+ if (part.glossNumber === i + 1 && (partParts.has(part.baseForm) || includeReadingExamples && (part.reading && partParts.has(part.reading) || part.inflectedForm && partParts.has(part.inflectedForm) || part.referenceID && partParts.has(part.referenceID)))) {
1770
+ example.glossNumber = {
1771
+ wordId: word.id,
1772
+ glossNumber: i + 1
1773
+ };
1774
1774
  glossSpecificExamples.push(example);
1775
1775
  seenPhrases.add(example.phrase);
1776
1776
  break outer;
@@ -1789,7 +1789,8 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1789
1789
  word.phrases = (wordExamples.length > 5 ? wordExamples.slice(0, 5) : wordExamples).map((ex) => ({
1790
1790
  phrase: ex.furigana ?? ex.phrase,
1791
1791
  translation: ex.translation,
1792
- originalPhrase: ex.phrase
1792
+ originalPhrase: ex.phrase,
1793
+ ...ex.glossNumber ? { glossNumber: ex.glossNumber } : {}
1793
1794
  }));
1794
1795
  }
1795
1796
  return word;
@@ -2067,18 +2068,34 @@ function makeSSML(formText, fullReading) {
2067
2068
  }
2068
2069
  return ssml;
2069
2070
  }
2070
- async function synthesizeSpeech(client, ssmlText, options) {
2071
+ async function synthesizeSpeech(ssmlText, apiKey, options) {
2071
2072
  return await new Promise(
2072
2073
  async (resolve, reject) => {
2073
2074
  try {
2074
- const command = new SynthesizeSpeechCommand({
2075
- Text: ssmlText,
2076
- TextType: "ssml",
2077
- ...options
2075
+ const res = await fetch("https://ttsfree.com/api/v1/tts", {
2076
+ method: "POST",
2077
+ body: JSON.stringify({
2078
+ text: ssmlText,
2079
+ ...options
2080
+ }),
2081
+ headers: {
2082
+ "Content-Type": "application/json",
2083
+ apikey: apiKey
2084
+ }
2078
2085
  });
2079
- const response = await client.send(command);
2080
- const stream = response.AudioStream ? Buffer.from(await response.AudioStream.transformToByteArray()) : null;
2081
- resolve(stream);
2086
+ if (!res.ok)
2087
+ throw new Error(
2088
+ `TTS request failed:
2089
+ ${res.status}: ${res.statusText}`
2090
+ );
2091
+ const data = await res.json();
2092
+ if (data.status !== "success" || data.mess !== "success" || data.audioData.length === 0)
2093
+ throw new Error("Invalid TTS response data");
2094
+ const mp3Buffer = Buffer.from(
2095
+ data.audioData,
2096
+ "base64"
2097
+ );
2098
+ resolve(mp3Buffer);
2082
2099
  } catch (err) {
2083
2100
  reject(err);
2084
2101
  }
@@ -2125,7 +2142,7 @@ function generateAnkiNote(entry) {
2125
2142
  ).join("") : noKanjiForms
2126
2143
  ],
2127
2144
  entry.translations.map(
2128
- (translationEntry, index) => `${index > 2 ? "<details><summary>Show translation</summary>" : ""}${createEntry(`<span class="word word-translation">${translationEntry.translation}</span>`, translationEntry.notes)}${index > 2 ? "</details>" : ""}`
2145
+ (translationEntry, index) => `<span class="word word-index${entry.phrases && entry.phrases.some((phrase, index2) => index === index2 && phrase.glossNumber && phrase.glossNumber.wordId === entry.id && phrase.glossNumber.glossNumber === index + 1) ? " gloss-specific" : ""}">${index + 1}</span>${index > 2 ? "<details><summary>Show translation</summary>" : ""}${createEntry(`<span class="word word-translation">${translationEntry.translation}</span>`, translationEntry.notes)}${index > 2 ? "</details>" : ""}`
2129
2146
  ).join(""),
2130
2147
  entry.kanji ? entry.kanji.map(
2131
2148
  (kanjiEntry) => createEntry(
@@ -2134,11 +2151,11 @@ function generateAnkiNote(entry) {
2134
2151
  )
2135
2152
  ).join("") : '<span class="word word-kanji">(no kanji)</span>',
2136
2153
  entry.phrases ? entry.phrases.map(
2137
- (phraseEntry) => createEntry(
2154
+ (phraseEntry, index) => `<span class="word word-index${entry.translations.some((_translation, index2) => index === index2 && phraseEntry.glossNumber && phraseEntry.glossNumber.wordId === entry.id && phraseEntry.glossNumber.glossNumber === index2 + 1) ? " gloss-specific" : ""}">${index + 1}</span>${createEntry(
2138
2155
  `<span class="word word-phrase"><span class="word word-phrase-original">${phraseEntry.originalPhrase}</span><span class="word word-phrase-furigana">${phraseEntry.phrase}</span></span>`,
2139
2156
  [phraseEntry.translation],
2140
2157
  true
2141
- )
2158
+ )}`
2142
2159
  ).join("") : '<span class="word word-phrase">(no phrases) (Search on dictionaries!)</span>',
2143
2160
  ...entry.tags && entry.tags.length > 0 ? [
2144
2161
  entry.tags.map(