@peerbit/native-backbone 0.1.2 → 0.1.4

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/src/js_interop.rs CHANGED
@@ -1,5 +1,6 @@
1
+ use crate::error::BackboneError;
1
2
  use js_sys::{Array, Reflect, Uint8Array};
2
- use peerbit_indexer_core::wire::{self, WireError};
3
+ use peerbit_indexer_core::wire;
3
4
  use std::collections::HashSet;
4
5
  use wasm_bindgen::prelude::*;
5
6
  use wasm_bindgen::JsCast;
@@ -22,51 +23,97 @@ pub(crate) fn clear_journal_prefix(
22
23
  *journal_record_count = journal_record_count.saturating_sub(record_count);
23
24
  }
24
25
 
25
- pub(crate) fn array_from_value(value: JsValue, label: &str) -> Result<Array, JsValue> {
26
+ /// Exclusive upper bound for f64 u64 conversion. `u64::MAX as f64` rounds
27
+ /// UP to 2^64 (exactly representable), so a `> u64::MAX as f64` check admits
28
+ /// 2^64 itself, which an `as` cast then saturates to `u64::MAX`. Valid u64
29
+ /// values in f64 form are exactly the integers in [0, 2^64).
30
+ const F64_U64_EXCLUSIVE_BOUND: f64 = 18_446_744_073_709_551_616.0; // 2^64
31
+
32
+ /// Rejects non-integral, negative, non-finite and out-of-range values instead
33
+ /// of silently truncating them with an `as` cast.
34
+ pub(crate) fn checked_usize_from_f64(value: f64) -> Option<usize> {
35
+ checked_u64_from_f64(value).and_then(|v| usize::try_from(v).ok())
36
+ }
37
+
38
+ /// Rejects non-integral, negative, non-finite and out-of-range values instead
39
+ /// of silently truncating them with an `as` cast.
40
+ pub(crate) fn checked_u64_from_f64(value: f64) -> Option<u64> {
41
+ if !value.is_finite() || value < 0.0 || value.fract() != 0.0 || value >= F64_U64_EXCLUSIVE_BOUND
42
+ {
43
+ return None;
44
+ }
45
+ Some(value as u64)
46
+ }
47
+
48
+ pub(crate) fn array_from_value(
49
+ value: JsValue,
50
+ label: &'static str,
51
+ ) -> Result<Array, BackboneError> {
26
52
  value
27
53
  .dyn_into::<Array>()
28
- .map_err(|_| JsValue::from_str(&format!("Expected {label} array")))
54
+ .map_err(|_| BackboneError::ExpectedArray(label))
29
55
  }
30
56
 
31
- pub(crate) fn string_field(row: &Array, index: u32, label: &str) -> Result<String, JsValue> {
57
+ pub(crate) fn string_field(
58
+ row: &Array,
59
+ index: u32,
60
+ label: &'static str,
61
+ ) -> Result<String, BackboneError> {
32
62
  row.get(index)
33
63
  .as_string()
34
- .ok_or_else(|| JsValue::from_str(&format!("Expected {label} string")))
64
+ .ok_or(BackboneError::ExpectedString(label))
35
65
  }
36
66
 
37
- pub(crate) fn stringish_field(row: &Array, index: u32, label: &str) -> Result<String, JsValue> {
67
+ pub(crate) fn stringish_field(
68
+ row: &Array,
69
+ index: u32,
70
+ label: &'static str,
71
+ ) -> Result<String, BackboneError> {
38
72
  let value = row.get(index);
39
73
  if let Some(value) = value.as_string() {
40
74
  return Ok(value);
41
75
  }
42
76
  if let Some(value) = value.as_f64() {
43
- return Ok((value as u64).to_string());
77
+ if let Some(value) = checked_u64_from_f64(value) {
78
+ return Ok(value.to_string());
79
+ }
44
80
  }
45
- Err(JsValue::from_str(&format!("Expected {label} string")))
81
+ Err(BackboneError::ExpectedString(label))
46
82
  }
47
83
 
48
- pub(crate) fn bool_field(row: &Array, index: u32, label: &str) -> Result<bool, JsValue> {
84
+ pub(crate) fn bool_field(
85
+ row: &Array,
86
+ index: u32,
87
+ label: &'static str,
88
+ ) -> Result<bool, BackboneError> {
49
89
  row.get(index)
50
90
  .as_bool()
51
- .ok_or_else(|| JsValue::from_str(&format!("Expected {label} boolean")))
91
+ .ok_or(BackboneError::ExpectedBoolean(label))
52
92
  }
53
93
 
54
- pub(crate) fn usize_field(row: &Array, index: u32, label: &str) -> Result<usize, JsValue> {
94
+ pub(crate) fn usize_field(
95
+ row: &Array,
96
+ index: u32,
97
+ label: &'static str,
98
+ ) -> Result<usize, BackboneError> {
55
99
  row.get(index)
56
100
  .as_f64()
57
- .map(|value| value as usize)
58
- .ok_or_else(|| JsValue::from_str(&format!("Expected {label} number")))
101
+ .and_then(checked_usize_from_f64)
102
+ .ok_or(BackboneError::ExpectedNumber(label))
59
103
  }
60
104
 
61
- pub(crate) fn bytes_field(row: &Array, index: u32, label: &str) -> Result<Vec<u8>, JsValue> {
62
- let value = row.get(index);
63
- if value.is_undefined() || value.is_null() {
64
- return Err(JsValue::from_str(&format!("Expected {label} bytes")));
65
- }
66
- Ok(Uint8Array::new(&value).to_vec())
105
+ pub(crate) fn bytes_field(
106
+ row: &Array,
107
+ index: u32,
108
+ label: &'static str,
109
+ ) -> Result<Vec<u8>, BackboneError> {
110
+ row.get(index)
111
+ .dyn_ref::<Uint8Array>()
112
+ .map(Uint8Array::to_vec)
113
+ .ok_or(BackboneError::ExpectedBytes(label))
67
114
  }
68
115
 
69
- pub(crate) fn trim_hashes_vec(trim_rows: &Array) -> Result<Vec<String>, JsValue> {
116
+ pub(crate) fn trim_hashes_vec(trim_rows: &Array) -> Result<Vec<String>, BackboneError> {
70
117
  let mut hashes = Vec::with_capacity(trim_rows.length() as usize);
71
118
  for index in 0..trim_rows.length() {
72
119
  let row = array_from_value(trim_rows.get(index), "trim row")?;
@@ -111,27 +158,29 @@ pub(crate) fn strings_slice_to_array(values: &[String]) -> Array {
111
158
  out
112
159
  }
113
160
 
114
- pub(crate) fn strings_from_array(values: Array) -> Result<Vec<String>, JsValue> {
161
+ pub(crate) fn strings_from_array(values: Array) -> Result<Vec<String>, BackboneError> {
115
162
  let mut out = Vec::with_capacity(values.length() as usize);
116
163
  for index in 0..values.length() {
117
164
  out.push(
118
165
  values
119
166
  .get(index)
120
167
  .as_string()
121
- .ok_or_else(|| JsValue::from_str("Expected string array"))?,
168
+ .ok_or(BackboneError::ExpectedStringArray)?,
122
169
  );
123
170
  }
124
171
  Ok(out)
125
172
  }
126
173
 
127
- pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>, JsValue> {
174
+ pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>, BackboneError> {
128
175
  let mut out = Vec::with_capacity(values.length() as usize);
129
176
  for index in 0..values.length() {
130
177
  let value = values.get(index);
131
- if value.is_undefined() || value.is_null() {
132
- return Err(JsValue::from_str("Expected bytes array"));
133
- }
134
- out.push(Uint8Array::new(&value).to_vec());
178
+ out.push(
179
+ value
180
+ .dyn_ref::<Uint8Array>()
181
+ .map(Uint8Array::to_vec)
182
+ .ok_or(BackboneError::ExpectedBytesArray)?,
183
+ );
135
184
  }
136
185
  Ok(out)
137
186
  }
@@ -139,73 +188,79 @@ pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>, JsValu
139
188
  pub(crate) fn required_bytes_from_array(
140
189
  values: &Array,
141
190
  index: u32,
142
- field: &str,
143
- ) -> Result<Uint8Array, JsValue> {
144
- let value = values.get(index);
145
- if value.is_undefined() || value.is_null() {
146
- return Err(JsValue::from_str(&format!("Expected {field} bytes")));
147
- }
148
- Ok(Uint8Array::new(&value))
191
+ field: &'static str,
192
+ ) -> Result<Uint8Array, BackboneError> {
193
+ values
194
+ .get(index)
195
+ .dyn_into::<Uint8Array>()
196
+ .map_err(|_| BackboneError::ExpectedBytes(field))
149
197
  }
150
198
 
151
199
  pub(crate) fn string_batches_from_array(
152
200
  values: Array,
153
- label: &str,
154
- ) -> Result<Vec<Vec<String>>, JsValue> {
201
+ label: &'static str,
202
+ ) -> Result<Vec<Vec<String>>, BackboneError> {
155
203
  let mut out = Vec::with_capacity(values.length() as usize);
156
204
  for index in 0..values.length() {
157
205
  let value = values.get(index);
158
206
  if !Array::is_array(&value) {
159
- return Err(JsValue::from_str(&format!("Expected {label}")));
207
+ return Err(BackboneError::Expected(label));
160
208
  }
161
209
  out.push(strings_from_array(Array::from(&value))?);
162
210
  }
163
211
  Ok(out)
164
212
  }
165
213
 
166
- pub(crate) fn usize_values_from_array(values: Array) -> Result<Vec<usize>, JsValue> {
214
+ pub(crate) fn usize_values_from_array(values: Array) -> Result<Vec<usize>, BackboneError> {
167
215
  let mut out = Vec::with_capacity(values.length() as usize);
168
216
  for index in 0..values.length() {
169
217
  let value = values
170
218
  .get(index)
171
219
  .as_f64()
172
- .ok_or_else(|| JsValue::from_str("Expected unsigned integer array"))?;
173
- if !value.is_finite() || value < 0.0 || value.fract() != 0.0 {
174
- return Err(JsValue::from_str("Expected unsigned integer array"));
175
- }
176
- out.push(value as usize);
220
+ .and_then(checked_usize_from_f64)
221
+ .ok_or(BackboneError::ExpectedUnsignedIntegerArray)?;
222
+ out.push(value);
177
223
  }
178
224
  Ok(out)
179
225
  }
180
226
 
181
- pub(crate) fn ensure_same_len(left: usize, right: usize, label: &str) -> Result<(), JsValue> {
227
+ pub(crate) fn ensure_same_len(
228
+ left: usize,
229
+ right: usize,
230
+ label: &'static str,
231
+ ) -> Result<(), BackboneError> {
182
232
  if left == right {
183
233
  Ok(())
184
234
  } else {
185
- Err(JsValue::from_str(&format!(
186
- "Mismatched {label} input lengths"
187
- )))
235
+ Err(BackboneError::MismatchedInputLengths(label))
188
236
  }
189
237
  }
190
238
 
191
- pub(crate) fn optional_bytes_from_js(value: JsValue) -> Option<Vec<u8>> {
239
+ pub(crate) fn optional_bytes_from_js(
240
+ value: JsValue,
241
+ label: &'static str,
242
+ ) -> Result<Option<Vec<u8>>, BackboneError> {
192
243
  if value.is_undefined() || value.is_null() {
193
- return None;
244
+ return Ok(None);
194
245
  }
195
- Some(Uint8Array::new(&value).to_vec())
246
+ value
247
+ .dyn_ref::<Uint8Array>()
248
+ .map(|value| Some(value.to_vec()))
249
+ .ok_or(BackboneError::ExpectedBytes(label))
196
250
  }
197
251
 
198
252
  pub(crate) fn optional_usize_from_js(
199
253
  value: JsValue,
200
- label: &str,
201
- ) -> Result<Option<usize>, JsValue> {
254
+ label: &'static str,
255
+ ) -> Result<Option<usize>, BackboneError> {
202
256
  if value.is_undefined() || value.is_null() {
203
257
  return Ok(None);
204
258
  }
205
259
  value
206
260
  .as_f64()
207
- .map(|value| Some(value as usize))
208
- .ok_or_else(|| JsValue::from_str(&format!("{label} must be a number")))
261
+ .and_then(checked_usize_from_f64)
262
+ .map(Some)
263
+ .ok_or(BackboneError::MustBeNumber(label))
209
264
  }
210
265
 
211
266
  pub(crate) fn number_strings_to_array(values: &[u64]) -> Array {
@@ -216,13 +271,16 @@ pub(crate) fn number_strings_to_array(values: &[u64]) -> Array {
216
271
  out
217
272
  }
218
273
 
219
- pub(crate) fn parse_u64_string(value: &str, label: &str) -> Result<u64, JsValue> {
274
+ pub(crate) fn parse_u64_string(value: &str, label: &'static str) -> Result<u64, BackboneError> {
220
275
  value
221
276
  .parse::<u64>()
222
- .map_err(|_| JsValue::from_str(&format!("Expected {label} u64 string")))
277
+ .map_err(|_| BackboneError::ExpectedU64String(label))
223
278
  }
224
279
 
225
- pub(crate) fn parse_optional_u64_string(value: &str, label: &str) -> Result<Option<u64>, JsValue> {
280
+ pub(crate) fn parse_optional_u64_string(
281
+ value: &str,
282
+ label: &'static str,
283
+ ) -> Result<Option<u64>, BackboneError> {
226
284
  if value.is_empty() {
227
285
  Ok(None)
228
286
  } else {
@@ -308,55 +366,54 @@ pub(crate) fn write_bytes(out: &mut Vec<u8>, value: &[u8]) {
308
366
  out.extend_from_slice(value);
309
367
  }
310
368
 
311
- pub(crate) fn wire_error_to_js(error: WireError) -> JsValue {
312
- JsValue::from_str(&error.to_string())
313
- }
314
-
315
369
  pub(crate) fn read_u32(
316
370
  bytes: &[u8],
317
371
  offset: &mut usize,
318
372
  label: &'static str,
319
- ) -> Result<u32, JsValue> {
320
- wire::read_u32(bytes, offset, label).map_err(wire_error_to_js)
373
+ ) -> Result<u32, BackboneError> {
374
+ Ok(wire::read_u32(bytes, offset, label)?)
321
375
  }
322
376
 
323
377
  pub(crate) fn read_u64(
324
378
  bytes: &[u8],
325
379
  offset: &mut usize,
326
380
  label: &'static str,
327
- ) -> Result<u64, JsValue> {
328
- wire::read_u64(bytes, offset, label).map_err(wire_error_to_js)
381
+ ) -> Result<u64, BackboneError> {
382
+ Ok(wire::read_u64(bytes, offset, label)?)
329
383
  }
330
384
 
331
385
  pub(crate) fn read_encoded_string(
332
386
  bytes: &[u8],
333
387
  offset: &mut usize,
334
388
  label: &'static str,
335
- ) -> Result<String, JsValue> {
336
- wire::read_encoded_string(bytes, offset, label).map_err(wire_error_to_js)
389
+ ) -> Result<String, BackboneError> {
390
+ Ok(wire::read_encoded_string(bytes, offset, label)?)
337
391
  }
338
392
 
339
393
  pub(crate) fn read_bytes(
340
394
  bytes: &[u8],
341
395
  offset: &mut usize,
342
396
  label: &'static str,
343
- ) -> Result<Vec<u8>, JsValue> {
344
- wire::read_bytes(bytes, offset, label).map_err(wire_error_to_js)
397
+ ) -> Result<Vec<u8>, BackboneError> {
398
+ Ok(wire::read_bytes(bytes, offset, label)?)
345
399
  }
346
400
 
347
401
  pub(crate) fn js_get(value: &JsValue, key: &str) -> JsValue {
348
402
  Reflect::get(value, &JsValue::from_str(key)).unwrap_or(JsValue::UNDEFINED)
349
403
  }
350
404
 
351
- fn js_string(value: JsValue, field: &str) -> Result<String, JsValue> {
405
+ fn js_string(value: JsValue, field: &'static str) -> Result<String, BackboneError> {
352
406
  value
353
407
  .as_string()
354
- .ok_or_else(|| JsValue::from_str(&format!("Missing or invalid {field}")))
408
+ .ok_or(BackboneError::MissingOrInvalid(field))
355
409
  }
356
410
 
357
- pub(crate) fn array_strings(value: JsValue, field: &str) -> Result<Vec<String>, JsValue> {
411
+ pub(crate) fn array_strings(
412
+ value: JsValue,
413
+ field: &'static str,
414
+ ) -> Result<Vec<String>, BackboneError> {
358
415
  if !Array::is_array(&value) {
359
- return Err(JsValue::from_str(&format!("{field} must be an array")));
416
+ return Err(BackboneError::MustBeArray(field));
360
417
  }
361
418
  let array = Array::from(&value);
362
419
  let mut out = Vec::with_capacity(array.length() as usize);
@@ -366,12 +423,17 @@ pub(crate) fn array_strings(value: JsValue, field: &str) -> Result<Vec<String>,
366
423
  Ok(out)
367
424
  }
368
425
 
369
- pub(crate) fn optional_string(value: JsValue) -> Option<String> {
426
+ pub(crate) fn optional_string(
427
+ value: JsValue,
428
+ field: &'static str,
429
+ ) -> Result<Option<String>, BackboneError> {
370
430
  if value.is_null() || value.is_undefined() {
371
- None
372
- } else {
373
- value.as_string()
431
+ return Ok(None);
374
432
  }
433
+ value
434
+ .as_string()
435
+ .map(Some)
436
+ .ok_or(BackboneError::MissingOrInvalid(field))
375
437
  }
376
438
 
377
439
  pub(crate) fn write_u8(out: &mut Vec<u8>, value: u8) {
@@ -382,38 +444,36 @@ pub(crate) fn write_bool(out: &mut Vec<u8>, value: bool) {
382
444
  out.push(if value { 1 } else { 0 });
383
445
  }
384
446
 
385
- pub(crate) fn js_error(error: impl std::fmt::Display) -> JsValue {
386
- JsValue::from_str(&error.to_string())
387
- }
388
-
389
- pub(crate) fn decode_error(error: impl std::fmt::Display) -> JsValue {
390
- JsValue::from_str(&error.to_string())
391
- }
392
-
393
- pub(crate) fn hash_number_u64(resolution: &str, digest: &[u8]) -> Result<u64, JsValue> {
447
+ pub(crate) fn hash_number_u64(resolution: &str, digest: &[u8]) -> Result<u64, BackboneError> {
394
448
  match resolution {
395
449
  "u32" => {
396
450
  if digest.len() < 4 {
397
- return Err(JsValue::from_str("hash digest must have at least 4 bytes"));
451
+ return Err(BackboneError::HashDigestTooShortU32);
398
452
  }
399
453
  Ok(u32::from_le_bytes(digest[0..4].try_into().unwrap()) as u64)
400
454
  }
401
455
  "u64" => {
402
456
  if digest.len() < 8 {
403
- return Err(JsValue::from_str("hash digest must have at least 8 bytes"));
457
+ return Err(BackboneError::HashDigestTooShortU64);
404
458
  }
405
459
  Ok(u64::from_le_bytes(digest[0..8].try_into().unwrap()))
406
460
  }
407
- _ => Err(JsValue::from_str("resolution must be u32 or u64")),
461
+ _ => Err(BackboneError::ResolutionMustBeU32OrU64),
408
462
  }
409
463
  }
410
464
 
411
465
  #[cfg(test)]
412
466
  mod tests {
413
- use super::{append_journal_delete_record, append_journal_put_record, hash_number_u64};
467
+ use super::{
468
+ append_journal_delete_record, append_journal_put_record, checked_u64_from_f64,
469
+ checked_usize_from_f64, ensure_same_len, hash_number_u64, parse_optional_u64_string,
470
+ parse_u64_string, read_u32,
471
+ };
472
+ use crate::error::BackboneError;
414
473
  use peerbit_indexer_core::persistence::{
415
474
  encode_journal_delete_record, encode_journal_put_record,
416
475
  };
476
+ use peerbit_indexer_core::wire::WireError;
417
477
 
418
478
  #[test]
419
479
  fn decodes_hash_numbers_like_shared_log_integer_helpers() {
@@ -422,6 +482,86 @@ mod tests {
422
482
  assert_eq!(hash_number_u64("u64", &bytes).unwrap(), 8_589_934_593);
423
483
  }
424
484
 
485
+ #[test]
486
+ fn hash_number_u64_reports_typed_errors() {
487
+ let error = hash_number_u64("u32", &[1, 2, 3]).unwrap_err();
488
+ assert_eq!(error, BackboneError::HashDigestTooShortU32);
489
+ assert_eq!(error.to_string(), "hash digest must have at least 4 bytes");
490
+
491
+ let error = hash_number_u64("u64", &[1, 2, 3, 4]).unwrap_err();
492
+ assert_eq!(error, BackboneError::HashDigestTooShortU64);
493
+ assert_eq!(error.to_string(), "hash digest must have at least 8 bytes");
494
+
495
+ let error = hash_number_u64("u128", &[0; 16]).unwrap_err();
496
+ assert_eq!(error, BackboneError::ResolutionMustBeU32OrU64);
497
+ assert_eq!(error.to_string(), "resolution must be u32 or u64");
498
+ }
499
+
500
+ #[test]
501
+ fn wire_reads_report_typed_errors() {
502
+ let mut offset = 0usize;
503
+ let error = read_u32(&[1, 2], &mut offset, "coordinate count").unwrap_err();
504
+ assert_eq!(
505
+ error,
506
+ BackboneError::Wire(WireError::Truncated("coordinate count"))
507
+ );
508
+ assert_eq!(error.to_string(), "Truncated coordinate count");
509
+ }
510
+
511
+ #[test]
512
+ fn parse_u64_string_reports_typed_errors() {
513
+ assert_eq!(parse_u64_string("42", "coordinate").unwrap(), 42);
514
+ assert_eq!(parse_optional_u64_string("", "coordinate").unwrap(), None);
515
+
516
+ let error = parse_u64_string("not-a-number", "coordinate").unwrap_err();
517
+ assert_eq!(error, BackboneError::ExpectedU64String("coordinate"));
518
+ assert_eq!(error.to_string(), "Expected coordinate u64 string");
519
+ }
520
+
521
+ #[test]
522
+ fn ensure_same_len_reports_typed_errors() {
523
+ assert_eq!(ensure_same_len(2, 2, "batch gids"), Ok(()));
524
+
525
+ let error = ensure_same_len(1, 2, "batch gids").unwrap_err();
526
+ assert_eq!(error, BackboneError::MismatchedInputLengths("batch gids"));
527
+ assert_eq!(error.to_string(), "Mismatched batch gids input lengths");
528
+ }
529
+
530
+ #[test]
531
+ fn checked_integer_conversions_reject_invalid_numbers() {
532
+ assert_eq!(checked_usize_from_f64(0.0), Some(0));
533
+ assert_eq!(checked_usize_from_f64(3.0), Some(3));
534
+ assert_eq!(checked_usize_from_f64(-1.0), None);
535
+ assert_eq!(checked_usize_from_f64(1.5), None);
536
+ assert_eq!(checked_usize_from_f64(f64::NAN), None);
537
+ assert_eq!(checked_usize_from_f64(f64::INFINITY), None);
538
+ assert_eq!(checked_usize_from_f64(1e20), None);
539
+
540
+ assert_eq!(checked_u64_from_f64(42.0), Some(42));
541
+ assert_eq!(checked_u64_from_f64(-0.5), None);
542
+ assert_eq!(checked_u64_from_f64(f64::NEG_INFINITY), None);
543
+ assert_eq!(checked_u64_from_f64(1e20), None);
544
+ }
545
+
546
+ #[test]
547
+ fn checked_integer_conversions_handle_the_two_pow_64_boundary() {
548
+ // 2^64 is exactly representable and equals `u64::MAX as f64` after
549
+ // rounding; it must be rejected, not saturated to u64::MAX.
550
+ let two_pow_64 = 18_446_744_073_709_551_616.0_f64;
551
+ assert_eq!(checked_u64_from_f64(two_pow_64), None);
552
+ assert_eq!(checked_u64_from_f64(u64::MAX as f64), None);
553
+ assert_eq!(checked_usize_from_f64(two_pow_64), None);
554
+ // The largest f64 strictly below 2^64 is a valid u64.
555
+ let below = 18_446_744_073_709_549_568.0_f64; // 2^64 - 2048
556
+ assert_eq!(
557
+ checked_u64_from_f64(below),
558
+ Some(18_446_744_073_709_549_568)
559
+ );
560
+ // 2^53 region is unaffected.
561
+ let two_pow_53 = 9_007_199_254_740_992.0_f64;
562
+ assert_eq!(checked_u64_from_f64(two_pow_53), Some(1 << 53));
563
+ }
564
+
425
565
  #[test]
426
566
  fn journal_record_encoding_matches_indexer_core() {
427
567
  for (key, value) in [
package/src/lib.rs CHANGED
@@ -10,14 +10,18 @@ use wasm_bindgen::prelude::*;
10
10
  mod append_tx;
11
11
  mod coordinates;
12
12
  mod documents;
13
+ mod error;
13
14
  mod graph_blocks;
14
15
  mod js_interop;
15
16
  mod profile;
16
17
  mod raw_receive;
17
18
  mod shared_log_plan;
18
19
  mod sync_send;
20
+ mod time;
19
21
  mod wire_sync;
20
22
 
23
+ pub use crate::error::BackboneError;
24
+
21
25
  use crate::documents::{DocumentContextFields, DocumentPreviousSignerFact, ParsedProjectionPlan};
22
26
  use crate::profile::NativeBackboneAppendProfile;
23
27
  use crate::raw_receive::PendingRawReceiveEntry;