@dra2020/baseclient 1.0.173 → 1.0.175

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.
@@ -2261,6 +2261,8 @@ class FsmManager {
2261
2261
  this.bTickSet = false;
2262
2262
  this.theTickList = new Set();
2263
2263
  this.theBusyLoopCount = 0;
2264
+ this.theExceptionCount = 0;
2265
+ this.onException = (fsm, err) => { console.error(`Fsm(${fsm.constructor.name}): exception in tick:`, err); };
2264
2266
  this.doTick = this.doTick.bind(this);
2265
2267
  }
2266
2268
  forceTick(fsm) {
@@ -2277,7 +2279,14 @@ class FsmManager {
2277
2279
  nLoops++;
2278
2280
  let thisTickList = this.theTickList;
2279
2281
  this.theTickList = new Set();
2280
- thisTickList.forEach((f) => f.tick());
2282
+ thisTickList.forEach((f) => {
2283
+ try {
2284
+ f.tick();
2285
+ }
2286
+ catch (err) {
2287
+ this.tickException(f, err);
2288
+ }
2289
+ });
2281
2290
  }
2282
2291
  if (this.theTickList.size == 0)
2283
2292
  this.theBusyLoopCount = 0;
@@ -2285,6 +2294,27 @@ class FsmManager {
2285
2294
  this.theBusyLoopCount++;
2286
2295
  this.theEpoch++;
2287
2296
  }
2297
+ // An exception escaping a tick would otherwise unwind out of setImmediate and take down the whole
2298
+ // process (or, in the browser, leave the tick loop wedged). Instead, report it and let the Fsm
2299
+ // fail itself so anything waiting on it sees a dependent error rather than hanging forever.
2300
+ tickException(f, err) {
2301
+ this.theExceptionCount++;
2302
+ try {
2303
+ this.onException(f, err);
2304
+ }
2305
+ catch (e) { }
2306
+ try {
2307
+ f.tickException(err);
2308
+ }
2309
+ catch (e) {
2310
+ try {
2311
+ this.onException(f, e);
2312
+ }
2313
+ catch (e2) { }
2314
+ if (!f.done)
2315
+ f.setState(exports.FSM_ERROR);
2316
+ }
2317
+ }
2288
2318
  }
2289
2319
  exports.FsmManager = FsmManager;
2290
2320
  class Fsm {
@@ -2364,6 +2394,12 @@ class Fsm {
2364
2394
  }
2365
2395
  tick() {
2366
2396
  }
2397
+ // Called (after FsmManager.onException has reported it) when tick() throws. Override to clean up or
2398
+ // respond, but make sure the Fsm ends up done.
2399
+ tickException(err) {
2400
+ if (!this.done)
2401
+ this.setState(exports.FSM_ERROR);
2402
+ }
2367
2403
  }
2368
2404
  exports.Fsm = Fsm;
2369
2405
  // Launches callback provided when the associated Fsm (or Fsm array) completes.
@@ -3451,6 +3487,16 @@ function reverseBlockMapping(bm) {
3451
3487
  Object.values(rev).forEach((a) => a.sort());
3452
3488
  return rev;
3453
3489
  }
