envio 2.7.2 → 2.7.3
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 +7 -6
- package/rescript.json +3 -1
- package/src/Throttler.res +55 -0
- package/src/Utils.res +346 -0
- package/src/bindings/Pino.res +175 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.7.
|
|
3
|
+
"version": "v2.7.3",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,13 +23,14 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://envio.dev",
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"envio-linux-x64": "v2.7.
|
|
27
|
-
"envio-linux-arm64": "v2.7.
|
|
28
|
-
"envio-darwin-x64": "v2.7.
|
|
29
|
-
"envio-darwin-arm64": "v2.7.
|
|
26
|
+
"envio-linux-x64": "v2.7.3",
|
|
27
|
+
"envio-linux-arm64": "v2.7.3",
|
|
28
|
+
"envio-darwin-x64": "v2.7.3",
|
|
29
|
+
"envio-darwin-arm64": "v2.7.3"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"rescript": "11.1.3"
|
|
32
|
+
"rescript": "11.1.3",
|
|
33
|
+
"rescript-schema": "8.2.0"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"bin.js",
|
package/rescript.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type t = {
|
|
2
|
+
mutable lastRunTimeMillis: float,
|
|
3
|
+
mutable isRunning: bool,
|
|
4
|
+
mutable isAwaitingInterval: bool,
|
|
5
|
+
mutable scheduled: option<unit => promise<unit>>,
|
|
6
|
+
intervalMillis: float,
|
|
7
|
+
logger: Pino.t,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let make = (~intervalMillis: int, ~logger) => {
|
|
11
|
+
lastRunTimeMillis: 0.,
|
|
12
|
+
isRunning: false,
|
|
13
|
+
isAwaitingInterval: false,
|
|
14
|
+
scheduled: None,
|
|
15
|
+
intervalMillis: intervalMillis->Belt.Int.toFloat,
|
|
16
|
+
logger,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let rec startInternal = async (throttler: t) => {
|
|
20
|
+
switch throttler {
|
|
21
|
+
| {scheduled: Some(fn), isRunning: false, isAwaitingInterval: false} =>
|
|
22
|
+
let timeSinceLastRun = Js.Date.now() -. throttler.lastRunTimeMillis
|
|
23
|
+
|
|
24
|
+
//Only execute if we are passed the interval
|
|
25
|
+
if timeSinceLastRun >= throttler.intervalMillis {
|
|
26
|
+
throttler.isRunning = true
|
|
27
|
+
throttler.scheduled = None
|
|
28
|
+
throttler.lastRunTimeMillis = Js.Date.now()
|
|
29
|
+
|
|
30
|
+
switch await fn() {
|
|
31
|
+
| exception exn =>
|
|
32
|
+
throttler.logger->Pino.errorExn(
|
|
33
|
+
Pino.createPinoMessageWithError("Scheduled action failed in throttler", exn),
|
|
34
|
+
)
|
|
35
|
+
| _ => ()
|
|
36
|
+
}
|
|
37
|
+
throttler.isRunning = false
|
|
38
|
+
|
|
39
|
+
await throttler->startInternal
|
|
40
|
+
} else {
|
|
41
|
+
//Store isAwaitingInterval in state so that timers don't continuously get created
|
|
42
|
+
throttler.isAwaitingInterval = true
|
|
43
|
+
let _ = Js.Global.setTimeout(() => {
|
|
44
|
+
throttler.isAwaitingInterval = false
|
|
45
|
+
throttler->startInternal->ignore
|
|
46
|
+
}, Belt.Int.fromFloat(throttler.intervalMillis -. timeSinceLastRun))
|
|
47
|
+
}
|
|
48
|
+
| _ => ()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let schedule = (throttler: t, fn) => {
|
|
53
|
+
throttler.scheduled = Some(fn)
|
|
54
|
+
throttler->startInternal->ignore
|
|
55
|
+
}
|
package/src/Utils.res
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
external magic: 'a => 'b = "%identity"
|
|
2
|
+
|
|
3
|
+
module Option = {
|
|
4
|
+
let mapNone = (opt: option<'a>, val: 'b): option<'b> => {
|
|
5
|
+
switch opt {
|
|
6
|
+
| None => Some(val)
|
|
7
|
+
| Some(_) => None
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let catchToNone: (unit => 'a) => option<'a> = unsafeFunc => {
|
|
12
|
+
try {
|
|
13
|
+
unsafeFunc()->Some
|
|
14
|
+
} catch {
|
|
15
|
+
| _ => None
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let flatten = opt =>
|
|
20
|
+
switch opt {
|
|
21
|
+
| None => None
|
|
22
|
+
| Some(opt) => opt
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let getExn = (opt, message) => {
|
|
26
|
+
switch opt {
|
|
27
|
+
| None => Js.Exn.raiseError(message)
|
|
28
|
+
| Some(v) => v
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module Tuple = {
|
|
34
|
+
/**Access a tuple value by its index*/
|
|
35
|
+
@warning("-27")
|
|
36
|
+
let get = (tuple: 'a, index: int): option<'b> => %raw(`tuple[index]`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module Dict = {
|
|
40
|
+
@get_index
|
|
41
|
+
/**
|
|
42
|
+
It's the same as `Js.Dict.get` but it doesn't have runtime overhead to check if the key exists.
|
|
43
|
+
*/
|
|
44
|
+
external dangerouslyGetNonOption: (dict<'a>, string) => option<'a> = ""
|
|
45
|
+
|
|
46
|
+
let merge: (dict<'a>, dict<'a>) => dict<'a> = %raw(`(dictA, dictB) => ({...dictA, ...dictB})`)
|
|
47
|
+
|
|
48
|
+
let updateImmutable: (
|
|
49
|
+
dict<'a>,
|
|
50
|
+
string,
|
|
51
|
+
'a,
|
|
52
|
+
) => dict<'a> = %raw(`(dict, key, value) => ({...dict, [key]: value})`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module Math = {
|
|
56
|
+
let minOptInt = (a, b) =>
|
|
57
|
+
switch (a, b) {
|
|
58
|
+
| (Some(a), Some(b)) => Pervasives.min(a, b)->Some
|
|
59
|
+
| (Some(a), None) => Some(a)
|
|
60
|
+
| (None, Some(b)) => Some(b)
|
|
61
|
+
| (None, None) => None
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
module Array = {
|
|
65
|
+
@val external jsArrayCreate: int => array<'a> = "Array"
|
|
66
|
+
|
|
67
|
+
/* Given a comaprator and two sorted lists, combine them into a single sorted list */
|
|
68
|
+
let mergeSorted = (f: ('a, 'a) => bool, xs: array<'a>, ys: array<'a>) => {
|
|
69
|
+
if Array.length(xs) == 0 {
|
|
70
|
+
ys
|
|
71
|
+
} else if Array.length(ys) == 0 {
|
|
72
|
+
xs
|
|
73
|
+
} else {
|
|
74
|
+
let n = Array.length(xs) + Array.length(ys)
|
|
75
|
+
let result = jsArrayCreate(n)
|
|
76
|
+
|
|
77
|
+
let rec loop = (i, j, k) => {
|
|
78
|
+
if i < Array.length(xs) && j < Array.length(ys) {
|
|
79
|
+
if f(xs[i], ys[j]) {
|
|
80
|
+
result[k] = xs[i]
|
|
81
|
+
loop(i + 1, j, k + 1)
|
|
82
|
+
} else {
|
|
83
|
+
result[k] = ys[j]
|
|
84
|
+
loop(i, j + 1, k + 1)
|
|
85
|
+
}
|
|
86
|
+
} else if i < Array.length(xs) {
|
|
87
|
+
result[k] = xs[i]
|
|
88
|
+
loop(i + 1, j, k + 1)
|
|
89
|
+
} else if j < Array.length(ys) {
|
|
90
|
+
result[k] = ys[j]
|
|
91
|
+
loop(i, j + 1, k + 1)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
loop(0, 0, 0)
|
|
96
|
+
result
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
Creates a shallow copy of the array and sets the value at the given index
|
|
102
|
+
*/
|
|
103
|
+
let setIndexImmutable = (arr: array<'a>, index: int, value: 'a): array<'a> => {
|
|
104
|
+
let shallowCopy = arr->Belt.Array.copy
|
|
105
|
+
shallowCopy->Js.Array2.unsafe_set(index, value)
|
|
106
|
+
shallowCopy
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let transposeResults = (results: array<result<'a, 'b>>): result<array<'a>, 'b> => {
|
|
110
|
+
let rec loop = (index: int, output: array<'a>): result<array<'a>, 'b> => {
|
|
111
|
+
if index >= Array.length(results) {
|
|
112
|
+
Ok(output)
|
|
113
|
+
} else {
|
|
114
|
+
switch results->Js.Array2.unsafe_get(index) {
|
|
115
|
+
| Ok(value) => {
|
|
116
|
+
output[index] = value
|
|
117
|
+
loop(index + 1, output)
|
|
118
|
+
}
|
|
119
|
+
| Error(_) as err => err->(magic: result<'a, 'b> => result<array<'a>, 'b>)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
loop(0, Belt.Array.makeUninitializedUnsafe(results->Js.Array2.length))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
Helper to check if a value exists in an array
|
|
129
|
+
*/
|
|
130
|
+
let includes = (arr: array<'a>, val: 'a) =>
|
|
131
|
+
arr->Js.Array2.find(item => item == val)->Belt.Option.isSome
|
|
132
|
+
|
|
133
|
+
let isEmpty = (arr: array<_>) =>
|
|
134
|
+
switch arr {
|
|
135
|
+
| [] => true
|
|
136
|
+
| _ => false
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let awaitEach = async (arr: array<'a>, fn: 'a => promise<unit>) => {
|
|
140
|
+
for i in 0 to arr->Array.length - 1 {
|
|
141
|
+
let item = arr[i]
|
|
142
|
+
await item->fn
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
Creates a new array removing the item at the given index
|
|
148
|
+
|
|
149
|
+
Index > array length or < 0 results in a copy of the array
|
|
150
|
+
*/
|
|
151
|
+
let removeAtIndex = (array, index) => {
|
|
152
|
+
if index < 0 || index >= array->Array.length {
|
|
153
|
+
array->Array.copy
|
|
154
|
+
} else {
|
|
155
|
+
let head = array->Js.Array2.slice(~start=0, ~end_=index)
|
|
156
|
+
let tail = array->Belt.Array.sliceToEnd(index + 1)
|
|
157
|
+
[...head, ...tail]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let last = (arr: array<'a>): option<'a> => arr->Belt.Array.get(arr->Array.length - 1)
|
|
162
|
+
|
|
163
|
+
let findReverseWithIndex = (arr: array<'a>, fn: 'a => bool): option<('a, int)> => {
|
|
164
|
+
let rec loop = (index: int) => {
|
|
165
|
+
if index < 0 {
|
|
166
|
+
None
|
|
167
|
+
} else {
|
|
168
|
+
let item = arr[index]
|
|
169
|
+
if fn(item) {
|
|
170
|
+
Some((item, index))
|
|
171
|
+
} else {
|
|
172
|
+
loop(index - 1)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
loop(arr->Array.length - 1)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
Currently a bug in rescript if you ignore the return value of spliceInPlace
|
|
181
|
+
https://github.com/rescript-lang/rescript-compiler/issues/6991
|
|
182
|
+
*/
|
|
183
|
+
@send
|
|
184
|
+
external spliceInPlace: (array<'a>, ~pos: int, ~remove: int) => array<'a> = "splice"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module String = {
|
|
188
|
+
let capitalize = str => {
|
|
189
|
+
str->Js.String2.slice(~from=0, ~to_=1)->Js.String.toUpperCase ++
|
|
190
|
+
str->Js.String2.sliceToEnd(~from=1)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
Useful when an unsafe unwrap is needed on Result type
|
|
196
|
+
and Error holds an exn. This is better than Result.getExn
|
|
197
|
+
because the excepion is not just NOT_FOUND but will rather
|
|
198
|
+
bet the actual underlying exn
|
|
199
|
+
*/
|
|
200
|
+
let unwrapResultExn = res =>
|
|
201
|
+
switch res {
|
|
202
|
+
| Ok(v) => v
|
|
203
|
+
| Error(exn) => exn->raise
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
external queueMicrotask: (unit => unit) => unit = "queueMicrotask"
|
|
207
|
+
|
|
208
|
+
module Schema = {
|
|
209
|
+
let getNonOptionalFieldNames = schema => {
|
|
210
|
+
let acc = []
|
|
211
|
+
switch schema->S.classify {
|
|
212
|
+
| Object({items}) =>
|
|
213
|
+
items->Js.Array2.forEach(item => {
|
|
214
|
+
switch item.schema->S.classify {
|
|
215
|
+
// Check for null, since we generate S.null schema for db serializing
|
|
216
|
+
// In the future it should be changed to Option
|
|
217
|
+
| Null(_) => ()
|
|
218
|
+
| _ => acc->Js.Array2.push(item.location)->ignore
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
| _ => ()
|
|
222
|
+
}
|
|
223
|
+
acc
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let getCapitalizedFieldNames = schema => {
|
|
227
|
+
switch schema->S.classify {
|
|
228
|
+
| Object({items}) => items->Js.Array2.map(item => item.location->String.capitalize)
|
|
229
|
+
| _ => []
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// When trying to serialize data to Json pg type, it will fail with
|
|
234
|
+
// PostgresError: column "params" is of type json but expression is of type boolean
|
|
235
|
+
// If there's bool or null on the root level. It works fine as object field values.
|
|
236
|
+
let coerceToJsonPgType = schema => {
|
|
237
|
+
schema->S.preprocess(s => {
|
|
238
|
+
switch s.schema->S.classify {
|
|
239
|
+
| Literal(Null(_))
|
|
240
|
+
| // This is a workaround for Fuel Bytes type
|
|
241
|
+
Unknown => {serializer: _ => %raw(`"null"`)}
|
|
242
|
+
| Null(_)
|
|
243
|
+
| Bool => {
|
|
244
|
+
serializer: unknown => {
|
|
245
|
+
if unknown === %raw(`null`) {
|
|
246
|
+
%raw(`"null"`)
|
|
247
|
+
} else if unknown === %raw(`false`) {
|
|
248
|
+
%raw(`"false"`)
|
|
249
|
+
} else if unknown === %raw(`true`) {
|
|
250
|
+
%raw(`"true"`)
|
|
251
|
+
} else {
|
|
252
|
+
unknown
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
| _ => {}
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module Set = {
|
|
263
|
+
type t<'value>
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* Constructor
|
|
267
|
+
*/
|
|
268
|
+
@ocaml.doc("Creates a new `Set` object.") @new
|
|
269
|
+
external make: unit => t<'value> = "Set"
|
|
270
|
+
|
|
271
|
+
@ocaml.doc("Creates a new `Set` object.") @new
|
|
272
|
+
external fromEntries: array<'value> => t<'value> = "Set"
|
|
273
|
+
|
|
274
|
+
/*
|
|
275
|
+
* Instance properties
|
|
276
|
+
*/
|
|
277
|
+
@ocaml.doc("Returns the number of values in the `Set` object.") @get
|
|
278
|
+
external size: t<'value> => int = "size"
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* Instance methods
|
|
282
|
+
*/
|
|
283
|
+
@ocaml.doc("Appends `value` to the `Set` object. Returns the `Set` object with added value.")
|
|
284
|
+
@send
|
|
285
|
+
external add: (t<'value>, 'value) => t<'value> = "add"
|
|
286
|
+
|
|
287
|
+
@ocaml.doc("Removes all elements from the `Set` object.") @send
|
|
288
|
+
external clear: t<'value> => unit = "clear"
|
|
289
|
+
|
|
290
|
+
@ocaml.doc(
|
|
291
|
+
"Removes the element associated to the `value` and returns a boolean asserting whether an element was successfully removed or not. `Set.prototype.has(value)` will return `false` afterwards."
|
|
292
|
+
)
|
|
293
|
+
@send
|
|
294
|
+
external delete: (t<'value>, 'value) => bool = "delete"
|
|
295
|
+
|
|
296
|
+
@ocaml.doc(
|
|
297
|
+
"Returns a boolean asserting whether an element is present with the given value in the `Set` object or not."
|
|
298
|
+
)
|
|
299
|
+
@send
|
|
300
|
+
external has: (t<'value>, 'value) => bool = "has"
|
|
301
|
+
|
|
302
|
+
external toArray: t<'a> => array<'a> = "Array.from"
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
* Iteration methods
|
|
306
|
+
*/
|
|
307
|
+
/*
|
|
308
|
+
/// NOTE - if we need iteration we can add this back - currently it requires the `rescript-js-iterator` library.
|
|
309
|
+
@ocaml.doc(
|
|
310
|
+
"Returns a new iterator object that yields the **values** for each element in the `Set` object in insertion order."
|
|
311
|
+
)
|
|
312
|
+
@send
|
|
313
|
+
external values: t<'value> => Js_iterator.t<'value> = "values"
|
|
314
|
+
|
|
315
|
+
@ocaml.doc("An alias for `Set.prototype.values()`.") @send
|
|
316
|
+
external keys: t<'value> => Js_iterator.t<'value> = "values"
|
|
317
|
+
|
|
318
|
+
@ocaml.doc("Returns a new iterator object that contains **an array of [value, value]** for each element in the `Set` object, in insertion order.
|
|
319
|
+
|
|
320
|
+
This is similar to the [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, so that each entry's `key` is the same as its `value` for a `Set`.")
|
|
321
|
+
@send
|
|
322
|
+
external entries: t<'value> => Js_iterator.t<('value, 'value)> = "entries"
|
|
323
|
+
*/
|
|
324
|
+
@ocaml.doc(
|
|
325
|
+
"Calls `callbackFn` once for each value present in the `Set` object, in insertion order."
|
|
326
|
+
)
|
|
327
|
+
@send
|
|
328
|
+
external forEach: (t<'value>, 'value => unit) => unit = "forEach"
|
|
329
|
+
|
|
330
|
+
@ocaml.doc(
|
|
331
|
+
"Calls `callbackFn` once for each value present in the `Set` object, in insertion order."
|
|
332
|
+
)
|
|
333
|
+
@send
|
|
334
|
+
external forEachWithSet: (t<'value>, ('value, 'value, t<'value>) => unit) => unit = "forEach"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module WeakMap = {
|
|
338
|
+
type t<'k, 'v> = Js.WeakMap.t<'k, 'v>
|
|
339
|
+
|
|
340
|
+
@new external make: unit => t<'k, 'v> = "WeakMap"
|
|
341
|
+
|
|
342
|
+
@send external get: (t<'k, 'v>, 'k) => option<'v> = "get"
|
|
343
|
+
@send external unsafeGet: (t<'k, 'v>, 'k) => 'v = "get"
|
|
344
|
+
@send external has: (t<'k, 'v>, 'k) => bool = "has"
|
|
345
|
+
@send external set: (t<'k, 'v>, 'k, 'v) => t<'k, 'v> = "set"
|
|
346
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
type logLevelBuiltin = [
|
|
2
|
+
| #trace
|
|
3
|
+
| #debug
|
|
4
|
+
| #info
|
|
5
|
+
| #warn
|
|
6
|
+
| #error
|
|
7
|
+
| #fatal
|
|
8
|
+
]
|
|
9
|
+
@genType
|
|
10
|
+
type logLevelUser = [
|
|
11
|
+
| // NOTE: pino does better when these are all lowercase - some parts of the code lower case logs.
|
|
12
|
+
#udebug
|
|
13
|
+
| #uinfo
|
|
14
|
+
| #uwarn
|
|
15
|
+
| #uerror
|
|
16
|
+
]
|
|
17
|
+
type logLevel = [logLevelBuiltin | logLevelUser]
|
|
18
|
+
|
|
19
|
+
type pinoMessageBlob
|
|
20
|
+
type pinoMessageBlobWithError
|
|
21
|
+
@genType
|
|
22
|
+
type t = {
|
|
23
|
+
trace: pinoMessageBlob => unit,
|
|
24
|
+
debug: pinoMessageBlob => unit,
|
|
25
|
+
info: pinoMessageBlob => unit,
|
|
26
|
+
warn: pinoMessageBlob => unit,
|
|
27
|
+
error: pinoMessageBlob => unit,
|
|
28
|
+
fatal: pinoMessageBlob => unit,
|
|
29
|
+
}
|
|
30
|
+
@send external errorExn: (t, pinoMessageBlobWithError) => unit = "error"
|
|
31
|
+
|
|
32
|
+
// Bind to the 'level' property getter
|
|
33
|
+
@get external getLevel: t => logLevel = "level"
|
|
34
|
+
|
|
35
|
+
@ocaml.doc(`Get the available logging levels`) @get
|
|
36
|
+
external levels: t => 'a = "levels"
|
|
37
|
+
|
|
38
|
+
// Bind to the 'level' property setter
|
|
39
|
+
@set external setLevel: (t, logLevel) => unit = "level"
|
|
40
|
+
|
|
41
|
+
@ocaml.doc(`Identity function to help co-erce any type to a pino log message`)
|
|
42
|
+
let createPinoMessage = (message): pinoMessageBlob => Utils.magic(message)
|
|
43
|
+
let createPinoMessageWithError = (message, err): pinoMessageBlobWithError => {
|
|
44
|
+
//See https://github.com/pinojs/pino-std-serializers for standard pino serializers
|
|
45
|
+
//for common objects. We have also defined the serializer in this format in the
|
|
46
|
+
// serializers type below: `type serializers = {err: Js.Json.t => Js.Json.t}`
|
|
47
|
+
Utils.magic({
|
|
48
|
+
"msg": message,
|
|
49
|
+
"err": err,
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module Transport = {
|
|
54
|
+
type t
|
|
55
|
+
type optionsObject
|
|
56
|
+
let makeTransportOptions: 'a => optionsObject = Utils.magic
|
|
57
|
+
|
|
58
|
+
// NOTE: this config is pretty polymorphic - so keeping this as all optional fields.
|
|
59
|
+
type rec transportTarget = {
|
|
60
|
+
target?: string,
|
|
61
|
+
targets?: array<transportTarget>,
|
|
62
|
+
options?: optionsObject,
|
|
63
|
+
levels?: dict<int>,
|
|
64
|
+
level?: logLevel,
|
|
65
|
+
}
|
|
66
|
+
@module("pino")
|
|
67
|
+
external make: transportTarget => t = "transport"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@module external makeWithTransport: Transport.t => t = "pino"
|
|
71
|
+
|
|
72
|
+
type hooks = {logMethod: (array<string>, string, logLevel) => unit}
|
|
73
|
+
|
|
74
|
+
type formatters = {
|
|
75
|
+
level: (string, int) => Js.Json.t,
|
|
76
|
+
bindings: Js.Json.t => Js.Json.t,
|
|
77
|
+
log: Js.Json.t => Js.Json.t,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type serializers = {err: Js.Json.t => Js.Json.t}
|
|
81
|
+
|
|
82
|
+
type options = {
|
|
83
|
+
name?: string,
|
|
84
|
+
level?: logLevel,
|
|
85
|
+
customLevels?: dict<int>,
|
|
86
|
+
useOnlyCustomLevels?: bool,
|
|
87
|
+
depthLimit?: int,
|
|
88
|
+
edgeLimit?: int,
|
|
89
|
+
mixin?: unit => Js.Json.t,
|
|
90
|
+
mixinMergeStrategy?: (Js.Json.t, Js.Json.t) => Js.Json.t,
|
|
91
|
+
redact?: array<string>,
|
|
92
|
+
hooks?: hooks,
|
|
93
|
+
formatters?: formatters,
|
|
94
|
+
serializers?: serializers,
|
|
95
|
+
msgPrefix?: string,
|
|
96
|
+
base?: Js.Json.t,
|
|
97
|
+
enabled?: bool,
|
|
98
|
+
crlf?: bool,
|
|
99
|
+
timestamp?: bool,
|
|
100
|
+
messageKey?: string,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@module external make: options => t = "pino"
|
|
104
|
+
@module external makeWithOptionsAndTransport: (options, Transport.t) => t = "pino"
|
|
105
|
+
|
|
106
|
+
type childParams
|
|
107
|
+
let createChildParams: 'a => childParams = Utils.magic
|
|
108
|
+
@send external child: (t, childParams) => t = "child"
|
|
109
|
+
|
|
110
|
+
module ECS = {
|
|
111
|
+
@module
|
|
112
|
+
external make: 'a => options = "@elastic/ecs-pino-format"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
Jank solution to make logs use console log wrather than stream.write so that ink
|
|
117
|
+
can render the logs statically.
|
|
118
|
+
*/
|
|
119
|
+
module MultiStreamLogger = {
|
|
120
|
+
type stream = {write: string => unit}
|
|
121
|
+
type multiStream = {stream: stream, level: logLevel}
|
|
122
|
+
type multiStreamRes
|
|
123
|
+
@module("pino") external multistream: array<multiStream> => multiStreamRes = "multistream"
|
|
124
|
+
|
|
125
|
+
@module external makeWithMultiStream: (options, multiStreamRes) => t = "pino"
|
|
126
|
+
|
|
127
|
+
type destinationOpts = {
|
|
128
|
+
dest: string, //file path
|
|
129
|
+
sync: bool,
|
|
130
|
+
mkdir: bool,
|
|
131
|
+
}
|
|
132
|
+
@module("pino") external destination: destinationOpts => stream = "destination"
|
|
133
|
+
|
|
134
|
+
type prettyFactoryOpts = {...options, customColors?: string}
|
|
135
|
+
@module("pino-pretty")
|
|
136
|
+
external prettyFactory: prettyFactoryOpts => string => string = "prettyFactory"
|
|
137
|
+
|
|
138
|
+
let makeFormatter = logLevels => {
|
|
139
|
+
prettyFactory({
|
|
140
|
+
customLevels: logLevels,
|
|
141
|
+
customColors: "fatal:bgRed,error:red,warn:yellow,info:green,udebug:bgBlue,uinfo:bgGreen,uwarn:bgYellow,uerror:bgRed,debug:blue,trace:gray",
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let makeStreams = (~userLogLevel, ~formatter, ~logFile, ~defaultFileLogLevel) => {
|
|
146
|
+
let stream = {
|
|
147
|
+
stream: {write: v => formatter(v)->Js.log},
|
|
148
|
+
level: userLogLevel,
|
|
149
|
+
}
|
|
150
|
+
let maybeFileStream = logFile->Belt.Option.mapWithDefault([], dest => [
|
|
151
|
+
{
|
|
152
|
+
level: defaultFileLogLevel,
|
|
153
|
+
stream: destination({dest, sync: false, mkdir: true}),
|
|
154
|
+
},
|
|
155
|
+
])
|
|
156
|
+
[stream]->Belt.Array.concat(maybeFileStream)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let make = (
|
|
160
|
+
~userLogLevel: logLevel,
|
|
161
|
+
~customLevels: dict<int>,
|
|
162
|
+
~logFile: option<string>,
|
|
163
|
+
~options: option<options>,
|
|
164
|
+
~defaultFileLogLevel,
|
|
165
|
+
) => {
|
|
166
|
+
let options = switch options {
|
|
167
|
+
| Some(opts) => {...opts, customLevels, level: userLogLevel}
|
|
168
|
+
| None => {customLevels, level: userLogLevel}
|
|
169
|
+
}
|
|
170
|
+
let formatter = makeFormatter(customLevels)
|
|
171
|
+
let ms = makeStreams(~userLogLevel, ~formatter, ~logFile, ~defaultFileLogLevel)->multistream
|
|
172
|
+
|
|
173
|
+
makeWithMultiStream(options, ms)
|
|
174
|
+
}
|
|
175
|
+
}
|