envio 2.9.1 → 2.11.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/evm.schema.json +103 -92
- package/package.json +5 -5
- package/src/Internal.gen.ts +47 -0
- package/src/Internal.res +124 -0
- package/src/LazyLoader.res +134 -0
- package/src/ReorgDetection.res +432 -0
- package/src/TopicFilter.res +27 -0
- package/src/Utils.res +26 -0
- package/src/bindings/BigInt.res +15 -3
- package/src/bindings/Ethers.gen.ts +14 -0
- package/src/bindings/Ethers.res +259 -0
- package/src/bindings/SDSL.res +12 -0
- package/src/sources/HyperSyncJsonApi.res +376 -0
- /package/src/{bindings → sources}/HyperSyncClient.res +0 -0
package/evm.schema.json
CHANGED
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
]
|
|
84
84
|
},
|
|
85
85
|
"field_selection": {
|
|
86
|
-
"description": "
|
|
86
|
+
"description": "Select the block and transaction fields to include in all events globally",
|
|
87
87
|
"anyOf": [
|
|
88
88
|
{
|
|
89
89
|
"$ref": "#/$defs/FieldSelection"
|
|
@@ -159,6 +159,17 @@
|
|
|
159
159
|
"string",
|
|
160
160
|
"null"
|
|
161
161
|
]
|
|
162
|
+
},
|
|
163
|
+
"field_selection": {
|
|
164
|
+
"description": "Select the block and transaction fields to include in the specific event",
|
|
165
|
+
"anyOf": [
|
|
166
|
+
{
|
|
167
|
+
"$ref": "#/$defs/FieldSelection"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"type": "null"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
162
173
|
}
|
|
163
174
|
},
|
|
164
175
|
"additionalProperties": false,
|
|
@@ -166,6 +177,97 @@
|
|
|
166
177
|
"event"
|
|
167
178
|
]
|
|
168
179
|
},
|
|
180
|
+
"FieldSelection": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"properties": {
|
|
183
|
+
"transaction_fields": {
|
|
184
|
+
"description": "The transaction fields to include in the event, or in all events if applied globally",
|
|
185
|
+
"type": [
|
|
186
|
+
"array",
|
|
187
|
+
"null"
|
|
188
|
+
],
|
|
189
|
+
"items": {
|
|
190
|
+
"$ref": "#/$defs/TransactionField"
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"block_fields": {
|
|
194
|
+
"description": "The block fields to include in the event, or in all events if applied globally",
|
|
195
|
+
"type": [
|
|
196
|
+
"array",
|
|
197
|
+
"null"
|
|
198
|
+
],
|
|
199
|
+
"items": {
|
|
200
|
+
"$ref": "#/$defs/BlockField"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"additionalProperties": false
|
|
205
|
+
},
|
|
206
|
+
"TransactionField": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"enum": [
|
|
209
|
+
"transactionIndex",
|
|
210
|
+
"hash",
|
|
211
|
+
"from",
|
|
212
|
+
"to",
|
|
213
|
+
"gas",
|
|
214
|
+
"gasPrice",
|
|
215
|
+
"maxPriorityFeePerGas",
|
|
216
|
+
"maxFeePerGas",
|
|
217
|
+
"cumulativeGasUsed",
|
|
218
|
+
"effectiveGasPrice",
|
|
219
|
+
"gasUsed",
|
|
220
|
+
"input",
|
|
221
|
+
"nonce",
|
|
222
|
+
"value",
|
|
223
|
+
"v",
|
|
224
|
+
"r",
|
|
225
|
+
"s",
|
|
226
|
+
"contractAddress",
|
|
227
|
+
"logsBloom",
|
|
228
|
+
"root",
|
|
229
|
+
"status",
|
|
230
|
+
"yParity",
|
|
231
|
+
"chainId",
|
|
232
|
+
"maxFeePerBlobGas",
|
|
233
|
+
"blobVersionedHashes",
|
|
234
|
+
"kind",
|
|
235
|
+
"l1Fee",
|
|
236
|
+
"l1GasPrice",
|
|
237
|
+
"l1GasUsed",
|
|
238
|
+
"l1FeeScalar",
|
|
239
|
+
"gasUsedForL1"
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
"BlockField": {
|
|
243
|
+
"type": "string",
|
|
244
|
+
"enum": [
|
|
245
|
+
"parentHash",
|
|
246
|
+
"nonce",
|
|
247
|
+
"sha3Uncles",
|
|
248
|
+
"logsBloom",
|
|
249
|
+
"transactionsRoot",
|
|
250
|
+
"stateRoot",
|
|
251
|
+
"receiptsRoot",
|
|
252
|
+
"miner",
|
|
253
|
+
"difficulty",
|
|
254
|
+
"totalDifficulty",
|
|
255
|
+
"extraData",
|
|
256
|
+
"size",
|
|
257
|
+
"gasLimit",
|
|
258
|
+
"gasUsed",
|
|
259
|
+
"uncles",
|
|
260
|
+
"baseFeePerGas",
|
|
261
|
+
"blobGasUsed",
|
|
262
|
+
"excessBlobGas",
|
|
263
|
+
"parentBeaconBlockRoot",
|
|
264
|
+
"withdrawalsRoot",
|
|
265
|
+
"l1BlockNumber",
|
|
266
|
+
"sendCount",
|
|
267
|
+
"sendRoot",
|
|
268
|
+
"mixHash"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
169
271
|
"Network": {
|
|
170
272
|
"type": "object",
|
|
171
273
|
"properties": {
|
|
@@ -405,97 +507,6 @@
|
|
|
405
507
|
"viem",
|
|
406
508
|
"hypersync-client"
|
|
407
509
|
]
|
|
408
|
-
},
|
|
409
|
-
"FieldSelection": {
|
|
410
|
-
"type": "object",
|
|
411
|
-
"properties": {
|
|
412
|
-
"transaction_fields": {
|
|
413
|
-
"description": "Fields of a transaction to add to the event passed to handlers",
|
|
414
|
-
"type": [
|
|
415
|
-
"array",
|
|
416
|
-
"null"
|
|
417
|
-
],
|
|
418
|
-
"items": {
|
|
419
|
-
"$ref": "#/$defs/TransactionField"
|
|
420
|
-
}
|
|
421
|
-
},
|
|
422
|
-
"block_fields": {
|
|
423
|
-
"description": "Fields of a block to add to the event passed to handlers",
|
|
424
|
-
"type": [
|
|
425
|
-
"array",
|
|
426
|
-
"null"
|
|
427
|
-
],
|
|
428
|
-
"items": {
|
|
429
|
-
"$ref": "#/$defs/BlockField"
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
},
|
|
433
|
-
"additionalProperties": false
|
|
434
|
-
},
|
|
435
|
-
"TransactionField": {
|
|
436
|
-
"type": "string",
|
|
437
|
-
"enum": [
|
|
438
|
-
"transactionIndex",
|
|
439
|
-
"hash",
|
|
440
|
-
"from",
|
|
441
|
-
"to",
|
|
442
|
-
"gas",
|
|
443
|
-
"gasPrice",
|
|
444
|
-
"maxPriorityFeePerGas",
|
|
445
|
-
"maxFeePerGas",
|
|
446
|
-
"cumulativeGasUsed",
|
|
447
|
-
"effectiveGasPrice",
|
|
448
|
-
"gasUsed",
|
|
449
|
-
"input",
|
|
450
|
-
"nonce",
|
|
451
|
-
"value",
|
|
452
|
-
"v",
|
|
453
|
-
"r",
|
|
454
|
-
"s",
|
|
455
|
-
"contractAddress",
|
|
456
|
-
"logsBloom",
|
|
457
|
-
"root",
|
|
458
|
-
"status",
|
|
459
|
-
"yParity",
|
|
460
|
-
"chainId",
|
|
461
|
-
"maxFeePerBlobGas",
|
|
462
|
-
"blobVersionedHashes",
|
|
463
|
-
"kind",
|
|
464
|
-
"l1Fee",
|
|
465
|
-
"l1GasPrice",
|
|
466
|
-
"l1GasUsed",
|
|
467
|
-
"l1FeeScalar",
|
|
468
|
-
"gasUsedForL1"
|
|
469
|
-
]
|
|
470
|
-
},
|
|
471
|
-
"BlockField": {
|
|
472
|
-
"type": "string",
|
|
473
|
-
"enum": [
|
|
474
|
-
"parentHash",
|
|
475
|
-
"nonce",
|
|
476
|
-
"sha3Uncles",
|
|
477
|
-
"logsBloom",
|
|
478
|
-
"transactionsRoot",
|
|
479
|
-
"stateRoot",
|
|
480
|
-
"receiptsRoot",
|
|
481
|
-
"miner",
|
|
482
|
-
"difficulty",
|
|
483
|
-
"totalDifficulty",
|
|
484
|
-
"extraData",
|
|
485
|
-
"size",
|
|
486
|
-
"gasLimit",
|
|
487
|
-
"gasUsed",
|
|
488
|
-
"uncles",
|
|
489
|
-
"baseFeePerGas",
|
|
490
|
-
"blobGasUsed",
|
|
491
|
-
"excessBlobGas",
|
|
492
|
-
"parentBeaconBlockRoot",
|
|
493
|
-
"withdrawalsRoot",
|
|
494
|
-
"l1BlockNumber",
|
|
495
|
-
"sendCount",
|
|
496
|
-
"sendRoot",
|
|
497
|
-
"mixHash"
|
|
498
|
-
]
|
|
499
510
|
}
|
|
500
511
|
}
|
|
501
512
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.
|
|
3
|
+
"version": "v2.11.0",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://envio.dev",
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"envio-linux-x64": "v2.
|
|
27
|
-
"envio-linux-arm64": "v2.
|
|
28
|
-
"envio-darwin-x64": "v2.
|
|
29
|
-
"envio-darwin-arm64": "v2.
|
|
26
|
+
"envio-linux-x64": "v2.11.0",
|
|
27
|
+
"envio-linux-arm64": "v2.11.0",
|
|
28
|
+
"envio-darwin-x64": "v2.11.0",
|
|
29
|
+
"envio-darwin-arm64": "v2.11.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@envio-dev/hypersync-client": "0.6.2",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* TypeScript file generated from Internal.res by genType. */
|
|
2
|
+
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
/* tslint:disable */
|
|
5
|
+
|
|
6
|
+
import type {t as Address_t} from './Address.gen';
|
|
7
|
+
|
|
8
|
+
export type genericEvent<params,block,transaction> = {
|
|
9
|
+
readonly params: params;
|
|
10
|
+
readonly chainId: number;
|
|
11
|
+
readonly srcAddress: Address_t;
|
|
12
|
+
readonly logIndex: number;
|
|
13
|
+
readonly transaction: transaction;
|
|
14
|
+
readonly block: block
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type genericLoaderArgs<event,context> = { readonly event: event; readonly context: context };
|
|
18
|
+
|
|
19
|
+
export type genericLoader<args,loaderReturn> = (_1:args) => Promise<loaderReturn>;
|
|
20
|
+
|
|
21
|
+
export type genericContractRegisterArgs<event,context> = { readonly event: event; readonly context: context };
|
|
22
|
+
|
|
23
|
+
export type genericContractRegister<args> = (_1:args) => void;
|
|
24
|
+
|
|
25
|
+
export type genericHandlerArgs<event,context,loaderReturn> = {
|
|
26
|
+
readonly event: event;
|
|
27
|
+
readonly context: context;
|
|
28
|
+
readonly loaderReturn: loaderReturn
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type genericHandler<args> = (_1:args) => Promise<void>;
|
|
32
|
+
|
|
33
|
+
export type genericHandlerWithLoader<loader,handler,eventFilters> = {
|
|
34
|
+
readonly loader: loader;
|
|
35
|
+
readonly handler: handler;
|
|
36
|
+
readonly wildcard?: boolean;
|
|
37
|
+
readonly eventFilters?: eventFilters;
|
|
38
|
+
readonly preRegisterDynamicContracts?: boolean
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type fuelSupplyParams = { readonly subId: string; readonly amount: bigint };
|
|
42
|
+
|
|
43
|
+
export type fuelTransferParams = {
|
|
44
|
+
readonly to: Address_t;
|
|
45
|
+
readonly assetId: string;
|
|
46
|
+
readonly amount: bigint
|
|
47
|
+
};
|
package/src/Internal.res
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
type eventParams
|
|
2
|
+
type eventBlock
|
|
3
|
+
type eventTransaction
|
|
4
|
+
|
|
5
|
+
@genType
|
|
6
|
+
type genericEvent<'params, 'block, 'transaction> = {
|
|
7
|
+
params: 'params,
|
|
8
|
+
chainId: int,
|
|
9
|
+
srcAddress: Address.t,
|
|
10
|
+
logIndex: int,
|
|
11
|
+
transaction: 'transaction,
|
|
12
|
+
block: 'block,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type event = genericEvent<eventParams, eventBlock, eventTransaction>
|
|
16
|
+
|
|
17
|
+
external fromGenericEvent: genericEvent<'a, 'b, 'c> => event = "%identity"
|
|
18
|
+
|
|
19
|
+
type loaderReturn
|
|
20
|
+
|
|
21
|
+
@genType
|
|
22
|
+
type genericLoaderArgs<'event, 'context> = {
|
|
23
|
+
event: 'event,
|
|
24
|
+
context: 'context,
|
|
25
|
+
}
|
|
26
|
+
@genType
|
|
27
|
+
type genericLoader<'args, 'loaderReturn> = 'args => promise<'loaderReturn>
|
|
28
|
+
|
|
29
|
+
type loaderContext
|
|
30
|
+
type loaderArgs = genericLoaderArgs<event, loaderContext>
|
|
31
|
+
type loader = genericLoader<loaderArgs, loaderReturn>
|
|
32
|
+
|
|
33
|
+
@genType
|
|
34
|
+
type genericContractRegisterArgs<'event, 'context> = {
|
|
35
|
+
event: 'event,
|
|
36
|
+
context: 'context,
|
|
37
|
+
}
|
|
38
|
+
@genType
|
|
39
|
+
type genericContractRegister<'args> = 'args => unit
|
|
40
|
+
|
|
41
|
+
type contractRegisterContext
|
|
42
|
+
type contractRegisterArgs = genericContractRegisterArgs<event, contractRegisterContext>
|
|
43
|
+
type contractRegister = genericContractRegister<contractRegisterArgs>
|
|
44
|
+
|
|
45
|
+
@genType
|
|
46
|
+
type genericHandlerArgs<'event, 'context, 'loaderReturn> = {
|
|
47
|
+
event: 'event,
|
|
48
|
+
context: 'context,
|
|
49
|
+
loaderReturn: 'loaderReturn,
|
|
50
|
+
}
|
|
51
|
+
@genType
|
|
52
|
+
type genericHandler<'args> = 'args => promise<unit>
|
|
53
|
+
|
|
54
|
+
type handlerContext
|
|
55
|
+
type handlerArgs = genericHandlerArgs<event, handlerContext, loaderReturn>
|
|
56
|
+
type handler = genericHandler<handlerArgs>
|
|
57
|
+
|
|
58
|
+
@genType
|
|
59
|
+
type genericHandlerWithLoader<'loader, 'handler, 'eventFilters> = {
|
|
60
|
+
loader: 'loader,
|
|
61
|
+
handler: 'handler,
|
|
62
|
+
wildcard?: bool,
|
|
63
|
+
eventFilters?: 'eventFilters,
|
|
64
|
+
preRegisterDynamicContracts?: bool,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type eventItem = {
|
|
68
|
+
eventName: string,
|
|
69
|
+
contractName: string,
|
|
70
|
+
loader: option<loader>,
|
|
71
|
+
handler: option<handler>,
|
|
72
|
+
contractRegister: option<contractRegister>,
|
|
73
|
+
timestamp: int,
|
|
74
|
+
chain: ChainMap.Chain.t,
|
|
75
|
+
blockNumber: int,
|
|
76
|
+
logIndex: int,
|
|
77
|
+
event: event,
|
|
78
|
+
paramsRawEventSchema: S.schema<eventParams>,
|
|
79
|
+
//Default to false, if an event needs to
|
|
80
|
+
//be reprocessed after it has loaded dynamic contracts
|
|
81
|
+
//This gets set to true and does not try and reload events
|
|
82
|
+
hasRegisteredDynamicContracts?: bool,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type fuelEventKind =
|
|
86
|
+
| LogData({logId: string, decode: string => eventParams})
|
|
87
|
+
| Mint
|
|
88
|
+
| Burn
|
|
89
|
+
| Transfer
|
|
90
|
+
| Call
|
|
91
|
+
type fuelEventConfig = {
|
|
92
|
+
name: string,
|
|
93
|
+
kind: fuelEventKind,
|
|
94
|
+
isWildcard: bool,
|
|
95
|
+
loader: option<loader>,
|
|
96
|
+
handler: option<handler>,
|
|
97
|
+
contractRegister: option<contractRegister>,
|
|
98
|
+
paramsRawEventSchema: S.schema<eventParams>,
|
|
99
|
+
}
|
|
100
|
+
type fuelContractConfig = {
|
|
101
|
+
name: string,
|
|
102
|
+
events: array<fuelEventConfig>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@genType
|
|
106
|
+
type fuelSupplyParams = {
|
|
107
|
+
subId: string,
|
|
108
|
+
amount: bigint,
|
|
109
|
+
}
|
|
110
|
+
let fuelSupplyParamsSchema = S.schema(s => {
|
|
111
|
+
subId: s.matches(S.string),
|
|
112
|
+
amount: s.matches(BigInt.schema),
|
|
113
|
+
})
|
|
114
|
+
@genType
|
|
115
|
+
type fuelTransferParams = {
|
|
116
|
+
to: Address.t,
|
|
117
|
+
assetId: string,
|
|
118
|
+
amount: bigint,
|
|
119
|
+
}
|
|
120
|
+
let fuelTransferParamsSchema = S.schema(s => {
|
|
121
|
+
to: s.matches(Address.schema),
|
|
122
|
+
assetId: s.matches(S.string),
|
|
123
|
+
amount: s.matches(BigInt.schema),
|
|
124
|
+
})
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
exception LoaderTimeout(string)
|
|
2
|
+
|
|
3
|
+
type rec asyncMap<'key, 'value> = {
|
|
4
|
+
// The number of loaded results to keep cached. (For block loading this should be our maximum block interval)
|
|
5
|
+
_cacheSize: int,
|
|
6
|
+
// The maximum number of results we can try to load simultaneously.
|
|
7
|
+
_loaderPoolSize: int,
|
|
8
|
+
// How long to wait before retrying failures
|
|
9
|
+
// TODO: Add randomized exponential back-off (outdated)
|
|
10
|
+
_retryDelayMillis: int,
|
|
11
|
+
// How long to wait before cancelling a load request
|
|
12
|
+
_timeoutMillis: int,
|
|
13
|
+
// The promises we return to callers. We satisfy them asynchronously.
|
|
14
|
+
externalPromises: Utils.Map.t<'key, promise<'value>>,
|
|
15
|
+
// The handled used to populate the external promises once we have loaded their data.
|
|
16
|
+
resolvers: Utils.Map.t<'key, 'value => unit>,
|
|
17
|
+
// The keys currently being loaded
|
|
18
|
+
inProgress: Utils.Set.t<'key>,
|
|
19
|
+
// Keys for items that we have not started loading yet.
|
|
20
|
+
loaderQueue: SDSL.Queue.t<'key>,
|
|
21
|
+
// Keys for items that have been loaded already. Used to evict the oldest keys from cache.
|
|
22
|
+
loadedKeys: SDSL.Queue.t<'key>,
|
|
23
|
+
// The function used to load the result.
|
|
24
|
+
loaderFn: 'key => promise<'value>,
|
|
25
|
+
// Callback on load error
|
|
26
|
+
onError: option<(asyncMap<'key, 'value>, ~exn: exn) => unit>,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let make = (
|
|
30
|
+
~loaderFn,
|
|
31
|
+
~onError=?,
|
|
32
|
+
~cacheSize: int=10_000,
|
|
33
|
+
~loaderPoolSize: int=10,
|
|
34
|
+
~retryDelayMillis=5_000,
|
|
35
|
+
~timeoutMillis=300_000,
|
|
36
|
+
) => // After 5 minutes (unclear what is best to do here - crash or just keep printing the error)
|
|
37
|
+
{
|
|
38
|
+
_cacheSize: cacheSize,
|
|
39
|
+
_loaderPoolSize: loaderPoolSize,
|
|
40
|
+
_retryDelayMillis: retryDelayMillis,
|
|
41
|
+
_timeoutMillis: timeoutMillis,
|
|
42
|
+
externalPromises: Utils.Map.make(),
|
|
43
|
+
resolvers: Utils.Map.make(),
|
|
44
|
+
inProgress: Utils.Set.make(),
|
|
45
|
+
loaderQueue: SDSL.Queue.make(),
|
|
46
|
+
loadedKeys: SDSL.Queue.make(),
|
|
47
|
+
loaderFn,
|
|
48
|
+
onError,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let deleteKey: (dict<'value>, string) => unit = (_obj, _k) => %raw(`delete _obj[_k]`)
|
|
52
|
+
|
|
53
|
+
// If something takes longer than this to load, reject the promise and try again
|
|
54
|
+
let timeoutAfter = timeoutMillis =>
|
|
55
|
+
Utils.delay(timeoutMillis)->Promise.then(() =>
|
|
56
|
+
Promise.reject(
|
|
57
|
+
LoaderTimeout(`Query took longer than ${Belt.Int.toString(timeoutMillis / 1000)} seconds`),
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
let rec loadNext = async (am: asyncMap<'key, 'value>, k: 'key) => {
|
|
62
|
+
// Track that we are loading it now
|
|
63
|
+
let _ = am.inProgress->Utils.Set.add(k)
|
|
64
|
+
|
|
65
|
+
let awaitTaskPromiseAndLoadNextWithTimeout = async () => {
|
|
66
|
+
let val = await Promise.race([am.loaderFn(k), timeoutAfter(am._timeoutMillis)])
|
|
67
|
+
// Resolve the external promise
|
|
68
|
+
am.resolvers
|
|
69
|
+
->Utils.Map.get(k)
|
|
70
|
+
->Belt.Option.forEach(r => {
|
|
71
|
+
let _ = am.resolvers->Utils.Map.delete(k)
|
|
72
|
+
r(val)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Track that it is no longer in progress
|
|
76
|
+
let _ = am.inProgress->Utils.Set.delete(k)
|
|
77
|
+
|
|
78
|
+
// Track that we've loaded this key
|
|
79
|
+
let loadedKeysNumber = am.loadedKeys->SDSL.Queue.push(k)
|
|
80
|
+
|
|
81
|
+
// Delete the oldest key if the cache is overly full
|
|
82
|
+
if loadedKeysNumber > am._cacheSize {
|
|
83
|
+
switch am.loadedKeys->SDSL.Queue.pop {
|
|
84
|
+
| None => ()
|
|
85
|
+
| Some(old) =>
|
|
86
|
+
let _ = am.externalPromises->Utils.Map.delete(old)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Load the next one, if there is anything in the queue
|
|
91
|
+
switch am.loaderQueue->SDSL.Queue.pop {
|
|
92
|
+
| None => ()
|
|
93
|
+
| Some(next) => await loadNext(am, next)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await (
|
|
98
|
+
switch await awaitTaskPromiseAndLoadNextWithTimeout() {
|
|
99
|
+
| _ => Promise.resolve()
|
|
100
|
+
| exception err =>
|
|
101
|
+
switch am.onError {
|
|
102
|
+
| None => ()
|
|
103
|
+
| Some(onError) => onError(am, ~exn=err)
|
|
104
|
+
}
|
|
105
|
+
await Utils.delay(am._retryDelayMillis)
|
|
106
|
+
awaitTaskPromiseAndLoadNextWithTimeout()
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let get = (am: asyncMap<'key, 'value>, k: 'key): promise<'value> => {
|
|
112
|
+
switch am.externalPromises->Utils.Map.get(k) {
|
|
113
|
+
| Some(x) => x
|
|
114
|
+
| None => {
|
|
115
|
+
// Create a promise to deliver the eventual value asynchronously
|
|
116
|
+
let promise = Promise.make((resolve, _) => {
|
|
117
|
+
// Expose the resolver externally, so that we can run it from the loader.
|
|
118
|
+
let _ = am.resolvers->Utils.Map.set(k, resolve)
|
|
119
|
+
})
|
|
120
|
+
// Cache the promise to de-duplicate requests
|
|
121
|
+
let _ = am.externalPromises->Utils.Map.set(k, promise)
|
|
122
|
+
|
|
123
|
+
// Do we have a free loader in the pool?
|
|
124
|
+
if am.inProgress->Utils.Set.size < am._loaderPoolSize {
|
|
125
|
+
loadNext(am, k)->ignore
|
|
126
|
+
} else {
|
|
127
|
+
// Queue the loader
|
|
128
|
+
let _ = am.loaderQueue->SDSL.Queue.push(k)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
promise
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|