3490
+ // A set of block mappings consulted as one, without copying any of them.
3491
+ //
3492
+ // Entries are either independent (e.g. one per state of a multi-state map, whose block ids never
3493
+ // overlap) or overlapping (a base mapping followed by overrides such as precinct updates). Overlapping
3494
+ // entries must be added in priority order: map() and rev() walk from the last entry back, so a later
3495
+ // entry wins.
3496
+ //
3497
+ // A full block mapping, and the reverse mapping computed from it, are large (TX has ~670K blocks), so
3498
+ // combining mappings shares them rather than copying: add() takes an already-computed reverse mapping,
3499
+ // and addFrom() takes both from another MultiBlockMapping.
3454
3500
  class MultiBlockMapping {
3455
3501
  constructor(tag, bm) {
3456
3502
  this.entries = [];
@@ -3458,16 +3504,30 @@ class MultiBlockMapping {
3458
3504
  if (tag && bm)
3459
3505
  this.entries.push({ tag, bm });
3460
3506
  }
3461
- add(tag, bm) {
3462
- this.entries.forEach(e => { if (e.tag === tag) {
3507
+ // Adds or replaces the entry for tag. Pass rbm when the reverse mapping of bm is already computed,
3508
+ // so it is shared rather than built again.
3509
+ add(tag, bm, rbm) {
3510
+ let e = this.entries.find(e => e.tag === tag);
3511
+ if (e) {
3463
3512
  e.bm = bm;
3464
- delete e.rbm;
3465
- bm = null;
3466
- } });
3467
- if (bm)
3468
- this.entries.push({ tag, bm });
3513
+ if (rbm)
3514
+ e.rbm = rbm;
3515
+ else
3516
+ delete e.rbm;
3517
+ }
3518
+ else
3519
+ this.entries.push(rbm ? { tag, bm, rbm } : { tag, bm });
3469
3520
  this.stamp++;
3470
3521
  }
3522
+ // Adds the entry for tag from another mapping - or, with no tag, all of its entries in order - sharing
3523
+ // both the block mapping and its reverse mapping. The reverse mapping is computed on the source first
3524
+ // if need be, so that it is built once, there, and not separately in each mapping that shares it.
3525
+ addFrom(src, tag) {
3526
+ src.entries.forEach(e => {
3527
+ if (tag === undefined || e.tag === tag)
3528
+ this.add(e.tag, e.bm, src.rbmOf(e.tag));
3529
+ });
3530
+ }
3471
3531
  remove(tag) {
3472
3532
  for (let i = this.entries.length - 1; i >= 0; i--)
3473
3533
  if (this.entries[i].tag === tag) {
@@ -3475,6 +3535,9 @@ class MultiBlockMapping {
3475
3535
  this.entries.splice(i, 1);
3476
3536
  }
3477
3537
  }
3538
+ has(tag) {
3539
+ return this.entries.some(e => e.tag === tag);
3540
+ }
3478
3541
  map(blockid) {
3479
3542
  // Walk backwards to pick up overrides first
3480
3543
  for (let i = this.entries.length - 1; i >= 0; i--) {
@@ -3494,6 +3557,17 @@ class MultiBlockMapping {
3494
3557
  let e = this.entries.find(e => e.tag === tag);
3495
3558
  return e ? e.bm : null;
3496
3559
  }
3560
+ // The reverse mapping of the entry for tag, computed on first use.
3561
+ rbmOf(tag) {
3562
+ let e = this.entries.find(e => e.tag === tag);
3563
+ if (!e)
3564
+ return null;
3565
+ if (!e.rbm)
3566
+ e.rbm = reverseBlockMapping(e.bm);
3567
+ return e.rbm;
3568
+ }
3569
+ // Maps through the first entry only. Meaningful only where that entry is the base mapping and the
3570
+ // rest are overrides; for independent entries (a multi-state mapping) use map().
3497
3571
  mapmain(blockid) {
3498
3572
  return this.entries.length ? this.entries[0].bm[blockid] : undefined;
3499
3573
  }
@@ -3507,10 +3581,18 @@ class MultiBlockMapping {
3507
3581
  }
3508
3582
  return undefined;
3509
3583
  }
3510
- // Just walks first map
3511
- forEach(cb) {
3512
- if (this.entries.length)
3513
- Object.keys(this.entries[0].bm).forEach(blockid => cb(blockid));
3584
+ // Walks the blocks of one entry: the one for tag, or with no tag the first. This is the right walk
3585
+ // over "all blocks" only when that entry is a base mapping covering them and the others are overrides
3586
+ // of it - it sees nothing of a multi-state mapping but its first state. See forEachBlock.
3587
+ forEachBase(cb, tag) {
3588
+ let e = tag === undefined ? this.entries[0] : this.entries.find(e => e.tag === tag);
3589
+ if (e)
3590
+ Object.keys(e.bm).forEach(blockid => cb(blockid));
3591
+ }
3592
+ // Walks the blocks of every entry. A block mapped by several entries (a base and its overrides) is
3593
+ // visited once for each of them; independent entries (a multi-state mapping) never repeat a block.
3594
+ forEachBlock(cb) {
3595
+ this.entries.forEach(e => Object.keys(e.bm).forEach(blockid => cb(blockid)));
3514
3596
  }
3515
3597
  // Walk maps in reverse order, careful to not invoke on same geoid
3516
3598
  forEachRev(cb) {