@eten-tech-foundation/scripture-utilities 0.1.3 → 0.1.4

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.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("@xmldom/xmldom"),T="USJ",A="3.1",v=Object.freeze({type:T,version:A,content:[]}),M=["type","marker","content","sid","eid","number","code","altnumber","pubnumber","caller","align","category"];function m(e){return X.includes(e)}const X=["GEN","EXO","LEV","NUM","DEU","JOS","JDG","RUT","1SA","2SA","1KI","2KI","1CH","2CH","EZR","NEH","EST","JOB","PSA","PRO","ECC","SNG","ISA","JER","LAM","EZK","DAN","HOS","JOL","AMO","OBA","JON","MIC","NAM","HAB","ZEP","HAG","ZEC","MAL","MAT","MRK","LUK","JHN","ACT","ROM","1CO","2CO","GAL","EPH","PHP","COL","1TH","2TH","1TI","2TI","TIT","PHM","HEB","JAS","1PE","2PE","1JN","2JN","3JN","JUD","REV","TOB","JDT","ESG","WIS","SIR","BAR","LJE","S3Y","SUS","BEL","1MA","2MA","3MA","4MA","1ES","2ES","MAN","PS2","ODA","PSS","EZA","5EZ","6EZ","DAG","PS3","2BA","LBA","JUB","ENO","1MQ","2MQ","3MQ","REP","4BA","LAO","FRT","BAK","OTH","INT","CNC","GLO","TDX","NDX","XXA","XXB","XXC","XXD","XXE","XXF","XXG"],l="usx",O="3.1",_=`<${l} version="${O}" />`;function y(e){const n=new N.DOMParser().parseFromString(e,"text/xml");return R(n.documentElement)}function R(e){const[t]=e?J(e):[{content:[]}];return t.type=T,t.version=A,t}function J(e){const t={};let n=e.tagName,o,s,c="append";if(["row","cell"].includes(n)&&(n="table:"+n),e.attributes)for(const r of Array.from(e.attributes))t[r.name]=r.value;t.style&&(o=t.style,delete t.style),t.vid&&delete t.vid,t.status&&delete t.status;let i={type:n};o&&(i.marker=o),i={...i,...t},e.firstChild&&e.firstChild.nodeType===e.firstChild.TEXT_NODE&&e.firstChild.nodeValue&&h(e.firstChild.nodeValue)!==""&&(s=e.firstChild.nodeValue);const f=Array.from(e.childNodes);i.content=[],s&&i.content.push(s);for(const r of f){if(r.tagName===void 0)continue;const[u,S]=J(r);switch(S){case"append":i.content.push(u);break;case"merge":i.content=i.content.concat(u);break}r.nextSibling&&r.nextSibling.nodeType===r.nextSibling.TEXT_NODE&&r.nextSibling.nodeValue&&(h(r.nextSibling.nodeValue)!==""||r.nextSibling.nodeValue===" ")&&i.content.push(r.nextSibling.nodeValue)}return i.content.length===0&&i.type!==l&&delete i.content,"eid"in i&&["verse","chapter"].includes(n)&&(c="ignore"),[i,c]}function h(e){return e.replace(/(^[ \t\n\r\f\v]+)|([ \t\n\r\f\v]+$)/g,"")}let d,a;function U(e){const t=new N.DOMImplementation().createDocument("",l);return t.documentElement&&(t.documentElement.setAttribute("version",O),I(e,t)),t.toString()}function I(e,t){if(t.documentElement){for(const[n,o]of e.content.entries()){const s=n===e.content.length-1;b(o,t.documentElement,t,s)}return t.documentElement??void 0}}function b(e,t,n,o){let s,c,i;if(typeof e=="string")s=n.createTextNode(e);else if(c=e.type.replace("table:",""),s=n.createElement(c),B(s,e),e.content)for(const[r,u]of e.content.entries()){const S=r===e.content.length-1;b(u,s,n,S)}a&&(c==="verse"||t.tagName==="para"&&o)&&(i=p(n,a),a=void 0),c==="verse"&&typeof e!="string"&&e.sid!==void 0&&(a=e.sid),d&&(c==="chapter"||c==="para"&&o)&&(i=P(n,d),d=void 0),c==="chapter"&&typeof e!="string"&&e.sid!==void 0&&(d=e.sid);const f=t.nodeName===l&&(i==null?void 0:i.tagName)==="verse";i&&(!o||f)&&t.appendChild(i),t.appendChild(s),i&&o&&!f&&t.appendChild(i),o&&t.nodeName===l&&(a&&t.appendChild(p(n,a)),d&&t.appendChild(P(n,d)),a=void 0,d=void 0)}function B(e,t){t.type==="unmatched"?e.setAttribute("marker",t.marker):e.setAttribute("style",t.marker);for(const[n,o]of Object.entries(t))o&&!["type","marker","content"].includes(n)&&e.setAttribute(n,o)}function p(e,t){const n=e.createElement("verse");return n.setAttribute("eid",t),n}function P(e,t){const n=e.createElement("chapter");return n.setAttribute("eid",t),n}const E="$",g=".content[";function V(e){const t=e.split(g);if(t.shift()!==E)throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${E}'`);return t.map(o=>parseInt(o,10))}function C(e){return e.reduce((t,n)=>`${t}${g}${n}]`,E)}exports.EMPTY_USJ=v;exports.EMPTY_USX=_;exports.MARKER_OBJECT_PROPS=M;exports.USJ_TYPE=T;exports.USJ_VERSION=A;exports.USX_TYPE=l;exports.USX_VERSION=O;exports.indexesFromUsjJsonPath=V;exports.isValidBookCode=m;exports.usjJsonPathFromIndexes=C;exports.usjToUsxString=U;exports.usxStringToUsj=y;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("@xmldom/xmldom"),T="USJ",A="3.1",M=Object.freeze({type:T,version:A,content:[]}),_=["type","marker","content","sid","eid","number","code","altnumber","pubnumber","caller","align","category"];function m(e){return J.includes(e)}const J=["GEN","EXO","LEV","NUM","DEU","JOS","JDG","RUT","1SA","2SA","1KI","2KI","1CH","2CH","EZR","NEH","EST","JOB","PSA","PRO","ECC","SNG","ISA","JER","LAM","EZK","DAN","HOS","JOL","AMO","OBA","JON","MIC","NAM","HAB","ZEP","HAG","ZEC","MAL","MAT","MRK","LUK","JHN","ACT","ROM","1CO","2CO","GAL","EPH","PHP","COL","1TH","2TH","1TI","2TI","TIT","PHM","HEB","JAS","1PE","2PE","1JN","2JN","3JN","JUD","REV","TOB","JDT","ESG","WIS","SIR","BAR","LJE","S3Y","SUS","BEL","1MA","2MA","3MA","4MA","1ES","2ES","MAN","PS2","ODA","PSS","EZA","5EZ","6EZ","DAG","PS3","2BA","LBA","JUB","ENO","1MQ","2MQ","3MQ","REP","4BA","LAO","FRT","BAK","OTH","INT","CNC","GLO","TDX","NDX","XXA","XXB","XXC","XXD","XXE","XXF","XXG"],l="usx",O="3.1",X=`<${l} version="${O}" />`;function y(e){const n=new N.DOMParser().parseFromString(e,"text/xml");return R(n.documentElement)}function R(e){const[t]=e?b(e):[{content:[]}];return t.type=T,t.version=A,t}function b(e){const t={};let n=e.tagName,o,s,c="append";if(["row","cell"].includes(n)&&(n="table:"+n),e.attributes)for(const r of Array.from(e.attributes))t[r.name]=r.value;t.style&&(o=t.style,delete t.style),t.vid&&delete t.vid,t.status&&delete t.status;let i={type:n};o&&(i.marker=o),i={...i,...t},e.firstChild&&e.firstChild.nodeType===e.firstChild.TEXT_NODE&&e.firstChild.nodeValue&&h(e.firstChild.nodeValue)!==""&&(s=e.firstChild.nodeValue);const f=Array.from(e.childNodes);i.content=[],s&&i.content.push(s);for(const r of f){if(r.tagName===void 0)continue;const[u,S]=b(r);switch(S){case"append":i.content.push(u);break;case"merge":i.content=i.content.concat(u);break}r.nextSibling&&r.nextSibling.nodeType===r.nextSibling.TEXT_NODE&&r.nextSibling.nodeValue&&(h(r.nextSibling.nodeValue)!==""||r.nextSibling.nodeValue===" ")&&i.content.push(r.nextSibling.nodeValue)}return i.content.length===0&&i.type!==l&&delete i.content,"eid"in i&&["verse","chapter"].includes(n)&&(c="ignore"),[i,c]}function h(e){return e.replace(/(^[ \t\n\r\f\v]+)|([ \t\n\r\f\v]+$)/g,"")}let d,a;function U(e){const t=new N.DOMImplementation().createDocument("",l);return t.documentElement&&(t.documentElement.setAttribute("version",O),I(e,t)),t.toString()}function I(e,t){if(t.documentElement){for(const[n,o]of e.content.entries()){const s=n===e.content.length-1;g(o,t.documentElement,t,s)}return t.documentElement??void 0}}function g(e,t,n,o){let s,c,i;if(typeof e=="string")s=n.createTextNode(e);else if(c=e.type.replace("table:",""),s=n.createElement(c),B(s,e),e.content)for(const[r,u]of e.content.entries()){const S=r===e.content.length-1;g(u,s,n,S)}a&&(c==="verse"||t.tagName==="para"&&o)&&(i=p(n,a),a=void 0),c==="verse"&&typeof e!="string"&&e.sid!==void 0&&(a=e.sid),d&&(c==="chapter"||c==="para"&&o)&&(i=P(n,d),d=void 0),c==="chapter"&&typeof e!="string"&&e.sid!==void 0&&(d=e.sid);const f=t.nodeName===l&&(i==null?void 0:i.tagName)==="verse";i&&(!o||f)&&t.appendChild(i),t.appendChild(s),i&&o&&!f&&t.appendChild(i),o&&t.nodeName===l&&(a&&t.appendChild(p(n,a)),d&&t.appendChild(P(n,d)),a=void 0,d=void 0)}function B(e,t){t.marker&&(t.type==="unmatched"?e.setAttribute("marker",t.marker):e.setAttribute("style",t.marker));for(const[n,o]of Object.entries(t))o&&!["type","marker","content"].includes(n)&&e.setAttribute(n,o)}function p(e,t){const n=e.createElement("verse");return n.setAttribute("eid",t),n}function P(e,t){const n=e.createElement("chapter");return n.setAttribute("eid",t),n}const E="$",v=".content[";function V(e){const t=e.split(v);if(t.shift()!==E)throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${E}'`);return t.map(o=>parseInt(o,10))}function C(e){return e.reduce((t,n)=>`${t}${v}${n}]`,E)}exports.EMPTY_USJ=M;exports.EMPTY_USX=X;exports.MARKER_OBJECT_PROPS=_;exports.USJ_TYPE=T;exports.USJ_VERSION=A;exports.USX_TYPE=l;exports.USX_VERSION=O;exports.VALID_BOOK_CODES=J;exports.indexesFromUsjJsonPath=V;exports.isValidBookCode=m;exports.usjJsonPathFromIndexes=C;exports.usjToUsxString=U;exports.usxStringToUsj=y;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/converters/usj/usj.model.ts","../src/converters/usj/usx.model.ts","../src/converters/usj/usx-to-usj.ts","../src/converters/usj/usj-to-usx.ts","../src/converters/usj/jsonpath-indexes.ts"],"sourcesContent":["/**\n * Unified Scripture JSON (USJ) - The JSON variant of USFM and USX data models.\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js\n */\n\n/** The USJ spec type */\nexport const USJ_TYPE = \"USJ\";\n\n/** The USJ spec version */\nexport const USJ_VERSION = \"3.1\";\n\nexport const EMPTY_USJ = Object.freeze<Usj>({ type: USJ_TYPE, version: USJ_VERSION, content: [] });\n\n/** List of known properties of `MarkerObject` */\nexport const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [\n \"type\",\n \"marker\",\n \"content\",\n \"sid\",\n \"eid\",\n \"number\",\n \"code\",\n \"altnumber\",\n \"pubnumber\",\n \"caller\",\n \"align\",\n \"category\",\n];\n\n/** Single piece of Scripture content */\nexport type MarkerContent = string | MarkerObject;\n\n/** A Scripture Marker and its contents */\nexport interface MarkerObject {\n /**\n * The kind/category of node or element this is, corresponding the USFM marker and USX node\n * @example `para`, `verse`, `char`\n */\n type: string;\n /**\n * The corresponding marker in USFM or style in USX\n * @example `p`, `v`, `nd`\n */\n marker: string;\n /** This marker's contents laid out in order */\n content?: MarkerContent[];\n /** Indicates the Book-chapter-verse value in the paragraph based structure */\n sid?: string;\n /** Milestone end ID, matches start ID (not currently included in USJ spec) */\n eid?: string;\n /** Chapter number or verse number */\n number?: string;\n /** The 3-letter book code in ID element */\n code?: BookCode;\n /** Alternate chapter number or verse number */\n altnumber?: string;\n /** Published character of chapter or verse */\n pubnumber?: string;\n /** Caller character for footnotes and cross-refs */\n caller?: string;\n /** Alignment of table cells */\n align?: string;\n /** Category of extended study bible sections */\n category?: string;\n}\n\n/** Scripture data represented in JSON format. Data compatible transformation from USX/USFM */\nexport interface Usj {\n /** The USJ spec type */\n type: typeof USJ_TYPE;\n /** The USJ spec version */\n version: typeof USJ_VERSION;\n /** The JSON representation of scripture contents from USFM/USX */\n content: MarkerContent[];\n}\n\nexport function isValidBookCode(code: string): boolean {\n return VALID_BOOK_CODES.includes(code as BookCode);\n}\n\n/** 3-letter Scripture book code */\nexport type BookCode = (typeof VALID_BOOK_CODES)[number];\n\nconst VALID_BOOK_CODES = [\n // Old Testament\n \"GEN\",\n \"EXO\",\n \"LEV\",\n \"NUM\",\n \"DEU\",\n \"JOS\",\n \"JDG\",\n \"RUT\",\n \"1SA\",\n \"2SA\",\n \"1KI\",\n \"2KI\",\n \"1CH\",\n \"2CH\",\n \"EZR\",\n \"NEH\",\n \"EST\",\n \"JOB\",\n \"PSA\",\n \"PRO\",\n \"ECC\",\n \"SNG\",\n \"ISA\",\n \"JER\",\n \"LAM\",\n \"EZK\",\n \"DAN\",\n \"HOS\",\n \"JOL\",\n \"AMO\",\n \"OBA\",\n \"JON\",\n \"MIC\",\n \"NAM\",\n \"HAB\",\n \"ZEP\",\n \"HAG\",\n \"ZEC\",\n \"MAL\",\n // New Testament\n \"MAT\",\n \"MRK\",\n \"LUK\",\n \"JHN\",\n \"ACT\",\n \"ROM\",\n \"1CO\",\n \"2CO\",\n \"GAL\",\n \"EPH\",\n \"PHP\",\n \"COL\",\n \"1TH\",\n \"2TH\",\n \"1TI\",\n \"2TI\",\n \"TIT\",\n \"PHM\",\n \"HEB\",\n \"JAS\",\n \"1PE\",\n \"2PE\",\n \"1JN\",\n \"2JN\",\n \"3JN\",\n \"JUD\",\n \"REV\",\n // Deuterocanon\n \"TOB\",\n \"JDT\",\n \"ESG\",\n \"WIS\",\n \"SIR\",\n \"BAR\",\n \"LJE\",\n \"S3Y\",\n \"SUS\",\n \"BEL\",\n \"1MA\",\n \"2MA\",\n \"3MA\",\n \"4MA\",\n \"1ES\",\n \"2ES\",\n \"MAN\",\n \"PS2\",\n \"ODA\",\n \"PSS\",\n \"EZA\",\n \"5EZ\",\n \"6EZ\",\n \"DAG\",\n \"PS3\",\n \"2BA\",\n \"LBA\",\n \"JUB\",\n \"ENO\",\n \"1MQ\",\n \"2MQ\",\n \"3MQ\",\n \"REP\",\n \"4BA\",\n \"LAO\",\n // Non scripture\n \"FRT\",\n \"BAK\",\n \"OTH\",\n \"INT\",\n \"CNC\",\n \"GLO\",\n \"TDX\",\n \"NDX\",\n \"XXA\",\n \"XXB\",\n \"XXC\",\n \"XXD\",\n \"XXE\",\n \"XXF\",\n \"XXG\",\n] as const;\n","/**\n * Unified Scripture XML (USX).\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/main/grammar/usx.rng\n */\n\n/** The USX spec type */\nexport const USX_TYPE = \"usx\";\n\n/** The USX spec version */\nexport const USX_VERSION = \"3.1\";\n\nexport const EMPTY_USX = `<${USX_TYPE} version=\"${USX_VERSION}\" />`;\n","/**\n * Convert Scripture from USX to USJ.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMParser, Element } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, USJ_TYPE, USJ_VERSION, Usj } from \"./usj.model.js\";\nimport { USX_TYPE } from \"./usx.model.js\";\n\ntype Action = \"append\" | \"merge\" | \"ignore\";\ninterface Attribs {\n [name: string]: string;\n}\n\nexport function usxStringToUsj(usxString: string): Usj {\n const parser = new DOMParser();\n const inputUsxDom = parser.parseFromString(usxString, \"text/xml\");\n return usxDomToUsj(inputUsxDom.documentElement);\n}\n\nexport function usxDomToUsj(inputUsxDom: Element | null): Usj {\n const [outputJson] = inputUsxDom\n ? convertUsxRecurse(inputUsxDom)\n : [{ content: [] as MarkerContent[] } as Usj];\n outputJson.type = USJ_TYPE;\n outputJson.version = USJ_VERSION;\n return outputJson;\n}\n\nfunction convertUsxRecurse<T extends Usj | MarkerObject = Usj>(\n inputUsxElement: Element,\n): [outputJson: T, action: Action] {\n const attribs: Attribs = {};\n let type: string = inputUsxElement.tagName;\n let marker: string | undefined;\n let text: string | undefined;\n let action: Action = \"append\";\n\n if ([\"row\", \"cell\"].includes(type)) type = \"table:\" + type;\n if (inputUsxElement.attributes) {\n for (const attrib of Array.from(inputUsxElement.attributes)) {\n attribs[attrib.name] = attrib.value;\n }\n }\n\n if (attribs.style) {\n marker = attribs.style;\n delete attribs.style;\n }\n // dropping because presence of vid in para elements is not consistent in USX\n if (attribs.vid) delete attribs.vid;\n // Not dropping `attribs.closed` for backwards compatibility.\n // dropping because it is nonstandard derived metadata that could get out of date\n if (attribs.status) delete attribs.status;\n\n let outObj: T = { type } as T;\n if (marker) (outObj as MarkerObject).marker = marker;\n outObj = { ...outObj, ...attribs };\n\n if (\n inputUsxElement.firstChild &&\n inputUsxElement.firstChild.nodeType === inputUsxElement.firstChild.TEXT_NODE &&\n inputUsxElement.firstChild.nodeValue &&\n asciiTrim(inputUsxElement.firstChild.nodeValue) !== \"\"\n ) {\n text = inputUsxElement.firstChild.nodeValue;\n }\n\n const children = Array.from(inputUsxElement.childNodes);\n outObj.content = [];\n\n if (text) {\n outObj.content.push(text);\n }\n\n for (const child of children) {\n // ChildNodes are Elements.\n if ((child as Element).tagName === undefined) {\n continue;\n }\n // ChildNodes are Elements.\n const [childDict, whatToDo] = convertUsxRecurse<MarkerObject>(child as Element);\n\n switch (whatToDo) {\n case \"append\":\n outObj.content.push(childDict);\n break;\n case \"merge\":\n outObj.content = outObj.content.concat(childDict);\n break;\n case \"ignore\":\n break;\n default:\n break;\n }\n\n // Handle tail text\n if (\n child.nextSibling &&\n child.nextSibling.nodeType === child.nextSibling.TEXT_NODE &&\n child.nextSibling.nodeValue &&\n (asciiTrim(child.nextSibling.nodeValue) !== \"\" || child.nextSibling.nodeValue === \" \")\n ) {\n outObj.content.push(child.nextSibling.nodeValue);\n }\n }\n\n // For backward compatibility, not deleting content for type: chapter, verse, optbreak, ms OR\n // marker: va, ca, b.\n if (outObj.content.length === 0 && outObj.type !== USX_TYPE) {\n delete outObj.content;\n }\n\n if (\"eid\" in outObj && [\"verse\", \"chapter\"].includes(type)) {\n action = \"ignore\";\n }\n\n return [outObj, action];\n}\n\n/**\n * Removes leading and trailing ASCII whitespace.\n *\n * Only trim ASCII whitespace characters: space, tab, line feed, carriage return, form feed,\n * vertical tab.\n * @param str - The string to remove whitespace from.\n * @returns the string with leading and trailing whitespace removed.\n */\nfunction asciiTrim(str: string): string {\n return str.replace(/(^[ \\t\\n\\r\\f\\v]+)|([ \\t\\n\\r\\f\\v]+$)/g, \"\");\n}\n","/**\n * Convert Scripture from USJ to USX.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMImplementation, Document, Element, Text } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, Usj } from \"./usj.model.js\";\nimport { USX_TYPE, USX_VERSION } from \"./usx.model.js\";\n\nlet chapterEid: string | undefined;\nlet verseEid: string | undefined;\n\nexport function usjToUsxString(usj: Usj): string {\n const usxDoc = new DOMImplementation().createDocument(\"\", USX_TYPE);\n if (usxDoc.documentElement) {\n usxDoc.documentElement.setAttribute(\"version\", USX_VERSION);\n usjToUsxDom(usj, usxDoc);\n }\n return usxDoc.toString();\n}\n\nexport function usjToUsxDom(usj: Usj, usxDoc: Document): Element | undefined {\n if (!usxDoc.documentElement) return undefined;\n\n for (const [index, markerContent] of usj.content.entries()) {\n const isLastItem = index === usj.content.length - 1;\n convertUsjRecurse(markerContent, usxDoc.documentElement, usxDoc, isLastItem);\n }\n return usxDoc.documentElement ?? undefined;\n}\n\nfunction convertUsjRecurse(\n markerContent: MarkerContent,\n parentElement: Element,\n usxDoc: Document,\n isLastItem: boolean,\n) {\n let element: Text | Element;\n let type: string | undefined;\n let eidElement: Element | undefined;\n if (typeof markerContent === \"string\") element = usxDoc.createTextNode(markerContent);\n else {\n type = markerContent.type.replace(\"table:\", \"\");\n element = usxDoc.createElement(type);\n setAttributes(element, markerContent);\n if (markerContent.content) {\n for (const [index, item] of markerContent.content.entries()) {\n const _isLastItem = index === markerContent.content.length - 1;\n convertUsjRecurse(item, element, usxDoc, _isLastItem);\n }\n }\n }\n\n // Create chapter and verse end elements from SID attributes.\n if (verseEid && (type === \"verse\" || (parentElement.tagName === \"para\" && isLastItem))) {\n eidElement = createVerseEndElement(usxDoc, verseEid);\n verseEid = undefined;\n }\n if (type === \"verse\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n verseEid = markerContent.sid;\n\n if (chapterEid && (type === \"chapter\" || (type === \"para\" && isLastItem))) {\n eidElement = createChapterEndElement(usxDoc, chapterEid);\n chapterEid = undefined;\n }\n if (type === \"chapter\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n chapterEid = markerContent.sid;\n\n // Append to parent.\n const isVerseInImpliedPara =\n parentElement.nodeName === USX_TYPE && eidElement?.tagName === \"verse\";\n if (eidElement && (!isLastItem || isVerseInImpliedPara)) parentElement.appendChild(eidElement);\n parentElement.appendChild(element);\n if (eidElement && isLastItem && !isVerseInImpliedPara) parentElement.appendChild(eidElement);\n\n // Allow for final chapter and verse end elements at the end of an implied para.\n if (isLastItem && parentElement.nodeName === USX_TYPE) {\n if (verseEid) parentElement.appendChild(createVerseEndElement(usxDoc, verseEid));\n if (chapterEid) parentElement.appendChild(createChapterEndElement(usxDoc, chapterEid));\n verseEid = undefined;\n chapterEid = undefined;\n }\n}\n\nfunction setAttributes(element: Element, markerContent: MarkerObject) {\n if (markerContent.type === \"unmatched\") element.setAttribute(\"marker\", markerContent.marker);\n else element.setAttribute(\"style\", markerContent.marker);\n for (const [key, value] of Object.entries(markerContent)) {\n if (value && ![\"type\", \"marker\", \"content\"].includes(key)) {\n element.setAttribute(key, value as string);\n }\n }\n}\n\nfunction createVerseEndElement(usxDoc: Document, verseEid: string): Element {\n const eidElement = usxDoc.createElement(\"verse\");\n eidElement.setAttribute(\"eid\", verseEid);\n return eidElement;\n}\n\nfunction createChapterEndElement(usxDoc: Document, chapterEid: string): Element {\n const eidElement = usxDoc.createElement(\"chapter\");\n eidElement.setAttribute(\"eid\", chapterEid);\n return eidElement;\n}\n","const JSON_PATH_START = \"$\";\nconst JSON_PATH_CONTENT = \".content[\";\n\n/**\n * Converts a USJ JSONPath string into an array of indexes.\n *\n * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.\n * @returns An array of numeric indexes extracted from the JSONPath.\n * @throws Will throw an error if the JSONPath does not start with `$`.\n */\nexport function indexesFromUsjJsonPath(jsonPath: string): number[] {\n const path = jsonPath.split(JSON_PATH_CONTENT);\n if (path.shift() !== JSON_PATH_START)\n throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${JSON_PATH_START}'`);\n\n const indexes = path.map((str) => parseInt(str, 10));\n return indexes;\n}\n\n/**\n * Converts an array of indexes into a USJ JSONPath string.\n *\n * @param indexes - An array of numeric indexes to convert.\n * @returns A USJ JSONPath string constructed from the indexes.\n */\nexport function usjJsonPathFromIndexes(indexes: number[]): string {\n return indexes.reduce((path, index) => `${path}${JSON_PATH_CONTENT}${index}]`, JSON_PATH_START);\n}\n"],"names":["USJ_TYPE","USJ_VERSION","EMPTY_USJ","MARKER_OBJECT_PROPS","isValidBookCode","code","VALID_BOOK_CODES","USX_TYPE","USX_VERSION","EMPTY_USX","usxStringToUsj","usxString","inputUsxDom","DOMParser","usxDomToUsj","outputJson","convertUsxRecurse","inputUsxElement","attribs","type","marker","text","action","attrib","outObj","asciiTrim","children","child","childDict","whatToDo","str","chapterEid","verseEid","usjToUsxString","usj","usxDoc","DOMImplementation","usjToUsxDom","index","markerContent","isLastItem","convertUsjRecurse","parentElement","element","eidElement","setAttributes","item","_isLastItem","createVerseEndElement","createChapterEndElement","isVerseInImpliedPara","key","value","JSON_PATH_START","JSON_PATH_CONTENT","indexesFromUsjJsonPath","jsonPath","path","usjJsonPathFromIndexes","indexes"],"mappings":"kHAOaA,EAAW,MAGXC,EAAc,MAEdC,EAAY,OAAO,OAAY,CAAE,KAAMF,EAAU,QAASC,EAAa,QAAS,EAAC,CAAG,EAGpFE,EAA8C,CACzD,OACA,SACA,UACA,MACA,MACA,SACA,OACA,YACA,YACA,SACA,QACA,UACF,EAiDO,SAASC,EAAgBC,EAAuB,CACrD,OAAOC,EAAiB,SAASD,CAAgB,CACnD,CAKA,MAAMC,EAAmB,CAEvB,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,KACF,ECtMaC,EAAW,MAGXC,EAAc,MAEdC,EAAY,IAAIF,CAAQ,aAAaC,CAAW,OCGtD,SAASE,EAAeC,EAAwB,CAErD,MAAMC,EADS,IAAIC,YAAA,EACQ,gBAAgBF,EAAW,UAAU,EAChE,OAAOG,EAAYF,EAAY,eAAe,CAChD,CAEO,SAASE,EAAYF,EAAkC,CAC5D,KAAM,CAACG,CAAU,EAAIH,EACjBI,EAAkBJ,CAAW,EAC7B,CAAC,CAAE,QAAS,CAAA,EAA8B,EAC9C,OAAAG,EAAW,KAAOf,EAClBe,EAAW,QAAUd,EACdc,CACT,CAEA,SAASC,EACPC,EACiC,CACjC,MAAMC,EAAmB,CAAA,EACzB,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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/converters/usj/usj.model.ts","../src/converters/usj/usx.model.ts","../src/converters/usj/usx-to-usj.ts","../src/converters/usj/usj-to-usx.ts","../src/converters/usj/jsonpath-indexes.ts"],"sourcesContent":["/**\n * Unified Scripture JSON (USJ) - The JSON variant of USFM and USX data models.\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js\n */\n\n/**\n * The USJ spec type\n * @public\n */\nexport const USJ_TYPE = \"USJ\";\n\n/**\n * The USJ spec version\n * @public\n */\nexport const USJ_VERSION = \"3.1\";\n\n/**\n * An empty USJ object\n * @public\n */\nexport const EMPTY_USJ = Object.freeze<Usj>({ type: USJ_TYPE, version: USJ_VERSION, content: [] });\n\n/**\n * List of known properties of `MarkerObject`\n * @public\n */\nexport const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [\n \"type\",\n \"marker\",\n \"content\",\n \"sid\",\n \"eid\",\n \"number\",\n \"code\",\n \"altnumber\",\n \"pubnumber\",\n \"caller\",\n \"align\",\n \"category\",\n];\n\n/**\n * Single piece of Scripture content\n * @public\n */\nexport type MarkerContent = string | MarkerObject;\n\n/**\n * A Scripture Marker and its contents\n * @public\n */\nexport interface MarkerObject {\n /**\n * The kind/category of node or element this is, corresponding the USFM marker and USX node\n * @example `para`, `verse`, `char`\n */\n type: string;\n /**\n * The corresponding marker in USFM or style in USX\n * @example `p`, `v`, `nd`\n */\n marker?: string;\n /** This marker's contents laid out in order */\n content?: MarkerContent[];\n /** Indicates the Book-chapter-verse value in the paragraph based structure */\n sid?: string;\n /** Milestone end ID, matches start ID (not currently included in USJ spec) */\n eid?: string;\n /** Chapter number or verse number */\n number?: string;\n /** The 3-letter book code in ID element */\n code?: BookCode;\n /** Alternate chapter number or verse number */\n altnumber?: string;\n /** Published character of chapter or verse */\n pubnumber?: string;\n /** Caller character for footnotes and cross-refs */\n caller?: string;\n /** Alignment of table cells */\n align?: string;\n /** Category of extended study bible sections */\n category?: string;\n}\n\n/**\n * Scripture data represented in JSON format. Data compatible transformation from USX/USFM\n * @public\n */\nexport interface Usj {\n /** The USJ spec type */\n type: typeof USJ_TYPE;\n /** The USJ spec version */\n version: typeof USJ_VERSION;\n /** The JSON representation of scripture contents from USFM/USX */\n content: MarkerContent[];\n}\n\n/**\n * Check if the given code is a valid 3-letter Scripture book code.\n * @public\n */\nexport function isValidBookCode(code: string): boolean {\n return VALID_BOOK_CODES.includes(code as BookCode);\n}\n\n/**\n * 3-letter Scripture book code\n * @public\n */\nexport type BookCode = (typeof VALID_BOOK_CODES)[number];\n\n/**\n * List of valid 3-letter Scripture book codes\n * @public\n */\nexport const VALID_BOOK_CODES = [\n // Old Testament\n \"GEN\",\n \"EXO\",\n \"LEV\",\n \"NUM\",\n \"DEU\",\n \"JOS\",\n \"JDG\",\n \"RUT\",\n \"1SA\",\n \"2SA\",\n \"1KI\",\n \"2KI\",\n \"1CH\",\n \"2CH\",\n \"EZR\",\n \"NEH\",\n \"EST\",\n \"JOB\",\n \"PSA\",\n \"PRO\",\n \"ECC\",\n \"SNG\",\n \"ISA\",\n \"JER\",\n \"LAM\",\n \"EZK\",\n \"DAN\",\n \"HOS\",\n \"JOL\",\n \"AMO\",\n \"OBA\",\n \"JON\",\n \"MIC\",\n \"NAM\",\n \"HAB\",\n \"ZEP\",\n \"HAG\",\n \"ZEC\",\n \"MAL\",\n // New Testament\n \"MAT\",\n \"MRK\",\n \"LUK\",\n \"JHN\",\n \"ACT\",\n \"ROM\",\n \"1CO\",\n \"2CO\",\n \"GAL\",\n \"EPH\",\n \"PHP\",\n \"COL\",\n \"1TH\",\n \"2TH\",\n \"1TI\",\n \"2TI\",\n \"TIT\",\n \"PHM\",\n \"HEB\",\n \"JAS\",\n \"1PE\",\n \"2PE\",\n \"1JN\",\n \"2JN\",\n \"3JN\",\n \"JUD\",\n \"REV\",\n // Deuterocanon\n \"TOB\",\n \"JDT\",\n \"ESG\",\n \"WIS\",\n \"SIR\",\n \"BAR\",\n \"LJE\",\n \"S3Y\",\n \"SUS\",\n \"BEL\",\n \"1MA\",\n \"2MA\",\n \"3MA\",\n \"4MA\",\n \"1ES\",\n \"2ES\",\n \"MAN\",\n \"PS2\",\n \"ODA\",\n \"PSS\",\n \"EZA\",\n \"5EZ\",\n \"6EZ\",\n \"DAG\",\n \"PS3\",\n \"2BA\",\n \"LBA\",\n \"JUB\",\n \"ENO\",\n \"1MQ\",\n \"2MQ\",\n \"3MQ\",\n \"REP\",\n \"4BA\",\n \"LAO\",\n // Non scripture\n \"FRT\",\n \"BAK\",\n \"OTH\",\n \"INT\",\n \"CNC\",\n \"GLO\",\n \"TDX\",\n \"NDX\",\n \"XXA\",\n \"XXB\",\n \"XXC\",\n \"XXD\",\n \"XXE\",\n \"XXF\",\n \"XXG\",\n] as const;\n","/**\n * Unified Scripture XML (USX).\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/main/grammar/usx.rng\n */\n\n/**\n * The USX spec type\n * @public\n */\nexport const USX_TYPE = \"usx\";\n\n/**\n * The USX spec version\n * @public\n */\nexport const USX_VERSION = \"3.1\";\n\n/**\n * An empty USX string\n * @public\n */\nexport const EMPTY_USX = `<${USX_TYPE} version=\"${USX_VERSION}\" />`;\n","/**\n * Convert Scripture from USX to USJ.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMParser, Element } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, USJ_TYPE, USJ_VERSION, Usj } from \"./usj.model.js\";\nimport { USX_TYPE } from \"./usx.model.js\";\n\ntype Action = \"append\" | \"merge\" | \"ignore\";\ninterface Attribs {\n [name: string]: string;\n}\n\n/**\n * Converts a USX string to a USJ object.\n *\n * @param usxString - The USX string to convert.\n * @returns The converted USJ object.\n *\n * @public\n */\nexport function usxStringToUsj(usxString: string): Usj {\n const parser = new DOMParser();\n const inputUsxDom = parser.parseFromString(usxString, \"text/xml\");\n return usxDomToUsj(inputUsxDom.documentElement);\n}\n\nexport function usxDomToUsj(inputUsxDom: Element | null): Usj {\n const [outputJson] = inputUsxDom\n ? convertUsxRecurse(inputUsxDom)\n : [{ content: [] as MarkerContent[] } as Usj];\n outputJson.type = USJ_TYPE;\n outputJson.version = USJ_VERSION;\n return outputJson;\n}\n\nfunction convertUsxRecurse<T extends Usj | MarkerObject = Usj>(\n inputUsxElement: Element,\n): [outputJson: T, action: Action] {\n const attribs: Attribs = {};\n let type: string = inputUsxElement.tagName;\n let marker: string | undefined;\n let text: string | undefined;\n let action: Action = \"append\";\n\n if ([\"row\", \"cell\"].includes(type)) type = \"table:\" + type;\n if (inputUsxElement.attributes) {\n for (const attrib of Array.from(inputUsxElement.attributes)) {\n attribs[attrib.name] = attrib.value;\n }\n }\n\n if (attribs.style) {\n marker = attribs.style;\n delete attribs.style;\n }\n // dropping because presence of vid in para elements is not consistent in USX\n if (attribs.vid) delete attribs.vid;\n // Not dropping `attribs.closed` for backwards compatibility.\n // dropping because it is nonstandard derived metadata that could get out of date\n if (attribs.status) delete attribs.status;\n\n let outObj: T = { type } as T;\n if (marker) (outObj as MarkerObject).marker = marker;\n outObj = { ...outObj, ...attribs };\n\n if (\n inputUsxElement.firstChild &&\n inputUsxElement.firstChild.nodeType === inputUsxElement.firstChild.TEXT_NODE &&\n inputUsxElement.firstChild.nodeValue &&\n asciiTrim(inputUsxElement.firstChild.nodeValue) !== \"\"\n ) {\n text = inputUsxElement.firstChild.nodeValue;\n }\n\n const children = Array.from(inputUsxElement.childNodes);\n outObj.content = [];\n\n if (text) {\n outObj.content.push(text);\n }\n\n for (const child of children) {\n // ChildNodes are Elements.\n if ((child as Element).tagName === undefined) {\n continue;\n }\n // ChildNodes are Elements.\n const [childDict, whatToDo] = convertUsxRecurse<MarkerObject>(child as Element);\n\n switch (whatToDo) {\n case \"append\":\n outObj.content.push(childDict);\n break;\n case \"merge\":\n outObj.content = outObj.content.concat(childDict);\n break;\n case \"ignore\":\n break;\n default:\n break;\n }\n\n // Handle tail text\n if (\n child.nextSibling &&\n child.nextSibling.nodeType === child.nextSibling.TEXT_NODE &&\n child.nextSibling.nodeValue &&\n (asciiTrim(child.nextSibling.nodeValue) !== \"\" || child.nextSibling.nodeValue === \" \")\n ) {\n outObj.content.push(child.nextSibling.nodeValue);\n }\n }\n\n // For backward compatibility, not deleting content for type: chapter, verse, optbreak, ms OR\n // marker: va, ca, b.\n if (outObj.content.length === 0 && outObj.type !== USX_TYPE) {\n delete outObj.content;\n }\n\n if (\"eid\" in outObj && [\"verse\", \"chapter\"].includes(type)) {\n action = \"ignore\";\n }\n\n return [outObj, action];\n}\n\n/**\n * Removes leading and trailing ASCII whitespace.\n *\n * Only trim ASCII whitespace characters: space, tab, line feed, carriage return, form feed,\n * vertical tab.\n * @param str - The string to remove whitespace from.\n * @returns the string with leading and trailing whitespace removed.\n */\nfunction asciiTrim(str: string): string {\n return str.replace(/(^[ \\t\\n\\r\\f\\v]+)|([ \\t\\n\\r\\f\\v]+$)/g, \"\");\n}\n","/**\n * Convert Scripture from USJ to USX.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMImplementation, Document, Element, Text } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, Usj } from \"./usj.model.js\";\nimport { USX_TYPE, USX_VERSION } from \"./usx.model.js\";\n\nlet chapterEid: string | undefined;\nlet verseEid: string | undefined;\n\n/**\n * Converts a USJ object to a USX string.\n *\n * @param usj - The USJ object to convert\n * @returns The converted USX string.\n *\n * @public\n */\nexport function usjToUsxString(usj: Usj): string {\n const usxDoc = new DOMImplementation().createDocument(\"\", USX_TYPE);\n if (usxDoc.documentElement) {\n usxDoc.documentElement.setAttribute(\"version\", USX_VERSION);\n usjToUsxDom(usj, usxDoc);\n }\n return usxDoc.toString();\n}\n\nexport function usjToUsxDom(usj: Usj, usxDoc: Document): Element | undefined {\n if (!usxDoc.documentElement) return undefined;\n\n for (const [index, markerContent] of usj.content.entries()) {\n const isLastItem = index === usj.content.length - 1;\n convertUsjRecurse(markerContent, usxDoc.documentElement, usxDoc, isLastItem);\n }\n return usxDoc.documentElement ?? undefined;\n}\n\nfunction convertUsjRecurse(\n markerContent: MarkerContent,\n parentElement: Element,\n usxDoc: Document,\n isLastItem: boolean,\n) {\n let element: Text | Element;\n let type: string | undefined;\n let eidElement: Element | undefined;\n if (typeof markerContent === \"string\") element = usxDoc.createTextNode(markerContent);\n else {\n type = markerContent.type.replace(\"table:\", \"\");\n element = usxDoc.createElement(type);\n setAttributes(element, markerContent);\n if (markerContent.content) {\n for (const [index, item] of markerContent.content.entries()) {\n const _isLastItem = index === markerContent.content.length - 1;\n convertUsjRecurse(item, element, usxDoc, _isLastItem);\n }\n }\n }\n\n // Create chapter and verse end elements from SID attributes.\n if (verseEid && (type === \"verse\" || (parentElement.tagName === \"para\" && isLastItem))) {\n eidElement = createVerseEndElement(usxDoc, verseEid);\n verseEid = undefined;\n }\n if (type === \"verse\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n verseEid = markerContent.sid;\n\n if (chapterEid && (type === \"chapter\" || (type === \"para\" && isLastItem))) {\n eidElement = createChapterEndElement(usxDoc, chapterEid);\n chapterEid = undefined;\n }\n if (type === \"chapter\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n chapterEid = markerContent.sid;\n\n // Append to parent.\n const isVerseInImpliedPara =\n parentElement.nodeName === USX_TYPE && eidElement?.tagName === \"verse\";\n if (eidElement && (!isLastItem || isVerseInImpliedPara)) parentElement.appendChild(eidElement);\n parentElement.appendChild(element);\n if (eidElement && isLastItem && !isVerseInImpliedPara) parentElement.appendChild(eidElement);\n\n // Allow for final chapter and verse end elements at the end of an implied para.\n if (isLastItem && parentElement.nodeName === USX_TYPE) {\n if (verseEid) parentElement.appendChild(createVerseEndElement(usxDoc, verseEid));\n if (chapterEid) parentElement.appendChild(createChapterEndElement(usxDoc, chapterEid));\n verseEid = undefined;\n chapterEid = undefined;\n }\n}\n\nfunction setAttributes(element: Element, markerContent: MarkerObject) {\n if (markerContent.marker) {\n if (markerContent.type === \"unmatched\") element.setAttribute(\"marker\", markerContent.marker);\n else element.setAttribute(\"style\", markerContent.marker);\n }\n for (const [key, value] of Object.entries(markerContent)) {\n if (value && ![\"type\", \"marker\", \"content\"].includes(key)) {\n element.setAttribute(key, value as string);\n }\n }\n}\n\nfunction createVerseEndElement(usxDoc: Document, verseEid: string): Element {\n const eidElement = usxDoc.createElement(\"verse\");\n eidElement.setAttribute(\"eid\", verseEid);\n return eidElement;\n}\n\nfunction createChapterEndElement(usxDoc: Document, chapterEid: string): Element {\n const eidElement = usxDoc.createElement(\"chapter\");\n eidElement.setAttribute(\"eid\", chapterEid);\n return eidElement;\n}\n","const JSON_PATH_START = \"$\";\nconst JSON_PATH_CONTENT = \".content[\";\n\n/**\n * Converts a USJ JSONPath string into an array of indexes.\n *\n * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.\n * @returns An array of numeric indexes extracted from the JSONPath.\n * @throws Will throw an error if the JSONPath does not start with `$`.\n *\n * @public\n */\nexport function indexesFromUsjJsonPath(jsonPath: string): number[] {\n const path = jsonPath.split(JSON_PATH_CONTENT);\n if (path.shift() !== JSON_PATH_START)\n throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${JSON_PATH_START}'`);\n\n const indexes = path.map((str) => parseInt(str, 10));\n return indexes;\n}\n\n/**\n * Converts an array of indexes into a USJ JSONPath string.\n *\n * @param indexes - An array of numeric indexes to convert.\n * @returns A USJ JSONPath string constructed from the indexes.\n *\n * @public\n */\nexport function usjJsonPathFromIndexes(indexes: number[]): string {\n return indexes.reduce((path, index) => `${path}${JSON_PATH_CONTENT}${index}]`, JSON_PATH_START);\n}\n"],"names":["USJ_TYPE","USJ_VERSION","EMPTY_USJ","MARKER_OBJECT_PROPS","isValidBookCode","code","VALID_BOOK_CODES","USX_TYPE","USX_VERSION","EMPTY_USX","usxStringToUsj","usxString","inputUsxDom","DOMParser","usxDomToUsj","outputJson","convertUsxRecurse","inputUsxElement","attribs","type","marker","text","action","attrib","outObj","asciiTrim","children","child","childDict","whatToDo","str","chapterEid","verseEid","usjToUsxString","usj","usxDoc","DOMImplementation","usjToUsxDom","index","markerContent","isLastItem","convertUsjRecurse","parentElement","element","eidElement","setAttributes","item","_isLastItem","createVerseEndElement","createChapterEndElement","isVerseInImpliedPara","key","value","JSON_PATH_START","JSON_PATH_CONTENT","indexesFromUsjJsonPath","jsonPath","path","usjJsonPathFromIndexes","indexes"],"mappings":"kHAUaA,EAAW,MAMXC,EAAc,MAMdC,EAAY,OAAO,OAAY,CAAE,KAAMF,EAAU,QAASC,EAAa,QAAS,EAAC,CAAG,EAMpFE,EAA8C,CACzD,OACA,SACA,UACA,MACA,MACA,SACA,OACA,YACA,YACA,SACA,QACA,UACF,EA8DO,SAASC,EAAgBC,EAAuB,CACrD,OAAOC,EAAiB,SAASD,CAAgB,CACnD,CAYO,MAAMC,EAAmB,CAE9B,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MAEA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,KACF,ECpOaC,EAAW,MAMXC,EAAc,MAMdC,EAAY,IAAIF,CAAQ,aAAaC,CAAW,OCCtD,SAASE,EAAeC,EAAwB,CAErD,MAAMC,EADS,IAAIC,YAAA,EACQ,gBAAgBF,EAAW,UAAU,EAChE,OAAOG,EAAYF,EAAY,eAAe,CAChD,CAEO,SAASE,EAAYF,EAAkC,CAC5D,KAAM,CAACG,CAAU,EAAIH,EACjBI,EAAkBJ,CAAW,EAC7B,CAAC,CAAE,QAAS,CAAA,EAA8B,EAC9C,OAAAG,EAAW,KAAOf,EAClBe,EAAW,QAAUd,EACdc,CACT,CAEA,SAASC,EACPC,EACiC,CACjC,MAAMC,EAAmB,CAAA,EACzB,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"}
package/dist/index.d.ts CHANGED
@@ -1,95 +1,280 @@
1
- /** 3-letter Scripture book code */
2
- export declare type BookCode = (typeof VALID_BOOK_CODES)[number];
3
-
4
- export declare const EMPTY_USJ: Readonly<Usj>;
5
-
6
- export declare const EMPTY_USX = "<usx version=\"3.1\" />";
7
-
8
- /**
9
- * Converts a USJ JSONPath string into an array of indexes.
10
- *
11
- * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.
12
- * @returns An array of numeric indexes extracted from the JSONPath.
13
- * @throws Will throw an error if the JSONPath does not start with `$`.
14
- */
15
- export declare function indexesFromUsjJsonPath(jsonPath: string): number[];
16
-
17
- export declare function isValidBookCode(code: string): boolean;
18
-
19
- /** List of known properties of `MarkerObject` */
20
- export declare const MARKER_OBJECT_PROPS: (keyof MarkerObject)[];
21
-
22
- /** Single piece of Scripture content */
23
- export declare type MarkerContent = string | MarkerObject;
24
-
25
- /** A Scripture Marker and its contents */
26
- export declare interface MarkerObject {
27
- /**
28
- * The kind/category of node or element this is, corresponding the USFM marker and USX node
29
- * @example `para`, `verse`, `char`
30
- */
31
- type: string;
32
- /**
33
- * The corresponding marker in USFM or style in USX
34
- * @example `p`, `v`, `nd`
35
- */
36
- marker: string;
37
- /** This marker's contents laid out in order */
38
- content?: MarkerContent[];
39
- /** Indicates the Book-chapter-verse value in the paragraph based structure */
40
- sid?: string;
41
- /** Milestone end ID, matches start ID (not currently included in USJ spec) */
42
- eid?: string;
43
- /** Chapter number or verse number */
44
- number?: string;
45
- /** The 3-letter book code in ID element */
46
- code?: BookCode;
47
- /** Alternate chapter number or verse number */
48
- altnumber?: string;
49
- /** Published character of chapter or verse */
50
- pubnumber?: string;
51
- /** Caller character for footnotes and cross-refs */
52
- caller?: string;
53
- /** Alignment of table cells */
54
- align?: string;
55
- /** Category of extended study bible sections */
56
- category?: string;
57
- }
58
-
59
- /** Scripture data represented in JSON format. Data compatible transformation from USX/USFM */
60
- export declare interface Usj {
61
- /** The USJ spec type */
62
- type: typeof USJ_TYPE;
63
- /** The USJ spec version */
64
- version: typeof USJ_VERSION;
65
- /** The JSON representation of scripture contents from USFM/USX */
66
- content: MarkerContent[];
67
- }
68
-
69
- /** The USJ spec type */
70
- export declare const USJ_TYPE = "USJ";
71
-
72
- /** The USJ spec version */
73
- export declare const USJ_VERSION = "3.1";
74
-
75
- /**
76
- * Converts an array of indexes into a USJ JSONPath string.
77
- *
78
- * @param indexes - An array of numeric indexes to convert.
79
- * @returns A USJ JSONPath string constructed from the indexes.
80
- */
81
- export declare function usjJsonPathFromIndexes(indexes: number[]): string;
82
-
83
- export declare function usjToUsxString(usj: Usj): string;
84
-
85
- /** The USX spec type */
86
- export declare const USX_TYPE = "usx";
87
-
88
- /** The USX spec version */
89
- export declare const USX_VERSION = "3.1";
90
-
91
- export declare function usxStringToUsj(usxString: string): Usj;
92
-
93
- declare const VALID_BOOK_CODES: readonly ["GEN", "EXO", "LEV", "NUM", "DEU", "JOS", "JDG", "RUT", "1SA", "2SA", "1KI", "2KI", "1CH", "2CH", "EZR", "NEH", "EST", "JOB", "PSA", "PRO", "ECC", "SNG", "ISA", "JER", "LAM", "EZK", "DAN", "HOS", "JOL", "AMO", "OBA", "JON", "MIC", "NAM", "HAB", "ZEP", "HAG", "ZEC", "MAL", "MAT", "MRK", "LUK", "JHN", "ACT", "ROM", "1CO", "2CO", "GAL", "EPH", "PHP", "COL", "1TH", "2TH", "1TI", "2TI", "TIT", "PHM", "HEB", "JAS", "1PE", "2PE", "1JN", "2JN", "3JN", "JUD", "REV", "TOB", "JDT", "ESG", "WIS", "SIR", "BAR", "LJE", "S3Y", "SUS", "BEL", "1MA", "2MA", "3MA", "4MA", "1ES", "2ES", "MAN", "PS2", "ODA", "PSS", "EZA", "5EZ", "6EZ", "DAG", "PS3", "2BA", "LBA", "JUB", "ENO", "1MQ", "2MQ", "3MQ", "REP", "4BA", "LAO", "FRT", "BAK", "OTH", "INT", "CNC", "GLO", "TDX", "NDX", "XXA", "XXB", "XXC", "XXD", "XXE", "XXF", "XXG"];
94
-
95
- export { }
1
+ /**
2
+ * @packageDocumentation
3
+ * Utilities for Scripture data conversion and manipulation, including USJ/USX format conversion.
4
+ */
5
+
6
+ /**
7
+ * 3-letter Scripture book code
8
+ * @public
9
+ */
10
+ export declare type BookCode = (typeof VALID_BOOK_CODES)[number];
11
+
12
+ /**
13
+ * An empty USJ object
14
+ * @public
15
+ */
16
+ export declare const EMPTY_USJ: Readonly<Usj>;
17
+
18
+ /**
19
+ * An empty USX string
20
+ * @public
21
+ */
22
+ export declare const EMPTY_USX = '<usx version="3.1" />';
23
+
24
+ /**
25
+ * Converts a USJ JSONPath string into an array of indexes.
26
+ *
27
+ * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.
28
+ * @returns An array of numeric indexes extracted from the JSONPath.
29
+ * @throws Will throw an error if the JSONPath does not start with `$`.
30
+ *
31
+ * @public
32
+ */
33
+ export declare function indexesFromUsjJsonPath(jsonPath: string): number[];
34
+
35
+ /**
36
+ * Check if the given code is a valid 3-letter Scripture book code.
37
+ * @public
38
+ */
39
+ export declare function isValidBookCode(code: string): boolean;
40
+
41
+ /**
42
+ * List of known properties of `MarkerObject`
43
+ * @public
44
+ */
45
+ export declare const MARKER_OBJECT_PROPS: (keyof MarkerObject)[];
46
+
47
+ /**
48
+ * Single piece of Scripture content
49
+ * @public
50
+ */
51
+ export declare type MarkerContent = string | MarkerObject;
52
+
53
+ /**
54
+ * A Scripture Marker and its contents
55
+ * @public
56
+ */
57
+ export declare interface MarkerObject {
58
+ /**
59
+ * The kind/category of node or element this is, corresponding the USFM marker and USX node
60
+ * @example `para`, `verse`, `char`
61
+ */
62
+ type: string;
63
+ /**
64
+ * The corresponding marker in USFM or style in USX
65
+ * @example `p`, `v`, `nd`
66
+ */
67
+ marker?: string;
68
+ /** This marker's contents laid out in order */
69
+ content?: MarkerContent[];
70
+ /** Indicates the Book-chapter-verse value in the paragraph based structure */
71
+ sid?: string;
72
+ /** Milestone end ID, matches start ID (not currently included in USJ spec) */
73
+ eid?: string;
74
+ /** Chapter number or verse number */
75
+ number?: string;
76
+ /** The 3-letter book code in ID element */
77
+ code?: BookCode;
78
+ /** Alternate chapter number or verse number */
79
+ altnumber?: string;
80
+ /** Published character of chapter or verse */
81
+ pubnumber?: string;
82
+ /** Caller character for footnotes and cross-refs */
83
+ caller?: string;
84
+ /** Alignment of table cells */
85
+ align?: string;
86
+ /** Category of extended study bible sections */
87
+ category?: string;
88
+ }
89
+
90
+ /**
91
+ * Scripture data represented in JSON format. Data compatible transformation from USX/USFM
92
+ * @public
93
+ */
94
+ export declare interface Usj {
95
+ /** The USJ spec type */
96
+ type: typeof USJ_TYPE;
97
+ /** The USJ spec version */
98
+ version: typeof USJ_VERSION;
99
+ /** The JSON representation of scripture contents from USFM/USX */
100
+ content: MarkerContent[];
101
+ }
102
+
103
+ /**
104
+ * The USJ spec type
105
+ * @public
106
+ */
107
+ export declare const USJ_TYPE = "USJ";
108
+
109
+ /**
110
+ * The USJ spec version
111
+ * @public
112
+ */
113
+ export declare const USJ_VERSION = "3.1";
114
+
115
+ /**
116
+ * Converts an array of indexes into a USJ JSONPath string.
117
+ *
118
+ * @param indexes - An array of numeric indexes to convert.
119
+ * @returns A USJ JSONPath string constructed from the indexes.
120
+ *
121
+ * @public
122
+ */
123
+ export declare function usjJsonPathFromIndexes(indexes: number[]): string;
124
+
125
+ /**
126
+ * Converts a USJ object to a USX string.
127
+ *
128
+ * @param usj - The USJ object to convert
129
+ * @returns The converted USX string.
130
+ *
131
+ * @public
132
+ */
133
+ export declare function usjToUsxString(usj: Usj): string;
134
+
135
+ /**
136
+ * The USX spec type
137
+ * @public
138
+ */
139
+ export declare const USX_TYPE = "usx";
140
+
141
+ /**
142
+ * The USX spec version
143
+ * @public
144
+ */
145
+ export declare const USX_VERSION = "3.1";
146
+
147
+ /**
148
+ * Converts a USX string to a USJ object.
149
+ *
150
+ * @param usxString - The USX string to convert.
151
+ * @returns The converted USJ object.
152
+ *
153
+ * @public
154
+ */
155
+ export declare function usxStringToUsj(usxString: string): Usj;
156
+
157
+ /**
158
+ * List of valid 3-letter Scripture book codes
159
+ * @public
160
+ */
161
+ export declare const VALID_BOOK_CODES: readonly [
162
+ "GEN",
163
+ "EXO",
164
+ "LEV",
165
+ "NUM",
166
+ "DEU",
167
+ "JOS",
168
+ "JDG",
169
+ "RUT",
170
+ "1SA",
171
+ "2SA",
172
+ "1KI",
173
+ "2KI",
174
+ "1CH",
175
+ "2CH",
176
+ "EZR",
177
+ "NEH",
178
+ "EST",
179
+ "JOB",
180
+ "PSA",
181
+ "PRO",
182
+ "ECC",
183
+ "SNG",
184
+ "ISA",
185
+ "JER",
186
+ "LAM",
187
+ "EZK",
188
+ "DAN",
189
+ "HOS",
190
+ "JOL",
191
+ "AMO",
192
+ "OBA",
193
+ "JON",
194
+ "MIC",
195
+ "NAM",
196
+ "HAB",
197
+ "ZEP",
198
+ "HAG",
199
+ "ZEC",
200
+ "MAL",
201
+ "MAT",
202
+ "MRK",
203
+ "LUK",
204
+ "JHN",
205
+ "ACT",
206
+ "ROM",
207
+ "1CO",
208
+ "2CO",
209
+ "GAL",
210
+ "EPH",
211
+ "PHP",
212
+ "COL",
213
+ "1TH",
214
+ "2TH",
215
+ "1TI",
216
+ "2TI",
217
+ "TIT",
218
+ "PHM",
219
+ "HEB",
220
+ "JAS",
221
+ "1PE",
222
+ "2PE",
223
+ "1JN",
224
+ "2JN",
225
+ "3JN",
226
+ "JUD",
227
+ "REV",
228
+ "TOB",
229
+ "JDT",
230
+ "ESG",
231
+ "WIS",
232
+ "SIR",
233
+ "BAR",
234
+ "LJE",
235
+ "S3Y",
236
+ "SUS",
237
+ "BEL",
238
+ "1MA",
239
+ "2MA",
240
+ "3MA",
241
+ "4MA",
242
+ "1ES",
243
+ "2ES",
244
+ "MAN",
245
+ "PS2",
246
+ "ODA",
247
+ "PSS",
248
+ "EZA",
249
+ "5EZ",
250
+ "6EZ",
251
+ "DAG",
252
+ "PS3",
253
+ "2BA",
254
+ "LBA",
255
+ "JUB",
256
+ "ENO",
257
+ "1MQ",
258
+ "2MQ",
259
+ "3MQ",
260
+ "REP",
261
+ "4BA",
262
+ "LAO",
263
+ "FRT",
264
+ "BAK",
265
+ "OTH",
266
+ "INT",
267
+ "CNC",
268
+ "GLO",
269
+ "TDX",
270
+ "NDX",
271
+ "XXA",
272
+ "XXB",
273
+ "XXC",
274
+ "XXD",
275
+ "XXE",
276
+ "XXF",
277
+ "XXG",
278
+ ];
279
+
280
+ export {};
package/dist/index.js CHANGED
@@ -137,8 +137,8 @@ const M = [
137
137
  "XXE",
138
138
  "XXF",
139
139
  "XXG"
140
- ], l = "usx", N = "3.1", I = `<${l} version="${N}" />`;
141
- function C(e) {
140
+ ], f = "usx", N = "3.1", I = `<${f} version="${N}" />`;
141
+ function V(e) {
142
142
  const n = new g().parseFromString(e, "text/xml");
143
143
  return m(n.documentElement);
144
144
  }
