@elaraai/east 0.0.1-beta.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +15 -666
- package/README.md +113 -88
- package/dist/src/analyze.d.ts +3 -0
- package/dist/src/analyze.d.ts.map +1 -1
- package/dist/src/analyze.js +223 -111
- package/dist/src/analyze.js.map +1 -1
- package/dist/src/analyze.spec.d.ts +2 -0
- package/dist/src/analyze.spec.d.ts.map +1 -0
- package/dist/src/analyze.spec.js +445 -0
- package/dist/src/analyze.spec.js.map +1 -0
- package/dist/src/ast.d.ts +54 -35
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast_to_ir.coerce_to.spec.d.ts +15 -0
- package/dist/src/ast_to_ir.coerce_to.spec.d.ts.map +1 -0
- package/dist/src/ast_to_ir.coerce_to.spec.js +420 -0
- package/dist/src/ast_to_ir.coerce_to.spec.js.map +1 -0
- package/dist/src/ast_to_ir.d.ts +18 -4
- package/dist/src/ast_to_ir.d.ts.map +1 -1
- package/dist/src/ast_to_ir.js +233 -178
- package/dist/src/ast_to_ir.js.map +1 -1
- package/dist/src/builtins.d.ts +1 -1
- package/dist/src/builtins.d.ts.map +1 -1
- package/dist/src/builtins.js +194 -2
- package/dist/src/builtins.js.map +1 -1
- package/dist/src/comparison.d.ts +2 -2
- package/dist/src/comparison.d.ts.map +1 -1
- package/dist/src/comparison.js +115 -40
- package/dist/src/comparison.js.map +1 -1
- package/dist/src/comparison.spec.d.ts +2 -0
- package/dist/src/comparison.spec.d.ts.map +1 -0
- package/dist/src/comparison.spec.js +1200 -0
- package/dist/src/comparison.spec.js.map +1 -0
- package/dist/src/compile.d.ts +31 -9
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1048 -548
- package/dist/src/compile.js.map +1 -1
- package/dist/src/containers/matrix.d.ts +15 -0
- package/dist/src/containers/matrix.d.ts.map +1 -0
- package/dist/src/containers/matrix.js +15 -0
- package/dist/src/containers/matrix.js.map +1 -0
- package/dist/src/containers/ref.d.ts.map +1 -1
- package/dist/src/containers/ref.js +1 -1
- package/dist/src/containers/ref.js.map +1 -1
- package/dist/src/containers/sortedmap.spec.d.ts +2 -0
- package/dist/src/containers/sortedmap.spec.d.ts.map +1 -0
- package/dist/src/containers/sortedmap.spec.js +180 -0
- package/dist/src/containers/sortedmap.spec.js.map +1 -0
- package/dist/src/containers/sortedset.spec.d.ts +2 -0
- package/dist/src/containers/sortedset.spec.d.ts.map +1 -0
- package/dist/src/containers/sortedset.spec.js +228 -0
- package/dist/src/containers/sortedset.spec.js.map +1 -0
- package/dist/src/containers/variant.d.ts.map +1 -1
- package/dist/src/containers/variant.js +1 -1
- package/dist/src/containers/variant.js.map +1 -1
- package/dist/src/containers/variant.spec.d.ts +2 -0
- package/dist/src/containers/variant.spec.d.ts.map +1 -0
- package/dist/src/containers/variant.spec.js +217 -0
- package/dist/src/containers/variant.spec.js.map +1 -0
- package/dist/src/datetime_format/parse.spec.d.ts +6 -0
- package/dist/src/datetime_format/parse.spec.d.ts.map +1 -0
- package/dist/src/datetime_format/parse.spec.js +379 -0
- package/dist/src/datetime_format/parse.spec.js.map +1 -0
- package/dist/src/datetime_format/print.spec.d.ts +6 -0
- package/dist/src/datetime_format/print.spec.d.ts.map +1 -0
- package/dist/src/datetime_format/print.spec.js +257 -0
- package/dist/src/datetime_format/print.spec.js.map +1 -0
- package/dist/src/datetime_format/tokenize.spec.d.ts +2 -0
- package/dist/src/datetime_format/tokenize.spec.d.ts.map +1 -0
- package/dist/src/datetime_format/tokenize.spec.js +508 -0
- package/dist/src/datetime_format/tokenize.spec.js.map +1 -0
- package/dist/src/datetime_format/types.d.ts +23 -23
- package/dist/src/default.d.ts.map +1 -1
- package/dist/src/default.js +33 -0
- package/dist/src/default.js.map +1 -1
- package/dist/src/default.spec.d.ts +2 -0
- package/dist/src/default.spec.d.ts.map +1 -0
- package/dist/src/default.spec.js +178 -0
- package/dist/src/default.spec.js.map +1 -0
- package/dist/src/eastir.d.ts +7 -0
- package/dist/src/eastir.d.ts.map +1 -1
- package/dist/src/eastir.js +55 -7
- package/dist/src/eastir.js.map +1 -1
- package/dist/src/error.d.ts +14 -4
- package/dist/src/error.d.ts.map +1 -1
- package/dist/src/error.js +28 -3
- package/dist/src/error.js.map +1 -1
- package/dist/src/example.d.ts +87 -0
- package/dist/src/example.d.ts.map +1 -0
- package/dist/src/example.js +18 -0
- package/dist/src/example.js.map +1 -0
- package/dist/src/expr/array.d.ts +43 -1
- package/dist/src/expr/array.d.ts.map +1 -1
- package/dist/src/expr/array.js +103 -53
- package/dist/src/expr/array.js.map +1 -1
- package/dist/src/expr/ast.d.ts +1 -1
- package/dist/src/expr/ast.d.ts.map +1 -1
- package/dist/src/expr/ast.js +112 -54
- package/dist/src/expr/ast.js.map +1 -1
- package/dist/src/expr/asyncfunction.d.ts.map +1 -1
- package/dist/src/expr/asyncfunction.js +6 -4
- package/dist/src/expr/asyncfunction.js.map +1 -1
- package/dist/src/expr/blob.d.ts +48 -0
- package/dist/src/expr/blob.d.ts.map +1 -1
- package/dist/src/expr/blob.js +63 -8
- package/dist/src/expr/blob.js.map +1 -1
- package/dist/src/expr/block.d.ts +213 -12
- package/dist/src/expr/block.d.ts.map +1 -1
- package/dist/src/expr/block.js +796 -293
- package/dist/src/expr/block.js.map +1 -1
- package/dist/src/expr/boolean.d.ts +44 -0
- package/dist/src/expr/boolean.d.ts.map +1 -1
- package/dist/src/expr/boolean.js +58 -6
- package/dist/src/expr/boolean.js.map +1 -1
- package/dist/src/expr/datetime.d.ts +135 -0
- package/dist/src/expr/datetime.d.ts.map +1 -1
- package/dist/src/expr/datetime.js +184 -34
- package/dist/src/expr/datetime.js.map +1 -1
- package/dist/src/expr/dict.d.ts +47 -2
- package/dist/src/expr/dict.d.ts.map +1 -1
- package/dist/src/expr/dict.js +122 -61
- package/dist/src/expr/dict.js.map +1 -1
- package/dist/src/expr/expr.d.ts +3 -1
- package/dist/src/expr/expr.d.ts.map +1 -1
- package/dist/src/expr/expr.js +4 -3
- package/dist/src/expr/expr.js.map +1 -1
- package/dist/src/expr/float.d.ts +153 -0
- package/dist/src/expr/float.d.ts.map +1 -1
- package/dist/src/expr/float.js +191 -17
- package/dist/src/expr/float.js.map +1 -1
- package/dist/src/expr/function.d.ts +7 -2
- package/dist/src/expr/function.d.ts.map +1 -1
- package/dist/src/expr/function.js +6 -4
- package/dist/src/expr/function.js.map +1 -1
- package/dist/src/expr/index.d.ts +186 -2
- package/dist/src/expr/index.d.ts.map +1 -1
- package/dist/src/expr/index.js +183 -2
- package/dist/src/expr/index.js.map +1 -1
- package/dist/src/expr/integer.d.ts +180 -0
- package/dist/src/expr/integer.d.ts.map +1 -1
- package/dist/src/expr/integer.js +189 -18
- package/dist/src/expr/integer.js.map +1 -1
- package/dist/src/expr/libs/array.js +3 -3
- package/dist/src/expr/libs/array.js.map +1 -1
- package/dist/src/expr/libs/blob.js +3 -3
- package/dist/src/expr/libs/blob.js.map +1 -1
- package/dist/src/expr/libs/dict.js +1 -1
- package/dist/src/expr/libs/dict.js.map +1 -1
- package/dist/src/expr/libs/matrix.d.ts +17 -0
- package/dist/src/expr/libs/matrix.d.ts.map +1 -0
- package/dist/src/expr/libs/matrix.js +60 -0
- package/dist/src/expr/libs/matrix.js.map +1 -0
- package/dist/src/expr/libs/set.js +1 -1
- package/dist/src/expr/libs/set.js.map +1 -1
- package/dist/src/expr/libs/string.d.ts.map +1 -1
- package/dist/src/expr/libs/string.js +2 -1
- package/dist/src/expr/libs/string.js.map +1 -1
- package/dist/src/expr/libs/vector.d.ts +16 -0
- package/dist/src/expr/libs/vector.d.ts.map +1 -0
- package/dist/src/expr/libs/vector.js +44 -0
- package/dist/src/expr/libs/vector.js.map +1 -0
- package/dist/src/expr/matrix.d.ts +109 -0
- package/dist/src/expr/matrix.d.ts.map +1 -0
- package/dist/src/expr/matrix.js +224 -0
- package/dist/src/expr/matrix.js.map +1 -0
- package/dist/src/expr/recursive.d.ts +83 -0
- package/dist/src/expr/recursive.d.ts.map +1 -0
- package/dist/src/expr/recursive.js +99 -0
- package/dist/src/expr/recursive.js.map +1 -0
- package/dist/src/expr/ref.js +5 -5
- package/dist/src/expr/ref.js.map +1 -1
- package/dist/src/expr/set.d.ts +44 -2
- package/dist/src/expr/set.d.ts.map +1 -1
- package/dist/src/expr/set.js +102 -52
- package/dist/src/expr/set.js.map +1 -1
- package/dist/src/expr/string.d.ts +134 -0
- package/dist/src/expr/string.d.ts.map +1 -1
- package/dist/src/expr/string.js +173 -23
- package/dist/src/expr/string.js.map +1 -1
- package/dist/src/expr/struct.d.ts +1 -1
- package/dist/src/expr/struct.d.ts.map +1 -1
- package/dist/src/expr/struct.js +2 -2
- package/dist/src/expr/struct.js.map +1 -1
- package/dist/src/expr/types.d.ts +12 -9
- package/dist/src/expr/types.d.ts.map +1 -1
- package/dist/src/expr/variant.d.ts +108 -11
- package/dist/src/expr/variant.d.ts.map +1 -1
- package/dist/src/expr/variant.js +76 -38
- package/dist/src/expr/variant.js.map +1 -1
- package/dist/src/expr/vector.d.ts +102 -0
- package/dist/src/expr/vector.d.ts.map +1 -0
- package/dist/src/expr/vector.js +202 -0
- package/dist/src/expr/vector.js.map +1 -0
- package/dist/src/fuzz.d.ts +36 -2
- package/dist/src/fuzz.d.ts.map +1 -1
- package/dist/src/fuzz.js +409 -77
- package/dist/src/fuzz.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +12 -2
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +13 -2
- package/dist/src/internal.js.map +1 -1
- package/dist/src/ir.d.ts +1754 -1484
- package/dist/src/ir.d.ts.map +1 -1
- package/dist/src/ir.js +51 -34
- package/dist/src/ir.js.map +1 -1
- package/dist/src/ir.spec.d.ts +2 -0
- package/dist/src/ir.spec.d.ts.map +1 -0
- package/dist/src/ir.spec.js +35 -0
- package/dist/src/ir.spec.js.map +1 -0
- package/dist/src/location.d.ts +56 -17
- package/dist/src/location.d.ts.map +1 -1
- package/dist/src/location.js +226 -29
- package/dist/src/location.js.map +1 -1
- package/dist/src/location.spec.d.ts +2 -0
- package/dist/src/location.spec.d.ts.map +1 -0
- package/dist/src/location.spec.js +102 -0
- package/dist/src/location.spec.js.map +1 -0
- package/dist/src/patch/apply.d.ts +15 -0
- package/dist/src/patch/apply.d.ts.map +1 -0
- package/dist/src/patch/apply.js +352 -0
- package/dist/src/patch/apply.js.map +1 -0
- package/dist/src/patch/compose.d.ts +15 -0
- package/dist/src/patch/compose.d.ts.map +1 -0
- package/dist/src/patch/compose.js +448 -0
- package/dist/src/patch/compose.js.map +1 -0
- package/dist/src/patch/diff.d.ts +15 -0
- package/dist/src/patch/diff.d.ts.map +1 -0
- package/dist/src/patch/diff.js +347 -0
- package/dist/src/patch/diff.js.map +1 -0
- package/dist/src/patch/fuzz.d.ts +73 -0
- package/dist/src/patch/fuzz.d.ts.map +1 -0
- package/dist/src/patch/fuzz.js +159 -0
- package/dist/src/patch/fuzz.js.map +1 -0
- package/dist/src/patch/index.d.ts +32 -0
- package/dist/src/patch/index.d.ts.map +1 -0
- package/dist/src/patch/index.js +48 -0
- package/dist/src/patch/index.js.map +1 -0
- package/dist/src/patch/invert.d.ts +15 -0
- package/dist/src/patch/invert.d.ts.map +1 -0
- package/dist/src/patch/invert.js +284 -0
- package/dist/src/patch/invert.js.map +1 -0
- package/dist/src/patch/merge.d.ts +56 -0
- package/dist/src/patch/merge.d.ts.map +1 -0
- package/dist/src/patch/merge.js +586 -0
- package/dist/src/patch/merge.js.map +1 -0
- package/dist/src/patch/merge.spec.d.ts +2 -0
- package/dist/src/patch/merge.spec.d.ts.map +1 -0
- package/dist/src/patch/merge.spec.js +1139 -0
- package/dist/src/patch/merge.spec.js.map +1 -0
- package/dist/src/patch/patch.spec.d.ts +2 -0
- package/dist/src/patch/patch.spec.d.ts.map +1 -0
- package/dist/src/patch/patch.spec.js +452 -0
- package/dist/src/patch/patch.spec.js.map +1 -0
- package/dist/src/patch/path.d.ts +115 -0
- package/dist/src/patch/path.d.ts.map +1 -0
- package/dist/src/patch/path.js +192 -0
- package/dist/src/patch/path.js.map +1 -0
- package/dist/src/patch/path.spec.d.ts +6 -0
- package/dist/src/patch/path.spec.d.ts.map +1 -0
- package/dist/src/patch/path.spec.js +322 -0
- package/dist/src/patch/path.spec.js.map +1 -0
- package/dist/src/patch/prune.d.ts +77 -0
- package/dist/src/patch/prune.d.ts.map +1 -0
- package/dist/src/patch/prune.js +115 -0
- package/dist/src/patch/prune.js.map +1 -0
- package/dist/src/patch/prune.spec.d.ts +6 -0
- package/dist/src/patch/prune.spec.d.ts.map +1 -0
- package/dist/src/patch/prune.spec.js +473 -0
- package/dist/src/patch/prune.spec.js.map +1 -0
- package/dist/src/patch/type_of_patch.d.ts +25 -0
- package/dist/src/patch/type_of_patch.d.ts.map +1 -0
- package/dist/src/patch/type_of_patch.js +144 -0
- package/dist/src/patch/type_of_patch.js.map +1 -0
- package/dist/src/patch/types.d.ts +220 -0
- package/dist/src/patch/types.d.ts.map +1 -0
- package/dist/src/patch/types.js +62 -0
- package/dist/src/patch/types.js.map +1 -0
- package/dist/src/patch/validate.d.ts +44 -0
- package/dist/src/patch/validate.d.ts.map +1 -0
- package/dist/src/patch/validate.js +190 -0
- package/dist/src/patch/validate.js.map +1 -0
- package/dist/src/patch/validate.spec.d.ts +6 -0
- package/dist/src/patch/validate.spec.d.ts.map +1 -0
- package/dist/src/patch/validate.spec.js +225 -0
- package/dist/src/patch/validate.spec.js.map +1 -0
- package/dist/src/patch/walk.d.ts +121 -0
- package/dist/src/patch/walk.d.ts.map +1 -0
- package/dist/src/patch/walk.js +385 -0
- package/dist/src/patch/walk.js.map +1 -0
- package/dist/src/patch/walk.spec.d.ts +6 -0
- package/dist/src/patch/walk.spec.d.ts.map +1 -0
- package/dist/src/patch/walk.spec.js +621 -0
- package/dist/src/patch/walk.spec.js.map +1 -0
- package/dist/src/platform.d.ts +6 -0
- package/dist/src/platform.d.ts.map +1 -1
- package/dist/src/platform.spec.d.ts +2 -0
- package/dist/src/platform.spec.d.ts.map +1 -0
- package/dist/src/platform.spec.js +330 -0
- package/dist/src/platform.spec.js.map +1 -0
- package/dist/src/serialization/beast.d.ts.map +1 -1
- package/dist/src/serialization/beast.js +12 -0
- package/dist/src/serialization/beast.js.map +1 -1
- package/dist/src/serialization/beast.spec.d.ts +6 -0
- package/dist/src/serialization/beast.spec.d.ts.map +1 -0
- package/dist/src/serialization/beast.spec.js +406 -0
- package/dist/src/serialization/beast.spec.js.map +1 -0
- package/dist/src/serialization/beast2/index.d.ts +50 -0
- package/dist/src/serialization/beast2/index.d.ts.map +1 -0
- package/dist/src/serialization/beast2/index.js +756 -0
- package/dist/src/serialization/beast2/index.js.map +1 -0
- package/dist/src/serialization/beast2/index.spec.d.ts +6 -0
- package/dist/src/serialization/beast2/index.spec.d.ts.map +1 -0
- package/dist/src/serialization/beast2/index.spec.js +528 -0
- package/dist/src/serialization/beast2/index.spec.js.map +1 -0
- package/dist/src/serialization/beast2/sourcemap-table.d.ts +29 -0
- package/dist/src/serialization/beast2/sourcemap-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/sourcemap-table.js +82 -0
- package/dist/src/serialization/beast2/sourcemap-table.js.map +1 -0
- package/dist/src/serialization/beast2/string-table.d.ts +16 -0
- package/dist/src/serialization/beast2/string-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/string-table.js +38 -0
- package/dist/src/serialization/beast2/string-table.js.map +1 -0
- package/dist/src/serialization/beast2/type-table.d.ts +86 -0
- package/dist/src/serialization/beast2/type-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/type-table.js +618 -0
- package/dist/src/serialization/beast2/type-table.js.map +1 -0
- package/dist/src/serialization/beast2/value-table.d.ts +40 -0
- package/dist/src/serialization/beast2/value-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/value-table.js +157 -0
- package/dist/src/serialization/beast2/value-table.js.map +1 -0
- package/dist/src/serialization/binary-utils.d.ts +33 -0
- package/dist/src/serialization/binary-utils.d.ts.map +1 -1
- package/dist/src/serialization/binary-utils.js +157 -0
- package/dist/src/serialization/binary-utils.js.map +1 -1
- package/dist/src/serialization/csv.d.ts +17 -17
- package/dist/src/serialization/csv.d.ts.map +1 -1
- package/dist/src/serialization/csv.js +10 -8
- package/dist/src/serialization/csv.js.map +1 -1
- package/dist/src/serialization/csv.spec.d.ts +2 -0
- package/dist/src/serialization/csv.spec.d.ts.map +1 -0
- package/dist/src/serialization/csv.spec.js +556 -0
- package/dist/src/serialization/csv.spec.js.map +1 -0
- package/dist/src/serialization/east-inferred.spec.d.ts +2 -0
- package/dist/src/serialization/east-inferred.spec.d.ts.map +1 -0
- package/dist/src/serialization/east-inferred.spec.js +322 -0
- package/dist/src/serialization/east-inferred.spec.js.map +1 -0
- package/dist/src/serialization/east.d.ts +1 -1
- package/dist/src/serialization/east.d.ts.map +1 -1
- package/dist/src/serialization/east.js +178 -26
- package/dist/src/serialization/east.js.map +1 -1
- package/dist/src/serialization/east.spec.d.ts +2 -0
- package/dist/src/serialization/east.spec.d.ts.map +1 -0
- package/dist/src/serialization/east.spec.js +1548 -0
- package/dist/src/serialization/east.spec.js.map +1 -0
- package/dist/src/serialization/index.d.ts +1 -2
- package/dist/src/serialization/index.d.ts.map +1 -1
- package/dist/src/serialization/index.js +1 -2
- package/dist/src/serialization/index.js.map +1 -1
- package/dist/src/serialization/json.d.ts +1 -1
- package/dist/src/serialization/json.d.ts.map +1 -1
- package/dist/src/serialization/json.js +123 -24
- package/dist/src/serialization/json.js.map +1 -1
- package/dist/src/serialization/json.spec.d.ts +2 -0
- package/dist/src/serialization/json.spec.d.ts.map +1 -0
- package/dist/src/serialization/json.spec.js +1297 -0
- package/dist/src/serialization/json.spec.js.map +1 -0
- package/dist/src/type_of_type.d.ts +72 -35
- package/dist/src/type_of_type.d.ts.map +1 -1
- package/dist/src/type_of_type.is_data.spec.d.ts +6 -0
- package/dist/src/type_of_type.is_data.spec.d.ts.map +1 -0
- package/dist/src/type_of_type.is_data.spec.js +221 -0
- package/dist/src/type_of_type.is_data.spec.js.map +1 -0
- package/dist/src/type_of_type.js +355 -80
- package/dist/src/type_of_type.js.map +1 -1
- package/dist/src/type_of_type.to_etv.spec.d.ts +6 -0
- package/dist/src/type_of_type.to_etv.spec.d.ts.map +1 -0
- package/dist/src/type_of_type.to_etv.spec.js +263 -0
- package/dist/src/type_of_type.to_etv.spec.js.map +1 -0
- package/dist/src/types.d.ts +82 -12
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.error.spec.d.ts +2 -0
- package/dist/src/types.error.spec.d.ts.map +1 -0
- package/dist/src/types.error.spec.js +665 -0
- package/dist/src/types.error.spec.js.map +1 -0
- package/dist/src/types.js +663 -156
- package/dist/src/types.js.map +1 -1
- package/dist/src/types.spec.d.ts +2 -0
- package/dist/src/types.spec.d.ts.map +1 -0
- package/dist/src/types.spec.js +946 -0
- package/dist/src/types.spec.js.map +1 -0
- package/dist/src/walker.d.ts +65 -0
- package/dist/src/walker.d.ts.map +1 -0
- package/dist/src/walker.js +165 -0
- package/dist/src/walker.js.map +1 -0
- package/dist/src/walker.spec.d.ts +6 -0
- package/dist/src/walker.spec.d.ts.map +1 -0
- package/dist/src/walker.spec.js +109 -0
- package/dist/src/walker.spec.js.map +1 -0
- package/package.json +16 -38
- package/dist/src/serialization/beast2-stream.d.ts +0 -38
- package/dist/src/serialization/beast2-stream.d.ts.map +0 -1
- package/dist/src/serialization/beast2-stream.js +0 -670
- package/dist/src/serialization/beast2-stream.js.map +0 -1
- package/dist/src/serialization/beast2.d.ts +0 -41
- package/dist/src/serialization/beast2.d.ts.map +0 -1
- package/dist/src/serialization/beast2.js +0 -495
- package/dist/src/serialization/beast2.js.map +0 -1
|
@@ -0,0 +1,1297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { variant, some, none } from '../containers/variant.js';
|
|
8
|
+
import { fuzzerTest } from '../fuzz.js';
|
|
9
|
+
import { ArrayType, AsyncFunctionType, BlobType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NeverType, NullType, RecursiveType, SetType, StringType, StructType, VariantType, VectorType, MatrixType, } from '../types.js';
|
|
10
|
+
import { matrix } from '../containers/matrix.js';
|
|
11
|
+
import { decodeJSONFor, encodeJSONFor, fromJSONFor, toJSONFor } from "./json.js";
|
|
12
|
+
import { compareFor, equalFor } from "../comparison.js";
|
|
13
|
+
import { SortedSet } from "../containers/sortedset.js";
|
|
14
|
+
import { SortedMap } from "../containers/sortedmap.js";
|
|
15
|
+
describe('Json encoding/decoding of EAST values', () => {
|
|
16
|
+
function run(type, decoded, encoded, erroneous = []) {
|
|
17
|
+
assert.strictEqual(decoded.length, encoded.length);
|
|
18
|
+
const toJson = toJSONFor(type);
|
|
19
|
+
const fromJson = fromJSONFor(type);
|
|
20
|
+
const equal = equalFor(type);
|
|
21
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
22
|
+
assert.deepEqual(toJson(decoded[i]), encoded[i]);
|
|
23
|
+
assert.ok(equal(fromJson(encoded[i]), decoded[i]), `Round-trip failed for ${JSON.stringify(encoded[i])}`);
|
|
24
|
+
}
|
|
25
|
+
for (const v of erroneous) {
|
|
26
|
+
assert.throws(() => fromJson(v));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
test('should encode/decode null', () => {
|
|
30
|
+
const type = NullType;
|
|
31
|
+
const decoded = [
|
|
32
|
+
null,
|
|
33
|
+
];
|
|
34
|
+
const encoded = [
|
|
35
|
+
null,
|
|
36
|
+
];
|
|
37
|
+
const erroneous = [
|
|
38
|
+
undefined,
|
|
39
|
+
true,
|
|
40
|
+
1,
|
|
41
|
+
"",
|
|
42
|
+
[],
|
|
43
|
+
{},
|
|
44
|
+
];
|
|
45
|
+
run(type, decoded, encoded, erroneous);
|
|
46
|
+
});
|
|
47
|
+
test('should encode/decode boolean', () => {
|
|
48
|
+
const type = BooleanType;
|
|
49
|
+
const decoded = [
|
|
50
|
+
true,
|
|
51
|
+
false,
|
|
52
|
+
];
|
|
53
|
+
const encoded = [
|
|
54
|
+
true,
|
|
55
|
+
false,
|
|
56
|
+
];
|
|
57
|
+
const erroneous = [
|
|
58
|
+
undefined,
|
|
59
|
+
null,
|
|
60
|
+
1,
|
|
61
|
+
"",
|
|
62
|
+
[],
|
|
63
|
+
{}
|
|
64
|
+
];
|
|
65
|
+
run(type, decoded, encoded, erroneous);
|
|
66
|
+
});
|
|
67
|
+
test('should encode/decode integer', () => {
|
|
68
|
+
const type = IntegerType;
|
|
69
|
+
const decoded = [
|
|
70
|
+
0n,
|
|
71
|
+
42n,
|
|
72
|
+
-1n,
|
|
73
|
+
90071992547409919n,
|
|
74
|
+
9223372036854775807n,
|
|
75
|
+
-9223372036854775808n,
|
|
76
|
+
];
|
|
77
|
+
const encoded = [
|
|
78
|
+
"0",
|
|
79
|
+
"42",
|
|
80
|
+
"-1",
|
|
81
|
+
"90071992547409919",
|
|
82
|
+
"9223372036854775807",
|
|
83
|
+
"-9223372036854775808",
|
|
84
|
+
];
|
|
85
|
+
const erroneous = [
|
|
86
|
+
undefined,
|
|
87
|
+
null,
|
|
88
|
+
"",
|
|
89
|
+
[],
|
|
90
|
+
{},
|
|
91
|
+
"abc",
|
|
92
|
+
"9223372036854775808",
|
|
93
|
+
"-9223372036854775809",
|
|
94
|
+
];
|
|
95
|
+
run(type, decoded, encoded, erroneous);
|
|
96
|
+
});
|
|
97
|
+
test('should encode/decode float', () => {
|
|
98
|
+
const type = FloatType;
|
|
99
|
+
const decoded = [
|
|
100
|
+
0,
|
|
101
|
+
3.14,
|
|
102
|
+
-1e6,
|
|
103
|
+
Infinity,
|
|
104
|
+
-Infinity,
|
|
105
|
+
NaN,
|
|
106
|
+
];
|
|
107
|
+
const encoded = [
|
|
108
|
+
0,
|
|
109
|
+
3.14,
|
|
110
|
+
-1e6,
|
|
111
|
+
"Infinity",
|
|
112
|
+
"-Infinity",
|
|
113
|
+
"NaN",
|
|
114
|
+
];
|
|
115
|
+
const erroneous = [
|
|
116
|
+
undefined,
|
|
117
|
+
null,
|
|
118
|
+
"",
|
|
119
|
+
"abc",
|
|
120
|
+
"Inf",
|
|
121
|
+
"-Inf",
|
|
122
|
+
[],
|
|
123
|
+
{}
|
|
124
|
+
];
|
|
125
|
+
run(type, decoded, encoded, erroneous);
|
|
126
|
+
});
|
|
127
|
+
test('should encode/decode string', () => {
|
|
128
|
+
const type = StringType;
|
|
129
|
+
const decoded = [
|
|
130
|
+
"",
|
|
131
|
+
"abc",
|
|
132
|
+
"いろはにほへとちりぬるを",
|
|
133
|
+
];
|
|
134
|
+
const encoded = [
|
|
135
|
+
"",
|
|
136
|
+
"abc",
|
|
137
|
+
"いろはにほへとちりぬるを",
|
|
138
|
+
];
|
|
139
|
+
const erroneous = [
|
|
140
|
+
undefined,
|
|
141
|
+
null,
|
|
142
|
+
1,
|
|
143
|
+
[],
|
|
144
|
+
{}
|
|
145
|
+
];
|
|
146
|
+
run(type, decoded, encoded, erroneous);
|
|
147
|
+
});
|
|
148
|
+
test('should encode/decode date', () => {
|
|
149
|
+
const type = DateTimeType;
|
|
150
|
+
const decoded = [
|
|
151
|
+
new Date(0),
|
|
152
|
+
new Date("2022-06-29T13:43:00.123+00:00"),
|
|
153
|
+
new Date("2022-06-29T13:43:00.123Z"),
|
|
154
|
+
new Date("2022-06-29T13:43:00.123+05:00"),
|
|
155
|
+
];
|
|
156
|
+
const encoded = [
|
|
157
|
+
"1970-01-01T00:00:00.000+00:00",
|
|
158
|
+
"2022-06-29T13:43:00.123+00:00",
|
|
159
|
+
"2022-06-29T13:43:00.123+00:00",
|
|
160
|
+
"2022-06-29T08:43:00.123+00:00",
|
|
161
|
+
];
|
|
162
|
+
const erroneous = [
|
|
163
|
+
undefined,
|
|
164
|
+
null,
|
|
165
|
+
1,
|
|
166
|
+
[],
|
|
167
|
+
{},
|
|
168
|
+
"1970-13-01T00:00:00.000+00:00", // Invalid month
|
|
169
|
+
"1970-01-01T00:00:00.000", // Missing timezone
|
|
170
|
+
"1970-01-01 00:00:00.000Z", // Space instead of T
|
|
171
|
+
"1970-01-01T00:00:00Z", // Missing milliseconds
|
|
172
|
+
"2022-06-29T13:43:00.123+5:00", // Invalid offset format (should be +05:00)
|
|
173
|
+
];
|
|
174
|
+
run(type, decoded, encoded, erroneous);
|
|
175
|
+
});
|
|
176
|
+
test('should encode/decode array', () => {
|
|
177
|
+
const type = ArrayType(DateTimeType);
|
|
178
|
+
const decoded = [
|
|
179
|
+
[],
|
|
180
|
+
[
|
|
181
|
+
new Date(0),
|
|
182
|
+
new Date("2022-06-29T13:43:00.123+00:00"),
|
|
183
|
+
],
|
|
184
|
+
];
|
|
185
|
+
const encoded = [
|
|
186
|
+
[],
|
|
187
|
+
[
|
|
188
|
+
"1970-01-01T00:00:00.000+00:00",
|
|
189
|
+
"2022-06-29T13:43:00.123+00:00",
|
|
190
|
+
],
|
|
191
|
+
];
|
|
192
|
+
const erroneous = [
|
|
193
|
+
undefined,
|
|
194
|
+
null,
|
|
195
|
+
1,
|
|
196
|
+
"",
|
|
197
|
+
{},
|
|
198
|
+
[undefined],
|
|
199
|
+
[null],
|
|
200
|
+
[1],
|
|
201
|
+
[[]],
|
|
202
|
+
[{}],
|
|
203
|
+
["1970-01-01T00:00:00.000"], // Missing timezone
|
|
204
|
+
];
|
|
205
|
+
run(type, decoded, encoded, erroneous);
|
|
206
|
+
});
|
|
207
|
+
test('should encode/decode set', () => {
|
|
208
|
+
const type = SetType(StringType);
|
|
209
|
+
const compare = compareFor(type.key);
|
|
210
|
+
const decoded = [
|
|
211
|
+
new SortedSet([], compare),
|
|
212
|
+
new SortedSet(["abc", "def"], compare),
|
|
213
|
+
];
|
|
214
|
+
const encoded = [
|
|
215
|
+
[],
|
|
216
|
+
["abc", "def"],
|
|
217
|
+
];
|
|
218
|
+
const erroneous = [
|
|
219
|
+
undefined,
|
|
220
|
+
null,
|
|
221
|
+
1,
|
|
222
|
+
"",
|
|
223
|
+
{},
|
|
224
|
+
[undefined],
|
|
225
|
+
[null],
|
|
226
|
+
[1],
|
|
227
|
+
[[]],
|
|
228
|
+
[{}],
|
|
229
|
+
];
|
|
230
|
+
run(type, decoded, encoded, erroneous);
|
|
231
|
+
});
|
|
232
|
+
test('should encode/decode dict', () => {
|
|
233
|
+
const type = DictType(StringType, DateTimeType);
|
|
234
|
+
const compare = compareFor(StringType);
|
|
235
|
+
const decoded = [
|
|
236
|
+
new SortedMap([], compare),
|
|
237
|
+
new SortedMap([
|
|
238
|
+
["abc", new Date(0)],
|
|
239
|
+
["def", new Date("2022-06-29T13:43:00.123+00:00")],
|
|
240
|
+
], compare),
|
|
241
|
+
];
|
|
242
|
+
const encoded = [
|
|
243
|
+
[],
|
|
244
|
+
[
|
|
245
|
+
{ key: "abc", value: "1970-01-01T00:00:00.000+00:00" },
|
|
246
|
+
{ key: "def", value: "2022-06-29T13:43:00.123+00:00" },
|
|
247
|
+
],
|
|
248
|
+
];
|
|
249
|
+
const erroneous = [
|
|
250
|
+
undefined,
|
|
251
|
+
null,
|
|
252
|
+
1,
|
|
253
|
+
"",
|
|
254
|
+
{},
|
|
255
|
+
[undefined],
|
|
256
|
+
[null],
|
|
257
|
+
[1],
|
|
258
|
+
["abc"],
|
|
259
|
+
[[]],
|
|
260
|
+
[{}],
|
|
261
|
+
[{ key: "abc" }],
|
|
262
|
+
[{ value: "1970-01-01T00:00:00.000+00:00" }],
|
|
263
|
+
[{ key: 1, value: "1970-01-01T00:00:00.000+00:00" }],
|
|
264
|
+
[{ key: "abc", value: "1970-01-01T00:00:00.000" }], // Missing timezone
|
|
265
|
+
[{ key: "abc", value: "1970-01-01T00:00:00.000+00:00", extra: "naughty" }],
|
|
266
|
+
];
|
|
267
|
+
run(type, decoded, encoded, erroneous);
|
|
268
|
+
});
|
|
269
|
+
test('should encode/decode struct', () => {
|
|
270
|
+
const type = StructType({
|
|
271
|
+
boolean: BooleanType,
|
|
272
|
+
string: StringType,
|
|
273
|
+
date: DateTimeType,
|
|
274
|
+
});
|
|
275
|
+
const decoded = [
|
|
276
|
+
{ boolean: true, string: "good", date: new Date(0) },
|
|
277
|
+
{ boolean: false, string: "bad", date: new Date("2022-06-29T13:43:00.123+00:00") },
|
|
278
|
+
];
|
|
279
|
+
const encoded = [
|
|
280
|
+
{ boolean: true, string: "good", date: "1970-01-01T00:00:00.000+00:00" },
|
|
281
|
+
{ boolean: false, string: "bad", date: "2022-06-29T13:43:00.123+00:00" },
|
|
282
|
+
];
|
|
283
|
+
const erroneous = [
|
|
284
|
+
undefined,
|
|
285
|
+
null,
|
|
286
|
+
1,
|
|
287
|
+
"",
|
|
288
|
+
{},
|
|
289
|
+
{ boolean: true, string: "good" },
|
|
290
|
+
{ boolean: true, string: "good", date: "1970-01-01T00:00:00.000" }, // Missing timezone
|
|
291
|
+
{ boolean: true, string: "good", date: "1970-01-01T00:00:00.000+00:00", extra: "naughty" },
|
|
292
|
+
];
|
|
293
|
+
run(type, decoded, encoded, erroneous);
|
|
294
|
+
});
|
|
295
|
+
test('should encode/decode variant', () => {
|
|
296
|
+
const type = VariantType({
|
|
297
|
+
none: NullType,
|
|
298
|
+
some: DateTimeType,
|
|
299
|
+
});
|
|
300
|
+
const decoded = [
|
|
301
|
+
none,
|
|
302
|
+
some(new Date("2022-06-29T13:43:00.123+00:00")),
|
|
303
|
+
];
|
|
304
|
+
const encoded = [
|
|
305
|
+
{ type: "none", value: null },
|
|
306
|
+
{ type: "some", value: "2022-06-29T13:43:00.123+00:00" },
|
|
307
|
+
];
|
|
308
|
+
const erroneous = [
|
|
309
|
+
undefined,
|
|
310
|
+
null,
|
|
311
|
+
1,
|
|
312
|
+
"",
|
|
313
|
+
{},
|
|
314
|
+
{ type: "none" },
|
|
315
|
+
{ value: null },
|
|
316
|
+
{ type: "nothing", value: null },
|
|
317
|
+
{ type: "none", value: 1 },
|
|
318
|
+
];
|
|
319
|
+
run(type, decoded, encoded, erroneous);
|
|
320
|
+
});
|
|
321
|
+
test('should encode/decode simple linked list', () => {
|
|
322
|
+
// LinkedList = Variant<nil: Null, cons: Struct<head: Integer, tail: LinkedList>>
|
|
323
|
+
const LinkedListType = RecursiveType((self) => VariantType({
|
|
324
|
+
nil: NullType,
|
|
325
|
+
cons: StructType({
|
|
326
|
+
head: IntegerType,
|
|
327
|
+
tail: self,
|
|
328
|
+
}),
|
|
329
|
+
}));
|
|
330
|
+
const decoded = [
|
|
331
|
+
variant("nil", null),
|
|
332
|
+
variant("cons", { head: 1n, tail: variant("nil", null) }),
|
|
333
|
+
variant("cons", {
|
|
334
|
+
head: 1n,
|
|
335
|
+
tail: variant("cons", {
|
|
336
|
+
head: 2n,
|
|
337
|
+
tail: variant("cons", {
|
|
338
|
+
head: 3n,
|
|
339
|
+
tail: variant("nil", null),
|
|
340
|
+
}),
|
|
341
|
+
}),
|
|
342
|
+
}),
|
|
343
|
+
];
|
|
344
|
+
const encoded = [
|
|
345
|
+
{ type: "nil", value: null },
|
|
346
|
+
{ type: "cons", value: { head: "1", tail: { type: "nil", value: null } } },
|
|
347
|
+
{
|
|
348
|
+
type: "cons",
|
|
349
|
+
value: {
|
|
350
|
+
head: "1",
|
|
351
|
+
tail: {
|
|
352
|
+
type: "cons",
|
|
353
|
+
value: {
|
|
354
|
+
head: "2",
|
|
355
|
+
tail: {
|
|
356
|
+
type: "cons",
|
|
357
|
+
value: { head: "3", tail: { type: "nil", value: null } },
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
];
|
|
364
|
+
const erroneous = [
|
|
365
|
+
undefined,
|
|
366
|
+
null,
|
|
367
|
+
1,
|
|
368
|
+
"",
|
|
369
|
+
{},
|
|
370
|
+
{ type: "cons" },
|
|
371
|
+
{ type: "cons", value: {} },
|
|
372
|
+
{ type: "cons", value: { head: "1" } },
|
|
373
|
+
{ type: "cons", value: { head: "not an int", tail: { type: "nil", value: null } } },
|
|
374
|
+
];
|
|
375
|
+
run(LinkedListType, decoded, encoded, erroneous);
|
|
376
|
+
});
|
|
377
|
+
test('should encode/decode binary tree', () => {
|
|
378
|
+
// Tree = Variant<leaf: Integer, node: Struct<left: Tree, right: Tree>>
|
|
379
|
+
const TreeType = RecursiveType((self) => VariantType({
|
|
380
|
+
leaf: IntegerType,
|
|
381
|
+
node: StructType({
|
|
382
|
+
left: self,
|
|
383
|
+
right: self,
|
|
384
|
+
}),
|
|
385
|
+
}));
|
|
386
|
+
const decoded = [
|
|
387
|
+
variant("leaf", 42n),
|
|
388
|
+
variant("node", {
|
|
389
|
+
left: variant("leaf", 1n),
|
|
390
|
+
right: variant("leaf", 2n),
|
|
391
|
+
}),
|
|
392
|
+
variant("node", {
|
|
393
|
+
left: variant("node", {
|
|
394
|
+
left: variant("leaf", 1n),
|
|
395
|
+
right: variant("leaf", 2n),
|
|
396
|
+
}),
|
|
397
|
+
right: variant("leaf", 3n),
|
|
398
|
+
}),
|
|
399
|
+
];
|
|
400
|
+
const encoded = [
|
|
401
|
+
{ type: "leaf", value: "42" },
|
|
402
|
+
{
|
|
403
|
+
type: "node",
|
|
404
|
+
value: {
|
|
405
|
+
left: { type: "leaf", value: "1" },
|
|
406
|
+
right: { type: "leaf", value: "2" },
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: "node",
|
|
411
|
+
value: {
|
|
412
|
+
left: {
|
|
413
|
+
type: "node",
|
|
414
|
+
value: {
|
|
415
|
+
left: { type: "leaf", value: "1" },
|
|
416
|
+
right: { type: "leaf", value: "2" },
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
right: { type: "leaf", value: "3" },
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
];
|
|
423
|
+
const erroneous = [
|
|
424
|
+
undefined,
|
|
425
|
+
null,
|
|
426
|
+
1,
|
|
427
|
+
"",
|
|
428
|
+
{},
|
|
429
|
+
{ type: "leaf" },
|
|
430
|
+
{ type: "leaf", value: "not an int" },
|
|
431
|
+
{ type: "node", value: {} },
|
|
432
|
+
{ type: "node", value: { left: { type: "leaf", value: "1" } } },
|
|
433
|
+
];
|
|
434
|
+
run(TreeType, decoded, encoded, erroneous);
|
|
435
|
+
});
|
|
436
|
+
test('should encode/decode tree with array children', () => {
|
|
437
|
+
// Node = Struct<value: Integer, children: Array<Node>>
|
|
438
|
+
const NodeType = RecursiveType((self) => StructType({
|
|
439
|
+
value: IntegerType,
|
|
440
|
+
children: ArrayType(self),
|
|
441
|
+
}));
|
|
442
|
+
const decoded = [
|
|
443
|
+
{ value: 1n, children: [] },
|
|
444
|
+
{
|
|
445
|
+
value: 1n,
|
|
446
|
+
children: [
|
|
447
|
+
{ value: 2n, children: [] },
|
|
448
|
+
{ value: 3n, children: [] },
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
value: 1n,
|
|
453
|
+
children: [
|
|
454
|
+
{
|
|
455
|
+
value: 2n,
|
|
456
|
+
children: [
|
|
457
|
+
{ value: 4n, children: [] },
|
|
458
|
+
{ value: 5n, children: [] },
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
{ value: 3n, children: [] },
|
|
462
|
+
],
|
|
463
|
+
},
|
|
464
|
+
];
|
|
465
|
+
const encoded = [
|
|
466
|
+
{ value: "1", children: [] },
|
|
467
|
+
{
|
|
468
|
+
value: "1",
|
|
469
|
+
children: [
|
|
470
|
+
{ value: "2", children: [] },
|
|
471
|
+
{ value: "3", children: [] },
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
value: "1",
|
|
476
|
+
children: [
|
|
477
|
+
{
|
|
478
|
+
value: "2",
|
|
479
|
+
children: [
|
|
480
|
+
{ value: "4", children: [] },
|
|
481
|
+
{ value: "5", children: [] },
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
{ value: "3", children: [] },
|
|
485
|
+
],
|
|
486
|
+
},
|
|
487
|
+
];
|
|
488
|
+
const erroneous = [
|
|
489
|
+
undefined,
|
|
490
|
+
null,
|
|
491
|
+
1,
|
|
492
|
+
"",
|
|
493
|
+
{},
|
|
494
|
+
{ value: "1" },
|
|
495
|
+
{ children: [] },
|
|
496
|
+
{ value: "not an int", children: [] },
|
|
497
|
+
{ value: "1", children: [{}] },
|
|
498
|
+
];
|
|
499
|
+
run(NodeType, decoded, encoded, erroneous);
|
|
500
|
+
});
|
|
501
|
+
test('should encode/decode graph with string labels', () => {
|
|
502
|
+
// GraphNode = Struct<label: String, edges: Array<GraphNode>>
|
|
503
|
+
const GraphNodeType = RecursiveType((self) => StructType({
|
|
504
|
+
label: StringType,
|
|
505
|
+
edges: ArrayType(self),
|
|
506
|
+
}));
|
|
507
|
+
const decoded = [
|
|
508
|
+
{ label: "A", edges: [] },
|
|
509
|
+
{
|
|
510
|
+
label: "A",
|
|
511
|
+
edges: [
|
|
512
|
+
{ label: "B", edges: [] },
|
|
513
|
+
{ label: "C", edges: [] },
|
|
514
|
+
],
|
|
515
|
+
},
|
|
516
|
+
];
|
|
517
|
+
const encoded = [
|
|
518
|
+
{ label: "A", edges: [] },
|
|
519
|
+
{
|
|
520
|
+
label: "A",
|
|
521
|
+
edges: [
|
|
522
|
+
{ label: "B", edges: [] },
|
|
523
|
+
{ label: "C", edges: [] },
|
|
524
|
+
],
|
|
525
|
+
},
|
|
526
|
+
];
|
|
527
|
+
const erroneous = [
|
|
528
|
+
undefined,
|
|
529
|
+
null,
|
|
530
|
+
1,
|
|
531
|
+
"",
|
|
532
|
+
{},
|
|
533
|
+
{ label: "A" },
|
|
534
|
+
{ edges: [] },
|
|
535
|
+
{ label: 123, edges: [] },
|
|
536
|
+
{ label: "A", edges: [{ label: "B" }] },
|
|
537
|
+
];
|
|
538
|
+
run(GraphNodeType, decoded, encoded, erroneous);
|
|
539
|
+
});
|
|
540
|
+
test('should encode/decode nested variant structures', () => {
|
|
541
|
+
// Expr = Variant<num: Integer, add: Struct<left: Expr, right: Expr>, mul: Struct<left: Expr, right: Expr>>
|
|
542
|
+
const ExprType = RecursiveType((self) => VariantType({
|
|
543
|
+
num: IntegerType,
|
|
544
|
+
add: StructType({
|
|
545
|
+
left: self,
|
|
546
|
+
right: self,
|
|
547
|
+
}),
|
|
548
|
+
mul: StructType({
|
|
549
|
+
left: self,
|
|
550
|
+
right: self,
|
|
551
|
+
}),
|
|
552
|
+
}));
|
|
553
|
+
const decoded = [
|
|
554
|
+
variant("num", 42n),
|
|
555
|
+
variant("add", {
|
|
556
|
+
left: variant("num", 1n),
|
|
557
|
+
right: variant("num", 2n),
|
|
558
|
+
}),
|
|
559
|
+
variant("mul", {
|
|
560
|
+
left: variant("add", {
|
|
561
|
+
left: variant("num", 2n),
|
|
562
|
+
right: variant("num", 3n),
|
|
563
|
+
}),
|
|
564
|
+
right: variant("num", 4n),
|
|
565
|
+
}),
|
|
566
|
+
];
|
|
567
|
+
const encoded = [
|
|
568
|
+
{ type: "num", value: "42" },
|
|
569
|
+
{
|
|
570
|
+
type: "add",
|
|
571
|
+
value: {
|
|
572
|
+
left: { type: "num", value: "1" },
|
|
573
|
+
right: { type: "num", value: "2" },
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
type: "mul",
|
|
578
|
+
value: {
|
|
579
|
+
left: {
|
|
580
|
+
type: "add",
|
|
581
|
+
value: {
|
|
582
|
+
left: { type: "num", value: "2" },
|
|
583
|
+
right: { type: "num", value: "3" },
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
right: { type: "num", value: "4" },
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
];
|
|
590
|
+
const erroneous = [
|
|
591
|
+
undefined,
|
|
592
|
+
null,
|
|
593
|
+
1,
|
|
594
|
+
"",
|
|
595
|
+
{},
|
|
596
|
+
{ type: "num" },
|
|
597
|
+
{ type: "add", value: {} },
|
|
598
|
+
{ type: "add", value: { left: { type: "num", value: "1" } } },
|
|
599
|
+
{ type: "unknown", value: "1" },
|
|
600
|
+
];
|
|
601
|
+
run(ExprType, decoded, encoded, erroneous);
|
|
602
|
+
});
|
|
603
|
+
test('should encode/decode blob', () => {
|
|
604
|
+
const type = BlobType;
|
|
605
|
+
const decoded = [
|
|
606
|
+
new Uint8Array([1, 3, 3, 7]),
|
|
607
|
+
new Uint8Array([]),
|
|
608
|
+
];
|
|
609
|
+
const encoded = [
|
|
610
|
+
'0x01030307',
|
|
611
|
+
'0x',
|
|
612
|
+
];
|
|
613
|
+
const erroneous = [
|
|
614
|
+
undefined,
|
|
615
|
+
null,
|
|
616
|
+
1,
|
|
617
|
+
true,
|
|
618
|
+
"",
|
|
619
|
+
{},
|
|
620
|
+
[256],
|
|
621
|
+
["0"],
|
|
622
|
+
"abc",
|
|
623
|
+
"0xgg",
|
|
624
|
+
"0x123"
|
|
625
|
+
];
|
|
626
|
+
run(type, decoded, encoded, erroneous);
|
|
627
|
+
});
|
|
628
|
+
test('should encode/decode float vector', () => {
|
|
629
|
+
const type = VectorType(FloatType);
|
|
630
|
+
const decoded = [
|
|
631
|
+
new Float64Array([]),
|
|
632
|
+
new Float64Array([1.0, 2.5, -3.14]),
|
|
633
|
+
new Float64Array([NaN, Infinity, -Infinity]),
|
|
634
|
+
];
|
|
635
|
+
const encoded = [
|
|
636
|
+
[],
|
|
637
|
+
[1.0, 2.5, -3.14],
|
|
638
|
+
["NaN", "Infinity", "-Infinity"],
|
|
639
|
+
];
|
|
640
|
+
run(type, decoded, encoded);
|
|
641
|
+
});
|
|
642
|
+
test('should encode/decode integer vector', () => {
|
|
643
|
+
const type = VectorType(IntegerType);
|
|
644
|
+
const decoded = [
|
|
645
|
+
new BigInt64Array([]),
|
|
646
|
+
new BigInt64Array([1n, -2n, 300n]),
|
|
647
|
+
new BigInt64Array([9223372036854775807n]),
|
|
648
|
+
];
|
|
649
|
+
const encoded = [
|
|
650
|
+
[],
|
|
651
|
+
["1", "-2", "300"],
|
|
652
|
+
["9223372036854775807"],
|
|
653
|
+
];
|
|
654
|
+
run(type, decoded, encoded);
|
|
655
|
+
});
|
|
656
|
+
test('should encode/decode boolean vector', () => {
|
|
657
|
+
const type = VectorType(BooleanType);
|
|
658
|
+
const decoded = [
|
|
659
|
+
new Uint8ClampedArray([]),
|
|
660
|
+
new Uint8ClampedArray([1, 0, 1]),
|
|
661
|
+
];
|
|
662
|
+
const encoded = [
|
|
663
|
+
[],
|
|
664
|
+
[true, false, true],
|
|
665
|
+
];
|
|
666
|
+
run(type, decoded, encoded);
|
|
667
|
+
});
|
|
668
|
+
test('should encode/decode float matrix', () => {
|
|
669
|
+
const type = MatrixType(FloatType);
|
|
670
|
+
const decoded = [
|
|
671
|
+
matrix(new Float64Array([]), 0, 0),
|
|
672
|
+
matrix(new Float64Array([1.0, 2.0, 3.0, 4.0]), 2, 2),
|
|
673
|
+
];
|
|
674
|
+
const encoded = [
|
|
675
|
+
[],
|
|
676
|
+
[[1.0, 2.0], [3.0, 4.0]],
|
|
677
|
+
];
|
|
678
|
+
run(type, decoded, encoded);
|
|
679
|
+
});
|
|
680
|
+
test('should encode/decode integer matrix', () => {
|
|
681
|
+
const type = MatrixType(IntegerType);
|
|
682
|
+
const decoded = [
|
|
683
|
+
matrix(new BigInt64Array([]), 0, 0),
|
|
684
|
+
matrix(new BigInt64Array([1n, 2n, 3n, 4n, 5n, 6n]), 2, 3),
|
|
685
|
+
];
|
|
686
|
+
const encoded = [
|
|
687
|
+
[],
|
|
688
|
+
[["1", "2", "3"], ["4", "5", "6"]],
|
|
689
|
+
];
|
|
690
|
+
run(type, decoded, encoded);
|
|
691
|
+
});
|
|
692
|
+
test('should format error messages correctly', () => {
|
|
693
|
+
// Test error for wrong type
|
|
694
|
+
const booleanDecoder = fromJSONFor(BooleanType);
|
|
695
|
+
try {
|
|
696
|
+
booleanDecoder("not a boolean");
|
|
697
|
+
assert.fail('Should have thrown');
|
|
698
|
+
}
|
|
699
|
+
catch (e) {
|
|
700
|
+
assert.equal(e.message, 'Error occurred because expected boolean, got "not a boolean" (line 1, col 1) while parsing value of type ".Boolean"');
|
|
701
|
+
}
|
|
702
|
+
// Test error with path for nested array
|
|
703
|
+
const arrayDecoder = fromJSONFor(ArrayType(IntegerType));
|
|
704
|
+
try {
|
|
705
|
+
arrayDecoder(["42", "not an integer"]);
|
|
706
|
+
assert.fail('Should have thrown');
|
|
707
|
+
}
|
|
708
|
+
catch (e) {
|
|
709
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an integer" at [1] (line 1, col 1) while parsing value of type ".Array .Integer"');
|
|
710
|
+
}
|
|
711
|
+
// Test error with path for struct field
|
|
712
|
+
const structDecoder = fromJSONFor(StructType({ name: StringType, age: IntegerType }));
|
|
713
|
+
try {
|
|
714
|
+
structDecoder({ name: "Alice", age: 42 });
|
|
715
|
+
assert.fail('Should have thrown');
|
|
716
|
+
}
|
|
717
|
+
catch (e) {
|
|
718
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got 42 at .age (line 1, col 1) while parsing value of type ".Struct [(name="name", type=.String), (name="age", type=.Integer)]"');
|
|
719
|
+
}
|
|
720
|
+
// Test error for missing struct field
|
|
721
|
+
try {
|
|
722
|
+
structDecoder({ name: "Alice" });
|
|
723
|
+
assert.fail('Should have thrown');
|
|
724
|
+
}
|
|
725
|
+
catch (e) {
|
|
726
|
+
assert.equal(e.message, 'Error occurred because missing field "age" in Struct, got {"name":"Alice"} (line 1, col 1) while parsing value of type ".Struct [(name="name", type=.String), (name="age", type=.Integer)]"');
|
|
727
|
+
}
|
|
728
|
+
// Test error for unexpected struct field
|
|
729
|
+
const simpleStructDecoder = fromJSONFor(StructType({ name: StringType }));
|
|
730
|
+
try {
|
|
731
|
+
simpleStructDecoder({ name: "Alice", extra: "unexpected" });
|
|
732
|
+
assert.fail('Should have thrown');
|
|
733
|
+
}
|
|
734
|
+
catch (e) {
|
|
735
|
+
assert.equal(e.message, 'Error occurred because unexpected field "extra" in Struct, got {"name":"Alice","extra":"unexpected"} (line 1, col 1) while parsing value of type ".Struct [(name="name", type=.String)]"');
|
|
736
|
+
}
|
|
737
|
+
// Test error for dict entry value
|
|
738
|
+
const dictDecoder = fromJSONFor(DictType(StringType, IntegerType));
|
|
739
|
+
try {
|
|
740
|
+
dictDecoder([{ key: "a", value: "not an integer" }]);
|
|
741
|
+
assert.fail('Should have thrown');
|
|
742
|
+
}
|
|
743
|
+
catch (e) {
|
|
744
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an integer" at [0].value (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Integer)"');
|
|
745
|
+
}
|
|
746
|
+
// Test error for variant case value
|
|
747
|
+
const variantDecoder = fromJSONFor(VariantType({ none: NullType, some: IntegerType }));
|
|
748
|
+
try {
|
|
749
|
+
variantDecoder({ type: "some", value: "not an integer" });
|
|
750
|
+
assert.fail('Should have thrown');
|
|
751
|
+
}
|
|
752
|
+
catch (e) {
|
|
753
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an integer" at .some (line 1, col 1) while parsing value of type ".Variant [(name="none", type=.Null), (name="some", type=.Integer)]"');
|
|
754
|
+
}
|
|
755
|
+
// Test error for unknown variant type
|
|
756
|
+
try {
|
|
757
|
+
variantDecoder({ type: "unknown", value: null });
|
|
758
|
+
assert.fail('Should have thrown');
|
|
759
|
+
}
|
|
760
|
+
catch (e) {
|
|
761
|
+
assert.equal(e.message, 'Error occurred because unknown variant type "unknown", got {"type":"unknown","value":null} (line 1, col 1) while parsing value of type ".Variant [(name="none", type=.Null), (name="some", type=.Integer)]"');
|
|
762
|
+
}
|
|
763
|
+
// Test error for deeply nested structure - array of structs with error in second element
|
|
764
|
+
const nestedDecoder = fromJSONFor(ArrayType(StructType({ id: IntegerType, name: StringType })));
|
|
765
|
+
try {
|
|
766
|
+
nestedDecoder([
|
|
767
|
+
{ id: "1", name: "Alice" },
|
|
768
|
+
{ id: "not an int", name: "Bob" }
|
|
769
|
+
]);
|
|
770
|
+
assert.fail('Should have thrown');
|
|
771
|
+
}
|
|
772
|
+
catch (e) {
|
|
773
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an int" at [1].id (line 1, col 1) while parsing value of type ".Array .Struct [(name="id", type=.Integer), (name="name", type=.String)]"');
|
|
774
|
+
}
|
|
775
|
+
// Test error for dict with array values - error in array element
|
|
776
|
+
const dictArrayDecoder = fromJSONFor(DictType(StringType, ArrayType(IntegerType)));
|
|
777
|
+
try {
|
|
778
|
+
dictArrayDecoder([
|
|
779
|
+
{ key: "nums", value: ["1", "2", "not an int"] }
|
|
780
|
+
]);
|
|
781
|
+
assert.fail('Should have thrown');
|
|
782
|
+
}
|
|
783
|
+
catch (e) {
|
|
784
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an int" at [0].value[2] (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Array .Integer)"');
|
|
785
|
+
}
|
|
786
|
+
// Test error for struct containing variant with error
|
|
787
|
+
const structVariantDecoder = fromJSONFor(StructType({
|
|
788
|
+
result: VariantType({ ok: IntegerType, error: StringType })
|
|
789
|
+
}));
|
|
790
|
+
try {
|
|
791
|
+
structVariantDecoder({ result: { type: "ok", value: "not an int" } });
|
|
792
|
+
assert.fail('Should have thrown');
|
|
793
|
+
}
|
|
794
|
+
catch (e) {
|
|
795
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an int" at .result.ok (line 1, col 1) while parsing value of type ".Struct [(name="result", type=.Variant [(name="error", type=.String), (name="ok", type=.Integer)])]"');
|
|
796
|
+
}
|
|
797
|
+
// Test error for set element
|
|
798
|
+
const setDecoder = fromJSONFor(SetType(IntegerType));
|
|
799
|
+
try {
|
|
800
|
+
setDecoder(["1", "2", "not an int"]);
|
|
801
|
+
assert.fail('Should have thrown');
|
|
802
|
+
}
|
|
803
|
+
catch (e) {
|
|
804
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an int" at [2] (line 1, col 1) while parsing value of type ".Set .Integer"');
|
|
805
|
+
}
|
|
806
|
+
// Test error for dict with missing key field
|
|
807
|
+
try {
|
|
808
|
+
dictDecoder([{ value: "123" }]);
|
|
809
|
+
assert.fail('Should have thrown');
|
|
810
|
+
}
|
|
811
|
+
catch (e) {
|
|
812
|
+
assert.equal(e.message, 'Error occurred because expected object with key and value for Dict entry, got {"value":"123"} at [0] (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Integer)"');
|
|
813
|
+
}
|
|
814
|
+
// Test error for dict with extra field
|
|
815
|
+
try {
|
|
816
|
+
dictDecoder([{ key: "a", value: "123", extra: "bad" }]);
|
|
817
|
+
assert.fail('Should have thrown');
|
|
818
|
+
}
|
|
819
|
+
catch (e) {
|
|
820
|
+
assert.equal(e.message, 'Error occurred because unexpected field "extra" in Dict entry, got {"key":"a","value":"123","extra":"bad"} at [0] (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Integer)"');
|
|
821
|
+
}
|
|
822
|
+
// Test error for extremely complex nested structure:
|
|
823
|
+
// Dict<String, Array<Struct{ results: Array<Variant<ok: Struct{items: Set<Integer>}, error: String>> }>>
|
|
824
|
+
const veryComplexType = DictType(StringType, ArrayType(StructType({
|
|
825
|
+
results: ArrayType(VariantType({
|
|
826
|
+
ok: StructType({
|
|
827
|
+
items: SetType(IntegerType)
|
|
828
|
+
}),
|
|
829
|
+
error: StringType
|
|
830
|
+
}))
|
|
831
|
+
})));
|
|
832
|
+
const veryComplexDecoder = fromJSONFor(veryComplexType);
|
|
833
|
+
try {
|
|
834
|
+
veryComplexDecoder([
|
|
835
|
+
{
|
|
836
|
+
key: "batch1",
|
|
837
|
+
value: [
|
|
838
|
+
{
|
|
839
|
+
results: [
|
|
840
|
+
{ type: "ok", value: { items: ["1", "2", "not an int"] } }
|
|
841
|
+
]
|
|
842
|
+
}
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
]);
|
|
846
|
+
assert.fail('Should have thrown');
|
|
847
|
+
}
|
|
848
|
+
catch (e) {
|
|
849
|
+
assert.equal(e.message, 'Error occurred because expected string representing integer, got "not an int" at [0].value[0].results[0].ok.items[2] (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Array .Struct [(name="results", type=.Array .Variant [(name="error", type=.String), (name="ok", type=.Struct [(name="items", type=.Set .Integer)])])])"');
|
|
850
|
+
}
|
|
851
|
+
// Another complex error - missing field deep in structure
|
|
852
|
+
try {
|
|
853
|
+
veryComplexDecoder([
|
|
854
|
+
{
|
|
855
|
+
key: "batch1",
|
|
856
|
+
value: [
|
|
857
|
+
{
|
|
858
|
+
results: [
|
|
859
|
+
{ type: "ok", value: { wrong: ["1"] } } // missing 'items' field
|
|
860
|
+
]
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
}
|
|
864
|
+
]);
|
|
865
|
+
assert.fail('Should have thrown');
|
|
866
|
+
}
|
|
867
|
+
catch (e) {
|
|
868
|
+
assert.equal(e.message, 'Error occurred because unexpected field "wrong" in Struct, got {"wrong":["1"]} at [0].value[0].results[0].ok (line 1, col 1) while parsing value of type ".Dict (key=.String, value=.Array .Struct [(name="results", type=.Array .Variant [(name="error", type=.String), (name="ok", type=.Struct [(name="items", type=.Set .Integer)])])])"');
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
test('should encode/decode using Uint8Array with encodeJSONFor/decodeJSONFor', () => {
|
|
872
|
+
const encode = encodeJSONFor(IntegerType);
|
|
873
|
+
const decode = decodeJSONFor(IntegerType);
|
|
874
|
+
const value = 42n;
|
|
875
|
+
const encoded = encode(value);
|
|
876
|
+
const decoded = decode(encoded);
|
|
877
|
+
assert.strictEqual(decoded, value);
|
|
878
|
+
assert.ok(encoded instanceof Uint8Array);
|
|
879
|
+
});
|
|
880
|
+
test('should encode/decode complex types with encodeJSONFor/decodeJSONFor', () => {
|
|
881
|
+
const type = StructType({ name: StringType, age: IntegerType });
|
|
882
|
+
const encode = encodeJSONFor(type);
|
|
883
|
+
const decode = decodeJSONFor(type);
|
|
884
|
+
const value = { name: "Alice", age: 30n };
|
|
885
|
+
const encoded = encode(value);
|
|
886
|
+
const decoded = decode(encoded);
|
|
887
|
+
assert.deepEqual(decoded, value);
|
|
888
|
+
});
|
|
889
|
+
test('should throw when encoding Never type', () => {
|
|
890
|
+
const toJson = toJSONFor(NeverType);
|
|
891
|
+
assert.throws(() => toJson(null), /Cannot encode Never type to JSON/);
|
|
892
|
+
});
|
|
893
|
+
test('should throw when decoding Never type with fromJSONFor', () => {
|
|
894
|
+
const fromJson = fromJSONFor(NeverType);
|
|
895
|
+
assert.throws(() => fromJson(null), /Cannot decode Never type from JSON/);
|
|
896
|
+
});
|
|
897
|
+
test('should throw when decoding Never type with decodeJSONFor', () => {
|
|
898
|
+
const textEncoder = new TextEncoder();
|
|
899
|
+
const decode = decodeJSONFor(NeverType);
|
|
900
|
+
const encoded = textEncoder.encode('null');
|
|
901
|
+
assert.throws(() => decode(encoded), /Cannot decode Never type from JSON/);
|
|
902
|
+
});
|
|
903
|
+
test('should throw when encoding Function type', () => {
|
|
904
|
+
const funcType = FunctionType([], IntegerType);
|
|
905
|
+
assert.throws(() => toJSONFor(funcType), /Cannot encode Function type to JSON/);
|
|
906
|
+
});
|
|
907
|
+
test('should throw when decoding Function type with fromJSONFor', () => {
|
|
908
|
+
const funcType = FunctionType([], IntegerType);
|
|
909
|
+
assert.throws(() => fromJSONFor(funcType), /Cannot decode Function type from JSON/);
|
|
910
|
+
});
|
|
911
|
+
test('should throw when creating decoder for Function type with decodeJSONFor', () => {
|
|
912
|
+
const funcType = FunctionType([], IntegerType);
|
|
913
|
+
assert.throws(() => decodeJSONFor(funcType), /Cannot decode Function type from JSON/);
|
|
914
|
+
});
|
|
915
|
+
test('should throw when encoding AsyncFunction type', () => {
|
|
916
|
+
const funcType = AsyncFunctionType([], IntegerType);
|
|
917
|
+
assert.throws(() => toJSONFor(funcType), /Cannot encode AsyncFunction type to JSON/);
|
|
918
|
+
});
|
|
919
|
+
test('should throw when decoding AsyncFunction type with fromJSONFor', () => {
|
|
920
|
+
const funcType = AsyncFunctionType([], IntegerType);
|
|
921
|
+
assert.throws(() => fromJSONFor(funcType), /Cannot decode AsyncFunction type from JSON/);
|
|
922
|
+
});
|
|
923
|
+
test('should throw when creating decoder for AsyncFunction type with decodeJSONFor', () => {
|
|
924
|
+
const funcType = AsyncFunctionType([], IntegerType);
|
|
925
|
+
assert.throws(() => decodeJSONFor(funcType), /Cannot decode AsyncFunction type from JSON/);
|
|
926
|
+
});
|
|
927
|
+
test('should freeze decoded Date when frozen=true', () => {
|
|
928
|
+
const fromJson = fromJSONFor(DateTimeType, true);
|
|
929
|
+
const date = fromJson("2022-06-29T13:43:00.123+00:00");
|
|
930
|
+
assert.ok(Object.isFrozen(date));
|
|
931
|
+
});
|
|
932
|
+
test('should not freeze decoded Date when frozen=false', () => {
|
|
933
|
+
const fromJson = fromJSONFor(DateTimeType, false);
|
|
934
|
+
const date = fromJson("2022-06-29T13:43:00.123+00:00");
|
|
935
|
+
assert.ok(!Object.isFrozen(date));
|
|
936
|
+
});
|
|
937
|
+
test('should attempt to freeze decoded Blob when frozen=true', () => {
|
|
938
|
+
// Note: Uint8Array cannot actually be frozen in JavaScript
|
|
939
|
+
// This test verifies that the frozen code path is executed
|
|
940
|
+
const fromJson = fromJSONFor(BlobType, true);
|
|
941
|
+
// This will execute the Object.freeze code path, which will throw
|
|
942
|
+
// because Uint8Array cannot be frozen
|
|
943
|
+
assert.throws(() => fromJson("0x01020304"), /Cannot freeze array buffer views/);
|
|
944
|
+
});
|
|
945
|
+
test('should not freeze decoded Blob when frozen=false', () => {
|
|
946
|
+
const fromJson = fromJSONFor(BlobType, false);
|
|
947
|
+
const blob = fromJson("0x01020304");
|
|
948
|
+
assert.ok(!Object.isFrozen(blob));
|
|
949
|
+
});
|
|
950
|
+
test('should freeze decoded Array when frozen=true', () => {
|
|
951
|
+
const fromJson = fromJSONFor(ArrayType(IntegerType), true);
|
|
952
|
+
const arr = fromJson(["1", "2"]);
|
|
953
|
+
assert.ok(Object.isFrozen(arr));
|
|
954
|
+
});
|
|
955
|
+
test('should not freeze decoded Array when frozen=false', () => {
|
|
956
|
+
const fromJson = fromJSONFor(ArrayType(IntegerType), false);
|
|
957
|
+
const arr = fromJson(["1", "2"]);
|
|
958
|
+
assert.ok(!Object.isFrozen(arr));
|
|
959
|
+
});
|
|
960
|
+
test('should freeze decoded Set when frozen=true', () => {
|
|
961
|
+
const fromJson = fromJSONFor(SetType(StringType), true);
|
|
962
|
+
const set = fromJson(["a", "b"]);
|
|
963
|
+
assert.ok(Object.isFrozen(set));
|
|
964
|
+
});
|
|
965
|
+
test('should not freeze decoded Set when frozen=false', () => {
|
|
966
|
+
const fromJson = fromJSONFor(SetType(StringType), false);
|
|
967
|
+
const set = fromJson(["a", "b"]);
|
|
968
|
+
assert.ok(!Object.isFrozen(set));
|
|
969
|
+
});
|
|
970
|
+
test('should freeze decoded Dict when frozen=true', () => {
|
|
971
|
+
const fromJson = fromJSONFor(DictType(StringType, IntegerType), true);
|
|
972
|
+
const dict = fromJson([{ key: "a", value: "1" }]);
|
|
973
|
+
assert.ok(Object.isFrozen(dict));
|
|
974
|
+
});
|
|
975
|
+
test('should not freeze decoded Dict when frozen=false', () => {
|
|
976
|
+
const fromJson = fromJSONFor(DictType(StringType, IntegerType), false);
|
|
977
|
+
const dict = fromJson([{ key: "a", value: "1" }]);
|
|
978
|
+
assert.ok(!Object.isFrozen(dict));
|
|
979
|
+
});
|
|
980
|
+
test('should freeze decoded Struct when frozen=true', () => {
|
|
981
|
+
const fromJson = fromJSONFor(StructType({ x: IntegerType }), true);
|
|
982
|
+
const struct = fromJson({ x: "42" });
|
|
983
|
+
assert.ok(Object.isFrozen(struct));
|
|
984
|
+
});
|
|
985
|
+
test('should not freeze decoded Struct when frozen=false', () => {
|
|
986
|
+
const fromJson = fromJSONFor(StructType({ x: IntegerType }), false);
|
|
987
|
+
const struct = fromJson({ x: "42" });
|
|
988
|
+
assert.ok(!Object.isFrozen(struct));
|
|
989
|
+
});
|
|
990
|
+
test('should freeze decoded Variant when frozen=true', () => {
|
|
991
|
+
const fromJson = fromJSONFor(VariantType({ some: IntegerType }), true);
|
|
992
|
+
const v = fromJson({ type: "some", value: "42" });
|
|
993
|
+
assert.ok(Object.isFrozen(v));
|
|
994
|
+
});
|
|
995
|
+
test('should not freeze decoded Variant when frozen=false', () => {
|
|
996
|
+
const fromJson = fromJSONFor(VariantType({ some: IntegerType }), false);
|
|
997
|
+
const v = fromJson({ type: "some", value: "42" });
|
|
998
|
+
assert.ok(!Object.isFrozen(v));
|
|
999
|
+
});
|
|
1000
|
+
test('should handle JSON.parse syntax errors in decodeJSONFor', () => {
|
|
1001
|
+
const textEncoder = new TextEncoder();
|
|
1002
|
+
const decode = decodeJSONFor(IntegerType);
|
|
1003
|
+
// Invalid JSON
|
|
1004
|
+
const invalidJson = textEncoder.encode('{"broken": }');
|
|
1005
|
+
assert.throws(() => decode(invalidJson), /Error occurred because.*line \d+, col \d+/);
|
|
1006
|
+
});
|
|
1007
|
+
test('should track line and column numbers in JSON parse errors', () => {
|
|
1008
|
+
const textEncoder = new TextEncoder();
|
|
1009
|
+
const decode = decodeJSONFor(IntegerType);
|
|
1010
|
+
// Multi-line invalid JSON
|
|
1011
|
+
const invalidJson = textEncoder.encode('{\n"foo": \n}');
|
|
1012
|
+
assert.throws(() => decode(invalidJson), /line \d+, col \d+/);
|
|
1013
|
+
});
|
|
1014
|
+
test('should handle JSON parse errors without position info', () => {
|
|
1015
|
+
const textEncoder = new TextEncoder();
|
|
1016
|
+
const decode = decodeJSONFor(IntegerType);
|
|
1017
|
+
// Another invalid JSON case
|
|
1018
|
+
const invalidJson = textEncoder.encode('[');
|
|
1019
|
+
assert.throws(() => decode(invalidJson), /Error occurred because/);
|
|
1020
|
+
});
|
|
1021
|
+
test('should use decodeJSONFor with frozen parameter', () => {
|
|
1022
|
+
const encode = encodeJSONFor(ArrayType(IntegerType));
|
|
1023
|
+
const decode = decodeJSONFor(ArrayType(IntegerType), true);
|
|
1024
|
+
const value = [1n, 2n, 3n];
|
|
1025
|
+
const encoded = encode(value);
|
|
1026
|
+
const decoded = decode(encoded);
|
|
1027
|
+
assert.ok(Object.isFrozen(decoded));
|
|
1028
|
+
assert.deepEqual(decoded, value);
|
|
1029
|
+
});
|
|
1030
|
+
test('should handle non-SyntaxError exceptions in decodeJSONFor', () => {
|
|
1031
|
+
const textEncoder = new TextEncoder();
|
|
1032
|
+
const decode = decodeJSONFor(IntegerType);
|
|
1033
|
+
// Create a malformed but parseable JSON that will fail type checking
|
|
1034
|
+
const validJson = textEncoder.encode('"not an integer"');
|
|
1035
|
+
assert.throws(() => decode(validJson), /Error occurred because/);
|
|
1036
|
+
});
|
|
1037
|
+
test('should round-trip random types and values', { timeout: 60_000 }, async () => {
|
|
1038
|
+
const result = await fuzzerTest((type) => {
|
|
1039
|
+
const toJson = toJSONFor(type);
|
|
1040
|
+
const fromJson = fromJSONFor(type);
|
|
1041
|
+
const equal = equalFor(type);
|
|
1042
|
+
return async (value) => {
|
|
1043
|
+
// Encode and decode
|
|
1044
|
+
const encoded = toJson(value);
|
|
1045
|
+
const decoded = fromJson(encoded);
|
|
1046
|
+
// Check value equality
|
|
1047
|
+
if (!equal(decoded, value)) {
|
|
1048
|
+
throw new Error(`Round-trip failed: values not equal`);
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
}, 100, // 100 random types
|
|
1052
|
+
10, // 10 samples per type
|
|
1053
|
+
{ includeRecursive: false } // TODO: recursive value generation needs work
|
|
1054
|
+
);
|
|
1055
|
+
assert.strictEqual(result, true, "Fuzz test failed");
|
|
1056
|
+
});
|
|
1057
|
+
test('should round-trip with Uint8Array encoding for random types', { timeout: 60_000 }, async () => {
|
|
1058
|
+
const result = await fuzzerTest((type) => {
|
|
1059
|
+
const encode = encodeJSONFor(type);
|
|
1060
|
+
const decode = decodeJSONFor(type);
|
|
1061
|
+
const equal = equalFor(type);
|
|
1062
|
+
return async (value) => {
|
|
1063
|
+
// Encode and decode
|
|
1064
|
+
const encoded = encode(value);
|
|
1065
|
+
const decoded = decode(encoded);
|
|
1066
|
+
// Verify it's a Uint8Array
|
|
1067
|
+
if (!(encoded instanceof Uint8Array)) {
|
|
1068
|
+
throw new Error(`Encoded value is not a Uint8Array`);
|
|
1069
|
+
}
|
|
1070
|
+
// Check value equality
|
|
1071
|
+
if (!equal(decoded, value)) {
|
|
1072
|
+
throw new Error(`Round-trip failed: values not equal`);
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
}, 100, // 100 random types
|
|
1076
|
+
10, // 10 samples per type
|
|
1077
|
+
{ includeRecursive: false } // TODO: recursive value generation needs work
|
|
1078
|
+
);
|
|
1079
|
+
assert.strictEqual(result, true, "Fuzz test failed");
|
|
1080
|
+
});
|
|
1081
|
+
test('should re-throw non-JSONDecodeError exceptions in Array decoding', () => {
|
|
1082
|
+
// This tests the throw e; path at line 260 in the Array decoder
|
|
1083
|
+
const type = ArrayType(IntegerType);
|
|
1084
|
+
const fromJson = fromJSONFor(type);
|
|
1085
|
+
// Create a scenario where a non-JSONDecodeError might be thrown
|
|
1086
|
+
// by passing malformed data that causes a different type of error
|
|
1087
|
+
try {
|
|
1088
|
+
// Using a symbol which should cause a different error type
|
|
1089
|
+
fromJson([Symbol('test')]);
|
|
1090
|
+
assert.fail('Should have thrown');
|
|
1091
|
+
}
|
|
1092
|
+
catch (e) {
|
|
1093
|
+
// Should re-throw the error
|
|
1094
|
+
assert.ok(e);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
test('should re-throw non-JSONDecodeError exceptions in Set decoding', () => {
|
|
1098
|
+
const type = SetType(IntegerType);
|
|
1099
|
+
const fromJson = fromJSONFor(type);
|
|
1100
|
+
try {
|
|
1101
|
+
fromJson([Symbol('test')]);
|
|
1102
|
+
assert.fail('Should have thrown');
|
|
1103
|
+
}
|
|
1104
|
+
catch (e) {
|
|
1105
|
+
assert.ok(e);
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
test('should re-throw non-JSONDecodeError exceptions in Dict key decoding', () => {
|
|
1109
|
+
const type = DictType(IntegerType, StringType);
|
|
1110
|
+
const fromJson = fromJSONFor(type);
|
|
1111
|
+
try {
|
|
1112
|
+
fromJson([{ key: Symbol('test'), value: "test" }]);
|
|
1113
|
+
assert.fail('Should have thrown');
|
|
1114
|
+
}
|
|
1115
|
+
catch (e) {
|
|
1116
|
+
assert.ok(e);
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
test('should re-throw non-JSONDecodeError exceptions in Dict value decoding', () => {
|
|
1120
|
+
const type = DictType(StringType, IntegerType);
|
|
1121
|
+
const fromJson = fromJSONFor(type);
|
|
1122
|
+
try {
|
|
1123
|
+
fromJson([{ key: "test", value: Symbol('test') }]);
|
|
1124
|
+
assert.fail('Should have thrown');
|
|
1125
|
+
}
|
|
1126
|
+
catch (e) {
|
|
1127
|
+
assert.ok(e);
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
test('should re-throw non-JSONDecodeError exceptions in Struct decoding', () => {
|
|
1131
|
+
const type = StructType({ x: IntegerType });
|
|
1132
|
+
const fromJson = fromJSONFor(type);
|
|
1133
|
+
try {
|
|
1134
|
+
fromJson({ x: Symbol('test') });
|
|
1135
|
+
assert.fail('Should have thrown');
|
|
1136
|
+
}
|
|
1137
|
+
catch (e) {
|
|
1138
|
+
assert.ok(e);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
test('should re-throw non-JSONDecodeError exceptions in Variant decoding', () => {
|
|
1142
|
+
const type = VariantType({ some: IntegerType });
|
|
1143
|
+
const fromJson = fromJSONFor(type);
|
|
1144
|
+
try {
|
|
1145
|
+
fromJson({ type: "some", value: Symbol('test') });
|
|
1146
|
+
assert.fail('Should have thrown');
|
|
1147
|
+
}
|
|
1148
|
+
catch (e) {
|
|
1149
|
+
assert.ok(e);
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
test('should encode shared array references within RecursiveType', () => {
|
|
1153
|
+
// Create a type that can hold shared arrays
|
|
1154
|
+
const NodeType = RecursiveType((self) => StructType({
|
|
1155
|
+
value: IntegerType,
|
|
1156
|
+
children: ArrayType(self),
|
|
1157
|
+
metadata: ArrayType(StringType),
|
|
1158
|
+
}));
|
|
1159
|
+
// Create a shared array that appears in multiple places
|
|
1160
|
+
const sharedMetadata = ["tag1", "tag2"];
|
|
1161
|
+
const value = {
|
|
1162
|
+
value: 1n,
|
|
1163
|
+
children: [
|
|
1164
|
+
{
|
|
1165
|
+
value: 2n,
|
|
1166
|
+
children: [],
|
|
1167
|
+
metadata: sharedMetadata, // First reference
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
value: 3n,
|
|
1171
|
+
children: [],
|
|
1172
|
+
metadata: sharedMetadata, // Second reference (same instance)
|
|
1173
|
+
},
|
|
1174
|
+
],
|
|
1175
|
+
metadata: sharedMetadata, // Third reference (same instance)
|
|
1176
|
+
};
|
|
1177
|
+
const toJson = toJSONFor(NodeType);
|
|
1178
|
+
const fromJson = fromJSONFor(NodeType);
|
|
1179
|
+
const encoded = toJson(value);
|
|
1180
|
+
// Check that references appear in the encoding (first occurrence is real data, rest are refs)
|
|
1181
|
+
assert.deepEqual(encoded.children[0].metadata, ["tag1", "tag2"]);
|
|
1182
|
+
assert.deepEqual(encoded.children[1].metadata, { "$ref": "2#0/metadata" });
|
|
1183
|
+
assert.deepEqual(encoded.metadata, { "$ref": "1#children/0/metadata" });
|
|
1184
|
+
// Check that decoding preserves shared references
|
|
1185
|
+
const decoded = fromJson(encoded);
|
|
1186
|
+
assert.ok(decoded.metadata === decoded.children[0].metadata, "metadata should be same instance as children[0].metadata");
|
|
1187
|
+
assert.ok(decoded.metadata === decoded.children[1].metadata, "metadata should be same instance as children[1].metadata");
|
|
1188
|
+
// Check round-trip equality
|
|
1189
|
+
const equal = equalFor(NodeType);
|
|
1190
|
+
assert.ok(equal(decoded, value), "decoded value should equal original");
|
|
1191
|
+
});
|
|
1192
|
+
test('should encode shared set references within RecursiveType', () => {
|
|
1193
|
+
const NodeType = RecursiveType((self) => StructType({
|
|
1194
|
+
value: IntegerType,
|
|
1195
|
+
children: ArrayType(self),
|
|
1196
|
+
tags: SetType(StringType),
|
|
1197
|
+
}));
|
|
1198
|
+
const compare = compareFor(StringType);
|
|
1199
|
+
const sharedTags = new SortedSet(["a", "b", "c"], compare);
|
|
1200
|
+
const value = {
|
|
1201
|
+
value: 1n,
|
|
1202
|
+
children: [
|
|
1203
|
+
{
|
|
1204
|
+
value: 2n,
|
|
1205
|
+
children: [],
|
|
1206
|
+
tags: sharedTags, // First reference
|
|
1207
|
+
},
|
|
1208
|
+
],
|
|
1209
|
+
tags: sharedTags, // Second reference (same instance)
|
|
1210
|
+
};
|
|
1211
|
+
const toJson = toJSONFor(NodeType);
|
|
1212
|
+
const fromJson = fromJSONFor(NodeType);
|
|
1213
|
+
const encoded = toJson(value);
|
|
1214
|
+
// Check encoding (first occurrence is real data, rest are refs)
|
|
1215
|
+
assert.deepEqual(encoded.children[0].tags, ["a", "b", "c"]);
|
|
1216
|
+
assert.deepEqual(encoded.tags, { "$ref": "1#children/0/tags" });
|
|
1217
|
+
// Check decoding preserves shared references
|
|
1218
|
+
const decoded = fromJson(encoded);
|
|
1219
|
+
assert.ok(decoded.tags === decoded.children[0].tags, "tags should be same instance");
|
|
1220
|
+
// Check round-trip equality
|
|
1221
|
+
const equal = equalFor(NodeType);
|
|
1222
|
+
assert.ok(equal(decoded, value), "decoded value should equal original");
|
|
1223
|
+
});
|
|
1224
|
+
test('should encode shared dict references within RecursiveType', () => {
|
|
1225
|
+
const NodeType = RecursiveType((self) => StructType({
|
|
1226
|
+
value: IntegerType,
|
|
1227
|
+
children: ArrayType(self),
|
|
1228
|
+
properties: DictType(StringType, IntegerType),
|
|
1229
|
+
}));
|
|
1230
|
+
const compare = compareFor(StringType);
|
|
1231
|
+
const sharedProps = new SortedMap([["x", 10n], ["y", 20n]], compare);
|
|
1232
|
+
const value = {
|
|
1233
|
+
value: 1n,
|
|
1234
|
+
children: [
|
|
1235
|
+
{
|
|
1236
|
+
value: 2n,
|
|
1237
|
+
children: [],
|
|
1238
|
+
properties: sharedProps,
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
value: 3n,
|
|
1242
|
+
children: [],
|
|
1243
|
+
properties: sharedProps,
|
|
1244
|
+
},
|
|
1245
|
+
],
|
|
1246
|
+
properties: sharedProps,
|
|
1247
|
+
};
|
|
1248
|
+
const toJson = toJSONFor(NodeType);
|
|
1249
|
+
const fromJson = fromJSONFor(NodeType);
|
|
1250
|
+
const encoded = toJson(value);
|
|
1251
|
+
// Check that references appear in the encoding (first occurrence is real data, rest are refs)
|
|
1252
|
+
assert.deepEqual(encoded.children[0].properties, [
|
|
1253
|
+
{ key: "x", value: "10" },
|
|
1254
|
+
{ key: "y", value: "20" },
|
|
1255
|
+
]);
|
|
1256
|
+
assert.deepEqual(encoded.children[1].properties, { "$ref": "2#0/properties" });
|
|
1257
|
+
assert.deepEqual(encoded.properties, { "$ref": "1#children/0/properties" });
|
|
1258
|
+
// Check that decoding preserves shared references
|
|
1259
|
+
const decoded = fromJson(encoded);
|
|
1260
|
+
assert.ok(decoded.properties === decoded.children[0].properties, "properties should be same instance as children[0].properties");
|
|
1261
|
+
assert.ok(decoded.properties === decoded.children[1].properties, "properties should be same instance as children[1].properties");
|
|
1262
|
+
// Check round-trip equality
|
|
1263
|
+
const equal = equalFor(NodeType);
|
|
1264
|
+
assert.ok(equal(decoded, value), "decoded value should equal original");
|
|
1265
|
+
});
|
|
1266
|
+
test('should handle JSON Pointer escaping in field names', () => {
|
|
1267
|
+
// Create a struct with field names that need escaping
|
|
1268
|
+
const NodeType = RecursiveType((self) => StructType({
|
|
1269
|
+
"field/with/slashes": IntegerType,
|
|
1270
|
+
"field~with~tildes": IntegerType,
|
|
1271
|
+
"normal": ArrayType(IntegerType),
|
|
1272
|
+
children: ArrayType(self),
|
|
1273
|
+
}));
|
|
1274
|
+
const sharedArray = [1n, 2n, 3n];
|
|
1275
|
+
const value = {
|
|
1276
|
+
"field/with/slashes": 42n,
|
|
1277
|
+
"field~with~tildes": 99n,
|
|
1278
|
+
"normal": sharedArray,
|
|
1279
|
+
children: [
|
|
1280
|
+
{
|
|
1281
|
+
"field/with/slashes": 1n,
|
|
1282
|
+
"field~with~tildes": 2n,
|
|
1283
|
+
"normal": sharedArray, // Reference to same array
|
|
1284
|
+
children: [],
|
|
1285
|
+
},
|
|
1286
|
+
],
|
|
1287
|
+
};
|
|
1288
|
+
const toJson = toJSONFor(NodeType);
|
|
1289
|
+
const encoded = toJson(value);
|
|
1290
|
+
// First array should be actual data
|
|
1291
|
+
assert.deepEqual(encoded.normal, ["1", "2", "3"]);
|
|
1292
|
+
// Second occurrence should be a reference with properly escaped path
|
|
1293
|
+
// Path should be 3#normal (go up 3 levels from children[0].normal to root.normal)
|
|
1294
|
+
assert.deepEqual(encoded.children[0].normal, { "$ref": "3#normal" });
|
|
1295
|
+
});
|
|
1296
|
+
});
|
|
1297
|
+
//# sourceMappingURL=json.spec.js.map
|