diff --git a/internal/treesitter/parser_test.go b/internal/treesitter/parser_test.go index e436411..725edab 100644 --- a/internal/treesitter/parser_test.go +++ b/internal/treesitter/parser_test.go @@ -90,6 +90,17 @@ func TestParse(t *testing.T) { } }) + t.Run("valid typescript source", func(t *testing.T) { + src := []byte(`interface User { id: number; name: string; } const u: User = { id: 1, name: "Alice" };`) + ast, err := Parse(src, "main.ts") + if err != nil { + t.Fatalf("Parse(): unexpected error: %v", err) + } + if ast == nil { + t.Error("Parse(): expected non-nil AST root") + } + }) + t.Run("valid jsx source", func(t *testing.T) { src := []byte(`const App = () =>
Hello
;`) ast, err := Parse(src, "App.jsx") @@ -101,6 +112,17 @@ func TestParse(t *testing.T) { } }) + t.Run("valid tsx source", func(t *testing.T) { + src := []byte(`export const App = (): JSX.Element =>

Hello World

;`) + ast, err := Parse(src, "main.tsx") + if err != nil { + t.Fatalf("Parse(): unexpected error: %v", err) + } + if ast == nil { + t.Error("Parse(): expected non-nil AST root") + } + }) + t.Run("unknown extension", func(t *testing.T) { src := []byte(`some text`) ast, err := Parse(src, "main.xyz") diff --git a/internal/treesitter/rules_test.go b/internal/treesitter/rules_test.go index 693a7ac..84a50e2 100644 --- a/internal/treesitter/rules_test.go +++ b/internal/treesitter/rules_test.go @@ -59,6 +59,21 @@ func TestRulesAliased(t *testing.T) { "type", "interface", "namespace", "enum", "abstract", "readonly", }, }, + { + lang: "tsx", + operators: []string{ + "+", "-", "*", "/", "%", "**", + "==", "!=", "===", "!==", "<", "<=", ">", ">=", + "&&", "||", "??", + "=", "+=", "-=", "*=", "/=", "%=", "**=", + "&&=", "||=", "??=", + "&", "|", "^", "<<", ">>", ">>>", + "!", "~", "++", "--", + "=>", + "type", "interface", "namespace", "enum", "abstract", "readonly", + "/>", "": comparison_operator_literal + ">=": comparison_operator_literal + + # Logical operators + "&&": logical_operator_literal + "||": logical_operator_literal + "??": logical_operator_literal + + # Assignment operators + "=": assignment_operator_literal + "+=": assignment_operator_literal + "-=": assignment_operator_literal + "*=": assignment_operator_literal + "/=": assignment_operator_literal + "%=": assignment_operator_literal + "**=": assignment_operator_literal + "&&=": assignment_operator_literal + "||=": assignment_operator_literal + "??=": assignment_operator_literal + "&=": assignment_operator_literal + "|=": assignment_operator_literal + "^=": assignment_operator_literal + "<<=": assignment_operator_literal + ">>=": assignment_operator_literal + ">>>=": assignment_operator_literal + + # Arithmetic operators + "+": arithmetic_operator_literal + "-": arithmetic_operator_literal + "*": arithmetic_operator_literal + "/": arithmetic_operator_literal + "%": arithmetic_operator_literal + "**": arithmetic_operator_literal + + # Bitwise operators + "&": bitwise_operator_literal + "|": bitwise_operator_literal + "^": bitwise_operator_literal + "<<": bitwise_operator_literal + ">>": bitwise_operator_literal + ">>>": bitwise_operator_literal + + # Unary / Update operators + "!": unary_operator_literal + "~": bitwise_operator_literal + "++": update_operator_literal + "--": update_operator_literal + "=>": arrow + + # Keywords + "if": "condition_declaration" + "else": "else" + "for": "for" + "while": "while" + "do": "do" + "switch": "switch" + "case": "case" + "default": "default" + "try": "try" + "catch": "catch" + "finally": "finally" + "throw": "throw" + "return": "return" + "break": "break" + "continue": "continue" + "function": "function" + "class": "class" + "const": "const" + "let": "let" + "var": "var" + "type": "type" + "interface": "interface" + "namespace": "namespace" + "enum": "enum" + "abstract": "abstract" + "readonly": "readonly" + "export": "export" + "import": "import" + "from": "from" + "as": "as" + "async": "async" + "await": "await" + + # Punctuation + ".": dot + ":": colon + ",": comma + ";": semicolon + "?.": optional_chaining + + # JSX element tags + "/>": jsx_self_closing_tag_slash_gt + "": comparison_operator_literal + ">=": comparison_operator_literal + + # Logical operators + "&&": logical_operator_literal + "||": logical_operator_literal + "??": logical_operator_literal + + # Assignment operators + "=": assignment_operator_literal + "+=": assignment_operator_literal + "-=": assignment_operator_literal + "*=": assignment_operator_literal + "/=": assignment_operator_literal + "%=": assignment_operator_literal + "**=": assignment_operator_literal + "&&=": assignment_operator_literal + "||=": assignment_operator_literal + "??=": assignment_operator_literal + "&=": assignment_operator_literal + "|=": assignment_operator_literal + "^=": assignment_operator_literal + "<<=": assignment_operator_literal + ">>=": assignment_operator_literal + ">>>=": assignment_operator_literal + + # Arithmetic operators + "+": arithmetic_operator_literal + "-": arithmetic_operator_literal + "*": arithmetic_operator_literal + "/": arithmetic_operator_literal + "%": arithmetic_operator_literal + "**": arithmetic_operator_literal + + # Bitwise operators + "&": bitwise_operator_literal + "|": bitwise_operator_literal + "^": bitwise_operator_literal + "<<": bitwise_operator_literal + ">>": bitwise_operator_literal + ">>>": bitwise_operator_literal + + # Unary / Update operators + "!": unary_operator_literal + "~": bitwise_operator_literal + "++": update_operator_literal + "--": update_operator_literal + "=>": arrow + + # Keywords + "if": "condition_declaration" + "else": "else" + "for": "for" + "while": "while" + "do": "do" + "switch": "switch" + "case": "case" + "default": "default" + "try": "try" + "catch": "catch" + "finally": "finally" + "throw": "throw" + "return": "return" + "break": "break" + "continue": "continue" + "function": "function" + "class": "class" + "const": "const" + "let": "let" + "var": "var" + "type": "type" + "interface": "interface" + "namespace": "namespace" + "enum": "enum" + "abstract": "abstract" + "readonly": "readonly" + "export": "export" + "import": "import" + "from": "from" + "as": "as" + "async": "async" + "await": "await" + + # Punctuation + ".": dot + ":": colon + ",": comma + ";": semicolon + "?.": optional_chaining + +ignored: + - "\n" + - "escape_sequence" + +scaffolding: + - program + - statement_block + - formal_parameters + - arguments + - parenthesized_expression + - expression_statement + - function_declaration + - method_definition + - arrow_function + - class_declaration + - class_body + - interface_declaration + - interface_body + - enum_declaration + - enum_body + - type_alias_declaration + - if_statement + - for_statement + - for_in_statement + - switch_statement + - switch_body + - switch_case + - try_statement + - catch_clause + - import_statement + - export_statement + - object + - array + - type_arguments + - type_parameters diff --git a/tests/testdata/ts_react_router_filter_removed/expected.json b/tests/testdata/ts_react_router_filter_removed/expected.json new file mode 100644 index 0000000..ced44f3 --- /dev/null +++ b/tests/testdata/ts_react_router_filter_removed/expected.json @@ -0,0 +1,1220 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2, + 0 + ], + "type": "binary_expression", + "start_byte": 5560, + "end_byte": 5582 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2 + ], + "type": "binary_expression", + "start_byte": 5560, + "end_byte": 5608 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2, + 1 + ], + "type": "logical_operator_literal", + "label": "\u0026\u0026", + "start_byte": 5583, + "end_byte": 5585 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2 + ], + "type": "binary_expression", + "start_byte": 5560, + "end_byte": 5608 + }, + "position": 1 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2 + ], + "type": "call_expression", + "start_byte": 5560, + "end_byte": 5582 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0, + 2 + ], + "type": "binary_expression", + "start_byte": 5560, + "end_byte": 5608 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 5, + 4, + 1, + 3, + 0, + 0, + 1, + 0 + ], + "type": "arrow_function", + "start_byte": 5553, + "end_byte": 5582 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 5586, + "dest_end_byte": 5608 + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_filter_removed/metadata.json b/tests/testdata/ts_react_router_filter_removed/metadata.json new file mode 100644 index 0000000..a9a87de --- /dev/null +++ b/tests/testdata/ts_react_router_filter_removed/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/remix-run/react-router", + "parent": "db526f08df9e7678e1265269f782b3dec62454f1", + "commit": "966103936377bd9e51496f077496166ddc12627c", + "message": "Filter removed change files from pr check workflow", + "author": "Matt Brophy", + "date": "2026-06-29T11:36:15-04:00", + "language": "typescript", + "files_changed": 1, + "insertions": 0, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_filter_removed/new.ts b/tests/testdata/ts_react_router_filter_removed/new.ts new file mode 100644 index 0000000..0da0c9d --- /dev/null +++ b/tests/testdata/ts_react_router_filter_removed/new.ts @@ -0,0 +1,266 @@ +/** + * Runs a set of checks against a PR and applies the resulting actions. + * + * Two-phase to avoid running with write permissions on PRs from forks. + * See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + * + * check Inspects the PR via the GitHub API and writes a list of + * "actions" to pr-checks-result.json. Safe to run with a + * read-only token (Workflow A: 🔍 Check PR). + * + * actions Reads pr-checks-result.json and applies each action. Runs + * in Workflow B (PR (Actions)) under `workflow_run` with + * write permissions but never executes any PR code. + * + * Usage: + * node scripts/pr.ts check + * node scripts/pr.ts actions + * + * Environment (check): + * GITHUB_TOKEN - Required (read-only PR scope is enough). + * PR_NUMBER - Required. github.event.pull_request.number + * PR_BASE - Required. github.event.pull_request.base.ref + * EVENT_ACTION - Required. github.event.action (opened|synchronize|reopened|labeled) + * LABEL_NAME - Optional. github.event.label.name (set when EVENT_ACTION=labeled) + * + * Environment (actions): + * GITHUB_TOKEN - Required (issues:write + pull-requests:write). + */ +import * as fs from "node:fs"; +import * as util from "node:util"; + +import { + closePr, + createPrComment, + getPrComments, + getPrFiles, + removePrLabel, + updatePrComment, +} from "./utils/github.ts"; + +type Action = + | { type: "upsert-sticky-comment"; marker: string; body: string } + | { type: "create-comment"; body: string } + | { type: "remove-label"; label: string } + | { type: "close-pr" }; + +type CheckContext = { + prNumber: number; + baseBranch: string; + eventAction: string; + labelName: string; +}; + +type Check = (ctx: CheckContext) => Promise; + +const CHANGE_FILE_MARKER = ""; + +type ChangeFileSummary = { + type: string; + firstLine: string; +}; + +const CHANGE_FILE_FOUND_COMMENT = `${CHANGE_FILE_MARKER} +### ✅ Change File Found + +One or more [change files](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) found. +`; + +const CHANGE_FILE_MISSING_COMMENT = `${CHANGE_FILE_MARKER} +### ⚠️ No Change File Found + +This PR doesn't include a [change file](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) which is used for automated release notes. +If your change affects users, please add one (or more) change files and commit the generated file(s). + +\`\`\`sh +pnpm run changes:add +\`\`\` + +> This script requires Node 24+. If you are on a lower version, please [add a file manually](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) + +> Not every PR needs a change file — you can skip this step if the change is internal-only +> (tests, tooling, docs)`; + +const CLOSE_FEATURE_PR_COMMENT = `\ +To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now asking that all new features go through the [Proposal/RFC process](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#new-feature-process) and that we don't open PRs until a proposal has been accepted and advanced to Stage 1. + +If this feature doesn't have a Proposal, please [open one](https://github.com/remix-run/react-router/discussions/new?category=proposals) so we can evaluate/discuss the proposed feature. You can link to this PR as an example of a potential implementation and we can re-open it if the proposal advances. + +If this PR already has a Proposal but it has not yet been accepted, let's continue the discussion in the Proposal until it gets accepted and then we can look to open a PR. Feel free to link to this PR or to a branch in a forked repo to show what a potential implementation might look like. + +If you have any questions, you can always reach out on [Discord](https://remix.run/discord). Thanks again for providing feedback and helping us make React Router even better! +`; + +let { positionals } = util.parseArgs({ allowPositionals: true }); +let [mode, arg] = positionals; + +if (mode === "check") { + await runChecks(); +} else if (mode === "actions") { + if (!arg) usage(); + await runActions(arg); +} else { + usage(); +} + +// ---------- Checks ---------- + +async function runChecks() { + let prNumber = parseInt(requireEnv("PR_NUMBER"), 10); + if (isNaN(prNumber)) { + console.error("PR_NUMBER must be numeric"); + process.exit(1); + } + + let checks: Check[] = [changeFileCheck, featurePrCheck]; + + let ctx: CheckContext = { + prNumber, + baseBranch: requireEnv("PR_BASE"), + eventAction: requireEnv("EVENT_ACTION"), + labelName: process.env.LABEL_NAME ?? "", + }; + console.log("ctx:", ctx); + + let result: { prNumber: number; actions: Action[] } = { + prNumber, + actions: [], + }; + + for (let check of checks) { + result.actions.push(...(await check(ctx))); + } + + // Matches pr-checks.yml/pr-actions.yml workflow artifact name + let filename = "pr-checks-result.json"; + console.log(`Writing ${filename}:`, JSON.stringify(result)); + fs.writeFileSync(filename, JSON.stringify(result)); +} + +async function changeFileCheck(ctx: CheckContext): Promise { + if (ctx.baseBranch !== "main") return []; + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return []; + } + + let files = await getPrFiles(ctx.prNumber); + let regex = + /^packages\/[^/]+\/\.changes\/(major|minor|patch|unstable)\.[^/]+\.md$/; + let summaries: ChangeFileSummary[] = files + .filter((f) => f.status !== "removed" && regex.test(f.filename)) + .map((f) => { + let type = f.filename.match(regex)?.[1] ?? "unknown"; + let firstLine = + fs.readFileSync(f.filename, "utf8").split(/\r?\n/, 1)[0].trim() ?? + "_No first line found._"; + + return { + type, + firstLine, + }; + }); + + console.log(`changeFileCheck: found ${summaries.length} change files`); + + let body = CHANGE_FILE_MISSING_COMMENT; + + if (summaries.length > 0) { + body = [ + CHANGE_FILE_FOUND_COMMENT, + "| Type | Change |", + "| --- | --- |", + ...summaries.map( + (s) => `| \`${s.type}\` | ${s.firstLine.replaceAll("|", "\\|")} |`, + ), + ].join("\n"); + } + + return [ + { + type: "upsert-sticky-comment", + marker: CHANGE_FILE_MARKER, + body, + }, + ]; +} + +async function featurePrCheck(ctx: CheckContext): Promise { + if (ctx.eventAction !== "labeled") return []; + if (ctx.labelName !== "feature-request") return []; + + console.log(`featurePrCheck: closing PR ${ctx.prNumber}`); + return [ + { type: "create-comment", body: CLOSE_FEATURE_PR_COMMENT }, + { type: "remove-label", label: ctx.labelName }, + { type: "close-pr" }, + ]; +} + +// ---------- Action dispatch ---------- + +async function runActions(resultPath: string) { + let { prNumber, actions } = JSON.parse( + fs.readFileSync(resultPath, "utf8"), + ) as { prNumber: number; actions: Action[] }; + + if (actions.length === 0) { + console.log("No actions to apply"); + return; + } + + for (let action of actions) { + switch (action.type) { + case "upsert-sticky-comment": { + let comments = await getPrComments(prNumber); + let existing = comments.find( + (c) => + c.user?.login === "github-actions[bot]" && + c.body?.includes(action.marker), + ); + if (existing) { + console.log(`Updating sticky comment #${existing.id}`); + await updatePrComment(existing.id, action.body); + } else { + console.log("Creating sticky comment"); + await createPrComment(prNumber, action.body); + } + return; + } + case "create-comment": { + console.log("Creating comment"); + await createPrComment(prNumber, action.body); + return; + } + case "remove-label": { + console.log(`Removing label '${action.label}'`); + await removePrLabel(prNumber, action.label); + return; + } + case "close-pr": { + console.log(`Closing PR ${prNumber}`); + await closePr(prNumber); + return; + } + } + } +} + +// Utils + +function usage(): never { + console.error( + "Usage:\n" + + " node scripts/pr.ts check\n" + + " node scripts/pr.ts actions ", + ); + process.exit(1); +} + +function requireEnv(name: string): string { + let value = process.env[name]; + if (!value) { + console.error(`Missing required env var: ${name}`); + process.exit(1); + } + return value; +} diff --git a/tests/testdata/ts_react_router_filter_removed/old.ts b/tests/testdata/ts_react_router_filter_removed/old.ts new file mode 100644 index 0000000..7575e54 --- /dev/null +++ b/tests/testdata/ts_react_router_filter_removed/old.ts @@ -0,0 +1,266 @@ +/** + * Runs a set of checks against a PR and applies the resulting actions. + * + * Two-phase to avoid running with write permissions on PRs from forks. + * See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + * + * check Inspects the PR via the GitHub API and writes a list of + * "actions" to pr-checks-result.json. Safe to run with a + * read-only token (Workflow A: 🔍 Check PR). + * + * actions Reads pr-checks-result.json and applies each action. Runs + * in Workflow B (PR (Actions)) under `workflow_run` with + * write permissions but never executes any PR code. + * + * Usage: + * node scripts/pr.ts check + * node scripts/pr.ts actions + * + * Environment (check): + * GITHUB_TOKEN - Required (read-only PR scope is enough). + * PR_NUMBER - Required. github.event.pull_request.number + * PR_BASE - Required. github.event.pull_request.base.ref + * EVENT_ACTION - Required. github.event.action (opened|synchronize|reopened|labeled) + * LABEL_NAME - Optional. github.event.label.name (set when EVENT_ACTION=labeled) + * + * Environment (actions): + * GITHUB_TOKEN - Required (issues:write + pull-requests:write). + */ +import * as fs from "node:fs"; +import * as util from "node:util"; + +import { + closePr, + createPrComment, + getPrComments, + getPrFiles, + removePrLabel, + updatePrComment, +} from "./utils/github.ts"; + +type Action = + | { type: "upsert-sticky-comment"; marker: string; body: string } + | { type: "create-comment"; body: string } + | { type: "remove-label"; label: string } + | { type: "close-pr" }; + +type CheckContext = { + prNumber: number; + baseBranch: string; + eventAction: string; + labelName: string; +}; + +type Check = (ctx: CheckContext) => Promise; + +const CHANGE_FILE_MARKER = ""; + +type ChangeFileSummary = { + type: string; + firstLine: string; +}; + +const CHANGE_FILE_FOUND_COMMENT = `${CHANGE_FILE_MARKER} +### ✅ Change File Found + +One or more [change files](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) found. +`; + +const CHANGE_FILE_MISSING_COMMENT = `${CHANGE_FILE_MARKER} +### ⚠️ No Change File Found + +This PR doesn't include a [change file](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) which is used for automated release notes. +If your change affects users, please add one (or more) change files and commit the generated file(s). + +\`\`\`sh +pnpm run changes:add +\`\`\` + +> This script requires Node 24+. If you are on a lower version, please [add a file manually](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) + +> Not every PR needs a change file — you can skip this step if the change is internal-only +> (tests, tooling, docs)`; + +const CLOSE_FEATURE_PR_COMMENT = `\ +To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now asking that all new features go through the [Proposal/RFC process](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#new-feature-process) and that we don't open PRs until a proposal has been accepted and advanced to Stage 1. + +If this feature doesn't have a Proposal, please [open one](https://github.com/remix-run/react-router/discussions/new?category=proposals) so we can evaluate/discuss the proposed feature. You can link to this PR as an example of a potential implementation and we can re-open it if the proposal advances. + +If this PR already has a Proposal but it has not yet been accepted, let's continue the discussion in the Proposal until it gets accepted and then we can look to open a PR. Feel free to link to this PR or to a branch in a forked repo to show what a potential implementation might look like. + +If you have any questions, you can always reach out on [Discord](https://remix.run/discord). Thanks again for providing feedback and helping us make React Router even better! +`; + +let { positionals } = util.parseArgs({ allowPositionals: true }); +let [mode, arg] = positionals; + +if (mode === "check") { + await runChecks(); +} else if (mode === "actions") { + if (!arg) usage(); + await runActions(arg); +} else { + usage(); +} + +// ---------- Checks ---------- + +async function runChecks() { + let prNumber = parseInt(requireEnv("PR_NUMBER"), 10); + if (isNaN(prNumber)) { + console.error("PR_NUMBER must be numeric"); + process.exit(1); + } + + let checks: Check[] = [changeFileCheck, featurePrCheck]; + + let ctx: CheckContext = { + prNumber, + baseBranch: requireEnv("PR_BASE"), + eventAction: requireEnv("EVENT_ACTION"), + labelName: process.env.LABEL_NAME ?? "", + }; + console.log("ctx:", ctx); + + let result: { prNumber: number; actions: Action[] } = { + prNumber, + actions: [], + }; + + for (let check of checks) { + result.actions.push(...(await check(ctx))); + } + + // Matches pr-checks.yml/pr-actions.yml workflow artifact name + let filename = "pr-checks-result.json"; + console.log(`Writing ${filename}:`, JSON.stringify(result)); + fs.writeFileSync(filename, JSON.stringify(result)); +} + +async function changeFileCheck(ctx: CheckContext): Promise { + if (ctx.baseBranch !== "main") return []; + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return []; + } + + let files = await getPrFiles(ctx.prNumber); + let regex = + /^packages\/[^/]+\/\.changes\/(major|minor|patch|unstable)\.[^/]+\.md$/; + let summaries: ChangeFileSummary[] = files + .filter((f) => regex.test(f.filename)) + .map((f) => { + let type = f.filename.match(regex)?.[1] ?? "unknown"; + let firstLine = + fs.readFileSync(f.filename, "utf8").split(/\r?\n/, 1)[0].trim() ?? + "_No first line found._"; + + return { + type, + firstLine, + }; + }); + + console.log(`changeFileCheck: found ${summaries.length} change files`); + + let body = CHANGE_FILE_MISSING_COMMENT; + + if (summaries.length > 0) { + body = [ + CHANGE_FILE_FOUND_COMMENT, + "| Type | Change |", + "| --- | --- |", + ...summaries.map( + (s) => `| \`${s.type}\` | ${s.firstLine.replaceAll("|", "\\|")} |`, + ), + ].join("\n"); + } + + return [ + { + type: "upsert-sticky-comment", + marker: CHANGE_FILE_MARKER, + body, + }, + ]; +} + +async function featurePrCheck(ctx: CheckContext): Promise { + if (ctx.eventAction !== "labeled") return []; + if (ctx.labelName !== "feature-request") return []; + + console.log(`featurePrCheck: closing PR ${ctx.prNumber}`); + return [ + { type: "create-comment", body: CLOSE_FEATURE_PR_COMMENT }, + { type: "remove-label", label: ctx.labelName }, + { type: "close-pr" }, + ]; +} + +// ---------- Action dispatch ---------- + +async function runActions(resultPath: string) { + let { prNumber, actions } = JSON.parse( + fs.readFileSync(resultPath, "utf8"), + ) as { prNumber: number; actions: Action[] }; + + if (actions.length === 0) { + console.log("No actions to apply"); + return; + } + + for (let action of actions) { + switch (action.type) { + case "upsert-sticky-comment": { + let comments = await getPrComments(prNumber); + let existing = comments.find( + (c) => + c.user?.login === "github-actions[bot]" && + c.body?.includes(action.marker), + ); + if (existing) { + console.log(`Updating sticky comment #${existing.id}`); + await updatePrComment(existing.id, action.body); + } else { + console.log("Creating sticky comment"); + await createPrComment(prNumber, action.body); + } + return; + } + case "create-comment": { + console.log("Creating comment"); + await createPrComment(prNumber, action.body); + return; + } + case "remove-label": { + console.log(`Removing label '${action.label}'`); + await removePrLabel(prNumber, action.label); + return; + } + case "close-pr": { + console.log(`Closing PR ${prNumber}`); + await closePr(prNumber); + return; + } + } + } +} + +// Utils + +function usage(): never { + console.error( + "Usage:\n" + + " node scripts/pr.ts check\n" + + " node scripts/pr.ts actions ", + ); + process.exit(1); +} + +function requireEnv(name: string): string { + let value = process.env[name]; + if (!value) { + console.error(`Missing required env var: ${name}`); + process.exit(1); + } + return value; +} diff --git a/tests/testdata/ts_react_router_take_cla/expected.json b/tests/testdata/ts_react_router_take_cla/expected.json new file mode 100644 index 0000000..e4f1efa --- /dev/null +++ b/tests/testdata/ts_react_router_take_cla/expected.json @@ -0,0 +1,1724 @@ +{ + "version": "v1", + "actions": [ + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 23, + 5, + 6 + ], + "type": "comment", + "label": "// Dry runs to start so we can monitor the flow alongside the bot", + "start_byte": 7871, + "end_byte": 7936 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 23, + 5, + 7 + ], + "type": "lexical_declaration", + "start_byte": 7939, + "end_byte": 7958 + }, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 23, + 5, + 8 + ], + "type": "if_statement", + "start_byte": 7961, + "end_byte": 8283 + }, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": -1 + }, + { + "left_line": 226, + "right_line": -1 + }, + { + "left_line": 227, + "right_line": -1 + }, + { + "left_line": 228, + "right_line": -1 + }, + { + "left_line": 229, + "right_line": -1 + }, + { + "left_line": 230, + "right_line": -1 + }, + { + "left_line": 231, + "right_line": -1 + }, + { + "left_line": 232, + "right_line": -1 + }, + { + "left_line": 233, + "right_line": -1 + }, + { + "left_line": 234, + "right_line": -1 + }, + { + "left_line": 235, + "right_line": -1 + }, + { + "left_line": 236, + "right_line": -1 + }, + { + "left_line": 237, + "right_line": -1 + }, + { + "left_line": 238, + "right_line": -1 + }, + { + "left_line": 239, + "right_line": -1 + }, + { + "left_line": 240, + "right_line": 225 + }, + { + "left_line": 241, + "right_line": 226 + }, + { + "left_line": 242, + "right_line": 227 + }, + { + "left_line": 243, + "right_line": 228 + }, + { + "left_line": 244, + "right_line": 229 + }, + { + "left_line": 245, + "right_line": 230 + }, + { + "left_line": 246, + "right_line": 231 + }, + { + "left_line": 247, + "right_line": 232 + }, + { + "left_line": 248, + "right_line": 233 + }, + { + "left_line": 249, + "right_line": 234 + }, + { + "left_line": 250, + "right_line": 235 + }, + { + "left_line": 251, + "right_line": 236 + }, + { + "left_line": 252, + "right_line": 237 + }, + { + "left_line": 253, + "right_line": 238 + }, + { + "left_line": 254, + "right_line": 239 + }, + { + "left_line": 255, + "right_line": 240 + }, + { + "left_line": 256, + "right_line": 241 + }, + { + "left_line": 257, + "right_line": 242 + }, + { + "left_line": 258, + "right_line": 243 + }, + { + "left_line": 259, + "right_line": 244 + }, + { + "left_line": 260, + "right_line": 245 + }, + { + "left_line": 261, + "right_line": 246 + }, + { + "left_line": 262, + "right_line": 247 + }, + { + "left_line": 263, + "right_line": 248 + }, + { + "left_line": 264, + "right_line": 249 + }, + { + "left_line": 265, + "right_line": 250 + }, + { + "left_line": 266, + "right_line": 251 + }, + { + "left_line": 267, + "right_line": 252 + }, + { + "left_line": 268, + "right_line": 253 + }, + { + "left_line": 269, + "right_line": 254 + }, + { + "left_line": 270, + "right_line": 255 + }, + { + "left_line": 271, + "right_line": 256 + }, + { + "left_line": 272, + "right_line": 257 + }, + { + "left_line": 273, + "right_line": 258 + }, + { + "left_line": 274, + "right_line": 259 + }, + { + "left_line": 275, + "right_line": 260 + }, + { + "left_line": 276, + "right_line": 261 + }, + { + "left_line": 277, + "right_line": 262 + }, + { + "left_line": 278, + "right_line": 263 + }, + { + "left_line": 279, + "right_line": 264 + }, + { + "left_line": 280, + "right_line": 265 + }, + { + "left_line": 281, + "right_line": 266 + }, + { + "left_line": 282, + "right_line": 267 + }, + { + "left_line": 283, + "right_line": 268 + }, + { + "left_line": 284, + "right_line": 269 + }, + { + "left_line": 285, + "right_line": 270 + }, + { + "left_line": 286, + "right_line": 271 + }, + { + "left_line": 287, + "right_line": 272 + }, + { + "left_line": 288, + "right_line": 273 + }, + { + "left_line": 289, + "right_line": 274 + }, + { + "left_line": 290, + "right_line": 275 + }, + { + "left_line": 291, + "right_line": 276 + }, + { + "left_line": 292, + "right_line": 277 + }, + { + "left_line": 293, + "right_line": 278 + }, + { + "left_line": 294, + "right_line": 279 + }, + { + "left_line": 295, + "right_line": 280 + }, + { + "left_line": 296, + "right_line": 281 + }, + { + "left_line": 297, + "right_line": 282 + }, + { + "left_line": 298, + "right_line": 283 + }, + { + "left_line": 299, + "right_line": 284 + }, + { + "left_line": 300, + "right_line": 285 + }, + { + "left_line": 301, + "right_line": 286 + }, + { + "left_line": 302, + "right_line": 287 + }, + { + "left_line": 303, + "right_line": 288 + }, + { + "left_line": 304, + "right_line": 289 + }, + { + "left_line": 305, + "right_line": 290 + }, + { + "left_line": 306, + "right_line": 291 + }, + { + "left_line": 307, + "right_line": 292 + }, + { + "left_line": 308, + "right_line": 293 + }, + { + "left_line": 309, + "right_line": 294 + }, + { + "left_line": 310, + "right_line": 295 + }, + { + "left_line": 311, + "right_line": 296 + }, + { + "left_line": 312, + "right_line": 297 + }, + { + "left_line": 313, + "right_line": 298 + }, + { + "left_line": 314, + "right_line": 299 + }, + { + "left_line": 315, + "right_line": 300 + }, + { + "left_line": 316, + "right_line": 301 + }, + { + "left_line": 317, + "right_line": 302 + }, + { + "left_line": 318, + "right_line": 303 + }, + { + "left_line": 319, + "right_line": 304 + }, + { + "left_line": 320, + "right_line": 305 + }, + { + "left_line": 321, + "right_line": 306 + }, + { + "left_line": 322, + "right_line": 307 + }, + { + "left_line": 323, + "right_line": 308 + }, + { + "left_line": 324, + "right_line": 309 + }, + { + "left_line": 325, + "right_line": 310 + }, + { + "left_line": 326, + "right_line": 311 + }, + { + "left_line": 327, + "right_line": 312 + }, + { + "left_line": 328, + "right_line": 313 + }, + { + "left_line": 329, + "right_line": 314 + }, + { + "left_line": 330, + "right_line": 315 + }, + { + "left_line": 331, + "right_line": 316 + }, + { + "left_line": 332, + "right_line": 317 + }, + { + "left_line": 333, + "right_line": 318 + }, + { + "left_line": 334, + "right_line": 319 + }, + { + "left_line": 335, + "right_line": 320 + }, + { + "left_line": 336, + "right_line": 321 + }, + { + "left_line": 337, + "right_line": 322 + }, + { + "left_line": 338, + "right_line": 323 + }, + { + "left_line": 339, + "right_line": 324 + }, + { + "left_line": 340, + "right_line": 325 + }, + { + "left_line": 341, + "right_line": 326 + }, + { + "left_line": 342, + "right_line": 327 + }, + { + "left_line": 343, + "right_line": 328 + }, + { + "left_line": 344, + "right_line": 329 + }, + { + "left_line": 345, + "right_line": 330 + }, + { + "left_line": 346, + "right_line": 331 + }, + { + "left_line": 347, + "right_line": 332 + }, + { + "left_line": 348, + "right_line": 333 + }, + { + "left_line": 349, + "right_line": 334 + }, + { + "left_line": 350, + "right_line": 335 + }, + { + "left_line": 351, + "right_line": 336 + }, + { + "left_line": 352, + "right_line": 337 + }, + { + "left_line": 353, + "right_line": 338 + }, + { + "left_line": 354, + "right_line": 339 + }, + { + "left_line": 355, + "right_line": 340 + }, + { + "left_line": 356, + "right_line": 341 + }, + { + "left_line": 357, + "right_line": 342 + }, + { + "left_line": 358, + "right_line": 343 + }, + { + "left_line": 359, + "right_line": 344 + }, + { + "left_line": 360, + "right_line": 345 + }, + { + "left_line": 361, + "right_line": 346 + }, + { + "left_line": 362, + "right_line": 347 + }, + { + "left_line": 363, + "right_line": 348 + }, + { + "left_line": 364, + "right_line": 349 + }, + { + "left_line": 365, + "right_line": 350 + }, + { + "left_line": 366, + "right_line": 351 + }, + { + "left_line": 367, + "right_line": 352 + }, + { + "left_line": 368, + "right_line": 353 + }, + { + "left_line": 369, + "right_line": 354 + }, + { + "left_line": 370, + "right_line": 355 + }, + { + "left_line": 371, + "right_line": 356 + }, + { + "left_line": 372, + "right_line": 357 + }, + { + "left_line": 373, + "right_line": 358 + }, + { + "left_line": 374, + "right_line": 359 + }, + { + "left_line": 375, + "right_line": 360 + }, + { + "left_line": 376, + "right_line": 361 + }, + { + "left_line": 377, + "right_line": 362 + }, + { + "left_line": 378, + "right_line": 363 + }, + { + "left_line": 379, + "right_line": 364 + }, + { + "left_line": 380, + "right_line": 365 + }, + { + "left_line": 381, + "right_line": 366 + }, + { + "left_line": 382, + "right_line": 367 + }, + { + "left_line": 383, + "right_line": 368 + }, + { + "left_line": 384, + "right_line": 369 + }, + { + "left_line": 385, + "right_line": 370 + }, + { + "left_line": 386, + "right_line": 371 + }, + { + "left_line": 387, + "right_line": 372 + }, + { + "left_line": 388, + "right_line": 373 + }, + { + "left_line": 389, + "right_line": 374 + }, + { + "left_line": 390, + "right_line": 375 + }, + { + "left_line": 391, + "right_line": 376 + }, + { + "left_line": 392, + "right_line": 377 + }, + { + "left_line": 393, + "right_line": 378 + }, + { + "left_line": 394, + "right_line": 379 + }, + { + "left_line": 395, + "right_line": 380 + }, + { + "left_line": 396, + "right_line": 381 + }, + { + "left_line": 397, + "right_line": 382 + }, + { + "left_line": 398, + "right_line": 383 + }, + { + "left_line": 399, + "right_line": 384 + }, + { + "left_line": 400, + "right_line": 385 + }, + { + "left_line": 401, + "right_line": 386 + }, + { + "left_line": 402, + "right_line": 387 + }, + { + "left_line": 403, + "right_line": 388 + }, + { + "left_line": 404, + "right_line": 389 + }, + { + "left_line": 405, + "right_line": 390 + }, + { + "left_line": 406, + "right_line": 391 + }, + { + "left_line": 407, + "right_line": 392 + }, + { + "left_line": 408, + "right_line": 393 + }, + { + "left_line": 409, + "right_line": 394 + }, + { + "left_line": 410, + "right_line": 395 + }, + { + "left_line": 411, + "right_line": 396 + }, + { + "left_line": 412, + "right_line": 397 + }, + { + "left_line": 413, + "right_line": 398 + }, + { + "left_line": 414, + "right_line": 399 + }, + { + "left_line": 415, + "right_line": 400 + }, + { + "left_line": 416, + "right_line": 401 + }, + { + "left_line": 417, + "right_line": 402 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_take_cla/metadata.json b/tests/testdata/ts_react_router_take_cla/metadata.json new file mode 100644 index 0000000..227a944 --- /dev/null +++ b/tests/testdata/ts_react_router_take_cla/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/remix-run/react-router", + "parent": "70e2d2fe4c5aa246997ea7427f3a2f05ea8edb75", + "commit": "bd8f05c8a7c67a14ce56970ddc217bbc3a1e2228", + "message": "Take CLA check out of dry run mode", + "author": "Matt Brophy", + "date": "2026-07-02T10:15:10-04:00", + "language": "typescript", + "files_changed": 1, + "insertions": 0, + "deletions": 15 +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_take_cla/new.ts b/tests/testdata/ts_react_router_take_cla/new.ts new file mode 100644 index 0000000..5139084 --- /dev/null +++ b/tests/testdata/ts_react_router_take_cla/new.ts @@ -0,0 +1,402 @@ +/** + * Runs a set of checks against a PR and applies the resulting actions. + * + * Two-phase to avoid running with write permissions on PRs from forks. + * See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + * + * check Inspects the PR via the GitHub API and writes a list of + * "actions" to the given result file. Safe to run with a + * read-only token (Workflow A: 🔍 Check PR). + * + * actions Reads the given result file and applies each action. Runs + * in Workflow B (PR (Actions)) under `workflow_run` with + * write permissions but never executes any PR code. + * + * Usage: + * node scripts/pr.ts check + * node scripts/pr.ts actions + * + * Environment (check): + * GITHUB_TOKEN - Required (read-only PR scope is enough). + * PR_NUMBER - Required. github.event.pull_request.number + * PR_BASE - Required. github.event.pull_request.base.ref + * PR_AUTHOR - Required. github.event.pull_request.user.login + * PR_HEAD_OWNER - Required. github.event.pull_request.head.repo.owner.login + * PR_HEAD_REPO - Required. github.event.pull_request.head.repo.name + * PR_HEAD_REF - Required. github.event.pull_request.head.ref + * EVENT_ACTION - Required. github.event.action (opened|synchronize|reopened|labeled) + * LABEL_NAME - Optional. github.event.label.name (set when EVENT_ACTION=labeled) + * + * Environment (actions): + * GITHUB_TOKEN - Required (issues:write + pull-requests:write). + */ +import * as fs from "node:fs"; +import * as util from "node:util"; + +import { + addPrLabels, + closePr, + createPrComment, + getPrComments, + getPrFiles, + removePrLabel, + updatePrComment, +} from "./utils/github.ts"; + +type Action = + | { type: "upsert-sticky-comment"; marker: string; body: string } + | { type: "create-comment"; body: string } + | { type: "add-label"; label: string } + | { type: "remove-label"; label: string } + | { type: "close-pr" }; + +type CheckContext = { + prNumber: number; + baseBranch: string; + author: string; + headOwner: string; + headRepo: string; + headRef: string; + eventAction: string; + labelName: string; +}; + +type CheckResult = { + actions: Action[]; + failureMessage?: string; +}; + +type Check = (ctx: CheckContext) => Promise; + +const CHANGE_FILE_MARKER = ""; +const CLA_MARKER = ""; +const CLA_SIGNED_LABEL = "CLA Signed"; + +type ChangeFileSummary = { + type: string; + firstLine: string; +}; + +const CHANGE_FILE_FOUND_COMMENT = `${CHANGE_FILE_MARKER} +### ✅ Change File Found + +One or more [change files](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) found. +`; + +const CHANGE_FILE_MISSING_COMMENT = `${CHANGE_FILE_MARKER} +### ⚠️ No Change File Found + +This PR doesn't include a [change file](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) which is used for automated release notes. +If your change affects users, please add one (or more) change files and commit the generated file(s). + +\`\`\`sh +pnpm run changes:add +\`\`\` + +> This script requires Node 24+. If you are on a lower version, please [add a file manually](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) + +> Not every PR needs a change file — you can skip this step if the change is internal-only +> (tests, tooling, docs)`; + +const CLOSE_FEATURE_PR_COMMENT = `\ +To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now asking that all new features go through the [Proposal/RFC process](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#new-feature-process) and that we don't open PRs until a proposal has been accepted and advanced to Stage 1. + +If this feature doesn't have a Proposal, please [open one](https://github.com/remix-run/react-router/discussions/new?category=proposals) so we can evaluate/discuss the proposed feature. You can link to this PR as an example of a potential implementation and we can re-open it if the proposal advances. + +If this PR already has a Proposal but it has not yet been accepted, let's continue the discussion in the Proposal until it gets accepted and then we can look to open a PR. Feel free to link to this PR or to a branch in a forked repo to show what a potential implementation might look like. + +If you have any questions, you can always reach out on [Discord](https://remix.run/discord). Thanks again for providing feedback and helping us make React Router even better! +`; + +const CLA_SIGNED_COMMENT = `${CLA_MARKER} +### ✅ CLA Signed + +Thanks for signing the [Contributor License Agreement](https://github.com/remix-run/react-router/blob/main/CLA.md).`; + +function getClaMissingComment(ctx: CheckContext) { + return `${CLA_MARKER} +### ⚠️ CLA Signature Required + +Hi @${ctx.author}, thanks for contributing to React Router! + +Before we consider your pull request, we ask that you sign our **Contributor License Agreement** (CLA). We require this only once. + +You may [review the CLA](https://github.com/remix-run/react-router/blob/main/CLA.md) and sign it by [adding your GitHub username to contributors.yml](https://github.com/${ctx.headOwner}/${ctx.headRepo}/edit/${ctx.headRef}/contributors.yml). + +Once the CLA is signed, the \`${CLA_SIGNED_LABEL}\` label will be added to the pull request and the CI check will pass. + +Thanks! + +\\- The Remix team +`; +} + +let { positionals } = util.parseArgs({ allowPositionals: true }); +let [mode, filename] = positionals; + +if (!filename) { + usage(); +} + +if (mode === "check") { + await runChecks(); +} else if (mode === "actions") { + await runActions(); +} else { + usage(); +} + +// ---------- Checks ---------- + +async function runChecks() { + let prNumber = parseInt(requireEnv("PR_NUMBER"), 10); + if (isNaN(prNumber)) { + console.error("PR_NUMBER must be numeric"); + process.exit(1); + } + + let checks: Check[] = [claCheck, changeFileCheck, featurePrCheck]; + + let ctx: CheckContext = { + prNumber, + baseBranch: requireEnv("PR_BASE"), + author: requireEnv("PR_AUTHOR"), + headOwner: requireEnv("PR_HEAD_OWNER"), + headRepo: requireEnv("PR_HEAD_REPO"), + headRef: requireEnv("PR_HEAD_REF"), + eventAction: requireEnv("EVENT_ACTION"), + labelName: process.env.LABEL_NAME ?? "", + }; + console.log("ctx:", ctx); + + let result: { prNumber: number; actions: Action[] } = { + prNumber, + actions: [], + }; + let failures = ""; + + for (let check of checks) { + let checkResult = await check(ctx); + result.actions.push(...checkResult.actions); + failures += checkResult.failureMessage + ? ` - ${checkResult.failureMessage}\n` + : ""; + } + + console.log(`Checks result:`); + console.log( + JSON.stringify( + result, + (_, value) => + typeof value === "string" && value.length > 60 + ? value.substring(0, 60) + "..." + : value, + 2, + ), + ); + + console.log(`Writing ${filename}`); + fs.writeFileSync(filename, JSON.stringify(result)); + + if (failures.length > 0) { + console.error(`Failures:\n${failures}`); + process.exit(1); + } +} + +async function claCheck(ctx: CheckContext): Promise { + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return { actions: [] }; + } + + let author = ctx.author.toLowerCase(); + if (author === "dependabot[bot]") { + console.log(`claCheck: ignoring ${ctx.author}`); + return { actions: [] }; + } + + let contributors = fs + .readFileSync("contributors.yml", "utf8") + .split(/\r?\n/) + .map((line) => line.match(/^\s*-\s*(.+?)\s*$/)?.[1]?.toLowerCase()) + .filter((contributor): contributor is string => Boolean(contributor)); + let signedCla = contributors.includes(author); + console.log(`claCheck: ${ctx.author} signed CLA: ${signedCla}`); + + if (signedCla) { + return { + actions: [ + { type: "add-label", label: CLA_SIGNED_LABEL }, + { + type: "upsert-sticky-comment", + marker: CLA_MARKER, + body: CLA_SIGNED_COMMENT, + }, + ], + }; + } + + return { + actions: [ + { + type: "upsert-sticky-comment", + marker: CLA_MARKER, + body: getClaMissingComment(ctx), + }, + ], + failureMessage: `CLA check failed: ${ctx.author} is not listed in contributors.yml`, + }; +} + +async function changeFileCheck(ctx: CheckContext): Promise { + if (ctx.baseBranch !== "main") return { actions: [] }; + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return { actions: [] }; + } + + let files = await getPrFiles(ctx.prNumber); + let touchesPackageFiles = files.some((f) => + f.filename.startsWith("packages/"), + ); + let regex = + /^packages\/[^/]+\/\.changes\/(major|minor|patch|unstable)\.[^/]+\.md$/; + let summaries: ChangeFileSummary[] = files + .filter((f) => f.status !== "removed" && regex.test(f.filename)) + .map((f) => { + let type = f.filename.match(regex)?.[1] ?? "unknown"; + let firstLine = + fs.readFileSync(f.filename, "utf8").split(/\r?\n/, 1)[0].trim() ?? + "_No first line found._"; + + return { + type, + firstLine, + }; + }); + + console.log(`changeFileCheck: found ${summaries.length} change files`); + + if (summaries.length === 0 && !touchesPackageFiles) { + console.log("changeFileCheck: no package files changed"); + return { actions: [] }; + } + + let body = CHANGE_FILE_MISSING_COMMENT; + + if (summaries.length > 0) { + body = [ + CHANGE_FILE_FOUND_COMMENT, + "| Type | Change |", + "| --- | --- |", + ...summaries.map( + (s) => `| \`${s.type}\` | ${s.firstLine.replaceAll("|", "\\|")} |`, + ), + ].join("\n"); + } + + return { + actions: [ + { + type: "upsert-sticky-comment", + marker: CHANGE_FILE_MARKER, + body, + }, + ], + }; +} + +async function featurePrCheck(ctx: CheckContext): Promise { + if (ctx.eventAction !== "labeled") return { actions: [] }; + if (ctx.labelName !== "feature-request") return { actions: [] }; + + console.log(`featurePrCheck: closing PR ${ctx.prNumber}`); + return { + actions: [ + { type: "create-comment", body: CLOSE_FEATURE_PR_COMMENT }, + { type: "remove-label", label: ctx.labelName }, + { type: "close-pr" }, + ], + }; +} + +// ---------- Action dispatch ---------- + +async function runActions() { + if (!fs.existsSync(filename)) { + console.log(`No result file found at ${filename}; skipping actions`); + return; + } + + let { prNumber, actions } = JSON.parse(fs.readFileSync(filename, "utf8")) as { + prNumber: number; + actions: Action[]; + }; + + if (actions.length === 0) { + console.log("No actions to apply"); + return; + } + + console.log(actions); + + for (let action of actions) { + switch (action.type) { + case "upsert-sticky-comment": { + let comments = await getPrComments(prNumber); + let existing = comments.find( + (c) => + c.user?.login === "github-actions[bot]" && + c.body?.includes(action.marker), + ); + if (existing) { + console.log(`Updating sticky comment #${existing.id}`); + await updatePrComment(existing.id, action.body); + } else { + console.log("Creating sticky comment"); + await createPrComment(prNumber, action.body); + } + break; + } + case "create-comment": { + console.log("Creating comment"); + await createPrComment(prNumber, action.body); + break; + } + case "add-label": { + console.log(`Adding label '${action.label}'`); + await addPrLabels(prNumber, [action.label]); + break; + } + case "remove-label": { + console.log(`Removing label '${action.label}'`); + await removePrLabel(prNumber, action.label); + break; + } + case "close-pr": { + console.log(`Closing PR ${prNumber}`); + await closePr(prNumber); + break; + } + } + } +} + +// Utils + +function usage(): never { + console.error( + "Usage:\n" + + " node scripts/pr.ts check \n" + + " node scripts/pr.ts actions ", + ); + process.exit(1); +} + +function requireEnv(name: string): string { + let value = process.env[name]; + if (!value) { + console.error(`Missing required env var: ${name}`); + process.exit(1); + } + return value; +} diff --git a/tests/testdata/ts_react_router_take_cla/old.ts b/tests/testdata/ts_react_router_take_cla/old.ts new file mode 100644 index 0000000..baf7cb9 --- /dev/null +++ b/tests/testdata/ts_react_router_take_cla/old.ts @@ -0,0 +1,417 @@ +/** + * Runs a set of checks against a PR and applies the resulting actions. + * + * Two-phase to avoid running with write permissions on PRs from forks. + * See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + * + * check Inspects the PR via the GitHub API and writes a list of + * "actions" to the given result file. Safe to run with a + * read-only token (Workflow A: 🔍 Check PR). + * + * actions Reads the given result file and applies each action. Runs + * in Workflow B (PR (Actions)) under `workflow_run` with + * write permissions but never executes any PR code. + * + * Usage: + * node scripts/pr.ts check + * node scripts/pr.ts actions + * + * Environment (check): + * GITHUB_TOKEN - Required (read-only PR scope is enough). + * PR_NUMBER - Required. github.event.pull_request.number + * PR_BASE - Required. github.event.pull_request.base.ref + * PR_AUTHOR - Required. github.event.pull_request.user.login + * PR_HEAD_OWNER - Required. github.event.pull_request.head.repo.owner.login + * PR_HEAD_REPO - Required. github.event.pull_request.head.repo.name + * PR_HEAD_REF - Required. github.event.pull_request.head.ref + * EVENT_ACTION - Required. github.event.action (opened|synchronize|reopened|labeled) + * LABEL_NAME - Optional. github.event.label.name (set when EVENT_ACTION=labeled) + * + * Environment (actions): + * GITHUB_TOKEN - Required (issues:write + pull-requests:write). + */ +import * as fs from "node:fs"; +import * as util from "node:util"; + +import { + addPrLabels, + closePr, + createPrComment, + getPrComments, + getPrFiles, + removePrLabel, + updatePrComment, +} from "./utils/github.ts"; + +type Action = + | { type: "upsert-sticky-comment"; marker: string; body: string } + | { type: "create-comment"; body: string } + | { type: "add-label"; label: string } + | { type: "remove-label"; label: string } + | { type: "close-pr" }; + +type CheckContext = { + prNumber: number; + baseBranch: string; + author: string; + headOwner: string; + headRepo: string; + headRef: string; + eventAction: string; + labelName: string; +}; + +type CheckResult = { + actions: Action[]; + failureMessage?: string; +}; + +type Check = (ctx: CheckContext) => Promise; + +const CHANGE_FILE_MARKER = ""; +const CLA_MARKER = ""; +const CLA_SIGNED_LABEL = "CLA Signed"; + +type ChangeFileSummary = { + type: string; + firstLine: string; +}; + +const CHANGE_FILE_FOUND_COMMENT = `${CHANGE_FILE_MARKER} +### ✅ Change File Found + +One or more [change files](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) found. +`; + +const CHANGE_FILE_MISSING_COMMENT = `${CHANGE_FILE_MARKER} +### ⚠️ No Change File Found + +This PR doesn't include a [change file](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) which is used for automated release notes. +If your change affects users, please add one (or more) change files and commit the generated file(s). + +\`\`\`sh +pnpm run changes:add +\`\`\` + +> This script requires Node 24+. If you are on a lower version, please [add a file manually](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files) + +> Not every PR needs a change file — you can skip this step if the change is internal-only +> (tests, tooling, docs)`; + +const CLOSE_FEATURE_PR_COMMENT = `\ +To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now asking that all new features go through the [Proposal/RFC process](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#new-feature-process) and that we don't open PRs until a proposal has been accepted and advanced to Stage 1. + +If this feature doesn't have a Proposal, please [open one](https://github.com/remix-run/react-router/discussions/new?category=proposals) so we can evaluate/discuss the proposed feature. You can link to this PR as an example of a potential implementation and we can re-open it if the proposal advances. + +If this PR already has a Proposal but it has not yet been accepted, let's continue the discussion in the Proposal until it gets accepted and then we can look to open a PR. Feel free to link to this PR or to a branch in a forked repo to show what a potential implementation might look like. + +If you have any questions, you can always reach out on [Discord](https://remix.run/discord). Thanks again for providing feedback and helping us make React Router even better! +`; + +const CLA_SIGNED_COMMENT = `${CLA_MARKER} +### ✅ CLA Signed + +Thanks for signing the [Contributor License Agreement](https://github.com/remix-run/react-router/blob/main/CLA.md).`; + +function getClaMissingComment(ctx: CheckContext) { + return `${CLA_MARKER} +### ⚠️ CLA Signature Required + +Hi @${ctx.author}, thanks for contributing to React Router! + +Before we consider your pull request, we ask that you sign our **Contributor License Agreement** (CLA). We require this only once. + +You may [review the CLA](https://github.com/remix-run/react-router/blob/main/CLA.md) and sign it by [adding your GitHub username to contributors.yml](https://github.com/${ctx.headOwner}/${ctx.headRepo}/edit/${ctx.headRef}/contributors.yml). + +Once the CLA is signed, the \`${CLA_SIGNED_LABEL}\` label will be added to the pull request and the CI check will pass. + +Thanks! + +\\- The Remix team +`; +} + +let { positionals } = util.parseArgs({ allowPositionals: true }); +let [mode, filename] = positionals; + +if (!filename) { + usage(); +} + +if (mode === "check") { + await runChecks(); +} else if (mode === "actions") { + await runActions(); +} else { + usage(); +} + +// ---------- Checks ---------- + +async function runChecks() { + let prNumber = parseInt(requireEnv("PR_NUMBER"), 10); + if (isNaN(prNumber)) { + console.error("PR_NUMBER must be numeric"); + process.exit(1); + } + + let checks: Check[] = [claCheck, changeFileCheck, featurePrCheck]; + + let ctx: CheckContext = { + prNumber, + baseBranch: requireEnv("PR_BASE"), + author: requireEnv("PR_AUTHOR"), + headOwner: requireEnv("PR_HEAD_OWNER"), + headRepo: requireEnv("PR_HEAD_REPO"), + headRef: requireEnv("PR_HEAD_REF"), + eventAction: requireEnv("EVENT_ACTION"), + labelName: process.env.LABEL_NAME ?? "", + }; + console.log("ctx:", ctx); + + let result: { prNumber: number; actions: Action[] } = { + prNumber, + actions: [], + }; + let failures = ""; + + for (let check of checks) { + let checkResult = await check(ctx); + result.actions.push(...checkResult.actions); + failures += checkResult.failureMessage + ? ` - ${checkResult.failureMessage}\n` + : ""; + } + + console.log(`Checks result:`); + console.log( + JSON.stringify( + result, + (_, value) => + typeof value === "string" && value.length > 60 + ? value.substring(0, 60) + "..." + : value, + 2, + ), + ); + + console.log(`Writing ${filename}`); + fs.writeFileSync(filename, JSON.stringify(result)); + + if (failures.length > 0) { + console.error(`Failures:\n${failures}`); + process.exit(1); + } +} + +async function claCheck(ctx: CheckContext): Promise { + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return { actions: [] }; + } + + let author = ctx.author.toLowerCase(); + if (author === "dependabot[bot]") { + console.log(`claCheck: ignoring ${ctx.author}`); + return { actions: [] }; + } + + let contributors = fs + .readFileSync("contributors.yml", "utf8") + .split(/\r?\n/) + .map((line) => line.match(/^\s*-\s*(.+?)\s*$/)?.[1]?.toLowerCase()) + .filter((contributor): contributor is string => Boolean(contributor)); + let signedCla = contributors.includes(author); + console.log(`claCheck: ${ctx.author} signed CLA: ${signedCla}`); + + // Dry runs to start so we can monitor the flow alongside the bot + let DRY_RUN = true; + if (DRY_RUN) { + if (signedCla) { + console.log( + `claCheck: dry run; would add '${CLA_SIGNED_LABEL}' label and comment that the CLA is signed`, + ); + } else { + console.log( + `claCheck: dry run; would request CLA signature and fail PR checks`, + ); + } + return { actions: [] }; + } + + if (signedCla) { + return { + actions: [ + { type: "add-label", label: CLA_SIGNED_LABEL }, + { + type: "upsert-sticky-comment", + marker: CLA_MARKER, + body: CLA_SIGNED_COMMENT, + }, + ], + }; + } + + return { + actions: [ + { + type: "upsert-sticky-comment", + marker: CLA_MARKER, + body: getClaMissingComment(ctx), + }, + ], + failureMessage: `CLA check failed: ${ctx.author} is not listed in contributors.yml`, + }; +} + +async function changeFileCheck(ctx: CheckContext): Promise { + if (ctx.baseBranch !== "main") return { actions: [] }; + if (!["opened", "synchronize", "reopened"].includes(ctx.eventAction)) { + return { actions: [] }; + } + + let files = await getPrFiles(ctx.prNumber); + let touchesPackageFiles = files.some((f) => + f.filename.startsWith("packages/"), + ); + let regex = + /^packages\/[^/]+\/\.changes\/(major|minor|patch|unstable)\.[^/]+\.md$/; + let summaries: ChangeFileSummary[] = files + .filter((f) => f.status !== "removed" && regex.test(f.filename)) + .map((f) => { + let type = f.filename.match(regex)?.[1] ?? "unknown"; + let firstLine = + fs.readFileSync(f.filename, "utf8").split(/\r?\n/, 1)[0].trim() ?? + "_No first line found._"; + + return { + type, + firstLine, + }; + }); + + console.log(`changeFileCheck: found ${summaries.length} change files`); + + if (summaries.length === 0 && !touchesPackageFiles) { + console.log("changeFileCheck: no package files changed"); + return { actions: [] }; + } + + let body = CHANGE_FILE_MISSING_COMMENT; + + if (summaries.length > 0) { + body = [ + CHANGE_FILE_FOUND_COMMENT, + "| Type | Change |", + "| --- | --- |", + ...summaries.map( + (s) => `| \`${s.type}\` | ${s.firstLine.replaceAll("|", "\\|")} |`, + ), + ].join("\n"); + } + + return { + actions: [ + { + type: "upsert-sticky-comment", + marker: CHANGE_FILE_MARKER, + body, + }, + ], + }; +} + +async function featurePrCheck(ctx: CheckContext): Promise { + if (ctx.eventAction !== "labeled") return { actions: [] }; + if (ctx.labelName !== "feature-request") return { actions: [] }; + + console.log(`featurePrCheck: closing PR ${ctx.prNumber}`); + return { + actions: [ + { type: "create-comment", body: CLOSE_FEATURE_PR_COMMENT }, + { type: "remove-label", label: ctx.labelName }, + { type: "close-pr" }, + ], + }; +} + +// ---------- Action dispatch ---------- + +async function runActions() { + if (!fs.existsSync(filename)) { + console.log(`No result file found at ${filename}; skipping actions`); + return; + } + + let { prNumber, actions } = JSON.parse(fs.readFileSync(filename, "utf8")) as { + prNumber: number; + actions: Action[]; + }; + + if (actions.length === 0) { + console.log("No actions to apply"); + return; + } + + console.log(actions); + + for (let action of actions) { + switch (action.type) { + case "upsert-sticky-comment": { + let comments = await getPrComments(prNumber); + let existing = comments.find( + (c) => + c.user?.login === "github-actions[bot]" && + c.body?.includes(action.marker), + ); + if (existing) { + console.log(`Updating sticky comment #${existing.id}`); + await updatePrComment(existing.id, action.body); + } else { + console.log("Creating sticky comment"); + await createPrComment(prNumber, action.body); + } + break; + } + case "create-comment": { + console.log("Creating comment"); + await createPrComment(prNumber, action.body); + break; + } + case "add-label": { + console.log(`Adding label '${action.label}'`); + await addPrLabels(prNumber, [action.label]); + break; + } + case "remove-label": { + console.log(`Removing label '${action.label}'`); + await removePrLabel(prNumber, action.label); + break; + } + case "close-pr": { + console.log(`Closing PR ${prNumber}`); + await closePr(prNumber); + break; + } + } + } +} + +// Utils + +function usage(): never { + console.error( + "Usage:\n" + + " node scripts/pr.ts check \n" + + " node scripts/pr.ts actions ", + ); + process.exit(1); +} + +function requireEnv(name: string): string { + let value = process.env[name]; + if (!value) { + console.error(`Missing required env var: ${name}`); + process.exit(1); + } + return value; +} diff --git a/tests/testdata/ts_react_router_update_link/expected.json b/tests/testdata/ts_react_router_update_link/expected.json new file mode 100644 index 0000000..de74934 --- /dev/null +++ b/tests/testdata/ts_react_router_update_link/expected.json @@ -0,0 +1,5196 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 42, + 4, + 0, + 0, + 1, + 0, + 1, + 0, + 2 + ], + "type": "template_string", + "label": "` -\u003e https://reactrouter.com/7.16.0/upgrading/future#future${flag.toLowerCase()}`", + "start_byte": 22717, + "end_byte": 22802 + }, + "old_value": "` -\u003e https://reactrouter.com/7.16.0/upgrading/future#future${flag.toLowerCase()}`", + "new_value": "` -\u003e https://reactrouter.com/v7/upgrading/future#future${flag.toLowerCase()}`", + "dest_node": { + "tree": "after", + "path": [ + 42, + 4, + 0, + 0, + 1, + 0, + 1, + 0, + 2 + ], + "type": "template_string", + "label": "` -\u003e https://reactrouter.com/v7/upgrading/future#future${flag.toLowerCase()}`", + "start_byte": 22717, + "end_byte": 22798 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": 871 + }, + { + "left_line": 872, + "right_line": 872 + }, + { + "left_line": 873, + "right_line": 873 + }, + { + "left_line": 874, + "right_line": 874 + }, + { + "left_line": 875, + "right_line": 875 + }, + { + "left_line": 876, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": 894 + }, + { + "left_line": 895, + "right_line": 895 + }, + { + "left_line": 896, + "right_line": 896 + }, + { + "left_line": 897, + "right_line": 897 + }, + { + "left_line": 898, + "right_line": 898 + }, + { + "left_line": 899, + "right_line": 899 + }, + { + "left_line": 900, + "right_line": 900 + }, + { + "left_line": 901, + "right_line": 901 + }, + { + "left_line": 902, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": 905, + "right_line": 905 + }, + { + "left_line": 906, + "right_line": 906 + }, + { + "left_line": 907, + "right_line": 907 + }, + { + "left_line": 908, + "right_line": 908 + }, + { + "left_line": 909, + "right_line": 909 + }, + { + "left_line": 910, + "right_line": 910 + }, + { + "left_line": 911, + "right_line": 911 + }, + { + "left_line": 912, + "right_line": 912 + }, + { + "left_line": 913, + "right_line": 913 + }, + { + "left_line": 914, + "right_line": 914 + }, + { + "left_line": 915, + "right_line": 915 + }, + { + "left_line": 916, + "right_line": 916 + }, + { + "left_line": 917, + "right_line": 917 + }, + { + "left_line": 918, + "right_line": 918 + }, + { + "left_line": 919, + "right_line": 919 + }, + { + "left_line": 920, + "right_line": 920 + }, + { + "left_line": 921, + "right_line": 921 + }, + { + "left_line": 922, + "right_line": 922 + }, + { + "left_line": 923, + "right_line": 923 + }, + { + "left_line": 924, + "right_line": 924 + }, + { + "left_line": 925, + "right_line": 925 + }, + { + "left_line": 926, + "right_line": 926 + }, + { + "left_line": 927, + "right_line": 927 + }, + { + "left_line": 928, + "right_line": 928 + }, + { + "left_line": 929, + "right_line": 929 + }, + { + "left_line": 930, + "right_line": 930 + }, + { + "left_line": 931, + "right_line": 931 + }, + { + "left_line": 932, + "right_line": 932 + }, + { + "left_line": 933, + "right_line": 933 + }, + { + "left_line": 934, + "right_line": 934 + }, + { + "left_line": 935, + "right_line": 935 + }, + { + "left_line": 936, + "right_line": 936 + }, + { + "left_line": 937, + "right_line": 937 + }, + { + "left_line": 938, + "right_line": 938 + }, + { + "left_line": 939, + "right_line": 939 + }, + { + "left_line": 940, + "right_line": 940 + }, + { + "left_line": 941, + "right_line": 941 + }, + { + "left_line": 942, + "right_line": 942 + }, + { + "left_line": 943, + "right_line": 943 + }, + { + "left_line": 944, + "right_line": 944 + }, + { + "left_line": 945, + "right_line": 945 + }, + { + "left_line": 946, + "right_line": 946 + }, + { + "left_line": 947, + "right_line": 947 + }, + { + "left_line": 948, + "right_line": 948 + }, + { + "left_line": 949, + "right_line": 949 + }, + { + "left_line": 950, + "right_line": 950 + }, + { + "left_line": 951, + "right_line": 951 + }, + { + "left_line": 952, + "right_line": 952 + }, + { + "left_line": 953, + "right_line": 953 + }, + { + "left_line": 954, + "right_line": 954 + }, + { + "left_line": 955, + "right_line": 955 + }, + { + "left_line": 956, + "right_line": 956 + }, + { + "left_line": 957, + "right_line": 957 + }, + { + "left_line": 958, + "right_line": 958 + }, + { + "left_line": 959, + "right_line": 959 + }, + { + "left_line": 960, + "right_line": 960 + }, + { + "left_line": 961, + "right_line": 961 + }, + { + "left_line": 962, + "right_line": 962 + }, + { + "left_line": 963, + "right_line": 963 + }, + { + "left_line": 964, + "right_line": 964 + }, + { + "left_line": 965, + "right_line": 965 + }, + { + "left_line": 966, + "right_line": 966 + }, + { + "left_line": 967, + "right_line": 967 + }, + { + "left_line": 968, + "right_line": 968 + }, + { + "left_line": 969, + "right_line": 969 + }, + { + "left_line": 970, + "right_line": 970 + }, + { + "left_line": 971, + "right_line": 971 + }, + { + "left_line": 972, + "right_line": 972 + }, + { + "left_line": 973, + "right_line": 973 + }, + { + "left_line": 974, + "right_line": 974 + }, + { + "left_line": 975, + "right_line": 975 + }, + { + "left_line": 976, + "right_line": 976 + }, + { + "left_line": 977, + "right_line": 977 + }, + { + "left_line": 978, + "right_line": 978 + }, + { + "left_line": 979, + "right_line": 979 + }, + { + "left_line": 980, + "right_line": 980 + }, + { + "left_line": 981, + "right_line": 981 + }, + { + "left_line": 982, + "right_line": 982 + }, + { + "left_line": 983, + "right_line": 983 + }, + { + "left_line": 984, + "right_line": 984 + }, + { + "left_line": 985, + "right_line": 985 + }, + { + "left_line": 986, + "right_line": 986 + }, + { + "left_line": 987, + "right_line": 987 + }, + { + "left_line": 988, + "right_line": 988 + }, + { + "left_line": 989, + "right_line": 989 + }, + { + "left_line": 990, + "right_line": 990 + }, + { + "left_line": 991, + "right_line": 991 + }, + { + "left_line": 992, + "right_line": 992 + }, + { + "left_line": 993, + "right_line": 993 + }, + { + "left_line": 994, + "right_line": 994 + }, + { + "left_line": 995, + "right_line": 995 + }, + { + "left_line": 996, + "right_line": 996 + }, + { + "left_line": 997, + "right_line": 997 + }, + { + "left_line": 998, + "right_line": 998 + }, + { + "left_line": 999, + "right_line": 999 + }, + { + "left_line": 1000, + "right_line": 1000 + }, + { + "left_line": 1001, + "right_line": 1001 + }, + { + "left_line": 1002, + "right_line": 1002 + }, + { + "left_line": 1003, + "right_line": 1003 + }, + { + "left_line": 1004, + "right_line": 1004 + }, + { + "left_line": 1005, + "right_line": 1005 + }, + { + "left_line": 1006, + "right_line": 1006 + }, + { + "left_line": 1007, + "right_line": 1007 + }, + { + "left_line": 1008, + "right_line": 1008 + }, + { + "left_line": 1009, + "right_line": 1009 + }, + { + "left_line": 1010, + "right_line": 1010 + }, + { + "left_line": 1011, + "right_line": 1011 + }, + { + "left_line": 1012, + "right_line": 1012 + }, + { + "left_line": 1013, + "right_line": 1013 + }, + { + "left_line": 1014, + "right_line": 1014 + }, + { + "left_line": 1015, + "right_line": 1015 + }, + { + "left_line": 1016, + "right_line": 1016 + }, + { + "left_line": 1017, + "right_line": 1017 + }, + { + "left_line": 1018, + "right_line": 1018 + }, + { + "left_line": 1019, + "right_line": 1019 + }, + { + "left_line": 1020, + "right_line": 1020 + }, + { + "left_line": 1021, + "right_line": 1021 + }, + { + "left_line": 1022, + "right_line": 1022 + }, + { + "left_line": 1023, + "right_line": 1023 + }, + { + "left_line": 1024, + "right_line": 1024 + }, + { + "left_line": 1025, + "right_line": 1025 + }, + { + "left_line": 1026, + "right_line": 1026 + }, + { + "left_line": 1027, + "right_line": 1027 + }, + { + "left_line": 1028, + "right_line": 1028 + }, + { + "left_line": 1029, + "right_line": 1029 + }, + { + "left_line": 1030, + "right_line": 1030 + }, + { + "left_line": 1031, + "right_line": 1031 + }, + { + "left_line": 1032, + "right_line": 1032 + }, + { + "left_line": 1033, + "right_line": 1033 + }, + { + "left_line": 1034, + "right_line": 1034 + }, + { + "left_line": 1035, + "right_line": 1035 + }, + { + "left_line": 1036, + "right_line": 1036 + }, + { + "left_line": 1037, + "right_line": 1037 + }, + { + "left_line": 1038, + "right_line": 1038 + }, + { + "left_line": 1039, + "right_line": 1039 + }, + { + "left_line": 1040, + "right_line": 1040 + }, + { + "left_line": 1041, + "right_line": 1041 + }, + { + "left_line": 1042, + "right_line": 1042 + }, + { + "left_line": 1043, + "right_line": 1043 + }, + { + "left_line": 1044, + "right_line": 1044 + }, + { + "left_line": 1045, + "right_line": 1045 + }, + { + "left_line": 1046, + "right_line": 1046 + }, + { + "left_line": 1047, + "right_line": 1047 + }, + { + "left_line": 1048, + "right_line": 1048 + }, + { + "left_line": 1049, + "right_line": 1049 + }, + { + "left_line": 1050, + "right_line": 1050 + }, + { + "left_line": 1051, + "right_line": 1051 + }, + { + "left_line": 1052, + "right_line": 1052 + }, + { + "left_line": 1053, + "right_line": 1053 + }, + { + "left_line": 1054, + "right_line": 1054 + }, + { + "left_line": 1055, + "right_line": 1055 + }, + { + "left_line": 1056, + "right_line": 1056 + }, + { + "left_line": 1057, + "right_line": 1057 + }, + { + "left_line": 1058, + "right_line": 1058 + }, + { + "left_line": 1059, + "right_line": 1059 + }, + { + "left_line": 1060, + "right_line": 1060 + }, + { + "left_line": 1061, + "right_line": 1061 + }, + { + "left_line": 1062, + "right_line": 1062 + }, + { + "left_line": 1063, + "right_line": 1063 + }, + { + "left_line": 1064, + "right_line": 1064 + }, + { + "left_line": 1065, + "right_line": 1065 + }, + { + "left_line": 1066, + "right_line": 1066 + }, + { + "left_line": 1067, + "right_line": 1067 + }, + { + "left_line": 1068, + "right_line": 1068 + }, + { + "left_line": 1069, + "right_line": 1069 + }, + { + "left_line": 1070, + "right_line": 1070 + }, + { + "left_line": 1071, + "right_line": 1071 + }, + { + "left_line": 1072, + "right_line": 1072 + }, + { + "left_line": 1073, + "right_line": 1073 + }, + { + "left_line": 1074, + "right_line": 1074 + }, + { + "left_line": 1075, + "right_line": 1075 + }, + { + "left_line": 1076, + "right_line": 1076 + }, + { + "left_line": 1077, + "right_line": 1077 + }, + { + "left_line": 1078, + "right_line": 1078 + }, + { + "left_line": 1079, + "right_line": 1079 + }, + { + "left_line": 1080, + "right_line": 1080 + }, + { + "left_line": 1081, + "right_line": 1081 + }, + { + "left_line": 1082, + "right_line": 1082 + }, + { + "left_line": 1083, + "right_line": 1083 + }, + { + "left_line": 1084, + "right_line": 1084 + }, + { + "left_line": 1085, + "right_line": 1085 + }, + { + "left_line": 1086, + "right_line": 1086 + }, + { + "left_line": 1087, + "right_line": 1087 + }, + { + "left_line": 1088, + "right_line": 1088 + }, + { + "left_line": 1089, + "right_line": 1089 + }, + { + "left_line": 1090, + "right_line": 1090 + }, + { + "left_line": 1091, + "right_line": 1091 + }, + { + "left_line": 1092, + "right_line": 1092 + }, + { + "left_line": 1093, + "right_line": 1093 + }, + { + "left_line": 1094, + "right_line": 1094 + }, + { + "left_line": 1095, + "right_line": 1095 + }, + { + "left_line": 1096, + "right_line": 1096 + }, + { + "left_line": 1097, + "right_line": 1097 + }, + { + "left_line": 1098, + "right_line": 1098 + }, + { + "left_line": 1099, + "right_line": 1099 + }, + { + "left_line": 1100, + "right_line": 1100 + }, + { + "left_line": 1101, + "right_line": 1101 + }, + { + "left_line": 1102, + "right_line": 1102 + }, + { + "left_line": 1103, + "right_line": 1103 + }, + { + "left_line": 1104, + "right_line": 1104 + }, + { + "left_line": 1105, + "right_line": 1105 + }, + { + "left_line": 1106, + "right_line": 1106 + }, + { + "left_line": 1107, + "right_line": 1107 + }, + { + "left_line": 1108, + "right_line": 1108 + }, + { + "left_line": 1109, + "right_line": 1109 + }, + { + "left_line": 1110, + "right_line": 1110 + }, + { + "left_line": 1111, + "right_line": 1111 + }, + { + "left_line": 1112, + "right_line": 1112 + }, + { + "left_line": 1113, + "right_line": 1113 + }, + { + "left_line": 1114, + "right_line": 1114 + }, + { + "left_line": 1115, + "right_line": 1115 + }, + { + "left_line": 1116, + "right_line": 1116 + }, + { + "left_line": 1117, + "right_line": 1117 + }, + { + "left_line": 1118, + "right_line": 1118 + }, + { + "left_line": 1119, + "right_line": 1119 + }, + { + "left_line": 1120, + "right_line": 1120 + }, + { + "left_line": 1121, + "right_line": 1121 + }, + { + "left_line": 1122, + "right_line": 1122 + }, + { + "left_line": 1123, + "right_line": 1123 + }, + { + "left_line": 1124, + "right_line": 1124 + }, + { + "left_line": 1125, + "right_line": 1125 + }, + { + "left_line": 1126, + "right_line": 1126 + }, + { + "left_line": 1127, + "right_line": 1127 + }, + { + "left_line": 1128, + "right_line": 1128 + }, + { + "left_line": 1129, + "right_line": 1129 + }, + { + "left_line": 1130, + "right_line": 1130 + }, + { + "left_line": 1131, + "right_line": 1131 + }, + { + "left_line": 1132, + "right_line": 1132 + }, + { + "left_line": 1133, + "right_line": 1133 + }, + { + "left_line": 1134, + "right_line": 1134 + }, + { + "left_line": 1135, + "right_line": 1135 + }, + { + "left_line": 1136, + "right_line": 1136 + }, + { + "left_line": 1137, + "right_line": 1137 + }, + { + "left_line": 1138, + "right_line": 1138 + }, + { + "left_line": 1139, + "right_line": 1139 + }, + { + "left_line": 1140, + "right_line": 1140 + }, + { + "left_line": 1141, + "right_line": 1141 + }, + { + "left_line": 1142, + "right_line": 1142 + }, + { + "left_line": 1143, + "right_line": 1143 + }, + { + "left_line": 1144, + "right_line": 1144 + }, + { + "left_line": 1145, + "right_line": 1145 + }, + { + "left_line": 1146, + "right_line": 1146 + }, + { + "left_line": 1147, + "right_line": 1147 + }, + { + "left_line": 1148, + "right_line": 1148 + }, + { + "left_line": 1149, + "right_line": 1149 + }, + { + "left_line": 1150, + "right_line": 1150 + }, + { + "left_line": 1151, + "right_line": 1151 + }, + { + "left_line": 1152, + "right_line": 1152 + }, + { + "left_line": 1153, + "right_line": 1153 + }, + { + "left_line": 1154, + "right_line": 1154 + }, + { + "left_line": 1155, + "right_line": 1155 + }, + { + "left_line": 1156, + "right_line": 1156 + }, + { + "left_line": 1157, + "right_line": 1157 + }, + { + "left_line": 1158, + "right_line": 1158 + }, + { + "left_line": 1159, + "right_line": 1159 + }, + { + "left_line": 1160, + "right_line": 1160 + }, + { + "left_line": 1161, + "right_line": 1161 + }, + { + "left_line": 1162, + "right_line": 1162 + }, + { + "left_line": 1163, + "right_line": 1163 + }, + { + "left_line": 1164, + "right_line": 1164 + }, + { + "left_line": 1165, + "right_line": 1165 + }, + { + "left_line": 1166, + "right_line": 1166 + }, + { + "left_line": 1167, + "right_line": 1167 + }, + { + "left_line": 1168, + "right_line": 1168 + }, + { + "left_line": 1169, + "right_line": 1169 + }, + { + "left_line": 1170, + "right_line": 1170 + }, + { + "left_line": 1171, + "right_line": 1171 + }, + { + "left_line": 1172, + "right_line": 1172 + }, + { + "left_line": 1173, + "right_line": 1173 + }, + { + "left_line": 1174, + "right_line": 1174 + }, + { + "left_line": 1175, + "right_line": 1175 + }, + { + "left_line": 1176, + "right_line": 1176 + }, + { + "left_line": 1177, + "right_line": 1177 + }, + { + "left_line": 1178, + "right_line": 1178 + }, + { + "left_line": 1179, + "right_line": 1179 + }, + { + "left_line": 1180, + "right_line": 1180 + }, + { + "left_line": 1181, + "right_line": 1181 + }, + { + "left_line": 1182, + "right_line": 1182 + }, + { + "left_line": 1183, + "right_line": 1183 + }, + { + "left_line": 1184, + "right_line": 1184 + }, + { + "left_line": 1185, + "right_line": 1185 + }, + { + "left_line": 1186, + "right_line": 1186 + }, + { + "left_line": 1187, + "right_line": 1187 + }, + { + "left_line": 1188, + "right_line": 1188 + }, + { + "left_line": 1189, + "right_line": 1189 + }, + { + "left_line": 1190, + "right_line": 1190 + }, + { + "left_line": 1191, + "right_line": 1191 + }, + { + "left_line": 1192, + "right_line": 1192 + }, + { + "left_line": 1193, + "right_line": 1193 + }, + { + "left_line": 1194, + "right_line": 1194 + }, + { + "left_line": 1195, + "right_line": 1195 + }, + { + "left_line": 1196, + "right_line": 1196 + }, + { + "left_line": 1197, + "right_line": 1197 + }, + { + "left_line": 1198, + "right_line": 1198 + }, + { + "left_line": 1199, + "right_line": 1199 + }, + { + "left_line": 1200, + "right_line": 1200 + }, + { + "left_line": 1201, + "right_line": 1201 + }, + { + "left_line": 1202, + "right_line": 1202 + }, + { + "left_line": 1203, + "right_line": 1203 + }, + { + "left_line": 1204, + "right_line": 1204 + }, + { + "left_line": 1205, + "right_line": 1205 + }, + { + "left_line": 1206, + "right_line": 1206 + }, + { + "left_line": 1207, + "right_line": 1207 + }, + { + "left_line": 1208, + "right_line": 1208 + }, + { + "left_line": 1209, + "right_line": 1209 + }, + { + "left_line": 1210, + "right_line": 1210 + }, + { + "left_line": 1211, + "right_line": 1211 + }, + { + "left_line": 1212, + "right_line": 1212 + }, + { + "left_line": 1213, + "right_line": 1213 + }, + { + "left_line": 1214, + "right_line": 1214 + }, + { + "left_line": 1215, + "right_line": 1215 + }, + { + "left_line": 1216, + "right_line": 1216 + }, + { + "left_line": 1217, + "right_line": 1217 + }, + { + "left_line": 1218, + "right_line": 1218 + }, + { + "left_line": 1219, + "right_line": 1219 + }, + { + "left_line": 1220, + "right_line": 1220 + }, + { + "left_line": 1221, + "right_line": 1221 + }, + { + "left_line": 1222, + "right_line": 1222 + }, + { + "left_line": 1223, + "right_line": 1223 + }, + { + "left_line": 1224, + "right_line": 1224 + }, + { + "left_line": 1225, + "right_line": 1225 + }, + { + "left_line": 1226, + "right_line": 1226 + }, + { + "left_line": 1227, + "right_line": 1227 + }, + { + "left_line": 1228, + "right_line": 1228 + }, + { + "left_line": 1229, + "right_line": 1229 + }, + { + "left_line": 1230, + "right_line": 1230 + }, + { + "left_line": 1231, + "right_line": 1231 + }, + { + "left_line": 1232, + "right_line": 1232 + }, + { + "left_line": 1233, + "right_line": 1233 + }, + { + "left_line": 1234, + "right_line": 1234 + }, + { + "left_line": 1235, + "right_line": 1235 + }, + { + "left_line": 1236, + "right_line": 1236 + }, + { + "left_line": 1237, + "right_line": 1237 + }, + { + "left_line": 1238, + "right_line": 1238 + }, + { + "left_line": 1239, + "right_line": 1239 + }, + { + "left_line": 1240, + "right_line": 1240 + }, + { + "left_line": 1241, + "right_line": 1241 + }, + { + "left_line": 1242, + "right_line": 1242 + }, + { + "left_line": 1243, + "right_line": 1243 + }, + { + "left_line": 1244, + "right_line": 1244 + }, + { + "left_line": 1245, + "right_line": 1245 + }, + { + "left_line": 1246, + "right_line": 1246 + }, + { + "left_line": 1247, + "right_line": 1247 + }, + { + "left_line": 1248, + "right_line": 1248 + }, + { + "left_line": 1249, + "right_line": 1249 + }, + { + "left_line": 1250, + "right_line": 1250 + }, + { + "left_line": 1251, + "right_line": 1251 + }, + { + "left_line": 1252, + "right_line": 1252 + }, + { + "left_line": 1253, + "right_line": 1253 + }, + { + "left_line": 1254, + "right_line": 1254 + }, + { + "left_line": 1255, + "right_line": 1255 + }, + { + "left_line": 1256, + "right_line": 1256 + }, + { + "left_line": 1257, + "right_line": 1257 + }, + { + "left_line": 1258, + "right_line": 1258 + }, + { + "left_line": 1259, + "right_line": 1259 + }, + { + "left_line": 1260, + "right_line": 1260 + }, + { + "left_line": 1261, + "right_line": 1261 + }, + { + "left_line": 1262, + "right_line": 1262 + }, + { + "left_line": 1263, + "right_line": 1263 + }, + { + "left_line": 1264, + "right_line": 1264 + }, + { + "left_line": 1265, + "right_line": 1265 + }, + { + "left_line": 1266, + "right_line": 1266 + }, + { + "left_line": 1267, + "right_line": 1267 + }, + { + "left_line": 1268, + "right_line": 1268 + }, + { + "left_line": 1269, + "right_line": 1269 + }, + { + "left_line": 1270, + "right_line": 1270 + }, + { + "left_line": 1271, + "right_line": 1271 + }, + { + "left_line": 1272, + "right_line": 1272 + }, + { + "left_line": 1273, + "right_line": 1273 + }, + { + "left_line": 1274, + "right_line": 1274 + }, + { + "left_line": 1275, + "right_line": 1275 + }, + { + "left_line": 1276, + "right_line": 1276 + }, + { + "left_line": 1277, + "right_line": 1277 + }, + { + "left_line": 1278, + "right_line": 1278 + }, + { + "left_line": 1279, + "right_line": 1279 + }, + { + "left_line": 1280, + "right_line": 1280 + }, + { + "left_line": 1281, + "right_line": 1281 + }, + { + "left_line": 1282, + "right_line": 1282 + }, + { + "left_line": 1283, + "right_line": 1283 + }, + { + "left_line": 1284, + "right_line": 1284 + }, + { + "left_line": 1285, + "right_line": 1285 + }, + { + "left_line": 1286, + "right_line": 1286 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_update_link/metadata.json b/tests/testdata/ts_react_router_update_link/metadata.json new file mode 100644 index 0000000..524ed2d --- /dev/null +++ b/tests/testdata/ts_react_router_update_link/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/remix-run/react-router", + "parent": "963affeb924f2256bd20c0c5c87e4c4b4fcbd188", + "commit": "7965e50072eb7d3a28b770a5e131c45ed9367926", + "message": "Update link to v7 instead of a specific version (#15147)", + "author": "Matt Brophy", + "date": "2026-06-04T10:17:03-04:00", + "language": "typescript", + "files_changed": 1, + "insertions": 0, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_react_router_update_link/new.ts b/tests/testdata/ts_react_router_update_link/new.ts new file mode 100644 index 0000000..bc3148d --- /dev/null +++ b/tests/testdata/ts_react_router_update_link/new.ts @@ -0,0 +1,1286 @@ +import fs from "node:fs"; +import { execSync } from "node:child_process"; +import * as ViteNode from "../vite/vite-node"; +import type * as Vite from "vite"; +import Path from "pathe"; +import chokidar, { + type FSWatcher, + type EmitArgs as ChokidarEmitArgs, +} from "chokidar"; +import colors from "picocolors"; +import pick from "lodash/pick"; +import omit from "lodash/omit"; +import cloneDeep from "lodash/cloneDeep"; +import isEqual from "lodash/isEqual"; + +import { + type RouteManifest, + type RouteManifestEntry, + type RouteConfigEntry, + setAppDirectory, + validateRouteConfig, + configRoutesToRouteManifest, +} from "./routes"; +import { detectPackageManager } from "../cli/detectPackageManager"; + +const excludedConfigPresetKeys = ["presets"] as const satisfies ReadonlyArray< + keyof ReactRouterConfig +>; + +type ExcludedConfigPresetKey = (typeof excludedConfigPresetKeys)[number]; + +type ConfigPreset = Omit; + +export type Preset = { + name: string; + reactRouterConfig?: (args: { + reactRouterUserConfig: ReactRouterConfig; + }) => ConfigPreset | Promise; + reactRouterConfigResolved?: (args: { + reactRouterConfig: ResolvedReactRouterConfig; + }) => void | Promise; +}; + +// Only expose a subset of route properties to the "serverBundles" function +const branchRouteProperties = [ + "id", + "path", + "file", + "index", +] as const satisfies ReadonlyArray; +type BranchRoute = Pick< + RouteManifestEntry, + (typeof branchRouteProperties)[number] +>; + +export const configRouteToBranchRoute = ( + configRoute: RouteManifestEntry, +): BranchRoute => pick(configRoute, branchRouteProperties); + +export type ServerBundlesFunction = (args: { + branch: BranchRoute[]; +}) => string | Promise; + +type BaseBuildManifest = { + routes: RouteManifest; +}; + +type DefaultBuildManifest = BaseBuildManifest & { + serverBundles?: never; + routeIdToServerBundleId?: never; +}; + +type ServerBundlesBuildManifest = BaseBuildManifest & { + serverBundles: { + [serverBundleId: string]: { + id: string; + file: string; + }; + }; + routeIdToServerBundleId: Record; +}; + +type ServerModuleFormat = "esm" | "cjs"; + +type ValidateConfigFunction = (config: ReactRouterConfig) => string | void; + +interface FutureConfig { + unstable_optimizeDeps: boolean; + v8_passThroughRequests: boolean; + v8_trailingSlashAwareDataRequests: boolean; + /** + * Prerender with Vite Preview server + */ + unstable_previewServerPrerendering?: boolean; + /** + * Enable route middleware + */ + v8_middleware: boolean; + /** + * Automatically split route modules into multiple chunks when possible. + */ + v8_splitRouteModules: boolean | "enforce"; + /** + * Use Vite Environment API + */ + v8_viteEnvironmentApi: boolean; +} + +export type BuildManifest = DefaultBuildManifest | ServerBundlesBuildManifest; + +type BuildEndHook = (args: { + buildManifest: BuildManifest | undefined; + reactRouterConfig: ResolvedReactRouterConfig; + viteConfig: Vite.ResolvedConfig; +}) => void | Promise; + +export type PrerenderPaths = + | boolean + | Array + | ((args: { + getStaticPaths: () => string[]; + }) => Array | Promise>); + +/** + * Config to be exported via the default export from `react-router.config.ts`. + */ +export type ReactRouterConfig = { + /** + * The path to the `app` directory, relative to the root directory. Defaults + * to `"app"`. + */ + appDirectory?: string; + + /** + * The output format of the server build. Defaults to "esm". + */ + serverModuleFormat?: ServerModuleFormat; + + /** + * Enabled future flags + */ + future?: [keyof FutureConfig] extends [never] + ? // Partial doesn't work when it's empty so just prevent any keys + { [key: string]: never } + : Partial; + + /** + * The React Router app basename. Defaults to `"/"`. + */ + basename?: string; + /** + * The path to the build directory, relative to the project. Defaults to + * `"build"`. + */ + buildDirectory?: string; + /** + * A function that is called after the full React Router build is complete. + */ + buildEnd?: BuildEndHook; + /** + * An array of URLs to prerender to HTML files at build time. Can also be a + * function returning an array to dynamically generate URLs. + * + * `concurrency` defaults to 1, which means "no concurrency" - fully serial execution. + * Setting it to a value more than 1 enables concurrent prerendering. + * Setting it to a value higher than one can increase the speed of the build, + * but may consume more resources, and send more concurrent requests to the + * server/CMS. + */ + prerender?: + | PrerenderPaths + | { + paths: PrerenderPaths; + concurrency?: number; + }; + /** + * An array of React Router plugin config presets to ease integration with + * other platforms and tools. + */ + presets?: Array; + /** + * Control the "Lazy Route Discovery" behavior + * + * - `routeDiscovery.mode`: By default, this resolves to `lazy` which will + * lazily discover routes as the user navigates around your application. + * You can set this to `initial` to opt-out of this behavior and load all + * routes with the initial HTML document load. + * - `routeDiscovery.manifestPath`: The path to serve the manifest file from. + * Only applies to `mode: "lazy"` and defaults to `/__manifest`. + */ + routeDiscovery?: + | { + mode: "lazy"; + manifestPath?: string; + } + | { + mode: "initial"; + }; + /** + * The file name of the server build output. This file + * should end in a `.js` extension and should be deployed to your server. + * Defaults to `"index.js"`. + */ + serverBuildFile?: string; + /** + * A function for assigning routes to different server bundles. This + * function should return a server bundle ID which will be used as the + * bundle's directory name within the server build directory. + */ + serverBundles?: ServerBundlesFunction; + /** + * Enable server-side rendering for your application. Disable to use "SPA + * Mode", which will request the `/` path at build-time and save it as an + * `index.html` file with your assets so your application can be deployed as a + * SPA without server-rendering. Default's to `true`. + */ + ssr?: boolean; + + /** + * Enable subresource integrity hashes on asset script tags. Defaults to + * `false`. + */ + subResourceIntegrity?: boolean; + + /** + * An array of allowed origin hosts for action submissions to UI routes (does not apply + * to resource routes). Supports micromatch glob patterns (`*` to match one segment, + * `**` to match multiple). + * + * ```tsx + * export default { + * allowedActionOrigins: [ + * "example.com", + * "*.example.com", // sub.example.com + * "**.example.com", // sub.domain.example.com + * ], + * } satisfies Config; + * ``` + * + * If you need to set this value at runtime, you can do in by setting the value + * on the server build in your custom server. For example, when using `express`: + * + * ```ts + * import express from "express"; + * import { createRequestHandler } from "@react-router/express"; + * import type { ServerBuild } from "react-router"; + * + * export const app = express(); + * + * async function getBuild() { + * let build: ServerBuild = await import( + * "virtual:react-router/server-build" + * ); + * return { + * ...build, + * allowedActionOrigins: + * process.env.NODE_ENV === "development" + * ? undefined + * : ["staging.example.com", "www.example.com"], + * }; + * } + * + * app.use(createRequestHandler({ build: getBuild })); + */ + allowedActionOrigins?: string[]; +}; + +export type ResolvedReactRouterConfig = Readonly<{ + /** + * The absolute path to the application source directory. + */ + appDirectory: string; + /** + * The React Router app basename. Defaults to `"/"`. + */ + basename: string; + /** + * The absolute path to the build directory. + */ + buildDirectory: string; + /** + * A function that is called after the full React Router build is complete. + */ + buildEnd?: BuildEndHook; + /** + * Enabled future flags + */ + future: FutureConfig; + /** + * An array of URLs to prerender to HTML files at build time. Can also be a + * function returning an array to dynamically generate URLs. + */ + prerender: ReactRouterConfig["prerender"]; + /** + * Control the "Lazy Route Discovery" behavior + * + * - `routeDiscovery.mode`: By default, this resolves to `lazy` which will + * lazily discover routes as the user navigates around your application. + * You can set this to `initial` to opt-out of this behavior and load all + * routes with the initial HTML document load. + * - `routeDiscovery.manifestPath`: The path to serve the manifest file from. + * Only applies to `mode: "lazy"` and defaults to `/__manifest`. + */ + routeDiscovery: ReactRouterConfig["routeDiscovery"]; + /** + * An object of all available routes, keyed by route id. + */ + routes: RouteManifest; + /** + * The file name of the server build output. This file + * should end in a `.js` extension and should be deployed to your server. + * Defaults to `"index.js"`. + */ + serverBuildFile: string; + /** + * A function for assigning routes to different server bundles. This + * function should return a server bundle ID which will be used as the + * bundle's directory name within the server build directory. + */ + serverBundles?: ServerBundlesFunction; + /** + * The output format of the server build. Defaults to "esm". + */ + serverModuleFormat: ServerModuleFormat; + /** + * Enable server-side rendering for your application. Disable to use "SPA + * Mode", which will request the `/` path at build-time and save it as an + * `index.html` file with your assets so your application can be deployed as a + * SPA without server-rendering. Default's to `true`. + */ + ssr: boolean; + /** + * Whether to generate subresource integrity hashes for asset script tags. + */ + subResourceIntegrity: boolean; + /** + * The allowed origins for actions / mutations. Does not apply to routes + * without a component. micromatch glob patterns are supported. + */ + allowedActionOrigins: string[] | false; + /** + * The resolved array of route config entries exported from `routes.ts` + */ + unstable_routeConfig: RouteConfigEntry[]; +}>; + +let mergeReactRouterConfig = ( + ...configs: ReactRouterConfig[] +): ReactRouterConfig => { + let reducer = ( + configA: ReactRouterConfig, + configB: ReactRouterConfig, + ): ReactRouterConfig => { + let mergeRequired = (key: keyof ReactRouterConfig) => + configA[key] !== undefined && configB[key] !== undefined; + + return { + ...configA, + ...configB, + ...(mergeRequired("buildEnd") + ? { + buildEnd: async (...args) => { + await Promise.all([ + configA.buildEnd?.(...args), + configB.buildEnd?.(...args), + ]); + }, + } + : {}), + ...(mergeRequired("future") + ? { + future: { + ...configA.future, + ...configB.future, + }, + } + : {}), + ...(mergeRequired("presets") + ? { + presets: [...(configA.presets ?? []), ...(configB.presets ?? [])], + } + : {}), + }; + }; + + return configs.reduce(reducer, {}); +}; + +// Inlined from https://github.com/jsdf/deep-freeze +let deepFreeze = (o: any) => { + Object.freeze(o); + let oIsFunction = typeof o === "function"; + let hasOwnProp = Object.prototype.hasOwnProperty; + Object.getOwnPropertyNames(o).forEach(function (prop) { + if ( + hasOwnProp.call(o, prop) && + (oIsFunction + ? prop !== "caller" && prop !== "callee" && prop !== "arguments" + : true) && + o[prop] !== null && + (typeof o[prop] === "object" || typeof o[prop] === "function") && + !Object.isFrozen(o[prop]) + ) { + deepFreeze(o[prop]); + } + }); + return o; +}; + +type Result = + | { + ok: true; + value: T; + error?: undefined; + } + | { + ok: false; + value?: undefined; + error: string; + }; + +type ConfigResult = Result; + +function ok(value: T): Result { + return { ok: true, value }; +} + +function err(error: string): Result { + return { ok: false, error }; +} + +async function resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, +}: { + root: string; + viteNodeContext: ViteNode.Context; + reactRouterConfigFile?: string; + skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; + shouldLogFutureFlagWarnings?: boolean; +}): Promise { + let reactRouterUserConfig: ReactRouterConfig = {}; + + if (reactRouterConfigFile) { + try { + if (!fs.existsSync(reactRouterConfigFile)) { + return err(`${reactRouterConfigFile} no longer exists`); + } + + let configModule = await viteNodeContext.runner.executeFile( + reactRouterConfigFile, + ); + + if (configModule.default === undefined) { + return err(`${reactRouterConfigFile} must provide a default export`); + } + + if (typeof configModule.default !== "object") { + return err(`${reactRouterConfigFile} must export a config`); + } + + reactRouterUserConfig = configModule.default; + + if (validateConfig) { + const error = validateConfig(reactRouterUserConfig); + if (error) { + return err(error); + } + } + } catch (error) { + return err(`Error loading ${reactRouterConfigFile}: ${error}`); + } + } + + // Prevent mutations to the user config + reactRouterUserConfig = deepFreeze(cloneDeep(reactRouterUserConfig)); + + let presets: ReactRouterConfig[] = ( + await Promise.all( + (reactRouterUserConfig.presets ?? []).map(async (preset) => { + if (!preset.name) { + throw new Error( + "React Router presets must have a `name` property defined.", + ); + } + + if (!preset.reactRouterConfig) { + return null; + } + + let configPreset: ReactRouterConfig = omit( + await preset.reactRouterConfig({ reactRouterUserConfig }), + excludedConfigPresetKeys, + ); + + return configPreset; + }), + ) + ).filter(function isNotNull(value: T | null): value is T { + return value !== null; + }); + + let defaults = { + basename: "/", + buildDirectory: "build", + serverBuildFile: "index.js", + serverModuleFormat: "esm", + ssr: true, + } as const satisfies Partial; + + let userAndPresetConfigs = mergeReactRouterConfig( + ...presets, + reactRouterUserConfig, + ); + + let { + appDirectory: userAppDirectory, + basename, + buildDirectory: userBuildDirectory, + buildEnd, + prerender, + routeDiscovery: userRouteDiscovery, + serverBuildFile, + serverBundles, + serverModuleFormat, + ssr, + } = { + ...defaults, // Default values should be completely overridden by user/preset config, not merged + ...userAndPresetConfigs, + }; + + if (!ssr && serverBundles) { + serverBundles = undefined; + } + + if (prerender) { + let isValidPrerenderPathsConfig = (p: unknown) => + typeof p === "boolean" || typeof p === "function" || Array.isArray(p); + + let isValidPrerenderConfig = + isValidPrerenderPathsConfig(prerender) || + (typeof prerender === "object" && + "paths" in prerender && + isValidPrerenderPathsConfig(prerender.paths)); + + if (!isValidPrerenderConfig) { + return err( + "The `prerender`/`prerender.paths` config must be a boolean, an array " + + "of string paths, or a function returning a boolean or array of string paths.", + ); + } + + if (typeof prerender === "object" && "unstable_concurrency" in prerender) { + return err( + "The `prerender.unstable_concurrency` config field has been stabilized as `prerender.concurrency`", + ); + } + + let isValidConcurrencyConfig = + typeof prerender != "object" || + !("concurrency" in prerender) || + (typeof prerender.concurrency === "number" && + Number.isInteger(prerender.concurrency) && + prerender.concurrency > 0); + + if (!isValidConcurrencyConfig) { + return err( + "The `prerender.concurrency` config must be a positive integer if specified.", + ); + } + } + + let routeDiscovery: ResolvedReactRouterConfig["routeDiscovery"]; + if (userRouteDiscovery == null) { + if (ssr) { + routeDiscovery = { + mode: "lazy", + manifestPath: "/__manifest", + }; + } else { + routeDiscovery = { mode: "initial" }; + } + } else if (userRouteDiscovery.mode === "initial") { + routeDiscovery = userRouteDiscovery; + } else if (userRouteDiscovery.mode === "lazy") { + if (!ssr) { + return err( + 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`', + ); + } + + let { manifestPath } = userRouteDiscovery; + if (manifestPath != null && !manifestPath.startsWith("/")) { + return err( + "The `routeDiscovery.manifestPath` config must be a root-relative " + + 'pathname beginning with a slash (i.e., "/__manifest")', + ); + } + + routeDiscovery = userRouteDiscovery; + } + + let appDirectory = Path.resolve(root, userAppDirectory || "app"); + let buildDirectory = Path.resolve(root, userBuildDirectory); + + let rootRouteFile = findEntry(appDirectory, "root", { absolute: true }); + if (!rootRouteFile) { + let rootRouteDisplayPath = Path.relative( + root, + Path.join(appDirectory, "root.tsx"), + ); + return err( + `Could not find a root route module in the app directory as "${rootRouteDisplayPath}"`, + ); + } + + let routes: RouteManifest; + let routeConfig: RouteConfigEntry[] = []; + + if (skipRoutes) { + routes = {}; + } else { + let routeConfigFile = findEntry(appDirectory, "routes"); + + try { + if (!routeConfigFile) { + let routeConfigDisplayPath = Path.relative( + root, + Path.join(appDirectory, "routes.ts"), + ); + return err( + `Route config file not found at "${routeConfigDisplayPath}".`, + ); + } + + setAppDirectory(appDirectory); + let routeConfigExport = ( + await viteNodeContext.runner.executeFile( + Path.join(appDirectory, routeConfigFile), + ) + ).default; + let result = validateRouteConfig({ + routeConfigFile, + routeConfig: await routeConfigExport, + }); + + if (!result.valid) { + return err(result.message); + } + + // Nest the route config under the resolved root route + routeConfig = [ + { + id: "root", + path: "", + file: Path.relative(appDirectory, rootRouteFile), + children: result.routeConfig, + }, + ]; + + routes = configRoutesToRouteManifest(appDirectory, routeConfig); + } catch (error: any) { + return err( + [ + colors.red(`Route config in "${routeConfigFile}" is invalid.`), + "", + error.loc?.file && error.loc?.column && error.frame + ? [ + Path.relative(appDirectory, error.loc.file) + + ":" + + error.loc.line + + ":" + + error.loc.column, + error.frame.trim?.(), + ] + : error.stack, + ] + .flat() + .join("\n"), + ); + } + } + + // Check for renamed flags and provide helpful error messages + let futureConfig = userAndPresetConfigs.future; + if (futureConfig) { + if ("unstable_splitRouteModules" in futureConfig) { + return err( + "The `future.unstable_splitRouteModules` flag has been stabilized as `future.v8_splitRouteModules`", + ); + } + if ("unstable_viteEnvironmentApi" in futureConfig) { + return err( + "The `future.unstable_viteEnvironmentApi` flag has been stabilized as `future.v8_viteEnvironmentApi`", + ); + } + if ("unstable_passThroughRequests" in futureConfig) { + return err( + "The `future.unstable_passThroughRequests` flag has been stabilized as `future.v8_passThroughRequests`", + ); + } + if ("unstable_trailingSlashAwareDataRequests" in futureConfig) { + return err( + "The `future.unstable_trailingSlashAwareDataRequests` flag has been stabilized as `future.v8_trailingSlashAwareDataRequests`", + ); + } + if ("unstable_subResourceIntegrity" in futureConfig) { + return err( + "The `future.unstable_subResourceIntegrity` flag has been stabilized and moved to a top-level `config.subResourceIntegrity` field", + ); + } + } + + let future: FutureConfig = { + unstable_optimizeDeps: + userAndPresetConfigs.future?.unstable_optimizeDeps ?? false, + v8_passThroughRequests: + userAndPresetConfigs.future?.v8_passThroughRequests ?? false, + v8_trailingSlashAwareDataRequests: + userAndPresetConfigs.future?.v8_trailingSlashAwareDataRequests ?? false, + unstable_previewServerPrerendering: + userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false, + v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false, + v8_splitRouteModules: + userAndPresetConfigs.future?.v8_splitRouteModules ?? false, + v8_viteEnvironmentApi: + (userAndPresetConfigs.future?.v8_viteEnvironmentApi || + userAndPresetConfigs.future?.unstable_previewServerPrerendering) ?? + false, + }; + + let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false; + let subResourceIntegrity = userAndPresetConfigs.subResourceIntegrity ?? false; + + let reactRouterConfig: ResolvedReactRouterConfig = deepFreeze({ + appDirectory, + basename, + buildDirectory, + buildEnd, + future, + prerender, + routes, + routeDiscovery, + serverBuildFile, + serverBundles, + serverModuleFormat, + ssr, + subResourceIntegrity, + allowedActionOrigins, + unstable_routeConfig: routeConfig, + } satisfies ResolvedReactRouterConfig); + + for (let preset of reactRouterUserConfig.presets ?? []) { + await preset.reactRouterConfigResolved?.({ reactRouterConfig }); + } + + if (shouldLogFutureFlagWarnings) { + logFutureFlagWarnings(userAndPresetConfigs.future || {}); + } + + return ok(reactRouterConfig); +} + +function logFutureFlagWarning(flag: string, message: string): void { + console.log( + colors.yellow( + ` ⚠️ Future Flag Warning: ${message}\n` + + ` You can use the \`future.${flag}\` flag to opt in early.\n` + + ` -> https://reactrouter.com/v7/upgrading/future#future${flag.toLowerCase()}`, + ), + ); +} + +export function logFutureFlagWarnings(future: Partial): void { + if (future.v8_middleware === undefined) { + logFutureFlagWarning( + "v8_middleware", + "Route middleware support is changing in React Router v8.", + ); + } + if (future.v8_splitRouteModules === undefined) { + logFutureFlagWarning( + "v8_splitRouteModules", + "Route module splitting behavior is changing in React Router v8.", + ); + } + if (future.v8_viteEnvironmentApi === undefined) { + logFutureFlagWarning( + "v8_viteEnvironmentApi", + "Vite Environment API usage is changing in React Router v8.", + ); + } + if (future.v8_passThroughRequests === undefined) { + logFutureFlagWarning( + "v8_passThroughRequests", + "Request handling behavior is changing in React Router v8.", + ); + } + if (future.v8_trailingSlashAwareDataRequests === undefined) { + logFutureFlagWarning( + "v8_trailingSlashAwareDataRequests", + "Data request URL formats are changing in React Router v8.", + ); + } +} + +type ChokidarEventName = ChokidarEmitArgs[0]; + +type ChangeHandler = (args: { + result: ConfigResult; + configCodeChanged: boolean; + routeConfigCodeChanged: boolean; + configChanged: boolean; + routeConfigChanged: boolean; + path: string; + event: ChokidarEventName; +}) => void; + +export type ConfigLoader = { + getConfig: () => Promise; + onChange: (handler: ChangeHandler) => () => void; + close: () => Promise; +}; + +export async function createConfigLoader({ + rootDirectory: root, + watch, + mode, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, +}: { + watch: boolean; + rootDirectory?: string; + mode: string; + skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; + shouldLogFutureFlagWarnings?: boolean; +}): Promise { + root = Path.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd()); + + let vite = await import("vite"); + let viteNodeContext = await ViteNode.createContext({ + root, + mode, + // Filter out any info level logs from vite-node + customLogger: vite.createLogger("warn", { + prefix: "[react-router]", + }), + }); + + let reactRouterConfigFile: string | undefined; + + let updateReactRouterConfigFile = () => { + reactRouterConfigFile = findEntry(root, "react-router.config", { + absolute: true, + }); + }; + + updateReactRouterConfigFile(); + + let getConfig = () => + resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, + }); + + let appDirectory: string; + + let initialConfigResult = await resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + }); + + if (!initialConfigResult.ok) { + throw new Error(initialConfigResult.error); + } + + appDirectory = Path.normalize(initialConfigResult.value.appDirectory); + + let currentConfig = initialConfigResult.value; + + let fsWatcher: FSWatcher | undefined; + let changeHandlers: ChangeHandler[] = []; + + return { + getConfig, + onChange: (handler: ChangeHandler) => { + if (!watch) { + throw new Error( + "onChange is not supported when watch mode is disabled", + ); + } + + changeHandlers.push(handler); + + if (!fsWatcher) { + fsWatcher = chokidar.watch([root, appDirectory], { + ignoreInitial: true, + ignored: (path) => isIgnoredByWatcher(path, { root, appDirectory }), + }); + + fsWatcher.on("error", (error: unknown) => { + let message = error instanceof Error ? error.message : String(error); + console.warn(colors.yellow(`File watcher error: ${message}`)); + }); + + fsWatcher.on("all", async (...args) => { + let [event, rawFilepath] = args; + let filepath = Path.normalize(rawFilepath); + + let fileAddedOrRemoved = event === "add" || event === "unlink"; + + let appFileAddedOrRemoved = + fileAddedOrRemoved && + filepath.startsWith(Path.normalize(appDirectory)); + + let rootRelativeFilepath = Path.relative(root, filepath); + + let configFileAddedOrRemoved = + fileAddedOrRemoved && + isEntryFile("react-router.config", rootRelativeFilepath); + + if (configFileAddedOrRemoved) { + updateReactRouterConfigFile(); + } + + let moduleGraphChanged = + configFileAddedOrRemoved || + Boolean( + viteNodeContext.devServer?.moduleGraph.getModuleById(filepath), + ); + + // Bail out if no relevant changes detected + if (!moduleGraphChanged && !appFileAddedOrRemoved) { + return; + } + + viteNodeContext.devServer?.moduleGraph.invalidateAll(); + viteNodeContext.runner?.moduleCache.clear(); + + let result = await getConfig(); + + let prevAppDirectory = appDirectory; + appDirectory = Path.normalize( + (result.value ?? currentConfig).appDirectory, + ); + + if (appDirectory !== prevAppDirectory) { + fsWatcher!.unwatch(prevAppDirectory); + fsWatcher!.add(appDirectory); + } + + let configCodeChanged = + configFileAddedOrRemoved || + (reactRouterConfigFile !== undefined && + isEntryFileDependency( + viteNodeContext.devServer.moduleGraph, + reactRouterConfigFile, + filepath, + )); + + let routeConfigFile = !skipRoutes + ? findEntry(appDirectory, "routes", { + absolute: true, + }) + : undefined; + let routeConfigCodeChanged = + routeConfigFile !== undefined && + isEntryFileDependency( + viteNodeContext.devServer.moduleGraph, + routeConfigFile, + filepath, + ); + + let configChanged = + result.ok && + !isEqual(omitRoutes(currentConfig), omitRoutes(result.value)); + + let routeConfigChanged = + result.ok && !isEqual(currentConfig?.routes, result.value.routes); + + for (let handler of changeHandlers) { + handler({ + result, + configCodeChanged, + routeConfigCodeChanged, + configChanged, + routeConfigChanged, + path: filepath, + event, + }); + } + + if (result.ok) { + currentConfig = result.value; + } + }); + } + + return () => { + changeHandlers = changeHandlers.filter( + (changeHandler) => changeHandler !== handler, + ); + }; + }, + close: async () => { + changeHandlers = []; + await viteNodeContext.devServer.close(); + await fsWatcher?.close(); + }, + }; +} + +export async function loadConfig({ + rootDirectory, + mode, + skipRoutes, +}: { + rootDirectory: string; + mode: string; + skipRoutes?: boolean; +}) { + let configLoader = await createConfigLoader({ + rootDirectory, + mode, + skipRoutes, + watch: false, + }); + let config = await configLoader.getConfig(); + await configLoader.close(); + return config; +} + +export async function resolveEntryFiles({ + rootDirectory, + reactRouterConfig, +}: { + rootDirectory: string; + reactRouterConfig: ResolvedReactRouterConfig; +}) { + let { appDirectory } = reactRouterConfig; + + let defaultsDirectory = Path.resolve( + Path.dirname(require.resolve("@react-router/dev/package.json")), + "dist", + "config", + "defaults", + ); + + let userEntryClientFile = findEntry(appDirectory, "entry.client"); + let userEntryServerFile = findEntry(appDirectory, "entry.server"); + + let entryServerFile: string; + let entryClientFile = userEntryClientFile || "entry.client.tsx"; + + if (userEntryServerFile) { + entryServerFile = userEntryServerFile; + } else { + let packageJsonPath = findEntry(rootDirectory, "package", { + extensions: [".json"], + absolute: true, + walkParents: true, + }); + + if (!packageJsonPath) { + throw new Error( + `Could not find package.json in ${rootDirectory} or any of its parent directories. Please add a package.json, or provide a custom entry.server.tsx/jsx file in your app directory.`, + ); + } + + // TODO(v8): Remove - only required for Node 20.18 and below + let { readPackageJSON, sortPackage, updatePackage } = await import( + "pkg-types" + ); + let packageJsonDirectory = Path.dirname(packageJsonPath); + let pkgJson = await readPackageJSON(packageJsonDirectory); + let deps = pkgJson.dependencies ?? {}; + + if (!deps["@react-router/node"]) { + throw new Error( + `Could not determine server runtime. Please install @react-router/node, or provide a custom entry.server.tsx/jsx file in your app directory.`, + ); + } + + if (!deps["isbot"]) { + console.log( + "adding `isbot@5` to your package.json, you should commit this change", + ); + + await updatePackage(packageJsonPath, (pkg) => { + pkg.dependencies ??= {}; + pkg.dependencies.isbot = "^5"; + sortPackage(pkg); + }); + + let packageManager = detectPackageManager() ?? "npm"; + + execSync(`${packageManager} install`, { + cwd: packageJsonDirectory, + stdio: "inherit", + }); + } + + entryServerFile = `entry.server.node.tsx`; + } + + let entryClientFilePath = userEntryClientFile + ? Path.resolve(reactRouterConfig.appDirectory, userEntryClientFile) + : Path.resolve(defaultsDirectory, entryClientFile); + + let entryServerFilePath = userEntryServerFile + ? Path.resolve(reactRouterConfig.appDirectory, userEntryServerFile) + : Path.resolve(defaultsDirectory, entryServerFile); + + return { entryClientFilePath, entryServerFilePath }; +} + +export async function resolveRSCEntryFiles({ + reactRouterConfig, +}: { + reactRouterConfig: ResolvedReactRouterConfig; +}) { + let { appDirectory } = reactRouterConfig; + + let defaultsDirectory = Path.resolve( + Path.dirname(require.resolve("@react-router/dev/package.json")), + "dist", + "config", + "default-rsc-entries", + ); + + let userEntryClientFile = findEntry(appDirectory, "entry.client", { + absolute: true, + }); + let userEntryRSCFile = findEntry(appDirectory, "entry.rsc", { + absolute: true, + }); + let userEntrySSRFile = findEntry(appDirectory, "entry.ssr", { + absolute: true, + }); + + let client = + userEntryClientFile ?? Path.join(defaultsDirectory, "entry.client.tsx"); + let rsc = userEntryRSCFile ?? Path.join(defaultsDirectory, "entry.rsc.tsx"); + let ssr = userEntrySSRFile ?? Path.join(defaultsDirectory, "entry.ssr.tsx"); + + return { client, rsc, ssr }; +} + +function omitRoutes( + config: ResolvedReactRouterConfig, +): ResolvedReactRouterConfig { + return { + ...config, + routes: {}, + }; +} + +const entryExts = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".mts"]; + +function isEntryFile(entryBasename: string, filename: string) { + return entryExts.some((ext) => filename === `${entryBasename}${ext}`); +} + +function findEntry( + dir: string, + basename: string, + options?: { + absolute?: boolean; + extensions?: string[]; + walkParents?: boolean; + }, +): string | undefined { + let currentDir = Path.resolve(dir); + let { root } = Path.parse(currentDir); + + while (true) { + for (let ext of options?.extensions ?? entryExts) { + let file = Path.resolve(currentDir, basename + ext); + if (fs.existsSync(file)) { + return (options?.absolute ?? false) ? file : Path.relative(dir, file); + } + } + + if (!options?.walkParents) { + return undefined; + } + + let parentDir = Path.dirname(currentDir); + // Break out when we've reached the root directory or we're about to get + // stuck in a loop where `path.dirname` keeps returning "/" + if (currentDir === root || parentDir === currentDir) { + return undefined; + } + + currentDir = parentDir; + } +} + +function isEntryFileDependency( + moduleGraph: Vite.ModuleGraph, + entryFilepath: string, + filepath: string, + visited = new Set(), +): boolean { + // Ensure normalized paths + entryFilepath = Path.normalize(entryFilepath); + filepath = Path.normalize(filepath); + + if (visited.has(filepath)) { + return false; + } + + visited.add(filepath); + + if (filepath === entryFilepath) { + return true; + } + + let mod = moduleGraph.getModuleById(filepath); + + if (!mod) { + return false; + } + + // Recursively check all importers to see if any of them are the entry file + for (let importer of mod.importers) { + if (!importer.id) { + continue; + } + + if ( + importer.id === entryFilepath || + isEntryFileDependency(moduleGraph, entryFilepath, importer.id, visited) + ) { + return true; + } + } + + return false; +} + +export function isIgnoredByWatcher( + path: string, + { root, appDirectory }: { root: string; appDirectory: string }, +): boolean { + let dirname = Path.dirname(path); + + let ignoredByPath = + !dirname.startsWith(appDirectory) && + // Ensure we're only watching files outside of the app directory + // that are at the root level, not nested in subdirectories + path !== root && // Watch the root directory itself + dirname !== root; // Watch files at the root level + + if (ignoredByPath) { + return true; + } + + // Filter out non-regular files (sockets, pipes, etc.) that + // crash `fs.watch()` on macOS with errno -102 + // https://github.com/paulmillr/chokidar/issues/1391 + try { + let stat = fs.statSync(path, { throwIfNoEntry: false }); + if (stat && !stat.isFile() && !stat.isDirectory()) { + return true; + } + } catch { + return true; + } + + return false; +} diff --git a/tests/testdata/ts_react_router_update_link/old.ts b/tests/testdata/ts_react_router_update_link/old.ts new file mode 100644 index 0000000..15d0ca2 --- /dev/null +++ b/tests/testdata/ts_react_router_update_link/old.ts @@ -0,0 +1,1286 @@ +import fs from "node:fs"; +import { execSync } from "node:child_process"; +import * as ViteNode from "../vite/vite-node"; +import type * as Vite from "vite"; +import Path from "pathe"; +import chokidar, { + type FSWatcher, + type EmitArgs as ChokidarEmitArgs, +} from "chokidar"; +import colors from "picocolors"; +import pick from "lodash/pick"; +import omit from "lodash/omit"; +import cloneDeep from "lodash/cloneDeep"; +import isEqual from "lodash/isEqual"; + +import { + type RouteManifest, + type RouteManifestEntry, + type RouteConfigEntry, + setAppDirectory, + validateRouteConfig, + configRoutesToRouteManifest, +} from "./routes"; +import { detectPackageManager } from "../cli/detectPackageManager"; + +const excludedConfigPresetKeys = ["presets"] as const satisfies ReadonlyArray< + keyof ReactRouterConfig +>; + +type ExcludedConfigPresetKey = (typeof excludedConfigPresetKeys)[number]; + +type ConfigPreset = Omit; + +export type Preset = { + name: string; + reactRouterConfig?: (args: { + reactRouterUserConfig: ReactRouterConfig; + }) => ConfigPreset | Promise; + reactRouterConfigResolved?: (args: { + reactRouterConfig: ResolvedReactRouterConfig; + }) => void | Promise; +}; + +// Only expose a subset of route properties to the "serverBundles" function +const branchRouteProperties = [ + "id", + "path", + "file", + "index", +] as const satisfies ReadonlyArray; +type BranchRoute = Pick< + RouteManifestEntry, + (typeof branchRouteProperties)[number] +>; + +export const configRouteToBranchRoute = ( + configRoute: RouteManifestEntry, +): BranchRoute => pick(configRoute, branchRouteProperties); + +export type ServerBundlesFunction = (args: { + branch: BranchRoute[]; +}) => string | Promise; + +type BaseBuildManifest = { + routes: RouteManifest; +}; + +type DefaultBuildManifest = BaseBuildManifest & { + serverBundles?: never; + routeIdToServerBundleId?: never; +}; + +type ServerBundlesBuildManifest = BaseBuildManifest & { + serverBundles: { + [serverBundleId: string]: { + id: string; + file: string; + }; + }; + routeIdToServerBundleId: Record; +}; + +type ServerModuleFormat = "esm" | "cjs"; + +type ValidateConfigFunction = (config: ReactRouterConfig) => string | void; + +interface FutureConfig { + unstable_optimizeDeps: boolean; + v8_passThroughRequests: boolean; + v8_trailingSlashAwareDataRequests: boolean; + /** + * Prerender with Vite Preview server + */ + unstable_previewServerPrerendering?: boolean; + /** + * Enable route middleware + */ + v8_middleware: boolean; + /** + * Automatically split route modules into multiple chunks when possible. + */ + v8_splitRouteModules: boolean | "enforce"; + /** + * Use Vite Environment API + */ + v8_viteEnvironmentApi: boolean; +} + +export type BuildManifest = DefaultBuildManifest | ServerBundlesBuildManifest; + +type BuildEndHook = (args: { + buildManifest: BuildManifest | undefined; + reactRouterConfig: ResolvedReactRouterConfig; + viteConfig: Vite.ResolvedConfig; +}) => void | Promise; + +export type PrerenderPaths = + | boolean + | Array + | ((args: { + getStaticPaths: () => string[]; + }) => Array | Promise>); + +/** + * Config to be exported via the default export from `react-router.config.ts`. + */ +export type ReactRouterConfig = { + /** + * The path to the `app` directory, relative to the root directory. Defaults + * to `"app"`. + */ + appDirectory?: string; + + /** + * The output format of the server build. Defaults to "esm". + */ + serverModuleFormat?: ServerModuleFormat; + + /** + * Enabled future flags + */ + future?: [keyof FutureConfig] extends [never] + ? // Partial doesn't work when it's empty so just prevent any keys + { [key: string]: never } + : Partial; + + /** + * The React Router app basename. Defaults to `"/"`. + */ + basename?: string; + /** + * The path to the build directory, relative to the project. Defaults to + * `"build"`. + */ + buildDirectory?: string; + /** + * A function that is called after the full React Router build is complete. + */ + buildEnd?: BuildEndHook; + /** + * An array of URLs to prerender to HTML files at build time. Can also be a + * function returning an array to dynamically generate URLs. + * + * `concurrency` defaults to 1, which means "no concurrency" - fully serial execution. + * Setting it to a value more than 1 enables concurrent prerendering. + * Setting it to a value higher than one can increase the speed of the build, + * but may consume more resources, and send more concurrent requests to the + * server/CMS. + */ + prerender?: + | PrerenderPaths + | { + paths: PrerenderPaths; + concurrency?: number; + }; + /** + * An array of React Router plugin config presets to ease integration with + * other platforms and tools. + */ + presets?: Array; + /** + * Control the "Lazy Route Discovery" behavior + * + * - `routeDiscovery.mode`: By default, this resolves to `lazy` which will + * lazily discover routes as the user navigates around your application. + * You can set this to `initial` to opt-out of this behavior and load all + * routes with the initial HTML document load. + * - `routeDiscovery.manifestPath`: The path to serve the manifest file from. + * Only applies to `mode: "lazy"` and defaults to `/__manifest`. + */ + routeDiscovery?: + | { + mode: "lazy"; + manifestPath?: string; + } + | { + mode: "initial"; + }; + /** + * The file name of the server build output. This file + * should end in a `.js` extension and should be deployed to your server. + * Defaults to `"index.js"`. + */ + serverBuildFile?: string; + /** + * A function for assigning routes to different server bundles. This + * function should return a server bundle ID which will be used as the + * bundle's directory name within the server build directory. + */ + serverBundles?: ServerBundlesFunction; + /** + * Enable server-side rendering for your application. Disable to use "SPA + * Mode", which will request the `/` path at build-time and save it as an + * `index.html` file with your assets so your application can be deployed as a + * SPA without server-rendering. Default's to `true`. + */ + ssr?: boolean; + + /** + * Enable subresource integrity hashes on asset script tags. Defaults to + * `false`. + */ + subResourceIntegrity?: boolean; + + /** + * An array of allowed origin hosts for action submissions to UI routes (does not apply + * to resource routes). Supports micromatch glob patterns (`*` to match one segment, + * `**` to match multiple). + * + * ```tsx + * export default { + * allowedActionOrigins: [ + * "example.com", + * "*.example.com", // sub.example.com + * "**.example.com", // sub.domain.example.com + * ], + * } satisfies Config; + * ``` + * + * If you need to set this value at runtime, you can do in by setting the value + * on the server build in your custom server. For example, when using `express`: + * + * ```ts + * import express from "express"; + * import { createRequestHandler } from "@react-router/express"; + * import type { ServerBuild } from "react-router"; + * + * export const app = express(); + * + * async function getBuild() { + * let build: ServerBuild = await import( + * "virtual:react-router/server-build" + * ); + * return { + * ...build, + * allowedActionOrigins: + * process.env.NODE_ENV === "development" + * ? undefined + * : ["staging.example.com", "www.example.com"], + * }; + * } + * + * app.use(createRequestHandler({ build: getBuild })); + */ + allowedActionOrigins?: string[]; +}; + +export type ResolvedReactRouterConfig = Readonly<{ + /** + * The absolute path to the application source directory. + */ + appDirectory: string; + /** + * The React Router app basename. Defaults to `"/"`. + */ + basename: string; + /** + * The absolute path to the build directory. + */ + buildDirectory: string; + /** + * A function that is called after the full React Router build is complete. + */ + buildEnd?: BuildEndHook; + /** + * Enabled future flags + */ + future: FutureConfig; + /** + * An array of URLs to prerender to HTML files at build time. Can also be a + * function returning an array to dynamically generate URLs. + */ + prerender: ReactRouterConfig["prerender"]; + /** + * Control the "Lazy Route Discovery" behavior + * + * - `routeDiscovery.mode`: By default, this resolves to `lazy` which will + * lazily discover routes as the user navigates around your application. + * You can set this to `initial` to opt-out of this behavior and load all + * routes with the initial HTML document load. + * - `routeDiscovery.manifestPath`: The path to serve the manifest file from. + * Only applies to `mode: "lazy"` and defaults to `/__manifest`. + */ + routeDiscovery: ReactRouterConfig["routeDiscovery"]; + /** + * An object of all available routes, keyed by route id. + */ + routes: RouteManifest; + /** + * The file name of the server build output. This file + * should end in a `.js` extension and should be deployed to your server. + * Defaults to `"index.js"`. + */ + serverBuildFile: string; + /** + * A function for assigning routes to different server bundles. This + * function should return a server bundle ID which will be used as the + * bundle's directory name within the server build directory. + */ + serverBundles?: ServerBundlesFunction; + /** + * The output format of the server build. Defaults to "esm". + */ + serverModuleFormat: ServerModuleFormat; + /** + * Enable server-side rendering for your application. Disable to use "SPA + * Mode", which will request the `/` path at build-time and save it as an + * `index.html` file with your assets so your application can be deployed as a + * SPA without server-rendering. Default's to `true`. + */ + ssr: boolean; + /** + * Whether to generate subresource integrity hashes for asset script tags. + */ + subResourceIntegrity: boolean; + /** + * The allowed origins for actions / mutations. Does not apply to routes + * without a component. micromatch glob patterns are supported. + */ + allowedActionOrigins: string[] | false; + /** + * The resolved array of route config entries exported from `routes.ts` + */ + unstable_routeConfig: RouteConfigEntry[]; +}>; + +let mergeReactRouterConfig = ( + ...configs: ReactRouterConfig[] +): ReactRouterConfig => { + let reducer = ( + configA: ReactRouterConfig, + configB: ReactRouterConfig, + ): ReactRouterConfig => { + let mergeRequired = (key: keyof ReactRouterConfig) => + configA[key] !== undefined && configB[key] !== undefined; + + return { + ...configA, + ...configB, + ...(mergeRequired("buildEnd") + ? { + buildEnd: async (...args) => { + await Promise.all([ + configA.buildEnd?.(...args), + configB.buildEnd?.(...args), + ]); + }, + } + : {}), + ...(mergeRequired("future") + ? { + future: { + ...configA.future, + ...configB.future, + }, + } + : {}), + ...(mergeRequired("presets") + ? { + presets: [...(configA.presets ?? []), ...(configB.presets ?? [])], + } + : {}), + }; + }; + + return configs.reduce(reducer, {}); +}; + +// Inlined from https://github.com/jsdf/deep-freeze +let deepFreeze = (o: any) => { + Object.freeze(o); + let oIsFunction = typeof o === "function"; + let hasOwnProp = Object.prototype.hasOwnProperty; + Object.getOwnPropertyNames(o).forEach(function (prop) { + if ( + hasOwnProp.call(o, prop) && + (oIsFunction + ? prop !== "caller" && prop !== "callee" && prop !== "arguments" + : true) && + o[prop] !== null && + (typeof o[prop] === "object" || typeof o[prop] === "function") && + !Object.isFrozen(o[prop]) + ) { + deepFreeze(o[prop]); + } + }); + return o; +}; + +type Result = + | { + ok: true; + value: T; + error?: undefined; + } + | { + ok: false; + value?: undefined; + error: string; + }; + +type ConfigResult = Result; + +function ok(value: T): Result { + return { ok: true, value }; +} + +function err(error: string): Result { + return { ok: false, error }; +} + +async function resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, +}: { + root: string; + viteNodeContext: ViteNode.Context; + reactRouterConfigFile?: string; + skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; + shouldLogFutureFlagWarnings?: boolean; +}): Promise { + let reactRouterUserConfig: ReactRouterConfig = {}; + + if (reactRouterConfigFile) { + try { + if (!fs.existsSync(reactRouterConfigFile)) { + return err(`${reactRouterConfigFile} no longer exists`); + } + + let configModule = await viteNodeContext.runner.executeFile( + reactRouterConfigFile, + ); + + if (configModule.default === undefined) { + return err(`${reactRouterConfigFile} must provide a default export`); + } + + if (typeof configModule.default !== "object") { + return err(`${reactRouterConfigFile} must export a config`); + } + + reactRouterUserConfig = configModule.default; + + if (validateConfig) { + const error = validateConfig(reactRouterUserConfig); + if (error) { + return err(error); + } + } + } catch (error) { + return err(`Error loading ${reactRouterConfigFile}: ${error}`); + } + } + + // Prevent mutations to the user config + reactRouterUserConfig = deepFreeze(cloneDeep(reactRouterUserConfig)); + + let presets: ReactRouterConfig[] = ( + await Promise.all( + (reactRouterUserConfig.presets ?? []).map(async (preset) => { + if (!preset.name) { + throw new Error( + "React Router presets must have a `name` property defined.", + ); + } + + if (!preset.reactRouterConfig) { + return null; + } + + let configPreset: ReactRouterConfig = omit( + await preset.reactRouterConfig({ reactRouterUserConfig }), + excludedConfigPresetKeys, + ); + + return configPreset; + }), + ) + ).filter(function isNotNull(value: T | null): value is T { + return value !== null; + }); + + let defaults = { + basename: "/", + buildDirectory: "build", + serverBuildFile: "index.js", + serverModuleFormat: "esm", + ssr: true, + } as const satisfies Partial; + + let userAndPresetConfigs = mergeReactRouterConfig( + ...presets, + reactRouterUserConfig, + ); + + let { + appDirectory: userAppDirectory, + basename, + buildDirectory: userBuildDirectory, + buildEnd, + prerender, + routeDiscovery: userRouteDiscovery, + serverBuildFile, + serverBundles, + serverModuleFormat, + ssr, + } = { + ...defaults, // Default values should be completely overridden by user/preset config, not merged + ...userAndPresetConfigs, + }; + + if (!ssr && serverBundles) { + serverBundles = undefined; + } + + if (prerender) { + let isValidPrerenderPathsConfig = (p: unknown) => + typeof p === "boolean" || typeof p === "function" || Array.isArray(p); + + let isValidPrerenderConfig = + isValidPrerenderPathsConfig(prerender) || + (typeof prerender === "object" && + "paths" in prerender && + isValidPrerenderPathsConfig(prerender.paths)); + + if (!isValidPrerenderConfig) { + return err( + "The `prerender`/`prerender.paths` config must be a boolean, an array " + + "of string paths, or a function returning a boolean or array of string paths.", + ); + } + + if (typeof prerender === "object" && "unstable_concurrency" in prerender) { + return err( + "The `prerender.unstable_concurrency` config field has been stabilized as `prerender.concurrency`", + ); + } + + let isValidConcurrencyConfig = + typeof prerender != "object" || + !("concurrency" in prerender) || + (typeof prerender.concurrency === "number" && + Number.isInteger(prerender.concurrency) && + prerender.concurrency > 0); + + if (!isValidConcurrencyConfig) { + return err( + "The `prerender.concurrency` config must be a positive integer if specified.", + ); + } + } + + let routeDiscovery: ResolvedReactRouterConfig["routeDiscovery"]; + if (userRouteDiscovery == null) { + if (ssr) { + routeDiscovery = { + mode: "lazy", + manifestPath: "/__manifest", + }; + } else { + routeDiscovery = { mode: "initial" }; + } + } else if (userRouteDiscovery.mode === "initial") { + routeDiscovery = userRouteDiscovery; + } else if (userRouteDiscovery.mode === "lazy") { + if (!ssr) { + return err( + 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`', + ); + } + + let { manifestPath } = userRouteDiscovery; + if (manifestPath != null && !manifestPath.startsWith("/")) { + return err( + "The `routeDiscovery.manifestPath` config must be a root-relative " + + 'pathname beginning with a slash (i.e., "/__manifest")', + ); + } + + routeDiscovery = userRouteDiscovery; + } + + let appDirectory = Path.resolve(root, userAppDirectory || "app"); + let buildDirectory = Path.resolve(root, userBuildDirectory); + + let rootRouteFile = findEntry(appDirectory, "root", { absolute: true }); + if (!rootRouteFile) { + let rootRouteDisplayPath = Path.relative( + root, + Path.join(appDirectory, "root.tsx"), + ); + return err( + `Could not find a root route module in the app directory as "${rootRouteDisplayPath}"`, + ); + } + + let routes: RouteManifest; + let routeConfig: RouteConfigEntry[] = []; + + if (skipRoutes) { + routes = {}; + } else { + let routeConfigFile = findEntry(appDirectory, "routes"); + + try { + if (!routeConfigFile) { + let routeConfigDisplayPath = Path.relative( + root, + Path.join(appDirectory, "routes.ts"), + ); + return err( + `Route config file not found at "${routeConfigDisplayPath}".`, + ); + } + + setAppDirectory(appDirectory); + let routeConfigExport = ( + await viteNodeContext.runner.executeFile( + Path.join(appDirectory, routeConfigFile), + ) + ).default; + let result = validateRouteConfig({ + routeConfigFile, + routeConfig: await routeConfigExport, + }); + + if (!result.valid) { + return err(result.message); + } + + // Nest the route config under the resolved root route + routeConfig = [ + { + id: "root", + path: "", + file: Path.relative(appDirectory, rootRouteFile), + children: result.routeConfig, + }, + ]; + + routes = configRoutesToRouteManifest(appDirectory, routeConfig); + } catch (error: any) { + return err( + [ + colors.red(`Route config in "${routeConfigFile}" is invalid.`), + "", + error.loc?.file && error.loc?.column && error.frame + ? [ + Path.relative(appDirectory, error.loc.file) + + ":" + + error.loc.line + + ":" + + error.loc.column, + error.frame.trim?.(), + ] + : error.stack, + ] + .flat() + .join("\n"), + ); + } + } + + // Check for renamed flags and provide helpful error messages + let futureConfig = userAndPresetConfigs.future; + if (futureConfig) { + if ("unstable_splitRouteModules" in futureConfig) { + return err( + "The `future.unstable_splitRouteModules` flag has been stabilized as `future.v8_splitRouteModules`", + ); + } + if ("unstable_viteEnvironmentApi" in futureConfig) { + return err( + "The `future.unstable_viteEnvironmentApi` flag has been stabilized as `future.v8_viteEnvironmentApi`", + ); + } + if ("unstable_passThroughRequests" in futureConfig) { + return err( + "The `future.unstable_passThroughRequests` flag has been stabilized as `future.v8_passThroughRequests`", + ); + } + if ("unstable_trailingSlashAwareDataRequests" in futureConfig) { + return err( + "The `future.unstable_trailingSlashAwareDataRequests` flag has been stabilized as `future.v8_trailingSlashAwareDataRequests`", + ); + } + if ("unstable_subResourceIntegrity" in futureConfig) { + return err( + "The `future.unstable_subResourceIntegrity` flag has been stabilized and moved to a top-level `config.subResourceIntegrity` field", + ); + } + } + + let future: FutureConfig = { + unstable_optimizeDeps: + userAndPresetConfigs.future?.unstable_optimizeDeps ?? false, + v8_passThroughRequests: + userAndPresetConfigs.future?.v8_passThroughRequests ?? false, + v8_trailingSlashAwareDataRequests: + userAndPresetConfigs.future?.v8_trailingSlashAwareDataRequests ?? false, + unstable_previewServerPrerendering: + userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false, + v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false, + v8_splitRouteModules: + userAndPresetConfigs.future?.v8_splitRouteModules ?? false, + v8_viteEnvironmentApi: + (userAndPresetConfigs.future?.v8_viteEnvironmentApi || + userAndPresetConfigs.future?.unstable_previewServerPrerendering) ?? + false, + }; + + let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false; + let subResourceIntegrity = userAndPresetConfigs.subResourceIntegrity ?? false; + + let reactRouterConfig: ResolvedReactRouterConfig = deepFreeze({ + appDirectory, + basename, + buildDirectory, + buildEnd, + future, + prerender, + routes, + routeDiscovery, + serverBuildFile, + serverBundles, + serverModuleFormat, + ssr, + subResourceIntegrity, + allowedActionOrigins, + unstable_routeConfig: routeConfig, + } satisfies ResolvedReactRouterConfig); + + for (let preset of reactRouterUserConfig.presets ?? []) { + await preset.reactRouterConfigResolved?.({ reactRouterConfig }); + } + + if (shouldLogFutureFlagWarnings) { + logFutureFlagWarnings(userAndPresetConfigs.future || {}); + } + + return ok(reactRouterConfig); +} + +function logFutureFlagWarning(flag: string, message: string): void { + console.log( + colors.yellow( + ` ⚠️ Future Flag Warning: ${message}\n` + + ` You can use the \`future.${flag}\` flag to opt in early.\n` + + ` -> https://reactrouter.com/7.16.0/upgrading/future#future${flag.toLowerCase()}`, + ), + ); +} + +export function logFutureFlagWarnings(future: Partial): void { + if (future.v8_middleware === undefined) { + logFutureFlagWarning( + "v8_middleware", + "Route middleware support is changing in React Router v8.", + ); + } + if (future.v8_splitRouteModules === undefined) { + logFutureFlagWarning( + "v8_splitRouteModules", + "Route module splitting behavior is changing in React Router v8.", + ); + } + if (future.v8_viteEnvironmentApi === undefined) { + logFutureFlagWarning( + "v8_viteEnvironmentApi", + "Vite Environment API usage is changing in React Router v8.", + ); + } + if (future.v8_passThroughRequests === undefined) { + logFutureFlagWarning( + "v8_passThroughRequests", + "Request handling behavior is changing in React Router v8.", + ); + } + if (future.v8_trailingSlashAwareDataRequests === undefined) { + logFutureFlagWarning( + "v8_trailingSlashAwareDataRequests", + "Data request URL formats are changing in React Router v8.", + ); + } +} + +type ChokidarEventName = ChokidarEmitArgs[0]; + +type ChangeHandler = (args: { + result: ConfigResult; + configCodeChanged: boolean; + routeConfigCodeChanged: boolean; + configChanged: boolean; + routeConfigChanged: boolean; + path: string; + event: ChokidarEventName; +}) => void; + +export type ConfigLoader = { + getConfig: () => Promise; + onChange: (handler: ChangeHandler) => () => void; + close: () => Promise; +}; + +export async function createConfigLoader({ + rootDirectory: root, + watch, + mode, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, +}: { + watch: boolean; + rootDirectory?: string; + mode: string; + skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; + shouldLogFutureFlagWarnings?: boolean; +}): Promise { + root = Path.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd()); + + let vite = await import("vite"); + let viteNodeContext = await ViteNode.createContext({ + root, + mode, + // Filter out any info level logs from vite-node + customLogger: vite.createLogger("warn", { + prefix: "[react-router]", + }), + }); + + let reactRouterConfigFile: string | undefined; + + let updateReactRouterConfigFile = () => { + reactRouterConfigFile = findEntry(root, "react-router.config", { + absolute: true, + }); + }; + + updateReactRouterConfigFile(); + + let getConfig = () => + resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + shouldLogFutureFlagWarnings, + }); + + let appDirectory: string; + + let initialConfigResult = await resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + }); + + if (!initialConfigResult.ok) { + throw new Error(initialConfigResult.error); + } + + appDirectory = Path.normalize(initialConfigResult.value.appDirectory); + + let currentConfig = initialConfigResult.value; + + let fsWatcher: FSWatcher | undefined; + let changeHandlers: ChangeHandler[] = []; + + return { + getConfig, + onChange: (handler: ChangeHandler) => { + if (!watch) { + throw new Error( + "onChange is not supported when watch mode is disabled", + ); + } + + changeHandlers.push(handler); + + if (!fsWatcher) { + fsWatcher = chokidar.watch([root, appDirectory], { + ignoreInitial: true, + ignored: (path) => isIgnoredByWatcher(path, { root, appDirectory }), + }); + + fsWatcher.on("error", (error: unknown) => { + let message = error instanceof Error ? error.message : String(error); + console.warn(colors.yellow(`File watcher error: ${message}`)); + }); + + fsWatcher.on("all", async (...args) => { + let [event, rawFilepath] = args; + let filepath = Path.normalize(rawFilepath); + + let fileAddedOrRemoved = event === "add" || event === "unlink"; + + let appFileAddedOrRemoved = + fileAddedOrRemoved && + filepath.startsWith(Path.normalize(appDirectory)); + + let rootRelativeFilepath = Path.relative(root, filepath); + + let configFileAddedOrRemoved = + fileAddedOrRemoved && + isEntryFile("react-router.config", rootRelativeFilepath); + + if (configFileAddedOrRemoved) { + updateReactRouterConfigFile(); + } + + let moduleGraphChanged = + configFileAddedOrRemoved || + Boolean( + viteNodeContext.devServer?.moduleGraph.getModuleById(filepath), + ); + + // Bail out if no relevant changes detected + if (!moduleGraphChanged && !appFileAddedOrRemoved) { + return; + } + + viteNodeContext.devServer?.moduleGraph.invalidateAll(); + viteNodeContext.runner?.moduleCache.clear(); + + let result = await getConfig(); + + let prevAppDirectory = appDirectory; + appDirectory = Path.normalize( + (result.value ?? currentConfig).appDirectory, + ); + + if (appDirectory !== prevAppDirectory) { + fsWatcher!.unwatch(prevAppDirectory); + fsWatcher!.add(appDirectory); + } + + let configCodeChanged = + configFileAddedOrRemoved || + (reactRouterConfigFile !== undefined && + isEntryFileDependency( + viteNodeContext.devServer.moduleGraph, + reactRouterConfigFile, + filepath, + )); + + let routeConfigFile = !skipRoutes + ? findEntry(appDirectory, "routes", { + absolute: true, + }) + : undefined; + let routeConfigCodeChanged = + routeConfigFile !== undefined && + isEntryFileDependency( + viteNodeContext.devServer.moduleGraph, + routeConfigFile, + filepath, + ); + + let configChanged = + result.ok && + !isEqual(omitRoutes(currentConfig), omitRoutes(result.value)); + + let routeConfigChanged = + result.ok && !isEqual(currentConfig?.routes, result.value.routes); + + for (let handler of changeHandlers) { + handler({ + result, + configCodeChanged, + routeConfigCodeChanged, + configChanged, + routeConfigChanged, + path: filepath, + event, + }); + } + + if (result.ok) { + currentConfig = result.value; + } + }); + } + + return () => { + changeHandlers = changeHandlers.filter( + (changeHandler) => changeHandler !== handler, + ); + }; + }, + close: async () => { + changeHandlers = []; + await viteNodeContext.devServer.close(); + await fsWatcher?.close(); + }, + }; +} + +export async function loadConfig({ + rootDirectory, + mode, + skipRoutes, +}: { + rootDirectory: string; + mode: string; + skipRoutes?: boolean; +}) { + let configLoader = await createConfigLoader({ + rootDirectory, + mode, + skipRoutes, + watch: false, + }); + let config = await configLoader.getConfig(); + await configLoader.close(); + return config; +} + +export async function resolveEntryFiles({ + rootDirectory, + reactRouterConfig, +}: { + rootDirectory: string; + reactRouterConfig: ResolvedReactRouterConfig; +}) { + let { appDirectory } = reactRouterConfig; + + let defaultsDirectory = Path.resolve( + Path.dirname(require.resolve("@react-router/dev/package.json")), + "dist", + "config", + "defaults", + ); + + let userEntryClientFile = findEntry(appDirectory, "entry.client"); + let userEntryServerFile = findEntry(appDirectory, "entry.server"); + + let entryServerFile: string; + let entryClientFile = userEntryClientFile || "entry.client.tsx"; + + if (userEntryServerFile) { + entryServerFile = userEntryServerFile; + } else { + let packageJsonPath = findEntry(rootDirectory, "package", { + extensions: [".json"], + absolute: true, + walkParents: true, + }); + + if (!packageJsonPath) { + throw new Error( + `Could not find package.json in ${rootDirectory} or any of its parent directories. Please add a package.json, or provide a custom entry.server.tsx/jsx file in your app directory.`, + ); + } + + // TODO(v8): Remove - only required for Node 20.18 and below + let { readPackageJSON, sortPackage, updatePackage } = await import( + "pkg-types" + ); + let packageJsonDirectory = Path.dirname(packageJsonPath); + let pkgJson = await readPackageJSON(packageJsonDirectory); + let deps = pkgJson.dependencies ?? {}; + + if (!deps["@react-router/node"]) { + throw new Error( + `Could not determine server runtime. Please install @react-router/node, or provide a custom entry.server.tsx/jsx file in your app directory.`, + ); + } + + if (!deps["isbot"]) { + console.log( + "adding `isbot@5` to your package.json, you should commit this change", + ); + + await updatePackage(packageJsonPath, (pkg) => { + pkg.dependencies ??= {}; + pkg.dependencies.isbot = "^5"; + sortPackage(pkg); + }); + + let packageManager = detectPackageManager() ?? "npm"; + + execSync(`${packageManager} install`, { + cwd: packageJsonDirectory, + stdio: "inherit", + }); + } + + entryServerFile = `entry.server.node.tsx`; + } + + let entryClientFilePath = userEntryClientFile + ? Path.resolve(reactRouterConfig.appDirectory, userEntryClientFile) + : Path.resolve(defaultsDirectory, entryClientFile); + + let entryServerFilePath = userEntryServerFile + ? Path.resolve(reactRouterConfig.appDirectory, userEntryServerFile) + : Path.resolve(defaultsDirectory, entryServerFile); + + return { entryClientFilePath, entryServerFilePath }; +} + +export async function resolveRSCEntryFiles({ + reactRouterConfig, +}: { + reactRouterConfig: ResolvedReactRouterConfig; +}) { + let { appDirectory } = reactRouterConfig; + + let defaultsDirectory = Path.resolve( + Path.dirname(require.resolve("@react-router/dev/package.json")), + "dist", + "config", + "default-rsc-entries", + ); + + let userEntryClientFile = findEntry(appDirectory, "entry.client", { + absolute: true, + }); + let userEntryRSCFile = findEntry(appDirectory, "entry.rsc", { + absolute: true, + }); + let userEntrySSRFile = findEntry(appDirectory, "entry.ssr", { + absolute: true, + }); + + let client = + userEntryClientFile ?? Path.join(defaultsDirectory, "entry.client.tsx"); + let rsc = userEntryRSCFile ?? Path.join(defaultsDirectory, "entry.rsc.tsx"); + let ssr = userEntrySSRFile ?? Path.join(defaultsDirectory, "entry.ssr.tsx"); + + return { client, rsc, ssr }; +} + +function omitRoutes( + config: ResolvedReactRouterConfig, +): ResolvedReactRouterConfig { + return { + ...config, + routes: {}, + }; +} + +const entryExts = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".mts"]; + +function isEntryFile(entryBasename: string, filename: string) { + return entryExts.some((ext) => filename === `${entryBasename}${ext}`); +} + +function findEntry( + dir: string, + basename: string, + options?: { + absolute?: boolean; + extensions?: string[]; + walkParents?: boolean; + }, +): string | undefined { + let currentDir = Path.resolve(dir); + let { root } = Path.parse(currentDir); + + while (true) { + for (let ext of options?.extensions ?? entryExts) { + let file = Path.resolve(currentDir, basename + ext); + if (fs.existsSync(file)) { + return (options?.absolute ?? false) ? file : Path.relative(dir, file); + } + } + + if (!options?.walkParents) { + return undefined; + } + + let parentDir = Path.dirname(currentDir); + // Break out when we've reached the root directory or we're about to get + // stuck in a loop where `path.dirname` keeps returning "/" + if (currentDir === root || parentDir === currentDir) { + return undefined; + } + + currentDir = parentDir; + } +} + +function isEntryFileDependency( + moduleGraph: Vite.ModuleGraph, + entryFilepath: string, + filepath: string, + visited = new Set(), +): boolean { + // Ensure normalized paths + entryFilepath = Path.normalize(entryFilepath); + filepath = Path.normalize(filepath); + + if (visited.has(filepath)) { + return false; + } + + visited.add(filepath); + + if (filepath === entryFilepath) { + return true; + } + + let mod = moduleGraph.getModuleById(filepath); + + if (!mod) { + return false; + } + + // Recursively check all importers to see if any of them are the entry file + for (let importer of mod.importers) { + if (!importer.id) { + continue; + } + + if ( + importer.id === entryFilepath || + isEntryFileDependency(moduleGraph, entryFilepath, importer.id, visited) + ) { + return true; + } + } + + return false; +} + +export function isIgnoredByWatcher( + path: string, + { root, appDirectory }: { root: string; appDirectory: string }, +): boolean { + let dirname = Path.dirname(path); + + let ignoredByPath = + !dirname.startsWith(appDirectory) && + // Ensure we're only watching files outside of the app directory + // that are at the root level, not nested in subdirectories + path !== root && // Watch the root directory itself + dirname !== root; // Watch files at the root level + + if (ignoredByPath) { + return true; + } + + // Filter out non-regular files (sockets, pipes, etc.) that + // crash `fs.watch()` on macOS with errno -102 + // https://github.com/paulmillr/chokidar/issues/1391 + try { + let stat = fs.statSync(path, { throwIfNoEntry: false }); + if (stat && !stat.isFile() && !stat.isDirectory()) { + return true; + } + } catch { + return true; + } + + return false; +} diff --git a/tests/testdata/ts_trpc_chore_export/expected.json b/tests/testdata/ts_trpc_chore_export/expected.json new file mode 100644 index 0000000..d35d858 --- /dev/null +++ b/tests/testdata/ts_trpc_chore_export/expected.json @@ -0,0 +1,904 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 14 + ], + "type": "export_statement", + "start_byte": 932, + "end_byte": 1608 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 6964 + }, + "position": 14 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 14, + 0 + ], + "type": "export", + "label": "export", + "start_byte": 932, + "end_byte": 938 + }, + "parent": { + "tree": "after", + "path": [ + 14 + ], + "type": "export_statement", + "start_byte": 932, + "end_byte": 1608 + }, + "position": 0 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 14 + ], + "type": "type_alias_declaration", + "start_byte": 932, + "end_byte": 1601 + }, + "parent": { + "tree": "after", + "path": [ + 14 + ], + "type": "export_statement", + "start_byte": 932, + "end_byte": 1608 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 6957 + }, + "old_position": 14, + "subtree": true, + "dest_start_byte": 939, + "dest_end_byte": 1608 + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_chore_export/metadata.json b/tests/testdata/ts_trpc_chore_export/metadata.json new file mode 100644 index 0000000..92fef84 --- /dev/null +++ b/tests/testdata/ts_trpc_chore_export/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/trpc/trpc", + "parent": "9111f0bab80a3f25384b5930b56005c2292cb874", + "commit": "62c6ab085f2ab0219811215436427fee2bc85c47", + "message": "chore: export `HTTPBatchStreamLinkOptions` (#7250)", + "author": "Anatol Zakrividoroga", + "date": "2026-03-15T11:58:04Z", + "language": "typescript", + "files_changed": 1, + "insertions": 0, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_chore_export/new.ts b/tests/testdata/ts_trpc_chore_export/new.ts new file mode 100644 index 0000000..719a0b0 --- /dev/null +++ b/tests/testdata/ts_trpc_chore_export/new.ts @@ -0,0 +1,204 @@ +import type { AnyRouter, ProcedureType } from '@trpc/server'; +import { observable } from '@trpc/server/observable'; +import type { TRPCErrorShape, TRPCResponse } from '@trpc/server/rpc'; +import type { AnyClientTypes } from '@trpc/server/unstable-core-do-not-import'; +import { jsonlStreamConsumer } from '@trpc/server/unstable-core-do-not-import'; +import type { BatchLoader } from '../internals/dataLoader'; +import { dataLoader } from '../internals/dataLoader'; +import { allAbortSignals, raceAbortSignals } from '../internals/signals'; +import type { NonEmptyArray } from '../internals/types'; +import { TRPCClientError } from '../TRPCClientError'; +import type { HTTPBatchLinkOptions } from './HTTPBatchLinkOptions'; +import type { HTTPResult } from './internals/httpUtils'; +import { + fetchHTTPResponse, + getBody, + getUrl, + resolveHTTPLinkOptions, +} from './internals/httpUtils'; +import type { Operation, TRPCLink } from './types'; + +export type HTTPBatchStreamLinkOptions = + HTTPBatchLinkOptions & { + /** + * Which header to use to signal the server that the client wants a streaming response. + * - `'trpc-accept'` (default): sends `trpc-accept: application/jsonl` header + * - `'accept'`: sends `Accept: application/jsonl` header, which can avoid CORS preflight for cross-origin streaming queries. Be aware that `application/jsonl` is not an official MIME type and so this is not completely spec-compliant - you should test that your infrastructure supports this value. + * @default 'trpc-accept' + */ + streamHeader?: 'trpc-accept' | 'accept'; + }; + +/** + * @see https://trpc.io/docs/client/links/httpBatchStreamLink + */ +export function httpBatchStreamLink( + opts: HTTPBatchStreamLinkOptions, +): TRPCLink { + const resolvedOpts = resolveHTTPLinkOptions(opts); + const maxURLLength = opts.maxURLLength ?? Infinity; + const maxItems = opts.maxItems ?? Infinity; + + return () => { + const batchLoader = ( + type: ProcedureType, + ): BatchLoader => { + return { + validate(batchOps) { + if (maxURLLength === Infinity && maxItems === Infinity) { + // escape hatch for quick calcs + return true; + } + if (batchOps.length > maxItems) { + return false; + } + const path = batchOps.map((op) => op.path).join(','); + const inputs = batchOps.map((op) => op.input); + + const url = getUrl({ + ...resolvedOpts, + type, + path, + inputs, + signal: null, + }); + + return url.length <= maxURLLength; + }, + async fetch(batchOps) { + const path = batchOps.map((op) => op.path).join(','); + const inputs = batchOps.map((op) => op.input); + + const batchSignals = allAbortSignals( + ...batchOps.map((op) => op.signal), + ); + const abortController = new AbortController(); + + const responsePromise = fetchHTTPResponse({ + ...resolvedOpts, + signal: raceAbortSignals(batchSignals, abortController.signal), + type, + contentTypeHeader: 'application/json', + trpcAcceptHeader: 'application/jsonl', + trpcAcceptHeaderKey: opts.streamHeader ?? 'trpc-accept', + getUrl, + getBody, + inputs, + path, + headers() { + if (!opts.headers) { + return {}; + } + if (typeof opts.headers === 'function') { + return opts.headers({ + opList: batchOps as NonEmptyArray, + }); + } + return opts.headers; + }, + }); + + const res = await responsePromise; + const [head] = await jsonlStreamConsumer< + Record> + >({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + from: res.body!, + deserialize: (data) => + resolvedOpts.transformer.output.deserialize(data), + // onError: console.error, + formatError(opts) { + const error = opts.error as TRPCErrorShape; + return TRPCClientError.from({ + error, + }); + }, + abortController, + }); + const promises = Object.keys(batchOps).map( + async (key): Promise => { + let json: TRPCResponse = await Promise.resolve(head[key]); + + if ('result' in json) { + /** + * Not very pretty, but we need to unwrap nested data as promises + * Our stream producer will only resolve top-level async values or async values that are directly nested in another async value + */ + const result = await Promise.resolve(json.result); + json = { + result: { + data: await Promise.resolve(result.data), + }, + }; + } + + return { + json, + meta: { + response: res, + }, + }; + }, + ); + return promises; + }, + }; + }; + + const query = dataLoader(batchLoader('query')); + const mutation = dataLoader(batchLoader('mutation')); + + const loaders = { query, mutation }; + return ({ op }) => { + return observable((observer) => { + /* istanbul ignore if -- @preserve */ + if (op.type === 'subscription') { + throw new Error( + 'Subscriptions are unsupported by `httpBatchStreamLink` - use `httpSubscriptionLink` or `wsLink`', + ); + } + const loader = loaders[op.type]; + const promise = loader.load(op); + + let _res = undefined as HTTPResult | undefined; + promise + .then((res) => { + _res = res; + if ('error' in res.json) { + observer.error( + TRPCClientError.from(res.json, { + meta: res.meta, + }), + ); + return; + } else if ('result' in res.json) { + observer.next({ + context: res.meta, + result: res.json.result, + }); + observer.complete(); + return; + } + + observer.complete(); + }) + .catch((err) => { + observer.error( + TRPCClientError.from(err, { + meta: _res?.meta, + }), + ); + }); + + return () => { + // noop + }; + }); + }; + }; +} + +/** + * @deprecated use {@link httpBatchStreamLink} instead + */ +export const unstable_httpBatchStreamLink = httpBatchStreamLink; diff --git a/tests/testdata/ts_trpc_chore_export/old.ts b/tests/testdata/ts_trpc_chore_export/old.ts new file mode 100644 index 0000000..74082c6 --- /dev/null +++ b/tests/testdata/ts_trpc_chore_export/old.ts @@ -0,0 +1,204 @@ +import type { AnyRouter, ProcedureType } from '@trpc/server'; +import { observable } from '@trpc/server/observable'; +import type { TRPCErrorShape, TRPCResponse } from '@trpc/server/rpc'; +import type { AnyClientTypes } from '@trpc/server/unstable-core-do-not-import'; +import { jsonlStreamConsumer } from '@trpc/server/unstable-core-do-not-import'; +import type { BatchLoader } from '../internals/dataLoader'; +import { dataLoader } from '../internals/dataLoader'; +import { allAbortSignals, raceAbortSignals } from '../internals/signals'; +import type { NonEmptyArray } from '../internals/types'; +import { TRPCClientError } from '../TRPCClientError'; +import type { HTTPBatchLinkOptions } from './HTTPBatchLinkOptions'; +import type { HTTPResult } from './internals/httpUtils'; +import { + fetchHTTPResponse, + getBody, + getUrl, + resolveHTTPLinkOptions, +} from './internals/httpUtils'; +import type { Operation, TRPCLink } from './types'; + +type HTTPBatchStreamLinkOptions = + HTTPBatchLinkOptions & { + /** + * Which header to use to signal the server that the client wants a streaming response. + * - `'trpc-accept'` (default): sends `trpc-accept: application/jsonl` header + * - `'accept'`: sends `Accept: application/jsonl` header, which can avoid CORS preflight for cross-origin streaming queries. Be aware that `application/jsonl` is not an official MIME type and so this is not completely spec-compliant - you should test that your infrastructure supports this value. + * @default 'trpc-accept' + */ + streamHeader?: 'trpc-accept' | 'accept'; + }; + +/** + * @see https://trpc.io/docs/client/links/httpBatchStreamLink + */ +export function httpBatchStreamLink( + opts: HTTPBatchStreamLinkOptions, +): TRPCLink { + const resolvedOpts = resolveHTTPLinkOptions(opts); + const maxURLLength = opts.maxURLLength ?? Infinity; + const maxItems = opts.maxItems ?? Infinity; + + return () => { + const batchLoader = ( + type: ProcedureType, + ): BatchLoader => { + return { + validate(batchOps) { + if (maxURLLength === Infinity && maxItems === Infinity) { + // escape hatch for quick calcs + return true; + } + if (batchOps.length > maxItems) { + return false; + } + const path = batchOps.map((op) => op.path).join(','); + const inputs = batchOps.map((op) => op.input); + + const url = getUrl({ + ...resolvedOpts, + type, + path, + inputs, + signal: null, + }); + + return url.length <= maxURLLength; + }, + async fetch(batchOps) { + const path = batchOps.map((op) => op.path).join(','); + const inputs = batchOps.map((op) => op.input); + + const batchSignals = allAbortSignals( + ...batchOps.map((op) => op.signal), + ); + const abortController = new AbortController(); + + const responsePromise = fetchHTTPResponse({ + ...resolvedOpts, + signal: raceAbortSignals(batchSignals, abortController.signal), + type, + contentTypeHeader: 'application/json', + trpcAcceptHeader: 'application/jsonl', + trpcAcceptHeaderKey: opts.streamHeader ?? 'trpc-accept', + getUrl, + getBody, + inputs, + path, + headers() { + if (!opts.headers) { + return {}; + } + if (typeof opts.headers === 'function') { + return opts.headers({ + opList: batchOps as NonEmptyArray, + }); + } + return opts.headers; + }, + }); + + const res = await responsePromise; + const [head] = await jsonlStreamConsumer< + Record> + >({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + from: res.body!, + deserialize: (data) => + resolvedOpts.transformer.output.deserialize(data), + // onError: console.error, + formatError(opts) { + const error = opts.error as TRPCErrorShape; + return TRPCClientError.from({ + error, + }); + }, + abortController, + }); + const promises = Object.keys(batchOps).map( + async (key): Promise => { + let json: TRPCResponse = await Promise.resolve(head[key]); + + if ('result' in json) { + /** + * Not very pretty, but we need to unwrap nested data as promises + * Our stream producer will only resolve top-level async values or async values that are directly nested in another async value + */ + const result = await Promise.resolve(json.result); + json = { + result: { + data: await Promise.resolve(result.data), + }, + }; + } + + return { + json, + meta: { + response: res, + }, + }; + }, + ); + return promises; + }, + }; + }; + + const query = dataLoader(batchLoader('query')); + const mutation = dataLoader(batchLoader('mutation')); + + const loaders = { query, mutation }; + return ({ op }) => { + return observable((observer) => { + /* istanbul ignore if -- @preserve */ + if (op.type === 'subscription') { + throw new Error( + 'Subscriptions are unsupported by `httpBatchStreamLink` - use `httpSubscriptionLink` or `wsLink`', + ); + } + const loader = loaders[op.type]; + const promise = loader.load(op); + + let _res = undefined as HTTPResult | undefined; + promise + .then((res) => { + _res = res; + if ('error' in res.json) { + observer.error( + TRPCClientError.from(res.json, { + meta: res.meta, + }), + ); + return; + } else if ('result' in res.json) { + observer.next({ + context: res.meta, + result: res.json.result, + }); + observer.complete(); + return; + } + + observer.complete(); + }) + .catch((err) => { + observer.error( + TRPCClientError.from(err, { + meta: _res?.meta, + }), + ); + }); + + return () => { + // noop + }; + }); + }; + }; +} + +/** + * @deprecated use {@link httpBatchStreamLink} instead + */ +export const unstable_httpBatchStreamLink = httpBatchStreamLink; diff --git a/tests/testdata/ts_trpc_export_datatransformer/expected.json b/tests/testdata/ts_trpc_export_datatransformer/expected.json new file mode 100644 index 0000000..254eaef --- /dev/null +++ b/tests/testdata/ts_trpc_export_datatransformer/expected.json @@ -0,0 +1,669 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 0, + 1, + 62 + ], + "type": "export_specifier", + "start_byte": 1356, + "end_byte": 1399 + }, + "parent": { + "tree": "after", + "path": [ + 0, + 1 + ], + "type": "export_clause", + "start_byte": 7, + "end_byte": 2812 + }, + "position": 62, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 12 + ], + "type": "comment", + "label": "/**\n * @deprecated use `TRPCDataTransformer` instead\n */", + "start_byte": 3210, + "end_byte": 3270 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2 + ], + "type": "export_clause", + "start_byte": 2868, + "end_byte": 3823 + }, + "position": 12 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 13 + ], + "type": "export_specifier", + "start_byte": 3273, + "end_byte": 3288 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2 + ], + "type": "export_clause", + "start_byte": 2868, + "end_byte": 3823 + }, + "position": 13, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": -1, + "right_line": 35 + }, + { + "left_line": 35, + "right_line": 36 + }, + { + "left_line": 36, + "right_line": 37 + }, + { + "left_line": 37, + "right_line": 38 + }, + { + "left_line": 38, + "right_line": 39 + }, + { + "left_line": 39, + "right_line": 40 + }, + { + "left_line": 40, + "right_line": 41 + }, + { + "left_line": 41, + "right_line": 42 + }, + { + "left_line": 42, + "right_line": 43 + }, + { + "left_line": 43, + "right_line": 44 + }, + { + "left_line": 44, + "right_line": 45 + }, + { + "left_line": 45, + "right_line": 46 + }, + { + "left_line": 46, + "right_line": 47 + }, + { + "left_line": 47, + "right_line": 48 + }, + { + "left_line": 48, + "right_line": 49 + }, + { + "left_line": 49, + "right_line": 50 + }, + { + "left_line": 50, + "right_line": 51 + }, + { + "left_line": 51, + "right_line": 52 + }, + { + "left_line": 52, + "right_line": 53 + }, + { + "left_line": 53, + "right_line": 54 + }, + { + "left_line": 54, + "right_line": 55 + }, + { + "left_line": 55, + "right_line": 56 + }, + { + "left_line": 56, + "right_line": 57 + }, + { + "left_line": 57, + "right_line": 58 + }, + { + "left_line": 58, + "right_line": 59 + }, + { + "left_line": 59, + "right_line": 60 + }, + { + "left_line": 60, + "right_line": 61 + }, + { + "left_line": 61, + "right_line": 62 + }, + { + "left_line": 62, + "right_line": 63 + }, + { + "left_line": 63, + "right_line": 64 + }, + { + "left_line": 64, + "right_line": 65 + }, + { + "left_line": 65, + "right_line": 66 + }, + { + "left_line": 66, + "right_line": 67 + }, + { + "left_line": 67, + "right_line": 68 + }, + { + "left_line": 68, + "right_line": 69 + }, + { + "left_line": 69, + "right_line": 70 + }, + { + "left_line": 70, + "right_line": 71 + }, + { + "left_line": 71, + "right_line": 72 + }, + { + "left_line": 72, + "right_line": 73 + }, + { + "left_line": 73, + "right_line": 74 + }, + { + "left_line": 74, + "right_line": 75 + }, + { + "left_line": 75, + "right_line": 76 + }, + { + "left_line": 76, + "right_line": 77 + }, + { + "left_line": 77, + "right_line": 78 + }, + { + "left_line": 78, + "right_line": 79 + }, + { + "left_line": 79, + "right_line": 80 + }, + { + "left_line": 80, + "right_line": 81 + }, + { + "left_line": 81, + "right_line": 82 + }, + { + "left_line": 82, + "right_line": 83 + }, + { + "left_line": 83, + "right_line": 84 + }, + { + "left_line": 84, + "right_line": 85 + }, + { + "left_line": 85, + "right_line": 86 + }, + { + "left_line": 86, + "right_line": 87 + }, + { + "left_line": 87, + "right_line": 88 + }, + { + "left_line": 88, + "right_line": 89 + }, + { + "left_line": 89, + "right_line": 90 + }, + { + "left_line": 90, + "right_line": 91 + }, + { + "left_line": 91, + "right_line": 92 + }, + { + "left_line": 92, + "right_line": 93 + }, + { + "left_line": 93, + "right_line": 94 + }, + { + "left_line": 94, + "right_line": 95 + }, + { + "left_line": 95, + "right_line": 96 + }, + { + "left_line": 96, + "right_line": 97 + }, + { + "left_line": -1, + "right_line": 98 + }, + { + "left_line": -1, + "right_line": 99 + }, + { + "left_line": -1, + "right_line": 100 + }, + { + "left_line": -1, + "right_line": 101 + }, + { + "left_line": 97, + "right_line": 102 + }, + { + "left_line": 98, + "right_line": 103 + }, + { + "left_line": 99, + "right_line": 104 + }, + { + "left_line": 100, + "right_line": 105 + }, + { + "left_line": 101, + "right_line": 106 + }, + { + "left_line": 102, + "right_line": 107 + }, + { + "left_line": 103, + "right_line": 108 + }, + { + "left_line": 104, + "right_line": 109 + }, + { + "left_line": 105, + "right_line": 110 + }, + { + "left_line": 106, + "right_line": 111 + }, + { + "left_line": 107, + "right_line": 112 + }, + { + "left_line": 108, + "right_line": 113 + }, + { + "left_line": 109, + "right_line": 114 + }, + { + "left_line": 110, + "right_line": 115 + }, + { + "left_line": 111, + "right_line": 116 + }, + { + "left_line": 112, + "right_line": 117 + }, + { + "left_line": 113, + "right_line": 118 + }, + { + "left_line": 114, + "right_line": 119 + }, + { + "left_line": 115, + "right_line": 120 + }, + { + "left_line": 116, + "right_line": 121 + }, + { + "left_line": 117, + "right_line": 122 + }, + { + "left_line": 118, + "right_line": 123 + }, + { + "left_line": 119, + "right_line": 124 + }, + { + "left_line": 120, + "right_line": 125 + }, + { + "left_line": 121, + "right_line": 126 + }, + { + "left_line": 122, + "right_line": 127 + }, + { + "left_line": 123, + "right_line": 128 + }, + { + "left_line": 124, + "right_line": 129 + }, + { + "left_line": 125, + "right_line": 130 + }, + { + "left_line": 126, + "right_line": 131 + }, + { + "left_line": 127, + "right_line": 132 + }, + { + "left_line": 128, + "right_line": 133 + }, + { + "left_line": 129, + "right_line": 134 + }, + { + "left_line": 130, + "right_line": 135 + }, + { + "left_line": 131, + "right_line": 136 + }, + { + "left_line": 132, + "right_line": 137 + }, + { + "left_line": 133, + "right_line": 138 + }, + { + "left_line": 134, + "right_line": 139 + }, + { + "left_line": 135, + "right_line": 140 + }, + { + "left_line": 136, + "right_line": 141 + }, + { + "left_line": 137, + "right_line": 142 + }, + { + "left_line": 138, + "right_line": 143 + }, + { + "left_line": 139, + "right_line": 144 + }, + { + "left_line": 140, + "right_line": 145 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_export_datatransformer/metadata.json b/tests/testdata/ts_trpc_export_datatransformer/metadata.json new file mode 100644 index 0000000..eb07cd9 --- /dev/null +++ b/tests/testdata/ts_trpc_export_datatransformer/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/trpc/trpc", + "parent": "aaf09d0f0f6bee0e580a0c20e8f9afe977b3dd43", + "commit": "e272d689530531a498195276f14dd71bda534afe", + "message": "fix: export DataTransformer interface marked as @public (#6927)\n\n# Pull Request Summary: Export DataTransformer Interface #6916 \n\n## Description\nThis PR resolves an issue where the `DataTransformer` interface was\nmarked as `@public` but not exported from either `@trpc/server` or\n`@trpc/client` packages, preventing users from properly typing their\ncustom transformers.\n\n## Issue Fixed\n- **Problem**: Users couldn't import `DataTransformer` interface despite\nit being marked as public API\n- **Workaround**: Users had to use confusing\n`CombinedDataTransformer[\"input\"]` hack\n- **Root Cause**: Missing exports in package entry points\n- \n##\u00a0 Changes Made\n### Server Package (`@trpc/server`)\n- Added `DataTransformer` export to main index\n- Added `TRPCDataTransformer` alias following naming conventions\n- Added backward compatibility deprecation notice\n\n### Client Package (`@trpc/client`)\u00a0\n- Added convenient re-export of `DataTransformer` from server package\n- Maintains single source of truth\n\n## Testing\n- [x] All packages build successfully\n- [x] Type exports verified in built packages\n- [x] Existing transformer tests pass\n- [x] No breaking changes introduced\n- [x] Backward compatibility maintained\n## Usage\n### Before\n```typescript\nimport type { DataTransformer } from '@trpc/server';\ntype MyTransformer = CombinedDataTransformer[\"input\"];\n```\n### After\n```typescript\nimport type { DataTransformer } from '@trpc/server';\nimport type { DataTransformer } from '@trpc/client';\nconst myTransformer: DataTransformer = {\n\u00a0 serialize: (obj) => obj,\n\u00a0 deserialize: (obj) => obj,\n};\n```\n## Safety\n- **Breaking Changes**: None\n- **Runtime Impact**: None (types only)\n- **Backward Compatibility**: Full\n- **Code Style**: Follows project conventions\n## Contribution Guidelines Compliance\n- [x] Used pnpm for package management\n- [x] Ran `pnpm build` successfully\n- [x] Ran `pnpm lint-fix`\n- [x] Added proper documentation\n- [x] Followed existing code patterns\n- [x] Maintained type safety\n- [x] No cryptic errors introduced\n\n\n\n\n## Summary by CodeRabbit\n\n* New Features\n* Introduced TRPCDataTransformer type alias for clearer naming in server\nexports.\n* Client package now publicly re-exports the DataTransformer type for\nconvenience.\n\n* Documentation\n* Deprecated DataTransformer in favor of TRPCDataTransformer across\npublic exports.\n* Clarified that these are type-only changes with no impact on runtime\nbehavior.\n\n\n\n---------\n\nCo-authored-by: I528989 \nCo-authored-by: Alex / KATT ", + "author": "Bhaskar Kulshrestha", + "date": "2025-09-05T14:30:18Z", + "language": "typescript", + "files_changed": 1, + "insertions": 5, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_export_datatransformer/new.ts b/tests/testdata/ts_trpc_export_datatransformer/new.ts new file mode 100644 index 0000000..7c39514 --- /dev/null +++ b/tests/testdata/ts_trpc_export_datatransformer/new.ts @@ -0,0 +1,145 @@ +export { + TRPCError, + /** + * @deprecated use `experimental_trpcMiddleware` instead + */ + experimental_standaloneMiddleware, + experimental_standaloneMiddleware as experimental_trpcMiddleware, + initTRPC, + // --- FIXME a bunch of these exports are only useful for plugins - move them somewhere else? ---- + getTRPCErrorFromUnknown, + transformTRPCResponse, + createFlatProxy as createTRPCFlatProxy, + createRecursiveProxy as createTRPCRecursiveProxy, + type inferProcedureInput, + type inferProcedureOutput, + type inferProcedureBuilderResolverOptions, + type inferRouterError, + type inferRouterInputs, + type inferRouterOutputs, + type inferRouterContext, + type inferClientTypes as inferTRPCClientTypes, + type AnyClientTypes as AnyTRPCClientTypes, + type inferTransformedProcedureOutput, + type inferTransformedSubscriptionOutput, + type AnyProcedure as AnyTRPCProcedure, + type AnyRouter as AnyTRPCRouter, + type RouterDef as TRPCRouterDef, + type RouterBuilder as TRPCRouterBuilder, + type RouterCallerFactory as TRPCRouterCallerFactory, + type RootConfig as TRPCRootConfig, + type AnyRootTypes as AnyTRPCRootTypes, + type MiddlewareFunction as TRPCMiddlewareFunction, + type MiddlewareBuilder as TRPCMiddlewareBuilder, + type AnyMiddlewareFunction as AnyTRPCMiddlewareFunction, + type CombinedDataTransformer as TRPCCombinedDataTransformer, + type DataTransformer as TRPCDataTransformer, + type ProcedureType as TRPCProcedureType, + type AnyMutationProcedure as AnyTRPCMutationProcedure, + type AnyQueryProcedure as AnyTRPCQueryProcedure, + type RouterRecord as TRPCRouterRecord, + type MergeRouters as TRPCMergeRouters, + type AnySubscriptionProcedure as AnyTRPCSubscriptionProcedure, + type CreateContextCallback, + type MutationProcedure as TRPCMutationProcedure, + type QueryProcedure as TRPCQueryProcedure, + type BuiltRouter as TRPCBuiltRouter, + type SubscriptionProcedure as TRPCSubscriptionProcedure, + type TRPCBuilder, + type ProcedureBuilder as TRPCProcedureBuilder, + type RuntimeConfigOptions as TRPCRuntimeConfigOptions, + type TRPCRootObject, + type ErrorFormatter as TRPCErrorFormatter, + type TRPCErrorShape, + type DefaultErrorShape as TRPCDefaultErrorShape, + type DefaultErrorData as TRPCDefaultErrorData, + type TRPC_ERROR_CODE_KEY, + type TRPC_ERROR_CODE_NUMBER, + type DecorateCreateRouterOptions as TRPCDecorateCreateRouterOptions, + type CreateRouterOptions as TRPCCreateRouterOptions, + type RouterCaller as TRPCRouterCaller, + StandardSchemaV1Error, + /** + * @deprecated use `tracked(id, data)` instead + */ + sse, + tracked, + type TrackedEnvelope, + isTrackedEnvelope, + lazy, + /** + * @deprecated use {@link lazy} instead + */ + lazy as experimental_lazy, + callProcedure as callTRPCProcedure, + + /** + * @internal + */ + type UnsetMarker as TRPCUnsetMarker, +} from '../../unstable-core-do-not-import'; + +export type { + /** + * @deprecated use `AnyTRPCProcedure` instead + */ + AnyProcedure, + /** + * @deprecated use `AnyTRPCRouter` instead + */ + AnyRouter, + /** + * @deprecated use `AnyTRPCMiddlewareFunction` instead + */ + AnyMiddlewareFunction, + /** + * @deprecated use `TRPCCombinedDataTransformer` instead + */ + CombinedDataTransformer, + /** + * @deprecated use `TRPCDataTransformer` instead + */ + DataTransformer, + + /** + * @deprecated This is a utility type will be removed in v12 + */ + Dict, + + /** + * @deprecated This is a utility type will be removed in v12 + */ + DeepPartial, + /** + * @deprecated use `TRPCProcedureType` instead + */ + ProcedureType, + /** + * @deprecated use `AnyTRPCMutationProcedure` instead + */ + AnyMutationProcedure, + + /** + * @deprecated use `AnyTRPCQueryProcedure` instead + */ + AnyQueryProcedure, + /** + * @deprecated use `AnyTRPCSubscriptionProcedure` instead + */ + AnySubscriptionProcedure, +} from '../../unstable-core-do-not-import'; + +export { + /** + * @deprecated use `getTRPCErrorShape` instead + */ + getErrorShape, + getErrorShape as getTRPCErrorShape, +} from '../../unstable-core-do-not-import'; + +/** + * @deprecated + * Use `Awaited>` instead + */ +export type inferAsyncReturnType any> = + Awaited>; diff --git a/tests/testdata/ts_trpc_export_datatransformer/old.ts b/tests/testdata/ts_trpc_export_datatransformer/old.ts new file mode 100644 index 0000000..2bfbcbd --- /dev/null +++ b/tests/testdata/ts_trpc_export_datatransformer/old.ts @@ -0,0 +1,140 @@ +export { + TRPCError, + /** + * @deprecated use `experimental_trpcMiddleware` instead + */ + experimental_standaloneMiddleware, + experimental_standaloneMiddleware as experimental_trpcMiddleware, + initTRPC, + // --- FIXME a bunch of these exports are only useful for plugins - move them somewhere else? ---- + getTRPCErrorFromUnknown, + transformTRPCResponse, + createFlatProxy as createTRPCFlatProxy, + createRecursiveProxy as createTRPCRecursiveProxy, + type inferProcedureInput, + type inferProcedureOutput, + type inferProcedureBuilderResolverOptions, + type inferRouterError, + type inferRouterInputs, + type inferRouterOutputs, + type inferRouterContext, + type inferClientTypes as inferTRPCClientTypes, + type AnyClientTypes as AnyTRPCClientTypes, + type inferTransformedProcedureOutput, + type inferTransformedSubscriptionOutput, + type AnyProcedure as AnyTRPCProcedure, + type AnyRouter as AnyTRPCRouter, + type RouterDef as TRPCRouterDef, + type RouterBuilder as TRPCRouterBuilder, + type RouterCallerFactory as TRPCRouterCallerFactory, + type RootConfig as TRPCRootConfig, + type AnyRootTypes as AnyTRPCRootTypes, + type MiddlewareFunction as TRPCMiddlewareFunction, + type MiddlewareBuilder as TRPCMiddlewareBuilder, + type AnyMiddlewareFunction as AnyTRPCMiddlewareFunction, + type CombinedDataTransformer as TRPCCombinedDataTransformer, + type ProcedureType as TRPCProcedureType, + type AnyMutationProcedure as AnyTRPCMutationProcedure, + type AnyQueryProcedure as AnyTRPCQueryProcedure, + type RouterRecord as TRPCRouterRecord, + type MergeRouters as TRPCMergeRouters, + type AnySubscriptionProcedure as AnyTRPCSubscriptionProcedure, + type CreateContextCallback, + type MutationProcedure as TRPCMutationProcedure, + type QueryProcedure as TRPCQueryProcedure, + type BuiltRouter as TRPCBuiltRouter, + type SubscriptionProcedure as TRPCSubscriptionProcedure, + type TRPCBuilder, + type ProcedureBuilder as TRPCProcedureBuilder, + type RuntimeConfigOptions as TRPCRuntimeConfigOptions, + type TRPCRootObject, + type ErrorFormatter as TRPCErrorFormatter, + type TRPCErrorShape, + type DefaultErrorShape as TRPCDefaultErrorShape, + type DefaultErrorData as TRPCDefaultErrorData, + type TRPC_ERROR_CODE_KEY, + type TRPC_ERROR_CODE_NUMBER, + type DecorateCreateRouterOptions as TRPCDecorateCreateRouterOptions, + type CreateRouterOptions as TRPCCreateRouterOptions, + type RouterCaller as TRPCRouterCaller, + StandardSchemaV1Error, + /** + * @deprecated use `tracked(id, data)` instead + */ + sse, + tracked, + type TrackedEnvelope, + isTrackedEnvelope, + lazy, + /** + * @deprecated use {@link lazy} instead + */ + lazy as experimental_lazy, + callProcedure as callTRPCProcedure, + + /** + * @internal + */ + type UnsetMarker as TRPCUnsetMarker, +} from '../../unstable-core-do-not-import'; + +export type { + /** + * @deprecated use `AnyTRPCProcedure` instead + */ + AnyProcedure, + /** + * @deprecated use `AnyTRPCRouter` instead + */ + AnyRouter, + /** + * @deprecated use `AnyTRPCMiddlewareFunction` instead + */ + AnyMiddlewareFunction, + /** + * @deprecated use `TRPCCombinedDataTransformer` instead + */ + CombinedDataTransformer, + + /** + * @deprecated This is a utility type will be removed in v12 + */ + Dict, + + /** + * @deprecated This is a utility type will be removed in v12 + */ + DeepPartial, + /** + * @deprecated use `TRPCProcedureType` instead + */ + ProcedureType, + /** + * @deprecated use `AnyTRPCMutationProcedure` instead + */ + AnyMutationProcedure, + + /** + * @deprecated use `AnyTRPCQueryProcedure` instead + */ + AnyQueryProcedure, + /** + * @deprecated use `AnyTRPCSubscriptionProcedure` instead + */ + AnySubscriptionProcedure, +} from '../../unstable-core-do-not-import'; + +export { + /** + * @deprecated use `getTRPCErrorShape` instead + */ + getErrorShape, + getErrorShape as getTRPCErrorShape, +} from '../../unstable-core-do-not-import'; + +/** + * @deprecated + * Use `Awaited>` instead + */ +export type inferAsyncReturnType any> = + Awaited>; diff --git a/tests/testdata/ts_trpc_patchclient_upgrade/expected.json b/tests/testdata/ts_trpc_patchclient_upgrade/expected.json new file mode 100644 index 0000000..d9b3d11 --- /dev/null +++ b/tests/testdata/ts_trpc_patchclient_upgrade/expected.json @@ -0,0 +1,1210 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 14 + ], + "type": "comment", + "label": "/**\n * @deprecated Renamed to `unstable_localLink`. This alias will be removed in a future major release.\n */", + "start_byte": 8792, + "end_byte": 8901 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 8989 + }, + "position": 14 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15 + ], + "type": "export_statement", + "start_byte": 8902, + "end_byte": 8988 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 8989 + }, + "position": 15, + "subtree": true + }, + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 13, + 1, + 1 + ], + "type": "identifier", + "label": "experimental_localLink", + "start_byte": 1473, + "end_byte": 1495 + }, + "old_value": "experimental_localLink", + "new_value": "unstable_localLink", + "dest_node": { + "tree": "after", + "path": [ + 13, + 1, + 1 + ], + "type": "identifier", + "label": "unstable_localLink", + "start_byte": 1473, + "end_byte": 1491 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": -1, + "right_line": 277 + }, + { + "left_line": -1, + "right_line": 278 + }, + { + "left_line": -1, + "right_line": 279 + }, + { + "left_line": -1, + "right_line": 280 + }, + { + "left_line": -1, + "right_line": 281 + }, + { + "left_line": 277, + "right_line": 282 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_patchclient_upgrade/metadata.json b/tests/testdata/ts_trpc_patchclient_upgrade/metadata.json new file mode 100644 index 0000000..3b6e280 --- /dev/null +++ b/tests/testdata/ts_trpc_patchclient_upgrade/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/trpc/trpc", + "parent": "326b89a3166d0b1f218286ef742736fe824f89f1", + "commit": "e70e78413d5a05447c29f8b1e60aaa330607888f", + "message": "patch(`client`): upgrade `localLink` from experimental -> `unstable` (#6817)\n\n_Notetoself: not auto-merging as it updates docs and then we gotta\ntrigger a release which I ain't doing right now_\n\n\n## Summary by CodeRabbit\n\n- **Documentation**\n- Updated documentation to reflect the new naming convention for the\nlocal link API, changing references from `experimental_localLink` to\n`unstable_localLink`.\n - Example code and descriptive text now use `unstable_localLink`.\n\n- **Refactor**\n- Renamed the local link API export from `experimental_localLink` to\n`unstable_localLink`.\n- Added a deprecated alias so `experimental_localLink` now points to\n`unstable_localLink`.\n\n\n---------\n\nCo-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>\nCo-authored-by: Julius Marminge \nCo-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>\nCo-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>", + "author": "Alex / KATT", + "date": "2025-06-12T13:30:12+02:00", + "language": "typescript", + "files_changed": 1, + "insertions": 5, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_trpc_patchclient_upgrade/new.ts b/tests/testdata/ts_trpc_patchclient_upgrade/new.ts new file mode 100644 index 0000000..fa3783b --- /dev/null +++ b/tests/testdata/ts_trpc_patchclient_upgrade/new.ts @@ -0,0 +1,282 @@ +import { + getTRPCErrorFromUnknown, + getTRPCErrorShape, + isTrackedEnvelope, +} from '@trpc/server'; +import { behaviorSubject, observable } from '@trpc/server/observable'; +import { TRPC_ERROR_CODES_BY_KEY, type TRPCResult } from '@trpc/server/rpc'; +import { + callProcedure, + isAbortError, + isAsyncIterable, + iteratorResource, + makeResource, + retryableRpcCodes, + run, + type AnyRouter, + type ErrorHandlerOptions, + type inferClientTypes, + type inferRouterContext, +} from '@trpc/server/unstable-core-do-not-import'; +import { inputWithTrackedEventId } from '../internals/inputWithTrackedEventId'; +import { abortSignalToPromise, raceAbortSignals } from '../internals/signals'; +import { getTransformer } from '../internals/transformer'; +import type { TransformerOptions } from '../internals/transformer'; +import { isTRPCClientError, TRPCClientError } from '../TRPCClientError'; +import type { TRPCConnectionState } from './internals/subscriptions'; +import type { TRPCLink } from './types'; + +export type LocalLinkOptions = { + router: TRouter; + createContext: () => Promise>; + onError?: (opts: ErrorHandlerOptions>) => void; +} & TransformerOptions>; + +/** + * localLink is a terminating link that allows you to make tRPC procedure calls directly in your application without going through HTTP. + * + * @see https://trpc.io/docs/links/localLink + */ +export function unstable_localLink( + opts: LocalLinkOptions, +): TRPCLink { + const transformer = getTransformer(opts.transformer); + + const transformChunk = (chunk: unknown) => { + if (opts.transformer) { + // assume transformer will do the right thing + return chunk; + } + // Special case for undefined, because `JSON.stringify(undefined)` throws + if (chunk === undefined) { + return chunk; + } + const serialized = JSON.stringify(transformer.input.serialize(chunk)); + const deserialized = JSON.parse(transformer.output.deserialize(serialized)); + return deserialized; + }; + + return () => + ({ op }) => + observable((observer) => { + let ctx: inferRouterContext | undefined = undefined; + const ac = new AbortController(); + + const signal = raceAbortSignals(op.signal, ac.signal); + const signalPromise = abortSignalToPromise(signal); + + signalPromise.catch(() => { + // prevent unhandled rejection + }); + + let input = op.input; + async function runProcedure(newInput: unknown): Promise { + input = newInput; + + ctx = await opts.createContext(); + + return callProcedure({ + router: opts.router, + path: op.path, + getRawInput: async () => newInput, + ctx, + type: op.type, + signal, + }); + } + + function onErrorCallback(cause: unknown) { + if (isAbortError(cause)) { + return; + } + opts.onError?.({ + error: getTRPCErrorFromUnknown(cause), + type: op.type, + path: op.path, + input, + ctx, + }); + } + + function coerceToTRPCClientError(cause: unknown) { + if (isTRPCClientError(cause)) { + return cause; + } + const error = getTRPCErrorFromUnknown(cause); + + const shape = getTRPCErrorShape({ + config: opts.router._def._config, + ctx, + error, + input, + path: op.path, + type: op.type, + }); + return TRPCClientError.from({ + error: transformChunk(shape), + }); + } + + run(async () => { + switch (op.type) { + case 'query': + case 'mutation': { + const result = await runProcedure(op.input); + if (!isAsyncIterable(result)) { + observer.next({ + result: { data: transformChunk(result) }, + }); + observer.complete(); + break; + } + + observer.next({ + result: { + data: (async function* () { + await using iterator = iteratorResource(result); + using _finally = makeResource({}, () => { + observer.complete(); + }); + try { + while (true) { + const res = await Promise.race([ + iterator.next(), + signalPromise, + ]); + if (res.done) { + return transformChunk(res.value); + } + yield transformChunk(res.value); + } + } catch (cause) { + onErrorCallback(cause); + throw coerceToTRPCClientError(cause); + } + })(), + }, + }); + break; + } + case 'subscription': { + const connectionState = behaviorSubject< + TRPCConnectionState> + >({ + type: 'state', + state: 'connecting', + error: null, + }); + + const connectionSub = connectionState.subscribe({ + next(state) { + observer.next({ + result: state, + }); + }, + }); + let lastEventId: string | undefined = undefined; + + using _finally = makeResource({}, async () => { + observer.complete(); + + connectionState.next({ + type: 'state', + state: 'idle', + error: null, + }); + connectionSub.unsubscribe(); + }); + while (true) { + const result = await runProcedure( + inputWithTrackedEventId(op.input, lastEventId), + ); + if (!isAsyncIterable(result)) { + throw new Error('Expected an async iterable'); + } + await using iterator = iteratorResource(result); + + observer.next({ + result: { + type: 'started', + }, + }); + connectionState.next({ + type: 'state', + state: 'pending', + error: null, + }); + + // Use a while loop to handle errors and reconnects + while (true) { + let res; + try { + res = await Promise.race([iterator.next(), signalPromise]); + } catch (cause) { + if (isAbortError(cause)) { + return; + } + const error = getTRPCErrorFromUnknown(cause); + + if ( + !retryableRpcCodes.includes( + TRPC_ERROR_CODES_BY_KEY[error.code], + ) + ) { + throw coerceToTRPCClientError(error); + } + + onErrorCallback(error); + connectionState.next({ + type: 'state', + state: 'connecting', + error: coerceToTRPCClientError(error), + }); + + break; + } + + if (res.done) { + return; + } + let chunk: TRPCResult; + if (isTrackedEnvelope(res.value)) { + lastEventId = res.value[0]; + + chunk = { + id: res.value[0], + data: { + id: res.value[0], + data: res.value[1], + }, + }; + } else { + chunk = { + data: res.value, + }; + } + + observer.next({ + result: { + ...chunk, + data: transformChunk(chunk.data), + }, + }); + } + } + break; + } + } + }).catch((cause) => { + onErrorCallback(cause); + observer.error(coerceToTRPCClientError(cause)); + }); + + return () => { + ac.abort(); + }; + }); +} +/** + * @deprecated Renamed to `unstable_localLink`. This alias will be removed in a future major release. + */ +export const experimental_localLink: typeof unstable_localLink = + unstable_localLink; diff --git a/tests/testdata/ts_trpc_patchclient_upgrade/old.ts b/tests/testdata/ts_trpc_patchclient_upgrade/old.ts new file mode 100644 index 0000000..98a7669 --- /dev/null +++ b/tests/testdata/ts_trpc_patchclient_upgrade/old.ts @@ -0,0 +1,277 @@ +import { + getTRPCErrorFromUnknown, + getTRPCErrorShape, + isTrackedEnvelope, +} from '@trpc/server'; +import { behaviorSubject, observable } from '@trpc/server/observable'; +import { TRPC_ERROR_CODES_BY_KEY, type TRPCResult } from '@trpc/server/rpc'; +import { + callProcedure, + isAbortError, + isAsyncIterable, + iteratorResource, + makeResource, + retryableRpcCodes, + run, + type AnyRouter, + type ErrorHandlerOptions, + type inferClientTypes, + type inferRouterContext, +} from '@trpc/server/unstable-core-do-not-import'; +import { inputWithTrackedEventId } from '../internals/inputWithTrackedEventId'; +import { abortSignalToPromise, raceAbortSignals } from '../internals/signals'; +import { getTransformer } from '../internals/transformer'; +import type { TransformerOptions } from '../internals/transformer'; +import { isTRPCClientError, TRPCClientError } from '../TRPCClientError'; +import type { TRPCConnectionState } from './internals/subscriptions'; +import type { TRPCLink } from './types'; + +export type LocalLinkOptions = { + router: TRouter; + createContext: () => Promise>; + onError?: (opts: ErrorHandlerOptions>) => void; +} & TransformerOptions>; + +/** + * localLink is a terminating link that allows you to make tRPC procedure calls directly in your application without going through HTTP. + * + * @see https://trpc.io/docs/links/localLink + */ +export function experimental_localLink( + opts: LocalLinkOptions, +): TRPCLink { + const transformer = getTransformer(opts.transformer); + + const transformChunk = (chunk: unknown) => { + if (opts.transformer) { + // assume transformer will do the right thing + return chunk; + } + // Special case for undefined, because `JSON.stringify(undefined)` throws + if (chunk === undefined) { + return chunk; + } + const serialized = JSON.stringify(transformer.input.serialize(chunk)); + const deserialized = JSON.parse(transformer.output.deserialize(serialized)); + return deserialized; + }; + + return () => + ({ op }) => + observable((observer) => { + let ctx: inferRouterContext | undefined = undefined; + const ac = new AbortController(); + + const signal = raceAbortSignals(op.signal, ac.signal); + const signalPromise = abortSignalToPromise(signal); + + signalPromise.catch(() => { + // prevent unhandled rejection + }); + + let input = op.input; + async function runProcedure(newInput: unknown): Promise { + input = newInput; + + ctx = await opts.createContext(); + + return callProcedure({ + router: opts.router, + path: op.path, + getRawInput: async () => newInput, + ctx, + type: op.type, + signal, + }); + } + + function onErrorCallback(cause: unknown) { + if (isAbortError(cause)) { + return; + } + opts.onError?.({ + error: getTRPCErrorFromUnknown(cause), + type: op.type, + path: op.path, + input, + ctx, + }); + } + + function coerceToTRPCClientError(cause: unknown) { + if (isTRPCClientError(cause)) { + return cause; + } + const error = getTRPCErrorFromUnknown(cause); + + const shape = getTRPCErrorShape({ + config: opts.router._def._config, + ctx, + error, + input, + path: op.path, + type: op.type, + }); + return TRPCClientError.from({ + error: transformChunk(shape), + }); + } + + run(async () => { + switch (op.type) { + case 'query': + case 'mutation': { + const result = await runProcedure(op.input); + if (!isAsyncIterable(result)) { + observer.next({ + result: { data: transformChunk(result) }, + }); + observer.complete(); + break; + } + + observer.next({ + result: { + data: (async function* () { + await using iterator = iteratorResource(result); + using _finally = makeResource({}, () => { + observer.complete(); + }); + try { + while (true) { + const res = await Promise.race([ + iterator.next(), + signalPromise, + ]); + if (res.done) { + return transformChunk(res.value); + } + yield transformChunk(res.value); + } + } catch (cause) { + onErrorCallback(cause); + throw coerceToTRPCClientError(cause); + } + })(), + }, + }); + break; + } + case 'subscription': { + const connectionState = behaviorSubject< + TRPCConnectionState> + >({ + type: 'state', + state: 'connecting', + error: null, + }); + + const connectionSub = connectionState.subscribe({ + next(state) { + observer.next({ + result: state, + }); + }, + }); + let lastEventId: string | undefined = undefined; + + using _finally = makeResource({}, async () => { + observer.complete(); + + connectionState.next({ + type: 'state', + state: 'idle', + error: null, + }); + connectionSub.unsubscribe(); + }); + while (true) { + const result = await runProcedure( + inputWithTrackedEventId(op.input, lastEventId), + ); + if (!isAsyncIterable(result)) { + throw new Error('Expected an async iterable'); + } + await using iterator = iteratorResource(result); + + observer.next({ + result: { + type: 'started', + }, + }); + connectionState.next({ + type: 'state', + state: 'pending', + error: null, + }); + + // Use a while loop to handle errors and reconnects + while (true) { + let res; + try { + res = await Promise.race([iterator.next(), signalPromise]); + } catch (cause) { + if (isAbortError(cause)) { + return; + } + const error = getTRPCErrorFromUnknown(cause); + + if ( + !retryableRpcCodes.includes( + TRPC_ERROR_CODES_BY_KEY[error.code], + ) + ) { + throw coerceToTRPCClientError(error); + } + + onErrorCallback(error); + connectionState.next({ + type: 'state', + state: 'connecting', + error: coerceToTRPCClientError(error), + }); + + break; + } + + if (res.done) { + return; + } + let chunk: TRPCResult; + if (isTrackedEnvelope(res.value)) { + lastEventId = res.value[0]; + + chunk = { + id: res.value[0], + data: { + id: res.value[0], + data: res.value[1], + }, + }; + } else { + chunk = { + data: res.value, + }; + } + + observer.next({ + result: { + ...chunk, + data: transformChunk(chunk.data), + }, + }); + } + } + break; + } + } + }).catch((cause) => { + onErrorCallback(cause); + observer.error(coerceToTRPCClientError(cause)); + }); + + return () => { + ac.abort(); + }; + }); +} diff --git a/tests/testdata/ts_zod_improve_mini/expected.json b/tests/testdata/ts_zod_improve_mini/expected.json new file mode 100644 index 0000000..f2e89c1 --- /dev/null +++ b/tests/testdata/ts_zod_improve_mini/expected.json @@ -0,0 +1,7743 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 3 + ], + "type": "string", + "label": "\"../core/index.js\"", + "start_byte": 63, + "end_byte": 81 + }, + "old_value": "\"../core/index.js\"", + "new_value": "\"../core/util.js\"", + "dest_node": { + "tree": "after", + "path": [ + 1, + 3 + ], + "type": "string", + "label": "\"../core/util.js\"", + "start_byte": 64, + "end_byte": 81 + } + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 1, + 0 + ], + "type": "namespace_import", + "start_byte": 49, + "end_byte": 58 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 1 + ], + "type": "import_clause", + "start_byte": 49, + "end_byte": 58 + }, + "position": 0, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 1, + 0 + ], + "type": "named_imports", + "start_byte": 49, + "end_byte": 57 + }, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": 871 + }, + { + "left_line": 872, + "right_line": 872 + }, + { + "left_line": 873, + "right_line": 873 + }, + { + "left_line": 874, + "right_line": 874 + }, + { + "left_line": 875, + "right_line": 875 + }, + { + "left_line": 876, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": 894 + }, + { + "left_line": 895, + "right_line": 895 + }, + { + "left_line": 896, + "right_line": 896 + }, + { + "left_line": 897, + "right_line": 897 + }, + { + "left_line": 898, + "right_line": 898 + }, + { + "left_line": 899, + "right_line": 899 + }, + { + "left_line": 900, + "right_line": 900 + }, + { + "left_line": 901, + "right_line": 901 + }, + { + "left_line": 902, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": 905, + "right_line": 905 + }, + { + "left_line": 906, + "right_line": 906 + }, + { + "left_line": 907, + "right_line": 907 + }, + { + "left_line": 908, + "right_line": 908 + }, + { + "left_line": 909, + "right_line": 909 + }, + { + "left_line": 910, + "right_line": 910 + }, + { + "left_line": 911, + "right_line": 911 + }, + { + "left_line": 912, + "right_line": 912 + }, + { + "left_line": 913, + "right_line": 913 + }, + { + "left_line": 914, + "right_line": 914 + }, + { + "left_line": 915, + "right_line": 915 + }, + { + "left_line": 916, + "right_line": 916 + }, + { + "left_line": 917, + "right_line": 917 + }, + { + "left_line": 918, + "right_line": 918 + }, + { + "left_line": 919, + "right_line": 919 + }, + { + "left_line": 920, + "right_line": 920 + }, + { + "left_line": 921, + "right_line": 921 + }, + { + "left_line": 922, + "right_line": 922 + }, + { + "left_line": 923, + "right_line": 923 + }, + { + "left_line": 924, + "right_line": 924 + }, + { + "left_line": 925, + "right_line": 925 + }, + { + "left_line": 926, + "right_line": 926 + }, + { + "left_line": 927, + "right_line": 927 + }, + { + "left_line": 928, + "right_line": 928 + }, + { + "left_line": 929, + "right_line": 929 + }, + { + "left_line": 930, + "right_line": 930 + }, + { + "left_line": 931, + "right_line": 931 + }, + { + "left_line": 932, + "right_line": 932 + }, + { + "left_line": 933, + "right_line": 933 + }, + { + "left_line": 934, + "right_line": 934 + }, + { + "left_line": 935, + "right_line": 935 + }, + { + "left_line": 936, + "right_line": 936 + }, + { + "left_line": 937, + "right_line": 937 + }, + { + "left_line": 938, + "right_line": 938 + }, + { + "left_line": 939, + "right_line": 939 + }, + { + "left_line": 940, + "right_line": 940 + }, + { + "left_line": 941, + "right_line": 941 + }, + { + "left_line": 942, + "right_line": 942 + }, + { + "left_line": 943, + "right_line": 943 + }, + { + "left_line": 944, + "right_line": 944 + }, + { + "left_line": 945, + "right_line": 945 + }, + { + "left_line": 946, + "right_line": 946 + }, + { + "left_line": 947, + "right_line": 947 + }, + { + "left_line": 948, + "right_line": 948 + }, + { + "left_line": 949, + "right_line": 949 + }, + { + "left_line": 950, + "right_line": 950 + }, + { + "left_line": 951, + "right_line": 951 + }, + { + "left_line": 952, + "right_line": 952 + }, + { + "left_line": 953, + "right_line": 953 + }, + { + "left_line": 954, + "right_line": 954 + }, + { + "left_line": 955, + "right_line": 955 + }, + { + "left_line": 956, + "right_line": 956 + }, + { + "left_line": 957, + "right_line": 957 + }, + { + "left_line": 958, + "right_line": 958 + }, + { + "left_line": 959, + "right_line": 959 + }, + { + "left_line": 960, + "right_line": 960 + }, + { + "left_line": 961, + "right_line": 961 + }, + { + "left_line": 962, + "right_line": 962 + }, + { + "left_line": 963, + "right_line": 963 + }, + { + "left_line": 964, + "right_line": 964 + }, + { + "left_line": 965, + "right_line": 965 + }, + { + "left_line": 966, + "right_line": 966 + }, + { + "left_line": 967, + "right_line": 967 + }, + { + "left_line": 968, + "right_line": 968 + }, + { + "left_line": 969, + "right_line": 969 + }, + { + "left_line": 970, + "right_line": 970 + }, + { + "left_line": 971, + "right_line": 971 + }, + { + "left_line": 972, + "right_line": 972 + }, + { + "left_line": 973, + "right_line": 973 + }, + { + "left_line": 974, + "right_line": 974 + }, + { + "left_line": 975, + "right_line": 975 + }, + { + "left_line": 976, + "right_line": 976 + }, + { + "left_line": 977, + "right_line": 977 + }, + { + "left_line": 978, + "right_line": 978 + }, + { + "left_line": 979, + "right_line": 979 + }, + { + "left_line": 980, + "right_line": 980 + }, + { + "left_line": 981, + "right_line": 981 + }, + { + "left_line": 982, + "right_line": 982 + }, + { + "left_line": 983, + "right_line": 983 + }, + { + "left_line": 984, + "right_line": 984 + }, + { + "left_line": 985, + "right_line": 985 + }, + { + "left_line": 986, + "right_line": 986 + }, + { + "left_line": 987, + "right_line": 987 + }, + { + "left_line": 988, + "right_line": 988 + }, + { + "left_line": 989, + "right_line": 989 + }, + { + "left_line": 990, + "right_line": 990 + }, + { + "left_line": 991, + "right_line": 991 + }, + { + "left_line": 992, + "right_line": 992 + }, + { + "left_line": 993, + "right_line": 993 + }, + { + "left_line": 994, + "right_line": 994 + }, + { + "left_line": 995, + "right_line": 995 + }, + { + "left_line": 996, + "right_line": 996 + }, + { + "left_line": 997, + "right_line": 997 + }, + { + "left_line": 998, + "right_line": 998 + }, + { + "left_line": 999, + "right_line": 999 + }, + { + "left_line": 1000, + "right_line": 1000 + }, + { + "left_line": 1001, + "right_line": 1001 + }, + { + "left_line": 1002, + "right_line": 1002 + }, + { + "left_line": 1003, + "right_line": 1003 + }, + { + "left_line": 1004, + "right_line": 1004 + }, + { + "left_line": 1005, + "right_line": 1005 + }, + { + "left_line": 1006, + "right_line": 1006 + }, + { + "left_line": 1007, + "right_line": 1007 + }, + { + "left_line": 1008, + "right_line": 1008 + }, + { + "left_line": 1009, + "right_line": 1009 + }, + { + "left_line": 1010, + "right_line": 1010 + }, + { + "left_line": 1011, + "right_line": 1011 + }, + { + "left_line": 1012, + "right_line": 1012 + }, + { + "left_line": 1013, + "right_line": 1013 + }, + { + "left_line": 1014, + "right_line": 1014 + }, + { + "left_line": 1015, + "right_line": 1015 + }, + { + "left_line": 1016, + "right_line": 1016 + }, + { + "left_line": 1017, + "right_line": 1017 + }, + { + "left_line": 1018, + "right_line": 1018 + }, + { + "left_line": 1019, + "right_line": 1019 + }, + { + "left_line": 1020, + "right_line": 1020 + }, + { + "left_line": 1021, + "right_line": 1021 + }, + { + "left_line": 1022, + "right_line": 1022 + }, + { + "left_line": 1023, + "right_line": 1023 + }, + { + "left_line": 1024, + "right_line": 1024 + }, + { + "left_line": 1025, + "right_line": 1025 + }, + { + "left_line": 1026, + "right_line": 1026 + }, + { + "left_line": 1027, + "right_line": 1027 + }, + { + "left_line": 1028, + "right_line": 1028 + }, + { + "left_line": 1029, + "right_line": 1029 + }, + { + "left_line": 1030, + "right_line": 1030 + }, + { + "left_line": 1031, + "right_line": 1031 + }, + { + "left_line": 1032, + "right_line": 1032 + }, + { + "left_line": 1033, + "right_line": 1033 + }, + { + "left_line": 1034, + "right_line": 1034 + }, + { + "left_line": 1035, + "right_line": 1035 + }, + { + "left_line": 1036, + "right_line": 1036 + }, + { + "left_line": 1037, + "right_line": 1037 + }, + { + "left_line": 1038, + "right_line": 1038 + }, + { + "left_line": 1039, + "right_line": 1039 + }, + { + "left_line": 1040, + "right_line": 1040 + }, + { + "left_line": 1041, + "right_line": 1041 + }, + { + "left_line": 1042, + "right_line": 1042 + }, + { + "left_line": 1043, + "right_line": 1043 + }, + { + "left_line": 1044, + "right_line": 1044 + }, + { + "left_line": 1045, + "right_line": 1045 + }, + { + "left_line": 1046, + "right_line": 1046 + }, + { + "left_line": 1047, + "right_line": 1047 + }, + { + "left_line": 1048, + "right_line": 1048 + }, + { + "left_line": 1049, + "right_line": 1049 + }, + { + "left_line": 1050, + "right_line": 1050 + }, + { + "left_line": 1051, + "right_line": 1051 + }, + { + "left_line": 1052, + "right_line": 1052 + }, + { + "left_line": 1053, + "right_line": 1053 + }, + { + "left_line": 1054, + "right_line": 1054 + }, + { + "left_line": 1055, + "right_line": 1055 + }, + { + "left_line": 1056, + "right_line": 1056 + }, + { + "left_line": 1057, + "right_line": 1057 + }, + { + "left_line": 1058, + "right_line": 1058 + }, + { + "left_line": 1059, + "right_line": 1059 + }, + { + "left_line": 1060, + "right_line": 1060 + }, + { + "left_line": 1061, + "right_line": 1061 + }, + { + "left_line": 1062, + "right_line": 1062 + }, + { + "left_line": 1063, + "right_line": 1063 + }, + { + "left_line": 1064, + "right_line": 1064 + }, + { + "left_line": 1065, + "right_line": 1065 + }, + { + "left_line": 1066, + "right_line": 1066 + }, + { + "left_line": 1067, + "right_line": 1067 + }, + { + "left_line": 1068, + "right_line": 1068 + }, + { + "left_line": 1069, + "right_line": 1069 + }, + { + "left_line": 1070, + "right_line": 1070 + }, + { + "left_line": 1071, + "right_line": 1071 + }, + { + "left_line": 1072, + "right_line": 1072 + }, + { + "left_line": 1073, + "right_line": 1073 + }, + { + "left_line": 1074, + "right_line": 1074 + }, + { + "left_line": 1075, + "right_line": 1075 + }, + { + "left_line": 1076, + "right_line": 1076 + }, + { + "left_line": 1077, + "right_line": 1077 + }, + { + "left_line": 1078, + "right_line": 1078 + }, + { + "left_line": 1079, + "right_line": 1079 + }, + { + "left_line": 1080, + "right_line": 1080 + }, + { + "left_line": 1081, + "right_line": 1081 + }, + { + "left_line": 1082, + "right_line": 1082 + }, + { + "left_line": 1083, + "right_line": 1083 + }, + { + "left_line": 1084, + "right_line": 1084 + }, + { + "left_line": 1085, + "right_line": 1085 + }, + { + "left_line": 1086, + "right_line": 1086 + }, + { + "left_line": 1087, + "right_line": 1087 + }, + { + "left_line": 1088, + "right_line": 1088 + }, + { + "left_line": 1089, + "right_line": 1089 + }, + { + "left_line": 1090, + "right_line": 1090 + }, + { + "left_line": 1091, + "right_line": 1091 + }, + { + "left_line": 1092, + "right_line": 1092 + }, + { + "left_line": 1093, + "right_line": 1093 + }, + { + "left_line": 1094, + "right_line": 1094 + }, + { + "left_line": 1095, + "right_line": 1095 + }, + { + "left_line": 1096, + "right_line": 1096 + }, + { + "left_line": 1097, + "right_line": 1097 + }, + { + "left_line": 1098, + "right_line": 1098 + }, + { + "left_line": 1099, + "right_line": 1099 + }, + { + "left_line": 1100, + "right_line": 1100 + }, + { + "left_line": 1101, + "right_line": 1101 + }, + { + "left_line": 1102, + "right_line": 1102 + }, + { + "left_line": 1103, + "right_line": 1103 + }, + { + "left_line": 1104, + "right_line": 1104 + }, + { + "left_line": 1105, + "right_line": 1105 + }, + { + "left_line": 1106, + "right_line": 1106 + }, + { + "left_line": 1107, + "right_line": 1107 + }, + { + "left_line": 1108, + "right_line": 1108 + }, + { + "left_line": 1109, + "right_line": 1109 + }, + { + "left_line": 1110, + "right_line": 1110 + }, + { + "left_line": 1111, + "right_line": 1111 + }, + { + "left_line": 1112, + "right_line": 1112 + }, + { + "left_line": 1113, + "right_line": 1113 + }, + { + "left_line": 1114, + "right_line": 1114 + }, + { + "left_line": 1115, + "right_line": 1115 + }, + { + "left_line": 1116, + "right_line": 1116 + }, + { + "left_line": 1117, + "right_line": 1117 + }, + { + "left_line": 1118, + "right_line": 1118 + }, + { + "left_line": 1119, + "right_line": 1119 + }, + { + "left_line": 1120, + "right_line": 1120 + }, + { + "left_line": 1121, + "right_line": 1121 + }, + { + "left_line": 1122, + "right_line": 1122 + }, + { + "left_line": 1123, + "right_line": 1123 + }, + { + "left_line": 1124, + "right_line": 1124 + }, + { + "left_line": 1125, + "right_line": 1125 + }, + { + "left_line": 1126, + "right_line": 1126 + }, + { + "left_line": 1127, + "right_line": 1127 + }, + { + "left_line": 1128, + "right_line": 1128 + }, + { + "left_line": 1129, + "right_line": 1129 + }, + { + "left_line": 1130, + "right_line": 1130 + }, + { + "left_line": 1131, + "right_line": 1131 + }, + { + "left_line": 1132, + "right_line": 1132 + }, + { + "left_line": 1133, + "right_line": 1133 + }, + { + "left_line": 1134, + "right_line": 1134 + }, + { + "left_line": 1135, + "right_line": 1135 + }, + { + "left_line": 1136, + "right_line": 1136 + }, + { + "left_line": 1137, + "right_line": 1137 + }, + { + "left_line": 1138, + "right_line": 1138 + }, + { + "left_line": 1139, + "right_line": 1139 + }, + { + "left_line": 1140, + "right_line": 1140 + }, + { + "left_line": 1141, + "right_line": 1141 + }, + { + "left_line": 1142, + "right_line": 1142 + }, + { + "left_line": 1143, + "right_line": 1143 + }, + { + "left_line": 1144, + "right_line": 1144 + }, + { + "left_line": 1145, + "right_line": 1145 + }, + { + "left_line": 1146, + "right_line": 1146 + }, + { + "left_line": 1147, + "right_line": 1147 + }, + { + "left_line": 1148, + "right_line": 1148 + }, + { + "left_line": 1149, + "right_line": 1149 + }, + { + "left_line": 1150, + "right_line": 1150 + }, + { + "left_line": 1151, + "right_line": 1151 + }, + { + "left_line": 1152, + "right_line": 1152 + }, + { + "left_line": 1153, + "right_line": 1153 + }, + { + "left_line": 1154, + "right_line": 1154 + }, + { + "left_line": 1155, + "right_line": 1155 + }, + { + "left_line": 1156, + "right_line": 1156 + }, + { + "left_line": 1157, + "right_line": 1157 + }, + { + "left_line": 1158, + "right_line": 1158 + }, + { + "left_line": 1159, + "right_line": 1159 + }, + { + "left_line": 1160, + "right_line": 1160 + }, + { + "left_line": 1161, + "right_line": 1161 + }, + { + "left_line": 1162, + "right_line": 1162 + }, + { + "left_line": 1163, + "right_line": 1163 + }, + { + "left_line": 1164, + "right_line": 1164 + }, + { + "left_line": 1165, + "right_line": 1165 + }, + { + "left_line": 1166, + "right_line": 1166 + }, + { + "left_line": 1167, + "right_line": 1167 + }, + { + "left_line": 1168, + "right_line": 1168 + }, + { + "left_line": 1169, + "right_line": 1169 + }, + { + "left_line": 1170, + "right_line": 1170 + }, + { + "left_line": 1171, + "right_line": 1171 + }, + { + "left_line": 1172, + "right_line": 1172 + }, + { + "left_line": 1173, + "right_line": 1173 + }, + { + "left_line": 1174, + "right_line": 1174 + }, + { + "left_line": 1175, + "right_line": 1175 + }, + { + "left_line": 1176, + "right_line": 1176 + }, + { + "left_line": 1177, + "right_line": 1177 + }, + { + "left_line": 1178, + "right_line": 1178 + }, + { + "left_line": 1179, + "right_line": 1179 + }, + { + "left_line": 1180, + "right_line": 1180 + }, + { + "left_line": 1181, + "right_line": 1181 + }, + { + "left_line": 1182, + "right_line": 1182 + }, + { + "left_line": 1183, + "right_line": 1183 + }, + { + "left_line": 1184, + "right_line": 1184 + }, + { + "left_line": 1185, + "right_line": 1185 + }, + { + "left_line": 1186, + "right_line": 1186 + }, + { + "left_line": 1187, + "right_line": 1187 + }, + { + "left_line": 1188, + "right_line": 1188 + }, + { + "left_line": 1189, + "right_line": 1189 + }, + { + "left_line": 1190, + "right_line": 1190 + }, + { + "left_line": 1191, + "right_line": 1191 + }, + { + "left_line": 1192, + "right_line": 1192 + }, + { + "left_line": 1193, + "right_line": 1193 + }, + { + "left_line": 1194, + "right_line": 1194 + }, + { + "left_line": 1195, + "right_line": 1195 + }, + { + "left_line": 1196, + "right_line": 1196 + }, + { + "left_line": 1197, + "right_line": 1197 + }, + { + "left_line": 1198, + "right_line": 1198 + }, + { + "left_line": 1199, + "right_line": 1199 + }, + { + "left_line": 1200, + "right_line": 1200 + }, + { + "left_line": 1201, + "right_line": 1201 + }, + { + "left_line": 1202, + "right_line": 1202 + }, + { + "left_line": 1203, + "right_line": 1203 + }, + { + "left_line": 1204, + "right_line": 1204 + }, + { + "left_line": 1205, + "right_line": 1205 + }, + { + "left_line": 1206, + "right_line": 1206 + }, + { + "left_line": 1207, + "right_line": 1207 + }, + { + "left_line": 1208, + "right_line": 1208 + }, + { + "left_line": 1209, + "right_line": 1209 + }, + { + "left_line": 1210, + "right_line": 1210 + }, + { + "left_line": 1211, + "right_line": 1211 + }, + { + "left_line": 1212, + "right_line": 1212 + }, + { + "left_line": 1213, + "right_line": 1213 + }, + { + "left_line": 1214, + "right_line": 1214 + }, + { + "left_line": 1215, + "right_line": 1215 + }, + { + "left_line": 1216, + "right_line": 1216 + }, + { + "left_line": 1217, + "right_line": 1217 + }, + { + "left_line": 1218, + "right_line": 1218 + }, + { + "left_line": 1219, + "right_line": 1219 + }, + { + "left_line": 1220, + "right_line": 1220 + }, + { + "left_line": 1221, + "right_line": 1221 + }, + { + "left_line": 1222, + "right_line": 1222 + }, + { + "left_line": 1223, + "right_line": 1223 + }, + { + "left_line": 1224, + "right_line": 1224 + }, + { + "left_line": 1225, + "right_line": 1225 + }, + { + "left_line": 1226, + "right_line": 1226 + }, + { + "left_line": 1227, + "right_line": 1227 + }, + { + "left_line": 1228, + "right_line": 1228 + }, + { + "left_line": 1229, + "right_line": 1229 + }, + { + "left_line": 1230, + "right_line": 1230 + }, + { + "left_line": 1231, + "right_line": 1231 + }, + { + "left_line": 1232, + "right_line": 1232 + }, + { + "left_line": 1233, + "right_line": 1233 + }, + { + "left_line": 1234, + "right_line": 1234 + }, + { + "left_line": 1235, + "right_line": 1235 + }, + { + "left_line": 1236, + "right_line": 1236 + }, + { + "left_line": 1237, + "right_line": 1237 + }, + { + "left_line": 1238, + "right_line": 1238 + }, + { + "left_line": 1239, + "right_line": 1239 + }, + { + "left_line": 1240, + "right_line": 1240 + }, + { + "left_line": 1241, + "right_line": 1241 + }, + { + "left_line": 1242, + "right_line": 1242 + }, + { + "left_line": 1243, + "right_line": 1243 + }, + { + "left_line": 1244, + "right_line": 1244 + }, + { + "left_line": 1245, + "right_line": 1245 + }, + { + "left_line": 1246, + "right_line": 1246 + }, + { + "left_line": 1247, + "right_line": 1247 + }, + { + "left_line": 1248, + "right_line": 1248 + }, + { + "left_line": 1249, + "right_line": 1249 + }, + { + "left_line": 1250, + "right_line": 1250 + }, + { + "left_line": 1251, + "right_line": 1251 + }, + { + "left_line": 1252, + "right_line": 1252 + }, + { + "left_line": 1253, + "right_line": 1253 + }, + { + "left_line": 1254, + "right_line": 1254 + }, + { + "left_line": 1255, + "right_line": 1255 + }, + { + "left_line": 1256, + "right_line": 1256 + }, + { + "left_line": 1257, + "right_line": 1257 + }, + { + "left_line": 1258, + "right_line": 1258 + }, + { + "left_line": 1259, + "right_line": 1259 + }, + { + "left_line": 1260, + "right_line": 1260 + }, + { + "left_line": 1261, + "right_line": 1261 + }, + { + "left_line": 1262, + "right_line": 1262 + }, + { + "left_line": 1263, + "right_line": 1263 + }, + { + "left_line": 1264, + "right_line": 1264 + }, + { + "left_line": 1265, + "right_line": 1265 + }, + { + "left_line": 1266, + "right_line": 1266 + }, + { + "left_line": 1267, + "right_line": 1267 + }, + { + "left_line": 1268, + "right_line": 1268 + }, + { + "left_line": 1269, + "right_line": 1269 + }, + { + "left_line": 1270, + "right_line": 1270 + }, + { + "left_line": 1271, + "right_line": 1271 + }, + { + "left_line": 1272, + "right_line": 1272 + }, + { + "left_line": 1273, + "right_line": 1273 + }, + { + "left_line": 1274, + "right_line": 1274 + }, + { + "left_line": 1275, + "right_line": 1275 + }, + { + "left_line": 1276, + "right_line": 1276 + }, + { + "left_line": 1277, + "right_line": 1277 + }, + { + "left_line": 1278, + "right_line": 1278 + }, + { + "left_line": 1279, + "right_line": 1279 + }, + { + "left_line": 1280, + "right_line": 1280 + }, + { + "left_line": 1281, + "right_line": 1281 + }, + { + "left_line": 1282, + "right_line": 1282 + }, + { + "left_line": 1283, + "right_line": 1283 + }, + { + "left_line": 1284, + "right_line": 1284 + }, + { + "left_line": 1285, + "right_line": 1285 + }, + { + "left_line": 1286, + "right_line": 1286 + }, + { + "left_line": 1287, + "right_line": 1287 + }, + { + "left_line": 1288, + "right_line": 1288 + }, + { + "left_line": 1289, + "right_line": 1289 + }, + { + "left_line": 1290, + "right_line": 1290 + }, + { + "left_line": 1291, + "right_line": 1291 + }, + { + "left_line": 1292, + "right_line": 1292 + }, + { + "left_line": 1293, + "right_line": 1293 + }, + { + "left_line": 1294, + "right_line": 1294 + }, + { + "left_line": 1295, + "right_line": 1295 + }, + { + "left_line": 1296, + "right_line": 1296 + }, + { + "left_line": 1297, + "right_line": 1297 + }, + { + "left_line": 1298, + "right_line": 1298 + }, + { + "left_line": 1299, + "right_line": 1299 + }, + { + "left_line": 1300, + "right_line": 1300 + }, + { + "left_line": 1301, + "right_line": 1301 + }, + { + "left_line": 1302, + "right_line": 1302 + }, + { + "left_line": 1303, + "right_line": 1303 + }, + { + "left_line": 1304, + "right_line": 1304 + }, + { + "left_line": 1305, + "right_line": 1305 + }, + { + "left_line": 1306, + "right_line": 1306 + }, + { + "left_line": 1307, + "right_line": 1307 + }, + { + "left_line": 1308, + "right_line": 1308 + }, + { + "left_line": 1309, + "right_line": 1309 + }, + { + "left_line": 1310, + "right_line": 1310 + }, + { + "left_line": 1311, + "right_line": 1311 + }, + { + "left_line": 1312, + "right_line": 1312 + }, + { + "left_line": 1313, + "right_line": 1313 + }, + { + "left_line": 1314, + "right_line": 1314 + }, + { + "left_line": 1315, + "right_line": 1315 + }, + { + "left_line": 1316, + "right_line": 1316 + }, + { + "left_line": 1317, + "right_line": 1317 + }, + { + "left_line": 1318, + "right_line": 1318 + }, + { + "left_line": 1319, + "right_line": 1319 + }, + { + "left_line": 1320, + "right_line": 1320 + }, + { + "left_line": 1321, + "right_line": 1321 + }, + { + "left_line": 1322, + "right_line": 1322 + }, + { + "left_line": 1323, + "right_line": 1323 + }, + { + "left_line": 1324, + "right_line": 1324 + }, + { + "left_line": 1325, + "right_line": 1325 + }, + { + "left_line": 1326, + "right_line": 1326 + }, + { + "left_line": 1327, + "right_line": 1327 + }, + { + "left_line": 1328, + "right_line": 1328 + }, + { + "left_line": 1329, + "right_line": 1329 + }, + { + "left_line": 1330, + "right_line": 1330 + }, + { + "left_line": 1331, + "right_line": 1331 + }, + { + "left_line": 1332, + "right_line": 1332 + }, + { + "left_line": 1333, + "right_line": 1333 + }, + { + "left_line": 1334, + "right_line": 1334 + }, + { + "left_line": 1335, + "right_line": 1335 + }, + { + "left_line": 1336, + "right_line": 1336 + }, + { + "left_line": 1337, + "right_line": 1337 + }, + { + "left_line": 1338, + "right_line": 1338 + }, + { + "left_line": 1339, + "right_line": 1339 + }, + { + "left_line": 1340, + "right_line": 1340 + }, + { + "left_line": 1341, + "right_line": 1341 + }, + { + "left_line": 1342, + "right_line": 1342 + }, + { + "left_line": 1343, + "right_line": 1343 + }, + { + "left_line": 1344, + "right_line": 1344 + }, + { + "left_line": 1345, + "right_line": 1345 + }, + { + "left_line": 1346, + "right_line": 1346 + }, + { + "left_line": 1347, + "right_line": 1347 + }, + { + "left_line": 1348, + "right_line": 1348 + }, + { + "left_line": 1349, + "right_line": 1349 + }, + { + "left_line": 1350, + "right_line": 1350 + }, + { + "left_line": 1351, + "right_line": 1351 + }, + { + "left_line": 1352, + "right_line": 1352 + }, + { + "left_line": 1353, + "right_line": 1353 + }, + { + "left_line": 1354, + "right_line": 1354 + }, + { + "left_line": 1355, + "right_line": 1355 + }, + { + "left_line": 1356, + "right_line": 1356 + }, + { + "left_line": 1357, + "right_line": 1357 + }, + { + "left_line": 1358, + "right_line": 1358 + }, + { + "left_line": 1359, + "right_line": 1359 + }, + { + "left_line": 1360, + "right_line": 1360 + }, + { + "left_line": 1361, + "right_line": 1361 + }, + { + "left_line": 1362, + "right_line": 1362 + }, + { + "left_line": 1363, + "right_line": 1363 + }, + { + "left_line": 1364, + "right_line": 1364 + }, + { + "left_line": 1365, + "right_line": 1365 + }, + { + "left_line": 1366, + "right_line": 1366 + }, + { + "left_line": 1367, + "right_line": 1367 + }, + { + "left_line": 1368, + "right_line": 1368 + }, + { + "left_line": 1369, + "right_line": 1369 + }, + { + "left_line": 1370, + "right_line": 1370 + }, + { + "left_line": 1371, + "right_line": 1371 + }, + { + "left_line": 1372, + "right_line": 1372 + }, + { + "left_line": 1373, + "right_line": 1373 + }, + { + "left_line": 1374, + "right_line": 1374 + }, + { + "left_line": 1375, + "right_line": 1375 + }, + { + "left_line": 1376, + "right_line": 1376 + }, + { + "left_line": 1377, + "right_line": 1377 + }, + { + "left_line": 1378, + "right_line": 1378 + }, + { + "left_line": 1379, + "right_line": 1379 + }, + { + "left_line": 1380, + "right_line": 1380 + }, + { + "left_line": 1381, + "right_line": 1381 + }, + { + "left_line": 1382, + "right_line": 1382 + }, + { + "left_line": 1383, + "right_line": 1383 + }, + { + "left_line": 1384, + "right_line": 1384 + }, + { + "left_line": 1385, + "right_line": 1385 + }, + { + "left_line": 1386, + "right_line": 1386 + }, + { + "left_line": 1387, + "right_line": 1387 + }, + { + "left_line": 1388, + "right_line": 1388 + }, + { + "left_line": 1389, + "right_line": 1389 + }, + { + "left_line": 1390, + "right_line": 1390 + }, + { + "left_line": 1391, + "right_line": 1391 + }, + { + "left_line": 1392, + "right_line": 1392 + }, + { + "left_line": 1393, + "right_line": 1393 + }, + { + "left_line": 1394, + "right_line": 1394 + }, + { + "left_line": 1395, + "right_line": 1395 + }, + { + "left_line": 1396, + "right_line": 1396 + }, + { + "left_line": 1397, + "right_line": 1397 + }, + { + "left_line": 1398, + "right_line": 1398 + }, + { + "left_line": 1399, + "right_line": 1399 + }, + { + "left_line": 1400, + "right_line": 1400 + }, + { + "left_line": 1401, + "right_line": 1401 + }, + { + "left_line": 1402, + "right_line": 1402 + }, + { + "left_line": 1403, + "right_line": 1403 + }, + { + "left_line": 1404, + "right_line": 1404 + }, + { + "left_line": 1405, + "right_line": 1405 + }, + { + "left_line": 1406, + "right_line": 1406 + }, + { + "left_line": 1407, + "right_line": 1407 + }, + { + "left_line": 1408, + "right_line": 1408 + }, + { + "left_line": 1409, + "right_line": 1409 + }, + { + "left_line": 1410, + "right_line": 1410 + }, + { + "left_line": 1411, + "right_line": 1411 + }, + { + "left_line": 1412, + "right_line": 1412 + }, + { + "left_line": 1413, + "right_line": 1413 + }, + { + "left_line": 1414, + "right_line": 1414 + }, + { + "left_line": 1415, + "right_line": 1415 + }, + { + "left_line": 1416, + "right_line": 1416 + }, + { + "left_line": 1417, + "right_line": 1417 + }, + { + "left_line": 1418, + "right_line": 1418 + }, + { + "left_line": 1419, + "right_line": 1419 + }, + { + "left_line": 1420, + "right_line": 1420 + }, + { + "left_line": 1421, + "right_line": 1421 + }, + { + "left_line": 1422, + "right_line": 1422 + }, + { + "left_line": 1423, + "right_line": 1423 + }, + { + "left_line": 1424, + "right_line": 1424 + }, + { + "left_line": 1425, + "right_line": 1425 + }, + { + "left_line": 1426, + "right_line": 1426 + }, + { + "left_line": 1427, + "right_line": 1427 + }, + { + "left_line": 1428, + "right_line": 1428 + }, + { + "left_line": 1429, + "right_line": 1429 + }, + { + "left_line": 1430, + "right_line": 1430 + }, + { + "left_line": 1431, + "right_line": 1431 + }, + { + "left_line": 1432, + "right_line": 1432 + }, + { + "left_line": 1433, + "right_line": 1433 + }, + { + "left_line": 1434, + "right_line": 1434 + }, + { + "left_line": 1435, + "right_line": 1435 + }, + { + "left_line": 1436, + "right_line": 1436 + }, + { + "left_line": 1437, + "right_line": 1437 + }, + { + "left_line": 1438, + "right_line": 1438 + }, + { + "left_line": 1439, + "right_line": 1439 + }, + { + "left_line": 1440, + "right_line": 1440 + }, + { + "left_line": 1441, + "right_line": 1441 + }, + { + "left_line": 1442, + "right_line": 1442 + }, + { + "left_line": 1443, + "right_line": 1443 + }, + { + "left_line": 1444, + "right_line": 1444 + }, + { + "left_line": 1445, + "right_line": 1445 + }, + { + "left_line": 1446, + "right_line": 1446 + }, + { + "left_line": 1447, + "right_line": 1447 + }, + { + "left_line": 1448, + "right_line": 1448 + }, + { + "left_line": 1449, + "right_line": 1449 + }, + { + "left_line": 1450, + "right_line": 1450 + }, + { + "left_line": 1451, + "right_line": 1451 + }, + { + "left_line": 1452, + "right_line": 1452 + }, + { + "left_line": 1453, + "right_line": 1453 + }, + { + "left_line": 1454, + "right_line": 1454 + }, + { + "left_line": 1455, + "right_line": 1455 + }, + { + "left_line": 1456, + "right_line": 1456 + }, + { + "left_line": 1457, + "right_line": 1457 + }, + { + "left_line": 1458, + "right_line": 1458 + }, + { + "left_line": 1459, + "right_line": 1459 + }, + { + "left_line": 1460, + "right_line": 1460 + }, + { + "left_line": 1461, + "right_line": 1461 + }, + { + "left_line": 1462, + "right_line": 1462 + }, + { + "left_line": 1463, + "right_line": 1463 + }, + { + "left_line": 1464, + "right_line": 1464 + }, + { + "left_line": 1465, + "right_line": 1465 + }, + { + "left_line": 1466, + "right_line": 1466 + }, + { + "left_line": 1467, + "right_line": 1467 + }, + { + "left_line": 1468, + "right_line": 1468 + }, + { + "left_line": 1469, + "right_line": 1469 + }, + { + "left_line": 1470, + "right_line": 1470 + }, + { + "left_line": 1471, + "right_line": 1471 + }, + { + "left_line": 1472, + "right_line": 1472 + }, + { + "left_line": 1473, + "right_line": 1473 + }, + { + "left_line": 1474, + "right_line": 1474 + }, + { + "left_line": 1475, + "right_line": 1475 + }, + { + "left_line": 1476, + "right_line": 1476 + }, + { + "left_line": 1477, + "right_line": 1477 + }, + { + "left_line": 1478, + "right_line": 1478 + }, + { + "left_line": 1479, + "right_line": 1479 + }, + { + "left_line": 1480, + "right_line": 1480 + }, + { + "left_line": 1481, + "right_line": 1481 + }, + { + "left_line": 1482, + "right_line": 1482 + }, + { + "left_line": 1483, + "right_line": 1483 + }, + { + "left_line": 1484, + "right_line": 1484 + }, + { + "left_line": 1485, + "right_line": 1485 + }, + { + "left_line": 1486, + "right_line": 1486 + }, + { + "left_line": 1487, + "right_line": 1487 + }, + { + "left_line": 1488, + "right_line": 1488 + }, + { + "left_line": 1489, + "right_line": 1489 + }, + { + "left_line": 1490, + "right_line": 1490 + }, + { + "left_line": 1491, + "right_line": 1491 + }, + { + "left_line": 1492, + "right_line": 1492 + }, + { + "left_line": 1493, + "right_line": 1493 + }, + { + "left_line": 1494, + "right_line": 1494 + }, + { + "left_line": 1495, + "right_line": 1495 + }, + { + "left_line": 1496, + "right_line": 1496 + }, + { + "left_line": 1497, + "right_line": 1497 + }, + { + "left_line": 1498, + "right_line": 1498 + }, + { + "left_line": 1499, + "right_line": 1499 + }, + { + "left_line": 1500, + "right_line": 1500 + }, + { + "left_line": 1501, + "right_line": 1501 + }, + { + "left_line": 1502, + "right_line": 1502 + }, + { + "left_line": 1503, + "right_line": 1503 + }, + { + "left_line": 1504, + "right_line": 1504 + }, + { + "left_line": 1505, + "right_line": 1505 + }, + { + "left_line": 1506, + "right_line": 1506 + }, + { + "left_line": 1507, + "right_line": 1507 + }, + { + "left_line": 1508, + "right_line": 1508 + }, + { + "left_line": 1509, + "right_line": 1509 + }, + { + "left_line": 1510, + "right_line": 1510 + }, + { + "left_line": 1511, + "right_line": 1511 + }, + { + "left_line": 1512, + "right_line": 1512 + }, + { + "left_line": 1513, + "right_line": 1513 + }, + { + "left_line": 1514, + "right_line": 1514 + }, + { + "left_line": 1515, + "right_line": 1515 + }, + { + "left_line": 1516, + "right_line": 1516 + }, + { + "left_line": 1517, + "right_line": 1517 + }, + { + "left_line": 1518, + "right_line": 1518 + }, + { + "left_line": 1519, + "right_line": 1519 + }, + { + "left_line": 1520, + "right_line": 1520 + }, + { + "left_line": 1521, + "right_line": 1521 + }, + { + "left_line": 1522, + "right_line": 1522 + }, + { + "left_line": 1523, + "right_line": 1523 + }, + { + "left_line": 1524, + "right_line": 1524 + }, + { + "left_line": 1525, + "right_line": 1525 + }, + { + "left_line": 1526, + "right_line": 1526 + }, + { + "left_line": 1527, + "right_line": 1527 + }, + { + "left_line": 1528, + "right_line": 1528 + }, + { + "left_line": 1529, + "right_line": 1529 + }, + { + "left_line": 1530, + "right_line": 1530 + }, + { + "left_line": 1531, + "right_line": 1531 + }, + { + "left_line": 1532, + "right_line": 1532 + }, + { + "left_line": 1533, + "right_line": 1533 + }, + { + "left_line": 1534, + "right_line": 1534 + }, + { + "left_line": 1535, + "right_line": 1535 + }, + { + "left_line": 1536, + "right_line": 1536 + }, + { + "left_line": 1537, + "right_line": 1537 + }, + { + "left_line": 1538, + "right_line": 1538 + }, + { + "left_line": 1539, + "right_line": 1539 + }, + { + "left_line": 1540, + "right_line": 1540 + }, + { + "left_line": 1541, + "right_line": 1541 + }, + { + "left_line": 1542, + "right_line": 1542 + }, + { + "left_line": 1543, + "right_line": 1543 + }, + { + "left_line": 1544, + "right_line": 1544 + }, + { + "left_line": 1545, + "right_line": 1545 + }, + { + "left_line": 1546, + "right_line": 1546 + }, + { + "left_line": 1547, + "right_line": 1547 + }, + { + "left_line": 1548, + "right_line": 1548 + }, + { + "left_line": 1549, + "right_line": 1549 + }, + { + "left_line": 1550, + "right_line": 1550 + }, + { + "left_line": 1551, + "right_line": 1551 + }, + { + "left_line": 1552, + "right_line": 1552 + }, + { + "left_line": 1553, + "right_line": 1553 + }, + { + "left_line": 1554, + "right_line": 1554 + }, + { + "left_line": 1555, + "right_line": 1555 + }, + { + "left_line": 1556, + "right_line": 1556 + }, + { + "left_line": 1557, + "right_line": 1557 + }, + { + "left_line": 1558, + "right_line": 1558 + }, + { + "left_line": 1559, + "right_line": 1559 + }, + { + "left_line": 1560, + "right_line": 1560 + }, + { + "left_line": 1561, + "right_line": 1561 + }, + { + "left_line": 1562, + "right_line": 1562 + }, + { + "left_line": 1563, + "right_line": 1563 + }, + { + "left_line": 1564, + "right_line": 1564 + }, + { + "left_line": 1565, + "right_line": 1565 + }, + { + "left_line": 1566, + "right_line": 1566 + }, + { + "left_line": 1567, + "right_line": 1567 + }, + { + "left_line": 1568, + "right_line": 1568 + }, + { + "left_line": 1569, + "right_line": 1569 + }, + { + "left_line": 1570, + "right_line": 1570 + }, + { + "left_line": 1571, + "right_line": 1571 + }, + { + "left_line": 1572, + "right_line": 1572 + }, + { + "left_line": 1573, + "right_line": 1573 + }, + { + "left_line": 1574, + "right_line": 1574 + }, + { + "left_line": 1575, + "right_line": 1575 + }, + { + "left_line": 1576, + "right_line": 1576 + }, + { + "left_line": 1577, + "right_line": 1577 + }, + { + "left_line": 1578, + "right_line": 1578 + }, + { + "left_line": 1579, + "right_line": 1579 + }, + { + "left_line": 1580, + "right_line": 1580 + }, + { + "left_line": 1581, + "right_line": 1581 + }, + { + "left_line": 1582, + "right_line": 1582 + }, + { + "left_line": 1583, + "right_line": 1583 + }, + { + "left_line": 1584, + "right_line": 1584 + }, + { + "left_line": 1585, + "right_line": 1585 + }, + { + "left_line": 1586, + "right_line": 1586 + }, + { + "left_line": 1587, + "right_line": 1587 + }, + { + "left_line": 1588, + "right_line": 1588 + }, + { + "left_line": 1589, + "right_line": 1589 + }, + { + "left_line": 1590, + "right_line": 1590 + }, + { + "left_line": 1591, + "right_line": 1591 + }, + { + "left_line": 1592, + "right_line": 1592 + }, + { + "left_line": 1593, + "right_line": 1593 + }, + { + "left_line": 1594, + "right_line": 1594 + }, + { + "left_line": 1595, + "right_line": 1595 + }, + { + "left_line": 1596, + "right_line": 1596 + }, + { + "left_line": 1597, + "right_line": 1597 + }, + { + "left_line": 1598, + "right_line": 1598 + }, + { + "left_line": 1599, + "right_line": 1599 + }, + { + "left_line": 1600, + "right_line": 1600 + }, + { + "left_line": 1601, + "right_line": 1601 + }, + { + "left_line": 1602, + "right_line": 1602 + }, + { + "left_line": 1603, + "right_line": 1603 + }, + { + "left_line": 1604, + "right_line": 1604 + }, + { + "left_line": 1605, + "right_line": 1605 + }, + { + "left_line": 1606, + "right_line": 1606 + }, + { + "left_line": 1607, + "right_line": 1607 + }, + { + "left_line": 1608, + "right_line": 1608 + }, + { + "left_line": 1609, + "right_line": 1609 + }, + { + "left_line": 1610, + "right_line": 1610 + }, + { + "left_line": 1611, + "right_line": 1611 + }, + { + "left_line": 1612, + "right_line": 1612 + }, + { + "left_line": 1613, + "right_line": 1613 + }, + { + "left_line": 1614, + "right_line": 1614 + }, + { + "left_line": 1615, + "right_line": 1615 + }, + { + "left_line": 1616, + "right_line": 1616 + }, + { + "left_line": 1617, + "right_line": 1617 + }, + { + "left_line": 1618, + "right_line": 1618 + }, + { + "left_line": 1619, + "right_line": 1619 + }, + { + "left_line": 1620, + "right_line": 1620 + }, + { + "left_line": 1621, + "right_line": 1621 + }, + { + "left_line": 1622, + "right_line": 1622 + }, + { + "left_line": 1623, + "right_line": 1623 + }, + { + "left_line": 1624, + "right_line": 1624 + }, + { + "left_line": 1625, + "right_line": 1625 + }, + { + "left_line": 1626, + "right_line": 1626 + }, + { + "left_line": 1627, + "right_line": 1627 + }, + { + "left_line": 1628, + "right_line": 1628 + }, + { + "left_line": 1629, + "right_line": 1629 + }, + { + "left_line": 1630, + "right_line": 1630 + }, + { + "left_line": 1631, + "right_line": 1631 + }, + { + "left_line": 1632, + "right_line": 1632 + }, + { + "left_line": 1633, + "right_line": 1633 + }, + { + "left_line": 1634, + "right_line": 1634 + }, + { + "left_line": 1635, + "right_line": 1635 + }, + { + "left_line": 1636, + "right_line": 1636 + }, + { + "left_line": 1637, + "right_line": 1637 + }, + { + "left_line": 1638, + "right_line": 1638 + }, + { + "left_line": 1639, + "right_line": 1639 + }, + { + "left_line": 1640, + "right_line": 1640 + }, + { + "left_line": 1641, + "right_line": 1641 + }, + { + "left_line": 1642, + "right_line": 1642 + }, + { + "left_line": 1643, + "right_line": 1643 + }, + { + "left_line": 1644, + "right_line": 1644 + }, + { + "left_line": 1645, + "right_line": 1645 + }, + { + "left_line": 1646, + "right_line": 1646 + }, + { + "left_line": 1647, + "right_line": 1647 + }, + { + "left_line": 1648, + "right_line": 1648 + }, + { + "left_line": 1649, + "right_line": 1649 + }, + { + "left_line": 1650, + "right_line": 1650 + }, + { + "left_line": 1651, + "right_line": 1651 + }, + { + "left_line": 1652, + "right_line": 1652 + }, + { + "left_line": 1653, + "right_line": 1653 + }, + { + "left_line": 1654, + "right_line": 1654 + }, + { + "left_line": 1655, + "right_line": 1655 + }, + { + "left_line": 1656, + "right_line": 1656 + }, + { + "left_line": 1657, + "right_line": 1657 + }, + { + "left_line": 1658, + "right_line": 1658 + }, + { + "left_line": 1659, + "right_line": 1659 + }, + { + "left_line": 1660, + "right_line": 1660 + }, + { + "left_line": 1661, + "right_line": 1661 + }, + { + "left_line": 1662, + "right_line": 1662 + }, + { + "left_line": 1663, + "right_line": 1663 + }, + { + "left_line": 1664, + "right_line": 1664 + }, + { + "left_line": 1665, + "right_line": 1665 + }, + { + "left_line": 1666, + "right_line": 1666 + }, + { + "left_line": 1667, + "right_line": 1667 + }, + { + "left_line": 1668, + "right_line": 1668 + }, + { + "left_line": 1669, + "right_line": 1669 + }, + { + "left_line": 1670, + "right_line": 1670 + }, + { + "left_line": 1671, + "right_line": 1671 + }, + { + "left_line": 1672, + "right_line": 1672 + }, + { + "left_line": 1673, + "right_line": 1673 + }, + { + "left_line": 1674, + "right_line": 1674 + }, + { + "left_line": 1675, + "right_line": 1675 + }, + { + "left_line": 1676, + "right_line": 1676 + }, + { + "left_line": 1677, + "right_line": 1677 + }, + { + "left_line": 1678, + "right_line": 1678 + }, + { + "left_line": 1679, + "right_line": 1679 + }, + { + "left_line": 1680, + "right_line": 1680 + }, + { + "left_line": 1681, + "right_line": 1681 + }, + { + "left_line": 1682, + "right_line": 1682 + }, + { + "left_line": 1683, + "right_line": 1683 + }, + { + "left_line": 1684, + "right_line": 1684 + }, + { + "left_line": 1685, + "right_line": 1685 + }, + { + "left_line": 1686, + "right_line": 1686 + }, + { + "left_line": 1687, + "right_line": 1687 + }, + { + "left_line": 1688, + "right_line": 1688 + }, + { + "left_line": 1689, + "right_line": 1689 + }, + { + "left_line": 1690, + "right_line": 1690 + }, + { + "left_line": 1691, + "right_line": 1691 + }, + { + "left_line": 1692, + "right_line": 1692 + }, + { + "left_line": 1693, + "right_line": 1693 + }, + { + "left_line": 1694, + "right_line": 1694 + }, + { + "left_line": 1695, + "right_line": 1695 + }, + { + "left_line": 1696, + "right_line": 1696 + }, + { + "left_line": 1697, + "right_line": 1697 + }, + { + "left_line": 1698, + "right_line": 1698 + }, + { + "left_line": 1699, + "right_line": 1699 + }, + { + "left_line": 1700, + "right_line": 1700 + }, + { + "left_line": 1701, + "right_line": 1701 + }, + { + "left_line": 1702, + "right_line": 1702 + }, + { + "left_line": 1703, + "right_line": 1703 + }, + { + "left_line": 1704, + "right_line": 1704 + }, + { + "left_line": 1705, + "right_line": 1705 + }, + { + "left_line": 1706, + "right_line": 1706 + }, + { + "left_line": 1707, + "right_line": 1707 + }, + { + "left_line": 1708, + "right_line": 1708 + }, + { + "left_line": 1709, + "right_line": 1709 + }, + { + "left_line": 1710, + "right_line": 1710 + }, + { + "left_line": 1711, + "right_line": 1711 + }, + { + "left_line": 1712, + "right_line": 1712 + }, + { + "left_line": 1713, + "right_line": 1713 + }, + { + "left_line": 1714, + "right_line": 1714 + }, + { + "left_line": 1715, + "right_line": 1715 + }, + { + "left_line": 1716, + "right_line": 1716 + }, + { + "left_line": 1717, + "right_line": 1717 + }, + { + "left_line": 1718, + "right_line": 1718 + }, + { + "left_line": 1719, + "right_line": 1719 + }, + { + "left_line": 1720, + "right_line": 1720 + }, + { + "left_line": 1721, + "right_line": 1721 + }, + { + "left_line": 1722, + "right_line": 1722 + }, + { + "left_line": 1723, + "right_line": 1723 + }, + { + "left_line": 1724, + "right_line": 1724 + }, + { + "left_line": 1725, + "right_line": 1725 + }, + { + "left_line": 1726, + "right_line": 1726 + }, + { + "left_line": 1727, + "right_line": 1727 + }, + { + "left_line": 1728, + "right_line": 1728 + }, + { + "left_line": 1729, + "right_line": 1729 + }, + { + "left_line": 1730, + "right_line": 1730 + }, + { + "left_line": 1731, + "right_line": 1731 + }, + { + "left_line": 1732, + "right_line": 1732 + }, + { + "left_line": 1733, + "right_line": 1733 + }, + { + "left_line": 1734, + "right_line": 1734 + }, + { + "left_line": 1735, + "right_line": 1735 + }, + { + "left_line": 1736, + "right_line": 1736 + }, + { + "left_line": 1737, + "right_line": 1737 + }, + { + "left_line": 1738, + "right_line": 1738 + }, + { + "left_line": 1739, + "right_line": 1739 + }, + { + "left_line": 1740, + "right_line": 1740 + }, + { + "left_line": 1741, + "right_line": 1741 + }, + { + "left_line": 1742, + "right_line": 1742 + }, + { + "left_line": 1743, + "right_line": 1743 + }, + { + "left_line": 1744, + "right_line": 1744 + }, + { + "left_line": 1745, + "right_line": 1745 + }, + { + "left_line": 1746, + "right_line": 1746 + }, + { + "left_line": 1747, + "right_line": 1747 + }, + { + "left_line": 1748, + "right_line": 1748 + }, + { + "left_line": 1749, + "right_line": 1749 + }, + { + "left_line": 1750, + "right_line": 1750 + }, + { + "left_line": 1751, + "right_line": 1751 + }, + { + "left_line": 1752, + "right_line": 1752 + }, + { + "left_line": 1753, + "right_line": 1753 + }, + { + "left_line": 1754, + "right_line": 1754 + }, + { + "left_line": 1755, + "right_line": 1755 + }, + { + "left_line": 1756, + "right_line": 1756 + }, + { + "left_line": 1757, + "right_line": 1757 + }, + { + "left_line": 1758, + "right_line": 1758 + }, + { + "left_line": 1759, + "right_line": 1759 + }, + { + "left_line": 1760, + "right_line": 1760 + }, + { + "left_line": 1761, + "right_line": 1761 + }, + { + "left_line": 1762, + "right_line": 1762 + }, + { + "left_line": 1763, + "right_line": 1763 + }, + { + "left_line": 1764, + "right_line": 1764 + }, + { + "left_line": 1765, + "right_line": 1765 + }, + { + "left_line": 1766, + "right_line": 1766 + }, + { + "left_line": 1767, + "right_line": 1767 + }, + { + "left_line": 1768, + "right_line": 1768 + }, + { + "left_line": 1769, + "right_line": 1769 + }, + { + "left_line": 1770, + "right_line": 1770 + }, + { + "left_line": 1771, + "right_line": 1771 + }, + { + "left_line": 1772, + "right_line": 1772 + }, + { + "left_line": 1773, + "right_line": 1773 + }, + { + "left_line": 1774, + "right_line": 1774 + }, + { + "left_line": 1775, + "right_line": 1775 + }, + { + "left_line": 1776, + "right_line": 1776 + }, + { + "left_line": 1777, + "right_line": 1777 + }, + { + "left_line": 1778, + "right_line": 1778 + }, + { + "left_line": 1779, + "right_line": 1779 + }, + { + "left_line": 1780, + "right_line": 1780 + }, + { + "left_line": 1781, + "right_line": 1781 + }, + { + "left_line": 1782, + "right_line": 1782 + }, + { + "left_line": 1783, + "right_line": 1783 + }, + { + "left_line": 1784, + "right_line": 1784 + }, + { + "left_line": 1785, + "right_line": 1785 + }, + { + "left_line": 1786, + "right_line": 1786 + }, + { + "left_line": 1787, + "right_line": 1787 + }, + { + "left_line": 1788, + "right_line": 1788 + }, + { + "left_line": 1789, + "right_line": 1789 + }, + { + "left_line": 1790, + "right_line": 1790 + }, + { + "left_line": 1791, + "right_line": 1791 + }, + { + "left_line": 1792, + "right_line": 1792 + }, + { + "left_line": 1793, + "right_line": 1793 + }, + { + "left_line": 1794, + "right_line": 1794 + }, + { + "left_line": 1795, + "right_line": 1795 + }, + { + "left_line": 1796, + "right_line": 1796 + }, + { + "left_line": 1797, + "right_line": 1797 + }, + { + "left_line": 1798, + "right_line": 1798 + }, + { + "left_line": 1799, + "right_line": 1799 + }, + { + "left_line": 1800, + "right_line": 1800 + }, + { + "left_line": 1801, + "right_line": 1801 + }, + { + "left_line": 1802, + "right_line": 1802 + }, + { + "left_line": 1803, + "right_line": 1803 + }, + { + "left_line": 1804, + "right_line": 1804 + }, + { + "left_line": 1805, + "right_line": 1805 + }, + { + "left_line": 1806, + "right_line": 1806 + }, + { + "left_line": 1807, + "right_line": 1807 + }, + { + "left_line": 1808, + "right_line": 1808 + }, + { + "left_line": 1809, + "right_line": 1809 + }, + { + "left_line": 1810, + "right_line": 1810 + }, + { + "left_line": 1811, + "right_line": 1811 + }, + { + "left_line": 1812, + "right_line": 1812 + }, + { + "left_line": 1813, + "right_line": 1813 + }, + { + "left_line": 1814, + "right_line": 1814 + }, + { + "left_line": 1815, + "right_line": 1815 + }, + { + "left_line": 1816, + "right_line": 1816 + }, + { + "left_line": 1817, + "right_line": 1817 + }, + { + "left_line": 1818, + "right_line": 1818 + }, + { + "left_line": 1819, + "right_line": 1819 + }, + { + "left_line": 1820, + "right_line": 1820 + }, + { + "left_line": 1821, + "right_line": 1821 + }, + { + "left_line": 1822, + "right_line": 1822 + }, + { + "left_line": 1823, + "right_line": 1823 + }, + { + "left_line": 1824, + "right_line": 1824 + }, + { + "left_line": 1825, + "right_line": 1825 + }, + { + "left_line": 1826, + "right_line": 1826 + }, + { + "left_line": 1827, + "right_line": 1827 + }, + { + "left_line": 1828, + "right_line": 1828 + }, + { + "left_line": 1829, + "right_line": 1829 + }, + { + "left_line": 1830, + "right_line": 1830 + }, + { + "left_line": 1831, + "right_line": 1831 + }, + { + "left_line": 1832, + "right_line": 1832 + }, + { + "left_line": 1833, + "right_line": 1833 + }, + { + "left_line": 1834, + "right_line": 1834 + }, + { + "left_line": 1835, + "right_line": 1835 + }, + { + "left_line": 1836, + "right_line": 1836 + }, + { + "left_line": 1837, + "right_line": 1837 + }, + { + "left_line": 1838, + "right_line": 1838 + }, + { + "left_line": 1839, + "right_line": 1839 + }, + { + "left_line": 1840, + "right_line": 1840 + }, + { + "left_line": 1841, + "right_line": 1841 + }, + { + "left_line": 1842, + "right_line": 1842 + }, + { + "left_line": 1843, + "right_line": 1843 + }, + { + "left_line": 1844, + "right_line": 1844 + }, + { + "left_line": 1845, + "right_line": 1845 + }, + { + "left_line": 1846, + "right_line": 1846 + }, + { + "left_line": 1847, + "right_line": 1847 + }, + { + "left_line": 1848, + "right_line": 1848 + }, + { + "left_line": 1849, + "right_line": 1849 + }, + { + "left_line": 1850, + "right_line": 1850 + }, + { + "left_line": 1851, + "right_line": 1851 + }, + { + "left_line": 1852, + "right_line": 1852 + }, + { + "left_line": 1853, + "right_line": 1853 + }, + { + "left_line": 1854, + "right_line": 1854 + }, + { + "left_line": 1855, + "right_line": 1855 + }, + { + "left_line": 1856, + "right_line": 1856 + }, + { + "left_line": 1857, + "right_line": 1857 + }, + { + "left_line": 1858, + "right_line": 1858 + }, + { + "left_line": 1859, + "right_line": 1859 + }, + { + "left_line": 1860, + "right_line": 1860 + }, + { + "left_line": 1861, + "right_line": 1861 + }, + { + "left_line": 1862, + "right_line": 1862 + }, + { + "left_line": 1863, + "right_line": 1863 + }, + { + "left_line": 1864, + "right_line": 1864 + }, + { + "left_line": 1865, + "right_line": 1865 + }, + { + "left_line": 1866, + "right_line": 1866 + }, + { + "left_line": 1867, + "right_line": 1867 + }, + { + "left_line": 1868, + "right_line": 1868 + }, + { + "left_line": 1869, + "right_line": 1869 + }, + { + "left_line": 1870, + "right_line": 1870 + }, + { + "left_line": 1871, + "right_line": 1871 + }, + { + "left_line": 1872, + "right_line": 1872 + }, + { + "left_line": 1873, + "right_line": 1873 + }, + { + "left_line": 1874, + "right_line": 1874 + }, + { + "left_line": 1875, + "right_line": 1875 + }, + { + "left_line": 1876, + "right_line": 1876 + }, + { + "left_line": 1877, + "right_line": 1877 + }, + { + "left_line": 1878, + "right_line": 1878 + }, + { + "left_line": 1879, + "right_line": 1879 + }, + { + "left_line": 1880, + "right_line": 1880 + }, + { + "left_line": 1881, + "right_line": 1881 + }, + { + "left_line": 1882, + "right_line": 1882 + }, + { + "left_line": 1883, + "right_line": 1883 + }, + { + "left_line": 1884, + "right_line": 1884 + }, + { + "left_line": 1885, + "right_line": 1885 + }, + { + "left_line": 1886, + "right_line": 1886 + }, + { + "left_line": 1887, + "right_line": 1887 + }, + { + "left_line": 1888, + "right_line": 1888 + }, + { + "left_line": 1889, + "right_line": 1889 + }, + { + "left_line": 1890, + "right_line": 1890 + }, + { + "left_line": 1891, + "right_line": 1891 + }, + { + "left_line": 1892, + "right_line": 1892 + }, + { + "left_line": 1893, + "right_line": 1893 + }, + { + "left_line": 1894, + "right_line": 1894 + }, + { + "left_line": 1895, + "right_line": 1895 + }, + { + "left_line": 1896, + "right_line": 1896 + }, + { + "left_line": 1897, + "right_line": 1897 + }, + { + "left_line": 1898, + "right_line": 1898 + }, + { + "left_line": 1899, + "right_line": 1899 + }, + { + "left_line": 1900, + "right_line": 1900 + }, + { + "left_line": 1901, + "right_line": 1901 + }, + { + "left_line": 1902, + "right_line": 1902 + }, + { + "left_line": 1903, + "right_line": 1903 + }, + { + "left_line": 1904, + "right_line": 1904 + }, + { + "left_line": 1905, + "right_line": 1905 + }, + { + "left_line": 1906, + "right_line": 1906 + }, + { + "left_line": 1907, + "right_line": 1907 + }, + { + "left_line": 1908, + "right_line": 1908 + }, + { + "left_line": 1909, + "right_line": 1909 + }, + { + "left_line": 1910, + "right_line": 1910 + }, + { + "left_line": 1911, + "right_line": 1911 + }, + { + "left_line": 1912, + "right_line": 1912 + }, + { + "left_line": 1913, + "right_line": 1913 + }, + { + "left_line": 1914, + "right_line": 1914 + }, + { + "left_line": 1915, + "right_line": 1915 + }, + { + "left_line": 1916, + "right_line": 1916 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ts_zod_improve_mini/metadata.json b/tests/testdata/ts_zod_improve_mini/metadata.json new file mode 100644 index 0000000..4845110 --- /dev/null +++ b/tests/testdata/ts_zod_improve_mini/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/colinhacks/zod", + "parent": "21afffdb42ccab554036312e33fed0ea3cb8f982", + "commit": "e36743e513aadb307b29949a80d6eb0dcc8fc278", + "message": "Improve mini treeshaking", + "author": "Colin McDonnell", + "date": "2026-01-04T00:03:53-08:00", + "language": "typescript", + "files_changed": 1, + "insertions": 0, + "deletions": 0 +} \ No newline at end of file diff --git a/tests/testdata/ts_zod_improve_mini/new.ts b/tests/testdata/ts_zod_improve_mini/new.ts new file mode 100644 index 0000000..0e9cedf --- /dev/null +++ b/tests/testdata/ts_zod_improve_mini/new.ts @@ -0,0 +1,1916 @@ +import * as core from "../core/index.js"; +import * as util from "../core/util.js"; +import * as parse from "./parse.js"; + +type SomeType = core.SomeType; + +export interface ZodMiniType< + out Output = unknown, + out Input = unknown, + out Internals extends core.$ZodTypeInternals = core.$ZodTypeInternals, +> extends core.$ZodType { + type: Internals["def"]["type"]; + check(...checks: (core.CheckFn> | core.$ZodCheck>)[]): this; + with(...checks: (core.CheckFn> | core.$ZodCheck>)[]): this; + clone(def?: Internals["def"], params?: { parent: boolean }): this; + register( + registry: R, + ...meta: this extends R["_schema"] + ? undefined extends R["_meta"] + ? [core.$replace?] + : [core.$replace] + : ["Incompatible schema"] + ): this; + brand( + value?: T + ): PropertyKey extends T ? this : core.$ZodBranded; + + def: Internals["def"]; + + parse(data: unknown, params?: core.ParseContext): core.output; + safeParse(data: unknown, params?: core.ParseContext): util.SafeParseResult>; + parseAsync(data: unknown, params?: core.ParseContext): Promise>; + safeParseAsync( + data: unknown, + params?: core.ParseContext + ): Promise>>; + apply(fn: (schema: this) => T): T; +} + +interface _ZodMiniType + extends ZodMiniType {} + +export const ZodMiniType: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniType", + (inst, def) => { + if (!inst._zod) throw new Error("Uninitialized schema in ZodMiniType."); + + core.$ZodType.init(inst, def); + + inst.def = def; + inst.type = def.type; + inst.parse = (data, params) => parse.parse(inst, data, params, { callee: inst.parse }); + inst.safeParse = (data, params) => parse.safeParse(inst, data, params); + inst.parseAsync = async (data, params) => parse.parseAsync(inst, data, params, { callee: inst.parseAsync }); + inst.safeParseAsync = async (data, params) => parse.safeParseAsync(inst, data, params); + inst.check = (...checks) => { + return inst.clone( + { + ...def, + checks: [ + ...(def.checks ?? []), + ...checks.map((ch) => + typeof ch === "function" ? { _zod: { check: ch, def: { check: "custom" }, onattach: [] } } : ch + ), + ], + }, + { parent: true } + ); + }; + inst.with = inst.check; + inst.clone = (_def, params) => core.clone(inst, _def, params); + inst.brand = () => inst as any; + inst.register = ((reg: any, meta: any) => { + reg.add(inst, meta); + return inst; + }) as any; + inst.apply = (fn) => fn(inst); + } +); + +export interface _ZodMiniString = core.$ZodStringInternals> + extends _ZodMiniType, + core.$ZodString { + _zod: T; +} + +// ZodMiniString +export interface ZodMiniString + extends _ZodMiniString>, + core.$ZodString {} +export const ZodMiniString: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniString", + (inst, def) => { + core.$ZodString.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function string(params?: string | core.$ZodStringParams): ZodMiniString { + return core._string(ZodMiniString, params) as any; +} + +// ZodMiniStringFormat +export interface ZodMiniStringFormat + extends _ZodMiniString>, + core.$ZodStringFormat { + // _zod: core.$ZodStringFormatInternals; +} +export const ZodMiniStringFormat: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniStringFormat", + (inst, def) => { + core.$ZodStringFormat.init(inst, def); + ZodMiniString.init(inst, def); + } +); + +// ZodMiniEmail +export interface ZodMiniEmail extends _ZodMiniString {} +export const ZodMiniEmail: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniEmail", + (inst, def) => { + core.$ZodEmail.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function email(params?: string | core.$ZodEmailParams): ZodMiniEmail { + return core._email(ZodMiniEmail, params); +} + +// ZodMiniGUID +export interface ZodMiniGUID extends _ZodMiniString { + // _zod: core.$ZodGUIDInternals; +} +export const ZodMiniGUID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniGUID", + (inst, def) => { + core.$ZodGUID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function guid(params?: string | core.$ZodGUIDParams): ZodMiniGUID { + return core._guid(ZodMiniGUID, params); +} + +// ZodMiniUUID +export interface ZodMiniUUID extends _ZodMiniString { + // _zod: core.$ZodUUIDInternals; +} +export const ZodMiniUUID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniUUID", + (inst, def) => { + core.$ZodUUID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function uuid(params?: string | core.$ZodUUIDParams): ZodMiniUUID { + return core._uuid(ZodMiniUUID, params); +} + +// @__NO_SIDE_EFFECTS__ +export function uuidv4(params?: string | core.$ZodUUIDv4Params): ZodMiniUUID { + return core._uuidv4(ZodMiniUUID, params); +} + +// ZodMiniUUIDv6 + +// @__NO_SIDE_EFFECTS__ +export function uuidv6(params?: string | core.$ZodUUIDv6Params): ZodMiniUUID { + return core._uuidv6(ZodMiniUUID, params); +} + +// ZodMiniUUIDv7 + +// @__NO_SIDE_EFFECTS__ +export function uuidv7(params?: string | core.$ZodUUIDv7Params): ZodMiniUUID { + return core._uuidv7(ZodMiniUUID, params); +} + +// ZodMiniURL +export interface ZodMiniURL extends _ZodMiniString { + // _zod: core.$ZodURLInternals; +} +export const ZodMiniURL: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniURL", (inst, def) => { + core.$ZodURL.init(inst, def); + ZodMiniStringFormat.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function url(params?: string | core.$ZodURLParams): ZodMiniURL { + return core._url(ZodMiniURL, params); +} + +// @__NO_SIDE_EFFECTS__ +export function httpUrl(params?: string | Omit): ZodMiniURL { + return core._url(ZodMiniURL, { + protocol: /^https?$/, + hostname: core.regexes.domain, + ...util.normalizeParams(params), + }); +} + +// ZodMiniEmoji +export interface ZodMiniEmoji extends _ZodMiniString { + // _zod: core.$ZodEmojiInternals; +} +export const ZodMiniEmoji: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniEmoji", + (inst, def) => { + core.$ZodEmoji.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function emoji(params?: string | core.$ZodEmojiParams): ZodMiniEmoji { + return core._emoji(ZodMiniEmoji, params); +} + +// ZodMiniNanoID +export interface ZodMiniNanoID extends _ZodMiniString { + // _zod: core.$ZodNanoIDInternals; +} +export const ZodMiniNanoID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNanoID", + (inst, def) => { + core.$ZodNanoID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function nanoid(params?: string | core.$ZodNanoIDParams): ZodMiniNanoID { + return core._nanoid(ZodMiniNanoID, params); +} + +// ZodMiniCUID +export interface ZodMiniCUID extends _ZodMiniString { + // _zod: core.$ZodCUIDInternals; +} +export const ZodMiniCUID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCUID", + (inst, def) => { + core.$ZodCUID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function cuid(params?: string | core.$ZodCUIDParams): ZodMiniCUID { + return core._cuid(ZodMiniCUID, params); +} + +// ZodMiniCUID2 +export interface ZodMiniCUID2 extends _ZodMiniString { + // _zod: core.$ZodCUID2Internals; +} +export const ZodMiniCUID2: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCUID2", + (inst, def) => { + core.$ZodCUID2.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function cuid2(params?: string | core.$ZodCUID2Params): ZodMiniCUID2 { + return core._cuid2(ZodMiniCUID2, params); +} + +// ZodMiniULID +export interface ZodMiniULID extends _ZodMiniString { + // _zod: core.$ZodULIDInternals; +} +export const ZodMiniULID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniULID", + (inst, def) => { + core.$ZodULID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function ulid(params?: string | core.$ZodULIDParams): ZodMiniULID { + return core._ulid(ZodMiniULID, params); +} + +// ZodMiniXID +export interface ZodMiniXID extends _ZodMiniString { + // _zod: core.$ZodXIDInternals; +} +export const ZodMiniXID: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniXID", (inst, def) => { + core.$ZodXID.init(inst, def); + ZodMiniStringFormat.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function xid(params?: string | core.$ZodXIDParams): ZodMiniXID { + return core._xid(ZodMiniXID, params); +} + +// ZodMiniKSUID +export interface ZodMiniKSUID extends _ZodMiniString { + // _zod: core.$ZodKSUIDInternals; +} +export const ZodMiniKSUID: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniKSUID", + (inst, def) => { + core.$ZodKSUID.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function ksuid(params?: string | core.$ZodKSUIDParams): ZodMiniKSUID { + return core._ksuid(ZodMiniKSUID, params); +} + +// ZodMiniIPv4 +export interface ZodMiniIPv4 extends _ZodMiniString { + // _zod: core.$ZodIPv4Internals; +} +export const ZodMiniIPv4: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniIPv4", + (inst, def) => { + core.$ZodIPv4.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function ipv4(params?: string | core.$ZodIPv4Params): ZodMiniIPv4 { + return core._ipv4(ZodMiniIPv4, params); +} + +// ZodMiniIPv6 +export interface ZodMiniIPv6 extends _ZodMiniString { + // _zod: core.$ZodIPv6Internals; +} +export const ZodMiniIPv6: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniIPv6", + (inst, def) => { + core.$ZodIPv6.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function ipv6(params?: string | core.$ZodIPv6Params): ZodMiniIPv6 { + return core._ipv6(ZodMiniIPv6, params); +} + +// ZodMiniCIDRv4 +export interface ZodMiniCIDRv4 extends _ZodMiniString { + // _zod: core.$ZodCIDRv4Internals; +} +export const ZodMiniCIDRv4: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCIDRv4", + (inst, def) => { + core.$ZodCIDRv4.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function cidrv4(params?: string | core.$ZodCIDRv4Params): ZodMiniCIDRv4 { + return core._cidrv4(ZodMiniCIDRv4, params); +} + +// ZodMiniCIDRv6 +export interface ZodMiniCIDRv6 extends _ZodMiniString { + // _zod: core.$ZodCIDRv6Internals; +} +export const ZodMiniCIDRv6: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCIDRv6", + (inst, def) => { + core.$ZodCIDRv6.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function cidrv6(params?: string | core.$ZodCIDRv6Params): ZodMiniCIDRv6 { + return core._cidrv6(ZodMiniCIDRv6, params); +} + +// ZodMiniMAC +export interface ZodMiniMAC extends _ZodMiniString { + // _zod: core.$ZodMACInternals; +} +export const ZodMiniMAC: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniMAC", (inst, def) => { + core.$ZodMAC.init(inst, def); + ZodMiniStringFormat.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function mac(params?: string | core.$ZodMACParams): ZodMiniMAC { + return core._mac(ZodMiniMAC, params); +} + +// ZodMiniBase64 +export interface ZodMiniBase64 extends _ZodMiniString { + // _zod: core.$ZodBase64Internals; +} +export const ZodMiniBase64: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniBase64", + (inst, def) => { + core.$ZodBase64.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); +// @__NO_SIDE_EFFECTS__ +export function base64(params?: string | core.$ZodBase64Params): ZodMiniBase64 { + return core._base64(ZodMiniBase64, params); +} + +// ZodMiniBase64URL +export interface ZodMiniBase64URL extends _ZodMiniString { + // _zod: core.$ZodBase64URLInternals; +} +export const ZodMiniBase64URL: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniBase64URL", + (inst, def) => { + core.$ZodBase64URL.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); +// @__NO_SIDE_EFFECTS__ +export function base64url(params?: string | core.$ZodBase64URLParams): ZodMiniBase64URL { + return core._base64url(ZodMiniBase64URL, params); +} + +// ZodMiniE164 +export interface ZodMiniE164 extends _ZodMiniString { + // _zod: core.$ZodE164Internals; +} +export const ZodMiniE164: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniE164", + (inst, def) => { + core.$ZodE164.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function e164(params?: string | core.$ZodE164Params): ZodMiniE164 { + return core._e164(ZodMiniE164, params); +} + +// ZodMiniJWT +export interface ZodMiniJWT extends _ZodMiniString { + // _zod: core.$ZodJWTInternals; +} +export const ZodMiniJWT: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniJWT", (inst, def) => { + core.$ZodJWT.init(inst, def); + ZodMiniStringFormat.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function jwt(params?: string | core.$ZodJWTParams): ZodMiniJWT { + return core._jwt(ZodMiniJWT, params); +} + +// ZodMiniCustomStringFormat +export interface ZodMiniCustomStringFormat + extends ZodMiniStringFormat, + core.$ZodCustomStringFormat { + _zod: core.$ZodCustomStringFormatInternals; +} +export const ZodMiniCustomStringFormat: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCustomStringFormat", + (inst, def) => { + core.$ZodCustomStringFormat.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function stringFormat( + format: Format, + fnOrRegex: ((arg: string) => util.MaybeAsync) | RegExp, + _params: string | core.$ZodStringFormatParams = {} +): ZodMiniCustomStringFormat { + return core._stringFormat(ZodMiniCustomStringFormat, format, fnOrRegex, _params) as any; +} + +// @__NO_SIDE_EFFECTS__ +export function hostname(_params?: string | core.$ZodStringFormatParams): ZodMiniCustomStringFormat<"hostname"> { + return core._stringFormat(ZodMiniCustomStringFormat, "hostname", core.regexes.hostname, _params) as any; +} + +// @__NO_SIDE_EFFECTS__ +export function hex(_params?: string | core.$ZodStringFormatParams): ZodMiniCustomStringFormat<"hex"> { + return core._stringFormat(ZodMiniCustomStringFormat, "hex", core.regexes.hex, _params) as any; +} + +// @__NO_SIDE_EFFECTS__ +export function hash( + alg: Alg, + params?: { + enc?: Enc; + } & core.$ZodStringFormatParams +): ZodMiniCustomStringFormat<`${Alg}_${Enc}`> { + const enc = params?.enc ?? "hex"; + const format = `${alg}_${enc}` as const; + const regex = core.regexes[format as keyof typeof core.regexes] as RegExp; + // check for unrecognized format + if (!regex) throw new Error(`Unrecognized hash format: ${format}`); + return core._stringFormat(ZodMiniCustomStringFormat, format, regex, params) as any; +} + +// ZodMiniNumber +interface _ZodMiniNumber = core.$ZodNumberInternals> + extends _ZodMiniType, + core.$ZodNumber { + _zod: T; +} +export interface ZodMiniNumber + extends _ZodMiniNumber>, + core.$ZodNumber {} +export const ZodMiniNumber: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNumber", + (inst, def) => { + core.$ZodNumber.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function number(params?: string | core.$ZodNumberParams): ZodMiniNumber { + return core._number(ZodMiniNumber, params) as any; +} + +// ZodMiniNumberFormat +export interface ZodMiniNumberFormat extends _ZodMiniNumber, core.$ZodNumberFormat {} +export const ZodMiniNumberFormat: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNumberFormat", + (inst, def) => { + core.$ZodNumberFormat.init(inst, def); + ZodMiniNumber.init(inst, def); + } +); + +// int + +// @__NO_SIDE_EFFECTS__ +export function int(params?: string | core.$ZodCheckNumberFormatParams): ZodMiniNumberFormat { + return core._int(ZodMiniNumberFormat, params); +} + +// float32 + +// @__NO_SIDE_EFFECTS__ +export function float32(params?: string | core.$ZodCheckNumberFormatParams): ZodMiniNumberFormat { + return core._float32(ZodMiniNumberFormat, params); +} + +// float64 + +// @__NO_SIDE_EFFECTS__ +export function float64(params?: string | core.$ZodCheckNumberFormatParams): ZodMiniNumberFormat { + return core._float64(ZodMiniNumberFormat, params); +} + +// int32 + +// @__NO_SIDE_EFFECTS__ +export function int32(params?: string | core.$ZodCheckNumberFormatParams): ZodMiniNumberFormat { + return core._int32(ZodMiniNumberFormat, params); +} + +// uint32 + +// @__NO_SIDE_EFFECTS__ +export function uint32(params?: string | core.$ZodCheckNumberFormatParams): ZodMiniNumberFormat { + return core._uint32(ZodMiniNumberFormat, params); +} + +// ZodMiniBoolean +export interface ZodMiniBoolean extends _ZodMiniType> { + // _zod: core.$ZodBooleanInternals; +} +export const ZodMiniBoolean: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniBoolean", + (inst, def) => { + core.$ZodBoolean.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function boolean(params?: string | core.$ZodBooleanParams): ZodMiniBoolean { + return core._boolean(ZodMiniBoolean, params) as any; +} + +// ZodMiniBigInt +export interface ZodMiniBigInt extends _ZodMiniType>, core.$ZodBigInt { + // _zod: core.$ZodBigIntInternals; +} +export const ZodMiniBigInt: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniBigInt", + (inst, def) => { + core.$ZodBigInt.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function bigint(params?: string | core.$ZodBigIntParams): ZodMiniBigInt { + return core._bigint(ZodMiniBigInt, params) as any; +} + +// bigint formats + +// ZodMiniBigIntFormat +export interface ZodMiniBigIntFormat extends _ZodMiniType { + // _zod: core.$ZodBigIntFormatInternals; +} +export const ZodMiniBigIntFormat: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniBigIntFormat", + (inst, def) => { + core.$ZodBigIntFormat.init(inst, def); + ZodMiniBigInt.init(inst, def); + } +); + +// int64 + +// @__NO_SIDE_EFFECTS__ +export function int64(params?: string | core.$ZodBigIntFormatParams): ZodMiniBigIntFormat { + return core._int64(ZodMiniBigIntFormat, params); +} + +// uint64 + +// @__NO_SIDE_EFFECTS__ +export function uint64(params?: string | core.$ZodBigIntFormatParams): ZodMiniBigIntFormat { + return core._uint64(ZodMiniBigIntFormat, params); +} + +// ZodMiniSymbol +export interface ZodMiniSymbol extends _ZodMiniType { + // _zod: core.$ZodSymbolInternals; +} +export const ZodMiniSymbol: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniSymbol", + (inst, def) => { + core.$ZodSymbol.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function symbol(params?: string | core.$ZodSymbolParams): ZodMiniSymbol { + return core._symbol(ZodMiniSymbol, params) as any; +} + +// ZodMiniUndefined +export interface ZodMiniUndefined extends _ZodMiniType { + // _zod: core.$ZodUndefinedInternals; +} +export const ZodMiniUndefined: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniUndefined", + (inst, def) => { + core.$ZodUndefined.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +function _undefined(params?: string | core.$ZodUndefinedParams): ZodMiniUndefined { + return core._undefined(ZodMiniUndefined, params) as any; +} +export { _undefined as undefined }; + +// ZodMiniNull +export interface ZodMiniNull extends _ZodMiniType { + // _zod: core.$ZodNullInternals; +} +export const ZodMiniNull: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNull", + (inst, def) => { + core.$ZodNull.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +function _null(params?: string | core.$ZodNullParams): ZodMiniNull { + return core._null(ZodMiniNull, params) as any; +} +export { _null as null }; + +// ZodMiniAny +export interface ZodMiniAny extends _ZodMiniType { + // _zod: core.$ZodAnyInternals; +} +export const ZodMiniAny: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniAny", (inst, def) => { + core.$ZodAny.init(inst, def); + ZodMiniType.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function any(): ZodMiniAny { + return core._any(ZodMiniAny) as any; +} + +// ZodMiniUnknown +export interface ZodMiniUnknown extends _ZodMiniType { + // _zod: core.$ZodUnknownInternals; +} +export const ZodMiniUnknown: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniUnknown", + (inst, def) => { + core.$ZodUnknown.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function unknown(): ZodMiniUnknown { + return core._unknown(ZodMiniUnknown) as any; +} + +// ZodMiniNever +export interface ZodMiniNever extends _ZodMiniType { + // _zod: core.$ZodNeverInternals; +} +export const ZodMiniNever: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNever", + (inst, def) => { + core.$ZodNever.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function never(params?: string | core.$ZodNeverParams): ZodMiniNever { + return core._never(ZodMiniNever, params) as any; +} + +// ZodMiniVoid +export interface ZodMiniVoid extends _ZodMiniType { + // _zod: core.$ZodVoidInternals; +} +export const ZodMiniVoid: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniVoid", + (inst, def) => { + core.$ZodVoid.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +function _void(params?: string | core.$ZodVoidParams): ZodMiniVoid { + return core._void(ZodMiniVoid, params) as any; +} +export { _void as void }; + +// ZodMiniDate +export interface ZodMiniDate extends _ZodMiniType> { + // _zod: core.$ZodDateInternals; +} + +export const ZodMiniDate: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniDate", + (inst, def) => { + core.$ZodDate.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function date(params?: string | core.$ZodDateParams): ZodMiniDate { + return core._date(ZodMiniDate, params) as any; +} + +// ZodMiniArray +export interface ZodMiniArray + extends _ZodMiniType>, + core.$ZodArray { + // _zod: core.$ZodArrayInternals; +} +export const ZodMiniArray: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniArray", + (inst, def) => { + core.$ZodArray.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +export function array(element: T, params?: string | core.$ZodArrayParams): ZodMiniArray; +// @__NO_SIDE_EFFECTS__ +export function array(element: SomeType, params?: any): ZodMiniArray { + return new ZodMiniArray({ + type: "array", + element: element as core.$ZodType, + ...util.normalizeParams(params), + }) as any; +} + +// .keyof +// @__NO_SIDE_EFFECTS__ +export function keyof(schema: T): ZodMiniEnum> { + const shape = schema._zod.def.shape; + return _enum(Object.keys(shape)) as any; +} + +// ZodMiniObject +export interface ZodMiniObject< + /** @ts-ignore Cast variance */ + out Shape extends core.$ZodShape = core.$ZodShape, + out Config extends core.$ZodObjectConfig = core.$strip, +> extends ZodMiniType>, + core.$ZodObject { + shape: Shape; +} +export const ZodMiniObject: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniObject", + (inst, def) => { + core.$ZodObject.init(inst, def); + ZodMiniType.init(inst, def); + util.defineLazy(inst, "shape", () => def.shape); + } +); +// @__NO_SIDE_EFFECTS__ +export function object>( + shape?: T, + params?: string | core.$ZodObjectParams +): ZodMiniObject { + const def: core.$ZodObjectDef = { + type: "object", + shape: shape ?? {}, + ...util.normalizeParams(params), + }; + return new ZodMiniObject(def) as any; +} + +// strictObject +// @__NO_SIDE_EFFECTS__ +export function strictObject( + shape: T, + params?: string | core.$ZodObjectParams +): ZodMiniObject { + return new ZodMiniObject({ + type: "object", + shape, + catchall: never(), + ...util.normalizeParams(params), + }) as any; +} + +// looseObject +// @__NO_SIDE_EFFECTS__ +export function looseObject( + shape: T, + params?: string | core.$ZodObjectParams +): ZodMiniObject { + return new ZodMiniObject({ + type: "object", + shape, + catchall: unknown(), + ...util.normalizeParams(params), + }) as any; +} + +// object methods +// @__NO_SIDE_EFFECTS__ +export function extend( + schema: T, + shape: U +): ZodMiniObject, T["_zod"]["config"]> { + return util.extend(schema, shape); +} + +export type SafeExtendShape = { + [K in keyof Ext]: K extends keyof Base + ? core.output extends core.output + ? core.input extends core.input + ? Ext[K] + : never + : never + : Ext[K]; +}; + +// @__NO_SIDE_EFFECTS__ +export function safeExtend( + schema: T, + shape: SafeExtendShape +): ZodMiniObject, T["_zod"]["config"]> { + return util.safeExtend(schema, shape as any); +} + +/** @deprecated Identical to `z.extend(A, B)` */ +export function merge( + a: T, + b: U +): ZodMiniObject, T["_zod"]["config"]>; +// @__NO_SIDE_EFFECTS__ +export function merge(schema: ZodMiniObject, shape: any): ZodMiniObject { + return util.extend(schema, shape); +} + +// @__NO_SIDE_EFFECTS__ +export function pick>( + schema: T, + mask: M & Record, never> +): ZodMiniObject>, T["_zod"]["config"]> { + return util.pick(schema, mask as any); +} + +// .omit + +// @__NO_SIDE_EFFECTS__ +export function omit>( + schema: T, + mask: M & Record, never> +): ZodMiniObject>, T["_zod"]["config"]> { + return util.omit(schema, mask); +} + +// @__NO_SIDE_EFFECTS__ +export function partial( + schema: T +): ZodMiniObject< + { + [k in keyof T["shape"]]: ZodMiniOptional; + }, + T["_zod"]["config"] +>; +// @__NO_SIDE_EFFECTS__ +export function partial>( + schema: T, + mask: M & Record, never> +): ZodMiniObject< + { + [k in keyof T["shape"]]: k extends keyof M ? ZodMiniOptional : T["shape"][k]; + }, + T["_zod"]["config"] +>; +// @__NO_SIDE_EFFECTS__ +export function partial(schema: ZodMiniObject, mask?: object) { + return util.partial(ZodMiniOptional, schema, mask); +} + +export type RequiredInterfaceShape< + Shape extends core.$ZodLooseShape, + Keys extends PropertyKey = keyof Shape, +> = util.Identity< + { + [k in keyof Shape as k extends Keys ? k : never]: ZodMiniNonOptional; + } & { + [k in keyof Shape as k extends Keys ? never : k]: Shape[k]; + } +>; + +// @__NO_SIDE_EFFECTS__ +export function required( + schema: T +): ZodMiniObject< + { + [k in keyof T["shape"]]: ZodMiniNonOptional; + }, + T["_zod"]["config"] +>; +// @__NO_SIDE_EFFECTS__ +export function required>( + schema: T, + mask: M & Record, never> +): ZodMiniObject< + util.Extend< + T["shape"], + { + [k in keyof M & keyof T["shape"]]: ZodMiniNonOptional; + } + >, + T["_zod"]["config"] +>; +// @__NO_SIDE_EFFECTS__ +export function required(schema: ZodMiniObject, mask?: object) { + return util.required(ZodMiniNonOptional, schema, mask); +} + +// @__NO_SIDE_EFFECTS__ +export function catchall( + inst: T, + catchall: U +): ZodMiniObject> { + return inst.clone({ ...inst._zod.def, catchall: catchall as any }) as any; +} + +// ZodMiniUnion +export interface ZodMiniUnion + extends _ZodMiniType> { + // _zod: core.$ZodUnionInternals; +} +export const ZodMiniUnion: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniUnion", + (inst, def) => { + core.$ZodUnion.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function union( + options: T, + params?: string | core.$ZodUnionParams +): ZodMiniUnion { + return new ZodMiniUnion({ + type: "union", + options: options as any as core.$ZodType[], + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniXor +export interface ZodMiniXor + extends _ZodMiniType> { + // _zod: core.$ZodXorInternals; +} +export const ZodMiniXor: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniXor", (inst, def) => { + ZodMiniUnion.init(inst, def); + core.$ZodXor.init(inst, def); +}); + +/** Creates an exclusive union (XOR) where exactly one option must match. + * Unlike regular unions that succeed when any option matches, xor fails if + * zero or more than one option matches the input. */ +export function xor( + options: T, + params?: string | core.$ZodXorParams +): ZodMiniXor { + return new ZodMiniXor({ + type: "union", + options: options as any as core.$ZodType[], + inclusive: false, + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniDiscriminatedUnion +export interface ZodMiniDiscriminatedUnion< + Options extends readonly SomeType[] = readonly core.$ZodType[], + Disc extends string = string, +> extends ZodMiniUnion { + _zod: core.$ZodDiscriminatedUnionInternals; +} +export const ZodMiniDiscriminatedUnion: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniDiscriminatedUnion", + (inst, def) => { + core.$ZodDiscriminatedUnion.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function discriminatedUnion< + Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]], + Disc extends string, +>( + discriminator: Disc, + options: Types, + params?: string | core.$ZodDiscriminatedUnionParams +): ZodMiniDiscriminatedUnion { + return new ZodMiniDiscriminatedUnion({ + type: "union", + options, + discriminator, + ...util.normalizeParams(params), + }) as ZodMiniDiscriminatedUnion; +} + +// ZodMiniIntersection +export interface ZodMiniIntersection + extends _ZodMiniType> { + // _zod: core.$ZodIntersectionInternals; +} +export const ZodMiniIntersection: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniIntersection", + (inst, def) => { + core.$ZodIntersection.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function intersection(left: T, right: U): ZodMiniIntersection { + return new ZodMiniIntersection({ + type: "intersection", + left: left as any as core.$ZodType, + right: right as any as core.$ZodType, + }) as any; +} + +// ZodMiniTuple +export interface ZodMiniTuple< + T extends util.TupleItems = readonly core.$ZodType[], + Rest extends SomeType | null = core.$ZodType | null, +> extends _ZodMiniType> { + // _zod: core.$ZodTupleInternals; +} +export const ZodMiniTuple: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniTuple", + (inst, def) => { + core.$ZodTuple.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +export function tuple( + items: T, + params?: string | core.$ZodTupleParams +): ZodMiniTuple; +export function tuple( + items: T, + rest: Rest, + params?: string | core.$ZodTupleParams +): ZodMiniTuple; +export function tuple(items: [], params?: string | core.$ZodTupleParams): ZodMiniTuple<[], null>; +// @__NO_SIDE_EFFECTS__ +export function tuple( + items: SomeType[], + _paramsOrRest?: string | core.$ZodTupleParams | SomeType, + _params?: string | core.$ZodTupleParams +) { + const hasRest = _paramsOrRest instanceof core.$ZodType; + const params = hasRest ? _params : _paramsOrRest; + const rest = hasRest ? _paramsOrRest : null; + return new ZodMiniTuple({ + type: "tuple", + items: items as any as core.$ZodType[], + rest, + ...util.normalizeParams(params), + }); +} + +// ZodMiniRecord +export interface ZodMiniRecord< + Key extends core.$ZodRecordKey = core.$ZodRecordKey, + Value extends SomeType = core.$ZodType, +> extends _ZodMiniType> { + // _zod: core.$ZodRecordInternals; +} +export const ZodMiniRecord: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniRecord", + (inst, def) => { + core.$ZodRecord.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function record( + keyType: Key, + valueType: Value, + params?: string | core.$ZodRecordParams +): ZodMiniRecord { + return new ZodMiniRecord({ + type: "record", + keyType, + valueType: valueType as any as core.$ZodType, + ...util.normalizeParams(params), + }) as any; +} +// @__NO_SIDE_EFFECTS__ +export function partialRecord( + keyType: Key, + valueType: Value, + params?: string | core.$ZodRecordParams +): ZodMiniRecord { + const k = core.clone(keyType); + k._zod.values = undefined; + return new ZodMiniRecord({ + type: "record", + keyType: k, + valueType: valueType as any, + ...util.normalizeParams(params), + }) as any; +} + +export function looseRecord( + keyType: Key, + valueType: Value, + params?: string | core.$ZodRecordParams +): ZodMiniRecord { + return new ZodMiniRecord({ + type: "record", + keyType, + valueType: valueType as any as core.$ZodType, + mode: "loose", + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniMap +export interface ZodMiniMap + extends _ZodMiniType> { + // _zod: core.$ZodMapInternals; +} +export const ZodMiniMap: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniMap", (inst, def) => { + core.$ZodMap.init(inst, def); + ZodMiniType.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function map( + keyType: Key, + valueType: Value, + params?: string | core.$ZodMapParams +): ZodMiniMap { + return new ZodMiniMap({ + type: "map", + keyType: keyType as any as core.$ZodType, + valueType: valueType as any as core.$ZodType, + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniSet +export interface ZodMiniSet extends _ZodMiniType> { + // _zod: core.$ZodSetInternals; +} +export const ZodMiniSet: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniSet", (inst, def) => { + core.$ZodSet.init(inst, def); + ZodMiniType.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function set(valueType: Value, params?: string | core.$ZodSetParams): ZodMiniSet { + return new ZodMiniSet({ + type: "set", + valueType: valueType as any as core.$ZodType, + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniEnum +export interface ZodMiniEnum extends _ZodMiniType> { + // _zod: core.$ZodEnumInternals; + options: Array; +} +export const ZodMiniEnum: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniEnum", + (inst, def) => { + core.$ZodEnum.init(inst, def); + ZodMiniType.init(inst, def); + + inst.options = Object.values(def.entries); + } +); + +function _enum( + values: T, + params?: string | core.$ZodEnumParams +): ZodMiniEnum>; +function _enum(entries: T, params?: string | core.$ZodEnumParams): ZodMiniEnum; +// @__NO_SIDE_EFFECTS__ +function _enum(values: any, params?: string | core.$ZodEnumParams) { + const entries: any = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values; + + return new ZodMiniEnum({ + type: "enum", + entries, + ...util.normalizeParams(params), + }) as any; +} +export { _enum as enum }; + +// @__NO_SIDE_EFFECTS__ +/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead. + * + * ```ts + * enum Colors { red, green, blue } + * z.enum(Colors); + * ``` + */ +export function nativeEnum(entries: T, params?: string | core.$ZodEnumParams): ZodMiniEnum { + return new ZodMiniEnum({ + type: "enum", + entries, + ...util.normalizeParams(params), + }) as any as ZodMiniEnum; +} + +// ZodMiniLiteral +export interface ZodMiniLiteral + extends _ZodMiniType> { + // _zod: core.$ZodLiteralInternals; +} +export const ZodMiniLiteral: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniLiteral", + (inst, def) => { + core.$ZodLiteral.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +export function literal>( + value: T, + params?: string | core.$ZodLiteralParams +): ZodMiniLiteral; +export function literal( + value: T, + params?: string | core.$ZodLiteralParams +): ZodMiniLiteral; +// @__NO_SIDE_EFFECTS__ +export function literal(value: any, params: any) { + return new ZodMiniLiteral({ + type: "literal", + values: Array.isArray(value) ? value : [value], + ...util.normalizeParams(params), + }); +} + +// ZodMiniFile +export interface ZodMiniFile extends _ZodMiniType { + // _zod: core.$ZodFileInternals; +} +export const ZodMiniFile: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniFile", + (inst, def) => { + core.$ZodFile.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function file(params?: string | core.$ZodFileParams): ZodMiniFile { + return core._file(ZodMiniFile, params) as any; +} + +// ZodMiniTransform +export interface ZodMiniTransform extends _ZodMiniType> { + // _zod: core.$ZodTransformInternals; +} +export const ZodMiniTransform: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniTransform", + (inst, def) => { + core.$ZodTransform.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function transform( + fn: (input: I, ctx: core.ParsePayload) => O +): ZodMiniTransform, I> { + return new ZodMiniTransform({ + type: "transform", + transform: fn as any, + }) as any; +} + +// ZodMiniOptional +export interface ZodMiniOptional + extends _ZodMiniType>, + core.$ZodOptional { + // _zod: core.$ZodOptionalInternals; +} +export const ZodMiniOptional: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniOptional", + (inst, def) => { + core.$ZodOptional.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function optional(innerType: T): ZodMiniOptional { + return new ZodMiniOptional({ + type: "optional", + innerType: innerType as any as core.$ZodType, + }) as any; +} + +// ZodMiniExactOptional +export interface ZodMiniExactOptional + extends _ZodMiniType>, + core.$ZodExactOptional { + // _zod: core.$ZodExactOptionalInternals; +} +export const ZodMiniExactOptional: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniExactOptional", + (inst, def) => { + core.$ZodExactOptional.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function exactOptional(innerType: T): ZodMiniExactOptional { + return new ZodMiniExactOptional({ + type: "optional", + innerType: innerType as any as core.$ZodType, + }) as any; +} + +// ZodMiniNullable +export interface ZodMiniNullable + extends _ZodMiniType> { + // _zod: core.$ZodNullableInternals; +} +export const ZodMiniNullable: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNullable", + (inst, def) => { + core.$ZodNullable.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function nullable(innerType: T): ZodMiniNullable { + return new ZodMiniNullable({ + type: "nullable", + innerType: innerType as any as core.$ZodType, + }) as any; +} + +// nullish +// @__NO_SIDE_EFFECTS__ +export function nullish(innerType: T): ZodMiniOptional> { + return optional(nullable(innerType)); +} + +// ZodMiniDefault +export interface ZodMiniDefault extends _ZodMiniType> { + // _zod: core.$ZodDefaultInternals; +} +export const ZodMiniDefault: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniDefault", + (inst, def) => { + core.$ZodDefault.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function _default( + innerType: T, + defaultValue: util.NoUndefined> | (() => util.NoUndefined>) +): ZodMiniDefault { + return new ZodMiniDefault({ + type: "default", + innerType: innerType as any as core.$ZodType, + get defaultValue() { + return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue); + }, + }) as any; +} + +// ZodMiniPrefault +export interface ZodMiniPrefault + extends _ZodMiniType> { + // _zod: core.$ZodPrefaultInternals; +} +export const ZodMiniPrefault: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniPrefault", + (inst, def) => { + core.$ZodPrefault.init(inst, def); + ZodMiniType.init(inst, def); + } +); +// @__NO_SIDE_EFFECTS__ +export function prefault( + innerType: T, + defaultValue: util.NoUndefined> | (() => util.NoUndefined>) +): ZodMiniPrefault { + return new ZodMiniPrefault({ + type: "prefault", + innerType: innerType as any as core.$ZodType, + get defaultValue() { + return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue); + }, + }) as any; +} + +// ZodMiniNonOptional +export interface ZodMiniNonOptional + extends _ZodMiniType> { + // _zod: core.$ZodNonOptionalInternals; +} +export const ZodMiniNonOptional: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniNonOptional", + (inst, def) => { + core.$ZodNonOptional.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function nonoptional( + innerType: T, + params?: string | core.$ZodNonOptionalParams +): ZodMiniNonOptional { + return new ZodMiniNonOptional({ + type: "nonoptional", + innerType: innerType as any as core.$ZodType, + ...util.normalizeParams(params), + }) as any; +} + +// ZodMiniSuccess +export interface ZodMiniSuccess extends _ZodMiniType> { + // _zod: core.$ZodSuccessInternals; +} +export const ZodMiniSuccess: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniSuccess", + (inst, def) => { + core.$ZodSuccess.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function success(innerType: T): ZodMiniSuccess { + return new ZodMiniSuccess({ + type: "success", + innerType: innerType as any as core.$ZodType, + }) as any; +} + +// ZodMiniCatch +export interface ZodMiniCatch extends _ZodMiniType> { + // _zod: core.$ZodCatchInternals; +} +export const ZodMiniCatch: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCatch", + (inst, def) => { + core.$ZodCatch.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +function _catch( + innerType: T, + catchValue: core.output | ((ctx: core.$ZodCatchCtx) => core.output) +): ZodMiniCatch { + return new ZodMiniCatch({ + type: "catch", + innerType: innerType as any as core.$ZodType, + catchValue: (typeof catchValue === "function" ? catchValue : () => catchValue) as ( + ctx: core.$ZodCatchCtx + ) => core.output, + }) as any; +} +export { _catch as catch }; + +// ZodMiniNaN +export interface ZodMiniNaN extends _ZodMiniType { + // _zod: core.$ZodNaNInternals; +} +export const ZodMiniNaN: core.$constructor = /*@__PURE__*/ core.$constructor("ZodMiniNaN", (inst, def) => { + core.$ZodNaN.init(inst, def); + ZodMiniType.init(inst, def); +}); + +// @__NO_SIDE_EFFECTS__ +export function nan(params?: string | core.$ZodNaNParams): ZodMiniNaN { + return core._nan(ZodMiniNaN, params) as any; +} + +// ZodMiniPipe +export interface ZodMiniPipe + extends _ZodMiniType> { + // _zod: core.$ZodPipeInternals; +} +export const ZodMiniPipe: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniPipe", + (inst, def) => { + core.$ZodPipe.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function pipe< + const A extends SomeType, + B extends core.$ZodType> = core.$ZodType>, +>(in_: A, out: B | core.$ZodType>): ZodMiniPipe { + return new ZodMiniPipe({ + type: "pipe", + in: in_ as any as core.$ZodType, + out: out as any as core.$ZodType, + }) as any; +} + +// ZodMiniCodec +export interface ZodMiniCodec + extends ZodMiniPipe, + core.$ZodCodec { + _zod: core.$ZodCodecInternals; + def: core.$ZodCodecDef; +} +export const ZodMiniCodec: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniCodec", + (inst, def) => { + ZodMiniPipe.init(inst, def); + core.$ZodCodec.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function codec( + in_: A, + out: B, + params: { + decode: (value: core.output, payload: core.ParsePayload>) => core.util.MaybeAsync>; + encode: (value: core.input, payload: core.ParsePayload>) => core.util.MaybeAsync>; + } +): ZodMiniCodec { + return new ZodMiniCodec({ + type: "pipe", + in: in_ as any as core.$ZodType, + out: out as any as core.$ZodType, + transform: params.decode as any, + reverseTransform: params.encode as any, + }) as any; +} + +// /** @deprecated Use `z.pipe()` and `z.transform()` instead. */ +// export function preprocess( +// fn: (arg: unknown, ctx: core.ParsePayload) => A, +// schema: U, +// params?: ZodPreprocessParams +// ): ZodPipe, U> { +// return pipe(transform(fn as any, params), schema as any, params); +// } + +// ZodMiniReadonly +export interface ZodMiniReadonly + extends _ZodMiniType> { + // _zod: core.$ZodReadonlyInternals; +} +export const ZodMiniReadonly: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniReadonly", + (inst, def) => { + core.$ZodReadonly.init(inst, def); + ZodMiniType.init(inst, def); + } +); + +// @__NO_SIDE_EFFECTS__ +export function readonly(innerType: T): ZodMiniReadonly { + return new ZodMiniReadonly({ + type: "readonly", + innerType: innerType as any as core.$ZodType, + }) as any; +} + +// ZodMiniTemplateLiteral +export interface ZodMiniTemplateLiteral