m(a,t).re),r=e.of.length>0?m(e.of[0],t).literal:"";return{re:`(?:${n.join("|")})`,literal:r}}case"any_char_class":{if(e.range){let[n,r]=e.range;return{re:`[${U(n)}-${U(r)}]`,literal:n??""}}return e.chars&&e.chars.length>0?{re:`[${e.chars.map(r=>U(r)).join("")}]`,literal:e.chars[0]}:{re:"[^]",literal:""}}case"group":{let n=e.items.map(s=>m(s,t)),r=n.map(s=>s.re).join(""),a=n.map(s=>s.literal).join("");return{re:`(?:${r})`,literal:a}}case"repeat":{let n=m(e.item,t).re,{min:r,max:a}=e,s=a===null?1/0:a,o=s===1/0?r===0?"*":"+":`{${r},${s}}`;return{re:`(?:${n})${o}`,literal:""}}case"stage_ref":return{re:"",literal:""}}}function F(e,t){switch(e.kind){case"string":return e.value;case"alias":{let n=Z[e.name];if(n)return n.literal===""?null:n.literal;let r=t.resolveAlias(e.name);return r?F(r,t):null}case"capture_group":case"capture_ref":case"repeat":case"stage_ref":return null;case"any_char_class":return e.chars&&e.chars.length>0?e.chars[0]:e.range?e.range[0]:null;case"any":return e.of.length===0?null:F(e.of[0],t);case"group":{let n="";for(let r of e.items){let a=F(r,t);if(a===null)return null;n+=a}return n}}}function I(e,t){switch(e.kind){case"string":return[e.value];case"alias":{if(Z[e.name])return null;let r=t.resolveAlias(e.name);return r?I(r,t):null}case"any_char_class":{if(e.chars)return[...e.chars];if(e.range){let[n,r]=e.range,a=n.codePointAt(0),s=r.codePointAt(0),o=[];for(let c=a;c<=s;c++)o.push(String.fromCodePoint(c));return o}return null}case"any":{let n=[];for(let r of e.of){let a=I(r,t);if(a===null)return null;n.push(...a)}return n}case"group":{let n=[""];for(let r of e.items){let a=I(r,t);if(a===null)return null;let s=[];for(let o of n)for(let c of a)s.push(o+c);n=s}return n}default:return null}}function w(e,t){switch(e.kind){case"string":return e.value.length;case"capture_group":return w(e.data,t);case"capture_ref":return 1;case"alias":{if(e.name==="none")return 0;if(Z[e.name])return 1;let n=t.resolveAlias(e.name);return n?w(n,t):1}case"any":return e.of.reduce((n,r)=>Math.max(n,w(r,t)),0);case"any_char_class":return 1;case"group":return e.items.reduce((n,r)=>n+w(r,t),0);case"repeat":return w(e.item,t);case"stage_ref":return 1}}var Z=Object.freeze({any_character:{re:".",literal:""},none:{re:"",literal:""},space:{re:" ",literal:" "},whitespace:{re:"\\s+",literal:" "},boundary:{re:"(?:(?at(e,t??{}),separate:(e,t)=>st(e,t??{}),compose:ot,decompose:it};function lt(e,t){let n=e.functions.get(t)?.impl;if(n)return n;let r=gn[t];if(r)return r}var bn={sub:(e,t)=>yn(e,t),run:(e,t)=>{let n=e.docName?t.loadDependency(e.docName):t.map,r=n.stages.find(s=>s.name===e.stage);if(!r)throw new A(`Stage not found: ${e.stage}`);let a=t.withMap(n);for(let s of r.rules)T(s,a);t.current=a.current},funcall:(e,t)=>{let n=lt(t,e.name);if(!n)throw new A(`Unknown function: ${e.name}`);t.current=n(t.current,e.kwargs??{})},parallel:(e,t)=>{let n=[],r=[];for(let o of e.rules)o.kind==="sub"&&o.from?n.push(o):r.push(o);let a=o=>!o.before&&!o.after&&!o.notBefore&&!o.notAfter&&I(o.from,t)!==null;if(n.some(o=>!a(o))){let c=n.map((i,l)=>({rule:i,idx:l,len:w(i.from,t)+(i.before?w(i.before,t):0)+(i.after?w(i.after,t):0)+(i.notBefore?w(i.notBefore,t):0)+(i.notAfter?w(i.notAfter,t):0)+(i.priority??0)})).sort((i,l)=>l.len-i.len||i.idx-l.idx).map(({rule:i})=>{let l=m(i.from,t),p=i.before?m(i.before,t).re:"",u=i.after?m(i.after,t).re:"",S=i.notBefore?m(i.notBefore,t).re:"",y=i.notAfter?m(i.notAfter,t).re:"",x=[p?`(?<=${p})`:"",S?`(?pt(v,f,Y):v}});t.current=ct(t.current,c)}else{let o=[];for(let c of n){if(c.to&&c.to.kind==="funcall_inline")continue;let i=c.to?F(c.to,t):"";if(i===null)continue;let l=I(c.from,t);if(l!==null)for(let p of l)o.push([p,i])}o.length>0&&(t.current=tt(t.current,et(o)))}for(let o of r)T(o,t)},sequential:(e,t)=>{for(let n of e.rules)T(n,t)}};function yn(e,t){if(!e.from)throw new A("Sub rule missing 'from'");let n=m(e.from,t),r=e.before?m(e.before,t).re:"",a=e.after?m(e.after,t).re:"",s=e.notBefore?m(e.notBefore,t).re:"",o=e.notAfter?m(e.notAfter,t).re:"",c=[];r&&c.push(`(?<=${r})`),s&&c.push(`(?{let p=l;return(u,...S)=>pt(p,u,S)})():l)}function pt(e,t,n){return e.replace(/\$(\d+)/g,(r,a)=>{let s=parseInt(a,10);return n[s-1]??t})}function dt(e,t){if(!e)return"";if(e.kind==="funcall_inline"){let r=lt(t,e.name);if(!r)throw new A(`Unknown inline function: ${e.name}`);return a=>r(a)}return m(e,t).literal}function T(e,t){let n=bn[e.kind];n(e,t)}var Ke=new Set(["rababa","secryst"]);function Wt(e){for(let t of e){if(t.kind==="funcall"&&Ke.has(t.name))return!0;if(t.kind==="parallel"||t.kind==="sequential"){for(let n of t.rules)if(n.kind==="funcall"&&Ke.has(n.name))return!0}}return!1}function W(e,t,n,r){let a=e.stages.find(o=>o.name===t);if(!a)return n;if(Wt(a.rules))throw new Error(`Stage "${t}" contains ML function calls (rababa/secryst). Use transliterateAsync() instead of transliterate().`);let s=new P(e,n,r);for(let o of a.rules)T(o,s);return s.current}async function Qt(e,t,n,r){let a=e.stages.find(o=>o.name===t);if(!a)return n;if(!Wt(a.rules))return W(e,t,n,r);let s=new P(e,n,r);for(let o of a.rules)await er(o,s);return s.current}async function er(e,t){if(e.kind==="funcall"){await tr(e,t);return}T(e,t)}async function tr(e,t){if(e.name==="rababa"){let{rababa:n}=await Promise.resolve().then(()=>(xt(),_t));t.current=await n(t.current,e.kwargs);return}if(Ke.has(e.name)){let{loadModel:n}=await Promise.resolve().then(()=>(Kt(),Vt)),r=e.kwargs?.config??e.kwargs?.model??"default",a=await n({kind:e.name,id:r});t.current=await a.transform(t.current);return}T({kind:"funcall",name:e.name,kwargs:e.kwargs},t)}function nr(e,t){if(e===t)return 0;if(e.length===0)return t.length;if(t.length===0)return e.length;let n=new Array(t.length+1),r=new Array(t.length+1);for(let a=0;a<=t.length;a++)n[a]=a;for(let a=1;a<=e.length;a++){r[0]=a;for(let s=1;s<=t.length;s++){let o=e[a-1]===t[s-1]?0:1;r[s]=Math.min(n[s]+1,r[s-1]+1,n[s-1]+o)}[n,r]=[r,n]}return n[t.length]}function rr(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`)}function Jt(e,t,n,r={},a){let s=[],o=r.mapPattern?rr(r.mapPattern):null,c=a??n.loadedMaps();for(let i of c){if(o&&!o.test(i))continue;let l;try{let p=n.load(i);l=W(p,"main",e,n)}catch(p){if(p instanceof L)continue;throw p}s.push({mapName:i,distance:nr(l,t)})}return s.sort((i,l)=>i.distance-l.distance)}function We(e){let t={schemaVersion:e.schemaVersion,systemCode:e.systemCode,dependencies:e.dependencies,stages:e.stages,aliases:new Map(Object.entries(e.aliases)),functions:new Map};return e.metadata&&(t.metadata=e.metadata),t}var ar="main",de=class{loader;defaultStage;constructor(t={}){this.loader=new X(t.strategies??[]),this.defaultStage=t.defaultStage??ar}loadMap(t){let n=this.loader.load(t);for(let r of n.dependencies)try{this.loader.load(r)}catch(a){throw a instanceof g?new C(r):a}return n}async loadMapAsync(t){let n=await this.loader.loadAsync(t),r=new Set,a=[...n.dependencies];for(;a.length>0;){let s=a.shift();if(!r.has(s)){r.add(s);try{let o=await this.loader.loadAsync(s);for(let c of o.dependencies)r.has(c)||a.push(c)}catch(o){throw o instanceof g?new C(s):o}}}return n}transliterate(t,n,r){try{let a=this.loadMap(t),s=r??this.defaultStage;return W(a,s,n,this.loader)}catch(a){throw a instanceof L?a:new M(`Transliteration failed for ${t}: ${a.message}`,{cause:a})}}async transliterateAsync(t,n,r){try{let a=await this.loadMapAsync(t),s=r??this.defaultStage;return await Qt(a,s,n,this.loader)}catch(a){throw a instanceof L?a:new M(`Transliteration failed for ${t}: ${a.message}`,{cause:a})}}loadedMaps(){return this.loader.loadedMaps()}getLoader(){return this.loader}detect(t,n,r={},a){return Jt(t,n,this.loader,r,a)}},pe;function Yt(e){pe=new de(e)}function sr(){return pe||(pe=new de),pe}function Qe(e,t,n){return sr().transliterateAsync(e,t,n)}var Xt=["acadsin-zho-Hani-Latn-2002","alalc-amh-Ethi-Latn-1997","alalc-amh-Ethi-Latn-2011","alalc-ara-Arab-Latn-1997","alalc-asm-Deva-Latn-1997","alalc-asm-Deva-Latn-2012","alalc-aze-Arab-Latn-1997","alalc-aze-Cyrl-Latn-1997","alalc-bel-Cyrl-Latn-1997","alalc-ben-Beng-Latn-1997","alalc-ben-Beng-Latn-2017","alalc-bul-Cyrl-Latn-1997","alalc-div-Thaa-Latn-1997","alalc-div-Thaa-Latn-2012","alalc-ell-Grek-Latn-1997","alalc-ell-Grek-Latn-2010","alalc-fas-Arab-Latn-1997","alalc-guj-Gujr-Latn-1997","alalc-guj-Gujr-Latn-2011","alalc-hin-Deva-Latn-1997","alalc-hin-Deva-Latn-2011","alalc-kan-Kana-Latn-1997","alalc-kan-Kana-Latn-2011","alalc-kat-Geok-Latn-1997","alalc-kat-Geok-Latn-2011","alalc-kat-Geor-Latn-1997","alalc-kat-Geor-Latn-2011","alalc-kor-Hang-Latn-1997","alalc-mal-Mlym-Latn-1997","alalc-mal-Mlym-Latn-2012","alalc-mar-Deva-Latn-1997","alalc-mar-Deva-Latn-2011","alalc-mkd-Cyrl-Latn-1997","alalc-mkd-Cyrl-Latn-2013","alalc-mon-Cyrl-Latn-1997","alalc-ori-Orya-Latn-1997","alalc-ori-Orya-Latn-2011","alalc-pan-Guru-Latn-1997","alalc-pan-Guru-Latn-2011","alalc-pli-Deva-Latn-2012","alalc-pra-Deva-Latn-2012","alalc-rus-Cyrl-Latn-1997","alalc-rus-Cyrl-Latn-2012","alalc-san-Deva-Latn-2012","alalc-sin-Sinh-Latn-1997","alalc-sin-Sinh-Latn-2011","alalc-srp-Cyrl-Latn-1997","alalc-srp-Cyrl-Latn-2013","alalc-tam-Taml-Latn-1997","alalc-tam-Taml-Latn-2011","alalc-tel-Telu-Latn-1997","alalc-tel-Telu-Latn-2011","alalc-tir-Ethi-Latn-1997","alalc-tir-Ethi-Latn-2011","alalc-ukr-Cyrl-Latn-1997","alalc-ukr-Cyrl-Latn-2011","apcbg-bul-Cyrl-Latn-1995","az-aze-Cyrl-Latn-1939","az-aze-Cyrl-Latn-1958","bas-rus-Cyrl-Latn-2017-bss","bas-rus-Cyrl-Latn-2017-oss","bgn-jpn-Hrkt-Latn-1962","bgn-kor-Hang-Latn-1943","bgn-kor-Kore-Latn-1943","bgna-bul-Cyrl-Latn-2006","bgna-bul-Cyrl-Latn-2009","bgnpcgn-amh-Ethi-Latn-1967","bgnpcgn-ara-Arab-Latn-1956","bgnpcgn-arm-Armn-Latn-1981","bgnpcgn-aze-Cyrl-Latn-1993","bgnpcgn-bak-Cyrl-Latn-2007","bgnpcgn-bal-Arab-Latn-2008","bgnpcgn-bel-Cyrl-Latn-1979","bgnpcgn-bul-Cyrl-Latn-1952","bgnpcgn-bul-Cyrl-Latn-2013","bgnpcgn-che-Cyrl-Latn-2008","bgnpcgn-deu-Latn-Latn-2000","bgnpcgn-div-Thaa-Latn-1972","bgnpcgn-div-Thaa-Latn-1988","bgnpcgn-ell-Grek-Latn-1962","bgnpcgn-ell-Grek-Latn-1996","bgnpcgn-fao-Latn-Latn-1964","bgnpcgn-fao-Latn-Latn-1968","bgnpcgn-fas-Arab-Latn-1956","bgnpcgn-isl-Latn-Latn-1964","bgnpcgn-isl-Latn-Latn-1968","bgnpcgn-jpn-Hrkt-Latn-1976","bgnpcgn-kat-Geor-Latn-1981","bgnpcgn-kat-Geor-Latn-2009","bgnpcgn-kaz-Cyrl-Latn-1979","bgnpcgn-kir-Cyrl-Latn-1979","bgnpcgn-kor-Hang-Latn-kn-1945","bgnpcgn-kor-Hang-Latn-rok-2011","bgnpcgn-kor-Kore-Latn-rok-2011","bgnpcgn-kur-Arab-Latn-2007","bgnpcgn-mkd-Cyrl-Latn-1981","bgnpcgn-mkd-Cyrl-Latn-2013","bgnpcgn-mon-Cyrl-Latn-1964","bgnpcgn-nep-Deva-Latn-2011","bgnpcgn-per-Arab-Latn-1958","bgnpcgn-prs-Arab-Latn-2007","bgnpcgn-prs-Arab-Latn-yaghoubi","bgnpcgn-pus-Arab-Latn-1968","bgnpcgn-ron-Cyrl-Latn-2002","bgnpcgn-rue-Cyrl-Latn-2016","bgnpcgn-rus-Cyrl-Latn-1947","bgnpcgn-sme-Latn-Latn-1984","bgnpcgn-srp-Cyrl-Latn-1962","bgnpcgn-srp-Cyrl-Latn-2005","bgnpcgn-tat-Cyrl-Latn-2007","bgnpcgn-tgk-Cyrl-Latn-1994","bgnpcgn-tir-Ethi-Latn-2007","bgnpcgn-ukr-Cyrl-Latn-1965","bgnpcgn-ukr-Cyrl-Latn-2019","bgnpcgn-urd-Arab-Latn-2007","bgnpcgn-uzb-Cyrl-Latn-1979","bgnpcgn-uzb-Cyrl-Latn-2000","bgnpcgn-zho-Hans-Latn-1979","bis-asm-Beng-Latn-13194-1991","bis-ben-Beng-Latn-13194-1991","bis-dev-Deva-Latn-13194-1991","bis-guj-Gujr-Latn-13194-1991","bis-kan-Kana-Latn-13194-1991","bis-mlm-Mlym-Latn-13194-1991","bis-ori-Orya-Latn-13194-1991","bis-pnj-Guru-Latn-13194-1991","bis-tel-Telu-Latn-13194-1991","bis-tml-Taml-Latn-13194-1991","by-bel-Cyrl-Latn-1998","by-bel-Cyrl-Latn-2007","din-grc-Grek-Latn-31634-2011-t1","din-hin-Deva-Latn-33904-2018","din-kat-Geor-Latn-32707-2010","din-mar-Deva-Latn-33904-2018","din-nep-Deva-Latn-33904-2018","din-pli-Deva-Latn-33904-2018","din-pra-Deva-Latn-33904-2018","din-san-Deva-Latn-33904-2018","din-tam-Taml-Latn-33903-2016","dos-nep-Deva-Latn-1997","elot-ell-Grek-Latn-743-1982-tl","elot-ell-Grek-Latn-743-1982-ts","elot-ell-Grek-Latn-743-2001-tl","elot-ell-Grek-Latn-743-2001-ts","ggg-kat-Geor-Latn-2002","gki-bel-Cyrl-Latn-1992","gki-bel-Cyrl-Latn-2000","gost-rus-Cyrl-Latn-16876-71-1983","gost-rus-Cyrl-Latn-7.79-2000-2002","hk-yue-Hani-Latn-1888","icao-bel-Cyrl-Latn-9303","icao-bul-Cyrl-Latn-9303","icao-fas-Arab-Latn-9303","icao-heb-Hebr-Latn-9303","icao-mkd-Cyrl-Latn-9303","icao-rus-Cyrl-Latn-9303","icao-srp-Cyrl-Latn-9303","icao-ukr-Cyrl-Latn-9303","iso-ara-Arab-Latn-233-1984","iso-ara-Arab-Latn-233-2-1993","iso-asm-Beng-Latn-15919-2001","iso-ben-Beng-Latn-15919-2001","iso-ell-Grek-Latn-843-1997-t1","iso-ell-Grek-Latn-843-1997-t2","iso-guj-Gujr-Latn-15919-2001","iso-hin-Deva-Latn-15919-2001","iso-inc-Deva-Latn-15919-2001","iso-jpn-Hrkt-Latn-3602-1989","iso-kan-Kana-Latn-15919-2001","iso-kat-Geor-Latn-9984-1996","iso-kor-Hang-Latn-1996-method1","iso-kor-Hang-Latn-1996-method2","iso-mal-Mlym-Latn-15919-2001","iso-mar-Deva-Latn-15919-2001","iso-nep-Deva-Latn-15919-2001","iso-ori-Orya-Latn-15919-2001","iso-pan-Guru-Latn-15919-2001","iso-pli-Beng-Latn-15919-2001","iso-pli-Deva-Latn-15919-2001","iso-pli-Sinh-Latn-15919-2001","iso-pli-Thai-Latn-15919-2001","iso-pra-Deva-Latn-15919-2001","iso-prs-Arab-Latn-233-3-1999","iso-rus-Cyrl-Latn-9-1995","iso-san-Deva-Latn-15919-2001","iso-tam-Taml-Latn-15919-2001","iso-tel-Telu-Latn-15919-2001","iso-tha-Thai-Latn-11940-1998","kp-kor-Hang-Latn-2002","lshk-yue-Hani-Latn-jyutping-1993","masm-mon-Cyrl-Latn-5217-2012","masm-mon-Latn-Cyrl-5217-2012","mext-jpn-Hrkt-Latn-1954","moct-kor-Hang-Latn-2000","mofa-jpn-Hrkt-Latn-1989","mv-div-Thaa-Latn-1987","mvd-bel-Cyrl-Latn-2008","mvd-bel-Cyrl-Latn-2010","mvd-rus-Cyrl-Latn-2008","mvd-rus-Cyrl-Latn-2010","odni-ara-Arab-Latn-2004","odni-ara-Arab-Latn-2015","odni-aze-Cyrl-Latn-2015","odni-bel-Cyrl-Latn-2015","odni-bul-Cyrl-Latn-2005","odni-bul-Cyrl-Latn-2015","odni-che-Cyrl-Latn-2015","odni-fas-Arab-Latn-2004","odni-fas-Arab-Latn-2015","odni-hin-Deva-Latn-2004","odni-hin-Deva-Latn-2015","odni-kat-Geor-Latn-2015","odni-kaz-Cyrl-Latn-2015","odni-kir-Cyrl-Latn-2015","odni-kor-Hang-Latn-2015","odni-mkd-Cyrl-Latn-2005","odni-mkd-Cyrl-Latn-2015","odni-prs-Arab-Latn-2004","odni-prs-Arab-Latn-2015","odni-pus-Arab-Latn-2011","odni-rus-Cyrl-Latn-2015","odni-srp-Cyrl-Latn-2005","odni-srp-Cyrl-Latn-2015","odni-tat-Cyrl-Latn-2015","odni-tgk-Cyrl-Latn-2015","odni-tuk-Cyrl-Latn-2015","odni-uig-Cyrl-Latn-2015","odni-ukr-Cyrl-Latn-2015","odni-urd-Arab-Latn-2015","odni-uzb-Cyrl-Latn-2015","posix","sac-zho-Hans-Latn-1979","sasm-mon-Mong-Latn-general-1978","sasm-mon-Mong-Latn-phonetic-1978","ses-ara-Arab-Latn-1930","stategeocadastre-ukr-Cyrl-Latn-1993","ua-ukr-Cyrl-Latn-1996","ua-ukr-Cyrl-Latn-2007","ua-ukr-Cyrl-Latn-2010","un-amh-Ethi-Latn-2016","un-ara-Arab-Latn-1971","un-ara-Arab-Latn-1972","un-ara-Arab-Latn-2017","un-asm-Beng-Latn-1972","un-bel-Cyrl-Latn-2007","un-ben-Beng-Latn-2016","un-bul-Cyrl-Latn-1977","un-ell-Grek-Latn-1987-phonetic","un-ell-Grek-Latn-1987-tl","un-ell-Grek-Latn-1987-ts","un-guj-Gujr-Latn-1972","un-hin-Deva-Latn-2016","un-kan-Kana-Latn-2016","un-mal-Mlym-Latn-1972","un-mar-Deva-Latn-2016","un-mkd-Cyrl-Latn-1977","un-mon-Mong-Latn-general-2013","un-mon-Mong-Latn-phonetic-2013","un-nep-Deva-Latn-1972","un-nep-Deva-Latn-2013","un-ori-Orya-Latn-1972","un-pan-Guru-Latn-1972","un-prs-Arab-Latn-1967","un-rus-Cyrl-Latn-1987","un-sin-Sinh-Latn-1972","un-srp-Cyrl-Latn-1997","un-tam-Taml-Latn-1972","un-tel-Telu-Latn-1972","un-ukr-Cyrl-Latn-1998","un-ukr-Cyrl-Latn-2012","un-urd-Arab-Latn-1972","unicode","var-Cyrl","var-amh-Ethi-Latn-eae-2003","var-ara-Arab-Arab-rababa","var-gez-Ethi-Latn-eae-2003","var-hin-Deva-Latn-hunterian-1872","var-jpn-Hrkt-Latn-hepburn-1886","var-jpn-Hrkt-Latn-hepburn-1954","var-kor","var-kor-Hang-Hang-jamo","var-kor-Hang-Latn-mr-1939","var-kor-Kore-Hang-2013","var-kor-Kore-Latn-mr-1939","var-mar-Deva-Latn-hunterian-1872","var-mon-Mong-Latn-1930","var-mon-Mong-Latn-lessing","var-mon-Mong-Latn-vpmc","var-pra-Deva-Latn-iast-1912","var-san-Deva-Latn-iast-1912","var-zho-Hani-Latn-wd-1979"];var or=new Set(["unicode","posix","var-Cyrl","var-kor"]),ir=new Set(["var-ara-Arab-Arab-rababa"]),en=Xt.filter(e=>!or.has(e)),cr=en.filter(e=>!ir.has(e)),Zt=new Map;function _(){return en}function tn(){return cr}async function nn(e,t){let n=Zt.get(t);if(n)return n;let r=await e.fetch(new Request(`https://assets.internal/${t}.json`));if(!r.ok)throw new g(t);let a=await r.json(),s=We(a);return Zt.set(t,s),s}var rn;function an(e){if(rn===e)return;Yt({strategies:[n=>nn(e,n)]}),rn=e}async function sn(e,t,n){return an(e),Qe(t,n)}function lr(e,t){if(e===t)return 0;if(!e.length)return t.length;if(!t.length)return e.length;let n=Array.from({length:t.length+1},(r,a)=>a);for(let r=1;r<=e.length;r++){let a=[r];for(let s=1;s<=t.length;s++)a[s]=Math.min(n[s]+1,a[s-1]+1,n[s-1]+(e[r-1]===t[s-1]?0:1));n=a}return n[t.length]}async function on(e,t,n){an(e);let r=[];for(let a of tn())try{let s=await Qe(a,t);r.push({mapName:a,distance:lr(s,n)})}catch{continue}return r.sort((a,s)=>a.distance-s.distance)}var j=class extends Error{constructor(n){super(`Input is too long (max ${n} characters)`);this.max=n;this.name="InputTooLongError"}max},$=class extends Error{constructor(n){super(`Couldn't locate ${n}`);this.systemCode=n;this.name="MapNotFoundError"}systemCode};var D={input_max_size:1e6},ue=12e4;var fe="3.0.0-cloudflare.1",Je;function Q(e){Je=e}var me;function cn(e,t){me=e&&t?{endpoint:e,token:t}:void 0}var pr={"var-ara-Arab-Arab-rababa":"ara-diac-1.0"};async function dr(e,t){if(!me)throw new Error("ML inference unavailable \u2014 ML_ENDPOINT/ML_TOKEN not set");let n=await fetch(`${me.endpoint}/infer`,{method:"POST",headers:{"content-type":"application/json","x-api-key":me.token},body:JSON.stringify({model:e,input:t}),signal:AbortSignal.timeout(ue)}).catch(()=>null);if(!n||!n.ok)throw new Error("ML inference upstream failed");let r=await n.json().catch(()=>null);if(!r||typeof r.output!="string")throw new Error("ML inference returned no output");return r.output}function ln(){if(!Je)throw new Error("ASSETS binding missing \u2014 see README deployment notes");return Je}async function pn(){return JSON.stringify({version:fe,engine:"interscript-ts@native-interpreter"})}function dn(){return[..._()]}async function he(e,t){if(t.length>D.input_max_size)throw new j(D.input_max_size);let n=pr[e];if(n)return dr(n,t);try{return await sn(ln(),e,t)}catch(r){throw r instanceof g?new $(e):r}}async function ge(e,t){return on(ln(),e,t)}var un={version:1,models:[{id:"khm-latn-1.0",task:"translit",scripts:["Khmr","Latn"],precision:"fp32",filename:"khm-latn-1.0-fp32.zip",url:"https://github.com/interscript/interscript-ml/releases/download/khm-latn-1.0/khm-latn-1.0-fp32.zip",parts:null,sha256:"55993d473a2ed9489058779cad7e115db05e4020085b71616468cfae4f2f65cb",size:1418009977,metrics:[{name:"cer",value:27.42,source:"secryst/docs/RESULTS.md#khmer-transliteration-2026-08-14"},{name:"em",value:59.66,source:"secryst/docs/RESULTS.md#khmer-transliteration-2026-08-14"}],parity:{samples:895,cer_delta:0},license:"BSD-3-Clause"},{id:"urd-g2p-1.0",task:"g2p",scripts:["Arab","IPA"],precision:"fp32",filename:"urd-g2p-1.0-fp32.zip",url:"https://github.com/interscript/interscript-ml/releases/download/urd-g2p-1.0/urd-g2p-1.0-fp32.zip",parts:null,sha256:"9f0a248c7116aa7f6b9ac125fb9684a3556f3b8ce8b64567c05a5e7e7a018ebf",size:1418039605,metrics:[{name:"cer",value:14.77,source:"rababa-urdu/docs/RESULTS.md#g2p-urdu-text--ipa"},{name:"em",value:33.6,source:"rababa-urdu/docs/RESULTS.md#g2p-urdu-text--ipa"}],parity:{samples:12699,cer_delta:0},license:"BSD-3-Clause"},{id:"urd-diac-1.0",task:"diacritization",scripts:["Arab","Arab"],precision:"fp32",filename:"urd-diac-1.0-fp32.zip",url:"https://github.com/interscript/interscript-ml/releases/download/urd-diac-1.0/urd-diac-1.0-fp32.zip",parts:null,sha256:"c299ea44a4da4851a52283f8e42d2fb6f8c7a9c443ba7eb0270f97372e10b8ea",size:1417851421,metrics:[{name:"cer",value:3.74,source:"rababa-urdu/docs/RESULTS.md#diacritization-urdu-text--text--haraqat"}],parity:{samples:11940,cer_delta:0},license:"BSD-3-Clause"},{id:"heb-diac-1.0",task:"diacritization",scripts:["Hebr","Hebr"],precision:"fp32",filename:"heb-diac-1.0-fp32.zip",url:null,parts:[{url:"https://github.com/interscript/interscript-ml/releases/download/heb-diac-1.0/heb-diac-1.0-fp32.zip.part-00",sha256:"5497f70cc3c96829e65f1eb0cc401c2d1ffeb4fa909acd4def824c2e019cbc37",size:15e8},{url:"https://github.com/interscript/interscript-ml/releases/download/heb-diac-1.0/heb-diac-1.0-fp32.zip.part-01",sha256:"a7d2011ab4f9326a86e234cc49cb50daf82949e51efbf03a21d5796c46465e9d",size:1281139753}],sha256:"b32fa239e0c56708b6004942de4b9e6e9f265e7cc3828f41d560e96386e20dd8",size:2781139753,metrics:[{name:"der_greedy",value:29,source:"rababa/docs/RESULTS.md#hebrew-diacritization"},{name:"der_beam4",value:17.46,source:"rababa/docs/RESULTS.md#hebrew-diacritization"}],parity:{samples:600,cer_delta:0},license:"BSD-3-Clause"},{id:"tha-g2p-base-1.0",task:"g2p",scripts:["Thai","IPA"],precision:"fp32",filename:"tha-g2p-base-1.0-fp32.zip",url:null,parts:[{url:"https://github.com/interscript/interscript-ml/releases/download/tha-g2p-base-1.0/tha-g2p-base-1.0-fp32.zip.part-00",sha256:"c4525414e5dec91a54d055ca9f1acd51c198f81fbfc55977aa82c1fb038d58d7",size:15e8},{url:"https://github.com/interscript/interscript-ml/releases/download/tha-g2p-base-1.0/tha-g2p-base-1.0-fp32.zip.part-01",sha256:"f5d11fbd75f92e8b5471add867418e2dd4f49a5e258b9ded2f3b5ffa630abe24",size:1281174343}],sha256:"f341a5868867a9847d3f897d1fcc4d1d63b9a59b8913871bcf4ecab48f4fa895",size:2781174343,metrics:[{name:"per_teacher",value:1.25,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-base-1.0"},{name:"per_student",value:3.53,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-base-1.0"}],parity:{samples:1219,cer_delta:0},license:"BSD-3-Clause"},{id:"tha-g2p-small-1.0",task:"g2p",scripts:["Thai","IPA"],precision:"int8",filename:"tha-g2p-small-1.0-int8.zip",url:"https://github.com/interscript/interscript-ml/releases/download/tha-g2p-small-1.0/tha-g2p-small-1.0-int8.zip",parts:null,sha256:"f2cfa93a42d98aabf08fdc82dfc6bd1574e21993f36558aa581fbe29e0588a94",size:257468517,metrics:[{name:"per_teacher",value:4.43,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"},{name:"per_student_beam4",value:12.06,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"},{name:"per_student",value:2.85,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"}],parity:{samples:1219,cer_delta:.0803},license:null},{id:"tha-g2p-small-1.0-int4",task:"g2p",scripts:["Thai","IPA"],precision:"int4",filename:"tha-g2p-small-1.0-int4.zip",url:"https://github.com/interscript/interscript-ml/releases/download/tha-g2p-small-1.0/tha-g2p-small-1.0-int4.zip",parts:null,sha256:"e5d12a3d65c6ac39fa08f52c975c99ff7a51c3e7b7aca7fbb461d2de2345e4cb",size:201958073,metrics:[{name:"per_teacher",value:4.43,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"},{name:"per_student_beam4",value:12.06,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"},{name:"per_student",value:2.85,source:"interscript/interscript-ml docs/RESULTS.md#tha-g2p-small-1.0"}],parity:{samples:600,cer_delta:.0734},license:"BSD-3-Clause"},{id:"heb-diac-small-1.0",task:"diacritization",scripts:["Hebr","Hebr"],precision:"fp32",filename:"heb-diac-small-1.0-fp32.zip",url:"https://github.com/interscript/interscript-ml/releases/download/heb-diac-small-1.0/heb-diac-small-1.0-fp32.zip",parts:null,sha256:"905c53873d5a62bbe6252c6ec0ecef5f37e96e78cb01e8cd99b6b24c4d4d74ad",size:1418663344,metrics:[{name:"der_teacher_greedy",value:24.79,source:"interscript/interscript-ml docs/RESULTS.md#heb-diac-small-1.0"},{name:"der_student_greedy",value:30.37,source:"interscript/interscript-ml docs/RESULTS.md#heb-diac-small-1.0"}],parity:{samples:1864,cer_delta:0},license:null},{id:"fas-g2p-1.0",task:"g2p",scripts:["Arab","Latn"],precision:"fp32",filename:"fas-g2p-1.0-fp32.zip",url:null,parts:[{url:"https://github.com/interscript/interscript-ml/releases/download/fas-g2p-1.0/fas-g2p-1.0-fp32.zip.part-00",sha256:"6c475e53f3c7ef3edcef5a24d2d81f51d13ddf4c417de98440943c9a32b03e55",size:15e8},{url:"https://github.com/interscript/interscript-ml/releases/download/fas-g2p-1.0/fas-g2p-1.0-fp32.zip.part-01",sha256:"c5656c649412f41e201c567e002514d31390b636d4650d9dcb6dfed1973fb3d5",size:1280254270}],sha256:"b53fd69196127efcc596935d24e135b3c897006872c1646c674e70f3c0d9a0e3",size:2780254270,metrics:[{name:"cer",value:1.6,source:"interscript/interscript-ml docs/RESULTS.md#fas-g2p-1.0"},{name:"sb_homograph_ezafe_norm",value:77.34,source:"interscript/interscript-ml docs/RESULTS.md#fas-g2p-1.0"}],parity:{samples:1e3,cer_delta:0},license:"BSD-3-Clause"},{id:"ara-diac-small-1.0",task:"diacritization",scripts:["Arab","Arab"],precision:"fp32",filename:"ara-diac-small-1.0-fp32.zip",url:"https://github.com/interscript/interscript-ml/releases/download/ara-diac-small-1.0/ara-diac-small-1.0-fp32.zip",parts:null,sha256:"8b149beb04294a2e482af876a34ae0fca3e87412606116fae1a376845fd4111a",size:1418126835,metrics:[{name:"der_teacher_greedy",value:1.3205,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_student_greedy",value:3.658,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_teacher_fullset",value:2.5815,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_student_fullset",value:8.259,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"}],parity:{samples:800,cer_delta:0},license:"BSD-3-Clause"},{id:"heb-diac-1.1",task:"diacritization",scripts:["Hebr","Hebr"],precision:"fp32",filename:"heb.zip",url:null,parts:[{url:"https://github.com/interscript/interscript-ml/releases/download/heb-diac-1.1/heb.zip.part-00",sha256:"8dac0d62e9b0ac4b137412eaf3a542953f11575f12f66f76cb93d3f5d788fc1b",size:15e8},{url:"https://github.com/interscript/interscript-ml/releases/download/heb-diac-1.1/heb.zip.part-01",sha256:"230c5ab731ea5f001f802d60b9e9a328c10b43bc311a19ef1974f114384313ed",size:1280987905}],sha256:"f0022dde8d06e46e8f08cf0b2f7bbb887a05ed686b90f1d5aa82ca3ef690a0b0",size:2780987905,metrics:[{name:"der_greedy",value:16.44,source:"rababa/docs/RESULTS.md#hebrew-diacritization"},{name:"der_beam4",value:16.43,source:"rababa/docs/RESULTS.md#hebrew-diacritization"}],parity:{samples:500,cer_delta:0},license:"BSD-3-Clause"},{id:"ara-diac-1.0",task:"diacritization",scripts:["Arab","Arab"],precision:"fp32",filename:"ara-diac-1.0-fp32.zip",url:null,parts:[{url:"https://github.com/interscript/interscript-ml/releases/download/ara-diac-1.0/ara-diac-1.0-fp32.zip.part-00",sha256:"6282a9291fb45ac7fcc3abe79d33b5913e91f87dcff8b305bf1d57ed98a3ed0b",size:15e8},{url:"https://github.com/interscript/interscript-ml/releases/download/ara-diac-1.0/ara-diac-1.0-fp32.zip.part-01",sha256:"7e7254a89ec75493871f6f61a993315097dad1bb0ba63c4584453c309e8cb906",size:1280311342}],sha256:"9e6d9777fe2474b150450774aaa1c9e8a9051b502b184fde298b91636dcfdac6",size:2780311342,metrics:[{name:"der_total_greedy",value:2.5793,source:"rababa/docs/RESULTS.md#r6-verdict-table-sadeeddiac-25-2026-08-21"},{name:"der_morph_greedy",value:1.5317,source:"rababa/docs/RESULTS.md#r6-verdict-table-sadeeddiac-25-2026-08-21"}],parity:{samples:500,cer_delta:0},license:"BSD-3-Clause"},{id:"ara-diac-small-1.0-int8",task:"diacritization",scripts:["Arab","Arab"],precision:"int8",filename:"ara-diac-small-1.0-int8.zip",url:"https://github.com/interscript/interscript-ml/releases/download/ara-diac-small-1.0/ara-diac-small-1.0-int8.zip",parts:null,sha256:"af247d837bee7e755b5287102cfb715c48dc90c2cf41ec1c41e7238e1a72b678",size:256959713,metrics:[{name:"der_teacher_greedy",value:1.3205,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_student_greedy",value:3.658,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_teacher_fullset",value:2.5815,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"},{name:"der_student_fullset",value:8.259,source:"interscript/interscript-ml docs/RESULTS.md#ara-diac-small-1.0"}],parity:{samples:800,cer_delta:0},license:"BSD-3-Clause"}]};var Ye=un;function be(){return Ye.models}function Xe(e){return Ye.models.find(t=>t.id===e)}var mn=Ye.version;var fn={openapi:"3.1.0",info:{title:"Interscript API",version:"1.0.0",description:"Transliteration and neural text processing. Maps run in-edge on the interscript-ts native interpreter (289 systems); neural models (diacritization, grapheme-to-phoneme) run on Interscript's inference service. GraphQL remains available at POST /graphql.",license:{name:"BSD-3-Clause"}},servers:[{url:"https://api.interscript.org"}],tags:[{name:"service"},{name:"maps"},{name:"transliteration"},{name:"models"},{name:"inference"}],paths:{"/":{get:{tags:["service"],summary:"Service index",responses:{200:{$ref:"#/components/responses/Ok"}}}},"/v1/info":{get:{tags:["service"],summary:"Version and capability summary",responses:{200:{$ref:"#/components/responses/Ok"}}}},"/v1/maps":{get:{tags:["maps"],summary:"Every bundled transliteration system code",responses:{200:{$ref:"#/components/responses/Ok"}}}},"/v1/maps/{code}":{get:{tags:["maps"],summary:"One system's compiled map (JSON IR)",parameters:[{name:"code",in:"path",required:!0,schema:{type:"string"}}],responses:{200:{$ref:"#/components/responses/Ok"},404:{$ref:"#/components/responses/NotFound"}}}},"/v1/transliterate":{post:{tags:["transliteration"],summary:"Transliterate text with a system",requestBody:{required:!0,content:{"application/json":{schema:{$ref:"#/components/schemas/TransliterateRequest"},examples:{gek:{value:{system:"ungegn-kor-Kore-Latn-1939",input:"\uD3C9\uC591"}}}}}},responses:{200:{description:"Transliterated output",content:{"application/json":{schema:{$ref:"#/components/schemas/TransliterateResponse"}}}},404:{$ref:"#/components/responses/NotFound"},413:{$ref:"#/components/responses/Error"}}}},"/v1/detect":{post:{tags:["transliteration"],summary:"Rank systems by how well they map input to output",requestBody:{required:!0,content:{"application/json":{schema:{$ref:"#/components/schemas/DetectRequest"}}}},responses:{200:{$ref:"#/components/responses/Ok"}}}},"/v1/models":{get:{tags:["models"],summary:"The neural model index (IMF v1)",responses:{200:{$ref:"#/components/responses/Ok"}}}},"/v1/models/{id}":{get:{tags:["models"],summary:"One model's full entry (metrics, parity, download)",parameters:[{name:"id",in:"path",required:!0,schema:{type:"string"}}],responses:{200:{$ref:"#/components/responses/Ok"},404:{$ref:"#/components/responses/NotFound"}}}},"/v1/infer":{post:{tags:["inference"],summary:"Run a neural model (diacritization or G2P)",description:"Proxy to Interscript's inference service. Only models whose task is diacritization or g2p are served; see GET /v1/models for the catalog.",requestBody:{required:!0,content:{"application/json":{schema:{$ref:"#/components/schemas/InferRequest"},examples:{hebrew:{value:{model:"heb-diac-1.0",input:"\u05E9\u05DC\u05D5\u05DD \u05E2\u05DC\u05D9\u05DB\u05DD"},summary:"Hebrew nikud restoration"},urdu:{value:{model:"urd-g2p-1.0",input:"\u0627\u0631\u062F\u0648"},summary:"Urdu grapheme-to-phoneme"}}}}},responses:{200:{description:"Model output",content:{"application/json":{schema:{$ref:"#/components/schemas/InferResponse"}}}},404:{$ref:"#/components/responses/NotFound"},502:{$ref:"#/components/responses/Error"},503:{$ref:"#/components/responses/Error"}}}}},components:{schemas:{Error:{type:"object",properties:{error:{type:"object",properties:{code:{type:"string"},message:{type:"string"}}}}},TransliterateRequest:{type:"object",required:["system","input"],properties:{system:{type:"string",description:"A system code from GET /v1/maps",examples:["ungegn-kor-Kore-Latn-1939"]},input:{type:"string",maxLength:1e6}}},TransliterateResponse:{type:"object",properties:{system:{type:"string"},input:{type:"string"},output:{type:"string"}}},DetectRequest:{type:"object",required:["input","output"],properties:{input:{type:"string"},output:{type:"string"}}},InferRequest:{type:"object",required:["model","input"],properties:{model:{type:"string",description:"A model id from GET /v1/models",examples:["heb-diac-1.0"]},input:{type:"string",maxLength:4e3}}},InferResponse:{type:"object",properties:{model:{type:"string"},task:{type:"string",enum:["diacritization","g2p"]},input:{type:"string"},output:{type:"string"}}}},responses:{Ok:{description:"Success",content:{"application/json":{schema:{type:"object"}}}},NotFound:{description:"Unknown system or model",content:{"application/json":{schema:{$ref:"#/components/schemas/Error"}}}},Error:{description:"Error",content:{"application/json":{schema:{$ref:"#/components/schemas/Error"}}}}}}};var mr=[{method:"GET",path:"/v1/info",note:"version + capability summary"},{method:"GET",path:"/v1/maps",note:"every transliteration system code"},{method:"GET",path:"/v1/maps/{code}",note:"one system's compiled map (JSON IR)"},{method:"POST",path:"/v1/transliterate",note:"{system, input} \u2192 {output}"},{method:"POST",path:"/v1/detect",note:"{input, output} \u2192 ranked systems"},{method:"GET",path:"/v1/models",note:"neural model index (IMF v1)"},{method:"GET",path:"/v1/models/{id}",note:"one model's metadata"},{method:"POST",path:"/v1/infer",note:"{model, input} \u2192 {output}"},{method:"POST",path:"/graphql",note:"GraphQL endpoint (introspection enabled)"},{method:"GET",path:"/openapi.json",note:"OpenAPI 3.1 document"}];function ye(e){return e.replace(/&/g,"&").replace(//g,">")}function Ze(e){return`
+
+
+
+
+Interscript API
+
+
+
+
+ Interscript API
+ Authority-backed transliteration for every script \u2014 REST v1 + GraphQL on the edge.
+
+
+ | Method | Endpoint | Behavior |
+
+${mr.map(n=>`| ${ye(n.method)} | ${ye(n.path)} | ${ye(n.note)} |
`).join(`
+`)}
+
+
+
+
+ Quick start
+ curl -X POST https://api.interscript.org/v1/transliterate \\
+ -H 'Content-Type: application/json' \\
+ -d '{"system":"alalc-ara-Arab-Latn-1997","input":"\u0627\u0644\u0633\u0644\u0627\u0645 \u0639\u0644\u064A\u0643\u0645"}'
+
+
+
+
+
+
+`}function h(e,t,n){return Response.json({error:{code:t,message:n}},{status:e})}var b=new fr,hr=e=>(e??"").toLowerCase().includes("text/html");b.get("/docs",e=>e.html(Ze("/openapi.json")));b.get("/",e=>hr(e.req.header("accept"))?e.html(Ze("/openapi.json")):e.json({name:"Interscript API",version:"v1",openapi:"/openapi.json",endpoints:["GET /v1/info","GET /v1/maps","GET /v1/maps/{code}","POST /v1/transliterate","POST /v1/detect","GET /v1/models","GET /v1/models/{id}","POST /v1/infer","POST /graphql"]}));b.get("/openapi.json",e=>e.json(fn));b.get("/v1/info",e=>e.json({api_version:"1.0.0",engine_version:fe,engine:"interscript-ts@native-interpreter",maps:_().length,models:be().length,models_index_version:mn,limits:D}));b.get("/v1/maps",e=>e.json({count:_().length,maps:_()}));b.get("/v1/maps/:code",async e=>{let t=e.env?.ASSETS;if(!t)return h(503,"assets_missing","ASSETS binding not configured");let n=e.req.param("code");if(!_().includes(n))return h(404,"map_not_found",`Couldn't locate ${n}`);let r=await t.fetch(`https://assets.internal/${n}.json`);return r.ok?new Response(r.body,{headers:{"content-type":"application/json; charset=utf-8"}}):h(404,"map_not_found",`Couldn't locate ${n}`)});b.post("/v1/transliterate",async e=>{let t=e.env?.ASSETS;if(!t)return h(503,"assets_missing","ASSETS binding not configured");Q(t);let n=await e.req.json().catch(()=>null);if(!n||typeof n!="object")return h(400,"bad_request","body must be JSON {system, input}");let{system:r,input:a}=n;if(typeof r!="string"||typeof a!="string")return h(400,"bad_request","system and input must be strings");try{let s=await he(r,a);return e.json({system:r,input:a,output:s})}catch(s){return s instanceof j?h(413,"input_too_long",s.message):s instanceof $?h(404,"map_not_found",s.message):h(500,"engine_error",String(s))}});b.post("/v1/detect",async e=>{let t=e.env?.ASSETS;if(!t)return h(503,"assets_missing","ASSETS binding not configured");Q(t);let n=await e.req.json().catch(()=>null),{input:r,output:a}=n??{};if(typeof r!="string"||typeof a!="string")return h(400,"bad_request","input and output must be strings");let s=await ge(r,a);return e.json({count:s.length,results:s})});b.get("/v1/models",e=>e.json({count:be().length,models:be()}));b.get("/v1/models/:id",e=>{let t=Xe(e.req.param("id"));return t?e.json(t):h(404,"model_not_found",`Couldn't locate ${e.req.param("id")}`)});b.post("/v1/infer",async e=>{let t=await e.req.json().catch(()=>null),{model:n,input:r}=t??{};if(typeof n!="string"||typeof r!="string")return h(400,"bad_request","body must be JSON {model, input}");if(!Xe(n))return h(404,"model_not_found",`Couldn't locate ${n}`);let{ML_ENDPOINT:a,ML_TOKEN:s}=e.env??{};if(!a||!s)return h(503,"inference_unconfigured","ML_ENDPOINT/ML_TOKEN are not set on this deployment");let o=await fetch(`${a}/infer`,{method:"POST",headers:{"content-type":"application/json","x-api-key":s},body:JSON.stringify({model:n,input:r}),signal:AbortSignal.timeout(ue)}).catch(()=>null);if(!o||!o.ok){let i=o?await o.text().catch(()=>""):"upstream unreachable";return h(502,"inference_upstream",i.slice(0,500))}let c=await o.json();return e.json({model:n,task:c.task,input:r,output:c.output})});var wr=gr({schema:br({typeDefs:Le,resolvers:{Query:{info:()=>pn(),systemCodes:()=>dn(),transliterate:(e,t)=>he(t.systemCode,t.input),detect:(e,t)=>ge(t.input,t.output)}}}),graphqlEndpoint:"*",landingPage:!1,maskedErrors:!1}),J=new yr;J.use("*",Lr({origin:"*",allowMethods:["GET","POST","OPTIONS"]}));J.route("/",b);J.all("*",async e=>{let t=e.env;t?.ASSETS&&Q(t.ASSETS),cn(t?.ML_ENDPOINT,t?.ML_TOKEN);let n;try{n=e.executionCtx}catch{n=void 0}let r=e.req.raw,a=(r.headers.get("content-type")??"").toLowerCase();if(r.method==="POST"&&!a.includes("application/json")){let s=await r.text(),o=!1;try{JSON.parse(s),o=!0}catch{}s.trim()&&!o&&(r=new Request(r.url,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:s})}))}return wr.fetch(r,e.env,n)});var Sr=J;export{fe as API_VERSION,D as LIMITS,J as app,Sr as default,Le as typeDefs};
diff --git a/dist/src/rest.js b/dist/src/rest.js
index 98c32e6..27ba8a1 100644
--- a/dist/src/rest.js
+++ b/dist/src/rest.js
@@ -22,26 +22,31 @@ import { INFER_TIMEOUT_MS, LIMITS } from "./limits.js";
import { bundledSystemCodes } from "./engine.js";
import { getModel, listModels, MODELS_INDEX_VERSION } from "./models.js";
import { OPENAPI } from "./openapi.js";
+import { docsPage } from "./docs.js";
function errorResponse(status, code, message) {
return Response.json({ error: { code, message } }, { status });
}
export const rest = new Hono();
-rest.get("/", (c) => c.json({
- name: "Interscript API",
- version: "v1",
- openapi: "/openapi.json",
- endpoints: [
- "GET /v1/info",
- "GET /v1/maps",
- "GET /v1/maps/{code}",
- "POST /v1/transliterate",
- "POST /v1/detect",
- "GET /v1/models",
- "GET /v1/models/{id}",
- "POST /v1/infer",
- "POST /graphql",
- ],
-}));
+const wantsHtml = (accept) => (accept ?? "").toLowerCase().includes("text/html");
+rest.get("/docs", (c) => c.html(docsPage("/openapi.json")));
+rest.get("/", (c) => wantsHtml(c.req.header("accept"))
+ ? c.html(docsPage("/openapi.json"))
+ : c.json({
+ name: "Interscript API",
+ version: "v1",
+ openapi: "/openapi.json",
+ endpoints: [
+ "GET /v1/info",
+ "GET /v1/maps",
+ "GET /v1/maps/{code}",
+ "POST /v1/transliterate",
+ "POST /v1/detect",
+ "GET /v1/models",
+ "GET /v1/models/{id}",
+ "POST /v1/infer",
+ "POST /graphql",
+ ],
+ }));
rest.get("/openapi.json", (c) => c.json(OPENAPI));
rest.get("/v1/info", (c) => c.json({
api_version: "1.0.0",
diff --git a/dist/test/rest.test.js b/dist/test/rest.test.js
index 08d8754..07f9e4d 100644
--- a/dist/test/rest.test.js
+++ b/dist/test/rest.test.js
@@ -34,6 +34,24 @@ describe("REST index + docs", () => {
const body = (await res.json());
expect(body.endpoints).toContain("POST /v1/infer");
});
+ it("GET / serves the docs page to browsers, JSON to API clients", async () => {
+ const html = await get("/", { accept: "text/html,application/xhtml+xml" });
+ expect(html.status).toBe(200);
+ expect(html.headers.get("content-type")).toContain("text/html");
+ const page = await html.text();
+ expect(page).toContain("");
+ expect(page).toMatch(/POST<\/span><\/td>\/v1\/transliterate/);
+ const json = await get("/");
+ expect(json.headers.get("content-type")).toContain("application/json");
+ });
+ it("GET /docs always serves the docs page", async () => {
+ const res = await get("/docs");
+ expect(res.status).toBe(200);
+ expect(res.headers.get("content-type")).toContain("text/html");
+ const page = await res.text();
+ expect(page).toContain("openapi.json");
+ expect(page).toContain("/graphql");
+ });
it("GET /openapi.json serves a 3.1 document covering all routes", async () => {
const res = await get("/openapi.json");
expect(res.status).toBe(200);
diff --git a/src/docs.ts b/src/docs.ts
new file mode 100644
index 0000000..ff53c98
--- /dev/null
+++ b/src/docs.ts
@@ -0,0 +1,93 @@
+/**
+ * The API's own webpage — served from the deployed worker at `/` (to
+ * browsers, via content negotiation) and at `/docs` (always). Deployed
+ * content belongs to the api.interscript.org deployment, not the main
+ * website. Self-contained HTML: no external assets, system font stack.
+ */
+
+const ENDPOINTS: { method: string; path: string; note: string }[] = [
+ { method: "GET", path: "/v1/info", note: "version + capability summary" },
+ { method: "GET", path: "/v1/maps", note: "every transliteration system code" },
+ { method: "GET", path: "/v1/maps/{code}", note: "one system's compiled map (JSON IR)" },
+ { method: "POST", path: "/v1/transliterate", note: "{system, input} → {output}" },
+ { method: "POST", path: "/v1/detect", note: "{input, output} → ranked systems" },
+ { method: "GET", path: "/v1/models", note: "neural model index (IMF v1)" },
+ { method: "GET", path: "/v1/models/{id}", note: "one model's metadata" },
+ { method: "POST", path: "/v1/infer", note: "{model, input} → {output}" },
+ { method: "POST", path: "/graphql", note: "GraphQL endpoint (introspection enabled)" },
+ { method: "GET", path: "/openapi.json", note: "OpenAPI 3.1 document" },
+]
+
+function esc(s: string): string {
+ return s.replace(/&/g, "&").replace(//g, ">")
+}
+
+export function docsPage(openapiPath: string): string {
+ const rows = ENDPOINTS.map(
+ (e) =>
+ `| ${esc(e.method)} | ${esc(e.path)} | ${esc(e.note)} | `,
+ ).join("\n")
+ return `
+
+
+
+
+Interscript API
+
+
+
+
+ Interscript API
+ Authority-backed transliteration for every script — REST v1 + GraphQL on the edge.
+
+
+ | Method | Endpoint | Behavior |
+
+${rows}
+
+
+
+
+ Quick start
+ curl -X POST https://api.interscript.org/v1/transliterate \\
+ -H 'Content-Type: application/json' \\
+ -d '{"system":"alalc-ara-Arab-Latn-1997","input":"السلام عليكم"}'
+
+
+
+
+
+
+`
+}
diff --git a/src/rest.ts b/src/rest.ts
index 5d3f308..cfb38be 100644
--- a/src/rest.ts
+++ b/src/rest.ts
@@ -28,6 +28,7 @@ import { INFER_TIMEOUT_MS, LIMITS } from "./limits.js"
import { bundledSystemCodes } from "./engine.js"
import { getModel, listModels, MODELS_INDEX_VERSION } from "./models.js"
import { OPENAPI } from "./openapi.js"
+import { docsPage } from "./docs.js"
function errorResponse(status: number, code: string, message: string) {
return Response.json({ error: { code, message } }, { status })
@@ -35,23 +36,30 @@ function errorResponse(status: number, code: string, message: string) {
export const rest = new Hono<{ Bindings: Env }>()
+const wantsHtml = (accept: string | undefined): boolean =>
+ (accept ?? "").toLowerCase().includes("text/html")
+
+rest.get("/docs", (c) => c.html(docsPage("/openapi.json")))
+
rest.get("/", (c) =>
- c.json({
- name: "Interscript API",
- version: "v1",
- openapi: "/openapi.json",
- endpoints: [
- "GET /v1/info",
- "GET /v1/maps",
- "GET /v1/maps/{code}",
- "POST /v1/transliterate",
- "POST /v1/detect",
- "GET /v1/models",
- "GET /v1/models/{id}",
- "POST /v1/infer",
- "POST /graphql",
- ],
- }),
+ wantsHtml(c.req.header("accept"))
+ ? c.html(docsPage("/openapi.json"))
+ : c.json({
+ name: "Interscript API",
+ version: "v1",
+ openapi: "/openapi.json",
+ endpoints: [
+ "GET /v1/info",
+ "GET /v1/maps",
+ "GET /v1/maps/{code}",
+ "POST /v1/transliterate",
+ "POST /v1/detect",
+ "GET /v1/models",
+ "GET /v1/models/{id}",
+ "POST /v1/infer",
+ "POST /graphql",
+ ],
+ }),
)
rest.get("/openapi.json", (c) => c.json(OPENAPI))
diff --git a/test/rest.test.ts b/test/rest.test.ts
index 1f6cc85..041cf7b 100644
--- a/test/rest.test.ts
+++ b/test/rest.test.ts
@@ -50,6 +50,27 @@ describe("REST index + docs", () => {
expect(body.endpoints).toContain("POST /v1/infer")
})
+ it("GET / serves the docs page to browsers, JSON to API clients", async () => {
+ const html = await get("/", { accept: "text/html,application/xhtml+xml" })
+ expect(html.status).toBe(200)
+ expect(html.headers.get("content-type")).toContain("text/html")
+ const page = await html.text()
+ expect(page).toContain("")
+ expect(page).toMatch(/POST<\/span><\/td>\/v1\/transliterate/)
+
+ const json = await get("/")
+ expect(json.headers.get("content-type")).toContain("application/json")
+ })
+
+ it("GET /docs always serves the docs page", async () => {
+ const res = await get("/docs")
+ expect(res.status).toBe(200)
+ expect(res.headers.get("content-type")).toContain("text/html")
+ const page = await res.text()
+ expect(page).toContain("openapi.json")
+ expect(page).toContain("/graphql")
+ })
+
it("GET /openapi.json serves a 3.1 document covering all routes", async () => {
const res = await get("/openapi.json")
expect(res.status).toBe(200)
| |