envio 2.32.0 → 2.32.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "v2.32.0",
3
+ "version": "v2.32.2",
4
4
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
5
5
  "bin": "./bin.js",
6
6
  "main": "./index.js",
@@ -25,10 +25,10 @@
25
25
  },
26
26
  "homepage": "https://envio.dev",
27
27
  "optionalDependencies": {
28
- "envio-linux-x64": "v2.32.0",
29
- "envio-linux-arm64": "v2.32.0",
30
- "envio-darwin-x64": "v2.32.0",
31
- "envio-darwin-arm64": "v2.32.0"
28
+ "envio-linux-x64": "v2.32.2",
29
+ "envio-linux-arm64": "v2.32.2",
30
+ "envio-darwin-x64": "v2.32.2",
31
+ "envio-darwin-arm64": "v2.32.2"
32
32
  },
33
33
  "dependencies": {
34
34
  "@envio-dev/hypersync-client": "0.6.6",
@@ -0,0 +1,6 @@
1
+ /* TypeScript file generated from InMemoryStore.res by genType. */
2
+
3
+ /* eslint-disable */
4
+ /* tslint:disable */
5
+
6
+ export type rawEventsKey = { readonly chainId: number; readonly eventId: string };
@@ -0,0 +1,157 @@
1
+ @genType
2
+ type rawEventsKey = {
3
+ chainId: int,
4
+ eventId: string,
5
+ }
6
+
7
+ let hashRawEventsKey = (key: rawEventsKey) =>
8
+ EventUtils.getEventIdKeyString(~chainId=key.chainId, ~eventId=key.eventId)
9
+
10
+ module EntityTables = {
11
+ type t = dict<InMemoryTable.Entity.t<Internal.entity>>
12
+ exception UndefinedEntity({entityName: string})
13
+ let make = (entities: array<Internal.entityConfig>): t => {
14
+ let init = Js.Dict.empty()
15
+ entities->Belt.Array.forEach(entityConfig => {
16
+ init->Js.Dict.set((entityConfig.name :> string), InMemoryTable.Entity.make())
17
+ })
18
+ init
19
+ }
20
+
21
+ let get = (type entity, self: t, ~entityName: string) => {
22
+ switch self->Utils.Dict.dangerouslyGetNonOption(entityName) {
23
+ | Some(table) =>
24
+ table->(
25
+ Utils.magic: InMemoryTable.Entity.t<Internal.entity> => InMemoryTable.Entity.t<
26
+ entity,
27
+ >
28
+ )
29
+
30
+ | None =>
31
+ UndefinedEntity({entityName: entityName})->ErrorHandling.mkLogAndRaise(
32
+ ~msg="Unexpected, entity InMemoryTable is undefined",
33
+ )
34
+ }
35
+ }
36
+
37
+ let clone = (self: t) => {
38
+ self
39
+ ->Js.Dict.entries
40
+ ->Belt.Array.map(((k, v)) => (k, v->InMemoryTable.Entity.clone))
41
+ ->Js.Dict.fromArray
42
+ }
43
+ }
44
+
45
+ type effectCacheInMemTable = {
46
+ idsToStore: array<string>,
47
+ mutable invalidationsCount: int,
48
+ dict: dict<Internal.effectOutput>,
49
+ effect: Internal.effect,
50
+ }
51
+
52
+ type t = {
53
+ rawEvents: InMemoryTable.t<rawEventsKey, InternalTable.RawEvents.t>,
54
+ entities: dict<InMemoryTable.Entity.t<Internal.entity>>,
55
+ effects: dict<effectCacheInMemTable>,
56
+ rollbackTargetCheckpointId: option<int>,
57
+ }
58
+
59
+ let make = (
60
+ ~entities: array<Internal.entityConfig>,
61
+ ~rollbackTargetCheckpointId=?,
62
+ ): t => {
63
+ rawEvents: InMemoryTable.make(~hash=hashRawEventsKey),
64
+ entities: EntityTables.make(entities),
65
+ effects: Js.Dict.empty(),
66
+ rollbackTargetCheckpointId,
67
+ }
68
+
69
+ let clone = (self: t) => {
70
+ rawEvents: self.rawEvents->InMemoryTable.clone,
71
+ entities: self.entities->EntityTables.clone,
72
+ effects: Js.Dict.map(table => {
73
+ idsToStore: table.idsToStore->Array.copy,
74
+ invalidationsCount: table.invalidationsCount,
75
+ dict: table.dict->Utils.Dict.shallowCopy,
76
+ effect: table.effect,
77
+ }, self.effects),
78
+ rollbackTargetCheckpointId: self.rollbackTargetCheckpointId,
79
+ }
80
+
81
+ let getEffectInMemTable = (inMemoryStore: t, ~effect: Internal.effect) => {
82
+ let key = effect.name
83
+ switch inMemoryStore.effects->Utils.Dict.dangerouslyGetNonOption(key) {
84
+ | Some(table) => table
85
+ | None =>
86
+ let table = {
87
+ idsToStore: [],
88
+ dict: Js.Dict.empty(),
89
+ invalidationsCount: 0,
90
+ effect,
91
+ }
92
+ inMemoryStore.effects->Js.Dict.set(key, table)
93
+ table
94
+ }
95
+ }
96
+
97
+ let getInMemTable = (
98
+ inMemoryStore: t,
99
+ ~entityConfig: Internal.entityConfig,
100
+ ): InMemoryTable.Entity.t<Internal.entity> => {
101
+ inMemoryStore.entities->EntityTables.get(~entityName=entityConfig.name)
102
+ }
103
+
104
+ let isRollingBack = (inMemoryStore: t) => inMemoryStore.rollbackTargetCheckpointId !== None
105
+
106
+ let setBatchDcs = (inMemoryStore: t, ~batch: Batch.t, ~shouldSaveHistory) => {
107
+ let inMemTable =
108
+ inMemoryStore->getInMemTable(
109
+ ~entityConfig=InternalTable.DynamicContractRegistry.config,
110
+ )
111
+
112
+ let itemIdx = ref(0)
113
+
114
+ for checkpoint in 0 to batch.checkpointIds->Array.length - 1 {
115
+ let checkpointId = batch.checkpointIds->Js.Array2.unsafe_get(checkpoint)
116
+ let chainId = batch.checkpointChainIds->Js.Array2.unsafe_get(checkpoint)
117
+ let checkpointEventsProcessed =
118
+ batch.checkpointEventsProcessed->Js.Array2.unsafe_get(checkpoint)
119
+
120
+ for idx in 0 to checkpointEventsProcessed - 1 {
121
+ let item = batch.items->Js.Array2.unsafe_get(itemIdx.contents + idx)
122
+ switch item->Internal.getItemDcs {
123
+ | None => ()
124
+ | Some(dcs) =>
125
+ // Currently only events support contract registration, so we can cast to event item
126
+ let eventItem = item->Internal.castUnsafeEventItem
127
+ for dcIdx in 0 to dcs->Array.length - 1 {
128
+ let dc = dcs->Js.Array2.unsafe_get(dcIdx)
129
+ let entity: InternalTable.DynamicContractRegistry.t = {
130
+ id: InternalTable.DynamicContractRegistry.makeId(~chainId, ~contractAddress=dc.address),
131
+ chainId,
132
+ contractAddress: dc.address,
133
+ contractName: dc.contractName,
134
+ registeringEventBlockNumber: eventItem.blockNumber,
135
+ registeringEventLogIndex: eventItem.logIndex,
136
+ registeringEventBlockTimestamp: eventItem.timestamp,
137
+ registeringEventContractName: eventItem.eventConfig.contractName,
138
+ registeringEventName: eventItem.eventConfig.name,
139
+ registeringEventSrcAddress: eventItem.event.srcAddress,
140
+ }
141
+
142
+ inMemTable->InMemoryTable.Entity.set(
143
+ {
144
+ entityId: entity.id,
145
+ checkpointId,
146
+ entityUpdateAction: Set(entity->InternalTable.DynamicContractRegistry.castToInternal),
147
+ },
148
+ ~shouldSaveHistory,
149
+ )
150
+ }
151
+ }
152
+ }
153
+
154
+ itemIdx := itemIdx.contents + checkpointEventsProcessed
155
+ }
156
+ }
157
+
@@ -0,0 +1,163 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ 'use strict';
3
+
4
+ var $$Array = require("rescript/lib/js/array.js");
5
+ var Utils = require("./Utils.res.js");
6
+ var Js_dict = require("rescript/lib/js/js_dict.js");
7
+ var Belt_Array = require("rescript/lib/js/belt_Array.js");
8
+ var EventUtils = require("./EventUtils.res.js");
9
+ var ErrorHandling = require("./ErrorHandling.res.js");
10
+ var InMemoryTable = require("./InMemoryTable.res.js");
11
+ var InternalTable = require("./db/InternalTable.res.js");
12
+ var Caml_exceptions = require("rescript/lib/js/caml_exceptions.js");
13
+
14
+ function hashRawEventsKey(key) {
15
+ return EventUtils.getEventIdKeyString(key.chainId, key.eventId);
16
+ }
17
+
18
+ var UndefinedEntity = /* @__PURE__ */Caml_exceptions.create("InMemoryStore.EntityTables.UndefinedEntity");
19
+
20
+ function make(entities) {
21
+ var init = {};
22
+ Belt_Array.forEach(entities, (function (entityConfig) {
23
+ init[entityConfig.name] = InMemoryTable.Entity.make();
24
+ }));
25
+ return init;
26
+ }
27
+
28
+ function get(self, entityName) {
29
+ var table = self[entityName];
30
+ if (table !== undefined) {
31
+ return table;
32
+ } else {
33
+ return ErrorHandling.mkLogAndRaise(undefined, "Unexpected, entity InMemoryTable is undefined", {
34
+ RE_EXN_ID: UndefinedEntity,
35
+ entityName: entityName
36
+ });
37
+ }
38
+ }
39
+
40
+ function clone(self) {
41
+ return Js_dict.fromArray(Belt_Array.map(Js_dict.entries(self), (function (param) {
42
+ return [
43
+ param[0],
44
+ InMemoryTable.Entity.clone(param[1])
45
+ ];
46
+ })));
47
+ }
48
+
49
+ var EntityTables = {
50
+ UndefinedEntity: UndefinedEntity,
51
+ make: make,
52
+ get: get,
53
+ clone: clone
54
+ };
55
+
56
+ function make$1(entities, rollbackTargetCheckpointId) {
57
+ return {
58
+ rawEvents: InMemoryTable.make(hashRawEventsKey),
59
+ entities: make(entities),
60
+ effects: {},
61
+ rollbackTargetCheckpointId: rollbackTargetCheckpointId
62
+ };
63
+ }
64
+
65
+ function clone$1(self) {
66
+ return {
67
+ rawEvents: InMemoryTable.clone(self.rawEvents),
68
+ entities: clone(self.entities),
69
+ effects: Js_dict.map((function (table) {
70
+ return {
71
+ idsToStore: $$Array.copy(table.idsToStore),
72
+ invalidationsCount: table.invalidationsCount,
73
+ dict: Utils.Dict.shallowCopy(table.dict),
74
+ effect: table.effect
75
+ };
76
+ }), self.effects),
77
+ rollbackTargetCheckpointId: self.rollbackTargetCheckpointId
78
+ };
79
+ }
80
+
81
+ function getEffectInMemTable(inMemoryStore, effect) {
82
+ var key = effect.name;
83
+ var table = inMemoryStore.effects[key];
84
+ if (table !== undefined) {
85
+ return table;
86
+ }
87
+ var table$1 = {
88
+ idsToStore: [],
89
+ invalidationsCount: 0,
90
+ dict: {},
91
+ effect: effect
92
+ };
93
+ inMemoryStore.effects[key] = table$1;
94
+ return table$1;
95
+ }
96
+
97
+ function getInMemTable(inMemoryStore, entityConfig) {
98
+ return get(inMemoryStore.entities, entityConfig.name);
99
+ }
100
+
101
+ function isRollingBack(inMemoryStore) {
102
+ return inMemoryStore.rollbackTargetCheckpointId !== undefined;
103
+ }
104
+
105
+ function setBatchDcs(inMemoryStore, batch, shouldSaveHistory) {
106
+ var inMemTable = getInMemTable(inMemoryStore, InternalTable.DynamicContractRegistry.config);
107
+ var itemIdx = 0;
108
+ for(var checkpoint = 0 ,checkpoint_finish = batch.checkpointIds.length; checkpoint < checkpoint_finish; ++checkpoint){
109
+ var checkpointId = batch.checkpointIds[checkpoint];
110
+ var chainId = batch.checkpointChainIds[checkpoint];
111
+ var checkpointEventsProcessed = batch.checkpointEventsProcessed[checkpoint];
112
+ for(var idx = 0; idx < checkpointEventsProcessed; ++idx){
113
+ var item = batch.items[itemIdx + idx | 0];
114
+ var dcs = item.dcs;
115
+ if (dcs !== undefined) {
116
+ for(var dcIdx = 0 ,dcIdx_finish = dcs.length; dcIdx < dcIdx_finish; ++dcIdx){
117
+ var dc = dcs[dcIdx];
118
+ var entity_id = InternalTable.DynamicContractRegistry.makeId(chainId, dc.address);
119
+ var entity_registering_event_block_number = item.blockNumber;
120
+ var entity_registering_event_log_index = item.logIndex;
121
+ var entity_registering_event_block_timestamp = item.timestamp;
122
+ var entity_registering_event_contract_name = item.eventConfig.contractName;
123
+ var entity_registering_event_name = item.eventConfig.name;
124
+ var entity_registering_event_src_address = item.event.srcAddress;
125
+ var entity_contract_address = dc.address;
126
+ var entity_contract_name = dc.contractName;
127
+ var entity = {
128
+ id: entity_id,
129
+ chain_id: chainId,
130
+ registering_event_block_number: entity_registering_event_block_number,
131
+ registering_event_log_index: entity_registering_event_log_index,
132
+ registering_event_block_timestamp: entity_registering_event_block_timestamp,
133
+ registering_event_contract_name: entity_registering_event_contract_name,
134
+ registering_event_name: entity_registering_event_name,
135
+ registering_event_src_address: entity_registering_event_src_address,
136
+ contract_address: entity_contract_address,
137
+ contract_name: entity_contract_name
138
+ };
139
+ InMemoryTable.Entity.set(inMemTable, {
140
+ entityId: entity_id,
141
+ entityUpdateAction: {
142
+ TAG: "Set",
143
+ _0: entity
144
+ },
145
+ checkpointId: checkpointId
146
+ }, shouldSaveHistory, undefined);
147
+ }
148
+ }
149
+
150
+ }
151
+ itemIdx = itemIdx + checkpointEventsProcessed | 0;
152
+ }
153
+ }
154
+
155
+ exports.hashRawEventsKey = hashRawEventsKey;
156
+ exports.EntityTables = EntityTables;
157
+ exports.make = make$1;
158
+ exports.clone = clone$1;
159
+ exports.getEffectInMemTable = getEffectInMemTable;
160
+ exports.getInMemTable = getInMemTable;
161
+ exports.isRollingBack = isRollingBack;
162
+ exports.setBatchDcs = setBatchDcs;
163
+ /* Utils Not a pure module */
@@ -7,6 +7,7 @@ module Call = {
7
7
  input: input,
8
8
  resolve: output => unit,
9
9
  reject: exn => unit,
10
+ mutable exn: option<exn>,
10
11
  mutable promise: promise<output>,
11
12
  mutable isLoading: bool,
12
13
  }
@@ -16,7 +17,7 @@ module Group = {
16
17
  type t = {
17
18
  // Unique calls by input as a key
18
19
  calls: dict<Call.t>,
19
- load: array<Call.input> => promise<unit>,
20
+ load: (array<Call.input>, ~onError: (~inputKey: string, ~exn: exn) => unit) => promise<unit>,
20
21
  getUnsafeInMemory: string => Call.output,
21
22
  hasInMemory: string => bool,
22
23
  }
@@ -66,32 +67,32 @@ let schedule = async loadManager => {
66
67
  }
67
68
  })