@@ -155,9 +155,9 @@ function b(e) {
155
155
  t.style && (o = t.style, delete t.style), t.vid && delete t.vid, t.status && delete t.status;
156
156
  let i = { type: n };
157
157
  o && (i.marker = o), i = { ...i, ...t }, e.firstChild && e.firstChild.nodeType === e.firstChild.TEXT_NODE && e.firstChild.nodeValue && E(e.firstChild.nodeValue) !== "" && (s = e.firstChild.nodeValue);
158
- const f = Array.from(e.childNodes);
158
+ const l = Array.from(e.childNodes);
159
159
  i.content = [], s && i.content.push(s);
160
- for (const r of f) {
160
+ for (const r of l) {
161
161
  if (r.tagName === void 0)
162
162
  continue;
163
163
  const [u, S] = b(r);
@@ -171,14 +171,14 @@ function b(e) {
171
171
  }
172
172
  r.nextSibling && r.nextSibling.nodeType === r.nextSibling.TEXT_NODE && r.nextSibling.nodeValue && (E(r.nextSibling.nodeValue) !== "" || r.nextSibling.nodeValue === " ") && i.content.push(r.nextSibling.nodeValue);
173
173
  }
174
- return i.content.length === 0 && i.type !== l && delete i.content, "eid" in i && ["verse", "chapter"].includes(n) && (c = "ignore"), [i, c];
174
+ return i.content.length === 0 && i.type !== f && delete i.content, "eid" in i && ["verse", "chapter"].includes(n) && (c = "ignore"), [i, c];
175
175
  }
176
176
  function E(e) {
177
177
  return e.replace(/(^[ \t\n\r\f\v]+)|([ \t\n\r\f\v]+$)/g, "");
178
178
  }
179
179
  let d, a;
180
- function V(e) {
181
- const t = new J().createDocument("", l);
180
+ function C(e) {
181
+ const t = new J().createDocument("", f);
182
182
  return t.documentElement && (t.documentElement.setAttribute("version", N), y(e, t)), t.toString();
183
183
  }
184
184
  function y(e, t) {
@@ -199,11 +199,11 @@ function v(e, t, n, o) {
199
199
  v(u, s, n, S);
200
200
  }
201
201
  a && (c === "verse" || t.tagName === "para" && o) && (i = T(n, a), a = void 0), c === "verse" && typeof e != "string" && e.sid !== void 0 && (a = e.sid), d && (c === "chapter" || c === "para" && o) && (i = h(n, d), d = void 0), c === "chapter" && typeof e != "string" && e.sid !== void 0 && (d = e.sid);
202
- const f = t.nodeName === l && (i == null ? void 0 : i.tagName) === "verse";
203
- i && (!o || f) && t.appendChild(i), t.appendChild(s), i && o && !f && t.appendChild(i), o && t.nodeName === l && (a && t.appendChild(T(n, a)), d && t.appendChild(h(n, d)), a = void 0, d = void 0);
202
+ const l = t.nodeName === f && (i == null ? void 0 : i.tagName) === "verse";
203
+ i && (!o || l) && t.appendChild(i), t.appendChild(s), i && o && !l && t.appendChild(i), o && t.nodeName === f && (a && t.appendChild(T(n, a)), d && t.appendChild(h(n, d)), a = void 0, d = void 0);
204
204
  }
205
205
  function X(e, t) {
206
- t.type === "unmatched" ? e.setAttribute("marker", t.marker) : e.setAttribute("style", t.marker);
206
+ t.marker && (t.type === "unmatched" ? e.setAttribute("marker", t.marker) : e.setAttribute("style", t.marker));
207
207
  for (const [n, o] of Object.entries(t))
208
208
  o && !["type", "marker", "content"].includes(n) && e.setAttribute(n, o);
209
209
  }
@@ -231,12 +231,13 @@ export {
231
231
  B as MARKER_OBJECT_PROPS,
232
232
  p as USJ_TYPE,
233
233
  O as USJ_VERSION,
234
- l as USX_TYPE,
234
+ f as USX_TYPE,
235
235
  N as USX_VERSION,
236
+ M as VALID_BOOK_CODES,
236
237
  D as indexesFromUsjJsonPath,
237
238
  H as isValidBookCode,
238
239
  U as usjJsonPathFromIndexes,
239
- V as usjToUsxString,
240
- C as usxStringToUsj
240
+ C as usjToUsxString,
241
+ V as usxStringToUsj
241
242
  };
242
243
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/converters/usj/usj.model.ts","../src/converters/usj/usx.model.ts","../src/converters/usj/usx-to-usj.ts","../src/converters/usj/usj-to-usx.ts","../src/converters/usj/jsonpath-indexes.ts"],"sourcesContent":["/**\n * Unified Scripture JSON (USJ) - The JSON variant of USFM and USX data models.\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js\n */\n\n/** The USJ spec type */\nexport const USJ_TYPE = \"USJ\";\n\n/** The USJ spec version */\nexport const USJ_VERSION = \"3.1\";\n\nexport const EMPTY_USJ = Object.freeze<Usj>({ type: USJ_TYPE, version: USJ_VERSION, content: [] });\n\n/** List of known properties of `MarkerObject` */\nexport const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [\n \"type\",\n \"marker\",\n \"content\",\n \"sid\",\n \"eid\",\n \"number\",\n \"code\",\n \"altnumber\",\n \"pubnumber\",\n \"caller\",\n \"align\",\n \"category\",\n];\n\n/** Single piece of Scripture content */\nexport type MarkerContent = string | MarkerObject;\n\n/** A Scripture Marker and its contents */\nexport interface MarkerObject {\n /**\n * The kind/category of node or element this is, corresponding the USFM marker and USX node\n * @example `para`, `verse`, `char`\n */\n type: string;\n /**\n * The corresponding marker in USFM or style in USX\n * @example `p`, `v`, `nd`\n */\n marker: string;\n /** This marker's contents laid out in order */\n content?: MarkerContent[];\n /** Indicates the Book-chapter-verse value in the paragraph based structure */\n sid?: string;\n /** Milestone end ID, matches start ID (not currently included in USJ spec) */\n eid?: string;\n /** Chapter number or verse number */\n number?: string;\n /** The 3-letter book code in ID element */\n code?: BookCode;\n /** Alternate chapter number or verse number */\n altnumber?: string;\n /** Published character of chapter or verse */\n pubnumber?: string;\n /** Caller character for footnotes and cross-refs */\n caller?: string;\n /** Alignment of table cells */\n align?: string;\n /** Category of extended study bible sections */\n category?: string;\n}\n\n/** Scripture data represented in JSON format. Data compatible transformation from USX/USFM */\nexport interface Usj {\n /** The USJ spec type */\n type: typeof USJ_TYPE;\n /** The USJ spec version */\n version: typeof USJ_VERSION;\n /** The JSON representation of scripture contents from USFM/USX */\n content: MarkerContent[];\n}\n\nexport function isValidBookCode(code: string): boolean {\n return VALID_BOOK_CODES.includes(code as BookCode);\n}\n\n/** 3-letter Scripture book code */\nexport type BookCode = (typeof VALID_BOOK_CODES)[number];\n\nconst VALID_BOOK_CODES = [\n // Old Testament\n \"GEN\",\n \"EXO\",\n \"LEV\",\n \"NUM\",\n \"DEU\",\n \"JOS\",\n \"JDG\",\n \"RUT\",\n \"1SA\",\n \"2SA\",\n \"1KI\",\n \"2KI\",\n \"1CH\",\n \"2CH\",\n \"EZR\",\n \"NEH\",\n \"EST\",\n \"JOB\",\n \"PSA\",\n \"PRO\",\n \"ECC\",\n \"SNG\",\n \"ISA\",\n \"JER\",\n \"LAM\",\n \"EZK\",\n \"DAN\",\n \"HOS\",\n \"JOL\",\n \"AMO\",\n \"OBA\",\n \"JON\",\n \"MIC\",\n \"NAM\",\n \"HAB\",\n \"ZEP\",\n \"HAG\",\n \"ZEC\",\n \"MAL\",\n // New Testament\n \"MAT\",\n \"MRK\",\n \"LUK\",\n \"JHN\",\n \"ACT\",\n \"ROM\",\n \"1CO\",\n \"2CO\",\n \"GAL\",\n \"EPH\",\n \"PHP\",\n \"COL\",\n \"1TH\",\n \"2TH\",\n \"1TI\",\n \"2TI\",\n \"TIT\",\n \"PHM\",\n \"HEB\",\n \"JAS\",\n \"1PE\",\n \"2PE\",\n \"1JN\",\n \"2JN\",\n \"3JN\",\n \"JUD\",\n \"REV\",\n // Deuterocanon\n \"TOB\",\n \"JDT\",\n \"ESG\",\n \"WIS\",\n \"SIR\",\n \"BAR\",\n \"LJE\",\n \"S3Y\",\n \"SUS\",\n \"BEL\",\n \"1MA\",\n \"2MA\",\n \"3MA\",\n \"4MA\",\n \"1ES\",\n \"2ES\",\n \"MAN\",\n \"PS2\",\n \"ODA\",\n \"PSS\",\n \"EZA\",\n \"5EZ\",\n \"6EZ\",\n \"DAG\",\n \"PS3\",\n \"2BA\",\n \"LBA\",\n \"JUB\",\n \"ENO\",\n \"1MQ\",\n \"2MQ\",\n \"3MQ\",\n \"REP\",\n \"4BA\",\n \"LAO\",\n // Non scripture\n \"FRT\",\n \"BAK\",\n \"OTH\",\n \"INT\",\n \"CNC\",\n \"GLO\",\n \"TDX\",\n \"NDX\",\n \"XXA\",\n \"XXB\",\n \"XXC\",\n \"XXD\",\n \"XXE\",\n \"XXF\",\n \"XXG\",\n] as const;\n","/**\n * Unified Scripture XML (USX).\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/main/grammar/usx.rng\n */\n\n/** The USX spec type */\nexport const USX_TYPE = \"usx\";\n\n/** The USX spec version */\nexport const USX_VERSION = \"3.1\";\n\nexport const EMPTY_USX = `<${USX_TYPE} version=\"${USX_VERSION}\" />`;\n","/**\n * Convert Scripture from USX to USJ.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMParser, Element } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, USJ_TYPE, USJ_VERSION, Usj } from \"./usj.model.js\";\nimport { USX_TYPE } from \"./usx.model.js\";\n\ntype Action = \"append\" | \"merge\" | \"ignore\";\ninterface Attribs {\n [name: string]: string;\n}\n\nexport function usxStringToUsj(usxString: string): Usj {\n const parser = new DOMParser();\n const inputUsxDom = parser.parseFromString(usxString, \"text/xml\");\n return usxDomToUsj(inputUsxDom.documentElement);\n}\n\nexport function usxDomToUsj(inputUsxDom: Element | null): Usj {\n const [outputJson] = inputUsxDom\n ? convertUsxRecurse(inputUsxDom)\n : [{ content: [] as MarkerContent[] } as Usj];\n outputJson.type = USJ_TYPE;\n outputJson.version = USJ_VERSION;\n return outputJson;\n}\n\nfunction convertUsxRecurse<T extends Usj | MarkerObject = Usj>(\n inputUsxElement: Element,\n): [outputJson: T, action: Action] {\n const attribs: Attribs = {};\n let type: string = inputUsxElement.tagName;\n let marker: string | undefined;\n let text: string | undefined;\n let action: Action = \"append\";\n\n if ([\"row\", \"cell\"].includes(type)) type = \"table:\" + type;\n if (inputUsxElement.attributes) {\n for (const attrib of Array.from(inputUsxElement.attributes)) {\n attribs[attrib.name] = attrib.value;\n }\n }\n\n if (attribs.style) {\n marker = attribs.style;\n delete attribs.style;\n }\n // dropping because presence of vid in para elements is not consistent in USX\n if (attribs.vid) delete attribs.vid;\n // Not dropping `attribs.closed` for backwards compatibility.\n // dropping because it is nonstandard derived metadata that could get out of date\n if (attribs.status) delete attribs.status;\n\n let outObj: T = { type } as T;\n if (marker) (outObj as MarkerObject).marker = marker;\n outObj = { ...outObj, ...attribs };\n\n if (\n inputUsxElement.firstChild &&\n inputUsxElement.firstChild.nodeType === inputUsxElement.firstChild.TEXT_NODE &&\n inputUsxElement.firstChild.nodeValue &&\n asciiTrim(inputUsxElement.firstChild.nodeValue) !== \"\"\n ) {\n text = inputUsxElement.firstChild.nodeValue;\n }\n\n const children = Array.from(inputUsxElement.childNodes);\n outObj.content = [];\n\n if (text) {\n outObj.content.push(text);\n }\n\n for (const child of children) {\n // ChildNodes are Elements.\n if ((child as Element).tagName === undefined) {\n continue;\n }\n // ChildNodes are Elements.\n const [childDict, whatToDo] = convertUsxRecurse<MarkerObject>(child as Element);\n\n switch (whatToDo) {\n case \"append\":\n outObj.content.push(childDict);\n break;\n case \"merge\":\n outObj.content = outObj.content.concat(childDict);\n break;\n case \"ignore\":\n break;\n default:\n break;\n }\n\n // Handle tail text\n if (\n child.nextSibling &&\n child.nextSibling.nodeType === child.nextSibling.TEXT_NODE &&\n child.nextSibling.nodeValue &&\n (asciiTrim(child.nextSibling.nodeValue) !== \"\" || child.nextSibling.nodeValue === \" \")\n ) {\n outObj.content.push(child.nextSibling.nodeValue);\n }\n }\n\n // For backward compatibility, not deleting content for type: chapter, verse, optbreak, ms OR\n // marker: va, ca, b.\n if (outObj.content.length === 0 && outObj.type !== USX_TYPE) {\n delete outObj.content;\n }\n\n if (\"eid\" in outObj && [\"verse\", \"chapter\"].includes(type)) {\n action = \"ignore\";\n }\n\n return [outObj, action];\n}\n\n/**\n * Removes leading and trailing ASCII whitespace.\n *\n * Only trim ASCII whitespace characters: space, tab, line feed, carriage return, form feed,\n * vertical tab.\n * @param str - The string to remove whitespace from.\n * @returns the string with leading and trailing whitespace removed.\n */\nfunction asciiTrim(str: string): string {\n return str.replace(/(^[ \\t\\n\\r\\f\\v]+)|([ \\t\\n\\r\\f\\v]+$)/g, \"\");\n}\n","/**\n * Convert Scripture from USJ to USX.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMImplementation, Document, Element, Text } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, Usj } from \"./usj.model.js\";\nimport { USX_TYPE, USX_VERSION } from \"./usx.model.js\";\n\nlet chapterEid: string | undefined;\nlet verseEid: string | undefined;\n\nexport function usjToUsxString(usj: Usj): string {\n const usxDoc = new DOMImplementation().createDocument(\"\", USX_TYPE);\n if (usxDoc.documentElement) {\n usxDoc.documentElement.setAttribute(\"version\", USX_VERSION);\n usjToUsxDom(usj, usxDoc);\n }\n return usxDoc.toString();\n}\n\nexport function usjToUsxDom(usj: Usj, usxDoc: Document): Element | undefined {\n if (!usxDoc.documentElement) return undefined;\n\n for (const [index, markerContent] of usj.content.entries()) {\n const isLastItem = index === usj.content.length - 1;\n convertUsjRecurse(markerContent, usxDoc.documentElement, usxDoc, isLastItem);\n }\n return usxDoc.documentElement ?? undefined;\n}\n\nfunction convertUsjRecurse(\n markerContent: MarkerContent,\n parentElement: Element,\n usxDoc: Document,\n isLastItem: boolean,\n) {\n let element: Text | Element;\n let type: string | undefined;\n let eidElement: Element | undefined;\n if (typeof markerContent === \"string\") element = usxDoc.createTextNode(markerContent);\n else {\n type = markerContent.type.replace(\"table:\", \"\");\n element = usxDoc.createElement(type);\n setAttributes(element, markerContent);\n if (markerContent.content) {\n for (const [index, item] of markerContent.content.entries()) {\n const _isLastItem = index === markerContent.content.length - 1;\n convertUsjRecurse(item, element, usxDoc, _isLastItem);\n }\n }\n }\n\n // Create chapter and verse end elements from SID attributes.\n if (verseEid && (type === \"verse\" || (parentElement.tagName === \"para\" && isLastItem))) {\n eidElement = createVerseEndElement(usxDoc, verseEid);\n verseEid = undefined;\n }\n if (type === \"verse\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n verseEid = markerContent.sid;\n\n if (chapterEid && (type === \"chapter\" || (type === \"para\" && isLastItem))) {\n eidElement = createChapterEndElement(usxDoc, chapterEid);\n chapterEid = undefined;\n }\n if (type === \"chapter\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n chapterEid = markerContent.sid;\n\n // Append to parent.\n const isVerseInImpliedPara =\n parentElement.nodeName === USX_TYPE && eidElement?.tagName === \"verse\";\n if (eidElement && (!isLastItem || isVerseInImpliedPara)) parentElement.appendChild(eidElement);\n parentElement.appendChild(element);\n if (eidElement && isLastItem && !isVerseInImpliedPara) parentElement.appendChild(eidElement);\n\n // Allow for final chapter and verse end elements at the end of an implied para.\n if (isLastItem && parentElement.nodeName === USX_TYPE) {\n if (verseEid) parentElement.appendChild(createVerseEndElement(usxDoc, verseEid));\n if (chapterEid) parentElement.appendChild(createChapterEndElement(usxDoc, chapterEid));\n verseEid = undefined;\n chapterEid = undefined;\n }\n}\n\nfunction setAttributes(element: Element, markerContent: MarkerObject) {\n if (markerContent.type === \"unmatched\") element.setAttribute(\"marker\", markerContent.marker);\n else element.setAttribute(\"style\", markerContent.marker);\n for (const [key, value] of Object.entries(markerContent)) {\n if (value && ![\"type\", \"marker\", \"content\"].includes(key)) {\n element.setAttribute(key, value as string);\n }\n }\n}\n\nfunction createVerseEndElement(usxDoc: Document, verseEid: string): Element {\n const eidElement = usxDoc.createElement(\"verse\");\n eidElement.setAttribute(\"eid\", verseEid);\n return eidElement;\n}\n\nfunction createChapterEndElement(usxDoc: Document, chapterEid: string): Element {\n const eidElement = usxDoc.createElement(\"chapter\");\n eidElement.setAttribute(\"eid\", chapterEid);\n return eidElement;\n}\n","const JSON_PATH_START = \"$\";\nconst JSON_PATH_CONTENT = \".content[\";\n\n/**\n * Converts a USJ JSONPath string into an array of indexes.\n *\n * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.\n * @returns An array of numeric indexes extracted from the JSONPath.\n * @throws Will throw an error if the JSONPath does not start with `$`.\n */\nexport function indexesFromUsjJsonPath(jsonPath: string): number[] {\n const path = jsonPath.split(JSON_PATH_CONTENT);\n if (path.shift() !== JSON_PATH_START)\n throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${JSON_PATH_START}'`);\n\n const indexes = path.map((str) => parseInt(str, 10));\n return indexes;\n}\n\n/**\n * Converts an array of indexes into a USJ JSONPath string.\n *\n * @param indexes - An array of numeric indexes to convert.\n * @returns A USJ JSONPath string constructed from the indexes.\n */\nexport function usjJsonPathFromIndexes(indexes: number[]): string {\n return indexes.reduce((path, index) => `${path}${JSON_PATH_CONTENT}${index}]`, JSON_PATH_START);\n}\n"],"names":["USJ_TYPE","USJ_VERSION","EMPTY_USJ","MARKER_OBJECT_PROPS","isValidBookCode","code","VALID_BOOK_CODES","USX_TYPE","USX_VERSION","EMPTY_USX","usxStringToUsj","usxString","inputUsxDom","DOMParser","usxDomToUsj","outputJson","convertUsxRecurse","inputUsxElement","attribs","type","marker","text","action","attrib","outObj","asciiTrim","children","child","childDict","whatToDo","str","chapterEid","verseEid","usjToUsxString","usj","usxDoc","DOMImplementation","usjToUsxDom","index","markerContent","isLastItem","convertUsjRecurse","parentElement","element","eidElement","setAttributes","item","_isLastItem","createVerseEndElement","createChapterEndElement","isVerseInImpliedPara","key","value","JSON_PATH_START","JSON_PATH_CONTENT","indexesFromUsjJsonPath","jsonPath","path","usjJsonPathFromIndexes","indexes"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/converters/usj/usj.model.ts","../src/converters/usj/usx.model.ts","../src/converters/usj/usx-to-usj.ts","../src/converters/usj/usj-to-usx.ts","../src/converters/usj/jsonpath-indexes.ts"],"sourcesContent":["/**\n * Unified Scripture JSON (USJ) - The JSON variant of USFM and USX data models.\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js\n */\n\n/**\n * The USJ spec type\n * @public\n */\nexport const USJ_TYPE = \"USJ\";\n\n/**\n * The USJ spec version\n * @public\n */\nexport const USJ_VERSION = \"3.1\";\n\n/**\n * An empty USJ object\n * @public\n */\nexport const EMPTY_USJ = Object.freeze<Usj>({ type: USJ_TYPE, version: USJ_VERSION, content: [] });\n\n/**\n * List of known properties of `MarkerObject`\n * @public\n */\nexport const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [\n \"type\",\n \"marker\",\n \"content\",\n \"sid\",\n \"eid\",\n \"number\",\n \"code\",\n \"altnumber\",\n \"pubnumber\",\n \"caller\",\n \"align\",\n \"category\",\n];\n\n/**\n * Single piece of Scripture content\n * @public\n */\nexport type MarkerContent = string | MarkerObject;\n\n/**\n * A Scripture Marker and its contents\n * @public\n */\nexport interface MarkerObject {\n /**\n * The kind/category of node or element this is, corresponding the USFM marker and USX node\n * @example `para`, `verse`, `char`\n */\n type: string;\n /**\n * The corresponding marker in USFM or style in USX\n * @example `p`, `v`, `nd`\n */\n marker?: string;\n /** This marker's contents laid out in order */\n content?: MarkerContent[];\n /** Indicates the Book-chapter-verse value in the paragraph based structure */\n sid?: string;\n /** Milestone end ID, matches start ID (not currently included in USJ spec) */\n eid?: string;\n /** Chapter number or verse number */\n number?: string;\n /** The 3-letter book code in ID element */\n code?: BookCode;\n /** Alternate chapter number or verse number */\n altnumber?: string;\n /** Published character of chapter or verse */\n pubnumber?: string;\n /** Caller character for footnotes and cross-refs */\n caller?: string;\n /** Alignment of table cells */\n align?: string;\n /** Category of extended study bible sections */\n category?: string;\n}\n\n/**\n * Scripture data represented in JSON format. Data compatible transformation from USX/USFM\n * @public\n */\nexport interface Usj {\n /** The USJ spec type */\n type: typeof USJ_TYPE;\n /** The USJ spec version */\n version: typeof USJ_VERSION;\n /** The JSON representation of scripture contents from USFM/USX */\n content: MarkerContent[];\n}\n\n/**\n * Check if the given code is a valid 3-letter Scripture book code.\n * @public\n */\nexport function isValidBookCode(code: string): boolean {\n return VALID_BOOK_CODES.includes(code as BookCode);\n}\n\n/**\n * 3-letter Scripture book code\n * @public\n */\nexport type BookCode = (typeof VALID_BOOK_CODES)[number];\n\n/**\n * List of valid 3-letter Scripture book codes\n * @public\n */\nexport const VALID_BOOK_CODES = [\n // Old Testament\n \"GEN\",\n \"EXO\",\n \"LEV\",\n \"NUM\",\n \"DEU\",\n \"JOS\",\n \"JDG\",\n \"RUT\",\n \"1SA\",\n \"2SA\",\n \"1KI\",\n \"2KI\",\n \"1CH\",\n \"2CH\",\n \"EZR\",\n \"NEH\",\n \"EST\",\n \"JOB\",\n \"PSA\",\n \"PRO\",\n \"ECC\",\n \"SNG\",\n \"ISA\",\n \"JER\",\n \"LAM\",\n \"EZK\",\n \"DAN\",\n \"HOS\",\n \"JOL\",\n \"AMO\",\n \"OBA\",\n \"JON\",\n \"MIC\",\n \"NAM\",\n \"HAB\",\n \"ZEP\",\n \"HAG\",\n \"ZEC\",\n \"MAL\",\n // New Testament\n \"MAT\",\n \"MRK\",\n \"LUK\",\n \"JHN\",\n \"ACT\",\n \"ROM\",\n \"1CO\",\n \"2CO\",\n \"GAL\",\n \"EPH\",\n \"PHP\",\n \"COL\",\n \"1TH\",\n \"2TH\",\n \"1TI\",\n \"2TI\",\n \"TIT\",\n \"PHM\",\n \"HEB\",\n \"JAS\",\n \"1PE\",\n \"2PE\",\n \"1JN\",\n \"2JN\",\n \"3JN\",\n \"JUD\",\n \"REV\",\n // Deuterocanon\n \"TOB\",\n \"JDT\",\n \"ESG\",\n \"WIS\",\n \"SIR\",\n \"BAR\",\n \"LJE\",\n \"S3Y\",\n \"SUS\",\n \"BEL\",\n \"1MA\",\n \"2MA\",\n \"3MA\",\n \"4MA\",\n \"1ES\",\n \"2ES\",\n \"MAN\",\n \"PS2\",\n \"ODA\",\n \"PSS\",\n \"EZA\",\n \"5EZ\",\n \"6EZ\",\n \"DAG\",\n \"PS3\",\n \"2BA\",\n \"LBA\",\n \"JUB\",\n \"ENO\",\n \"1MQ\",\n \"2MQ\",\n \"3MQ\",\n \"REP\",\n \"4BA\",\n \"LAO\",\n // Non scripture\n \"FRT\",\n \"BAK\",\n \"OTH\",\n \"INT\",\n \"CNC\",\n \"GLO\",\n \"TDX\",\n \"NDX\",\n \"XXA\",\n \"XXB\",\n \"XXC\",\n \"XXD\",\n \"XXE\",\n \"XXF\",\n \"XXG\",\n] as const;\n","/**\n * Unified Scripture XML (USX).\n * These types follow this schema:\n * @see https://github.com/usfm-bible/tcdocs/blob/main/grammar/usx.rng\n */\n\n/**\n * The USX spec type\n * @public\n */\nexport const USX_TYPE = \"usx\";\n\n/**\n * The USX spec version\n * @public\n */\nexport const USX_VERSION = \"3.1\";\n\n/**\n * An empty USX string\n * @public\n */\nexport const EMPTY_USX = `<${USX_TYPE} version=\"${USX_VERSION}\" />`;\n","/**\n * Convert Scripture from USX to USJ.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMParser, Element } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, USJ_TYPE, USJ_VERSION, Usj } from \"./usj.model.js\";\nimport { USX_TYPE } from \"./usx.model.js\";\n\ntype Action = \"append\" | \"merge\" | \"ignore\";\ninterface Attribs {\n [name: string]: string;\n}\n\n/**\n * Converts a USX string to a USJ object.\n *\n * @param usxString - The USX string to convert.\n * @returns The converted USJ object.\n *\n * @public\n */\nexport function usxStringToUsj(usxString: string): Usj {\n const parser = new DOMParser();\n const inputUsxDom = parser.parseFromString(usxString, \"text/xml\");\n return usxDomToUsj(inputUsxDom.documentElement);\n}\n\nexport function usxDomToUsj(inputUsxDom: Element | null): Usj {\n const [outputJson] = inputUsxDom\n ? convertUsxRecurse(inputUsxDom)\n : [{ content: [] as MarkerContent[] } as Usj];\n outputJson.type = USJ_TYPE;\n outputJson.version = USJ_VERSION;\n return outputJson;\n}\n\nfunction convertUsxRecurse<T extends Usj | MarkerObject = Usj>(\n inputUsxElement: Element,\n): [outputJson: T, action: Action] {\n const attribs: Attribs = {};\n let type: string = inputUsxElement.tagName;\n let marker: string | undefined;\n let text: string | undefined;\n let action: Action = \"append\";\n\n if ([\"row\", \"cell\"].includes(type)) type = \"table:\" + type;\n if (inputUsxElement.attributes) {\n for (const attrib of Array.from(inputUsxElement.attributes)) {\n attribs[attrib.name] = attrib.value;\n }\n }\n\n if (attribs.style) {\n marker = attribs.style;\n delete attribs.style;\n }\n // dropping because presence of vid in para elements is not consistent in USX\n if (attribs.vid) delete attribs.vid;\n // Not dropping `attribs.closed` for backwards compatibility.\n // dropping because it is nonstandard derived metadata that could get out of date\n if (attribs.status) delete attribs.status;\n\n let outObj: T = { type } as T;\n if (marker) (outObj as MarkerObject).marker = marker;\n outObj = { ...outObj, ...attribs };\n\n if (\n inputUsxElement.firstChild &&\n inputUsxElement.firstChild.nodeType === inputUsxElement.firstChild.TEXT_NODE &&\n inputUsxElement.firstChild.nodeValue &&\n asciiTrim(inputUsxElement.firstChild.nodeValue) !== \"\"\n ) {\n text = inputUsxElement.firstChild.nodeValue;\n }\n\n const children = Array.from(inputUsxElement.childNodes);\n outObj.content = [];\n\n if (text) {\n outObj.content.push(text);\n }\n\n for (const child of children) {\n // ChildNodes are Elements.\n if ((child as Element).tagName === undefined) {\n continue;\n }\n // ChildNodes are Elements.\n const [childDict, whatToDo] = convertUsxRecurse<MarkerObject>(child as Element);\n\n switch (whatToDo) {\n case \"append\":\n outObj.content.push(childDict);\n break;\n case \"merge\":\n outObj.content = outObj.content.concat(childDict);\n break;\n case \"ignore\":\n break;\n default:\n break;\n }\n\n // Handle tail text\n if (\n child.nextSibling &&\n child.nextSibling.nodeType === child.nextSibling.TEXT_NODE &&\n child.nextSibling.nodeValue &&\n (asciiTrim(child.nextSibling.nodeValue) !== \"\" || child.nextSibling.nodeValue === \" \")\n ) {\n outObj.content.push(child.nextSibling.nodeValue);\n }\n }\n\n // For backward compatibility, not deleting content for type: chapter, verse, optbreak, ms OR\n // marker: va, ca, b.\n if (outObj.content.length === 0 && outObj.type !== USX_TYPE) {\n delete outObj.content;\n }\n\n if (\"eid\" in outObj && [\"verse\", \"chapter\"].includes(type)) {\n action = \"ignore\";\n }\n\n return [outObj, action];\n}\n\n/**\n * Removes leading and trailing ASCII whitespace.\n *\n * Only trim ASCII whitespace characters: space, tab, line feed, carriage return, form feed,\n * vertical tab.\n * @param str - The string to remove whitespace from.\n * @returns the string with leading and trailing whitespace removed.\n */\nfunction asciiTrim(str: string): string {\n return str.replace(/(^[ \\t\\n\\r\\f\\v]+)|([ \\t\\n\\r\\f\\v]+$)/g, \"\");\n}\n","/**\n * Convert Scripture from USJ to USX.\n * Adapted to TypeScript from this file:\n * @see https://github.com/usfm-bible/usfmtc/blob/0afa385a1f282b286cc6bff7bbc953ae788aa10c/src/usfmtc/usjproc.py\n */\n\nimport { DOMImplementation, Document, Element, Text } from \"@xmldom/xmldom\";\nimport { MarkerContent, MarkerObject, Usj } from \"./usj.model.js\";\nimport { USX_TYPE, USX_VERSION } from \"./usx.model.js\";\n\nlet chapterEid: string | undefined;\nlet verseEid: string | undefined;\n\n/**\n * Converts a USJ object to a USX string.\n *\n * @param usj - The USJ object to convert\n * @returns The converted USX string.\n *\n * @public\n */\nexport function usjToUsxString(usj: Usj): string {\n const usxDoc = new DOMImplementation().createDocument(\"\", USX_TYPE);\n if (usxDoc.documentElement) {\n usxDoc.documentElement.setAttribute(\"version\", USX_VERSION);\n usjToUsxDom(usj, usxDoc);\n }\n return usxDoc.toString();\n}\n\nexport function usjToUsxDom(usj: Usj, usxDoc: Document): Element | undefined {\n if (!usxDoc.documentElement) return undefined;\n\n for (const [index, markerContent] of usj.content.entries()) {\n const isLastItem = index === usj.content.length - 1;\n convertUsjRecurse(markerContent, usxDoc.documentElement, usxDoc, isLastItem);\n }\n return usxDoc.documentElement ?? undefined;\n}\n\nfunction convertUsjRecurse(\n markerContent: MarkerContent,\n parentElement: Element,\n usxDoc: Document,\n isLastItem: boolean,\n) {\n let element: Text | Element;\n let type: string | undefined;\n let eidElement: Element | undefined;\n if (typeof markerContent === \"string\") element = usxDoc.createTextNode(markerContent);\n else {\n type = markerContent.type.replace(\"table:\", \"\");\n element = usxDoc.createElement(type);\n setAttributes(element, markerContent);\n if (markerContent.content) {\n for (const [index, item] of markerContent.content.entries()) {\n const _isLastItem = index === markerContent.content.length - 1;\n convertUsjRecurse(item, element, usxDoc, _isLastItem);\n }\n }\n }\n\n // Create chapter and verse end elements from SID attributes.\n if (verseEid && (type === \"verse\" || (parentElement.tagName === \"para\" && isLastItem))) {\n eidElement = createVerseEndElement(usxDoc, verseEid);\n verseEid = undefined;\n }\n if (type === \"verse\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n verseEid = markerContent.sid;\n\n if (chapterEid && (type === \"chapter\" || (type === \"para\" && isLastItem))) {\n eidElement = createChapterEndElement(usxDoc, chapterEid);\n chapterEid = undefined;\n }\n if (type === \"chapter\" && typeof markerContent !== \"string\" && markerContent.sid !== undefined)\n chapterEid = markerContent.sid;\n\n // Append to parent.\n const isVerseInImpliedPara =\n parentElement.nodeName === USX_TYPE && eidElement?.tagName === \"verse\";\n if (eidElement && (!isLastItem || isVerseInImpliedPara)) parentElement.appendChild(eidElement);\n parentElement.appendChild(element);\n if (eidElement && isLastItem && !isVerseInImpliedPara) parentElement.appendChild(eidElement);\n\n // Allow for final chapter and verse end elements at the end of an implied para.\n if (isLastItem && parentElement.nodeName === USX_TYPE) {\n if (verseEid) parentElement.appendChild(createVerseEndElement(usxDoc, verseEid));\n if (chapterEid) parentElement.appendChild(createChapterEndElement(usxDoc, chapterEid));\n verseEid = undefined;\n chapterEid = undefined;\n }\n}\n\nfunction setAttributes(element: Element, markerContent: MarkerObject) {\n if (markerContent.marker) {\n if (markerContent.type === \"unmatched\") element.setAttribute(\"marker\", markerContent.marker);\n else element.setAttribute(\"style\", markerContent.marker);\n }\n for (const [key, value] of Object.entries(markerContent)) {\n if (value && ![\"type\", \"marker\", \"content\"].includes(key)) {\n element.setAttribute(key, value as string);\n }\n }\n}\n\nfunction createVerseEndElement(usxDoc: Document, verseEid: string): Element {\n const eidElement = usxDoc.createElement(\"verse\");\n eidElement.setAttribute(\"eid\", verseEid);\n return eidElement;\n}\n\nfunction createChapterEndElement(usxDoc: Document, chapterEid: string): Element {\n const eidElement = usxDoc.createElement(\"chapter\");\n eidElement.setAttribute(\"eid\", chapterEid);\n return eidElement;\n}\n","const JSON_PATH_START = \"$\";\nconst JSON_PATH_CONTENT = \".content[\";\n\n/**\n * Converts a USJ JSONPath string into an array of indexes.\n *\n * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.\n * @returns An array of numeric indexes extracted from the JSONPath.\n * @throws Will throw an error if the JSONPath does not start with `$`.\n *\n * @public\n */\nexport function indexesFromUsjJsonPath(jsonPath: string): number[] {\n const path = jsonPath.split(JSON_PATH_CONTENT);\n if (path.shift() !== JSON_PATH_START)\n throw new Error(`indexesFromJsonPath: jsonPath didn't start with '${JSON_PATH_START}'`);\n\n const indexes = path.map((str) => parseInt(str, 10));\n return indexes;\n}\n\n/**\n * Converts an array of indexes into a USJ JSONPath string.\n *\n * @param indexes - An array of numeric indexes to convert.\n * @returns A USJ JSONPath string constructed from the indexes.\n *\n * @public\n */\nexport function usjJsonPathFromIndexes(indexes: number[]): string {\n return indexes.reduce((path, index) => `${path}${JSON_PATH_CONTENT}${index}]`, JSON_PATH_START);\n}\n"],"names":["USJ_TYPE","USJ_VERSION","EMPTY_USJ","MARKER_OBJECT_PROPS","isValidBookCode","code","VALID_BOOK_CODES","USX_TYPE","USX_VERSION","EMPTY_USX","usxStringToUsj","usxString","inputUsxDom","DOMParser","usxDomToUsj","outputJson","convertUsxRecurse","inputUsxElement","attribs","type","marker","text","action","attrib","outObj","asciiTrim","children","child","childDict","whatToDo","str","chapterEid","verseEid","usjToUsxString","usj","usxDoc","DOMImplementation","usjToUsxDom","index","markerContent","isLastItem","convertUsjRecurse","parentElement","element","eidElement","setAttributes","item","_isLastItem","createVerseEndElement","createChapterEndElement","isVerseInImpliedPara","key","value","JSON_PATH_START","JSON_PATH_CONTENT","indexesFromUsjJsonPath","jsonPath","path","usjJsonPathFromIndexes","indexes"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.52.13"
9
+ }
10
+ ]
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eten-tech-foundation/scripture-utilities",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Utilities for working with Scripture data.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/eten-tech-foundation/scripture-editors/tree/main/packages/utilities#readme",
@@ -7,6 +7,8 @@ const JSON_PATH_CONTENT = ".content[";
7
7
  * @param jsonPath - The USJ JSONPath string to convert. It must start with `$` and contain `.content[index]` segments.
