@willbooster/shared-lib 10.23.0 → 10.24.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.
Files changed (72) hide show
  1. package/dist/appEnv.cjs.map +1 -1
  2. package/dist/appEnv.js.map +1 -1
  3. package/dist/array.cjs.map +1 -1
  4. package/dist/array.js.map +1 -1
  5. package/dist/assert.cjs.map +1 -1
  6. package/dist/assert.js.map +1 -1
  7. package/dist/concurrency.cjs.map +1 -1
  8. package/dist/concurrency.js.map +1 -1
  9. package/dist/crypto.cjs.map +1 -1
  10. package/dist/crypto.js.map +1 -1
  11. package/dist/error.cjs.map +1 -1
  12. package/dist/error.js.map +1 -1
  13. package/dist/filePath.cjs.map +1 -1
  14. package/dist/filePath.js.map +1 -1
  15. package/dist/foreignCjk.cjs.map +1 -1
  16. package/dist/foreignCjk.js.map +1 -1
  17. package/dist/formatIsoDate.cjs.map +1 -1
  18. package/dist/formatIsoDate.js.map +1 -1
  19. package/dist/github.cjs.map +1 -1
  20. package/dist/github.js.map +1 -1
  21. package/dist/githubTemplates.cjs.map +1 -1
  22. package/dist/githubTemplates.js.map +1 -1
  23. package/dist/http.cjs.map +1 -1
  24. package/dist/http.js.map +1 -1
  25. package/dist/humanize.cjs.map +1 -1
  26. package/dist/humanize.js.map +1 -1
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.js +1 -1
  30. package/dist/mail.cjs.map +1 -1
  31. package/dist/mail.js.map +1 -1
  32. package/dist/markdown.cjs +6 -5
  33. package/dist/markdown.cjs.map +1 -1
  34. package/dist/markdown.d.ts +11 -3
  35. package/dist/markdown.js +6 -5
  36. package/dist/markdown.js.map +1 -1
  37. package/dist/number.cjs.map +1 -1
  38. package/dist/number.js.map +1 -1
  39. package/dist/object.cjs.map +1 -1
  40. package/dist/object.js.map +1 -1
  41. package/dist/parseCommandLineArgs.cjs.map +1 -1
  42. package/dist/parseCommandLineArgs.js.map +1 -1
  43. package/dist/parseIsoDate.cjs.map +1 -1
  44. package/dist/parseIsoDate.js.map +1 -1
  45. package/dist/prompt.cjs.map +1 -1
  46. package/dist/prompt.js.map +1 -1
  47. package/dist/promptFormatters.cjs.map +1 -1
  48. package/dist/promptFormatters.js.map +1 -1
  49. package/dist/recoverJson.cjs.map +1 -1
  50. package/dist/recoverJson.js.map +1 -1
  51. package/dist/shuffle.cjs.map +1 -1
  52. package/dist/shuffle.js.map +1 -1
  53. package/dist/sleep.cjs.map +1 -1
  54. package/dist/sleep.js.map +1 -1
  55. package/dist/sqlite.cjs.map +1 -1
  56. package/dist/sqlite.js.map +1 -1
  57. package/dist/text.cjs +2 -2
  58. package/dist/text.cjs.map +1 -1
  59. package/dist/text.d.ts +2 -0
  60. package/dist/text.js +2 -2
  61. package/dist/text.js.map +1 -1
  62. package/dist/throttledFetch.cjs.map +1 -1
  63. package/dist/throttledFetch.js.map +1 -1
  64. package/dist/timeout.cjs.map +1 -1
  65. package/dist/timeout.js.map +1 -1
  66. package/dist/url.cjs.map +1 -1
  67. package/dist/url.js.map +1 -1
  68. package/dist/yaml.cjs.map +1 -1
  69. package/dist/yaml.js.map +1 -1
  70. package/dist/zenkaku.cjs.map +1 -1
  71. package/dist/zenkaku.js.map +1 -1
  72. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"recoverJson.js","names":["MAX_INPUT_LENGTH","QUOTES","CLOSERS","KEYWORDS","recoverJson","result","text","fences","findFences","consumed","fence","extractThroughFence","extractRegion","error","marker","markerEnd","lineEnd","info","inlinePrefix","inlineStart","start","isContainerStart","scalarStart","parsedThrough","close","match","end","extendedEnd","outerClose","next","RegionExtractor","commentFragment","firstIndex","initialParser","RecoveryParser","initialValueStart","repair","initialValue","atInitialValue","index","uriEnd","unquotedUriEnd","whitespace","container","trivia","valueStart","bounded","parser","boundedJson","extended","json","valueEnd","getErrorMessage","surplusStart","mark","excerptEnd","offset","excerpt","summary","char","first","keyword","word","atom","bullet","retainLiteral","regionEnd","depth","token","canonical","object","open","items","keys","afterComma","commaOffset","key","decoded","seen","rawKey","value","tail","append","embeddedQuote","family","alternative","after","delimited","context","nextClose","afterNext","escapeOffset","escape","hex","quote","continuation","reason","brackets"],"sources":["../src/recoverJson.ts"],"sourcesContent":["/*! JSON recovery informed by jsonrepair, Copyright (c) 2020-2026 Jos de Jong (ISC). See NOTICE. */\n\nimport { getErrorMessage } from './error.js';\n\nexport interface JsonRepair {\n /** UTF-16 offset in the original response. */\n offset: number;\n kind: 'syntax' | 'incomplete' | 'ambiguous';\n reason: string;\n}\n\nexport interface RecoveredJson {\n value: unknown;\n json: string;\n start: number;\n end: number;\n repairs: JsonRepair[];\n /** Missing content or an ambiguous interpretation requires confirmation before acting on this value. */\n requiresConfirmation: boolean;\n}\n\nexport interface JsonRecovery {\n candidates: RecoveredJson[];\n errors: { offset: number; message: string }[];\n}\n\nconst MAX_INPUT_LENGTH = 1_000_000;\nconst MAX_DEPTH = 128;\nconst MAX_CANDIDATES = 32;\nconst MAX_ERRORS = 32;\nconst MAX_DIAGNOSTIC_EXCERPT = 256;\nconst QUOTES: Record<string, string> = { '\"': '\"', \"'\": \"'\", '“': '”', '”': '”', '‘': '’', '’': '’' };\nconst CLOSERS = { '{': '}', '[': ']' } as const;\nconst KEYWORDS: Record<string, string> = {\n true: 'true',\n false: 'false',\n null: 'null',\n True: 'true',\n False: 'false',\n None: 'null',\n};\n\n/**\n * Extracts JSON values from a response, repairing common LLM syntax and retaining repair provenance.\n * Offsets refer to the original string. All candidates are returned: choosing among multiple answers,\n * schema validation, and provider truncation signals belong to the caller. No schema values are coerced.\n * Bounded to one million UTF-16 code units, 128 nesting levels, and 32 candidates/errors each.\n * A region is abandoned after 32 failed starts; later Markdown regions can still yield candidates.\n */\nexport function recoverJson(text: string): JsonRecovery {\n const result: JsonRecovery = { candidates: [], errors: [] };\n if (text.length > MAX_INPUT_LENGTH) {\n result.errors.push({ offset: MAX_INPUT_LENGTH, message: 'JSON recovery input exceeds one million characters' });\n return result;\n }\n const fences = findFences(text);\n let consumed = 0;\n for (const fence of fences) {\n if (fence.index >= consumed) consumed = extractThroughFence(text, fences, fence, consumed, result);\n }\n extractRegion(text, consumed, text.length, result);\n if (result.candidates.length === MAX_CANDIDATES) {\n const error = {\n offset: result.candidates.at(-1)!.end,\n message: 'JSON candidate limit reached; extraction may be incomplete',\n };\n if (result.errors.length === MAX_ERRORS) result.errors[MAX_ERRORS - 1] = error;\n else result.errors.push(error);\n }\n return result;\n}\n\ninterface Fence {\n index: number;\n marker: string;\n /** Where the fenced content starts: after the opening line, or within it when JSON follows the info string. */\n start: number;\n}\n\nfunction findFences(text: string): Fence[] {\n return [...text.matchAll(/^(?: {0,3})(`{3,}|~{3,})[^\\r\\n]*(?:\\r\\n|[\\r\\n]|$)/gm)].flatMap((fence) => {\n const marker = fence[1]!;\n const markerEnd = fence.index + fence[0].indexOf(marker) + marker.length;\n const lineEnd = fence.index + fence[0].length;\n const info = text.slice(markerEnd, lineEnd);\n const inlinePrefix = /^[ \\t]*json(?=[ \\t{[\"'“”‘’])[ \\t]*/i.exec(info);\n const inlineStart = inlinePrefix === null ? lineEnd : markerEnd + inlinePrefix[0].length;\n const start =\n isContainerStart(text[inlineStart]) || scalarStart(text, inlineStart, lineEnd) ? inlineStart : lineEnd;\n return marker[0] === '`' && start === lineEnd && info.includes('`') ? [] : [{ index: fence.index, marker, start }];\n });\n}\n\n/** Extracts the prose before `fence` and then its content, returning the offset up to which the text is consumed. */\nfunction extractThroughFence(\n text: string,\n fences: Fence[],\n fence: Fence,\n consumed: number,\n result: JsonRecovery\n): number {\n extractRegion(text, consumed, fence.index, result);\n const parsedThrough = result.candidates.at(-1)?.end ?? consumed;\n if (parsedThrough > fence.index) return parsedThrough;\n const { marker, start } = fence;\n const close = new RegExp(`^ {0,3}${marker[0]}{${marker.length},}[ \\\\t]*$`, 'gm');\n close.lastIndex = start;\n const match = close.exec(text);\n const end = match?.index ?? text.length;\n extractRegion(text, start, end, result);\n const extendedEnd = result.candidates.at(-1)?.end ?? start;\n if (extendedEnd > end) {\n close.lastIndex = extendedEnd;\n const outerClose = close.exec(text);\n if (outerClose !== null && !fences.some((next) => next.index >= extendedEnd && next.index < outerClose.index)) {\n extractRegion(text, extendedEnd, outerClose.index, result);\n return outerClose.index + outerClose[0].length;\n }\n }\n return Math.max(result.candidates.at(-1)?.end ?? start, match === null ? text.length : match.index + match[0].length);\n}\n\nfunction extractRegion(text: string, start: number, end: number, result: JsonRecovery, commentFragment = false): void {\n if (result.candidates.length < MAX_CANDIDATES) new RegionExtractor(text, start, end, result, commentFragment).run();\n}\n\nclass RegionExtractor {\n private readonly text: string;\n private readonly start: number;\n private readonly end: number;\n private readonly result: JsonRecovery;\n private readonly commentFragment: boolean;\n private index: number;\n private failures = 0;\n private ambiguousScalarTail = false;\n private scalarBoundary = true;\n private uriBoundaryEnd: number;\n private uriTokenEnd: number;\n private uriContainerStart: number;\n\n constructor(text: string, start: number, end: number, result: JsonRecovery, commentFragment: boolean) {\n this.text = text;\n this.start = start;\n this.end = end;\n this.result = result;\n this.commentFragment = commentFragment;\n this.index = start;\n this.uriBoundaryEnd = start;\n this.uriTokenEnd = start;\n this.uriContainerStart = start;\n }\n\n run(): void {\n const { text, end, result } = this;\n while (this.index < end && /\\s/.test(text[this.index]!)) this.index++;\n const firstIndex = this.index;\n const initialParser = new RecoveryParser(text, this.index, end);\n if (!this.commentFragment) initialParser.space();\n const initialValueStart = initialParser.index;\n if (initialValueStart >= end) {\n if (initialParser.repairs.some((repair) => repair.kind === 'incomplete'))\n this.reportError(this.index, 'Incomplete leading JSON comment');\n return;\n }\n // Within prose, scalar starts need a line boundary; containers may appear inline.\n const initialValue = scalarStart(text, initialValueStart, end) || isContainerStart(text[initialValueStart]);\n if (!initialValue) this.index = initialValueStart;\n while (this.index < end && result.candidates.length < MAX_CANDIDATES && this.failures < MAX_ERRORS) {\n const atInitialValue = initialValue && this.index === firstIndex;\n if (!atInitialValue) this.skipProse();\n if (this.index >= end || result.candidates.length >= MAX_CANDIDATES) break;\n this.extractCandidate(atInitialValue ? initialParser : new RecoveryParser(text, this.index, end));\n }\n }\n\n /** Advances to the next position where a JSON value may start. */\n private skipProse(): void {\n const { text, end } = this;\n while (this.index < end && !isContainerStart(text[this.index])) {\n if (this.scalarBoundary && scalarStart(text, this.index, end)) break;\n if (this.skipUri() || this.skipCommentLikeSpan()) continue;\n if (/[\\r\\n]/.test(text[this.index]!)) this.scalarBoundary = true;\n else if (!/[\\s,]/.test(text[this.index]!)) this.scalarBoundary = false;\n this.index++;\n }\n }\n\n private skipUri(): boolean {\n const { text, end, index } = this;\n if (index !== this.start && /[\\w+.-]/.test(text[index - 1]!)) return false;\n const uriEnd = unquotedUriEnd(text, index, end);\n if (uriEnd === undefined) return false;\n if (index >= this.uriTokenEnd) {\n const whitespace = text.slice(index, end).search(/\\s/);\n this.uriTokenEnd = whitespace === -1 ? end : index + whitespace;\n this.uriContainerStart = index;\n }\n if (index >= this.uriContainerStart) {\n const container = text.slice(index, this.uriTokenEnd).search(/[{[]/);\n this.uriContainerStart = container === -1 ? this.uriTokenEnd : index + container;\n }\n if (this.uriContainerStart < this.uriTokenEnd)\n this.uriBoundaryEnd = Math.max(this.uriBoundaryEnd, this.uriTokenEnd);\n this.index = Math.min(uriEnd, this.uriContainerStart);\n this.scalarBoundary = false;\n return true;\n }\n\n private skipCommentLikeSpan(): boolean {\n const { text, end, index } = this;\n if (this.commentFragment || text[index] !== '/') return false;\n const trivia = new RecoveryParser(text, index, end);\n trivia.space();\n if (trivia.index <= index) return false;\n if (trivia.repairs.some((repair) => repair.kind === 'incomplete'))\n this.reportError(index, 'Possibly unterminated comment-like span in prose');\n extractRegion(text, index + 2, trivia.index, this.result, true);\n this.scalarBoundary ||= /[\\r\\n]/.test(text.slice(index, trivia.index));\n this.index = trivia.index;\n return true;\n }\n\n private extractCandidate(bounded: RecoveryParser): void {\n const { text, end, index } = this;\n const valueStart = bounded.index;\n let parser = bounded;\n try {\n const boundedJson = bounded.value(0);\n const extended = this.parsePastRegionEnd(bounded, valueStart);\n parser = extended?.parser ?? bounded;\n const json = extended?.json ?? boundedJson;\n const valueEnd = parser.index;\n if (!isContainerStart(text[valueStart]) && !parser.finishScalar(end, valueStart)) {\n this.scalarBoundary = false;\n this.index = valueEnd;\n return;\n }\n parser.space();\n this.consumeSurplusClosers(parser);\n if (text[valueStart]! in QUOTES && parser.hasRepair('trailing-scalar-content'))\n this.reportExcerpt(valueEnd, 'Ambiguous quoted-scalar tail');\n this.markFragmentProvenance(parser);\n this.ambiguousScalarTail ||= parser.hasRepair('trailing-scalar-content');\n this.result.candidates.push({\n value: JSON.parse(json),\n json,\n start: index,\n end: parser.index,\n repairs: parser.repairs,\n requiresConfirmation: parser.repairs.some((repair) => repair.kind !== 'syntax'),\n });\n } catch (error) {\n this.reportError(parser.index, getErrorMessage(error));\n this.failures++;\n this.index = valueStart + 1;\n this.scalarBoundary = false;\n return;\n }\n this.index = Math.max(index + 1, parser.index);\n this.scalarBoundary = true;\n }\n\n /**\n * Reparses a container that `bounded` ended exactly at the region end against the rest of the text,\n * as the fence that bounds the region may be literal text inside one of its strings.\n */\n private parsePastRegionEnd(\n bounded: RecoveryParser,\n valueStart: number\n ): { parser: RecoveryParser; json: string } | undefined {\n const { text, end } = this;\n if (this.commentFragment || !isContainerStart(text[valueStart]) || bounded.index !== end || end >= text.length)\n return undefined;\n const parser = new RecoveryParser(text, this.index, text.length);\n try {\n const json = parser.value(0);\n if (parser.index > end && parser.repairs.every((repair) => repair.reason === 'unescaped-control-character')) {\n parser.repairs.push({ offset: end, kind: 'ambiguous', reason: 'literal-fence-in-string' });\n return { parser, json };\n }\n } catch {\n // Keep the bounded interpretation when extending it is not a complete JSON document.\n }\n return undefined;\n }\n\n private consumeSurplusClosers(parser: RecoveryParser): void {\n if (this.commentFragment || this.failures > 0) return;\n const surplusStart = parser.index;\n while (parser.index < this.end && /[}\\]]/.test(this.text[parser.index]!)) {\n parser.repairs.push({ offset: parser.index, kind: 'ambiguous', reason: 'surplus-closing-bracket' });\n parser.index++;\n parser.space();\n }\n if (parser.index > surplusStart)\n this.reportExcerpt(surplusStart, 'Surplus closing brackets and possible continuation');\n }\n\n private markFragmentProvenance(parser: RecoveryParser): void {\n const mark = (reason: string): void => {\n parser.repairs.unshift({ offset: this.index, kind: 'ambiguous', reason });\n };\n if (this.commentFragment) mark('fragment-in-comment-like-prose');\n if (this.failures > 0) mark('fragment-after-rejected-document');\n if (this.ambiguousScalarTail) mark('fragment-after-ambiguous-scalar');\n if (this.index < this.uriBoundaryEnd) mark('ambiguous-uri-boundary');\n }\n\n private reportExcerpt(offset: number, summary: string): void {\n const excerptEnd = Math.min(this.end, offset + MAX_DIAGNOSTIC_EXCERPT);\n const excerpt = JSON.stringify(this.text.slice(offset, excerptEnd));\n this.reportError(offset, `${summary} (${excerptEnd < this.end ? 'truncated excerpt' : 'excerpt'}): ${excerpt}`);\n }\n\n private reportError(offset: number, message: string): void {\n if (this.result.errors.length < MAX_ERRORS) this.result.errors.push({ offset, message });\n }\n}\n\nfunction isContainerStart(char: string | undefined): char is keyof typeof CLOSERS {\n return char === '{' || char === '[';\n}\n\nfunction scalarStart(text: string, start: number, end: number): boolean {\n const first = text[start];\n if (first === undefined) return false;\n const keyword = /^([A-Za-z]+)/.exec(text.slice(start, end))?.[1];\n return (\n first in QUOTES ||\n /[-\\d]/.test(first) ||\n (keyword !== undefined &&\n (Object.hasOwn(KEYWORDS, keyword) ||\n (Object.keys(KEYWORDS).some((word) => word.startsWith(keyword)) &&\n /^[^\\S\\r\\n]*(?:[\\r\\n]|$)/.test(text.slice(start + keyword.length, end)))))\n );\n}\n\nclass RecoveryParser {\n readonly repairs: JsonRepair[] = [];\n readonly text: string;\n readonly end: number;\n index: number;\n\n constructor(text: string, index: number, end: number) {\n this.text = text;\n this.index = index;\n this.end = end;\n }\n\n finishScalar(regionEnd: number, start: number): boolean {\n const valueEnd = this.index;\n const atom = this.text.slice(start, valueEnd);\n const bullet = atom === '-' && /\\s/.test(this.text[valueEnd] ?? '');\n const retainLiteral = this.text[start]! in QUOTES || this.hasRepair('unquoted-value');\n this.space();\n if (bullet) return false;\n if (this.index >= regionEnd) return true;\n if (/[\\r\\n]/.test(this.text.slice(valueEnd, this.index))) {\n this.repair('ambiguous', 'scalar-before-prose', valueEnd);\n return true;\n }\n if (/^\\d+[.)]$/.test(atom) && /[ \\t]/.test(this.text[valueEnd] ?? '')) return false;\n if (retainLiteral || /^[\\p{P}\\p{S}]/u.test(this.text.slice(this.index, regionEnd))) {\n this.repair('ambiguous', 'trailing-scalar-content', valueEnd);\n return true;\n }\n return false;\n }\n\n value(depth: number): string {\n if (depth > MAX_DEPTH) throw new Error('JSON nesting exceeds 128 levels');\n this.space();\n const char = this.text[this.index];\n if (this.index >= this.end || char === ',' || char === '}' || char === ']') {\n this.repair('incomplete', 'missing-value');\n return 'null';\n }\n if (isContainerStart(char)) {\n if (depth >= MAX_DEPTH) throw new Error('JSON nesting exceeds 128 levels');\n return this.container(depth, char);\n }\n if (char !== undefined && char in QUOTES) return this.string(depth > 0 ? 'value' : 'root');\n const start = this.index;\n const uriEnd = unquotedUriEnd(this.text, start, this.end);\n if (uriEnd !== undefined) this.index = uriEnd;\n else\n while (\n this.index < this.end &&\n !/[\\s,}\\]]/.test(this.text[this.index]!) &&\n !(depth === 0 && /[{[]/.test(this.text[this.index]!)) &&\n !this.comment()\n )\n this.index++;\n if (this.index === start) throw new Error('Expected a JSON value');\n const token = this.text.slice(start, this.index);\n if (/^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$/.test(token)) return token;\n if (Object.hasOwn(KEYWORDS, token)) {\n const canonical = KEYWORDS[token]!;\n if (canonical !== token) this.repair('syntax', 'python-keyword', start);\n return canonical;\n }\n this.repair(this.index >= this.end ? 'incomplete' : 'ambiguous', 'unquoted-value', start);\n return JSON.stringify(token);\n }\n\n private container(depth: number, open: keyof typeof CLOSERS): string {\n this.index++;\n const object = open === '{';\n const close = CLOSERS[open];\n const items: string[] = [];\n const keys = new Set<string>();\n let afterComma = false;\n let commaOffset = this.index;\n for (;;) {\n this.space();\n if (this.index >= this.end || this.text[this.index] === close) {\n if (this.index >= this.end) this.repair('incomplete', `missing-${close}`);\n else this.index++;\n if (afterComma) this.repair('syntax', 'trailing-comma', commaOffset);\n return open + items.join(',') + close;\n }\n if (this.text[this.index] === '}' || this.text[this.index] === ']') throw new Error('Mismatched closing bracket');\n if (this.text[this.index] === ',') {\n if (object) this.repair('ambiguous', 'extra-comma');\n else {\n items.push('null');\n this.repair('incomplete', 'missing-value');\n }\n commaOffset = this.index;\n afterComma = true;\n this.index++;\n continue;\n }\n items.push(object ? `${this.key(keys)}:${this.value(depth + 1)}` : this.value(depth + 1));\n this.space();\n afterComma = this.text[this.index] === ',';\n if (afterComma) {\n commaOffset = this.index;\n this.index++;\n } else if (this.index < this.end && this.text[this.index] !== close) this.repair('syntax', 'missing-comma');\n }\n }\n\n /** Parses an object key and the colon after it, returning the key as JSON. */\n private key(seen: Set<string>): string {\n const start = this.index;\n const key = this.text[start]! in QUOTES ? this.string() : this.unquotedKey();\n const decoded: string = JSON.parse(key);\n if (seen.has(decoded)) this.repair('ambiguous', 'duplicate-key', start);\n seen.add(decoded);\n this.space();\n if (this.text[this.index] === ':') this.index++;\n else this.repair('ambiguous', 'missing-colon');\n return key;\n }\n\n private unquotedKey(): string {\n const start = this.index;\n while (this.index < this.end && !/[\\s:,{}[\\]]/.test(this.text[this.index]!) && !this.comment()) this.index++;\n if (this.index === start) throw new Error('Expected an object key');\n const rawKey = this.text.slice(start, this.index);\n this.repair(/[\"'“”‘’]/.test(rawKey) ? 'ambiguous' : 'syntax', 'unquoted-key', start);\n return JSON.stringify(rawKey);\n }\n\n private string(context: 'root' | 'key' | 'value' = 'key'): string {\n const start = this.index;\n const open = this.text[this.index++]!;\n const close = QUOTES[open]!;\n if (open !== '\"') this.repair('syntax', 'non-json-quote', start);\n let value = '';\n let tail = '';\n const append = (text: string): void => {\n value += text;\n tail = (tail + text).slice(-2);\n };\n let embeddedQuote = false;\n const family = '\"“”'.includes(open) ? '\"“”' : \"'‘’\";\n let alternative: { end: number; value: string; repairCount: number } | undefined;\n while (this.index < this.end) {\n const char = this.text[this.index++]!;\n if (\n \"'‘’\".includes(open) &&\n \"'’\".includes(char) &&\n /[\\p{L}\\p{N}]$/u.test(tail) &&\n /^[\\p{L}\\p{N}]/u.test(this.text.slice(this.index, this.end))\n ) {\n this.repair('ambiguous', 'literal-apostrophe', this.index - 1);\n append(char);\n continue;\n }\n if (family.includes(char)) {\n const after = this.charAfterSpace(this.index);\n const delimited = after === undefined || /[,}\\]:\"']/.test(after);\n if (char !== close) {\n if (delimited) alternative ??= { end: this.index, value, repairCount: this.repairs.length };\n } else if (!embeddedQuote && context !== 'root' && alternative !== undefined && !delimited) {\n this.index = alternative.end;\n this.repairs.length = alternative.repairCount;\n this.repair('ambiguous', 'mismatched-quote', this.index - 1);\n return JSON.stringify(alternative.value);\n } else if (embeddedQuote || (context === 'value' && !delimited && this.opensEmbeddedQuote(close))) {\n embeddedQuote = !embeddedQuote;\n this.repair('ambiguous', 'unescaped-quote', this.index - 1);\n } else return JSON.stringify(value);\n }\n if (char === '\\\\') append(this.escapeSequence(open));\n else {\n if (char.codePointAt(0)! < 32) this.repair('syntax', 'unescaped-control-character', this.index - 1);\n append(char);\n }\n }\n this.repair('incomplete', 'unterminated-string', start);\n return JSON.stringify(value);\n }\n\n /** Whether the closing quote just consumed and the next one more plausibly enclose a phrase quoted within the string. */\n private opensEmbeddedQuote(close: string): boolean {\n const nextClose = this.text.indexOf(close, this.index);\n const afterNext = this.charAfterSpace(nextClose + 1);\n return (\n !/\\s/.test(this.text[this.index] ?? '') &&\n nextClose > this.index &&\n nextClose < this.end &&\n afterNext !== undefined &&\n (!/[,}:\\]]/.test(afterNext) || (afterNext === ',' && this.continuesStringAfterComma(nextClose + 1, close))) &&\n !/[\\\\*:,{}[\\]\\r\\n]|\\/\\//.test(this.text.slice(this.index, nextClose)) &&\n !this.startsKey(nextClose)\n );\n }\n\n /** Decodes the escape sequence whose backslash was just consumed. */\n private escapeSequence(open: string): string {\n const escapeOffset = this.index - 1;\n if (this.index >= this.end) {\n this.repair('incomplete', 'truncated-escape', escapeOffset);\n return '\\\\';\n }\n const escape = this.text[this.index++]!;\n if (escape === 'u') {\n const hex = this.text.slice(this.index, Math.min(this.index + 4, this.end));\n if (/^[\\da-fA-F]{4}$/.test(hex)) {\n this.index += 4;\n return String.fromCodePoint(Number.parseInt(hex, 16));\n }\n this.repair(/^[\\da-fA-F]{0,3}$/.test(hex) ? 'incomplete' : 'ambiguous', 'invalid-unicode-escape', escapeOffset);\n return String.raw`\\u`;\n }\n if (String.raw`\"\\/bfnrt`.includes(escape)) return JSON.parse(`\"\\\\${escape}\"`) as string;\n if (escape === \"'\" && open === \"'\") return \"'\";\n this.repair('ambiguous', 'invalid-escape', escapeOffset);\n return `\\\\${escape}`;\n }\n\n private continuesStringAfterComma(start: number, quote: string): boolean {\n const parser = new RecoveryParser(this.text, start - 1, this.end);\n parser.string();\n const end = parser.index - 1;\n if (\n this.text[end] !== quote ||\n parser.repairs.some(\n (repair) => !['non-json-quote', 'literal-apostrophe', 'unescaped-control-character'].includes(repair.reason)\n )\n )\n return false;\n const continuation = this.text.slice(start, end);\n const after = this.charAfterSpace(end + 1);\n return (\n /^\\s*,\\s*[\\p{L}\\p{N}]/u.test(continuation) &&\n !/[\\\\:{}[\\]]/.test(continuation) &&\n (after === undefined || /[,}\\]]/.test(after))\n );\n }\n\n private startsKey(index: number): boolean {\n const parser = new RecoveryParser(this.text, index, this.end);\n parser.string();\n parser.space();\n return this.text[parser.index] === ':';\n }\n\n space(): void {\n while (this.index < this.end) {\n if (/\\s/.test(this.text[this.index]!)) {\n if (!/[ \\t\\r\\n]/.test(this.text[this.index]!)) this.repair('syntax', 'non-json-whitespace');\n this.index++;\n } else if (this.text.startsWith('//', this.index)) {\n this.repair('syntax', 'line-comment');\n while (this.index < this.end && !/[\\r\\n]/.test(this.text[this.index]!)) this.index++;\n } else if (this.text.startsWith('/*', this.index)) {\n const end = this.text.indexOf('*/', this.index + 2);\n this.repair(end === -1 || end >= this.end ? 'incomplete' : 'syntax', 'block-comment');\n this.index = end === -1 || end >= this.end ? this.end : end + 2;\n } else break;\n }\n }\n\n hasRepair(reason: string): boolean {\n return this.repairs.some((repair) => repair.reason === reason);\n }\n\n private charAfterSpace(index: number): string | undefined {\n return this.text.slice(index, this.end).trimStart()[0];\n }\n\n private repair(kind: JsonRepair['kind'], reason: string, offset = this.index): void {\n this.repairs.push({ offset, kind, reason });\n }\n\n private comment(): boolean {\n return this.text.startsWith('//', this.index) || this.text.startsWith('/*', this.index);\n }\n}\n\nfunction unquotedUriEnd(text: string, start: number, end: number): number | undefined {\n const match = /^[A-Za-z][A-Za-z\\d+.-]*:\\/\\//.exec(text.slice(start, end));\n if (match === null) return;\n let index = start + match[0].length;\n let brackets = 0;\n while (index < end && !/[\\s}]/.test(text[index]!)) {\n if (text[index] === ',' && brackets === 0) break;\n if (text[index] === '[') brackets++;\n else if (text[index] === ']') {\n if (brackets === 0) break;\n brackets--;\n }\n index++;\n }\n return index;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recoverJson.js","names":["MAX_INPUT_LENGTH","QUOTES","CLOSERS","KEYWORDS","recoverJson","result","text","fences","findFences","consumed","fence","extractThroughFence","extractRegion","error","marker","markerEnd","lineEnd","info","inlinePrefix","inlineStart","start","isContainerStart","scalarStart","parsedThrough","close","match","end","extendedEnd","outerClose","next","RegionExtractor","commentFragment","firstIndex","initialParser","RecoveryParser","initialValueStart","repair","initialValue","atInitialValue","index","uriEnd","unquotedUriEnd","whitespace","container","trivia","valueStart","bounded","parser","boundedJson","extended","json","valueEnd","getErrorMessage","surplusStart","mark","excerptEnd","offset","excerpt","summary","char","first","keyword","word","atom","bullet","retainLiteral","regionEnd","depth","token","canonical","object","open","items","keys","afterComma","commaOffset","key","decoded","seen","rawKey","value","tail","append","embeddedQuote","family","alternative","after","delimited","context","nextClose","afterNext","escapeOffset","escape","hex","quote","continuation","reason","brackets"],"sources":["../src/recoverJson.ts"],"sourcesContent":["/*! JSON recovery informed by jsonrepair, Copyright (c) 2020-2026 Jos de Jong (ISC). See NOTICE. */\n\nimport { getErrorMessage } from './error.js';\n\nexport interface JsonRepair {\n /** UTF-16 offset in the original response. */\n offset: number;\n kind: 'syntax' | 'incomplete' | 'ambiguous';\n reason: string;\n}\n\nexport interface RecoveredJson {\n value: unknown;\n json: string;\n start: number;\n end: number;\n repairs: JsonRepair[];\n /** Missing content or an ambiguous interpretation requires confirmation before acting on this value. */\n requiresConfirmation: boolean;\n}\n\nexport interface JsonRecovery {\n candidates: RecoveredJson[];\n errors: { offset: number; message: string }[];\n}\n\nconst MAX_INPUT_LENGTH = 1_000_000;\nconst MAX_DEPTH = 128;\nconst MAX_CANDIDATES = 32;\nconst MAX_ERRORS = 32;\nconst MAX_DIAGNOSTIC_EXCERPT = 256;\nconst QUOTES: Record<string, string> = { '\"': '\"', \"'\": \"'\", '“': '”', '”': '”', '‘': '’', '’': '’' };\nconst CLOSERS = { '{': '}', '[': ']' } as const;\nconst KEYWORDS: Record<string, string> = {\n true: 'true',\n false: 'false',\n null: 'null',\n True: 'true',\n False: 'false',\n None: 'null',\n};\n\n/**\n * Extracts JSON values from a response, repairing common LLM syntax and retaining repair provenance.\n * Offsets refer to the original string. All candidates are returned: choosing among multiple answers,\n * schema validation, and provider truncation signals belong to the caller. No schema values are coerced.\n * Bounded to one million UTF-16 code units, 128 nesting levels, and 32 candidates/errors each.\n * A region is abandoned after 32 failed starts; later Markdown regions can still yield candidates.\n */\nexport function recoverJson(text: string): JsonRecovery {\n const result: JsonRecovery = { candidates: [], errors: [] };\n if (text.length > MAX_INPUT_LENGTH) {\n result.errors.push({ offset: MAX_INPUT_LENGTH, message: 'JSON recovery input exceeds one million characters' });\n return result;\n }\n const fences = findFences(text);\n let consumed = 0;\n for (const fence of fences) {\n if (fence.index >= consumed) consumed = extractThroughFence(text, fences, fence, consumed, result);\n }\n extractRegion(text, consumed, text.length, result);\n if (result.candidates.length === MAX_CANDIDATES) {\n const error = {\n offset: result.candidates.at(-1)!.end,\n message: 'JSON candidate limit reached; extraction may be incomplete',\n };\n if (result.errors.length === MAX_ERRORS) result.errors[MAX_ERRORS - 1] = error;\n else result.errors.push(error);\n }\n return result;\n}\n\ninterface Fence {\n index: number;\n marker: string;\n /** Where the fenced content starts: after the opening line, or within it when JSON follows the info string. */\n start: number;\n}\n\nfunction findFences(text: string): Fence[] {\n return [...text.matchAll(/^(?: {0,3})(`{3,}|~{3,})[^\\r\\n]*(?:\\r\\n|[\\r\\n]|$)/gm)].flatMap((fence) => {\n const marker = fence[1]!;\n const markerEnd = fence.index + fence[0].indexOf(marker) + marker.length;\n const lineEnd = fence.index + fence[0].length;\n const info = text.slice(markerEnd, lineEnd);\n const inlinePrefix = /^[ \\t]*json(?=[ \\t{[\"'“”‘’])[ \\t]*/i.exec(info);\n const inlineStart = inlinePrefix === null ? lineEnd : markerEnd + inlinePrefix[0].length;\n const start =\n isContainerStart(text[inlineStart]) || scalarStart(text, inlineStart, lineEnd) ? inlineStart : lineEnd;\n return marker[0] === '`' && start === lineEnd && info.includes('`') ? [] : [{ index: fence.index, marker, start }];\n });\n}\n\n/** Extracts the prose before `fence` and then its content, returning the offset up to which the text is consumed. */\nfunction extractThroughFence(\n text: string,\n fences: Fence[],\n fence: Fence,\n consumed: number,\n result: JsonRecovery\n): number {\n extractRegion(text, consumed, fence.index, result);\n const parsedThrough = result.candidates.at(-1)?.end ?? consumed;\n if (parsedThrough > fence.index) return parsedThrough;\n const { marker, start } = fence;\n const close = new RegExp(`^ {0,3}${marker[0]}{${marker.length},}[ \\\\t]*$`, 'gm');\n close.lastIndex = start;\n const match = close.exec(text);\n const end = match?.index ?? text.length;\n extractRegion(text, start, end, result);\n const extendedEnd = result.candidates.at(-1)?.end ?? start;\n if (extendedEnd > end) {\n close.lastIndex = extendedEnd;\n const outerClose = close.exec(text);\n if (outerClose !== null && !fences.some((next) => next.index >= extendedEnd && next.index < outerClose.index)) {\n extractRegion(text, extendedEnd, outerClose.index, result);\n return outerClose.index + outerClose[0].length;\n }\n }\n return Math.max(result.candidates.at(-1)?.end ?? start, match === null ? text.length : match.index + match[0].length);\n}\n\nfunction extractRegion(text: string, start: number, end: number, result: JsonRecovery, commentFragment = false): void {\n if (result.candidates.length < MAX_CANDIDATES) new RegionExtractor(text, start, end, result, commentFragment).run();\n}\n\nclass RegionExtractor {\n private readonly text: string;\n private readonly start: number;\n private readonly end: number;\n private readonly result: JsonRecovery;\n private readonly commentFragment: boolean;\n private index: number;\n private failures = 0;\n private ambiguousScalarTail = false;\n private scalarBoundary = true;\n private uriBoundaryEnd: number;\n private uriTokenEnd: number;\n private uriContainerStart: number;\n\n constructor(text: string, start: number, end: number, result: JsonRecovery, commentFragment: boolean) {\n this.text = text;\n this.start = start;\n this.end = end;\n this.result = result;\n this.commentFragment = commentFragment;\n this.index = start;\n this.uriBoundaryEnd = start;\n this.uriTokenEnd = start;\n this.uriContainerStart = start;\n }\n\n run(): void {\n const { text, end, result } = this;\n while (this.index < end && /\\s/.test(text[this.index]!)) this.index++;\n const firstIndex = this.index;\n const initialParser = new RecoveryParser(text, this.index, end);\n if (!this.commentFragment) initialParser.space();\n const initialValueStart = initialParser.index;\n if (initialValueStart >= end) {\n if (initialParser.repairs.some((repair) => repair.kind === 'incomplete'))\n this.reportError(this.index, 'Incomplete leading JSON comment');\n return;\n }\n // Within prose, scalar starts need a line boundary; containers may appear inline.\n const initialValue = scalarStart(text, initialValueStart, end) || isContainerStart(text[initialValueStart]);\n if (!initialValue) this.index = initialValueStart;\n while (this.index < end && result.candidates.length < MAX_CANDIDATES && this.failures < MAX_ERRORS) {\n const atInitialValue = initialValue && this.index === firstIndex;\n if (!atInitialValue) this.skipProse();\n if (this.index >= end || result.candidates.length >= MAX_CANDIDATES) break;\n this.extractCandidate(atInitialValue ? initialParser : new RecoveryParser(text, this.index, end));\n }\n }\n\n /** Advances to the next position where a JSON value may start. */\n private skipProse(): void {\n const { text, end } = this;\n while (this.index < end && !isContainerStart(text[this.index])) {\n if (this.scalarBoundary && scalarStart(text, this.index, end)) break;\n if (this.skipUri() || this.skipCommentLikeSpan()) continue;\n if (/[\\r\\n]/.test(text[this.index]!)) this.scalarBoundary = true;\n else if (!/[\\s,]/.test(text[this.index]!)) this.scalarBoundary = false;\n this.index++;\n }\n }\n\n private skipUri(): boolean {\n const { text, end, index } = this;\n if (index !== this.start && /[\\w+.-]/.test(text[index - 1]!)) return false;\n const uriEnd = unquotedUriEnd(text, index, end);\n if (uriEnd === undefined) return false;\n if (index >= this.uriTokenEnd) {\n const whitespace = text.slice(index, end).search(/\\s/);\n this.uriTokenEnd = whitespace === -1 ? end : index + whitespace;\n this.uriContainerStart = index;\n }\n if (index >= this.uriContainerStart) {\n const container = text.slice(index, this.uriTokenEnd).search(/[{[]/);\n this.uriContainerStart = container === -1 ? this.uriTokenEnd : index + container;\n }\n if (this.uriContainerStart < this.uriTokenEnd)\n this.uriBoundaryEnd = Math.max(this.uriBoundaryEnd, this.uriTokenEnd);\n this.index = Math.min(uriEnd, this.uriContainerStart);\n this.scalarBoundary = false;\n return true;\n }\n\n private skipCommentLikeSpan(): boolean {\n const { text, end, index } = this;\n if (this.commentFragment || text[index] !== '/') return false;\n const trivia = new RecoveryParser(text, index, end);\n trivia.space();\n if (trivia.index <= index) return false;\n if (trivia.repairs.some((repair) => repair.kind === 'incomplete'))\n this.reportError(index, 'Possibly unterminated comment-like span in prose');\n extractRegion(text, index + 2, trivia.index, this.result, true);\n this.scalarBoundary ||= /[\\r\\n]/.test(text.slice(index, trivia.index));\n this.index = trivia.index;\n return true;\n }\n\n private extractCandidate(bounded: RecoveryParser): void {\n const { text, end, index } = this;\n const valueStart = bounded.index;\n let parser = bounded;\n try {\n const boundedJson = bounded.value(0);\n const extended = this.parsePastRegionEnd(bounded, valueStart);\n parser = extended?.parser ?? bounded;\n const json = extended?.json ?? boundedJson;\n const valueEnd = parser.index;\n if (!isContainerStart(text[valueStart]) && !parser.finishScalar(end, valueStart)) {\n this.scalarBoundary = false;\n this.index = valueEnd;\n return;\n }\n parser.space();\n this.consumeSurplusClosers(parser);\n if (text[valueStart]! in QUOTES && parser.hasRepair('trailing-scalar-content'))\n this.reportExcerpt(valueEnd, 'Ambiguous quoted-scalar tail');\n this.markFragmentProvenance(parser);\n this.ambiguousScalarTail ||= parser.hasRepair('trailing-scalar-content');\n this.result.candidates.push({\n value: JSON.parse(json),\n json,\n start: index,\n end: parser.index,\n repairs: parser.repairs,\n requiresConfirmation: parser.repairs.some((repair) => repair.kind !== 'syntax'),\n });\n } catch (error) {\n this.reportError(parser.index, getErrorMessage(error));\n this.failures++;\n this.index = valueStart + 1;\n this.scalarBoundary = false;\n return;\n }\n this.index = Math.max(index + 1, parser.index);\n this.scalarBoundary = true;\n }\n\n /**\n * Reparses a container that `bounded` ended exactly at the region end against the rest of the text,\n * as the fence that bounds the region may be literal text inside one of its strings.\n */\n private parsePastRegionEnd(\n bounded: RecoveryParser,\n valueStart: number\n ): { parser: RecoveryParser; json: string } | undefined {\n const { text, end } = this;\n if (this.commentFragment || !isContainerStart(text[valueStart]) || bounded.index !== end || end >= text.length)\n return undefined;\n const parser = new RecoveryParser(text, this.index, text.length);\n try {\n const json = parser.value(0);\n if (parser.index > end && parser.repairs.every((repair) => repair.reason === 'unescaped-control-character')) {\n parser.repairs.push({ offset: end, kind: 'ambiguous', reason: 'literal-fence-in-string' });\n return { parser, json };\n }\n } catch {\n // Keep the bounded interpretation when extending it is not a complete JSON document.\n }\n return undefined;\n }\n\n private consumeSurplusClosers(parser: RecoveryParser): void {\n if (this.commentFragment || this.failures > 0) return;\n const surplusStart = parser.index;\n while (parser.index < this.end && /[}\\]]/.test(this.text[parser.index]!)) {\n parser.repairs.push({ offset: parser.index, kind: 'ambiguous', reason: 'surplus-closing-bracket' });\n parser.index++;\n parser.space();\n }\n if (parser.index > surplusStart)\n this.reportExcerpt(surplusStart, 'Surplus closing brackets and possible continuation');\n }\n\n private markFragmentProvenance(parser: RecoveryParser): void {\n const mark = (reason: string): void => {\n parser.repairs.unshift({ offset: this.index, kind: 'ambiguous', reason });\n };\n if (this.commentFragment) mark('fragment-in-comment-like-prose');\n if (this.failures > 0) mark('fragment-after-rejected-document');\n if (this.ambiguousScalarTail) mark('fragment-after-ambiguous-scalar');\n if (this.index < this.uriBoundaryEnd) mark('ambiguous-uri-boundary');\n }\n\n private reportExcerpt(offset: number, summary: string): void {\n const excerptEnd = Math.min(this.end, offset + MAX_DIAGNOSTIC_EXCERPT);\n const excerpt = JSON.stringify(this.text.slice(offset, excerptEnd));\n this.reportError(offset, `${summary} (${excerptEnd < this.end ? 'truncated excerpt' : 'excerpt'}): ${excerpt}`);\n }\n\n private reportError(offset: number, message: string): void {\n if (this.result.errors.length < MAX_ERRORS) this.result.errors.push({ offset, message });\n }\n}\n\nfunction isContainerStart(char: string | undefined): char is keyof typeof CLOSERS {\n return char === '{' || char === '[';\n}\n\nfunction scalarStart(text: string, start: number, end: number): boolean {\n const first = text[start];\n if (first === undefined) return false;\n const keyword = /^([A-Za-z]+)/.exec(text.slice(start, end))?.[1];\n return (\n first in QUOTES ||\n /[-\\d]/.test(first) ||\n (keyword !== undefined &&\n (Object.hasOwn(KEYWORDS, keyword) ||\n (Object.keys(KEYWORDS).some((word) => word.startsWith(keyword)) &&\n /^[^\\S\\r\\n]*(?:[\\r\\n]|$)/.test(text.slice(start + keyword.length, end)))))\n );\n}\n\nclass RecoveryParser {\n readonly repairs: JsonRepair[] = [];\n readonly text: string;\n readonly end: number;\n index: number;\n\n constructor(text: string, index: number, end: number) {\n this.text = text;\n this.index = index;\n this.end = end;\n }\n\n finishScalar(regionEnd: number, start: number): boolean {\n const valueEnd = this.index;\n const atom = this.text.slice(start, valueEnd);\n const bullet = atom === '-' && /\\s/.test(this.text[valueEnd] ?? '');\n const retainLiteral = this.text[start]! in QUOTES || this.hasRepair('unquoted-value');\n this.space();\n if (bullet) return false;\n if (this.index >= regionEnd) return true;\n if (/[\\r\\n]/.test(this.text.slice(valueEnd, this.index))) {\n this.repair('ambiguous', 'scalar-before-prose', valueEnd);\n return true;\n }\n if (/^\\d+[.)]$/.test(atom) && /[ \\t]/.test(this.text[valueEnd] ?? '')) return false;\n if (retainLiteral || /^[\\p{P}\\p{S}]/u.test(this.text.slice(this.index, regionEnd))) {\n this.repair('ambiguous', 'trailing-scalar-content', valueEnd);\n return true;\n }\n return false;\n }\n\n value(depth: number): string {\n if (depth > MAX_DEPTH) throw new Error('JSON nesting exceeds 128 levels');\n this.space();\n const char = this.text[this.index];\n if (this.index >= this.end || char === ',' || char === '}' || char === ']') {\n this.repair('incomplete', 'missing-value');\n return 'null';\n }\n if (isContainerStart(char)) {\n if (depth >= MAX_DEPTH) throw new Error('JSON nesting exceeds 128 levels');\n return this.container(depth, char);\n }\n if (char !== undefined && char in QUOTES) return this.string(depth > 0 ? 'value' : 'root');\n const start = this.index;\n const uriEnd = unquotedUriEnd(this.text, start, this.end);\n if (uriEnd !== undefined) this.index = uriEnd;\n else\n while (\n this.index < this.end &&\n !/[\\s,}\\]]/.test(this.text[this.index]!) &&\n !(depth === 0 && /[{[]/.test(this.text[this.index]!)) &&\n !this.comment()\n )\n this.index++;\n if (this.index === start) throw new Error('Expected a JSON value');\n const token = this.text.slice(start, this.index);\n if (/^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$/.test(token)) return token;\n if (Object.hasOwn(KEYWORDS, token)) {\n const canonical = KEYWORDS[token]!;\n if (canonical !== token) this.repair('syntax', 'python-keyword', start);\n return canonical;\n }\n this.repair(this.index >= this.end ? 'incomplete' : 'ambiguous', 'unquoted-value', start);\n return JSON.stringify(token);\n }\n\n private container(depth: number, open: keyof typeof CLOSERS): string {\n this.index++;\n const object = open === '{';\n const close = CLOSERS[open];\n const items: string[] = [];\n const keys = new Set<string>();\n let afterComma = false;\n let commaOffset = this.index;\n for (;;) {\n this.space();\n if (this.index >= this.end || this.text[this.index] === close) {\n if (this.index >= this.end) this.repair('incomplete', `missing-${close}`);\n else this.index++;\n if (afterComma) this.repair('syntax', 'trailing-comma', commaOffset);\n return open + items.join(',') + close;\n }\n if (this.text[this.index] === '}' || this.text[this.index] === ']') throw new Error('Mismatched closing bracket');\n if (this.text[this.index] === ',') {\n if (object) this.repair('ambiguous', 'extra-comma');\n else {\n items.push('null');\n this.repair('incomplete', 'missing-value');\n }\n commaOffset = this.index;\n afterComma = true;\n this.index++;\n continue;\n }\n items.push(object ? `${this.key(keys)}:${this.value(depth + 1)}` : this.value(depth + 1));\n this.space();\n afterComma = this.text[this.index] === ',';\n if (afterComma) {\n commaOffset = this.index;\n this.index++;\n } else if (this.index < this.end && this.text[this.index] !== close) this.repair('syntax', 'missing-comma');\n }\n }\n\n /** Parses an object key and the colon after it, returning the key as JSON. */\n private key(seen: Set<string>): string {\n const start = this.index;\n const key = this.text[start]! in QUOTES ? this.string() : this.unquotedKey();\n const decoded: string = JSON.parse(key);\n if (seen.has(decoded)) this.repair('ambiguous', 'duplicate-key', start);\n seen.add(decoded);\n this.space();\n if (this.text[this.index] === ':') this.index++;\n else this.repair('ambiguous', 'missing-colon');\n return key;\n }\n\n private unquotedKey(): string {\n const start = this.index;\n while (this.index < this.end && !/[\\s:,{}[\\]]/.test(this.text[this.index]!) && !this.comment()) this.index++;\n if (this.index === start) throw new Error('Expected an object key');\n const rawKey = this.text.slice(start, this.index);\n this.repair(/[\"'“”‘’]/.test(rawKey) ? 'ambiguous' : 'syntax', 'unquoted-key', start);\n return JSON.stringify(rawKey);\n }\n\n private string(context: 'root' | 'key' | 'value' = 'key'): string {\n const start = this.index;\n const open = this.text[this.index++]!;\n const close = QUOTES[open]!;\n if (open !== '\"') this.repair('syntax', 'non-json-quote', start);\n let value = '';\n let tail = '';\n const append = (text: string): void => {\n value += text;\n tail = (tail + text).slice(-2);\n };\n let embeddedQuote = false;\n const family = '\"“”'.includes(open) ? '\"“”' : \"'‘’\";\n let alternative: { end: number; value: string; repairCount: number } | undefined;\n while (this.index < this.end) {\n const char = this.text[this.index++]!;\n if (\n \"'‘’\".includes(open) &&\n \"'’\".includes(char) &&\n /[\\p{L}\\p{N}]$/u.test(tail) &&\n /^[\\p{L}\\p{N}]/u.test(this.text.slice(this.index, this.end))\n ) {\n this.repair('ambiguous', 'literal-apostrophe', this.index - 1);\n append(char);\n continue;\n }\n if (family.includes(char)) {\n const after = this.charAfterSpace(this.index);\n const delimited = after === undefined || /[,}\\]:\"']/.test(after);\n if (char !== close) {\n if (delimited) alternative ??= { end: this.index, value, repairCount: this.repairs.length };\n } else if (!embeddedQuote && context !== 'root' && alternative !== undefined && !delimited) {\n this.index = alternative.end;\n this.repairs.length = alternative.repairCount;\n this.repair('ambiguous', 'mismatched-quote', this.index - 1);\n return JSON.stringify(alternative.value);\n } else if (embeddedQuote || (context === 'value' && !delimited && this.opensEmbeddedQuote(close))) {\n embeddedQuote = !embeddedQuote;\n this.repair('ambiguous', 'unescaped-quote', this.index - 1);\n } else return JSON.stringify(value);\n }\n if (char === '\\\\') append(this.escapeSequence(open));\n else {\n if (char.codePointAt(0)! < 32) this.repair('syntax', 'unescaped-control-character', this.index - 1);\n append(char);\n }\n }\n this.repair('incomplete', 'unterminated-string', start);\n return JSON.stringify(value);\n }\n\n /** Whether the closing quote just consumed and the next one more plausibly enclose a phrase quoted within the string. */\n private opensEmbeddedQuote(close: string): boolean {\n const nextClose = this.text.indexOf(close, this.index);\n const afterNext = this.charAfterSpace(nextClose + 1);\n return (\n !/\\s/.test(this.text[this.index] ?? '') &&\n nextClose > this.index &&\n nextClose < this.end &&\n afterNext !== undefined &&\n (!/[,}:\\]]/.test(afterNext) || (afterNext === ',' && this.continuesStringAfterComma(nextClose + 1, close))) &&\n !/[\\\\*:,{}[\\]\\r\\n]|\\/\\//.test(this.text.slice(this.index, nextClose)) &&\n !this.startsKey(nextClose)\n );\n }\n\n /** Decodes the escape sequence whose backslash was just consumed. */\n private escapeSequence(open: string): string {\n const escapeOffset = this.index - 1;\n if (this.index >= this.end) {\n this.repair('incomplete', 'truncated-escape', escapeOffset);\n return '\\\\';\n }\n const escape = this.text[this.index++]!;\n if (escape === 'u') {\n const hex = this.text.slice(this.index, Math.min(this.index + 4, this.end));\n if (/^[\\da-fA-F]{4}$/.test(hex)) {\n this.index += 4;\n return String.fromCodePoint(Number.parseInt(hex, 16));\n }\n this.repair(/^[\\da-fA-F]{0,3}$/.test(hex) ? 'incomplete' : 'ambiguous', 'invalid-unicode-escape', escapeOffset);\n return String.raw`\\u`;\n }\n if (String.raw`\"\\/bfnrt`.includes(escape)) return JSON.parse(`\"\\\\${escape}\"`) as string;\n if (escape === \"'\" && open === \"'\") return \"'\";\n this.repair('ambiguous', 'invalid-escape', escapeOffset);\n return `\\\\${escape}`;\n }\n\n private continuesStringAfterComma(start: number, quote: string): boolean {\n const parser = new RecoveryParser(this.text, start - 1, this.end);\n parser.string();\n const end = parser.index - 1;\n if (\n this.text[end] !== quote ||\n parser.repairs.some(\n (repair) => !['non-json-quote', 'literal-apostrophe', 'unescaped-control-character'].includes(repair.reason)\n )\n )\n return false;\n const continuation = this.text.slice(start, end);\n const after = this.charAfterSpace(end + 1);\n return (\n /^\\s*,\\s*[\\p{L}\\p{N}]/u.test(continuation) &&\n !/[\\\\:{}[\\]]/.test(continuation) &&\n (after === undefined || /[,}\\]]/.test(after))\n );\n }\n\n private startsKey(index: number): boolean {\n const parser = new RecoveryParser(this.text, index, this.end);\n parser.string();\n parser.space();\n return this.text[parser.index] === ':';\n }\n\n space(): void {\n while (this.index < this.end) {\n if (/\\s/.test(this.text[this.index]!)) {\n if (!/[ \\t\\r\\n]/.test(this.text[this.index]!)) this.repair('syntax', 'non-json-whitespace');\n this.index++;\n } else if (this.text.startsWith('//', this.index)) {\n this.repair('syntax', 'line-comment');\n while (this.index < this.end && !/[\\r\\n]/.test(this.text[this.index]!)) this.index++;\n } else if (this.text.startsWith('/*', this.index)) {\n const end = this.text.indexOf('*/', this.index + 2);\n this.repair(end === -1 || end >= this.end ? 'incomplete' : 'syntax', 'block-comment');\n this.index = end === -1 || end >= this.end ? this.end : end + 2;\n } else break;\n }\n }\n\n hasRepair(reason: string): boolean {\n return this.repairs.some((repair) => repair.reason === reason);\n }\n\n private charAfterSpace(index: number): string | undefined {\n return this.text.slice(index, this.end).trimStart()[0];\n }\n\n private repair(kind: JsonRepair['kind'], reason: string, offset = this.index): void {\n this.repairs.push({ offset, kind, reason });\n }\n\n private comment(): boolean {\n return this.text.startsWith('//', this.index) || this.text.startsWith('/*', this.index);\n }\n}\n\nfunction unquotedUriEnd(text: string, start: number, end: number): number | undefined {\n const match = /^[A-Za-z][A-Za-z\\d+.-]*:\\/\\//.exec(text.slice(start, end));\n if (match === null) return;\n let index = start + match[0].length;\n let brackets = 0;\n while (index < end && !/[\\s}]/.test(text[index]!)) {\n if (text[index] === ',' && brackets === 0) break;\n if (text[index] === '[') brackets++;\n else if (text[index] === ']') {\n if (brackets === 0) break;\n brackets--;\n }\n index++;\n }\n return index;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shuffle.cjs","names":["shuffle","shuffleWithRandom","array","shuffleWithSeed","state","seed","character","value","index","index_","random"],"sources":["../src/shuffle.ts"],"sourcesContent":["export function shuffle<T>(array: T[]): T[] {\n return shuffleWithRandom(array, Math.random);\n}\n\n/** Shuffles an array in place, returning the same array. Equal string seeds produce equal permutations. */\nexport function shuffleWithSeed<T>(array: T[], seed: string): T[] {\n let state = 1_779_033_703 ^ seed.length;\n for (const character of seed) {\n state = Math.imul(state ^ character.codePointAt(0)!, 3_432_918_353);\n state = (state << 13) | (state >>> 19);\n }\n state = Math.imul(state ^ (state >>> 16), 2_246_822_507);\n state = Math.imul(state ^ (state >>> 13), 3_266_489_909);\n state ^= state >>> 16;\n\n return shuffleWithRandom(array, () => {\n state += 1_831_565_813;\n let value = Math.imul(state ^ (state >>> 15), state | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296;\n });\n}\n\nfunction shuffleWithRandom<T>(array: T[], random: () => number): T[] {\n for (let index = array.length - 1; index > 0; index--) {\n const index_ = Math.floor(random() * (index + 1));\n [array[index], array[index_]] = [array[index_], array[index]] as [T, T];\n }\n return array;\n}\n"],"mappings":"aAAA,SAAgBA,EAAW,EAAiB,CAC1C,OAAOC,EAAkBC,EAAO,KAAK,MAAM,CAC7C,CAGA,SAAgBC,EAAmB,EAAY,EAAmB,CAChE,IAAIC,EAAQ,WAAgBC,EAAK,OACjC,IAAK,IAAMC,KAAaD,EACtB,EAAQ,KAAK,KAAKD,EAAQE,EAAU,YAAY,CAAC,EAAI,UAAa,EAClE,EAASF,GAAS,GAAOA,IAAU,GAMrC,MAJA,GAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,EAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,GAASA,IAAU,GAEZH,EAAkBC,MAAa,CACpC,GAAS,WACT,IAAIK,EAAQ,KAAK,KAAKH,EAASA,IAAU,GAAKA,EAAQ,CAAC,EAEvD,MADA,IAASG,EAAQ,KAAK,KAAKA,EAASA,IAAU,EAAIA,EAAQ,EAAE,IACnDA,EAASA,IAAU,MAAS,GAAK,UAC5C,CAAC,CACH,CAEA,SAASN,EAAqB,EAAY,EAA2B,CACnE,IAAK,IAAIO,EAAQN,EAAM,OAAS,EAAGM,EAAQ,EAAG,IAAS,CACrD,IAAMC,EAAS,KAAK,MAAMC,EAAO,GAAKF,EAAQ,EAAE,EAChD,CAACN,EAAMM,GAAQN,EAAMO,IAAW,CAACP,EAAMO,GAASP,EAAMM,EAAM,CAC9D,CACA,OAAON,CACT"}
1
+ {"version":3,"file":"shuffle.cjs","names":["shuffle","shuffleWithRandom","array","shuffleWithSeed","state","seed","character","value","index","index_","random"],"sources":["../src/shuffle.ts"],"sourcesContent":["export function shuffle<T>(array: T[]): T[] {\n return shuffleWithRandom(array, Math.random);\n}\n\n/** Shuffles an array in place, returning the same array. Equal string seeds produce equal permutations. */\nexport function shuffleWithSeed<T>(array: T[], seed: string): T[] {\n let state = 1_779_033_703 ^ seed.length;\n for (const character of seed) {\n state = Math.imul(state ^ character.codePointAt(0)!, 3_432_918_353);\n state = (state << 13) | (state >>> 19);\n }\n state = Math.imul(state ^ (state >>> 16), 2_246_822_507);\n state = Math.imul(state ^ (state >>> 13), 3_266_489_909);\n state ^= state >>> 16;\n\n return shuffleWithRandom(array, () => {\n state += 1_831_565_813;\n let value = Math.imul(state ^ (state >>> 15), state | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296;\n });\n}\n\nfunction shuffleWithRandom<T>(array: T[], random: () => number): T[] {\n for (let index = array.length - 1; index > 0; index--) {\n const index_ = Math.floor(random() * (index + 1));\n [array[index], array[index_]] = [array[index_], array[index]] as [T, T];\n }\n return array;\n}\n"],"mappings":"A,aAAA,SAAgBA,EAAW,EAAiB,CAC1C,OAAOC,EAAkBC,EAAO,KAAK,MAAM,CAC7C,CAGA,SAAgBC,EAAmB,EAAY,EAAmB,CAChE,IAAIC,EAAQ,WAAgBC,EAAK,OACjC,IAAK,IAAMC,KAAaD,EACtB,EAAQ,KAAK,KAAKD,EAAQE,EAAU,YAAY,CAAC,EAAI,UAAa,EAClE,EAASF,GAAS,GAAOA,IAAU,GAMrC,MAJA,GAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,EAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,GAASA,IAAU,GAEZH,EAAkBC,MAAa,CACpC,GAAS,WACT,IAAIK,EAAQ,KAAK,KAAKH,EAASA,IAAU,GAAKA,EAAQ,CAAC,EAEvD,MADA,IAASG,EAAQ,KAAK,KAAKA,EAASA,IAAU,EAAIA,EAAQ,EAAE,IACnDA,EAASA,IAAU,MAAS,GAAK,UAC5C,CAAC,CACH,CAEA,SAASN,EAAqB,EAAY,EAA2B,CACnE,IAAK,IAAIO,EAAQN,EAAM,OAAS,EAAGM,EAAQ,EAAG,IAAS,CACrD,IAAMC,EAAS,KAAK,MAAMC,EAAO,GAAKF,EAAQ,EAAE,EAChD,CAACN,EAAMM,GAAQN,EAAMO,IAAW,CAACP,EAAMO,GAASP,EAAMM,EAAM,CAC9D,CACA,OAAON,CACT,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"shuffle.js","names":["shuffle","shuffleWithRandom","array","shuffleWithSeed","state","seed","character","value","index","index_","random"],"sources":["../src/shuffle.ts"],"sourcesContent":["export function shuffle<T>(array: T[]): T[] {\n return shuffleWithRandom(array, Math.random);\n}\n\n/** Shuffles an array in place, returning the same array. Equal string seeds produce equal permutations. */\nexport function shuffleWithSeed<T>(array: T[], seed: string): T[] {\n let state = 1_779_033_703 ^ seed.length;\n for (const character of seed) {\n state = Math.imul(state ^ character.codePointAt(0)!, 3_432_918_353);\n state = (state << 13) | (state >>> 19);\n }\n state = Math.imul(state ^ (state >>> 16), 2_246_822_507);\n state = Math.imul(state ^ (state >>> 13), 3_266_489_909);\n state ^= state >>> 16;\n\n return shuffleWithRandom(array, () => {\n state += 1_831_565_813;\n let value = Math.imul(state ^ (state >>> 15), state | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296;\n });\n}\n\nfunction shuffleWithRandom<T>(array: T[], random: () => number): T[] {\n for (let index = array.length - 1; index > 0; index--) {\n const index_ = Math.floor(random() * (index + 1));\n [array[index], array[index_]] = [array[index_], array[index]] as [T, T];\n }\n return array;\n}\n"],"mappings":"AAAA,SAAgBA,EAAW,EAAiB,CAC1C,OAAOC,EAAkBC,EAAO,KAAK,MAAM,CAC7C,CAGA,SAAgBC,EAAmB,EAAY,EAAmB,CAChE,IAAIC,EAAQ,WAAgBC,EAAK,OACjC,IAAK,IAAMC,KAAaD,EACtB,EAAQ,KAAK,KAAKD,EAAQE,EAAU,YAAY,CAAC,EAAI,UAAa,EAClE,EAASF,GAAS,GAAOA,IAAU,GAMrC,MAJA,GAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,EAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,GAASA,IAAU,GAEZH,EAAkBC,MAAa,CACpC,GAAS,WACT,IAAIK,EAAQ,KAAK,KAAKH,EAASA,IAAU,GAAKA,EAAQ,CAAC,EAEvD,MADA,IAASG,EAAQ,KAAK,KAAKA,EAASA,IAAU,EAAIA,EAAQ,EAAE,IACnDA,EAASA,IAAU,MAAS,GAAK,UAC5C,CAAC,CACH,CAEA,SAASN,EAAqB,EAAY,EAA2B,CACnE,IAAK,IAAIO,EAAQN,EAAM,OAAS,EAAGM,EAAQ,EAAG,IAAS,CACrD,IAAMC,EAAS,KAAK,MAAMC,EAAO,GAAKF,EAAQ,EAAE,EAChD,CAACN,EAAMM,GAAQN,EAAMO,IAAW,CAACP,EAAMO,GAASP,EAAMM,EAAM,CAC9D,CACA,OAAON,CACT"}
1
+ {"version":3,"file":"shuffle.js","names":["shuffle","shuffleWithRandom","array","shuffleWithSeed","state","seed","character","value","index","index_","random"],"sources":["../src/shuffle.ts"],"sourcesContent":["export function shuffle<T>(array: T[]): T[] {\n return shuffleWithRandom(array, Math.random);\n}\n\n/** Shuffles an array in place, returning the same array. Equal string seeds produce equal permutations. */\nexport function shuffleWithSeed<T>(array: T[], seed: string): T[] {\n let state = 1_779_033_703 ^ seed.length;\n for (const character of seed) {\n state = Math.imul(state ^ character.codePointAt(0)!, 3_432_918_353);\n state = (state << 13) | (state >>> 19);\n }\n state = Math.imul(state ^ (state >>> 16), 2_246_822_507);\n state = Math.imul(state ^ (state >>> 13), 3_266_489_909);\n state ^= state >>> 16;\n\n return shuffleWithRandom(array, () => {\n state += 1_831_565_813;\n let value = Math.imul(state ^ (state >>> 15), state | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296;\n });\n}\n\nfunction shuffleWithRandom<T>(array: T[], random: () => number): T[] {\n for (let index = array.length - 1; index > 0; index--) {\n const index_ = Math.floor(random() * (index + 1));\n [array[index], array[index_]] = [array[index_], array[index]] as [T, T];\n }\n return array;\n}\n"],"mappings":"AAAA,SAAgBA,EAAW,EAAiB,CAC1C,OAAOC,EAAkBC,EAAO,KAAK,MAAM,CAC7C,CAGA,SAAgBC,EAAmB,EAAY,EAAmB,CAChE,IAAIC,EAAQ,WAAgBC,EAAK,OACjC,IAAK,IAAMC,KAAaD,EACtB,EAAQ,KAAK,KAAKD,EAAQE,EAAU,YAAY,CAAC,EAAI,UAAa,EAClE,EAASF,GAAS,GAAOA,IAAU,GAMrC,MAJA,GAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,EAAQ,KAAK,KAAKA,EAASA,IAAU,GAAK,UAAa,EACvD,GAASA,IAAU,GAEZH,EAAkBC,MAAa,CACpC,GAAS,WACT,IAAIK,EAAQ,KAAK,KAAKH,EAASA,IAAU,GAAKA,EAAQ,CAAC,EAEvD,MADA,IAASG,EAAQ,KAAK,KAAKA,EAASA,IAAU,EAAIA,EAAQ,EAAE,IACnDA,EAASA,IAAU,MAAS,GAAK,UAC5C,CAAC,CACH,CAEA,SAASN,EAAqB,EAAY,EAA2B,CACnE,IAAK,IAAIO,EAAQN,EAAM,OAAS,EAAGM,EAAQ,EAAG,IAAS,CACrD,IAAMC,EAAS,KAAK,MAAMC,EAAO,GAAKF,EAAQ,EAAE,EAChD,CAACN,EAAMM,GAAQN,EAAMO,IAAW,CAACP,EAAMO,GAASP,EAAMM,EAAM,CAC9D,CACA,OAAON,CACT,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.cjs","names":["sleep","resolve","milliseconds"],"sources":["../src/sleep.ts"],"sourcesContent":["export async function sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"aAAA,eAAsBA,EAAM,EAAqC,CAC/D,OAAO,IAAI,QAAS,GAAY,WAAWC,EAASC,CAAY,CAAC,CACnE"}
1
+ {"version":3,"file":"sleep.cjs","names":["sleep","resolve","milliseconds"],"sources":["../src/sleep.ts"],"sourcesContent":["export async function sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"A,aAAA,eAAsBA,EAAM,EAAqC,CAC/D,OAAO,IAAI,QAAS,GAAY,WAAWC,EAASC,CAAY,CAAC,CACnE,C"}
package/dist/sleep.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.js","names":["sleep","resolve","milliseconds"],"sources":["../src/sleep.ts"],"sourcesContent":["export async function sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"AAAA,eAAsBA,EAAM,EAAqC,CAC/D,OAAO,IAAI,QAAS,GAAY,WAAWC,EAASC,CAAY,CAAC,CACnE"}
1
+ {"version":3,"file":"sleep.js","names":["sleep","resolve","milliseconds"],"sources":["../src/sleep.ts"],"sourcesContent":["export async function sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"AAAA,eAAsBA,EAAM,EAAqC,CAC/D,OAAO,IAAI,QAAS,GAAY,WAAWC,EAASC,CAAY,CAAC,CACnE,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.cjs","names":["getConnectionLevelSqlitePragmas","getPersistentSqlitePragmas","escapeLikePattern","text"],"sources":["../src/sqlite.ts"],"sourcesContent":["export function getConnectionLevelSqlitePragmas(): string {\n // cf. https://github.com/benbjohnson/litestream/issues/724#issue-3367254318\n // cf. https://github.com/benbjohnson/litestream/blob/9bd72348b7e7b1b3b8cf1e8befbee3b2c17444f7/.claude/agents/performance-optimizer.md?plain=1#L225-L232\n return 'PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; PRAGMA cache_size = -16000; PRAGMA synchronous = NORMAL; PRAGMA wal_autocheckpoint = 10000;';\n}\n\nexport function getPersistentSqlitePragmas(): string {\n return 'PRAGMA journal_mode = WAL;';\n}\n\n/**\n * Escapes `%`, `_`, and `\\` so that `LIKE` does not read them as wildcards or escapes. SQLite's `LIKE` still matches\n * ASCII letters case-insensitively unless `PRAGMA case_sensitive_like = ON`; use `=` when case must match exactly.\n * The query must declare the escape character, e.g. `WHERE name LIKE ? ESCAPE '\\'` in SQL, which is `ESCAPE '\\\\'`\n * inside a JavaScript string literal.\n */\nexport function escapeLikePattern(text: string): string {\n return text.replaceAll(/[%\\\\_]/gu, String.raw`\\$&`);\n}\n"],"mappings":"aAAA,SAAgBA,GAA0C,CAGxD,MAAO,mJACT,CAEA,SAAgBC,GAAqC,CACnD,MAAO,4BACT,CAQA,SAAgBC,EAAkB,EAAsB,CACtD,OAAOC,EAAK,WAAW,WAAY,OAAO,GAAG,KAAK,CACpD"}
1
+ {"version":3,"file":"sqlite.cjs","names":["getConnectionLevelSqlitePragmas","getPersistentSqlitePragmas","escapeLikePattern","text"],"sources":["../src/sqlite.ts"],"sourcesContent":["export function getConnectionLevelSqlitePragmas(): string {\n // cf. https://github.com/benbjohnson/litestream/issues/724#issue-3367254318\n // cf. https://github.com/benbjohnson/litestream/blob/9bd72348b7e7b1b3b8cf1e8befbee3b2c17444f7/.claude/agents/performance-optimizer.md?plain=1#L225-L232\n return 'PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; PRAGMA cache_size = -16000; PRAGMA synchronous = NORMAL; PRAGMA wal_autocheckpoint = 10000;';\n}\n\nexport function getPersistentSqlitePragmas(): string {\n return 'PRAGMA journal_mode = WAL;';\n}\n\n/**\n * Escapes `%`, `_`, and `\\` so that `LIKE` does not read them as wildcards or escapes. SQLite's `LIKE` still matches\n * ASCII letters case-insensitively unless `PRAGMA case_sensitive_like = ON`; use `=` when case must match exactly.\n * The query must declare the escape character, e.g. `WHERE name LIKE ? ESCAPE '\\'` in SQL, which is `ESCAPE '\\\\'`\n * inside a JavaScript string literal.\n */\nexport function escapeLikePattern(text: string): string {\n return text.replaceAll(/[%\\\\_]/gu, String.raw`\\$&`);\n}\n"],"mappings":"A,aAAA,SAAgBA,GAA0C,CAGxD,MAAO,mJACT,CAEA,SAAgBC,GAAqC,CACnD,MAAO,4BACT,CAQA,SAAgBC,EAAkB,EAAsB,CACtD,OAAOC,EAAK,WAAW,WAAY,OAAO,GAAG,KAAK,CACpD,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","names":["getConnectionLevelSqlitePragmas","getPersistentSqlitePragmas","escapeLikePattern","text"],"sources":["../src/sqlite.ts"],"sourcesContent":["export function getConnectionLevelSqlitePragmas(): string {\n // cf. https://github.com/benbjohnson/litestream/issues/724#issue-3367254318\n // cf. https://github.com/benbjohnson/litestream/blob/9bd72348b7e7b1b3b8cf1e8befbee3b2c17444f7/.claude/agents/performance-optimizer.md?plain=1#L225-L232\n return 'PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; PRAGMA cache_size = -16000; PRAGMA synchronous = NORMAL; PRAGMA wal_autocheckpoint = 10000;';\n}\n\nexport function getPersistentSqlitePragmas(): string {\n return 'PRAGMA journal_mode = WAL;';\n}\n\n/**\n * Escapes `%`, `_`, and `\\` so that `LIKE` does not read them as wildcards or escapes. SQLite's `LIKE` still matches\n * ASCII letters case-insensitively unless `PRAGMA case_sensitive_like = ON`; use `=` when case must match exactly.\n * The query must declare the escape character, e.g. `WHERE name LIKE ? ESCAPE '\\'` in SQL, which is `ESCAPE '\\\\'`\n * inside a JavaScript string literal.\n */\nexport function escapeLikePattern(text: string): string {\n return text.replaceAll(/[%\\\\_]/gu, String.raw`\\$&`);\n}\n"],"mappings":"AAAA,SAAgBA,GAA0C,CAGxD,MAAO,mJACT,CAEA,SAAgBC,GAAqC,CACnD,MAAO,4BACT,CAQA,SAAgBC,EAAkB,EAAsB,CACtD,OAAOC,EAAK,WAAW,WAAY,OAAO,GAAG,KAAK,CACpD"}
1
+ {"version":3,"file":"sqlite.js","names":["getConnectionLevelSqlitePragmas","getPersistentSqlitePragmas","escapeLikePattern","text"],"sources":["../src/sqlite.ts"],"sourcesContent":["export function getConnectionLevelSqlitePragmas(): string {\n // cf. https://github.com/benbjohnson/litestream/issues/724#issue-3367254318\n // cf. https://github.com/benbjohnson/litestream/blob/9bd72348b7e7b1b3b8cf1e8befbee3b2c17444f7/.claude/agents/performance-optimizer.md?plain=1#L225-L232\n return 'PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; PRAGMA cache_size = -16000; PRAGMA synchronous = NORMAL; PRAGMA wal_autocheckpoint = 10000;';\n}\n\nexport function getPersistentSqlitePragmas(): string {\n return 'PRAGMA journal_mode = WAL;';\n}\n\n/**\n * Escapes `%`, `_`, and `\\` so that `LIKE` does not read them as wildcards or escapes. SQLite's `LIKE` still matches\n * ASCII letters case-insensitively unless `PRAGMA case_sensitive_like = ON`; use `=` when case must match exactly.\n * The query must declare the escape character, e.g. `WHERE name LIKE ? ESCAPE '\\'` in SQL, which is `ESCAPE '\\\\'`\n * inside a JavaScript string literal.\n */\nexport function escapeLikePattern(text: string): string {\n return text.replaceAll(/[%\\\\_]/gu, String.raw`\\$&`);\n}\n"],"mappings":"AAAA,SAAgBA,GAA0C,CAGxD,MAAO,mJACT,CAEA,SAAgBC,GAAqC,CACnD,MAAO,4BACT,CAQA,SAAgBC,EAAkB,EAAsB,CACtD,OAAOC,EAAK,WAAW,WAAY,OAAO,GAAG,KAAK,CACpD,C"}
package/dist/text.cjs CHANGED
@@ -1,4 +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`'\''`)}'`}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(`
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 a(`~`,e,t)}function r(e,t=``){return a("`",e,t)}function i(e){return o(`~`,e)}function a(e,t,n){let r=0;for(;n[r]===e;)r++;let i=o(e,t)+e.repeat(r);return`${i}${n.slice(r)}\n${t}${t.endsWith(`
2
2
  `)?``:`
3
- `}${s}`}function a(e,t,n=`…`){if(e.length<=t)return e;if(t<n.length)throw RangeError(`maxLength (${t}) must be at least the ellipsis length (${n.length})`);return`${o(e,t-n.length)}${n}`}function o(e,t){let n=e.charCodeAt(t-1);return e.slice(0,n>=55296&&n<=56319?t-1:t)}const s={'"':`&quot;`,"&":`&amp;`,"'":`&#39;`,"<":`&lt;`,">":`&gt;`},c=/["&'<>]/gu;function l(e){return e.search(c)===-1?e:e.replaceAll(c,e=>s[e])}exports.escapeHtml=l,exports.escapeRegExp=e,exports.quoteForShell=t,exports.sliceWithoutSplittingSurrogates=o,exports.toCodeBlock=r,exports.toTildeCodeBlock=n,exports.truncate=a;
3
+ `}${i}`}function o(e,t){let n=e.repeat(3),r=2,i=t.indexOf(n);for(;i!==-1;){let a=i+3;for(;t[a]===e;)a++;r=Math.max(r,a-i),i=t.indexOf(n,a)}return e.repeat(r+1)}function s(e,t,n=`…`){if(e.length<=t)return e;if(t<n.length)throw RangeError(`maxLength (${t}) must be at least the ellipsis length (${n.length})`);return`${c(e,t-n.length)}${n}`}function c(e,t){let n=e.charCodeAt(t-1);return e.slice(0,n>=55296&&n<=56319?t-1:t)}const l={'"':`&quot;`,"&":`&amp;`,"'":`&#39;`,"<":`&lt;`,">":`&gt;`},u=/["&'<>]/gu;function d(e){return e.search(u)===-1?e:e.replaceAll(u,e=>l[e])}exports.escapeHtml=d,exports.escapeRegExp=e,exports.getTildeFence=i,exports.quoteForShell=t,exports.sliceWithoutSplittingSurrogates=c,exports.toCodeBlock=r,exports.toTildeCodeBlock=n,exports.truncate=s;
4
4
  //# sourceMappingURL=text.cjs.map
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 >= 0xd8_00 && lastKeptUnit <= 0xdb_ff ? end - 1 : end);\n}\n\nconst htmlEntities: Readonly<Record<string, string>> = {\n '\"': '&quot;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\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"}
1
+ {"version":3,"file":"text.cjs","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","getTildeFence","getFence","languageStart","fenceChar","fence","shortestFence","longestRun","start","end","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\n/** Returns the tilde fence `toTildeCodeBlock` wraps `text` in: at least `~~~`, longer than any run of tildes in it. */\nexport function getTildeFence(text: string): string {\n return getFence('~', text);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = getFence(fenceChar, text) + fenceChar.repeat(languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\nfunction getFence(fenceChar: string, text: 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 return fenceChar.repeat(longestRun + 1);\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 '\"': '&quot;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\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":"A,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,CAGA,SAAgBE,EAAc,EAAsB,CAClD,OAAOC,EAAS,IAAKR,CAAI,CAC3B,CAEA,SAASI,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAIK,EAAgB,EACpB,KAAOJ,EAASI,KAAmBC,GAAW,IAC9C,IAAMC,EAAQH,EAASE,EAAWV,CAAI,EAAIU,EAAU,OAAOD,CAAa,EACxE,MAAO,GAAGE,IAAQN,EAAS,MAAMI,CAAa,EAAE,IAAIT,IAAOA,EAAK,SAAS;CAAI,EAAI,GAAK;IAAOW,GAC/F,CAEA,SAASH,EAAS,EAAmB,EAAsB,CACzD,IAAMI,EAAgBF,EAAU,OAAO,CAAC,EACpCG,EAAa,EACbC,EAAQd,EAAK,QAAQY,CAAa,EACtC,KAAOE,IAAU,IAAI,CACnB,IAAIC,EAAMD,EAAQ,EAClB,KAAOd,EAAKe,KAASL,GAAW,IAChC,EAAa,KAAK,IAAIG,EAAYE,EAAMD,CAAK,EAC7C,EAAQd,EAAK,QAAQY,EAAeG,CAAG,CACzC,CACA,OAAOL,EAAU,OAAOG,EAAa,CAAC,CACxC,CAMA,SAAgBG,EAAS,EAAc,EAAmB,EAAW,IAAa,CAChF,GAAIhB,EAAK,QAAUiB,EAAW,OAAOjB,EACrC,GAAIiB,EAAYC,EAAS,OACvB,MAAU,WAAW,cAAcD,EAAU,0CAA0CC,EAAS,OAAO,EAAE,EAE3G,MAAO,GAAGC,EAAgCnB,EAAMiB,EAAYC,EAAS,MAAM,IAAIA,GACjF,CAMA,SAAgBC,EAAgC,EAAc,EAAqB,CAEjF,IAAMC,EAAepB,EAAK,WAAWe,EAAM,CAAC,EAC5C,OAAOf,EAAK,MAAM,EAAGoB,GAAgB,OAAWA,GAAgB,MAAUL,EAAM,EAAIA,CAAG,CACzF,CAEA,MAAMM,EAAiD,CACrD,IAAK,SACL,IAAK,QACL,IAAK,QACL,IAAK,OACL,IAAK,MACP,EAEMC,EAA+B,YAGrC,SAAgBC,EAAW,EAAsB,CAG/C,OAAOvB,EAAK,OAAOsB,CAA4B,IAAM,GACjDtB,EACAA,EAAK,WAAWsB,EAA+B,GAAcD,EAAaG,EAAoB,CACpG,C"}
package/dist/text.d.ts CHANGED
@@ -12,6 +12,8 @@ export declare function toTildeCodeBlock(text: string, language?: string): strin
12
12
  * The fence is longer than any run of backticks in the text so that the text cannot close it.
13
13
  */
14
14
  export declare function toCodeBlock(text: string, language?: string): string;
15
+ /** Returns the tilde fence `toTildeCodeBlock` wraps `text` in: at least `~~~`, longer than any run of tildes in it. */
16
+ export declare function getTildeFence(text: string): string;
15
17
  /**
16
18
  * Shortens text to at most `maxLength` UTF-16 code units, including the `ellipsis` that marks the cut.
17
19
  * Throws a `RangeError` when `maxLength` cannot hold the ellipsis.
package/dist/text.js CHANGED
@@ -1,4 +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`'\''`)}'`}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(`
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 a(`~`,e,t)}function r(e,t=``){return a("`",e,t)}function i(e){return o(`~`,e)}function a(e,t,n){let r=0;for(;n[r]===e;)r++;let i=o(e,t)+e.repeat(r);return`${i}${n.slice(r)}\n${t}${t.endsWith(`
2
2
  `)?``:`
3
- `}${s}`}function a(e,t,n=`…`){if(e.length<=t)return e;if(t<n.length)throw RangeError(`maxLength (${t}) must be at least the ellipsis length (${n.length})`);return`${o(e,t-n.length)}${n}`}function o(e,t){let n=e.charCodeAt(t-1);return e.slice(0,n>=55296&&n<=56319?t-1:t)}const s={'"':`&quot;`,"&":`&amp;`,"'":`&#39;`,"<":`&lt;`,">":`&gt;`},c=/["&'<>]/gu;function l(e){return e.search(c)===-1?e:e.replaceAll(c,e=>s[e])}export{l as escapeHtml,e as escapeRegExp,t as quoteForShell,o as sliceWithoutSplittingSurrogates,r as toCodeBlock,n as toTildeCodeBlock,a as truncate};
3
+ `}${i}`}function o(e,t){let n=e.repeat(3),r=2,i=t.indexOf(n);for(;i!==-1;){let a=i+3;for(;t[a]===e;)a++;r=Math.max(r,a-i),i=t.indexOf(n,a)}return e.repeat(r+1)}function s(e,t,n=`…`){if(e.length<=t)return e;if(t<n.length)throw RangeError(`maxLength (${t}) must be at least the ellipsis length (${n.length})`);return`${c(e,t-n.length)}${n}`}function c(e,t){let n=e.charCodeAt(t-1);return e.slice(0,n>=55296&&n<=56319?t-1:t)}const l={'"':`&quot;`,"&":`&amp;`,"'":`&#39;`,"<":`&lt;`,">":`&gt;`},u=/["&'<>]/gu;function d(e){return e.search(u)===-1?e:e.replaceAll(u,e=>l[e])}export{d as escapeHtml,e as escapeRegExp,i as getTildeFence,t as quoteForShell,c as sliceWithoutSplittingSurrogates,r as toCodeBlock,n as toTildeCodeBlock,s as truncate};
4
4
  //# sourceMappingURL=text.js.map
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 >= 0xd8_00 && lastKeptUnit <= 0xdb_ff ? end - 1 : end);\n}\n\nconst htmlEntities: Readonly<Record<string, string>> = {\n '\"': '&quot;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\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"}
1
+ {"version":3,"file":"text.js","names":["escapeRegExp","text","quoteForShell","value","toTildeCodeBlock","toFencedCodeBlock","language","toCodeBlock","getTildeFence","getFence","languageStart","fenceChar","fence","shortestFence","longestRun","start","end","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\n/** Returns the tilde fence `toTildeCodeBlock` wraps `text` in: at least `~~~`, longer than any run of tildes in it. */\nexport function getTildeFence(text: string): string {\n return getFence('~', text);\n}\n\nfunction toFencedCodeBlock(fenceChar: string, text: string, language: string): string {\n let languageStart = 0;\n while (language[languageStart] === fenceChar) languageStart++;\n const fence = getFence(fenceChar, text) + fenceChar.repeat(languageStart);\n return `${fence}${language.slice(languageStart)}\\n${text}${text.endsWith('\\n') ? '' : '\\n'}${fence}`;\n}\n\nfunction getFence(fenceChar: string, text: 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 return fenceChar.repeat(longestRun + 1);\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 '\"': '&quot;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\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,CAGA,SAAgBE,EAAc,EAAsB,CAClD,OAAOC,EAAS,IAAKR,CAAI,CAC3B,CAEA,SAASI,EAAkB,EAAmB,EAAc,EAA0B,CACpF,IAAIK,EAAgB,EACpB,KAAOJ,EAASI,KAAmBC,GAAW,IAC9C,IAAMC,EAAQH,EAASE,EAAWV,CAAI,EAAIU,EAAU,OAAOD,CAAa,EACxE,MAAO,GAAGE,IAAQN,EAAS,MAAMI,CAAa,EAAE,IAAIT,IAAOA,EAAK,SAAS;CAAI,EAAI,GAAK;IAAOW,GAC/F,CAEA,SAASH,EAAS,EAAmB,EAAsB,CACzD,IAAMI,EAAgBF,EAAU,OAAO,CAAC,EACpCG,EAAa,EACbC,EAAQd,EAAK,QAAQY,CAAa,EACtC,KAAOE,IAAU,IAAI,CACnB,IAAIC,EAAMD,EAAQ,EAClB,KAAOd,EAAKe,KAASL,GAAW,IAChC,EAAa,KAAK,IAAIG,EAAYE,EAAMD,CAAK,EAC7C,EAAQd,EAAK,QAAQY,EAAeG,CAAG,CACzC,CACA,OAAOL,EAAU,OAAOG,EAAa,CAAC,CACxC,CAMA,SAAgBG,EAAS,EAAc,EAAmB,EAAW,IAAa,CAChF,GAAIhB,EAAK,QAAUiB,EAAW,OAAOjB,EACrC,GAAIiB,EAAYC,EAAS,OACvB,MAAU,WAAW,cAAcD,EAAU,0CAA0CC,EAAS,OAAO,EAAE,EAE3G,MAAO,GAAGC,EAAgCnB,EAAMiB,EAAYC,EAAS,MAAM,IAAIA,GACjF,CAMA,SAAgBC,EAAgC,EAAc,EAAqB,CAEjF,IAAMC,EAAepB,EAAK,WAAWe,EAAM,CAAC,EAC5C,OAAOf,EAAK,MAAM,EAAGoB,GAAgB,OAAWA,GAAgB,MAAUL,EAAM,EAAIA,CAAG,CACzF,CAEA,MAAMM,EAAiD,CACrD,IAAK,SACL,IAAK,QACL,IAAK,QACL,IAAK,OACL,IAAK,MACP,EAEMC,EAA+B,YAGrC,SAAgBC,EAAW,EAAsB,CAG/C,OAAOvB,EAAK,OAAOsB,CAA4B,IAAM,GACjDtB,EACAA,EAAK,WAAWsB,EAA+B,GAAcD,EAAaG,EAAoB,CACpG,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"throttledFetch.cjs","names":["sleep","require_sleep","createThrottledFetch","nextRequestAt","admission","previousAdmission","ready","intervalMilliseconds","response","input","init","waitMilliseconds","parseRetryAfterMilliseconds","rateLimitFallbackMilliseconds","IMF_FIXDATE","trimmed","value","milliseconds"],"sources":["../src/throttledFetch.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n// `setTimeout` fires after 1 ms for longer delays, which would turn a long `Retry-After` hold into a busy loop.\nconst MAX_TIMER_MILLISECONDS = 2_147_483_647;\n\nexport interface ThrottledFetchOptions {\n /** The minimum interval between the starts of consecutive requests. */\n intervalMilliseconds: number;\n /** How long to hold later requests after a 429 response without `Retry-After` in delay seconds or IMF-fixdate. */\n rateLimitFallbackMilliseconds: number;\n}\n\n/**\n * Creates a `fetch` for a rate-limited API that starts requests in call order, at least `intervalMilliseconds` apart.\n * A 429 response holds every later request until its `Retry-After` (delay seconds or an IMF-fixdate) elapses, including\n * requests already waiting; the 429 response itself is returned to the caller, not retried. An abort signal takes effect\n * only once its request starts, not while it waits in the queue.\n */\nexport function createThrottledFetch({\n intervalMilliseconds,\n rateLimitFallbackMilliseconds,\n}: ThrottledFetchOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response> {\n let nextRequestAt = 0;\n let admission = Promise.resolve();\n\n return async (input, init) => {\n const previousAdmission = admission;\n const ready = (async () => {\n await previousAdmission;\n // A response to an earlier request can push `nextRequestAt` back while this request sleeps.\n while (nextRequestAt > performance.now()) {\n await sleep(Math.min(nextRequestAt - performance.now(), MAX_TIMER_MILLISECONDS));\n }\n nextRequestAt = performance.now() + intervalMilliseconds;\n })();\n admission = ready;\n await ready;\n\n const response = await fetch(input, init);\n if (response.status === 429) {\n const waitMilliseconds = parseRetryAfterMilliseconds(response.headers.get('retry-after'));\n nextRequestAt = Math.max(nextRequestAt, performance.now() + (waitMilliseconds ?? rateLimitFallbackMilliseconds));\n }\n return response;\n };\n}\n\n// RFC 9110 requires senders to generate HTTP dates in this IMF-fixdate form, e.g. `Sun, 06 Nov 1994 08:49:37 GMT`.\n// `Date.parse` alone would also accept malformed values such as `1.5`.\nconst IMF_FIXDATE = /^[A-Z][a-z]{2}, \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$/u;\n\n/** Parses `Retry-After` in delay seconds or IMF-fixdate; the obsolete RFC 850 and asctime dates yield `undefined`. */\nfunction parseRetryAfterMilliseconds(value: string | null): number | undefined {\n const trimmed = value?.trim() ?? '';\n const milliseconds = /^\\d+$/u.test(trimmed)\n ? Number(trimmed) * 1000\n : IMF_FIXDATE.test(trimmed)\n ? Date.parse(trimmed) - Date.now()\n : Number.NaN;\n return Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : undefined;\n}\n"],"mappings":"4CAkBA,SAAgBE,EAAqB,CACnC,uBACA,iCACkG,CAClG,IAAIC,EAAgB,EAChBC,EAAY,QAAQ,QAAQ,EAEhC,OAAO,MAAO,EAAO,IAAS,CAC5B,IAAMC,EAAoBD,EACpBE,GAAS,SAAY,CAGzB,IAFA,MAAMD,EAECF,EAAgB,YAAY,IAAI,GACrC,MAAMH,EAAAA,MAAM,KAAK,IAAIG,EAAgB,YAAY,IAAI,EAAG,UAAsB,CAAC,EAEjF,EAAgB,YAAY,IAAI,EAAII,CACtC,EAAA,CAAG,EACH,EAAYD,EACZ,MAAMA,EAEN,IAAME,EAAW,MAAM,MAAMC,EAAOC,CAAI,EACxC,GAAIF,EAAS,SAAW,IAAK,CAC3B,IAAMG,EAAmBC,EAA4BJ,EAAS,QAAQ,IAAI,aAAa,CAAC,EACxF,EAAgB,KAAK,IAAIL,EAAe,YAAY,IAAI,GAAKQ,GAAoBE,EAA8B,CACjH,CACA,OAAOL,CACT,CACF,CAIA,MAAMM,EAAc,oEAGpB,SAASF,EAA4B,EAA0C,CAC7E,IAAMG,EAAUC,GAAO,KAAK,GAAK,GAC3BC,EAAe,SAAS,KAAKF,CAAO,EACtC,OAAOA,CAAO,EAAI,IAClBD,EAAY,KAAKC,CAAO,EACtB,KAAK,MAAMA,CAAO,EAAI,KAAK,IAAI,EAC/B,IACN,OAAO,OAAO,SAASE,CAAY,EAAI,KAAK,IAAI,EAAGA,CAAY,EAAI,IAAA,EACrE"}
1
+ {"version":3,"file":"throttledFetch.cjs","names":["sleep","require_sleep","createThrottledFetch","nextRequestAt","admission","previousAdmission","ready","intervalMilliseconds","response","input","init","waitMilliseconds","parseRetryAfterMilliseconds","rateLimitFallbackMilliseconds","IMF_FIXDATE","trimmed","value","milliseconds"],"sources":["../src/throttledFetch.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n// `setTimeout` fires after 1 ms for longer delays, which would turn a long `Retry-After` hold into a busy loop.\nconst MAX_TIMER_MILLISECONDS = 2_147_483_647;\n\nexport interface ThrottledFetchOptions {\n /** The minimum interval between the starts of consecutive requests. */\n intervalMilliseconds: number;\n /** How long to hold later requests after a 429 response without `Retry-After` in delay seconds or IMF-fixdate. */\n rateLimitFallbackMilliseconds: number;\n}\n\n/**\n * Creates a `fetch` for a rate-limited API that starts requests in call order, at least `intervalMilliseconds` apart.\n * A 429 response holds every later request until its `Retry-After` (delay seconds or an IMF-fixdate) elapses, including\n * requests already waiting; the 429 response itself is returned to the caller, not retried. An abort signal takes effect\n * only once its request starts, not while it waits in the queue.\n */\nexport function createThrottledFetch({\n intervalMilliseconds,\n rateLimitFallbackMilliseconds,\n}: ThrottledFetchOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response> {\n let nextRequestAt = 0;\n let admission = Promise.resolve();\n\n return async (input, init) => {\n const previousAdmission = admission;\n const ready = (async () => {\n await previousAdmission;\n // A response to an earlier request can push `nextRequestAt` back while this request sleeps.\n while (nextRequestAt > performance.now()) {\n await sleep(Math.min(nextRequestAt - performance.now(), MAX_TIMER_MILLISECONDS));\n }\n nextRequestAt = performance.now() + intervalMilliseconds;\n })();\n admission = ready;\n await ready;\n\n const response = await fetch(input, init);\n if (response.status === 429) {\n const waitMilliseconds = parseRetryAfterMilliseconds(response.headers.get('retry-after'));\n nextRequestAt = Math.max(nextRequestAt, performance.now() + (waitMilliseconds ?? rateLimitFallbackMilliseconds));\n }\n return response;\n };\n}\n\n// RFC 9110 requires senders to generate HTTP dates in this IMF-fixdate form, e.g. `Sun, 06 Nov 1994 08:49:37 GMT`.\n// `Date.parse` alone would also accept malformed values such as `1.5`.\nconst IMF_FIXDATE = /^[A-Z][a-z]{2}, \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$/u;\n\n/** Parses `Retry-After` in delay seconds or IMF-fixdate; the obsolete RFC 850 and asctime dates yield `undefined`. */\nfunction parseRetryAfterMilliseconds(value: string | null): number | undefined {\n const trimmed = value?.trim() ?? '';\n const milliseconds = /^\\d+$/u.test(trimmed)\n ? Number(trimmed) * 1000\n : IMF_FIXDATE.test(trimmed)\n ? Date.parse(trimmed) - Date.now()\n : Number.NaN;\n return Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : undefined;\n}\n"],"mappings":"A,4CAkBA,SAAgBE,EAAqB,CACnC,uBACA,iCACkG,CAClG,IAAIC,EAAgB,EAChBC,EAAY,QAAQ,QAAQ,EAEhC,OAAO,MAAO,EAAO,IAAS,CAC5B,IAAMC,EAAoBD,EACpBE,GAAS,SAAY,CAGzB,IAFA,MAAMD,EAECF,EAAgB,YAAY,IAAI,GACrC,MAAMH,EAAAA,MAAM,KAAK,IAAIG,EAAgB,YAAY,IAAI,EAAG,UAAsB,CAAC,EAEjF,EAAgB,YAAY,IAAI,EAAII,CACtC,EAAA,CAAG,EACH,EAAYD,EACZ,MAAMA,EAEN,IAAME,EAAW,MAAM,MAAMC,EAAOC,CAAI,EACxC,GAAIF,EAAS,SAAW,IAAK,CAC3B,IAAMG,EAAmBC,EAA4BJ,EAAS,QAAQ,IAAI,aAAa,CAAC,EACxF,EAAgB,KAAK,IAAIL,EAAe,YAAY,IAAI,GAAKQ,GAAoBE,EAA8B,CACjH,CACA,OAAOL,CACT,CACF,CAIA,MAAMM,EAAc,oEAGpB,SAASF,EAA4B,EAA0C,CAC7E,IAAMG,EAAUC,GAAO,KAAK,GAAK,GAC3BC,EAAe,SAAS,KAAKF,CAAO,EACtC,OAAOA,CAAO,EAAI,IAClBD,EAAY,KAAKC,CAAO,EACtB,KAAK,MAAMA,CAAO,EAAI,KAAK,IAAI,EAC/B,IACN,OAAO,OAAO,SAASE,CAAY,EAAI,KAAK,IAAI,EAAGA,CAAY,EAAI,IAAA,EACrE,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"throttledFetch.js","names":["createThrottledFetch","nextRequestAt","admission","previousAdmission","ready","sleep","intervalMilliseconds","response","input","init","waitMilliseconds","parseRetryAfterMilliseconds","rateLimitFallbackMilliseconds","IMF_FIXDATE","trimmed","value","milliseconds"],"sources":["../src/throttledFetch.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n// `setTimeout` fires after 1 ms for longer delays, which would turn a long `Retry-After` hold into a busy loop.\nconst MAX_TIMER_MILLISECONDS = 2_147_483_647;\n\nexport interface ThrottledFetchOptions {\n /** The minimum interval between the starts of consecutive requests. */\n intervalMilliseconds: number;\n /** How long to hold later requests after a 429 response without `Retry-After` in delay seconds or IMF-fixdate. */\n rateLimitFallbackMilliseconds: number;\n}\n\n/**\n * Creates a `fetch` for a rate-limited API that starts requests in call order, at least `intervalMilliseconds` apart.\n * A 429 response holds every later request until its `Retry-After` (delay seconds or an IMF-fixdate) elapses, including\n * requests already waiting; the 429 response itself is returned to the caller, not retried. An abort signal takes effect\n * only once its request starts, not while it waits in the queue.\n */\nexport function createThrottledFetch({\n intervalMilliseconds,\n rateLimitFallbackMilliseconds,\n}: ThrottledFetchOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response> {\n let nextRequestAt = 0;\n let admission = Promise.resolve();\n\n return async (input, init) => {\n const previousAdmission = admission;\n const ready = (async () => {\n await previousAdmission;\n // A response to an earlier request can push `nextRequestAt` back while this request sleeps.\n while (nextRequestAt > performance.now()) {\n await sleep(Math.min(nextRequestAt - performance.now(), MAX_TIMER_MILLISECONDS));\n }\n nextRequestAt = performance.now() + intervalMilliseconds;\n })();\n admission = ready;\n await ready;\n\n const response = await fetch(input, init);\n if (response.status === 429) {\n const waitMilliseconds = parseRetryAfterMilliseconds(response.headers.get('retry-after'));\n nextRequestAt = Math.max(nextRequestAt, performance.now() + (waitMilliseconds ?? rateLimitFallbackMilliseconds));\n }\n return response;\n };\n}\n\n// RFC 9110 requires senders to generate HTTP dates in this IMF-fixdate form, e.g. `Sun, 06 Nov 1994 08:49:37 GMT`.\n// `Date.parse` alone would also accept malformed values such as `1.5`.\nconst IMF_FIXDATE = /^[A-Z][a-z]{2}, \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$/u;\n\n/** Parses `Retry-After` in delay seconds or IMF-fixdate; the obsolete RFC 850 and asctime dates yield `undefined`. */\nfunction parseRetryAfterMilliseconds(value: string | null): number | undefined {\n const trimmed = value?.trim() ?? '';\n const milliseconds = /^\\d+$/u.test(trimmed)\n ? Number(trimmed) * 1000\n : IMF_FIXDATE.test(trimmed)\n ? Date.parse(trimmed) - Date.now()\n : Number.NaN;\n return Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : undefined;\n}\n"],"mappings":"mCAkBA,SAAgBA,EAAqB,CACnC,uBACA,iCACkG,CAClG,IAAIC,EAAgB,EAChBC,EAAY,QAAQ,QAAQ,EAEhC,OAAO,MAAO,EAAO,IAAS,CAC5B,IAAMC,EAAoBD,EACpBE,GAAS,SAAY,CAGzB,IAFA,MAAMD,EAECF,EAAgB,YAAY,IAAI,GACrC,MAAMI,EAAM,KAAK,IAAIJ,EAAgB,YAAY,IAAI,EAAG,UAAsB,CAAC,EAEjF,EAAgB,YAAY,IAAI,EAAIK,CACtC,EAAA,CAAG,EACH,EAAYF,EACZ,MAAMA,EAEN,IAAMG,EAAW,MAAM,MAAMC,EAAOC,CAAI,EACxC,GAAIF,EAAS,SAAW,IAAK,CAC3B,IAAMG,EAAmBC,EAA4BJ,EAAS,QAAQ,IAAI,aAAa,CAAC,EACxF,EAAgB,KAAK,IAAIN,EAAe,YAAY,IAAI,GAAKS,GAAoBE,EAA8B,CACjH,CACA,OAAOL,CACT,CACF,CAIA,MAAMM,EAAc,oEAGpB,SAASF,EAA4B,EAA0C,CAC7E,IAAMG,EAAUC,GAAO,KAAK,GAAK,GAC3BC,EAAe,SAAS,KAAKF,CAAO,EACtC,OAAOA,CAAO,EAAI,IAClBD,EAAY,KAAKC,CAAO,EACtB,KAAK,MAAMA,CAAO,EAAI,KAAK,IAAI,EAC/B,IACN,OAAO,OAAO,SAASE,CAAY,EAAI,KAAK,IAAI,EAAGA,CAAY,EAAI,IAAA,EACrE"}
1
+ {"version":3,"file":"throttledFetch.js","names":["createThrottledFetch","nextRequestAt","admission","previousAdmission","ready","sleep","intervalMilliseconds","response","input","init","waitMilliseconds","parseRetryAfterMilliseconds","rateLimitFallbackMilliseconds","IMF_FIXDATE","trimmed","value","milliseconds"],"sources":["../src/throttledFetch.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n// `setTimeout` fires after 1 ms for longer delays, which would turn a long `Retry-After` hold into a busy loop.\nconst MAX_TIMER_MILLISECONDS = 2_147_483_647;\n\nexport interface ThrottledFetchOptions {\n /** The minimum interval between the starts of consecutive requests. */\n intervalMilliseconds: number;\n /** How long to hold later requests after a 429 response without `Retry-After` in delay seconds or IMF-fixdate. */\n rateLimitFallbackMilliseconds: number;\n}\n\n/**\n * Creates a `fetch` for a rate-limited API that starts requests in call order, at least `intervalMilliseconds` apart.\n * A 429 response holds every later request until its `Retry-After` (delay seconds or an IMF-fixdate) elapses, including\n * requests already waiting; the 429 response itself is returned to the caller, not retried. An abort signal takes effect\n * only once its request starts, not while it waits in the queue.\n */\nexport function createThrottledFetch({\n intervalMilliseconds,\n rateLimitFallbackMilliseconds,\n}: ThrottledFetchOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response> {\n let nextRequestAt = 0;\n let admission = Promise.resolve();\n\n return async (input, init) => {\n const previousAdmission = admission;\n const ready = (async () => {\n await previousAdmission;\n // A response to an earlier request can push `nextRequestAt` back while this request sleeps.\n while (nextRequestAt > performance.now()) {\n await sleep(Math.min(nextRequestAt - performance.now(), MAX_TIMER_MILLISECONDS));\n }\n nextRequestAt = performance.now() + intervalMilliseconds;\n })();\n admission = ready;\n await ready;\n\n const response = await fetch(input, init);\n if (response.status === 429) {\n const waitMilliseconds = parseRetryAfterMilliseconds(response.headers.get('retry-after'));\n nextRequestAt = Math.max(nextRequestAt, performance.now() + (waitMilliseconds ?? rateLimitFallbackMilliseconds));\n }\n return response;\n };\n}\n\n// RFC 9110 requires senders to generate HTTP dates in this IMF-fixdate form, e.g. `Sun, 06 Nov 1994 08:49:37 GMT`.\n// `Date.parse` alone would also accept malformed values such as `1.5`.\nconst IMF_FIXDATE = /^[A-Z][a-z]{2}, \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$/u;\n\n/** Parses `Retry-After` in delay seconds or IMF-fixdate; the obsolete RFC 850 and asctime dates yield `undefined`. */\nfunction parseRetryAfterMilliseconds(value: string | null): number | undefined {\n const trimmed = value?.trim() ?? '';\n const milliseconds = /^\\d+$/u.test(trimmed)\n ? Number(trimmed) * 1000\n : IMF_FIXDATE.test(trimmed)\n ? Date.parse(trimmed) - Date.now()\n : Number.NaN;\n return Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : undefined;\n}\n"],"mappings":"A,mCAkBA,SAAgBA,EAAqB,CACnC,uBACA,iCACkG,CAClG,IAAIC,EAAgB,EAChBC,EAAY,QAAQ,QAAQ,EAEhC,OAAO,MAAO,EAAO,IAAS,CAC5B,IAAMC,EAAoBD,EACpBE,GAAS,SAAY,CAGzB,IAFA,MAAMD,EAECF,EAAgB,YAAY,IAAI,GACrC,MAAMI,EAAM,KAAK,IAAIJ,EAAgB,YAAY,IAAI,EAAG,UAAsB,CAAC,EAEjF,EAAgB,YAAY,IAAI,EAAIK,CACtC,EAAA,CAAG,EACH,EAAYF,EACZ,MAAMA,EAEN,IAAMG,EAAW,MAAM,MAAMC,EAAOC,CAAI,EACxC,GAAIF,EAAS,SAAW,IAAK,CAC3B,IAAMG,EAAmBC,EAA4BJ,EAAS,QAAQ,IAAI,aAAa,CAAC,EACxF,EAAgB,KAAK,IAAIN,EAAe,YAAY,IAAI,GAAKS,GAAoBE,EAA8B,CACjH,CACA,OAAOL,CACT,CACF,CAIA,MAAMM,EAAc,oEAGpB,SAASF,EAA4B,EAA0C,CAC7E,IAAMG,EAAUC,GAAO,KAAK,GAAK,GAC3BC,EAAe,SAAS,KAAKF,CAAO,EACtC,OAAOA,CAAO,EAAI,IAClBD,EAAY,KAAKC,CAAO,EACtB,KAAK,MAAMA,CAAO,EAAI,KAAK,IAAI,EAC/B,IACN,OAAO,OAAO,SAASE,CAAY,EAAI,KAAK,IAAI,EAAGA,CAAY,EAAI,IAAA,EACrE,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.cjs","names":["raceWithTimeout","timer","promise","resolve","timeoutMilliseconds"],"sources":["../src/timeout.ts"],"sourcesContent":["export type RaceWithTimeoutResult<T> = { timedOut: false; value: T } | { timedOut: true };\n\n/**\n * Waits for `promise` for at most `timeoutMilliseconds`. A rejection of `promise` within the limit propagates.\n * The timer is cleared as soon as either side settles, so it never keeps a process alive after the race.\n * `promise` itself is not canceled on timeout; pass an `AbortSignal` to the underlying work for that.\n */\nexport async function raceWithTimeout<T>(\n promise: Promise<T>,\n timeoutMilliseconds: number\n): Promise<RaceWithTimeoutResult<T>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise.then((value) => ({ timedOut: false as const, value })),\n new Promise<{ timedOut: true }>((resolve) => {\n timer = setTimeout(() => resolve({ timedOut: true }), timeoutMilliseconds);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"aAOA,eAAsBA,EACpB,EACA,EACmC,CACnC,IAAIC,EACJ,GAAI,CACF,OAAO,MAAM,QAAQ,KAAK,CACxBC,EAAQ,KAAM,IAAW,CAAE,SAAU,GAAgB,OAAM,EAAE,EAC7D,IAAI,QAA6B,GAAY,CAC3C,EAAQ,eAAiBC,EAAQ,CAAE,SAAU,EAAK,CAAC,EAAGC,CAAmB,CAC3E,CAAC,CACH,CAAC,CACH,QAAU,CACR,aAAaH,CAAK,CACpB,CACF"}
1
+ {"version":3,"file":"timeout.cjs","names":["raceWithTimeout","timer","promise","resolve","timeoutMilliseconds"],"sources":["../src/timeout.ts"],"sourcesContent":["export type RaceWithTimeoutResult<T> = { timedOut: false; value: T } | { timedOut: true };\n\n/**\n * Waits for `promise` for at most `timeoutMilliseconds`. A rejection of `promise` within the limit propagates.\n * The timer is cleared as soon as either side settles, so it never keeps a process alive after the race.\n * `promise` itself is not canceled on timeout; pass an `AbortSignal` to the underlying work for that.\n */\nexport async function raceWithTimeout<T>(\n promise: Promise<T>,\n timeoutMilliseconds: number\n): Promise<RaceWithTimeoutResult<T>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise.then((value) => ({ timedOut: false as const, value })),\n new Promise<{ timedOut: true }>((resolve) => {\n timer = setTimeout(() => resolve({ timedOut: true }), timeoutMilliseconds);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"A,aAOA,eAAsBA,EACpB,EACA,EACmC,CACnC,IAAIC,EACJ,GAAI,CACF,OAAO,MAAM,QAAQ,KAAK,CACxBC,EAAQ,KAAM,IAAW,CAAE,SAAU,GAAgB,OAAM,EAAE,EAC7D,IAAI,QAA6B,GAAY,CAC3C,EAAQ,eAAiBC,EAAQ,CAAE,SAAU,EAAK,CAAC,EAAGC,CAAmB,CAC3E,CAAC,CACH,CAAC,CACH,QAAU,CACR,aAAaH,CAAK,CACpB,CACF,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.js","names":["raceWithTimeout","timer","promise","resolve","timeoutMilliseconds"],"sources":["../src/timeout.ts"],"sourcesContent":["export type RaceWithTimeoutResult<T> = { timedOut: false; value: T } | { timedOut: true };\n\n/**\n * Waits for `promise` for at most `timeoutMilliseconds`. A rejection of `promise` within the limit propagates.\n * The timer is cleared as soon as either side settles, so it never keeps a process alive after the race.\n * `promise` itself is not canceled on timeout; pass an `AbortSignal` to the underlying work for that.\n */\nexport async function raceWithTimeout<T>(\n promise: Promise<T>,\n timeoutMilliseconds: number\n): Promise<RaceWithTimeoutResult<T>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise.then((value) => ({ timedOut: false as const, value })),\n new Promise<{ timedOut: true }>((resolve) => {\n timer = setTimeout(() => resolve({ timedOut: true }), timeoutMilliseconds);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"AAOA,eAAsBA,EACpB,EACA,EACmC,CACnC,IAAIC,EACJ,GAAI,CACF,OAAO,MAAM,QAAQ,KAAK,CACxBC,EAAQ,KAAM,IAAW,CAAE,SAAU,GAAgB,OAAM,EAAE,EAC7D,IAAI,QAA6B,GAAY,CAC3C,EAAQ,eAAiBC,EAAQ,CAAE,SAAU,EAAK,CAAC,EAAGC,CAAmB,CAC3E,CAAC,CACH,CAAC,CACH,QAAU,CACR,aAAaH,CAAK,CACpB,CACF"}
1
+ {"version":3,"file":"timeout.js","names":["raceWithTimeout","timer","promise","resolve","timeoutMilliseconds"],"sources":["../src/timeout.ts"],"sourcesContent":["export type RaceWithTimeoutResult<T> = { timedOut: false; value: T } | { timedOut: true };\n\n/**\n * Waits for `promise` for at most `timeoutMilliseconds`. A rejection of `promise` within the limit propagates.\n * The timer is cleared as soon as either side settles, so it never keeps a process alive after the race.\n * `promise` itself is not canceled on timeout; pass an `AbortSignal` to the underlying work for that.\n */\nexport async function raceWithTimeout<T>(\n promise: Promise<T>,\n timeoutMilliseconds: number\n): Promise<RaceWithTimeoutResult<T>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise.then((value) => ({ timedOut: false as const, value })),\n new Promise<{ timedOut: true }>((resolve) => {\n timer = setTimeout(() => resolve({ timedOut: true }), timeoutMilliseconds);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"AAOA,eAAsBA,EACpB,EACA,EACmC,CACnC,IAAIC,EACJ,GAAI,CACF,OAAO,MAAM,QAAQ,KAAK,CACxBC,EAAQ,KAAM,IAAW,CAAE,SAAU,GAAgB,OAAM,EAAE,EAC7D,IAAI,QAA6B,GAAY,CAC3C,EAAQ,eAAiBC,EAAQ,CAAE,SAAU,EAAK,CAAC,EAAGC,CAAmB,CAC3E,CAAC,CACH,CAAC,CACH,QAAU,CACR,aAAaH,CAAK,CACpB,CACF,C"}
package/dist/url.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"url.cjs","names":["getSafeRedirectPath","baseUrl","origin","rawValue","value","fallback","url"],"sources":["../src/url.ts"],"sourcesContent":["export interface SafeRedirectPathOptions {\n /** The app's own origin, e.g. `https://example.com`; an unparsable origin throws a `TypeError`. */\n origin: string;\n /** Returned when `value` does not point to a page on `origin`. */\n fallback?: string;\n}\n\n/**\n * Returns the path, query, and fragment of `value` when it points to a page on the app's own origin, and `fallback`\n * (`/` by default) otherwise, so that redirecting to a user-supplied callback URL cannot lead to another site.\n * `value` is resolved against `origin` the way browsers resolve links, so relative paths and absolute URLs on\n * `origin` are accepted, while `//evil.example`, `/\\evil.example`, and `/\\t/evil.example` are rejected. For an array,\n * such as a repeated query parameter, the first element is used.\n */\nexport function getSafeRedirectPath(\n value: string | readonly string[] | null | undefined,\n { origin, fallback = '/' }: SafeRedirectPathOptions\n): string {\n // Parsed outside the per-value `try` so that a misconfigured origin fails loudly instead of rejecting every value.\n const baseUrl = new URL(origin);\n const rawValue = (typeof value === 'string' ? value : value?.[0])?.trim();\n if (!rawValue) return fallback;\n\n let url: URL;\n try {\n url = new URL(rawValue, baseUrl);\n } catch {\n return fallback;\n }\n // A `blob:` URL shares its inner URL's origin but has that URL as its pathname, hence the protocol check.\n // Dot segments can leave a pathname such as `//evil.example`, which is protocol-relative once returned as a path.\n if (url.protocol !== baseUrl.protocol || url.origin !== baseUrl.origin || url.pathname.startsWith('//')) {\n return fallback;\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n"],"mappings":"aAcA,SAAgBA,EACd,EACA,CAAE,SAAQ,WAAW,KACb,CAER,IAAMC,EAAU,IAAI,IAAIC,CAAM,EACxBC,GAAY,OAAOC,GAAU,SAAWA,EAAQA,IAAQ,GAAA,EAAK,KAAK,EACxE,GAAI,CAACD,EAAU,OAAOE,EAEtB,IAAIC,EACJ,GAAI,CACF,EAAM,IAAI,IAAIH,EAAUF,CAAO,CACjC,MAAQ,CACN,OAAOI,CACT,CAMA,OAHIC,EAAI,WAAaL,EAAQ,UAAYK,EAAI,SAAWL,EAAQ,QAAUK,EAAI,SAAS,WAAW,IAAI,EAC7FD,EAEF,GAAGC,EAAI,WAAWA,EAAI,SAASA,EAAI,MAC5C"}
1
+ {"version":3,"file":"url.cjs","names":["getSafeRedirectPath","baseUrl","origin","rawValue","value","fallback","url"],"sources":["../src/url.ts"],"sourcesContent":["export interface SafeRedirectPathOptions {\n /** The app's own origin, e.g. `https://example.com`; an unparsable origin throws a `TypeError`. */\n origin: string;\n /** Returned when `value` does not point to a page on `origin`. */\n fallback?: string;\n}\n\n/**\n * Returns the path, query, and fragment of `value` when it points to a page on the app's own origin, and `fallback`\n * (`/` by default) otherwise, so that redirecting to a user-supplied callback URL cannot lead to another site.\n * `value` is resolved against `origin` the way browsers resolve links, so relative paths and absolute URLs on\n * `origin` are accepted, while `//evil.example`, `/\\evil.example`, and `/\\t/evil.example` are rejected. For an array,\n * such as a repeated query parameter, the first element is used.\n */\nexport function getSafeRedirectPath(\n value: string | readonly string[] | null | undefined,\n { origin, fallback = '/' }: SafeRedirectPathOptions\n): string {\n // Parsed outside the per-value `try` so that a misconfigured origin fails loudly instead of rejecting every value.\n const baseUrl = new URL(origin);\n const rawValue = (typeof value === 'string' ? value : value?.[0])?.trim();\n if (!rawValue) return fallback;\n\n let url: URL;\n try {\n url = new URL(rawValue, baseUrl);\n } catch {\n return fallback;\n }\n // A `blob:` URL shares its inner URL's origin but has that URL as its pathname, hence the protocol check.\n // Dot segments can leave a pathname such as `//evil.example`, which is protocol-relative once returned as a path.\n if (url.protocol !== baseUrl.protocol || url.origin !== baseUrl.origin || url.pathname.startsWith('//')) {\n return fallback;\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n"],"mappings":"A,aAcA,SAAgBA,EACd,EACA,CAAE,SAAQ,WAAW,KACb,CAER,IAAMC,EAAU,IAAI,IAAIC,CAAM,EACxBC,GAAY,OAAOC,GAAU,SAAWA,EAAQA,IAAQ,GAAA,EAAK,KAAK,EACxE,GAAI,CAACD,EAAU,OAAOE,EAEtB,IAAIC,EACJ,GAAI,CACF,EAAM,IAAI,IAAIH,EAAUF,CAAO,CACjC,MAAQ,CACN,OAAOI,CACT,CAMA,OAHIC,EAAI,WAAaL,EAAQ,UAAYK,EAAI,SAAWL,EAAQ,QAAUK,EAAI,SAAS,WAAW,IAAI,EAC7FD,EAEF,GAAGC,EAAI,WAAWA,EAAI,SAASA,EAAI,MAC5C,C"}
package/dist/url.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","names":["getSafeRedirectPath","baseUrl","origin","rawValue","value","fallback","url"],"sources":["../src/url.ts"],"sourcesContent":["export interface SafeRedirectPathOptions {\n /** The app's own origin, e.g. `https://example.com`; an unparsable origin throws a `TypeError`. */\n origin: string;\n /** Returned when `value` does not point to a page on `origin`. */\n fallback?: string;\n}\n\n/**\n * Returns the path, query, and fragment of `value` when it points to a page on the app's own origin, and `fallback`\n * (`/` by default) otherwise, so that redirecting to a user-supplied callback URL cannot lead to another site.\n * `value` is resolved against `origin` the way browsers resolve links, so relative paths and absolute URLs on\n * `origin` are accepted, while `//evil.example`, `/\\evil.example`, and `/\\t/evil.example` are rejected. For an array,\n * such as a repeated query parameter, the first element is used.\n */\nexport function getSafeRedirectPath(\n value: string | readonly string[] | null | undefined,\n { origin, fallback = '/' }: SafeRedirectPathOptions\n): string {\n // Parsed outside the per-value `try` so that a misconfigured origin fails loudly instead of rejecting every value.\n const baseUrl = new URL(origin);\n const rawValue = (typeof value === 'string' ? value : value?.[0])?.trim();\n if (!rawValue) return fallback;\n\n let url: URL;\n try {\n url = new URL(rawValue, baseUrl);\n } catch {\n return fallback;\n }\n // A `blob:` URL shares its inner URL's origin but has that URL as its pathname, hence the protocol check.\n // Dot segments can leave a pathname such as `//evil.example`, which is protocol-relative once returned as a path.\n if (url.protocol !== baseUrl.protocol || url.origin !== baseUrl.origin || url.pathname.startsWith('//')) {\n return fallback;\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n"],"mappings":"AAcA,SAAgBA,EACd,EACA,CAAE,SAAQ,WAAW,KACb,CAER,IAAMC,EAAU,IAAI,IAAIC,CAAM,EACxBC,GAAY,OAAOC,GAAU,SAAWA,EAAQA,IAAQ,GAAA,EAAK,KAAK,EACxE,GAAI,CAACD,EAAU,OAAOE,EAEtB,IAAIC,EACJ,GAAI,CACF,EAAM,IAAI,IAAIH,EAAUF,CAAO,CACjC,MAAQ,CACN,OAAOI,CACT,CAMA,OAHIC,EAAI,WAAaL,EAAQ,UAAYK,EAAI,SAAWL,EAAQ,QAAUK,EAAI,SAAS,WAAW,IAAI,EAC7FD,EAEF,GAAGC,EAAI,WAAWA,EAAI,SAASA,EAAI,MAC5C"}
1
+ {"version":3,"file":"url.js","names":["getSafeRedirectPath","baseUrl","origin","rawValue","value","fallback","url"],"sources":["../src/url.ts"],"sourcesContent":["export interface SafeRedirectPathOptions {\n /** The app's own origin, e.g. `https://example.com`; an unparsable origin throws a `TypeError`. */\n origin: string;\n /** Returned when `value` does not point to a page on `origin`. */\n fallback?: string;\n}\n\n/**\n * Returns the path, query, and fragment of `value` when it points to a page on the app's own origin, and `fallback`\n * (`/` by default) otherwise, so that redirecting to a user-supplied callback URL cannot lead to another site.\n * `value` is resolved against `origin` the way browsers resolve links, so relative paths and absolute URLs on\n * `origin` are accepted, while `//evil.example`, `/\\evil.example`, and `/\\t/evil.example` are rejected. For an array,\n * such as a repeated query parameter, the first element is used.\n */\nexport function getSafeRedirectPath(\n value: string | readonly string[] | null | undefined,\n { origin, fallback = '/' }: SafeRedirectPathOptions\n): string {\n // Parsed outside the per-value `try` so that a misconfigured origin fails loudly instead of rejecting every value.\n const baseUrl = new URL(origin);\n const rawValue = (typeof value === 'string' ? value : value?.[0])?.trim();\n if (!rawValue) return fallback;\n\n let url: URL;\n try {\n url = new URL(rawValue, baseUrl);\n } catch {\n return fallback;\n }\n // A `blob:` URL shares its inner URL's origin but has that URL as its pathname, hence the protocol check.\n // Dot segments can leave a pathname such as `//evil.example`, which is protocol-relative once returned as a path.\n if (url.protocol !== baseUrl.protocol || url.origin !== baseUrl.origin || url.pathname.startsWith('//')) {\n return fallback;\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n"],"mappings":"AAcA,SAAgBA,EACd,EACA,CAAE,SAAQ,WAAW,KACb,CAER,IAAMC,EAAU,IAAI,IAAIC,CAAM,EACxBC,GAAY,OAAOC,GAAU,SAAWA,EAAQA,IAAQ,GAAA,EAAK,KAAK,EACxE,GAAI,CAACD,EAAU,OAAOE,EAEtB,IAAIC,EACJ,GAAI,CACF,EAAM,IAAI,IAAIH,EAAUF,CAAO,CACjC,MAAQ,CACN,OAAOI,CACT,CAMA,OAHIC,EAAI,WAAaL,EAAQ,UAAYK,EAAI,SAAWL,EAAQ,QAAUK,EAAI,SAAS,WAAW,IAAI,EAC7FD,EAEF,GAAGC,EAAI,WAAWA,EAAI,SAASA,EAAI,MAC5C,C"}
package/dist/yaml.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.cjs","names":["yamlStringifyOptions"],"sources":["../src/yaml.ts"],"sourcesContent":["/**\n * Options for `stringify` of the `yaml` package that keep multiline strings readable: `blockQuote: 'literal'` writes\n * them as `key: |` blocks, and `lineWidth: 0` disables line wrapping.\n */\nexport const yamlStringifyOptions = { blockQuote: 'literal', lineWidth: 0 } as const;\n"],"mappings":"aAIA,MAAaA,EAAuB,CAAE,WAAY,UAAW,UAAW,CAAE"}
1
+ {"version":3,"file":"yaml.cjs","names":["yamlStringifyOptions"],"sources":["../src/yaml.ts"],"sourcesContent":["/**\n * Options for `stringify` of the `yaml` package that keep multiline strings readable: `blockQuote: 'literal'` writes\n * them as `key: |` blocks, and `lineWidth: 0` disables line wrapping.\n */\nexport const yamlStringifyOptions = { blockQuote: 'literal', lineWidth: 0 } as const;\n"],"mappings":"A,aAIA,MAAaA,EAAuB,CAAE,WAAY,UAAW,UAAW,CAAE,E"}
package/dist/yaml.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.js","names":["yamlStringifyOptions"],"sources":["../src/yaml.ts"],"sourcesContent":["/**\n * Options for `stringify` of the `yaml` package that keep multiline strings readable: `blockQuote: 'literal'` writes\n * them as `key: |` blocks, and `lineWidth: 0` disables line wrapping.\n */\nexport const yamlStringifyOptions = { blockQuote: 'literal', lineWidth: 0 } as const;\n"],"mappings":"AAIA,MAAaA,EAAuB,CAAE,WAAY,UAAW,UAAW,CAAE"}
1
+ {"version":3,"file":"yaml.js","names":["yamlStringifyOptions"],"sources":["../src/yaml.ts"],"sourcesContent":["/**\n * Options for `stringify` of the `yaml` package that keep multiline strings readable: `blockQuote: 'literal'` writes\n * them as `key: |` blocks, and `lineWidth: 0` disables line wrapping.\n */\nexport const yamlStringifyOptions = { blockQuote: 'literal', lineWidth: 0 } as const;\n"],"mappings":"AAIA,MAAaA,EAAuB,CAAE,WAAY,UAAW,UAAW,CAAE,E"}
@@ -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) - 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"}
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":"A,aAAA,SAAgBA,EAAgC,EAAqB,CACnE,OAAOC,EAAI,WAAW,eAAiB,GAC9B,OAAO,eAAeC,EAAE,YAAY,CAAC,GAAK,GAAK,KAAO,CAC9D,CACH,C"}
@@ -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) - 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"}
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,C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/shared-lib",
3
- "version": "10.23.0",
3
+ "version": "10.24.0",
4
4
  "description": "Shared library for WillBooster projects",
5
5
  "license": "Apache-2.0",
6
6
  "author": "WillBooster Inc.",
@@ -53,8 +53,8 @@
53
53
  "@types/bun": "1.4.3",
54
54
  "@types/node": "26.6.5",
55
55
  "@willbooster/oxlint-config": "1.4.9",
56
- "@willbooster/wb": "22.29.0",
57
- "build-ts": "21.0.27",
56
+ "@willbooster/wb": "22.32.2",
57
+ "build-ts": "21.0.29",
58
58
  "oxlint": "1.88.0",
59
59
  "oxlint-tsgolint": "7.0.2004",
60
60
  "yaml": "2.9.1"