68
69
 
69
- let isSuccess = if inputsToLoad->Utils.Array.isEmpty->not {
70
+ if inputsToLoad->Utils.Array.isEmpty->not {
70
71
  try {
71
- await group.load(inputsToLoad)
72
- true
72
+ await group.load(inputsToLoad, ~onError=(~inputKey, ~exn) => {
73
+ let call = calls->Js.Dict.unsafeGet(inputKey)
74
+ call.exn = Some(exn)
75
+ })
73
76
  } catch {
74
77
  | exn => {
75
78
  let exn = exn->Utils.prettifyExn
76
79
  currentInputKeys->Array.forEach(inputKey => {
77
80
  let call = calls->Js.Dict.unsafeGet(inputKey)
78
- call.reject(exn)
81
+ call.exn = Some(exn)
79
82
  })
80
- false
81
83
  }
82
84
  }
83
- } else {
84
- true
85
85
  }
86
86
 
87
87
  if currentInputKeys->Utils.Array.isEmpty->not {
88
- if isSuccess {
89
- currentInputKeys->Js.Array2.forEach(inputKey => {
90
- let call = calls->Js.Dict.unsafeGet(inputKey)
91
- calls->Utils.Dict.deleteInPlace(inputKey)
92
- call.resolve(group.getUnsafeInMemory(inputKey))
93
- })
94
- }
88
+ currentInputKeys->Js.Array2.forEach(inputKey => {
89
+ let call = calls->Js.Dict.unsafeGet(inputKey)
90
+ calls->Utils.Dict.deleteInPlace(inputKey)
91
+ switch call.exn {
92
+ | Some(exn) => call.reject(exn->Utils.prettifyExn)
93
+ | None => call.resolve(group.getUnsafeInMemory(inputKey))
94
+ }
95
+ })
95
96
 
96
97
  // Clean up executed batch to reset
97
98
  // provided load function which
@@ -145,7 +146,12 @@ let call = (
145
146
  let g: Group.t = {
146
147
  calls: Js.Dict.empty(),
147
148
  load: load->(
148
- Utils.magic: (array<'input> => promise<unit>) => array<Call.input> => promise<unit>
149
+ Utils.magic: (
150
+ (array<'input>, ~onError: (~inputKey: string, ~exn: exn) => unit) => promise<unit>
151
+ ) => (
152
+ array<Call.input>,
153
+ ~onError: (~inputKey: string, ~exn: exn) => unit,
154
+ ) => promise<unit>
149
155
  ),
150
156
  getUnsafeInMemory: getUnsafeInMemory->(
151
157
  Utils.magic: (string => 'output) => string => Call.output
@@ -166,6 +172,7 @@ let call = (
166
172
  resolve,
167
173
  reject,
168
174
  promise: %raw(`null`),
175
+ exn: None,
169
176
  isLoading: false,
170
177
  }
171
178
  group.calls->Js.Dict.set(inputKey, call)
@@ -40,34 +40,35 @@ async function schedule(loadManager) {
40
40
  }
41
41
 
42
42
  });
43
- var isSuccess;
44
- if (Utils.$$Array.isEmpty(inputsToLoad)) {
45
- isSuccess = true;
46
- } else {
43
+ if (!Utils.$$Array.isEmpty(inputsToLoad)) {
47
44
  try {
48
- await group.load(inputsToLoad);
49
- isSuccess = true;
45
+ await group.load(inputsToLoad, (function (inputKey, exn) {
46
+ var call = calls[inputKey];
47
+ call.exn = exn;
48
+ }));
50
49
  }
51
50
  catch (raw_exn){
52
51
  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
53
52
  var exn$1 = Utils.prettifyExn(exn);
54
53
  Belt_Array.forEach(currentInputKeys, (function (inputKey) {
55
54
  var call = calls[inputKey];
56
- call.reject(exn$1);
55
+ call.exn = exn$1;
57
56
  }));
58
- isSuccess = false;
59
57
  }
60
58
  }
61
59
  if (Utils.$$Array.isEmpty(currentInputKeys)) {
62
60
  return ;
63
61
  }
64
- if (isSuccess) {
65
- currentInputKeys.forEach(function (inputKey) {
66
- var call = calls[inputKey];
67
- Utils.Dict.deleteInPlace(calls, inputKey);
68
- call.resolve(group.getUnsafeInMemory(inputKey));
69
- });
70
- }
62
+ currentInputKeys.forEach(function (inputKey) {
63
+ var call = calls[inputKey];
64
+ Utils.Dict.deleteInPlace(calls, inputKey);
65
+ var exn = call.exn;
66
+ if (exn !== undefined) {
67
+ return call.reject(Utils.prettifyExn(exn));
68
+ } else {
69
+ return call.resolve(group.getUnsafeInMemory(inputKey));
70
+ }
71
+ });
71
72
  var latestGroup = groups[key];
72
73
  if (Utils.$$Array.isEmpty(Object.keys(latestGroup.calls))) {
73
74
  return Utils.Dict.deleteInPlace(groups, key);
@@ -109,6 +110,7 @@ function call(loadManager, input, key, load, hasher, shouldGroup, hasInMemory, g
109
110
  input: input,
110
111
  resolve: resolve,
111
112
  reject: reject,
113
+ exn: undefined,
112
114
  promise: null,
113
115
  isLoading: false
114
116
  };
@@ -653,6 +653,12 @@ module EffectQueueCount = {
653
653
  ~labelSchema=effectLabelsSchema,
654
654
  )
655
655
 
656
+ let timeCounter = SafeCounter.makeOrThrow(
657
+ ~name="envio_effect_queue_time",
658
+ ~help="The time spent waiting in the rate limit queue. (milliseconds)",
659
+ ~labelSchema=effectLabelsSchema,
660
+ )
661
+
656
662
  let set = (~count, ~effectName) => {
657
663
  gauge->SafeGauge.handleInt(~labels=effectName, ~value=count)
658
664
  }
@@ -742,12 +742,15 @@ var EffectCacheInvalidationsCount = {
742
742
 
743
743
  var gauge$22 = makeOrThrow$1("envio_effect_queue_count", "The number of effect calls waiting in the rate limit queue.", effectLabelsSchema);
744
744
 
745
+ var timeCounter$3 = makeOrThrow("envio_effect_queue_time", "The time spent waiting in the rate limit queue. (milliseconds)", effectLabelsSchema);
746
+
745
747
  function set$21(count, effectName) {
746
748
  handleInt$1(gauge$22, effectName, count);
747
749
  }
748
750
 
749
751
  var EffectQueueCount = {
750
752
  gauge: gauge$22,
753
+ timeCounter: timeCounter$3,
751
754
  set: set$21
752
755
  };
753
756
 
@@ -755,7 +758,7 @@ var operationLabelsSchema = S$RescriptSchema.object(function (s) {
755
758
  return s.f("operation", S$RescriptSchema.string);
756
759
  });
757
760
 
758
- var timeCounter$3 = makeOrThrow("envio_storage_load_time", "Processing time taken to load data from storage. (milliseconds)", operationLabelsSchema);
761
+ var timeCounter$4 = makeOrThrow("envio_storage_load_time", "Processing time taken to load data from storage. (milliseconds)", operationLabelsSchema);
759
762
 
760
763
  var sumTimeCounter$1 = makeOrThrow("envio_storage_load_sum_time", "Cumulative time spent loading data from storage during the indexing process. (milliseconds)", operationLabelsSchema);
761
764
 
@@ -784,7 +787,7 @@ function endOperation(timerRef, operation, whereSize, size) {
784
787
  var operationRef = operations[operation];
785
788
  operationRef.pendingCount = operationRef.pendingCount - 1 | 0;
786
789
  if (operationRef.pendingCount === 0) {
787
- handleInt(timeCounter$3, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(operationRef.timerRef))));
790
+ handleInt(timeCounter$4, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(operationRef.timerRef))));
788
791
  Utils.Dict.deleteInPlace(operations, operation);
789
792
  }
790
793
  handleInt(sumTimeCounter$1, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(timerRef))));
@@ -795,7 +798,7 @@ function endOperation(timerRef, operation, whereSize, size) {
795
798
 
796
799
  var StorageLoad = {
797
800
  operationLabelsSchema: operationLabelsSchema,
798
- timeCounter: timeCounter$3,
801
+ timeCounter: timeCounter$4,
799
802
  sumTimeCounter: sumTimeCounter$1,
800
803
  counter: counter$7,
801
804
  whereSizeCounter: whereSizeCounter,
@@ -39,3 +39,7 @@ let intFromMillis = toInt
39
39
  let intFromNanos = toInt
40
40
  let intFromSeconds = toInt
41
41
  let floatFromMillis = Utils.magic
42
+
43
+ let millisBetween = (~from: timeRef, ~to: timeRef): int => {
44
+ to->toMillis->intFromMillis - from->toMillis->intFromMillis
45
+ }
@@ -39,6 +39,10 @@ function floatFromMillis(prim) {
39
39
  return prim;
40
40
  }
41
41
 
42
+ function millisBetween(from, to) {
43
+ return (toMillis(to) | 0) - (toMillis(from) | 0) | 0;
44
+ }
45
+
42
46
  function makeTimer(prim) {
43
47
  return process.hrtime();
44
48
  }
@@ -63,4 +67,5 @@ exports.intFromMillis = intFromMillis;
63
67
  exports.intFromNanos = intFromNanos;
64
68
  exports.intFromSeconds = intFromSeconds;
65
69
  exports.floatFromMillis = floatFromMillis;
70
+ exports.millisBetween = millisBetween;
66
71
  /* No side effect */
@@ -22,3 +22,5 @@ let intFromMillis: milliseconds => int
22
22
  let intFromNanos: nanoseconds => int
23
23
  let intFromSeconds: seconds => int
24
24
  let floatFromMillis: milliseconds => float
25
+
26
+ let millisBetween: (~from: timeRef, ~to: timeRef) => int
@@ -51,6 +51,9 @@ let catch = (promise: promise<'a>, callback: exn => promise<'a>): promise<'a> =>
51
51
  })
52
52
  }
53
53
 
54
+ @send
55
+ external catchResolve: (t<'a>, exn => 'a) => t<'a> = "catch"
56
+
54
57
  @scope("Promise") @val
55
58
  external race: array<t<'a>> => t<'a> = "race"
56
59