@willbooster/shared-lib 10.21.0 → 10.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/foreignCjk.cjs.map +1 -1
- package/dist/foreignCjk.js.map +1 -1
- package/dist/prompt.cjs.map +1 -1
- package/dist/prompt.js.map +1 -1
- package/dist/text.cjs.map +1 -1
- package/dist/text.js.map +1 -1
- package/dist/zenkaku.cjs.map +1 -1
- package/dist/zenkaku.js.map +1 -1
- package/package.json +20 -23
package/dist/foreignCjk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreignCjk.cjs","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <=
|
|
1
|
+
{"version":3,"file":"foreignCjk.cjs","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xea; lead++) {\n if (lead >= 0xa0 && lead <= 0xdf) continue;\n for (let trail = 0x40; trail <= 0xfc; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"aAEA,MAAMA,EAAe,qBACfC,EAAY,kBAiBZC,kBAAmC,IAAI,IAC3C,+RACF,EAEA,IAAIC,EAOJ,SAAgBC,EAA2B,EAAc,EAA4C,CACnG,IAAMC,EAAaC,EAAmCC,EAAMC,CAAW,EACjEC,EAAkC,CAAC,EAGzC,OAFIJ,EAAW,KAAM,GAAS,CAACL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,SAAS,EAC7EJ,EAAW,KAAM,GAASL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,QAAQ,EACxEA,CACT,CAYA,SAAgBH,EAAmC,EAAc,EAAc,GAAc,CAC3F,IAAMK,EAAoB,IAAI,IAAIH,EAAY,UAAU,MAAM,CAAC,EACzDI,EAAY,IAAkBC,EAAsB,EACpDR,kBAAa,IAAI,IACvB,IAAK,IAAMK,KAAQH,EAAK,UAAU,MAAM,EAClCI,EAAkB,IAAID,CAAI,IAE5BV,EAAa,KAAKU,CAAI,GACrBT,EAAU,KAAKS,CAAI,IAAM,CAACE,EAAS,IAAIF,CAAI,GAAKR,EAAiC,IAAIQ,CAAI,KAE1FL,EAAW,IAAIK,CAAI,EAGvB,MAAO,CAAC,GAAGL,CAAU,CACvB,CAEA,SAASQ,GAAqC,CAE5C,IAAMC,EAAU,IAAI,YAAY,WAAW,EACrCF,kBAAW,IAAI,IAAI,qIAAsE,EAC/F,IAAK,IAAIG,EAAO,IAAMA,GAAQ,IAAM,IAClC,GAAI,KAAQ,KAAQA,GAAQ,KAC5B,IAAK,IAAIC,EAAQ,GAAMA,GAAS,IAAM,IACpCJ,EAAS,IAAIE,EAAQ,OAAO,IAAI,WAAW,CAACC,EAAMC,CAAK,CAAC,CAAC,CAAC,EAG9D,OAAOJ,CACT"}
|
package/dist/foreignCjk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreignCjk.js","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <=
|
|
1
|
+
{"version":3,"file":"foreignCjk.js","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xea; lead++) {\n if (lead >= 0xa0 && lead <= 0xdf) continue;\n for (let trail = 0x40; trail <= 0xfc; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"AAEA,MAAMA,EAAe,qBACfC,EAAY,kBAiBZC,kBAAmC,IAAI,IAC3C,+RACF,EAEA,IAAIC,EAOJ,SAAgBC,EAA2B,EAAc,EAA4C,CACnG,IAAMC,EAAaC,EAAmCC,EAAMC,CAAW,EACjEC,EAAkC,CAAC,EAGzC,OAFIJ,EAAW,KAAM,GAAS,CAACL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,SAAS,EAC7EJ,EAAW,KAAM,GAASL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,QAAQ,EACxEA,CACT,CAYA,SAAgBH,EAAmC,EAAc,EAAc,GAAc,CAC3F,IAAMK,EAAoB,IAAI,IAAIH,EAAY,UAAU,MAAM,CAAC,EACzDI,EAAY,IAAkBC,EAAsB,EACpDR,kBAAa,IAAI,IACvB,IAAK,IAAMK,KAAQH,EAAK,UAAU,MAAM,EAClCI,EAAkB,IAAID,CAAI,IAE5BV,EAAa,KAAKU,CAAI,GACrBT,EAAU,KAAKS,CAAI,IAAM,CAACE,EAAS,IAAIF,CAAI,GAAKR,EAAiC,IAAIQ,CAAI,KAE1FL,EAAW,IAAIK,CAAI,EAGvB,MAAO,CAAC,GAAGL,CAAU,CACvB,CAEA,SAASQ,GAAqC,CAE5C,IAAMC,EAAU,IAAI,YAAY,WAAW,EACrCF,kBAAW,IAAI,IAAI,qIAAsE,EAC/F,IAAK,IAAIG,EAAO,IAAMA,GAAQ,IAAM,IAClC,GAAI,KAAQ,KAAQA,GAAQ,KAC5B,IAAK,IAAIC,EAAQ,GAAMA,GAAS,IAAM,IACpCJ,EAAS,IAAIE,EAAQ,OAAO,IAAI,WAAW,CAACC,EAAMC,CAAK,CAAC,CAAC,CAAC,EAG9D,OAAOJ,CACT"}
|
package/dist/prompt.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.cjs","names":["toTildeCodeBlock","escapeRegExp","sliceWithoutSplittingSurrogates","require_text","NON_STRING_SCALAR","UNSAFE_PLAIN_START","UNSAFE_PLAIN_MIDDLE","UNSAFE_PLAIN_END","SHORT_UNICODE_ESCAPES","serializeForPrompt","stringifyValue","toSerializable","value","serializeForPromptInTag","tagPatterns","tagName","createTagPatterns","escapePromptTag","text","escapeTags","name","pattern","patterns","BLOCK_OPENER","MISPLACED_BLOCK_OPENER","formatPrompt","lines","prompt","blocks","findBlocks","formatted","prose","index","line","block","blockLines","dedentPromptMarkers","nestedFenceLengths","backtickClosers","tildeClosers","match","opener","fence","closers","low","high","middle","end","lengths","maximumFenceLengthAt","recordFenceLength","maximum","node","left","start","right","length","replaceIndentedMarker","marker","truncateForPrompt","maxLength","stringifyNumber","stringifyString","ctx","stringifyObject","stringifyArray","stringifyEntries","lazyEntries","separator","itemCtx","str","item","key","entries","stringifyPair","record","indent","rawKey","rawValue","keyStr","valueStr","isCollection","doubleQuotedString","plainString","implicitKey","quotedString","blockString","containsDocumentMarker","endStart","isBlockEndWhitespace","endNewlinePos","chomp","leadingWhitespace","startsWithSpace","indentLines","singleQuotedString","json","i","ch","code","foldMultiline","codePoint"],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, sliceWithoutSplittingSurrogates, toTildeCodeBlock } from './text.js';\n\nconst INDENT_STEP = ' ';\nconst MAX_IMPLICIT_KEY_LENGTH = 1024;\nconst MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH = 40;\n\n/** Strings matching this would be read back as a non-string scalar (null, bool, int, or float), so they must be quoted. */\nconst NON_STRING_SCALAR =\n /^(?:~|[Nn]ull|NULL|[Tt]rue|TRUE|[Ff]alse|FALSE|0o[0-7]+|[-+]?[0-9]+|0x[0-9a-fA-F]+|[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN|[-+]?(?:\\.[0-9]+|[0-9]+(?:\\.[0-9]*)?)[eE][-+]?[0-9]+|[-+]?(?:\\.[0-9]+|[0-9]+\\.[0-9]*))?$/u;\n\n// A single-line string must be quoted if it matches any of these. Checking the start, the middle, and the end separately\n// is much faster on long strings than one regex whose anchored alternatives are retried at every position.\nconst UNSAFE_PLAIN_START = /^(?:[\\t ,[\\]{}#&*!|>'\"%@`]|[?-](?:[ \\t]|$))/u;\nconst UNSAFE_PLAIN_MIDDLE = /:[ \\t]|[ \\t]#/u;\nconst UNSAFE_PLAIN_END = /[\\t :]$/u;\n\nconst SHORT_UNICODE_ESCAPES: Record<string, string> = {\n '0000': String.raw`\\0`,\n '0007': String.raw`\\a`,\n '000b': String.raw`\\v`,\n '001b': String.raw`\\e`,\n};\n\ninterface Context {\n indent: string;\n implicitKey?: boolean;\n tagPatterns?: readonly RegExp[];\n}\n\n/**\n * Serializes data into a code block to embed in an LLM prompt, so that the LLM can read its contents.\n * Accepts JSON data (honoring `toJSON` such as that of `Date`) as well as `undefined`, `NaN`, `Infinity`, bigints,\n * `Map`s (including non-string keys), `Set`s and other iterables, `Error`s (with their name, message, cause, and own properties),\n * and `RegExp`s. Symbols and functions without `toJSON` throw. The output is not meant to be deserialized back into the original value.\n * The format inside the code block is an implementation detail and may change.\n */\nexport function serializeForPrompt(value: unknown): string {\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '' })}\\n`, 'yaml');\n}\n\n/**\n * Serializes data with `serializeForPrompt` for embedding inside a `<tagName>` element of a prompt, escaping that tag\n * in every string so that untrusted data cannot close the element and have the rest read as instructions.\n * The caller must wrap the result in the same tag.\n *\n * Only the named elements are protected, so a block nested inside other tagged elements takes every enclosing name.\n * Escaping the finished block instead is not an option: it would turn a plain scalar holding the escaped notation\n * into a YAML flow sequence, since the quoting of each scalar is decided as it is written.\n *\n * The escaped notation is the one `escapePromptTag` writes, and text that already holds it is left as it is, so two\n * strings differing only in that notation are written alike. Keys taken from untrusted data can therefore collide\n * into one duplicate key of the emitted mapping.\n */\nexport function serializeForPromptInTag(value: unknown, tagName: string | readonly string[]): string {\n const escapedTags = typeof tagName === 'string' ? [tagName] : tagName;\n const tagPatterns = escapedTags.flatMap(createTagPatterns);\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '', tagPatterns })}\\n`, 'yaml');\n}\n\n/**\n * Replaces `<tagName` and `</tagName` not followed by `[\\w-]` with a harmless notation such as `[/tagName]`, so that\n * data embedded in a `<tagName>` element of a prompt cannot close the element and have the rest read as instructions.\n * A closing `>` is not required, since an HTML reader also closes an element on a tag carrying attribute-like junk,\n * on a trailing solidus, or on the next `>` anywhere in the prompt; the whitespace between `<` and the name is\n * dropped along with the brackets. Case is ignored deliberately: `tagName` is expected to be a literal written by\n * the caller, while the data is not.\n */\nexport function escapePromptTag(text: string, tagName: string): string {\n return text.includes('<') ? escapeTags(text, createTagPatterns(tagName)) : text;\n}\n\nfunction createTagPatterns(tagName: string): RegExp[] {\n const name = escapeRegExp(tagName);\n // Keep well-formed tags readable while also catching incomplete and malformed tags.\n return [\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})\\\\s*>`, 'giu'),\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})(?![\\\\w-])`, 'giu'),\n ];\n}\n\nfunction escapeTags(text: string, patterns: readonly RegExp[]): string {\n if (!text.includes('<')) return text;\n for (const pattern of patterns) text = text.replaceAll(pattern, '[$1$2]');\n return text;\n}\n\n/** The line opening a fenced block, with the indentation an interpolation may put before it. */\nconst BLOCK_OPENER = /^[ \\t]*((`{3,}|~{3,})[^\\n]*)$/u;\n\n/** A block interpolated after other text on its line, which no rule can tell from prose that ends in `~~~yaml`. */\nconst MISPLACED_BLOCK_OPENER = /[^\\s~][ \\t]*~{3,}yaml[ \\t]*$/u;\n\ninterface PromptBlock {\n opener: string;\n end: number;\n}\n\n/**\n * Strips template-literal indentation from Markdown headings, code fences, and triple-quote delimiters (`\"\"\"` and\n * `'''`), and collapses blank lines.\n * The contents of a fenced block are left untouched, since reindenting them would rewrite the data they carry, such\n * as the YAML of `serializeForPrompt` or the source of `toCodeBlock`. Every fenced block must be interpolated at\n * the start of a line. Only recognizable mid-line `~~~yaml` markers throw, including prose mentions;\n * this is not a complete placement validator, since preceding tildes can merge into an indistinguishable fence.\n * Blocks end at the first compatible same-character fence, which may carry trailing prose, unless a longer fence\n * of the same character opens and contains that candidate closing line. Other fence characters have no nesting\n * significance. Balance authored fences before interpolating blocks: an unmatched sample can consume part of a\n * later block and leave its tail subject to prose formatting.\n */\nexport function formatPrompt(prompt: string): string {\n const lines = prompt.split('\\n');\n const blocks = findBlocks(lines);\n let formatted = '';\n let prose = '';\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? '';\n const block = blocks[index];\n if (block === undefined) {\n // An opening fence the prompt itself writes and never closes is prose, and so is every line inside a block.\n if (MISPLACED_BLOCK_OPENER.test(line)) {\n throw new TypeError('Interpolate a serializeForPrompt block at the start of a line');\n }\n prose += `${line}\\n`;\n continue;\n }\n // Dropping the opener's indentation keeps four spaces of it from turning the fence into an indented code block.\n const blockLines = lines.slice(index + 1, block.end + 1);\n blockLines[blockLines.length - 1] = blockLines.at(-1)?.trimStart() ?? '';\n formatted += `${dedentPromptMarkers(prose)}${block.opener}\\n${blockLines.join('\\n')}`;\n prose = '\\n';\n index = block.end;\n }\n return (formatted + dedentPromptMarkers(prose)).trim();\n}\n\nfunction findBlocks(lines: string[]): (PromptBlock | undefined)[] {\n const blocks: (PromptBlock | undefined)[] = [];\n const nestedFenceLengths = new Map<string, Uint32Array>();\n const backtickClosers: { length: number; index: number }[] = [];\n const tildeClosers: { length: number; index: number }[] = [];\n for (let index = lines.length - 1; index >= 0; index--) {\n const match = BLOCK_OPENER.exec(lines[index] ?? '');\n const opener = match?.[1]?.trimEnd();\n const fence = match?.[2];\n if (opener === undefined || fence === undefined) continue;\n const closers = fence[0] === '`' ? backtickClosers : tildeClosers;\n let low = 0;\n let high = closers.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if ((closers[middle]?.length ?? 0) >= fence.length) low = middle + 1;\n else high = middle;\n }\n let end = closers[low - 1]?.index ?? -1;\n if (/^(?:[ \\t].*)?$/u.test((match?.[1] ?? '').slice(fence.length))) {\n // A nearer, longer closer also closes every fence that a shorter one could close.\n while (closers.length > 0 && (closers.at(-1)?.length ?? 0) <= fence.length) closers.pop();\n closers.push({ length: fence.length, index });\n }\n let lengths = nestedFenceLengths.get(fence[0] ?? '');\n if (end !== -1 && lengths !== undefined && maximumFenceLengthAt(lengths, end) > fence.length) end = -1;\n if (end !== -1) {\n blocks[index] = { opener, end };\n if (lengths === undefined) {\n lengths = new Uint32Array(lines.length * 2);\n nestedFenceLengths.set(fence[0] ?? '', lengths);\n }\n recordFenceLength(lengths, index + 1, end, fence.length);\n }\n }\n return blocks;\n}\n\nfunction maximumFenceLengthAt(lengths: Uint32Array, index: number): number {\n let maximum = 0;\n for (let node = index + lengths.length / 2; node > 0; node >>>= 1) {\n maximum = Math.max(maximum, lengths[node] ?? 0);\n }\n return maximum;\n}\n\nfunction recordFenceLength(lengths: Uint32Array, start: number, end: number, length: number): void {\n let left = start + lengths.length / 2;\n let right = end + lengths.length / 2;\n while (left <= right) {\n if (left % 2 === 1) {\n lengths[left] = Math.max(lengths[left] ?? 0, length);\n left++;\n }\n if (right % 2 === 0) {\n lengths[right] = Math.max(lengths[right] ?? 0, length);\n right--;\n }\n left >>>= 1;\n right >>>= 1;\n }\n}\n\nfunction dedentPromptMarkers(text: string): string {\n // Consume failed marker searches, and start blank-line searches only at whitespace-run boundaries.\n return text\n .replaceAll(/\\n\\s+(\"\"\"|'''|```|~{3,})|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/\\n\\s+(#+\\s)|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/(?<!\\s)\\s*\\n\\s*\\n/gu, '\\n\\n');\n}\n\nfunction replaceIndentedMarker(match: string, marker: string | undefined): string {\n return marker === undefined ? match : `\\n${marker}`;\n}\n\n/** Cuts text down to `maxLength` characters, marking it so that an LLM reads the rest as missing rather than absent. */\nexport function truncateForPrompt(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${sliceWithoutSplittingSurrogates(text, maxLength)}\\n...<truncated>`;\n}\n\n// The functions below reproduce `stringify(value, { lineWidth: 0, aliasDuplicateObjects: false, blockQuote: 'literal' })`\n// of the `yaml` package, except that `toSerializable` makes `Error`s and `RegExp`s readable instead of `{}`.\n\nfunction stringifyValue(value: unknown, ctx: Context): string {\n switch (typeof value) {\n case 'undefined': {\n return 'null';\n }\n case 'boolean': {\n return String(value);\n }\n case 'number': {\n return stringifyNumber(value);\n }\n case 'bigint': {\n return String(value);\n }\n case 'string': {\n return stringifyString(value, ctx);\n }\n case 'object': {\n return stringifyObject(value, ctx);\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\n}\n\nfunction stringifyObject(value: object | null, ctx: Context): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return stringifyArray(value, ctx);\n return stringifyEntries(value instanceof Map ? value : lazyEntries(value as Record<string, unknown>), ctx);\n}\n\nfunction stringifyArray(value: unknown[], ctx: Context): string {\n if (value.length === 0) return '[]';\n const separator = `\\n${ctx.indent}`;\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n let str = '';\n for (const item of value) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n}\n\nfunction stringifyEntries(entries: Iterable<[unknown, unknown]>, ctx: Context): string {\n const separator = `\\n${ctx.indent}`;\n let str = '';\n for (const [key, item] of entries) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n}\n\n// Reads each property only when its turn comes, as serializing an earlier one may run a `toJSON` that mutates the record.\nfunction* lazyEntries(record: Record<string, unknown>): Generator<[unknown, unknown]> {\n for (const key of Object.keys(record)) yield [key, record[key]];\n}\n\nfunction stringifyPair(rawKey: unknown, rawValue: unknown, ctx: Context): string {\n const indent = ctx.indent + INDENT_STEP;\n const key = toSerializable(rawKey);\n const value = toSerializable(rawValue);\n const keyStr = stringifyValue(key, { indent, implicitKey: true, tagPatterns: ctx.tagPatterns });\n const valueStr = stringifyValue(value, { indent, tagPatterns: ctx.tagPatterns });\n if (isCollection(key) || keyStr.length > MAX_IMPLICIT_KEY_LENGTH) return `? ${keyStr}\\n${ctx.indent}: ${valueStr}`;\n const isBlockCollection = isCollection(value) && valueStr !== '[]' && valueStr !== '{}';\n return `${keyStr}:${isBlockCollection ? `\\n${indent}` : ' '}${valueStr}`;\n}\n\nfunction toSerializable(rawValue: unknown): unknown {\n // Like `yaml`, call `toJSON` only once, but classify its result.\n const value =\n typeof (rawValue as { toJSON?: unknown } | null | undefined)?.toJSON === 'function'\n ? (rawValue as { toJSON: () => unknown }).toJSON()\n : rawValue;\n if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Map) return value;\n // oxlint-disable-next-line unicorn/no-instanceof-builtins -- boxed primitives have no typeof check; `yaml` unwraps them the same way.\n if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof BigInt) {\n return value.valueOf();\n }\n if (value instanceof Error) {\n // `name` and `message` usually live on the prototype, and `cause` and `errors` are not enumerable.\n return {\n name: value.name,\n message: value.message,\n ...Object.fromEntries(Object.entries(value)),\n ...('cause' in value && { cause: value.cause }),\n ...(value instanceof AggregateError && { errors: value.errors }),\n };\n }\n if (value instanceof RegExp) return String(value);\n return Symbol.iterator in value ? [...(value as Iterable<unknown>)] : value;\n}\n\nfunction isCollection(value: unknown): value is object {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringifyNumber(value: number): string {\n if (Number.isNaN(value)) return '.nan';\n if (!Number.isFinite(value)) return value < 0 ? '-.inf' : '.inf';\n return Object.is(value, -0) ? '-0' : JSON.stringify(value);\n}\n\nfunction stringifyString(rawValue: string, ctx: Context): string {\n const value = ctx.tagPatterns === undefined ? rawValue : escapeTags(rawValue, ctx.tagPatterns);\n // A closing quote separates an unfinished tag prefix from the next mapping key.\n if (ctx.tagPatterns?.length && /<\\s*(?:\\/\\s*)?$/u.test(value)) return doubleQuotedString(value, ctx, false);\n // oxlint-disable-next-line no-control-regex -- control characters and lone surrogates can only be written escaped.\n return /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F\\u{D800}-\\u{DFFF}]/u.test(value)\n ? doubleQuotedString(value, ctx)\n : plainString(value, ctx);\n}\n\nfunction plainString(value: string, ctx: Context): string {\n const { implicitKey, indent } = ctx;\n if (value.includes('\\n')) return implicitKey ? quotedString(value, ctx) : blockString(value, ctx);\n if (UNSAFE_PLAIN_START.test(value) || UNSAFE_PLAIN_END.test(value.slice(-1)) || UNSAFE_PLAIN_MIDDLE.test(value)) {\n return quotedString(value, ctx);\n }\n if (containsDocumentMarker(value)) {\n if (indent === '') return blockString(value, ctx);\n if (implicitKey && indent === INDENT_STEP) return quotedString(value, ctx);\n }\n return NON_STRING_SCALAR.test(value) ? quotedString(value, ctx) : value;\n}\n\nfunction blockString(value: string, ctx: Context): string {\n let endStart = value.length;\n while (endStart > 0 && isBlockEndWhitespace(value.codePointAt(endStart - 1))) endStart--;\n let end = value.slice(endStart);\n // A block scalar cannot end with a whitespace-only line.\n if (end.includes('\\n') && !end.endsWith('\\n')) return quotedString(value, ctx);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n\n const endNewlinePos = end.indexOf('\\n');\n const chomp = endNewlinePos === -1 ? '-' : value === end || endNewlinePos !== end.length - 1 ? '+' : '';\n if (end) {\n value = value.slice(0, -end.length);\n if (end.endsWith('\\n')) end = end.slice(0, -1);\n end = end.replaceAll(/(?:^|(?<!\\n))\\n+(?!\\n|$)/gu, `$&${indent}`);\n }\n\n const leadingWhitespace = /^[\\n ]*/u.exec(value)?.[0] ?? '';\n const startsWithSpace = leadingWhitespace.includes(' ');\n let start = value.slice(0, leadingWhitespace.lastIndexOf('\\n') + 1);\n if (start) {\n value = value.slice(start.length);\n start = start.replaceAll(/\\n+/gu, `$&${indent}`);\n }\n\n const header = (startsWithSpace ? (indent ? '2' : '1') : '') + chomp;\n return `|${header}\\n${indent}${start}${indentLines(value, indent)}${end}`;\n}\n\nfunction quotedString(value: string, ctx: Context): string {\n return value.includes('\"') && !value.includes(\"'\") ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx);\n}\n\nfunction singleQuotedString(value: string, ctx: Context): string {\n // Every multi-line value reaching here would contain a space or tab next to a newline or be an implicit key,\n // both of which need double quotes.\n if (value.includes('\\n')) return doubleQuotedString(value, ctx);\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction doubleQuotedString(value: string, ctx: Context, foldMultiline = !ctx.implicitKey): string {\n const json = JSON.stringify(value);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n let str = '';\n let start = 0;\n for (let i = 0; i < json.length; i++) {\n let ch = json[i];\n if (ch === ' ' && json[i + 1] === '\\\\' && json[i + 2] === 'n') {\n // A space before a line break would be folded away unless escaped.\n str += `${json.slice(start, i)}\\\\ `;\n i++;\n start = i;\n ch = '\\\\';\n }\n if (ch !== '\\\\') continue;\n if (json[i + 1] === 'u') {\n const code = json.slice(i + 2, i + 6);\n str +=\n json.slice(start, i) +\n (SHORT_UNICODE_ESCAPES[code] ?? (code.startsWith('00') ? `\\\\x${code.slice(2)}` : json.slice(i, i + 6)));\n i += 5;\n start = i + 1;\n } else if (\n json[i + 1] === 'n' &&\n foldMultiline &&\n json[i + 2] !== '\"' &&\n json.length >= MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH\n ) {\n // Folding turns the first of the written line breaks back into a space, hence the extra one.\n str += `${json.slice(start, i)}\\n\\n`;\n while (json[i + 2] === '\\\\' && json[i + 3] === 'n' && json[i + 4] !== '\"') {\n str += '\\n';\n i += 2;\n }\n str += indent;\n // A space after a line break would be folded away unless escaped.\n if (json[i + 2] === ' ') str += '\\\\';\n i++;\n start = i + 1;\n } else {\n i++;\n }\n }\n return start ? str + json.slice(start) : json;\n}\n\nfunction containsDocumentMarker(value: string): boolean {\n return /^(?:%|---|\\.\\.\\.)/mu.test(value);\n}\n\n/** Equivalent to `text.replaceAll(/\\n+/g, `$&${indent}`)` for text without leading or trailing newlines, but faster. */\nfunction indentLines(text: string, indent: string): string {\n const lines = text.split('\\n');\n let str = lines[0] ?? '';\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n str += line ? `\\n${indent}${line}` : '\\n';\n }\n return str;\n}\n\nfunction isBlockEndWhitespace(codePoint: number | undefined): boolean {\n return codePoint === 0x0A || codePoint === 0x09 || codePoint === 0x20;\n}\n"],"mappings":"2CAOMI,EACJ,wNAIIC,EAAqB,+CACrBC,EAAsB,iBACtBC,EAAmB,WAEnBC,EAAgD,CACpD,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,IACpB,EAeA,SAAgBC,EAAmB,EAAwB,CACzD,OAAOT,EAAAA,iBAAiB,GAAGU,EAAeC,EAAeC,CAAK,EAAG,CAAE,OAAQ,EAAG,CAAC,EAAE,IAAK,MAAM,CAC9F,CAeA,SAAgBC,EAAwB,EAAgB,EAA6C,CAEnG,IAAMC,GADc,OAAOC,GAAY,SAAW,CAACA,CAAO,EAAIA,EAAA,CAC9B,QAAQC,CAAiB,EACzD,OAAOhB,EAAAA,iBAAiB,GAAGU,EAAeC,EAAeC,CAAK,EAAG,CAAE,OAAQ,GAAI,aAAY,CAAC,EAAE,IAAK,MAAM,CAC3G,CAUA,SAAgBK,EAAgB,EAAc,EAAyB,CACrE,OAAOC,EAAK,SAAS,GAAG,EAAIC,EAAWD,EAAMF,EAAkBD,CAAO,CAAC,EAAIG,CAC7E,CAEA,SAASF,EAAkB,EAA2B,CACpD,IAAMI,EAAOnB,EAAAA,aAAac,CAAO,EAEjC,MAAO,CACD,OAAO,qBAAqBK,EAAK,QAAS,KAAK,EAC/C,OAAO,qBAAqBA,EAAK,aAAc,KAAK,CAC1D,CACF,CAEA,SAASD,EAAW,EAAc,EAAqC,CACrE,GAAI,CAACD,EAAK,SAAS,GAAG,EAAG,OAAOA,EAChC,IAAK,IAAMG,KAAWC,EAAU,EAAOJ,EAAK,WAAWG,EAAS,QAAQ,EACxE,OAAOH,CACT,CAGA,MAAMK,EAAe,iCAGfC,EAAyB,gCAmB/B,SAAgBC,EAAa,EAAwB,CACnD,IAAMC,EAAQC,EAAO,MAAM;CAAI,EACzBC,EAASC,EAAWH,CAAK,EAC3BI,EAAY,GACZC,EAAQ,GACZ,IAAK,IAAIC,EAAQ,EAAGA,EAAQN,EAAM,OAAQ,IAAS,CACjD,IAAMO,EAAOP,EAAMM,IAAU,GACvBE,EAAQN,EAAOI,GACrB,GAAIE,IAAU,IAAA,GAAW,CAEvB,GAAIV,EAAuB,KAAKS,CAAI,EAClC,MAAU,UAAU,+DAA+D,EAErF,GAAS,GAAGA,EAAK,IACjB,QACF,CAEA,IAAME,EAAaT,EAAM,MAAMM,EAAQ,EAAGE,EAAM,IAAM,CAAC,EACvD,EAAWC,EAAW,OAAS,GAAKA,EAAW,GAAG,EAAE,CAAC,EAAE,UAAU,GAAK,GACtE,GAAa,GAAGC,EAAoBL,CAAK,IAAIG,EAAM,OAAO,IAAIC,EAAW,KAAK;CAAI,IAClF,EAAQ;EACR,EAAQD,EAAM,GAChB,CACA,OAAQJ,EAAYM,EAAoBL,CAAK,EAAA,CAAG,KAAK,CACvD,CAEA,SAASF,EAAW,EAA8C,CAChE,IAAMD,EAAsC,CAAC,EACvCS,kBAAqB,IAAI,IACzBC,EAAuD,CAAC,EACxDC,EAAoD,CAAC,EAC3D,IAAK,IAAIP,EAAQN,EAAM,OAAS,EAAGM,GAAS,EAAG,IAAS,CACtD,IAAMQ,EAAQjB,EAAa,KAAKG,EAAMM,IAAU,EAAE,EAC5CS,EAASD,IAAQ,EAAE,EAAE,QAAQ,EAC7BE,EAAQF,IAAQ,GACtB,GAAIC,IAAW,IAAA,IAAaC,IAAU,IAAA,GAAW,SACjD,IAAMC,EAAUD,EAAM,KAAO,IAAMJ,EAAkBC,EACjDK,EAAM,EACNC,EAAOF,EAAQ,OACnB,KAAOC,EAAMC,GAAM,CACjB,IAAMC,EAAUF,EAAMC,IAAU,GAC3BF,EAAQG,EAAO,EAAE,QAAU,IAAMJ,EAAM,OAAQ,EAAMI,EAAS,EAC9D,EAAOA,CACd,CACA,IAAIC,EAAMJ,EAAQC,EAAM,EAAE,EAAE,OAAS,GACrC,GAAI,kBAAkB,MAAMJ,IAAQ,IAAM,GAAA,CAAI,MAAME,EAAM,MAAM,CAAC,EAAG,CAElE,KAAOC,EAAQ,OAAS,IAAMA,EAAQ,GAAG,EAAE,CAAC,EAAE,QAAU,IAAMD,EAAM,QAAQC,EAAQ,IAAI,EACxFA,EAAQ,KAAK,CAAE,OAAQD,EAAM,OAAQ,OAAM,CAAC,CAC9C,CACA,IAAIM,EAAUX,EAAmB,IAAIK,EAAM,IAAM,EAAE,EAC/CK,IAAQ,IAAMC,IAAY,IAAA,IAAaC,EAAqBD,EAASD,CAAG,EAAIL,EAAM,SAAQ,EAAM,IAChGK,IAAQ,KACV,EAAOf,GAAS,CAAE,SAAQ,KAAI,EAC1BgB,IAAY,IAAA,KACd,EAAU,IAAI,YAAYtB,EAAM,OAAS,CAAC,EAC1CW,EAAmB,IAAIK,EAAM,IAAM,GAAIM,CAAO,GAEhDE,EAAkBF,EAAShB,EAAQ,EAAGe,EAAKL,EAAM,MAAM,EAE3D,CACA,OAAOd,CACT,CAEA,SAASqB,EAAqB,EAAsB,EAAuB,CACzE,IAAIE,EAAU,EACd,IAAK,IAAIC,EAAOpB,EAAQgB,EAAQ,OAAS,EAAGI,EAAO,EAAG,KAAU,EAC9D,EAAU,KAAK,IAAID,EAASH,EAAQI,IAAS,CAAC,EAEhD,OAAOD,CACT,CAEA,SAASD,EAAkB,EAAsB,EAAe,EAAa,EAAsB,CACjG,IAAIG,EAAOC,EAAQN,EAAQ,OAAS,EAChCO,EAAQR,EAAMC,EAAQ,OAAS,EACnC,KAAOK,GAAQE,GACTF,EAAO,GAAM,IACf,EAAQA,GAAQ,KAAK,IAAIL,EAAQK,IAAS,EAAGG,CAAM,EACnD,KAEED,EAAQ,GAAM,IAChB,EAAQA,GAAS,KAAK,IAAIP,EAAQO,IAAU,EAAGC,CAAM,EACrD,KAEF,KAAU,EACV,KAAW,CAEf,CAEA,SAASpB,EAAoB,EAAsB,CAEjD,OAAOlB,EACJ,WAAW,mCAAoCuC,CAAqB,CAAC,CACrE,WAAW,sBAAuBA,CAAqB,CAAC,CACxD,WAAW,sBAAuB;;CAAM,CAC7C,CAEA,SAASA,EAAsB,EAAe,EAAoC,CAChF,OAAOC,IAAW,IAAA,GAAYlB,EAAQ,KAAKkB,GAC7C,CAGA,SAAgBC,EAAkB,EAAc,EAA2B,CAEzE,OADIzC,EAAK,QAAU0C,EAAkB1C,EAC9B,GAAGhB,EAAAA,gCAAgCgB,EAAM0C,CAAS,EAAE,iBAC7D,CAKA,SAASlD,EAAe,EAAgB,EAAsB,CAC5D,OAAQ,OAAOE,EAAf,CACE,IAAK,YACH,MAAO,OAET,IAAK,UACH,OAAO,OAAOA,CAAK,EAErB,IAAK,SACH,OAAOiD,EAAgBjD,CAAK,EAE9B,IAAK,SACH,OAAO,OAAOA,CAAK,EAErB,IAAK,SACH,OAAOkD,EAAgBlD,EAAOmD,CAAG,EAEnC,IAAK,SACH,OAAOC,EAAgBpD,EAAOmD,CAAG,EAEnC,QACE,MAAU,UAAU,sBAAsB,OAAOnD,EAAM,oBAAoB,CAE/E,CACF,CAEA,SAASoD,EAAgB,EAAsB,EAAsB,CAGnE,OAFIpD,IAAU,KAAa,OACvB,MAAM,QAAQA,CAAK,EAAUqD,EAAerD,EAAOmD,CAAG,EACnDG,EAAiBtD,aAAiB,IAAMA,EAAQuD,EAAYvD,CAAgC,EAAGmD,CAAG,CAC3G,CAEA,SAASE,EAAe,EAAkB,EAAsB,CAC9D,GAAIrD,EAAM,SAAW,EAAG,MAAO,KAC/B,IAAMwD,EAAY,KAAKL,EAAI,SACrBM,EAAU,CAAE,OAAQN,EAAI,OAAS,KAAa,YAAaA,EAAI,WAAY,EAC7EO,EAAM,GACV,IAAK,IAAMC,KAAQ3D,EACjB,GAAO,GAAG0D,EAAMF,EAAY,GAAG,IAAI1D,EAAeC,EAAe4D,CAAI,EAAGF,CAAO,IAEjF,OAAOC,CACT,CAEA,SAASJ,EAAiB,EAAuC,EAAsB,CACrF,IAAME,EAAY,KAAKL,EAAI,SACvBO,EAAM,GACV,IAAK,GAAM,CAACE,EAAKD,KAASE,EACpBF,IAAS,IAAA,KAAW,GAAO,GAAGD,EAAMF,EAAY,KAAKM,EAAcF,EAAKD,EAAMR,CAAG,KAEvF,OAAOO,GAAO,IAChB,CAGA,SAAUH,EAAY,EAAgE,CACpF,IAAK,IAAMK,KAAO,OAAO,KAAKG,CAAM,EAAG,KAAM,CAACH,EAAKG,EAAOH,EAAI,CAChE,CAEA,SAASE,EAAc,EAAiB,EAAmB,EAAsB,CAC/E,IAAME,EAASb,EAAI,OAAS,KACtBS,EAAM7D,EAAekE,CAAM,EAC3BjE,EAAQD,EAAemE,CAAQ,EAC/BC,EAASrE,EAAe8D,EAAK,CAAE,SAAQ,YAAa,GAAM,YAAaT,EAAI,WAAY,CAAC,EACxFiB,EAAWtE,EAAeE,EAAO,CAAE,SAAQ,YAAamD,EAAI,WAAY,CAAC,EAG/E,OAFIkB,EAAaT,CAAG,GAAKO,EAAO,OAAS,KAAgC,KAAKA,EAAO,IAAIhB,EAAI,OAAO,IAAIiB,IAEjG,GAAGD,EAAO,GADSE,EAAarE,CAAK,GAAKoE,IAAa,MAAQA,IAAa,KAC3C,KAAKJ,IAAW,MAAMI,GAChE,CAEA,SAASrE,EAAe,EAA4B,CAElD,IAAMC,EACJ,OAAQkE,GAAsD,QAAW,WACpEA,EAAuC,OAAO,EAC/CA,EAiBN,OAhBI,OAAOlE,GAAU,WAAYA,GAAkB,MAAM,QAAQA,CAAK,GAAKA,aAAiB,IAAYA,EAEpGA,aAAiB,QAAUA,aAAiB,QAAUA,aAAiB,SAAWA,aAAiB,OAC9FA,EAAM,QAAQ,EAEnBA,aAAiB,MAEZ,CACL,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,GAAG,OAAO,YAAY,OAAO,QAAQA,CAAK,CAAC,EAC3C,GAAI,UAAWA,GAAS,CAAE,MAAOA,EAAM,KAAM,EAC7C,GAAIA,aAAiB,gBAAkB,CAAE,OAAQA,EAAM,MAAO,CAChE,EAEEA,aAAiB,OAAe,OAAOA,CAAK,EACzC,OAAO,YAAYA,EAAQ,CAAC,GAAIA,CAA2B,EAAIA,CACxE,CAEA,SAASqE,EAAa,EAAiC,CACrD,OAAO,OAAOrE,GAAU,YAAYA,CACtC,CAEA,SAASiD,EAAgB,EAAuB,CAG9C,OAFI,OAAO,MAAMjD,CAAK,EAAU,OAC3B,OAAO,SAASA,CAAK,EACnB,OAAO,GAAGA,EAAO,EAAE,EAAI,KAAO,KAAK,UAAUA,CAAK,EADrBA,EAAQ,EAAI,QAAU,MAE5D,CAEA,SAASkD,EAAgB,EAAkB,EAAsB,CAC/D,IAAMlD,EAAQmD,EAAI,cAAgB,IAAA,GAAYe,EAAW3D,EAAW2D,EAAUf,EAAI,WAAW,EAI7F,OAFIA,EAAI,aAAa,QAAU,mBAAmB,KAAKnD,CAAK,EAAUsE,EAAmBtE,EAAOmD,EAAK,EAAK,EAEnG,8DAA8D,KAAKnD,CAAK,EAC3EsE,EAAmBtE,EAAOmD,CAAG,EAC7BoB,EAAYvE,EAAOmD,CAAG,CAC5B,CAEA,SAASoB,EAAY,EAAe,EAAsB,CACxD,GAAM,CAAE,cAAa,UAAWpB,EAChC,GAAInD,EAAM,SAAS;CAAI,EAAG,OAAOwE,EAAcC,EAAazE,EAAOmD,CAAG,EAAIuB,EAAY1E,EAAOmD,CAAG,EAChG,GAAI1D,EAAmB,KAAKO,CAAK,GAAKL,EAAiB,KAAKK,EAAM,MAAM,EAAE,CAAC,GAAKN,EAAoB,KAAKM,CAAK,EAC5G,OAAOyE,EAAazE,EAAOmD,CAAG,EAEhC,GAAIwB,EAAuB3E,CAAK,EAAG,CACjC,GAAIgE,IAAW,GAAI,OAAOU,EAAY1E,EAAOmD,CAAG,EAChD,GAAIqB,GAAeR,IAAW,KAAa,OAAOS,EAAazE,EAAOmD,CAAG,CAC3E,CACA,OAAO3D,EAAkB,KAAKQ,CAAK,EAAIyE,EAAazE,EAAOmD,CAAG,EAAInD,CACpE,CAEA,SAAS0E,EAAY,EAAe,EAAsB,CACxD,IAAIE,EAAW5E,EAAM,OACrB,KAAO4E,EAAW,GAAKC,EAAqB7E,EAAM,YAAY4E,EAAW,CAAC,CAAC,GAAG,IAC9E,IAAIzC,EAAMnC,EAAM,MAAM4E,CAAQ,EAE9B,GAAIzC,EAAI,SAAS;CAAI,GAAK,CAACA,EAAI,SAAS;CAAI,EAAG,OAAOsC,EAAazE,EAAOmD,CAAG,EAC7E,IAAMa,EAASb,EAAI,SAAWwB,EAAuB3E,CAAK,EAAI,KAAc,IAEtE8E,EAAgB3C,EAAI,QAAQ;CAAI,EAChC4C,EAAQD,IAAkB,GAAK,IAAM9E,IAAUmC,GAAO2C,IAAkB3C,EAAI,OAAS,EAAI,IAAM,GACrG,AAGEA,KAFA,EAAQnC,EAAM,MAAM,EAAG,CAACmC,EAAI,MAAM,EAC9BA,EAAI,SAAS;CAAI,IAAG,EAAMA,EAAI,MAAM,EAAG,EAAE,GACvCA,EAAI,WAAW,6BAA8B,KAAK6B,GAAQ,GAGlE,IAAMgB,EAAoB,WAAW,KAAKhF,CAAK,CAAC,GAAG,IAAM,GACnDiF,EAAkBD,EAAkB,SAAS,GAAG,EAClDtC,EAAQ1C,EAAM,MAAM,EAAGgF,EAAkB,YAAY;CAAI,EAAI,CAAC,EAOlE,MANA,CAEEtC,KADA,EAAQ1C,EAAM,MAAM0C,EAAM,MAAM,EACxBA,EAAM,WAAW,QAAS,KAAKsB,GAAQ,GAI1C,KADSiB,EAAmBjB,EAAS,IAAM,IAAO,IAAMe,EAC7C,IAAIf,IAAStB,IAAQwC,EAAYlF,EAAOgE,CAAM,IAAI7B,GACtE,CAEA,SAASsC,EAAa,EAAe,EAAsB,CACzD,OAAOzE,EAAM,SAAS,GAAG,GAAK,CAACA,EAAM,SAAS,GAAG,EAAImF,EAAmBnF,EAAOmD,CAAG,EAAImB,EAAmBtE,EAAOmD,CAAG,CACrH,CAEA,SAASgC,EAAmB,EAAe,EAAsB,CAI/D,OADInF,EAAM,SAAS;CAAI,EAAUsE,EAAmBtE,EAAOmD,CAAG,EACvD,IAAInD,EAAM,WAAW,IAAK,IAAI,EAAE,EACzC,CAEA,SAASsE,EAAmB,EAAe,EAAc,EAAgB,CAACnB,EAAI,YAAqB,CACjG,IAAMiC,EAAO,KAAK,UAAUpF,CAAK,EAC3BgE,EAASb,EAAI,SAAWwB,EAAuB3E,CAAK,EAAI,KAAc,IACxE0D,EAAM,GACNhB,EAAQ,EACZ,IAAK,IAAI2C,EAAI,EAAGA,EAAID,EAAK,OAAQ,IAAK,CACpC,IAAIE,EAAKF,EAAKC,GAQd,GAPIC,IAAO,KAAOF,EAAKC,EAAI,KAAO,MAAQD,EAAKC,EAAI,KAAO,MAExD,GAAO,GAAGD,EAAK,MAAM1C,EAAO2C,CAAC,EAAE,KAC/B,IACA,EAAQA,EACR,EAAK,MAEHC,IAAO,KACX,IAAIF,EAAKC,EAAI,KAAO,IAAK,CACvB,IAAME,EAAOH,EAAK,MAAMC,EAAI,EAAGA,EAAI,CAAC,EACpC,GACED,EAAK,MAAM1C,EAAO2C,CAAC,GAClBzF,EAAsB2F,KAAUA,EAAK,WAAW,IAAI,EAAI,MAAMA,EAAK,MAAM,CAAC,IAAMH,EAAK,MAAMC,EAAGA,EAAI,CAAC,IACtG,GAAK,EACL,EAAQA,EAAI,CACd,MAAO,GACLD,EAAKC,EAAI,KAAO,KAChBG,GACAJ,EAAKC,EAAI,KAAO,KAChBD,EAAK,QAAU,GACf,CAGA,IADA,GAAO,GAAGA,EAAK,MAAM1C,EAAO2C,CAAC,EAAE,MACxBD,EAAKC,EAAI,KAAO,MAAQD,EAAKC,EAAI,KAAO,KAAOD,EAAKC,EAAI,KAAO,KACpE,GAAO;EACP,GAAK,EAEP,GAAOrB,EAEHoB,EAAKC,EAAI,KAAO,MAAK,GAAO,MAChC,IACA,EAAQA,EAAI,CACd,KACE,IAAA,CAEJ,CACA,OAAO3C,EAAQgB,EAAM0B,EAAK,MAAM1C,CAAK,EAAI0C,CAC3C,CAEA,SAAST,EAAuB,EAAwB,CACtD,MAAO,sBAAsB,KAAK3E,CAAK,CACzC,CAGA,SAASkF,EAAY,EAAc,EAAwB,CACzD,IAAMpE,EAAQR,EAAK,MAAM;CAAI,EACzBoD,EAAM5C,EAAM,IAAM,GACtB,IAAK,IAAIuE,EAAI,EAAGA,EAAIvE,EAAM,OAAQ,IAAK,CACrC,IAAMO,EAAOP,EAAMuE,GACnB,GAAOhE,EAAO,KAAK2C,IAAS3C,IAAS;CACvC,CACA,OAAOqC,CACT,CAEA,SAASmB,EAAqB,EAAwC,CACpE,OAAOY,IAAc,IAAQA,IAAc,GAAQA,IAAc,EACnE"}
|
|
1
|
+
{"version":3,"file":"prompt.cjs","names":["toTildeCodeBlock","escapeRegExp","sliceWithoutSplittingSurrogates","require_text","NON_STRING_SCALAR","UNSAFE_PLAIN_START","UNSAFE_PLAIN_MIDDLE","UNSAFE_PLAIN_END","SHORT_UNICODE_ESCAPES","serializeForPrompt","stringifyValue","toSerializable","value","serializeForPromptInTag","tagPatterns","tagName","createTagPatterns","escapePromptTag","text","escapeTags","name","pattern","patterns","BLOCK_OPENER","MISPLACED_BLOCK_OPENER","formatPrompt","lines","prompt","blocks","findBlocks","formatted","prose","index","line","block","blockLines","dedentPromptMarkers","nestedFenceLengths","backtickClosers","tildeClosers","match","opener","fence","closers","low","high","middle","end","lengths","maximumFenceLengthAt","recordFenceLength","maximum","node","left","start","right","length","replaceIndentedMarker","marker","truncateForPrompt","maxLength","stringifyNumber","stringifyString","ctx","stringifyObject","stringifyArray","stringifyEntries","lazyEntries","separator","itemCtx","str","item","key","entries","stringifyPair","record","indent","rawKey","rawValue","keyStr","valueStr","isCollection","doubleQuotedString","plainString","implicitKey","quotedString","blockString","containsDocumentMarker","endStart","isBlockEndWhitespace","endNewlinePos","chomp","leadingWhitespace","startsWithSpace","indentLines","singleQuotedString","json","i","ch","code","foldMultiline","codePoint"],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, sliceWithoutSplittingSurrogates, toTildeCodeBlock } from './text.js';\n\nconst INDENT_STEP = ' ';\nconst MAX_IMPLICIT_KEY_LENGTH = 1024;\nconst MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH = 40;\n\n/** Strings matching this would be read back as a non-string scalar (null, bool, int, or float), so they must be quoted. */\nconst NON_STRING_SCALAR =\n /^(?:~|[Nn]ull|NULL|[Tt]rue|TRUE|[Ff]alse|FALSE|0o[0-7]+|[-+]?[0-9]+|0x[0-9a-fA-F]+|[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN|[-+]?(?:\\.[0-9]+|[0-9]+(?:\\.[0-9]*)?)[eE][-+]?[0-9]+|[-+]?(?:\\.[0-9]+|[0-9]+\\.[0-9]*))?$/u;\n\n// A single-line string must be quoted if it matches any of these. Checking the start, the middle, and the end separately\n// is much faster on long strings than one regex whose anchored alternatives are retried at every position.\nconst UNSAFE_PLAIN_START = /^(?:[\\t ,[\\]{}#&*!|>'\"%@`]|[?-](?:[ \\t]|$))/u;\nconst UNSAFE_PLAIN_MIDDLE = /:[ \\t]|[ \\t]#/u;\nconst UNSAFE_PLAIN_END = /[\\t :]$/u;\n\nconst SHORT_UNICODE_ESCAPES: Record<string, string> = {\n '0000': String.raw`\\0`,\n '0007': String.raw`\\a`,\n '000b': String.raw`\\v`,\n '001b': String.raw`\\e`,\n};\n\ninterface Context {\n indent: string;\n implicitKey?: boolean;\n tagPatterns?: readonly RegExp[];\n}\n\n/**\n * Serializes data into a code block to embed in an LLM prompt, so that the LLM can read its contents.\n * Accepts JSON data (honoring `toJSON` such as that of `Date`) as well as `undefined`, `NaN`, `Infinity`, bigints,\n * `Map`s (including non-string keys), `Set`s and other iterables, `Error`s (with their name, message, cause, and own properties),\n * and `RegExp`s. Symbols and functions without `toJSON` throw. The output is not meant to be deserialized back into the original value.\n * The format inside the code block is an implementation detail and may change.\n */\nexport function serializeForPrompt(value: unknown): string {\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '' })}\\n`, 'yaml');\n}\n\n/**\n * Serializes data with `serializeForPrompt` for embedding inside a `<tagName>` element of a prompt, escaping that tag\n * in every string so that untrusted data cannot close the element and have the rest read as instructions.\n * The caller must wrap the result in the same tag.\n *\n * Only the named elements are protected, so a block nested inside other tagged elements takes every enclosing name.\n * Escaping the finished block instead is not an option: it would turn a plain scalar holding the escaped notation\n * into a YAML flow sequence, since the quoting of each scalar is decided as it is written.\n *\n * The escaped notation is the one `escapePromptTag` writes, and text that already holds it is left as it is, so two\n * strings differing only in that notation are written alike. Keys taken from untrusted data can therefore collide\n * into one duplicate key of the emitted mapping.\n */\nexport function serializeForPromptInTag(value: unknown, tagName: string | readonly string[]): string {\n const escapedTags = typeof tagName === 'string' ? [tagName] : tagName;\n const tagPatterns = escapedTags.flatMap(createTagPatterns);\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '', tagPatterns })}\\n`, 'yaml');\n}\n\n/**\n * Replaces `<tagName` and `</tagName` not followed by `[\\w-]` with a harmless notation such as `[/tagName]`, so that\n * data embedded in a `<tagName>` element of a prompt cannot close the element and have the rest read as instructions.\n * A closing `>` is not required, since an HTML reader also closes an element on a tag carrying attribute-like junk,\n * on a trailing solidus, or on the next `>` anywhere in the prompt; the whitespace between `<` and the name is\n * dropped along with the brackets. Case is ignored deliberately: `tagName` is expected to be a literal written by\n * the caller, while the data is not.\n */\nexport function escapePromptTag(text: string, tagName: string): string {\n return text.includes('<') ? escapeTags(text, createTagPatterns(tagName)) : text;\n}\n\nfunction createTagPatterns(tagName: string): RegExp[] {\n const name = escapeRegExp(tagName);\n // Keep well-formed tags readable while also catching incomplete and malformed tags.\n return [\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})\\\\s*>`, 'giu'),\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})(?![\\\\w-])`, 'giu'),\n ];\n}\n\nfunction escapeTags(text: string, patterns: readonly RegExp[]): string {\n if (!text.includes('<')) return text;\n for (const pattern of patterns) text = text.replaceAll(pattern, '[$1$2]');\n return text;\n}\n\n/** The line opening a fenced block, with the indentation an interpolation may put before it. */\nconst BLOCK_OPENER = /^[ \\t]*((`{3,}|~{3,})[^\\n]*)$/u;\n\n/** A block interpolated after other text on its line, which no rule can tell from prose that ends in `~~~yaml`. */\nconst MISPLACED_BLOCK_OPENER = /[^\\s~][ \\t]*~{3,}yaml[ \\t]*$/u;\n\ninterface PromptBlock {\n opener: string;\n end: number;\n}\n\n/**\n * Strips template-literal indentation from Markdown headings, code fences, and triple-quote delimiters (`\"\"\"` and\n * `'''`), and collapses blank lines.\n * The contents of a fenced block are left untouched, since reindenting them would rewrite the data they carry, such\n * as the YAML of `serializeForPrompt` or the source of `toCodeBlock`. Every fenced block must be interpolated at\n * the start of a line. Only recognizable mid-line `~~~yaml` markers throw, including prose mentions;\n * this is not a complete placement validator, since preceding tildes can merge into an indistinguishable fence.\n * Blocks end at the first compatible same-character fence, which may carry trailing prose, unless a longer fence\n * of the same character opens and contains that candidate closing line. Other fence characters have no nesting\n * significance. Balance authored fences before interpolating blocks: an unmatched sample can consume part of a\n * later block and leave its tail subject to prose formatting.\n */\nexport function formatPrompt(prompt: string): string {\n const lines = prompt.split('\\n');\n const blocks = findBlocks(lines);\n let formatted = '';\n let prose = '';\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? '';\n const block = blocks[index];\n if (block === undefined) {\n // An opening fence the prompt itself writes and never closes is prose, and so is every line inside a block.\n if (MISPLACED_BLOCK_OPENER.test(line)) {\n throw new TypeError('Interpolate a serializeForPrompt block at the start of a line');\n }\n prose += `${line}\\n`;\n continue;\n }\n // Dropping the opener's indentation keeps four spaces of it from turning the fence into an indented code block.\n const blockLines = lines.slice(index + 1, block.end + 1);\n blockLines[blockLines.length - 1] = blockLines.at(-1)?.trimStart() ?? '';\n formatted += `${dedentPromptMarkers(prose)}${block.opener}\\n${blockLines.join('\\n')}`;\n prose = '\\n';\n index = block.end;\n }\n return (formatted + dedentPromptMarkers(prose)).trim();\n}\n\nfunction findBlocks(lines: string[]): (PromptBlock | undefined)[] {\n const blocks: (PromptBlock | undefined)[] = [];\n const nestedFenceLengths = new Map<string, Uint32Array>();\n const backtickClosers: { length: number; index: number }[] = [];\n const tildeClosers: { length: number; index: number }[] = [];\n for (let index = lines.length - 1; index >= 0; index--) {\n const match = BLOCK_OPENER.exec(lines[index] ?? '');\n const opener = match?.[1]?.trimEnd();\n const fence = match?.[2];\n if (opener === undefined || fence === undefined) continue;\n const closers = fence[0] === '`' ? backtickClosers : tildeClosers;\n let low = 0;\n let high = closers.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if ((closers[middle]?.length ?? 0) >= fence.length) low = middle + 1;\n else high = middle;\n }\n let end = closers[low - 1]?.index ?? -1;\n if (/^(?:[ \\t].*)?$/u.test((match?.[1] ?? '').slice(fence.length))) {\n // A nearer, longer closer also closes every fence that a shorter one could close.\n while (closers.length > 0 && (closers.at(-1)?.length ?? 0) <= fence.length) closers.pop();\n closers.push({ length: fence.length, index });\n }\n let lengths = nestedFenceLengths.get(fence[0] ?? '');\n if (end !== -1 && lengths !== undefined && maximumFenceLengthAt(lengths, end) > fence.length) end = -1;\n if (end !== -1) {\n blocks[index] = { opener, end };\n if (lengths === undefined) {\n lengths = new Uint32Array(lines.length * 2);\n nestedFenceLengths.set(fence[0] ?? '', lengths);\n }\n recordFenceLength(lengths, index + 1, end, fence.length);\n }\n }\n return blocks;\n}\n\nfunction maximumFenceLengthAt(lengths: Uint32Array, index: number): number {\n let maximum = 0;\n for (let node = index + lengths.length / 2; node > 0; node >>>= 1) {\n maximum = Math.max(maximum, lengths[node] ?? 0);\n }\n return maximum;\n}\n\nfunction recordFenceLength(lengths: Uint32Array, start: number, end: number, length: number): void {\n let left = start + lengths.length / 2;\n let right = end + lengths.length / 2;\n while (left <= right) {\n if (left % 2 === 1) {\n lengths[left] = Math.max(lengths[left] ?? 0, length);\n left++;\n }\n if (right % 2 === 0) {\n lengths[right] = Math.max(lengths[right] ?? 0, length);\n right--;\n }\n left >>>= 1;\n right >>>= 1;\n }\n}\n\nfunction dedentPromptMarkers(text: string): string {\n // Consume failed marker searches, and start blank-line searches only at whitespace-run boundaries.\n return text\n .replaceAll(/\\n\\s+(\"\"\"|'''|```|~{3,})|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/\\n\\s+(#+\\s)|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/(?<!\\s)\\s*\\n\\s*\\n/gu, '\\n\\n');\n}\n\nfunction replaceIndentedMarker(match: string, marker: string | undefined): string {\n return marker === undefined ? match : `\\n${marker}`;\n}\n\n/** Cuts text down to `maxLength` characters, marking it so that an LLM reads the rest as missing rather than absent. */\nexport function truncateForPrompt(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${sliceWithoutSplittingSurrogates(text, maxLength)}\\n...<truncated>`;\n}\n\n// The functions below reproduce `stringify(value, { lineWidth: 0, aliasDuplicateObjects: false, blockQuote: 'literal' })`\n// of the `yaml` package, except that `toSerializable` makes `Error`s and `RegExp`s readable instead of `{}`.\n\nfunction stringifyValue(value: unknown, ctx: Context): string {\n switch (typeof value) {\n case 'undefined': {\n return 'null';\n }\n case 'boolean': {\n return String(value);\n }\n case 'number': {\n return stringifyNumber(value);\n }\n case 'bigint': {\n return String(value);\n }\n case 'string': {\n return stringifyString(value, ctx);\n }\n case 'object': {\n return stringifyObject(value, ctx);\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\n}\n\nfunction stringifyObject(value: object | null, ctx: Context): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return stringifyArray(value, ctx);\n return stringifyEntries(value instanceof Map ? value : lazyEntries(value as Record<string, unknown>), ctx);\n}\n\nfunction stringifyArray(value: unknown[], ctx: Context): string {\n if (value.length === 0) return '[]';\n const separator = `\\n${ctx.indent}`;\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n let str = '';\n for (const item of value) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n}\n\nfunction stringifyEntries(entries: Iterable<[unknown, unknown]>, ctx: Context): string {\n const separator = `\\n${ctx.indent}`;\n let str = '';\n for (const [key, item] of entries) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n}\n\n// Reads each property only when its turn comes, as serializing an earlier one may run a `toJSON` that mutates the record.\nfunction* lazyEntries(record: Record<string, unknown>): Generator<[unknown, unknown]> {\n for (const key of Object.keys(record)) yield [key, record[key]];\n}\n\nfunction stringifyPair(rawKey: unknown, rawValue: unknown, ctx: Context): string {\n const indent = ctx.indent + INDENT_STEP;\n const key = toSerializable(rawKey);\n const value = toSerializable(rawValue);\n const keyStr = stringifyValue(key, { indent, implicitKey: true, tagPatterns: ctx.tagPatterns });\n const valueStr = stringifyValue(value, { indent, tagPatterns: ctx.tagPatterns });\n if (isCollection(key) || keyStr.length > MAX_IMPLICIT_KEY_LENGTH) return `? ${keyStr}\\n${ctx.indent}: ${valueStr}`;\n const isBlockCollection = isCollection(value) && valueStr !== '[]' && valueStr !== '{}';\n return `${keyStr}:${isBlockCollection ? `\\n${indent}` : ' '}${valueStr}`;\n}\n\nfunction toSerializable(rawValue: unknown): unknown {\n // Like `yaml`, call `toJSON` only once, but classify its result.\n const value =\n typeof (rawValue as { toJSON?: unknown } | null | undefined)?.toJSON === 'function'\n ? (rawValue as { toJSON: () => unknown }).toJSON()\n : rawValue;\n if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Map) return value;\n // oxlint-disable-next-line unicorn/no-instanceof-builtins -- boxed primitives have no typeof check; `yaml` unwraps them the same way.\n if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof BigInt) {\n return value.valueOf();\n }\n if (value instanceof Error) {\n // `name` and `message` usually live on the prototype, and `cause` and `errors` are not enumerable.\n return {\n name: value.name,\n message: value.message,\n ...Object.fromEntries(Object.entries(value)),\n ...('cause' in value && { cause: value.cause }),\n ...(value instanceof AggregateError && { errors: value.errors }),\n };\n }\n if (value instanceof RegExp) return String(value);\n return Symbol.iterator in value ? [...(value as Iterable<unknown>)] : value;\n}\n\nfunction isCollection(value: unknown): value is object {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringifyNumber(value: number): string {\n if (Number.isNaN(value)) return '.nan';\n if (!Number.isFinite(value)) return value < 0 ? '-.inf' : '.inf';\n return Object.is(value, -0) ? '-0' : JSON.stringify(value);\n}\n\nfunction stringifyString(rawValue: string, ctx: Context): string {\n const value = ctx.tagPatterns === undefined ? rawValue : escapeTags(rawValue, ctx.tagPatterns);\n // A closing quote separates an unfinished tag prefix from the next mapping key.\n if (ctx.tagPatterns?.length && /<\\s*(?:\\/\\s*)?$/u.test(value)) return doubleQuotedString(value, ctx, false);\n // oxlint-disable-next-line no-control-regex -- control characters and lone surrogates can only be written escaped.\n return /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F\\u{D800}-\\u{DFFF}]/u.test(value)\n ? doubleQuotedString(value, ctx)\n : plainString(value, ctx);\n}\n\nfunction plainString(value: string, ctx: Context): string {\n const { implicitKey, indent } = ctx;\n if (value.includes('\\n')) return implicitKey ? quotedString(value, ctx) : blockString(value, ctx);\n if (UNSAFE_PLAIN_START.test(value) || UNSAFE_PLAIN_END.test(value.slice(-1)) || UNSAFE_PLAIN_MIDDLE.test(value)) {\n return quotedString(value, ctx);\n }\n if (containsDocumentMarker(value)) {\n if (indent === '') return blockString(value, ctx);\n if (implicitKey && indent === INDENT_STEP) return quotedString(value, ctx);\n }\n return NON_STRING_SCALAR.test(value) ? quotedString(value, ctx) : value;\n}\n\nfunction blockString(value: string, ctx: Context): string {\n let endStart = value.length;\n while (endStart > 0 && isBlockEndWhitespace(value.codePointAt(endStart - 1))) endStart--;\n let end = value.slice(endStart);\n // A block scalar cannot end with a whitespace-only line.\n if (end.includes('\\n') && !end.endsWith('\\n')) return quotedString(value, ctx);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n\n const endNewlinePos = end.indexOf('\\n');\n const chomp = endNewlinePos === -1 ? '-' : value === end || endNewlinePos !== end.length - 1 ? '+' : '';\n if (end) {\n value = value.slice(0, -end.length);\n if (end.endsWith('\\n')) end = end.slice(0, -1);\n end = end.replaceAll(/(?:^|(?<!\\n))\\n+(?!\\n|$)/gu, `$&${indent}`);\n }\n\n const leadingWhitespace = /^[\\n ]*/u.exec(value)?.[0] ?? '';\n const startsWithSpace = leadingWhitespace.includes(' ');\n let start = value.slice(0, leadingWhitespace.lastIndexOf('\\n') + 1);\n if (start) {\n value = value.slice(start.length);\n start = start.replaceAll(/\\n+/gu, `$&${indent}`);\n }\n\n const header = (startsWithSpace ? (indent ? '2' : '1') : '') + chomp;\n return `|${header}\\n${indent}${start}${indentLines(value, indent)}${end}`;\n}\n\nfunction quotedString(value: string, ctx: Context): string {\n return value.includes('\"') && !value.includes(\"'\") ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx);\n}\n\nfunction singleQuotedString(value: string, ctx: Context): string {\n // Every multi-line value reaching here would contain a space or tab next to a newline or be an implicit key,\n // both of which need double quotes.\n if (value.includes('\\n')) return doubleQuotedString(value, ctx);\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction doubleQuotedString(value: string, ctx: Context, foldMultiline = !ctx.implicitKey): string {\n const json = JSON.stringify(value);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n let str = '';\n let start = 0;\n for (let i = 0; i < json.length; i++) {\n let ch = json[i];\n if (ch === ' ' && json[i + 1] === '\\\\' && json[i + 2] === 'n') {\n // A space before a line break would be folded away unless escaped.\n str += `${json.slice(start, i)}\\\\ `;\n i++;\n start = i;\n ch = '\\\\';\n }\n if (ch !== '\\\\') continue;\n if (json[i + 1] === 'u') {\n const code = json.slice(i + 2, i + 6);\n str +=\n json.slice(start, i) +\n (SHORT_UNICODE_ESCAPES[code] ?? (code.startsWith('00') ? `\\\\x${code.slice(2)}` : json.slice(i, i + 6)));\n i += 5;\n start = i + 1;\n } else if (\n json[i + 1] === 'n' &&\n foldMultiline &&\n json[i + 2] !== '\"' &&\n json.length >= MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH\n ) {\n // Folding turns the first of the written line breaks back into a space, hence the extra one.\n str += `${json.slice(start, i)}\\n\\n`;\n while (json[i + 2] === '\\\\' && json[i + 3] === 'n' && json[i + 4] !== '\"') {\n str += '\\n';\n i += 2;\n }\n str += indent;\n // A space after a line break would be folded away unless escaped.\n if (json[i + 2] === ' ') str += '\\\\';\n i++;\n start = i + 1;\n } else {\n i++;\n }\n }\n return start ? str + json.slice(start) : json;\n}\n\nfunction containsDocumentMarker(value: string): boolean {\n return /^(?:%|---|\\.\\.\\.)/mu.test(value);\n}\n\n/** Equivalent to `text.replaceAll(/\\n+/g, `$&${indent}`)` for text without leading or trailing newlines, but faster. */\nfunction indentLines(text: string, indent: string): string {\n const lines = text.split('\\n');\n let str = lines[0] ?? '';\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n str += line ? `\\n${indent}${line}` : '\\n';\n }\n return str;\n}\n\nfunction isBlockEndWhitespace(codePoint: number | undefined): boolean {\n return codePoint === 0x0a || codePoint === 0x09 || codePoint === 0x20;\n}\n"],"mappings":"2CAOMI,EACJ,wNAIIC,EAAqB,+CACrBC,EAAsB,iBACtBC,EAAmB,WAEnBC,EAAgD,CACpD,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,KAClB,OAAQ,OAAO,GAAG,IACpB,EAeA,SAAgBC,EAAmB,EAAwB,CACzD,OAAOT,EAAAA,iBAAiB,GAAGU,EAAeC,EAAeC,CAAK,EAAG,CAAE,OAAQ,EAAG,CAAC,EAAE,IAAK,MAAM,CAC9F,CAeA,SAAgBC,EAAwB,EAAgB,EAA6C,CAEnG,IAAMC,GADc,OAAOC,GAAY,SAAW,CAACA,CAAO,EAAIA,EAAA,CAC9B,QAAQC,CAAiB,EACzD,OAAOhB,EAAAA,iBAAiB,GAAGU,EAAeC,EAAeC,CAAK,EAAG,CAAE,OAAQ,GAAI,aAAY,CAAC,EAAE,IAAK,MAAM,CAC3G,CAUA,SAAgBK,EAAgB,EAAc,EAAyB,CACrE,OAAOC,EAAK,SAAS,GAAG,EAAIC,EAAWD,EAAMF,EAAkBD,CAAO,CAAC,EAAIG,CAC7E,CAEA,SAASF,EAAkB,EAA2B,CACpD,IAAMI,EAAOnB,EAAAA,aAAac,CAAO,EAEjC,MAAO,CACD,OAAO,qBAAqBK,EAAK,QAAS,KAAK,EAC/C,OAAO,qBAAqBA,EAAK,aAAc,KAAK,CAC1D,CACF,CAEA,SAASD,EAAW,EAAc,EAAqC,CACrE,GAAI,CAACD,EAAK,SAAS,GAAG,EAAG,OAAOA,EAChC,IAAK,IAAMG,KAAWC,EAAU,EAAOJ,EAAK,WAAWG,EAAS,QAAQ,EACxE,OAAOH,CACT,CAGA,MAAMK,EAAe,iCAGfC,EAAyB,gCAmB/B,SAAgBC,EAAa,EAAwB,CACnD,IAAMC,EAAQC,EAAO,MAAM;CAAI,EACzBC,EAASC,EAAWH,CAAK,EAC3BI,EAAY,GACZC,EAAQ,GACZ,IAAK,IAAIC,EAAQ,EAAGA,EAAQN,EAAM,OAAQ,IAAS,CACjD,IAAMO,EAAOP,EAAMM,IAAU,GACvBE,EAAQN,EAAOI,GACrB,GAAIE,IAAU,IAAA,GAAW,CAEvB,GAAIV,EAAuB,KAAKS,CAAI,EAClC,MAAU,UAAU,+DAA+D,EAErF,GAAS,GAAGA,EAAK,IACjB,QACF,CAEA,IAAME,EAAaT,EAAM,MAAMM,EAAQ,EAAGE,EAAM,IAAM,CAAC,EACvD,EAAWC,EAAW,OAAS,GAAKA,EAAW,GAAG,EAAE,CAAC,EAAE,UAAU,GAAK,GACtE,GAAa,GAAGC,EAAoBL,CAAK,IAAIG,EAAM,OAAO,IAAIC,EAAW,KAAK;CAAI,IAClF,EAAQ;EACR,EAAQD,EAAM,GAChB,CACA,OAAQJ,EAAYM,EAAoBL,CAAK,EAAA,CAAG,KAAK,CACvD,CAEA,SAASF,EAAW,EAA8C,CAChE,IAAMD,EAAsC,CAAC,EACvCS,kBAAqB,IAAI,IACzBC,EAAuD,CAAC,EACxDC,EAAoD,CAAC,EAC3D,IAAK,IAAIP,EAAQN,EAAM,OAAS,EAAGM,GAAS,EAAG,IAAS,CACtD,IAAMQ,EAAQjB,EAAa,KAAKG,EAAMM,IAAU,EAAE,EAC5CS,EAASD,IAAQ,EAAE,EAAE,QAAQ,EAC7BE,EAAQF,IAAQ,GACtB,GAAIC,IAAW,IAAA,IAAaC,IAAU,IAAA,GAAW,SACjD,IAAMC,EAAUD,EAAM,KAAO,IAAMJ,EAAkBC,EACjDK,EAAM,EACNC,EAAOF,EAAQ,OACnB,KAAOC,EAAMC,GAAM,CACjB,IAAMC,EAAUF,EAAMC,IAAU,GAC3BF,EAAQG,EAAO,EAAE,QAAU,IAAMJ,EAAM,OAAQ,EAAMI,EAAS,EAC9D,EAAOA,CACd,CACA,IAAIC,EAAMJ,EAAQC,EAAM,EAAE,EAAE,OAAS,GACrC,GAAI,kBAAkB,MAAMJ,IAAQ,IAAM,GAAA,CAAI,MAAME,EAAM,MAAM,CAAC,EAAG,CAElE,KAAOC,EAAQ,OAAS,IAAMA,EAAQ,GAAG,EAAE,CAAC,EAAE,QAAU,IAAMD,EAAM,QAAQC,EAAQ,IAAI,EACxFA,EAAQ,KAAK,CAAE,OAAQD,EAAM,OAAQ,OAAM,CAAC,CAC9C,CACA,IAAIM,EAAUX,EAAmB,IAAIK,EAAM,IAAM,EAAE,EAC/CK,IAAQ,IAAMC,IAAY,IAAA,IAAaC,EAAqBD,EAASD,CAAG,EAAIL,EAAM,SAAQ,EAAM,IAChGK,IAAQ,KACV,EAAOf,GAAS,CAAE,SAAQ,KAAI,EAC1BgB,IAAY,IAAA,KACd,EAAU,IAAI,YAAYtB,EAAM,OAAS,CAAC,EAC1CW,EAAmB,IAAIK,EAAM,IAAM,GAAIM,CAAO,GAEhDE,EAAkBF,EAAShB,EAAQ,EAAGe,EAAKL,EAAM,MAAM,EAE3D,CACA,OAAOd,CACT,CAEA,SAASqB,EAAqB,EAAsB,EAAuB,CACzE,IAAIE,EAAU,EACd,IAAK,IAAIC,EAAOpB,EAAQgB,EAAQ,OAAS,EAAGI,EAAO,EAAG,KAAU,EAC9D,EAAU,KAAK,IAAID,EAASH,EAAQI,IAAS,CAAC,EAEhD,OAAOD,CACT,CAEA,SAASD,EAAkB,EAAsB,EAAe,EAAa,EAAsB,CACjG,IAAIG,EAAOC,EAAQN,EAAQ,OAAS,EAChCO,EAAQR,EAAMC,EAAQ,OAAS,EACnC,KAAOK,GAAQE,GACTF,EAAO,GAAM,IACf,EAAQA,GAAQ,KAAK,IAAIL,EAAQK,IAAS,EAAGG,CAAM,EACnD,KAEED,EAAQ,GAAM,IAChB,EAAQA,GAAS,KAAK,IAAIP,EAAQO,IAAU,EAAGC,CAAM,EACrD,KAEF,KAAU,EACV,KAAW,CAEf,CAEA,SAASpB,EAAoB,EAAsB,CAEjD,OAAOlB,EACJ,WAAW,mCAAoCuC,CAAqB,CAAC,CACrE,WAAW,sBAAuBA,CAAqB,CAAC,CACxD,WAAW,sBAAuB;;CAAM,CAC7C,CAEA,SAASA,EAAsB,EAAe,EAAoC,CAChF,OAAOC,IAAW,IAAA,GAAYlB,EAAQ,KAAKkB,GAC7C,CAGA,SAAgBC,EAAkB,EAAc,EAA2B,CAEzE,OADIzC,EAAK,QAAU0C,EAAkB1C,EAC9B,GAAGhB,EAAAA,gCAAgCgB,EAAM0C,CAAS,EAAE,iBAC7D,CAKA,SAASlD,EAAe,EAAgB,EAAsB,CAC5D,OAAQ,OAAOE,EAAf,CACE,IAAK,YACH,MAAO,OAET,IAAK,UACH,OAAO,OAAOA,CAAK,EAErB,IAAK,SACH,OAAOiD,EAAgBjD,CAAK,EAE9B,IAAK,SACH,OAAO,OAAOA,CAAK,EAErB,IAAK,SACH,OAAOkD,EAAgBlD,EAAOmD,CAAG,EAEnC,IAAK,SACH,OAAOC,EAAgBpD,EAAOmD,CAAG,EAEnC,QACE,MAAU,UAAU,sBAAsB,OAAOnD,EAAM,oBAAoB,CAE/E,CACF,CAEA,SAASoD,EAAgB,EAAsB,EAAsB,CAGnE,OAFIpD,IAAU,KAAa,OACvB,MAAM,QAAQA,CAAK,EAAUqD,EAAerD,EAAOmD,CAAG,EACnDG,EAAiBtD,aAAiB,IAAMA,EAAQuD,EAAYvD,CAAgC,EAAGmD,CAAG,CAC3G,CAEA,SAASE,EAAe,EAAkB,EAAsB,CAC9D,GAAIrD,EAAM,SAAW,EAAG,MAAO,KAC/B,IAAMwD,EAAY,KAAKL,EAAI,SACrBM,EAAU,CAAE,OAAQN,EAAI,OAAS,KAAa,YAAaA,EAAI,WAAY,EAC7EO,EAAM,GACV,IAAK,IAAMC,KAAQ3D,EACjB,GAAO,GAAG0D,EAAMF,EAAY,GAAG,IAAI1D,EAAeC,EAAe4D,CAAI,EAAGF,CAAO,IAEjF,OAAOC,CACT,CAEA,SAASJ,EAAiB,EAAuC,EAAsB,CACrF,IAAME,EAAY,KAAKL,EAAI,SACvBO,EAAM,GACV,IAAK,GAAM,CAACE,EAAKD,KAASE,EACpBF,IAAS,IAAA,KAAW,GAAO,GAAGD,EAAMF,EAAY,KAAKM,EAAcF,EAAKD,EAAMR,CAAG,KAEvF,OAAOO,GAAO,IAChB,CAGA,SAAUH,EAAY,EAAgE,CACpF,IAAK,IAAMK,KAAO,OAAO,KAAKG,CAAM,EAAG,KAAM,CAACH,EAAKG,EAAOH,EAAI,CAChE,CAEA,SAASE,EAAc,EAAiB,EAAmB,EAAsB,CAC/E,IAAME,EAASb,EAAI,OAAS,KACtBS,EAAM7D,EAAekE,CAAM,EAC3BjE,EAAQD,EAAemE,CAAQ,EAC/BC,EAASrE,EAAe8D,EAAK,CAAE,SAAQ,YAAa,GAAM,YAAaT,EAAI,WAAY,CAAC,EACxFiB,EAAWtE,EAAeE,EAAO,CAAE,SAAQ,YAAamD,EAAI,WAAY,CAAC,EAG/E,OAFIkB,EAAaT,CAAG,GAAKO,EAAO,OAAS,KAAgC,KAAKA,EAAO,IAAIhB,EAAI,OAAO,IAAIiB,IAEjG,GAAGD,EAAO,GADSE,EAAarE,CAAK,GAAKoE,IAAa,MAAQA,IAAa,KAC3C,KAAKJ,IAAW,MAAMI,GAChE,CAEA,SAASrE,EAAe,EAA4B,CAElD,IAAMC,EACJ,OAAQkE,GAAsD,QAAW,WACpEA,EAAuC,OAAO,EAC/CA,EAiBN,OAhBI,OAAOlE,GAAU,WAAYA,GAAkB,MAAM,QAAQA,CAAK,GAAKA,aAAiB,IAAYA,EAEpGA,aAAiB,QAAUA,aAAiB,QAAUA,aAAiB,SAAWA,aAAiB,OAC9FA,EAAM,QAAQ,EAEnBA,aAAiB,MAEZ,CACL,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,GAAG,OAAO,YAAY,OAAO,QAAQA,CAAK,CAAC,EAC3C,GAAI,UAAWA,GAAS,CAAE,MAAOA,EAAM,KAAM,EAC7C,GAAIA,aAAiB,gBAAkB,CAAE,OAAQA,EAAM,MAAO,CAChE,EAEEA,aAAiB,OAAe,OAAOA,CAAK,EACzC,OAAO,YAAYA,EAAQ,CAAC,GAAIA,CAA2B,EAAIA,CACxE,CAEA,SAASqE,EAAa,EAAiC,CACrD,OAAO,OAAOrE,GAAU,YAAYA,CACtC,CAEA,SAASiD,EAAgB,EAAuB,CAG9C,OAFI,OAAO,MAAMjD,CAAK,EAAU,OAC3B,OAAO,SAASA,CAAK,EACnB,OAAO,GAAGA,EAAO,EAAE,EAAI,KAAO,KAAK,UAAUA,CAAK,EADrBA,EAAQ,EAAI,QAAU,MAE5D,CAEA,SAASkD,EAAgB,EAAkB,EAAsB,CAC/D,IAAMlD,EAAQmD,EAAI,cAAgB,IAAA,GAAYe,EAAW3D,EAAW2D,EAAUf,EAAI,WAAW,EAI7F,OAFIA,EAAI,aAAa,QAAU,mBAAmB,KAAKnD,CAAK,EAAUsE,EAAmBtE,EAAOmD,EAAK,EAAK,EAEnG,8DAA8D,KAAKnD,CAAK,EAC3EsE,EAAmBtE,EAAOmD,CAAG,EAC7BoB,EAAYvE,EAAOmD,CAAG,CAC5B,CAEA,SAASoB,EAAY,EAAe,EAAsB,CACxD,GAAM,CAAE,cAAa,UAAWpB,EAChC,GAAInD,EAAM,SAAS;CAAI,EAAG,OAAOwE,EAAcC,EAAazE,EAAOmD,CAAG,EAAIuB,EAAY1E,EAAOmD,CAAG,EAChG,GAAI1D,EAAmB,KAAKO,CAAK,GAAKL,EAAiB,KAAKK,EAAM,MAAM,EAAE,CAAC,GAAKN,EAAoB,KAAKM,CAAK,EAC5G,OAAOyE,EAAazE,EAAOmD,CAAG,EAEhC,GAAIwB,EAAuB3E,CAAK,EAAG,CACjC,GAAIgE,IAAW,GAAI,OAAOU,EAAY1E,EAAOmD,CAAG,EAChD,GAAIqB,GAAeR,IAAW,KAAa,OAAOS,EAAazE,EAAOmD,CAAG,CAC3E,CACA,OAAO3D,EAAkB,KAAKQ,CAAK,EAAIyE,EAAazE,EAAOmD,CAAG,EAAInD,CACpE,CAEA,SAAS0E,EAAY,EAAe,EAAsB,CACxD,IAAIE,EAAW5E,EAAM,OACrB,KAAO4E,EAAW,GAAKC,EAAqB7E,EAAM,YAAY4E,EAAW,CAAC,CAAC,GAAG,IAC9E,IAAIzC,EAAMnC,EAAM,MAAM4E,CAAQ,EAE9B,GAAIzC,EAAI,SAAS;CAAI,GAAK,CAACA,EAAI,SAAS;CAAI,EAAG,OAAOsC,EAAazE,EAAOmD,CAAG,EAC7E,IAAMa,EAASb,EAAI,SAAWwB,EAAuB3E,CAAK,EAAI,KAAc,IAEtE8E,EAAgB3C,EAAI,QAAQ;CAAI,EAChC4C,EAAQD,IAAkB,GAAK,IAAM9E,IAAUmC,GAAO2C,IAAkB3C,EAAI,OAAS,EAAI,IAAM,GACrG,AAGEA,KAFA,EAAQnC,EAAM,MAAM,EAAG,CAACmC,EAAI,MAAM,EAC9BA,EAAI,SAAS;CAAI,IAAG,EAAMA,EAAI,MAAM,EAAG,EAAE,GACvCA,EAAI,WAAW,6BAA8B,KAAK6B,GAAQ,GAGlE,IAAMgB,EAAoB,WAAW,KAAKhF,CAAK,CAAC,GAAG,IAAM,GACnDiF,EAAkBD,EAAkB,SAAS,GAAG,EAClDtC,EAAQ1C,EAAM,MAAM,EAAGgF,EAAkB,YAAY;CAAI,EAAI,CAAC,EAOlE,MANA,CAEEtC,KADA,EAAQ1C,EAAM,MAAM0C,EAAM,MAAM,EACxBA,EAAM,WAAW,QAAS,KAAKsB,GAAQ,GAI1C,KADSiB,EAAmBjB,EAAS,IAAM,IAAO,IAAMe,EAC7C,IAAIf,IAAStB,IAAQwC,EAAYlF,EAAOgE,CAAM,IAAI7B,GACtE,CAEA,SAASsC,EAAa,EAAe,EAAsB,CACzD,OAAOzE,EAAM,SAAS,GAAG,GAAK,CAACA,EAAM,SAAS,GAAG,EAAImF,EAAmBnF,EAAOmD,CAAG,EAAImB,EAAmBtE,EAAOmD,CAAG,CACrH,CAEA,SAASgC,EAAmB,EAAe,EAAsB,CAI/D,OADInF,EAAM,SAAS;CAAI,EAAUsE,EAAmBtE,EAAOmD,CAAG,EACvD,IAAInD,EAAM,WAAW,IAAK,IAAI,EAAE,EACzC,CAEA,SAASsE,EAAmB,EAAe,EAAc,EAAgB,CAACnB,EAAI,YAAqB,CACjG,IAAMiC,EAAO,KAAK,UAAUpF,CAAK,EAC3BgE,EAASb,EAAI,SAAWwB,EAAuB3E,CAAK,EAAI,KAAc,IACxE0D,EAAM,GACNhB,EAAQ,EACZ,IAAK,IAAI2C,EAAI,EAAGA,EAAID,EAAK,OAAQ,IAAK,CACpC,IAAIE,EAAKF,EAAKC,GAQd,GAPIC,IAAO,KAAOF,EAAKC,EAAI,KAAO,MAAQD,EAAKC,EAAI,KAAO,MAExD,GAAO,GAAGD,EAAK,MAAM1C,EAAO2C,CAAC,EAAE,KAC/B,IACA,EAAQA,EACR,EAAK,MAEHC,IAAO,KACX,IAAIF,EAAKC,EAAI,KAAO,IAAK,CACvB,IAAME,EAAOH,EAAK,MAAMC,EAAI,EAAGA,EAAI,CAAC,EACpC,GACED,EAAK,MAAM1C,EAAO2C,CAAC,GAClBzF,EAAsB2F,KAAUA,EAAK,WAAW,IAAI,EAAI,MAAMA,EAAK,MAAM,CAAC,IAAMH,EAAK,MAAMC,EAAGA,EAAI,CAAC,IACtG,GAAK,EACL,EAAQA,EAAI,CACd,MAAO,GACLD,EAAKC,EAAI,KAAO,KAChBG,GACAJ,EAAKC,EAAI,KAAO,KAChBD,EAAK,QAAU,GACf,CAGA,IADA,GAAO,GAAGA,EAAK,MAAM1C,EAAO2C,CAAC,EAAE,MACxBD,EAAKC,EAAI,KAAO,MAAQD,EAAKC,EAAI,KAAO,KAAOD,EAAKC,EAAI,KAAO,KACpE,GAAO;EACP,GAAK,EAEP,GAAOrB,EAEHoB,EAAKC,EAAI,KAAO,MAAK,GAAO,MAChC,IACA,EAAQA,EAAI,CACd,KACE,IAAA,CAEJ,CACA,OAAO3C,EAAQgB,EAAM0B,EAAK,MAAM1C,CAAK,EAAI0C,CAC3C,CAEA,SAAST,EAAuB,EAAwB,CACtD,MAAO,sBAAsB,KAAK3E,CAAK,CACzC,CAGA,SAASkF,EAAY,EAAc,EAAwB,CACzD,IAAMpE,EAAQR,EAAK,MAAM;CAAI,EACzBoD,EAAM5C,EAAM,IAAM,GACtB,IAAK,IAAIuE,EAAI,EAAGA,EAAIvE,EAAM,OAAQ,IAAK,CACrC,IAAMO,EAAOP,EAAMuE,GACnB,GAAOhE,EAAO,KAAK2C,IAAS3C,IAAS;CACvC,CACA,OAAOqC,CACT,CAEA,SAASmB,EAAqB,EAAwC,CACpE,OAAOY,IAAc,IAAQA,IAAc,GAAQA,IAAc,EACnE"}
|
package/dist/prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","names":["NON_STRING_SCALAR","UNSAFE_PLAIN_START","UNSAFE_PLAIN_MIDDLE","UNSAFE_PLAIN_END","SHORT_UNICODE_ESCAPES","serializeForPrompt","toTildeCodeBlock","stringifyValue","toSerializable","value","serializeForPromptInTag","tagPatterns","tagName","createTagPatterns","escapePromptTag","text","escapeTags","name","escapeRegExp","pattern","patterns","BLOCK_OPENER","MISPLACED_BLOCK_OPENER","formatPrompt","lines","prompt","blocks","findBlocks","formatted","prose","index","line","block","blockLines","dedentPromptMarkers","nestedFenceLengths","backtickClosers","tildeClosers","match","opener","fence","closers","low","high","middle","end","lengths","maximumFenceLengthAt","recordFenceLength","maximum","node","left","start","right","length","replaceIndentedMarker","marker","truncateForPrompt","maxLength","sliceWithoutSplittingSurrogates","stringifyNumber","stringifyString","ctx","stringifyObject","stringifyArray","stringifyEntries","lazyEntries","separator","itemCtx","str","item","key","entries","stringifyPair","record","indent","rawKey","rawValue","keyStr","valueStr","isCollection","doubleQuotedString","plainString","implicitKey","quotedString","blockString","containsDocumentMarker","endStart","isBlockEndWhitespace","endNewlinePos","chomp","leadingWhitespace","startsWithSpace","indentLines","singleQuotedString","json","i","ch","code","foldMultiline","codePoint"],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, sliceWithoutSplittingSurrogates, toTildeCodeBlock } from './text.js';\n\nconst INDENT_STEP = ' ';\nconst MAX_IMPLICIT_KEY_LENGTH = 1024;\nconst MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH = 40;\n\n/** Strings matching this would be read back as a non-string scalar (null, bool, int, or float), so they must be quoted. */\nconst NON_STRING_SCALAR =\n /^(?:~|[Nn]ull|NULL|[Tt]rue|TRUE|[Ff]alse|FALSE|0o[0-7]+|[-+]?[0-9]+|0x[0-9a-fA-F]+|[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN|[-+]?(?:\\.[0-9]+|[0-9]+(?:\\.[0-9]*)?)[eE][-+]?[0-9]+|[-+]?(?:\\.[0-9]+|[0-9]+\\.[0-9]*))?$/u;\n\n// A single-line string must be quoted if it matches any of these. Checking the start, the middle, and the end separately\n// is much faster on long strings than one regex whose anchored alternatives are retried at every position.\nconst UNSAFE_PLAIN_START = /^(?:[\\t ,[\\]{}#&*!|>'\"%@`]|[?-](?:[ \\t]|$))/u;\nconst UNSAFE_PLAIN_MIDDLE = /:[ \\t]|[ \\t]#/u;\nconst UNSAFE_PLAIN_END = /[\\t :]$/u;\n\nconst SHORT_UNICODE_ESCAPES: Record<string, string> = {\n '0000': String.raw`\\0`,\n '0007': String.raw`\\a`,\n '000b': String.raw`\\v`,\n '001b': String.raw`\\e`,\n};\n\ninterface Context {\n indent: string;\n implicitKey?: boolean;\n tagPatterns?: readonly RegExp[];\n}\n\n/**\n * Serializes data into a code block to embed in an LLM prompt, so that the LLM can read its contents.\n * Accepts JSON data (honoring `toJSON` such as that of `Date`) as well as `undefined`, `NaN`, `Infinity`, bigints,\n * `Map`s (including non-string keys), `Set`s and other iterables, `Error`s (with their name, message, cause, and own properties),\n * and `RegExp`s. Symbols and functions without `toJSON` throw. The output is not meant to be deserialized back into the original value.\n * The format inside the code block is an implementation detail and may change.\n */\nexport function serializeForPrompt(value: unknown): string {\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '' })}\\n`, 'yaml');\n}\n\n/**\n * Serializes data with `serializeForPrompt` for embedding inside a `<tagName>` element of a prompt, escaping that tag\n * in every string so that untrusted data cannot close the element and have the rest read as instructions.\n * The caller must wrap the result in the same tag.\n *\n * Only the named elements are protected, so a block nested inside other tagged elements takes every enclosing name.\n * Escaping the finished block instead is not an option: it would turn a plain scalar holding the escaped notation\n * into a YAML flow sequence, since the quoting of each scalar is decided as it is written.\n *\n * The escaped notation is the one `escapePromptTag` writes, and text that already holds it is left as it is, so two\n * strings differing only in that notation are written alike. Keys taken from untrusted data can therefore collide\n * into one duplicate key of the emitted mapping.\n */\nexport function serializeForPromptInTag(value: unknown, tagName: string | readonly string[]): string {\n const escapedTags = typeof tagName === 'string' ? [tagName] : tagName;\n const tagPatterns = escapedTags.flatMap(createTagPatterns);\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '', tagPatterns })}\\n`, 'yaml');\n}\n\n/**\n * Replaces `<tagName` and `</tagName` not followed by `[\\w-]` with a harmless notation such as `[/tagName]`, so that\n * data embedded in a `<tagName>` element of a prompt cannot close the element and have the rest read as instructions.\n * A closing `>` is not required, since an HTML reader also closes an element on a tag carrying attribute-like junk,\n * on a trailing solidus, or on the next `>` anywhere in the prompt; the whitespace between `<` and the name is\n * dropped along with the brackets. Case is ignored deliberately: `tagName` is expected to be a literal written by\n * the caller, while the data is not.\n */\nexport function escapePromptTag(text: string, tagName: string): string {\n return text.includes('<') ? escapeTags(text, createTagPatterns(tagName)) : text;\n}\n\nfunction createTagPatterns(tagName: string): RegExp[] {\n const name = escapeRegExp(tagName);\n // Keep well-formed tags readable while also catching incomplete and malformed tags.\n return [\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})\\\\s*>`, 'giu'),\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})(?![\\\\w-])`, 'giu'),\n ];\n}\n\nfunction escapeTags(text: string, patterns: readonly RegExp[]): string {\n if (!text.includes('<')) return text;\n for (const pattern of patterns) text = text.replaceAll(pattern, '[$1$2]');\n return text;\n}\n\n/** The line opening a fenced block, with the indentation an interpolation may put before it. */\nconst BLOCK_OPENER = /^[ \\t]*((`{3,}|~{3,})[^\\n]*)$/u;\n\n/** A block interpolated after other text on its line, which no rule can tell from prose that ends in `~~~yaml`. */\nconst MISPLACED_BLOCK_OPENER = /[^\\s~][ \\t]*~{3,}yaml[ \\t]*$/u;\n\ninterface PromptBlock {\n opener: string;\n end: number;\n}\n\n/**\n * Strips template-literal indentation from Markdown headings, code fences, and triple-quote delimiters (`\"\"\"` and\n * `'''`), and collapses blank lines.\n * The contents of a fenced block are left untouched, since reindenting them would rewrite the data they carry, such\n * as the YAML of `serializeForPrompt` or the source of `toCodeBlock`. Every fenced block must be interpolated at\n * the start of a line. Only recognizable mid-line `~~~yaml` markers throw, including prose mentions;\n * this is not a complete placement validator, since preceding tildes can merge into an indistinguishable fence.\n * Blocks end at the first compatible same-character fence, which may carry trailing prose, unless a longer fence\n * of the same character opens and contains that candidate closing line. Other fence characters have no nesting\n * significance. Balance authored fences before interpolating blocks: an unmatched sample can consume part of a\n * later block and leave its tail subject to prose formatting.\n */\nexport function formatPrompt(prompt: string): string {\n const lines = prompt.split('\\n');\n const blocks = findBlocks(lines);\n let formatted = '';\n let prose = '';\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? '';\n const block = blocks[index];\n if (block === undefined) {\n // An opening fence the prompt itself writes and never closes is prose, and so is every line inside a block.\n if (MISPLACED_BLOCK_OPENER.test(line)) {\n throw new TypeError('Interpolate a serializeForPrompt block at the start of a line');\n }\n prose += `${line}\\n`;\n continue;\n }\n // Dropping the opener's indentation keeps four spaces of it from turning the fence into an indented code block.\n const blockLines = lines.slice(index + 1, block.end + 1);\n blockLines[blockLines.length - 1] = blockLines.at(-1)?.trimStart() ?? '';\n formatted += `${dedentPromptMarkers(prose)}${block.opener}\\n${blockLines.join('\\n')}`;\n prose = '\\n';\n index = block.end;\n }\n return (formatted + dedentPromptMarkers(prose)).trim();\n}\n\nfunction findBlocks(lines: string[]): (PromptBlock | undefined)[] {\n const blocks: (PromptBlock | undefined)[] = [];\n const nestedFenceLengths = new Map<string, Uint32Array>();\n const backtickClosers: { length: number; index: number }[] = [];\n const tildeClosers: { length: number; index: number }[] = [];\n for (let index = lines.length - 1; index >= 0; index--) {\n const match = BLOCK_OPENER.exec(lines[index] ?? '');\n const opener = match?.[1]?.trimEnd();\n const fence = match?.[2];\n if (opener === undefined || fence === undefined) continue;\n const closers = fence[0] === '`' ? backtickClosers : tildeClosers;\n let low = 0;\n let high = closers.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if ((closers[middle]?.length ?? 0) >= fence.length) low = middle + 1;\n else high = middle;\n }\n let end = closers[low - 1]?.index ?? -1;\n if (/^(?:[ \\t].*)?$/u.test((match?.[1] ?? '').slice(fence.length))) {\n // A nearer, longer closer also closes every fence that a shorter one could close.\n while (closers.length > 0 && (closers.at(-1)?.length ?? 0) <= fence.length) closers.pop();\n closers.push({ length: fence.length, index });\n }\n let lengths = nestedFenceLengths.get(fence[0] ?? '');\n if (end !== -1 && lengths !== undefined && maximumFenceLengthAt(lengths, end) > fence.length) end = -1;\n if (end !== -1) {\n blocks[index] = { opener, end };\n if (lengths === undefined) {\n lengths = new Uint32Array(lines.length * 2);\n nestedFenceLengths.set(fence[0] ?? '', lengths);\n }\n recordFenceLength(lengths, index + 1, end, fence.length);\n }\n }\n return blocks;\n}\n\nfunction maximumFenceLengthAt(lengths: Uint32Array, index: number): number {\n let maximum = 0;\n for (let node = index + lengths.length / 2; node > 0; node >>>= 1) {\n maximum = Math.max(maximum, lengths[node] ?? 0);\n }\n return maximum;\n}\n\nfunction recordFenceLength(lengths: Uint32Array, start: number, end: number, length: number): void {\n let left = start + lengths.length / 2;\n let right = end + lengths.length / 2;\n while (left <= right) {\n if (left % 2 === 1) {\n lengths[left] = Math.max(lengths[left] ?? 0, length);\n left++;\n }\n if (right % 2 === 0) {\n lengths[right] = Math.max(lengths[right] ?? 0, length);\n right--;\n }\n left >>>= 1;\n right >>>= 1;\n }\n}\n\nfunction dedentPromptMarkers(text: string): string {\n // Consume failed marker searches, and start blank-line searches only at whitespace-run boundaries.\n return text\n .replaceAll(/\\n\\s+(\"\"\"|'''|```|~{3,})|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/\\n\\s+(#+\\s)|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/(?<!\\s)\\s*\\n\\s*\\n/gu, '\\n\\n');\n}\n\nfunction replaceIndentedMarker(match: string, marker: string | undefined): string {\n return marker === undefined ? match : `\\n${marker}`;\n}\n\n/** Cuts text down to `maxLength` characters, marking it so that an LLM reads the rest as missing rather than absent. */\nexport function truncateForPrompt(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${sliceWithoutSplittingSurrogates(text, maxLength)}\\n...<truncated>`;\n}\n\n// The functions below reproduce `stringify(value, { lineWidth: 0, aliasDuplicateObjects: false, blockQuote: 'literal' })`\n// of the `yaml` package, except that `toSerializable` makes `Error`s and `RegExp`s readable instead of `{}`.\n\nfunction stringifyValue(value: unknown, ctx: Context): string {\n switch (typeof value) {\n case 'undefined': {\n return 'null';\n }\n case 'boolean': {\n return String(value);\n }\n case 'number': {\n return stringifyNumber(value);\n }\n case 'bigint': {\n return String(value);\n }\n case 'string': {\n return stringifyString(value, ctx);\n }\n case 'object': {\n return stringifyObject(value, ctx);\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\n}\n\nfunction stringifyObject(value: object | null, ctx: Context): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return stringifyArray(value, ctx);\n return stringifyEntries(value instanceof Map ? value : lazyEntries(value as Record<string, unknown>), ctx);\n}\n\nfunction stringifyArray(value: unknown[], ctx: Context): string {\n if (value.length === 0) return '[]';\n const separator = `\\n${ctx.indent}`;\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n let str = '';\n for (const item of value) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n}\n\nfunction stringifyEntries(entries: Iterable<[unknown, unknown]>, ctx: Context): string {\n const separator = `\\n${ctx.indent}`;\n let str = '';\n for (const [key, item] of entries) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n}\n\n// Reads each property only when its turn comes, as serializing an earlier one may run a `toJSON` that mutates the record.\nfunction* lazyEntries(record: Record<string, unknown>): Generator<[unknown, unknown]> {\n for (const key of Object.keys(record)) yield [key, record[key]];\n}\n\nfunction stringifyPair(rawKey: unknown, rawValue: unknown, ctx: Context): string {\n const indent = ctx.indent + INDENT_STEP;\n const key = toSerializable(rawKey);\n const value = toSerializable(rawValue);\n const keyStr = stringifyValue(key, { indent, implicitKey: true, tagPatterns: ctx.tagPatterns });\n const valueStr = stringifyValue(value, { indent, tagPatterns: ctx.tagPatterns });\n if (isCollection(key) || keyStr.length > MAX_IMPLICIT_KEY_LENGTH) return `? ${keyStr}\\n${ctx.indent}: ${valueStr}`;\n const isBlockCollection = isCollection(value) && valueStr !== '[]' && valueStr !== '{}';\n return `${keyStr}:${isBlockCollection ? `\\n${indent}` : ' '}${valueStr}`;\n}\n\nfunction toSerializable(rawValue: unknown): unknown {\n // Like `yaml`, call `toJSON` only once, but classify its result.\n const value =\n typeof (rawValue as { toJSON?: unknown } | null | undefined)?.toJSON === 'function'\n ? (rawValue as { toJSON: () => unknown }).toJSON()\n : rawValue;\n if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Map) return value;\n // oxlint-disable-next-line unicorn/no-instanceof-builtins -- boxed primitives have no typeof check; `yaml` unwraps them the same way.\n if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof BigInt) {\n return value.valueOf();\n }\n if (value instanceof Error) {\n // `name` and `message` usually live on the prototype, and `cause` and `errors` are not enumerable.\n return {\n name: value.name,\n message: value.message,\n ...Object.fromEntries(Object.entries(value)),\n ...('cause' in value && { cause: value.cause }),\n ...(value instanceof AggregateError && { errors: value.errors }),\n };\n }\n if (value instanceof RegExp) return String(value);\n return Symbol.iterator in value ? [...(value as Iterable<unknown>)] : value;\n}\n\nfunction isCollection(value: unknown): value is object {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringifyNumber(value: number): string {\n if (Number.isNaN(value)) return '.nan';\n if (!Number.isFinite(value)) return value < 0 ? '-.inf' : '.inf';\n return Object.is(value, -0) ? '-0' : JSON.stringify(value);\n}\n\nfunction stringifyString(rawValue: string, ctx: Context): string {\n const value = ctx.tagPatterns === undefined ? rawValue : escapeTags(rawValue, ctx.tagPatterns);\n // A closing quote separates an unfinished tag prefix from the next mapping key.\n if (ctx.tagPatterns?.length && /<\\s*(?:\\/\\s*)?$/u.test(value)) return doubleQuotedString(value, ctx, false);\n // oxlint-disable-next-line no-control-regex -- control characters and lone surrogates can only be written escaped.\n return /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F\\u{D800}-\\u{DFFF}]/u.test(value)\n ? doubleQuotedString(value, ctx)\n : plainString(value, ctx);\n}\n\nfunction plainString(value: string, ctx: Context): string {\n const { implicitKey, indent } = ctx;\n if (value.includes('\\n')) return implicitKey ? quotedString(value, ctx) : blockString(value, ctx);\n if (UNSAFE_PLAIN_START.test(value) || UNSAFE_PLAIN_END.test(value.slice(-1)) || UNSAFE_PLAIN_MIDDLE.test(value)) {\n return quotedString(value, ctx);\n }\n if (containsDocumentMarker(value)) {\n if (indent === '') return blockString(value, ctx);\n if (implicitKey && indent === INDENT_STEP) return quotedString(value, ctx);\n }\n return NON_STRING_SCALAR.test(value) ? quotedString(value, ctx) : value;\n}\n\nfunction blockString(value: string, ctx: Context): string {\n let endStart = value.length;\n while (endStart > 0 && isBlockEndWhitespace(value.codePointAt(endStart - 1))) endStart--;\n let end = value.slice(endStart);\n // A block scalar cannot end with a whitespace-only line.\n if (end.includes('\\n') && !end.endsWith('\\n')) return quotedString(value, ctx);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n\n const endNewlinePos = end.indexOf('\\n');\n const chomp = endNewlinePos === -1 ? '-' : value === end || endNewlinePos !== end.length - 1 ? '+' : '';\n if (end) {\n value = value.slice(0, -end.length);\n if (end.endsWith('\\n')) end = end.slice(0, -1);\n end = end.replaceAll(/(?:^|(?<!\\n))\\n+(?!\\n|$)/gu, `$&${indent}`);\n }\n\n const leadingWhitespace = /^[\\n ]*/u.exec(value)?.[0] ?? '';\n const startsWithSpace = leadingWhitespace.includes(' ');\n let start = value.slice(0, leadingWhitespace.lastIndexOf('\\n') + 1);\n if (start) {\n value = value.slice(start.length);\n start = start.replaceAll(/\\n+/gu, `$&${indent}`);\n }\n\n const header = (startsWithSpace ? (indent ? '2' : '1') : '') + chomp;\n return `|${header}\\n${indent}${start}${indentLines(value, indent)}${end}`;\n}\n\nfunction quotedString(value: string, ctx: Context): string {\n return value.includes('\"') && !value.includes(\"'\") ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx);\n}\n\nfunction singleQuotedString(value: string, ctx: Context): string {\n // Every multi-line value reaching here would contain a space or tab next to a newline or be an implicit key,\n // both of which need double quotes.\n if (value.includes('\\n')) return doubleQuotedString(value, ctx);\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction doubleQuotedString(value: string, ctx: Context, foldMultiline = !ctx.implicitKey): string {\n const json = JSON.stringify(value);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n let str = '';\n let start = 0;\n for (let i = 0; i < json.length; i++) {\n let ch = json[i];\n if (ch === ' ' && json[i + 1] === '\\\\' && json[i + 2] === 'n') {\n // A space before a line break would be folded away unless escaped.\n str += `${json.slice(start, i)}\\\\ `;\n i++;\n start = i;\n ch = '\\\\';\n }\n if (ch !== '\\\\') continue;\n if (json[i + 1] === 'u') {\n const code = json.slice(i + 2, i + 6);\n str +=\n json.slice(start, i) +\n (SHORT_UNICODE_ESCAPES[code] ?? (code.startsWith('00') ? `\\\\x${code.slice(2)}` : json.slice(i, i + 6)));\n i += 5;\n start = i + 1;\n } else if (\n json[i + 1] === 'n' &&\n foldMultiline &&\n json[i + 2] !== '\"' &&\n json.length >= MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH\n ) {\n // Folding turns the first of the written line breaks back into a space, hence the extra one.\n str += `${json.slice(start, i)}\\n\\n`;\n while (json[i + 2] === '\\\\' && json[i + 3] === 'n' && json[i + 4] !== '\"') {\n str += '\\n';\n i += 2;\n }\n str += indent;\n // A space after a line break would be folded away unless escaped.\n if (json[i + 2] === ' ') str += '\\\\';\n i++;\n start = i + 1;\n } else {\n i++;\n }\n }\n return start ? str + json.slice(start) : json;\n}\n\nfunction containsDocumentMarker(value: string): boolean {\n return /^(?:%|---|\\.\\.\\.)/mu.test(value);\n}\n\n/** Equivalent to `text.replaceAll(/\\n+/g, `$&${indent}`)` for text without leading or trailing newlines, but faster. */\nfunction indentLines(text: string, indent: string): string {\n const lines = text.split('\\n');\n let str = lines[0] ?? '';\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n str += line ? `\\n${indent}${line}` : '\\n';\n }\n return str;\n}\n\nfunction isBlockEndWhitespace(codePoint: number | undefined): boolean {\n return codePoint === 0x0A || codePoint === 0x09 || codePoint === 0x20;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prompt.js","names":["NON_STRING_SCALAR","UNSAFE_PLAIN_START","UNSAFE_PLAIN_MIDDLE","UNSAFE_PLAIN_END","SHORT_UNICODE_ESCAPES","serializeForPrompt","toTildeCodeBlock","stringifyValue","toSerializable","value","serializeForPromptInTag","tagPatterns","tagName","createTagPatterns","escapePromptTag","text","escapeTags","name","escapeRegExp","pattern","patterns","BLOCK_OPENER","MISPLACED_BLOCK_OPENER","formatPrompt","lines","prompt","blocks","findBlocks","formatted","prose","index","line","block","blockLines","dedentPromptMarkers","nestedFenceLengths","backtickClosers","tildeClosers","match","opener","fence","closers","low","high","middle","end","lengths","maximumFenceLengthAt","recordFenceLength","maximum","node","left","start","right","length","replaceIndentedMarker","marker","truncateForPrompt","maxLength","sliceWithoutSplittingSurrogates","stringifyNumber","stringifyString","ctx","stringifyObject","stringifyArray","stringifyEntries","lazyEntries","separator","itemCtx","str","item","key","entries","stringifyPair","record","indent","rawKey","rawValue","keyStr","valueStr","isCollection","doubleQuotedString","plainString","implicitKey","quotedString","blockString","containsDocumentMarker","endStart","isBlockEndWhitespace","endNewlinePos","chomp","leadingWhitespace","startsWithSpace","indentLines","singleQuotedString","json","i","ch","code","foldMultiline","codePoint"],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, sliceWithoutSplittingSurrogates, toTildeCodeBlock } from './text.js';\n\nconst INDENT_STEP = ' ';\nconst MAX_IMPLICIT_KEY_LENGTH = 1024;\nconst MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH = 40;\n\n/** Strings matching this would be read back as a non-string scalar (null, bool, int, or float), so they must be quoted. */\nconst NON_STRING_SCALAR =\n /^(?:~|[Nn]ull|NULL|[Tt]rue|TRUE|[Ff]alse|FALSE|0o[0-7]+|[-+]?[0-9]+|0x[0-9a-fA-F]+|[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN|[-+]?(?:\\.[0-9]+|[0-9]+(?:\\.[0-9]*)?)[eE][-+]?[0-9]+|[-+]?(?:\\.[0-9]+|[0-9]+\\.[0-9]*))?$/u;\n\n// A single-line string must be quoted if it matches any of these. Checking the start, the middle, and the end separately\n// is much faster on long strings than one regex whose anchored alternatives are retried at every position.\nconst UNSAFE_PLAIN_START = /^(?:[\\t ,[\\]{}#&*!|>'\"%@`]|[?-](?:[ \\t]|$))/u;\nconst UNSAFE_PLAIN_MIDDLE = /:[ \\t]|[ \\t]#/u;\nconst UNSAFE_PLAIN_END = /[\\t :]$/u;\n\nconst SHORT_UNICODE_ESCAPES: Record<string, string> = {\n '0000': String.raw`\\0`,\n '0007': String.raw`\\a`,\n '000b': String.raw`\\v`,\n '001b': String.raw`\\e`,\n};\n\ninterface Context {\n indent: string;\n implicitKey?: boolean;\n tagPatterns?: readonly RegExp[];\n}\n\n/**\n * Serializes data into a code block to embed in an LLM prompt, so that the LLM can read its contents.\n * Accepts JSON data (honoring `toJSON` such as that of `Date`) as well as `undefined`, `NaN`, `Infinity`, bigints,\n * `Map`s (including non-string keys), `Set`s and other iterables, `Error`s (with their name, message, cause, and own properties),\n * and `RegExp`s. Symbols and functions without `toJSON` throw. The output is not meant to be deserialized back into the original value.\n * The format inside the code block is an implementation detail and may change.\n */\nexport function serializeForPrompt(value: unknown): string {\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '' })}\\n`, 'yaml');\n}\n\n/**\n * Serializes data with `serializeForPrompt` for embedding inside a `<tagName>` element of a prompt, escaping that tag\n * in every string so that untrusted data cannot close the element and have the rest read as instructions.\n * The caller must wrap the result in the same tag.\n *\n * Only the named elements are protected, so a block nested inside other tagged elements takes every enclosing name.\n * Escaping the finished block instead is not an option: it would turn a plain scalar holding the escaped notation\n * into a YAML flow sequence, since the quoting of each scalar is decided as it is written.\n *\n * The escaped notation is the one `escapePromptTag` writes, and text that already holds it is left as it is, so two\n * strings differing only in that notation are written alike. Keys taken from untrusted data can therefore collide\n * into one duplicate key of the emitted mapping.\n */\nexport function serializeForPromptInTag(value: unknown, tagName: string | readonly string[]): string {\n const escapedTags = typeof tagName === 'string' ? [tagName] : tagName;\n const tagPatterns = escapedTags.flatMap(createTagPatterns);\n return toTildeCodeBlock(`${stringifyValue(toSerializable(value), { indent: '', tagPatterns })}\\n`, 'yaml');\n}\n\n/**\n * Replaces `<tagName` and `</tagName` not followed by `[\\w-]` with a harmless notation such as `[/tagName]`, so that\n * data embedded in a `<tagName>` element of a prompt cannot close the element and have the rest read as instructions.\n * A closing `>` is not required, since an HTML reader also closes an element on a tag carrying attribute-like junk,\n * on a trailing solidus, or on the next `>` anywhere in the prompt; the whitespace between `<` and the name is\n * dropped along with the brackets. Case is ignored deliberately: `tagName` is expected to be a literal written by\n * the caller, while the data is not.\n */\nexport function escapePromptTag(text: string, tagName: string): string {\n return text.includes('<') ? escapeTags(text, createTagPatterns(tagName)) : text;\n}\n\nfunction createTagPatterns(tagName: string): RegExp[] {\n const name = escapeRegExp(tagName);\n // Keep well-formed tags readable while also catching incomplete and malformed tags.\n return [\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})\\\\s*>`, 'giu'),\n new RegExp(`<\\\\s*(?:(/)\\\\s*)?(${name})(?![\\\\w-])`, 'giu'),\n ];\n}\n\nfunction escapeTags(text: string, patterns: readonly RegExp[]): string {\n if (!text.includes('<')) return text;\n for (const pattern of patterns) text = text.replaceAll(pattern, '[$1$2]');\n return text;\n}\n\n/** The line opening a fenced block, with the indentation an interpolation may put before it. */\nconst BLOCK_OPENER = /^[ \\t]*((`{3,}|~{3,})[^\\n]*)$/u;\n\n/** A block interpolated after other text on its line, which no rule can tell from prose that ends in `~~~yaml`. */\nconst MISPLACED_BLOCK_OPENER = /[^\\s~][ \\t]*~{3,}yaml[ \\t]*$/u;\n\ninterface PromptBlock {\n opener: string;\n end: number;\n}\n\n/**\n * Strips template-literal indentation from Markdown headings, code fences, and triple-quote delimiters (`\"\"\"` and\n * `'''`), and collapses blank lines.\n * The contents of a fenced block are left untouched, since reindenting them would rewrite the data they carry, such\n * as the YAML of `serializeForPrompt` or the source of `toCodeBlock`. Every fenced block must be interpolated at\n * the start of a line. Only recognizable mid-line `~~~yaml` markers throw, including prose mentions;\n * this is not a complete placement validator, since preceding tildes can merge into an indistinguishable fence.\n * Blocks end at the first compatible same-character fence, which may carry trailing prose, unless a longer fence\n * of the same character opens and contains that candidate closing line. Other fence characters have no nesting\n * significance. Balance authored fences before interpolating blocks: an unmatched sample can consume part of a\n * later block and leave its tail subject to prose formatting.\n */\nexport function formatPrompt(prompt: string): string {\n const lines = prompt.split('\\n');\n const blocks = findBlocks(lines);\n let formatted = '';\n let prose = '';\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? '';\n const block = blocks[index];\n if (block === undefined) {\n // An opening fence the prompt itself writes and never closes is prose, and so is every line inside a block.\n if (MISPLACED_BLOCK_OPENER.test(line)) {\n throw new TypeError('Interpolate a serializeForPrompt block at the start of a line');\n }\n prose += `${line}\\n`;\n continue;\n }\n // Dropping the opener's indentation keeps four spaces of it from turning the fence into an indented code block.\n const blockLines = lines.slice(index + 1, block.end + 1);\n blockLines[blockLines.length - 1] = blockLines.at(-1)?.trimStart() ?? '';\n formatted += `${dedentPromptMarkers(prose)}${block.opener}\\n${blockLines.join('\\n')}`;\n prose = '\\n';\n index = block.end;\n }\n return (formatted + dedentPromptMarkers(prose)).trim();\n}\n\nfunction findBlocks(lines: string[]): (PromptBlock | undefined)[] {\n const blocks: (PromptBlock | undefined)[] = [];\n const nestedFenceLengths = new Map<string, Uint32Array>();\n const backtickClosers: { length: number; index: number }[] = [];\n const tildeClosers: { length: number; index: number }[] = [];\n for (let index = lines.length - 1; index >= 0; index--) {\n const match = BLOCK_OPENER.exec(lines[index] ?? '');\n const opener = match?.[1]?.trimEnd();\n const fence = match?.[2];\n if (opener === undefined || fence === undefined) continue;\n const closers = fence[0] === '`' ? backtickClosers : tildeClosers;\n let low = 0;\n let high = closers.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if ((closers[middle]?.length ?? 0) >= fence.length) low = middle + 1;\n else high = middle;\n }\n let end = closers[low - 1]?.index ?? -1;\n if (/^(?:[ \\t].*)?$/u.test((match?.[1] ?? '').slice(fence.length))) {\n // A nearer, longer closer also closes every fence that a shorter one could close.\n while (closers.length > 0 && (closers.at(-1)?.length ?? 0) <= fence.length) closers.pop();\n closers.push({ length: fence.length, index });\n }\n let lengths = nestedFenceLengths.get(fence[0] ?? '');\n if (end !== -1 && lengths !== undefined && maximumFenceLengthAt(lengths, end) > fence.length) end = -1;\n if (end !== -1) {\n blocks[index] = { opener, end };\n if (lengths === undefined) {\n lengths = new Uint32Array(lines.length * 2);\n nestedFenceLengths.set(fence[0] ?? '', lengths);\n }\n recordFenceLength(lengths, index + 1, end, fence.length);\n }\n }\n return blocks;\n}\n\nfunction maximumFenceLengthAt(lengths: Uint32Array, index: number): number {\n let maximum = 0;\n for (let node = index + lengths.length / 2; node > 0; node >>>= 1) {\n maximum = Math.max(maximum, lengths[node] ?? 0);\n }\n return maximum;\n}\n\nfunction recordFenceLength(lengths: Uint32Array, start: number, end: number, length: number): void {\n let left = start + lengths.length / 2;\n let right = end + lengths.length / 2;\n while (left <= right) {\n if (left % 2 === 1) {\n lengths[left] = Math.max(lengths[left] ?? 0, length);\n left++;\n }\n if (right % 2 === 0) {\n lengths[right] = Math.max(lengths[right] ?? 0, length);\n right--;\n }\n left >>>= 1;\n right >>>= 1;\n }\n}\n\nfunction dedentPromptMarkers(text: string): string {\n // Consume failed marker searches, and start blank-line searches only at whitespace-run boundaries.\n return text\n .replaceAll(/\\n\\s+(\"\"\"|'''|```|~{3,})|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/\\n\\s+(#+\\s)|\\n\\s*/gu, replaceIndentedMarker)\n .replaceAll(/(?<!\\s)\\s*\\n\\s*\\n/gu, '\\n\\n');\n}\n\nfunction replaceIndentedMarker(match: string, marker: string | undefined): string {\n return marker === undefined ? match : `\\n${marker}`;\n}\n\n/** Cuts text down to `maxLength` characters, marking it so that an LLM reads the rest as missing rather than absent. */\nexport function truncateForPrompt(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${sliceWithoutSplittingSurrogates(text, maxLength)}\\n...<truncated>`;\n}\n\n// The functions below reproduce `stringify(value, { lineWidth: 0, aliasDuplicateObjects: false, blockQuote: 'literal' })`\n// of the `yaml` package, except that `toSerializable` makes `Error`s and `RegExp`s readable instead of `{}`.\n\nfunction stringifyValue(value: unknown, ctx: Context): string {\n switch (typeof value) {\n case 'undefined': {\n return 'null';\n }\n case 'boolean': {\n return String(value);\n }\n case 'number': {\n return stringifyNumber(value);\n }\n case 'bigint': {\n return String(value);\n }\n case 'string': {\n return stringifyString(value, ctx);\n }\n case 'object': {\n return stringifyObject(value, ctx);\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\n}\n\nfunction stringifyObject(value: object | null, ctx: Context): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return stringifyArray(value, ctx);\n return stringifyEntries(value instanceof Map ? value : lazyEntries(value as Record<string, unknown>), ctx);\n}\n\nfunction stringifyArray(value: unknown[], ctx: Context): string {\n if (value.length === 0) return '[]';\n const separator = `\\n${ctx.indent}`;\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n let str = '';\n for (const item of value) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n}\n\nfunction stringifyEntries(entries: Iterable<[unknown, unknown]>, ctx: Context): string {\n const separator = `\\n${ctx.indent}`;\n let str = '';\n for (const [key, item] of entries) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n}\n\n// Reads each property only when its turn comes, as serializing an earlier one may run a `toJSON` that mutates the record.\nfunction* lazyEntries(record: Record<string, unknown>): Generator<[unknown, unknown]> {\n for (const key of Object.keys(record)) yield [key, record[key]];\n}\n\nfunction stringifyPair(rawKey: unknown, rawValue: unknown, ctx: Context): string {\n const indent = ctx.indent + INDENT_STEP;\n const key = toSerializable(rawKey);\n const value = toSerializable(rawValue);\n const keyStr = stringifyValue(key, { indent, implicitKey: true, tagPatterns: ctx.tagPatterns });\n const valueStr = stringifyValue(value, { indent, tagPatterns: ctx.tagPatterns });\n if (isCollection(key) || keyStr.length > MAX_IMPLICIT_KEY_LENGTH) return `? ${keyStr}\\n${ctx.indent}: ${valueStr}`;\n const isBlockCollection = isCollection(value) && valueStr !== '[]' && valueStr !== '{}';\n return `${keyStr}:${isBlockCollection ? `\\n${indent}` : ' '}${valueStr}`;\n}\n\nfunction toSerializable(rawValue: unknown): unknown {\n // Like `yaml`, call `toJSON` only once, but classify its result.\n const value =\n typeof (rawValue as { toJSON?: unknown } | null | undefined)?.toJSON === 'function'\n ? (rawValue as { toJSON: () => unknown }).toJSON()\n : rawValue;\n if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Map) return value;\n // oxlint-disable-next-line unicorn/no-instanceof-builtins -- boxed primitives have no typeof check; `yaml` unwraps them the same way.\n if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof BigInt) {\n return value.valueOf();\n }\n if (value instanceof Error) {\n // `name` and `message` usually live on the prototype, and `cause` and `errors` are not enumerable.\n return {\n name: value.name,\n message: value.message,\n ...Object.fromEntries(Object.entries(value)),\n ...('cause' in value && { cause: value.cause }),\n ...(value instanceof AggregateError && { errors: value.errors }),\n };\n }\n if (value instanceof RegExp) return String(value);\n return Symbol.iterator in value ? [...(value as Iterable<unknown>)] : value;\n}\n\nfunction isCollection(value: unknown): value is object {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringifyNumber(value: number): string {\n if (Number.isNaN(value)) return '.nan';\n if (!Number.isFinite(value)) return value < 0 ? '-.inf' : '.inf';\n return Object.is(value, -0) ? '-0' : JSON.stringify(value);\n}\n\nfunction stringifyString(rawValue: string, ctx: Context): string {\n const value = ctx.tagPatterns === undefined ? rawValue : escapeTags(rawValue, ctx.tagPatterns);\n // A closing quote separates an unfinished tag prefix from the next mapping key.\n if (ctx.tagPatterns?.length && /<\\s*(?:\\/\\s*)?$/u.test(value)) return doubleQuotedString(value, ctx, false);\n // oxlint-disable-next-line no-control-regex -- control characters and lone surrogates can only be written escaped.\n return /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F\\u{D800}-\\u{DFFF}]/u.test(value)\n ? doubleQuotedString(value, ctx)\n : plainString(value, ctx);\n}\n\nfunction plainString(value: string, ctx: Context): string {\n const { implicitKey, indent } = ctx;\n if (value.includes('\\n')) return implicitKey ? quotedString(value, ctx) : blockString(value, ctx);\n if (UNSAFE_PLAIN_START.test(value) || UNSAFE_PLAIN_END.test(value.slice(-1)) || UNSAFE_PLAIN_MIDDLE.test(value)) {\n return quotedString(value, ctx);\n }\n if (containsDocumentMarker(value)) {\n if (indent === '') return blockString(value, ctx);\n if (implicitKey && indent === INDENT_STEP) return quotedString(value, ctx);\n }\n return NON_STRING_SCALAR.test(value) ? quotedString(value, ctx) : value;\n}\n\nfunction blockString(value: string, ctx: Context): string {\n let endStart = value.length;\n while (endStart > 0 && isBlockEndWhitespace(value.codePointAt(endStart - 1))) endStart--;\n let end = value.slice(endStart);\n // A block scalar cannot end with a whitespace-only line.\n if (end.includes('\\n') && !end.endsWith('\\n')) return quotedString(value, ctx);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n\n const endNewlinePos = end.indexOf('\\n');\n const chomp = endNewlinePos === -1 ? '-' : value === end || endNewlinePos !== end.length - 1 ? '+' : '';\n if (end) {\n value = value.slice(0, -end.length);\n if (end.endsWith('\\n')) end = end.slice(0, -1);\n end = end.replaceAll(/(?:^|(?<!\\n))\\n+(?!\\n|$)/gu, `$&${indent}`);\n }\n\n const leadingWhitespace = /^[\\n ]*/u.exec(value)?.[0] ?? '';\n const startsWithSpace = leadingWhitespace.includes(' ');\n let start = value.slice(0, leadingWhitespace.lastIndexOf('\\n') + 1);\n if (start) {\n value = value.slice(start.length);\n start = start.replaceAll(/\\n+/gu, `$&${indent}`);\n }\n\n const header = (startsWithSpace ? (indent ? '2' : '1') : '') + chomp;\n return `|${header}\\n${indent}${start}${indentLines(value, indent)}${end}`;\n}\n\nfunction quotedString(value: string, ctx: Context): string {\n return value.includes('\"') && !value.includes(\"'\") ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx);\n}\n\nfunction singleQuotedString(value: string, ctx: Context): string {\n // Every multi-line value reaching here would contain a space or tab next to a newline or be an implicit key,\n // both of which need double quotes.\n if (value.includes('\\n')) return doubleQuotedString(value, ctx);\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction doubleQuotedString(value: string, ctx: Context, foldMultiline = !ctx.implicitKey): string {\n const json = JSON.stringify(value);\n const indent = ctx.indent || (containsDocumentMarker(value) ? INDENT_STEP : '');\n let str = '';\n let start = 0;\n for (let i = 0; i < json.length; i++) {\n let ch = json[i];\n if (ch === ' ' && json[i + 1] === '\\\\' && json[i + 2] === 'n') {\n // A space before a line break would be folded away unless escaped.\n str += `${json.slice(start, i)}\\\\ `;\n i++;\n start = i;\n ch = '\\\\';\n }\n if (ch !== '\\\\') continue;\n if (json[i + 1] === 'u') {\n const code = json.slice(i + 2, i + 6);\n str +=\n json.slice(start, i) +\n (SHORT_UNICODE_ESCAPES[code] ?? (code.startsWith('00') ? `\\\\x${code.slice(2)}` : json.slice(i, i + 6)));\n i += 5;\n start = i + 1;\n } else if (\n json[i + 1] === 'n' &&\n foldMultiline &&\n json[i + 2] !== '\"' &&\n json.length >= MIN_MULTI_LINE_DOUBLE_QUOTED_LENGTH\n ) {\n // Folding turns the first of the written line breaks back into a space, hence the extra one.\n str += `${json.slice(start, i)}\\n\\n`;\n while (json[i + 2] === '\\\\' && json[i + 3] === 'n' && json[i + 4] !== '\"') {\n str += '\\n';\n i += 2;\n }\n str += indent;\n // A space after a line break would be folded away unless escaped.\n if (json[i + 2] === ' ') str += '\\\\';\n i++;\n start = i + 1;\n } else {\n i++;\n }\n }\n return start ? str + json.slice(start) : json;\n}\n\nfunction containsDocumentMarker(value: string): boolean {\n return /^(?:%|---|\\.\\.\\.)/mu.test(value);\n}\n\n/** Equivalent to `text.replaceAll(/\\n+/g, `$&${indent}`)` for text without leading or trailing newlines, but faster. */\nfunction indentLines(text: string, indent: string): string {\n const lines = text.split('\\n');\n let str = lines[0] ?? '';\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n str += line ? `\\n${indent}${line}` : '\\n';\n }\n return str;\n}\n\nfunction isBlockEndWhitespace(codePoint: number | undefined): boolean {\n return codePoint === 0x0a || codePoint === 0x09 || codePoint === 0x20;\n}\n"],"mappings":"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"}
|
package/dist/text.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.cjs","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","shortestFence","fenceChar","longestRun","start","end","languageStart","fence","truncate","maxLength","ellipsis","sliceWithoutSplittingSurrogates","lastKeptUnit","htmlEntities","htmlSpecialCharactersPattern","escapeHtml","character"],"sources":["../src/text.ts"],"sourcesContent":["/** Escapes every RegExp metacharacter so the text matches itself literally. */\nexport function escapeRegExp(text: string): string {\n return text.replaceAll(/[$()*+.?[\\\\\\]^{|}]/gu, String.raw`\\$&`);\n}\n\n/** A POSIX shell word: safe characters pass through, anything else is single-quoted. */\nexport function quoteForShell(value: string): string {\n return /^[\\w./-]+$/u.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`;\n}\n\n/**\n * Wraps text in a Markdown code block fenced with tildes, which are rarer than backticks in Markdown content.\n * The fence is longer than any run of tildes in the text so that the text cannot close it.\n */\nexport function toTildeCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('~', text, language);\n}\n\n/**\n * Wraps text in a Markdown code block fenced with backticks.\n * The fence is longer than any run of backticks in the text so that the text cannot close it.\n */\nexport function toCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('`', text, language);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n const shortestFence = fenceChar.repeat(3);\n let longestRun = 2;\n let start = text.indexOf(shortestFence);\n while (start !== -1) {\n let end = start + 3;\n while (text[end] === fenceChar) end++;\n longestRun = Math.max(longestRun, end - start);\n start = text.indexOf(shortestFence, end);\n }\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = fenceChar.repeat(longestRun + 1 + languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\n/**\n * Shortens text to at most `maxLength` UTF-16 code units, including the `ellipsis` that marks the cut.\n * Throws a `RangeError` when `maxLength` cannot hold the ellipsis.\n */\nexport function truncate(text: string, maxLength: number, ellipsis = '…'): string {\n if (text.length <= maxLength) return text;\n if (maxLength < ellipsis.length) {\n throw new RangeError(`maxLength (${maxLength}) must be at least the ellipsis length (${ellipsis.length})`);\n }\n return `${sliceWithoutSplittingSurrogates(text, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\n/**\n * Returns the first `end` code units of `text`, one fewer when the last kept unit is a high surrogate, whose low half\n * is either cut off or absent from `text`, because an unpaired surrogate is replaced with U+FFFD by encoders.\n */\nexport function sliceWithoutSplittingSurrogates(text: string, end: number): string {\n // oxlint-disable-next-line unicorn/prefer-code-point -- the check is on a single UTF-16 code unit\n const lastKeptUnit = text.charCodeAt(end - 1);\n return text.slice(0, lastKeptUnit >=
|
|
1
|
+
{"version":3,"file":"text.cjs","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","shortestFence","fenceChar","longestRun","start","end","languageStart","fence","truncate","maxLength","ellipsis","sliceWithoutSplittingSurrogates","lastKeptUnit","htmlEntities","htmlSpecialCharactersPattern","escapeHtml","character"],"sources":["../src/text.ts"],"sourcesContent":["/** Escapes every RegExp metacharacter so the text matches itself literally. */\nexport function escapeRegExp(text: string): string {\n return text.replaceAll(/[$()*+.?[\\\\\\]^{|}]/gu, String.raw`\\$&`);\n}\n\n/** A POSIX shell word: safe characters pass through, anything else is single-quoted. */\nexport function quoteForShell(value: string): string {\n return /^[\\w./-]+$/u.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`;\n}\n\n/**\n * Wraps text in a Markdown code block fenced with tildes, which are rarer than backticks in Markdown content.\n * The fence is longer than any run of tildes in the text so that the text cannot close it.\n */\nexport function toTildeCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('~', text, language);\n}\n\n/**\n * Wraps text in a Markdown code block fenced with backticks.\n * The fence is longer than any run of backticks in the text so that the text cannot close it.\n */\nexport function toCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('`', text, language);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n const shortestFence = fenceChar.repeat(3);\n let longestRun = 2;\n let start = text.indexOf(shortestFence);\n while (start !== -1) {\n let end = start + 3;\n while (text[end] === fenceChar) end++;\n longestRun = Math.max(longestRun, end - start);\n start = text.indexOf(shortestFence, end);\n }\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = fenceChar.repeat(longestRun + 1 + languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\n/**\n * Shortens text to at most `maxLength` UTF-16 code units, including the `ellipsis` that marks the cut.\n * Throws a `RangeError` when `maxLength` cannot hold the ellipsis.\n */\nexport function truncate(text: string, maxLength: number, ellipsis = '…'): string {\n if (text.length <= maxLength) return text;\n if (maxLength < ellipsis.length) {\n throw new RangeError(`maxLength (${maxLength}) must be at least the ellipsis length (${ellipsis.length})`);\n }\n return `${sliceWithoutSplittingSurrogates(text, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\n/**\n * Returns the first `end` code units of `text`, one fewer when the last kept unit is a high surrogate, whose low half\n * is either cut off or absent from `text`, because an unpaired surrogate is replaced with U+FFFD by encoders.\n */\nexport function sliceWithoutSplittingSurrogates(text: string, end: number): string {\n // oxlint-disable-next-line unicorn/prefer-code-point -- the check is on a single UTF-16 code unit\n const lastKeptUnit = text.charCodeAt(end - 1);\n return text.slice(0, lastKeptUnit >= 0xd8_00 && lastKeptUnit <= 0xdb_ff ? end - 1 : end);\n}\n\nconst htmlEntities: Readonly<Record<string, string>> = {\n '\"': '"',\n '&': '&',\n \"'\": ''',\n '<': '<',\n '>': '>',\n};\n\nconst htmlSpecialCharactersPattern = /[\"&'<>]/gu;\n\n/** Escapes `&`, `<`, `>`, `\"`, and `'`, so the result is safe in HTML text and in quoted attribute values. */\nexport function escapeHtml(text: string): string {\n // Measured in V8 and JavaScriptCore: a native `search` first is faster than `replaceAll` alone for text without these\n // characters, and one native `replaceAll` beats a per-code-unit loop for text where they are rare.\n return text.search(htmlSpecialCharactersPattern) === -1\n ? text\n : text.replaceAll(htmlSpecialCharactersPattern, (character) => htmlEntities[character] as string);\n}\n"],"mappings":"aACA,SAAgBA,EAAa,EAAsB,CACjD,OAAOC,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgBC,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAKC,CAAK,EAAIA,EAAQ,IAAIA,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF,CAMA,SAAgBC,EAAiB,EAAc,EAAW,GAAY,CACpE,OAAOC,EAAkB,IAAKJ,EAAMK,CAAQ,CAC9C,CAMA,SAAgBC,EAAY,EAAc,EAAW,GAAY,CAC/D,OAAOF,EAAkB,IAAKJ,EAAMK,CAAQ,CAC9C,CAEA,SAASD,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAMG,EAAgBC,EAAU,OAAO,CAAC,EACpCC,EAAa,EACbC,EAAQV,EAAK,QAAQO,CAAa,EACtC,KAAOG,IAAU,IAAI,CACnB,IAAIC,EAAMD,EAAQ,EAClB,KAAOV,EAAKW,KAASH,GAAW,IAChC,EAAa,KAAK,IAAIC,EAAYE,EAAMD,CAAK,EAC7C,EAAQV,EAAK,QAAQO,EAAeI,CAAG,CACzC,CACA,IAAIC,EAAgB,EACpB,KAAOP,EAASO,KAAmBJ,GAAW,IAC9C,IAAMK,EAAQL,EAAU,OAAOC,EAAa,EAAIG,CAAa,EAC7D,MAAO,GAAGC,IAAQR,EAAS,MAAMO,CAAa,EAAE,IAAIZ,IAAOA,EAAK,SAAS;CAAI,EAAI,GAAK;IAAOa,GAC/F,CAMA,SAAgBC,EAAS,EAAc,EAAmB,EAAW,IAAa,CAChF,GAAId,EAAK,QAAUe,EAAW,OAAOf,EACrC,GAAIe,EAAYC,EAAS,OACvB,MAAU,WAAW,cAAcD,EAAU,0CAA0CC,EAAS,OAAO,EAAE,EAE3G,MAAO,GAAGC,EAAgCjB,EAAMe,EAAYC,EAAS,MAAM,IAAIA,GACjF,CAMA,SAAgBC,EAAgC,EAAc,EAAqB,CAEjF,IAAMC,EAAelB,EAAK,WAAWW,EAAM,CAAC,EAC5C,OAAOX,EAAK,MAAM,EAAGkB,GAAgB,OAAWA,GAAgB,MAAUP,EAAM,EAAIA,CAAG,CACzF,CAEA,MAAMQ,EAAiD,CACrD,IAAK,SACL,IAAK,QACL,IAAK,QACL,IAAK,OACL,IAAK,MACP,EAEMC,EAA+B,YAGrC,SAAgBC,EAAW,EAAsB,CAG/C,OAAOrB,EAAK,OAAOoB,CAA4B,IAAM,GACjDpB,EACAA,EAAK,WAAWoB,EAA+B,GAAcD,EAAaG,EAAoB,CACpG"}
|
package/dist/text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","shortestFence","fenceChar","longestRun","start","end","languageStart","fence","truncate","maxLength","ellipsis","sliceWithoutSplittingSurrogates","lastKeptUnit","htmlEntities","htmlSpecialCharactersPattern","escapeHtml","character"],"sources":["../src/text.ts"],"sourcesContent":["/** Escapes every RegExp metacharacter so the text matches itself literally. */\nexport function escapeRegExp(text: string): string {\n return text.replaceAll(/[$()*+.?[\\\\\\]^{|}]/gu, String.raw`\\$&`);\n}\n\n/** A POSIX shell word: safe characters pass through, anything else is single-quoted. */\nexport function quoteForShell(value: string): string {\n return /^[\\w./-]+$/u.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`;\n}\n\n/**\n * Wraps text in a Markdown code block fenced with tildes, which are rarer than backticks in Markdown content.\n * The fence is longer than any run of tildes in the text so that the text cannot close it.\n */\nexport function toTildeCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('~', text, language);\n}\n\n/**\n * Wraps text in a Markdown code block fenced with backticks.\n * The fence is longer than any run of backticks in the text so that the text cannot close it.\n */\nexport function toCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('`', text, language);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n const shortestFence = fenceChar.repeat(3);\n let longestRun = 2;\n let start = text.indexOf(shortestFence);\n while (start !== -1) {\n let end = start + 3;\n while (text[end] === fenceChar) end++;\n longestRun = Math.max(longestRun, end - start);\n start = text.indexOf(shortestFence, end);\n }\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = fenceChar.repeat(longestRun + 1 + languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\n/**\n * Shortens text to at most `maxLength` UTF-16 code units, including the `ellipsis` that marks the cut.\n * Throws a `RangeError` when `maxLength` cannot hold the ellipsis.\n */\nexport function truncate(text: string, maxLength: number, ellipsis = '…'): string {\n if (text.length <= maxLength) return text;\n if (maxLength < ellipsis.length) {\n throw new RangeError(`maxLength (${maxLength}) must be at least the ellipsis length (${ellipsis.length})`);\n }\n return `${sliceWithoutSplittingSurrogates(text, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\n/**\n * Returns the first `end` code units of `text`, one fewer when the last kept unit is a high surrogate, whose low half\n * is either cut off or absent from `text`, because an unpaired surrogate is replaced with U+FFFD by encoders.\n */\nexport function sliceWithoutSplittingSurrogates(text: string, end: number): string {\n // oxlint-disable-next-line unicorn/prefer-code-point -- the check is on a single UTF-16 code unit\n const lastKeptUnit = text.charCodeAt(end - 1);\n return text.slice(0, lastKeptUnit >=
|
|
1
|
+
{"version":3,"file":"text.js","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","shortestFence","fenceChar","longestRun","start","end","languageStart","fence","truncate","maxLength","ellipsis","sliceWithoutSplittingSurrogates","lastKeptUnit","htmlEntities","htmlSpecialCharactersPattern","escapeHtml","character"],"sources":["../src/text.ts"],"sourcesContent":["/** Escapes every RegExp metacharacter so the text matches itself literally. */\nexport function escapeRegExp(text: string): string {\n return text.replaceAll(/[$()*+.?[\\\\\\]^{|}]/gu, String.raw`\\$&`);\n}\n\n/** A POSIX shell word: safe characters pass through, anything else is single-quoted. */\nexport function quoteForShell(value: string): string {\n return /^[\\w./-]+$/u.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`;\n}\n\n/**\n * Wraps text in a Markdown code block fenced with tildes, which are rarer than backticks in Markdown content.\n * The fence is longer than any run of tildes in the text so that the text cannot close it.\n */\nexport function toTildeCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('~', text, language);\n}\n\n/**\n * Wraps text in a Markdown code block fenced with backticks.\n * The fence is longer than any run of backticks in the text so that the text cannot close it.\n */\nexport function toCodeBlock(text: string, language = ''): string {\n return toFencedCodeBlock('`', text, language);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n const shortestFence = fenceChar.repeat(3);\n let longestRun = 2;\n let start = text.indexOf(shortestFence);\n while (start !== -1) {\n let end = start + 3;\n while (text[end] === fenceChar) end++;\n longestRun = Math.max(longestRun, end - start);\n start = text.indexOf(shortestFence, end);\n }\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = fenceChar.repeat(longestRun + 1 + languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\n/**\n * Shortens text to at most `maxLength` UTF-16 code units, including the `ellipsis` that marks the cut.\n * Throws a `RangeError` when `maxLength` cannot hold the ellipsis.\n */\nexport function truncate(text: string, maxLength: number, ellipsis = '…'): string {\n if (text.length <= maxLength) return text;\n if (maxLength < ellipsis.length) {\n throw new RangeError(`maxLength (${maxLength}) must be at least the ellipsis length (${ellipsis.length})`);\n }\n return `${sliceWithoutSplittingSurrogates(text, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\n/**\n * Returns the first `end` code units of `text`, one fewer when the last kept unit is a high surrogate, whose low half\n * is either cut off or absent from `text`, because an unpaired surrogate is replaced with U+FFFD by encoders.\n */\nexport function sliceWithoutSplittingSurrogates(text: string, end: number): string {\n // oxlint-disable-next-line unicorn/prefer-code-point -- the check is on a single UTF-16 code unit\n const lastKeptUnit = text.charCodeAt(end - 1);\n return text.slice(0, lastKeptUnit >= 0xd8_00 && lastKeptUnit <= 0xdb_ff ? end - 1 : end);\n}\n\nconst htmlEntities: Readonly<Record<string, string>> = {\n '\"': '"',\n '&': '&',\n \"'\": ''',\n '<': '<',\n '>': '>',\n};\n\nconst htmlSpecialCharactersPattern = /[\"&'<>]/gu;\n\n/** Escapes `&`, `<`, `>`, `\"`, and `'`, so the result is safe in HTML text and in quoted attribute values. */\nexport function escapeHtml(text: string): string {\n // Measured in V8 and JavaScriptCore: a native `search` first is faster than `replaceAll` alone for text without these\n // characters, and one native `replaceAll` beats a per-code-unit loop for text where they are rare.\n return text.search(htmlSpecialCharactersPattern) === -1\n ? text\n : text.replaceAll(htmlSpecialCharactersPattern, (character) => htmlEntities[character] as string);\n}\n"],"mappings":"AACA,SAAgBA,EAAa,EAAsB,CACjD,OAAOC,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgBC,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAKC,CAAK,EAAIA,EAAQ,IAAIA,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF,CAMA,SAAgBC,EAAiB,EAAc,EAAW,GAAY,CACpE,OAAOC,EAAkB,IAAKJ,EAAMK,CAAQ,CAC9C,CAMA,SAAgBC,EAAY,EAAc,EAAW,GAAY,CAC/D,OAAOF,EAAkB,IAAKJ,EAAMK,CAAQ,CAC9C,CAEA,SAASD,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAMG,EAAgBC,EAAU,OAAO,CAAC,EACpCC,EAAa,EACbC,EAAQV,EAAK,QAAQO,CAAa,EACtC,KAAOG,IAAU,IAAI,CACnB,IAAIC,EAAMD,EAAQ,EAClB,KAAOV,EAAKW,KAASH,GAAW,IAChC,EAAa,KAAK,IAAIC,EAAYE,EAAMD,CAAK,EAC7C,EAAQV,EAAK,QAAQO,EAAeI,CAAG,CACzC,CACA,IAAIC,EAAgB,EACpB,KAAOP,EAASO,KAAmBJ,GAAW,IAC9C,IAAMK,EAAQL,EAAU,OAAOC,EAAa,EAAIG,CAAa,EAC7D,MAAO,GAAGC,IAAQR,EAAS,MAAMO,CAAa,EAAE,IAAIZ,IAAOA,EAAK,SAAS;CAAI,EAAI,GAAK;IAAOa,GAC/F,CAMA,SAAgBC,EAAS,EAAc,EAAmB,EAAW,IAAa,CAChF,GAAId,EAAK,QAAUe,EAAW,OAAOf,EACrC,GAAIe,EAAYC,EAAS,OACvB,MAAU,WAAW,cAAcD,EAAU,0CAA0CC,EAAS,OAAO,EAAE,EAE3G,MAAO,GAAGC,EAAgCjB,EAAMe,EAAYC,EAAS,MAAM,IAAIA,GACjF,CAMA,SAAgBC,EAAgC,EAAc,EAAqB,CAEjF,IAAMC,EAAelB,EAAK,WAAWW,EAAM,CAAC,EAC5C,OAAOX,EAAK,MAAM,EAAGkB,GAAgB,OAAWA,GAAgB,MAAUP,EAAM,EAAIA,CAAG,CACzF,CAEA,MAAMQ,EAAiD,CACrD,IAAK,SACL,IAAK,QACL,IAAK,QACL,IAAK,OACL,IAAK,MACP,EAEMC,EAA+B,YAGrC,SAAgBC,EAAW,EAAsB,CAG/C,OAAOrB,EAAK,OAAOoB,CAA4B,IAAM,GACjDpB,EACAA,EAAK,WAAWoB,EAA+B,GAAcD,EAAaG,EAAoB,CACpG"}
|
package/dist/zenkaku.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zenkaku.cjs","names":["zenkakuAlphanumericalsToHankaku","str","s"],"sources":["../src/zenkaku.ts"],"sourcesContent":["export function zenkakuAlphanumericalsToHankaku(str: string): string {\n return str.replaceAll(/[0-9A-Za-z]/g, (s: string) => {\n return String.fromCodePoint((s.codePointAt(0) ?? 0) -
|
|
1
|
+
{"version":3,"file":"zenkaku.cjs","names":["zenkakuAlphanumericalsToHankaku","str","s"],"sources":["../src/zenkaku.ts"],"sourcesContent":["export function zenkakuAlphanumericalsToHankaku(str: string): string {\n return str.replaceAll(/[0-9A-Za-z]/g, (s: string) => {\n return String.fromCodePoint((s.codePointAt(0) ?? 0) - 0xfe_e0);\n });\n}\n"],"mappings":"aAAA,SAAgBA,EAAgC,EAAqB,CACnE,OAAOC,EAAI,WAAW,eAAiB,GAC9B,OAAO,eAAeC,EAAE,YAAY,CAAC,GAAK,GAAK,KAAO,CAC9D,CACH"}
|
package/dist/zenkaku.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zenkaku.js","names":["zenkakuAlphanumericalsToHankaku","str","s"],"sources":["../src/zenkaku.ts"],"sourcesContent":["export function zenkakuAlphanumericalsToHankaku(str: string): string {\n return str.replaceAll(/[0-9A-Za-z]/g, (s: string) => {\n return String.fromCodePoint((s.codePointAt(0) ?? 0) -
|
|
1
|
+
{"version":3,"file":"zenkaku.js","names":["zenkakuAlphanumericalsToHankaku","str","s"],"sources":["../src/zenkaku.ts"],"sourcesContent":["export function zenkakuAlphanumericalsToHankaku(str: string): string {\n return str.replaceAll(/[0-9A-Za-z]/g, (s: string) => {\n return String.fromCodePoint((s.codePointAt(0) ?? 0) - 0xfe_e0);\n });\n}\n"],"mappings":"AAAA,SAAgBA,EAAgC,EAAqB,CACnE,OAAOC,EAAI,WAAW,eAAiB,GAC9B,OAAO,eAAeC,EAAE,YAAY,CAAC,GAAK,GAAK,KAAO,CAC9D,CACH"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willbooster/shared-lib",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.23.0",
|
|
4
4
|
"description": "Shared library for WillBooster projects",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "WillBooster Inc.",
|
|
5
7
|
"repository": {
|
|
6
8
|
"type": "git",
|
|
7
9
|
"url": "git+https://github.com/WillBooster/shared.git",
|
|
8
10
|
"directory": "packages/shared-lib"
|
|
9
11
|
},
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
"files": [
|
|
13
|
+
"NOTICE",
|
|
14
|
+
"bin/",
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
13
17
|
"type": "module",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"main": "dist/index.cjs",
|
|
20
|
+
"module": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
14
22
|
"exports": {
|
|
15
23
|
".": {
|
|
16
24
|
"types": "./dist/index.d.ts",
|
|
@@ -23,14 +31,10 @@
|
|
|
23
31
|
"import": "./src/index.ts"
|
|
24
32
|
}
|
|
25
33
|
},
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"NOTICE",
|
|
31
|
-
"bin/",
|
|
32
|
-
"dist/"
|
|
33
|
-
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"registry": "https://registry.npmjs.org/"
|
|
37
|
+
},
|
|
34
38
|
"scripts": {
|
|
35
39
|
"build": "build-ts lib",
|
|
36
40
|
"cleanup": "bun wb lint --fix --format",
|
|
@@ -46,20 +50,13 @@
|
|
|
46
50
|
"@tsconfig/bun": "1.0.11",
|
|
47
51
|
"@tsconfig/node-lts": "24.0.1",
|
|
48
52
|
"@tsconfig/node-ts": "23.6.4",
|
|
49
|
-
"@types/bun": "1.4.
|
|
53
|
+
"@types/bun": "1.4.3",
|
|
50
54
|
"@types/node": "26.6.5",
|
|
51
|
-
"@willbooster/
|
|
52
|
-
"@willbooster/
|
|
53
|
-
"@willbooster/wb": "22.26.2",
|
|
55
|
+
"@willbooster/oxlint-config": "1.4.9",
|
|
56
|
+
"@willbooster/wb": "22.29.0",
|
|
54
57
|
"build-ts": "21.0.27",
|
|
55
|
-
"
|
|
56
|
-
"oxlint": "1.87.0",
|
|
58
|
+
"oxlint": "1.88.0",
|
|
57
59
|
"oxlint-tsgolint": "7.0.2004",
|
|
58
|
-
"sort-package-json": "4.0.0",
|
|
59
60
|
"yaml": "2.9.1"
|
|
60
|
-
},
|
|
61
|
-
"publishConfig": {
|
|
62
|
-
"access": "public",
|
|
63
|
-
"registry": "https://registry.npmjs.org/"
|
|
64
61
|
}
|
|
65
62
|
}
|