{"id":27883360,"url":"https://github.com/zsakowitz/ithkuil","last_synced_at":"2025-07-03T12:03:55.587Z","repository":{"id":170859529,"uuid":"647121883","full_name":"zsakowitz/ithkuil","owner":"zsakowitz","description":"A complete toolkit for New Ithkuil, converting to and from native text, glosses, both scripts, and JSON objects.","archived":false,"fork":false,"pushed_at":"2025-03-14T16:06:18.000Z","size":950,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-03T16:21:45.374Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@zsnout/ithkuil","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zsakowitz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-05-30T05:34:08.000Z","updated_at":"2025-04-22T16:06:30.000Z","dependencies_parsed_at":"2024-03-18T00:31:32.770Z","dependency_job_id":"33602d25-be2c-46ae-89dc-929abe39abf4","html_url":"https://github.com/zsakowitz/ithkuil","commit_stats":null,"previous_names":["zsakowitz/ithkuil"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zsakowitz/ithkuil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zsakowitz%2Fithkuil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zsakowitz%2Fithkuil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zsakowitz%2Fithkuil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zsakowitz%2Fithkuil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zsakowitz","download_url":"https://codeload.github.com/zsakowitz/ithkuil/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zsakowitz%2Fithkuil/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263322786,"owners_count":23448712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-05T06:15:22.764Z","updated_at":"2025-07-03T12:03:55.560Z","avatar_url":"https://github.com/zsakowitz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `@zsnout/ithkuil`\n\n`@zsnout/ithkuil` is a package containing several utilities for working with New\nIthkuil text. It can generate romanized text from JSON objects, parse text into\nJSON objects, and write in Ithkuil's writing system using SVG paths.\n\n## Custom Character Syntax\n\nIn addition to standard romanized Ithkuil, this package adds many new word kinds\nfor writing script characters. See [this Google Slide](https://docs.google.com/presentation/d/1Mw3mNznsiX0PTC9bA1vYvxOyCHliN6t2Veo7eL9cJas/edit#slide=id.g26f27f18900_0_0)\nexplaining the syntax for more information. Examples include `ho1` for forcing a\nparticlar kind of register, `Q2world` for forcing the transcription of \"world\" to\nbe included, and `QA27o'u` to force a particular quaternary character.\n\n## Stability\n\nThis package is in active development, so expect change. That said, this package\nis completely suitable for use, and has even been tested somewhat.\n\n## Features\n\n`@zsnout/ithkuil` can generate (as of May 29, 2023), parse (as of July 6, 2023),\ncreate block script for (as of August 1, 2023), and gloss (as of August\n25, 2023) every kind of word in Ithkuil, including:\n\n- Formatives\n- Specialized Cs-root formatives\n- Specialized personal-reference formatives\n- Single- and dual- referentials\n- Combination referentials\n- Affixual adjuncts\n- Bias adjuncts\n- Modular adjuncts\n- Parsing adjuncts\n- Register adjuncts\n- Suppletive adjuncts\n- Referentials with suppletive adjuncts as heads\n\nIt also has many, many more functions. It can:\n\n- Map the values of various categories to their names\n- Provide descriptions for certain forms\n- Check whether a consonant form is phonotactically legal\n- Insert glottal stops into vowel forms\n- Compute Ca forms and their geminated counterparts\n- Validate incoming objects containing Ithkuilic word data\n\n`@zsnout/ithkuil` has many more functions. However, these are too numerous to\nplace into a single document. To experiment with all of `@zsnout/ithkuil`'s\nfunctionality, install it and use your code editor's tools to see the available\nfunctions it exports.\n\n## Important Note\n\nUnlike most packages available online, `@zsnout/ithkuil` does not have a\ntop-level export. That is, directly importing from `@zsnout/ithkuil` will fail.\n\n```ts\n// INCORRECT:\nimport { parseWord } from \"@zsnout/ithkuil\"\n```\n\nInstead, always make sure to import from the proper sub-package. Currently,\nthere are four sub-packages:\n\n- **`@zsnout/ithkuil/data`**, which has JSON information about all roots and\n  affixes.\n- **`@zsnout/ithkuil/generate`**, which generates romanized text.\n- **`@zsnout/ithkuil/gloss`**, which turns Ithkuil words from the JSON format\n  into gloss strings.\n- **`@zsnout/ithkuil/parse`**, which parses romanized text.\n- **`@zsnout/ithkuil/script`**, which generates SVG block script.\n- **`@zsnout/ithkuil/zod`**, which provides Zod validators for this project.\n\n```ts\n// CORRECT:\nimport { parseWord } from \"@zsnout/ithkuil/parse\"\n```\n\n## JSX\n\nIf you're generating block script using the `script` module, you may find it\nhelpful to use our mini JSX library to make things easier to work with. It's\ncompletely optional, but it makes writing code much simpler. Here's an example\nwithout JSX.\n\n```ts\nimport {\n  Anchor,\n  CharacterRow,\n  fitViewBox,\n  textToScript,\n} from \"@zsnout/ithkuil/script\"\n\nfunction displayText(text: string) {\n  const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\")\n\n  const result = textToScript(text)\n\n  if (result.ok) {\n    const row = CharacterRow({\n      children: result.value,\n      compact: true,\n    })\n\n    const anchored = Anchor({\n      at: \"cc\",\n      children: row,\n    })\n\n    svg.appendChild(anchored)\n  } else {\n    const text = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\")\n\n    text.textContent = result.reason\n\n    svg.appendChild(text)\n  }\n\n  document.body.append(svg)\n\n  fitViewBox(svg)\n}\n\ndisplayText(\"Wattunkí ruyün\")\n```\n\nAnd here's the same example with JSX:\n\n```tsx\nimport {\n  Anchor,\n  CharacterRow,\n  HandleResult,\n  fitViewBox,\n  textToScript,\n} from \"@zsnout/ithkuil/script\"\n\nfunction displayText(text: string) {\n  const svg = (\n    \u003csvg\u003e\n      \u003cHandleResult\n        ok={(value) =\u003e (\n          \u003cAnchor at=\"cc\"\u003e\n            \u003cCharacterRow compact\u003e{value}\u003c/CharacterRow\u003e\n          \u003c/Anchor\u003e\n        )}\n        error={(reason) =\u003e \u003ctext\u003e{reason}\u003c/text\u003e}\n      \u003e\n        {textToScript(text)}\n      \u003c/HandleResult\u003e\n    \u003c/svg\u003e\n  ) as SVGSVGElement\n\n  document.body.append(svg)\n\n  fitViewBox(svg)\n}\n\ndisplayText(\"Wattunkí ruyün\")\n```\n\nBefore setting up JSX, make sure you're in a TypeScript project.\n\nIf you don't already have a JSX transform, add these lines to your tsconfig's\n`compilerOptions` to enable JSX:\n\n```json\n{\n  \"compilerOptions\": {\n    ...\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"@zsnout/ithkuil/script\"\n    ...\n  }\n}\n```\n\nIf you're already using a JSX transform, write this at the top of any files you\nwant to use `@zsnout/ithkuil`'s JSX in.\n\n```ts\n/* @jsx react-jsx */\n/* @jsxRuntime automatic */\n/* @jsxImportSource @zsnout/ithkuil/script */\n```\n\nThat's it!\n\nNote that the JSX runtime is intentionally very minimal. There are no event\nlisteners, no signals, no hooks, very few type definitions, and it only\ngenerates SVG elements.\n\n## Example 1\n\nThis example compiles a simple formative.\n\n```ts\nimport { formativeToIthkuil } from \"@zsnout/ithkuil/generate\"\n\nconst result = formativeToIthkuil({\n  root: \"kš\",\n  type: \"UNF/C\",\n})\n\nconsole.log(result)\n// kšala\n```\n\nNotice how the function `formativeToIthkuil` is able to infer default values for\nmost of its arguments.\n\n## Example 2\n\nJust being able to convert formative roots into Ithkuil isn't very exciting.\nLet's try an example with many options.\n\n```ts\nimport { formativeToIthkuil } from \"@zsnout/ithkuil/generate\"\n\nconst result = formativeToIthkuil({\n  type: \"UNF/C\",\n\n  // Type-2 concatenation\n  concatenationType: 2,\n\n  // Completive Version\n  version: \"CPT\",\n\n  // Stem II\n  stem: 2,\n\n  // \"kš\" root\n  root: \"kš\",\n\n  // Dynamic Function\n  function: \"DYN\",\n\n  // Objective Specification\n  specification: \"OBJ\",\n\n  // Amalgamative Context\n  context: \"AMG\",\n\n  slotVAffixes: [\n    // Referential Affix\n    {\n      // 1m:BEN Referent\n      referents: [\"1m:BEN\"],\n\n      // Ergative Case\n      case: \"ERG\",\n    },\n  ],\n\n  ca: {\n    // Multiplex/Fuzzy/Connected Configuration\n    configuration: \"MFC\",\n\n    // Coalescent Affiliation\n    affiliation: \"COA\",\n\n    // Graduative Extension\n    extension: \"GRA\",\n  },\n\n  // Repetitive Phase\n  vn: \"REP\",\n})\n\nconsole.log(result)\n// hwikšöeroeržžgeiha\n```\n\nNotice that in the above example, we didn't have to say whether the Vn slot was\na Valence, Phase, etc. We simply specified \"REP\" as the Vn slot and\n`@zsnout/ithkuil` figured out the appropriate category automatically.\n\nIn addition, we only specified part of the Ca complex. We left out Monadic\nPerspective and Normal Essence, but `@zsnout/ithkuil` inferred those.\n\n## Example 3\n\nNot only can `@zsnout/ithkuil` generate formatives, but it can also parse them\nusing the `parseFormative` function. Let's try it out.\n\n```ts\nimport { parseFormative } from \"@zsnout/ithkuil/parse\"\n\nconst result = parseFormative(\"malëuţřait\")\n\nconsole.log(result)\n\n// {\n//   type: \"UNF/C\",\n//   concatenationType: undefined,\n//   shortcut: false,\n//   stem: 1,\n//   version: \"PRC\",\n//   root: \"m\",\n//   context: \"EXS\",\n//   specification: \"BSC\",\n//   function: \"STA\",\n//   slotVAffixes: [],\n//   ca: {},\n//   slotVIIAffixes: [\n//     { type: 2, degree: 5, cs: \"ţř\" },\n//     { type: 2, degree: 1, cs: \"t\" },\n//   ],\n//   mood: undefined,\n//   caseScope: undefined,\n//   vn: undefined,\n//   case: undefined,\n//   illocutionValidation: undefined,\n// }\n```\n\nAs you can see, `parseFormative` quickly turns formative strings into\nprogrammatically analyzable objects.\n\nNote that concatenated formatives must be parsed separately from their parents,\nas shown below with the example \"hlarrau-laza\" (a tribe of people owned by a\ncat).\n\n```ts\nimport { parseFormative } from \"@zsnout/ithkuil/parse\"\n\n// Correct:\n\nconst result1 = parseFormative(\"hlarrau\")\nconst result2 = parseFormative(\"laza\")\n\nconsole.log(result1, result2) // { type: \"UNF/C\", ... }, { type: \"UNF/C\", ... }\n\n// Incorrect:\n\nconst result = parseFormative(\"hlarrau-laza\")\n\nconsole.log(result) // undefined\n```\n\nNote that `parseFormative` can output three types of values.\n\n1. If the formative doesn't have a valid slot structure (e.g. an invalid number\n   of C and V forms), `undefined` is returned.\n\n2. If the formative has a valid slot structure but its slots are filled with\n   invalid values (such as üö in the Vc slot), an error is thrown.\n\n3. If the formative is a valid formative, the parsed formative is returned.\n\n## Example 4\n\n`@zsnout/ithkuil` doesn't just handle formatives; it can work with all types of\nIthkuilic words. Let's try generating an affixual adjunct, as those are used\nfrequently to move affixes out of formatives.\n\n```ts\nimport { affixualAdjunctToIthkuil } from \"@zsnout/ithkuil/generate\"\n\nconst result = affixualAdjunctToIthkuil({\n  affixes: [\n    {\n      type: 1,\n      degree: 2,\n      cs: \"c\",\n    },\n  ],\n  scope: \"VII:DOM\",\n})\n\nconsole.log(result)\n// äce\n```\n\n## Example 5\n\nThis example creates a referential (1m.BEN-CTE-GID₁/3).\n\n```ts\nimport { referentialToIthkuil } from \"@zsnout/ithkuil/generate\"\n\nconst result = referentialToIthkuil({\n  referents: [\"1m:BEN\"],\n  specification: \"CTE\",\n  affixes: [\n    {\n      type: 1,\n      degree: 3,\n      cs: \"c\",\n    },\n  ],\n})\n\nconsole.log(result)\n// raxtec\n```\n\n## Example 6\n\nThis example shows the glossing capabilities of `@zsnout/ithkuil`.\n\n```ts\nimport { glossWord } from \"@zsnout/ithkuil/gloss/index.js\"\nimport { parseWord } from \"@zsnout/ithkuil/parse/index.js\"\n\nconst result2 = parseWord(\"wetace\")\n\nif (result2) {\n  const { short, full } = glossWord(result2)\n\n  // S2-‘that one’-‘female’₁-ABS\n  console.log(short)\n\n  // stem_two-‘that one’-‘female’₁-absolutive\n  console.log(full)\n}\n```\n\nAs you can see, `@zsnout/ithkuil` provides simple glossing functionality for\nIthkuil words.\n\n## Example 7\n\nEvery data form has a corresponding Zod parser for it, which allows for quick\nand easy validation of it.\n\nThis example validates that an object is, in fact, an Ithkuilic adjunct. This\ncan be useful when integrating this project with external sources.\n\n```ts\nimport { adjunctToIthkuil } from \"@zsnout/ithkuil/generate\"\nimport { adjunct } from \"@zsnout/ithkuil/zod\"\n\nconst myAdjunct = getAdjunctFromSomeInternetSource()\n\ntry {\n  const realAdjunct = adjunct.parse(myAdjunct)\n\n  const result = adjunctToIthkuil(realAdjunct)\n\n  console.log(result)\n} catch (error) {\n  console.error(\"The adjunct was malformed.\", { cause: error })\n}\n```\n\n## Changelog\n\n### 0.1.52\n\n- **Breaking change:** Multiple referents may now be stored in a referential\n  affix. As such, all referential affixes now use the `referents` property\n  instead of the singular `referent` property, and take a `ReferentList` instead\n  of a single `Referent`.\n\n- **Breaking change:** `parseReferentialAffixCs` has been merged into\n  `parseReferentListAndPerspective`. As such, `parseReferentListAndPerspective`\n  now takes a boolean argument indicating whether it is parsing a referential\n  affix.\n\n- **Breaking change:** The `vnToAffix`, `toAffix`, and\n  `mergeAdjunctsAndFormative` functions have been moved from\n  `@zsnout/ithkuil/script` to `@zsnout/ithkuil/generate` to allow for them to be\n  imported without needing an SVG document in place.\n\n### 0.1.20\n\n- Fixed issues with examples.\n\n### 0.1.19\n\n- **Massive breaking change:** `@zsnout/ithkuil/generator`'s exports are now\n  only available under `@zsnout/ithkuil/generate`. They are no longer exported\n  from `@zsnout/ithkuil`.\n\n- **Massive breaking change:** `@zsnout/ithkuil/parser`'s exports are now only\n  available under `@zsnout/ithkuil/parse`. They are no longer exported from\n  `@zsnout/ithkuil`.\n\n- **Breaking change:** The `DFT` level has been correctly updated to `DFC`.\n\n- **New feature:** The `@zsnout/ithkuil/script` sub-package has been created,\n  and may be used to generate Ithkuil script as SVG paths.\n\n### 0.1.18\n\n- Fixed punctuation in README.\n\n### 0.1.17\n\n- **Output change and bug fix:** Referentials that have multiple referents are\n  now parsed correctly.\n\n### 0.1.16\n\n- **Breaking change:** The `Referent` type, the `ALL_REFERENTS` array, and the\n  `REFERENT_TO_ITHKUIL_MAP` and `REFERENT_TO_REFERENT_OBJECT_MAP` objects have\n  all been reordered. Where their unions, elements, and keys were previously\n  ordered `1m:NEU`, `2m:NEU`, `2p:NEU`, ..., `1m:BEN`, `2m:BEN`, ..., `1m:DET`,\n  ..., they are now ordered `1m:NEU`, `1m:BEN`, `1m:DET`, `2m:NEU`, `2m:BEN`,\n  etc.\n\n- **Breaking change:** Referentials can now take multiple referents in their\n  second referent slot. As such, the `referent2` property of referentials has\n  now been pluralized and changed to the `referents2` property.\n\n- **Breaking type-level change:** The `CN` type union now includes\n  `MoodOrCaseScope` instances. As such, it no longer only contains string\n  literals.\n\n- **Output change:** Passing differently ordered lists to\n  `referentListToIthkuil` now gives the same output.\n\n- Added `MoodOrCaseScope` class to represent Cn forms in Modular Adjuncts.\n\n- Added functions to lex and parse adjuncts\n\n- Added `parseWord` to parse formatives, referentials, and adjuncts all at once\n\n- Added `wordToIthkuil` to turn generic words into Ithkuil\n\n- Added `word` Zod parser to validate generic JSON objects representing words\n\n- Fix several issues involving the parsing of formatives with specialized\n  Cs-roots and specialized personal-reference roots\n\n- Allow multiple referents in C2 of dual referentials\n\n- Added `SingleRegisterAdjunct` to indicate individual registers, such as\n  `DSV:START`, `CGT:END`, and so on. Note that `SingleRegisterAdjunct` does not\n  include `NRR:START`, `NRR:END`, or `END:START`.\n\n### 0.1.15\n\n- **Output change:** Formatives with specialized personal-reference roots now\n  properly take a+Ca shortcuts\n\n- **Bug fix and output change:** Formatives with specialized Cs-roots or\n  personal-reference roots whose final Slot VII affix is NEG/4, DCD/4, or DCD/5\n  keep their proper Vv values instead of having them replaced with Slot VII\n  affix shortcut values\n\n- `parseFormative` now successfully parses formatives with specialized Cs-roots\n  and specialized personal-reference roots\n\n### 0.1.14\n\n- **Breaking change:** Changed `WithWYAlternative.precededByW` to\n  `WithWYAlternative.valueAfterW`\n\n- **Breaking change:** Changed `WithWYAlternative.precededByY` to\n  `WithWYAlternative.valueAfterY`\n\n- **Breaking change:** Renamed export `ALL_DIPTIONGS` to `ALL_DIPTHONGS`\n\n- **Breaking change:** Zod validators are now only exported from\n  `@zsnout/ithkuil/zod` under the direct names `aspect`, `caseScope`,\n  `partialFormative`, etc., and are no longer available from the core\n  `@zsnout/ithkuil`.\n\n- **Output change:** All generated words now end in a vowel form, fixing issues\n  with External Juncture.\n\n- **Output change and bug fix:** Vv forms are now properly generated.\n  Previously, a Stem 3 PRC word may have outputted a Stem 0 CPT Vv form, and\n  vice versa.\n\n- **Bug fix:** Moved Zod validators into a separate file. This fixes an issue\n  where imports were resolved in an order that caused many Zod validators to be\n  `undefined` or in a TDZ when they were expected to have values.\n\n- Added `STANDARD_VOWEL_TABLE`, an array of vowel forms indexed as would be\n  appropriate for humans. Where the previous tables had unexpected behavior\n  (`ZERO_INDEXED_VOWEL_TABLE[2][9]` = degree 0 of sequence 3), the new table\n  behaves exactly as would be expected (`STANDARD_VOWEL_TABLE[3][0]` = degree 0\n  of sequence 3). The old tables `ZERO_INDEXED_VOWEL_TABLE` and\n  `ONE_INDEXED_VOWEL_TABLE` will remain for performance- and TypeScript-related\n  reasons.\n\n- Use `STANDARD_VOWEL_TABLE` instead of `ONE_INDEXED_VOWEL_TABLE` in\n  `generator/affix/index.ts`.\n\n- Add `deepFreezeAndNullPrototype` helper\n\n- Add `insertGlottalStopIntoPossiblyWithWYAlternative` helper\n\n- Change formatting to insert commas at the end of multiline function calls\n\n- Add regular expression builder\n\n- Add basic regular expression forms such as V, C, H, CG, CNG, VG, and VNG\n\n- Add regular expression for non-shortcut formatives\n\n- Add parsers for affixes, Ca forms, case scopes, cases, formatives, illocutions\n  and validations, moods, referential affixes, and Vn forms\n\n- Add `transformWord`\n\n- Add `VowelForm` for systematic parsing and stringification of vowel forms\n\n- Add `parseFormative` to parse formatives\n\n- Add Slot VIII Mood/Case-Scope shortcut generation\n\n### 0.1.13\n\n- **Breaking change:** Fixed spelling mistake (`concatenatenationType` --\u003e\n  `concatenationType`)\n\n### 0.1.12\n\n- Added case-accessor and inverse case-accessor affixes\n\n### 0.1.11\n\n- Added an array constant that contains all cases arranged in order and that\n  properly skips the 8th degree of the last four case categories. This new\n  array, `ALL_CASES_SKIPPING_DEGREE_8`, has a length of `72` and contains four\n  empty cells: one before each of the `RLT`, `VOC`, `NAV`, and `PLM` cases.\n\n- Fixed Ca gemination algorithm (geminates involving N/RPV and A/RPV are\n  complicated, and many Ca forms simply cannot be geminated according to\n  Ithkuil's rules)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzsakowitz%2Fithkuil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzsakowitz%2Fithkuil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzsakowitz%2Fithkuil/lists"}