@kaiko.io/rescript-deser 7.0.0-alpha.1 → 7.0.0-alpha.2
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 +13 -2
- package/lib/bs/.compiler.log +2 -2
- package/lib/bs/build.ninja +0 -27
- package/lib/bs/compiler-info.json +8 -0
- package/lib/bs/src/Deser.ast +0 -0
- package/lib/bs/src/Deser.cmi +0 -0
- package/lib/bs/src/Deser.cmj +0 -0
- package/lib/bs/src/Deser.cmt +0 -0
- package/lib/bs/{___incremental → src}/Deser.res +19 -29
- package/lib/bs/tests/QUnit.ast +0 -0
- package/lib/bs/tests/QUnit.cmi +0 -0
- package/lib/bs/tests/QUnit.cmj +0 -0
- package/lib/bs/tests/QUnit.cmt +0 -0
- package/lib/bs/tests/QUnit.res +72 -0
- package/lib/bs/tests/index.ast +0 -0
- package/lib/bs/tests/index.cmi +0 -0
- package/lib/bs/tests/index.cmj +0 -0
- package/lib/bs/tests/index.cmt +0 -0
- package/lib/bs/{___incremental → tests}/index.res +24 -1
- package/lib/es6/src/Deser.js +297 -365
- package/lib/es6/tests/index.js +260 -241
- package/lib/js/src/Deser.js +293 -361
- package/lib/js/tests/index.js +261 -242
- package/lib/ocaml/.compiler.log +2 -0
- package/lib/ocaml/Deser.ast +0 -0
- package/lib/ocaml/Deser.cmi +0 -0
- package/lib/ocaml/Deser.cmj +0 -0
- package/lib/ocaml/Deser.cmt +0 -0
- package/lib/ocaml/Deser.res +341 -0
- package/lib/ocaml/QUnit.ast +0 -0
- package/lib/ocaml/QUnit.cmi +0 -0
- package/lib/ocaml/QUnit.cmj +0 -0
- package/lib/ocaml/QUnit.cmt +0 -0
- package/lib/ocaml/QUnit.res +72 -0
- package/lib/ocaml/index.ast +0 -0
- package/lib/ocaml/index.cmi +0 -0
- package/lib/ocaml/index.cmj +0 -0
- package/lib/ocaml/index.cmt +0 -0
- package/lib/ocaml/index.res +212 -0
- package/lib/rescript.lock +1 -0
- package/package.json +4 -6
- package/rescript.json +4 -6
- package/src/Deser.res +19 -29
- package/tests/QUnit.res +4 -4
- package/tests/index.res +24 -1
- package/yarn.lock +683 -0
- package/lib/bs/.bsbuild +0 -0
- package/lib/bs/.bsdeps +0 -9
- package/lib/bs/.ninja_log +0 -57
- package/lib/bs/.project-files-cache +0 -0
- package/lib/bs/.sourcedirs.json +0 -1
- package/lib/bs/___incremental/Deser.cmi +0 -0
- package/lib/bs/___incremental/Deser.cmj +0 -0
- package/lib/bs/___incremental/Deser.cmt +0 -0
- package/lib/bs/___incremental/index.cmi +0 -0
- package/lib/bs/___incremental/index.cmj +0 -0
- package/lib/bs/___incremental/index.cmt +0 -0
- package/lib/bs/install.ninja +0 -10
- package/lib/bs/src/Deser.d +0 -0
- package/lib/bs/tests/QUnit.d +0 -0
- package/lib/bs/tests/index.d +0 -1
package/README.md
CHANGED
|
@@ -104,8 +104,7 @@ module type Serializable = {
|
|
|
104
104
|
`Optional`, `OptionalWithDefault`.
|
|
105
105
|
|
|
106
106
|
- `Mapping(Field.t)`, parses a JSON object with unknown keys (of type string)
|
|
107
|
-
and a given type of value. Valid values have the internal type
|
|
108
|
-
`Prelude.Dict.t`.
|
|
107
|
+
and a given type of value. Valid values have the internal type `dict<_>`.
|
|
109
108
|
|
|
110
109
|
- `Deserializer(module(Deserializer))`, parses an JSON object with the function
|
|
111
110
|
`fromJSON` of another deserializer. This allows the composition of
|
|
@@ -136,6 +135,18 @@ type rec Node<'t> = Leave('t) | (Branch(array<Node<'t>>)
|
|
|
136
135
|
Cannot be automatically deserialized.
|
|
137
136
|
|
|
138
137
|
|
|
138
|
+
# Type safety disclaimer
|
|
139
|
+
|
|
140
|
+
`Deser` cannot guarantee type safety for ill-defined deserializers, the following deserializer will accept JSON payloads of the wrong type that can cause runtime type errors:
|
|
141
|
+
|
|
142
|
+
```rescript
|
|
143
|
+
module X = Deser.MakeDeserializer({
|
|
144
|
+
type t = string
|
|
145
|
+
let fields = Deser.Field.Array(Int)
|
|
146
|
+
})
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
|
|
139
150
|
## License
|
|
140
151
|
|
|
141
152
|
The MIT License
|
package/lib/bs/.compiler.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#Start(
|
|
2
|
-
#Done(
|
|
1
|
+
#Start(1766178477130)
|
|
2
|
+
#Done(1766178477131)
|
package/lib/bs/build.ninja
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
rescript = 1
|
|
2
|
-
g_finger := /home/manu/src/kaiko/rescript-deser/node_modules/@rescript/core/lib/ocaml/install.stamp
|
|
3
|
-
rule astj
|
|
4
|
-
command = /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/bsc.exe -warn-error +8+11+26+33+56 -bs-v 11.1.4 -uncurried -absname -bs-ast -o $out $i
|
|
5
|
-
o tests/index.ast : astj ../../tests/index.res
|
|
6
|
-
rule deps_dev
|
|
7
|
-
command = /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/bsb_helper.exe -g -hash 9a7a4be86983dcab705bb67a1ec483c7 $in
|
|
8
|
-
restat = 1
|
|
9
|
-
o tests/index.d : deps_dev tests/index.ast
|
|
10
|
-
rule mij_dev
|
|
11
|
-
command = /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/bsc.exe -I tests -I src/ -I /home/manu/src/kaiko/rescript-deser/node_modules/@rescript/core/lib/ocaml -warn-error +8+11+26+33+56 -uncurried -bs-package-name @kaiko.io/rescript-deser -bs-package-output commonjs:lib/js/$in_d:.js -bs-package-output esmodule:lib/es6/$in_d:.js -bs-v $g_finger $i
|
|
12
|
-
dyndep = 1
|
|
13
|
-
restat = 1
|
|
14
|
-
o tests/index.cmj tests/index.cmi ../es6/tests/index.js ../js/tests/index.js : mij_dev tests/index.ast
|
|
15
|
-
o tests/QUnit.ast : astj ../../tests/QUnit.res
|
|
16
|
-
o tests/QUnit.d : deps_dev tests/QUnit.ast
|
|
17
|
-
o tests/QUnit.cmj tests/QUnit.cmi ../es6/tests/QUnit.js ../js/tests/QUnit.js : mij_dev tests/QUnit.ast
|
|
18
|
-
o src/Deser.ast : astj ../../src/Deser.res
|
|
19
|
-
rule deps
|
|
20
|
-
command = /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/bsb_helper.exe -hash 9a7a4be86983dcab705bb67a1ec483c7 $in
|
|
21
|
-
restat = 1
|
|
22
|
-
o src/Deser.d : deps src/Deser.ast
|
|
23
|
-
rule mij
|
|
24
|
-
command = /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/bsc.exe -I src/ -I /home/manu/src/kaiko/rescript-deser/node_modules/@rescript/core/lib/ocaml -warn-error +8+11+26+33+56 -uncurried -bs-package-name @kaiko.io/rescript-deser -bs-package-output commonjs:lib/js/$in_d:.js -bs-package-output esmodule:lib/es6/$in_d:.js -bs-v $g_finger $i
|
|
25
|
-
dyndep = 1
|
|
26
|
-
restat = 1
|
|
27
|
-
o src/Deser.cmj src/Deser.cmi ../es6/src/Deser.js ../js/src/Deser.js : mij src/Deser.ast
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "12.0.1",
|
|
3
|
+
"bsc_path": "/home/manu/src/kaiko/rescript-deser/node_modules/@rescript/linux-x64/bin/bsc.exe",
|
|
4
|
+
"bsc_hash": "a2b93197b8c05fc70981fe131a9ed75f8462a9f615f71055f306c9deb058d3cf",
|
|
5
|
+
"rescript_config_hash": "6e13fe97e162573c9ab73327567294583b34d549775f8391e1d5c8ccc0c5d580",
|
|
6
|
+
"runtime_path": "/home/manu/src/kaiko/rescript-deser/node_modules/@rescript/runtime",
|
|
7
|
+
"generated_at": "1766178477132"
|
|
8
|
+
}
|
package/lib/bs/src/Deser.ast
CHANGED
|
Binary file
|
package/lib/bs/src/Deser.cmi
CHANGED
|
Binary file
|
package/lib/bs/src/Deser.cmj
CHANGED
|
Binary file
|
package/lib/bs/src/Deser.cmt
CHANGED
|
Binary file
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
open RescriptCore
|
|
2
|
-
|
|
3
1
|
module FieldValue = {
|
|
4
2
|
type t
|
|
5
3
|
external string: string => t = "%identity"
|
|
@@ -7,8 +5,8 @@ module FieldValue = {
|
|
|
7
5
|
external float: float => t = "%identity"
|
|
8
6
|
external boolean: bool => t = "%identity"
|
|
9
7
|
external array: array<t> => t = "%identity"
|
|
10
|
-
external object:
|
|
11
|
-
external mapping:
|
|
8
|
+
external object: dict<t> => t = "%identity"
|
|
9
|
+
external mapping: dict<t> => t = "%identity"
|
|
12
10
|
external any: 'a => t = "%identity"
|
|
13
11
|
@val external null: t = "undefined"
|
|
14
12
|
|
|
@@ -46,7 +44,6 @@ module Field = {
|
|
|
46
44
|
// We also allow floats and then use Date.fromTime
|
|
47
45
|
| Date
|
|
48
46
|
| Datetime // alias of Date
|
|
49
|
-
|
|
50
47
|
| Tuple(array<t>)
|
|
51
48
|
| Object(array<(string, t)>)
|
|
52
49
|
| Optional(t)
|
|
@@ -55,21 +52,17 @@ module Field = {
|
|
|
55
52
|
// difference with Object, is that you don't know the names of the
|
|
56
53
|
// expected entries.
|
|
57
54
|
| Mapping(t)
|
|
58
|
-
|
|
59
55
|
| Deserializer(module(Deserializer))
|
|
60
|
-
|
|
61
56
|
// A specialized Array of deserialized items that ignores unparsable
|
|
62
57
|
// items and returns the valid collection. This saves the user from
|
|
63
58
|
// writing 'Array(DefaultWhenInvalid(Optional(Deserializer(module(M)))))'
|
|
64
59
|
// and then post-process the list of items with 'Array.keepSome'
|
|
65
60
|
| Collection(module(Deserializer))
|
|
66
61
|
| DefaultWhenInvalid(t, FieldValue.t)
|
|
67
|
-
|
|
68
62
|
// FIXME: this is used to add additional restrictions like variadictInt or
|
|
69
63
|
// variadicString; but I find it too type-unsafe. I might consider having
|
|
70
64
|
// a Constraints for this in the future.
|
|
71
65
|
| Morphism(t, FieldValue.t => FieldValue.t)
|
|
72
|
-
|
|
73
66
|
| Self
|
|
74
67
|
|
|
75
68
|
let usingString = (f: string => 'a) => value => value->FieldValue.asString->f->FieldValue.any
|
|
@@ -85,7 +78,7 @@ module Field = {
|
|
|
85
78
|
switch i->fromJs {
|
|
86
79
|
| Some(internalValue) => internalValue
|
|
87
80
|
| None =>
|
|
88
|
-
|
|
81
|
+
throw(TypeError(`This Int(${i->Int.toString}) not a valid value here. Hint: ${hint}`))
|
|
89
82
|
}
|
|
90
83
|
}),
|
|
91
84
|
)
|
|
@@ -94,7 +87,7 @@ module Field = {
|
|
|
94
87
|
usingString(i => {
|
|
95
88
|
switch i->fromJs {
|
|
96
89
|
| Some(internalValue) => internalValue
|
|
97
|
-
| None =>
|
|
90
|
+
| None => throw(TypeError(`This String("${i}") not a valid value here. Hint: ${hint}`))
|
|
98
91
|
}
|
|
99
92
|
}),
|
|
100
93
|
)
|
|
@@ -147,12 +140,7 @@ module Field = {
|
|
|
147
140
|
}
|
|
148
141
|
}
|
|
149
142
|
|
|
150
|
-
let rec extractValue = (
|
|
151
|
-
values: Dict.t<JSON.t>,
|
|
152
|
-
field: string,
|
|
153
|
-
shape: t,
|
|
154
|
-
self: t,
|
|
155
|
-
): FieldValue.t => {
|
|
143
|
+
let rec extractValue = (values: dict<JSON.t>, field: string, shape: t, self: t): FieldValue.t => {
|
|
156
144
|
switch values->Dict.get(field) {
|
|
157
145
|
| Some(value) => value->fromUntagged(shape, self)
|
|
158
146
|
| None =>
|
|
@@ -160,7 +148,7 @@ module Field = {
|
|
|
160
148
|
| DefaultWhenInvalid(_, _) => JSON.Null->fromUntagged(shape, self)
|
|
161
149
|
| Optional(_) => JSON.Null->fromUntagged(shape, self)
|
|
162
150
|
| OptionalWithDefault(_, default) => default
|
|
163
|
-
| _ =>
|
|
151
|
+
| _ => throw(TypeError(`Missing non-optional field '${field}'`))
|
|
164
152
|
}
|
|
165
153
|
}
|
|
166
154
|
}
|
|
@@ -170,7 +158,7 @@ module Field = {
|
|
|
170
158
|
|
|
171
159
|
| (Literal(expected), String(text)) if text == expected => FieldValue.string(text)
|
|
172
160
|
| (Literal(expected), String(text)) =>
|
|
173
|
-
|
|
161
|
+
throw(TypeError(`Expecting literal ${expected}, got ${text}`))
|
|
174
162
|
|
|
175
163
|
| (String, String(text)) => FieldValue.string(text)
|
|
176
164
|
|
|
@@ -187,7 +175,7 @@ module Field = {
|
|
|
187
175
|
let values = Belt.Array.zipBy(items, bases, (i, b) => fromUntagged(i, b, self))
|
|
188
176
|
values->FieldValue.array
|
|
189
177
|
} else {
|
|
190
|
-
|
|
178
|
+
throw(
|
|
191
179
|
TypeError(`Expecting ${lenbases->Int.toString} items, got ${lenitems->Int.toString}`),
|
|
192
180
|
)
|
|
193
181
|
}
|
|
@@ -196,14 +184,14 @@ module Field = {
|
|
|
196
184
|
| (Datetime | Date, String(s)) =>
|
|
197
185
|
let r = Date.fromString(s)
|
|
198
186
|
if r->Date.getDate->Int.toFloat->Float.isNaN {
|
|
199
|
-
|
|
187
|
+
throw(TypeError(`Invalid date ${s}`))
|
|
200
188
|
}
|
|
201
189
|
r->FieldValue.any
|
|
202
190
|
|
|
203
191
|
| (Datetime | Date, Number(f)) =>
|
|
204
192
|
let r = Date.fromTime(f)
|
|
205
193
|
if r->Date.getDate->Int.toFloat->Float.isNaN {
|
|
206
|
-
|
|
194
|
+
throw(TypeError(`Invalid date ${f->Float.toString}`))
|
|
207
195
|
}
|
|
208
196
|
r->FieldValue.any
|
|
209
197
|
|
|
@@ -218,7 +206,7 @@ module Field = {
|
|
|
218
206
|
->Array.map(((field, shape)) => {
|
|
219
207
|
let value = switch extractValue(values, field, shape, self) {
|
|
220
208
|
| value => value
|
|
221
|
-
| exception TypeError(msg) =>
|
|
209
|
+
| exception TypeError(msg) => throw(TypeError(`Field "${field}": ${msg}`))
|
|
222
210
|
}
|
|
223
211
|
(field, value)
|
|
224
212
|
})
|
|
@@ -251,7 +239,7 @@ module Field = {
|
|
|
251
239
|
module M = unpack(m: Deserializer)
|
|
252
240
|
switch untagged->M.fromJSON {
|
|
253
241
|
| Ok(res) => res->FieldValue.any
|
|
254
|
-
| Error(msg) =>
|
|
242
|
+
| Error(msg) => throw(TypeError(msg))
|
|
255
243
|
}
|
|
256
244
|
}
|
|
257
245
|
|
|
@@ -265,7 +253,7 @@ module Field = {
|
|
|
265
253
|
}
|
|
266
254
|
| (Self, _) => untagged->fromUntagged(self, self)
|
|
267
255
|
| (expected, actual) =>
|
|
268
|
-
|
|
256
|
+
throw(TypeError(`Expected ${expected->toString}, but got ${actual->_taggedToString} instead`))
|
|
269
257
|
}
|
|
270
258
|
}
|
|
271
259
|
|
|
@@ -295,8 +283,9 @@ module Field = {
|
|
|
295
283
|
|
|
296
284
|
| (Object(fields), optional) =>
|
|
297
285
|
fields
|
|
298
|
-
->Array.map(((fieldName, field)) =>
|
|
299
|
-
checkFieldsSanity(`${name}::${fieldName}`, field, optional)
|
|
286
|
+
->Array.map(((fieldName, field)) =>
|
|
287
|
+
() => checkFieldsSanity(`${name}::${fieldName}`, field, optional)
|
|
288
|
+
)
|
|
300
289
|
->Array.reduce(Ok([]), (res, nextitem) =>
|
|
301
290
|
res->Result.flatMap(arr => nextitem()->Result.map(i => arr->Array.concat([i])))
|
|
302
291
|
)
|
|
@@ -308,8 +297,9 @@ module Field = {
|
|
|
308
297
|
|
|
309
298
|
| (Tuple(fields), optional) =>
|
|
310
299
|
fields
|
|
311
|
-
->Array.mapWithIndex((field, index) =>
|
|
312
|
-
checkFieldsSanity(`${name}[${index->Int.toString}]`, field, optional)
|
|
300
|
+
->Array.mapWithIndex((field, index) =>
|
|
301
|
+
() => checkFieldsSanity(`${name}[${index->Int.toString}]`, field, optional)
|
|
302
|
+
)
|
|
313
303
|
->Array.reduce(Ok([]), (res, nextitem) =>
|
|
314
304
|
res->Result.flatMap(arr => nextitem()->Result.map(i => arr->Array.concat([i])))
|
|
315
305
|
)
|
package/lib/bs/tests/QUnit.ast
CHANGED
|
Binary file
|
package/lib/bs/tests/QUnit.cmi
CHANGED
|
Binary file
|
package/lib/bs/tests/QUnit.cmj
CHANGED
|
Binary file
|
package/lib/bs/tests/QUnit.cmt
CHANGED
|
Binary file
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
type description = string
|
|
2
|
+
type message = string
|
|
3
|
+
|
|
4
|
+
type result<'a> = {
|
|
5
|
+
result: bool,
|
|
6
|
+
actual: 'a,
|
|
7
|
+
expected: 'a,
|
|
8
|
+
message: string,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type assertion
|
|
12
|
+
type block = assertion => unit
|
|
13
|
+
type matcher<'a> = 'a => bool
|
|
14
|
+
|
|
15
|
+
// Library stuff
|
|
16
|
+
|
|
17
|
+
@scope("QUnit") external start: unit => unit = "start"
|
|
18
|
+
@scope("QUnit") external done: (unit => unit) => unit = "done"
|
|
19
|
+
|
|
20
|
+
@send external expect: (assertion, int) => unit = "expect"
|
|
21
|
+
@send external pushResult: (assertion, result<'a>) => unit = "notOk"
|
|
22
|
+
@send external step: (assertion, description) => unit = "step"
|
|
23
|
+
@send external verifySteps: (assertion, array<description>) => unit = "verifySteps"
|
|
24
|
+
|
|
25
|
+
// Values
|
|
26
|
+
|
|
27
|
+
@send external equal: (assertion, 'a, 'a, description) => unit = "equal"
|
|
28
|
+
@send external notEqual: (assertion, 'a, 'a, description) => unit = "notEqual"
|
|
29
|
+
|
|
30
|
+
@send external isFalse: (assertion, 'a, description) => unit = "false"
|
|
31
|
+
@send external isTrue: (assertion, 'a, description) => unit = "true"
|
|
32
|
+
|
|
33
|
+
@send external deepEqual: (assertion, 'a, 'a, description) => unit = "deepEqual"
|
|
34
|
+
@send external notDeepEqual: (assertion, 'a, 'a, description) => unit = "notDeepEqual"
|
|
35
|
+
|
|
36
|
+
@send external strictEqual: (assertion, 'a, 'a, description) => unit = "strictEqual"
|
|
37
|
+
@send external notStrictEqual: (assertion, 'a, 'a, description) => unit = "notStrictEqual"
|
|
38
|
+
|
|
39
|
+
@send external ok: (assertion, 'a, 'a) => unit = "ok"
|
|
40
|
+
@send external notOk: (assertion, 'a, 'a) => unit = "notOk"
|
|
41
|
+
|
|
42
|
+
@send external propEqual: (assertion, 'a, 'a) => unit = "propEqual"
|
|
43
|
+
@send external notPropEqual: (assertion, 'a, 'a) => unit = "notPropEqual"
|
|
44
|
+
|
|
45
|
+
// Promises
|
|
46
|
+
|
|
47
|
+
type done = unit => unit
|
|
48
|
+
|
|
49
|
+
@send external async: assertion => done = "async"
|
|
50
|
+
@send external asyncMany: (assertion, int) => done = "async"
|
|
51
|
+
@send external rejects: (assertion, promise<'a>, message) => unit = "rejects"
|
|
52
|
+
@send external rejectsM: (assertion, promise<'a>, message) => unit = "rejects"
|
|
53
|
+
@send
|
|
54
|
+
external rejectMatches: (assertion, promise<'a>, matcher<'a>, message) => unit = "rejects"
|
|
55
|
+
@send
|
|
56
|
+
external rejectMatchesM: (assertion, promise<'a>, matcher<'a>, message) => unit = "rejects"
|
|
57
|
+
@send external timeout: (assertion, int) => unit = "timeout"
|
|
58
|
+
|
|
59
|
+
// Exceptions
|
|
60
|
+
|
|
61
|
+
@send external throws: (assertion, block, message) => unit = "throws"
|
|
62
|
+
@send
|
|
63
|
+
external throwMatches: (assertion, block, matcher<'a>, message) => unit = "throws"
|
|
64
|
+
|
|
65
|
+
type hooks
|
|
66
|
+
@send external before: (hooks, block) => unit = "before"
|
|
67
|
+
@send external beforeEach: (hooks, block) => unit = "beforeEach"
|
|
68
|
+
@send external afterEach: (hooks, block) => unit = "afterEach"
|
|
69
|
+
@send external after: (hooks, block) => unit = "after"
|
|
70
|
+
|
|
71
|
+
@module("qunit") @val external module_: (description, hooks => unit) => unit = "module"
|
|
72
|
+
@module("qunit") @val external test: (description, block) => unit = "test"
|
package/lib/bs/tests/index.ast
CHANGED
|
Binary file
|
package/lib/bs/tests/index.cmi
CHANGED
|
Binary file
|
package/lib/bs/tests/index.cmj
CHANGED
|
Binary file
|
package/lib/bs/tests/index.cmt
CHANGED
|
Binary file
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
open QUnit
|
|
2
|
-
open RescriptCore
|
|
3
2
|
|
|
4
3
|
module Appointment = {
|
|
5
4
|
type t = {
|
|
@@ -187,3 +186,27 @@ module_("Recursive deserializer", _ => {
|
|
|
187
186
|
qunit->deepEqual(data->Ledger.fromJSON->Obj.magic, Ok(expected), "nice ledger")
|
|
188
187
|
})
|
|
189
188
|
})
|
|
189
|
+
|
|
190
|
+
module_("Type safety limits", _ => {
|
|
191
|
+
test("ill-defined deserializer can cheat the type system", qunit => {
|
|
192
|
+
module X = Deser.MakeDeserializer({
|
|
193
|
+
type t = string
|
|
194
|
+
let fields = Deser.Field.Array(Int)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
let data = %raw("[1]")
|
|
198
|
+
qunit->throws(
|
|
199
|
+
_ => {
|
|
200
|
+
let illString = data->X.fromJSON->Result.getOr("")
|
|
201
|
+
try {
|
|
202
|
+
Console.info3(__MODULE__, "This will fail with a type error", illString->String.charAt(0))
|
|
203
|
+
} catch {
|
|
204
|
+
| e =>
|
|
205
|
+
Console.error(e)
|
|
206
|
+
throw(e)
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"Expected: TypeError: illString.charAt is not a function",
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
})
|