8
8
  * @returns An array of numeric indexes extracted from the JSONPath.
9
9
  * @throws Will throw an error if the JSONPath does not start with `$`.
10
+ *
11
+ * @public
10
12
  */
11
13
  export function indexesFromUsjJsonPath(jsonPath: string): number[] {
12
14
  const path = jsonPath.split(JSON_PATH_CONTENT);
@@ -22,6 +24,8 @@ export function indexesFromUsjJsonPath(jsonPath: string): number[] {
22
24
  *
23
25
  * @param indexes - An array of numeric indexes to convert.
24
26
  * @returns A USJ JSONPath string constructed from the indexes.
27
+ *
28
+ * @public
25
29
  */
26
30
  export function usjJsonPathFromIndexes(indexes: number[]): string {
27
31
  return indexes.reduce((path, index) => `${path}${JSON_PATH_CONTENT}${index}]`, JSON_PATH_START);
@@ -11,6 +11,14 @@ import { USX_TYPE, USX_VERSION } from "./usx.model.js";
11
11
  let chapterEid: string | undefined;
12
12
  let verseEid: string | undefined;
13
13
 
14
+ /**
15
+ * Converts a USJ object to a USX string.
16
+ *
17
+ * @param usj - The USJ object to convert
18
+ * @returns The converted USX string.
19
+ *
20
+ * @public
21
+ */
14
22
  export function usjToUsxString(usj: Usj): string {
15
23
  const usxDoc = new DOMImplementation().createDocument("", USX_TYPE);
16
24
  if (usxDoc.documentElement) {
@@ -84,8 +92,10 @@ function convertUsjRecurse(
84
92
  }
85
93
 
86
94
  function setAttributes(element: Element, markerContent: MarkerObject) {
87
- if (markerContent.type === "unmatched") element.setAttribute("marker", markerContent.marker);
88
- else element.setAttribute("style", markerContent.marker);
95
+ if (markerContent.marker) {
96
+ if (markerContent.type === "unmatched") element.setAttribute("marker", markerContent.marker);
97
+ else element.setAttribute("style", markerContent.marker);
98
+ }
89
99
  for (const [key, value] of Object.entries(markerContent)) {
90
100
  if (value && !["type", "marker", "content"].includes(key)) {
91
101
  element.setAttribute(key, value as string);
@@ -4,15 +4,28 @@
4
4
  * @see https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js
5
5
  */
6
6
 
7
- /** The USJ spec type */
7
+ /**
8
+ * The USJ spec type
9
+ * @public
10
+ */
8
11
  export const USJ_TYPE = "USJ";
9
12
 
10
- /** The USJ spec version */
13
+ /**
14
+ * The USJ spec version
15
+ * @public
16
+ */
11
17
  export const USJ_VERSION = "3.1";
12
18
 
19
+ /**
20
+ * An empty USJ object
21
+ * @public
22
+ */
13
23
  export const EMPTY_USJ = Object.freeze<Usj>({ type: USJ_TYPE, version: USJ_VERSION, content: [] });
14
24
 
15
- /** List of known properties of `MarkerObject` */
25
+ /**
26
+ * List of known properties of `MarkerObject`
27
+ * @public
28
+ */
16
29
  export const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [
17
30
  "type",
18
31
  "marker",
@@ -28,10 +41,16 @@ export const MARKER_OBJECT_PROPS: (keyof MarkerObject)[] = [
28
41
  "category",
29
42
  ];
30
43
 
31
- /** Single piece of Scripture content */
44
+ /**
45
+ * Single piece of Scripture content
46
+ * @public
47
+ */
32
48
  export type MarkerContent = string | MarkerObject;
33
49
 
34
- /** A Scripture Marker and its contents */
50
+ /**
51
+ * A Scripture Marker and its contents
52
+ * @public
53
+ */
35
54
  export interface MarkerObject {
36
55
  /**
37
56
  * The kind/category of node or element this is, corresponding the USFM marker and USX node
@@ -42,7 +61,7 @@ export interface MarkerObject {
42
61
  * The corresponding marker in USFM or style in USX
43
62
  * @example `p`, `v`, `nd`
44
63
  */
45
- marker: string;
64
+ marker?: string;
46
65
  /** This marker's contents laid out in order */
47
66
  content?: MarkerContent[];
48
67
  /** Indicates the Book-chapter-verse value in the paragraph based structure */
@@ -65,7 +84,10 @@ export interface MarkerObject {
65
84
  category?: string;
66
85
  }
67
86
 
68
- /** Scripture data represented in JSON format. Data compatible transformation from USX/USFM */
87
+ /**
88
+ * Scripture data represented in JSON format. Data compatible transformation from USX/USFM
89
+ * @public
90
+ */
69
91
  export interface Usj {
70
92
  /** The USJ spec type */
71
93
  type: typeof USJ_TYPE;
@@ -75,14 +97,25 @@ export interface Usj {
75
97
  content: MarkerContent[];
76
98
  }
77
99
 
100
+ /**
101
+ * Check if the given code is a valid 3-letter Scripture book code.
102
+ * @public
103
+ */
78
104
  export function isValidBookCode(code: string): boolean {
79
105
  return VALID_BOOK_CODES.includes(code as BookCode);
80
106
  }
81
107
 
82
- /** 3-letter Scripture book code */
108
+ /**
109
+ * 3-letter Scripture book code
110
+ * @public
111
+ */
83
112
  export type BookCode = (typeof VALID_BOOK_CODES)[number];
84
113
 
85
- const VALID_BOOK_CODES = [
114
+ /**
115
+ * List of valid 3-letter Scripture book codes
116
+ * @public
117
+ */
118
+ export const VALID_BOOK_CODES = [
86
119
  // Old Testament
87
120
  "GEN",
88
121
  "EXO",
@@ -13,6 +13,14 @@ interface Attribs {
13
13
  [name: string]: string;
14
14
  }
15
15
 
16
+ /**
17
+ * Converts a USX string to a USJ object.
18
+ *
19
+ * @param usxString - The USX string to convert.
20
+ * @returns The converted USJ object.
21
+ *
22
+ * @public
23
+ */
16
24
  export function usxStringToUsj(usxString: string): Usj {
17
25
  const parser = new DOMParser();
18
26
  const inputUsxDom = parser.parseFromString(usxString, "text/xml");
@@ -4,10 +4,20 @@
4
4
  * @see https://github.com/usfm-bible/tcdocs/blob/main/grammar/usx.rng
5
5
  */
6
6
 
7
- /** The USX spec type */
7
+ /**
8
+ * The USX spec type
9
+ * @public
10
+ */
8
11
  export const USX_TYPE = "usx";
9
12
 
10
- /** The USX spec version */
13
+ /**
14
+ * The USX spec version
15
+ * @public
16
+ */
11
17
  export const USX_VERSION = "3.1";
12
18
 
19
+ /**
20
+ * An empty USX string
21
+ * @public
22
+ */
13
23
  export const EMPTY_USX = `<${USX_TYPE} version="${USX_VERSION}" />`;
package/src/index.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Utilities for Scripture data conversion and manipulation, including USJ/USX format conversion.
4
+ */
5
+
1
6
  export type { Usj, BookCode, MarkerContent, MarkerObject } from "./converters/usj/usj.model.js";
2
7
  export {
3
8
  EMPTY_USJ,
@@ -5,6 +10,7 @@ export {
5
10
  USJ_TYPE,
6
11
  USJ_VERSION,
7
12
  isValidBookCode,
13
+ VALID_BOOK_CODES,
8
14
  } from "./converters/usj/usj.model.js";
9
15
  export { EMPTY_USX, USX_TYPE, USX_VERSION } from "./converters/usj/usx.model.js";
10
16
  export { usxStringToUsj } from "./converters/usj/usx-to-usj.js";
@@ -0,0 +1,5 @@
1
+ // This was added to silence a warning from `api-extractor`.
2
+ declare module "rollup/parseAst" {
3
+ export function parseAst(...args: unknown[]): unknown;
4
+ export function parseAstAsync(...args: unknown[]): unknown;
5
+ }