[{"data":1,"prerenderedAt":846},["ShallowReactive",2],{"navigation_docs":3,"-guide-custom":84,"-guide-custom-surround":841},[4,48],{"title":5,"path":6,"stem":7,"children":8,"icon":47},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19,23,27,31,35,39,43],{"title":10,"path":6,"stem":7},"Getting Started",{"title":12,"path":13,"stem":14},"Compressing","\u002Fguide\u002Fcompressing","1.guide\u002F02.compressing",{"title":16,"path":17,"stem":18},"Containers","\u002Fguide\u002Fcontainers","1.guide\u002F03.containers",{"title":20,"path":21,"stem":22},"Checksums","\u002Fguide\u002Fchecksums","1.guide\u002F04.checksums",{"title":24,"path":25,"stem":26},"Identify and peel","\u002Fguide\u002Fidentify","1.guide\u002F05.identify",{"title":28,"path":29,"stem":30},"Limits","\u002Fguide\u002Flimits","1.guide\u002F06.limits",{"title":32,"path":33,"stem":34},"CLI","\u002Fguide\u002Fcli","1.guide\u002F07.cli",{"title":36,"path":37,"stem":38},"Agents","\u002Fguide\u002Fagents","1.guide\u002F08.agents",{"title":40,"path":41,"stem":42},"Custom formats","\u002Fguide\u002Fcustom","1.guide\u002F09.custom",{"title":44,"path":45,"stem":46},"Playground","\u002Fguide\u002Fplayground","1.guide\u002F10.playground","i-lucide-book-open",{"title":49,"path":50,"stem":51,"children":52,"icon":83},"Formats","\u002Fformats","2.formats\u002F00.index",[53,55,59,63,67,71,75,79],{"title":54,"path":50,"stem":51},"Every format",{"title":56,"path":57,"stem":58},"deflate","\u002Fformats\u002Fdeflate","2.formats\u002F01.deflate",{"title":60,"path":61,"stem":62},"bzip2","\u002Fformats\u002Fbzip2","2.formats\u002F02.bzip2",{"title":64,"path":65,"stem":66},"LZMA","\u002Fformats\u002Flzma","2.formats\u002F03.lzma",{"title":68,"path":69,"stem":70},"Zstandard","\u002Fformats\u002Fzstd","2.formats\u002F04.zstd",{"title":72,"path":73,"stem":74},"Brotli","\u002Fformats\u002Fbrotli","2.formats\u002F05.brotli",{"title":76,"path":77,"stem":78},"LZ4","\u002Fformats\u002Flz4","2.formats\u002F06.lz4",{"title":80,"path":81,"stem":82},"Unix compress","\u002Fformats\u002Flzw","2.formats\u002F07.lzw","i-lucide-library",{"id":85,"title":40,"body":86,"description":834,"extension":835,"links":836,"meta":837,"navigation":294,"path":41,"seo":839,"stem":42,"__hash__":840},"docs\u002F1.guide\u002F09.custom.md",{"type":87,"value":88,"toc":828},"minimark",[89,94,119,264,268,726,730,757,771,797,801,824],[90,91,93],"h2",{"id":92},"two-functions-and-some-metadata","Two functions and some metadata",[95,96,97,98,102,103,106,107,110,111,114,115,118],"p",{},"Every built-in comes out of ",[99,100,101],"code",{},"defineCompression",", and yours can too. Hand it ",[99,104,105],{},"info"," and a ",[99,108,109],{},"decompress",", and a ",[99,112,113],{},"compress"," if you write the format as well. What comes back is a ",[99,116,117],{},"Compression",", the same shape as gzip's family:",[120,121,126],"pre",{"className":122,"code":123,"language":124,"meta":125,"style":125},"language-ts shiki shiki-themes compressions compressions compressions","interface Compression {\n  readonly name: string;\n  info(): CompressionInfo;\n  compress(input: string | Uint8Array, options?: CompressOptions): Uint8Array;\n  decompress(data: Uint8Array, options?: DecompressOptions): { bytes: Uint8Array; details: Details };\n}\n","ts","",[99,127,128,145,163,179,216,258],{"__ignoreMap":125},[129,130,133,137,141],"span",{"class":131,"line":132},"line",1,[129,134,136],{"class":135},"skH_V","interface",[129,138,140],{"class":139},"sK71F"," Compression",[129,142,144],{"class":143},"s38Sx"," {\n",[129,146,148,151,154,157,160],{"class":131,"line":147},2,[129,149,150],{"class":135},"  readonly",[129,152,153],{"class":143}," name",[129,155,156],{"class":135},":",[129,158,159],{"class":139}," string",[129,161,162],{"class":143},";\n",[129,164,166,169,172,174,177],{"class":131,"line":165},3,[129,167,168],{"class":139},"  info",[129,170,171],{"class":143},"()",[129,173,156],{"class":135},[129,175,176],{"class":139}," CompressionInfo",[129,178,162],{"class":143},[129,180,182,185,188,190,192,195,198,201,204,207,210,212,214],{"class":131,"line":181},4,[129,183,184],{"class":139},"  compress",[129,186,187],{"class":143},"(input",[129,189,156],{"class":135},[129,191,159],{"class":139},[129,193,194],{"class":135}," |",[129,196,197],{"class":139}," Uint8Array",[129,199,200],{"class":143},", options",[129,202,203],{"class":135},"?:",[129,205,206],{"class":139}," CompressOptions",[129,208,209],{"class":143},")",[129,211,156],{"class":135},[129,213,197],{"class":139},[129,215,162],{"class":143},[129,217,219,222,225,227,229,231,233,236,238,240,243,245,247,250,252,255],{"class":131,"line":218},5,[129,220,221],{"class":139},"  decompress",[129,223,224],{"class":143},"(data",[129,226,156],{"class":135},[129,228,197],{"class":139},[129,230,200],{"class":143},[129,232,203],{"class":135},[129,234,235],{"class":139}," DecompressOptions",[129,237,209],{"class":143},[129,239,156],{"class":135},[129,241,242],{"class":143}," { bytes",[129,244,156],{"class":135},[129,246,197],{"class":139},[129,248,249],{"class":143},"; details",[129,251,156],{"class":135},[129,253,254],{"class":139}," Details",[129,256,257],{"class":143}," };\n",[129,259,261],{"class":131,"line":260},6,[129,262,263],{"class":143},"}\n",[90,265,267],{"id":266},"run-length-because-a-puzzle-printed-counts-and-bytes","Run-length, because a puzzle printed counts and bytes",[120,269,271],{"className":122,"code":270,"language":124,"meta":125,"style":125},"import { compress, decompress, defineCompression, register } from \"@agntn\u002Fcompressions\";\n\nconst rle = defineCompression({\n  info: {\n    name: \"rle\",\n    label: \"Run-length\",\n    description: \"Each run of one byte as its length and the byte\",\n    standard: \"none\",\n    containers: [{ name: \"rle\", label: \"Run-length\", standard: \"none\", extensions: [\".rle\"] }],\n    compress: true,\n    options: [],\n  },\n  compress(bytes) {\n    const out: number[] = [];\n    for (let i = 0, run = 1; i \u003C bytes.length; i += run, run = 1) {\n      while (run \u003C 255 && bytes[i + run] === bytes[i]) run++;\n      out.push(run, bytes[i]!);\n    }\n    return Uint8Array.from(out);\n  },\n  decompress(data, out) {\n    if (data.length % 2) throw out.fail(\"a run needs its length and its byte\");\n    for (let i = 0; i \u003C data.length; i += 2) out.copyByte(data[i + 1]!, data[i]!);\n    return {};\n  },\n});\n\nregister(rle);\ncompress(\"rle\", \"aaaabbb\");                     \u002F\u002F Uint8Array [4, 97, 3, 98]\ndecompress(\"rle\", compress(\"rle\", \"aaaabbb\")).bytes;  \u002F\u002F the bytes of \"aaaabbb\"\n",[99,272,273,290,296,313,318,329,339,350,361,388,394,400,406,414,436,478,515,533,539,553,558,566,598,644,652,657,663,668,677,699],{"__ignoreMap":125},[129,274,275,278,281,284,288],{"class":131,"line":132},[129,276,277],{"class":135},"import",[129,279,280],{"class":143}," { compress, decompress, defineCompression, register } ",[129,282,283],{"class":135},"from",[129,285,287],{"class":286},"shU9J"," \"@agntn\u002Fcompressions\"",[129,289,162],{"class":143},[129,291,292],{"class":131,"line":147},[129,293,295],{"emptyLinePlaceholder":294},true,"\n",[129,297,298,301,304,307,310],{"class":131,"line":165},[129,299,300],{"class":135},"const",[129,302,303],{"class":143}," rle ",[129,305,306],{"class":135},"=",[129,308,309],{"class":139}," defineCompression",[129,311,312],{"class":143},"({\n",[129,314,315],{"class":131,"line":181},[129,316,317],{"class":143},"  info: {\n",[129,319,320,323,326],{"class":131,"line":218},[129,321,322],{"class":143},"    name: ",[129,324,325],{"class":286},"\"rle\"",[129,327,328],{"class":143},",\n",[129,330,331,334,337],{"class":131,"line":260},[129,332,333],{"class":143},"    label: ",[129,335,336],{"class":286},"\"Run-length\"",[129,338,328],{"class":143},[129,340,342,345,348],{"class":131,"line":341},7,[129,343,344],{"class":143},"    description: ",[129,346,347],{"class":286},"\"Each run of one byte as its length and the byte\"",[129,349,328],{"class":143},[129,351,353,356,359],{"class":131,"line":352},8,[129,354,355],{"class":143},"    standard: ",[129,357,358],{"class":286},"\"none\"",[129,360,328],{"class":143},[129,362,364,367,369,372,374,377,379,382,385],{"class":131,"line":363},9,[129,365,366],{"class":143},"    containers: [{ name: ",[129,368,325],{"class":286},[129,370,371],{"class":143},", label: ",[129,373,336],{"class":286},[129,375,376],{"class":143},", standard: ",[129,378,358],{"class":286},[129,380,381],{"class":143},", extensions: [",[129,383,384],{"class":286},"\".rle\"",[129,386,387],{"class":143},"] }],\n",[129,389,391],{"class":131,"line":390},10,[129,392,393],{"class":143},"    compress: true,\n",[129,395,397],{"class":131,"line":396},11,[129,398,399],{"class":143},"    options: [],\n",[129,401,403],{"class":131,"line":402},12,[129,404,405],{"class":143},"  },\n",[129,407,409,411],{"class":131,"line":408},13,[129,410,184],{"class":139},[129,412,413],{"class":143},"(bytes) {\n",[129,415,417,420,423,425,428,431,433],{"class":131,"line":416},14,[129,418,419],{"class":135},"    const",[129,421,422],{"class":143}," out",[129,424,156],{"class":135},[129,426,427],{"class":139}," number",[129,429,430],{"class":143},"[] ",[129,432,306],{"class":135},[129,434,435],{"class":143}," [];\n",[129,437,439,442,445,448,451,453,456,458,461,464,467,470,473,475],{"class":131,"line":438},15,[129,440,441],{"class":135},"    for",[129,443,444],{"class":143}," (",[129,446,447],{"class":135},"let",[129,449,450],{"class":143}," i ",[129,452,306],{"class":135},[129,454,455],{"class":143}," 0, run ",[129,457,306],{"class":135},[129,459,460],{"class":143}," 1; i ",[129,462,463],{"class":135},"\u003C",[129,465,466],{"class":143}," bytes.length; i ",[129,468,469],{"class":135},"+=",[129,471,472],{"class":143}," run, run ",[129,474,306],{"class":135},[129,476,477],{"class":143}," 1) {\n",[129,479,481,484,487,489,492,495,498,501,504,507,510,513],{"class":131,"line":480},16,[129,482,483],{"class":135},"      while",[129,485,486],{"class":143}," (run ",[129,488,463],{"class":135},[129,490,491],{"class":143}," 255 ",[129,493,494],{"class":135},"&&",[129,496,497],{"class":143}," bytes[i ",[129,499,500],{"class":135},"+",[129,502,503],{"class":143}," run] ",[129,505,506],{"class":135},"===",[129,508,509],{"class":143}," bytes[i]) run",[129,511,512],{"class":135},"++",[129,514,162],{"class":143},[129,516,518,521,524,527,530],{"class":131,"line":517},17,[129,519,520],{"class":143},"      out.",[129,522,523],{"class":139},"push",[129,525,526],{"class":143},"(run, bytes[i]",[129,528,529],{"class":135},"!",[129,531,532],{"class":143},");\n",[129,534,536],{"class":131,"line":535},18,[129,537,538],{"class":143},"    }\n",[129,540,542,545,548,550],{"class":131,"line":541},19,[129,543,544],{"class":135},"    return",[129,546,547],{"class":143}," Uint8Array.",[129,549,283],{"class":139},[129,551,552],{"class":143},"(out);\n",[129,554,556],{"class":131,"line":555},20,[129,557,405],{"class":143},[129,559,561,563],{"class":131,"line":560},21,[129,562,221],{"class":139},[129,564,565],{"class":143},"(data, out) {\n",[129,567,569,572,575,578,581,584,587,590,593,596],{"class":131,"line":568},22,[129,570,571],{"class":135},"    if",[129,573,574],{"class":143}," (data.length ",[129,576,577],{"class":135},"%",[129,579,580],{"class":143}," 2) ",[129,582,583],{"class":135},"throw",[129,585,586],{"class":143}," out.",[129,588,589],{"class":139},"fail",[129,591,592],{"class":143},"(",[129,594,595],{"class":286},"\"a run needs its length and its byte\"",[129,597,532],{"class":143},[129,599,601,603,605,607,609,611,614,616,619,621,624,627,630,632,635,637,640,642],{"class":131,"line":600},23,[129,602,441],{"class":135},[129,604,444],{"class":143},[129,606,447],{"class":135},[129,608,450],{"class":143},[129,610,306],{"class":135},[129,612,613],{"class":143}," 0; i ",[129,615,463],{"class":135},[129,617,618],{"class":143}," data.length; i ",[129,620,469],{"class":135},[129,622,623],{"class":143}," 2) out.",[129,625,626],{"class":139},"copyByte",[129,628,629],{"class":143},"(data[i ",[129,631,500],{"class":135},[129,633,634],{"class":143}," 1]",[129,636,529],{"class":135},[129,638,639],{"class":143},", data[i]",[129,641,529],{"class":135},[129,643,532],{"class":143},[129,645,647,649],{"class":131,"line":646},24,[129,648,544],{"class":135},[129,650,651],{"class":143}," {};\n",[129,653,655],{"class":131,"line":654},25,[129,656,405],{"class":143},[129,658,660],{"class":131,"line":659},26,[129,661,662],{"class":143},"});\n",[129,664,666],{"class":131,"line":665},27,[129,667,295],{"emptyLinePlaceholder":294},[129,669,671,674],{"class":131,"line":670},28,[129,672,673],{"class":139},"register",[129,675,676],{"class":143},"(rle);\n",[129,678,680,682,684,686,689,692,695],{"class":131,"line":679},29,[129,681,113],{"class":139},[129,683,592],{"class":143},[129,685,325],{"class":286},[129,687,688],{"class":143},", ",[129,690,691],{"class":286},"\"aaaabbb\"",[129,693,694],{"class":143},");                     ",[129,696,698],{"class":697},"scIB-","\u002F\u002F Uint8Array [4, 97, 3, 98]\n",[129,700,702,704,706,708,710,712,714,716,718,720,723],{"class":131,"line":701},30,[129,703,109],{"class":139},[129,705,592],{"class":143},[129,707,325],{"class":286},[129,709,688],{"class":143},[129,711,113],{"class":139},[129,713,592],{"class":143},[129,715,325],{"class":286},[129,717,688],{"class":143},[129,719,691],{"class":286},[129,721,722],{"class":143},")).bytes;  ",[129,724,725],{"class":697},"\u002F\u002F the bytes of \"aaaabbb\"\n",[90,727,729],{"id":728},"what-comes-free","What comes free",[95,731,732,734,735,738,739,742,743,688,746,688,749,752,753,756],{},[99,733,109],{}," writes into ",[99,736,737],{},"out",", an ",[99,740,741],{},"Output"," that holds the caller's limit. It has ",[99,744,745],{},"push(byte)",[99,747,748],{},"append(chunk)",[99,750,751],{},"copyByte(byte, count)"," for runs and ",[99,754,755],{},"copy(distance, count)"," for LZ77 matches, overlap included. Each one checks the limit before it writes, so a bomb in your format stops where gzip's would.",[95,758,759,762,763,766,767,770],{},[99,760,761],{},"out.fail(message, offset)"," builds a ",[99,764,765],{},"DecompressError"," that carries everything written so far. Throw it, and ",[99,768,769],{},"partial: true"," works on your format the same way it works on the built-ins.",[95,772,773,774,777,778,781,782,785,786,789,790,792,793,796],{},"Options get checked before your function runs: unknown names, numbers out of ",[99,775,776],{},"min"," and ",[99,779,780],{},"max",", values outside ",[99,783,784],{},"choices",", an option scoped to another container. ",[99,787,788],{},"container"," always arrives filled in, with the first container as the default. A format without ",[99,791,113],{}," throws a clear ",[99,794,795],{},"CompressionError"," when someone tries to write it.",[90,798,800],{"id":799},"where-it-shows-up","Where it shows up",[95,802,803,805,806,688,808,688,810,688,813,777,816,819,820,823],{},[99,804,673],{}," puts it in the registry, replacing any format with the same name. From then on ",[99,807,113],{},[99,809,109],{},[99,811,812],{},"create",[99,814,815],{},"formats",[99,817,818],{},"formatInfos"," see it, and so do the four tools and the CLI in the same process. ",[99,821,822],{},"identify"," doesn't: it only tries containers it knows how to recognize, and a format of your own has no probe there.",[825,826,827],"style",{},"html pre.shiki code .skH_V, html code.shiki .skH_V{--shiki-light:var(--shiki-token-keyword);--shiki-default:var(--shiki-token-keyword);--shiki-dark:var(--shiki-token-keyword)}html pre.shiki code .sK71F, html code.shiki .sK71F{--shiki-light:var(--shiki-token-function);--shiki-default:var(--shiki-token-function);--shiki-dark:var(--shiki-token-function)}html pre.shiki code .s38Sx, html code.shiki .s38Sx{--shiki-light:var(--ui-text-highlighted);--shiki-default:var(--ui-text-highlighted);--shiki-dark:var(--ui-text-highlighted)}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .shU9J, html code.shiki .shU9J{--shiki-light:var(--shiki-token-string);--shiki-default:var(--shiki-token-string);--shiki-dark:var(--shiki-token-string)}html pre.shiki code .scIB-, html code.shiki .scIB-{--shiki-light:var(--shiki-token-comment);--shiki-default:var(--shiki-token-comment);--shiki-dark:var(--shiki-token-comment)}",{"title":125,"searchDepth":147,"depth":147,"links":829},[830,831,832,833],{"id":92,"depth":147,"text":93},{"id":266,"depth":147,"text":267},{"id":728,"depth":147,"text":729},{"id":799,"depth":147,"text":800},"defineCompression takes the metadata and two functions. Register the result and compress and decompress and the tools see it","md",null,{"icon":838},"i-lucide-puzzle",{"title":40,"description":834},"dU7SMj_RGTiIDrW8__jNIVFwVrl_iPgBXzKCavqfnxU",[842,844],{"title":36,"path":37,"stem":38,"description":843,"children":-1},"The same tools over MCP and Pi and OMP and the AI SDK. What they take and what they refuse and what the model reads back",{"title":44,"path":45,"stem":46,"description":845,"children":-1},"Every tool in your browser with the CLI line and the tool call for every state",1791071141269]