@willbooster/shared-lib 10.6.0 → 10.8.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/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./appEnv.cjs"),t=require("./assert.cjs"),n=require("./githubTemplates.cjs"),r=require("./sleep.cjs"),i=require("./error.cjs"),a=require("./humanize.cjs"),o=require("./mail.cjs"),s=require("./parseCommandLineArgs.cjs"),c=require("./shuffle.cjs"),l=require("./sqlite.cjs"),u=require("./text.cjs"),d=require("./testingRules.cjs"),f=require("./zenkaku.cjs");exports.BUG_ISSUE_SECTIONS=n.BUG_ISSUE_SECTIONS,exports.CHANGE_ISSUE_SECTIONS=n.CHANGE_ISSUE_SECTIONS,exports.ISSUE_TEMPLATE_RULES=n.ISSUE_TEMPLATE_RULES,exports.PULL_REQUEST_BODY_RULES=n.PULL_REQUEST_BODY_RULES,exports.PULL_REQUEST_REQUIREMENTS_RULES=n.PULL_REQUEST_REQUIREMENTS_RULES,exports.PULL_REQUEST_SECTIONS=n.PULL_REQUEST_SECTIONS,exports.TEST_WRITING_RULES=d.TEST_WRITING_RULES,exports.ensureTruthy=t.ensureTruthy,exports.errorify=i.errorify,exports.escapeRegExp=u.escapeRegExp,exports.getAppEnv=e.getAppEnv,exports.getConnectionLevelSqlitePragmas=l.getConnectionLevelSqlitePragmas,exports.getPersistentSqlitePragmas=l.getPersistentSqlitePragmas,exports.humanizeNumber=a.humanizeNumber,exports.ignoreEnoent=i.ignoreEnoent,exports.ignoreEnoentAsync=i.ignoreEnoentAsync,exports.ignoreError=i.ignoreError,exports.ignoreErrorAsync=i.ignoreErrorAsync,exports.mailTemplates=o.mailTemplates,exports.parseCommandLineArgs=s.parseCommandLineArgs,exports.quoteForShell=u.quoteForShell,exports.renderSectionChecklist=n.renderSectionChecklist,exports.renderSectionTemplate=n.renderSectionTemplate,exports.shuffle=c.shuffle,exports.sleep=r.sleep,exports.withRetry=i.withRetry,exports.zenkakuAlphanumericalsToHankaku=f.zenkakuAlphanumericalsToHankaku;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./appEnv.cjs"),t=require("./assert.cjs"),n=require("./githubTemplates.cjs"),r=require("./sleep.cjs"),i=require("./error.cjs"),a=require("./humanize.cjs"),o=require("./mail.cjs"),s=require("./parseCommandLineArgs.cjs"),c=require("./text.cjs"),l=require("./prompt.cjs"),u=require("./shuffle.cjs"),d=require("./sqlite.cjs"),f=require("./testingRules.cjs"),p=require("./zenkaku.cjs");exports.BUG_ISSUE_SECTIONS=n.BUG_ISSUE_SECTIONS,exports.CHANGE_ISSUE_SECTIONS=n.CHANGE_ISSUE_SECTIONS,exports.ISSUE_TEMPLATE_RULES=n.ISSUE_TEMPLATE_RULES,exports.PULL_REQUEST_BODY_RULES=n.PULL_REQUEST_BODY_RULES,exports.PULL_REQUEST_REQUIREMENTS_RULES=n.PULL_REQUEST_REQUIREMENTS_RULES,exports.PULL_REQUEST_SECTIONS=n.PULL_REQUEST_SECTIONS,exports.TEST_WRITING_RULES=f.TEST_WRITING_RULES,exports.ensureTruthy=t.ensureTruthy,exports.errorify=i.errorify,exports.escapePromptTag=l.escapePromptTag,exports.escapeRegExp=c.escapeRegExp,exports.formatPrompt=l.formatPrompt,exports.getAppEnv=e.getAppEnv,exports.getConnectionLevelSqlitePragmas=d.getConnectionLevelSqlitePragmas,exports.getPersistentSqlitePragmas=d.getPersistentSqlitePragmas,exports.humanizeNumber=a.humanizeNumber,exports.ignoreEnoent=i.ignoreEnoent,exports.ignoreEnoentAsync=i.ignoreEnoentAsync,exports.ignoreError=i.ignoreError,exports.ignoreErrorAsync=i.ignoreErrorAsync,exports.mailTemplates=o.mailTemplates,exports.parseCommandLineArgs=s.parseCommandLineArgs,exports.quoteForShell=c.quoteForShell,exports.renderSectionChecklist=n.renderSectionChecklist,exports.renderSectionTemplate=n.renderSectionTemplate,exports.serializeForPrompt=l.serializeForPrompt,exports.serializeForPromptInTag=l.serializeForPromptInTag,exports.shuffle=u.shuffle,exports.sleep=r.sleep,exports.toCodeBlock=c.toCodeBlock,exports.toTildeCodeBlock=c.toTildeCodeBlock,exports.truncateForPrompt=l.truncateForPrompt,exports.withRetry=i.withRetry,exports.zenkakuAlphanumericalsToHankaku=p.zenkakuAlphanumericalsToHankaku;
package/dist/index.d.ts CHANGED
@@ -5,10 +5,11 @@ export { errorify, ignoreError, ignoreEnoent, ignoreErrorAsync, ignoreEnoentAsyn
5
5
  export { humanizeNumber } from './humanize.js';
6
6
  export { mailTemplates } from './mail.js';
7
7
  export { parseCommandLineArgs } from './parseCommandLineArgs.js';
8
+ export { escapePromptTag, formatPrompt, serializeForPrompt, serializeForPromptInTag, truncateForPrompt, } from './prompt.js';
8
9
  export { shuffle } from './shuffle.js';
9
10
  export { sleep } from './sleep.js';
10
11
  export { getConnectionLevelSqlitePragmas, getPersistentSqlitePragmas } from './sqlite.js';
11
- export { escapeRegExp, quoteForShell } from './text.js';
12
+ export { escapeRegExp, quoteForShell, toCodeBlock, toTildeCodeBlock } from './text.js';
12
13
  export { TEST_WRITING_RULES } from './testingRules.js';
13
14
  export { zenkakuAlphanumericalsToHankaku } from './zenkaku.js';
14
15
  export type { AppEnv } from './appEnv.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{getAppEnv as e}from"./appEnv.js";import{ensureTruthy as t}from"./assert.js";import{BUG_ISSUE_SECTIONS as n,CHANGE_ISSUE_SECTIONS as r,ISSUE_TEMPLATE_RULES as i,PULL_REQUEST_BODY_RULES as a,PULL_REQUEST_REQUIREMENTS_RULES as o,PULL_REQUEST_SECTIONS as s,renderSectionChecklist as c,renderSectionTemplate as l}from"./githubTemplates.js";import{sleep as u}from"./sleep.js";import{errorify as d,ignoreEnoent as f,ignoreEnoentAsync as p,ignoreError as m,ignoreErrorAsync as h,withRetry as g}from"./error.js";import{humanizeNumber as _}from"./humanize.js";import{mailTemplates as v}from"./mail.js";import{parseCommandLineArgs as y}from"./parseCommandLineArgs.js";import{shuffle as b}from"./shuffle.js";import{getConnectionLevelSqlitePragmas as x,getPersistentSqlitePragmas as S}from"./sqlite.js";import{escapeRegExp as C,quoteForShell as w}from"./text.js";import{TEST_WRITING_RULES as T}from"./testingRules.js";import{zenkakuAlphanumericalsToHankaku as E}from"./zenkaku.js";export{n as BUG_ISSUE_SECTIONS,r as CHANGE_ISSUE_SECTIONS,i as ISSUE_TEMPLATE_RULES,a as PULL_REQUEST_BODY_RULES,o as PULL_REQUEST_REQUIREMENTS_RULES,s as PULL_REQUEST_SECTIONS,T as TEST_WRITING_RULES,t as ensureTruthy,d as errorify,C as escapeRegExp,e as getAppEnv,x as getConnectionLevelSqlitePragmas,S as getPersistentSqlitePragmas,_ as humanizeNumber,f as ignoreEnoent,p as ignoreEnoentAsync,m as ignoreError,h as ignoreErrorAsync,v as mailTemplates,y as parseCommandLineArgs,w as quoteForShell,c as renderSectionChecklist,l as renderSectionTemplate,b as shuffle,u as sleep,g as withRetry,E as zenkakuAlphanumericalsToHankaku};
1
+ import{getAppEnv as e}from"./appEnv.js";import{ensureTruthy as t}from"./assert.js";import{BUG_ISSUE_SECTIONS as n,CHANGE_ISSUE_SECTIONS as r,ISSUE_TEMPLATE_RULES as i,PULL_REQUEST_BODY_RULES as a,PULL_REQUEST_REQUIREMENTS_RULES as o,PULL_REQUEST_SECTIONS as s,renderSectionChecklist as c,renderSectionTemplate as l}from"./githubTemplates.js";import{sleep as u}from"./sleep.js";import{errorify as d,ignoreEnoent as f,ignoreEnoentAsync as p,ignoreError as m,ignoreErrorAsync as h,withRetry as g}from"./error.js";import{humanizeNumber as _}from"./humanize.js";import{mailTemplates as v}from"./mail.js";import{parseCommandLineArgs as y}from"./parseCommandLineArgs.js";import{escapeRegExp as b,quoteForShell as x,toCodeBlock as S,toTildeCodeBlock as C}from"./text.js";import{escapePromptTag as w,formatPrompt as T,serializeForPrompt as E,serializeForPromptInTag as D,truncateForPrompt as O}from"./prompt.js";import{shuffle as k}from"./shuffle.js";import{getConnectionLevelSqlitePragmas as A,getPersistentSqlitePragmas as j}from"./sqlite.js";import{TEST_WRITING_RULES as M}from"./testingRules.js";import{zenkakuAlphanumericalsToHankaku as N}from"./zenkaku.js";export{n as BUG_ISSUE_SECTIONS,r as CHANGE_ISSUE_SECTIONS,i as ISSUE_TEMPLATE_RULES,a as PULL_REQUEST_BODY_RULES,o as PULL_REQUEST_REQUIREMENTS_RULES,s as PULL_REQUEST_SECTIONS,M as TEST_WRITING_RULES,t as ensureTruthy,d as errorify,w as escapePromptTag,b as escapeRegExp,T as formatPrompt,e as getAppEnv,A as getConnectionLevelSqlitePragmas,j as getPersistentSqlitePragmas,_ as humanizeNumber,f as ignoreEnoent,p as ignoreEnoentAsync,m as ignoreError,h as ignoreErrorAsync,v as mailTemplates,y as parseCommandLineArgs,x as quoteForShell,c as renderSectionChecklist,l as renderSectionTemplate,E as serializeForPrompt,D as serializeForPromptInTag,k as shuffle,u as sleep,S as toCodeBlock,C as toTildeCodeBlock,O as truncateForPrompt,g as withRetry,N as zenkakuAlphanumericalsToHankaku};
@@ -0,0 +1,17 @@
1
+ "use strict";const e=require("./text.cjs"),t=/^(?:~|[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=/^(?:[\t ,[\]{}#&*!|>'"%@`]|[?-](?:[ \t]|$))/u,r=/:[ \t]|[ \t]#/u,i=/[\t :]$/u,a={"0000":String.raw`\0`,"0007":String.raw`\a`,"000b":String.raw`\v`,"001b":String.raw`\e`};function o(t){return e.toTildeCodeBlock(`${b(S(t),{indent:``})}\n`,`yaml`)}function s(t,n){let r=(typeof n==`string`?[n]:n).flatMap(l);return e.toTildeCodeBlock(`${b(S(t),{indent:``,tagPatterns:r})}\n`,`yaml`)}function c(e,t){return e.includes(`<`)?u(e,l(t)):e}function l(t){let n=e.escapeRegExp(t);return[RegExp(`<\\s*(?:(/)\\s*)?(${n})\\s*>`,`giu`),RegExp(`<\\s*(?:(/)\\s*)?(${n})(?![\\w-])`,`giu`)]}function u(e,t){if(!e.includes(`<`))return e;for(let n of t)e=e.replaceAll(n,`[$1$2]`);return e}const d=/^[ \t]*((`{3,}|~{3,})[^\n]*)$/u,f=/[^\s~][ \t]*~{3,}yaml[ \t]*$/u;function p(e){let t=e.split(`
2
+ `),n=m(t),r=``,i=``;for(let e=0;e<t.length;e++){let a=t[e]??``,o=n[e];if(o===void 0){if(f.test(a))throw TypeError(`Interpolate a serializeForPrompt block at the start of a line`);i+=`${a}\n`;continue}let s=t.slice(e+1,o.end+1);s[s.length-1]=s.at(-1)?.trimStart()??``,r+=`${_(i)}${o.opener}\n${s.join(`
3
+ `)}`,i=`
4
+ `,e=o.end}return(r+_(i)).trim()}function m(e){let t=[],n=new Map,r=[],i=[];for(let a=e.length-1;a>=0;a--){let o=d.exec(e[a]??``),s=o?.[1]?.trimEnd(),c=o?.[2];if(s===void 0||c===void 0)continue;let l=c[0]==="`"?r:i,u=0,f=l.length;for(;u<f;){let e=u+f>>>1;(l[e]?.length??0)>=c.length?u=e+1:f=e}let p=l[u-1]?.index??-1;if(/^(?:[ \t].*)?$/u.test((o?.[1]??``).slice(c.length))){for(;l.length>0&&(l.at(-1)?.length??0)<=c.length;)l.pop();l.push({length:c.length,index:a})}let m=n.get(c[0]??``);p!==-1&&m!==void 0&&h(m,p)>c.length&&(p=-1),p!==-1&&(t[a]={opener:s,end:p},m===void 0&&(m=new Uint32Array(e.length*2),n.set(c[0]??``,m)),g(m,a+1,p,c.length))}return t}function h(e,t){let n=0;for(let r=t+e.length/2;r>0;r>>>=1)n=Math.max(n,e[r]??0);return n}function g(e,t,n,r){let i=t+e.length/2,a=n+e.length/2;for(;i<=a;)i%2==1&&(e[i]=Math.max(e[i]??0,r),i++),a%2==0&&(e[a]=Math.max(e[a]??0,r),a--),i>>>=1,a>>>=1}function _(e){return e.replaceAll(/\n\s+("""|'''|```|~{3,})|\n\s*/gu,v).replaceAll(/\n\s+(#+\s)|\n\s*/gu,v).replaceAll(/(?<!\s)\s*\n\s*\n/gu,`
5
+
6
+ `)}function v(e,t){return t===void 0?e:`\n${t}`}function y(e,t){if(e.length<=t)return e;let n=e.slice(0,t);return`${/[\uD800-\uDBFF]$/u.test(n)?n.slice(0,-1):n}\n...<truncated>`}function b(e,t){switch(typeof e){case`undefined`:return`null`;case`boolean`:return String(e);case`number`:return w(e);case`bigint`:return String(e);case`string`:return T(e,t);case`object`:{if(e===null)return`null`;let n=`\n${t.indent}`,r=``;if(Array.isArray(e)){if(e.length===0)return`[]`;let i={indent:t.indent+` `,tagPatterns:t.tagPatterns};for(let t of e)r+=`${r?n:``}- ${b(S(t),i)}`;return r}if(e instanceof Map){for(let[i,a]of e)a!==void 0&&(r+=`${r?n:``}${x(i,a,t)}`);return r||`{}`}let i=e;for(let e of Object.keys(i)){let a=i[e];a!==void 0&&(r+=`${r?n:``}${x(e,a,t)}`)}return r||`{}`}default:throw TypeError(`Cannot serialize a ${typeof e} value for a prompt`)}}function x(e,t,n){let r=n.indent+` `,i=S(e),a=S(t),o=b(i,{indent:r,implicitKey:!0,tagPatterns:n.tagPatterns}),s=b(a,{indent:r,tagPatterns:n.tagPatterns});return C(i)||o.length>1024?`? ${o}\n${n.indent}: ${s}`:`${o}:${C(a)&&s!==`[]`&&s!==`{}`?`\n${r}`:` `}${s}`}function S(e){let t=typeof e?.toJSON==`function`?e.toJSON():e;return typeof t!=`object`||!t||Array.isArray(t)||t instanceof Map?t:t instanceof String||t instanceof Number||t instanceof Boolean||t instanceof BigInt?t.valueOf():t instanceof Error?{name:t.name,message:t.message,...Object.fromEntries(Object.entries(t)),...`cause`in t&&{cause:t.cause},...t instanceof AggregateError&&{errors:t.errors}}:t instanceof RegExp?String(t):Symbol.iterator in t?[...t]:t}function C(e){return typeof e==`object`&&!!e}function w(e){return Number.isNaN(e)?`.nan`:Number.isFinite(e)?Object.is(e,-0)?`-0`:JSON.stringify(e):e<0?`-.inf`:`.inf`}function T(e,t){let n=t.tagPatterns===void 0?e:u(e,t.tagPatterns);return t.tagPatterns?.length&&/<\s*(?:\/\s*)?$/u.test(n)?A(n,t,!1):/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u{D800}-\u{DFFF}]/u.test(n)?A(n,t):E(n,t)}function E(e,a){let{implicitKey:o,indent:s}=a;if(e.includes(`
7
+ `))return o?O(e,a):D(e,a);if(n.test(e)||i.test(e.slice(-1))||r.test(e))return O(e,a);if(j(e)){if(s===``)return D(e,a);if(o&&s===` `)return O(e,a)}return t.test(e)?O(e,a):e}function D(e,t){let n=e.length;for(;n>0&&N(e.codePointAt(n-1));)n--;let r=e.slice(n);if(r.includes(`
8
+ `)&&!r.endsWith(`
9
+ `))return O(e,t);let i=t.indent||(j(e)?` `:``),a=r.indexOf(`
10
+ `),o=a===-1?`-`:e===r||a!==r.length-1?`+`:``;r&&=(e=e.slice(0,-r.length),r.endsWith(`
11
+ `)&&(r=r.slice(0,-1)),r.replaceAll(/(?:^|(?<!\n))\n+(?!\n|$)/gu,`$&${i}`));let s=/^[\n ]*/u.exec(e)?.[0]??``,c=s.includes(` `),l=e.slice(0,s.lastIndexOf(`
12
+ `)+1);return l&&=(e=e.slice(l.length),l.replaceAll(/\n+/gu,`$&${i}`)),`|${(c?i?`2`:`1`:``)+o}\n${i}${l}${M(e,i)}${r}`}function O(e,t){return e.includes(`"`)&&!e.includes(`'`)?k(e,t):A(e,t)}function k(e,t){return e.includes(`
13
+ `)?A(e,t):`'${e.replaceAll(`'`,`''`)}'`}function A(e,t,n=!t.implicitKey){let r=JSON.stringify(e),i=t.indent||(j(e)?` `:``),o=``,s=0;for(let e=0;e<r.length;e++){let t=r[e];if(t===` `&&r[e+1]===`\\`&&r[e+2]===`n`&&(o+=`${r.slice(s,e)}\\ `,e++,s=e,t=`\\`),t===`\\`){if(r[e+1]===`u`){let t=r.slice(e+2,e+6);o+=r.slice(s,e)+(a[t]??(t.startsWith(`00`)?`\\x${t.slice(2)}`:r.slice(e,e+6))),e+=5,s=e+1}else if(r[e+1]===`n`&&n&&r[e+2]!==`"`&&r.length>=40){for(o+=`${r.slice(s,e)}\n\n`;r[e+2]===`\\`&&r[e+3]===`n`&&r[e+4]!==`"`;)o+=`
14
+ `,e+=2;o+=i,r[e+2]===` `&&(o+=`\\`),e++,s=e+1}else e++}}return s?o+r.slice(s):r}function j(e){return/^(?:%|---|\.\.\.)/mu.test(e)}function M(e,t){let n=e.split(`
15
+ `),r=n[0]??``;for(let e=1;e<n.length;e++){let i=n[e];r+=i?`\n${t}${i}`:`
16
+ `}return r}function N(e){return e===10||e===9||e===32}exports.escapePromptTag=c,exports.formatPrompt=p,exports.serializeForPrompt=o,exports.serializeForPromptInTag=s,exports.truncateForPrompt=y;
17
+ //# sourceMappingURL=prompt.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.cjs","names":["toTildeCodeBlock","escapeRegExp"],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, 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 const kept = text.slice(0, maxLength);\n // Cutting between the halves of a surrogate pair would leave an unpaired one, which encoders replace with U+FFFD.\n return `${/[\\uD800-\\uDBFF]$/u.test(kept) ? kept.slice(0, -1) : kept}\\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 if (value === null) return 'null';\n const separator = `\\n${ctx.indent}`;\n let str = '';\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]';\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n for (const item of value as unknown[]) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n }\n if (value instanceof Map) {\n for (const [key, item] of value) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n }\n const record = value as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n const item = record[key];\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\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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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Serializes data into a code block to embed in an LLM prompt, so that the LLM can read its contents.
3
+ * Accepts JSON data (honoring `toJSON` such as that of `Date`) as well as `undefined`, `NaN`, `Infinity`, bigints,
4
+ * `Map`s (including non-string keys), `Set`s and other iterables, `Error`s (with their name, message, cause, and own properties),
5
+ * and `RegExp`s. Symbols and functions without `toJSON` throw. The output is not meant to be deserialized back into the original value.
6
+ * The format inside the code block is an implementation detail and may change.
7
+ */
8
+ export declare function serializeForPrompt(value: unknown): string;
9
+ /**
10
+ * Serializes data with `serializeForPrompt` for embedding inside a `<tagName>` element of a prompt, escaping that tag
11
+ * in every string so that untrusted data cannot close the element and have the rest read as instructions.
12
+ * The caller must wrap the result in the same tag.
13
+ *
14
+ * Only the named elements are protected, so a block nested inside other tagged elements takes every enclosing name.
15
+ * Escaping the finished block instead is not an option: it would turn a plain scalar holding the escaped notation
16
+ * into a YAML flow sequence, since the quoting of each scalar is decided as it is written.
17
+ *
18
+ * The escaped notation is the one `escapePromptTag` writes, and text that already holds it is left as it is, so two
19
+ * strings differing only in that notation are written alike. Keys taken from untrusted data can therefore collide
20
+ * into one duplicate key of the emitted mapping.
21
+ */
22
+ export declare function serializeForPromptInTag(value: unknown, tagName: string | readonly string[]): string;
23
+ /**
24
+ * Replaces `<tagName` and `</tagName` not followed by `[\w-]` with a harmless notation such as `[/tagName]`, so that
25
+ * data embedded in a `<tagName>` element of a prompt cannot close the element and have the rest read as instructions.
26
+ * A closing `>` is not required, since an HTML reader also closes an element on a tag carrying attribute-like junk,
27
+ * on a trailing solidus, or on the next `>` anywhere in the prompt; the whitespace between `<` and the name is
28
+ * dropped along with the brackets. Case is ignored deliberately: `tagName` is expected to be a literal written by
29
+ * the caller, while the data is not.
30
+ */
31
+ export declare function escapePromptTag(text: string, tagName: string): string;
32
+ /**
33
+ * Strips template-literal indentation from Markdown headings, code fences, and triple-quote delimiters (`"""` and
34
+ * `'''`), and collapses blank lines.
35
+ * The contents of a fenced block are left untouched, since reindenting them would rewrite the data they carry, such
36
+ * as the YAML of `serializeForPrompt` or the source of `toCodeBlock`. Every fenced block must be interpolated at
37
+ * the start of a line. Only recognizable mid-line `~~~yaml` markers throw, including prose mentions;
38
+ * this is not a complete placement validator, since preceding tildes can merge into an indistinguishable fence.
39
+ * Blocks end at the first compatible same-character fence, which may carry trailing prose, unless a longer fence
40
+ * of the same character opens and contains that candidate closing line. Other fence characters have no nesting
41
+ * significance. Balance authored fences before interpolating blocks: an unmatched sample can consume part of a
42
+ * later block and leave its tail subject to prose formatting.
43
+ */
44
+ export declare function formatPrompt(prompt: string): string;
45
+ /** Cuts text down to `maxLength` characters, marking it so that an LLM reads the rest as missing rather than absent. */
46
+ export declare function truncateForPrompt(text: string, maxLength: number): string;
package/dist/prompt.js ADDED
@@ -0,0 +1,17 @@
1
+ import{escapeRegExp as e,toTildeCodeBlock as t}from"./text.js";const 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,r=/^(?:[\t ,[\]{}#&*!|>'"%@`]|[?-](?:[ \t]|$))/u,i=/:[ \t]|[ \t]#/u,a=/[\t :]$/u,o={"0000":String.raw`\0`,"0007":String.raw`\a`,"000b":String.raw`\v`,"001b":String.raw`\e`};function s(e){return t(`${x(C(e),{indent:``})}\n`,`yaml`)}function c(e,n){let r=(typeof n==`string`?[n]:n).flatMap(u);return t(`${x(C(e),{indent:``,tagPatterns:r})}\n`,`yaml`)}function l(e,t){return e.includes(`<`)?d(e,u(t)):e}function u(t){let n=e(t);return[RegExp(`<\\s*(?:(/)\\s*)?(${n})\\s*>`,`giu`),RegExp(`<\\s*(?:(/)\\s*)?(${n})(?![\\w-])`,`giu`)]}function d(e,t){if(!e.includes(`<`))return e;for(let n of t)e=e.replaceAll(n,`[$1$2]`);return e}const f=/^[ \t]*((`{3,}|~{3,})[^\n]*)$/u,p=/[^\s~][ \t]*~{3,}yaml[ \t]*$/u;function m(e){let t=e.split(`
2
+ `),n=h(t),r=``,i=``;for(let e=0;e<t.length;e++){let a=t[e]??``,o=n[e];if(o===void 0){if(p.test(a))throw TypeError(`Interpolate a serializeForPrompt block at the start of a line`);i+=`${a}\n`;continue}let s=t.slice(e+1,o.end+1);s[s.length-1]=s.at(-1)?.trimStart()??``,r+=`${v(i)}${o.opener}\n${s.join(`
3
+ `)}`,i=`
4
+ `,e=o.end}return(r+v(i)).trim()}function h(e){let t=[],n=new Map,r=[],i=[];for(let a=e.length-1;a>=0;a--){let o=f.exec(e[a]??``),s=o?.[1]?.trimEnd(),c=o?.[2];if(s===void 0||c===void 0)continue;let l=c[0]==="`"?r:i,u=0,d=l.length;for(;u<d;){let e=u+d>>>1;(l[e]?.length??0)>=c.length?u=e+1:d=e}let p=l[u-1]?.index??-1;if(/^(?:[ \t].*)?$/u.test((o?.[1]??``).slice(c.length))){for(;l.length>0&&(l.at(-1)?.length??0)<=c.length;)l.pop();l.push({length:c.length,index:a})}let m=n.get(c[0]??``);p!==-1&&m!==void 0&&g(m,p)>c.length&&(p=-1),p!==-1&&(t[a]={opener:s,end:p},m===void 0&&(m=new Uint32Array(e.length*2),n.set(c[0]??``,m)),_(m,a+1,p,c.length))}return t}function g(e,t){let n=0;for(let r=t+e.length/2;r>0;r>>>=1)n=Math.max(n,e[r]??0);return n}function _(e,t,n,r){let i=t+e.length/2,a=n+e.length/2;for(;i<=a;)i%2==1&&(e[i]=Math.max(e[i]??0,r),i++),a%2==0&&(e[a]=Math.max(e[a]??0,r),a--),i>>>=1,a>>>=1}function v(e){return e.replaceAll(/\n\s+("""|'''|```|~{3,})|\n\s*/gu,y).replaceAll(/\n\s+(#+\s)|\n\s*/gu,y).replaceAll(/(?<!\s)\s*\n\s*\n/gu,`
5
+
6
+ `)}function y(e,t){return t===void 0?e:`\n${t}`}function b(e,t){if(e.length<=t)return e;let n=e.slice(0,t);return`${/[\uD800-\uDBFF]$/u.test(n)?n.slice(0,-1):n}\n...<truncated>`}function x(e,t){switch(typeof e){case`undefined`:return`null`;case`boolean`:return String(e);case`number`:return T(e);case`bigint`:return String(e);case`string`:return E(e,t);case`object`:{if(e===null)return`null`;let n=`\n${t.indent}`,r=``;if(Array.isArray(e)){if(e.length===0)return`[]`;let i={indent:t.indent+` `,tagPatterns:t.tagPatterns};for(let t of e)r+=`${r?n:``}- ${x(C(t),i)}`;return r}if(e instanceof Map){for(let[i,a]of e)a!==void 0&&(r+=`${r?n:``}${S(i,a,t)}`);return r||`{}`}let i=e;for(let e of Object.keys(i)){let a=i[e];a!==void 0&&(r+=`${r?n:``}${S(e,a,t)}`)}return r||`{}`}default:throw TypeError(`Cannot serialize a ${typeof e} value for a prompt`)}}function S(e,t,n){let r=n.indent+` `,i=C(e),a=C(t),o=x(i,{indent:r,implicitKey:!0,tagPatterns:n.tagPatterns}),s=x(a,{indent:r,tagPatterns:n.tagPatterns});return w(i)||o.length>1024?`? ${o}\n${n.indent}: ${s}`:`${o}:${w(a)&&s!==`[]`&&s!==`{}`?`\n${r}`:` `}${s}`}function C(e){let t=typeof e?.toJSON==`function`?e.toJSON():e;return typeof t!=`object`||!t||Array.isArray(t)||t instanceof Map?t:t instanceof String||t instanceof Number||t instanceof Boolean||t instanceof BigInt?t.valueOf():t instanceof Error?{name:t.name,message:t.message,...Object.fromEntries(Object.entries(t)),...`cause`in t&&{cause:t.cause},...t instanceof AggregateError&&{errors:t.errors}}:t instanceof RegExp?String(t):Symbol.iterator in t?[...t]:t}function w(e){return typeof e==`object`&&!!e}function T(e){return Number.isNaN(e)?`.nan`:Number.isFinite(e)?Object.is(e,-0)?`-0`:JSON.stringify(e):e<0?`-.inf`:`.inf`}function E(e,t){let n=t.tagPatterns===void 0?e:d(e,t.tagPatterns);return t.tagPatterns?.length&&/<\s*(?:\/\s*)?$/u.test(n)?j(n,t,!1):/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u{D800}-\u{DFFF}]/u.test(n)?j(n,t):D(n,t)}function D(e,t){let{implicitKey:o,indent:s}=t;if(e.includes(`
7
+ `))return o?k(e,t):O(e,t);if(r.test(e)||a.test(e.slice(-1))||i.test(e))return k(e,t);if(M(e)){if(s===``)return O(e,t);if(o&&s===` `)return k(e,t)}return n.test(e)?k(e,t):e}function O(e,t){let n=e.length;for(;n>0&&P(e.codePointAt(n-1));)n--;let r=e.slice(n);if(r.includes(`
8
+ `)&&!r.endsWith(`
9
+ `))return k(e,t);let i=t.indent||(M(e)?` `:``),a=r.indexOf(`
10
+ `),o=a===-1?`-`:e===r||a!==r.length-1?`+`:``;r&&=(e=e.slice(0,-r.length),r.endsWith(`
11
+ `)&&(r=r.slice(0,-1)),r.replaceAll(/(?:^|(?<!\n))\n+(?!\n|$)/gu,`$&${i}`));let s=/^[\n ]*/u.exec(e)?.[0]??``,c=s.includes(` `),l=e.slice(0,s.lastIndexOf(`
12
+ `)+1);return l&&=(e=e.slice(l.length),l.replaceAll(/\n+/gu,`$&${i}`)),`|${(c?i?`2`:`1`:``)+o}\n${i}${l}${N(e,i)}${r}`}function k(e,t){return e.includes(`"`)&&!e.includes(`'`)?A(e,t):j(e,t)}function A(e,t){return e.includes(`
13
+ `)?j(e,t):`'${e.replaceAll(`'`,`''`)}'`}function j(e,t,n=!t.implicitKey){let r=JSON.stringify(e),i=t.indent||(M(e)?` `:``),a=``,s=0;for(let e=0;e<r.length;e++){let t=r[e];if(t===` `&&r[e+1]===`\\`&&r[e+2]===`n`&&(a+=`${r.slice(s,e)}\\ `,e++,s=e,t=`\\`),t===`\\`){if(r[e+1]===`u`){let t=r.slice(e+2,e+6);a+=r.slice(s,e)+(o[t]??(t.startsWith(`00`)?`\\x${t.slice(2)}`:r.slice(e,e+6))),e+=5,s=e+1}else if(r[e+1]===`n`&&n&&r[e+2]!==`"`&&r.length>=40){for(a+=`${r.slice(s,e)}\n\n`;r[e+2]===`\\`&&r[e+3]===`n`&&r[e+4]!==`"`;)a+=`
14
+ `,e+=2;a+=i,r[e+2]===` `&&(a+=`\\`),e++,s=e+1}else e++}}return s?a+r.slice(s):r}function M(e){return/^(?:%|---|\.\.\.)/mu.test(e)}function N(e,t){let n=e.split(`
15
+ `),r=n[0]??``;for(let e=1;e<n.length;e++){let i=n[e];r+=i?`\n${t}${i}`:`
16
+ `}return r}function P(e){return e===10||e===9||e===32}export{l as escapePromptTag,m as formatPrompt,s as serializeForPrompt,c as serializeForPromptInTag,b as truncateForPrompt};
17
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","names":[],"sources":["../src/prompt.ts"],"sourcesContent":["import { escapeRegExp, 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 const kept = text.slice(0, maxLength);\n // Cutting between the halves of a surrogate pair would leave an unpaired one, which encoders replace with U+FFFD.\n return `${/[\\uD800-\\uDBFF]$/u.test(kept) ? kept.slice(0, -1) : kept}\\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 if (value === null) return 'null';\n const separator = `\\n${ctx.indent}`;\n let str = '';\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]';\n const itemCtx = { indent: ctx.indent + INDENT_STEP, tagPatterns: ctx.tagPatterns };\n for (const item of value as unknown[]) {\n str += `${str ? separator : ''}- ${stringifyValue(toSerializable(item), itemCtx)}`;\n }\n return str;\n }\n if (value instanceof Map) {\n for (const [key, item] of value) {\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n }\n const record = value as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n const item = record[key];\n if (item !== undefined) str += `${str ? separator : ''}${stringifyPair(key, item, ctx)}`;\n }\n return str || '{}';\n }\n default: {\n throw new TypeError(`Cannot serialize a ${typeof value} value for a prompt`);\n }\n }\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 CHANGED
@@ -1,2 +1,4 @@
1
- "use strict";function e(e){return e.replaceAll(/[$()*+.?[\\\]^{|}]/gu,String.raw`\$&`)}function t(e){return/^[\w./-]+$/u.test(e)?e:`'${e.replaceAll(`'`,String.raw`'\''`)}'`}exports.escapeRegExp=e,exports.quoteForShell=t;
1
+ "use strict";function e(e){return e.replaceAll(/[$()*+.?[\\\]^{|}]/gu,String.raw`\$&`)}function t(e){return/^[\w./-]+$/u.test(e)?e:`'${e.replaceAll(`'`,String.raw`'\''`)}'`}function n(e,t=``){return i(`~`,e,t)}function r(e,t=``){return i("`",e,t)}function i(e,t,n){let r=e.repeat(3),i=2,a=t.indexOf(r);for(;a!==-1;){let n=a+3;for(;t[n]===e;)n++;i=Math.max(i,n-a),a=t.indexOf(r,n)}let o=0;for(;n[o]===e;)o++;let s=e.repeat(i+1+o);return`${s}${n.slice(o)}\n${t}${t.endsWith(`
2
+ `)?``:`
3
+ `}${s}`}exports.escapeRegExp=e,exports.quoteForShell=t,exports.toCodeBlock=r,exports.toTildeCodeBlock=n;
2
4
  //# sourceMappingURL=text.cjs.map
package/dist/text.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"text.cjs","names":[],"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"],"mappings":"aACA,SAAgB,EAAa,EAAsB,CACjD,OAAO,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgB,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAK,CAAK,EAAI,EAAQ,IAAI,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF"}
1
+ {"version":3,"file":"text.cjs","names":[],"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"],"mappings":"aACA,SAAgB,EAAa,EAAsB,CACjD,OAAO,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgB,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAK,CAAK,EAAI,EAAQ,IAAI,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF,CAMA,SAAgB,EAAiB,EAAc,EAAW,GAAY,CACpE,OAAO,EAAkB,IAAK,EAAM,CAAQ,CAC9C,CAMA,SAAgB,EAAY,EAAc,EAAW,GAAY,CAC/D,OAAO,EAAkB,IAAK,EAAM,CAAQ,CAC9C,CAEA,SAAS,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAM,EAAgB,EAAU,OAAO,CAAC,EACpC,EAAa,EACb,EAAQ,EAAK,QAAQ,CAAa,EACtC,KAAO,IAAU,IAAI,CACnB,IAAI,EAAM,EAAQ,EAClB,KAAO,EAAK,KAAS,GAAW,IAChC,EAAa,KAAK,IAAI,EAAY,EAAM,CAAK,EAC7C,EAAQ,EAAK,QAAQ,EAAe,CAAG,CACzC,CACA,IAAI,EAAgB,EACpB,KAAO,EAAS,KAAmB,GAAW,IAC9C,IAAM,EAAQ,EAAU,OAAO,EAAa,EAAI,CAAa,EAC7D,MAAO,GAAG,IAAQ,EAAS,MAAM,CAAa,EAAE,IAAI,IAAO,EAAK,SAAS;CAAI,EAAI,GAAK;IAAO,GAC/F"}
package/dist/text.d.ts CHANGED
@@ -2,3 +2,13 @@
2
2
  export declare function escapeRegExp(text: string): string;
3
3
  /** A POSIX shell word: safe characters pass through, anything else is single-quoted. */
4
4
  export declare function quoteForShell(value: string): string;
5
+ /**
6
+ * Wraps text in a Markdown code block fenced with tildes, which are rarer than backticks in Markdown content.
7
+ * The fence is longer than any run of tildes in the text so that the text cannot close it.
8
+ */
9
+ export declare function toTildeCodeBlock(text: string, language?: string): string;
10
+ /**
11
+ * Wraps text in a Markdown code block fenced with backticks.
12
+ * The fence is longer than any run of backticks in the text so that the text cannot close it.
13
+ */
14
+ export declare function toCodeBlock(text: string, language?: string): string;
package/dist/text.js CHANGED
@@ -1,2 +1,4 @@
1
- function e(e){return e.replaceAll(/[$()*+.?[\\\]^{|}]/gu,String.raw`\$&`)}function t(e){return/^[\w./-]+$/u.test(e)?e:`'${e.replaceAll(`'`,String.raw`'\''`)}'`}export{e as escapeRegExp,t as quoteForShell};
1
+ function e(e){return e.replaceAll(/[$()*+.?[\\\]^{|}]/gu,String.raw`\$&`)}function t(e){return/^[\w./-]+$/u.test(e)?e:`'${e.replaceAll(`'`,String.raw`'\''`)}'`}function n(e,t=``){return i(`~`,e,t)}function r(e,t=``){return i("`",e,t)}function i(e,t,n){let r=e.repeat(3),i=2,a=t.indexOf(r);for(;a!==-1;){let n=a+3;for(;t[n]===e;)n++;i=Math.max(i,n-a),a=t.indexOf(r,n)}let o=0;for(;n[o]===e;)o++;let s=e.repeat(i+1+o);return`${s}${n.slice(o)}\n${t}${t.endsWith(`
2
+ `)?``:`
3
+ `}${s}`}export{e as escapeRegExp,t as quoteForShell,r as toCodeBlock,n as toTildeCodeBlock};
2
4
  //# sourceMappingURL=text.js.map
package/dist/text.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"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"],"mappings":"AACA,SAAgB,EAAa,EAAsB,CACjD,OAAO,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgB,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAK,CAAK,EAAI,EAAQ,IAAI,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF"}
1
+ {"version":3,"file":"text.js","names":[],"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"],"mappings":"AACA,SAAgB,EAAa,EAAsB,CACjD,OAAO,EAAK,WAAW,uBAAwB,OAAO,GAAG,KAAK,CAChE,CAGA,SAAgB,EAAc,EAAuB,CACnD,MAAO,cAAc,KAAK,CAAK,EAAI,EAAQ,IAAI,EAAM,WAAW,IAAK,OAAO,GAAG,MAAM,EAAE,EACzF,CAMA,SAAgB,EAAiB,EAAc,EAAW,GAAY,CACpE,OAAO,EAAkB,IAAK,EAAM,CAAQ,CAC9C,CAMA,SAAgB,EAAY,EAAc,EAAW,GAAY,CAC/D,OAAO,EAAkB,IAAK,EAAM,CAAQ,CAC9C,CAEA,SAAS,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAM,EAAgB,EAAU,OAAO,CAAC,EACpC,EAAa,EACb,EAAQ,EAAK,QAAQ,CAAa,EACtC,KAAO,IAAU,IAAI,CACnB,IAAI,EAAM,EAAQ,EAClB,KAAO,EAAK,KAAS,GAAW,IAChC,EAAa,KAAK,IAAI,EAAY,EAAM,CAAK,EAC7C,EAAQ,EAAK,QAAQ,EAAe,CAAG,CACzC,CACA,IAAI,EAAgB,EACpB,KAAO,EAAS,KAAmB,GAAW,IAC9C,IAAM,EAAQ,EAAU,OAAO,EAAa,EAAI,CAAa,EAC7D,MAAO,GAAG,IAAQ,EAAS,MAAM,CAAa,EAAE,IAAI,IAAO,EAAK,SAAS;CAAI,EAAI,GAAK;IAAO,GAC/F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/shared-lib",
3
- "version": "10.6.0",
3
+ "version": "10.8.0",
4
4
  "description": "Shared library for WillBooster projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,18 +45,19 @@
45
45
  "@tsconfig/bun": "1.0.11",
46
46
  "@tsconfig/node-lts": "24.0.1",
47
47
  "@tsconfig/node-ts": "23.6.4",
48
- "@types/bun": "1.4.1",
49
- "@types/node": "26.4.1",
48
+ "@types/bun": "1.4.2",
49
+ "@types/node": "26.5.1",
50
50
  "@willbooster/oxfmt-config": "1.2.2",
51
51
  "@willbooster/oxlint-config": "1.4.8",
52
52
  "@willbooster/wb": "22.4.0",
53
53
  "build-ts": "21.0.14",
54
- "oxfmt": "0.66.0",
55
- "oxlint": "1.81.0",
54
+ "oxfmt": "0.67.0",
55
+ "oxlint": "1.82.0",
56
56
  "oxlint-tsgolint": "7.0.2001",
57
57
  "sort-package-json": "4.0.0",
58
58
  "typescript": "7.0.2",
59
- "vitest": "5.0.0"
59
+ "vitest": "5.0.0",
60
+ "yaml": "2.9.0"
60
61
  },
61
62
  "publishConfig": {
62
63
  "access": "public",