@colisweb/rescript-toolkit 5.4.2 → 5.5.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/package.json +3 -1
- package/rescript.json +2 -1
- package/src/request/Request.res +205 -0
- package/src/vendors/Swr.res +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colisweb/rescript-toolkit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rescript clean",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@colisweb/bs-react-intl-extractor-bin": "0.12.2",
|
|
31
31
|
"@datadog/browser-rum": "5.8.0",
|
|
32
|
+
"@dck/rescript-ky": "^2.0.0",
|
|
32
33
|
"@dck/rescript-promise": "1.1.0",
|
|
33
34
|
"@dck/restorative": "1.1.0",
|
|
34
35
|
"@greenlabs/ppx-spice": "0.2.1",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"copy-to-clipboard": "3.3.3",
|
|
44
45
|
"date-fns": "3.2.0",
|
|
45
46
|
"dedent": "0.7.0",
|
|
47
|
+
"ky": "^1.2.4",
|
|
46
48
|
"lenses-ppx": "6.1.10",
|
|
47
49
|
"list-selectors": "2.0.1",
|
|
48
50
|
"lodash": "4.17.21",
|
package/rescript.json
CHANGED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
type error<'apiError> =
|
|
2
|
+
| DecodeError(Spice.decodeError)
|
|
3
|
+
| Unknown(Ky.error<Js.Json.t>)
|
|
4
|
+
| Custom('apiError)
|
|
5
|
+
|
|
6
|
+
type requestConfig<'apiError, 'response> = {
|
|
7
|
+
path: string,
|
|
8
|
+
requestOptions: Ky.requestOptions<Js.Json.t, Js.Json.t, Js.Json.t, 'response>,
|
|
9
|
+
key?: array<string>,
|
|
10
|
+
customError?: Ky.error<Js.Json.t> => Promise.t<error<'apiError>>,
|
|
11
|
+
mapPromise?: Js.Json.t => result<'response, error<'apiError>>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
%%private(
|
|
15
|
+
let fetch = (
|
|
16
|
+
~instance,
|
|
17
|
+
~path,
|
|
18
|
+
~requestOptions,
|
|
19
|
+
~mapPromise=?,
|
|
20
|
+
~customError=?,
|
|
21
|
+
~response_decode,
|
|
22
|
+
) => {
|
|
23
|
+
// TODO :
|
|
24
|
+
// - parseJson
|
|
25
|
+
// - abort controller signal
|
|
26
|
+
Ky.Instance.asCallable(instance)(path, ~options=requestOptions)
|
|
27
|
+
->Ky.Response.json()
|
|
28
|
+
->Promise.Js.fromBsPromise
|
|
29
|
+
->Promise.Js.toResult
|
|
30
|
+
->Promise.flatMap(response => {
|
|
31
|
+
switch response {
|
|
32
|
+
| Error(err) => {
|
|
33
|
+
let error: Ky.error<'a> = err->Obj.magic
|
|
34
|
+
customError->Option.mapWithDefault(Promise.resolved(Error(Unknown(error))), fn =>
|
|
35
|
+
fn(error)->Promise.map(e => Error(e))
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
| Ok(response) =>
|
|
39
|
+
switch mapPromise {
|
|
40
|
+
| None =>
|
|
41
|
+
Promise.resolved(
|
|
42
|
+
switch response->response_decode {
|
|
43
|
+
| Ok(_) as ok => ok
|
|
44
|
+
| Error(decodeError) => Error(DecodeError(decodeError))
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
| Some(fn) => Promise.resolved(fn(response))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
module type Config = {
|
|
54
|
+
type argument
|
|
55
|
+
type response
|
|
56
|
+
type error
|
|
57
|
+
let instance: Ky.Instance.t
|
|
58
|
+
let response_decode: Js.Json.t => result<response, Spice.decodeError>
|
|
59
|
+
let config: argument => requestConfig<error, response>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let fetchAPI = (
|
|
63
|
+
type argument response err,
|
|
64
|
+
config: module(Config with
|
|
65
|
+
type argument = argument
|
|
66
|
+
and type response = response
|
|
67
|
+
and type error = err
|
|
68
|
+
),
|
|
69
|
+
argument: argument,
|
|
70
|
+
): Promise.t<result<response, error<err>>> => {
|
|
71
|
+
let module(C) = config
|
|
72
|
+
let requestConfig = C.config(argument)
|
|
73
|
+
|
|
74
|
+
fetch(
|
|
75
|
+
~instance=C.instance,
|
|
76
|
+
~path=requestConfig.path,
|
|
77
|
+
~response_decode=C.response_decode,
|
|
78
|
+
~requestOptions=requestConfig.requestOptions,
|
|
79
|
+
~customError=?requestConfig.customError,
|
|
80
|
+
~mapPromise=?requestConfig.mapPromise,
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
let useFetcher = (
|
|
84
|
+
type argument response error,
|
|
85
|
+
~options: option<Swr.fetcherOptions>=?,
|
|
86
|
+
config: module(Config with
|
|
87
|
+
type argument = argument
|
|
88
|
+
and type response = response
|
|
89
|
+
and type error = error
|
|
90
|
+
),
|
|
91
|
+
argument: option<argument>,
|
|
92
|
+
): Toolkit__Hooks.fetcher<response> => {
|
|
93
|
+
let module(C) = config
|
|
94
|
+
|
|
95
|
+
Toolkit__Hooks.useFetcher(
|
|
96
|
+
~options?,
|
|
97
|
+
argument->Option.flatMap(argument => {
|
|
98
|
+
let requestConfig = C.config(argument)
|
|
99
|
+
|
|
100
|
+
switch requestConfig.key->Obj.magic {
|
|
101
|
+
| None =>
|
|
102
|
+
Js.Exn.raiseError(
|
|
103
|
+
`You are using a config without a key for this path ${requestConfig.path}`,
|
|
104
|
+
)
|
|
105
|
+
| Some(key) => key
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
() => {
|
|
109
|
+
fetchAPI(config, argument->Option.getExn)->Promise.Js.fromResult
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let useOptionalFetcher = (
|
|
115
|
+
type argument response error,
|
|
116
|
+
~options: option<Swr.fetcherOptions>=?,
|
|
117
|
+
config: module(Config with
|
|
118
|
+
type argument = argument
|
|
119
|
+
and type response = response
|
|
120
|
+
and type error = error
|
|
121
|
+
),
|
|
122
|
+
argument: option<argument>,
|
|
123
|
+
): Toolkit__Hooks.fetcher<response> => {
|
|
124
|
+
let module(C) = config
|
|
125
|
+
|
|
126
|
+
Toolkit__Hooks.useOptionalFetcher(
|
|
127
|
+
~options?,
|
|
128
|
+
argument->Option.flatMap(argument => {
|
|
129
|
+
let requestConfig = C.config(argument)
|
|
130
|
+
|
|
131
|
+
switch requestConfig.key->Obj.magic {
|
|
132
|
+
| None =>
|
|
133
|
+
Js.Exn.raiseError(
|
|
134
|
+
`You are using a config without a key for this path ${requestConfig.path}`,
|
|
135
|
+
)
|
|
136
|
+
| Some(key) => key
|
|
137
|
+
}
|
|
138
|
+
}),
|
|
139
|
+
() => {
|
|
140
|
+
fetchAPI(config, argument->Option.getExn)->Promise.Js.fromResult
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
type state<'data, 'error> =
|
|
146
|
+
| NotAsked
|
|
147
|
+
| Loading
|
|
148
|
+
| Done(result<'data, 'error>)
|
|
149
|
+
|
|
150
|
+
%%private(
|
|
151
|
+
let minInt = -999999999
|
|
152
|
+
let maxInt = 1000000000
|
|
153
|
+
|
|
154
|
+
let increment = (num: int): int => num !== maxInt ? num + 1 : minInt
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
let useManualRequest = (
|
|
158
|
+
type argument response error,
|
|
159
|
+
config: module(Config with
|
|
160
|
+
type argument = argument
|
|
161
|
+
and type response = response
|
|
162
|
+
and type error = error
|
|
163
|
+
),
|
|
164
|
+
) => {
|
|
165
|
+
let module(Config) = config
|
|
166
|
+
|
|
167
|
+
let lastCallId = React.useRef(0)
|
|
168
|
+
let canceled = React.useRef(false)
|
|
169
|
+
let (state, set) = React.useState(() => NotAsked)
|
|
170
|
+
let isMounted = ReactUse.useMountedState(.)
|
|
171
|
+
|
|
172
|
+
let trigger = argument => {
|
|
173
|
+
lastCallId.current = lastCallId.current->increment
|
|
174
|
+
let callId = lastCallId.current
|
|
175
|
+
|
|
176
|
+
set(_ => Loading)
|
|
177
|
+
|
|
178
|
+
canceled.current = false
|
|
179
|
+
|
|
180
|
+
fetchAPI(module(Config), argument)->Promise.map(result => {
|
|
181
|
+
let isCanceled = callId !== lastCallId.current || canceled.current
|
|
182
|
+
|
|
183
|
+
if isMounted() && !isCanceled {
|
|
184
|
+
set(_ => Done(result))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
(result, isCanceled)
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let cancel = React.useCallback(() => canceled.current = true, [])
|
|
192
|
+
|
|
193
|
+
(state, trigger, cancel)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
external exnToError: Js.Exn.t => error<'a> = "%identity"
|
|
197
|
+
|
|
198
|
+
let decodeResponseError = (responseError, decoder) => {
|
|
199
|
+
responseError
|
|
200
|
+
->Ky.Response.json()
|
|
201
|
+
->Promise.Js.fromBsPromise
|
|
202
|
+
->Promise.Js.toResult
|
|
203
|
+
->Promise.mapError(Obj.magic)
|
|
204
|
+
->Promise.flatMapOk(json => json->decoder->Promise.resolved)
|
|
205
|
+
}
|
package/src/vendors/Swr.res
CHANGED
|
@@ -63,6 +63,11 @@ type mutationFetcher<'params, 'data, 'error> = {
|
|
|
63
63
|
|
|
64
64
|
module SwrKey = Toolkit__Identifier.MakeString()
|
|
65
65
|
|
|
66
|
+
@unboxed
|
|
67
|
+
type key =
|
|
68
|
+
| String(string)
|
|
69
|
+
| Array(array<string>)
|
|
70
|
+
|
|
66
71
|
@module("swr")
|
|
67
72
|
external useSwr: (option<SwrKey.t>, 'fn, 'fetcherOptions) => fetcher<'data> = "default"
|
|
68
73
|
|
|
@@ -88,6 +93,8 @@ module SwrConfig = {
|
|
|
88
93
|
|
|
89
94
|
@send
|
|
90
95
|
external mutate0: (t, SwrKey.t) => unit = "mutate"
|
|
96
|
+
@send
|
|
97
|
+
external mutateByKey: (t, key) => unit = "mutate"
|
|
91
98
|
|
|
92
99
|
@send
|
|
93
100
|
external mutate: (t, SwrKey.t, 'data, bool) => unit = "mutate"
|