From e645a6c485ccfb67feb656641d7e445c6572ffc6 Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol <990485+sdogruyol@users.noreply.github.com> Date: Wed, 4 Nov 2026 07:37:48 +0300 Subject: [PATCH 1/4] re-record wasm, diagnostics, the agent loop, highlighting and the stdlib at 0.20.1 --- records/agent.json | 10 +-- records/diagnostics.json | 6 +- records/highlight.json | 8 +- records/stdlib.json | 16 ++-- records/wasm/arrays.wasm | Bin 145881 -> 145881 bytes records/wasm/blocks.wasm | Bin 127866 -> 127866 bytes records/wasm/bounds.wasm | Bin 129541 -> 129541 bytes records/wasm/case.wasm | Bin 127806 -> 127806 bytes records/wasm/classes.wasm | Bin 124398 -> 124398 bytes records/wasm/conditions.wasm | Bin 124828 -> 124828 bytes records/wasm/constants.wasm | Bin 160443 -> 160443 bytes records/wasm/conversions.wasm | Bin 187416 -> 187416 bytes records/wasm/defaults.wasm | Bin 127100 -> 127100 bytes records/wasm/defer.wasm | Bin 128138 -> 128138 bytes records/wasm/dispatch.wasm | Bin 136069 -> 136069 bytes records/wasm/errors.wasm | Bin 160371 -> 160371 bytes records/wasm/exports.wasm | Bin 124838 -> 124838 bytes records/wasm/functions.wasm | Bin 125184 -> 125184 bytes records/wasm/generic-functions.wasm | Bin 151699 -> 151699 bytes records/wasm/generic-types.wasm | Bin 135597 -> 135597 bytes records/wasm/hashes.wasm | Bin 165179 -> 165179 bytes records/wasm/hello.wasm | Bin 123087 -> 123087 bytes records/wasm/iyi-basics.wasm | Bin 213443 -> 213443 bytes records/wasm/iyi-calc.wasm | Bin 200618 -> 200618 bytes records/wasm/iyi-collections.wasm | Bin 228588 -> 228588 bytes records/wasm/iyi-config.wasm | Bin 177376 -> 177376 bytes records/wasm/iyi-derive.wasm | Bin 128268 -> 128268 bytes records/wasm/iyi-enums.wasm | Bin 151146 -> 151146 bytes records/wasm/iyi-errors.wasm | Bin 138685 -> 138685 bytes records/wasm/iyi-files.wasm | Bin 125156 -> 125156 bytes records/wasm/iyi-format.wasm | Bin 308041 -> 308041 bytes records/wasm/iyi-formatting.wasm | Bin 143143 -> 143143 bytes records/wasm/iyi-generics.wasm | Bin 143824 -> 143824 bytes records/wasm/iyi-grid.wasm | Bin 187747 -> 187747 bytes records/wasm/iyi-hello.wasm | Bin 130696 -> 130696 bytes records/wasm/iyi-immutable.wasm | Bin 197267 -> 197267 bytes records/wasm/iyi-init_order.wasm | Bin 124809 -> 124809 bytes records/wasm/iyi-inventory.wasm | Bin 218027 -> 218027 bytes records/wasm/iyi-io.wasm | Bin 131609 -> 131609 bytes records/wasm/iyi-modules.wasm | Bin 126751 -> 126751 bytes records/wasm/iyi-sessions.wasm | Bin 139540 -> 139540 bytes records/wasm/iyi-shapes.wasm | Bin 197147 -> 197147 bytes records/wasm/iyi-socket.wasm | Bin 413767 -> 413767 bytes records/wasm/iyi-std_iterator.wasm | Bin 208688 -> 208688 bytes records/wasm/iyi-std_text.wasm | Bin 195805 -> 195805 bytes records/wasm/iyi-std_time.wasm | Bin 275376 -> 275376 bytes records/wasm/iyi-visited.wasm | Bin 176116 -> 176116 bytes records/wasm/iyi-webapp.wasm | Bin 171353 -> 171353 bytes records/wasm/loops.wasm | Bin 126424 -> 126424 bytes records/wasm/manifest.json | 122 ++++++++++++++-------------- records/wasm/methods.wasm | Bin 161051 -> 161051 bytes records/wasm/modules.wasm | Bin 124842 -> 124842 bytes records/wasm/nil.wasm | Bin 125476 -> 125476 bytes records/wasm/ranges.wasm | Bin 141760 -> 141760 bytes records/wasm/returns.wasm | Bin 127402 -> 127402 bytes records/wasm/strings.wasm | Bin 160621 -> 160621 bytes records/wasm/structs.wasm | Bin 125572 -> 125572 bytes records/wasm/traits.wasm | Bin 160535 -> 160535 bytes records/wasm/tuples.wasm | Bin 127759 -> 127759 bytes records/wasm/types.wasm | Bin 158793 -> 158793 bytes records/wasm/unions.wasm | Bin 135190 -> 135190 bytes records/wasm/variables.wasm | Bin 156061 -> 156061 bytes 62 files changed, 81 insertions(+), 81 deletions(-) diff --git a/records/agent.json b/records/agent.json index 40e4ea6..6447e61 100644 --- a/records/agent.json +++ b/records/agent.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.0 (built on Crystal 1.22.0-dev [224ed74fc] (2026-11-02))", - "commit": "224ed74fc7ba9d78345dfd10f21ece4b499702fa", + "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", + "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:agent", - "when": "2026-11-02T07:19:31.432Z" + "when": "2026-11-04T04:32:53.960Z" }, "project": [ { @@ -104,7 +104,7 @@ "command": "iyi test --json .", "exitCode": 0, "payload": "stdout", - "stdout": "{\"tests\":[{\"file\":\"./app_test.iyi\",\"status\":\"pass\",\"seconds\":0.394}],\"passed\":1,\"failed\":0,\"skipped\":0}\n", + "stdout": "{\"tests\":[{\"file\":\"./app_test.iyi\",\"status\":\"pass\",\"seconds\":0.281}],\"passed\":1,\"failed\":0,\"skipped\":0}\n", "stderr": "" }, { @@ -167,7 +167,7 @@ }, "serverInfo": { "name": "iyi", - "version": "0.20.0" + "version": "0.20.1" } }, "tools": [ diff --git a/records/diagnostics.json b/records/diagnostics.json index 2d10ef1..029a842 100644 --- a/records/diagnostics.json +++ b/records/diagnostics.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.0 (built on Crystal 1.22.0-dev [224ed74fc] (2026-11-02))", - "commit": "224ed74fc7ba9d78345dfd10f21ece4b499702fa", + "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", + "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:diagnostics", - "when": "2026-11-02T07:19:23.048Z" + "when": "2026-11-04T04:32:47.944Z" }, "cases": [ { diff --git a/records/highlight.json b/records/highlight.json index e4b069a..f2a119a 100644 --- a/records/highlight.json +++ b/records/highlight.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.0 (built on Crystal 1.22.0-dev [224ed74fc] (2026-11-02))", - "commit": "224ed74fc7ba9d78345dfd10f21ece4b499702fa", + "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", + "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:highlight", - "when": "2026-11-02T07:19:35.714Z" + "when": "2026-11-04T04:32:57.045Z" }, "files": { "samples/iyi/app/formal.iyi": "# A second module, exporting a `title` of its own. See `../modules.iyi`.\nmodule app/formal\n\npub def title : String\n "esteemed colleague"\nend\n\npub def address(name : String) : String\n "Good day, #{name}."\nend\n", @@ -15,7 +15,7 @@ "samples/iyi/calc/ast.iyi": "module calc/ast\n\n# What an evaluated program can go wrong with. Both are ordinary members of the\n# union an evaluation returns (SPEC.md III.1), so a caller either handles them\n# or propagates with `!`.\npub struct UnknownName\n getter name : String\n\n def initialize(@name : String)\n end\nend\n\nimpl Error for UnknownName\n def message : String\n "unknown name '#{name}'"\n end\nend\n\npub struct DividedByZero\n def initialize\n end\nend\n\nimpl Error for DividedByZero\n def message : String\n "divided by zero"\n end\nend\n\n# The one thing every node can do. A trait rather than a base class, because\n# there is nothing to inherit and R-3 has no opinion to break here.\npub trait Node\n abstract def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\nend\n\npub struct Number\n getter value : Int32\n\n def initialize(@value : Int32)\n end\nend\n\nimpl Node for Number\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n value\n end\nend\n\npub struct Name\n getter name : String\n\n def initialize(@name : String)\n end\nend\n\nimpl Node for Name\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n return UnknownName.new(name) unless scope.has_key?(name)\n scope[name]\n end\nend\n\npub class Binary\n getter op : Char\n getter left : Number | Name | Binary\n getter right : Number | Name | Binary\n\n def initialize(@op : Char, @left : Number | Name | Binary, @right : Number | Name | Binary)\n end\nend\n\nimpl Node for Binary\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n a = left.evaluate(scope)!\n b = right.evaluate(scope)!\n\n if op == '+'\n a + b\n elsif op == '-'\n a - b\n elsif op == '*'\n a * b\n else\n return DividedByZero.new if b == 0\n a // b\n end\n end\nend\n", "samples/iyi/calc/lexer.iyi": "module calc/lexer\n\n# What the scanner produces. A token is its kind, the text it covers and where\n# it started, because an error that cannot say where is half an error.\npub struct Token\n getter kind : String\n getter text : String\n getter at : Int32\n\n def initialize(@kind : String, @text : String, @at : Int32)\n end\nend\n\npub struct BadCharacter\n getter at : Int32\n getter found : Char\n\n def initialize(@at : Int32, @found : Char)\n end\nend\n\nimpl Error for BadCharacter\n def message : String\n "unexpected character at #{at}"\n end\nend\n\npub def scan(source : String) : Array(Token) | BadCharacter\n tokens = [] of Token\n index = 0\n\n while index < source.size\n c = source[index]\n\n # A line ends in CRLF on Windows: `echo 2 + 3 * 4| iyi run\n # samples\\iyi\\calc.iyi` in cmd answered "unexpected character at 9",\n # the `\\r`.\n if c == ' ' || c == '\\n' || c == '\\t' || c == '\\r'\n index = index + 1\n elsif digit?(c)\n start = index\n while index < source.size && digit?(source[index])\n index = index + 1\n end\n tokens << Token.new("number", source[start, index - start], start)\n elsif letter?(c)\n start = index\n while index < source.size && (letter?(source[index]) || digit?(source[index]))\n index = index + 1\n end\n tokens << Token.new("name", source[start, index - start], start)\n elsif operator?(c)\n tokens << Token.new("op", source[index, 1], index)\n index = index + 1\n else\n return BadCharacter.new(index, c)\n end\n end\n\n tokens << Token.new("end", "", index)\n tokens\nend\n\ndef digit?(c : Char) : Bool\n c >= '0' && c <= '9'\nend\n\ndef letter?(c : Char) : Bool\n (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'\nend\n\ndef operator?(c : Char) : Bool\n c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '='\nend\n", "samples/iyi/calc/parser.iyi": "module calc/parser\n\nimport calc/lexer::{Token}\nimport calc/ast::{Number, Name, Binary}\n\npub struct SyntaxError\n getter at : Int32\n getter wanted : String\n\n def initialize(@at : Int32, @wanted : String)\n end\nend\n\nimpl Error for SyntaxError\n def message : String\n "expected #{wanted} at #{at}"\n end\nend\n\n# One statement: `name = expression`, or an expression on its own, which the\n# runner prints. Kept as a pair rather than a node type, because a statement is\n# not something this language evaluates twice.\npub struct Statement\n getter target : String\n getter body : Number | Name | Binary\n\n def initialize(@target : String, @body : Number | Name | Binary)\n end\nend\n\n# A cursor over the tokens. A struct with an index rather than a shifting array,\n# because backing up one token is the parser's whole trick.\npub class Reader\n getter tokens : Array(Token)\n getter at : Int32\n\n def initialize(@tokens : Array(Token))\n @at = 0\n end\n\n def peek : Token\n tokens[at]\n end\n\n def advance : Token\n token = tokens[at]\n @at = at + 1\n token\n end\nend\n\npub def parse(tokens : Array(Token)) : Array(Statement) | SyntaxError\n reader = Reader.new(tokens)\n statements = [] of Statement\n\n while reader.peek.kind != "end"\n statements << statement(reader)!\n end\n\n statements\nend\n\ndef statement(reader : Reader) : Statement | SyntaxError\n first = reader.peek\n\n if first.kind == "name" && reader.tokens[reader.at + 1].text == "="\n reader.advance\n reader.advance\n Statement.new(first.text, expression(reader)!)\n else\n Statement.new("", expression(reader)!)\n end\nend\n\ndef expression(reader : Reader) : Number | Name | Binary | SyntaxError\n left = term(reader)!\n\n while reader.peek.kind == "op" && (reader.peek.text == "+" || reader.peek.text == "-")\n op = reader.advance.text[0]\n left = Binary.new(op, left, term(reader)!)\n end\n\n left\nend\n\ndef term(reader : Reader) : Number | Name | Binary | SyntaxError\n left = factor(reader)!\n\n while reader.peek.kind == "op" && (reader.peek.text == "*" || reader.peek.text == "/")\n op = reader.advance.text[0]\n left = Binary.new(op, left, factor(reader)!)\n end\n\n left\nend\n\ndef factor(reader : Reader) : Number | Name | Binary | SyntaxError\n token = reader.advance\n\n if token.kind == "number"\n Number.new(token.text.to_i)\n elsif token.kind == "name"\n Name.new(token.text)\n elsif token.text == "("\n inside = expression(reader)!\n closing = reader.advance\n return SyntaxError.new(closing.at, "')'") unless closing.text == ")"\n inside\n else\n SyntaxError.new(token.at, "a number, a name or '('")\n end\nend\n", - "samples/iyi/calc.iyi": "# A language, in the language — the biggest program this prelude can hold.\n#\n# echo "x = 2 + 3 * 4\n# x * 10" | ./bin/iyi run samples/iyi/calc.iyi\n#\n# The other samples each document a rule. This one documents nothing and is\n# here for a different reason: everything else in `samples/iyi` is a page long,\n# and a language that has only been used for pages has not been used. This is\n# three modules and a scanner, a parser and an evaluator, the shape of every\n# real program that reads something, written against iyi's own 25,725-line\n# library and nothing else.\n#\n# What it cost to write is recorded where it landed. `String#[]`, `String#[](start,\n# count)`, `String#to_i` and `read_input` are in the prelude because this file\n# asked for them, which is the rule the prelude grows by (SPEC.md "0.1.0",\n# item 3). Nothing else was missing.\nmodule samples/calc\n\nimport calc/lexer::{scan, BadCharacter}\nimport calc/parser::{parse, Statement, SyntaxError}\nimport calc/ast::{Number, Name, Binary, UnknownName, DividedByZero}\n\n# What runs when nothing is piped in, so that the sample is a program rather\n# than a prompt. CI runs it this way. Its last two lines are errors on\n# purpose, and the demo exits 0 with them.\nDEMO = "x = 2 + 3 * 4\ny = (x - 4) / 2\nx + y * 10\nmissing + 1\n1 / 0"\n\n# Whether every statement answered. An error goes to standard error, and\n# input with one makes the program exit 1: all of them went to standard\n# output at exit 0, so `echo 1 / 0 | iyi run samples/iyi/calc.iyi && next`\n# ran `next`.\npub def run(source : String) : Bool\n tokens = scan(source)\n if tokens.is_a?(BadCharacter)\n stderr.puts "error: #{tokens.message}"\n return false\n end\n\n program = parse(tokens)\n if program.is_a?(SyntaxError)\n stderr.puts "error: #{program.message}"\n return false\n end\n\n answered = true\n scope = {} of String => Int32\n program.each do |statement|\n value = statement.body.evaluate(scope)\n\n if value.is_a?(Int32)\n if statement.target.empty?\n puts value\n else\n scope[statement.target] = value\n end\n else\n # `value` is an error member of the union, and every one of them answers\n # `message` because they implement `Error` (SPEC.md III.1).\n stderr.puts "error: #{value.message}"\n answered = false\n end\n end\n answered\nend\n\n# Input that is only line ends is nothing piped in: PowerShell's `"" |`\n# sends a CRLF. And its pipe starts what it sends with a byte order mark,\n# which is not part of the program: "unexpected character at 0".\ntyped = read_input.lchop('\\uFEFF')\nif typed.strip.empty?\n run(DEMO)\nelsif !run(typed)\n exit 1\nend\n", + "samples/iyi/calc.iyi": "# A language, in the language — the biggest program this prelude can hold.\n#\n# echo "x = 2 + 3 * 4\n# x * 10" | ./bin/iyi run samples/iyi/calc.iyi\n#\n# The other samples each document a rule. This one documents nothing and is\n# here for a different reason: everything else in `samples/iyi` is a page long,\n# and a language that has only been used for pages has not been used. This is\n# three modules and a scanner, a parser and an evaluator, the shape of every\n# real program that reads something, written against iyi's own 25,763-line\n# library and nothing else.\n#\n# What it cost to write is recorded where it landed. `String#[]`, `String#[](start,\n# count)`, `String#to_i` and `read_input` are in the prelude because this file\n# asked for them, which is the rule the prelude grows by (SPEC.md "0.1.0",\n# item 3). Nothing else was missing.\nmodule samples/calc\n\nimport calc/lexer::{scan, BadCharacter}\nimport calc/parser::{parse, Statement, SyntaxError}\nimport calc/ast::{Number, Name, Binary, UnknownName, DividedByZero}\n\n# What runs when nothing is piped in, so that the sample is a program rather\n# than a prompt. CI runs it this way. Its last two lines are errors on\n# purpose, and the demo exits 0 with them.\nDEMO = "x = 2 + 3 * 4\ny = (x - 4) / 2\nx + y * 10\nmissing + 1\n1 / 0"\n\n# Whether every statement answered. An error goes to standard error, and\n# input with one makes the program exit 1: all of them went to standard\n# output at exit 0, so `echo 1 / 0 | iyi run samples/iyi/calc.iyi && next`\n# ran `next`.\npub def run(source : String) : Bool\n tokens = scan(source)\n if tokens.is_a?(BadCharacter)\n stderr.puts "error: #{tokens.message}"\n return false\n end\n\n program = parse(tokens)\n if program.is_a?(SyntaxError)\n stderr.puts "error: #{program.message}"\n return false\n end\n\n answered = true\n scope = {} of String => Int32\n program.each do |statement|\n value = statement.body.evaluate(scope)\n\n if value.is_a?(Int32)\n if statement.target.empty?\n puts value\n else\n scope[statement.target] = value\n end\n else\n # `value` is an error member of the union, and every one of them answers\n # `message` because they implement `Error` (SPEC.md III.1).\n stderr.puts "error: #{value.message}"\n answered = false\n end\n end\n answered\nend\n\n# Input that is only line ends is nothing piped in: PowerShell's `"" |`\n# sends a CRLF. And its pipe starts what it sends with a byte order mark,\n# which is not part of the program: "unexpected character at 0".\ntyped = read_input.lchop('\\uFEFF')\nif typed.strip.empty?\n run(DEMO)\nelsif !run(typed)\n exit 1\nend\n", "samples/iyi/collections.iyi": "# The `Enumerable` port, exercised — SPEC.md II.6.\n#\n# ./bin/iyi run samples/iyi/collections.iyi\n#\n# `std/enumerable.iyi` holds the trait. This file implements it twice and calls\n# every method it defines. Both matter: a default method is only typed when it\n# is called, so a trait that merely compiles has proved nothing about whether\n# its bodies are sound.\n#\n# `Nums` and `Words` answer `Elem` differently — `Int32` and `String` — and each\n# gets the same 57 default methods from one `each`. That is the claim II.6\n# makes, running.\n\nmodule samples/collections\n\nimport std/enumerable::{Enumerable}\n\npub struct Nums\n def initialize(@a : Array(Int32))\n end\nend\n\nimpl Enumerable for Nums\n type Elem = Int32\n\n def each(& : Int32 -> Nil) : Nil\n @a.each { |x| yield x }\n end\nend\n\npub struct Words\n def initialize(@a : Array(String))\n end\nend\n\nimpl Enumerable for Words\n type Elem = String\n\n def each(& : String -> Nil) : Nil\n @a.each { |x| yield x }\n end\nend\n\nn = Nums.new([3, 1, 4, 1, 5])\nw = Words.new(["pear", "fig", "plum", "kiwi"])\n\nputs "to_a #{n.to_a.join(",")}"\nputs "size #{n.size}"\nputs "empty? #{n.empty?} #{Nums.new([] of Int32).empty?}"\nputs "all? #{n.all? { |x| x > 0 }}"\nputs "any? #{n.any? { |x| x > 4 }}"\nputs "none? #{n.none? { |x| x > 9 }}"\nputs "one? #{n.one? { |x| x == 4 }}"\nputs "count #{n.count { |x| x == 1 }}"\nputs "find #{n.find { |x| x > 3 }} #{n.find { |x| x > 9 }.inspect}"\nputs "index #{n.index { |x| x == 4 }} #{n.index { |x| x == 9 }.inspect}"\nputs "first?/first #{n.first?} #{n.first}"\nputs "map #{n.map { |x| x * 2 }.join(",")}"\nputs "flat_map #{n.flat_map { |x| [x, x] }.join(",")}"\nputs "map_with_index #{n.map_with_index { |x, i| x * i }.join(",")}"\nputs "select #{n.select { |x| x > 2 }.join(",")}"\nputs "reject #{n.reject { |x| x > 2 }.join(",")}"\nputs "partition #{n.partition { |x| x > 2 }.map(&.join("+")).join(" | ")}"\nputs "take/skip #{n.take(2).join(",")} #{n.skip(3).join(",")}"\nputs "take_while #{n.take_while { |x| x < 4 }.join(",")}"\nputs "skip_while #{n.skip_while { |x| x < 4 }.join(",")}"\nputs "reduce #{n.reduce(0) { |a, x| a + x }}"\nputs "each_with_index #{n.map_with_index { |x, i| i }.join(",")}"\nputs "each_with_object #{n.each_with_object([] of Int32) { |e, o| o << e * 10 }.join(",")}"\nputs "join #{n.join("-")}"\nputs "includes? #{n.includes?(4)} #{n.includes?(9)}"\nputs "max?/max #{n.max?} #{n.max}"\nputs "min?/min #{n.min?} #{n.min}"\nputs "sum/product #{n.sum} #{n.product}"\nputs "sum of nothing #{Nums.new([] of Int32).sum}"\nputs "tally #{n.tally[1]}"\nputs "sorted #{n.sorted.join(",")}"\nputs "max_by #{w.max_by { |s| s.size }}"\nputs "group_by #{w.group_by { |s| s.size }[4].join(",")}"\nputs "index_by #{w.index_by { |s| s.size }[3]}"\n\nputs "each_slice #{n.each_slice(2).map(&.join("+")).join(" ")}"\nputs "each_cons #{n.each_cons(2).map(&.join("+")).join(" ")}"\nputs "chunk_while #{n.chunk_while { |a, b| b >= a }.map(&.join("+")).join(" ")}"\nputs "first(n) #{n.first(3).join(",")}"\nputs "compact_map #{n.compact_map { |x| x > 2 ? x * 10 : nil }.join(",")}"\nputs "find_value #{n.find_value { |x| x > 3 ? "big" : nil }}"\nputs "accumulate #{n.accumulate(0) { |a, x| a + x }.join(",")}"\nputs "reduce? #{n.reduce? { |a, x| a + x }}"\nputs "in_groups_of #{n.in_groups_of(2, 0).map(&.join("+")).join(" ")}"\nputs "min_by #{w.min_by { |s| s.size }}"\nputs "max_of/min_of #{w.max_of { |s| s.size }} #{w.min_of { |s| s.size }}"\nputs "minmax #{n.minmax[0]},#{n.minmax[1]}"\nputs "tally_by #{w.tally_by { |s| s.size }[4]}"\nputs "sum_by #{w.sum_by { |s| s.size }}"\n\n# SPEC II.6 finding 6: one bound replaces `Indexable | Iterable | Iterator`,\n# and `O::Elem` names what the other collection yields. Four numbers for\n# four words: a shorter other collection is refused, as the other library\n# refuses it.\nputs "zip #{Nums.new([3, 1, 4, 1]).zip(w).map { |pair| "#{pair[0]}#{pair[1]}" }.join(" ")}"\n\ncycled = [] of Int32\nNums.new([1, 2]).cycle(2) { |x| cycled << x }\nputs "cycle #{cycled.join(",")}"\n\n# A trait with an associated type used as a type: one function, any implementer.\ndef describe(e : Enumerable) : String\n "#{e.size} items"\nend\n\nputs "as a type #{describe(n)} / #{describe(w)}"\n\n# ---------------------------------------------------------------------------\n# What this sample would report as an error\n# ---------------------------------------------------------------------------\n#\n# * `Words.new([...]).sum` — `sum` is bounded `where Elem : Num` and\n# `String` implements `Comparable` but not `Num`. The error names the element\n# type and the bound rather than failing somewhere inside the method body.\n#\n# * A second `impl Enumerable for Nums` — a trait with an associated type is\n# implementable once per type, because a second answer for `Elem` would make\n# every call on `Nums` ambiguous.\n#\n# * `impl Num for X` with no `impl Comparable for X` before it —\n# `Num : Comparable` requires it, and the impls have to be written in\n# dependency order.\n", "samples/iyi/config.iyi": "# The first program that reads text - a `key = value` file, with the line\n# number in its error.\n#\n# ./bin/iyi run samples/iyi/config.iyi\n#\n# Text comes apart into lines, a line into its two halves around a `=`, and\n# each half loses its whitespace; a line that does not parse is an error\n# value carrying where it was (SPEC.md III.1), not a printed message. Like\n# `basics` and `inventory`, this documents no rule: it is here because the\n# prelude grows only when a program in this repository needs something\n# (SPEC.md 0.1.0 item 3), and cutting text is what the third program does.\nmodule samples/config\n\nstruct ParseError\n property line : Int32\n property message : String\n\n def initialize(@line : Int32, @message : String)\n end\nend\n\nimpl Error for ParseError\n def message : String\n "line #{@line}: #{@message}"\n end\nend\n\ndef parse(text : String) : Hash(String, String) | ParseError\n config = {} of String => String\n text.lines.each_with_index do |raw, index|\n line = raw.strip\n next if line.empty? || line.starts_with?('#')\n eq = line.index('=')\n return ParseError.new(index + 1, "no '=' in #{line.inspect}") unless eq\n key = line[0, eq].strip\n return ParseError.new(index + 1, "empty key") if key.empty?\n config[key] = line[eq + 1, line.size - eq - 1].strip\n end\n config\nend\n\ngood = "# the service\\nname = iyi\\nport= 8080\\n\\nmode =fast\\n"\ncase parse(good)\nin Hash(String, String)\n puts it # => {"name" => "iyi", "port" => "8080", "mode" => "fast"}\n puts it["port"].to_i + 1 # => 8081\n puts it.keys.join(", ") # => name, port, mode\nin ParseError then puts "error: #{it.message}"\nend\n\nbad = "name = iyi\\nbroken line\\n"\ncase parse(bad)\nin Hash(String, String) then puts "parsed"\nin ParseError then puts "error: #{it.message}" # => error: line 2: no '=' in "broken line"\nend\n\n# The pieces a parser cuts, on their own.\nputs "a,,b".split(',') # => ["a", "", "b"]\nputs "one\\ntwo\\n".lines # => ["one", "two"]\nputs " padded\\t".strip.inspect # => "padded"\nputs "config.iyi".ends_with?(".iyi") # => true\n", "samples/iyi/cores.iyi": "# Work on every core - SPEC.md III.4.13, as a person writes it.\n#\n# ./bin/iyi run samples/iyi/cores.iyi\n#\n# `spawn` is a task on the thread that spawned it, and its block may touch\n# anything; `spawn_parallel` is a task a pool of workers runs on any core,\n# and its block may capture only what is `Share` (III.4.4). std/http's\n# `Server.serve` puts each connection on that pool, so its block is checked\n# the same way. When a block reaches module state through a call, the\n# compiler warns, and `@[SafeReach("why")]` on the state says why it is safe.\nmodule samples/cores\n\nimport std/http::{HTTP, Response, Server}\nimport std/socket::{IyiSocket}\nimport std/mutex::{Mutex}\n\ndef collatz_steps(n : Int64) : Int64\n steps = 0_i64\n while n != 1\n n = n % 2 == 0 ? n // 2 : 3 * n + 1\n steps += 1\n end\n steps\nend\n\n# 1. `spawn` and `spawn_parallel`.\n#\n# The tasks `spawn` starts share their spawner's thread, so they may append\n# to an `Array` they captured. The tasks `spawn_parallel` starts run on\n# whichever core is free; each answers its own value, and a task's answer\n# need not be `Share`, because it is handed over once, after the task ends.\nseen = [] of Int64\ngroup do |g|\n (1..3).each do |n|\n g.spawn do\n seen << collatz_steps(n.to_i64)\n 0\n end\n end\nend\nputs seen.sorted # => [0, 1, 7]\n\nlongest = 0_i64\ngroup do |g|\n handles = [] of IyiTask(Int64)\n 4.times do |part|\n handles << g.spawn_parallel do\n best = 0_i64\n start = part.to_i64 * 25_000 + 1\n (start..start + 24_999).each { |n| best = collatz_steps(n) if collatz_steps(n) > best }\n best\n end\n end\n handles.each do |handle|\n steps = handle.value\n longest = steps if steps.is_a?(Int64) && steps > longest\n end\nend\nputs longest # => 350, the longest chain below 100,000\n\n# 2. State the pool may share.\n#\n# A counter that every core adds to is an `Atomic`; an `Array` that every\n# core appends to sits behind a `Mutex`. Both are `Share`, so a parallel\n# block may capture them.\nhits = Atomic(Int64).new(0_i64)\npaths = Mutex.new { [] of String }\n\n# 3. Module state a call reaches.\n#\n# A parallel block that names `GREETING` is refused, because an `Array` is\n# not `Share`. One that only calls `greeting` used to compile with nothing\n# said; now the compiler warns at the block, naming the path. This constant\n# is set before the server starts and only read afterwards, so it says so.\n@[SafeReach("set once before the server starts, then only read")]\nGREETING = ["hello", "from", "every", "core"]\n\ndef greeting : String\n GREETING.join(" ")\nend\n\n# 4. A server on every core.\n#\n# `Server.serve` runs each connection on the pool's workers, so its block is\n# checked as `spawn_parallel`'s is. It stops through a channel: closing the\n# listener from inside the block would capture it, and `/stop` may arrive on\n# any worker. `workers: 1` keeps the one-thread server, whose block may\n# capture anything.\nlistener = IyiSocket.listen("127.0.0.1", 0)\nbase = "http://127.0.0.1:#{listener.local_port}"\nstop = Channel(Int32).new(1)\ngroup do |g|\n g.spawn do\n stop.receive\n listener.close\n 0\n end\n g.spawn do\n Server.serve(listener) do |req|\n hits.add(1_i64)\n path = req.path\n paths.lock { |list| list << path }\n if path == "/stop"\n stop.send(0)\n Response.new(200, "bye")\n else\n Response.new(200, greeting)\n end\n end\n 0\n end\n g.spawn do\n puts HTTP.get(base + "/").or_panic.body # => hello from every core\n HTTP.get(base + "/stop").or_panic\n 0\n end\nend\nputs hits.get # => 2\npaths.lock { |list| puts list.sorted } # => ["/", "/stop"]\n", diff --git a/records/stdlib.json b/records/stdlib.json index 41d10b5..74348df 100644 --- a/records/stdlib.json +++ b/records/stdlib.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.0 (built on Crystal 1.22.0-dev [224ed74fc] (2026-11-02))", - "commit": "224ed74fc7ba9d78345dfd10f21ece4b499702fa", + "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", + "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", "machine": "Linux 7.0.0-38-generic x64, QEMU Virtual CPU version 2.5+", "command": "npm run record:stdlib", - "when": "2026-11-02T07:19:39.580Z" + "when": "2026-11-04T04:32:59.606Z" }, "modules": [ { @@ -274,8 +274,8 @@ { "name": "std/http", "path": "src/std/http.iyi", - "sourceSha256": "c834c001d26e1a45a8e7163e78255438004bb09caad82542270e011ef7dc82c3", - "surface": "module std/http\n\n# A body as it arrives: from a connection, framed by its length, by chunks\n# or by the connection's close, or from a string already in hand. `read`\n# answers the next piece, \"\" once the body is done; what the other end\n# does wrong is an `HTTPError`, and a connection that fails or is\n# cancelled answers that. The first failure is kept: every read after it\n# answers the same. A body coded `gzip`, `deflate` or `zstd` is decoded\n# as it is read when the client asked for that.\n#\n# A body has a limit: past it a read answers an `HTTPError` with status\n# 413. The server's is `Server::MAX_BODY`; a client's is none.\npub class BodyReader\n # The body's coding undone from here on: `Envelope::Gzip` for gzip,\n # `Envelope::Zlib` for HTTP's deflate - which some servers send as the\n # bare stream instead, without zlib's two bytes of header, so the first\n # two bytes are looked at first, and a pair that is not a zlib header\n # (RFC 1950 §2.2: method 8, a multiple of 31) is read as the bare\n # stream. A body that does not decode is an `HTTPError` naming the\n # coding and why.\n def decode(envelope : Envelope) : Nil\n # The body's `zstd` coding (RFC 8878) undone from here on, by std/zstd's\n # `ZstdReader` as the pieces arrive, frames one after another. A frame\n # that asks for a window past *max_window* bytes is refused rather than\n # given the memory (a window outside 1 KiB to 512 MiB is a panic), and a\n # body that does not decode is an `HTTPError` naming the coding and why.\n def decode_zstd(max_window : Int32 = Zstd::WINDOW_LIMIT) : Nil\n # Reads past the rest of the body, *limit* bytes at most: whether it\n # ended within them. A server reads past what a block left unread, so\n # the next request on the connection starts where it should.\n def drain(limit : Int64) : Bool\n # Whether every byte of the body has been read, the framing's end too.\n def finished? : Bool\n # A body that arrives as an HTTP/2 stream's DATA: refused past *limit*.\n protected def self.frames(h2 : H2Connection, id : Int32, limit : Int64) : BodyReader\n def initialize(conn : Conn | ::Nil, kind : Int32, length : Int64, limit : Int64, text : String = \"\", hello : Proc(Bool) | ::Nil = nil)\n protected def on_frames(h2 : H2Connection, id : Int32) : Nil\n # The body's bytes as the framing delivers them, before any decoding.\n protected def raw(max : Int32) : String | Cancelled | SocketError | HTTPError\n # The next piece of the body, at most *max* bytes; \"\" when it is done.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the body as one string, refused past *limit* bytes (413).\n # The pieces are kept as they arrive and joined once: a builder of the\n # length a head declared was made before any of it had come, and a head\n # that declared 64 MB and sent two bytes cost the server 64 MB.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\n protected def remember(fault : HTTPError | Cancelled | SocketError) : HTTPError | Cancelled | SocketError\n # A body that is a string already in hand.\n def self.text(text : String) : BodyReader\n # Whether the body ends where the connection does: nothing can follow\n # it on the connection.\n def to_close? : Bool\nend\n\n# What a streamed body is written to: an answer's `Response.stream`\n# block's, and a client's upload from an `IyiIO`. Each `write` goes out as\n# one chunk (RFC 9112 §7.1), or as it is where the connection's close ends\n# the body, so a block that writes a line a second is read a line a\n# second. Under a server's `compress`, each write is a gzip piece flushed\n# whole (std/compress's `Deflater`), which decodes as it arrives too. The\n# first failure is kept, and every write after it answers it; `finish`,\n# which the server calls when the block returns, writes the end.\npub class BodyWriter\n # The end of the body: the gzip trailer, and the last chunk with the\n # blank line after it. Once; the server's call after a block that\n # finished its own body writes nothing.\n def finish : Nil | Cancelled | SocketError\n def initialize(socket : IyiSocket | TlsStream | H2Out, chunked : Bool, deflater : Deflater | ::Nil = nil)\n def write(text : String) : Nil | Cancelled | SocketError\n # Bytes of the body written so far, before any framing or coding.\n def written : Int64\nend\n\n# What `HTTP.request`'s `jar:` keeps cookies in: the `Cookie` header a\n# request to a URL carries, and an answer's `Set-Cookie` lines taken in.\n# std/cookie's `CookieJar` is one; std/http itself reads no clock, so the\n# expiry of a cookie is the store's to keep.\npub trait CookieStore\n abstract def header_for(url : String) : String | ::Nil\n abstract def update(url : String, response : Response) : Nil\nend\n\npub struct HTTP\n # For a request target in absolute form (RFC 9112 §3.2.2), where its\n # authority starts: after `http://` or `https://`, in any case.\n def self.absolute_form(target : String) : Int32 | ::Nil\n # Whether a list of codings a client accepts (`Accept-Encoding`, RFC\n # 9110 §12.5.3) takes *coding*: named, or `*`, with a `q` above 0.\n def self.accepts?(list : String, coding : String) : Bool\n # A 1xx, 204 or 304: an answer with no content (RFC 9110 §15).\n def self.bodiless?(status : Int32) : Bool\n # Where a chunked body that starts at *from* ends in `text` — the byte\n # after the last chunk's terminating CRLF (trailers included) — nil when\n # it has not all arrived, or why it is not a chunked body. What\n # `decode_chunked` will then read whole.\n def self.chunked_end(text : String, from : Int32 = 0) : Int32 | String | Nil\n # How much a list of codings a client accepts wants *coding*, as its\n # `q` in thousandths: the weight it is named with, else `*`'s, else 0 -\n # and 0 refuses it.\n def self.coding_weight(list : String, coding : String) : Int32\n # A `Content-Length` as a number, or nil when it is not one. Repeated\n # lines arrive joined, and are one length only when every one says the\n # same (RFC 9112 §6.3). A length past 2147483647 is still a length, too\n # large for either side, and is read as one past it: `to_i?` answered\n # nil for it, and 3000000000 was refused as \"not a number\".\n def self.content_length(value : String) : Int64 | ::Nil\n # A control character other than a tab, or DEL: what a field value may\n # not hold (RFC 9110 §5.5), nor a request target or a reason phrase.\n def self.control?(text : String) : Bool\n # The pairs of a request's `Cookie` header, `a=1; b=2` (RFC 6265 §4.2),\n # a value's quotes taken off; the first of a name is kept, as a server\n # reads the most specific cookie first.\n def self.cookie_pairs(value : String) : Hash(String, String)\n # A chunked body (RFC 9112 §7.1) as one string: each chunk's size line\n # is hexadecimal, extensions after `;` are ignored, and a zero-length\n # chunk ends it. What follows the last chunk is trailers, which nothing\n # here reads.\n def self.decode_chunked(text : String) : String\n def self.delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def self.find_headers_end(text : String, from : Int32 = 0) : Int32 | ::Nil\n # The request as it goes on the wire, whole. `Host`, `Connection` and\n # `Content-Length` are the request's own lines; a caller's header of\n # that name is refused rather than sent twice. A body's length is\n # always written for the methods a body belongs to, so `POST` with\n # nothing to say still says `Content-Length: 0`. A byte of the path past\n # 127 goes as `%` and two hex digits: a request target is ASCII (RFC\n # 9112 §3.2, RFC 3986 §2.1), and `/ü` went out as its UTF-8 bytes, raw.\n def self.format_request(method : String, path : String, host : String, body : String = \"\", headers : Hash(String, String) = {} of String => String) : String\n def self.get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # *text* in gzip, whole: an answer's body under a server's `compress`.\n def self.gzip(text : String) : String\n # A writer whose body goes out in gzip as it is written.\n def self.gzip_writer(socket : IyiSocket | TlsStream | H2Out, chunked : Bool) : BodyWriter\n def self.has_break?(text : String) : Bool\n # Whether a list of tokens (RFC 9110 §5.6.1), its repeated lines joined\n # with \", \", holds *token*, in any case.\n def self.has_token?(list : String, token : String) : Bool\n def self.head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The value of a chunk size's hex digits, nil for anything else or for\n # one past what an Int32 holds.\n def self.hex(text : String) : Int32 | ::Nil\n # *n* in lowercase hexadecimal: a chunk's size line.\n def self.hex_text(n : Int64) : String\n # A Host value - the `a` of `Host: a` - or an absolute-form target's\n # authority, made of what a host and its port are spelled with (RFC 3986\n # §3.2.2, §3.2.3): a name, an IPv4 address or an IPv6 one in brackets,\n # and digits after a colon. Empty is one too, for a target with no\n # authority (RFC 9112 §3.2). A blank, a `/`, an `@` - the bytes another\n # host or a path begins with - are not.\n def self.host?(text : String) : Bool\n # The endpoints *answer*'s ServiceMode records name for a client whose\n # configuration offers *alpn* by ALPN (none: it speaks http/1.1 alone),\n # to an origin on *port*, in the order to try them: a record whose\n # mandatory keys are not all ones std/dns reads, or whose protocols -\n # alpn, and http/1.1 unless no-default-alpn - hold none of the client's,\n # names none (RFC 9460 sections 7.1.2 and 8), and neither does one whose\n # target is not a host name. Each is the record's target, or its owner\n # for \".\"; its port, or *port*; its `ech` where the ECHConfigList holds\n # a config std/tls can use (RFC 9849 section 6.1), nil otherwise; and\n # *alpn* with the protocols the record's alpn lists first, in *alpn*'s\n # order otherwise.\n def self.https_endpoints(answer : SvcbAnswer, alpn : Array(String), port : Int32) : Array(HttpsEndpoint)\n def initialize\n # One request, and the block's answer to the response, its body still\n # arriving: `response.body_io.read` takes it a piece at a time, and\n # `response.body` all of it. The connection closes when the block\n # returns. A body of *body* is a string, sent with its length, or an\n # `IyiIO`, sent chunked as it is read - which a redirect that keeps the\n # method (307, 308) cannot send again, so that answer is handed to the\n # block as it is.\n #\n # The redirects followed are RFC 9110 §15.4's: 301 and 302 make a POST a\n # GET, 303 makes anything but a HEAD a GET, and 307 and 308 keep the\n # method and the body. `Authorization`, `Proxy-Authorization` and\n # `Cookie` written by the caller are not sent to another scheme, host or\n # port - `https://` to `http://` at one host included, as curl does (Go\n # strips them for another host) - and a jar's `Secure` cookies go over\n # `https://` alone. The downgrade itself is followed, as Go's client and\n # `curl -L` follow it; any other header the caller wrote, and a 307 or\n # 308's body, go on in the clear. One more than *redirects* is an\n # `HTTPError`; with 0 the first answer is the block's.\n #\n # *timeout_ms* bounds the connect, and each read and write after it -\n # a pause, not the whole exchange, as Python's `timeout` is: a body that\n # keeps arriving is read for as long as it takes. One that runs out is a\n # `SocketError` whose `timed_out?` is true. 0, the default, waits as\n # long as the server does, as Go's and Python's clients do; a client\n # that talks to servers it does not control should give one.\n #\n # *proxy* is a proxy's URL - `http://user:password@host:port`, or\n # `host:port` alone - and every request goes through it, every redirect\n # too: `http://` as an absolute-form request to the proxy (RFC 9112\n # §3.2.2), which carries `Proxy-Authorization: Basic` when the URL has\n # a user; `https://` through a tunnel the proxy opens with `CONNECT\n # host:port` (RFC 9110 §9.3.6), the credential on the CONNECT alone and\n # TLS to the server inside it, its certificate checked as without a\n # proxy. A caller's own `Proxy-Authorization` is the proxy's too: sent\n # on the CONNECT when the URL has no user, and never inside the tunnel.\n # A CONNECT answered with anything but a 2xx - 407 when the proxy wants\n # credentials, 403, 502 - is an `HTTPError` naming the proxy and its\n # answer. `http://` through a proxy is HTTP/1.1 whatever\n # *prior_knowledge* says. Which proxy, if any, a URL's environment\n # names is `proxy_for`'s, and std/http_proxy's `HTTPProxy.from_env`.\n def self.open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, client : HTTPClient | ::Nil = nil, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n # The field lines after the first, or why they are not fields. A name\n # repeated, in any case, is one field, its values joined in order with\n # \", \" (RFC 9110 §5.3): into a Hash by name, a second line spelled the\n # same replaced the first and one spelled otherwise was an entry\n # `header` never reached, so two different `Content-Length`s were read\n # as whichever came first or last. A value with a control character in\n # it - a CR or LF that ends no line, a NUL - is refused (§5.5) rather than\n # handed to whoever reads it or echoes it into a header.\n #\n # *unfold*, for an answer: a line that begins with a blank continues the\n # field above it (obs-fold, RFC 9112 §5.2), and a user agent joins it on\n # with one space; read as a field of its own, `HTTP.get` panicked with\n # \"header without a colon: \\\" b\\\"\". A server may refuse one, and this\n # one does.\n #\n # *apart*, when given, is handed each repeated field's values by the\n # name in the answer, a line each, before they are joined.\n def self.parse_fields(lines : Array(String), unfold : Bool = false, apart : Hash(String, Array(String)) | ::Nil = nil) : Hash(String, String) | String\n # The response as it came off the wire, whole. `head` says the request\n # was a `HEAD`, whose answer carries the headers of a body it does not\n # send. An interim answer - `100 Continue`, `103 Early Hints` - is read\n # past to the final one after it (RFC 9110 §15.2). A `101` switches\n # protocols, and HTTP ends with it. What is not a response panics with\n # why: this is the text a program has in hand; the client answers the\n # same sentences as `HTTPError`s.\n def self.parse_response(text : String, head : Bool = false) : Response\n # A response's head - the text before its blank line - read: status,\n # reason, the fields, and the lines of those on more than one. A line\n # that begins with a blank continues the field above it (obs-fold, RFC\n # 9112 §5.2), which a user agent joins on with one space.\n def self.parse_response_head(text : String) : ::Tuple(Int32, String, Hash(String, String), Hash(String, Array(String)) | ::Nil) | HTTPError\n # Where *text* goes on at *from* past the empty lines there: what a\n # server skips before a request line (RFC 9112 §2.2).\n def self.past_empty_lines(text : String, from : Int32) : Int32\n def self.post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The proxy a request for *url* goes through, given the values of\n # `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` - nil when it goes\n # direct. Go's `golang.org/x/net/http/httpproxy` is the rule, line for\n # line, as its documentation and tests state it:\n #\n # * `https://` takes *https_proxy*, `http://` *http_proxy*, any other\n # scheme none. A proxy written without a scheme is `http://`'s, and is\n # answered with it, ready for `open`'s *proxy*.\n # * `localhost` (in lower case: Go's comparison) and a loopback address\n # (127.0.0.0/8, ::1, the first as IPv4-mapped too) never go through a\n # proxy.\n # * *no_proxy* is a comma-separated list, each entry trimmed and read\n # without regard to case: `*` alone sends everything direct; an IP\n # address or a CIDR range (`10.0.0.0/8`, `2001:db8::/32`) matches the\n # addresses in it, IPv4 and its IPv4-mapped form alike, and a CIDR\n # range of one family never the other's; a domain matches itself and\n # every name under it (`foo.com`: `foo.com`, `bar.foo.com`, not\n # `barfoo.com`); with a leading dot, or `*.`, only the names under it\n # (`.y.com`: `x.y.com`, not `y.com`). An address or domain may carry\n # a port (`1.2.3.4:80`, `[::1]:443`, `foo.com:8080`), and matches\n # then on that port alone, the URL's own or its scheme's default.\n # An entry that is none of these (`:80`) is passed over.\n # * A host is compared as IDNA's ASCII form, as Go compares it.\n #\n # Pure: the values are the caller's, so std/http reaches no environment;\n # std/http_proxy reads them from it.\n def self.proxy_for(url : String, http_proxy : String | ::Nil, https_proxy : String | ::Nil, no_proxy : String | ::Nil = nil) : String | ::Nil\n def self.put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The reason phrase a status carries (RFC 9110 §15), for a response the\n # server writes; a status this table does not know gets its class's.\n def self.reason_for(status : Int32) : String\n # One request and its whole answer: the answer as `open` hands it, its\n # body read. Redirects are followed, *redirects* at most; *jar* keeps\n # the cookies the answers set and sends them back; *decompress* asks\n # for gzip, deflate and zstd and undoes them. A refused or reset\n # connection answers `SocketError`; a task cancelled answers\n # `Cancelled`; what the server does wrong answers `HTTPError`. `!`\n # passes any of them up, `.or_panic` refuses it.\n def self.request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Response | Cancelled | SocketError | HTTPError\n # A request's head: the request line, `Host`, `Connection` (*connection*:\n # `close`, or `Upgrade`), the caller's headers, and the body's framing -\n # *length*, chunks, or none.\n def self.request_head(method : String, path : String, host : String, headers : Hash(String, String), length : Int64 | ::Nil, chunked : Bool, connection : String) : String\n # Two field names for the same field: ASCII case is the only difference\n # a name may carry.\n def self.same_name?(a : String, b : String) : Bool\n def self.split_crlf(text : String) : Array(String)\n # A status line read: the status and its reason, or why it is not one.\n def self.status_line(line : String) : ::Tuple(Int32, String) | HTTPError\n # What an `https://` request is checked against and offers: the\n # system's roots, and ALPN's `http/1.1`, unless a program sets its own -\n # roots of its own for a server it runs, say.\n def self.tls_config : TlsConfig\n def self.tls_config=(config : TlsConfig) : TlsConfig\n # A token (RFC 9110 §5.6.2): what a method or a field name is made of.\n def self.token?(text : String) : Bool\n # Without the blanks at either end (RFC 9112 §5.1: field values are\n # trimmed of the whitespace around them).\n def self.trim(s : String) : String\n # The 101 an upgrade to *protocol* is answered with, and the connection,\n # which is the caller's from then on: std/websocket's handshake. Any\n # other answer is an `HTTPError` naming it.\n #\n # *proxy* is `open`'s, and so is its credential, on the CONNECT alone;\n # but an upgrade goes through a tunnel for `http://` as for `https://`,\n # the handshake inside it in origin form. `Upgrade` belongs to the\n # connection it is sent on (RFC 9110 §7.8), and a proxy forwarding a\n # request removes `Connection` and every field it names (§7.6.1), so a\n # handshake handed to a forward proxy in absolute form reaches the\n # server, at best, as a plain GET: the gate's proxy, which drops\n # `Connection`, leaves std/websocket's server a request it answers 400.\n def self.upgrade(url : String, protocol : String, headers : Hash(String, String) = {} of String => String, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Upgraded | Cancelled | SocketError | HTTPError\nend\n\n# Requests that keep their connections: an `HTTPClient` holds the connections\n# its answers left open, by host and port, and the next request to that\n# server goes over one of them instead of a new one - the TCP handshake,\n# and the slow start after it, paid once rather than per request. Its\n# methods are `HTTP`'s, redirects, cookies and decoding included:\n#\n# ```\n# client = HTTPClient.new\n# client.get(\"http://127.0.0.1:8080/a\")\n# client.get(\"http://127.0.0.1:8080/b\") # the same connection\n# client.close\n# ```\n#\n# A connection goes back once its answer has been read to the end (inside\n# `open`, up to 64 KiB left unread is read past for it) and the answer\n# allows it; at most *idle* wait per server, and the rest close. A server\n# may close an idle connection whenever it likes (RFC 9112 §9.3.1), so a\n# request whose kept connection turns out closed - the write fails, or it\n# ends before a byte of an answer - is sent again once on a new one, when\n# its method is idempotent (RFC 9110 §9.2.2) and its body a string;\n# anything else answers the failure. One task at a time per request: two\n# tasks sending at once take two connections, and a client is not for\n# sharing across threads. An `https://` connection is kept under the\n# `HTTP.tls_config` it was made with, and handed only to a request made\n# under the same one (`TlsMadeUnder`): after `HTTP.tls_config=` the next\n# request makes a connection of its own, checked against the new roots.\n# A client made with *tls* uses that configuration instead of\n# `HTTP.tls_config`, and one made with *resolver* finds its hosts by it\n# instead of `Resolver.system` - an encrypted resolver, or a hosts text\n# that pins a name to an address: std/secure_dns's `DnsOverHttps` is a\n# client of its own with both, so its server is checked against the roots\n# it was given and reached at the address it was given.\n#\n# A client resumes TLS sessions (RFC 8446 §2.2): the tickets an\n# `https://` server sends are kept for its origin - scheme, host and\n# port, whichever proxy's tunnel they came through - under the TLS\n# configuration they came under, the client's own *tls* or else\n# `HTTP.tls_config` (`TlsMadeUnder`, as a kept connection is), and a new\n# connection to that origin offers the newest, each once.\n# A resumed handshake checks no certificate - the ticket stands for the\n# check the first one made - so a ticket is never offered to another\n# origin, nor under another configuration: after `HTTP.tls_config=`, or\n# roots, an identity, `now`, `min_version` or `require_staple` assigned\n# in place - in `HTTP.tls_config` or in the client's own *tls* - the next\n# connection is a full handshake under the new ones, and that origin's\n# tickets from under the old one are dropped. (std/tls's `TlsSession`\n# holds each ticket to its configuration too; this client drops them\n# where that one would only pass them over.) At most *origin_tickets* are\n# kept an origin - 4 by default, so that as many connections as *idle*\n# keeps by default can resume at once, where std/tls's server and\n# OpenSSL's send 2 a handshake - and *tickets* in all, 256 by default: as\n# many origins as that leaves room for at *origin_tickets* each, the one\n# longest unused dropped first. 0 for either keeps none, and nothing\n# resumes. `close`\n# leaves them. Nothing goes early (0-RTT), whatever a ticket allows: the\n# module's header says why. `connections_resumed`, `tickets_held` and\n# `Response#tls_resumed?` say what was kept and used. What it saves, by\n# bench/std_http_resume.iyi built --release on a 12-core machine other\n# jobs shared (medians of 200, the middle of three runs): the handshake\n# took 0.96 ms full and 0.51 resumed against std/http's server, 1.03 and\n# 0.57 against OpenSSL 3.5's s_server - and a GET on a new connection,\n# which waits about 40 ms at each iyi end that is not the handshake's,\n# 82.98 ms and 82.00, and 42.04 and 40.96.\n#\n# Over HTTP/2 - `https://` where ALPN chose `h2`, or `http://` with\n# *prior_knowledge* (h2c, RFC 9113 §3.3) - a kept connection carries the\n# next request on a new stream, until the server says GOAWAY.\n#\n# HTTPS records (RFC 9460), when `use_https_records` is set: before a new\n# connection to an `https://` origin - not through a proxy, and not for\n# an upgrade - the client asks its resolver for the origin's HTTPS\n# records (`Resolver#https_records`, AliasMode followed) and connects by\n# the first ServiceMode record it can use: one whose mandatory keys it\n# knows - the seven std/dns reads, port and no-default-alpn mandatory by\n# being there (section 9) - and whose protocols (alpn, and http/1.1\n# unless no-default-alpn) hold one of the configuration's `alpn`, or\n# http/1.1 where that is empty. Its target - the record's owner for \".\"\n# - and its port, or the origin's, are where the connection goes; its\n# `ech` is the connection's `ech_configs` (RFC 9848); and the protocols\n# it lists go first in the ClientHello's ALPN, which still offers every\n# one the configuration does (section 7.1.2: a record says which\n# protocols the endpoint has, and cannot take one away). The certificate\n# is checked, and the server named inside, for the origin's host,\n# whatever the target (section 2.3). An `ech` whose ECHConfigList holds\n# no config std/tls can use is passed over, as RFC 9849 section 6.1 has\n# it. The address hints are not read: the target's addresses are asked\n# for as any host's are.\n#\n# An endpoint that does not connect gives way to the next, then to the\n# last AliasMode target and to the origin itself (section 3) - unless\n# every endpoint carries `ech`, when the request fails rather than name\n# the server in the clear (RFC 9848 section 5.1). A TLS refusal is the\n# request's failure, but for one: a server that turned the encrypted\n# ClientHello down, its certificate checked for the config's\n# public_name, is asked once more on a new connection to the same\n# address, under the retry_configs it sent, or without ECH where it sent\n# none, which turns ECH off there (RFC 9849 section 6.1.6); a refusal of\n# that is the failure. No records (NXDOMAIN, NODATA), a set section 2.2\n# refuses, an AliasMode \".\" (the service says it is not there), or no\n# record the client can use: the connection is made as without records.\n# So is one whose lookup failed through a resolver that asks in the\n# clear, as section 3.1 lets a client choose; through encrypted\n# upstreams the failure is the request's, as section 3.1 asks, for\n# there the one who can drop the answer is an attacker taking the\n# records away. `localhost` and addresses are not asked about, and an\n# `http://` URL is not made `https://` by its records (section 9.5's\n# SHOULD): the program wrote the scheme.\n#\n# A record's `ech` and protocols make a configuration of their own - a\n# copy of the client's, made once and kept for the origin while the same\n# ones come under the same configuration - so that a kept connection and\n# a ticket are handed on only under the record they were made by\n# (`TlsMadeUnder` holds `ech_configs`). Off by default: every new\n# connection then waits on one DNS answer or more before its ClientHello\n# (RFC 9848 section 5.3 forbids sending it sooner), with no cache to\n# spare the next, and the records may move a connection to another host\n# and port, which is the program's to ask for.\npub class HTTPClient\n # Every idle connection closed. The client may be used after; it opens\n # new ones.\n def close : Nil\n # Connections this client has opened, and requests sent over one it\n # kept: what a program reads to see that its connections are kept.\n def connections_made : Int32\n # Connections this client opened whose TLS handshake resumed a session\n # by a ticket it kept.\n def connections_resumed : Int32\n def connections_reused : Int32\n def delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n protected def give_back(key : String, conn : Conn) : Nil\n def head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The configuration a connection to *origin* is made under by a record\n # whose `ech` is *ech* (nil: none) and whose protocols put *alpn* in its\n # order, *base* being the client's own or `HTTP.tls_config`: *base*\n # itself where those are its own, and otherwise a copy of it with them -\n # the same object every time while *base* is the same (`TlsMadeUnder`),\n # so that a connection kept and a ticket held under it serve the next\n # connection by the same record. It becomes the one the origin's kept\n # connections are taken under (`https_current`). The origin's\n # configurations from under another *base* go.\n protected def https_config(origin : String, base : TlsConfig, ech : Array(UInt8) | ::Nil, alpn : Array(String)) : TlsConfig\n # The configuration *origin*'s last connection by HTTPS records was made\n # under, while *base* is the one it came from - *base* itself where it\n # was made as without records: what a kept connection to the origin\n # must have been made under to be taken. Nil when none was made by them\n # under *base*, and then none is taken.\n protected def https_current(origin : String, base : TlsConfig) : TlsConfig | ::Nil\n # *timeout_ms* and *proxy* are `HTTP.open`'s, for every request the\n # client sends; *tls* and *resolver* are this client's own (above);\n # *origin_tickets* and *tickets* bound the TLS tickets kept, by origin\n # and in all.\n def initialize(idle : Int32 = 4, timeout_ms : Int32 = 0, prior_knowledge : Bool = false, proxy : String | ::Nil = nil, tls : TlsConfig | ::Nil = nil, resolver : Resolver | ::Nil = nil, origin_tickets : Int32 = 4, tickets : Int32 = 256)\n protected def made : Nil\n def open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n def post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # `http://` spoken as HTTP/2 from the first byte, without asking: for a\n # server known to take it.\n def prior_knowledge : Bool\n # The proxy every request goes through, `HTTP.open`'s *proxy*; its\n # connections are kept under it.\n def proxy : String | ::Nil\n def put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true) : Response | Cancelled | SocketError | HTTPError\n # What turns a host name into addresses for this client's connections,\n # instead of `Resolver.system`: encrypted upstreams, or a hosts text\n # alone that pins a name to an address, as std/secure_dns's\n # `DnsOverHttps` pins its server's.\n def resolver : Resolver | ::Nil\n protected def resumed : Nil\n # The session a new connection to *origin* under *tls* offers a ticket\n # from and keeps the server's new ones in; nil when the client keeps\n # none. The origin's tickets from under another configuration are\n # dropped, not left to take room that `TlsSession` would never offer\n # them from. The session handed out is the newest used; past the room\n # *tickets* leaves, the oldest goes.\n protected def session_for(origin : String, tls : TlsConfig) : TlsSession | ::Nil\n # A kept connection to *key*, nil when none waits. An HTTP/2 connection\n # that can take no more requests is closed rather than handed out, and\n # so is an `https://` one made under another TLS configuration than\n # *tls*, the one the request is made under (`TlsMadeUnder`): keyed by\n # scheme, host and port alone, a connection whose server was checked\n # against one set of roots carried requests after `HTTP.tls_config=`\n # named others that would have refused it.\n protected def take(key : String, tls : TlsConfig | ::Nil = nil) : Conn | ::Nil\n # The TLS tickets held for *origin* - `https://host:port`, the host in\n # lower case and the port written even where it is 443 - or for every\n # origin when nil.\n def tickets_held(origin : String | ::Nil = nil) : Int32\n # What this client's `https://` requests are checked against and offer,\n # instead of `HTTP.tls_config`: roots of its own, or ALPN's `h2` first.\n def tls : TlsConfig | ::Nil\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records : Bool\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records=(value : Bool) : Bool\nend\n\n# What the other end did that HTTP does not allow, as a value: `reason` is\n# the sentence, `status` the answer a server gives a request that did it\n# (400, or 413 and 431 for the limits). `message` says it is HTTP's.\npub struct HTTPError\n def initialize(reason : String, status : Int32 = 400)\n def reason : String\n def status : Int32\n def to_s : String\nend\n\n# One endpoint an origin's HTTPS records name, as `HTTP.https_endpoints`\n# reads it: the host and port to connect to, the ECHConfigList to encrypt\n# the ClientHello under (nil: none), and the protocols to offer by ALPN,\n# in order.\npub struct HttpsEndpoint\n def alpn : Array(String)\n def ech : Array(UInt8) | ::Nil\n def host : String\n def initialize(host : String, port : Int32, ech : Array(UInt8) | ::Nil, alpn : Array(String))\n def port : Int32\nend\n\n# A `multipart/form-data` body (RFC 7578) read a part at a time, as it\n# arrives: `next_part` answers the next `Part`, nil after the last, and a\n# part's `read` answers its bytes up to the boundary, \"\" at its end. A part\n# not read to its end is read past by the next `next_part`. The body of a\n# request is `Request#multipart`'s; any `BodyReader` and boundary will do.\n# A part's head is refused past 16 KiB, and a body that ends before its\n# closing boundary is an `HTTPError`.\npub class Multipart\n # The boundary a `Content-Type` of `multipart/...` names, nil when it\n # names none - or one of a length RFC 2046 §5.1.1 does not allow, 1 to\n # 70 characters, which `new` refuses with a panic. The type is the\n # peer's: `Request#multipart` handed a client's 90-character boundary\n # through to that panic (found by bench/std_fuzz_exercise.sh).\n def self.boundary(content_type : String) : String | ::Nil\n # Where *pattern* is in what is held, -1 when it is not there yet.\n protected def find(pattern : String) : Int32\n def initialize(body : BodyReader, boundary : String)\n # The next part, nil after the last.\n def next_part : Part | Nil | Cancelled | SocketError | HTTPError\n # Up to *max* bytes of the part being read, \"\" at its boundary. What is\n # held back is what may yet be the start of the boundary.\n protected def part_read(max : Int32) : String | Cancelled | SocketError | HTTPError\n # One more piece of the body onto what is held: false at its end.\n protected def pull : Bool | Cancelled | SocketError | HTTPError\nend\n\n# A multipart/form-data body to send: fields and files in order. Its\n# boundary is chosen when the body is first asked for - `finish`,\n# `content_type` or `boundary`, whichever comes first, after which no\n# part is added - as `iyi-` and 32 hex digits of the parts' SHA-1, made\n# again from itself in the rare case a part holds it: so it is none of\n# the parts' bytes by construction rather than by chance, the same\n# fields make the same body, and std/http reaches no source of\n# randomness for it. A boundary given to `new` is used as it is, and a\n# part that holds it is refused.\npub class MultipartBuilder\n def boundary : String\n def content_type : String\n def field(name : String, value : String) : self\n def file(name : String, filename : String, data : String, content_type : String = MIME.from_extension(filename)) : self\n # The body, closing boundary and all.\n def finish : String\n # The boundary ends a part where it follows a CRLF, the part's first\n # bytes included, which follow the head's blank line.\n protected def self.holds?(data : String, mark : String) : Bool\n def initialize(boundary : String | ::Nil = nil)\n # A name inside quotes, as browsers write one: a quote and a line break\n # percent-encoded (the WHATWG form encoding), so no field can end the\n # parameter or the line early.\n protected def self.quoted(text : String) : String\nend\n\n# One part of a multipart body: its head's fields, its name and file name\n# from `Content-Disposition`, and its bytes, read up to its boundary.\npub class Part\n def content_type : String | ::Nil\n # The file name a browser sent the part under, `filename=`.\n def filename : String | ::Nil\n def finished? : Bool\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n def initialize(owner : Multipart, headers : Hash(String, String))\n # The form field's name, `name=` of `Content-Disposition`.\n def name : String | ::Nil\n # The part's next bytes, at most *max*; \"\" at its end.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the part as one string, refused past *limit* bytes.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\nend\n\n# What a server's block is handed: one request. Its body is `body`, read\n# whole before the block ran, or under `stream_bodies` read by the block\n# from `body_io` as it arrives; `body` then reads the rest itself, and a\n# body that cannot be read - the client went, or sent one that is not\n# HTTP - ends the connection with that sentence rather than be answered as\n# if it were whole.\npub class Request\n def body : String\n def body_io : BodyReader\n # The pairs of the request's `Cookie` header, the first of a name kept.\n def cookies : Hash(String, String)\n # The fields of a form: the query's, and an\n # `application/x-www-form-urlencoded` body's after them. The body is\n # read for it.\n def form : Params\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n # A request whose body is in hand: what `parse_request` answers.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), body : String, version : String = \"HTTP/1.1\", remote_address : String | ::Nil = nil)\n # A request whose body is still on the connection.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), version : String, reader : BodyReader, remote_address : String | ::Nil = nil)\n def method : String\n # The body as `multipart/form-data` parts, read as they arrive; nil when\n # the `Content-Type` is not multipart or names no boundary\n # (`Multipart.boundary`).\n def multipart : Multipart | ::Nil\n def path : String\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates : Array(Certificate)\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates=(value : Array(Certificate)) : Array(Certificate)\n def query : String | ::Nil\n # The client's address, as the server's socket saw it (no port): nil\n # for a request a program made, or when the client had already gone. A\n # proxy in front is what this names; `X-Forwarded-For` is the proxy's\n # word, and a header any client can write.\n def remote_address : String | ::Nil\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the TLS connection the request came over resumed a session by\n # a ticket, under `serve(tls:)`: its `peer_certificates` are then the\n # ticket's. False in the clear.\n def tls_resumed? : Bool\n def version : String\nend\n\n# A response: what a server's block answers, and what a client is handed.\n#\n# To write one: `Response.new(200, \"hello\")`, a status and a string;\n# `Response.new(200, io)`, a stream the server copies out and closes, with\n# the caller's `Content-Length` when it gives one and chunked otherwise;\n# `Response.stream(200) { |w| w.write(\"...\") }`, whose block writes as it\n# goes; `Response.upgrade(headers) { |socket, leftover| ... }`, a 101 after\n# which the connection is the block's. `add_header` writes a field on a\n# line of its own, as each `Set-Cookie` must be.\n#\n# What a client is handed: the status, reason and headers, and the body -\n# `body` whole, or `body_io` as it arrives inside `HTTP.open`'s block.\npub class Response\n # One more line of the field *name*, written on a line of its own: what\n # two `Set-Cookie`s need, since the \", \" a field's lines are joined with\n # is part of a cookie's date.\n def add_header(name : String, value : String) : Nil\n # The lines of each field written on more than one, by its name in\n # `headers`; nil when every field is on one line.\n def apart : Hash(String, Array(String)) | ::Nil\n # The body: whole. Inside `HTTP.open`'s block it is read here, the first\n # time it is asked for, and a body that cannot be read panics with why;\n # `body_io` answers that as a value.\n def body : String\n # The body as it arrives. For a response a program made, its string.\n def body_io : BodyReader\n # Whether the client undid the body's `gzip`, `deflate` or `zstd`\n # coding; the `Content-Encoding` and `Content-Length` that described it\n # are gone from the headers then.\n def decoded? : Bool\n # A header by name, as the server spelled it or otherwise: a field\n # name is case-insensitive (RFC 9110 §5.1), and `content-length` is as\n # common on the wire as `Content-Length`.\n def header(name : String) : String | ::Nil\n # A field's lines apart, in the order they came: `header` joins them\n # with \", \", and `Set-Cookie` is the field that join breaks (RFC 9110\n # §5.3), since a cookie's `Expires` date has a comma in it - two cookies\n # read as \"a=1; Expires=Wed, 21 Oct 2026 07:28:00 GMT, b=2\", which no\n # split takes back apart. Empty for a field that is not there.\n def header_values(name : String) : Array(String)\n def headers : Hash(String, String)\n # A response a client read: its body comes from *reader*, and *url* is\n # where it came from.\n def initialize(status : Int32, reason : String, headers : Hash(String, String), apart : Hash(String, Array(String)) | ::Nil, reader : BodyReader, url : String, decoded : Bool)\n def initialize(status : Int32, reason : String, headers : Hash(String, String), body : String, apart : Hash(String, Array(String)) | ::Nil = nil)\n # A response to write: the reason is the status's own, the headers the\n # caller's. `Response.new(200, \"hello\")` is the common shape.\n def initialize(status : Int32, body : String = \"\", headers : Hash(String, String) = {} of String => String)\n # A response whose body is *source*, copied out by the server and\n # closed after.\n def initialize(status : Int32, source : IyiIO, headers : Hash(String, String) = {} of String => String)\n # The body read whole into the response, which is answered; why not,\n # when it cannot be.\n def load : Response | Cancelled | SocketError | HTTPError\n def reason : String\n def source : IyiIO | ::Nil\n def status : Int32\n # A response whose body the block writes as it goes, chunked.\n def self.stream(status : Int32, headers : Hash(String, String) = {} of String => String, &writer : (BodyWriter -> Nil)) : Response\n def success? : Bool\n def takeover : Proc(IyiSocket | TlsStream, String, Nil) | ::Nil\n protected def takeover=(takeover : Proc(IyiSocket | TlsStream, String, Nil)) : Proc(IyiSocket | TlsStream, String, Nil)\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the `https://` connection the answer came over resumed a TLS\n # session by a ticket an `HTTPClient` kept, rather than a full\n # handshake; false in the clear and for one a program made.\n def tls_resumed? : Bool\n # A 101 Switching Protocols with *headers* (`Upgrade` and the\n # protocol's own), after which the connection is the block's: it is\n # handed the connection - the socket, or under `serve(tls:)` the\n # `TlsStream` over it - and the bytes the client sent after its request,\n # and the connection closes when it returns.\n def self.upgrade(headers : Hash(String, String), &takeover : (IyiSocket | TlsStream, String -> Nil)) : Response\n # The URL the answer came from, after any redirects; nil for one a\n # program made.\n def url : String | ::Nil\n def writer : Proc(BodyWriter, Nil) | ::Nil\n protected def writer=(writer : Proc(BodyWriter, Nil)) : Proc(BodyWriter, Nil)\nend\n\npub struct Server\n # The headers a coded body goes with: the coding, and `Vary` so that a\n # cache keeps the answers apart. A `Content-Length` the block gave is\n # the plain body's, and goes.\n protected def self.coded(response : Response, coding : String) : Response\n # The coding *response* goes in to the client of *req*, or nil: one the\n # client takes (RFC 9110 §12.5.3, a `q` of 0 refusing it), for an\n # answer with a body and a type worth compressing, and no coding of its\n # own. A whole body goes `zstd` when the client's `q` for it is at least\n # its `q` for gzip, and `gzip` otherwise. The tie goes to zstd for the\n # server's time: measured on this machine, release builds, on\n # src/std/http.iyi (199,134 bytes) `Gzip.compress` wrote 54,484 bytes in\n # 5 ms and `Zstd.compress` 61,753 in 1 ms, and on CHANGELOG.md (1.5 MB)\n # 552,446 in 58 ms against 589,785 in 13 ms - a quarter of the time, for\n # an eighth more bytes at most. A stream - an `IyiIO` or a\n # `Response.stream` - goes gzip or as it is: std/compress's `Deflater`\n # writes each piece as it comes, where std/zstd's compressor takes its\n # input whole, and a frame a write would code each piece alone.\n def self.coding_for(req : Request, response : Response) : String | ::Nil\n # A string body compressed whole in *coding*, `zstd` or `gzip`, with\n # the headers that say so. `Zstd.compress`'s frame needs a window of\n # 1 MiB at most, inside the 8 MB RFC 9659 §3 holds an encoder to.\n protected def self.compressed(response : Response, coding : String) : Response\n # A response's head. `Content-Length` is *length*'s - the caller's own\n # line is checked against it, and kept only where no body follows it: a\n # HEAD, or a 304 that names the length a 200 would have (RFC 9110 §8.6) -\n # or `Transfer-Encoding: chunked` when *chunked*, or neither for a body\n # the connection's close ends. `Connection` says what the connection will\n # do next, except on an *upgrade*, whose own line says it. A 1xx, 204 or\n # 304 has no content (RFC 9110 §15), and writes neither a body nor a\n # length of its own (§8.6): the body was read by the client as the start\n # of the next answer, and `Content-Length: 0` on a 304 told a cache that\n # the resource had become empty. A field added on lines of its own\n # (`add_header`) is written a line each.\n def self.format_head(response : Response, head : Bool, close : Bool, length : Int64 | ::Nil, chunked : Bool, upgrade : Bool = false) : String\n # The response as it goes on the wire, whole: `format_head` for its\n # string body, and the body.\n def self.format_response(response : Response, head : Bool, close : Bool) : String\n # One connection: requests until the client is done. A request that is\n # not HTTP is answered with why and the connection closed, since the\n # bytes after it cannot be trusted to be a request either. Requests that\n # came in one read are read where they lie, one after another. A\n # connection that opens with HTTP/2's preface is served as HTTP/2. Under\n # *tls* the handshake comes first, and the connection ends with\n # close_notify. A connection this side wrote last on - an answer, a\n # refusal, a GOAWAY - closes by lingering (`Conn#close`), so the reset\n # a close with the client's bytes unread is cannot take what was\n # written from the client before it reads it. One that timed out or was\n # cancelled, with nothing of this side's waiting to be read, closes at\n # once, and so does one whose client said `close` and sent nothing past\n # its request (`serve_h1`).\n def self.handle(client : IyiSocket, handler : Proc(Request, Response), listener : IyiSocket | ServeStop | Nil = nil, idle : Hash(UInt64, Conn) | ::Nil = nil, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 0, tls : TlsServerConfig | TlsFrozenServerConfig | Nil = nil, request_timeout_ms : Int32 = 0) : Nil\n def initialize\n # A request's head - the text before its blank line - read and checked,\n # or why it is not one.\n def self.parse_head(text : String) : RequestHead | HTTPError\n # A request at *from* in `text`, if a whole one is there. No request and\n # no error means \"not yet\". `consumed` counts from the start of `text`.\n def self.parse_request(text : String, from : Int32 = 0) : ParsedRequest\n # Serve `listener` until it is closed, on *workers* threads: each\n # connection is a task on the pool `spawn_parallel` starts (SPEC.md\n # III.4.13), started by whichever worker is free and served there to\n # its end, while the thread that called this accepts. The block runs on\n # the connection's worker, once per request, on several workers at\n # once; what it answers is written back.\n # The close ends a connection waiting on its client too, for its next\n # request or for the rest of one, and one that is answering ends when its\n # answer is written: a keep-alive client that sat idle kept `serve` from\n # returning for as long as it stayed, and so did one that had sent\n # what has arrived of a request so far - one byte of it was enough.\n #\n # *stream_bodies*: the block reads each request's body from `body_io`\n # rather than being handed it whole. *compress*: an answer whose type\n # std/mime calls compressible goes zstd or gzip to a client that\n # accepts one (`coding_for`).\n #\n # *timeout_ms*: a request's head must have arrived within it, counted\n # from when the server starts waiting for one - a kept-alive connection's\n # idle time included - and no read of a body nor write of an answer may\n # wait longer; a connection that does is closed. Without it, a client\n # that sends a byte of its head every few seconds (Slowloris) holds a\n # connection and its fiber for as long as it likes, and so does one that\n # reads none of its answer. A minute by default, nginx's\n # `client_header_timeout`; 0 for none. A connection handed over by\n # `Response.upgrade` waits as its protocol likes.\n #\n # *request_timeout_ms*: the whole request - head and body - must have\n # arrived within it, counted from the same moment as *timeout_ms*, as\n # Go's `ReadTimeout` beside its `ReadHeaderTimeout`; the head is held to\n # the sooner of the two. Without it a body sent a byte every few seconds\n # was bounded per read alone, and held its connection for as long as it\n # dripped. A block that reads the body itself (*stream_bodies*) reads\n # under it until the block returns, and what it leaves unread is read\n # past under it too. Over HTTP/2 the wait for each request and its body\n # is held to it on the connection the same way, the head to the sooner\n # of the two. Five minutes by default, which a 64 MiB body (`MAX_BODY`)\n # meets at 224 KB/s; a server taking large uploads over slow links\n # raises it. 0 for none. A connection past it is closed, unanswered, as\n # one past *timeout_ms* is.\n #\n # *max_connections*: at most this many connections are served at once;\n # 1024 by default, 0 for no limit. Past it the server stops accepting\n # until one ends, so the next connection waits in the kernel's listen\n # queue - queued, not refused, as Go's `netutil.LimitListener` does - and\n # is served in its turn; a client that gives up waiting goes unserved.\n # Each served connection holds its place until it closes, a kept-alive\n # one between requests too, which *timeout_ms* bounds. The process's\n # own limit on descriptors may come first.\n #\n # *tls*: each connection is TLS (std/tls's `TlsServer`, 1.3 or 1.2) under this\n # configuration, its handshake held to *timeout_ms* too and run on the\n # worker that serves it; one whose handshake fails is closed, the\n # client told why by an alert. Inside it a connection that opens with\n # HTTP/2's preface - as a client whose ALPN chose `h2` does, when\n # `tls.alpn` offers it - is served HTTP/2, and any other HTTP/1.1. A\n # `Response.upgrade` block over TLS is handed the `TlsStream`, so a\n # WebSocket is `wss://`. On more than one worker the config is a\n # `TlsFrozenServerConfig` - `config.frozen` - since every worker reads\n # it at once: its ticket keys are one set behind a lock, so a ticket a\n # connection on one worker was sent resumes on any other, and an\n # identity source given to `frozen` is a block the compiler checked. A\n # `TlsServerConfig` itself is refused at the call (\"Server.serve hands\n # `tls : TlsServerConfig` to other threads, several at once, and it is\n # not Share: ... Pass `config.frozen` ...\"), not frozen here: `frozen`\n # is a copy, so a property assigned after the server started would be\n # served by no worker and nothing would say so, and a config with an\n # `identity_source` cannot be frozen without its source given again as\n # a block the compiler checks. Under `workers: 1` either is served.\n #\n # *workers*: the pool's size, `IyiWorkers.count` by default - `IYI_WORKERS`\n # from the environment, else the cores this process may run on. The\n # pool is the process's one, and it does not grow or shrink once it has\n # started, so a count other than the one it started with panics. Every\n # worker runs the block at once, so the compiler checks it where it is\n # written as it checks a parallel task's (`@[Share]`, SPEC.md III.4.4):\n # every value it captures must be `Share` - a table behind a `Mutex`\n # (std/mutex), a count in an `Atomic`, a `SharedRouter` (std/router) -\n # every captured local assigned once, and the constants and class\n # variables it names `Share`. The refusal names the variable, why its\n # type is not `Share`, and the two ways out: the lock, or `workers: 1`.\n # `workers: 1`, written as the literal, serves on the calling thread\n # alone - each connection a task beside the accept loop, as `spawn`\n # makes one - and its block is not checked: a block that calls a\n # `Router`, appends to an `Array` or keeps a handle that is not `Share`\n # is served there. A count in a variable is checked whatever it holds,\n # since the compiler cannot know it is 1. A count of 1 from the default\n # (`IYI_WORKERS=1`, one core) serves on the calling thread too.\n #\n # A connection stays on the worker that started it (III.4.13, part 1):\n # connections spread over the workers free as they arrive, and one kept\n # alive is not moved later. A block that computes without parking holds\n # its worker meanwhile, as any task does. The listener is the calling\n # thread's, and a close of it is made there: a block that stops the\n # server sends on a `Channel` a task beside the server receives from,\n # and that task closes it. At the stop each connection waiting on its\n # client is closed on its own worker, sent close_notify there first\n # under *tls*.\n def self.serve(listener : IyiSocket, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count, &handler : (Request -> Response)) : Nil forall C\n # `serve` with a value for its handler rather than a block: a\n # `SharedProc`, a `SharedRouter` (std/router), or one of the program's\n # own, whose `call(request : Request) : Response` answers each request,\n # as Go's `http.Handler` does. On more than one worker it must be\n # `Share` (SPEC.md III.4.4), since every connection on every worker\n # calls the one value at once: the compiler refuses one that is not at\n # the argument, naming the field that failed and the two ways out - a\n # count in an `Atomic`, a pool or a cache behind a `Mutex`, a\n # `SharedRouter` for a `Router`, or `workers: 1`, under which any value\n # with that `call` is served on the calling thread, a `Router`\n # included. What the check does not see is the edge `spawn_parallel`\n # has too: a method the handler calls that writes a module's state. A\n # struct handler is copied into each connection's task, and an `Atomic`\n # in it with it: what the connections share is a class, or held by\n # one.\n #\n # The rest is the block form's: *stream_bodies*, *compress*,\n # *timeout_ms*, *tls* (`config.frozen` on more than one worker),\n # *request_timeout_ms*, *max_connections* and *workers*, HTTP/2 by its\n # preface, `Response.upgrade`, and a close of *listener* - on the\n # thread that called this - ending every connection waiting on its\n # client and returning once the others have answered.\n def self.serve(listener : IyiSocket, handler : H, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count) : Nil forall H, C\n protected def self.writer_for(socket : IyiSocket | TlsStream, chunked : Bool, gzip : Bool) : BodyWriter\nend\n\n# A TLS connection used as a socket is: read into a buffer, written to,\n# closed, its timeouts set - by a `Conn`, a `BodyWriter`, and a connection\n# handed over by an upgrade (`wss://` at either end). What TLS refuses is a\n# `SocketError` carrying TLS's own sentence (`layer_reason`), so answers\n# keep the types they have over a plain socket.\npub class TlsStream\n # The socket closed at once, nothing sent or read first.\n def abort : Nil\n # close_notify, the sending half shut, and what the peer still sends\n # read past for a bounded time before the socket closes\n # (`TlsConnection#close`).\n def close : Nil\n # close_notify and the sending half shut; reads go on until the peer's\n # own (`TlsConnection#close_write`).\n def close_write : SocketError | ::Nil\n def closed? : Bool\n def initialize(tls : TlsConnection, host : String)\n # The peer's certificate chain, leaf first, as the handshake checked it:\n # the server's, at a client; the client's, at a server that asked.\n def peer_certificates : Array(Certificate)\n # Up to *count* bytes into *buffer*: 0 once the peer closed with\n # close_notify.\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n # Up to *max_bytes* as a string, what is held first: \"\" once the peer\n # closed with close_notify. `IyiSocket#read`'s form, for a block an\n # upgrade hands either.\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # How long a read or a write of the connection under it may wait.\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n # Whether the handshake resumed a session (`TlsConnection#resumed?`).\n def resumed? : Bool\n def write(text : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\n# What `HTTP.upgrade` answers: the 101, and the connection, which is the\n# caller's now - the socket, and the bytes the server sent after its head.\npub class Upgraded\n def initialize(response : Response, socket : IyiSocket | TlsStream, leftover : String)\n def leftover : String\n def response : Response\n # The connection: the socket, or for `https://` the `TlsStream` over it.\n def socket : IyiSocket | TlsStream\nend\n\nimpl Error for HTTPError\n def message : String\nend\n" + "sourceSha256": "c7ddc59f99e543868102459ccd62385e941d6ac8e85de415eb34e30e57f71dcd", + "surface": "module std/http\n\n# A body as it arrives: from a connection, framed by its length, by chunks\n# or by the connection's close, or from a string already in hand. `read`\n# answers the next piece, \"\" once the body is done; what the other end\n# does wrong is an `HTTPError`, and a connection that fails or is\n# cancelled answers that. The first failure is kept: every read after it\n# answers the same. A body coded `gzip`, `deflate` or `zstd` is decoded\n# as it is read when the client asked for that.\n#\n# A body has a limit: past it a read answers an `HTTPError` with status\n# 413. The server's is `Server::MAX_BODY`; a client's is none.\npub class BodyReader\n # The body's coding undone from here on: `Envelope::Gzip` for gzip,\n # `Envelope::Zlib` for HTTP's deflate - which some servers send as the\n # bare stream instead, without zlib's two bytes of header, so the first\n # two bytes are looked at first, and a pair that is not a zlib header\n # (RFC 1950 §2.2: method 8, a multiple of 31) is read as the bare\n # stream. A body that does not decode is an `HTTPError` naming the\n # coding and why.\n def decode(envelope : Envelope) : Nil\n # The body's `zstd` coding (RFC 8878) undone from here on, by std/zstd's\n # `ZstdReader` as the pieces arrive, frames one after another. A frame\n # that asks for a window past *max_window* bytes is refused rather than\n # given the memory (a window outside 1 KiB to 512 MiB is a panic), and a\n # body that does not decode is an `HTTPError` naming the coding and why.\n def decode_zstd(max_window : Int32 = Zstd::WINDOW_LIMIT) : Nil\n # Reads past the rest of the body, *limit* bytes at most: whether it\n # ended within them. A server reads past what a block left unread, so\n # the next request on the connection starts where it should.\n def drain(limit : Int64) : Bool\n # Whether every byte of the body has been read, the framing's end too.\n def finished? : Bool\n # A body that arrives as an HTTP/2 stream's DATA: refused past *limit*.\n protected def self.frames(h2 : H2Connection, id : Int32, limit : Int64) : BodyReader\n def initialize(conn : Conn | ::Nil, kind : Int32, length : Int64, limit : Int64, text : String = \"\", hello : Proc(Bool) | ::Nil = nil)\n # Whether a server reading past the rest of this body, so that the next\n # request starts where it should, would close instead: its length says\n # more than *limit* bytes are left, or its client still waits for the\n # `100 Continue` its `Expect` asked for - which, written after the\n # answer, the client reads as its next request's, and the body it\n # holds back is then waited for. A chunked body's rest is not known\n # until it is read.\n def left_past?(limit : Int64) : Bool\n protected def on_frames(h2 : H2Connection, id : Int32) : Nil\n # The body's bytes as the framing delivers them, before any decoding.\n protected def raw(max : Int32) : String | Cancelled | SocketError | HTTPError\n # The next piece of the body, at most *max* bytes; \"\" when it is done.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the body as one string, refused past *limit* bytes (413).\n # The pieces are kept as they arrive and joined once: a builder of the\n # length a head declared was made before any of it had come, and a head\n # that declared 64 MB and sent two bytes cost the server 64 MB.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\n protected def remember(fault : HTTPError | Cancelled | SocketError) : HTTPError | Cancelled | SocketError\n # A body that is a string already in hand.\n def self.text(text : String) : BodyReader\n # Whether the body ends where the connection does: nothing can follow\n # it on the connection.\n def to_close? : Bool\nend\n\n# What a streamed body is written to: an answer's `Response.stream`\n# block's, and a client's upload from an `IyiIO`. Each `write` goes out as\n# one chunk (RFC 9112 §7.1), or as it is where the connection's close ends\n# the body, so a block that writes a line a second is read a line a\n# second. Under a server's `compress`, each write is a gzip piece flushed\n# whole (std/compress's `Deflater`), which decodes as it arrives too. The\n# first failure is kept, and every write after it answers it; `finish`,\n# which the server calls when the block returns, writes the end.\npub class BodyWriter\n # The end of the body: the gzip trailer, and the last chunk with the\n # blank line after it. Once; the server's call after a block that\n # finished its own body writes nothing.\n def finish : Nil | Cancelled | SocketError\n def initialize(socket : IyiSocket | TlsStream | H2Out, chunked : Bool, deflater : Deflater | ::Nil = nil)\n def write(text : String) : Nil | Cancelled | SocketError\n # Bytes of the body written so far, before any framing or coding.\n def written : Int64\nend\n\n# What `HTTP.request`'s `jar:` keeps cookies in: the `Cookie` header a\n# request to a URL carries, and an answer's `Set-Cookie` lines taken in.\n# std/cookie's `CookieJar` is one; std/http itself reads no clock, so the\n# expiry of a cookie is the store's to keep.\npub trait CookieStore\n abstract def header_for(url : String) : String | ::Nil\n abstract def update(url : String, response : Response) : Nil\nend\n\npub struct HTTP\n # For a request target in absolute form (RFC 9112 §3.2.2), where its\n # authority starts: after `http://` or `https://`, in any case.\n def self.absolute_form(target : String) : Int32 | ::Nil\n # Whether a list of codings a client accepts (`Accept-Encoding`, RFC\n # 9110 §12.5.3) takes *coding*: named, or `*`, with a `q` above 0.\n def self.accepts?(list : String, coding : String) : Bool\n # A 1xx, 204 or 304: an answer with no content (RFC 9110 §15).\n def self.bodiless?(status : Int32) : Bool\n # Where a chunked body that starts at *from* ends in `text` — the byte\n # after the last chunk's terminating CRLF (trailers included) — nil when\n # it has not all arrived, or why it is not a chunked body. What\n # `decode_chunked` will then read whole.\n def self.chunked_end(text : String, from : Int32 = 0) : Int32 | String | Nil\n # How much a list of codings a client accepts wants *coding*, as its\n # `q` in thousandths: the weight it is named with, else `*`'s, else 0 -\n # and 0 refuses it.\n def self.coding_weight(list : String, coding : String) : Int32\n # A `Content-Length` as a number, or nil when it is not one. Repeated\n # lines arrive joined, and are one length only when every one says the\n # same (RFC 9112 §6.3). A length past 2147483647 is still a length, too\n # large for either side, and is read as one past it: `to_i?` answered\n # nil for it, and 3000000000 was refused as \"not a number\".\n def self.content_length(value : String) : Int64 | ::Nil\n # A control character other than a tab, or DEL: what a field value may\n # not hold (RFC 9110 §5.5), nor a request target or a reason phrase.\n def self.control?(text : String) : Bool\n # The pairs of a request's `Cookie` header, `a=1; b=2` (RFC 6265 §4.2),\n # a value's quotes taken off; the first of a name is kept, as a server\n # reads the most specific cookie first.\n def self.cookie_pairs(value : String) : Hash(String, String)\n # A chunked body (RFC 9112 §7.1) as one string: each chunk's size line\n # is hexadecimal, extensions after `;` are ignored, and a zero-length\n # chunk ends it. What follows the last chunk is trailers, which nothing\n # here reads.\n def self.decode_chunked(text : String) : String\n def self.delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def self.find_headers_end(text : String, from : Int32 = 0) : Int32 | ::Nil\n # The request as it goes on the wire, whole. `Host`, `Connection` and\n # `Content-Length` are the request's own lines; a caller's header of\n # that name is refused rather than sent twice. A body's length is\n # always written for the methods a body belongs to, so `POST` with\n # nothing to say still says `Content-Length: 0`. A byte of the path past\n # 127 goes as `%` and two hex digits: a request target is ASCII (RFC\n # 9112 §3.2, RFC 3986 §2.1), and `/ü` went out as its UTF-8 bytes, raw.\n def self.format_request(method : String, path : String, host : String, body : String = \"\", headers : Hash(String, String) = {} of String => String) : String\n def self.get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # *text* in gzip, whole: an answer's body under a server's `compress`.\n def self.gzip(text : String) : String\n # A writer whose body goes out in gzip as it is written.\n def self.gzip_writer(socket : IyiSocket | TlsStream | H2Out, chunked : Bool) : BodyWriter\n def self.has_break?(text : String) : Bool\n # Whether a list of tokens (RFC 9110 §5.6.1), its repeated lines joined\n # with \", \", holds *token*, in any case.\n def self.has_token?(list : String, token : String) : Bool\n def self.head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The value of a chunk size's hex digits, nil for anything else or for\n # one past what an Int32 holds.\n def self.hex(text : String) : Int32 | ::Nil\n # *n* in lowercase hexadecimal: a chunk's size line.\n def self.hex_text(n : Int64) : String\n # A Host value - the `a` of `Host: a` - or an absolute-form target's\n # authority, made of what a host and its port are spelled with (RFC 3986\n # §3.2.2, §3.2.3): a name, an IPv4 address or an IPv6 one in brackets,\n # and digits after a colon. Empty is one too, for a target with no\n # authority (RFC 9112 §3.2). A blank, a `/`, an `@` - the bytes another\n # host or a path begins with - are not.\n def self.host?(text : String) : Bool\n # The endpoints *answer*'s ServiceMode records name for a client whose\n # configuration offers *alpn* by ALPN (none: it speaks http/1.1 alone),\n # to an origin on *port*, in the order to try them: a record whose\n # mandatory keys are not all ones std/dns reads, or whose protocols -\n # alpn, and http/1.1 unless no-default-alpn - hold none of the client's,\n # names none (RFC 9460 sections 7.1.2 and 8), and neither does one whose\n # target is not a host name. Each is the record's target, or its owner\n # for \".\"; its port, or *port*; its `ech` where the ECHConfigList holds\n # a config std/tls can use (RFC 9849 section 6.1), nil otherwise; and\n # *alpn* with the protocols the record's alpn lists first, in *alpn*'s\n # order otherwise.\n def self.https_endpoints(answer : SvcbAnswer, alpn : Array(String), port : Int32) : Array(HttpsEndpoint)\n def initialize\n # One request, and the block's answer to the response, its body still\n # arriving: `response.body_io.read` takes it a piece at a time, and\n # `response.body` all of it. The connection closes when the block\n # returns. A body of *body* is a string, sent with its length, or an\n # `IyiIO`, sent chunked as it is read - which a redirect that keeps the\n # method (307, 308) cannot send again, so that answer is handed to the\n # block as it is.\n #\n # The redirects followed are RFC 9110 §15.4's: 301 and 302 make a POST a\n # GET, 303 makes anything but a HEAD a GET, and 307 and 308 keep the\n # method and the body. `Authorization`, `Proxy-Authorization` and\n # `Cookie` written by the caller are not sent to another scheme, host or\n # port - `https://` to `http://` at one host included, as curl does (Go\n # strips them for another host) - and a jar's `Secure` cookies go over\n # `https://` alone. The downgrade itself is followed, as Go's client and\n # `curl -L` follow it; any other header the caller wrote, and a 307 or\n # 308's body, go on in the clear. One more than *redirects* is an\n # `HTTPError`; with 0 the first answer is the block's.\n #\n # *timeout_ms* bounds the connect, and each read and write after it -\n # a pause, not the whole exchange, as Python's `timeout` is: a body that\n # keeps arriving is read for as long as it takes. One that runs out is a\n # `SocketError` whose `timed_out?` is true. 0, the default, waits as\n # long as the server does, as Go's and Python's clients do; a client\n # that talks to servers it does not control should give one.\n #\n # *proxy* is a proxy's URL - `http://user:password@host:port`, or\n # `host:port` alone - and every request goes through it, every redirect\n # too: `http://` as an absolute-form request to the proxy (RFC 9112\n # §3.2.2), which carries `Proxy-Authorization: Basic` when the URL has\n # a user; `https://` through a tunnel the proxy opens with `CONNECT\n # host:port` (RFC 9110 §9.3.6), the credential on the CONNECT alone and\n # TLS to the server inside it, its certificate checked as without a\n # proxy. A caller's own `Proxy-Authorization` is the proxy's too: sent\n # on the CONNECT when the URL has no user, and never inside the tunnel.\n # A CONNECT answered with anything but a 2xx - 407 when the proxy wants\n # credentials, 403, 502 - is an `HTTPError` naming the proxy and its\n # answer. `http://` through a proxy is HTTP/1.1 whatever\n # *prior_knowledge* says. Which proxy, if any, a URL's environment\n # names is `proxy_for`'s, and std/http_proxy's `HTTPProxy.from_env`.\n def self.open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, client : HTTPClient | ::Nil = nil, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n # The field lines after the first, or why they are not fields. A name\n # repeated, in any case, is one field, its values joined in order with\n # \", \" (RFC 9110 §5.3): into a Hash by name, a second line spelled the\n # same replaced the first and one spelled otherwise was an entry\n # `header` never reached, so two different `Content-Length`s were read\n # as whichever came first or last. A value with a control character in\n # it - a CR or LF that ends no line, a NUL - is refused (§5.5) rather than\n # handed to whoever reads it or echoes it into a header.\n #\n # *unfold*, for an answer: a line that begins with a blank continues the\n # field above it (obs-fold, RFC 9112 §5.2), and a user agent joins it on\n # with one space; read as a field of its own, `HTTP.get` panicked with\n # \"header without a colon: \\\" b\\\"\". A server may refuse one, and this\n # one does.\n #\n # *apart*, when given, is handed each repeated field's values by the\n # name in the answer, a line each, before they are joined.\n def self.parse_fields(lines : Array(String), unfold : Bool = false, apart : Hash(String, Array(String)) | ::Nil = nil) : Hash(String, String) | String\n # The response as it came off the wire, whole. `head` says the request\n # was a `HEAD`, whose answer carries the headers of a body it does not\n # send. An interim answer - `100 Continue`, `103 Early Hints` - is read\n # past to the final one after it (RFC 9110 §15.2). A `101` switches\n # protocols, and HTTP ends with it. What is not a response panics with\n # why: this is the text a program has in hand; the client answers the\n # same sentences as `HTTPError`s.\n def self.parse_response(text : String, head : Bool = false) : Response\n # A response's head - the text before its blank line - read: status,\n # reason, the fields, and the lines of those on more than one. A line\n # that begins with a blank continues the field above it (obs-fold, RFC\n # 9112 §5.2), which a user agent joins on with one space.\n def self.parse_response_head(text : String) : ::Tuple(Int32, String, Hash(String, String), Hash(String, Array(String)) | ::Nil) | HTTPError\n # Where *text* goes on at *from* past the empty lines there: what a\n # server skips before a request line (RFC 9112 §2.2).\n def self.past_empty_lines(text : String, from : Int32) : Int32\n def self.post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The proxy a request for *url* goes through, given the values of\n # `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` - nil when it goes\n # direct. Go's `golang.org/x/net/http/httpproxy` is the rule, line for\n # line, as its documentation and tests state it:\n #\n # * `https://` takes *https_proxy*, `http://` *http_proxy*, any other\n # scheme none. A proxy written without a scheme is `http://`'s, and is\n # answered with it, ready for `open`'s *proxy*.\n # * `localhost` (in lower case: Go's comparison) and a loopback address\n # (127.0.0.0/8, ::1, the first as IPv4-mapped too) never go through a\n # proxy.\n # * *no_proxy* is a comma-separated list, each entry trimmed and read\n # without regard to case: `*` alone sends everything direct; an IP\n # address or a CIDR range (`10.0.0.0/8`, `2001:db8::/32`) matches the\n # addresses in it, IPv4 and its IPv4-mapped form alike, and a CIDR\n # range of one family never the other's; a domain matches itself and\n # every name under it (`foo.com`: `foo.com`, `bar.foo.com`, not\n # `barfoo.com`); with a leading dot, or `*.`, only the names under it\n # (`.y.com`: `x.y.com`, not `y.com`). An address or domain may carry\n # a port (`1.2.3.4:80`, `[::1]:443`, `foo.com:8080`), and matches\n # then on that port alone, the URL's own or its scheme's default.\n # An entry that is none of these (`:80`) is passed over.\n # * A host is compared as IDNA's ASCII form, as Go compares it.\n #\n # Pure: the values are the caller's, so std/http reaches no environment;\n # std/http_proxy reads them from it.\n def self.proxy_for(url : String, http_proxy : String | ::Nil, https_proxy : String | ::Nil, no_proxy : String | ::Nil = nil) : String | ::Nil\n def self.put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The reason phrase a status carries (RFC 9110 §15), for a response the\n # server writes; a status this table does not know gets its class's.\n def self.reason_for(status : Int32) : String\n # One request and its whole answer: the answer as `open` hands it, its\n # body read. Redirects are followed, *redirects* at most; *jar* keeps\n # the cookies the answers set and sends them back; *decompress* asks\n # for gzip, deflate and zstd and undoes them. A refused or reset\n # connection answers `SocketError`; a task cancelled answers\n # `Cancelled`; what the server does wrong answers `HTTPError`. `!`\n # passes any of them up, `.or_panic` refuses it.\n def self.request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Response | Cancelled | SocketError | HTTPError\n # A request's head: the request line, `Host`, `Connection` (*connection*:\n # `close`, or `Upgrade`), the caller's headers, and the body's framing -\n # *length*, chunks, or none.\n def self.request_head(method : String, path : String, host : String, headers : Hash(String, String), length : Int64 | ::Nil, chunked : Bool, connection : String) : String\n # Two field names for the same field: ASCII case is the only difference\n # a name may carry.\n def self.same_name?(a : String, b : String) : Bool\n def self.split_crlf(text : String) : Array(String)\n # A status line read: the status and its reason, or why it is not one.\n def self.status_line(line : String) : ::Tuple(Int32, String) | HTTPError\n # What an `https://` request is checked against and offers: the\n # system's roots, and ALPN's `http/1.1`, unless a program sets its own -\n # roots of its own for a server it runs, say.\n def self.tls_config : TlsConfig\n def self.tls_config=(config : TlsConfig) : TlsConfig\n # A token (RFC 9110 §5.6.2): what a method or a field name is made of.\n def self.token?(text : String) : Bool\n # Without the blanks at either end (RFC 9112 §5.1: field values are\n # trimmed of the whitespace around them).\n def self.trim(s : String) : String\n # The 101 an upgrade to *protocol* is answered with, and the connection,\n # which is the caller's from then on: std/websocket's handshake. Any\n # other answer is an `HTTPError` naming it.\n #\n # *proxy* is `open`'s, and so is its credential, on the CONNECT alone;\n # but an upgrade goes through a tunnel for `http://` as for `https://`,\n # the handshake inside it in origin form. `Upgrade` belongs to the\n # connection it is sent on (RFC 9110 §7.8), and a proxy forwarding a\n # request removes `Connection` and every field it names (§7.6.1), so a\n # handshake handed to a forward proxy in absolute form reaches the\n # server, at best, as a plain GET: the gate's proxy, which drops\n # `Connection`, leaves std/websocket's server a request it answers 400.\n def self.upgrade(url : String, protocol : String, headers : Hash(String, String) = {} of String => String, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Upgraded | Cancelled | SocketError | HTTPError\nend\n\n# Requests that keep their connections: an `HTTPClient` holds the connections\n# its answers left open, by host and port, and the next request to that\n# server goes over one of them instead of a new one - the TCP handshake,\n# and the slow start after it, paid once rather than per request. Its\n# methods are `HTTP`'s, redirects, cookies and decoding included:\n#\n# ```\n# client = HTTPClient.new\n# client.get(\"http://127.0.0.1:8080/a\")\n# client.get(\"http://127.0.0.1:8080/b\") # the same connection\n# client.close\n# ```\n#\n# A connection goes back once its answer has been read to the end (inside\n# `open`, up to 64 KiB left unread is read past for it) and the answer\n# allows it; at most *idle* wait per server, and the rest close. A server\n# may close an idle connection whenever it likes (RFC 9112 §9.3.1), so a\n# request whose kept connection turns out closed - the write fails, or it\n# ends before a byte of an answer - is sent again once on a new one, when\n# its method is idempotent (RFC 9110 §9.2.2) and its body a string;\n# anything else answers the failure. One task at a time per request: two\n# tasks sending at once take two connections, and a client is not for\n# sharing across threads. An `https://` connection is kept under the\n# `HTTP.tls_config` it was made with, and handed only to a request made\n# under the same one (`TlsMadeUnder`): after `HTTP.tls_config=` the next\n# request makes a connection of its own, checked against the new roots.\n# A client made with *tls* uses that configuration instead of\n# `HTTP.tls_config`, and one made with *resolver* finds its hosts by it\n# instead of `Resolver.system` - an encrypted resolver, or a hosts text\n# that pins a name to an address: std/secure_dns's `DnsOverHttps` is a\n# client of its own with both, so its server is checked against the roots\n# it was given and reached at the address it was given.\n#\n# A client resumes TLS sessions (RFC 8446 §2.2): the tickets an\n# `https://` server sends are kept for its origin - scheme, host and\n# port, whichever proxy's tunnel they came through - under the TLS\n# configuration they came under, the client's own *tls* or else\n# `HTTP.tls_config` (`TlsMadeUnder`, as a kept connection is), and a new\n# connection to that origin offers the newest, each once.\n# A resumed handshake checks no certificate - the ticket stands for the\n# check the first one made - so a ticket is never offered to another\n# origin, nor under another configuration: after `HTTP.tls_config=`, or\n# roots, an identity, `now`, `min_version` or `require_staple` assigned\n# in place - in `HTTP.tls_config` or in the client's own *tls* - the next\n# connection is a full handshake under the new ones, and that origin's\n# tickets from under the old one are dropped. (std/tls's `TlsSession`\n# holds each ticket to its configuration too; this client drops them\n# where that one would only pass them over.) At most *origin_tickets* are\n# kept an origin - 4 by default, so that as many connections as *idle*\n# keeps by default can resume at once, where std/tls's server and\n# OpenSSL's send 2 a handshake - and *tickets* in all, 256 by default: as\n# many origins as that leaves room for at *origin_tickets* each, the one\n# longest unused dropped first. 0 for either keeps none, and nothing\n# resumes. `close`\n# leaves them. Nothing goes early (0-RTT), whatever a ticket allows: the\n# module's header says why. `connections_resumed`, `tickets_held` and\n# `Response#tls_resumed?` say what was kept and used. What it saves, by\n# bench/std_http_resume.iyi built --release on a 12-core machine other\n# jobs shared (medians of 200, the middle of three runs): the handshake\n# took 0.96 ms full and 0.51 resumed against std/http's server, 1.03 and\n# 0.57 against OpenSSL 3.5's s_server - and a GET on a new connection,\n# which waits about 40 ms at each iyi end that is not the handshake's,\n# 82.98 ms and 82.00, and 42.04 and 40.96.\n#\n# Over HTTP/2 - `https://` where ALPN chose `h2`, or `http://` with\n# *prior_knowledge* (h2c, RFC 9113 §3.3) - a kept connection carries the\n# next request on a new stream, until the server says GOAWAY.\n#\n# HTTPS records (RFC 9460), when `use_https_records` is set: before a new\n# connection to an `https://` origin - not through a proxy, and not for\n# an upgrade - the client asks its resolver for the origin's HTTPS\n# records (`Resolver#https_records`, AliasMode followed) and connects by\n# the first ServiceMode record it can use: one whose mandatory keys it\n# knows - the seven std/dns reads, port and no-default-alpn mandatory by\n# being there (section 9) - and whose protocols (alpn, and http/1.1\n# unless no-default-alpn) hold one of the configuration's `alpn`, or\n# http/1.1 where that is empty. Its target - the record's owner for \".\"\n# - and its port, or the origin's, are where the connection goes; its\n# `ech` is the connection's `ech_configs` (RFC 9848); and the protocols\n# it lists go first in the ClientHello's ALPN, which still offers every\n# one the configuration does (section 7.1.2: a record says which\n# protocols the endpoint has, and cannot take one away). The certificate\n# is checked, and the server named inside, for the origin's host,\n# whatever the target (section 2.3). An `ech` whose ECHConfigList holds\n# no config std/tls can use is passed over, as RFC 9849 section 6.1 has\n# it. The address hints are not read: the target's addresses are asked\n# for as any host's are.\n#\n# An endpoint that does not connect gives way to the next, then to the\n# last AliasMode target and to the origin itself (section 3) - unless\n# every endpoint carries `ech`, when the request fails rather than name\n# the server in the clear (RFC 9848 section 5.1). A TLS refusal is the\n# request's failure, but for one: a server that turned the encrypted\n# ClientHello down, its certificate checked for the config's\n# public_name, is asked once more on a new connection to the same\n# address, under the retry_configs it sent, or without ECH where it sent\n# none, which turns ECH off there (RFC 9849 section 6.1.6); a refusal of\n# that is the failure. No records (NXDOMAIN, NODATA), a set section 2.2\n# refuses, an AliasMode \".\" (the service says it is not there), or no\n# record the client can use: the connection is made as without records.\n# So is one whose lookup failed through a resolver that asks in the\n# clear, as section 3.1 lets a client choose; through encrypted\n# upstreams the failure is the request's, as section 3.1 asks, for\n# there the one who can drop the answer is an attacker taking the\n# records away. `localhost` and addresses are not asked about, and an\n# `http://` URL is not made `https://` by its records (section 9.5's\n# SHOULD): the program wrote the scheme.\n#\n# A record's `ech` and protocols make a configuration of their own - a\n# copy of the client's, made once and kept for the origin while the same\n# ones come under the same configuration - so that a kept connection and\n# a ticket are handed on only under the record they were made by\n# (`TlsMadeUnder` holds `ech_configs`). Off by default: every new\n# connection then waits on one DNS answer or more before its ClientHello\n# (RFC 9848 section 5.3 forbids sending it sooner), with no cache to\n# spare the next, and the records may move a connection to another host\n# and port, which is the program's to ask for.\npub class HTTPClient\n # Every idle connection closed. The client may be used after; it opens\n # new ones.\n def close : Nil\n # Connections this client has opened, and requests sent over one it\n # kept: what a program reads to see that its connections are kept.\n def connections_made : Int32\n # Connections this client opened whose TLS handshake resumed a session\n # by a ticket it kept.\n def connections_resumed : Int32\n def connections_reused : Int32\n def delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n protected def give_back(key : String, conn : Conn) : Nil\n def head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The configuration a connection to *origin* is made under by a record\n # whose `ech` is *ech* (nil: none) and whose protocols put *alpn* in its\n # order, *base* being the client's own or `HTTP.tls_config`: *base*\n # itself where those are its own, and otherwise a copy of it with them -\n # the same object every time while *base* is the same (`TlsMadeUnder`),\n # so that a connection kept and a ticket held under it serve the next\n # connection by the same record. It becomes the one the origin's kept\n # connections are taken under (`https_current`). The origin's\n # configurations from under another *base* go.\n protected def https_config(origin : String, base : TlsConfig, ech : Array(UInt8) | ::Nil, alpn : Array(String)) : TlsConfig\n # The configuration *origin*'s last connection by HTTPS records was made\n # under, while *base* is the one it came from - *base* itself where it\n # was made as without records: what a kept connection to the origin\n # must have been made under to be taken. Nil when none was made by them\n # under *base*, and then none is taken.\n protected def https_current(origin : String, base : TlsConfig) : TlsConfig | ::Nil\n # *timeout_ms* and *proxy* are `HTTP.open`'s, for every request the\n # client sends; *tls* and *resolver* are this client's own (above);\n # *origin_tickets* and *tickets* bound the TLS tickets kept, by origin\n # and in all.\n def initialize(idle : Int32 = 4, timeout_ms : Int32 = 0, prior_knowledge : Bool = false, proxy : String | ::Nil = nil, tls : TlsConfig | ::Nil = nil, resolver : Resolver | ::Nil = nil, origin_tickets : Int32 = 4, tickets : Int32 = 256)\n protected def made : Nil\n def open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n def post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # `http://` spoken as HTTP/2 from the first byte, without asking: for a\n # server known to take it.\n def prior_knowledge : Bool\n # The proxy every request goes through, `HTTP.open`'s *proxy*; its\n # connections are kept under it.\n def proxy : String | ::Nil\n def put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true) : Response | Cancelled | SocketError | HTTPError\n # What turns a host name into addresses for this client's connections,\n # instead of `Resolver.system`: encrypted upstreams, or a hosts text\n # alone that pins a name to an address, as std/secure_dns's\n # `DnsOverHttps` pins its server's.\n def resolver : Resolver | ::Nil\n protected def resumed : Nil\n # The session a new connection to *origin* under *tls* offers a ticket\n # from and keeps the server's new ones in; nil when the client keeps\n # none. The origin's tickets from under another configuration are\n # dropped, not left to take room that `TlsSession` would never offer\n # them from. The session handed out is the newest used; past the room\n # *tickets* leaves, the oldest goes.\n protected def session_for(origin : String, tls : TlsConfig) : TlsSession | ::Nil\n # A kept connection to *key*, nil when none waits. An HTTP/2 connection\n # that can take no more requests is closed rather than handed out, and\n # so is an `https://` one made under another TLS configuration than\n # *tls*, the one the request is made under (`TlsMadeUnder`): keyed by\n # scheme, host and port alone, a connection whose server was checked\n # against one set of roots carried requests after `HTTP.tls_config=`\n # named others that would have refused it.\n protected def take(key : String, tls : TlsConfig | ::Nil = nil) : Conn | ::Nil\n # The TLS tickets held for *origin* - `https://host:port`, the host in\n # lower case and the port written even where it is 443 - or for every\n # origin when nil.\n def tickets_held(origin : String | ::Nil = nil) : Int32\n # What this client's `https://` requests are checked against and offer,\n # instead of `HTTP.tls_config`: roots of its own, or ALPN's `h2` first.\n def tls : TlsConfig | ::Nil\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records : Bool\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records=(value : Bool) : Bool\nend\n\n# What the other end did that HTTP does not allow, as a value: `reason` is\n# the sentence, `status` the answer a server gives a request that did it\n# (400, or 413 and 431 for the limits). `message` says it is HTTP's.\npub struct HTTPError\n def initialize(reason : String, status : Int32 = 400)\n def reason : String\n def status : Int32\n def to_s : String\nend\n\n# One endpoint an origin's HTTPS records name, as `HTTP.https_endpoints`\n# reads it: the host and port to connect to, the ECHConfigList to encrypt\n# the ClientHello under (nil: none), and the protocols to offer by ALPN,\n# in order.\npub struct HttpsEndpoint\n def alpn : Array(String)\n def ech : Array(UInt8) | ::Nil\n def host : String\n def initialize(host : String, port : Int32, ech : Array(UInt8) | ::Nil, alpn : Array(String))\n def port : Int32\nend\n\n# A `multipart/form-data` body (RFC 7578) read a part at a time, as it\n# arrives: `next_part` answers the next `Part`, nil after the last, and a\n# part's `read` answers its bytes up to the boundary, \"\" at its end. A part\n# not read to its end is read past by the next `next_part`. The body of a\n# request is `Request#multipart`'s; any `BodyReader` and boundary will do.\n# A part's head is refused past 16 KiB, and a body that ends before its\n# closing boundary is an `HTTPError`.\npub class Multipart\n # The boundary a `Content-Type` of `multipart/...` names, nil when it\n # names none - or one of a length RFC 2046 §5.1.1 does not allow, 1 to\n # 70 characters, which `new` refuses with a panic. The type is the\n # peer's: `Request#multipart` handed a client's 90-character boundary\n # through to that panic (found by bench/std_fuzz_exercise.sh).\n def self.boundary(content_type : String) : String | ::Nil\n # Where *pattern* is in what is held, -1 when it is not there yet.\n protected def find(pattern : String) : Int32\n def initialize(body : BodyReader, boundary : String)\n # The next part, nil after the last.\n def next_part : Part | Nil | Cancelled | SocketError | HTTPError\n # Up to *max* bytes of the part being read, \"\" at its boundary. What is\n # held back is what may yet be the start of the boundary.\n protected def part_read(max : Int32) : String | Cancelled | SocketError | HTTPError\n # One more piece of the body onto what is held: false at its end.\n protected def pull : Bool | Cancelled | SocketError | HTTPError\nend\n\n# A multipart/form-data body to send: fields and files in order. Its\n# boundary is chosen when the body is first asked for - `finish`,\n# `content_type` or `boundary`, whichever comes first, after which no\n# part is added - as `iyi-` and 32 hex digits of the parts' SHA-1, made\n# again from itself in the rare case a part holds it: so it is none of\n# the parts' bytes by construction rather than by chance, the same\n# fields make the same body, and std/http reaches no source of\n# randomness for it. A boundary given to `new` is used as it is, and a\n# part that holds it is refused.\npub class MultipartBuilder\n def boundary : String\n def content_type : String\n def field(name : String, value : String) : self\n def file(name : String, filename : String, data : String, content_type : String = MIME.from_extension(filename)) : self\n # The body, closing boundary and all.\n def finish : String\n # The boundary ends a part where it follows a CRLF, the part's first\n # bytes included, which follow the head's blank line.\n protected def self.holds?(data : String, mark : String) : Bool\n def initialize(boundary : String | ::Nil = nil)\n # A name inside quotes, as browsers write one: a quote and a line break\n # percent-encoded (the WHATWG form encoding), so no field can end the\n # parameter or the line early.\n protected def self.quoted(text : String) : String\nend\n\n# One part of a multipart body: its head's fields, its name and file name\n# from `Content-Disposition`, and its bytes, read up to its boundary.\npub class Part\n def content_type : String | ::Nil\n # The file name a browser sent the part under, `filename=`.\n def filename : String | ::Nil\n def finished? : Bool\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n def initialize(owner : Multipart, headers : Hash(String, String))\n # The form field's name, `name=` of `Content-Disposition`.\n def name : String | ::Nil\n # The part's next bytes, at most *max*; \"\" at its end.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the part as one string, refused past *limit* bytes.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\nend\n\n# What a server's block is handed: one request. Its body is `body`, read\n# whole before the block ran, or under `stream_bodies` read by the block\n# from `body_io` as it arrives; `body` then reads the rest itself, and a\n# body that cannot be read - the client went, or sent one that is not\n# HTTP - ends the connection with that sentence rather than be answered as\n# if it were whole.\npub class Request\n def body : String\n def body_io : BodyReader\n # The pairs of the request's `Cookie` header, the first of a name kept.\n def cookies : Hash(String, String)\n # The fields of a form: the query's, and an\n # `application/x-www-form-urlencoded` body's after them. The body is\n # read for it.\n def form : Params\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n # A request whose body is in hand: what `parse_request` answers.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), body : String, version : String = \"HTTP/1.1\", remote_address : String | ::Nil = nil)\n # A request whose body is still on the connection.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), version : String, reader : BodyReader, remote_address : String | ::Nil = nil)\n def method : String\n # The body as `multipart/form-data` parts, read as they arrive; nil when\n # the `Content-Type` is not multipart or names no boundary\n # (`Multipart.boundary`).\n def multipart : Multipart | ::Nil\n def path : String\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates : Array(Certificate)\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates=(value : Array(Certificate)) : Array(Certificate)\n def query : String | ::Nil\n # The client's address, as the server's socket saw it (no port): nil\n # for a request a program made, or when the client had already gone. A\n # proxy in front is what this names; `X-Forwarded-For` is the proxy's\n # word, and a header any client can write.\n def remote_address : String | ::Nil\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the TLS connection the request came over resumed a session by\n # a ticket, under `serve(tls:)`: its `peer_certificates` are then the\n # ticket's. False in the clear.\n def tls_resumed? : Bool\n def version : String\nend\n\n# A response: what a server's block answers, and what a client is handed.\n#\n# To write one: `Response.new(200, \"hello\")`, a status and a string;\n# `Response.new(200, io)`, a stream the server copies out and closes, with\n# the caller's `Content-Length` when it gives one and chunked otherwise;\n# `Response.stream(200) { |w| w.write(\"...\") }`, whose block writes as it\n# goes; `Response.upgrade(headers) { |socket, leftover| ... }`, a 101 after\n# which the connection is the block's. `add_header` writes a field on a\n# line of its own, as each `Set-Cookie` must be.\n#\n# What a client is handed: the status, reason and headers, and the body -\n# `body` whole, or `body_io` as it arrives inside `HTTP.open`'s block.\npub class Response\n # One more line of the field *name*, written on a line of its own: what\n # two `Set-Cookie`s need, since the \", \" a field's lines are joined with\n # is part of a cookie's date.\n def add_header(name : String, value : String) : Nil\n # The lines of each field written on more than one, by its name in\n # `headers`; nil when every field is on one line.\n def apart : Hash(String, Array(String)) | ::Nil\n # The body: whole. Inside `HTTP.open`'s block it is read here, the first\n # time it is asked for, and a body that cannot be read panics with why;\n # `body_io` answers that as a value.\n def body : String\n # The body as it arrives. For a response a program made, its string.\n def body_io : BodyReader\n # Whether the client undid the body's `gzip`, `deflate` or `zstd`\n # coding; the `Content-Encoding` and `Content-Length` that described it\n # are gone from the headers then.\n def decoded? : Bool\n # A header by name, as the server spelled it or otherwise: a field\n # name is case-insensitive (RFC 9110 §5.1), and `content-length` is as\n # common on the wire as `Content-Length`.\n def header(name : String) : String | ::Nil\n # A field's lines apart, in the order they came: `header` joins them\n # with \", \", and `Set-Cookie` is the field that join breaks (RFC 9110\n # §5.3), since a cookie's `Expires` date has a comma in it - two cookies\n # read as \"a=1; Expires=Wed, 21 Oct 2026 07:28:00 GMT, b=2\", which no\n # split takes back apart. Empty for a field that is not there.\n def header_values(name : String) : Array(String)\n def headers : Hash(String, String)\n # A response a client read: its body comes from *reader*, and *url* is\n # where it came from.\n def initialize(status : Int32, reason : String, headers : Hash(String, String), apart : Hash(String, Array(String)) | ::Nil, reader : BodyReader, url : String, decoded : Bool)\n def initialize(status : Int32, reason : String, headers : Hash(String, String), body : String, apart : Hash(String, Array(String)) | ::Nil = nil)\n # A response to write: the reason is the status's own, the headers the\n # caller's. `Response.new(200, \"hello\")` is the common shape.\n def initialize(status : Int32, body : String = \"\", headers : Hash(String, String) = {} of String => String)\n # A response whose body is *source*, copied out by the server and\n # closed after.\n def initialize(status : Int32, source : IyiIO, headers : Hash(String, String) = {} of String => String)\n # The body read whole into the response, which is answered; why not,\n # when it cannot be.\n def load : Response | Cancelled | SocketError | HTTPError\n def reason : String\n def source : IyiIO | ::Nil\n def status : Int32\n # A response whose body the block writes as it goes, chunked.\n def self.stream(status : Int32, headers : Hash(String, String) = {} of String => String, &writer : (BodyWriter -> Nil)) : Response\n def success? : Bool\n def takeover : Proc(IyiSocket | TlsStream, String, Nil) | ::Nil\n protected def takeover=(takeover : Proc(IyiSocket | TlsStream, String, Nil)) : Proc(IyiSocket | TlsStream, String, Nil)\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the `https://` connection the answer came over resumed a TLS\n # session by a ticket an `HTTPClient` kept, rather than a full\n # handshake; false in the clear and for one a program made.\n def tls_resumed? : Bool\n # A 101 Switching Protocols with *headers* (`Upgrade` and the\n # protocol's own), after which the connection is the block's: it is\n # handed the connection - the socket, or under `serve(tls:)` the\n # `TlsStream` over it - and the bytes the client sent after its request,\n # and the connection closes when it returns.\n def self.upgrade(headers : Hash(String, String), &takeover : (IyiSocket | TlsStream, String -> Nil)) : Response\n # The URL the answer came from, after any redirects; nil for one a\n # program made.\n def url : String | ::Nil\n def writer : Proc(BodyWriter, Nil) | ::Nil\n protected def writer=(writer : Proc(BodyWriter, Nil)) : Proc(BodyWriter, Nil)\nend\n\npub struct Server\n # The headers a coded body goes with: the coding, and `Vary` so that a\n # cache keeps the answers apart. A `Content-Length` the block gave is\n # the plain body's, and goes.\n protected def self.coded(response : Response, coding : String) : Response\n # The coding *response* goes in to the client of *req*, or nil: one the\n # client takes (RFC 9110 §12.5.3, a `q` of 0 refusing it), for an\n # answer with a body and a type worth compressing, and no coding of its\n # own. A whole body goes `zstd` when the client's `q` for it is at least\n # its `q` for gzip, and `gzip` otherwise. The tie goes to zstd for the\n # server's time: measured on this machine, release builds, on\n # src/std/http.iyi (199,134 bytes) `Gzip.compress` wrote 54,484 bytes in\n # 5 ms and `Zstd.compress` 61,753 in 1 ms, and on CHANGELOG.md (1.5 MB)\n # 552,446 in 58 ms against 589,785 in 13 ms - a quarter of the time, for\n # an eighth more bytes at most. A stream - an `IyiIO` or a\n # `Response.stream` - goes gzip or as it is: std/compress's `Deflater`\n # writes each piece as it comes, where std/zstd's compressor takes its\n # input whole, and a frame a write would code each piece alone.\n def self.coding_for(req : Request, response : Response) : String | ::Nil\n # A string body compressed whole in *coding*, `zstd` or `gzip`, with\n # the headers that say so. `Zstd.compress`'s frame needs a window of\n # 1 MiB at most, inside the 8 MB RFC 9659 §3 holds an encoder to.\n protected def self.compressed(response : Response, coding : String) : Response\n # A response's head. `Content-Length` is *length*'s - the caller's own\n # line is checked against it, and kept only where no body follows it: a\n # HEAD, or a 304 that names the length a 200 would have (RFC 9110 §8.6) -\n # or `Transfer-Encoding: chunked` when *chunked*, or neither for a body\n # the connection's close ends. `Connection` says what the connection will\n # do next, except on an *upgrade*, whose own line says it. A 1xx, 204 or\n # 304 has no content (RFC 9110 §15), and writes neither a body nor a\n # length of its own (§8.6): the body was read by the client as the start\n # of the next answer, and `Content-Length: 0` on a 304 told a cache that\n # the resource had become empty. A field added on lines of its own\n # (`add_header`) is written a line each.\n def self.format_head(response : Response, head : Bool, close : Bool, length : Int64 | ::Nil, chunked : Bool, upgrade : Bool = false) : String\n # The response as it goes on the wire, whole: `format_head` for its\n # string body, and the body.\n def self.format_response(response : Response, head : Bool, close : Bool) : String\n # One connection: requests until the client is done. A request that is\n # not HTTP is answered with why and the connection closed, since the\n # bytes after it cannot be trusted to be a request either. Requests that\n # came in one read are read where they lie, one after another. A\n # connection that opens with HTTP/2's preface is served as HTTP/2. Under\n # *tls* the handshake comes first, and the connection ends with\n # close_notify. A connection this side wrote last on - an answer, a\n # refusal, a GOAWAY - closes by lingering (`Conn#close`), so the reset\n # a close with the client's bytes unread is cannot take what was\n # written from the client before it reads it. One that timed out or was\n # cancelled, with nothing of this side's waiting to be read, closes at\n # once, and so does one whose client said `close` and sent nothing past\n # its request (`serve_h1`).\n def self.handle(client : IyiSocket, handler : Proc(Request, Response), listener : IyiSocket | ServeStop | Nil = nil, idle : Hash(UInt64, Conn) | ::Nil = nil, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 0, tls : TlsServerConfig | TlsFrozenServerConfig | Nil = nil, request_timeout_ms : Int32 = 0) : Nil\n def initialize\n # A request's head - the text before its blank line - read and checked,\n # or why it is not one.\n def self.parse_head(text : String) : RequestHead | HTTPError\n # A request at *from* in `text`, if a whole one is there. No request and\n # no error means \"not yet\". `consumed` counts from the start of `text`.\n def self.parse_request(text : String, from : Int32 = 0) : ParsedRequest\n # Serve `listener` until it is closed, on *workers* threads: each\n # connection is a task on the pool `spawn_parallel` starts (SPEC.md\n # III.4.13), started by whichever worker is free and served there to\n # its end, while the thread that called this accepts. The block runs on\n # the connection's worker, once per request, on several workers at\n # once; what it answers is written back.\n # The close ends a connection waiting on its client too, for its next\n # request or for the rest of one, and one that is answering ends when its\n # answer is written: a keep-alive client that sat idle kept `serve` from\n # returning for as long as it stayed, and so did one that had sent\n # what has arrived of a request so far - one byte of it was enough.\n #\n # *stream_bodies*: the block reads each request's body from `body_io`\n # rather than being handed it whole. *compress*: an answer whose type\n # std/mime calls compressible goes zstd or gzip to a client that\n # accepts one (`coding_for`).\n #\n # *timeout_ms*: a request's head must have arrived within it, counted\n # from when the server starts waiting for one - a kept-alive connection's\n # idle time included - and no read of a body nor write of an answer may\n # wait longer; a connection that does is closed. Without it, a client\n # that sends a byte of its head every few seconds (Slowloris) holds a\n # connection and its fiber for as long as it likes, and so does one that\n # reads none of its answer. A minute by default, nginx's\n # `client_header_timeout`; 0 for none. A connection handed over by\n # `Response.upgrade` waits as its protocol likes.\n #\n # *request_timeout_ms*: the whole request - head and body - must have\n # arrived within it, counted from the same moment as *timeout_ms*, as\n # Go's `ReadTimeout` beside its `ReadHeaderTimeout`; the head is held to\n # the sooner of the two. Without it a body sent a byte every few seconds\n # was bounded per read alone, and held its connection for as long as it\n # dripped. A block that reads the body itself (*stream_bodies*) reads\n # under it until the block returns, and what it leaves unread is read\n # past under it too. Over HTTP/2 the wait for each request and its body\n # is held to it on the connection the same way, the head to the sooner\n # of the two. Five minutes by default, which a 64 MiB body (`MAX_BODY`)\n # meets at 224 KB/s; a server taking large uploads over slow links\n # raises it. 0 for none. A connection past it is closed, unanswered, as\n # one past *timeout_ms* is.\n #\n # *max_connections*: at most this many connections are served at once;\n # 1024 by default, 0 for no limit. Past it the server stops accepting\n # until one ends, so the next connection waits in the kernel's listen\n # queue - queued, not refused, as Go's `netutil.LimitListener` does - and\n # is served in its turn; a client that gives up waiting goes unserved.\n # Each served connection holds its place until it closes, a kept-alive\n # one between requests too, which *timeout_ms* bounds. The process's\n # own limit on descriptors may come first.\n #\n # *tls*: each connection is TLS (std/tls's `TlsServer`, 1.3 or 1.2) under this\n # configuration, its handshake held to *timeout_ms* too and run on the\n # worker that serves it; one whose handshake fails is closed, the\n # client told why by an alert. Inside it a connection that opens with\n # HTTP/2's preface - as a client whose ALPN chose `h2` does, when\n # `tls.alpn` offers it - is served HTTP/2, and any other HTTP/1.1. A\n # `Response.upgrade` block over TLS is handed the `TlsStream`, so a\n # WebSocket is `wss://`. On more than one worker the config is a\n # `TlsFrozenServerConfig` - `config.frozen` - since every worker reads\n # it at once: its ticket keys are one set behind a lock, so a ticket a\n # connection on one worker was sent resumes on any other, and an\n # identity source given to `frozen` is a block the compiler checked. A\n # `TlsServerConfig` itself is refused at the call (\"Server.serve hands\n # `tls : TlsServerConfig` to other threads, several at once, and it is\n # not Share: ... Pass `config.frozen` ...\"), not frozen here: `frozen`\n # is a copy, so a property assigned after the server started would be\n # served by no worker and nothing would say so, and a config with an\n # `identity_source` cannot be frozen without its source given again as\n # a block the compiler checks. Under `workers: 1` either is served.\n #\n # *workers*: the pool's size, `IyiWorkers.count` by default - `IYI_WORKERS`\n # from the environment, else the cores this process may run on. The\n # pool is the process's one, and it does not grow or shrink once it has\n # started, so a count other than the one it started with panics. Every\n # worker runs the block at once, so the compiler checks it where it is\n # written as it checks a parallel task's (`@[Share]`, SPEC.md III.4.4):\n # every value it captures must be `Share` - a table behind a `Mutex`\n # (std/mutex), a count in an `Atomic`, a `SharedRouter` (std/router) -\n # every captured local assigned once, and the constants and class\n # variables it names `Share`. The refusal names the variable, why its\n # type is not `Share`, and the two ways out: the lock, or `workers: 1`.\n # `workers: 1`, written as the literal, serves on the calling thread\n # alone - each connection a task beside the accept loop, as `spawn`\n # makes one - and its block is not checked: a block that calls a\n # `Router`, appends to an `Array` or keeps a handle that is not `Share`\n # is served there. A count in a variable is checked whatever it holds,\n # since the compiler cannot know it is 1. A count of 1 from the default\n # (`IYI_WORKERS=1`, one core) serves on the calling thread too.\n #\n # A connection stays on the worker that started it (III.4.13, part 1):\n # connections spread over the workers free as they arrive, and one kept\n # alive is not moved later. A block that computes without parking holds\n # its worker meanwhile, as any task does. The listener is the calling\n # thread's, and a close of it is made there: a block that stops the\n # server sends on a `Channel` a task beside the server receives from,\n # and that task closes it. At the stop each connection waiting on its\n # client is closed on its own worker, sent close_notify there first\n # under *tls*.\n def self.serve(listener : IyiSocket, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count, &handler : (Request -> Response)) : Nil forall C\n # `serve` with a value for its handler rather than a block: a\n # `SharedProc`, a `SharedRouter` (std/router), or one of the program's\n # own, whose `call(request : Request) : Response` answers each request,\n # as Go's `http.Handler` does. On more than one worker it must be\n # `Share` (SPEC.md III.4.4), since every connection on every worker\n # calls the one value at once: the compiler refuses one that is not at\n # the argument, naming the field that failed and the two ways out - a\n # count in an `Atomic`, a pool or a cache behind a `Mutex`, a\n # `SharedRouter` for a `Router`, or `workers: 1`, under which any value\n # with that `call` is served on the calling thread, a `Router`\n # included. What the check does not see is the edge `spawn_parallel`\n # has too: a method the handler calls that writes a module's state. A\n # struct handler is copied into each connection's task, and an `Atomic`\n # in it with it: what the connections share is a class, or held by\n # one.\n #\n # The rest is the block form's: *stream_bodies*, *compress*,\n # *timeout_ms*, *tls* (`config.frozen` on more than one worker),\n # *request_timeout_ms*, *max_connections* and *workers*, HTTP/2 by its\n # preface, `Response.upgrade`, and a close of *listener* - on the\n # thread that called this - ending every connection waiting on its\n # client and returning once the others have answered.\n def self.serve(listener : IyiSocket, handler : H, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count) : Nil forall H, C\n protected def self.writer_for(socket : IyiSocket | TlsStream, chunked : Bool, gzip : Bool) : BodyWriter\nend\n\n# A TLS connection used as a socket is: read into a buffer, written to,\n# closed, its timeouts set - by a `Conn`, a `BodyWriter`, and a connection\n# handed over by an upgrade (`wss://` at either end). What TLS refuses is a\n# `SocketError` carrying TLS's own sentence (`layer_reason`), so answers\n# keep the types they have over a plain socket.\npub class TlsStream\n # The socket closed at once, nothing sent or read first.\n def abort : Nil\n # close_notify, the sending half shut, and what the peer still sends\n # read past for a bounded time before the socket closes\n # (`TlsConnection#close`).\n def close : Nil\n # close_notify and the sending half shut; reads go on until the peer's\n # own (`TlsConnection#close_write`).\n def close_write : SocketError | ::Nil\n def closed? : Bool\n def initialize(tls : TlsConnection, host : String)\n # The peer's certificate chain, leaf first, as the handshake checked it:\n # the server's, at a client; the client's, at a server that asked.\n def peer_certificates : Array(Certificate)\n # Up to *count* bytes into *buffer*: 0 once the peer closed with\n # close_notify.\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n # Up to *max_bytes* as a string, what is held first: \"\" once the peer\n # closed with close_notify. `IyiSocket#read`'s form, for a block an\n # upgrade hands either.\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # How long a read or a write of the connection under it may wait.\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n # Whether the handshake resumed a session (`TlsConnection#resumed?`).\n def resumed? : Bool\n def write(text : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\n# What `HTTP.upgrade` answers: the 101, and the connection, which is the\n# caller's now - the socket, and the bytes the server sent after its head.\npub class Upgraded\n def initialize(response : Response, socket : IyiSocket | TlsStream, leftover : String)\n def leftover : String\n def response : Response\n # The connection: the socket, or for `https://` the `TlsStream` over it.\n def socket : IyiSocket | TlsStream\nend\n\nimpl Error for HTTPError\n def message : String\nend\n" }, { "name": "std/http_proxy", @@ -538,7 +538,7 @@ { "name": "std/socket", "path": "src/std/socket.iyi", - "sourceSha256": "fe78564802a8a020e4fff120d2f676e30b86a1f5b31611d71c23f2f5b1ffed2f", + "sourceSha256": "eff1d8cd1e03a482ece90494c957df9f6d962808444e75f56a8b4843b2d5c894", "surface": "module std/socket\n\n# What a resolver answers and is built from, under the names a program\n# that connects by name already imports from here.\npub alias DnsError = Std::Dns::DnsError\n\n# An encrypted way to a name server, asked by a resolver `Resolver#encrypted`\n# made instead of its plaintext servers: std/secure_dns's `DnsOverTls`\n# (RFC 7858) and `DnsOverHttps` (RFC 8484). The trait is here and they are\n# there because they speak std/tls and std/http, which import this module,\n# and R-1 refuses the cycle - as std/http's `CookieStore` is std/cookie's\n# to implement.\npub trait DnsUpstream\n # Its connections closed; the next exchange opens new ones.\n abstract def close : Nil\n # The answer to each of *queries*, in their order, each one whole\n # message as it came; or why there are none. *timeout_ms* bounds each\n # wait on the server. An answer is matched to its query by the\n # transport - by ID over one connection, by request over HTTP - and the\n # resolver then reads it as it reads a datagram.\n abstract def exchange(queries : Array(Bytes), timeout_ms : Int32) : Array(Bytes) | DnsError | Cancelled\n # How a message names it: `dns.test (127.0.0.1:853) over TLS`.\n abstract def label : String\n # Whether its queries carry the ID 0: DNS over HTTPS's, which RFC 8484\n # 4.1 asks for so that an HTTP cache sees one request for one question.\n # The answer must carry the ID the query did all the same.\n abstract def zero_id? : Bool\nend\n\n# Either family's address: what a resolver answers for a name.\npub alias IPAddress = IPv4Address | IPv6Address\n\npub struct IPv4Address\n def b0 : UInt8\n def b1 : UInt8\n def b2 : UInt8\n def b3 : UInt8\n def initialize(b0 : UInt8, b1 : UInt8, b2 : UInt8, b3 : UInt8)\n def to_s : String\nend\n\n# Sixteen bytes in network order, held as two words: `high` is bytes 0 to\n# 7. Written the RFC 5952 way - lowercase, no leading zeros, the longest\n# run of two or more zero groups as `::`, the first of two equal runs -\n# and an IPv4-mapped address with its dotted tail, `::ffff:192.0.2.1`.\npub struct IPv6Address\n # Byte *index*, 0 to 15.\n def [](index : Int32) : UInt8\n # The 16-bit group *index*, 0 to 7. Not `group`: that name is the task\n # group's in iyi (SPEC.md III.4.9).\n def hextet(index : Int32) : Int32\n def high : UInt64\n def initialize(high : UInt64, low : UInt64)\n def low : UInt64\n # The sixteen bytes at *bytes*, network order.\n def self.new(bytes : Pointer(UInt8)) : IPv6Address\n def to_s : String\nend\n\npub class IyiSocket\n # Room for any family's `sockaddr`, zeroed, so a unix path the kernel\n # did not end with a NUL still ends.\n def self.__address_room : Pointer(UInt8)\n # A `sockaddr` the kernel filled, as text: dotted IPv4, IPv6 written as\n # RFC 5952 has it, or a unix socket's path.\n def self.__address_text(addr : Pointer(UInt8), family : Int32) : String\n # What every connected and accepted socket starts with: TCP_NODELAY, on\n # TCP; a unix socket has no Nagle.\n def self.__send_at_once(fd : Int32, family : Int32) : Nil\n def self.__sys3(number : Int64, a : Int64, b : Int64, c : Int64) : Int64\n def self.__sys5(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64) : Int64\n def self.__sys6(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, f : Int64) : Int64\n def self.__sys_accept(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32), family : Int32) : Int32\n def self.__sys_addr_size(family : Int32) : Int64\n def self.__sys_bind(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n # Every close of a socket in this file goes through here. `__iyi_close` is\n # the descriptor close, and on Windows a socket is not a descriptor:\n # `CloseHandle` on a SOCKET releases the kernel handle and leaves Winsock's\n # half of it standing, and the graceful close that makes a peer's read\n # answer EOF rather than a reset is `closesocket`'s.\n def self.__sys_close(fd : Int32) : Nil\n # A raw syscall's negative errno, as the number.\n def self.__sys_code(result : Int64) : Int32\n # *ms* is `connect_to`'s wait's: the call answers EINPROGRESS at once.\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), family : Int32, ms : Int32) : Int32\n def self.__sys_connects_blocking?(family : Int32) : ::Bool\n def self.__sys_dgram_connect(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n def self.__sys_dgram_send(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # The resolver's datagram socket: see the darwin arm. The raw call\n # answers the negative errno, which `__sys_code` reads.\n def self.__sys_dgram_socket(family : Int32) : Int32\n def self.__sys_dgram_wait(fd : Int32, ms : Int32) : ::Bool | Cancelled\n def self.__sys_getpeername(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_getsockname(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_in_progress?(result : Int32) : ::Bool\n def self.__sys_listen(fd : Int32, backlog : Int32) : Int32\n def self.__sys_no_sigpipe(fd : Int32) : Nil\n def self.__sys_nodelay?(fd : Int32) : ::Bool\n # `ppoll` on one descriptor for *ms* milliseconds - a timespec, or none\n # for -1 - and no signal mask. 0 when the time ran out.\n def self.__sys_poll(fd : Int32, events : Int32, ms : Int32) : Int32\n def self.__sys_recv(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # MSG_NOSIGNAL (0x4000): a write to a connection the peer closed answers\n # EPIPE instead of killing the process with SIGPIPE.\n def self.__sys_send(fd : Int32, buf : Pointer(UInt8), len : UInt64, ms : Int32) : Int64\n # TCP_NODELAY (1) at IPPROTO_TCP (6): Nagle's algorithm off when *on*.\n def self.__sys_set_nodelay(fd : Int32, on : ::Bool) : Int32\n # F_GETFL (3), then F_SETFL (4) with O_NONBLOCK (0x800) added.\n def self.__sys_set_nonblock(fd : Int32) : Int32\n def self.__sys_set_reuseaddr(fd : Int32) : Int32\n # IPV6_V6ONLY (26) at IPPROTO_IPV6 (41).\n def self.__sys_set_v6only(fd : Int32) : Int32\n def self.__sys_shutdown_write(fd : Int32) : Int64\n # SO_ERROR (4) at SOL_SOCKET (1): what a connect that was in progress\n # ended with, 0 when it went through.\n def self.__sys_sock_error(fd : Int32) : Int32\n def self.__sys_socket(family : Int32) : Int32\n # A raw syscall answers the negative errno: EAGAIN is 11, EINPROGRESS 115.\n def self.__sys_would_block?(result : Int64) : ::Bool\n def accept : IyiSocket | Cancelled | SocketError\n # The order `connect` tries a name's addresses in (RFC 8305 section 4):\n # one family's first address, then the other's, alternating, each family\n # in the resolver's order, and the longer family's rest at the end.\n # IPv6 goes first when this machine has a route to the first IPv6\n # address - RFC 6724's first rule, asked the way glibc's getaddrinfo\n # asks it, by connecting a UDP socket, which sends nothing - and IPv4\n # first when it has none, so a machine without IPv6 does not start every\n # connect with an attempt that cannot leave it. A name of one family is\n # left in the resolver's order, and asks nothing.\n def self.attempt_order(addresses : Array(IPAddress)) : Array(IPAddress)\n def self.await_readable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n def self.await_writable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n # A timeout as the waits take it: milliseconds, -1 for none.\n def self.bound(ms : Int32 | ::Nil) : Int32\n protected def bound_path(path : String) : Nil\n # *path* with the NUL a system call reads to.\n def self.c_path(path : String) : Pointer(UInt8)\n def close : Nil\n # The sending half ended, the receiving half kept: the peer reads the\n # end of the stream after what was written, and this end goes on reading\n # what the peer still sends until its own end. `shutdown(SHUT_WR)` on\n # POSIX, `shutdown(SD_SEND)` on Windows. A socket that is not connected\n # answers the platform's refusal as a `SocketError`; `close` is still\n # what gives the socket back.\n def close_write : SocketError | ::Nil\n def closed? : ::Bool\n # A host name is resolved by the machine's resolver (`Resolver.system`);\n # a name that does not resolve is a `SocketError` whose `unresolved?` is\n # true and whose sentence names the host. It panicked, \"cannot resolve\n # address: \\\"example.com\\\"\", for every name but `localhost`, so\n # `HTTP.get(\"http://example.com/\")` could not run.\n #\n # A name with more than one address is raced, Happy Eyeballs v2 (RFC\n # 8305): the first address is tried, the next `ATTEMPT_DELAY_MS` later\n # without waiting for the first to fail, or at once when an attempt\n # fails, and so on down the list (`attempt_order`'s: IPv6 first when\n # this machine has a route to it, the families alternating). The first\n # handshake to finish wins, and every other attempt is cancelled and its\n # socket closed before `connect` answers. The addresses were tried one\n # after another, IPv4 first, each with the whole timeout: a name whose\n # first address dropped SYNs cost all of *timeout_ms* - or the kernel's\n # two minutes of retries, unbounded - before the second was tried. Now\n # it costs the delay: 250 ms to a name whose first address is a full\n # listen queue and whose second listens (`bench/std_dns_exercise.sh`,\n # under a 5,000 ms bound).\n #\n # `localhost` is raced the same way, 127.0.0.1 and then ::1, without\n # asking the routing table: a listener here binds `localhost` at\n # 127.0.0.1, so it goes first, and Windows, which names ::1 first, has\n # dev servers that bind ::1 alone. This replaces the rule that tried\n # ::1 only after 127.0.0.1 refused: Windows takes 2 s to refuse a\n # connect, so a ::1-only listener was reached in 2017 ms and a port\n # nobody holds refused in 4059 ms, where now ::1 starts 250 ms after\n # 127.0.0.1 whatever 127.0.0.1 is still doing.\n #\n # *timeout_ms* bounds the whole connect, every attempt together (-1: as\n # long as the kernel waits, which on Linux is about two minutes of SYN\n # retries for a host that drops them). It bounded each address's\n # handshake, which a race has no use for: an attempt started 500 ms in\n # would have run 500 ms past the caller's bound. A connect that runs out\n # is a `SocketError` whose `timed_out?` is true. The name's lookup is\n # outside it, held by the resolver's own `timeout_ms` and `attempts`.\n #\n # A name none of whose addresses connects is one `SocketError` that\n # names each address in the order tried and how its attempt ended,\n # `tried`: `cannot connect to example.com:443: [2001:db8::1]:443 timed\n # out; 192.0.2.1:443 connection refused`. It answered the first\n # address's failure alone. Its `code` is the first attempt's, or a\n # timeout's when the time ran out first. A name with one address is\n # connected to as an address is, and fails with that address's error.\n def self.connect(host : String, port : Int32, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The same, with a host name resolved by *resolver*: name servers, a\n # hosts file and search list of the program's choosing, rather than the\n # machine's. An address and `localhost` are what they are either way.\n def self.connect(host : String, port : Int32, resolver : Resolver, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # A connect to *addr*, waiting at most *ms* milliseconds for the\n # handshake (-1: as long as the kernel does). The resolver's TCP query\n # and `connect` to an address are its callers with a bound; a race's\n # attempts wait as the kernel does, and the race keeps the bound. The\n # bound reaches `__sys_connect` as well as the wait after it, because\n # on Windows that call is the wait: it parks in `ConnectEx` until the\n # handshake is over.\n def self.connect_to(addr : Pointer(UInt8), family : Int32, peer : String, ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The stream socket listening at *path*.\n def self.connect_unix(path : String) : IyiSocket | Cancelled | SocketError\n # *host* and *port* as a message names them: an IPv6 literal in its\n # brackets, as a URL writes it. Bare, `::1:57456` was an address with\n # no port, and `ff02::1:80` another address altogether.\n def self.endpoint(host : String, port : Int32) : String\n # The address family, the platform's number for it: an accepted\n # connection is its listener's, and on Windows the socket `AcceptEx`\n # fills in has to be made in it. With `fd`, what makes the socket again\n # on another thread (std/http's `Server.serve`, on its pool).\n def family : Int32\n def fd : Int32\n def initialize(fd : Int32, family : Int32 = AF_INET)\n def self.listen(host : String, port : Int32, backlog : Int32 = 128) : IyiSocket\n def self.listen(port : Int32, backlog : Int32 = 128) : IyiSocket\n # A stream socket at *path*. The path must not exist: a socket file an\n # earlier run left behind is refused rather than taken over, since\n # which program owns it is not something a bind can know. `close`\n # removes the file.\n def self.listen_unix(path : String, backlog : Int32 = 128) : IyiSocket\n def local_address : String\n # The port sits at the same two bytes of a `sockaddr_in` and a\n # `sockaddr_in6`.\n def local_port : Int32\n # `localhost` in any case, and with the one trailing dot of a name\n # written fully qualified: a host name's case is not part of it (RFC\n # 4343), and getaddrinfo, Python and curl all answer 127.0.0.1 for\n # `LOCALHOST` and `localhost.`. Matched exactly, these were \"cannot\n # resolve address: \\\"LOCALHOST\\\"\", and so were `Localhost` and\n # `localhost.`.\n def self.localhost?(host : String) : ::Bool\n # What `localhost` stands for, in the order `connect` tries it.\n def self.loopback : Array(IPAddress)\n def self.make_sockaddr_in(ip : IPv4Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_in6`: family, port, a zero flow label, the address, a zero\n # scope. The family's number and where it sits are the platform's.\n def self.make_sockaddr_in6(ip : IPv6Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_un`: the family, then the path and its NUL. The room for\n # the path is the platform's - 104 bytes on darwin, 108 elsewhere - and a\n # longer one is refused rather than cut, since a cut path is another\n # path.\n def self.make_sockaddr_un(path : String) : Pointer(UInt8)\n # Whether a small write goes at once (TCP_NODELAY) rather than waiting\n # for an earlier one's acknowledgement, as Nagle's algorithm (RFC 896)\n # has it wait. A connected or accepted TCP socket here sends at once, as\n # Go's net package sets its own; `nodelay = false` turns Nagle back on\n # for a program that writes in pieces it would rather have joined. With\n # Nagle on, a write behind an unacknowledged one waited out the peer's\n # delayed ACK, 40 ms on Linux: measured on loopback, --release, medians\n # of 200, a GET on a new https:// connection took 82.89 ms between\n # std/http's ends and 42.00 to OpenSSL's s_server, and takes 1.07 and\n # 1.10 now; wrk against a server answering a streamed body went from\n # 1,211 to 10,702 requests a second. A unix socket has no Nagle to turn\n # off: it answers false and is left alone.\n def nodelay=(on : ::Bool) : ::Bool\n # Whether TCP_NODELAY is on, as the socket's option reads.\n def nodelay? : ::Bool\n # Dotted-decimal IPv4 only: four octets of 0 to 255, no leading zero\n # (`\"010.1.1.1\"` is refused; glibc's `inet_aton` reads `010` as octal 8,\n # CVE-2021-29921, so it must not quietly be 10 here), plus `localhost`.\n # The refusal inspects the host so a stray space shows.\n def self.parse_ip(host : String) : IPv4Address\n # Eight groups of up to four hex digits, `::` once for a run of zero\n # groups, and a dotted IPv4 tail for the last two (`::ffff:1.2.3.4`). A\n # zone (`fe80::1%eth0`) is refused by name: it names an interface, and\n # this parser has no way to ask the platform which.\n def self.parse_ipv6(host : String) : IPv6Address\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # The dotted-decimal reading alone, with the refusal's sentence as the\n # answer instead of a panic: the resolver reads the addresses a hosts\n # file and a resolv.conf write, and a line it cannot read is skipped\n # there, not the end of the program.\n def self.read_ipv4(host : String) : IPv4Address | String\n # The reading alone, the refusal's sentence as the answer: see\n # `read_ipv4`.\n def self.read_ipv6(host : String) : IPv6Address | String\n # The bounds on a read's and a write's wait, in milliseconds; nil waits\n # as long as it takes.\n def read_timeout_ms : Int32 | ::Nil\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n def recv(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def recv(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # The other end's address and port, as `local_address` and\n # `local_port` give this end's: what a server logs a client by. A unix\n # socket's peer has a path at most, often none (\"\"), and no port. Nil\n # when the other end is gone - the connection was reset or never made -\n # which is the peer's doing and no fault of the program's.\n def remote_address : String | ::Nil\n def remote_port : Int32 | ::Nil\n # Every address *host* stands for, by the machine's resolver, in the\n # resolver's order (see `Resolver#resolve`); `connect` tries them in\n # `attempt_order`'s.\n def self.resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def send(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def send(contents : String) : Int32 | Cancelled | SocketError\n # A host and port as the address a call takes, and its family: an IPv6\n # literal bare or in brackets, anything else IPv4.\n def self.sockaddr(host : String, port : Int32) : Tuple(Pointer(UInt8), Int32)\n # This socket as a stream (`SocketIO`), the same one every time, so\n # what one read took ahead the next reads. It was a stream over the\n # descriptor that read a socket with nothing yet to read as failed.\n def to_io : Std::Socket::SocketIO\n def to_unsafe : Int32\n # Whether this is a unix socket, which has a path where TCP has a port:\n # `remote_port` and `local_port` refuse it. std/tls asks before it keys\n # a session's tickets by the port they came from.\n def unix? : ::Bool\n def write(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def write(contents : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms : Int32 | ::Nil\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\npub alias ResolvConf = Std::Dns::ResolvConf\n\n# A stub resolver: a host name's addresses, from a hosts file and then\n# from name servers, as the C library's resolver finds them - but iyi's,\n# so asking costs no library (see the header of this file).\n#\n# `Resolver.system` is the machine's: `/etc/resolv.conf` and `/etc/hosts`\n# on Linux and darwin, the registry and\n# `%SystemRoot%\\System32\\drivers\\etc\\hosts` on Windows (see\n# `__sys_dns_config` for what the registry can and cannot say). A\n# resolver built with `new` or `from_config` asks exactly what it was\n# given, which is how a test asks a server it runs itself.\n#\n# `resolve` answers every address for a name, IPv4 first and then IPv6,\n# each family in the order the hosts file or the server gave it;\n# `connect` races them in `IyiSocket.attempt_order`'s order:\n#\n# 1. An address, or `localhost` (127.0.0.1 and ::1: RFC 6761 6.3 keeps it\n# off the network), is itself. Anything that is not a host name is\n# `invalid_name?`.\n# 2. The hosts file: every line naming the host, in any case. A name\n# found there is not asked about.\n# 3. The names the search list and `ndots` make of it (`DnsName.candidates`),\n# in order. Each is asked for A and then AAAA records; NXDOMAIN or no\n# records moves on to the next, and any other failure ends the lookup\n# with that failure - a server that timed out on `db.corp.example` must\n# not turn the answer into `db.example`'s (musl's rule; glibc moves on).\n#\n# A and AAAA are asked one after the other, and `connect` starts its race\n# once both have answered. RFC 8305 section 3 asks both at once and\n# starts connecting on the A answer when AAAA is 50 ms behind it; that\n# makes a lookup answer in pieces, and saves the AAAA query's round trip\n# at most - 5 to 65 ms uncached for five names through systemd-resolved\n# on the machine this was written on, 0 to 1 ms cached (dig's query\n# times) - where the race saves what an address that drops SYNs costs,\n# the whole timeout. Asked in turn, an NXDOMAIN for A spares the AAAA\n# query, as it does for most of a search list's candidates.\n#\n# A query is one UDP datagram from a fresh socket (so a fresh source\n# port), with an ID from `Random::Secure`, to each server in turn, each\n# waited for `timeout_ms`, and the round repeated `attempts` times. A\n# datagram that does not carry the ID and the question is ignored and the\n# wait goes on; one from any other address never arrives, because the\n# socket is connected to the server. An answer with TC set is asked again\n# over TCP. SERVFAIL, REFUSED and a server that does not answer move on to\n# the next server; NXDOMAIN is the answer. An answer that cannot be read\n# is ignored like a forgery, and is the failure reported if nothing\n# better arrives before the timeout.\n#\n# Encrypted:\n# `encrypted(upstreams)` is the same resolver - hosts file, search list,\n# `ndots`, timeout and attempts - asking `DnsUpstream`s instead of its\n# plaintext servers: DNS over TLS or over HTTPS, std/secure_dns's\n# `DnsOverTls` and `DnsOverHttps`. Each upstream is asked in turn, the\n# round repeated `attempts` times, as the servers are; A and AAAA go in\n# one exchange. An answer over one is held to more than a datagram is:\n# the ID and the question as ever, and no TC and no byte past the message,\n# since a stream has room for any answer and its frame is the message.\n# The plaintext servers are not asked at all, not even when every\n# upstream is down, unless the caller passes *plaintext_fallback*: RFC\n# 8310 5's strict profile, where a lookup that cannot be private fails,\n# is the default, because falling back hands the very question the\n# program meant to keep private to anyone on the path, and a program\n# that would rather have an answer says so. With *plaintext_fallback*\n# (the opportunistic profile) the plaintext servers are asked only when\n# no upstream could be: one that answered, SERVFAIL included, has spoken.\n# One task at a time per resolver with upstreams: an upstream holds its\n# connection, as an `HTTPClient` does.\npub class Resolver\n protected def ask_upstreams_instead(upstreams : Array(DnsUpstream), plaintext_fallback : Bool) : Nil\n def attempts : Int32\n # *records* by ascending priority, those of one priority shuffled.\n def self.by_priority(records : Array(SvcbRecord)) : Array(SvcbRecord)\n # Every upstream's connections closed.\n def close : Nil\n # This resolver asking *upstreams* instead of its plaintext servers,\n # which are asked only with *plaintext_fallback*, and then only when no\n # upstream could be (see the class).\n def encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # The machine's resolver - its hosts file and search list - asking\n # *upstreams*; its name servers only with *plaintext_fallback*.\n def self.encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def self.encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # A resolver from a parsed resolv.conf and a hosts file's text. A\n # `nameserver` line this resolver cannot use - an IPv6 address with a\n # zone, a typo - is skipped as glibc skips one; with none left, the\n # servers a machine without a configuration is assumed to run.\n def self.from_config(config : ResolvConf, hosts : String = \"\") : Resolver\n # A resolver from a resolv.conf and a hosts file at these paths. A file\n # that is not there reads as empty, as it does to the C library.\n def self.from_files(resolv_conf : String, hosts : String) : Resolver\n # The hosts file's text, read before any server is asked.\n def hosts : String\n # The HTTPS records (RFC 9460 section 9) of the origin `https://host:port`,\n # by SVCB resolution (section 3): the question is *host* for port 443\n # and `_port._https.host` for any other (section 9.1); an AliasMode\n # answer is followed to its target's HTTPS records, at most\n # `SvcbAnswer::MAX_ALIASES` of them, and CNAMEs on the way as `txt`\n # follows them; and the ServiceMode records found are answered by\n # priority, those of one priority in a random order (section 2.4.1).\n # The name is asked as written, as for `txt`, over the upstreams when\n # the resolver has them.\n #\n # Of an AliasMode set one record is followed, at random where there are\n # several (section 2.4.2), and any ServiceMode record beside it is\n # passed over (section 2.4.1). A target that has no HTTPS records ends\n # resolution there, with none: the answer's `name` is the target, which\n # section 3 still has a client connect to. An answer with a record\n # `SvcbRecord.parse` refuses is the whole set refused, `malformed?`, as\n # section 2.2 has it; so is a chain longer than the bound. NXDOMAIN and\n # NODATA for the origin's own name are `not_found?` and `no_address?`,\n # as they are for an address.\n def https_records(host : String, port : Int32 = 443) : SvcbAnswer | DnsError | Cancelled\n # *nameservers* are addresses, each with an optional port:\n # `\"192.0.2.1\"`, `\"192.0.2.1:5353\"`, `\"2001:db8::1\"`,\n # `\"[2001:db8::1]:5353\"`. One that is not is refused here, by name: it is\n # the program's own mistake. None at all is a resolver that reads only\n # *hosts*.\n def initialize(nameservers : Array(String), search : Array(String) = [] of String, ndots : Int32 = 1, timeout_ms : Int32 = 5000, attempts : Int32 = 2, hosts : String = \"\")\n # What NODATA for *type* says the name lacks: its records, by name, for\n # the types asked other than as addresses.\n def self.missing(type : Int32) : String\n def nameservers : Array(String)\n def ndots : Int32\n # The monotonic clock in milliseconds: the runtime's, which its sleep\n # queue keeps deadlines by. A target without the runtime has no socket\n # to wait on either (`__sys_dgram_socket` refuses there), so nothing\n # there reads a time.\n def self.now_ms : Int64\n # The answer to one question over plaintext: each server in turn, the\n # round repeated `attempts` times, until one answers NOERROR or\n # NXDOMAIN. The failure answered otherwise is the last server's.\n protected def plaintext(name : String, type : Int32) : Tuple(DnsMessage, String) | DnsError | Cancelled\n # Whether `nameservers` are asked when no upstream could be (RFC 8310's\n # opportunistic profile); false, the strict one, by default.\n def plaintext_fallback : Bool\n def self.rcode_name(rcode : Int32) : String\n # What an answer's RCODE makes of it: the message for NOERROR, and the\n # failure it says otherwise.\n def self.read_rcode(message : DnsMessage, name : String, label : String) : Tuple(DnsMessage, String) | DnsError\n # An answer that came over a stream, read: see `ask_upstream`.\n def self.read_streamed(bytes : Bytes, id : UInt16, name : String, type : Int32, label : String) : Tuple(DnsMessage, String) | DnsError\n # Every address *host* stands for: see the class's comment for the\n # order things are asked in.\n def resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def search : Array(String)\n # A name server written as an address with an optional port, or the\n # sentence saying why it is not one.\n def self.server(text : String) : NameServer | String\n # The machine's resolver, read afresh each time: a configuration the\n # network manager rewrote is the one the next lookup uses.\n def self.system : Resolver\n def timeout_ms : Int32\n # The TXT records of *name*, each one's character-strings joined\n # (`DnsRecord.character_strings`), in the order the answer gave them,\n # CNAMEs followed as `resolve` follows them. The name is asked as\n # written - no hosts file, no search list - since a TXT name\n # (`_acme-challenge.example.com`, a domain's SPF) is written in full.\n def txt(name : String) : Array(String) | DnsError | Cancelled\n # The encrypted upstreams asked instead of `nameservers`; none for a\n # plaintext resolver.\n def upstreams : Array(DnsUpstream)\nend\n\n# A connection's own failure: which operation, and the platform's error\n# number for why - or, for a host name that could not be turned into an\n# address, the resolver's `DnsError`, and no number (`code` 0); or, for a\n# protocol layered on the connection (TLS), that protocol's own sentence,\n# and no number either; or, for a name whose addresses were all tried and\n# none connected, every one of them with how its attempt ended.\npub struct SocketError\n def code : Int32\n # The peer is gone: it reset the connection, or closed it and the write\n # found the pipe broken, or the connection was aborted.\n def disconnected? : ::Bool\n # Why the host's name did not resolve; nil for every other failure.\n def dns_error : DnsError | ::Nil\n # A connect to a name none of whose addresses took the connection:\n # *code* is what `refused?` and `timed_out?` read, and *tried* the\n # sentence naming every address.\n def self.every_address(operation : String, code : Int32, tried : String) : SocketError\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil, tried : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil)\n def initialize(operation : String, code : Int32)\n # What the protocol over the connection said went wrong; nil otherwise.\n def layer_reason : String | ::Nil\n # A failure of the protocol over the connection, in its words: `cannot\n # read from example.com: tls: a record that does not decrypt`.\n def self.layered(operation : String, reason : String) : SocketError\n def operation : String\n def reason : String\n def refused? : ::Bool\n # A wait for the peer that ran past its socket's timeout, carrying the\n # platform's own number for it so `reason` and `timed_out?` read it as\n # they read the kernel's.\n def self.timed_out(operation : String) : SocketError\n # The peer did not answer in time: a connect the network timed out, or a\n # read or write past its socket's timeout.\n def timed_out? : ::Bool\n # For a connect to a name that stood for more than one address: each\n # address in the order it was tried and how its attempt ended -\n # `[2001:db8::1]:443 timed out; 192.0.2.1:443 connection refused` - which\n # `reason` answers. Nil for every other failure.\n def tried : String | ::Nil\n def unreachable? : ::Bool\n # The host was a name, and no address came of it: `dns_error` says why\n # (`not_found?` for NXDOMAIN, `server_failure?` for SERVFAIL, ...).\n def unresolved? : ::Bool\nend\n\n# A socket as a stream, for what is written against streams: `read_line`,\n# `puts`, `IyiIO.copy` between it and a file or a pipe. Its reads and\n# writes are the socket's own, which park the task and keep its\n# timeouts. A stream has no error to answer, so a read the socket refuses\n# ends the stream, `read_failed?` true, and a write it refuses panics,\n# naming the reason; a program that answers those reads the socket.\n# Writes are buffered: `flush`, or `IyiIO.copy`, which flushes, sends\n# them. Closing it closes the socket.\npub class SocketIO < ::IyiIO\n def initialize(socket : IyiSocket, buffer_size : Int32 = 4096)\n def socket : IyiSocket\nend\n\nimpl Error for SocketError\n def message : String\nend\n" }, { @@ -728,7 +728,7 @@ }, { "path": "src/iyi/concurrency.iyi", - "sha256": "0a2057fcd85011a1bdbd098176792092b1e5a5a4e0e9ddee6d23670e4fbf5214" + "sha256": "197f8f27f21e424be85d1f8b5d607659771202d51712f5304f7012125d44abd8" }, { "path": "src/iyi/enum.iyi", @@ -764,7 +764,7 @@ }, { "path": "src/iyi/prelude.iyi", - "sha256": "92596c2c55ebd108cc41a422f58d9ebd17be67f28639c8ad67aeae7482a99d14" + "sha256": "2c724ca28f93ee1260c649727ed33f201960b04ca64108a0e3fb07d7a52f051e" }, { "path": "src/iyi/primitives.iyi", diff --git a/records/wasm/arrays.wasm b/records/wasm/arrays.wasm index 43494561405d4e6227393419c1a7677d06657476..d529bc619209c4b754a3300398e1aa627e1b64b8 100755 GIT binary patch delta 1793 zcmbVMT})g>6rM9@=k~H~1C{_vi`|44kg~hnO(jsnPeO=@TKmvOh`&+PkVvA<_QAyD zYKf*)ZAfXD2Tf_!#MrW3*vd|`@xc%^#gZ;dfd(wWHrVt*UfMLq2dQW7?e4{-FM5;N z^PQhL=bLk8cHxe>aK{||vJ}pp|Eg3bs~fEDS}XFJ^#=1kAn3opBDImapk)PXcMo=F zQpLIOmjYGmT)SsJ2WxP8rWvgB1y{mAqGdL%MZtWt7W=M-If)Mz9PNk2{rjO;7KLFu z9F&ifXnL=*Z3|;~kG?}x+PBL1aDNG7dSSoo%n)uS=Z00JUp8zIQN1vxj)$QDUT)p# zB)MC`*yeR{2T9Qj+ub}^`EP%)fHA$W+x0(0{sXk%A<>l2m|pnXkOkXdO_o{k4z$W+ zTC0+0Ef|G_d@%wI5K%)B=omr{Is5|bMjj)rsdDtIHzvb zLmR*;S+fUbDABz=a0^bUTTeivwpKL4_D2>-h0tedf}_;m&Q0>4i!4dLt}y7*tIy zaH$6@=|2Mtc{iHg^!da_oKkwVP9|+0A*m)=$ki;+N`Uih!asKqwF&uL9JW?3 zQZc^C@lblI=A-`Nif1L0ZaFT?6LBbqzvYEEB=NiPk(P-cmDi^?*J~%;MM-#~9Nr{qmT6A3v6(57G2xIcn zckof!n5G4OGiBgq%JB?fLgynK#f0h|hoXF251C?k`4)G$ec(>lnR|tYqt477>+_y9 zl(Q09eJON+7kV*g8_6Nwekd`zvr&sU#G-^@BM@-`~9_b0fH zFfMB+p~xPmE6xkEgK7xT?pb~7sUw{Ro~1i~0B|l%qzR{Th__O&$xOFh$CP_97dX7??CehW9NGzWEU5>9X!mhVnM@vbF0MGaN_ zn!0D;wWOUAMeDSv*pSmyVhVaWjHy5x*5Lmncr^pxXDi$Pp>opB&cS`u@0Zd+#&m!4 zv;Eb7Sg2c05xT#>P&+#4TuG;sq?so*4!Ao66DlzaznXAd9$SJEYncKD=*d%rfuw{S nsL{#;P!vzA`6YPeDX@luyhuJ>$v3o?1X#%+U)b~yba(n? delta 1793 zcmbVMYiL|W6rM9@=k{jXlGu<$)5b2O&DJ#gB*vs650GFRZS9X16RZ#DLm~#OZ2t+j zH7GV}3ASWflm^rkcJ1zsiSCufA0j>ynwWWoNI0%{jwpAh^mFd*1I7nfoGey z+DUHJFt)xR?iEth!gePQ`v2_@lrW|i9&r5kk$*q!w@I`XGo}{)I2eIV@Q195z{}7q zPbtkOKaRi%B;WgTpRB@S;{ zt?h8B2g1^ufNarH>k*Gfti^fj*90_iSe30wsDNK=QTyBX4D@d9WdI&zf~&UU@o5GE zy?jgQF%AD7F?a~DPa105uqBgl5<;m71{%($4IU+FPOzY(ML;P&&T|R>+(Ohc@<|gm z*3MBezRvbgdaCB3{^GK0C6j77F3K||RKqR#xe232X2zz26?{8!?Gs-8X$|A1!J9xl zgo`uGr!;-QhQiw^cIutB<)ZNB-7&E<@9(*%zbEhS2PYt7?>C(MG0_iBgwT?TMzP*4 zg!Y>Df`%Va`ItZr|C(VzUYTu~%Qf<;O@BMUFu%Xe}CErgAKP3eD900k36+m<3=>xGXHZsbs3B7fuioGcw-21@+Hi8SF%9`Q%1*JP&Ku41a)m~od& z`bgq=$Nz(N53j3TLf(dJM9xm(9FHc>8ipb9tFreW%iqMdh ppbZsTxd)2jIcwnxJhuzNg8^P9ANTVU4zzyq^C|)1bbx2q{R5xn`Kfx)Z4IoHqUHMj?B-MUxZ*HfAP7$j2D{n>Rn?B!TRU2R=` zwd+>bCQ^Nu&Vie1yu1zEs~wj;pke9CHg2%=HTqvld!{nDYabj|-*PC0x76=uIHnwg zVmPD9k+QVjfv^T(yK1p0ZBj?ga8UJFQ2fN5%Bt0jVWyC={fsedk@4P2V9Y$$zxt*H zx6RBelK3jrHxgQ5L_HaS2B=d9BG9vSwA?31*o-|!1d&Cgf7ZLPm@)HM59$-oGWJ)n z0P8I&V9Y$$ta5E4o>F^kk~FK|v>}F|)kpb+YCVt-rvNVK#430VU_|$utGu7X%Gif4&#+Cq)+o9YUs!i~_q= z2-zv$ka(V!PYRUSInCmH-SXyJx6o$#1NFQ*`m4xm6zlEV5@$&K9-!GcNa`it9P;Ag z1&ITGM%APrioNzNoY_IC$O`~*OEyaUj+TpI+H9OcUQe$e$CDLBE3)K@HFU|lFvek6 z-FYA0D;YMjz_GIuCx~Mg;E3U)IEf>=^AvP*?<+C_U$f$cJwql&AB`^W(fAf;jAMwb zMdFv_m;HdVlSD>wHW%^jB$Hrris>l}dv1hwd2u(fO$Pq6<1Hee4r_?IHvvu?MvCYC zh_4D{f$>oXT~3_Q*)Aw`JY!AL!pqhMmOKf8Cw;eW2;-#hR3CjQMB_P>PS{x4G=!}; za@asF;;rFyzGZiqgwuF=h@K61TYZ#tKhSGlgt<@kVdp*6`r;>W*m}O#>`6GhDk}1i zh(cZy6%ly|f1>_PbgEyFPKiH`dRbApB1=-!a!v316rM&H)~7!M51KzVM@;T^cm;?E z-&`mXd_z>?T!!uuT`oFnKZ*OHf#)9*C9zNM?S+fY^r`vaE)=V^ci}j=n&NKy3hVWM Iz$=me0pO6QfB*mh delta 1505 zcmZuxQEU`N7@mLrow@B+C~}sD*xEBS6t47oJ3+2QG}>bFQ2#NhFeo-)>HJ9O@sr7=c9pt&4>v8?Y7RDa= z3$S!m8Dr+QCRC$M#A9l&O_C<``!+-n^6KkSLi2jC6ixvQ=;R7G1d!7MD`7i8pT1KD zKX*c0eg7f+5W2gy!{T^Hw;qEofI0R1M^FI|ydnDN1_QgDI}E_GnBf1>g(q$@5a{q1 z!bc=7j<~!5ut!yQ(X@f*Q!J;%&SJguiS^E6y%+q3f;Zpw`BS17{0V79Z=zRF#}Rm)!3OJrj7(D2e8jc@U~aSV~Q zNgO4=8~~iSNn{ikiV=T>WD-nHF+DXw&sboW*Y*(GHQ#@BtWA{CVQK2#2smySDW3Hr zzQUIU#z)=sapHAd=!I&>HP$38yk@m;$sX`M>A7`X5T`t+dg$u`8Y`l7!p6#`A!r>d zVuQtqw+GYtraeItOyjiydOFx`{ZZ2WSg&~z=6h-iI{(JJ&U^#!SuZQIC*knAuqZtu z%6UUrgk%TKQU69d)pMj%;_PL&Aj+3zDQa4(>HXiriwIf$`FG$#Yp*$Ca<{|Rg9!2F za*^cg!V>R|(03&944t)?#JteJvrmXpct-C_L#CB}HDBI`O11hv9D}%~xR3tAdhMU^ GR_K4{+NSvc diff --git a/records/wasm/bounds.wasm b/records/wasm/bounds.wasm index 0d6178a84f6ec9b5d1f4257976cca63df062057d..810f8fdf438ca9487dcc651d1910bdb41f0e5c4d 100755 GIT binary patch delta 1628 zcmZuxTTC2P7(U;eJ+rfmu*kB27G(`hX-nB0XL%tgEf7uQAP< z+O548hWcRkJ~q4q=6L&X97G0FGm5mQD~R-dJn}nCi9b+0eqr?0=W$s2byVP}tRm8Q ztg0l$82^ucZHzJB3e)=cmolaf?{UEmd5rBK(7(dzpY;Qbsl!wDNgaj2PWh3aLN>_> zB99+%y^_U@?O6@^FeOok-`MQrJ6^H|Tu-so;s45zfguX_g@JNxl-CW6U`+M|(11Go z%K(~vuw_93fwo;;fWtsWo(oaxjJy&;dE^- zoH|v9FUW8q+9*^{A@)()r9#}qr}o_a@F9?KoPBrv@@ydysZ`op@xaPeX2El{c z`f!`h{~WQjIQaWBEULF^5xQs}Cy)tk6ODnc{H%o@#4}S2I-kl|S{+e)ip5;Xgc72f z<~Wkqz9RCvDtC8c-RAG78MOP>ggitu;!B>E?A6%#qWqu}mAE6vI&s~)I_uQOOSNhk ztz9B$yr}bwX-jK>*u)oSSyV~J8HW|YX3=CcJDOn;%)v>~l*1GE#1lFE6kLO><8Qg~ zlj0Oy3!$e}86l%p2>pb9NayD%eNu3ppO|AYtz@Y!rfXBf3leuA&?M8BGY2x`?~k=G_7T0AWL`a%8^^|*!V8KAq>`HvYZDMOeDi6 zGBjXjwh(hga3qs0p`(*;MR&AsVV81dd4scdmh_h$IXcr0F5Q(IdT zi}#+r*pFE6Gb6hZ8*dwlf=DVoqexq(oJjB2W4|LNaTgoL&t7`_RUDRH9pyMGD~Z$} zuPhEQ#{Z*VD`U)ahiQY&C5)-VI~?QYJjQmD(R+u}KN|!XQ-`0oPULOV+YnCAEzYh@P#Tn--+UN#!ZB!4*yFA3|u6*?+lb7EEf!nAtk$g*nukR zdmkD+$jE{MGG?sm0=xr^%Dw=l9+lSuD65}#Qa60S*fA$H2Ga$Mslz86<6cVr6dBjL zIYFJO!>47i5Um8&U5G=JcBv4*;VbLr!|=d2Y6pAndF7cxJSzV#!gg}04dOCBx0Zsq zl<>!*@4wd^O?;SO^4)fnN~;|y->v!C(7c!oi6 zzqTpZs`GnerWOV7oMBuUJht@$Jp&9W-S4;kCY@(s&QhqI+{QZ zCo4+UNy&9NteH-1g$`=3t diff --git a/records/wasm/case.wasm b/records/wasm/case.wasm index 2d2c723fe7204e6c404dfa21041b0c8d1ea4de97..bd2b5c10444b0c3c1e2320641a1d64099b3bbaf2 100755 GIT binary patch delta 1508 zcmZuxZ)jUp6u;-5+?&^|X}ZNU4l4#S)>_-NFLa$#HqoKf`e%X+*AM$5b5eAEn3*<-cd6vRRPtwgYGLNWQ?+L@HGAwM9d`0X`=#{nXJgQruAj^|RItOb=43~*tIf9C zIy%$WC(nSL?mzbk*cZM!w+2KqQw2j_ojJe_nEm#7IHrbta3363lZL#hmVD3%L#kw? zX)OiR1MF8(Nn*F!P2}MtO=CgE@Xs(~2N`3&4aP^~A;!$X>n*PGcBD$V0~j+0Z>x_b zd~OxvT~6s*hNbJ2GTzpJsxGu99&h~Lhw3_=~E#%1(4RktuWjN zwmSV5d{guDzN0>$khr2>c^hg0Hq=NzMB%1OT5ertVD<6<1Mnde{2%(^2L%QK1AIsL zgv39x4sQqiP!)~aw7$^~L(raEVjyuT=kO#^y~Gk_$p$0DINuC-bt_f4pvF(aJuN>{ zGG6OyC_H7mK^@0+4<*P90oPRNB*fuw9dKX}qH|Oa4^{a+x}A*X^u#;R%3(nj2jDJe zeAeN6K-`aO%PfW@StBkh0zIPJ>T!`N5vZtBqPwE+yQ99Zq92B`LH>XK!%#L5a@y*3 z21Ur~6+#ZkmnCK>eM+Fjfg(%rhRtSt8K=5GUoLmIE&Pk9*(wtipPxixq6D!tq|uoj>@DCwOkW$wNoHh0_l8_`iD1oy?AQd6qX(>mda{q4R~= zs`{#}`uZXAbkXqUz&e@;S~1Uo8gxkFd3p>70apvuAmZ~Cj_;?Q6HL#**u*`XMXxmT zys==SVZgfkkk~?#pC#*7z@mvr{5lpDWFP`7I+}sQ@Yty7ya@0I!eYw_5#jA&;g?78 z8r`auQcloglXx}fSYp>?6#68}KJ0@w(%RM!t;~uFZN|hQ3Jw(io%^1BFTw&h=bYd9 zd(S!dU8?vmRs6-CS~xfJXziJ7O*-9i>y*zcb)O^c!)85@au%F{5w*m+yRW~gE!QB z65fZrYW7pOyz2Htk%Is3r<5_(V8BO^ur3?W=#i6FcLFs;Vw$Yol6TnCMN z7n}-qonY)`rvezCsAEhYoO6_ORKYG%`mZDXv+-A>jOl|5YFh|igE8w=2u=aKV+FUu za6hEf*cx!((t; z;wwtVYi$jMr)<}#Oc;b{j_To|D!&J|lTpr^I1ViwN^1Ee z++yYmCcg*7ow&NhVn~uPVzVO9Bf5ffD~%VoBbx*^E!(RQLFj)7{qahN#&iwi~N794h{CAa6WJ z)l0lmFq7h8iepQp&Dg9sV614{jHq#Bu_E_K{D!inV>qcVPCW0Vk+^Y6B3dP7s&ZJ7 zx8QZWJ*Vq|fKigTLeBdE^Ewdm8O&Sf--bxY)Z-=_9^dASc@UgA+Z(Xc<1_a3ZS*M* zO}eNNb6L-H&c&u&j`w=*{O-p*!E@tH9y;PJoMlpv|EpKuN$+T$?A+d!fZ;^EiU|B~bejUpSvJioJE1HGF@SxJ27Xf}(SZp~eBD^gu{IU-> z=vFP1GE0w5;`*#v5|Jt?Lq?lAYxVN|^22WdyXCe3qeO|t;(5KHPjn>Fj_|pd|>)oH=qWl-PGplj{ diff --git a/records/wasm/classes.wasm b/records/wasm/classes.wasm index cababa4959b89cb0479ced8ac98567410a733235..78cf67e6c8a1221dc88d103bf8bd8fee585eb30e 100755 GIT binary patch delta 1587 zcmZ`(Yitx%6u#%4y?1A~6o~6mm1@@%p%uFAY?^9~Kw^WGDyT68D)AA6nEin%WExP= z(GnAd#6l-#e2}!oSBx!vZ0IEWN96}$Mcbk+T2n2W;vX$07!!-M>$x*_W@E6)?fuR< z-#Pc(GvD3I=faoIh5M(%a58f-{Ks37dVOb&o_bn;#+vL(K&>^`m0jlh9#2%HYEpGo zx?WXNYkk%IF6dVJ#Cp&(AD^fO+9uxnU2VA!Tx<5jFCT^7)|sniu-E$KYT3HyS3OzG z80P7(l`$5YVZ3uw31e#E>t1k+U~D}F!!t<#StkHvYGH4t;qRsJM8h3!jh7cQMzws} zQ)H-xjb52I{u38V8B+^)dhz2F-%9yz54S8~Of9@<4O}aQ`ONUON`MCIuenmFu;TOm zFle2buZL7ez8=P{d-A0)YHiDx!FXn0z9@8?v%>8u*TUF-uO@VUwwy7w&~dsDb^{FA ziv^U?mK4wdUC!qM?gX%$-y|GMgKodn0-r{HUbib05(2-o@81J&nlr-;v}PWq34~B! zlnJhehgbf`KU?TuAip-kfO7eYSgXW8`%PXAcw~enWHXN!6n=yTkG;g@_bM-^bmy_X za1b6C$}y0b8!~x{b|=S@o~45_5}dnLh~Kt|N}T?Jj)SPy#;38M{3|qY{NBFZfcTBm z)FxlFkflBaPT5Tcl*63!oB{7}^Yp0OGnVkxG=M`Qs_l^2-DmQ85S2JJ$`Z=bj=E4p zn?$45d26}u!NYsJmlw-U#w`7J=%c#^u8_;sI|6dNS=xE4hX7mA0q z3plosOcC)M;(}Z&@f$iWfxYVD1bH2O2)|BNloVJ-!5Plx{jeNiz)l~4S8p9qZPAy> zN*tyd4+0LVdV~!ew2KbH+vb?k3Q@L8{DhRU6>yxCqHxwnynzlUH>p5z$^*{XKni*# z&0)k-p0-^S(FFR4yaw>RDoA+RMZDP42qrLUt_of6w!oY8y)-u^Q$+}&EEDQ9Cl9Dw^e)fGjQua1eLRuSjbF%g!#@CQm?LqmR_+Lkyu zWM)OYz%q!m5OYGupq+2(P?IL^K%vOit+ti#m2T(&zCZW z1^R1ejQOrI-n*@gF}3ip6Wk#f+eAVCRiyu{7l1Lfus^f;@1^kE`upq}hZZwNwOrZ@ zWT=G?Ic47YPh2czOf7uIiJzkQPRh4?xN8YxYT;MLM>oph&&snNW&^A~UTA=ELJS19q_lU`x>lxt;dzf%)>KUvP0T9~%_5MBj1 zYc3X0L0e3~aX4XpDquCh5$k6O$5IeC-)e=AgO@k9_|Y=sy2OBT`HFD6#Q71O*8+B4Vo}-5BSnRuq`_l9aruMF%PHM@ z`gJ%6kDkpjkod(pohNBe=UB|K5}=GI=XMq1cPye3+0W@X2n9O$3>K9?OasR+&3iS7 z+@7a4`J#m^^(k=5Y|@|-{;*!t;3U_x6L!y7#@EmQIz=e3N8(^w=M5mLacY7^l@&N@ zLlJ5cje#cHb4G;RuqGPac~GnhnJh zfl177B~t_)hnSQbBz{fDMX_I9oFK2K58>9yin1b0D>}p4z8{t&44SC}@b=w5bRC=6IPYQ`@ zn!{&~@&da>2~EHv@>;;0DoA+7M!eL~2qItIC`^{ zxYEO#twP(o%k#!uFU?KKR1rMtsOvl9BHrw!^ZU1ZhL^@ydETSmZPm-ifBRMARd1!< zWBx`z0*@bo?+}K~L^niW*xK3+2jF2-T~UPiny@Ho7ZF|?7Jk`+vy{G;hJ1?JmN;`x z&x%Nqr4eZ%X8DdmH{aH!CQZ@}@hT90zAhr__{y-vA4cerNbFJ1T^TVODl~Hoq+ySh eH(=iu`Z@Y(8p_SqX?O$T7UAnY+GoFkp#LA&#=!~z diff --git a/records/wasm/conditions.wasm b/records/wasm/conditions.wasm index 8b23f3e63304808564863021777581727116d93b..5e08ae14ce128b59aef9d8767ecc92907a87f93f 100755 GIT binary patch delta 1519 zcmZuxe@s<%6#t&@dEf87JVC6t$J2@W8mTDq;Jy*%A7+VdMC#g#)HPj8W3p*Bx?sB6 z-C|Z+ZOYxewQxXT&Iy7rxb>=k{7YQ(VS#R0f)4wm#vie@)|Sxu-rKvEHSTuq`F+mk z`#tC0_N>;P)%qtjxHNcIoBT9yqp`ozNWW^lW?$<{LY4h{SEeYi?MarVE7R5GhEZNw zWqkRbWJjwtf$OKn6iyvee2#oSaW6cu( z?6-IY;D^o*mDcMvm~a9rhDR7k92vHFnz%c{Qod$@Qj(nK3Lej=^ImaQw!rfBKT$KD z?|G;^b!$^In+mODQWu63&b}5Xfd|g}E$|gwcAq)|-C$iA^7#EZ1#^?>790!?xzka!KPH<@X@RZK}3b2Y^B+gnsE>wh%*F0qIv z-ABP20B@**Bu_2%L|))8Kd9w5)o)_VEo*~4u%$!YyomC$m{`;-;=CdzG}(Z^(5W^M zf^ND^i9Zip84;hSnG}@sxz}uXnm^m7V9>rsc_|2uKOPrVd`(Q^L_d8Q^tliqPf2;A mGEXfanb_sNcm`&+(m&+RZ76p3-G*aexFjzX#Q&OvbMjx7p0vUM delta 1519 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 X&QheyU*o&!Nj6V#P@j(RBRT&8p~kj? diff --git a/records/wasm/conversions.wasm b/records/wasm/conversions.wasm index d25ef42e997f4884647529fa63b1c13f39e8a7d2..c87bdb59163013b9eddd07e2cb782f4c4548eeab 100755 GIT binary patch delta 1606 zcmZ`(eQZ-z6u;-(-nZ_Ruwm)8=r*hSVDizebrcqKBP^1w0b|q&n}Ue};uJ73B?>Ni z%0eQj$<)M4GLt$6lEAihi}Ygs!)(U5CCWD4KoK@2`^$tFN&JKOdG32#Urn&d%{k|H ze)qg{&qq&Q^qsuuORYCys5@$^s0okP=Qo7+)P*C@h4;#L8zBhwa-rMj^VzJC%9 z$j|!VUU*slsKsIVM<0~JaanSTtUfiZpf-|CBgXom9U8k_XeUdB2IClVyC5nCT#BP-893P$AY8EAlrD)<;; z9#FF59HpStE9c+<?}7-I(=CnRdf zp%0&uO#_tuta^3;4g;j+4};JSBkI8+xC-!*TAqTp4R~MmreX92s?o0da4zpg<35jv ze=WD%kCWETOANg2Xfgl~7u;k5|Dhkh^*{LSTzrZA<|G5UijtGfNL+kKqe$0PPX%^CZF+)%!6(udMo6j&7ILgGXd{SdMs43*f2q?|nDkj=Q+Bqz7}wC#yM^g}+TeXj3mZT@ zz~`q}P+Q&}TNP$Zw0L7SG9b*XJ1$zX{?0r4JG1^1;1DwQ{bf%4xHtin*<2Wl%08se z#RWI`(P1#yCL@Y-toVOE(c@I!D^!033potSPuAnHvSD38FufUr zU!ri^fRE_Vq{sP)dMb*60!z1)I9PF))5>medTDpL+STKBhi!C+i-xi&<*i3R6I|+e z78}TF*zR7(joV$ry@sn?bn$7NhGhRJ)p;>r{U$w!QI~m(E45)O{>-fldUgwQChoN^ zx@n{SrKagE<}-RBs6aanexBB$4frgrgVr-y4V$S81kdP4J_re7gc>*gDhSiQXw2UnY-3ejh8o%%z5&^9B zi#%f=zfP}QODUYD>Kgo7+RBJPjuj`;l24U9gSX3C-_v=}44YU3A`dqNL_Jpf4L+Zu xZJ`y?8?TL!t!i!I4qBY|sg5I9z1{iG{=gBXvglJ>dA4O7g#!fC?r$)&{9o7W$r%6u delta 1606 zcmZ`(eMp^E7(dT*Jjd(nnAY7@DYaXE%-nX>o4amUDjOSm*&-_2v~B%SO0KmC?x>dV zZpr@03hbYb!$3Aa!i##PVrSj{2#c7)OpDUxOeh?|AYws5U(|EnSMPg+b>Z+l&+qx2 z=Y7uear-Z4_g~JQ+G)XPPsGv@3m&b_3WsYuo~f(td|JKT06_?=_ZuLYmqL#QE5g;` z$c~zt9o1p=S|bcVwJL6cMyS@unjlA#Jh>k}L6V^sNC3&zDEv%PoxOrv^bZH%+T+ln zKE43=z;Simh!@qL7oZT%s)CDTjp$t$As3)twG5Hjq`HhaqFx_@!s2U{e+3xhZxu7v z${5S|iQ!-=FlG+_O@B57d!e|#!XdrT!B{)tM1sK;Vw=N*s&oXVAg;a{f!$E4b4DSW z0SVO}rxX(Ur8qnfS(96x2&cQ%^aSMBubWHHGIq=TDjOWgVayyp=W=g;%GgQQ34;~n zFo(~m#&ODiPCqpcuK-M{ZzrG);`)I}SO)0Vo2TGS32*3uOE7($YIO8oT+Li==*-CA zU#mU$;k$vNnXL#3etB0;Xjnj~vn5H_f=WE_%<}l(hf8hyTw;#Xf_Jo<;+_Z^%wVQ4_ zsDGnrW{de%vk+9E!;*hM>(B~(j@H5G$&`kBsS5fw-K;&o;;xsk;IPPE zl3WveJMdW(k@Ph_s}fNRz*YTu6i@hILWdv2QiL&ms1&0ZwM%wV1k$V+kv4p~;0XR+)Y5D6pc%HX0z@X(1w<{D`z8Nzinc}a v^Je3X5pqK-DUD7n6_=8eRq?L-58j2K38cD%El$$mo z50_}uY7A6d=t%#zf*RDs#Dwm^Qm~{iNwFrjSqsIoL1Li~p{=D41zUUOUU%<4xG+2C zeCN#hnfcCruw*@0vL^EuT%BIBD!+)dCAyjt$u|;jrXF61L31j0F1?0VM=KMZID!d3OEZHH7qGtMqQS$etXW(mFr;aZ9f;J zFH>T(aKSfrQLbmnXssaqv-DsUV`gDahb)NU23_OTH0dzbR|t|{$Cz2D^)4Ge1o%>a zR|dTtQhMiV80n!q`u;umCUSRscPJzz{;fXlgEXwDef>}c_r0zDpPLNAo!n*sA!LF- zq#vG`XCTn#HPK!=HtF(4z*FiYb8K9X_QOeN%*-*6IG1sGl6F1E;=Yyuqr^Bb1ngTy z70#&F2Vh@+=gdvJII#SPoEv%$s0dogaW|_*c&kKrZCYEO_-`72iU|9v61_ zHHqgYT;2wv4)Y5vX0-5WPZaiE(H`FGA!EWWnjO(zwD&yL-cz&>gYS^_&bxkmM+}4S zA>_CzV@2+Vr}>*iHlVHKA_Wh zm8zHc!-N|bFGw8oG_pFLC|(ZV#>G9Piii)0+j5)4A1Pf7$4%oT@)ml6Vw|j~D$$&h z9Qy7c%y77-F1!yz&s;NQfgR3Dyh$-$1e`MQNOo{a?-_zq-2IA(z{^V>?AuCe$U~!r z9{0Brv}hPZ!465hLVP(0n470yBxj2f-%Vu_OiVF8)q&4!Ko>J6IPdc>96uz==&~lr zdn@2A6OrU)PvTGbWr5+*5t<)d)b0t`XuF1*R12?u%BSRU@Hy$RbyFZa9#cK^)c}nb zQMzG6W#bUI&K9w;qQpCcjBk=+?|LKC0I|gqf$7!1F$h_FE}98rqdM zJqep{iHfpbQNbId!jj$i3;A!QTRl&8O1zVCv!bFzbI7S+(`(Pd*P)Ix=4y%6Hm?O? z@#iW;Gv5@In4hHip`oHX_LR6M8hH6Jl7r{;hR@-8yZ;@oP=DQn!AG(l{uSP(&$Olw H^nmq02ji?> delta 1538 zcmZuxU1(fI6rM9@XYSpb+D6wTl-713p>5J;vsq%AQrd=!G$~q?T5QnbgKbI*3c`)1 zwq!6@E`YKgaoH}?Sitz)d>MzkXNpNJy3xtlt9~xX z<|(mWIO7{nQm)6zXxv8nXW4-o#`MB@H)22>w-oBUnx<^V`bt4=ZDLF>EVu_Pco$&A z{iXtXIh=Fb*TZlR&1n7&_#%39e|IDzB>ty9>Vqt-sUv5h27dFlhCAOfFuS}l{Fir#t0k8g`K z_#Q%D)@5ukyM&N$$)QCUv8mhvVQfvF}5b6`@}|bN#ZgUe+1|> zj#Bj!Zx)<{c#`6H8d+^m6wjE8xO|9I5%mGFDECSHk)cax^um7GC>^Psu^>Iq9)=TPWKeQ$6(g5KROq zO;}S|JA|$?0X7jxyemA;+nx@U@EG47qUXbI^?Szq_3Dez@2NHP{F9#cnGC!VIjD3` z!s0t)qM}z+@urwCWH+vn|1O&9r&OoJ)hkX>RF!EqIhAbg#u2z0=}ha<600q~1BAhM zSBVzBEhh1oDSAH=FVKuVCE?s>Vq@f8S|QC_9eKrXy@hB+nwU<9MGkeRZE_MsJj*cM*ffb@#4 zO|cIa)mcNly4WoF60rNa`%jzw5hw{5S6k_jMm4r*(}tU^vgf=n_r7;VcYE%6zJJfh zId98MLd#6T=_5My4!*0GbmXlyb`%-DmyMmw`2n%|Jo$=z#RY~@P_(-3kCWl7n0u{f zWt{2j&)dOh+y2D{Fb0G4Psik`QFZinKg;R3)7x3SZP$uZDU9JA`YogM3A3m$gWr%+ z5!Nqkzmdt9B5ZK%dnnK~%XskZY{nGfVF?IJp~qZ?kOtdIAGWYc?`14G4(mk{6k&y( ztLiTM@-)U2;SSrro$Q;VSZh)lQ-s&$2QJ8ge`L1{&cJ|tNx<_^D$fd1u9Oi0UEnjD zHK=rfDMxhDn&ylSy8uFROA;(y7qYWoqA}dCv$J46Wmbd{+jymku{UfZ1kciZ6yccc zrvwm@<4Ld^j?1!SkR)A8CXf;NCmriX?D(Al#y*P1m!~tPD4Kk3KVvPnkq6i0P+vrN zURI@8$=^(&bP@CY6!;9F&GeKZ3@@`*b;I83118^Y|T&*7ujWQ6Z^M^9_XgGH|zzYG7$SD=p zW8Q9p!%!F+W}xA4D8PMmn#0U%O9m*x!+A7enHt(T*}oU=&-;PEc#>tI^n|vF7Kqc1 zmQ-q|I4N)Kgj1qTzQ0=fNJ;II+B#HWAAc7TIxWs$mzsfz5!CQR;#CaE#-nG;>nZ?N>gzj?a)&n)+7YsQsqg z-mT&u+I$~*uL2xb8Ht~^IG${m1?p|A&QLX&Q=dXDKu~UIhphZ-)YK|M?4)68cr_FV zi_AF5FA`#sxxXC}VUcQrr$_txejnw$AUlr2;=lz}P`cIL87gKIr5&*vQPOhjQhqzi zsQE#X*OQebYLg`QF~xi4tEE!a$L#KLJ+;taoRGNyedBO=ekgm^fdwL`(fr)iY6(r)B+w0TN;5Xd(Xx#iY`K~}@Aq-{-HdK~?tT9MoTs;@UA?qD$LlSueX+Q3*G^{tfLMKzydG~+UO_>g$9wqC&%+r}_Zrvz zF{b`6+Q4&oN7s7r^abgkj;q}x>In5b#p$@&+flW>c1?E@V|bH(t0;ZkbyS$abEH&+ zuPkk!OJ__G?zZjwD9~}8@!%Voj48rq2?#5p(^!R&0^3XOHL*6_&RAj$*7GDN!bh!K zmABYer!b}npR?>c$i5|lRh-P2BD^F&a6lF;$Q}oL1>N!m0nb2*JR?Zyl4AmnLypm` zL4^ZC^1M#kkTI*ns{p-nYXYn+>9w+7q%q7{*;z1`GAqJi%Q(@%*lU&%f@f$xif~Y# zq69E3FC@TT_(YZ^f+XpuL;@L*H|QuCu;Od|jC~l1FHdDmQ6%|%6Jr6($b-dM)E5z+ zm6b_m^4&?4ZrFG~2|fcjXn0fL%R1eYzjx2h{JQ;hOn$c?mdoF#VVPV}4{yg`eq@)! zAvBzn$LnDdX5^<0kO{w?;2p^C9b%y2h0}iSr86|d+?G@TN^o%=Nm!%heyq_Fq7v}%`HNAKC59@$j=`J@c>SYGMAE^ zhfG%J6=I{a!qoH#J!f&Jph(N*o^mE`F$b)mWR+zs4|kB)k0`*YA9)bAnbv#cu+N#$!~yhL=NrwMDv72W{l*E!7#2i!|Qlgm#PPJXIR5= z3Vs5xTO}gihuy}Ww;(;)ueMA+JUi9eZVOoJH5Y%M&HK!yo7%0m)*aPSmFiX~2itWp zs_ThzyxI=u4?SrMb{Jn{YY*A?th%0NyjZX5NnOn%+w%_$8C4&_6#7rl<>o0dm+<8W zDbm!YMHZ%>KAu;vfCjWp!#;WgTL6bgs0ZT1QI2n*nUk7szw*hpeFp8))VBi9S#Qee z-X?CN%^T#s7H~*qB!1fDc%oGnsJBs=rfM*zTA>C&%JprKkvl_8ttG@GG)xVrPy53n zJx210xTs__wLv^AQ%&&HNMDy4DQCZIKLX4B{i>i;v%PPqn9Y=Sz-&ZGtIbRK!w941 zzb1JdSxKTcNpc=lylb&qDph^VZXMU-OAW>jnfKE-4!>U<3ZZ;UDCHD$)6T& zospKBoBxYlWv}6XSLIN=cqZ2Aq>IsXOsize0p diff --git a/records/wasm/dispatch.wasm b/records/wasm/dispatch.wasm index 9102983baf6a3accbe599ff823f8dc2bfb3be352..e94a8434f29e0203b5aab0f0aad2a3254046dfac 100755 GIT binary patch delta 1877 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 Oh;C8enqysD*Z&2(Tj!Yo delta 1748 zcmZuyeQZ-z6u;-5-nZAnShG(I6^dI#VePtx3<%7R85JA9LPBU6CL6+VBbuls68)pd zOn@!Mnf`HEFfJyJWX;x+%7p^tI1!Z@oaer`_SHy}+k3w6 z@0@$ieYdjCTUqCYup7<~N8G(XI)lNjhYxMr+V*n%@4vhdjC<~+EAz6KyblEf!3JM_ zy)O`q|9s~h1mdTc9)-a0*wQ6V+v&S?w8cMPegOg_I!qE{WH^-Xg~M`TA8ddQSsbSA zb?FO(2i}z-#q}ETNl1l@JIpR#+4Y`s~Ib& z7)1+6|EwU4QF;J2I|yK73t{of{Vnh&Y?hZ>;Ox$%1!?px#uhCAtnZBy#?-JQvn6JCj)EYwsABA z>+qQYO5i^szMz&sQ(BV4p?U&`tW5Q=hF2-i5J68)D(J-NA5(`+-hdBb$Y`B|gHZLq zuP{XAu=I52UIz1kxEcz_;+*dX)oQ>i>eMZAM0{=XaH=ZZ-8qTI-e(@y5F*(vk zO;Wt5PvhLv#0sY+5Yt+thBxRqFZQd%iR2Ab`h1+Us4C#11!ovneuW_3KcMhX|D-jX zCg*1X6DknVQA`-#-=Ukw)ZKD-E)6w-_^rBba--`Wrt97dI6-Z#=yaaqPY@CkQxhng zYTIVaZelgE_;s#%#40L>LB_R!<0>H0*|jFgi>*cmH8A~^3WL$~Cv0$;14CGNb*VKd zonTFhIYSTHbkv+5Q@YQV`twpMq8cz|V4IHRWrKN&M{IY#{W+Vl-S{S3+GA&{9vxHz zzEW~E&irt)`s(BUrSZ}h;V9{9gU?$MFAHj zqnyr|)A+_U&?WyV=T-GT&=mG)*sIVA|#txhH?t`@sAZ4YuD0T3I33Q&7tT4) z`+45`p68v&cqcO6*|&9^pYPYto%D4W2U?8Q*Nr#L+A|@vnY+%U14Z2{p>S(UYkQMn zG_|yuW#gw{n3LnXVa)$A9t0iwxm)VEGtsP$@Z{O&(Qp3ncMt>S8trkje7z3G&4%?L zrssRsD}XMuZzG6q^DylP%u^e6xNd$;`#}4?KSz|bTl`*E4 z2%9h5o5fsGq64kT-L$VJVX=RrS^ zvi52elCt_WjNpq=FS0Q0WiLLB`i$m718+1(<5Bbd@fz#HgSg@Q?a8-19;u70b#MSD z6Kj_k^j_{~fJaE-XOiEePu%_={oPXirt?P`28!j|D+e|4*KC3}LwuZJA+48Jl_btd zDtnrI`7Wj96mB0sgm>|DYKcJ;OQ{5JrPE$wk%DAE2_ep%EaYFII(qSYx(yqMA2S1CB|*-lvJNnFQ~E zd{pGJETklT*n!gDBfIq;M>8e;MRQDc7wyse+M`AL2^1XCPJCG*e@vbLRn`(mAn_Sp z7n4F0N0(WI2R4gW{1IOks^v|Uze``ItkHkg#0nMrETq)Ld8$?uKh7i~a*rma9Eq$k z2g-f=W#t=_zJe?-Yn__-iEaytY4x83c{?61$&;4VB~q;94*O;twE`C`?Fc@2=z=PX zULviDOO)gph&h#za7@fulShbPD~cQBvR@Nl5i?$dSS4mCoGv2XMXye3dUfSf@A@pd zPe0T^eaIA;rtgxT2k_qu``#vgID!|v~@p%?$v ztNN>kquuqqP4oZfC%7T17t}yil+3|u%V^7lb8F=juJ c#H~Frti~%CEgW_BuUBx*1H=C2I&NzJ0)a!dVE_OC delta 1481 zcmZ`&UuauZ7(c&ra&B(2mKAGLO6wBn+-kd~X(*kes}p1}tCNX}t5s0u1`+$vE(OC} zvspx}K9pY5Ynd`!o6wf6DFk~*@WB_cmDP5F?O3aGkfIdFP*6l$=67yvZXV`^bI$Mk zd%pXf-8y7lC!Cme1$`TG8rQzwk-O96z9gO{?tg6@?-T{skOZES9C z426x7u?q+p6Jv)E%KS3s2OWCyt~&loHL0U+V*ELrG#38x;|=2u?L9{6W)038^_zYq zGaZ{{K%3FE<;Ua38QNbqE^O7{hH;hlKQrHNy@p+Fb?v2$i9{7+FM%=F9fpf*j4`!D zz0 zM&s+>GU%P$%K(>@hJPYQfW+)-Rxt)dl%T!0R-_UjN>hJKJ zC`!L`C)vI;kM*JA!8IzAmuyGXE6a)*>q9O6us-dBF5=lGyJk_z8>j&T(yMoAVlo}) z2Ou92D@!b(Bz?$+(%T`A=pDA^vh)_rQF)|bkKETDDcA>*cgWiDCHeeOIS8sO6oW55 zPS-`H(8S+MEXaM^#Vh`Zi}`AKWBI!Dw96`eMH6dO>@$#36Dg`z6F1ZGpggRJaa$s5 z)Q0kyzN~zs(vz3vvevGNRk|%8lIlMR@>V=tlqV}Ii==4L9o9w+)dG{Ib{6kGG^xs> zm&j^jnUXvWF{Kg`j*2OB;w%yDn&Jkz9M?pKnDGL{0x?73Yyt5n=+#M0udaM*9iKVp z>HF)c4`1h*rXQC(r~?)`?}M0E2?^(H#7py>fX!v|%YL|U&HAbzU8QlwGb)DHK9J|v z0P+O0skg_$QJZ$Q_C-ei_@% diff --git a/records/wasm/functions.wasm b/records/wasm/functions.wasm index 2f99c47902be59bbe299367fd6e1562b44cd2c59..a7b4ab3f3bcc299557000f2120c0e71a194fd111 100755 GIT binary patch delta 1613 zcmZ`(U2GIp6u#%4y))Y_mBw~ih&8*W6u2jfB|Jr42iA;}cI*BQ3SX#8lgw6`yJoq7O#fdhSejXC82pJM*3Y zbI<+mbWExplWJ;Cg=?epYUTOO4O&Z`7JpfLm3a>k46UwsUA(?p)2i#XnNNI?S(kS| z607u?u>W(=%-;UpppDM#r_~i>6G%Ga^rK1P0ng`*S`TM_7Xf@TP=&lo+ zI*S;?6?)pJUSNUo-sT8na^Y#$xL+`~n~drL(wp@Hbte}NjkYWm1MD%|mZQ*Q_AEzt zw74M`9%AfJE??DJ#^l1e%}%j_VxO^s63Kz)38BAD>08yB>j{p<~z_yZtw6^`BfMf7j+AaS0dd5n9{cb2@OfF1Yr7E0-%k~u& z`T$J(nF2VS3~9B;TVJV7o=7sQdj{59f40Kqf?szX4FrUObJoSKngL=oh;ib=u6*8XM;ak9qkY< z%~G3uO)g5F8qQi*+o2qm?C;y*4Q||=aC*iF-$Da8B|^c&3ib^fya7ZN&Q7qHl!Dz3 z6rp{hDY(zk42n?Rtc#|+J^4U;GH*Wvu0zI&H{ATXI0LSSP=;h1Yl8_PlrH5p1usx} zU7&(pcUTQym)pz^VgX)pyXVyYG|exPW)M%^noIc(I3y$TLG5?OTz ziUYyBIQ2A%qQF&%yGo;iKTx?C4#|xZ?Wh-C{;*58 zQ{eK=Vd6#)>kbPY?Mt4vCa@Z1$5TypC|zx^xaA+H8uh2;Ccb*^z`A2TGKE0MfC~ fzIP6`?{$CMVQbqAbgp9iyBWv^K(pz8en9;X6e7c4 delta 1613 zcmZ`(Z%kWN6u;-5-uFrwjtE`1h-nSD4K_-th+7z&(JanQ=ghK5Gxup5TBC{E9>l+U z1)_-<6SQGZ<|aD+m=d5(n(W2K?}848d;l{Wm85LOWD)SX;7l0LOk4$uXKrMnnAmd{takukaOyE?1baCwojh7!qzf5n$qf2CYMtW`lE{?A$p#`?!$H`Mjt zr1y*w0H}f~qZPmpGv*0^QWqqRs32|9d|SXvaNnd|{Qk|1RTlNTrHV1RaMGw$;RE>4 z{7i*U0p2wqFM)TWo>0TNvnLy(uSS{Cmxay7A7|l{k{f&5T`r;E&&K6*a9LmahJic6 zJ+yHb3iL3+<;?Jx|IzY2G+OzI>0= zatb#;*We8}G&#*c!EdkXJWSu@Gz;2NBS;~@xs`?dyEjmZ%vV$nJnn9u!=m(EbT&9= z#5%!SnWZ-QhJ2JfHC!+folp&bnlqj7BG)ssR?q0;J7@qs!s9-!U?QpW-5_dlVU`7? zQcf@xyPF~AvOio}@$;*U~sQWFcLPqpJS zfgsQaT z5@p*-GrvT;P%w8@&j@dkq>)qJWA!!6g!K;)hIF-=;2snO)a>t&qC*T40Xq?X6{is2)FoPGxTRJ;}s z;}x}6lh;&dE%I{>6We(f6xQ?b?pj%Bn+SL9EybXwPBnIr52H5jr#*@jy9Q)_iz-@Ar-J&t^3HsDdRZEire4@tYJ33uA)lAih$^ZvQbtv;Vz zD#q2OGdPVKD(ftAaMK|r|6F7+n|Xi%KEWivOP_daj6sS3FABCB;`Ts-mqEO(8g*Eo z-Fg;>QI;BIFvMso!7GT`qb%Y|QD`B|`9i|Ny*WQ+~cfxf@JMjrOza=~2dPt*B!&qiEOKEf%FB#$kI^L4P5FM9T zgs)tvT@Q*l?xN?V!JnnST0Uf6F~kIcZv;h)lZ4(7-}EOUa*H8)9f_=#!^)@4%VOe5 z5~bf&$je5xA-<*K!lF+XPAspa4rKDAWzHhWT6Bhe;2d%V2G#qY;f?%34GRG?ZHSAM zV>`r<&PTi@hU`tBBeX1`n@m2uFxzeJA-LUj`o6~FEvM&>_E}FG$!H0z9t!Q?xn9WV zdNUkv_QLsN4W8hI@wJ||%NsMjVB#KLU6Y>7O3(8q?OOgMW(7{EyH^oPoT3ru!G%Ws zzM7&Z^wiUh>@>s)n)w|NlVb#$_;iNjF}iC~)3j@!LeJ-dhviR;vW>YrWbTwpsNwzO zydL6`PDuRxDu?A+?k+&@LN+}^V!}T09maTFx4wB9;Ol~NNxKa3vY>pxXca$FRqF}C zF}h7dOr;WO8CoP+{Czk}^w_s=p{d?|oI~pQYJTt@Z~s-z^ChA- Jz1kgF{{z!0%z6L- delta 1574 zcmZuxUu;uV7(c&rdai9@K~~C;{jtdwz`?q%;FQ2v-GYw7rb{F;VJ{*xoAJTWB|aGH zSd4MTIMRnBqAoKIv&q`&5-zEbg+#*^nVTeT6B=c(ImP&Zf-xF`-?^9d?tv!F_xt{y z-*>+6+(fS@(d!v%^Wx<2%ie-%PdL2g(2I|4X*rnao(&+J_+mD_I%{hVYzT+Km1Pwb zWub7Q|7dYQt*Jr5x|!1AT*kysKVy3tW3E3K z?#fC8RAnqJ|=}B z{+(n&zIw5Dy(pr`X3xuf^U_l#A97C{B17aGK-uCXu{Xr!R6HnmQXNYpZ_I-78TXXP zJVmPX*akUeR2kw59TyONx^aTMk|vO?la_f)wAhj}%p>QKFVL?(_!@5(_G?=3y3>Z3 zq8fW326R2bF)?6n{|5dQaXn;;;l;W3a64g7*P8n}2gj_LTh`|sYcOjivU(|WfD<~J zwVlc$-sGh7$Lk%%N#pAsYqzsz`hkgie05KHGb^3QKk2O%{(xD5L3MWq{`erRIQK0M z>c(2izNojJZe*V!hH2;bLX1xkX~O9&;jam@Y_oZjA+bj{O#(-$E{ZhQvkl^B*z6Ym~lu>E#=Ja#_3d^Kzejz-STk)YT?p z@FCr%A?8lU)6&00i&4;`Po2%66#eEx23L4Mw@Xh>_jZY>M5#v7>Vh=)$0Dh&{frY7 zedH>FtI(weZy~Q@fqdPDI8LMuK}yhq8qM7Y#l&&*_ANBl*&k=WI#9(s?qTzC4gp diff --git a/records/wasm/generic-types.wasm b/records/wasm/generic-types.wasm index 21b0886c445ef0aa34167430c6be983854a6a396..84e766fd1ecef897e710f3d4a67cd8338de908c5 100755 GIT binary patch delta 1834 zcmcIkYfM~46rM9@=W^KvsVoa9v|U9DR#>g9}=m{qO-2@$%zSWc+%(>!EFJ<-4zD4F96vb&RpB zWu$-B`_Wa5>4nd(9-j{|rWd~G#@lJ5XPNQdokfi4h5O{Wd{_?&1%6lq+Zt}Px!%iX zEH{H|I~jUmqmxVhD_*pQF}<+GiGND*tt(uWd5r0W-^!;7pcMX8T?McX;IQlu5cR11 zT#KaqDF8o1gPboUOojZt5QZt%Uj+3KmglvYlk1BqHZAWgrVVrIv10IfK*^n@U-;TZfLHrPmazlpiM^88-yuQ+zfML|B9LK42;|0M zS4wh-CrgNBgpSGcGRpa0&&-(i`nA{NVB1tj9JX*cA*bJff@t4Rl-Gl}2^XeW5XmxQ zwko_$qS0)!Jp;m<4o5^|I^Mc!yfq!~1P74%$No-m0K!O^&Cw2#XEqCA>@l7+@DR~Q z1RA*KGz;;4CSRvwHpr@`!=`G<{nE=cWikdMd5)1|lan&7H=SxtLS# z@Ljrv`Y@%lA!QRj>c9|zD*;dIgrqOp8qam60Q6AyhyvO*t1cXbM{}ck(5V()mhTLB z2RIXM&;ABikJxi>dmnbap|sZwrd1buD(xFcYrNUr&TqfZHQa5y-1V-^H5I;p7u9{G zUfo{(I_q5IMO3QrD4atWQ@^h*lU(|B+LvkI zXLOTIGz7^hpPm9T&9^bBK7Jog^34Y{j$V@BV?=E ly|{`N!9F#qU~V&gOYh*kRDJ^dC9o9fmpvqJ&EhY3{|5720}B8E delta 1834 zcmcIkTTGlq6rM9@=jUe^q_Vp#3T@Y-r6QMQMJmCn(U`QPULI_eSo>nZV$x{i5{-RW zDyfMS$?C=oP*WZ%O@Qt$4a(qZqc2v7|7v(MZZdmr z-<5P1FF1%b#4;V)4@_NyN)f&yw-m>F%fZw3K(;${7AdzdIOX zIaiSW*}!MFFs2v2w08WWpE14gfD>=0jjk(<59|vtrWYQPmkMDc3@Y%#I@r^Am&G+y z$XI?BR|^?>;lp+=^{;qf9b?dNX}PhOVw3WLV%jjRb{B)k1u40&l)Nc* zxD@sS<+;8d-h*EC)&^M7BnN^P$-6-UB~>yA4IGZBA2&g&4MMWF8|HJ*J=p4U2@n1t zUwsFbU|GJ>0|8jEM8&lk24*vNGXNJd!Sy|0$1DSZZhoh?-M~Lbqr3*NQ=Zb>2Gvv# zybU$UDFz09Galt(lBp?HZCkaVjUeZ%2}g9}((+IYN-E}wjOSSqN>6MP)JeSHc*&)k zi;MD149egawGxBdc~laa5N>{tS9scmpVxRrj?s$C#9m449}uB|OXJbB@Ml>O0y(kR z&5~^5nG#|dqGR%cta85BGn3F>ulBlZ?0(f5hn*bKa_)WbMTb&R-T>lmT%2P;B+HCh zs&F@n?PinZIVs$ka71j+#M{=4w`Jm8U<1*HiN|dD0?~Wks?$lutql!jKI0!{PNqx-4*; zX#*E1$44!^GA?p4IHEC+XXm1OAyl28I3`GwBT9 z^?RugU+HW}*@TBWpa@(EIHePkzGP`U-<|@{L)kBUwCk+8dK8|^kLp3ET6kHZJ>Z>S zPq;Pvn;ku3&AsJ)#_?8Xyk;$i+*OV#eB jHM9u!sTl>|?x1gJBj=^^3*c`+NRj@Bi{yPd{B`%=wI2dh diff --git a/records/wasm/hashes.wasm b/records/wasm/hashes.wasm index c1c1da5ba674bc33a4b6fd7ddbf655b18f919a11..22835daae8f9715a65a4fcece838d220d2d6300f 100755 GIT binary patch delta 1868 zcmb_dYiv|S6rMA)b9;A-Lb|jSsB~+Q7NL*bG}TJ6Mk17kSJbMFu}ZsuO8^zNniBOI z`Jt3Q8%Z-54Qmr03AXfM+%a1dBQb^=LW|OBP%J`)KvIpw5>1tQ&b{v5O|1WHGM#h2 z^O*D8JJ{hH?C|w%%zkdySzS+F*>5%mA9dxXOi*w*?h+6hy&`GEl>zO zI<^Jo0jySCEy&iZ&*%!PA6uXR&Z`@A-B62KAqE4gyA@>v>SQZ|33bKcykP_rYFQXM zQTAyVYVrEh26e$!IKt8Q;C8eqRo`xhy%19CpMt|H6HajEZpMCff@AUKxs1^fqmJ+; zi!qc_rtrP z;SE;o-P2kTX^*I102Zi+#^H1Cg?qMnJkk^=brbxWu>D(W+R?gr6>jrd$4Be}ihRBtyRl#T%*PDT)obw@kjupQ2n!Hr z#%xmh8f3lMU?bhqmlj85eOexwRUS#pJHSy$+V*oC|54ciSghD@kY~Mx&qbv$#4D#+ zInPVg<<~N2ogTZbDDGhN9qcy}=y;ixr-w@q4htjA`%FX5& z=|-j3;pLdI$`D`UYXQ+ic_PWH;r0waNtvI)MKj*e4O0*jxAs#!;4_nkIEB{hK@5;5 zq@!X$zwtX9lW(Q@qJF=XY&z8P|-LxM0+|CEH{|1SU2|fS- delta 1868 zcmb_dYfKzf6u#%)y~FHKZNnC@EtW-Up$}dgLak6~G%6*1MMWhl+VZGNYPELIv{AQ) zA4d9T6GLu^rW=z&!UlISO*&>VF(yVKDT0kcL#tH_B_*k*Mu-s=&zW&|HWB|xa=GVx z=W)+-XQ;~<>M{oRW{sU)|PFRZOz?pgC7FjQAp&ZuxI?mf$~6ANkv6T zc|e^2co)i54u@JOw;t#4BFFo457gqVv<%osa0WyG*##Z0B0J%QC1U-r^3~Q{CKNX` zwH7U{W=t&MUjbt*EHZyPV{}CO^76VI#^{KnuDk;cJvSMTOyx30N4%+CIsp05 zYlRQMDu8PB{y}7Ss!!>9TK#Yk^5ByCi>?K=t{K7*RRhf^i>eFF2*%V6hx4`|7*m^C zpc`eMv_K7Bf7qav*9vDi`W`xj7JhZ<5FCTeYWMSSx;o|rrypVLmJ=L{G_7Qeju>@> zZ-f{_IYkbU>zFbfF|6`~_~fWn8w9_AxO(m|dXHPLABML8PFwN_d?WC_d=%CSIAtxE zP}A-$FFW>fb6N0sQ2h+BMm=^FKJ!l9*XHp^U3_g7d=G;Vn)#A}-pCCG;1N<7OmZr| znEMa>Ryv+kJ~_q!ZTSXYhbI0U4Dm7$ony?eHS+8<#}8AUy{OA;2<2#Q?f(&8gRD(W(b2j#2X}cxiE+DtBA0K$ZgfgRe@YYm@etn*a+8=G zXMW=JuuV#%Ue@XLHZmZMl-QJYDS2>Nc`zmK0!JZX+plo^o3abASm9qGCv+B{Go{eP z@8hhL=OpX$PCMonaiZlVz8UG=A=l`Wnz(|&?gc3|(TCA$Vk#agl@Ds-tj(d+v`M*N zpCDaRdL3R)Xgf4<8DH~@Udj_mUIllj`ANv!G|o(W!>XT$fVi`t;sHZXXkr4bcY}zM zC!|dgwR(PoQ#?eyt#nI$@rqWA;YbcC_#q*LhX47)=~ zJ1f3O4vWmha#*Om6~qWRM0)xTo8-4S(+ad^#p@{sYiJSjjSwvW#=;A;oWbt|XY%cx z*yz%xy$?3}go~D@P^=;?3@Ny(!zpYaMe#=0oZqv@Wn42}t<8 diff --git a/records/wasm/hello.wasm b/records/wasm/hello.wasm index 774e2f3630f77674cab25fbe160d5fa24a1edcc6..54cd5804395645eca81e7ce8183a1eb3feaf8928 100755 GIT binary patch delta 1565 zcmZ`(Z%iCT6rVTmW^OrA8sTV&6fS|ZRLaSo1Z~pR7&IcaYSL6oYZGHoO9&Npt2G)o zp&zWpw8T}mM#DiRsR>a2rEGIe)0R)5rbqRtk+h*T_{B(!O;o^IeY0D3??c^WW`6Iz z-+S|B-n>m;2&XTEhyN6Ca_leBeJp3A-c+q8UeaH-y1QdgW1Z_x75ch8v5G`>qP9xc ztEy|Pqsh-fw<>!c0e!56W?K4t(`tG5%V*T`O`o|PTCJ^%rSOK;Li0OT$6_&jXnjR^ zaBOmM6+oT!<5DTqTT3)ISw+jmCD-o%B$qLqxQDSFjIq!HShObxtcZC^eHqt>s> z&z00wxWtYk#&(g7h>*NO9%^8P_2S?CkQmFo*$^sm%3bCM`x!gng@ojp`xsLLol^$1 z!j|!MF6Yx~#tMiNUO<{za*-%%;B7lw!`Sa$43^BvV@wU4u}e7=!?eALLn+MIFK~Dh zdhBTqzk%hPN7&m8y1l*?&W3;5@=7R#-`WeU@V>clk%898BMcyf0wYZDf9b;;|D#{Z z)~}Ecr5TVfFVEjE@sDAXKLFT~W--~wqgjDpqXXd}arq{t<>c=CvJc*at>aS+BufhDM{;ep@=kydac3L z42XztHblK|Z@#0w*|#4CkC1ZxO)tJ74ui)Ka!{p_r!@*8+vTegzo6|6ffCypG!T?_}+!3pwOs)rvZC01uiM%Erqj{$Gtnh})`RVF3zA~`<=II02>HgME= z@Bl>fOjT>*@UnZo8Xp9&$Zq-92H0>*?`oe9v~gccS*o|82my7{*A4iHHwNzf;cbBs zxbby?wljEbs$KDRzUoZsfz}4h8|XL%hu}Jzr_`ffmGu_8sEN_l=C2e?^(|&pr_crM zl-N&wvIB62`b6QBk9Zy3IjN}!6sIiUOa`fmw<%iM>pLyJQ`|+BA0qY!z)LD3;dvMF zyS=nPwf0f^rD4W7{t@f}NOm28!pf_3rVX@VAKj(IE8}KLM6)DAgnh|*^ay+gPkyX+ zrPmwb6(GX=p{S_gYx5<}4O5>`^QgvGLfnN4&EEkT*z1&ag8dl%md<3L%Ug<_eKT+~ L1iI5Q3r+Gr|GmF_ delta 1565 zcmZ`(Z%iCT6rVTmW^OrA8sTV&6fS|Zw3O>{5~}GRHEL|BwkAz$(Ka!$tsdC?TJ+-6Uq9T z+S-~#(ma;>01{?(-vf}yG}BDWiI1{sdF_*JYWc3;*be*5E%W8@qS;LIL9=7N6y7pF zB|MNBnO_31&iwI4IXr0Ipn11hvQS!fWz~E6jN#XJF}8y-7Mf!`RSAr#f!EFL3*9he z{<^TUY+aR0>?mREX|fR!Qq$z22FA>1|2hGyGWjBFo4 zqraA`UnIYoWk9~Xvha|^zlROJ2Cyy5VzQA(a{|9a2f_j3^7Tr~$=$ws0A7bJr$-q` z{Nb#@leDHsS=^IqK?yO=-6+KGi%n?tcy*)z3I00Cg0u;9>H<_4KKbf+QDN8c}k^Gpfw61kI3gG_S5#dK#2?E zEY6D;&!iq5ro8NVP2sG_+aOA`Nr}^x{8k`uJWkn5oJt#U@hHV{C9>)+6i;dsIQO&nfvw^!pf@QUn~e|dm)xAd;|sX!a|wUniL8;TH6M||B$AMwV(o!|FF zAOvoFMWF2rUYlxHyp^vylX{@_0rPiE+ePod&uE-dk9tYYTkN7HMps+7STNPMctLdv zUC=Ix8S0ZAfMe7r3OhdH57V8KntDKSDgw?(keYavqJ87_ofh9E?x4!s#9j?Jsv;7e zaS^}MOAAzMAERFyUa&uY7xn;HT}Pp~dX~<#nl}89?o#5+S;G<097z{pU$P%M3O%s7 zUF}M*H^Qqxg!$U2sN>5ECH^u@eIl_(HNFz!E>vjdHb}=FyP^}UN9eb7?hwnRucyvB6vDIoiRJ&XkvzGgdv}>5R(4LRDA@`yL-w5LliY$SVuHCHpsk zA7)6{2rVfw^fiA*UT$7~Rv?g-J41GEgwG%#S8sxO5Ky5_kOL%pHbbA5-*1H;@elmRoPY9|XQ}(*i%dsX=w{ zCj0_~uDAu`)0%8*WgjpGR0%4JQFAwyG5zqc`r;O(15~T_+pzu}>coOF%qFq64BHZZ ze`1Bp#kEBqFoFNlk6(WPekUG}I&be_KznhLcb&oiYA`Ve z_^uA-H%ieL7r1!=kqeP8&e15+cC}zVZh@R|8v}#4g-y&OsoGe!Z3RFZencw@`G2&V zLL8Tu-^X#&&Jz_J5f{ID4=oNjDEZfFs80qq;6(UGE#80?XdXXhvE^fMI;CDIJnp3i zKU8mG0f?!*?-cWE%Uxxu!c#1Y+{G4BCp@v`peTy1ue$I0s@VEYupL^g{iYp1D0YJF zA&iiwG0I&kgi&FvH2CLqd{A(MS9G#$Oo=96bbyli%4UyQ-iu<`Trt-Do58z?{PRG& z`B7qT@YD5XwkR}son_H#&{D+4Tx@IuEEdHaef}siIa-?)Ph)?7BMEy z3dWtGQn%nZ4vlh_iQ67))U?3kZZY^tit!xqCLNFTAa9cAO+2usOHT>8loaJ=s|1VEOd#3X2(#flO9Oz;JCKNu)3IrrOtMowa77? zZ8*h2tDNj!{*$sB;;YN4Z$-Z2d7Uejwg)2|nm?_k44YL-HExC{YjlRfgVViY_&VXk z9Ir?)R`4F$ok=zQl2{x3LfC8(zBnsLPSG`W<`bMp4OQO#xZ{xo>^*?(N%_suP8qzN z+Eh$z!d8dLav#*XA4kPqJR0UA~BH+UF{&GrzkB`NRJ<6g>kR|6)Dd=%Ms{eTT< z{=aNlx@?%4Y!z+8U>;4OF2#$G6juv9z+vS* zj=2u4%%N^lSBX}!O>1^Mt=0$j^B!7L*T&PzjM7@B;V7+wAzCH((wePlo&E`vMXXwn zjMl)G`_j_ywTGhhUON`yv-@

Ce@J86XmHrccbkbg#ktXuzuIzg@qIw2^J8S{t~J k7UYMN>sM?pu|Hi>W%p$MDt*q-XN)0i%%>Jj=j&7c15NI*8UO$Q delta 2406 zcmb_eYiv|i5I!?!&+W2MXu50(p_U~;DW%Vq6pA1qRBI!~N03(3AV?7s2*w3@dKWhM zfDj5bo;Dh4q7b*TyG;ySv-n6vLrbXTVG6;vbYq*Q255i^QR8FIz1;@}5`NI+W@f%K zGv}Mf?cEdp)D!;HCTE%Ba2rcI@ALT>tDW$8j=!wTC@X^l4-fNrrmuW$#`NkHvU?M_ z!6RWaL^9&&%kCUck*6fTxH!MaBhPMzkDy4d-2!u=$OvwMLLk|>6)r3J&35=s$?dv@ z2YqlE$=>USxg=$AHXm(7cR|h)SScNyFdo*(TqWL+6`ha?JLD>59WvhSgfxIhWL+1T z^W;}byd*DnL1xy)+>UXK@z3`&wwN(yyUMU(1~8@`e#=O|45=#8-A(!2a!xliL8H9Z z4W*DPbD~h1)jGvYa6G`+Ix7J*42)zcnNw@}J zHiV4l2A~Zcb(0hw+mEwAM*Cr`TzCWif;u_xrWv^GCS4zr+i$|J1im_Gf;$EkXdE1b zlR#+oEf}BGXi;1KHe*1YptfiY|70?z9v(72y#?6-dyG@JVdEN_#Qf!0Kw|xJY)QHF zXtmA8yNr1&aA&&S`x66u6*?%5jSKEz0{^8y{@Xq9>&bY`+1JK^VsVmlgU0`E(y>r2Mt}=&&P!lK&?Px*&@;;Y9e|SiA{$q8>S8vgKoN8kO!B4*OD# z2f{j*f|$xLoMCQd*=tNyI4VTBy~0F}2uJ+bE6U^NtM7WgI)1(zEQg30uUq-OVmDYG zLJKMyqwG~eXgjr48vl&0_X@7@KhLlN%!n0VUQfj|SnM(1*(-+45o7HaG~Pkvp9YHN zhl#z$FNO62QAT-8i%z|!DxR^o^NuIT6vHe)v}0*FynC3mo={#@i{!Q@|TlKGMCsQC`xqe%)!cCFD}6@h@pd76Wgk z9Z`BDuHgc@oM4*2@|l?MX-+JGdYAc0Yv=5hVmNicAm=>bEh-`DJ-kImedvPI#%&)i zP1V(c(cL&P-C8U^Ses?;Ty{eDn)_y=OA=^79Hl;~g;Wj+*XB5OB(7mqA{=KfN*IYS zoRUCm64gC_ovIt+tJ_DtTo1gLNNs@3izzgha7m&6}o9{xjyW9e?Ph3{b7H!*nT|U-_QG8 zTPU$Dl-O030!JIGQ&e>dbS_9J_O34Q7OwQJkrQgb4Rd5p4Me8KxGUT_g$0F0d0uZ` z!5sN}4eST6Y^jB(!KfS>5MI9j5)>_ zhWe8k(;vTX+xRwDGMqyca{Na`6frBy?wblnh?0x5S4 zYpxm?hOL_PMYU9}iNah0A4kCtBl3?Zw8Cz+YXp|86W;ud8&=88Qp}}MO)14f0t-v= z$As_a*E<{{f$x$coyY$E38Dt>13umNmGWc&@ zLCgpK-XL=ub(m%V zPX$iT{feSvVw}rF8a5xbxfDgupI?<5D=-WGQnCWCVDRiEGeMq?Gb#H<;c_lB_`%K~ z7K3<~BvVZYh&^zuoNPuebDkB5KUld2%|%% zk?gD!!e}sFGWZ7+J|MWk8-|z%r;Zoz&!%DyTiIh?>JK7ep2%=sF?f`cuL82>M=5)Q z4|fJVqQc(6XLQl&){EBI5+Rmg%gTJG=i9)h)9p40&y|a zv1f2Phi*Bz2)8}ht{`^lBE_ znzy)8OIF}_Jb0Y89Vf*t;oDhcX>wD?=QfZ_tf+n#)F8jXkI49ltY z#HQ`nI$5?(pPib%D}2y;WKNG?Or!c-Z%u>$mel@cyse+djuuR#+zz$iw|urP_TJ5+p)Aa^ zed@)~l~u;q>HLU?Wq1$ngyYh;7poyE_w2>bV3%C657WI@yXZ{O?b6qk6X7vYjg8(1 j3-E*L&^~NkM7MqJb!@djrn-3@PdezQ_Q)@I_mO`9ifKqc delta 2032 zcmbtVe@xV67=NGVz4yy;;t&xJlGYV24COdZ!@wT}${>c=T*5J7l_t%cHS;WUt1HOd zXiap?7qq&qg^f!%HaEWRH2uRgAWnpxQ0AI#7@MW8tk%!j^M1c_-}}SBv^eTs^>q&x+E5R#Rrp&0V?=F?DR zgRqRAA!As-b_TWq^h)1ZD%~r0orUa`QM2@4EsVWjmWE&nl~o@fFpb}5GWIYTldg06 z&w{NatB*(Zv9olf(4pTt2d{GIklThRbx6KDL+0|J@hB0^ltp%%#<1hT%v?@;yJ3y6w`RUWGv@=pAIB@zMSO71nrokhx8%5R~9T zj8~XLQ4#4b$9Z{UR2@?iQl`+b`DIH>Rn+|Xgxpb%Iq-`v%W(n&kqZVu?!<+JzC}3f z8#LY>4q!2ehxo(==2DWq&0vM2N>tjb49!vDh@1VQGH%~;SNoQ@y%kJ{h!Gz!^ZP|B zm>xpwQ7}^N^+IUP+H)HJh|2o~*Z3b7m>cI#H}A`(W`ZVq%ul~6l2?c<`-H|X6ZjgS zXnusyYy46;;1=bS$B@YCH(2q6eVkveAdv>DY(k7{D>VK!m2>ef)i|+OL?ejji3n$c z zH3t8n#rut+8`?Tc>yB#)tlA3YV7U&&bw}eI)?4BD$Qnzq!Z6p;wpn}jdNUpM9lWY1 zwVFki=TAJWuWQ6_d7y{39cLvh;oCVBX=u}@=N8Q))v&r2bU;3hAEQgL9(a^4h2kS| z4p-69lbW_)`Q%tW0~R#(uJF&zOJ;Zb#0**hoxB$TA5+& zmoYP-0!tDD2mUp%Kag2nD>TsBMDInntDhhf7AtQp?$AhZI%y zDcp9E@$JuG3>mfZ6C%A8e7Kr_0D1>>scO+d*r0`k!;q%>>mWskBBAz0SSgzYJ_f60 zhbC{yKLz?=lN@T0)Tr_e90o93mbgirD=RfwCM`GlT$Q^uxhubLJDP~Q0oxDEEIt^}+Eq9utqiH{6B zQ8q4RY*QGHx5WceIVfTKjKqYIX{?V_e7y_wy9l>%(+el#b~TRtwT` zArv3Zv11IK32XzUqH3@zSCqVXypqB=s;PeWR$eVKsdex&d~mS=m_gs_+-Qv3jYD--@|3bg>l+ ziA>vySK@vxe#PYyLwTRd+=lO_LXZ5i4pZQ!O>+CsGce20!vI{I3A{&td}A8}!96%S z@lAvGHCb2ye2cuRk2R{~L%0bF!mSJp-Ws+jk-=3f%XcI{Xdw%6Fks#Rs`8{fe;6}z zf1+ggh;5nob)E2-z|R9Z&5sg#gMSya z^2JjIZ?Gk@25eS5Z(ib^b4V0%jzU~AW*GberOV=tx^QB#h$aw;(;`x0q(Dp#mEV9F z9GYcm5Z8}r*08{1wix_8#dsEYNXH{Sz(eXr5R<%?9y0l0_M^^l*MT!#d+uYSe88T$ zt$iV?&5vjatX>N35OqBp(KSRkEQ=n;Y0pK4=rJ4@)mBH>Y>feo58%}`>CG&Pdfwzp zeR~3b;c#3AzrrNzIITD)4i4&8BMqukZ#{Knk-?A9&MyRhv5i0z-xA?)Huaj+wC&m_ zJL+>bibcO`yv?~o<{~kK1|B5m3BWsaK;pYoZ5HF5y8!ww_~^moovQp4wnLVNM^{8+ z`I?TFQr57Yl_YZy56fpx<1J`bGtbavgjyNw!{qT-sOAaed4PIt@XKMVMI^;Y0pX5F zYDg=-#~0BarQR%%mrq zH>n@4;U7SqK6M>e;sfWR7W80+`7nL$?Zfxoba3q*Oto}trv0}`Mq5KW^qbUuadxK~ zGY9KmyqxME7#SAQW!5{jAaah@LhAk+0lrUNzK4m6ou4TmImD;-FXjcp5t7ukrQDVF53QT5od5s; delta 2405 zcmbVOYiv|S6rOWt=kD!dS!(HfsSD*%3w^La0S#>l8n7zJ4&P=H7O9e_&9X+@9xr=Q)$T zdMEkno#cH-YN7eS(b_#_5IXScw)C4xKHu1RuZ$l%zfLY`fDD(f#8+B4dURolPewk1 zXnM?C;Ll%~~^)I_&5!XZoE3~o;D+jl~STJb5| zc98My9WacHTDgfxPYoZaX6%BVUR|b|-3+U>kP?I}b!QJ`Xebhz=D41n_noP!s+MJAw0m9OB6HZv>yU|nDKB6U;G5)6$*b>H=;~Ik|?k0`? zj@ibny^M_`qw^Z4|Ey_vCS&^J?P`S^y&7`o0Q{Wd&Pc%x(5Nn^U@Z_WPsK5OaM(t9 zua2<+2`IBXjOmYuQ|Ft~B3l}TXPKe?$X8Q6hIAsg<(IN9UDY+0I(*$`GUvvC%b zZO^wb`d0v3O`)h7Y}!ANG5zsgWBC@wh9ujA|KtmVn(MM#$ zdi*izeA#@5L-gl&Rqh6SHxn+&ulHaET(L;O^^*+DDs(Xb2WJBB(;r`cihR;@JJggvZc|W1utSBVBs#RGAd67F{j`( z1;a-y3&B&gCz{FF71xq#s#cwqsUggRTXK8|_XnaKRysTbM^dgUh0ClqcrX&6brX;C zvmMNGi7h^^N~Q2 zc$#o5iL8E$6)%`)c-K@CMUt%$XN*Y(|AE4J`EFe}u~ zRz`5uprB3*TxQhZX9(jt;317iyq|~E)d;4!1A55hgL#kI!(9pXbgj7$kMn+O=9c!w zxV9*!rDXL|XotA#v6!wU#$i?bI8JyzF2s-Fh`6>szGh1eV7xb9U6bC-(zxdh9#QRw z@MjLKGV%?k1zKsvF|}_{H(O~?C-l}+M`j!R0PXxN;O(a3&ooTU{Y~KaYcfmss9zUU~4`T;-b$WC~bS|gq zXeC7rTTw|exACxi_6XjDpsF}Zml33l+`{ycm#F4Z|KlT^Pp ze4qcHgWsP(w^91A{HRWJCtUP3^bYf&%KbK;?&SZF?!iFcsO@)SIlb2ecmF#)?xKZ0 zei1)}R#o#87MjyP>$F>U17GeNuYQ_&pT5fA7s3I2iW;?}QxB3PYANxtl&yJG(38#g zs&kj|H=s@*zJiPKp>t6)uVIb(fBM>U3qNqu!DV+bGoV{D;onU%-WuAWU!?Afy*u@o z+gJbG1yq0U$k0cZS?|=$#GTU1Nekx1Vfu_pyNAo*nY9{-ot+2HW$GyQmeL7IH2i5IlR{l zFUuFYp#ToaW11Y1pLIhX9G4fhG_J08Lpngc{IiF|Mw#6U1rN3t|CY@df3lddX2zIj zjbZftG{*GeD`mE^wKU1N4H(ml|Dz7}!m~*EX8Wj0iTu3}T3|{x9EC81KOEc$Q`V|wu=HPR1$p05gR zOmm+wMtL+~h%V$YrWaRgegJL)oR;6d1Md=P9i;M8@~uHa7*nSP;VD7l+Atg@(mnzY z5g8rP5Y^WsaFD}@3Q0KqVrsDJwL@)HT}Qg)UjGAMC)J4R6czW$J z18)nO48X$$H<`d&^yAn6Lw_?_zwUf&mI1|LVdm=wzj4mOD&Qw%odE=4eAU&GzwSC;mU|5h zz~AbyftyTgdd|kfeOOML>lLQA)!?HO7KTBT^5r?^*OGV0W`)@#8of=nW=xm~b3`;I z>|J-XcO~ot;5fwXc+1Hj5d+|O2xDBsNb|M`Ve}Z!8~j5m9}(Q(J@c#*v)4C2+(Dc9 z)IpCGnO8+>oyhfmZ}3F|zXvFqpC$AL|8Byn6uS&QW=mv^*sOTMdxbAPLZV1@6yl0e zXYd6o=jY>kqosakk zpHekhcmmG48G$75umu4xN_;`ZDdcs9&8V&6e zaD6Vh^Pz7ZpLGV#yIv}1n8%uE5;)dh)$6Ub;!(vxE9yDwz+3~O^3_tzF8_(_Tj>hjjtEaVD04NWpDcq6UjH4ZckIhv>pn r0b8iFS*>2Z0~g_=YW#7$doMkZ7e?`dv#)1>zo delta 1818 zcmb7FT})g>6rOWt=k9WC1);DI5pKGa-}1LXC|24gO|d0fO_j8W`a(fzjBRzT8Xs=y zhN4s=0n>qK#A@3JDbQ%bB%8*+q!D5fXcI7%hZ;dsBM+LiiGQhQ?k#)wp+30D?D;?E zn>lCZUb}8yyKbIuO@*`3wp7)Y3I%JD!r`jc{WVo>E%IazSP+&f2Vxmu4hpkBJmOQXY{81Ba&WBs1RxL$vP}3_V@J=JN z%I7*D7Y@ibG&w9k?tpCQkQcRdMlE+h3czN0^B9TSWJVNnH$PSIQwC%F@dCyg8DqW` zhC_qNjOoR{D|U>{MG3|lU`#K5Qyq%JGf4SnI;n~!f9-@O7?JhIp%#ke(c>@xV=}9Y zYK_U-F38?8?bf>SDq}CWwO}Zn>gdHMUE>C-bw3&XE1dq>(6&s*^y1U1zY8p$GoI&Q zn*E3|%A*0p(3Nb)^y1@ct{eUU7?9t*1#c5+?xFG{^7S4LsVb(!yyhQR9M0X2a+nvUwW~ny#2Ly`FAel)A=Lf1^?A8Z9X6WOI<|FfhU*0 zWe{jWlL7d+;3gAzi+=o<|Iq)Is9$wHI>UfsF)!_9ga32h#&Y0MS?9wbb*#~cZ$kOl z6a$0LP1qPFIzGk9T&WVYU?DmHC}HgeI)j*8Yv8@bm#G-8Nq|;QE#`}!mi%?s`GVYI zU{)p%X*FzYi8b)%UNeJVZ@vOm5Q~8MC2LE%Gm0`x}=KES{ zGea(VEKOS$Np&JCuwd{-0>2w5nx7@~2ERIPmx&#e$C1byaai$KV4g2NM50J?6=L3~ zGx+yZ&f;hE#)-vhYC$|tOav38NJ0%Ya}R!nFf9AlVds6r8Wxy=n8D{M$HTx!bUxxE zd_+~H;|YXO`9%=Z?NOQsOk3TQznwO9NskCc?=$$PG$4(@r)Mad_*k67?SzZeG#c6` z#o9gZUBjlidLLgJS)9TwNRg9Cb|3+YPoOX~Sd%r$L2i-ZM3 zsqPF$!5u(YD z2!0!{K6!nCsy)x&>9yD!!Yl)%JY0ksCCg;rNT>K7;WGHrgdG#<2~tG(L3LPS~pe-S=WsF<~6mEl*x;xm8OlJq6^UTYi~3)IhMtoaT}gcwh6D8(}b^=1TCD zAPlQr+i?nrrtQEK2qt88YxVx16RQX6u{dd0w=O_8&cs3xeym9sRhXY<@SiDvufYeY rfFqPStX41Gfs61#HTo#ty@wvi3#ag;OS09$4{^8tDt&eu*QfjoNb&YH diff --git a/records/wasm/iyi-derive.wasm b/records/wasm/iyi-derive.wasm index d3ee91b23e8bcf26448e855fec9085131d50c749..68d67e2455425c1d3baa5cee73f9c39c0b86c2a4 100755 GIT binary patch delta 1552 zcmZ`(U1(fI6rM9@XLk3lZbO&NQk&X^R2$P|le>gy&{#1PW6^JgHUQ9w$zVl+SvV$MFstugbIK*)fJ?$U7aQK^wFIdWej)38QaGg3#>6dRJDmQv+!=Mo2*NeB_9LE z%)-C*&q4Sd9&M{}N$V|)9Uz=4z|b8kWfs<`-@=fAY1Lst3#8S17JLdbYFmU9%&67~ z$y-tfj9E}`8*oXh2qY1-x*8=^>!m211sK+cD&Po*yuP*>da}Z9c=P1z>O?Qpf~~T> zkR~(O3&Y`Go;?-_2#J5HJ?}ulS-Zg?l;IWw2%tcV3H}c~c>jO!^>TcJ{J}f}+RJyu zx~cG_!y5q4&$Fb=@OW9_+YxHGKwSQeK{;*LC*Fl#XqcH}AaQQS;c2pZj-|ZF1`{MX zcdHQJicyJKRn-SuYkwpu{Gqb?2|6QO@loQNW5Z>2xDTq~FMXyDJ`6as3$9vB@LkmU zX<>y9N*tMRcngRpaCw0xO%%G|y29!Z?V%1A85341ofGY)eD;C)Y$<;ZJVMdk?|Aid z;v9GkAqz&vrcg!*c}jLk9HiIh1WG)0lcjj&M)PKZ@JmlUuaDgm;a0IF^n=7j62BK{ zH;$8fiQi2)DX~Z5m>W?x=epvR&@EhifkF}X0C7vUO8lN)m&Af;oLt^aM^LI$6p6AZ zSN0CQ|9zO{Fr^;phcnxzj4ZH1MTs}4#!Gg7sKkUf@a{<;XjRyV40BpA$Gn%A@S8w)4@-%oe=}zn8zMpd^)kXLF zXsU$L7dE482tM_C2^%YUJmYWYPrd91{x-hTM=$u@Uh5&<|LHXcVeV72&%BRXFDp16 zcz($ABrLuwCL-M;&KqJPD39S!#IK{TI!Zbv-kEWVB3>5dh_sQ@9}U8D0r*(W4@1IU zo}}wScgS3Mq#)(G##?!Sm%|aA7=f-A>6hfJ!Y7`o(&=B}ApNKf7S~^_K{og=JPoh( delta 1552 zcmZ`(VQ5=b6u#%4+?$sd&B{U=<~kSXbhT^KG?dO#>xMF%We$IAuG!p=IoA@wAJtSD zRA05uv98LX;N7&)iR~b?D6ObP2$d@WT?0BLkok};? zH#XL%(nB?c4@R%vB}5?A>Wi;5=ZIvIe`-YT3|_DxzW6yARKYs^ZMwj2(^hV#*)NkGJgTR{^T5-=hlQi`iimx`*ABZmu=zzVb*#(n)o;a+fTJm+bh5(Dfw!C0T3mo*Sy{^e=FX{-`w;vp>2G+2FreuCIIm diff --git a/records/wasm/iyi-enums.wasm b/records/wasm/iyi-enums.wasm index 01d18444899e2ed12f65a6b5bb4c0a4aebc6f07a..a0b018ec6dc5f7272583a905f3841de522d59fa2 100755 GIT binary patch delta 1917 zcmb_dZA@EL7(VYgJ(sqykJxo!fUR-Mh6sgLM47Z~L$ktQ;fF+OoF>bh zp7Va3^PJPAe%n&N?R0k`oI3Mn;lMdtjc0$Q$Ggw-T)6y-3#!61r(h>)=?Dks~u95UaPPm;;B?bB2p(>;gI&;HrNO+ zX=75nq9wLL5ggZUNNZH&Jql|9>b2rpGy|GXiv8N&TEtw_I#JmED*ePk2%FB+`Wm57 z#B1R*xuC<3kP_`3KLjDF{o;oj@M^9)I0-TBi#p5_)3D1T|H4*%dFMVt4iiEw%LF2q zR}&&XHfIE%E+gb24CX8|{7)j2`Gm-iErB0}WHgm|$Y z-U1jDw;uz>U{JfW3+qR<;(%;p*cSi{YW@b@`Ah@$9n)TIfG9+TyAcKfp^4pk@aAqf ziot_T7z~TTX85xmJX(POH;P>XzRkJrKVY%2o5J}y6fjuU+-D&lZs}s@?F$5~L24%e z7DhN-!9VtPf&gcBy4i6=VZWVLX$7#}1aT=r>ePW4L~N`8Cm9uyvv3^L*bD)M&BRpd zMJZ;8+ps(!4Hu>93NMu4s;OD50CsB^O|h(u9iD&AH)VRUbdPi;7xXq| zqo55>LIK08(YCM!oo;c6XvO-xkMEBP)MPXbEoQLk>aoHB0Qe0sdFh>)xsLY3Yh(*QH zdFZ9;1agJ0%{r}v6*!RYb);wfy#-zKl2n+iO<|uRrCNb4Af==p&uFwB+e9~VM`AXa zm}!$h@>;TlK~bx_jpyQKjUl=W*hQHU^^~sBRYqGN$@(nbS2i#1n}v;bovhdk-E@yZ z(qmw-ROjy&QxECf)xA%eUU$Z8b!Ai&I-T*2WHcHym(xd@O~YJ9x0>EQb6WX+FnPKD zIS6^fs!ilAd`ea=z#+@dh{SL^eZawUk8mfgaPS=E0K1C#GThaV@e(TRrRO&W1CVu@VIF*L0MEGGQd;Rz(Y2&dp1OTL~W`~BH5E)$Exzd|2i|0_$-1P$-Y zta$hBv;=q#eb~vX=oW{fpiAN{}(MdC{z Nl|PNby`9#s{u_C=7rp=h delta 1917 zcmb_de@v8R9DlygbI;4&fj`!HLId=cPKn@vi!hZtP-_B$V0MO~FZ!oBU84tsS+PuGN+;m!t3VzH#rh`CqZUe1CnO z?~mvCJkKq3I~KYfXIeAi)X?jhJ?9)gU-7=@Hx)NL*O@=#0bl3tnb_)tZ?7lESLiFr zFDl9}^mVS8Jqd-KvDq>x9J({B1B&MBWfVp1yCx-azb~*M;;DF8A>zmDV4rwr3#^3~ zMO2A{Vqyzq!BKHjS-moCE35?ADzZz_EEj$y_K3Zuh?x=1C>*zQKXehoM$<$`6=cd- zDSV<9GzAdi5pM+`2m|8B0BnX_;VFYIh=|Y2FiS*Wmsx@FjmGlkX9+n#2(jHF5T0I6 zi2B&58Qhac$Rij`xyA5536G=`qCPe*A1((ETN7PxB&mCwki!TtlH_C&qCOUt2Y0|5 z0A2FW$AB^D5_flE{Q;3(q1x#4R{#b@pwe(YU5R~1#H*F?F$_p=6}$%so!Dgrf7u0x zF}S}PgY7c22L5V5uz8%)7 z4`peqOh@8-Wb?U4A(NM{E!i+s?x(y+5kc&A=aI)04!*(6VNGFYo0s&{C@ki#%^Hqy8&0TQ`o)6u0?e(e;o4|7Dhvc%4_XY3LD}M)8})!?Yp5GT7vMmK*R?d`?OJqQR;0pY^%@&OO4R`yM@lI@meA-9Y!ltc9fetE zVMZ+iscXqDniREr>v<|}mPB+OurZYp^|+zYWoBET$odT4S9V!Ga0%8r4YFb{blrU> zNn5~Vslne3mL4*=Yj~fuyxxS@?n$T?bTr{RozQ5|T23FWu?%Y&-Dr6`tZCKz!PMpY z=OEM#E3uHj;Zw5gD!gRdE)<44=|e7_dWgGefs3bT4Qw9qdAO_HcnLKYAJ${son(a& zv=o!)W}%(e|Mx`0`qBXAL(#ViXXqE-AK(cjz3hy`*S2&cMe_Tz?IV>~9{3qL0Q+BDfNH30 zQ(5uuJ82H^6uQaHi|GcJ#(u(_9U42Ko=atT4OMCJduSncLLR>fuhimOo$9CWnj%Yn P;iu}SQF_a1>GHn;=6@Eo diff --git a/records/wasm/iyi-errors.wasm b/records/wasm/iyi-errors.wasm index 659d176e5674071ae608b789350232dd0f10df58..5dba5cbc70197fc59f0e730d1c262acef5c7b0c8 100755 GIT binary patch delta 2083 zcmaJ?eN0vflu z1+vD@fj2NRRt4VSwXtGAQZPO%$=YXCl5D?n8;M9xpqayaK7UcFu1xej`oz(~ku*Zs zZT!2F5E8%2@IUFAPa#Bp?AXTklng@T$4*%G77UDACA6zMlMwl_Q(`s;a-m=UH3!lF zsylK~su7wbP2#>>$c7)pK}r4+FXh7XP%T!Z8dN~3{<{WsaiELJ0yOG+ zV*wlm2#HB2<_U>koRD3)XyV9kCFD612Xu{N1M*{YrqR8HknL!UG#N&W@?(plx)540 z)o3AR42o|G!3ROT-UXEaZ|kpZg?9iJg|`UHPm6k!JS)kfXp>}0yjKLdo7X|O&Jyy6 z34(Nef&k>l7WL~z-~tHfiEeN{li)2re(X?j^NHrb>tjw_&65Ip;;&a8iHl=xy6p^{ zr3Y585>OkcjR3?k&TNEJxhQt?KlFdb>Lbo=vji}f=G$8owl?Ue#lT(_x88(IT#Vx! zoPy%eBmsp@hWxY?sXj?OrsM@FXp|ZNFyZ~1v5|2xa~?L`^(_{oNiopsH8d_;TJ&#} zswI)$33+f!ly^eVKR#!`VVSfDdppT(YNNus2mG`G_*S+wM>Hv^?FP$jb-YHcGcyi|moQS*2F{gM!oD2vd-#J2>oX*@`V5xuQ?Idw-6-+| zQ^Bt(I~Dd7meW|jY#doyjvI*P3G>Vt$rn>YcXz=i21BB@2VTh;lCZ$0h81=dbL;^Y zl=+bNv7o-v1CCU`Tr&F5ymWK9C&ApVv3IA%`;46%+Q%)eC#prT@=!x*)< z!3w84_F94!Mhh)%yLHUoe;G%;j#u_1PiDF0d6Vh-bP#@I{t-OeG%e-~j@*NhhSnb0 zo9aT@ zP7#~F>tpC;emNML9+{QD18aR@0;H~VdMIM(5fMYWf&4>6{xBArHjqnci*W;HW(oE+ zjPku`MG1$A6843>wmuUaNRrHlwfDttD>-qMxAzrEn(Gme7~?c zL+}`^R4;G9`B=9;GY(a|z^mgg@m_|{eq$2tFiEzal1%G3+8Wa7Zwe|qv*;b7DU0qv I9r9Y--(jUb?EnA( delta 2083 zcmaJ?YfM~46rMA~TrSHqy8(iQaxJfIDeSVLv;+&)rVp?sjgMH0H8B(nH5#=`eEiv3 zVxkxg2?+xhB%+W&DN9P|m8B0#O9}}qv_C*Af7GH;()bF-m^Agwz3kqNq9nt4pYxq_ zX6{~|P%lrYBP$N*AN}4@dM?J}DXu@Uv$&zI&3RP=Pg~{HKuTD5TyuKdo|1y1q5`+4 zEp75FxZB1iE5JRvHt7N+IaBkJtX;2=WXJRaBqBbBrj8!E`G!F}jEF3BI_txR|Y%EYRaG@)uR0)t{)gHqV0|E@t*6!=AX4jTP> zZ4SHyFf3+sG0(90F&EOygC>sbGlaZs;((qhY(O41V;T##5V8Y}p(X=}Q63f)d-I?P zQ;p|g#zFB_9(Z9;uXaK?K!^UpR`?VkD2np2{0&iUk{2ZjigzWM5`FoQxp^Igvy+fN zO%SB#3IdRa1@&+9!3prb9$NspCu7{MQ?J#z8sBVe`*1QBSM$7p-sn5!4N+07Rkw9O zCq1_E1p&2&+6X`t!1UnCc?ly?t>Iibqr9N7&#=6gGll)NKniI} zsQ0#P?4`#LOb{fFh4E z6?{?It+21LoW}ZO;hl^D2@7m$Kw*oR;}Ebx znGbm{8`M{OAvw_}myA9%%Wf{W8O-e(d(X9aud#DO`;4V63~LdrycE*Ga-9k5hQb`J zvBK%D!UsNys{^GGfOPb--G%4Z^j+J4gp>>4z zraDnICm#V0=%~U*@d-Q)EI5xnARh>GbT5t`)p+@(PnP8~V?mQ27fK^)+)``s- zLZQJ*_1z6PFYDBUQ&6!V+&caeA7=R3uZ^SaCP~*5;%OB}TaBImqM))njXoq!q|sfd ILtcye8)xr5(EtDd diff --git a/records/wasm/iyi-files.wasm b/records/wasm/iyi-files.wasm index 424402a81584804f4d5526800e6dbf2bdf14408f..fe94aca92e52d810ccc43cc10f2e054d0741f0d3 100755 GIT binary patch delta 1470 zcmZ`(U1%It6rS(ixpyWTONvVx#4S4#(Y9$enI!}RR>2quK}A7n)6xf}wgg(N?KC1r zM$#9pA%>3bR4vt5HI?SC3CX~|6oZuH!6s{)79<)Y+dc^XQ1C%8iRaFkofYan%=ylD ze&(Bd?%86&C>D%y-N5LiFh2aKqQySYXlGxtU-pIuEi`$vgZV8bZojoH+n8;xx9$4I zChyE=AH(*V&OZ)&(mDSg$mm~OR_2>8(`3Bj&=@)IUpV_bj(GNJ8ppin==r+$>S_w_ zc>RP&CP!B{1FhcWwKUqjWqKa)*4I+_$xE%Lu;#U_dzkRPCEV)$LeFg<80bfKDu)go zfxn_7g&4NS5aSRzU zX^I&?KXy>p`8EH_arC*rUe#cBvV;ccT=0Y@*gF0Ao&VwgD(5$n-=5b%wd}#<3CaH) zcUc4YyYreQJ6UyEgt<@h!7-P$Ky2sB3!0@ob0CBw(IMK+j*uA^iBi}R?WK6vUE^IU?pmtB zDMT6h|1>y-sDqFrN=B90DTM5mha^8suXhBOytklb*p`j+cwj53ABxno`s6K9u}j=% z-jw`0iGK!E%|}VSjpqM(Bx($QL){F3GRa>nuK^KAa$H zrm2+r(Vx4h0d*tcZiObl-F*^`nxudVp5&x@K zeUrMS&2i=(9t1D;VTHR>>RxRwyN~V;stnoW#=LSCQPc$ghW{;?(!H-e)PVj>2NVpgxwkeVYwcmb~(h&SU zfc<#l6Ez5tV0B4Rc|ugPhNLj$LH+}M>Yu&K}o|3DwaB%V8Cc2=nSaL;$X zd(N5f-gD0u=Z)gLF`*k6nH0v*#}(OZ)6pZ3G_@agFPyWGb*Il2wv@Po*0yXq+uV@J zG^Dfc;OCzp?KYi%0_jQn{QDrI4_{U0>yacGw|F5>&W9JzyojT2W;Kad-4|#*;htDc z;9a+$@bKjL>SkbBlB5H)3u0pxkG{$5b7{HXrj(b)&kNxjnMDDron_Yd`9nLC%&yV_j#9-TVM4A}Tazj78)->Pw;3)(OFBn`_;;IlL= z3KvRiwO`$BP`Sz-m z2LG1<9K=(eYJ`Zf`nae(DXLjxTo|&Quh1cPkb*OGxsn&JI0aE%=GhdqvFG=HfGQom z-q{>#GCxhwm`FaPrk|6O3ZO#8yC9nn`L}a;riK2S>X(oUO~U_S31@Yr{dbme%=ib@ C)Uu)g diff --git a/records/wasm/iyi-format.wasm b/records/wasm/iyi-format.wasm index 305b60a1abe1c63ebbbaba75d95674c6f4078279..200f3994f484bc6f0fdb66b486cddc07c12f306f 100755 GIT binary patch delta 1813 zcmb_dU1(fI6rOX=&dp{Mb@HIh&ogROnAHc2f?X)6KqVz;FE zfrX@!Hc3gb$}O>JTGZyD`N!JHC)HzK}6|)(eBJJ>Iq+FQ^_b`fF3Sgboyi!uvx9&CeS!0LA7{4H#dS zK=%c5!bRbdf>5ZSsMs8A#E&3kRyJWNgj9PIMh&)}7A!>#AH!GKbRWYmMswf8LPk|h z8d`B#5HokIa0fB7ZiU;MHrutCH2X9-t*)$KfIykQ z{lTbG@(#8EoL49R#QiC7-dw)RtWo8?hv^8h4>wRXjE>b)keG9kXetFxtEM#C_zv$` z594gE)l-$(7UHF^Yqi-g4c{G;O& z0wj6xDc=!;?hHq<5U6891dM9Tw1}K!?i=a6&;fO>kxoD~HZH)R@mLhYY}L33+EEB} z5I|g;;iQj8UH+P90FS!`=P9XTCm#y^z%5~V656fNc?adG_36>vwAH3=6wHgw^f=sA zUo=y>gb_t8v{yziPudJW=J6_9rN_P3pbJA$+ye4xnwt~>9l1|guJlyO3U{T2^h-}7 z9g!7@eC-4CwTXN?*o3$>K5EyG$ab(9(iqTfJmRjF(r7W>Hs~x*ACY8G%MB64b!(fX zMMZsf_gLV&B~!P`C)~3JO>y%(fk)FIcW=CKsaW-PPa+iCCrnYk^Q*A)b z7+Vecj;9OIfL=JeSi(n@s1uj|q$rX!hx+(9g=xcx?uQR8ZqRie{0h*hF2sI>M%8Ku zbzpRirGwt2!@IVfS7M>QwZ+xcGBl+%#>J`(y2J|G4K&RP(|$bRVLA85FiS~uvK`J< zM^BTDTy4TutUFjG(|DJLn4J%FLsw*f-tzDvyDiY#ZRQ(JQ!2NMvOJb{xfjgNur=EX zwrDLmZ*u&IC1nfUHkOHb@Oy{*URr~ zN$@rFU=GL>d@fTK<0hX$bHjY$`4!ObnGS-MtG)RLcoDjwPCx7V@^yYrZ+E)Zn}<7H Tjr{M!{+s$=X=j(~@9cj70lWap delta 1813 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 delta 1627 zcmZuxZ)jUp6u;-5+}D>(+hWM-Ty(E&YqoVwUP9Zn=-O=})}lk1;_5$S-8wSx&-C?! zeu=KgT$M2>>^WB~f^C;}ZGzQ32#ESYOvl<}{z>N+CPIra=?8`RVKC;o@0q-OXyE3Y z^E>CB`#blZ^CoYolQ-1N#zvT!3pNI4ys=p8;Y0D(f&P&_iyFj64lZWbSFo40Z81I8 z-V%+r=&_Ml{v7Bdi}`NQ=V}WPAklRtFGcOo-BRrP<##E<#k*XJ-;8HpIUEYqGlqBR z*T@*FSw;G1V})AAXyM2Yie0ku~}VTuS)#$lGuAM-uNOn7L;Hwt{Y+nSign*SZEg9wD$~?Vp4f z09MS|^Uwe{ZL#<7MFwgg_b~tuGQs5y<9m4q0)2doe^9|YnIw+@4&|Aq^znclNKT49 z`Vf&Cw>G8VBqV3&87O$=Vv@&5rsrAMv7#Uijq~ye=eE+W>?Jw@eClz&j8z#w4Uz9B zqQvj1LeERIW($s^fj3ieL&B%Ns9-9c`s_^xS9<|p-E(l-6ZHS(VKe47iQSlFh6Ogs{C!PF;7zQUoC^M2qo!Tda za$Gs0;1_hfAyC2F1s3M(%XhQdOl6HZ?0JiSMR<3K4eFADB_h8MXg5v}dj+qhlVR}~ z<*_aD8n!B)R*P7AoJ`?$08vzSDR`ZZ({NlCPAYGw7pmmRiiRr7s5--n&%j26l(|0x zA3T_nwCKsQ3Kl8HlYmn)AL#~8S+_C}sI!}ZV)*9!oW~ym=k@KrXmWMK?g<-x-bKR| zl*r1akpXUKu7X{tXuQwe&O4rR4R;$4x#(%P4gWepS1p_N|C(gyw!4x4VA?8v2Y+C4 zhK7OHSGD73HMG-4{pEK%7$NAgoEo~I0}9U3%sdCUKrSJ zn*2K8kIoFL;R9kFHGoCYI|1iqM$(sUjo<6g0y&Mt^x446)}bHZ8@|_+g$W;T_KS6c zBETblQKJmt3KhAN7>v^8D!6>fvDl} zfN14Seg*%`&=@IrR(>3$5w=ySEw7;&c-HE=3ZjR;P1iLZG+)*DyP#X7#{|jRCceS@ EA0#QnU;qFB diff --git a/records/wasm/iyi-generics.wasm b/records/wasm/iyi-generics.wasm index 24e074484710560f9d46186a2ad21602f3d4e110..9eb08cd5d7d8480aa95652c230aeefddd32ec1c5 100755 GIT binary patch delta 1944 zcmb7FYiJx*6u#%)y_3nNwn;Y++QejSlFh5xWVUH+=!>RUV~A-BY8ymBt&t$LP&S|- zOlk4e3I-RPijPgO(I0tl=?*NYHMTJcHQCmrRZ&t*L=XeEAW|XLb7ppT7K}d(+?>~U zUiX~28(&o8i|S~3E)1XdgPcclbLXy4bfDCEzyJ@(|$iYX6c;x7K( z#~9047WkiqFRf*ajyS%$-IdQ69r2Q5??y-0GUMU53K*j!-Y_nfK?w|*H_IR!V6Snj z9K{wR$A{#Iq4}T)ZW`@G{x+ZYfyNQ@K_!ZnMywJ#AY$ZJK@$Xw_9{3FQR9~?DA^vh zQ?~76?3SI9g=^O_Mn}xs#`qz|Ua*Z2zJQI;5piQrHD;bSpQ;A0C^pNi-Um)H_F@w2 zDPoL{FwJl+yav!~R@6Z+$K;b56zxhR-=D?UZ?=yNKZ1BVB4OOIhOw<^OkU>2LdNKb zh_NXE(->+w0NoHVpVfoECclAWu9(D{4uKYIuK`OnOEuHd1-;0 zbFk+qqd;~8;U37|pBBr7`4MI6$SB&8!^PVvArJ(tG~xm z_?oo%ztg2HIa$q6^^#Y>6Ts|k*JpjLElrOgS?H8P{XHi)YTlOo}`=Ccw|<$%;R+) znW1!w9}&M9Z}lA3sfZtA0T0%_j=!-t;6lusa$=b)XZ7>7@Z_9POOC%1^6OfhEMoq)f*iF~w7|0+@aP(;lQ zlTZjSW^SDVE|6}!0tFo2HV(`{fwnM;F9ALe^qL72{1!{x(kfg~^qFU8z;_Uz+s{k* YJ2ojYk9+ws{M{s7p84k{{@vPt0Hf3yWdHyG delta 1944 zcmb7FYe*bd6u#%4z2ms1iDrF}XmuNPeZ|$?7?WTgjiqTS)+Eq2kv^cLvDngvmKFM= zFlnR@Ore6%QQCA9niVu=S0c^8LfbS=jEaw~KQvAKQBVnl3boLd1|&UqW_M?y_D6;5 zd41<~&zZZiMK!jl4wq&_|A;4hVmRIJuRHbXuDZ69J%8VHgTH6%t!Qpi_maEZU+b^0 z_W7!7{XO?@eGaufAC4Y?+L6)GAP{LAJHUzbPiRu|OcoL8IW#pxLhf|)^Dmz&8+S2= z59#kQ##q`4(tj4bxsfq>@b&fWj$Fp%!MAOD2RS-c7!Q7$$Cy0$zJ9#~iXdeCQUaL( z4f<*+iI3}99wIO5J`WVY9lb@8KaDp$;NyfjUrwT1kCsC#4C>hxum>vjmI~;Fuzs%s ziW#sK z^BI!|2lZ`!n4nNAe&~QfzYb2x;8 z9~(C=!V%Dxrx~cN+{pkODA36Sm&3#R|Dj(^)hEDbW*Cq!FL8A!xHhcu8omA#1LAPf-oCEAJ!$U(3lKH^H7kBVbb*Bs$`x70Cbd-v z<*f3Wf|n?LK%jyTV$92P6V1;zQZqrTdS2~X7U}!M7Ij|11uDJ;$Q%2qdIj%BG_QD) z;+PUy112kus&lyT6p13;QiwTapMpPAIyZ)7dfs`D^&}F#6VL(3} zh7TSckY#~WjVd@tF`5AfWjx{oIA~-=;2hVk$kAt}4D8zyYRJ^O58BP<4;hTq^3=gm|`0jJW zR#_lFfZ0M>gYooLC~|6YHK`U}yxCgGPOx^;+*Xgx2h2@1wa0C(H>sr?ma8lQHgqhh z8%lD#)pqB*pRonojhETlZo6Cg@?gAPISBbZ)!WG5=@F{?2Htcu>ar){Cq)VnC}A@ezX#1>PE>yQRdhMCbgxb19X4kor5zvz zx4pohIH^E6@BiT=pT?w*Kc459m8Xz~FB2V%Cn`;Bk$f_7Kz}$258Uy5a+&^BqWUF< zjV)u44=`Zt90!g>cTYkdhc5l-H01g24bw}YV4r-=kOZ&EN^WT#E`WVT?=*Oh(R2G< Z5&z601;#sWewuzar7p+#Ya9P*<3Hk47<>Q# diff --git a/records/wasm/iyi-grid.wasm b/records/wasm/iyi-grid.wasm index b0670e17f8a1ab526480c00a76e63a5aae9702b2..3de0a786e7ba415803b943d1ade6809b01b074ca 100755 GIT binary patch delta 2203 zcmbVOeQ1?c7(dT*c3!V{+QfOcHrKp3ahub--J5PMXEs-`ZuSLjIx1MDg~B4^P6&#( z6e*2z74EEb-dz=B%()Np9=Hif6n+qHno|hF%t?Qw5<-MzAJ6l??!I?mgk5-fKF)J~ z=l6V^!@Zo!zMRS){Gt#JZ8=hCjub+)aB6LMZB;n(VtAd-Spq>=tgDtlDlhGQK3Ep1 zic~KOhZj{X?yQDg5Z1e^VFiTEg=&}sByZKg2+MD4$xU+FGN@!(R|j!P;iF&TEc5FM z@HIo5zPXb&y{-#7NxY$JJD~`+>1MY2&8AM60nnh|)nqp610TS=?Wju~-8_@F)M$&_(DCx|l_oUe*PJFs!e3K`lhg{7)g81x9b| zrbS~u?1nagq`tL>j!x>LIC;l(EsIgz94F_P*%YUp(4z-?$Q;(AJ+OnU)`VldouJU9 z=}tfm$BgZD7T4{AImN?n3bQJN_`pp;#(ysofyJpB{WUD@&d;-G$h$>}#h4_*&2} z0J5Z#exdMh`pFyr;=h^cp9p?$L;&r@dAaSD{B6+2N|5i5h@jPifehgGe9EMsVzG|B zNZZXvL3|S`lP3gN@T*@!J8#LeME(h&-SQBzx8#`tJJhNkvE)I= zq~(~Ss;7MCI5?)Jy0$uJEw|)%bX-vObK<08HT5KolT!H^RxINTgY$5Al+lQ7@YW6l&f-qoHrl=c!? z9up4mLXW3?2h$q1c-wL5%cK3Ud(Zi+J?^v zZN$BTzR((#O#?7M(F;MIW<=7*9Sx_uw198IK87D1NRSc?Q$ln^vr&O7eJ6pMN$&j>_8j9kiHCnAxx4=TEwS06JY!q>ts%p8*^NQhI{Ja0MzP(^{^aj&6RqX3ncF}z!=MOP2?syvl(hwwzNP@Quz31ILG|7 z9DJ>?N&mi&Hf_~KJtVg4rXDDTUAmR6L9?R=W&+dEV@Z(N5^qBmHC!>G6K(BCCDCv33qn zXu|XjKm*5IJnSrPIs$XchTIh9R15L0n}Uq}Rw@L4GGPuMf!m7k_Qm;V^GO`mGr|MI z4sK}J!F_5N9!D55-6voG;2jfA!nUnc+x{X9kr*q&Guf9P-JF#rzcYQs*rDvnuLSto z&@TY8q>_H2@NfFb@BhXBbEbbPc;}b^+Kcn^J1qIvh>bNMJI6%OYQsPVa7Q6!GDxx5 z!d|59=KUbP0X2!U0xbFUNgKl?lV?T9wd#P4Afkgp@w*!dBBfjAVadV^L9t@eQiovyRBC( zd6bTiDrw0-#zhDVrr7^U6D?yddt979sb(xwMZO73ULo?&0PU8?h`l8*C+$$XdV+u) zla`~7s-E**;^3&7;o9nwwak*=(s4mK$cdAR_0*FzPD&MKSka6#49>?A32|LkiF+Q3 zGcEA@QkJ|#7;8WdF&^nrIb=>(Vj$P%wsS|y^W1iKg4=Ir5-L1B>P&&-UFUg2X)lrG zG2s9&bTsWdp4PC<+m4M-dxp0SD?M+wH+VODsnmD*a!x#}^o~{h*skDZVQR&G|*MVcoy_ivt+SU&U z?TmW`eWCR#hX%k<^dgXF8Iknsj)t>bTEMq_JI~HJQ`3UWBxns0ELb^7NiCui4^lBL zdHtlFQh^LBs8_O+csS0r%blvy3r7i(3r8n# zl`c?D5n`8c>Oq$85WR~NE3wM=KTb5OA|6$H8dOi(_hxRN=FIP5&TsgCGN;zKxhkd1 z%WLq<+(HK>^F^C=m4VEv4Y)_H(|lW$AFK0KPKOH6g{OYtqCCmu8>ibg8iC0ln7J-% z$AWox>x^N#id^XDGYie5lrXLBcwr{QP1QDx yOYZ0ApN){CvUdGGT2$^gC9mVh&$@pAdR$Se&lJ#~0eu>8Wx1Y!Iq(IJ7XAaNLR~2U diff --git a/records/wasm/iyi-hello.wasm b/records/wasm/iyi-hello.wasm index 7785a65fe07e3d01120689199e0441e985ce6c3e..ea05c6eb86bfb28e734cd076bf99c47ebb8a3cf1 100755 GIT binary patch delta 1663 zcmb7FUrbw77(d@RJ-4^l!oaTGrpTJ$HiWfsJ48u_n;|C1HiC(NU}i7MG&Pxtvom4Y zypv(ZpQvDs$7EZ8WQGS@3MKYnUlw^9dl=wgOBNQ4!IzmOhD8&jh~K%D-agC+o7|r7 z`+a}TcfRj;?(JQ7_pZAqp7z4{RK2%+#M5AQ)|!zw%;WK6i6GR)KTKo;dF)uQGEy6< z51D4Dwl3b9ya;CeUh)8#Q&rdMP-LRdQNgp*=eEnVWO z4rS0MmX(&2f9X&H&?JfsRGUReEuG?sfj&QqchvH?ykJ0>mN!*CwuKOOwtq7aMLaBSbwcBi{34inJK=!!Hn*X9F-DHO!j# zPrg|b?TQjnat?NZDUP3mQO{4$z2$OojZKR!AHkp2{Tl@I4r&kp7vszzoNnNct^Ei7 zM!ALE#9w%@*d+SW{R*WRF8jLS3>?bL642Oe z#-b5i7hIL{6Jb&ZXtEZPA4DYi0Cf=bf|ZIK%-yjgFtkpwsL z<`{3z+oKP)NAvc6$T?)~cyBI$jQ0apw$TRx>k@7k<4j}S3nWYfo5d@Bv9VmWH01l4 zdm8x;{kFyyvDjANILmRYR%1V;tT2B;V+mVBYs?mTn|@3A#<(Y^^IKY@#%|(nL6%hK zM3L6Ru7W&SzO6uu73?9u>xUT%lj6)L@bMFqsw|vDR%16X$zfn=l@R3^OUv?4VSrlS zs)3gj%&GHnJdln0HqW@WAE8CXB23nXgH5zPZeX8gX4NFk0tUF?Qp{Bvqv1o38Q-)bkI58OI>)pf9qA9SJ$TA z@myn4K7R>LxL{bMMj%jq7wvnoZC9`^jV))aEcX{^G49T5^4Ae~*JY1P-REj-#&v95 z3r1(`rYmh9C?)7HRK1n#f&Pz}!#c)H=4LcV%M}q^c)w36Sl;~_s^G=Xl@%|GK`Vi~ z=`(&_M|b-)wlaa&67Q;d!6+qci%MF3h!kU^vTY2;4&e*%vjVPMy8Q)EqW8^T&jhfK2JFvbMgMlewa(|y{Q=@Lz*oe9h4 zV*|6TM1^3yTQ&nGumu7wvB}=-m#JTbWjLG(7{qT76O4&&aUWEI=e$Z^KglCDVJ1YDL=dpKehpRdw?I)%`Ddgx_nSEde|;g}kr<+!@3 zLkXNw3#1KOf9p^LuuByfs5Yn&Ew8Kn2KxMJTiMUBW6UZ z-g*0QRjeb%R8c2vfrx7FgwH(l&mMKTq$a*p>(0Pm=F+ze^fqoV0GE)$V3M!mPb~ij z{(C;Y=G=LQ0mkwteJ3=rGGX#65M6gzP;2ANW8r?iLio zbWRj`Eo{lllaU+pw0Pbg))xu*n!|`X^#Q#9#0ZszlgMad7L)7)F-i$hj*C&N^h4<3 z<~R+!C~r)XOsBi6z_jV(6n^=U&S~ZbI4!r_m8aYIACMWUdksk;nKgg%d@tJf; z@@_L``zFgtePgS91e;}{_jV9dlo91cF{K*%zz?^rSNmYIVG;vzb6)mnmU&%}rJha7 zt&SYG32CDT95kFmv8TjXa&TNHbJ$o;;%!bie`ddZm(ac)+ja%((!}C*Gb8T7Mr-Lc~J32R{#l L`K`zUXx0A#3QKSZeO5xf&p+F*#P@$iEn1Hrus{t$)!$-)~h=g1y zX#xf0u!c1%mJ$=K(64LnHaVLj`~X@EZLvi|!~;`;#`I4Uevk+{`@Y`2{XxPHW0SWt z^UUn*Gc&t;jrEfo>nC?iEr7PJX$6PpLa6I(&Ar=6%e>q2yuK~ocVw^-vS5+yEQDxA z0Sy@If$dErM0}KRr;R|WC z27k~Iz+B)*rKgaVwW>9Rcm#4IeGClV7YWct23LK|V_9C%Miyc`;Yw;Rza$?Q;f(pW zXfvE_dMG{ZRBVPMSKh^TJ#U2-?-4jX6#Wd$n1|KH!>ws4CHezq^8|?v) zSZ(n0rbS-AsfrEGn|!#COp#;(;-<0O;6KuKS-e#jPAcZp1QL0o!ac$APuN3MmEsHz zo$|wVSUYVhlnqYQXR=Of+ELn?DUrl$t!LaGs3@?PGRgkXrpOUtNAp>dozV1^x#Tiz+na#YqgwqQZc5I7S~=8oOr7xRv=o{w z2H(e9F1ilq4&q4&tL1e#JZarGLso0XaZCJgEY1MN<9LO^Z?*;S6>8bHbk|LbT1I+e z4|AHQs%ei7^;QG^3DBuZkKs;ydf$Kk2)hA3mdAtW_Qq%!L3-$jzQM`xn5x!BpP>19 gvl)<{?TH4IOqJ% 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 LAbRx{d@uh4(uTdZ diff --git a/records/wasm/iyi-inventory.wasm b/records/wasm/iyi-inventory.wasm index 1c87b2143d43ff54bc5b2c16f318ee94dcca001c..dbef64e9d48fa27076ef0e32dcf8fffb7e5c7ad3 100755 GIT binary patch delta 2142 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 diff --git a/records/wasm/iyi-io.wasm b/records/wasm/iyi-io.wasm index ee26d9ffb4e748a979003917336545a2fae189f3..9cae9e8f4a09d8b06de8605004f567b4a13c3159 100755 GIT binary patch delta 1460 zcmZuxYiJx*6rS(iy*Jafb+?=DHYKrHNQikfo1LXZ^P@gmlr&lqB$yRMlv<5gg))K) zGW4<9RHP*VFI6FlD5aWLlV(R2tx*3YTAK8c3Zlen`p^Ebwm%eNJ$ELVS)mKV`ObIF zJ?Gqe&e`LK%;Sg5iKPU_rmiJU4>fmKyR%mA1?xp4{DEL}<;!Jr?X8y8n%z3^-Bc+R zxIde2EHe$9{utIk+w_C5re2?}1&O1@IW3<1YLJn*T{`y^_NsGtqZa$rA6mSn*2YkS zKGhT>ZPeWvLl)Sf-j0*nsXo$Tw<^U^v-#KNuh$s{uTL9BH`NunZm_|6s#1UaUv)E% z9y+*dJ%wvjudK%eCRKF;9mu)e33Nr^s-YAaUH6+5_5wxKQbo~=>ai+f7u3sI%&USX z7u4rfn8A?y+9oUm1MaKUIMWMDwG@yc@k{~z6~8^OClcY8)L#XBkL&8pyV!^up4k5P zc>`viMGYXr4Z&{Hj~|{hAb6D3SH2gJy)J6l^8GPaqFOCd{HYBaT|w)de9 z%kG{&^hR)2&3}Lm&hWUyIv|?(;=GaOq?yM&D571W)9mt?5fKf{w&)D(y?3*9*sCix}GmP_+deR0C9 zogO`iQxY4?G`lQ^yWuFRIZml;KMMDp(q$oPmLxw)bR-0eA2zxk7UfzeI*}O zm+<>}5Pn0wRyTy$_S)>RPls$KV9j&@L)V#r8wn80hsW9dyFw*A#_kQ-W8q}|?$A@O z)T{5Lhp|2Myun>}>Qh|h&NR(8i8Mu-Y_Us7yk z4V98FPSCnYKCJgzD;W=Jv~&k#^I>=281j$Oe@g5hB-PcM*uR3^H*ev?2rQQjQS(1E Cpq>-} delta 1460 zcmZuxU1%It6rS(iy*Jafb+?=DHYKrHNQn7qHk&1+7OB>X(nc$S1hb-uXrn3qpo}1b zj4@rc5ot-lYg9;)rjpQR1KVa~(E8wmi6$oYA(6H(#RNh0q1yUT2=&~VWM+jf4Cg!F zIrp4%?>T2jj+!G!&8d|HPM1~_=T0`~avg_X*xk|lyb=CDFuMNbvboOI_V(6nZut9> zn+n{YNjH|6hR=PD>~P!p`;jfZcD@!Qj!!RW@$A>5jKnQ>?g<=Lb9bT^N7SEMyskFK zP=kKe6eDfSIT%A0xL3U$Cv~6tOpB+L8%NE~Uz@+#Vi>$WZ5Z8DSL82)jn-3@`s4qq z>v8nZ!9!aqTv{F3iYZK}>IC+n+38N8D}tgrnIdD+`8I`DfJxO-MbRhKBUQxCtCzHx zQ3XxTtFbD~;8W+-9asZCa$c^+g+63eO92@YPZcm!ap}I^NQ7Tj{}k{eR@H^~u^oSS zV*j3LcL!9n2=Cpl) zQM66Y8j$>#X`AIp7H5r&ueC!fX~s4(uo^n$s`)sC1fc}ji#2> z{$r@a4X5`Q`XX@E;>Xx#j~8vW2cn5DFB)l1nt8&5BHASmm|Y%oMnnU%B@P7kzT4XS z0{b9*2ls#H2jM#idDhI^10rVTg^=&b7bQPM=`Fz}zqx2+SZd?CKh;zDql^B7wpOl+ zO?$--b6N5$RQzM0)BH45FZrdSoe>XF98V*wo=R8 zdc9tKCq0avq37Sc=#;+1Rc@c9`DXF5v76RS^4Q9azU^xyYECZ#Jy4J2C0c`S@OfGT zjokpT2kGgFrg_&sb)nBpSlV;_6zvCpt;|f1xPu13A@5z_vpOQ-6%Vm3ep%29cCA*| z;5_gZ-eFMcyK$siR_Ri^DB~BjRFbbu+pb8KX_lx6wFxvu4i4(9G{jNX2vNayCq)Nq zsFeKoDOwlFNAzB6CF4PjR&IkVKH|JFiTuO#pAx%?q`GjplFAnTAJYW@d| C$(@}5 diff --git a/records/wasm/iyi-modules.wasm b/records/wasm/iyi-modules.wasm index 8097637d612173635700b21deb9c0efcb8e19cfd..88253a2232582038ccf32135e27edb2bdab4c365 100755 GIT binary patch delta 1530 zcmZuxU2IfE6rM9@XZEL7i0igdQ5I`pDc$YfwO0Co4K~Kos39>bi@^j$Odpyc-a@U3 zH&P7Jz7RGznyL$llqfANCGN!a!KCqlMnj9X7!7vSw5Ew^q9l-LL_Kq_-MbIn?A&wC zcYf|UGvD0&TrfWu9BT-|`HRWmgCB-lti5R~v&VWV*LfxmO}YLv#hNmv+%&m{!)xFv+y^6yTKJUj<(P!oxHI|o6a#?~{y?-_;*BwfHv$e! zvAE3g7y}3(Iho*t0UBUL|J(z|ps_H?K;mS<;Te*8lBGP$0%OEEcM~b=>nh5cQ1A7^ zJ@r3aX25T&DEYlPxOpF?_+JfFQggjf2e&kT9sUY96Vq;kSj#s-DBC9@q30!@9(8yN zh>cj9W^rSMj=QRebcps)hl>o0NZD)5fY$ zRtR}mzAW*5dc7@B;^8YS#cP&_HzdHbUiUm1T@c|d;;ztl5@)FSr+~8YJasSen^7kv z9+NoiTI97|RcsGk#hJ&+6k!h#SLGIo-_q;iIAR7Tl{eE7lQmyif>RW+3d^RTWuNX*P#@TK!fY+EI>Hkmg8bo_XyWUm z5=&!rf9O(~Yj2E{s~T;71iz}j1xu4YK%Zy5aS?9H{{V`a BvRVKD delta 1530 zcmZuxZ)jUp6u;-5cW+)&>xMBc>U5bMTh}$|YpmVIn01P{6_tTZm+1tVt5g_l`nr`0 zK4&Mx^vkf!yHYiNFp<`-DHQft{2<7{7FSpO(CRE1LZd|)IAQ%T9D441HZLCY+uFJg4|eC`kQ&V7iggw4P`oi^rP>p% ztqChNcrZT<*5Go!1FXsRFRlZbx{(e}=7ldiwD-~2KhM8%Wb>t3#_(VIY+#IqZXo@# z?B%-|(+l5NJ$^mLm|l21i1$#Taf9*f@p{Je!c*t|g7uJd*8S1*h!6p)5dbx6PtXi`eRc^Frgq;}(K zpQJuB>ZmqTDx=96cT_?gp`gAC6I*bX!f*!QynCz$z6Ka@ty(zOEv%Ne-aM@CoPZ{< z)U6YcBGY&h-0-!>x(q`|98gOqsj#e$^+G-T;hUT4vtGD2@$OXy0wa85v`6Bd35T}; z_FrXjndUJDU?2sV;KK%OAm{$t3m-ts_%s8FKU{Qpifmz;C4H|Iv=istE2OF`HB?np zLw)c-(~sXX;CEHE{8?YzLcmh~Z}DNp$YY}`dvvxFbwx{v znjMfhNdnprI70%`xL85_Ng9)AQi{%L2y&(ZxnL)C`_>nvnLHqB=&)SMZU&sz9SJXb zh}ZgKftKc1YxE3m=?tuoI9i%C3val`mt;Tqn)KAVDZsX;RF8czV3QS=PFPD>Cj_Ze z6>hA8cseNO+n)`bpp0)0*t5ZIOE%5@|GfGl^!wBvWd4l>cltv(YCNIzouRT2gg{H-ZT9Z86ctH$^4> zK0)t~#3B9KYbWVJjTToyHV(Oe4#WL>>2KzfGOSY%m!S_Vm+%1nJ-b_$U|IeL%?q*O diff --git a/records/wasm/iyi-sessions.wasm b/records/wasm/iyi-sessions.wasm index ef25f004e607daac28e55319f966481a0f3a2439..11f43ab9ec3c58550b1f477db5adb1751b94c53e 100755 GIT binary patch delta 1772 zcmZ`(YfM~46rM9@=WZOr5$O*vn`(g=smV}7_F+RW2xI&OaAhaz~} zTvpzwT=zphK%J=-lUr|=(e%iHZFTvKVTYHoM#h+Hjq!me^B7YDf2^>AoxY6Vc3?~m z{9C?ROx0o1>?@&qlji3o}x^D3_Od+BIAs zkMjuNJM+x1HF0l7;a_q{?Z^d;|!nb6UZ>KxQRR<-$Y6Dy`f9r!1_)Bi>hcEEO2_+yiudAF$7Mi!Y z&QO4$i}tSc=QX@SdukwQiFqZBJiTApexA-aYZWoWgHTX>JCR54wlkl}%;X`!gxN9( z@4-cRYY>{j_a8-Zwfuy9^*r^r9kti7@29#c!AVv`0ZFcES}JC z#PTQ_vrMs9zoxh`>RuHR;+j^g;diu}A4kcQ<$f%PPz6>vxv4+h47+%BsCX^m< z>q!l-Quc1ZNmYsL7*5LG5%A{4RdW)Dm(Xph7CixWS6WV)W5=vswb17rG@M3BrfOzI zaHz9sY$R>*CMTV5f68&3G``J2yPW5-bdt{awqCU-CAY?5-b5+i_zZr+xav@zzj-yy z4^!Hb>UKH`>h@vk_C~;EYH(#I(-yCzP9~V%fZ~)moLNVN`i$Ycs)kfe`)6}xI9;->n3-ta z9RJ}TqL5vf{8$KGgfWUC;hFkl3laexf{TK|8DoT)n2@LuvurVX?t7!JiIe1U&-b10 zocq0d&uizT-Z`m{_2k3(@!tIFcQ_Stz?@oE4dvQ zQxE=So+_p8Ff4n^sNb;sq>SLGT(;2-8~Ms%Cy{&0;Ur1s$}Q!ZB8O#VfY7Ln2Vjg` z-3(BP|M*hvp)jk&F*OA#PNfX=GU5;yrY;0ASzFH z!w2r=T}>{R$i-IEtUC*jg0cD)1HF;E8Gs7~x|!hr&>#N#Km706{D%0{JOirb<(?J| z{~j}V1h93U`L#yw&1(E}9{Ej@mhV(7r}Bv&cpW06vkWx+dcxo_T2r$uY@=#Wz|XnW zgye@k)FCY|^upbhSIG(Al-0h4?i{Zc92si+A@kTE&9|Mk_p$G%rb3xByOI#%MtZ?Idi3!i-D9h8>n~_H zlrs1OAnwDZ1?E>sZ?{6>j*EIdZZRXmok=G|eJ0<0+kA5--v+iIZPjnG`zJ&j&^bT# zJ`@>)v|U1=hBp>im=|pv=~^wh?zi3YD$lCOtrNxil7`pF?2{mbhUdwxhRZ1YTD^2XxhhV+8Y4pXuwsR&P04SO)}B+4Jb{SqnU9`sGl*MR^5=gi9EVulf*Rk zv#KG*D;#I#kC(vWqb(2R$OMMR<9{BXYDRTqXY@+OmhvO~aK5BQ3mH zC_$o)oY^@EM_kZ$W*U6ezt9KTu3@+O2|@y4D^$_SZK4EroAuLhd=LFde&pe2?4`s! Qx|z?oAZl(a=0D{83#V7;;s5{u diff --git a/records/wasm/iyi-shapes.wasm b/records/wasm/iyi-shapes.wasm index a2b20c516f0216189bce021ce6cbb4ceb668c39b..aeb71e4c14dbd9a63b382daaac22218a6ea0f1eb 100755 GIT binary patch delta 2086 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 EKTeGamjD0& delta 8499 zcmb_i34D}AvY+n$`pe`bOzs05AOuKYNVqRWR!|5cEV>d#MG+ zuBxtX*;$pbvnpfDjYHXnXC@8Jsz_L$lDK~6%$Y7%>}k&0t68gfXSJpqfA*Oh-&y`D ztF}*8uu~>msNP-Arm{t9=ms{NEw+EVf$d>z`P?xZnLTDBukFOYOR>LxjlZ`hx=){e z{*r_4P$!9cs?Ofi+lfGBJ7<2#dzwgvDR(W`1?$MR6IMu5P8_J~i{A$Y!(D z8p77wFJ2Uhj15(LF9E$$eNMy8D-w)sc5p>L{(iM$v@wYdT~P(tyW&M-3#(T5Fk>*w zvo|uM$mqB^CvJ$#C1$L3xgK!2T*hZER{3@(my3R4vz=%fcQaP69*Qz9!%z_gIeXL# zG&HJ%MBHPak1{&2=tmtv1vTn}1S1W*Ga%Zym9a-{6=T#xgnf0KaUXE?d_2CYRtMva zOTZ2$XzV-<^>%tYh{8A^(Rg1%Mrx{2$%gKU)jB>rnYp5v){$2li2(46-|0*)n!;Q# z#4%tuxab#u+&-0N{5OkI(p@h4#YZL^9#?CVjZEdwhUM?5-)BS00`*w7aRY`g zvW=ZE)b3zh*0H*qw&cDIEyB#obDdo-`iXjVt^;-^UHLlVi+c4{M_{Aug3BNc0>*i@ zr|$Lwtyc@Y#-O-*EzYPp!CWWRtgc3?I_5Pd#!QK$Rg9w3OfBtYWT~4v857w^Ro}@N z3df)9WMp?Mi`R;D$mXt;X0JVz>~hgBK1rKw^v&jKe4LS?uI_A1XG!+cosIDf0$7f* z1o|w^(OdR(4yyRsN;p<9k^?vv$#>2m+f-VFFr}# znrmES%k8P%j8Dk!F+H^1(|VBI_M9F@JY$9G4|!O&LVc2_b$Kvf^Q@wfuUFghjb$uN zjp=C&Vs$Fi)0hF`dBT3B3VVU&Gj$ye@#@ZAMlloitG$fX0!!UisKv}G)M6@};e|qc zSZ|*zG)4%v%pTC+xWL#WcELcS>X%5~H=iHcdiH+hB#`E-DK=y z?6~sPAS_PmA@B5F7jsYMF$g9@xFo-X|HRBUTuh1>o|81g6yI+N^4?68t9$9Q_4eEv zV?OI$|C)=L;>~SA-Us96*IYgaD`bRtId2wt)lh^|gBo6EWaYhu#dw^afq1Mo;c`qK zZl!{B3y}_}*>y%2_NCfbXH*3n_GxuQ8qbHW24a#OsE<-B4&84i#Szz(#?X^?4cu(CF5*o!tBSps!r(^li* z4x30V7UOO(MH6IP%S1KF16(Gm?Xk}oDeZ!^$>8B#JkI8pGiP`8zUQ{WWqQvwby6$U z*FuG|6cohJ%G%gMt8Ib!u0=MD>d92W;52p)e(KkCW_1ft(<3YcJ%zS zaa08B5ZgSlO$^Sy8DDB@QuDqY7!A^HN&+~j*c8to1%AQAo;P3v;D#26Uj^5LjNm7p zF0DL`t*V_}2WdNqWDM__Bx7gD6rP_1-S{|?g?4R1A(yCi_One!8S_SLrTr)4irSj$ zu7IVZ4;uTuY?IyLf^nmnv?q=6o+K+C+EQyEcBna*jIQjMU3~mu%f$9l{$IM_ypraX znP#zgxwH`zVmr`WvjX#BLVQNFOc86R^IVu)`H+sNqs$9YnOanW>5~%eVQCOMUj!`G*cRhB71~o zLIrWoV%P(sM3s}iMWlF(-a%R(T1^Jew`l>&t~?LckUdTK76N9Mk^u1?ypipAt{k3A zz zbvH^MaIZ`xOs`2;casl=^h@vpYQ-!#CEDbDJaUBg!$s|f5bU`KyPT2y;6RNu%KR{i z1gG``QKI38@2w)*-G#w)AHwO3j5pDxko5yRjy-2AXaqBt;Dfl?;N{)OFQ)jMCb0dB zF_B<~#2GDFqa92fP=l1-IGmmq&~<;Vo%n+NrwV3lnTJ!<~pNm`fYVHM1c=Yk}_B6fCHK~yJ_B3~e>?LYaz4D4$V0;)BYnc%>DkDh_WQWwxlVobw z`A&@(>5=)OOt%T4Y5aD4aFIlXAPSILm?Uou5^L`mel0fZt5Pj=K^l0zD#db>!~(>J z$DAR3W|DM+hTL_!((Ref3n9Bw?eBnl@wsHVgZ+cEtLWc9yH?du z-B#_>WNOO^IyiEI7Gw3+6LbhpP&Fn^&JU_|9Tr!4q?|EU=9J4qlXr#+7L~fkkvPY# z5REdYNRD?G1Fg{L9aM*oBkRUTxZKAyUDkr?bEq<#cXycDIde^zLxo%oagm$~{xGRN zq}+)!#aeJ6qjaUgCPXr-0#wSV4=p~AGeKxO>_rlYyr)MTwfI3Noz`In2|L9j<7ncD z#SdWur@|8?;Vk-Mi~oipjKshVph&XegRp~T-x*j|fIhs&QKc#p-`V*Nr>JWJ?qi#kSt@~SY9 zFo75=+Df_uyJCt*iTz_M9GQWl0h8Z=xO>Gyc)d)uDbDQ!#uq?mB**sj0RC8fF3_uJ zxnZyb99VJ=NKo!MEE{9GLnxCkm|_~vPsGyYTH9@4_IPM@fx&FET_`mJ5qk1)9SuX^w~u3 zisFm1xYgo)q53FHz*Y;LvMF9CJDwvu_7W8=13}$O)U6gZB8Y?cv1B|t2;G33PUR1P zW3#$HTjs}{Obr+69A0kMWXt{r8myyUnf{A^x5NAuUM+M>hz7UTY31lb%s&OWPF>eY z{+!j>Rh?v4o^}XcM3d7usa&59Pg+ef#}x0W!(C*0aGk{;##wO$HsQQli}NZEu7gAR z;A9q+7T<~-0Z!oU8bUT)gFNSV!iU!)iYhEVpU(cj;iD)Rk3bKL)fOV+7&&USOk%d@TNlKhd_rmWIRX(I+fx1II3Jv&eYu76 zhO*>yte1gRmRXHzco&)4H(q9d3ys_|bP1Tn$Y;Y;@P0w7Ew%U+Om+bhAQ*SgFVxJg zatiyidbg|04BCEuG#$*Ox)u@}{qA98?k?cT+?Ry-42b9aXg^J{KNIq538wq=Zo;1R z1WXmK_@B;C_x+;CH@_wy@Zfp|BwnBWNv5N#qAJveKpf>NOYabk=!NDsukU3 z=iop|eK8AP^cR;V!6UQ48!JGC`1R<*OZZ(dY;G2~ zE|eho=4Mg-_W0mt);C)qnwoF&?c`}+UQ)zi}1s)0O z@+4}~CcAG>iOTYyzb1r6VngceWS=}|{)2w$Ke+4T)t36Dsr_YY|9}4CfF~3_pt*CV zH(asIuI>*92bX2(9%`&dwnMF^SXfM<`a6W?|I%KppnVDh<~7bSy%OFHn2q9O2?|A= z01A-*LFkI3Fc~y!Wn6>!q=YXJOn_p#XF#sgreMcED&dbf+>d2>^HGpX_(UAI0vIh{ zfR&Gz@Eurfh)G1^D3hJUWA?3sr3&|4a+SZvd4w zD9>oQB)A4U8WZFGAKAln4-+4h@UFmd$ilsv@_sXAT{GoKGv$e9%6mk?Q!9D@aEZPZ zMc`vC)W3(gej1Fny<_AY%Ad<_lPN!YIkGKRL)uT>mOcqO#4i@fw4CN`D2Z%Ch)#>v zZCFg(pazeXFJ-L6E?@%+lH@J8h#?R^_Vf2}%s;9Uua(KcW6p6`OD^2+r>+-=-5U7b z!~4)-#|L}FhkkxN#BU^?gU!cXC1FRM;|?%hw((R=H9e9o@B8^*NU3$0Yd-M~`_ZR8 z2|^+tluXN zm686)94TN7V0Qp_JO|_-qNI*6#Z#*NI5{_?77-B}gG-Nltn6*_9v<-)GD;$FpB^WR zjh6e?h=_Bi{=m1lkZbcN%K6r5v~-a3TMrL2T`HQpq=nS1igRc_099e__PFy>V-$IC z`=qo%#vX4zN~45ILQ>uLlk56=#2gDZ z=5KUol_EaElbG7qL8iyh=~uA>vGS?<-Sx6_=fwfkFo-{!0q-vgkZNZ$yabVI>83cR zwu3J?KY+Xa*BL&UfT}bBeb;%jKK^KaD6PS{hyKO!06)Z{N2W-f}z~x#-0z69VWi@N78=|J_Mymd_xqR#1vo`{Q8m+f#L7DcF~P;e~gp889}-xj0W>h z&D=?`qD?_vtlw;?qMjmpb1I{g*A-Q_@vW)6no^n0tCdXIx5@aH=**As(<4YL`&|^B zCvKCA{#l85g5=ujcKKEmTWU|Aj!YP{fQma>yBKs`Xy6B?$bLK*JNMBR{GT#Ytb!UE z@oA9A$9@DV6RYeGe<}A)b^htVN@vJcGyaI@k_X0*!q9LYrG1eGw z3h`A;B&~LuXbMD)KP=^uuw%ET;qk+Q2@iq3Vv7&-hY1pENZKD2&)myCj5XRw*gfwv z-<FPAII`A%kQ@rNo? zkb{)-${8{P@`E!_1HE$o3~YgXxh?`Hio$CvG8p6TG{#aGW6n8F->mKXbjI|DFU6ft zF!mb-9CH*ru$(dd;nQ+4LXF2|`dM0hT5UZGehy0RJx5-pUOfkIfxA7+S|O!CULAnd zD$oxv#+>8r5{4iRdes*LunnMH-W;S(z4GoM>JU~1L$KOO-rXaRv8LCCDkmAcXhRWy z!};YsvVQ>XRedAy3#Y>NQLA15sMYT3C>(RZF?C=Pc0DhGxi1~uCvO$v{WSK4Ld+)- zD8fPa4@Gqjhj8(E^?MOMhvwWU16K`t7=VKd?qLEK>5pIk2mj4Pe>C~+F$PqNS>8s2 zFLaxj3%q5F`HdR%B{XizqPccbE^gIcq;hp-JH7@Fhld#$d^l_pP=l*smSbB%(1st; z>O%SVf-X2LD?YY9#}%)xOB zGf%_yG=OH|an%~UqszoCAlC7japu>StJPA4r(Be}$}P_+;faMqqBIt-ziqre7C!=Z zLd2?HX7>+?BS6S@lwsKp+|mQfp4tp(G#R&h!}j7+EoDW)vZYn@m|$Xi7jaM>C;Y5I3y1}jNI~iqZ`!E zAD_|`qGo#x{y7n7H}Gj9kk%tH4NC|yxrrP)Co`UNKF*Mq7pU8qO-R?uJtFC%!Mi9s z8~B9oNct?FkndHa52nnJA7C+cWWJ0Up%m1bR7ZF?CH zr7X?W{2y~oPX-%Dd@d@9T>mx~qP<<=WI|Q7;LonIqk1=V5qU5G!ik%FVk55c8hnN( z*-H1IzPz=OW2su3y^R*)9qRS>@j@BBU2130FB`AnPxQVgecETg?~mIeT{<76cmItA Stku5~=!bw$)!)Fc3jPE#-)uzy delta 2192 zcmbtWeN2^A7(dT*?s@MAESQ%IRw#Fq^eTvOW1v$&$J&HIw6r90tCJq=e#fXqh%}C-M#nue$MYa z=l6beUrp?$uPXpY8{7fq4uB)m9Sr90dZsYHcBf3O06zp}VFiR!quw399{8)>fP9>w#o|weoB)nd{{ZEuNG~igG4pwidstG6gwE zIjKTS>oC;es@PPxG)ZPoQb+^b}_0o$wa8;v+0XVqe9wu;(KK$B0_;1GhBgt=`Wk9u<>8&^T z?QRoS0dF|V{6;nU;u`PIq`9_HF0RvFq;fU20bhrQJBJt;{9La|Kn>nC#Byva547P& zw7O9K1FNV-c$k(0k83|p^SJhzc@#S@KP^RH%4LFyiIJ)j9hs2BrMLuU)la2((P4(i zEX+I&vuFSf!sDtjczc(LYd|dL6JyM;EmyOp3QxJ%=qk57$Al*u4vCG?c-G1 zRF|0}9y0h5%c5w=QpIN1IMvkRat31zX?mWzowf<-TDV;#Oc-2I zb~f-K-I4TZJ|y4Uj6OJ{+BRc`$JF$r#c|03n|2Lg^Uh-A(wH8y*l2klk9l*VUYf3^ zt4@fe4n}>)q8e7m%5imN%!rlYvY2=N061L$wFhW>^LX{-^)*uv%lw&lspcBo;{YkE zYB4oyhT_>YwoeF`!KZu8u<*rMA>oW#s^lr0#reVwcTw1DMgZD3kaMksv0c5p6Hi6j z-3<=eT#LR3+ck5NA{=>pA+^-Bp%pYo&eMsO)3!P-Hjw2y%sbU&End3El=?J<2eZ5) zpf5;Fi6zVpteAA94gJaPPP7J zd^Kf$uIB%kYkD%+IAU{A3FP{>xe)E`3MUh)stJE^l{MNWU} zG|6UzAJR8(ZRA+0)~4^Eh4>-$#)mjwMo*WTZ_zL7ui^|n?@7Ptv!C||Y>_UVYw6j4 TYZhztTLQfZ_*C63JYDc7j^ArI diff --git a/records/wasm/iyi-std_text.wasm b/records/wasm/iyi-std_text.wasm index 173bafb71bf57a6bfe9998ff76868a6518ae99e9..358b44159b69b268206c09cd4b8d410a49492175 100755 GIT binary patch delta 2292 zcmcImYiv|i5I!^0b9>oB0Jn=^By0kOqJ8aJYFh*%1udZKH+|Tl&!MX*WSricpH7ty1toQ#B?2B9SJTlqfpq-tGf~iHWkw*)xxC z=FFV=?%f?8PZ=IhX)Q^I6MIY3RcSi3rcEjcY|IG+YXY0>q%!bBuANo}kr^@TI)8dF zCzzKR2xR8u+BeGJJqXxuuZCq1P=l-CE#Uq6r(hZK-nfR%y#MqxWV3EhuY?iat~;9= ztb!I|@1AENwGy`3M?Qr)u)_}Xw)Eu%yJrePfAVLS5F+WC!1{S?(Tn!GPG6c3%h;KG zjrd#C8z6*U)U3Rp!8CZTD9uUoc$N@T5^!f&(Tj@RJQaWQAD$+JUbNBmZ(x4~$2&zF zpDKi2^rO9}1MY`g_R$VF4DITnFeD=s*^4{bnr>Hh!cGXHTZe~&c0cX{VhFtj4kPt7qPN?^@LoXVzzv$v?8d4)&;3rT=PV#X|%{~eH z02-HYUTBoLKE48_;d25EIv2LMOoP-pk>zRu(25_?$-?+m8Jyx&A0G!Evld4wA$>6q zpT4)J6{0V7l*`1)(I`y|8nFusaW4F!))nG0gf=CY;7tQg9a({?R%45W1t1sF$bj$@ zYi6AzN>7O_HcK2!lk~*gAz2*rZ@r^`Ys`NDTtMWu_ygbqq|t0vTYKeHvsy}{*4Sdu z`+R&zl0mhDA`53kyI8ZHyEr!BUUjDTs+_b$&N43>bdifM1CFK>T)jbGw^&(nr9n-O z#@>)4%5w9P#)jl1SC^NJB?f)Nrv?4gtQ%(;^LPTWJP~)}~M z47$LT^%aMg9sh=oKo~z9eVQKXgB(SqKle199?D5Crq0PpdqG>mLo9< zi?|>*GbJ=;ZXDAa=Y@U(so$lcStV2WR$3UG0W_!+vOGG|5pjyk7|=}5&(Srg^QAb) zV`&a@EjafcmwS7`C7(mf`EfbqP|~qh#;sW~tLcx4acFPM))bSlIv$SsPsWva7%qrg z>*C$cs^_{V>eV-|ADp~6a*UJ;uf=Ui%j6nH3q*$F7L*W`F~vW z8y4<|yU^D9Ter8x4cr|6R~Ox|2gjzbdPfh0UtJzd2bqiy`(!T8_Zl?9-!6V18HOc delta 2292 zcmcImX>3$g6u#%2-q*(r4d8STjD$%_X+gS;P)aLQS+t853<7NgiIgP}H7EnWjL*^^ z#Ntu|;gz5iqs2@Eof>2=odiu&q-8DegR+L0v>`De5fVrsZs)$oEH#*zD3jc|XZg;# z=iKwXH*@{I_xgQrXH_ojZ>-K$)w$4_Jt7#KzGn5z>1*rlv>FIN(9W)b*w}=1MIbj= z7%a{!D#|Mi+IMSU4;0#;FNAU^ROc7MXTbZLuR=NU{?%eO^ZxT|P{6u9W*OYz?Y6V2 z^R*Bm_BJes%w@2~-gO8j!Uj9a+njfEwu}>k{^ZYQAw=3OfvuC+q8Bx|PJgx#x9+-JM^MH`^PT6rZehB7X%0*yN>d45jEi` zYy)UfZ9l*|gcduan?db%UN?I?R8==*YwN}y$J(s74%OKMIlz@)ItHY@*N*e_TI{!a z9ni!yj6Sf2 z&I&N=&?5lSNRmfL)RUxN{=t4b)gA|b*e`%%aguMdLBDreSOC=6F9Jp#`co2*V-`>w zWAQm{MGjY$CHOv6cbpJl(D|svWf~MYA@W_V2(%JFbh0qMavG0@O=u-4&UgI)xWIRgKf(GnarI-bG)rwO579p(UYccULDI<1Mr(X|F0V%jVLI>WTlax5X? zJT8dMObLz2N@DtwywFb|^|~}PYo(WOMKO39&Sz^ZW zWsptLxG5~$1&x4NWXbp+?M=FeEj&>k03Eovxv&_B+XJw;EX$IqJNGN?*9U!BMoP{( zzw`H=`|IuS!?nZo$q2E**36L146%nlrH2B?3IoBTf#c$z_26MeVyhdXSxL0slNT%u z7W)DLUty70bi)-E5N%md$^tT$1>Z7~Z5yE!2#&n~he&MN3`5Gh89p;e*5<&nimKQF z%d1F+g3$CfYZOD5A)CD?rj$4%mM_B=)+RFB$r_La?XZ@yGEve&X1S+FPDHY(~mA;f~>^G^7JMTAdK zwTP$^RAyG3Qes-1Rp6}b6W~E6#dsH?QZ97CS;l%~<5d`BEF!mDgVSV-PrE64R19{* zQ?dqoENfDUh*;{ez@=UbTzwt-De;@@a6@OK@@OAiF-SfcfM$br$PaEpxRG}D`*!q` z$j-4ubq-FXKiGRh*EwQ~-0sDD%v_w*n4=b)8e=+gbZQ))(jPv04*qv4o(MiSr7_Bd z9j+z={~R`9H^U24n#ZUGcS_-RifDTS6bpM43Y0F-?8IjFMkJ;&17i^rf+S^3^T(|K zQ-%k?szUMlUMev*LG75+(E{_BQXcn_??wVrd*z+4u&s`ML`*|>i_@row4zIguD?&e&ypMg F-@jsMJ%IoK delta 2053 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 delta 1797 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

md5G(J6HOqvx)`GS0 zf55uFNWqfVe}T+LVlYahFxFB$8%3Eyk$VHC@Id5*SuF0xSyah3;nY`ayek~Q`5+$U zXHPJX;`F^HDV!@rnZCl592U;F+AqrD`r7;IYvcNMunZ!mf3g+dFWQ01Hgpp*0|zOc zUvQ1zIl&4rGj_W(mec8i7FsIdh#0d-Ow`Y6{33x}Nd?yUAwsM1^Wi{&Sf=rVCP!Mo zNs6cSGs@O4##p>Kqb<_-14e1&*n{Ad@PMAE7>|06sWGCPYW#6h1~(A~E%fvhmtB z{dV)ERna|sVRe~aCC1VX7-XCWd_V;xdf22e#hMgQlY+uSUdN2;JFq$>pr((opqH9M z&0rf?V`nlsHtjcu(Uex&(t@~@E>2CWGO$hiOA|&-J&!z! zS6#1yJJ+`Sm0rr-I$q*#)$o{_@Hn_szw{Vw$(V*9W${Fu5KseOk~mqB2OIb`?@_Gy zz-Pr;a@zrwB0BKC=%khZW74QBGc{D)?wxw< zv$hHH<(Kf?7@fBXpNY{On{4^B4fs_|zO)e!L}}x0BYvV2llD4#jBi?TwxI7NVvf?T mgwu?_+R#NGQ<6TS(&a}xQI`cfacfjykl;s0oN34XVJVQB)-tdr}1%?Ad5(tTI-;({XlI zRwai&>4cNqcWyVFk<;fYz~uAZDMLnB`bTq-3+&NbfX%4Y4;j*+ zUj&M=px-j&U)>2bu}O|(sZ?*57{su?E>VM+yOB_r1y?^GVx{Z0g>VQM(?8klXH18) zQM_x;Pvda25U=@ZpsR=TH;1NuWk;S6_px%d#$%kKIW(smveC-%Zf4_WoOQ2+@hXsX zV>w7RhMkHxnqR7DJklui*%lP(A6sxCw7Bb)tStJ<)rXDgvR-);g}CjJhyR=uuztCzS%Z!mbSXeN^tgf2b6%Pz zQRvpAD8fJb{i7I8j8FTDLb-#BJE6k%GZu9ZBxDWLc1ll+sFCb;k5%D1RcqIINS_L4 z)Nxgt(KoKEZ_MaV!8b^G_j7#zadirm)|1Cju;~nkiz~9|_>3r*1*>oI*G4X*SECL2 ziCNBXAC$7_5+`WU*8_=iwb!CPPhxG{W7U)PRWg#w_GNX|deEY8I6##8O=--^YVJYC zO-dE6k>dXg(e5|KR&KHlxr8CzlfXNhhRh3ce^M4rvb)DY!^So9aT<0Xc^i=pUdP!+ zZq4&MeFA=~y&>2Z@NsVdJam76mS<4T+f0Zt2uv?$u)Yi@>x1j^fhPhXxF$;i^iFAJ zs{d%>cQzAxrzSI|)q&-0a@~@4d{2o{o_v|RX7+9=zUiU)t4({oh@n}tBK$xHEV{@G z@-%3U7sT+X3@7(+7Z~PEFg9BQo6EsEn6Cg$`3q+k9Z+{&vuJ>=%Rn>6BlAl%qq{l~ z!K@zbz%2~9XFkN2K$m_&qhQBPE~bnl_3$HF^xaq@r6OyjxC&(?w_78R9{a$A<53OE zQmBx;KccGSwtS1ygS-^HDQ4A;Q0}pYTUv*UQ@8u~$Eer~N0%+&BcBwz=>-%hIPUL1 HU@`O`3UJ0z delta 1563 zcmZuxYiL|m5I*0ToVzz`)FtUA`H|8sm8P|6vRPtk5nCTrYtSNy*x=hfB!U|2rp7k7 z)!+{!l*mSpHHB2zq;9O)(2!nOK~R5~l;)w-Y)mSxC265pe1JuY66f68?2V9xvorI} zoOzs?xzn?O>Dj>Gw2i)@pX~G9g|S%Op52@3_U=l4or)lqyqLtfMZ{l?nb zjnP>0WN$a3$sK)L5gj_*R|6(r9iK4daC$3~(1@q(Di-1nRxZ|fkW(~=#`Nw&v~s+k3-K#Hc1{QJ zBJjQwD?+j<7_E7I->#abH=Be$)`D_?6RC`h_j1ZA>F~Vo2%Is;5fS~=3oTDWkkB39~{8RM0V0sl*tWTT)PU|Pg<1DB;;nO^^}_w5hK~1E~|nKYP;Rw zA}3TZua2wjd41Ee`lh_T1D-+FyBpyy-SL3x@gH_(_SrS=!{trexRKe z4e^3J4jSVHF?=@9$@{qr4D%)!n>D`8h`$czD?s0Q3ul+_R5#@;a@e{GG-^CDKS!gw zrwd`6(ZgN1gtT+?LwpNp{hUVWhIuZgiX$E6N3>|}R3fXwOQg68g3uIi!KlHQdo4vthy1ZUDj}O%W!cz;{5XoYPKP&t1jRpk5oA4FQ8N* K>Rg-0#lU|sW5vn< diff --git a/records/wasm/manifest.json b/records/wasm/manifest.json index cb43131..a7a42c4 100644 --- a/records/wasm/manifest.json +++ b/records/wasm/manifest.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.0 (built on Crystal 1.22.0-dev [224ed74fc] (2026-11-02))", - "commit": "224ed74fc7ba9d78345dfd10f21ece4b499702fa", + "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", + "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:wasm", - "when": "2026-11-02T07:18:22.704Z" + "when": "2026-11-04T04:32:07.924Z" }, "samples": [ { @@ -14,7 +14,7 @@ "sourceSha256": "676f8372f2cd5f8941b1dcd0ccb167ee6f6f27c1de4a77cef3af0372c323134d", "wasm": "hello.wasm", "bytes": 123087, - "sha256": "b2f99b569507b06c72ac45d9afb48ddf6342bd7fcb333a857392aa6318f5c65b", + "sha256": "38d89c17020feacd80582cc0d9b154fa22bd2568d5683b5e4a6294d45bd0ad19", "exitCode": 0, "nativeStdout": "Hello Türkiye!\n", "wasmStdout": "Hello Türkiye!\n", @@ -28,7 +28,7 @@ "sourceSha256": "c908a80d8c5a2c3f42bc6c90cd9d9a822a4c0b077cbd4713cc627f2d9f4912c0", "wasm": "modules.wasm", "bytes": 124842, - "sha256": "3148c581ed9ffba3999fec179835b1583eeb72ad033dd7ef3c5d7bd3fb110eb3", + "sha256": "421a51e54316bf4e162322ab988e25d4e8a18501fcabaf74b2e9a11f61e90e85", "exitCode": 0, "nativeStdout": "7\n", "wasmStdout": "7\n", @@ -42,7 +42,7 @@ "sourceSha256": "1d341c749781b957ea8e5729a226f84852f816c914224de63771b5a03bfb6153", "wasm": "exports.wasm", "bytes": 124838, - "sha256": "c81410a6f19ab5fd8abdb5888a2eebb027f205f501ac9e1acc1a0211dbc0e612", + "sha256": "165e1dc29c8306b68dc489f1be1c4e7cb49d94e2a0dfe37701a60b4ed42a9815", "exitCode": 0, "nativeStdout": "7\n", "wasmStdout": "7\n", @@ -56,7 +56,7 @@ "sourceSha256": "672aec63ec81d0a8dc9cc773688d853a6b9a246618705a4cfa782191efe3fea5", "wasm": "variables.wasm", "bytes": 156061, - "sha256": "e923e39c408a3c153216eff5469350c49d73ac7dd3e6074a739f4355d296e989", + "sha256": "b0a5a9e9e814b7ba37bd613d9c0a7e266a17ebe0a4754bdb4a06ad638dfe78af", "exitCode": 0, "nativeStdout": "iyi\n2026\n0.75\ntrue\n2027\n10\n", "wasmStdout": "iyi\n2026\n0.75\ntrue\n2027\n10\n", @@ -70,7 +70,7 @@ "sourceSha256": "bbd35199bdfae3aa05673d869e512e1713213fa1aeef929e6477fec7ff6e4464", "wasm": "types.wasm", "bytes": 158793, - "sha256": "02ce0e08a24e6218fc6820eff9835b0fefdf204fa54e330889fdc94c5185045a", + "sha256": "f8e6e6e6ffb943a0b51f6f847d1f74d0cd4cfa05d0b285bcb1eb9ee0c65b74c9", "exitCode": 0, "nativeStdout": "Int32\nInt64\nFloat64\nBool\nChar\nString\n3\n3.5\n1\n1024\n", "wasmStdout": "Int32\nInt64\nFloat64\nBool\nChar\nString\n3\n3.5\n1\n1024\n", @@ -84,7 +84,7 @@ "sourceSha256": "1b8cf5e80ec2dbe3ce9b207d12491e7b24761caf1eb2797a098f68f36e933b81", "wasm": "conversions.wasm", "bytes": 187416, - "sha256": "2c054e3c89a1a116d5885db0d3e6f8da9d20e82bba44d23469f58802c84580b8", + "sha256": "ce2964d3ac0e39d59624288868c4e11456da234312570b70a02dd816addca683", "exitCode": 0, "nativeStdout": "42.0\n42!\n9\n10.0\n18\n17\n\n65\nB\n", "wasmStdout": "42.0\n42!\n9\n10.0\n18\n17\n\n65\nB\n", @@ -98,7 +98,7 @@ "sourceSha256": "e5e15868dc555999be7b1c15e2349147be0e335a5b45792b893aa9c9d3e139ba", "wasm": "constants.wasm", "bytes": 160443, - "sha256": "899cc10df244def0c0b41d12d716abcc61c467b2351c736900923f81ec6784d9", + "sha256": "7a31ad7c728a18fb7ad4629d223eb6b3f1040b55ed1515d909e6058ee8e82435", "exitCode": 0, "nativeStdout": "12.57\nMerhaba, dünya\ntrue\n", "wasmStdout": "12.57\nMerhaba, dünya\ntrue\n", @@ -112,7 +112,7 @@ "sourceSha256": "1261f0c7c47c4f9469358362a6c52a84cd6450a6e4347e1d7ff9eba09a88f89a", "wasm": "functions.wasm", "bytes": 125184, - "sha256": "0d6e13b48be440c0b4158538681d8319bfd05bd8c9f843a6be119703e26b4470", + "sha256": "a6e3f66932f7ed2580d456041b5553d336b87a0472847092f7dce07d66f592ca", "exitCode": 0, "nativeStdout": "5\nnegative\nHello, Ayşe!\nSelam, Ayşe!\n", "wasmStdout": "5\nnegative\nHello, Ayşe!\nSelam, Ayşe!\n", @@ -126,7 +126,7 @@ "sourceSha256": "6b655fcd036d722550a028424fe4719cdfb89383fed7607e260c46fbf0fff932", "wasm": "returns.wasm", "bytes": 127402, - "sha256": "a562301ac6ac68f527583c80c033aa83dc650e65fb4c2d8eaa80ef74cc359226", + "sha256": "6f4b35be779b8d64e92c5d6b95fa959eccd30a9d9574f5d592e16e9cc2132dca", "exitCode": 0, "nativeStdout": "3\n2\n2\n1\n6\n\n", "wasmStdout": "3\n2\n2\n1\n6\n\n", @@ -140,7 +140,7 @@ "sourceSha256": "a559c045cfef442a9927954673d8ad5fc61f610a0908950bcdd79182853a4e04", "wasm": "nil.wasm", "bytes": 125476, - "sha256": "b910ce39a42b38d673f9888e725efd5c4fe263916392802f487c9a8702e54b6a", + "sha256": "68356136db87b27634e5c328e02d0386f3e2dd777ce9b463ec1428ab06c25a59", "exitCode": 0, "nativeStdout": "Ada\n\nADA\nnobody\n", "wasmStdout": "Ada\n\nADA\nnobody\n", @@ -154,7 +154,7 @@ "sourceSha256": "18f56f89ce4e1bcf3f1c2abff8e3539b5bde85135ec978931583dddd7882992c", "wasm": "loops.wasm", "bytes": 126424, - "sha256": "70b84276aadfc16114677bbf4beba2def27106595e68c4c125f60256869979bf", + "sha256": "e0a0758c488943d7bfe214a973aae9a620649dea031ad74262d68c92865f5173", "exitCode": 0, "nativeStdout": "1\n2\n3\nround 0\nround 1\nround 2\n64\ncount 1\ncount 3\ncount 4\n", "wasmStdout": "1\n2\n3\nround 0\nround 1\nround 2\n64\ncount 1\ncount 3\ncount 4\n", @@ -168,7 +168,7 @@ "sourceSha256": "418036dc21744938451d1210eeceaca1931d0edb5329f3f8e801f626fa234fd6", "wasm": "ranges.wasm", "bytes": 141760, - "sha256": "4765a3a9662c3d9d9bf6ffe04e86ec95481c9da2792ea4cf70e06bdd4886f750", + "sha256": "ce1431275be2f17eb1e7127f660eb3f9466b8639cb67eb14d772ec32da62f1b5", "exitCode": 0, "nativeStdout": "1\n2\n3\n4\n5\n1\n2\n3\n4\n[1, 4, 9, 16, 25]\n5050\n[3, 6, 9]\nadult\n", "wasmStdout": "1\n2\n3\n4\n5\n1\n2\n3\n4\n[1, 4, 9, 16, 25]\n5050\n[3, 6, 9]\nadult\n", @@ -182,7 +182,7 @@ "sourceSha256": "8ed0a951d7e760d2bb95c784b20c3e59771080e0578a2cadf0b1bdf41fd2eed2", "wasm": "conditions.wasm", "bytes": 124828, - "sha256": "0e3ae9ecb6b7748bf36c6736dd7fabe8f95e8f86c67870ca7d14b68695acb3fe", + "sha256": "0ff133e1140610da7c74ba371bc7385c1763c466778c10a92607d4a2c14e7807", "exitCode": 0, "nativeStdout": "warm\nabove freezing\nnice day\nwarm\nodd\ntrue\nfalse\n", "wasmStdout": "warm\nabove freezing\nnice day\nwarm\nodd\ntrue\nfalse\n", @@ -196,7 +196,7 @@ "sourceSha256": "71de25c31bbd4ad83f406525df3f263bbb18c8dcab00eee4dba6baa166409ffb", "wasm": "case.wasm", "bytes": 127806, - "sha256": "33112c07e0f4db76abd152f20e8e9d857b8b80397a64fe77035dc7749dbd0bf8", + "sha256": "eafde9ad418f89622e33a02edfb09460b720f0570fe05d83a5629a3de80b503d", "exitCode": 0, "nativeStdout": "weekend\nalmost\nweekday\nB\na number: 2\n", "wasmStdout": "weekend\nalmost\nweekday\nB\na number: 2\n", @@ -210,7 +210,7 @@ "sourceSha256": "c39edacef8695b8aa0170cd169373a19ece2ec4fcf204439ef48e22aadcf06f2", "wasm": "defer.wasm", "bytes": 128138, - "sha256": "edbb73210e8210678d0e93205a1f4cbb77e54b466b842a07289403329871a2a1", + "sha256": "8c66f8e24e1a3fb27ef3b6964f7d15278c7e867b0d84a9fc1817f6e00b1ac523", "exitCode": 0, "nativeStdout": "open\nworking\nclose\n42\nfirst\nsecond\nthird\nturn 0\ndone with 0\nturn 1\ndone with 1\n", "wasmStdout": "open\nworking\nclose\n42\nfirst\nsecond\nthird\nturn 0\ndone with 0\nturn 1\ndone with 1\n", @@ -224,7 +224,7 @@ "sourceSha256": "2354525f27753b7a4bdef384dc4c0c6507c12fe845e9d2927e7aec0f62c2814c", "wasm": "structs.wasm", "bytes": 125572, - "sha256": "ab6c2b2e09b57aeac3f2ac3aa4384b8c265054c056ea511f5672e479e7fe46f6", + "sha256": "653ee06a14e11ed4ba2853ac12e2cf95512e7fa1c534e25d8a37f9d73846c8c1", "exitCode": 0, "nativeStdout": "1\n(4, 6)\n(1, 2)\n", "wasmStdout": "1\n(4, 6)\n(1, 2)\n", @@ -238,7 +238,7 @@ "sourceSha256": "cfc6709102991d8da3357f3f6230408b0c79e8bf7199e63f8b40a272c65a2f6a", "wasm": "classes.wasm", "bytes": 124398, - "sha256": "f09ca5018601c91f292d49427f0712b88af0b418a0072d7b6ef67da83d613152", + "sha256": "51e10f413ca4fab84bbe5a3e08f03b0eb24d9b5dbc95a2f867f693f411a866f9", "exitCode": 0, "nativeStdout": "2\n0\n", "wasmStdout": "2\n0\n", @@ -252,7 +252,7 @@ "sourceSha256": "7f4841af80023b70c3e6f897ec7d5ba82001acd91a905e4772cc6f6f0dec0aa9", "wasm": "arrays.wasm", "bytes": 145881, - "sha256": "a936176105b1cf1b823a04753d93cb4cd86cb11a3dd7456c13830461b2a01f5e", + "sha256": "b6f59b198213a93a2aafcdd3c786e0a8d0d57c1ecd140163d3fc4ced243d2272", "exitCode": 0, "nativeStdout": "5\n3\n3\n5\n[3, 1, 4, 1, 5, 9]\ntrue\n23\n[1, 1, 3, 4, 5, 9]\n[3, 4, 5, 9]\n[30, 10, 40, 10, 50, 90]\n23\nAyşe, Mehmet\n1. Ayşe\n2. Mehmet\n", "wasmStdout": "5\n3\n3\n5\n[3, 1, 4, 1, 5, 9]\ntrue\n23\n[1, 1, 3, 4, 5, 9]\n[3, 4, 5, 9]\n[30, 10, 40, 10, 50, 90]\n23\nAyşe, Mehmet\n1. Ayşe\n2. Mehmet\n", @@ -266,7 +266,7 @@ "sourceSha256": "1b5fb76d25d7f5979248800422d871da36d4c2d0b829ec9c311c7b200beb167b", "wasm": "hashes.wasm", "bytes": 165179, - "sha256": "c57e6e01912536496dc25cf385feafc8fba8317663dc78b5f84e12b99cf3da27", + "sha256": "edf321c2fe8ad10a22a36bd2d1693c8e3a68023acd16c9c436877b596acb12ee", "exitCode": 0, "nativeStdout": "31\n2\n[\"Ayşe\", \"Mehmet\", \"Zeynep\"]\n\nfalse\nAyşe is 31\nMehmet is 27\nZeynep is 40\n{\"a\" => 2, \"b\" => 1}\n", "wasmStdout": "31\n2\n[\"Ayşe\", \"Mehmet\", \"Zeynep\"]\n\nfalse\nAyşe is 31\nMehmet is 27\nZeynep is 40\n{\"a\" => 2, \"b\" => 1}\n", @@ -280,7 +280,7 @@ "sourceSha256": "75f2414a08f54a9d8ecc53d7b0621d8c4906a3f39d9a673dd1b8d2f219b4856b", "wasm": "tuples.wasm", "bytes": 127759, - "sha256": "789f6da9fbc0ce1ce352cade9019cb9b17a85cf3d360dc0aca95cbeaf721eb19", + "sha256": "6056cbfa88e63e5d611ac94fa18b401da1f24eaffbe01f1a4ddb644d73cf43f3", "exitCode": 0, "nativeStdout": "1\none\n1 is one\nAyşe\n31\n2..9\n", "wasmStdout": "1\none\n1 is one\nAyşe\n31\n2..9\n", @@ -294,7 +294,7 @@ "sourceSha256": "f2ae3fc23344a9f94f896942b8609956979599387e313e98d49cb4977ba9dd17", "wasm": "blocks.wasm", "bytes": 127866, - "sha256": "b024efb9529caf394a9b41c630abb009f69a35dbc5d019ecbf8f51dc9df306d7", + "sha256": "e01d093cb97f30a1facc40fba0d44e3dd6898d0656431943b26f953fc7efa4fd", "exitCode": 0, "nativeStdout": "step 0\nstep 1\nstep 2\n6\n49\n2\n105\n", "wasmStdout": "step 0\nstep 1\nstep 2\n6\n49\n2\n105\n", @@ -308,7 +308,7 @@ "sourceSha256": "f4fb9b8be821d24f79148d110edb8f6bd36239bab164d7be45bc4d3da29eba7a", "wasm": "methods.wasm", "bytes": 161051, - "sha256": "900fd19677f0e65142dd426d78cfe686ca7c4f60767cc3d9854549ccded70a0e", + "sha256": "3d85a2d848d5011a19920b4762269a04aa7bac800a563275d46ff1264cb055bd", "exitCode": 0, "nativeStdout": "12.0\nfalse\n48.0\nIYI\n4.0\n6\n", "wasmStdout": "12.0\nfalse\n48.0\nIYI\n4.0\n6\n", @@ -322,7 +322,7 @@ "sourceSha256": "98aaeb457da3a65d50c3ce5402f3378e431010d50122396f9a353b4d958fc8a8", "wasm": "traits.wasm", "bytes": 160535, - "sha256": "d414044f7f0e6ff94bc7b5ade6b095c32c968333c5c7e49acd2c8ea710ccd92e", + "sha256": "1dccca26c8177bf515393a4db94b2f7fd31507fe822479a1d9770f0315ad9497", "exitCode": 0, "nativeStdout": "3.14\n4.0\n", "wasmStdout": "3.14\n4.0\n", @@ -336,7 +336,7 @@ "sourceSha256": "e9a5985790ff7c52c42846b394872bb18ac9040abfd59e9453d4ac71747fcfb9", "wasm": "dispatch.wasm", "bytes": 136069, - "sha256": "c619d9328cf1a563769a23b1fa9b75fdcd3e3432c5a78aebbd6590f77a587927", + "sha256": "00325eb2fcd4ec3a1af9143c6ff51df4bd1ac9c171a583b5d1b8ccb0ec9ff530", "exitCode": 0, "nativeStdout": "hav\nmiyav\nhav miyav\n", "wasmStdout": "hav\nmiyav\nhav miyav\n", @@ -350,7 +350,7 @@ "sourceSha256": "7e438eff3837000a1551861b7107a2a4f7f6f78988a283fec3174429a1f20844", "wasm": "defaults.wasm", "bytes": 127100, - "sha256": "c874918891eb024609cbfe95b478018e14ae1f53491ab22d3675e8bf8cfbf01e", + "sha256": "11fd75705a649460a5e68bcc738382a79b12cb33783069f7748d6aca563d3513", "exitCode": 0, "nativeStdout": "Merhaba, Ada!\nMERHABA, ADA!\nBEEP unit 7\nBEEP UNIT 7\n", "wasmStdout": "Merhaba, Ada!\nMERHABA, ADA!\nBEEP unit 7\nBEEP UNIT 7\n", @@ -364,7 +364,7 @@ "sourceSha256": "61cb534c8687c14b7392657f859b165eb063dc892ae64429ccc59dd241441466", "wasm": "unions.wasm", "bytes": 135190, - "sha256": "9b658342abd216814b620939a4d3506adf316add17b205b0d7725ed2c3e5b7e4", + "sha256": "1b1d08ebe15d3c2adec4e4efd232cb05f7fa75c3c9cdfd3b6b9362ff973e4627", "exitCode": 0, "nativeStdout": "number 24\nproblem, not a number: x\nnumber 14\n6\n", "wasmStdout": "number 24\nproblem, not a number: x\nnumber 14\n6\n", @@ -378,7 +378,7 @@ "sourceSha256": "af4c28f720136cd6c8b935273fa769fe234b55716da4abed70196e56d8b9faf7", "wasm": "errors.wasm", "bytes": 160371, - "sha256": "bf4685973716b806c46817d9091b6ec0861e06f247781223e6991ded1e41c979", + "sha256": "28f5753b1b2208e441777f7725c79c5edabcf1e2fca60a5bee643785aa926dd8", "exitCode": 0, "nativeStdout": "2027\nno such key: missing\ndefault\niyi\n", "wasmStdout": "2027\nno such key: missing\ndefault\niyi\n", @@ -392,7 +392,7 @@ "sourceSha256": "704f192ecd578a9847d911563129b559b5b04cc276327f8de24fd1502565f542", "wasm": "strings.wasm", "bytes": 160621, - "sha256": "6af479f7f4dd2febc2a18f5aed837b172b468b80f22e70f01b29d736bf5c54bc", + "sha256": "c48fff3f2df3aa6fd46e301aa0944db7dd8b51b5255cfccfcb08cc63125e875d", "exitCode": 0, "nativeStdout": "10\nHELLO, IYI\nhello, iyi\ntrue\nHello, iyi!\n[\"Hello,\", \"iyi\"]\n2 and true and [1, 2]\n12,05 TL\ntotal: 12,05 TL\nitem 12\ntotal 99\n", "wasmStdout": "10\nHELLO, IYI\nhello, iyi\ntrue\nHello, iyi!\n[\"Hello,\", \"iyi\"]\n2 and true and [1, 2]\n12,05 TL\ntotal: 12,05 TL\nitem 12\ntotal 99\n", @@ -406,7 +406,7 @@ "sourceSha256": "2b0ace59365d9bb35c904102d1131a95f5787113ba585975a819c6a354666e7f", "wasm": "generic-functions.wasm", "bytes": 151699, - "sha256": "46473ac6701e53a583ce021c8b0490161d086e01334c3b26f3088e92c98655be", + "sha256": "858cbbd01e1e6f1a5b490b2f4b83c20cbc02649b8c5618eda024760c5f846ca2", "exitCode": 0, "nativeStdout": "10\na\n{\"one\", 1}\n101\n", "wasmStdout": "10\na\n{\"one\", 1}\n101\n", @@ -420,7 +420,7 @@ "sourceSha256": "fe5f0b5626ef062f7af44196125d3b50d1914788fb1416d980d10e22f872aa8f", "wasm": "generic-types.wasm", "bytes": 135597, - "sha256": "5c3cfeed954628eb61366e149ad8b96558faaa70feb6b7ce63588796936c9bcc", + "sha256": "0a11fc1ef4fbbd9c159cee034d0086ee874a84e6a4f19e83ed62e1c95c185f9b", "exitCode": 0, "nativeStdout": "2\n2\n1\n\niyi\na stack of 1\na stack of 0\n", "wasmStdout": "2\n2\n1\n\niyi\na stack of 1\na stack of 0\n", @@ -434,7 +434,7 @@ "sourceSha256": "cd5324bcc2756eef8d4ebeca406c61ecf12cfd5898b26a01ec8fa4fd1ef64d26", "wasm": "bounds.wasm", "bytes": 129541, - "sha256": "fdc11a3ae994b9bd087d03c63a8b98c391ff7a326cc8124b5d627a553d9211fa", + "sha256": "073f84a1a4f1aa838ad40a6df38051c8f30fce51553a3f31a977d2d976457853", "exitCode": 0, "nativeStdout": "200\n45\n30\n", "wasmStdout": "200\n45\n30\n", @@ -448,7 +448,7 @@ "sourceSha256": "2fab386bd172c15319db0a4e553008d7ae7114fd8cdef72e7a48a850c301c72c", "wasm": "iyi-basics.wasm", "bytes": 213443, - "sha256": "1ceed0e4827fa6b8c8445d7c20558d0b0c304ceb052ecfc4dd3ce48ba6e3e625", + "sha256": "024d922379b0d19585337915d3847c2d8ab5b689f63a090f458cc09e8e23ff16", "exitCode": 0, "nativeStdout": "55\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n3\n4,5,9,6\ntrue\n1,1,2,3,4,5,6,9\n3,1,4,1,5,9,2,6\n1,1,2,3,4,5,6,9\n31\n31\nhello, world\ntrue\n12\nKhoor, Zruog\n3\n3\n{\"a\" => 3, \"b\" => 1, \"c\" => 1}\n14\nthe 5\ncat 2\nsat 2\n[\"cat\", \"end\"]\n", "wasmStdout": "55\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n3\n4,5,9,6\ntrue\n1,1,2,3,4,5,6,9\n3,1,4,1,5,9,2,6\n1,1,2,3,4,5,6,9\n31\n31\nhello, world\ntrue\n12\nKhoor, Zruog\n3\n3\n{\"a\" => 3, \"b\" => 1, \"c\" => 1}\n14\nthe 5\ncat 2\nsat 2\n[\"cat\", \"end\"]\n", @@ -459,10 +459,10 @@ "id": "iyi/calc", "set": "iyi", "path": "samples/iyi/calc.iyi", - "sourceSha256": "7310f0e547c6ec9632bcf318538cd8778d8c32409822dafd3b98c43fcbf4e614", + "sourceSha256": "9844743a83b1d7bd80eceef04339216ef04e8a446c800f9c50da265442f8dc8e", "wasm": "iyi-calc.wasm", "bytes": 200618, - "sha256": "aa2cfc22916be0e807cad21a8d95de21cb0c097b45add05dba340ceafc629503", + "sha256": "9eba1a8c39a31abf400ba374a183b7cb44cf4f502409883caba1d175943e99b0", "exitCode": 0, "nativeStdout": "64\n", "wasmStdout": "64\n", @@ -476,7 +476,7 @@ "sourceSha256": "9b8bb4c0bf703238a08afe44995207bb1cb6292578a7d255b5f3ece0c2a80213", "wasm": "iyi-collections.wasm", "bytes": 228588, - "sha256": "36964f3cc2d2ea02d38a0564a8ef13256bfc6ca1af838aa79dbb2b937b95d6e9", + "sha256": "c27bcfda04e61f7b14dee7b8f846e2a323865e72df156a646f1b400819af1f27", "exitCode": 0, "nativeStdout": "to_a 3,1,4,1,5\nsize 5\nempty? false true\nall? true\nany? true\nnone? true\none? true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect 3,4,5\nreject 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_index 0,1,2,3,4\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax?/max 5 5\nmin?/min 1 1\nsum/product 14 60\nsum of nothing 0\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\nindex_by fig\neach_slice 3+1 4+1 5\neach_cons 3+1 1+4 4+1 1+5\nchunk_while 3 1+4 1+5\nfirst(n) 3,1,4\ncompact_map 30,40,50\nfind_value big\naccumulate 0,3,4,8,9,14\nreduce? 14\nin_groups_of 3+1 4+1 5+0\nmin_by fig\nmax_of/min_of 4 3\nminmax 1,5\ntally_by 3\nsum_by 15\nzip 3pear 1fig 4plum 1kiwi\ncycle 1,2,1,2\nas a type 5 items / 4 items\n", "wasmStdout": "to_a 3,1,4,1,5\nsize 5\nempty? false true\nall? true\nany? true\nnone? true\none? true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect 3,4,5\nreject 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_index 0,1,2,3,4\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax?/max 5 5\nmin?/min 1 1\nsum/product 14 60\nsum of nothing 0\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\nindex_by fig\neach_slice 3+1 4+1 5\neach_cons 3+1 1+4 4+1 1+5\nchunk_while 3 1+4 1+5\nfirst(n) 3,1,4\ncompact_map 30,40,50\nfind_value big\naccumulate 0,3,4,8,9,14\nreduce? 14\nin_groups_of 3+1 4+1 5+0\nmin_by fig\nmax_of/min_of 4 3\nminmax 1,5\ntally_by 3\nsum_by 15\nzip 3pear 1fig 4plum 1kiwi\ncycle 1,2,1,2\nas a type 5 items / 4 items\n", @@ -490,7 +490,7 @@ "sourceSha256": "e1c60e92bb4150097bed4d7986dc678380776ef09fc95ff03b278746ff121c10", "wasm": "iyi-config.wasm", "bytes": 177376, - "sha256": "05857dcac41717b838f6759d649649fd76b56b6ca844daba07e794814b1b39c7", + "sha256": "a64c12afa8154579a053691b75b5aab9c38be7423a69ebf6e72669b29edb56e3", "exitCode": 0, "nativeStdout": "{\"name\" => \"iyi\", \"port\" => \"8080\", \"mode\" => \"fast\"}\n8081\nname, port, mode\nerror: line 2: no '=' in \"broken line\"\n[\"a\", \"\", \"b\"]\n[\"one\", \"two\"]\n\"padded\"\ntrue\n", "wasmStdout": "{\"name\" => \"iyi\", \"port\" => \"8080\", \"mode\" => \"fast\"}\n8081\nname, port, mode\nerror: line 2: no '=' in \"broken line\"\n[\"a\", \"\", \"b\"]\n[\"one\", \"two\"]\n\"padded\"\ntrue\n", @@ -504,7 +504,7 @@ "sourceSha256": "ab33030ac91db9cddf89e68f39e93fe96296f028f313e6e3c25afa76851aa32a", "wasm": "iyi-derive.wasm", "bytes": 128268, - "sha256": "158451a926a6df41b90c175e6f8a6864386a9f26001a5fab8d1e88ee404dfedd", + "sha256": "b5e0d05aaaa29c0218472ee8d118c58325694f2f9fb1d62c44bd54a106e180bf", "exitCode": 0, "nativeStdout": "Point(@x=3 @y=4 )\nSpan(@from=1 @to=9 @label=range )\nTag(@label=Std::Derives::Name @count=2 )\n@label= \n", "wasmStdout": "Point(@x=3 @y=4 )\nSpan(@from=1 @to=9 @label=range )\nTag(@label=Std::Derives::Name @count=2 )\n@label= \n", @@ -518,7 +518,7 @@ "sourceSha256": "98b275c766e680a4adc21a3fc80c18b94e954317dd4668a4b303fcbeea8c0c5d", "wasm": "iyi-enums.wasm", "bytes": 151146, - "sha256": "61a95872438a7e5b64b38c0d2d03bec8dadea2d309c927af0ea5836a6b533418", + "sha256": "0c4a1549d7642c41f006cb74806180daf689263e36015a665e3060c1406bcb0f", "exitCode": 0, "nativeStdout": "level: Warn\nspelled: Error\nsame: true\nother: false\nverdict: loud\nwarn? true\nerror? false\nat least info: true\nbelow error: true\nDebug Info Warn Error \nnames: 4\nparsed: Error\nlenient: Warn\nunknown: nil\nvalue: 3\nfrom 1: Info\nfrom 9: nil\nmode: Read | Write\nread: true\nappend: false\nnone: None\nmasked: Write\nwarns: 2\nerrors: 1\n", "wasmStdout": "level: Warn\nspelled: Error\nsame: true\nother: false\nverdict: loud\nwarn? true\nerror? false\nat least info: true\nbelow error: true\nDebug Info Warn Error \nnames: 4\nparsed: Error\nlenient: Warn\nunknown: nil\nvalue: 3\nfrom 1: Info\nfrom 9: nil\nmode: Read | Write\nread: true\nappend: false\nnone: None\nmasked: Write\nwarns: 2\nerrors: 1\n", @@ -532,7 +532,7 @@ "sourceSha256": "89e937ddfc973d79b5f00c8ea3b81315a68b5c2c6cf4d1bac4172a3da1b79bd0", "wasm": "iyi-errors.wasm", "bytes": 138685, - "sha256": "568f8fd704e78223ce04ae518ea433b633c34de8620851cc9bd85012d8c8a353", + "sha256": "edf9cb5dd070d1099e51ea5297f54cb1260135ed6874c553029c63593777412d", "exitCode": 0, "nativeStdout": "read 12\nio: no such file: missing\nparse: not a number: twelve\nor: 12\nor: 0\nor: 0\nor: -1\npanic: 12\nsuccess:\n open outer\n open inner\n close inner\n close outer\n12\npropagating:\n open outer\n close outer\n-1\n iteration 0\n iteration 0 closed\n iteration 1\n iteration 1 closed\n", "wasmStdout": "read 12\nio: no such file: missing\nparse: not a number: twelve\nor: 12\nor: 0\nor: 0\nor: -1\npanic: 12\nsuccess:\n open outer\n open inner\n close inner\n close outer\n12\npropagating:\n open outer\n close outer\n-1\n iteration 0\n iteration 0 closed\n iteration 1\n iteration 1 closed\n", @@ -546,7 +546,7 @@ "sourceSha256": "aaa45bbe135b26d2c882122da7a4fe69df4bcba517a6f8ed57ba0101eda8ba69", "wasm": "iyi-files.wasm", "bytes": 125156, - "sha256": "2b85382b6a4182d4bade780067738aca23596bea2e6d9a1646a3f66e0158461a", + "sha256": "938f2c61da810eafcf644405d304bfbb860920f9929fe22c1fa8ca2a6a0655bf", "exitCode": 1, "nativeStdout": "hello from iyi\ntrue\nfalse\nfalse\n", "wasmStdout": "", @@ -560,7 +560,7 @@ "sourceSha256": "54f6992a74d0bff957fed60b05e8bd9d7bb16b091ea1b79e8277acd7454128bb", "wasm": "iyi-format.wasm", "bytes": 308041, - "sha256": "147d0c42ecc035df2e8e50b0fd0452eb7a81478c36091d89bbc1da13361607ec", + "sha256": "d9342a14e3491780072b1038e479579ef3724e056e12fe7f56fccf1eb9080622", "exitCode": 0, "nativeStdout": "hello world\nchar 'A' (ascii 65)\nright ' hi'\nleft 'hi '\ndec: 255\nhex: ff\nHEX: 0XFF\noct: 0o377\nbin: 0b101\nplus: +42\nzero: 000042\nsign zero: +00042\nnegative zero: -00042\nint precision: 0007\nfloat precision: 3.14\nfloat precision: 3.1416\nstring precision: cin\nwider than field: 12345\nzero precision float: 4\nzero precision zero int: ' '\nmin int64: -9223372036854775808\nmax int64: 9223372036854775807\nString.format: answer = 42\npercent tuple: score = 0099\nprintf: run completed\n", "wasmStdout": "hello world\nchar 'A' (ascii 65)\nright ' hi'\nleft 'hi '\ndec: 255\nhex: ff\nHEX: 0XFF\noct: 0o377\nbin: 0b101\nplus: +42\nzero: 000042\nsign zero: +00042\nnegative zero: -00042\nint precision: 0007\nfloat precision: 3.14\nfloat precision: 3.1416\nstring precision: cin\nwider than field: 12345\nzero precision float: 4\nzero precision zero int: ' '\nmin int64: -9223372036854775808\nmax int64: 9223372036854775807\nString.format: answer = 42\npercent tuple: score = 0099\nprintf: run completed\n", @@ -574,7 +574,7 @@ "sourceSha256": "ad90bbbd17da587770609673949d55c990aee356dda1d437ce23394231ec75ee", "wasm": "iyi-formatting.wasm", "bytes": 143143, - "sha256": "bd023ab26162567aac8c0e688897d08812255bfd9e71d50e4b16a658389972e3", + "sha256": "455c2338487d1440594743329e00b74f1ea682bf18e35aac8a526ad4144e9bfd", "exitCode": 0, "nativeStdout": "last 30\nfirst 10\nwritten 99\nchar o\nslice lo\nhex ff\nbin 101\nrjust ' ab'\nljust 'ab***'\nrep ababab\nnil nil\nmaybe 99\n", "wasmStdout": "last 30\nfirst 10\nwritten 99\nchar o\nslice lo\nhex ff\nbin 101\nrjust ' ab'\nljust 'ab***'\nrep ababab\nnil nil\nmaybe 99\n", @@ -588,7 +588,7 @@ "sourceSha256": "b3ec6ebe033571c8bedecdb4d00a5df97eeca44b709d94352066c8f215f4bbe1", "wasm": "iyi-generics.wasm", "bytes": 143824, - "sha256": "f50fbc70667358f6bd3117167fd7f901fe0f116b2b6fc21a9defdab12043ff01", + "sha256": "6a8727c576c6679e07701cfd869abd86fee9b5c054dfd59b1a5f054b472d1923", "exitCode": 0, "nativeStdout": "Box(42)\nBox(hello)\nfirst=1, second=two\nBox(1)\nBox(two)\nfirst=3, second=four\nada, ada\n84\ntrue\n7\n", "wasmStdout": "Box(42)\nBox(hello)\nfirst=1, second=two\nBox(1)\nBox(two)\nfirst=3, second=four\nada, ada\n84\ntrue\n7\n", @@ -602,7 +602,7 @@ "sourceSha256": "2f28278d0111d0ecc5c45afa5c2de9841abb5ad5f0041417dc97caa4fbc5338f", "wasm": "iyi-grid.wasm", "bytes": 187747, - "sha256": "ad676e5b372c5034175d09f58220bc40833acb96676b153cb95b3cc60fd7737f", + "sha256": "fc5548ba269a5eb3ea6ad618af6fcf49f263afe863eb1225e63e50b71f66cf69", "exitCode": 0, "nativeStdout": " 0 1 2 3\n 4 5 6 7\n 8 9 10 11\n 12 13 14 15\n120\n[0, 5, 10, 15]\n[0, 4, 8, 12]\n[5, 4, 3, 2, 1]\n[2, 4, 6, 8, 10]\n55\n[3, 1, 2]\n[{1, \"a\"}, {2, \"b\"}, {3, \"c\"}]\n2\na\n[\"b\", \"c\"]\n01234\n", "wasmStdout": " 0 1 2 3\n 4 5 6 7\n 8 9 10 11\n 12 13 14 15\n120\n[0, 5, 10, 15]\n[0, 4, 8, 12]\n[5, 4, 3, 2, 1]\n[2, 4, 6, 8, 10]\n55\n[3, 1, 2]\n[{1, \"a\"}, {2, \"b\"}, {3, \"c\"}]\n2\na\n[\"b\", \"c\"]\n01234\n", @@ -616,7 +616,7 @@ "sourceSha256": "27cdcc173bc626bf84c26f468628026979b15bc6127e67594c5adf2930483a83", "wasm": "iyi-hello.wasm", "bytes": 130696, - "sha256": "20965268d4cb996ccc46053610de0fc1ff5c2b0a8e616f234d50dac815137557", + "sha256": "bfed0df44969235bb0212ac02af2c40bd3e58ecb9588c82fcd8ee03b0d9e5bf1", "exitCode": 0, "nativeStdout": "Hello, iyi!\nHELLO, IYI!\nBEEP 42\nHello, crystal!\nBEEP 7\n-> BEEP 9\n", "wasmStdout": "Hello, iyi!\nHELLO, IYI!\nBEEP 42\nHello, crystal!\nBEEP 7\n-> BEEP 9\n", @@ -630,7 +630,7 @@ "sourceSha256": "ffdb52f45c0426a876e109c9e26862d43d4bbc7bca547612e8b31dc5a10e7b9a", "wasm": "iyi-immutable.wasm", "bytes": 197267, - "sha256": "6b58efb5ee61baef3e766191b9e1705696c9f6d278b37546bf0d06df928af0f5", + "sha256": "11d4ebd1d2dcce4bad066884d950579d96256ecdb018ed985c61ceab8258e7d6", "exitCode": 0, "nativeStdout": "size 5\nempty? false true\nat 3 5\nappended 6 from 5, original 3,1,4,1,5\nconcatenated 3,1,4,1,5,7 / 5\ncopied list 2, source 3\nto_a 3,1,4,1,5\nall?/any? true true\nnone?/one? true true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect/reject 3,4,5 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax/min 5 1\nsum/product 14 60\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\ntwo elem types 4,2,5,2,6 / 4,3,4,4\n", "wasmStdout": "size 5\nempty? false true\nat 3 5\nappended 6 from 5, original 3,1,4,1,5\nconcatenated 3,1,4,1,5,7 / 5\ncopied list 2, source 3\nto_a 3,1,4,1,5\nall?/any? true true\nnone?/one? true true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect/reject 3,4,5 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax/min 5 1\nsum/product 14 60\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\ntwo elem types 4,2,5,2,6 / 4,3,4,4\n", @@ -644,7 +644,7 @@ "sourceSha256": "0eee830465aeffb6ce0cabe6230e1c8dd87b37458bb42f9ba9b567721dcdc340", "wasm": "iyi-init_order.wasm", "bytes": 124809, - "sha256": "c71d26e6e1579dd29cd5b44976cf9a6015ab83e04dd164075fa1fbc294707842", + "sha256": "b0f607519ec0dd50324fcb841e32ede39fe8f203908d6476de67ee438bf76416", "exitCode": 0, "nativeStdout": "1. boot/config\n2. boot/registry, above its own `import` line\n3. boot/registry, below it\n4. samples/init_order, above its own `import` lines\n5. Hello, iyi.\n", "wasmStdout": "1. boot/config\n2. boot/registry, above its own `import` line\n3. boot/registry, below it\n4. samples/init_order, above its own `import` lines\n5. Hello, iyi.\n", @@ -658,7 +658,7 @@ "sourceSha256": "8d17a1ab3590cbe103b0eedb8487c9df8cc2837e0b84b26a3c8abbe353af80d6", "wasm": "iyi-inventory.wasm", "bytes": 218027, - "sha256": "539236d4dd652a76671806850aec414c540083d9f1b21d90037981853f0501c1", + "sha256": "5b3186682657003e9a8ab9fda0c3d04b4b6e563f1a35e16d0576681edd83a52e", "exitCode": 0, "nativeStdout": "total: 11.0\ncheap: apple, pear\n2\ntrue\ntrue\nmelon\npear\n3.0\nnil\n1. apple 10 5.0\n2. pear 4 3.0\n3. melon 1 3.0\n[\"apple\", \"pear\", \"melon\"]\n15\npear\n", "wasmStdout": "total: 11.0\ncheap: apple, pear\n2\ntrue\ntrue\nmelon\npear\n3.0\nnil\n1. apple 10 5.0\n2. pear 4 3.0\n3. melon 1 3.0\n[\"apple\", \"pear\", \"melon\"]\n15\npear\n", @@ -672,7 +672,7 @@ "sourceSha256": "8878852be923dc681f060baf0ae863e04a30e87f123498813c37738c2c97d41f", "wasm": "iyi-io.wasm", "bytes": 131609, - "sha256": "5836629d0d408e7d5e0763d25608c69c95fd72fa14499f8bd2f9246ffbc2a75b", + "sha256": "6b01baf6530675f1c0de0ffa3b59514e636ae74e6612b50d93ec7b82d1755cbd", "exitCode": 0, "nativeStdout": "no lines read from standard input\n", "wasmStdout": "no lines read from standard input\n", @@ -686,7 +686,7 @@ "sourceSha256": "f4ea9f6e29e18cf9452eccc75ba8324c25d108f825b64765f44335c22c57e0be", "wasm": "iyi-modules.wasm", "bytes": 126751, - "sha256": "3f62b81809aab9324b80610bef0d02d4deeeb9aeea7b33b66679e94d7b952d37", + "sha256": "aefa7c4a462776d1d49bb86f7b8d17a2c6eee4855a055e064a3f17aebade4bf6", "exitCode": 0, "nativeStdout": "HELLO, IYI. Good day, iyi.\nfriend\nesteemed colleague\n", "wasmStdout": "HELLO, IYI. Good day, iyi.\nfriend\nesteemed colleague\n", @@ -700,7 +700,7 @@ "sourceSha256": "d33fc3528bffdd710b785425e5b0559e606c53184c2234774d56fc7d9f2f55c9", "wasm": "iyi-sessions.wasm", "bytes": 139540, - "sha256": "a727d57d37d0fef94b9250d39c4d9d417292225cc27704badf6fb3f74ccf49f6", + "sha256": "4485718365ed0640af3e3bddbbc19174f9524a49bff26f6619a3cfb07ca326c9", "exitCode": 0, "nativeStdout": "three arrivals:\n ada since tick 1\n linus since tick 2\n grace since tick 3\n (3 signed in)\nlinus left, had been in since tick 2\n ada since tick 1\n grace since tick 3\n (2 signed in)\nnobody named bob: true\nlinus is back:\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n", "wasmStdout": "three arrivals:\n ada since tick 1\n linus since tick 2\n grace since tick 3\n (3 signed in)\nlinus left, had been in since tick 2\n ada since tick 1\n grace since tick 3\n (2 signed in)\nnobody named bob: true\nlinus is back:\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n", @@ -714,7 +714,7 @@ "sourceSha256": "e3e4ca261b7f764ce2a168163c6da93909efc63d663fb1828f7b6d5a79b9f805", "wasm": "iyi-shapes.wasm", "bytes": 197147, - "sha256": "83afd9d2684181460bb4598b5fadd232bb9dfcdd995ea80fecc996544a847725", + "sha256": "1cd492ea85bbe9e4a9564b00c2737250ab7bea9fd339735e1484882e3d6ace71", "exitCode": 0, "nativeStdout": "circle: 3.14\nrect: 6.0\n9.1\nrect\n[12, nil, 7, -3]\n16\n[12, 7, 3]\n1024\n0.25\n1099511627776\n128\n8\n", "wasmStdout": "circle: 3.14\nrect: 6.0\n9.1\nrect\n[12, nil, 7, -3]\n16\n[12, 7, 3]\n1024\n0.25\n1099511627776\n128\n8\n", @@ -728,7 +728,7 @@ "sourceSha256": "afb4e7913d7b5c01a6e3ba5f42b3945e6ede3a374552325d07fa51ac25fa43e3", "wasm": "iyi-socket.wasm", "bytes": 413767, - "sha256": "f73737f8d2b26b4f39b4eedd2958fb3b16648bd979191fc517b8075b7777d26d", + "sha256": "6f04c78fd4d8e50b90c8d359d7421fc35a208207b664e2d20726e2278935dfd1", "exitCode": 1, "nativeStdout": "server listening on an ephemeral port\nserver received: hello from client\nclient received: hello from server\nclosed\n", "wasmStdout": "", @@ -742,7 +742,7 @@ "sourceSha256": "fdfedfc80d758e3490725c1fc6ebaa5648f5066854a28ccf1eb8cdb90b872fc8", "wasm": "iyi-std_iterator.wasm", "bytes": 208688, - "sha256": "92e59ce82a781f1fab12b70d2c721393e9aa4efaebf613fd9aa5a713722799c8", + "sha256": "4b03bfefec927743879d96208e917c1b76c74ed48789a16f29bb33c3bdbcb049", "exitCode": 0, "nativeStdout": "natural numbers first 5: [1, 2, 3, 4, 5]\npulls from generator: 5\neven squares first 4: [4, 16, 36, 64]\npulls for 4 matches: 8\nchained odds and evens: [1, 3, 5, 2, 4, 6]\nzipped pairs: [{\"apple\", 100}, {\"banana\", 95}, {\"cherry\", 88}]\neach_slice(3): [[1, 2, 3], [4, 5, 6], [7]]\neach_cons(3): [[10, 20, 30], [20, 30, 40], [30, 40, 50]]\nstepped by 2: [0, 20, 40, 60]\ncycled 2 times: [\"tick\", \"tock\", \"tick\", \"tock\"]\nwith_index(1): [{\"alpha\", 1}, {\"beta\", 2}, {\"gamma\", 3}]\nreduce sum: 100\nfirst > 10: 12\n", "wasmStdout": "natural numbers first 5: [1, 2, 3, 4, 5]\npulls from generator: 5\neven squares first 4: [4, 16, 36, 64]\npulls for 4 matches: 8\nchained odds and evens: [1, 3, 5, 2, 4, 6]\nzipped pairs: [{\"apple\", 100}, {\"banana\", 95}, {\"cherry\", 88}]\neach_slice(3): [[1, 2, 3], [4, 5, 6], [7]]\neach_cons(3): [[10, 20, 30], [20, 30, 40], [30, 40, 50]]\nstepped by 2: [0, 20, 40, 60]\ncycled 2 times: [\"tick\", \"tock\", \"tick\", \"tock\"]\nwith_index(1): [{\"alpha\", 1}, {\"beta\", 2}, {\"gamma\", 3}]\nreduce sum: 100\nfirst > 10: 12\n", @@ -756,7 +756,7 @@ "sourceSha256": "c891f86fe35a876e7f8279c549a0d16a39f38715593fae69a4ca387cb6468276", "wasm": "iyi-std_text.wasm", "bytes": 195805, - "sha256": "04241460a6d66a0766649a2f142d813f82856387e40cf1ae9035bf3972dd9c4d", + "sha256": "58b65f1dc5af2b855f2cbafa2d65b28915a0664a28b44b69109f783e4ff3858d", "exitCode": 0, "nativeStdout": "cleaned line: 'GET /api/v1/users HTTP/1.1'\nHTTP method: GET, path: /api/v1/users, version: HTTP/1.1\nHeader 'Authorization' = 'Bearer token123'\nslug: hello-world-this-is-iyi-std-text-\ngreeting size: 11 characters, 12 bytes\nreversed greeting: dlrow olléh\nhex 'ff' to decimal: 255\n", "wasmStdout": "cleaned line: 'GET /api/v1/users HTTP/1.1'\nHTTP method: GET, path: /api/v1/users, version: HTTP/1.1\nHeader 'Authorization' = 'Bearer token123'\nslug: hello-world-this-is-iyi-std-text-\ngreeting size: 11 characters, 12 bytes\nreversed greeting: dlrow olléh\nhex 'ff' to decimal: 255\n", @@ -770,7 +770,7 @@ "sourceSha256": "82a17d942f25fe20e124340ce9245293d280594fc4e07ee5a4e82dee62dd3620", "wasm": "iyi-std_time.wasm", "bytes": 275376, - "sha256": "0a094095f5727d4557ada435e75dd3bf9cc041500621f24f85ad21b678d4986f", + "sha256": "d494d5133d6a7daee036be9fe3610dc070da42ca509794c468f848959b6dcf7e", "exitCode": 0, "nativeStdout": "Reference time (UTC): 2024-02-29 12:30:45 UTC\nRFC 3339: 2024-02-29T12:30:45.123Z\nUnix timestamp: 1709209845 s (1709209845123 ms)\nYear: 2024\nMonth: 2\nDay: 29\nDay of week: Thursday\nDay of year: 60\nTomorrow: 2024-03-01T12:30:45Z\nYesterday: 2024-02-28T12:30:45Z\nLive wall clock valid: true\nMonotonic duration >= 0: true\nLeap day event: 2024-02-29 12:00:00 UTC\nIs leap year: true\n", "wasmStdout": "Reference time (UTC): 2024-02-29 12:30:45 UTC\nRFC 3339: 2024-02-29T12:30:45.123Z\nUnix timestamp: 1709209845 s (1709209845123 ms)\nYear: 2024\nMonth: 2\nDay: 29\nDay of week: Thursday\nDay of year: 60\nTomorrow: 2024-03-01T12:30:45Z\nYesterday: 2024-02-28T12:30:45Z\nLive wall clock valid: true\nMonotonic duration >= 0: true\nLeap day event: 2024-02-29 12:00:00 UTC\nIs leap year: true\n", @@ -784,7 +784,7 @@ "sourceSha256": "987608a702df760c9cb5f3c0c85a5f621553fda885504892acb2fbb67d57d744", "wasm": "iyi-visited.wasm", "bytes": 176116, - "sha256": "096c6ee3b3198ec711291e770e4eb6c44dcdf808b9b095c067e6d58a6e1145ba", + "sha256": "ed4b948df48e36d0f8dd7b4d73e31e9b937ee1aaae31a282027a047b49336e35", "exitCode": 0, "nativeStdout": "Set{\"home\", \"docs\", \"blog\", \"install\", \"about\"}\n5\nfalse\nSet{\"install\"}\nSet{\"orphan\"}\ntrue\nfalse\ntrue\ntrue\n[\"the\", \"cat\", \"and\", \"dog\", \"bird\"]\n", "wasmStdout": "Set{\"home\", \"docs\", \"blog\", \"install\", \"about\"}\n5\nfalse\nSet{\"install\"}\nSet{\"orphan\"}\ntrue\nfalse\ntrue\ntrue\n[\"the\", \"cat\", \"and\", \"dog\", \"bird\"]\n", @@ -798,7 +798,7 @@ "sourceSha256": "5bba5cdefa6407b2fe87e8c84bfdcf195bf02177193fbaea3e646c143018f296", "wasm": "iyi-webapp.wasm", "bytes": 171353, - "sha256": "f02d415451f16bed548312611374d917535ea7414bb670ce250c3d75f8827592", + "sha256": "c8989407961055e536ceb461549618dcd6fa29740e2635bfeb43c956bc08ddc2", "exitCode": 0, "nativeStdout": "GET /\nGET /users\nPOST /users\nGET /count\nGET /v1\nGET /v1/admin/dashboard\n1|42|0\n", "wasmStdout": "GET /\nGET /users\nPOST /users\nGET /count\nGET /v1\nGET /v1/admin/dashboard\n1|42|0\n", diff --git a/records/wasm/methods.wasm b/records/wasm/methods.wasm index f16fbf4d9566cad5957472254f439df9ed49ea6e..29a1df818dbba8af9ba4247c36e6766c3f4d7610 100755 GIT binary patch delta 1654 zcmZuxT}WJ45I%Ee&)watsf*inm85h*qt?XLyQH?Z#FR?GnluR{(pWLEt!wjJO1VDR zmxY8vwJ)JUFU6K^pwNeETn+AlZ3-n1K_3!LqA|u68?n@;@kJ;#Xn*G1t9zG{3$tft zzBx1BIWuPm9{L9!`cr=e;PREVz~b*!^+so{5qrmoGxq~h^-YP@#_FmKqq=razh^yD zlnrl>ROUd_>jPl)f3UF+j4P>)0u2oLBZnlXwL`!m0K-aYv>ItI96bu>td=%-7CyAP zX=&)G7%yZD|D~^+s(Mx!Pu>YLrUZK(@ZL7Y_7Uh^LHcD$AdX6KNZxLP032`JX(!QX z4~1sqEHac}lar_TfB2yQV@j~o!OID6q5U?ABL$2p!C$Ob+F=L$Biq~ID}Y98{WzJG zR`Cf)!KgKH0_q_qu>+btAgzO)1WI|P6V3sot+_ZIHElhP!;Xd-r;7ChWA8gvAi4My zV@hz!0oRHddp_p~kBE{IoRLSmphu;=@gB6osH`{%_W{0^{4`W^;!%4Rrl4O^*{0KU z;<9w+BCd0oZ$5!p2LMH z7EzY(qOA(OSv2{YZO@?4v#=?eviPYd;-|8BA2m(lPh zB{={%suGfJ;;8I-1fhb2>MF(X(n6=37r<$!-RN>xH|-|d-Zx!uH0vd{Y6?_<8#Tc&RzTq0~HeTU+FS>J8a+R)nldn3H8pS#{auua~YXcr&VvL5F2XclkpZ?g; z_6E25_Qkv8npRU!7j#U+?`Y;*0B0uY45VkW8gHa&CpS&IiYawt#$9UaZR4c#hJ4Xu zB98`On4)(B-clJ!U$Qm6&7lSAEv(a{$LX%yPu%)5!uH+0t;{*+E+L@38HDQF9mFBkGYL9=i@%vWFkOp&A6)_Ele zFMlB<_VDtch6^crMf9|YrENrQRk@d*pqY3{LJ8l00F2MQ@U-=IoS%18xfbUeo=x3< Ih8ODp0nZ`FEC2ui delta 1654 zcmZuxT})g>6rM9@=kD%>%7%4WNNd~#3YC^+*&wx&(ne!~SlcEov52%7S!`)Fnk*0X z<*LMo;!Bezjsy)ar3s{EHz6{~S`(iXl47wMmY=@ZKuWPZ*hEFa=$U(A@5XeKv-6#w zIp>=>XYSyte{j{G{xbmA#@7S)9#qHU^@*^=&qk&s z7v2%sljr(*V-R9j-rGC`vGMd~kpVIFEr%gaYo~x?05O?1XpK#H;p9n3$kujv8hT_e zEluxNO%^kT|I)9Qs(RKKANncGm=^rVan9{v><~GfH5ujhU2d!e=F^`Nt`HRObgD*!yT{-{!kqq@FhT_+&E2Y zM3$a`G>porGjIT^6?Q_42U7BA7dcbvd>6a}Fec{{bks5VR|0l5O*>WOH;ldORDq#; zPcWthCmrW{DPzy(9pNES(t^|KL^oX2DW~6tHW*b^XJG;0bH#h0h7*t21(<!hQcjQ@6(>1U`_@&%sStmPPXrhE;p1{_O__zBX>sReF#Ku3LZ~-ew@sqqacKV*NClw3n?oqUNZ2it&ulrLvh?U zt7DVG@91LIIB4J^Rg2)TJ|{um0K4<@WJEYmOXk&3U$4MUgb|tj6+U@tMAHJ(moadb zk{kgX)d>kFaa3J=2%#cNca>szd9l;YK5*J;H@ecrNxR9m^)=TT%~^@9o&p`e z*K>%sx!d`3ueyr6jaRwW%kEs2-K4AD;;YZ3N3p?;e1s|W`X>B>)(skF9>^QILi(_w zZ4GYsZGgUgcuP+`UC=25$7$wU0jF=%83<=`h`&hFPG*{R9aHYcOuE$c+s5ymH{^?+ z5(P8>ilX-cPU(zJZ6#_$ZC-i|O5%WmGXBaDh<)aTC*_+7{;>nqdV+6ywy^&^ HFE;-Js%6H7 diff --git a/records/wasm/modules.wasm b/records/wasm/modules.wasm index 0cf747ac167b869ab32871bafa60f1595183fadd..0c3aa49381acad36e413c3212e8103763e3b92d2 100755 GIT binary patch delta 1468 zcmZuxZ)jUp6o0>ab8lYKm3C%XovS9YF{@kCCX{NCy0JQ}!;K9QTZV`-Clv;^OTZ!Z zX{WGFKa@Vhqbk&{cFIiGuAzAl{2=?(`eA9cnP`?bC=HI##SPj$R1p&1v<>p*Xq$=cCAfeIvqe8 z66p?Fi|KyUV?O->A}G$}QRTbO-rioVY5bl(O?yq#G~W#kBePpIP5rrTU-#iSkh2a; zI+C+`C5G^^;o7A-~e?=J()CX9JS40feknMXaiEXIR-mkPJPfMv|O0`^c z%2b8II-SDZShYt}(7ADW-nG=RopifXBB&pce00)aZ4eLg<#{ctBz@3iZo{ zMg+^|r06W$d+uoODcjG&b;vvC4L5#LoQ3Nl&+S7LihUAt)4Z8ei4Cou|@w@@SyeDMhq zMZi^vtFm44Zzx@qPpFF%WUcgx%5n0du0l#y7p1h4W>^Iw(1*i66>mo98p@k!0W@>O;Y$ zq248`r~{MaybF9m1teT@5UX~10>wuzHqxm@d+rS!sdflSwXkh>xkNk#mx&HXcX&AI z@YB&g>uFab8lYKm3GUrI#*3(W1HKWrlE9>QCBvHbLeo0tYwPGT%`lSZV4z- z-_|N<^+VY+JflK!HN}RuDFpW*_`z?svaX%5)~)^ESc)>#IYB>YOFj3!H7_3)$i2Vw z=bm%!dB1aWGydF+f8tFY!-Z}=n+YTm&8J`6+uV7|j9iK$VYXc=ZYt|uj_ypv6DYa& zSNagG)9D`6V>102BAEWBh$`Qs`E9M$n#LbKt7&g(n&$gm!{Fo=O;dlKw{QFK1~6_N zmvm&@>Xx{OPc5Gxv-rrq?#Cxxh}&nn!4SClWT($31ixXW&LV>)tL7Z)aL3sW-n*(n z?_fa<@NrGBRr>MsGa3XBvTdQ1fYXt9`(W0`0g&jvSlJ9LnrDW~%=kOtn@hJ_G zU%PCu1nv1LE#^v1P(qZkwG128wTeGb1_bq9R^k;ALp5akkxF9gDzuL&?V_ip)G?)+ zzwVT&3WfDS3J>9L`^yw`ZWL!-OC8%yx9bx@{iNh0d4uhPc%0AAYEdQW0}d3yqvD8u z)X|KJVA-4$N6PlD2im*J_I|hyMd!TX#!rfVxE?}|sWNKx4k2W(d|mRdD1B0J$^V_z zVrzq6@LtLnh#U;lHbf5F>#pUI1*Wt4iwMpbA0h> z5=FpOh&kCR`2wYj@~pZzLDoW_s2ry#>MEpUMGpIqx3Ps|#0vJ{{YOSrS@g|{lFw0$ zXTftS9^oXmtLHQ6k)(GQ_2GM$ zhI+fGq7K;Pya#+r1teT@5UX~10>wuz*3+pg_SCyLS?v&#YGGS9xJ2xO%S4Bx+dZ6g z_~~d*c-mN5OMO!WRt}!)WLY;_My$g-&YpPA6TD+=ho>FzZmWizs{y*-UNuE6p66dQ z?|*)bTfAKrzM&mujmofX-0lb5QmWm*PYnJ$w9Ug!q4`GMKjwTlKhtmniM2k{%z+A(9)OZO fV;@N4?qT-<4O>qw;KF@uFD#(sqbG`9);sclwxX)0 diff --git a/records/wasm/nil.wasm b/records/wasm/nil.wasm index e60c633bb5d6b65d285d1f2816f437a0bf749086..325166561d7c7fa4d6accdec2fbbfe767f18e711 100755 GIT binary patch delta 1475 zcmZuxZ)hAv6rVTmX0q8RNxh`0m^3FTHpb@e_RwHHqy=Lj1gnD7BtoFr8odws&}L)* zsf#Ist(Fv*V`~)Bz|~@#H2-K8?o%lg8-ymQmkJV-R#PZQJ{T=Uu&HnMlHC)!Ff+gR z|IK@EHk~a>nRhL5uwd?fX+jPyx47HMFm#n!$yM)I)fCYg5g(CmF-Ot&F`xX@*u9 z3@%WrYGI9Y0N^4d+)56uA#j}!B$>PJXA-($=hSx3^vjnRD<$oM6{MXFR#q^k7Ou-5 zJj>XhBp}ypu%wtVwQ$~vYvgOz*{wkp%sbs0^uSs7jt2VxY`0m5Q%T*3^&H$6OCCuw zXYV3xasKXsfx_Q*c7;Ns5a%4nge6#U`g@@qR=uP4pBoIc4z4qR5Hi6Z(TAVUFc7Ho zZQ)*t_s1+A1N_37Rc0gZyiX+hv1po zUnm%V!jq7Dik99)!sUQVtZ~I!a_%02YIx`r9)_DR?k*gLG{9Bo)1y#o4UJm70mSpT zG|M7Lly=&KLT?oDsMT;*_K31Oe-nlXsbQ=SG*{7e8~Wi6T9;{@c}d>bouU_r z9yh$vF!ks#tLB~x)GqNNP5mCg`57t@;f#-X6V;s9H1A5MI?%Zur1tMI@-~xuMcS5j zQAC{|CG8!6bIK#(WqOa}Hk3iu{lSJ#cx6ykUg-RVuqf&kWjq!Z1+oi&rCaTwkZF2V z5*Mbdj3~>aOj6pAxm_u^$KfmIjWbYgERE5_kT^u~BoYzzphC-!K_(8l&KYQHroZTw X3#AS&LSGKL1B(z6>v-~aI3WK69>B32 delta 1475 zcmZuxZ%iCT6rVTmW>^*t=s~Fw${8AqSniHXMVp4!s3Z-sYGM>Hk~G1hXVMSIVr#3e zh4?|Vp~>blfF}6i4#U~@XfEj{_o-h@Ar`@=0j&gWgxF~Mq4i&4EcMME+@47{nVH}F z|K`0no4itzyiziK%Y@17?`FqXV=T7o&})zH>g{3v2L#QH6^q7Nnwp!NqA|xx=C%~n zyF(2{qGwW{Le%*wwGX1%(BwX#BMb#dE1_b?e{WjIm16F1dlUvx(Xo#`MAs z`QHyQc9R6;noU%cGo}~Lsi;A|X4C-#>R?VCHlQCy-D?K)0DSDWnQ$_0MkD=i^+e+D z#F;v{0$bG|{V-m7b#HGVAWHFjr7ZXn*3`%VRKu^{(RlM421XY*89)G;;CJZ559S#N zH2L=6fW+I=Hje-fsReD8a<31-M-VwX%Ru6{=WQONGc(J=d8!#S5#oH)q0-6c)aOUw z!N$uJjNju)$UQ|%-bcb^pG&NBnzf|XjzB&9sY;K+Wtei8k3te4r%oJ$N_#3}^W7jG z!KDQjLZXaQ9u#J~*l)CZoYTTAh%K?dAdlZw9xup;A+M18pZzf86@)xvblF3q+~^WQ z4$9XhCMmonP~txeEX=oT-dc|bPAufh*c4n7rLE!tV@cvNCI1|dHBM6Y60c_LuxKMc z9z{~igW?5aQR`aNH!TukQMO9_fuhAin9>&~%v-4T1wT1aT|`+$G2E|?!`oOprSk#P z$Vprz<7WZW+7V$3({85?RpqwsE9vn1`}6%Af_y{0RzK`x%WJd8?eMwb0@nx?pszYp z;GHfY-sPL~Cl2_8Z^n1{+*AH!y?=~qypgZIlODzvU-K4b+}$H^4ec{D&b*>%>`u}P zM2{QXY?yv@cuCJa6{u5UmZttCz`1!U5aC<_@u#Wg#HM-II`zKJjGx-S$I08p{3|lH zc8W6Uyi3|U0cW*G!mBu|&N)y8m)wgEbi-3hS6-O>(V!?B5LG-96eY43*XUL|DdcDL zswA$Sw{xPZh_Xm&Q|9(2;5vt4_53JQH~%tC4@2S@#gj-x*n=9a-UV4W=BiQXXrsUA Xg>Gt?}w0xL)PL0VVGmv0@ofIzQ-wX}UGCs4-i+tX!?r zAY0f6NH)*P7C2msN+Aml)<0$U zdcT0N^rT*Gq$t7$r`EB5`Af1GQ-m#!znA>`;=Q(KGNuS`TBBu<2Y<^;WsnX~Z(Z|~ z>bD;FVGJg$?>0jSs;t|JEn9g3^37YJ08O_nn*)&MDUc=hbPJy__JLC!`hLr0Oc6@i z5rj5?i1m8~VscXZ$Eq3;}IM$$#?o;j>DR@djNcJ&t`!?FEh}axt9TWkO{874}KnFAkfQ8 zvfDMhKW6d@z%Q(Bl{PHT4nP-FL}nRiI2$o}6>&Msf{tc@Qi?c_7d)CrTTNSs1|c_a zjhgWdwuj17x6kOpxawNTq>hd&)|o*lhQH*@AarnZdfuJ~eS8ZI+bO*Iehp8Lnmhzz zGp@|DBBkj)wkW)fqCs!8EknYaaEC=h!r!{Czct}M4o*PS&NrRrBtfv+s!1TJfZQRl_A3em77yj?nZP{yb_1 z#S0n^*&2DnwkY=KS8(Y?Qbnp`5LdKX4Hv0h5e}=xN#r&32@-Xp!k46llX}SR?;#gq z+&VV|$MVP3Sm4#88eXOvuK-S{dL)N&!rC)B1JolH>lcHy7?K}Lc{A3Gb(bDv@iw5eRAhC6nL8cd!dLKU(b2BS9@sAaXI-T zOmg%8FF*FRDzA={8GlJHpH!D26Ahs7I7jelq$@;XX$pFz>fd5S@;L~JmrkfSdb(cj z2a&>`&Jo*qX|{$d6xBoj9E3Sr3EHAEudXA7@wEIj20I(*pY)=SpLAHB{P+oe!vlsq Jkk4B-`~zct2ATi> delta 1889 zcmb_cYfM{Z82;Yx?RPkoaZ$pm;noF&t{bHk#?}zTOPt8uvL9Q747VR{C^O46DN&>G zOmHT{AD|>V!x?MBI)y3aqaEZw>FHsW{GvDu=wWl%s(%K%(;=1^j{fs+yWAe_)_&Hoi3# z{;b=R=K5{+BKQaT=9<7ik(et5Vt20Ik?gyLW0Ex`s)-GpUc5`n>7_%5j-II8b01@P zmwwx*SH^9W1B9n@7?Xr2H}`K8FeV9Kw*6g{=(x>z_$3!(lJG5kTLqLt#CWs<^5AI0 zLuRj6N*K#d>vf70N%)9W>+HY$<#~)r!gk9)K>owYUb}J`lZ4;su}Ua{KaBZG$OdT8 zS3RV<^*=o@4kP-f+aU;E{Rhbw^dc|$X7r$!rdu%DypZK6iItnvEuCTPRjWFLZxu2o z31ddL4^9C@^`EOLC#vW9;eEJZ4EdoL;DY{H4OvHwn>ElV)zVt2>P@xOCTg_TLJx;` zjqi8C#ZK_+Z-n8;%+-fG91g+pYvaX_VUELweqab(@RP~BzkJ0&ZR1V`;6Nt0{62Ul z!9bvsm*;mW`1iQRs{woTKA9FVJ{f{@P#v9Rpy1M^#skD-v&?5{H6Rr?=gESv71364 z{a6GFz1OH2-(q^GJav1AE{tool}z&JxT0T*KpFgQ%tWA@Yw<;M9(3^?G;EJ>sz($Y zj%hpyVmq!ZGPl&!ep3|A7SXJ>n3hrDOu0j%Ipy!%)Zdx%_ktA=H}f^Cen|9!6(N){ zIYzG9CWO+hJg?v-Y9A7);9rZ($G0YzKj@)l!q)V>Dt|*{Hi`$;6$RI5_ya)Mc$ubG za5bj+#1mA<)W{n$MR8DF#3+BUm1n7#lvzeaH??ym#N0nfFrUV$sruk_m9D#HZO5NA*~8d5DE?hE+i;~Pr4T4~h?(n6BDz#N)(FF_&-U&D>3zEEUN<7;F2S{+dAl#HSZ=8(6 zk!(!@j%MLy_gg^g0Sjp+1}khiWa7`XK4V*bDJ$Jg0#pXrp))DlXiDO3b~@j)-&X82 zUTItV?QP}D#(VS1^U7CJZ%6)0kNmwUxQVVYx(Lrpdk+l~FJs2JX?PtT)8)p($#>+7 ztS(W&tMf&M(t#UPZ71FQL)t>Y^+_!*3evO?1topP-p`@L(fZ#DO=`*YtarQ8Pje0% zQ(wRo*Z%+V;~&WK@;JH4m$dJ^ybPIW0Ex$I1fLeVLNt*jp+~Cv5k?I@2SM>vuZ*Lo z>*O8~8GLtvsN)s+3f`iqegy{!bEe`mMQL8&L<-@c@pc0CHq$@pRTn>Ru_EKm2l%%R M@Eh&L{P>oC0COJ&;s5{u diff --git a/records/wasm/returns.wasm b/records/wasm/returns.wasm index 7dff27f72f103a030a3ef680c308c26e78deb4d7..0a9f90ef55347dc9e7490586f383d06fa60ee1bd 100755 GIT binary patch delta 1456 zcmZuxYiL|W6rOKp=kDevZKCULQWEJxw25i+xJ#NEBn`9{V(PM5)yv2BrAJ$C-Q0-u&poz2|)2 zIdjf8cg}3zLa=Wkn0edA;OGf^^ZR*?&Yn6a{=D;I`iq_z>eIjWWTQFm*;r+~F5Xb> zIMsFa>5nhG2PfUu+YD#)-QM*eGv4 z4H(m@zT_ywF?G|R0c{Hu0ozq+fMAPyz?eO1SAcT9SNn|lN4*oklazAPph^{#$Y4Z` zOEe;`mnB*Q(5lI%pw`dWXa~mBL_XCSQ#bNawso%Jmo6M0VUYaK7tCOQ*o zr+1wy>BPmnAGW>}2*_eFtuA-sBP^-2`%#Q#*Hry=g(0+)+YAT@Ci%bg6Gx{Qq_Fut z;nywkXC}pKA>LB+X4!zgwI9b&J2uH+iOI1PkCWAttj0qQ42W^QQn0F!G+$Cf2T)q| zEj8l>ZiLEHw=H*2aM5RpdeBhw>W>2`$KR^t4Ya2&O}lbLG2cj94@*0=%M#~?QoIrJ zJ~2PdVg`jyx~{ZaWlN~lW%{L^3nyhuE}pn;JdukZhnJ9b*B5y8lkzx7SkXa5Q)j4M zQVL5Pnqf6Oy0X{Z5z^k{spZw-A7x&XTo?Mr60;=sQAlZtK~igpuZB`Ja)%}QT}08O z>&nMNS50nG=6P6NwVEvPHFb-L0kcnXc?0fURVOQpSE1yp9{TfxC>0o1KfQ?~cMls` zutQl(T%jTlLyVY$`01V@a(U z0+ZmUPUN`$oX2`f3erXu-^Vn$u>LIkt=Ru{%Ox>e>hq8q=XU2LKj=jrQN;@Vg$ zDuaSUsH1~EKz%JidgwRt6EkZ*~|dcGlSiTMm&47x4U*abDNYv{$> gcu8?i$Iha8hxdQk<(V=aUPM%SAflUp#w(%!03}YY6951J delta 1456 zcmZuxe`s4(6u#fRxi86Uw^n14j#d_^TW7bXY3N$V=ql5}EdCg%*g2KK+%U=pn-SJA z_nc**)xj{>yW(8`X*Uzsq)^zaLC}B1R$EsE+c5_++Ul$h*-!_9=(+FNy!^4id*^)L zIrp6J-g{nh^ZwktfAn1w=f+N&8$S#tldXGSY;WDSC-e3B7?PRa&KDyk?)lidWFpzt z)Y8(FNM=6$=mR7&-Pukg#!hEfgUopDGfv#$k>Bb4QvL}f)b4rQGd7j~{7LLpCplK* zWi_JBK{d@$hvRBVqhZ?=s0KExS|7m&)n;v;Q@ebWb6FkM<}dZW4^L6bl14G*ml#D( zjZ17oqkT)F%ZGyMFezBDpEa=`$g7D8s*_jOD^Ry(y5ZLqjESp<7(2ii^W9(=99qek ze#C?ujD#55NJ0M%L2ou_MH$nNn6|f8Vk>Z3omheE)c3$j%-|invkG=9m}q|W(4OX0 zPb$-rU8QPz@Nw|UmVG{-jEc)@st2E7QJp=4D3%=4c=H>Ez)o&5z$cjG|I$yqafv|+ zliwG5%@F^LTD%#eSN*7$4coVm;B7SLCm9U!?L~_x$rdJA+(j+WAjbJ}!Nw}mTvUal zsBQd?n(;~}LglGj`(64NJeH`3H8rRHIEs4wscK%weycd+$PrP#p0xH#GqB4L*@DG4 zLEbOsW>`$4z$wR-W|!O^=yI3~(kzA3a(gMBx??<5iub`yC_3va-TG+jA z3=|D9OGO@q$mxRQr$x?w>rI5KEPbhz!|U&MFS8f!Z94b7*7MWO9XsqZ9vd&Qq*f1s zPViDEO5BB#$9H)7cy_^Y8n*OZC zm+mJNsOgp!GypbbZ-AK870F)^lj>SO!kDse_hXzxsnG#M)-Td7Hc*Q*bb5wZxM&q+ zxD2JGzl7|Y1Ned?YujfK!y|pVAC0TY*FpMudsw#ewIM_NJW3yiAqJ?i1LBUW>4iIZ fX)$2O&Z2Xh`+wQ*nmRkQfQWQK*zWuVuLS-BPL{2V diff --git a/records/wasm/strings.wasm b/records/wasm/strings.wasm index 8b400827ca40494a6a9e058b41a05f8950505c3a..3c5240340f16c6e70b89585bae28c3e3348705ee 100755 GIT binary patch delta 1744 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 delta 1539 zcmZuxU2GIp6u#%4y?1A)wt$=MN&vg2gul}5cGDEmpayNML=4dwX;663t*MC*m?>$% zI6(2iB`RStL;Y(4-D;b*+hm1F_C+5w?V45!p)J8`6xwJ=5YRvZ3E;UibY>qq$({3^ zpL@=^XTCd!CWD72gTv2-pf}$XN`D?oCL2<3ZD?qHGjri&43e4KCkvIH?Ty%)WFpyE zTVG$BNM?#XUqB+0%03N=d^TGJB!23>ro^dlb4sN8$6nc)TK#o7V|bsQ#~5S5t4MFw zov&a_EqrHT|44)}wQ#rZZ>NoctBiNQ9A!)`JdpYHUKM2XxqFKNp3MAkzY3no{6X*6 zkHK=d@YxD_-#YaKl#|zL2GU|y3Sb!etx66XVV$*2iE%5<$v0|!!=Vbs?GX-Tfk$&I zozAP@W30oi4Bf?LjH!h=`?`d80fwxd8toafKG5JO9JiN*-~_;7`#T->v_ZnEG2r{q z#iv>W0U>bC`pB`*J4V2x2WxPq3KMv?C4_`WDfwZE-9ELigN;X!iVV3;NqUe;V9?g0tUr^BW=! zZiJA7s*OcjixBdld|P4A%5@kgiW*aXE4~cce;gwAWi zqO4s+cwJZoWh>56w)HggFX9O3Yz!X*<-K^Ur4LL6O(nm21Jl=h=>Nh zDlGA@VY)LCyVS2hDRBoXG`j#Yu*=?h9M(3`kEr5zSZ-y0gT0Wj3EvKo)GovO!T$kL C7q>Y8 diff --git a/records/wasm/traits.wasm b/records/wasm/traits.wasm index 73f4657007977aecf7233ba53f0ac547e22a2135..5fead95249a88430ca188b6464d5c04cb2501e45 100755 GIT binary patch delta 1525 zcmZ`(U1(fY5WX{K&&l20*tBl9LDI&>l$icxle@-fz*cE#U4u#=TI{keSv6UWwOC{e zmMT}83R=Mi7d)VX76c!fpG`{lz&`e=p|nYCl9okHHEBd7AQe%mX`OSgxpyDxeVLhW zzL`7c%*>HXLAewxmclS{PK6tP3wBw(?N;JttFQ3G?HF_v?%pnM@UTp*CDESfY_+V` z_Kw2jKc9nDIJEL4Sm(~J^l-3Ln8BM!@;C<@z>?EhT(9bCAiEz9%Oh!c5Z;o5S{#$b zG;D+qmK4?x?pjP}H0)J!H1Xy**@(%RhUd z3lhr8LOcLUW_!u0)VsZK7@#D7?W05`8O{+pD_gXfl6!Il&Z@o~yocmmdJR%AuAb_L z3cy#Y;Sg*ED5$+}K=O4u!|6G=6#QvtE)d{?{UTqv1|QqYR}3&y7%~6?jI)rz@h*Mr z*8kvtSL3VX4<-$uSlk>rD%c-I8{2?=G-<>{3ZqqtKQj%34HFli&?r*4I&vM}fwt0w z0fJ4GY)p_;6Gq&%EYL;_(aA#mOLdguf^4Wj!`2Fwg0)roudgHLqK^{)riSKawgOGC zqE1#I6R+w8*$vQdiaq>I?Ok2 z_1GG@$%8xi1LifsW~lh*fuh+6RWI1LqjsF{5p2k@$eVXmzR#RvGf$JrgD$}5#16r( z((htySRb5J?4%ZWdCI)L%F0*$Lp9%k1_ont*8;r1X-t;|A+s#l70U4}uyLJ_^gJ6^ ziwkgdjjg*(9BjJZ?d|}$?R6U8?CW`_=??mWkH$Teiq%u02|o3rhYfieru=Yx{5jw7 z!|-7r9rWky=VNr$|JAF{q(`&UXWn8;4g3y2GW)z9=e1QsIQ1w+I%s6IZ%q#oG^2Nf zF6f|OXK6zY0Gp)^(R$g_a5r5&!8HAv)8un5`la<9r)-n%#+mVhJWK;HO6={xrgcWr z7aa}nbISs~)a8h-L9P1>Hm!&As;w3`BaEs2YtR0<>;iEi?Z4q80a_oCL z^meM~Yr1T~zAM>f9<8$SL|XN!XcSN5GoR?PXv#y_0=xzvi}DU^jtDkiqxA_17 delta 1525 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 diff --git a/records/wasm/variables.wasm b/records/wasm/variables.wasm index 1db3aceb577c3e6f46a3a0bd9ff5963fd0985a5f..a2cb4a7f373a01dd182369a73da0866c7ce489cb 100755 GIT binary patch delta 1545 zcmZuxZ){Ul6u;-5-q+X8$+T|8xP_V#hrqhNiihZ`u;2cZQK5Sh0}KS-o1G5!KRvr7{mMYdz>-mT|)Y2 zgLfWeOfP(UWqU&%V|wBHuDy=}Jxh!ajs+Rh3;X5$7_5O|wdO^r0ce+zE>b&WOmF+- zp)RP0pXJAT`&XXpf|D>V+vBjCJU@>UxK64#oQJ5|y9+u!pyWt`P^GRW;2=O&wsljU ztW0)8{gye0Na6@%Z#hI@aGomA3uhc-kxG4a$ltya|>Z_!j29x3=u}cyL;#uM*)U8JUJ4lC!M ziR~r%Tpk`6$`A6;2>-})dAJA{)ZPM|f%K&rn-&K7 z2I727_{|;zPmiW~8;B>dIKx6(Gf&u}@OO$1v(rX~guh}=iH?f>z>4+*75i~;9CCJi z+R2|1$HDOs#;~Tb%1jDj95voBaDa|a2{iENH5TS|+#%2S*qvJw2tP3ZRw^Ao6 zd2%ASOiL|0L+!W@?_$FR%?JEu&cHkc?*JUviAYZ2xGK&;?W(lyGWqbvhn?;o1E;-q z4NT!CsJIaiT* zJKZ_K^!l|=qw90UrKTqw3(icL;e@E7&X1D!M!;)2BgqT4#H$@zpl9Qj{>sg&oxj2! zcz#efLiqWHfT-#dwY()DyvBa~k&4_%49?Ky8aSU#=S1x?Ek#acn_9X9TOWbR$!5Nm z(@1r3-eAC|@{q}cR&k8RhGvP_7)VOk7PYjnf|SD3>Y~X9w$u0Jw-~RNhhzK*Sc>G; M^wCygH$PeR9|U*9lmGw# delta 1545 zcmZuxZ)jUp6u;-5+?$uwx@v4Qv1O5tRl7A!nHL!U#1kr(()m&8xI~sZ{8i_VH zG&MCuq65CQFCa3IUfTnaiNe~ag0|j&=76?+cV6SPrSIO2cOI*&cz`jyN54lIW8M{{ ze>Qyke#Z2|6C2xWsuZaCwlu^W!hj6GHP!ej*#c)aRNiiio=&sZ*?7pRu5#=Sc1^3bu9sJ0gS1Z zcFHrRlI>8vd&VJ>IK|l84iOm6Qw4hAq+`56r9Mf>Lb7Sft=#ukOYRU9x{ZRDcxm&_?~pkzP3q5XKt-VcsL z&W<;o{3+27j)#z=n#Lw0DTMq`zA5oM9iI{?@y)>^G7i&f|AMhJFi3=2b5HOj!9++^x5lMkC5or|7v-`Uc8bjSbm)ivp- z+3b3*Vb=P(0KcL+N{^dYtb2qvpP)z^4HO#|4-qu2r+_XfA#s9c;AOy>IjVu=TuI`4 z>COqJ*ROqQU7typnx1g{#+fN2oDgNyd5gSv08Z(QBrn?%FL!8xo{iu1S8m!mbPJBc z3raUa`1$sLDC-rKyfGlWvKtFj Date: Wed, 4 Nov 2026 07:37:56 +0300 Subject: [PATCH 2/4] read the measurements out of BENCHMARKS.md, and stop comparing iyi's library with Crystal 0.1.0's as README did until 0.20.1 --- bench/site_facts.py | 119 ++++++++++++++++++++++++-------------------- doc/STACK.md | 17 ++++--- src/pages/why.astro | 8 ++- 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/bench/site_facts.py b/bench/site_facts.py index 577c2de..371c6d3 100755 --- a/bench/site_facts.py +++ b/bench/site_facts.py @@ -22,20 +22,23 @@ stated flatly. RECORDED. Seconds, bytes, milliseconds. These are a machine, not a language, -and README.md is scrupulous about saying so: it names the exact box and it -publishes other sessions reading 0.16 / 1.29 / 0.19 and 0.22 / 1.81 / 0.27 -where the table's read 0.13 / 1.17 / 0.16. So a recorded number is never emitted on its own. It is -emitted as a group carrying the machine that produced it, the command that -prints it, and every session README.md publishes, so the site can draw the -spread and lead with the ratio. The ratio is the claim. The second is a -circumstance. - -README.md is the source for the recorded class because that is where this -project publishes these measurements. Parsing it means the site restates the -repository rather than remembering it. When a pattern below stops matching, -that is a failure and not a default: a sentence this was written to cover was -reworded, so the site would be quoting something nobody is maintaining. Same -rule `doc_numbers.py` already applies to itself. +and the repository is scrupulous about saying so: BENCHMARKS.md names the +exact box and publishes other sessions reading 0.16 / 1.29 / 0.19 and +0.22 / 1.81 / 0.27 where the table's read 0.13 / 1.17 / 0.16. So a recorded +number is never emitted on its own. It is emitted as a group carrying the +machine that produced it, the command that prints it, and every session +BENCHMARKS.md publishes, so the site can draw the spread and lead with the +ratio. The ratio is the claim. The second is a circumstance. + +README.md and BENCHMARKS.md are the source for the recorded class because +that is where this project publishes these measurements: the claims a reader +meets first in README.md, and since 0.20.1 the build, run-time and server +tables behind them in BENCHMARKS.md. Each pattern below names the file it +reads. Parsing them means the site restates the repository rather than +remembering it. When a pattern stops matching, that is a failure and not a +default: a sentence this was written to cover was reworded or moved, so the +site would be quoting something nobody is maintaining. Same rule +`doc_numbers.py` already applies to itself. """ from __future__ import annotations @@ -64,7 +67,8 @@ def flat(text: str) -> str: - """README.md wraps at about 80 columns, so match against one long line.""" + """The repository's prose wraps at about 80 columns, so match against one + long line.""" return re.sub(r"\s+", " ", text) @@ -301,74 +305,85 @@ def context_scope() -> dict: } -# (key, pattern, group names). A miss is an error, never a default. -RECORDED_PATTERNS: list[tuple[str, str, tuple[str, ...]]] = [ - ("machine", r"\(([^()]*Ryzen[^()]*)\)", ("name",)), +# The two files the recorded class is published in. +README = "README.md" +BENCHMARKS = "BENCHMARKS.md" + +# (key, file, pattern, group names). A miss is an error, never a default. +RECORDED_PATTERNS: list[tuple[str, str, str, tuple[str, ...]]] = [ + ("machine", BENCHMARKS, r"\(([^()]*Ryzen[^()]*)\)", ("name",)), ( "edit_loop", + BENCHMARKS, r"\| \*\*one module's body\*\* \| \*\*([\d.]+)\*\* \| ([\d.]+) \| ([\d.]+) \|", ("iyi", "crystal", "go"), ), ( "edit_loop_sessions", + BENCHMARKS, r"other sessions on the same box read (\d+\.\d+ / \d+\.\d+ / \d+\.\d+) and\s+" r"(\d+\.\d+ / \d+\.\d+ / \d+\.\d+) on the first row", ("a", "b"), ), - ("edit_loop_ratio", r"costs \*\*([\d.]+)x less\*\*", ("times",)), + ("edit_loop_ratio", README, r"costs \*\*([\d.]+)x less\*\*", ("times",)), ( "hello_binary", + README, r"is a (?:\*\*)?([\d,]+) KB(?:\*\*)? binary that starts in (?:\*\*)?([\d.]+) ms", ("kb", "ms"), ), ( "crystal_binary", + README, r"library is ([\d,]+) KB and ([\d.]+) ms", ("kb", "ms"), ), ( "full_build", + BENCHMARKS, r"\| generated pair, ([\d,]+) lines \| ([\d.]+) s \| \*\*([\d.]+) s\*\* \|", ("lines", "iyi", "go"), ), ( "hello_build", + BENCHMARKS, r"\| `hello` \(([\d,]+) lines\) \| \*\*([\d.]+) s\*\* \| ([\d.]+) s \|", ("lines", "iyi", "go"), ), - ("front_end", r"answers `hello` in \*\*([\d.]+) s\*\*", ("seconds",)), + ("front_end", README, r"answers `hello` in \*\*([\d.]+) s\*\*", ("seconds",)), ( "runtime_string", + BENCHMARKS, r"\| string building \| ([\d.]+)x \| \*\*([\d.]+)x\*\* \|", ("as_it_runs", "gc_off"), ), ( "runtime_hash", + BENCHMARKS, r"\| hash insert and read \| ([\d.]+)x \| ([\d.]+)x \|", ("as_it_runs", "gc_off"), ), ( "runtime_array", + BENCHMARKS, r"\| array append and read \| ([\d.]+)x \| ([\d.]+)x \|", ("as_it_runs", "gc_off"), ), ( "runtime_arithmetic", + BENCHMARKS, r"\| arithmetic \| ([\d.]+)x \| ([\d.]+)x \|", ("as_it_runs", "gc_off"), ), - ( - "prelude_vs_crystal", - r"([\d,]+) lines of Crystal's standard library instead of ([\d,]+)", - ("crystal", "iyi"), - ), ( "context_pack", + README, r"at \*\*(\d+)–(\d+)%\*\* of the sources' size", ("size_low", "size_high"), ), ( "context_tokens", + README, r"\*\*(\d+)–(\d+)% fewer prompt tokens\*\*", ("low", "high"), ), @@ -380,17 +395,19 @@ def context_scope() -> dict: # up, and the site says that rather than a count nobody maintains. ( "context_runs", + README, r"over the runs written up in ([A-Z_]+\.md §\d+)", ("where",), ), ( "context_rounds", + README, r"rounds to green tied ([a-z]+) times and lost\s+([a-z]+)\.", ("tied", "lost"), ), ] -# Which command prints each recorded group, quoted from README.md's own +# Which command prints each recorded group, quoted from the repository's own # attribution of it. The site shows this beside the number. COMMANDS: dict[str, str] = { "edit_loop": "python3 bench/incremental.py", @@ -421,16 +438,16 @@ def context_scope() -> dict: } -def recorded(readme: str) -> tuple[dict, list[str]]: - text = flat(readme) +def recorded() -> tuple[dict, list[str]]: + texts = {name: flat((REPO / name).read_text()) for name in (README, BENCHMARKS)} out: dict = {} missing: list[str] = [] - for key, pattern, names in RECORDED_PATTERNS: - m = re.search(pattern, text) + for key, source, pattern, names in RECORDED_PATTERNS: + m = re.search(pattern, texts[source]) if not m: missing.append( - f"{key}: /{pattern}/ no longer matches README.md. The sentence " - f"this quoted was reworded or removed, so the site would be " + f"{key}: /{pattern}/ no longer matches {source}. The sentence " + f"this quoted was reworded or moved, so the site would be " f"publishing a number nobody is maintaining" ) continue @@ -439,16 +456,18 @@ def recorded(readme: str) -> tuple[dict, list[str]]: def sessions(raw: dict) -> list[dict[str, float]]: - """Every session README.md publishes for the edit loop, not just the best. - - The spread is the point. README.md prints three readings and says to read - the columns against each other because they pay the same machine together, - so the site draws all of them and leads with the ratio they agree on. - - README.md prints a third column, another compiler's, on the same row. The - site publishes iyi against the compiler it forked and nothing else, so that - column is parsed, because a drift in the row is still a drift, and then - dropped. + """Every session BENCHMARKS.md publishes for the edit loop, not just the + best. + + The spread is the point. BENCHMARKS.md prints three readings and says to + read the columns against each other because they pay the same machine + together, so the site draws all of them and leads with the ratio they + agree on. + + BENCHMARKS.md prints a third column, another compiler's, on the same row. + The site publishes iyi against the compiler it forked and nothing else, so + that column is parsed, because a drift in the row is still a drift, and + then dropped. """ # The table's own row is the first reading; the sentence under it gives # the other two sessions. @@ -465,8 +484,8 @@ def machine(name: str) -> str: def build() -> tuple[dict, list[str]]: - readme = (REPO / "README.md").read_text() - raw, missing = recorded(readme) + readme = (REPO / README).read_text() + raw, missing = recorded() if missing: return {}, missing @@ -487,7 +506,7 @@ def build() -> tuple[dict, list[str]]: "recorded": { "edit_loop": { # The claim, which is machine-independent and held across every - # session README.md publishes. + # session BENCHMARKS.md publishes. "claim": { "kind": "ratio", "value": float(raw["edit_loop_ratio"]["times"]), @@ -547,14 +566,6 @@ def build() -> tuple[dict, list[str]]: "command": COMMANDS["runtime_string"], "subject": "the same program under both libraries", }, - "library_size": { - "iyi": int(raw["prelude_vs_crystal"]["iyi"].replace(",", "")), - "crystal": int(raw["prelude_vs_crystal"]["crystal"].replace(",", "")), - "unit": "lines", - "machine": None, - "command": "python3 bench/doc_numbers.py", - "subject": "what a program has", - }, "front_end": { "seconds": float(raw["front_end"]["seconds"]), "unit": "s", @@ -586,7 +597,7 @@ def build() -> tuple[dict, list[str]]: "bench/doc_numbers.py measured(), the bench's own project " "generator, and git" ), - "recorded_source": "README.md, parsed", + "recorded_source": "README.md and BENCHMARKS.md, parsed", "generator": "bench/site_facts.py", }, } diff --git a/doc/STACK.md b/doc/STACK.md index d1e6457..b6ba8a5 100644 --- a/doc/STACK.md +++ b/doc/STACK.md @@ -43,8 +43,9 @@ site/ reads the iyi tree at it, and refuses a recording taken from anything else facts.mjs runs bench/site_facts.py, writes generated/facts.json - targets.mjs reads the platforms out of iyi's CI workflow and the - run set out of README.md, writes generated/targets.json + targets.mjs reads the platforms out of iyi's CI workflow, the + run set out of README.md and the object audit out + of INSTALL.md, writes generated/targets.json releases.mjs reads CHANGELOG.md's release headings, writes generated/releases.json samples.mjs transcludes samples/iyi, samples/tour, records/break @@ -76,12 +77,12 @@ This is the load-bearing part, and it is the reason the site cannot drift. repository through `bench/site_facts.py`, which has two sources. Structural numbers come from `bench/doc_numbers.py`'s `measured()`, the same function that already gates `README.md` in CI, so the page and the gate cannot disagree. -Recorded numbers (seconds, bytes, milliseconds) are parsed out of `README.md`, -because that is where this project publishes them, and they are emitted only as -groups carrying the machine that produced them, the command that prints them, -and every session the README publishes. If any pattern stops matching, the -script fails rather than defaulting, which is the same rule -`bench/doc_numbers.py` applies to itself. +Recorded numbers (seconds, bytes, milliseconds) are parsed out of `README.md` +and `BENCHMARKS.md`, because that is where this project publishes them, and +they are emitted only as groups carrying the machine that produced them, the +command that prints them, and every session BENCHMARKS.md publishes. If any +pattern stops matching, the script fails rather than defaulting, which is the +same rule `bench/doc_numbers.py` applies to itself. The build chain runs `bench/site_facts.py` and writes `src/generated/facts.json`. The components read that file. `Measure` diff --git a/src/pages/why.astro b/src/pages/why.astro index 83cfe6c..9d8c1c2 100644 --- a/src/pages/why.astro +++ b/src/pages/why.astro @@ -17,7 +17,6 @@ const bin = facts.recorded.hello_binary; const front = facts.recorded.front_end; const pack = facts.recorded.context_pack; const runtime = facts.recorded.runtime; -const lib = facts.recorded.library_size; const ratios = loop.ratios as number[]; const loopBand = `${Math.min(...ratios).toFixed(2)}x to ${Math.max(...ratios).toFixed(2)}x across ${loop.sessions.length} published sessions. The seconds moved; the ratio held, which is why the ratio is the claim.`; @@ -47,7 +46,7 @@ const ranCount = SPELLED[platforms.ran.length] ?? String(platforms.ran.length); const ranHow = platforms.ran.map((t) => platforms.how[t as keyof typeof platforms.how]); /* The object audit and the Windows jobs, as the live page states them: the - * audit count and its exceptions read out of README.md by + * audit count and its exceptions read out of INSTALL.md by * scripts/targets.mjs, the Windows jobs counted out of the workflow by * scripts/proofs.mjs, so neither is typed here. */ const auditedCount = SPELLED[platforms.audited.length] ?? String(platforms.audited.length); @@ -290,9 +289,8 @@ const shards = ["kemal", "db", "ameba", "sqlite3"]; iyi generates native code through LLVM. At run time the two libraries are within noise where they do the same work. What changes is what a program carries: it links only what it uses, and iyi's own - library is lines against Crystal's{" "} - {lib.crystal.toLocaleString("en-GB")}, {" "} - on disk beside the binary. + library is lines, written to be linked that + way, on disk beside the binary.

From 48398cc98e7f4b620b30c3371b16c078855ffb50 Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol <990485+sdogruyol@users.noreply.github.com> Date: Wed, 4 Nov 2026 07:37:56 +0300 Subject: [PATCH 3/4] read the object audit out of INSTALL.md, where 0.20.1 moved the targets --- scripts/proofs.mjs | 4 ++-- scripts/targets.mjs | 33 +++++++++++++++++++++------------ src/pages/targets.astro | 4 ++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/scripts/proofs.mjs b/scripts/proofs.mjs index 19a9c71..bf3b88e 100755 --- a/scripts/proofs.mjs +++ b/scripts/proofs.mjs @@ -219,7 +219,7 @@ const ALIAS = new Map([["x86_64-windows-gnu", "x86_64-w64-mingw32"]]); const auditedHere = audit.values.map((target) => ALIAS.get(target) ?? target); // And the same gate over the audit set. scripts/targets.mjs derives it from -// README's sentence about which targets are exempt; this script reads the loop +// INSTALL.md's sentence about which targets are exempt; this script reads the loop // itself. Two independent readings of one fact, so a disagreement means one of // them is wrong rather than that the page has a choice to make. if (Array.isArray(published.audited)) { @@ -232,7 +232,7 @@ if (Array.isArray(published.audited)) { `the audit loop covers ${auditedHere.join(", ")} and ` + `src/generated/targets.json says ${published.audited.join(", ")}. ` + `They differ on ${[...new Set(apart)].join(", ")}. One reads the ` + - `workflow and one reads README.md: a page cannot publish both.`, + `workflow and one reads INSTALL.md: a page cannot publish both.`, ); } } diff --git a/scripts/targets.mjs b/scripts/targets.mjs index 3f9ff92..1ebeae3 100755 --- a/scripts/targets.mjs +++ b/scripts/targets.mjs @@ -41,6 +41,9 @@ const out = resolve(site, "src", "generated"); const WORKFLOW = ".github/workflows/iyi.yml"; const CLAIM = "README.md"; +/* Where the object audit is stated. It was README.md's FAQ until 0.20.1, + * which moved the targets to INSTALL.md and left the portability row behind. */ +const AUDIT_CLAIM = "INSTALL.md"; /** * Spellings the repository uses for one thing. A triple says `x86_64` and @@ -104,23 +107,23 @@ const claimed = (() => { /** * The targets whose emitted object CI reads, and the two it does not. * - * A third reading of the same file, added because the why page had said "the + * A third reading of the same claim, added because the why page had said "the * rest type-check and have the object they emit audited", which is one claim - * too strong: README.md's own FAQ says the audit covers seven of the nine and - * names the two it misses. A page that overclaims a check is the same defect - * as a page that overstates a figure, and it is worse here, because the check - * is the thing the page is offering as evidence. + * too strong: INSTALL.md's Targets section says the audit covers seven of the + * nine and names the two it misses. A page that overclaims a check is the + * same defect as a page that overstates a figure, and it is worse here, + * because the check is the thing the page is offering as evidence. * * The count is cross-checked against the exceptions: seven audited plus two * named has to be the nine the workflow type-checks, or one of the two * sentences has moved and neither can be trusted. */ const audit = (() => { - const text = read(CLAIM); + const text = read(AUDIT_CLAIM); const counted = text.match(/audits the emitted object\s+for\s+(\S+)\s+of\s+the\s+nine/); if (!counted) { throw new Error( - `targets: ${CLAIM} no longer says how many triples CI audits the ` + + `targets: ${AUDIT_CLAIM} no longer says how many triples CI audits the ` + `emitted object for, so the site cannot say which of them it does.`, ); } @@ -129,7 +132,7 @@ const audit = (() => { ); if (exempt.length === 0) { throw new Error( - `targets: ${CLAIM} says the object audit covers ${counted[1]} triples ` + + `targets: ${AUDIT_CLAIM} says the object audit covers ${counted[1]} triples ` + `and names none of the ones it leaves out. The sentence that named ` + `them has moved, so the site would be publishing an exception list ` + `it cannot see.`, @@ -138,7 +141,7 @@ const audit = (() => { for (const triple of exempt) { if (!typechecked.includes(triple)) { throw new Error( - `targets: ${CLAIM} exempts ${triple} from the object audit and ` + + `targets: ${AUDIT_CLAIM} exempts ${triple} from the object audit and ` + `${WORKFLOW} does not type-check it at all, so the two files ` + `disagree about what the platform list even is.`, ); @@ -229,14 +232,14 @@ if (facts.structural.targets !== typechecked.length) { ); } -/* The audit's own arithmetic: the triples it covers plus the ones README +/* The audit's own arithmetic: the triples it covers plus the ones INSTALL.md * exempts have to be the whole list. A sentence that said seven while the * exemption named one would leave a triple in neither set, and the page would * be silent about it rather than wrong about it, which is harder to notice. */ const audited = typechecked.filter((triple) => !audit.exempt.includes(triple)); if (SPELLED[audited.length] !== audit.count) { throw new Error( - `targets: ${CLAIM} says the object audit covers ${audit.count} triples, ` + + `targets: ${AUDIT_CLAIM} says the object audit covers ${audit.count} triples, ` + `and exempting ${audit.exempt.join(" and ")} from ${typechecked.length} ` + `leaves ${audited.length}. The two sentences no longer agree.`, ); @@ -256,7 +259,13 @@ writeFileSync( resolve(out, "targets.json"), `${JSON.stringify( { - provenance: { generator: "scripts/targets.mjs", source: WORKFLOW, claim: CLAIM, commit }, + provenance: { + generator: "scripts/targets.mjs", + source: WORKFLOW, + claim: CLAIM, + auditClaim: AUDIT_CLAIM, + commit, + }, typechecked, ran, audited, diff --git a/src/pages/targets.astro b/src/pages/targets.astro index f47ff00..9b4a3af 100644 --- a/src/pages/targets.astro +++ b/src/pages/targets.astro @@ -3,7 +3,7 @@ * The platforms, with what CI proves about each. * * The list and the run set come from src/generated/targets.json, which - * scripts/targets.mjs reads out of the workflow and README. What is new here + * scripts/targets.mjs reads out of the workflow, README and INSTALL.md. What is new here * is src/generated/proofs.json: scripts/proofs.mjs reads the same workflow for * the two loops and the job graph, so the page can separate three claims that * are usually collapsed into the word "supported" - the library type-checks, @@ -84,7 +84,7 @@ const audit = proofs.loops.audit; * the Windows GNU target, which the type-check loop calls x86_64-w64-mingw32 * and the audit loop calls x86_64-windows-gnu: one platform, two of the * compiler's names for it. scripts/proofs.mjs maps it and then checks its - * audited set against the one scripts/targets.mjs derives from README, so a + * audited set against the one scripts/targets.mjs derives from INSTALL.md, so a * real divergence fails the build instead of reaching this paragraph. */ const otherSpelling = audit.values.filter( (target) => !platforms.typechecked.includes(target), From 746ce6e170f98d0d88da4975b441d9a5fea99d52 Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol <990485+sdogruyol@users.noreply.github.com> Date: Wed, 4 Nov 2026 07:37:56 +0300 Subject: [PATCH 4/4] follow README's install and Kemal passages to where 0.20.1 put them The pair install.sh picks and the by-hand blocks are INSTALL.md's now, the platform names come from README's new Install section, and the lesson that quoted README's deleted-library console block for the Kemal port restates the sentence that replaced it. Citations of the build and target facts that moved point at INSTALL.md and BENCHMARKS.md. --- scripts/install-targets.mjs | 195 ++++++++++-------- scripts/samples.mjs | 43 ++-- src/content/learn/declarations-not-bodies.mdx | 14 +- src/lib/release.ts | 4 +- src/pages/install.astro | 6 +- src/playground/engines/unavailable.ts | 6 +- 6 files changed, 152 insertions(+), 116 deletions(-) diff --git a/scripts/install-targets.mjs b/scripts/install-targets.mjs index 4248245..f74ab6f 100755 --- a/scripts/install-targets.mjs +++ b/scripts/install-targets.mjs @@ -24,28 +24,33 @@ // * the refusal the `*)` arm prints, which names the same targets in prose // for a person whose machine is neither. The arms and the message drifting // apart is the ordinary way this file goes wrong, and it is caught here; -// * README.md's "Getting it" section, which states the pair a reader is -// shown, so the site restates the repository rather than one file of it; -// * README.md's own platform words under the one-liner ("Linux x86-64 and -// macOS arm64"), which is where the prose name beside each target comes -// from. They are found by the fenced block that holds the `install.sh` -// one-liner rather than by the clause that used to follow them, because -// what follows is prose about other platforms and moves: 48bc8d0b5 put a -// PowerShell block between the sentence and the clause this once matched. -// What does not move is that the sentence sits directly under the command -// it qualifies. Each phrase is matched back to a target by scoring the -// target's own `uname` components against the phrase's words, so the only -// spellings this file knows are the two aliases below. An unmatched or -// ambiguous phrase fails and names itself; it is never guessed; +// * INSTALL.md's "Linux and macOS" section, which states the pair a reader +// is shown, so the site restates the repository rather than one file of +// it. It was README.md's "Getting it" until 0.20.1 moved installing to +// INSTALL.md; +// * README.md's own platform words above the one-liner ("Linux x86-64, or +// macOS 14.4 or later on arm64, into `~/.local`:"). They are found by the +// fenced block that holds the `install.sh` one-liner rather than by the +// clause that used to follow them, because what follows is prose about +// other platforms and moves: 48bc8d0b5 put a PowerShell block between the +// sentence and the clause this once matched. What does not move is that +// the sentence sits directly above the command it qualifies. Each phrase +// is matched back to a target by scoring the target's own `uname` +// components against the phrase's words, so the only spellings this file +// knows are the two aliases below. A target no phrase names, or a phrase +// naming a platform no target is, fails and names itself; it is never +// guessed. The prose name beside each target is README.md's own +// ("macOS arm64"), found by the target's own components rather than +// assembled from them, because the sentence now carries a floor inside +// the phrase; // * install.ps1, which is the same three questions for the one platform // install.sh refuses. 0.14.0 is the first release to publish a Windows // build, and it is a zip rather than a tarball with an installer of its // own, so it is recorded beside the tarballs instead of among them: one // target, read out of the `$Asset` line that names it, cross-checked // against the refusal that script prints for a 32-bit Windows and against -// the file name README.md spells out under "Getting it". Its prose name -// is README.md's own ("Windows x86-64"), found by the target's own -// components rather than assembled from them. +// the file name INSTALL.md spells out under "Windows". Its prose name is +// README.md's own ("Windows x86-64"), found the same way. // // WHAT IT DOES NOT DO. It does not check that a release actually published the // tarballs, or that a URL resolves. It checks that the site offers exactly what @@ -66,6 +71,7 @@ const out = resolve(site, "src", "generated"); const SOURCE = "install.sh"; const WINDOWS = "install.ps1"; const CLAIM = "README.md"; +const INSTALL = "INSTALL.md"; /** * Spellings the repository uses for one platform. `uname -s` says `Darwin` and @@ -93,9 +99,12 @@ const read = (relative) => { }; const script = read(SOURCE); -/* README.md wraps at about 80 columns, so its sentences are matched against - * one long line, the way bench/site_facts.py reads it. */ -const readme = read(CLAIM).replace(/\s+/g, " "); +/* README.md and INSTALL.md wrap at about 80 columns, so their sentences are + * matched against one long line, the way bench/site_facts.py reads them. */ +const flat = (text) => text.replace(/\s+/g, " "); +const readmeText = read(CLAIM); +const readme = flat(readmeText); +const install = flat(read(INSTALL)); /* The `case` block on `$os-$arch`, and nothing else in the script: a `case` on * something else must not be mistaken for this one. */ @@ -149,12 +158,12 @@ if (disagree.length > 0) { ); } -/* README's "Getting it" section, which is where a reader of the repository is - * told which tarball the installer picks. */ -const documented = /picks the\s+tarball for your machine,\s+([^,]*?),\s+and refuses/.exec(readme); +/* INSTALL.md's "Linux and macOS" section, which is where a reader of the + * repository is told which tarball the installer picks. */ +const documented = /picks the\s+tarball for your machine,\s+([^,]*?),\s+and refuses/.exec(install); if (!documented) { throw new Error( - `install-targets: ${CLAIM} no longer says which tarball install.sh picks ` + + `install-targets: ${INSTALL} no longer says which tarball install.sh picks ` + `for \`uname\`, so the target list has only install.sh stating it and ` + `this script has nothing to cross-check.`, ); @@ -166,29 +175,38 @@ const undocumented = [...accepted, ...listed].filter( if (undocumented.length > 0) { throw new Error( `install-targets: ${SOURCE} accepts [${[...accepted].join(", ")}] and ` + - `${CLAIM} says it picks [${[...listed].join(", ")}]. The repository ` + + `${INSTALL} says it picks [${[...listed].join(", ")}]. The repository ` + `states the set twice and the two have drifted, which is the drift this ` + `whole publication is arranged to refuse.`, ); } -/* The platform words, out of the parenthesis that introduces README's - * one-liner: "Install the latest release into `~/.local` (Linux x86-64, macOS - * arm64; a program built for macOS runs on macOS 14.4 or later, ...):", - * directly above the fenced command. The platforms are the list before the - * first semicolon; what follows it is a note about one of them, as 0.20.0 - * added. Anchored on that fence, so a paragraph about a platform this record - * does not carry - the Windows one-liner's, under it - cannot be read - * instead. */ -const prose = /\(([^()]*)\):\s*```sh\s+curl [^`]*?install\.sh \| sh/.exec(readme); -if (!prose) { +/* The platform words, out of the paragraph that introduces README's + * one-liner: "Linux x86-64, or macOS 14.4 or later on arm64, into + * `~/.local`:", directly above the fenced command. Anchored on that fence, so + * a paragraph about a platform this record does not carry - the Windows + * one-liner's, under it - cannot be read instead. */ +const lines = readmeText.replace(/\r\n/g, "\n").split("\n"); +const fence = lines.findIndex( + (line, i) => /^curl [^`]*install\.sh \| sh/.test(line) && /^```sh\s*$/.test(lines[i - 1] ?? ""), +); +let last = fence - 2; +while (last >= 0 && lines[last].trim() === "") last--; +let first = last; +while (first > 0 && lines[first - 1].trim() !== "" && !lines[first - 1].startsWith("#")) first--; +const sentence = fence < 0 || last < 0 ? "" : flat(lines.slice(first, last + 1).join(" ")).trim(); +if (!sentence.endsWith(":")) { throw new Error( - `install-targets: ${CLAIM} no longer states the platforms in the ` + - `parenthesis above the fenced ${SOURCE} one-liner, so there is no phrase in the ` + - `repository to take the prose name of each target from.`, + `install-targets: ${CLAIM} no longer introduces the fenced ${SOURCE} ` + + `one-liner with a sentence ending in a colon, so there is no statement ` + + `in the repository of which machines that command is for.`, ); } -const phrases = prose[1].split(";")[0].split(/\s*(?:,|and)\s*/).filter(Boolean); +const phrases = sentence + .replace(/:$/, "") + .split(/,\s*(?:(?:or|and)\s+)?/) + .map((phrase) => phrase.trim()) + .filter(Boolean); /** The words of a phrase, and the parts of any word that is itself hyphenated, * so `x86-64` is matched whole as well as by its halves. */ @@ -211,37 +229,62 @@ const score = (target, phrase) => { }).length; }; -/* One phrase per target, each phrase used once. A target with no single best - * phrase left is a failure naming the target, never a guess: a prose name - * attached to the wrong tarball is worse than none, because it would send a - * reader to a download for a machine they do not have. */ -if (phrases.length !== published.length) { +/* One phrase per target, naming both of its components, each phrase used + * once. A target with no such phrase is a failure naming the target, never a + * guess: the sentence above the command is what tells a reader which machine + * it is for. */ +const taken = new Set(); +for (const target of published) { + const naming = phrases.filter((phrase) => !taken.has(phrase) && score(target, phrase) === 2); + if (naming.length !== 1) { + throw new Error( + `install-targets: ${naming.length === 0 ? "no" : "more than one"} phrase ` + + `in ${CLAIM}'s "${sentence}" names ${target.target} (${target.os} ` + + `${target.arch}). Either the sentence was reworded or a spelling ` + + `belongs in ALIAS.`, + ); + } + taken.add(naming[0]); +} + +/* And no phrase there names a platform that is not one of the targets: a + * sentence offering the one-liner to a third machine would send that reader to + * a refusal. A phrase that names no component of any target is not a + * platform, such as where the install goes. */ +const stray = phrases.filter( + (phrase) => !taken.has(phrase) && published.some((target) => score(target, phrase) > 0), +); +if (stray.length > 0) { throw new Error( - `install-targets: ${CLAIM} names ${phrases.length} platform(s) ` + - `(${phrases.join(" / ")}) under its one-liner and ${SOURCE} ships ` + - `${published.length} tarball(s) (${[...accepted].join(", ")}). Every ` + - `published target needs the name a reader is shown.`, + `install-targets: ${CLAIM} offers the ${SOURCE} one-liner to ` + + `${stray.join(" / ")}, and ${SOURCE} ships ${[...accepted].join(", ")}. ` + + `Every platform the sentence names needs a published tarball.`, ); } -const taken = new Set(); -const targets = published.map((target) => { - const ranked = phrases - .filter((phrase) => !taken.has(phrase)) - .map((phrase) => ({ phrase, points: score(target, phrase) })) - .sort((a, b) => b.points - a.points); - const [best, next] = ranked; - if (best.points === 0 || (next && next.points === best.points)) { +/* The prose name of a target, in README.md's own words. The pattern is built + * from the target's own components and the aliases above, and what is + * recorded is the text README.md matched with, so the spelling a reader is + * shown is the repository's and never this file's. */ +const spell = (part) => ALIAS.get(part.toLowerCase()) ?? part; +const spoken = (os, arch, target) => { + const match = new RegExp( + `\\b${spell(os)}[ -]${spell(arch).replace(/[-_]/g, "[-_]")}\\b`, + "i", + ).exec(readme); + if (!match) { throw new Error( - `install-targets: no single platform name in ${CLAIM} matches ` + - `${target.target} (${target.os} ${target.arch}). Candidates: ` + - `${ranked.map((r) => `${r.phrase} = ${r.points}`).join("; ")}. Either ` + - `the sentence was reworded or a spelling belongs in ALIAS.`, + `install-targets: ${CLAIM} never names ${target} in prose, so there is ` + + `no sentence in the repository to take the name beside its archive ` + + `from.`, ); } - taken.add(best.phrase); - return { target: target.target, machine: best.phrase }; -}); + return match[0]; +}; +const targets = published.map((target) => ({ + target: target.target, + machine: spoken(target.os, target.arch, target.target), +})); /* What the installer asks the release for, and where. Kept as the shell wrote * them, `$version` and `$target` included, so the generated record holds the @@ -344,39 +387,23 @@ if (!refused.some((line) => line.includes(windowsTarget))) { ); } -/* And README.md's statement of it, under "Getting it", where the zip is +/* And INSTALL.md's statement of it, under "Windows", where the zip is * spelled with a version rather than a placeholder. The asset template is * turned into the pattern that file would have to match, so a renamed asset * fails here instead of becoming a 404 on the download page. */ const spelled = new RegExp( psAsset[1].replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\$Version", "\\d+\\.\\d+\\.\\d+"), ); -if (!spelled.test(readme)) { +if (!spelled.test(install)) { throw new Error( - `install-targets: ${CLAIM} no longer spells a file of the shape ` + + `install-targets: ${INSTALL} no longer spells a file of the shape ` + `"${psAsset[1]}", so ${WINDOWS} is the only statement of what a release ` + `publishes for Windows and this script has nothing to cross-check.`, ); } -/* The prose name, in README.md's own words. The pattern is built from the - * target's own components and the aliases above, and what is recorded is the - * text README.md matched with, so the spelling a reader is shown is the - * repository's and never this file's. */ -const spell = (part) => ALIAS.get(part) ?? part; const [windowsOs, windowsArch] = windowsTarget.split("-"); -const spoken = new RegExp( - `\\b${spell(windowsOs)}[ -]${spell(windowsArch).replace(/[-_]/g, "[-_]")}\\b`, - "i", -).exec(readme); -if (!spoken) { - throw new Error( - `install-targets: ${CLAIM} never names ${windowsTarget} in prose, so ` + - `there is no sentence in the repository to take the name beside the zip ` + - `from. The other targets take theirs from the sentence under the ` + - `one-liner; this one is named wherever the README names it.`, - ); -} +const windowsMachine = spoken(windowsOs, windowsArch, windowsTarget); const commit = execFileSync("git", ["-C", repo, "rev-parse", "--short", "HEAD"], { encoding: "utf8", @@ -386,7 +413,7 @@ const record = { provenance: { generator: "scripts/install-targets.mjs", source: `${SOURCE}, ${WINDOWS}`, - claim: CLAIM, + claim: `${CLAIM}, ${INSTALL}`, commit, }, repo: slug[1], @@ -401,7 +428,7 @@ const record = { windows: { source: WINDOWS, target: windowsTarget, - machine: spoken[0], + machine: windowsMachine, asset: psAsset[1], download: psBase[1].replace("$Repo", slug[1]), }, diff --git a/scripts/samples.mjs b/scripts/samples.mjs index ae062cd..d0a86c5 100755 --- a/scripts/samples.mjs +++ b/scripts/samples.mjs @@ -196,8 +196,13 @@ for (const [root, count] of [[breakRoot, "break"], [agentRoot, "agent"]]) { // README recordings -------------------------------------------------------- -const readmePath = resolve(repo, "README.md"); -const readmeLines = readFileSync(readmePath, "utf8").split("\n"); +// README.md, and INSTALL.md beside it: 0.20.1 moved installing by hand out of +// README.md and into its own file, and the install page quotes those blocks +// from where they now live. +const README = "README.md"; +const INSTALL = "INSTALL.md"; +const fileLines = (file) => readFileSync(resolve(repo, file), "utf8").split("\n"); +const readmeLines = fileLines(README); function fencedBlocks(lines) { const blocks = []; @@ -221,13 +226,12 @@ function fencedBlocks(lines) { return blocks; } -// Each entry is a claim that one block in README.md says this. The anchor is -// matched, never the line number, because a line number is a guess about a file -// nobody edits with the site in mind. +// Each entry is a claim that one block in README.md (or the `file` it names) +// says this. The anchor is matched, never the line number, because a line +// number is a guess about a file nobody edits with the site in mind. const WANTED = { first_run: { lang: "console", anchor: "$ iyi run main.iyi" }, the_rule: { lang: "console", anchor: "rm -r app" }, - library_deleted: { lang: "console", anchor: "rm -rf samples/iyi/kemal" }, non_exhaustive: { lang: "console", anchor: "case is not exhaustive" }, import_missing: { lang: "console", anchor: "is not imported here" }, orphan_rule: { lang: "console", anchor: "an impl must live in the module" }, @@ -239,28 +243,33 @@ const WANTED = { // run, with no prompt and no output, so the site sets them as commands and // does not draw a prompt the README never printed. The same on the Windows // side, where the fence says `powershell` and the two lines are the one - // install.ps1 runs and the hello it leaves behind. + // install.ps1 runs and the hello it leaves behind. The by-hand lines are + // INSTALL.md's, set the same way. install: { lang: "sh", anchor: "install.sh | sh" }, - install_manual: { lang: "sh", anchor: "tar -xzf iyi-" }, + install_manual: { lang: "sh", anchor: "tar -xzf iyi-", file: INSTALL }, install_windows: { lang: "powershell", anchor: "install.ps1 | iex" }, - install_manual_windows: { lang: "powershell", anchor: "Expand-Archive iyi-" }, + install_manual_windows: { lang: "powershell", anchor: "Expand-Archive iyi-", file: INSTALL }, }; -const blocks = fencedBlocks(readmeLines); +const blocksOf = new Map([ + [README, fencedBlocks(readmeLines)], + [INSTALL, fencedBlocks(fileLines(INSTALL))], +]); const readme = {}; for (const [id, want] of Object.entries(WANTED)) { - const hits = blocks.filter((b) => b.text.includes(want.anchor)); + const file = want.file ?? README; + const hits = blocksOf.get(file).filter((b) => b.text.includes(want.anchor)); if (hits.length === 0) { die( - `README.md has no fenced block containing "${want.anchor}", which the ` + + `${file} has no fenced block containing "${want.anchor}", which the ` + `lesson "${id}" quotes. Either the passage moved and the anchor needs ` + - `updating, or the lesson is claiming something the README no longer says.`, + `updating, or the lesson is claiming something ${file} no longer says.`, ); } if (hits.length > 1) { die( - `"${want.anchor}" matches ${hits.length} blocks in README.md ` + + `"${want.anchor}" matches ${hits.length} blocks in ${file} ` + `(lines ${hits.map((h) => h.from).join(", ")}), so "${id}" is ambiguous. ` + `Narrow the anchor.`, ); @@ -268,7 +277,7 @@ for (const [id, want] of Object.entries(WANTED)) { const [hit] = hits; if (hit.lang !== want.lang) { die( - `"${id}" expected a ${want.lang} block and README.md:${hit.from} is ` + + `"${id}" expected a ${want.lang} block and ${file}:${hit.from} is ` + `${hit.lang || "unlabelled"}. Output is quoted as output or not at all.`, ); } @@ -276,8 +285,8 @@ for (const [id, want] of Object.entries(WANTED)) { text: hit.text, from: hit.from, to: hit.to, - source: "README.md", - cite: `README.md, ${hit.to > hit.from ? `lines ${hit.from} to ${hit.to}` : `line ${hit.from}`}`, + source: file, + cite: `${file}, ${hit.to > hit.from ? `lines ${hit.from} to ${hit.to}` : `line ${hit.from}`}`, }; } diff --git a/src/content/learn/declarations-not-bodies.mdx b/src/content/learn/declarations-not-bodies.mdx index 4caaf22..5c49c81 100644 --- a/src/content/learn/declarations-not-bodies.mdx +++ b/src/content/learn/declarations-not-bodies.mdx @@ -19,7 +19,7 @@ bought: >- every push, so the boundary is a gate rather than a claim. sources: - file: README.md - note: the two console sequences, quoted with their lines on each block, and the paragraph under the first one + note: the console sequence, quoted with its lines on the block, the paragraph under it, and the sentence that does the same to the Kemal port - file: samples/iyi/app/greeter.iyi note: the library the two builds import, shown whole - file: samples/iyi/modules.iyi @@ -84,11 +84,11 @@ and no bodies at all. `greeting` is missing from it, because it never carried Two files is a demonstration. A web framework is a test. - - `samples/iyi/webapp.iyi` is a port of Kemal onto iyi's rules, and its router -lives in `samples/iyi/kemal`. That whole directory goes away between the two -builds, and the program still builds, links, runs, and prints the same output. +lives in `samples/iyi/kemal`. README.md takes it through the same three steps: +build with `--emit-iyimod mods`, `rm -rf samples/iyi/kemal`, and build again with +`--use-iyimod mods`. That whole directory goes away between the two builds, and +the program still builds, links, runs, and prints the same output. ## The roundtrip gate @@ -108,8 +108,8 @@ is to take the source away and see whether the build still produces a program that behaves. What that boundary saves in seconds belongs to a machine rather than to a -language, so this page will not quote you a number. README.md publishes them with -the machine attached and the command that measures them. +language, so this page will not quote you a number. BENCHMARKS.md publishes them +with the machine attached and the command that measures them. ## Acyclic imports diff --git a/src/lib/release.ts b/src/lib/release.ts index b4d554c..533285c 100644 --- a/src/lib/release.ts +++ b/src/lib/release.ts @@ -5,7 +5,7 @@ * recorded them. A version number and a target name are facts about the tree * like any other figure here, so neither is typed into a page: one generator * refuses to write a record whose changelog states no release, the other - * refuses one whose installers and README disagree about what a release + * refuses one whose installers and install docs disagree about what a release * ships. * * The site does not publish the changelog itself. So everything here that @@ -53,7 +53,7 @@ export function releaseUrl(release: Release = latestRelease()): string { * * Nothing here is written down twice. `scripts/install-targets.mjs` reads the * targets out of the installer's own `case` arms, cross-checks them against the - * refusal it prints and against README.md, and carries the `asset=` and + * refusal it prints and against README.md and INSTALL.md, and carries the `asset=` and * `base=` strings across with `$version` and `$target` still in them; this * fills those in from the recorded release. The pairs were a literal here * until then, which meant a third published tarball would have appeared diff --git a/src/pages/install.astro b/src/pages/install.astro index e4d3951..0744bbd 100644 --- a/src/pages/install.astro +++ b/src/pages/install.astro @@ -8,7 +8,7 @@ * reader's own first: `src/components/Platform.astro` is the switch and * `src/lib/platform.ts` is what it decides on. * - * Every command on this page is quoted out of README.md by + * Every command on this page is quoted out of README.md or INSTALL.md by * scripts/samples.mjs, and every file name and URL is built from what the two * installers themselves ask a release for, through * scripts/install-targets.mjs. A machine's name is README.md's own word for @@ -177,8 +177,8 @@ const tarballs = assets.map((asset) => asset.target).join(" or ");

Other platforms, or the current tree rather than a release: clone the repository and make. It needs a Crystal compiler to - bootstrap from and LLVM; the README's{" "} - Getting it{" "} + bootstrap from and LLVM; INSTALL.md's{" "} + From source{" "} section names the versions and every command.

github.com/iyilang/iyi → diff --git a/src/playground/engines/unavailable.ts b/src/playground/engines/unavailable.ts index df932c2..20605b0 100644 --- a/src/playground/engines/unavailable.ts +++ b/src/playground/engines/unavailable.ts @@ -34,9 +34,9 @@ import type { */ const MISSING: string[] = [ "no engine registered in src/playground/registry.ts", - "the compiler is a native program: building it needs LLVM 19 and a Crystal compiler to bootstrap from (README.md)", - "wasm32-wasi is a supported target, and one of the four that CI runs every build, under wasmtime (README.md)", - "but a wasm32-wasi module becomes a program only once wasi-libc's entry stub is linked in, and this fork prints cc --target=wasm32-wasi for that target rather than wasm-ld (README.md)", + "the compiler is a native program: building it needs LLVM and a Crystal compiler to bootstrap from (INSTALL.md)", + "wasm32-wasi is a supported target, and one of the five that CI runs every build, under wasmtime (INSTALL.md)", + "but a wasm32-wasi program is linked by a wasi toolchain's cc, not by a bare linker, and has no concurrency runtime (INSTALL.md)", "so a browser engine needs a link step and a wasi host in the page, not only a compiler", "and the page cannot be cross-origin isolated on GitHub Pages, so no SharedArrayBuffer, no wasm threads, no synchronous stdin (see src/playground/types.ts)", ];