goscript 0.1.4 → 0.2.1
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/README.md +5 -2
- package/cmd/go_js_wasm_exec/main.go +201 -0
- package/cmd/go_js_wasm_exec/main_test.go +83 -0
- package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
- package/cmd/goscript/cmd-test.go +14 -0
- package/cmd/goscript/cmd-test_test.go +1 -1
- package/cmd/goscript-wasm/main.go +38 -6
- package/compiler/compile-request.go +12 -9
- package/compiler/compliance_test.go +0 -1
- package/compiler/config.go +2 -0
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/request.go +28 -0
- package/compiler/gotest/runner.go +354 -44
- package/compiler/gotest/runner_test.go +293 -1
- package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
- package/compiler/gotest/testdata/browserapi/go.mod +3 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +33 -24
- package/compiler/lowering.go +746 -194
- package/compiler/lowering_bench_test.go +42 -27
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/override-facts.go +15 -0
- package/compiler/override-parity-verifier.go +450 -0
- package/compiler/override-parity.go +122 -0
- package/compiler/override-registry_test.go +559 -0
- package/compiler/protobuf-ts-binding.go +567 -0
- package/compiler/protobuf-ts-binding_test.go +402 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/semantic-model-types.go +9 -4
- package/compiler/semantic-model.go +282 -70
- package/compiler/semantic-model_test.go +82 -1
- package/compiler/service.go +21 -1
- package/compiler/skeleton_test.go +118 -10
- package/compiler/typescript-emitter.go +128 -13
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/{wasm_api.go → wasm-api.go} +57 -7
- package/dist/gs/builtin/hostio.js +5 -0
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +13 -2
- package/dist/gs/builtin/slice.js +187 -6
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +13 -5
- package/dist/gs/builtin/type.js +153 -60
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +11 -0
- package/dist/gs/builtin/varRef.js +57 -2
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/compress/zlib/index.d.ts +10 -3
- package/dist/gs/compress/zlib/index.js +50 -16
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/encoding/json/index.d.ts +114 -0
- package/dist/gs/encoding/json/index.js +544 -36
- package/dist/gs/encoding/json/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +101 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +589 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js +54 -30
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +2 -0
- package/dist/gs/go/scanner/index.js +29 -5
- package/dist/gs/go/scanner/index.js.map +1 -1
- package/dist/gs/go/token/index.js +22 -6
- package/dist/gs/go/token/index.js.map +1 -1
- package/dist/gs/hash/index.d.ts +6 -0
- package/dist/gs/hash/index.js +20 -0
- package/dist/gs/hash/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/internal/goarch/index.d.ts +43 -3
- package/dist/gs/internal/goarch/index.js +42 -10
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js +26 -14
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js +4 -2
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/sub.js +8 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.d.ts +2 -0
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +5 -0
- package/dist/gs/math/bits/index.js +16 -4
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/mime/index.d.ts +16 -0
- package/dist/gs/mime/index.js +315 -6
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/httptest/index.d.ts +12 -0
- package/dist/gs/net/http/httptest/index.js +85 -6
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +300 -5
- package/dist/gs/net/http/index.js +1598 -58
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +1 -1
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.d.ts +1 -0
- package/dist/gs/os/exec.gs.js +4 -8
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/index.d.ts +1 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/proc.gs.d.ts +4 -0
- package/dist/gs/os/proc.gs.js +12 -6
- package/dist/gs/os/proc.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +1 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/path.js +11 -7
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +5 -4
- package/dist/gs/reflect/index.js +4 -3
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +15 -0
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +25 -6
- package/dist/gs/reflect/type.js +1475 -228
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +14 -6
- package/dist/gs/reflect/types.js +35 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.d.ts +1 -0
- package/dist/gs/reflect/value.js +83 -41
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +4 -140
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/pprof/index.d.ts +8 -2
- package/dist/gs/runtime/pprof/index.js +50 -30
- package/dist/gs/runtime/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.js +5 -4
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.js +5 -19
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/complex.gs.d.ts +3 -0
- package/dist/gs/strconv/complex.gs.js +148 -0
- package/dist/gs/strconv/complex.gs.js.map +1 -0
- package/dist/gs/strconv/index.d.ts +1 -0
- package/dist/gs/strconv/index.js +1 -0
- package/dist/gs/strconv/index.js.map +1 -1
- package/dist/gs/strings/builder.js +1 -1
- package/dist/gs/strings/reader.js +9 -5
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js +15 -7
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/strings/strings.d.ts +5 -0
- package/dist/gs/strings/strings.js +57 -5
- package/dist/gs/strings/strings.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js +9 -9
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js +2 -2
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/syscall/env.js +22 -14
- package/dist/gs/syscall/env.js.map +1 -1
- package/dist/gs/testing/testing.js +55 -13
- package/dist/gs/testing/testing.js.map +1 -1
- package/dist/gs/time/time.d.ts +24 -1
- package/dist/gs/time/time.js +43 -3
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unique/index.js +7 -1
- package/dist/gs/unique/index.js.map +1 -1
- package/go.mod +3 -3
- package/go.sum +16 -0
- package/gs/builtin/hostio.test.ts +16 -0
- package/gs/builtin/hostio.ts +7 -0
- package/gs/builtin/runtime-contract.test.ts +246 -21
- package/gs/builtin/slice.ts +269 -24
- package/gs/builtin/type.ts +226 -59
- package/gs/builtin/varRef.ts +85 -2
- package/gs/bytes/buffer.gs.ts +1 -1
- package/gs/bytes/reader.gs.ts +1 -1
- package/gs/compress/zlib/index.test.ts +62 -1
- package/gs/compress/zlib/index.ts +53 -16
- package/gs/compress/zlib/parity.json +51 -0
- package/gs/encoding/json/index.test.ts +360 -6
- package/gs/encoding/json/index.ts +679 -38
- package/gs/encoding/json/parity.json +81 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +373 -3
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +893 -1
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/github.com/pkg/errors/errors.ts +54 -30
- package/gs/go/scanner/index.test.ts +39 -56
- package/gs/go/scanner/index.ts +33 -5
- package/gs/go/scanner/parity.json +27 -0
- package/gs/go/token/index.ts +22 -6
- package/gs/hash/index.test.ts +20 -33
- package/gs/hash/index.ts +28 -0
- package/gs/hash/parity.json +21 -0
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/internal/goarch/index.test.ts +32 -0
- package/gs/internal/goarch/index.ts +45 -13
- package/gs/internal/goarch/parity.json +144 -0
- package/gs/io/fs/fs.ts +26 -14
- package/gs/io/fs/readdir.ts +4 -4
- package/gs/io/fs/sub.ts +8 -1
- package/gs/io/io.ts +1 -0
- package/gs/io/parity.json +162 -0
- package/gs/math/bits/index.test.ts +14 -1
- package/gs/math/bits/index.ts +23 -4
- package/gs/math/bits/parity.json +156 -0
- package/gs/mime/index.test.ts +90 -0
- package/gs/mime/index.ts +369 -6
- package/gs/mime/parity.json +36 -0
- package/gs/net/http/httptest/index.test.ts +98 -2
- package/gs/net/http/httptest/index.ts +101 -6
- package/gs/net/http/httptest/parity.json +15 -0
- package/gs/net/http/index.test.ts +781 -12
- package/gs/net/http/index.ts +1860 -139
- package/gs/net/http/meta.json +16 -1
- package/gs/net/http/parity.json +193 -0
- package/gs/os/dir_unix.gs.ts +1 -1
- package/gs/os/error.gs.ts +1 -1
- package/gs/os/exec.gs.ts +4 -8
- package/gs/os/exec_posix.gs.ts +1 -1
- package/gs/os/index.test.ts +9 -0
- package/gs/os/index.ts +1 -0
- package/gs/os/parity.json +9 -0
- package/gs/os/proc.gs.ts +18 -5
- package/gs/os/proc.test.ts +26 -0
- package/gs/os/root_js.gs.ts +1 -1
- package/gs/os/types.gs.ts +1 -1
- package/gs/os/types_js.gs.ts +1 -1
- package/gs/os/types_unix.gs.ts +1 -1
- package/gs/path/path.ts +11 -7
- package/gs/reflect/field.test.ts +37 -15
- package/gs/reflect/function-types.test.ts +518 -22
- package/gs/reflect/index.ts +8 -6
- package/gs/reflect/map.ts +20 -0
- package/gs/reflect/meta.json +6 -4
- package/gs/reflect/parity.json +234 -0
- package/gs/reflect/sliceat.test.ts +156 -0
- package/gs/reflect/structof.test.ts +401 -0
- package/gs/reflect/type.ts +1961 -317
- package/gs/reflect/typefor.test.ts +530 -10
- package/gs/reflect/types.ts +43 -18
- package/gs/reflect/value.ts +105 -45
- package/gs/reflect/visiblefields.ts +5 -168
- package/gs/runtime/parity.json +24 -0
- package/gs/runtime/pprof/index.test.ts +29 -7
- package/gs/runtime/pprof/index.ts +56 -30
- package/gs/runtime/pprof/parity.json +27 -0
- package/gs/runtime/runtime.test.ts +3 -1
- package/gs/runtime/runtime.ts +4 -3
- package/gs/runtime/trace/index.test.ts +5 -3
- package/gs/runtime/trace/index.ts +8 -20
- package/gs/runtime/trace/parity.json +36 -0
- package/gs/strconv/atoi.gs.ts +1 -1
- package/gs/strconv/complex.gs.ts +174 -0
- package/gs/strconv/complex.test.ts +65 -0
- package/gs/strconv/index.ts +1 -0
- package/gs/strconv/parity.json +120 -0
- package/gs/strings/builder.ts +1 -1
- package/gs/strings/parity.json +186 -0
- package/gs/strings/reader.ts +9 -5
- package/gs/strings/replace.ts +15 -7
- package/gs/strings/strings.test.ts +22 -2
- package/gs/strings/strings.ts +64 -6
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/gs/sync/atomic/type.gs.ts +9 -9
- package/gs/sync/atomic/value.gs.ts +2 -2
- package/gs/syscall/env.ts +29 -14
- package/gs/testing/testing.test.ts +67 -0
- package/gs/testing/testing.ts +87 -19
- package/gs/time/parity.json +225 -0
- package/gs/time/time.test.ts +20 -2
- package/gs/time/time.ts +49 -7
- package/gs/unique/index.ts +7 -1
- package/package.json +4 -2
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
- package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
|
@@ -4,9 +4,43 @@ import * as $ from '@goscript/builtin/index.js'
|
|
|
4
4
|
import * as bytes from '@goscript/bytes/index.js'
|
|
5
5
|
import * as io from '@goscript/io/index.js'
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BestCompression,
|
|
9
|
+
BestSpeed,
|
|
10
|
+
DefaultCompression,
|
|
11
|
+
ErrChecksum,
|
|
12
|
+
ErrDictionary,
|
|
13
|
+
HuffmanOnly,
|
|
14
|
+
NewReader,
|
|
15
|
+
NewReaderDict,
|
|
16
|
+
NewWriter,
|
|
17
|
+
NewWriterLevel,
|
|
18
|
+
NewWriterLevelDict,
|
|
19
|
+
NoCompression,
|
|
20
|
+
} from './index.js'
|
|
8
21
|
|
|
9
22
|
describe('compress/zlib override', () => {
|
|
23
|
+
test('exports flate compression level constants', () => {
|
|
24
|
+
expect(NoCompression).toBe(0)
|
|
25
|
+
expect(BestSpeed).toBe(1)
|
|
26
|
+
expect(BestCompression).toBe(9)
|
|
27
|
+
expect(DefaultCompression).toBe(-1)
|
|
28
|
+
expect(HuffmanOnly).toBe(-2)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('rejects invalid compression levels', () => {
|
|
32
|
+
const buf = $.markAsStructValue(new bytes.Buffer())
|
|
33
|
+
|
|
34
|
+
expect(NewWriterLevel(buf, HuffmanOnly)[1]).toBeNull()
|
|
35
|
+
expect(NewWriterLevel(buf, BestCompression)[1]).toBeNull()
|
|
36
|
+
expect(NewWriterLevel(buf, HuffmanOnly - 1)[1]?.Error()).toBe(
|
|
37
|
+
'zlib: invalid compression level: -3',
|
|
38
|
+
)
|
|
39
|
+
expect(NewWriterLevelDict(buf, BestCompression + 1, null)[1]?.Error()).toBe(
|
|
40
|
+
'zlib: invalid compression level: 10',
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
|
|
10
44
|
test('round trips bytes through writer and reader', async () => {
|
|
11
45
|
const input = $.stringToBytes('hello compressed world')
|
|
12
46
|
const buf = $.markAsStructValue(new bytes.Buffer())
|
|
@@ -82,6 +116,33 @@ describe('compress/zlib override', () => {
|
|
|
82
116
|
expect($.bytesToString(secondOut)).toBe('second stream')
|
|
83
117
|
})
|
|
84
118
|
|
|
119
|
+
test('writer and reader honor preset dictionaries', async () => {
|
|
120
|
+
const dict = $.stringToBytes('hello dictionary')
|
|
121
|
+
const compressed = $.markAsStructValue(new bytes.Buffer())
|
|
122
|
+
const [writer, writerErr] = NewWriterLevelDict(compressed, DefaultCompression, dict)
|
|
123
|
+
expect(writerErr).toBeNull()
|
|
124
|
+
expect(writer!.Write($.stringToBytes('hello dictionary payload'))[1]).toBeNull()
|
|
125
|
+
expect(await writer!.Close()).toBeNull()
|
|
126
|
+
|
|
127
|
+
const [missingDictReader, missingDictErr] = NewReader(bytes.NewReader(compressed.Bytes()))
|
|
128
|
+
expect(missingDictReader).toBeNull()
|
|
129
|
+
expect(missingDictErr).toBe(ErrDictionary)
|
|
130
|
+
|
|
131
|
+
const [reader, readerErr] = NewReaderDict(bytes.NewReader(compressed.Bytes()), dict)
|
|
132
|
+
expect(readerErr).toBeNull()
|
|
133
|
+
const [out, readErr] = await io.ReadAll(reader!)
|
|
134
|
+
expect(readErr).toBeNull()
|
|
135
|
+
expect($.bytesToString(out)).toBe('hello dictionary payload')
|
|
136
|
+
|
|
137
|
+
const [, wrongDictErr] = NewReaderDict(bytes.NewReader(compressed.Bytes()), $.stringToBytes('wrong dictionary'))
|
|
138
|
+
expect(wrongDictErr).toBe(ErrDictionary)
|
|
139
|
+
|
|
140
|
+
const corrupt = Uint8Array.from(compressed.Bytes())
|
|
141
|
+
corrupt[corrupt.length - 1] ^= 0xff
|
|
142
|
+
const [, corruptErr] = NewReaderDict(bytes.NewReader(corrupt), dict)
|
|
143
|
+
expect(corruptErr).toBe(ErrChecksum)
|
|
144
|
+
})
|
|
145
|
+
|
|
85
146
|
test('reader reset accepts async generated readers', async () => {
|
|
86
147
|
const compressed = $.markAsStructValue(new bytes.Buffer())
|
|
87
148
|
const writer = NewWriter(compressed)
|
|
@@ -10,10 +10,18 @@ type maybeAsyncWriter = {
|
|
|
10
10
|
Write(p: $.Bytes): [number, $.GoError] | Promise<[number, $.GoError]>
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export const NoCompression = 0
|
|
14
|
+
export const BestSpeed = 1
|
|
15
|
+
export const BestCompression = 9
|
|
16
|
+
export const DefaultCompression = -1
|
|
17
|
+
export const HuffmanOnly = -2
|
|
18
|
+
|
|
13
19
|
export let ErrChecksum = errors.New('zlib: invalid checksum')
|
|
14
20
|
export let ErrDictionary = errors.New('zlib: invalid dictionary')
|
|
15
21
|
export let ErrHeader = errors.New('zlib: invalid header')
|
|
16
22
|
|
|
23
|
+
type nodeZlibError = Error & { code?: unknown }
|
|
24
|
+
|
|
17
25
|
export function __goscript_set_ErrChecksum(value: $.GoError): void {
|
|
18
26
|
ErrChecksum = value
|
|
19
27
|
}
|
|
@@ -89,7 +97,11 @@ export class Writer {
|
|
|
89
97
|
private chunks: Uint8Array[] = []
|
|
90
98
|
private closed = false
|
|
91
99
|
|
|
92
|
-
constructor(
|
|
100
|
+
constructor(
|
|
101
|
+
private w: io.Writer | null,
|
|
102
|
+
private dict: $.Bytes | null = null,
|
|
103
|
+
private level = DefaultCompression,
|
|
104
|
+
) {}
|
|
93
105
|
|
|
94
106
|
Write(p: $.Bytes): [number, $.GoError] {
|
|
95
107
|
if (this.closed) {
|
|
@@ -108,7 +120,7 @@ export class Writer {
|
|
|
108
120
|
if (this.w == null) {
|
|
109
121
|
return errors.New('zlib: nil writer')
|
|
110
122
|
}
|
|
111
|
-
const compressed = deflate(concat(this.chunks))
|
|
123
|
+
const compressed = deflate(concat(this.chunks), this.dict, this.level)
|
|
112
124
|
const writer = $.pointerValue<maybeAsyncWriter>(this.w)
|
|
113
125
|
const [, err] = await writer.Write(compressed)
|
|
114
126
|
return err
|
|
@@ -131,17 +143,23 @@ export function NewWriter(w: io.Writer | null): Writer {
|
|
|
131
143
|
|
|
132
144
|
export function NewWriterLevel(
|
|
133
145
|
w: io.Writer | null,
|
|
134
|
-
|
|
146
|
+
level: number,
|
|
135
147
|
): [Writer | null, $.GoError] {
|
|
136
|
-
|
|
148
|
+
if (level < HuffmanOnly || level > BestCompression) {
|
|
149
|
+
return [null, errors.New(`zlib: invalid compression level: ${level}`)]
|
|
150
|
+
}
|
|
151
|
+
return [new Writer(w, null, level), null]
|
|
137
152
|
}
|
|
138
153
|
|
|
139
154
|
export function NewWriterLevelDict(
|
|
140
155
|
w: io.Writer | null,
|
|
141
|
-
|
|
156
|
+
level: number,
|
|
142
157
|
_dict: $.Bytes | null,
|
|
143
158
|
): [Writer | null, $.GoError] {
|
|
144
|
-
|
|
159
|
+
if (level < HuffmanOnly || level > BestCompression) {
|
|
160
|
+
return [null, errors.New(`zlib: invalid compression level: ${level}`)]
|
|
161
|
+
}
|
|
162
|
+
return [new Writer(w, _dict, level), null]
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
export function NewReader(
|
|
@@ -162,14 +180,25 @@ export function NewReaderDict(
|
|
|
162
180
|
return [reader as any, null]
|
|
163
181
|
}
|
|
164
182
|
|
|
165
|
-
function deflate(data: Uint8Array): Uint8Array {
|
|
183
|
+
function deflate(data: Uint8Array, dict: $.Bytes | null, level: number): Uint8Array {
|
|
166
184
|
const zlib = nodeZlib()
|
|
167
|
-
|
|
185
|
+
const opts: Record<string, unknown> = {}
|
|
186
|
+
if (dict != null && $.len(dict) > 0) {
|
|
187
|
+
opts.dictionary = $.bytesToUint8Array(dict)
|
|
188
|
+
}
|
|
189
|
+
if (level === HuffmanOnly) {
|
|
190
|
+
opts.strategy = zlib.constants?.Z_HUFFMAN_ONLY
|
|
191
|
+
opts.level = DefaultCompression
|
|
192
|
+
} else {
|
|
193
|
+
opts.level = level
|
|
194
|
+
}
|
|
195
|
+
return new Uint8Array(zlib.deflateSync(data, opts))
|
|
168
196
|
}
|
|
169
197
|
|
|
170
|
-
function inflate(data: Uint8Array): Uint8Array {
|
|
198
|
+
function inflate(data: Uint8Array, dict: $.Bytes | null): Uint8Array {
|
|
171
199
|
const zlib = nodeZlib()
|
|
172
|
-
|
|
200
|
+
const opts = dict != null && $.len(dict) > 0 ? { dictionary: $.bytesToUint8Array(dict) } : undefined
|
|
201
|
+
return new Uint8Array(zlib.inflateSync(data, opts))
|
|
173
202
|
}
|
|
174
203
|
|
|
175
204
|
function nodeZlib(): any {
|
|
@@ -266,13 +295,21 @@ function recordCompressedBytes(
|
|
|
266
295
|
}
|
|
267
296
|
const compressed = new Uint8Array(chunks)
|
|
268
297
|
try {
|
|
269
|
-
return { data: inflate(compressed), err: null }
|
|
270
|
-
} catch {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
298
|
+
return { data: inflate(compressed, dict), err: null }
|
|
299
|
+
} catch (err) {
|
|
300
|
+
return { data: null, err: classifyInflateError(err) }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function classifyInflateError(err: unknown): $.GoError {
|
|
305
|
+
const zerr = err instanceof Error ? err as nodeZlibError : null
|
|
306
|
+
if (zerr?.code === 'Z_NEED_DICT') {
|
|
307
|
+
return ErrDictionary
|
|
308
|
+
}
|
|
309
|
+
if (zerr?.code === 'Z_DATA_ERROR' && zerr.message.includes('incorrect data check')) {
|
|
310
|
+
return ErrChecksum
|
|
275
311
|
}
|
|
312
|
+
return ErrHeader
|
|
276
313
|
}
|
|
277
314
|
|
|
278
315
|
function concat(chunks: Uint8Array[]): Uint8Array {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"strict": true,
|
|
4
|
+
"symbols": {
|
|
5
|
+
"BestCompression": {
|
|
6
|
+
"status": "real"
|
|
7
|
+
},
|
|
8
|
+
"BestSpeed": {
|
|
9
|
+
"status": "real"
|
|
10
|
+
},
|
|
11
|
+
"DefaultCompression": {
|
|
12
|
+
"status": "real"
|
|
13
|
+
},
|
|
14
|
+
"ErrChecksum": {
|
|
15
|
+
"status": "real"
|
|
16
|
+
},
|
|
17
|
+
"ErrDictionary": {
|
|
18
|
+
"status": "real"
|
|
19
|
+
},
|
|
20
|
+
"ErrHeader": {
|
|
21
|
+
"status": "real"
|
|
22
|
+
},
|
|
23
|
+
"HuffmanOnly": {
|
|
24
|
+
"status": "real"
|
|
25
|
+
},
|
|
26
|
+
"NewReader": {
|
|
27
|
+
"status": "real"
|
|
28
|
+
},
|
|
29
|
+
"NewReaderDict": {
|
|
30
|
+
"status": "real"
|
|
31
|
+
},
|
|
32
|
+
"NewWriter": {
|
|
33
|
+
"status": "real"
|
|
34
|
+
},
|
|
35
|
+
"NewWriterLevel": {
|
|
36
|
+
"status": "real"
|
|
37
|
+
},
|
|
38
|
+
"NewWriterLevelDict": {
|
|
39
|
+
"status": "real"
|
|
40
|
+
},
|
|
41
|
+
"NoCompression": {
|
|
42
|
+
"status": "real"
|
|
43
|
+
},
|
|
44
|
+
"Resetter": {
|
|
45
|
+
"status": "real"
|
|
46
|
+
},
|
|
47
|
+
"Writer": {
|
|
48
|
+
"status": "real"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
import * as bytes from '@goscript/bytes/index.js'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Compact,
|
|
8
|
+
HTMLEscape,
|
|
9
|
+
Indent,
|
|
10
|
+
InvalidUTF8Error,
|
|
11
|
+
InvalidUnmarshalError,
|
|
12
|
+
MarshalerError,
|
|
13
|
+
Marshal,
|
|
14
|
+
MarshalIndent,
|
|
15
|
+
NewDecoder,
|
|
16
|
+
NewEncoder,
|
|
17
|
+
Number_Float64,
|
|
18
|
+
Number_Int64,
|
|
19
|
+
Number_String,
|
|
20
|
+
RawMessage_MarshalJSON,
|
|
21
|
+
RawMessage_UnmarshalJSON,
|
|
22
|
+
SyntaxError as JSONSyntaxError,
|
|
23
|
+
UnmarshalFieldError,
|
|
24
|
+
Unmarshal,
|
|
25
|
+
UnmarshalTypeError,
|
|
26
|
+
UnsupportedTypeError,
|
|
27
|
+
UnsupportedValueError,
|
|
28
|
+
Valid,
|
|
29
|
+
type Marshaler,
|
|
30
|
+
type Unmarshaler,
|
|
31
|
+
} from './index.js'
|
|
6
32
|
|
|
7
33
|
class Person {
|
|
8
34
|
public _fields = {
|
|
@@ -16,37 +42,92 @@ class Person {
|
|
|
16
42
|
new Person(),
|
|
17
43
|
[],
|
|
18
44
|
Person,
|
|
19
|
-
|
|
20
|
-
|
|
45
|
+
[
|
|
46
|
+
{
|
|
47
|
+
name: 'Name',
|
|
48
|
+
key: 'Name',
|
|
21
49
|
type: { kind: $.TypeKind.Basic, name: 'string' },
|
|
22
50
|
tag: 'json:"name"',
|
|
23
51
|
},
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
{
|
|
53
|
+
name: 'Age',
|
|
54
|
+
key: 'Age',
|
|
55
|
+
type: { kind: $.TypeKind.Basic, name: 'int' },
|
|
56
|
+
tag: 'json:"age"',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Active',
|
|
60
|
+
key: 'Active',
|
|
26
61
|
type: { kind: $.TypeKind.Basic, name: 'bool' },
|
|
27
62
|
tag: 'json:"active"',
|
|
28
63
|
},
|
|
29
|
-
|
|
64
|
+
],
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class FieldAlias {
|
|
69
|
+
public _fields = {
|
|
70
|
+
Name: $.varRef(''),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static __typeInfo = $.registerStructType(
|
|
74
|
+
'test.FieldAlias',
|
|
75
|
+
new FieldAlias(),
|
|
76
|
+
[],
|
|
77
|
+
FieldAlias,
|
|
78
|
+
[
|
|
79
|
+
{
|
|
80
|
+
name: 'FullName',
|
|
81
|
+
key: 'Name',
|
|
82
|
+
type: { kind: $.TypeKind.Basic, name: 'string' },
|
|
83
|
+
},
|
|
84
|
+
],
|
|
30
85
|
)
|
|
31
86
|
}
|
|
32
87
|
|
|
33
88
|
describe('encoding/json override', () => {
|
|
34
89
|
it('registers the Unmarshaler interface shape', () => {
|
|
90
|
+
class CustomMarshaler implements Marshaler {
|
|
91
|
+
MarshalJSON(): [$.Slice<number>, $.GoError] {
|
|
92
|
+
return [$.stringToBytes('{"ok":true}'), null]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
35
96
|
class CustomUnmarshaler implements Unmarshaler {
|
|
36
97
|
UnmarshalJSON(_data: $.Slice<number>): $.GoError {
|
|
37
98
|
return null
|
|
38
99
|
}
|
|
39
100
|
}
|
|
40
101
|
|
|
102
|
+
const [, marshalOK] = $.typeAssertTuple<Marshaler>(
|
|
103
|
+
new CustomMarshaler(),
|
|
104
|
+
'json.Marshaler',
|
|
105
|
+
)
|
|
41
106
|
const [value, ok] = $.typeAssertTuple<Unmarshaler>(
|
|
42
107
|
new CustomUnmarshaler(),
|
|
43
108
|
'json.Unmarshaler',
|
|
44
109
|
)
|
|
45
110
|
|
|
111
|
+
expect(marshalOK).toBe(true)
|
|
46
112
|
expect(ok).toBe(true)
|
|
47
113
|
expect(value.UnmarshalJSON($.stringToBytes('{}'))).toBeNull()
|
|
48
114
|
})
|
|
49
115
|
|
|
116
|
+
it('exposes JSON error structs as Go errors', () => {
|
|
117
|
+
for (const err of [
|
|
118
|
+
new JSONSyntaxError(),
|
|
119
|
+
new InvalidUTF8Error({ S: 'bad' }),
|
|
120
|
+
new InvalidUnmarshalError(),
|
|
121
|
+
new MarshalerError({ Err: $.newError('bad marshal') }),
|
|
122
|
+
new UnmarshalFieldError({ Key: 'field' }),
|
|
123
|
+
new UnmarshalTypeError({ Value: 'string' }),
|
|
124
|
+
new UnsupportedTypeError(),
|
|
125
|
+
new UnsupportedValueError({ Str: 'NaN' }),
|
|
126
|
+
]) {
|
|
127
|
+
expect(err.Error()).toBe(err.message)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
50
131
|
it('marshals struct fields through json tags', () => {
|
|
51
132
|
const person = new Person()
|
|
52
133
|
person._fields.Name.value = 'Alice'
|
|
@@ -61,6 +142,187 @@ describe('encoding/json override', () => {
|
|
|
61
142
|
)
|
|
62
143
|
})
|
|
63
144
|
|
|
145
|
+
it('uses descriptor names separately from storage keys', () => {
|
|
146
|
+
const alias = new FieldAlias()
|
|
147
|
+
alias._fields.Name.value = 'Ada'
|
|
148
|
+
|
|
149
|
+
const [data, err] = Marshal(alias)
|
|
150
|
+
expect(err).toBeNull()
|
|
151
|
+
expect($.bytesToString(data)).toBe('{"FullName":"Ada"}')
|
|
152
|
+
|
|
153
|
+
const target = $.varRef(new FieldAlias())
|
|
154
|
+
expect(
|
|
155
|
+
Unmarshal($.stringToBytes('{"FullName":"Grace"}'), target),
|
|
156
|
+
).toBeNull()
|
|
157
|
+
expect(target.value._fields.Name.value).toBe('Grace')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('rejects unsupported values and invalid unmarshal targets', () => {
|
|
161
|
+
const [nanData, nanErr] = Marshal(Number.NaN)
|
|
162
|
+
expect(nanData).toBeNull()
|
|
163
|
+
expect(nanErr).toBeInstanceOf(UnsupportedValueError)
|
|
164
|
+
|
|
165
|
+
const [htmlData, htmlErr] = Marshal(new Map([['text', '<tag>&']]))
|
|
166
|
+
expect(htmlErr).toBeNull()
|
|
167
|
+
expect($.bytesToString(htmlData)).toBe(
|
|
168
|
+
'{"text":"\\u003ctag\\u003e\\u0026"}',
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
expect(Unmarshal($.stringToBytes('{}'), null)).toBeInstanceOf(
|
|
172
|
+
InvalidUnmarshalError,
|
|
173
|
+
)
|
|
174
|
+
expect(Unmarshal($.stringToBytes('{}'), 1)).toBeInstanceOf(
|
|
175
|
+
InvalidUnmarshalError,
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('uses custom marshal and unmarshal hooks', () => {
|
|
180
|
+
class CustomJSON implements Marshaler, Unmarshaler {
|
|
181
|
+
public Text = ''
|
|
182
|
+
|
|
183
|
+
MarshalJSON(): [$.Slice<number>, $.GoError] {
|
|
184
|
+
return [$.stringToBytes('{"hook":true}'), null]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
UnmarshalJSON(data: $.Slice<number>): $.GoError {
|
|
188
|
+
this.Text = $.bytesToString(data)
|
|
189
|
+
return null
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class RawEnvelope {
|
|
194
|
+
public _fields = {
|
|
195
|
+
Raw: $.varRef($.stringToBytes('{"embedded":true}')),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static __typeInfo = $.registerStructType(
|
|
199
|
+
'test.RawEnvelope',
|
|
200
|
+
new RawEnvelope(),
|
|
201
|
+
[],
|
|
202
|
+
RawEnvelope,
|
|
203
|
+
[
|
|
204
|
+
{
|
|
205
|
+
name: 'Raw',
|
|
206
|
+
key: 'Raw',
|
|
207
|
+
type: 'json.RawMessage',
|
|
208
|
+
tag: 'json:"raw"',
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
class Envelope {
|
|
215
|
+
public _fields = {
|
|
216
|
+
Person: $.varRef(new Person()),
|
|
217
|
+
Data: $.varRef(new Uint8Array(0)),
|
|
218
|
+
Raw: $.varRef(new Uint8Array(0)),
|
|
219
|
+
Hook: $.varRef(new CustomJSON()),
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
static __typeInfo = $.registerStructType(
|
|
223
|
+
'test.Envelope',
|
|
224
|
+
new Envelope(),
|
|
225
|
+
[],
|
|
226
|
+
Envelope,
|
|
227
|
+
[
|
|
228
|
+
{
|
|
229
|
+
name: 'Person',
|
|
230
|
+
key: 'Person',
|
|
231
|
+
type: { kind: $.TypeKind.Struct, name: 'test.Person' },
|
|
232
|
+
tag: 'json:"person"',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'Data',
|
|
236
|
+
key: 'Data',
|
|
237
|
+
type: {
|
|
238
|
+
kind: $.TypeKind.Slice,
|
|
239
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
240
|
+
},
|
|
241
|
+
tag: 'json:"data"',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'Raw',
|
|
245
|
+
key: 'Raw',
|
|
246
|
+
type: {
|
|
247
|
+
kind: $.TypeKind.Slice,
|
|
248
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
249
|
+
typeName: 'json.RawMessage',
|
|
250
|
+
},
|
|
251
|
+
tag: 'json:"raw"',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'Hook',
|
|
255
|
+
key: 'Hook',
|
|
256
|
+
type: { kind: $.TypeKind.Struct, name: 'test.CustomJSON' },
|
|
257
|
+
tag: 'json:"hook"',
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const [data, err] = Marshal(new CustomJSON())
|
|
264
|
+
expect(err).toBeNull()
|
|
265
|
+
expect($.bytesToString(data)).toBe('{"hook":true}')
|
|
266
|
+
|
|
267
|
+
const [indented, indentErr] = MarshalIndent(new CustomJSON(), '', ' ')
|
|
268
|
+
expect(indentErr).toBeNull()
|
|
269
|
+
expect($.bytesToString(indented)).toBe('{\n "hook": true\n}')
|
|
270
|
+
|
|
271
|
+
const target = $.varRef(new CustomJSON())
|
|
272
|
+
expect(Unmarshal($.stringToBytes('{"input":1}'), target)).toBeNull()
|
|
273
|
+
expect(target.value.Text).toBe('{"input":1}')
|
|
274
|
+
|
|
275
|
+
const raw = $.namedValueInterfaceValue<unknown>(
|
|
276
|
+
$.stringToBytes('{"raw":true}'),
|
|
277
|
+
'json.RawMessage',
|
|
278
|
+
{ MarshalJSON: RawMessage_MarshalJSON },
|
|
279
|
+
)
|
|
280
|
+
const [rawData, rawErr] = Marshal(raw)
|
|
281
|
+
expect(rawErr).toBeNull()
|
|
282
|
+
expect($.bytesToString(rawData)).toBe('{"raw":true}')
|
|
283
|
+
|
|
284
|
+
const [byteData, byteErr] = Marshal($.stringToBytes('x'))
|
|
285
|
+
expect(byteErr).toBeNull()
|
|
286
|
+
expect($.bytesToString(byteData)).toBe('"eA=="')
|
|
287
|
+
|
|
288
|
+
const [rawEnvelopeData, rawEnvelopeErr] = Marshal(new RawEnvelope())
|
|
289
|
+
expect(rawEnvelopeErr).toBeNull()
|
|
290
|
+
expect($.bytesToString(rawEnvelopeData)).toBe('{"raw":{"embedded":true}}')
|
|
291
|
+
|
|
292
|
+
const rawEnvelopeTarget = $.varRef(new RawEnvelope())
|
|
293
|
+
expect(
|
|
294
|
+
Unmarshal($.stringToBytes('{"raw":{"next":1}}'), rawEnvelopeTarget),
|
|
295
|
+
).toBeNull()
|
|
296
|
+
expect($.bytesToString(rawEnvelopeTarget.value._fields.Raw.value)).toBe(
|
|
297
|
+
'{"next":1}',
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
const byteTarget = $.varRef(new Uint8Array(0))
|
|
301
|
+
expect(Unmarshal($.stringToBytes('"eA=="'), byteTarget)).toBeNull()
|
|
302
|
+
expect($.bytesToString(byteTarget.value)).toBe('x')
|
|
303
|
+
|
|
304
|
+
const envelope = $.varRef(new Envelope())
|
|
305
|
+
expect(
|
|
306
|
+
Unmarshal(
|
|
307
|
+
$.stringToBytes(
|
|
308
|
+
'{"person":{"name":"Eve","age":31},"data":"eA==","raw":{"keep":true},"hook":{"nested":true}}',
|
|
309
|
+
),
|
|
310
|
+
envelope,
|
|
311
|
+
),
|
|
312
|
+
).toBeNull()
|
|
313
|
+
expect(envelope.value._fields.Person.value._fields.Name.value).toBe('Eve')
|
|
314
|
+
expect(envelope.value._fields.Person.value._fields.Age.value).toBe(31)
|
|
315
|
+
expect($.bytesToString(envelope.value._fields.Data.value)).toBe('x')
|
|
316
|
+
expect($.bytesToString(envelope.value._fields.Raw.value)).toBe(
|
|
317
|
+
'{"keep":true}',
|
|
318
|
+
)
|
|
319
|
+
expect(envelope.value._fields.Hook.value.Text).toBe('{"nested":true}')
|
|
320
|
+
|
|
321
|
+
const [envelopeData, envelopeErr] = Marshal(envelope.value)
|
|
322
|
+
expect(envelopeErr).toBeNull()
|
|
323
|
+
expect($.bytesToString(envelopeData)).toContain('"raw":{"keep":true}')
|
|
324
|
+
})
|
|
325
|
+
|
|
64
326
|
it('marshals indented JSON with a line prefix', () => {
|
|
65
327
|
const person = new Person()
|
|
66
328
|
person._fields.Name.value = 'Alice'
|
|
@@ -98,4 +360,96 @@ describe('encoding/json override', () => {
|
|
|
98
360
|
expect(mapRef.value?.get('age')).toBe(22)
|
|
99
361
|
expect(mapRef.value?.get('active')).toBe(true)
|
|
100
362
|
})
|
|
363
|
+
|
|
364
|
+
it('encodes JSON to an io.Writer with newline, indentation, and HTML escaping', () => {
|
|
365
|
+
const chunks: string[] = []
|
|
366
|
+
const writer = {
|
|
367
|
+
Write(p: $.Bytes): [number, $.GoError] {
|
|
368
|
+
chunks.push($.bytesToString(p))
|
|
369
|
+
return [$.len(p), null]
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const encoder = NewEncoder(writer)
|
|
374
|
+
expect(encoder.Encode(new Map([['text', '<tag>&']]))).toBeNull()
|
|
375
|
+
encoder.SetEscapeHTML(false)
|
|
376
|
+
encoder.SetIndent('', ' ')
|
|
377
|
+
expect(encoder.Encode(new Map([['text', '<tag>&']]))).toBeNull()
|
|
378
|
+
|
|
379
|
+
expect(chunks).toEqual([
|
|
380
|
+
'{"text":"\\u003ctag\\u003e\\u0026"}\n',
|
|
381
|
+
'{\n "text": "<tag>&"\n}\n',
|
|
382
|
+
])
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
it('validates, compacts, indents, and escapes JSON bytes', () => {
|
|
386
|
+
expect(Valid($.stringToBytes('{"ok":true}'))).toBe(true)
|
|
387
|
+
expect(Valid($.stringToBytes('{'))).toBe(false)
|
|
388
|
+
|
|
389
|
+
const compact = new bytes.Buffer()
|
|
390
|
+
expect(Compact(compact, $.stringToBytes('{ "ok" : true }'))).toBeNull()
|
|
391
|
+
expect(compact.String()).toBe('{"ok":true}')
|
|
392
|
+
|
|
393
|
+
const indented = new bytes.Buffer()
|
|
394
|
+
expect(
|
|
395
|
+
Indent(indented, $.stringToBytes('{"ok":true}'), '> ', ' '),
|
|
396
|
+
).toBeNull()
|
|
397
|
+
expect(indented.String()).toBe('{\n> "ok": true\n> }')
|
|
398
|
+
|
|
399
|
+
const escaped = new bytes.Buffer()
|
|
400
|
+
HTMLEscape(escaped, $.stringToBytes('"<tag>&"'))
|
|
401
|
+
expect(escaped.String()).toBe('"\\u003ctag\\u003e\\u0026"')
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('decodes from readers and exposes raw message and number helpers', () => {
|
|
405
|
+
const reader = bytes.NewBufferString('{"name":"Dana","age":28}')!
|
|
406
|
+
const decoder = NewDecoder(reader)
|
|
407
|
+
const target = $.varRef(new Person())
|
|
408
|
+
|
|
409
|
+
expect(decoder.Decode(target)).toBeNull()
|
|
410
|
+
expect(target.value._fields.Name.value).toBe('Dana')
|
|
411
|
+
expect(target.value._fields.Age.value).toBe(28)
|
|
412
|
+
expect(decoder.InputOffset()).toBeGreaterThan(0)
|
|
413
|
+
|
|
414
|
+
const raw = $.stringToBytes('{"raw":true}')
|
|
415
|
+
const [marshaled, marshalErr] = RawMessage_MarshalJSON(raw)
|
|
416
|
+
expect(marshalErr).toBeNull()
|
|
417
|
+
expect($.bytesToString(marshaled)).toBe('{"raw":true}')
|
|
418
|
+
|
|
419
|
+
const rawRef = $.varRef<$.Bytes>(null)
|
|
420
|
+
expect(
|
|
421
|
+
RawMessage_UnmarshalJSON(rawRef, $.stringToBytes('[1,2]')),
|
|
422
|
+
).toBeNull()
|
|
423
|
+
expect($.bytesToString(rawRef.value)).toBe('[1,2]')
|
|
424
|
+
|
|
425
|
+
expect(Number_String('42')).toBe('42')
|
|
426
|
+
expect(Number_Int64('42')).toEqual([42, null])
|
|
427
|
+
expect(Number_Float64('3.5')).toEqual([3.5, null])
|
|
428
|
+
expect(Number.isNaN(Number_Float64('NaN')[0])).toBe(true)
|
|
429
|
+
expect(Number_Float64('-Inf')).toEqual([-Infinity, null])
|
|
430
|
+
expect(Number_Float64('1x')[1]?.Error()).toContain('invalid syntax')
|
|
431
|
+
expect(Number_Float64('1e999')[1]?.Error()).toContain('value out of range')
|
|
432
|
+
expect(Number_Int64('9223372036854775808')[1]?.Error()).toContain(
|
|
433
|
+
'value out of range',
|
|
434
|
+
)
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
it('applies decoder UseNumber and DisallowUnknownFields options', () => {
|
|
438
|
+
const numberReader = bytes.NewBufferString('{"n":12}')!
|
|
439
|
+
const numberDecoder = NewDecoder(numberReader)
|
|
440
|
+
numberDecoder.UseNumber()
|
|
441
|
+
const numberTarget = $.varRef<Map<string, unknown> | null>(null)
|
|
442
|
+
|
|
443
|
+
expect(numberDecoder.Decode(numberTarget)).toBeNull()
|
|
444
|
+
expect(numberTarget.value?.get('n')).toBe('12')
|
|
445
|
+
|
|
446
|
+
const strictReader = bytes.NewBufferString('{"name":"Ada","extra":true}')!
|
|
447
|
+
const strictDecoder = NewDecoder(strictReader)
|
|
448
|
+
strictDecoder.DisallowUnknownFields()
|
|
449
|
+
const strictTarget = $.varRef(new Person())
|
|
450
|
+
|
|
451
|
+
expect(strictDecoder.Decode(strictTarget)?.Error()).toBe(
|
|
452
|
+
'json: unknown field "extra"',
|
|
453
|
+
)
|
|
454
|
+
})
|
|
101
455
|
})
|