bruce-models 2.1.0 → 2.1.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.
@@ -3146,260 +3146,125 @@ module.exports = localforage_js;
3146
3146
  });
3147
3147
  });
3148
3148
 
3149
- var ObjectUtils;
3150
- (function (ObjectUtils) {
3151
- /**
3152
- * Generates a Bruce compatible UID.
3153
- * @returns
3154
- */
3155
- function UId() {
3156
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
3157
- const r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8);
3158
- return v.toString(16);
3159
- });
3160
- }
3161
- ObjectUtils.UId = UId;
3162
- })(ObjectUtils || (ObjectUtils = {}));
3163
-
3164
- class CacheDictionary {
3149
+ const CACHE_VERSION_PREFIX = "v2_";
3150
+ class CacheUtil {
3165
3151
  constructor(id) {
3166
- this.memory = Object.create(null);
3167
- if (!id) {
3168
- id = "BRUCE_UI_MODELS_CACHE_DICT_" + ObjectUtils.UId();
3169
- }
3170
- this.id = id;
3171
3152
  this.store = localforage.createInstance({
3172
- name: id
3153
+ name: CACHE_VERSION_PREFIX + id,
3173
3154
  });
3155
+ this.inMemoryPromises = {};
3156
+ this.keyVersions = {};
3174
3157
  }
3175
- get Id() {
3176
- return this.id;
3177
- }
3178
- GetItems() {
3158
+ Set(key, value, duration = 300000) {
3179
3159
  return __awaiter(this, void 0, void 0, function* () {
3180
- if (this.items == null) {
3181
- const raw = yield this.store.getItem(this.id);
3182
- if (raw == null) {
3183
- this.items = [];
3184
- }
3185
- else {
3186
- // Check if any are expired.
3187
- // We can clean this every first time we load.
3188
- const now = new Date().getTime();
3189
- for (const item of raw) {
3190
- if (item.duration > -1 && (now - item.created) / 1000 > item.duration) {
3191
- if (item.inMemory) {
3192
- this.memory[item.key] = null;
3193
- delete this.memory[item.key];
3194
- }
3195
- else {
3196
- yield this.store.removeItem(item.key);
3197
- }
3198
- }
3160
+ if (value instanceof Promise) {
3161
+ this.inMemoryPromises[key] = value;
3162
+ const currentVersion = (this.keyVersions[key] = (this.keyVersions[key] || 0) + 1);
3163
+ value
3164
+ .then((resolvedValue) => {
3165
+ if (this.keyVersions[key] === currentVersion) {
3166
+ this.Set(key, resolvedValue, duration);
3199
3167
  }
3200
- this.items = raw;
3201
- }
3202
- }
3203
- return this.items;
3204
- });
3205
- }
3206
- GetKeys() {
3207
- return __awaiter(this, void 0, void 0, function* () {
3208
- return (yield this.GetItems()).map(x => "" + x.key);
3209
- });
3210
- }
3211
- Set(key, value, duration) {
3212
- return __awaiter(this, void 0, void 0, function* () {
3213
- key = "" + key;
3214
- const items = yield this.GetItems();
3215
- const itemIndex = items.findIndex(x => x.key == key);
3216
- if (itemIndex > -1) {
3217
- const item = items[itemIndex];
3218
- if (item.inMemory) {
3219
- this.memory[item.key] = null;
3220
- delete this.memory[item.key];
3221
- }
3222
- else {
3223
- yield this.store.removeItem(item.key);
3224
- }
3225
- items.splice(itemIndex, 1);
3226
- }
3227
- const item = {
3228
- key: key,
3229
- duration: duration,
3230
- created: new Date().getTime(),
3231
- inMemory: false
3232
- };
3233
- // Short duration, keep in memory.
3234
- if (duration < 60) {
3235
- item.inMemory = true;
3236
- this.memory[item.key] = value;
3237
- }
3238
- // Let's resolve and store the value in local-storage.
3239
- // If resolution crashes then we'll keep retaining that in memory.
3240
- else if (value instanceof Promise) {
3241
- value.then((x) => __awaiter(this, void 0, void 0, function* () {
3242
- try {
3243
- const items = yield this.GetItems();
3244
- const item = items.find(x => x.key == key);
3245
- if (!item) {
3246
- console.log("Item was removed from cache before promise resolved.", key, items);
3247
- return;
3248
- }
3249
- try {
3250
- yield this.store.setItem(item.key, x);
3251
- item.inMemory = false;
3252
- this.memory[item.key] = null;
3253
- delete this.memory[item.key];
3254
- yield this.store.setItem(this.id, items);
3255
- }
3256
- catch (e) {
3257
- console.warn(e);
3258
- }
3259
- }
3260
- catch (e) {
3261
- console.warn(e);
3168
+ })
3169
+ .catch((error) => {
3170
+ if (this.keyVersions[key] === currentVersion) {
3171
+ this.Set(key, error, duration);
3262
3172
  }
3263
- }));
3264
- item.inMemory = true;
3265
- this.memory[item.key] = value;
3173
+ })
3174
+ .finally(() => {
3175
+ delete this.inMemoryPromises[key];
3176
+ });
3266
3177
  }
3267
- if (!item.inMemory) {
3268
- try {
3269
- yield this.store.setItem(item.key, value);
3270
- }
3271
- catch (e) {
3272
- item.inMemory = true;
3273
- this.memory[item.key] = value;
3274
- console.warn(e);
3275
- }
3178
+ else {
3179
+ const item = {
3180
+ value,
3181
+ expiry: Date.now() + duration,
3182
+ };
3183
+ yield this.store.setItem(key, item);
3276
3184
  }
3277
- items.push(item);
3278
- yield this.store.setItem(this.id, items);
3279
- this.items = items;
3280
3185
  });
3281
3186
  }
3282
- Remove(key) {
3187
+ Get(key) {
3283
3188
  return __awaiter(this, void 0, void 0, function* () {
3284
- key = "" + key;
3285
- const items = yield this.GetItems();
3286
- const index = items.findIndex(x => x.key == key);
3287
- if (index >= 0) {
3288
- const item = items[index];
3289
- if (item.inMemory) {
3290
- this.memory[item.key] = null;
3291
- delete this.memory[item.key];
3292
- }
3293
- else {
3294
- this.store.removeItem(item.key);
3295
- }
3296
- items.splice(index, 1);
3297
- this.store.setItem(this.id, items);
3189
+ if (this.inMemoryPromises[key]) {
3190
+ return this.inMemoryPromises[key];
3298
3191
  }
3299
- this.items = items;
3300
- });
3301
- }
3302
- Clear() {
3303
- return __awaiter(this, void 0, void 0, function* () {
3304
- const items = yield this.GetItems();
3305
- for (const item of items) {
3306
- if (item.inMemory) {
3307
- this.store.removeItem(item.key);
3308
- }
3192
+ const item = (yield this.store.getItem(key));
3193
+ if (!item) {
3194
+ return null;
3309
3195
  }
3310
- this.store.setItem(this.id, []);
3311
- this.items = [];
3312
- this.memory = Object.create(null);
3196
+ if (Date.now() > item.expiry) {
3197
+ yield this.store.removeItem(key);
3198
+ return null;
3199
+ }
3200
+ return item.value;
3313
3201
  });
3314
3202
  }
3315
- GetData(key) {
3203
+ ClearCache(pattern, mode = "equals") {
3316
3204
  return __awaiter(this, void 0, void 0, function* () {
3317
- key = "" + key;
3318
- const item = (yield this.GetItems()).find(x => x.key == key);
3319
- const isExpired = item == null || item.duration > -1 && (new Date().getTime() - item.created) / 1000 > item.duration;
3320
- if (isExpired) {
3321
- this.Remove(key);
3322
- return null;
3205
+ const keys = yield this.store.keys();
3206
+ const checkFuncs = {
3207
+ equals: (key) => key === pattern,
3208
+ contains: (key) => key.includes(pattern),
3209
+ startswith: (key) => key.startsWith(pattern),
3210
+ };
3211
+ const check = checkFuncs[mode];
3212
+ if (!check) {
3213
+ throw new Error("Invalid mode");
3323
3214
  }
3324
- if (item.inMemory) {
3325
- return this.memory[item.key];
3215
+ for (const key of keys) {
3216
+ if (check(key)) {
3217
+ yield this.store.removeItem(key);
3218
+ }
3326
3219
  }
3327
- const raw = yield this.store.getItem(item.key);
3328
- if (raw == null) {
3329
- return null;
3220
+ for (const key in this.inMemoryPromises) {
3221
+ if (check(key)) {
3222
+ delete this.inMemoryPromises[key];
3223
+ this.keyVersions[key]++;
3224
+ }
3330
3225
  }
3331
- return raw;
3332
3226
  });
3333
3227
  }
3334
3228
  }
3335
3229
  class CacheControl {
3336
3230
  constructor(id) {
3337
3231
  this.Disabled = false;
3338
- this.data = new CacheDictionary(id);
3232
+ this.data = new CacheUtil(id);
3339
3233
  }
3340
3234
  /**
3341
3235
  * @param id
3342
3236
  * @param data
3343
3237
  * @param duration seconds to keep the data in cache. -1 for infinite.
3344
3238
  */
3345
- Set(id, data, duration = -1) {
3239
+ Set(id, data, duration = 300) {
3346
3240
  return __awaiter(this, void 0, void 0, function* () {
3347
- return this.data.Set(id, data, duration);
3241
+ const durationMs = duration === -1 ? undefined : duration * 1000;
3242
+ return this.data.Set(String(id), data, durationMs);
3348
3243
  });
3349
3244
  }
3350
3245
  Get(id) {
3351
3246
  return __awaiter(this, void 0, void 0, function* () {
3352
- return this.data.GetData(id);
3247
+ return (yield this.data.Get(String(id)));
3353
3248
  });
3354
3249
  }
3355
3250
  Clear() {
3356
3251
  return __awaiter(this, void 0, void 0, function* () {
3357
- return this.data.Clear();
3252
+ return this.data.ClearCache("");
3358
3253
  });
3359
3254
  }
3360
3255
  Remove(id) {
3361
3256
  return __awaiter(this, void 0, void 0, function* () {
3362
- return this.data.Remove(id);
3257
+ return this.data.ClearCache(String(id), "equals");
3363
3258
  });
3364
3259
  }
3365
3260
  RemoveByStartsWith(text) {
3366
3261
  return __awaiter(this, void 0, void 0, function* () {
3367
- const items = [...(yield this.data.GetItems())];
3368
- for (const item of items) {
3369
- if (item.key.startsWith(text)) {
3370
- yield this.data.Remove(item.key);
3371
- }
3372
- }
3262
+ return this.data.ClearCache(text, "startswith");
3373
3263
  });
3374
3264
  }
3375
3265
  RemoveByContains(text) {
3376
3266
  return __awaiter(this, void 0, void 0, function* () {
3377
- const items = [...(yield this.data.GetItems())];
3378
- for (const item of items) {
3379
- if (item.key.includes(text)) {
3380
- yield this.data.Remove(item.key);
3381
- }
3382
- }
3383
- });
3384
- }
3385
- GetKeys() {
3386
- return __awaiter(this, void 0, void 0, function* () {
3387
- if (this.Disabled) {
3388
- return [];
3389
- }
3390
- const items = yield this.data.GetItems();
3391
- return items.map(x => x.key);
3392
- });
3393
- }
3394
- GetValues() {
3395
- return __awaiter(this, void 0, void 0, function* () {
3396
- if (this.Disabled) {
3397
- return [];
3398
- }
3399
- const items = yield this.data.GetItems();
3400
- const keys = items.map(x => x.key);
3401
- const values = yield Promise.all(keys.map(key => this.data.GetData(key)));
3402
- return values.filter(x => x != null);
3267
+ return this.data.ClearCache(text, "contains");
3403
3268
  });
3404
3269
  }
3405
3270
  }
@@ -4658,6 +4523,21 @@ var Color;
4658
4523
  Color.ColorFromStr = ColorFromStr;
4659
4524
  })(Color || (Color = {}));
4660
4525
 
4526
+ var ObjectUtils;
4527
+ (function (ObjectUtils) {
4528
+ /**
4529
+ * Generates a Bruce compatible UID.
4530
+ * @returns
4531
+ */
4532
+ function UId() {
4533
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
4534
+ const r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8);
4535
+ return v.toString(16);
4536
+ });
4537
+ }
4538
+ ObjectUtils.UId = UId;
4539
+ })(ObjectUtils || (ObjectUtils = {}));
4540
+
4661
4541
  /**
4662
4542
  * Utility to help with parsing and wrapping Bruce paths.
4663
4543
  */
@@ -7212,6 +7092,7 @@ var EntityGlobe;
7212
7092
  this.IsFetched = null;
7213
7093
  this.FetchPageIndex = 0;
7214
7094
  this.Boundaries = null;
7095
+ this.Fetching = false;
7215
7096
  }
7216
7097
  GetBounds() {
7217
7098
  // Entity data works in -180 to 180 range.
@@ -7238,93 +7119,47 @@ var EntityGlobe;
7238
7119
  class Grid {
7239
7120
  constructor() {
7240
7121
  this.cells = {};
7241
- this.patches = [];
7242
7122
  }
7243
7123
  GetCellsForView(viewRect) {
7124
+ const cells = [];
7125
+ const minLat = viewRect.south;
7126
+ const minLon = viewRect.west;
7127
+ const maxLat = viewRect.north;
7128
+ const maxLon = viewRect.east;
7244
7129
  const MAX_CELLS = 150;
7245
- const { south: minLat, west: minLon, north: maxLat, east: maxLon } = viewRect;
7246
7130
  const cellDegreeSize = getCellSizeFromHeight(viewRect.alt);
7247
- const [curMinLon, curMinLat] = [minLon, minLat].map((v) => floorValueToCellSize(cellDegreeSize, v));
7248
- const [width, height] = [maxLon - curMinLon, maxLat - curMinLat].map((v) => v / cellDegreeSize);
7249
- const ids = [];
7250
- const cells = [];
7251
- let max = [curMinLon, curMinLat];
7252
- const intersections = this.intersections(viewRect);
7253
- const addCell = (x, y) => {
7254
- const lon1 = x * cellDegreeSize + curMinLon;
7255
- const lat1 = y * cellDegreeSize + curMinLat;
7256
- const lon2 = lon1 + cellDegreeSize;
7257
- const lat2 = lat1 + cellDegreeSize;
7258
- const [id, cell] = getOrCreateCell(this.cells, cellDegreeSize, lon1, lon2, lat1, lat2);
7259
- /*
7260
- {
7261
- const cell_bounds = { south: lat1, west: lon1, north: lat2, east: lon2 };
7262
- const subcells = [];
7263
- for (const id of intersections) {
7264
- const c = this.cells[id].GetBounds();
7265
- if (contains(c, cell_bounds)) {
7266
- cell.Fetched = true;
7267
- return;
7268
- }
7269
- if (intersects(c, cell_bounds)) {
7270
- const cropped = crop(c, cell.GetBounds());
7271
- subcells.push(cropped);
7272
- }
7273
- }
7274
- let filled = 0;
7275
- while (subcells.length > 0) {
7276
- const c = subcells.pop();
7277
- filled += area(c);
7278
- for (const s of subcells) {
7279
- filled -= intersection(s, c);
7280
- }
7281
- }
7282
- if (filled + Number.EPSILON * 2 > area(cell.GetBounds())) {
7283
- cell.Fetched = true;
7284
- return;
7285
- }
7286
-
7287
- let [x2, y2] = max;
7288
- x2 = Math.max(x2, lon2);
7289
- y2 = Math.max(y2, lat2);
7290
- max = [x2, y2];
7291
- }
7292
- */
7293
- ids.push(id);
7294
- cells.push(cell);
7295
- cells[id] = cell;
7296
- };
7131
+ const curMinLon = floorValueToCellSize(cellDegreeSize, minLon);
7132
+ const curMinLat = floorValueToCellSize(cellDegreeSize, minLat);
7133
+ const width = (maxLon - curMinLon) / cellDegreeSize;
7134
+ const height = (maxLat - curMinLat) / cellDegreeSize;
7135
+ const centerX = (minLon + maxLon) / 2;
7136
+ const centerY = (minLat + maxLat) / 2;
7297
7137
  for (let x = 0; x < width; x++) {
7298
7138
  for (let y = 0; y < height; y++) {
7299
- addCell(x, y);
7300
- if (cells.length >= MAX_CELLS) {
7139
+ const lon = x * cellDegreeSize + curMinLon;
7140
+ const lat = y * cellDegreeSize + curMinLat;
7141
+ const [id, cell] = getOrCreateCell(this.cells, cellDegreeSize, lon, lon + cellDegreeSize, lat, lat + cellDegreeSize);
7142
+ cells.push(cell);
7143
+ if (cells.length > MAX_CELLS) {
7301
7144
  break;
7302
7145
  }
7303
7146
  }
7304
7147
  }
7305
- const [x2, y2] = max;
7306
- if (x2 != curMinLon && y2 != curMinLat) {
7307
- this.patches.push([{ west: curMinLon, east: x2, south: curMinLat, north: y2 }, ids]);
7308
- }
7148
+ // Sort cells so that the ones closest to the center are first
7149
+ cells.sort((a, b) => {
7150
+ const aCenterX = (a.Boundaries.minLongitude + a.Boundaries.maxLongitude) / 2;
7151
+ const aCenterY = (a.Boundaries.minLatitude + a.Boundaries.maxLatitude) / 2;
7152
+ const bCenterX = (b.Boundaries.minLongitude + b.Boundaries.maxLongitude) / 2;
7153
+ const bCenterY = (b.Boundaries.minLatitude + b.Boundaries.maxLatitude) / 2;
7154
+ const aDist = Math.sqrt(Math.pow(aCenterX - centerX, 2) + Math.pow(aCenterY - centerY, 2));
7155
+ const bDist = Math.sqrt(Math.pow(bCenterX - centerX, 2) + Math.pow(bCenterY - centerY, 2));
7156
+ return aDist - bDist;
7157
+ });
7309
7158
  return cells;
7310
7159
  }
7311
- intersections(withRect) {
7312
- return this.patches
7313
- .filter(([rect, _]) => intersects(rect, withRect))
7314
- .map(([_, ids]) => ids)
7315
- .reduce((all, ids) => {
7316
- all.push(...ids);
7317
- return all;
7318
- }, []);
7319
- }
7320
7160
  }
7321
7161
  EntityGlobe.Grid = Grid;
7322
7162
  })(EntityGlobe || (EntityGlobe = {}));
7323
- function intersects(l, r) {
7324
- const { west: w, south: s, north: n, east: e } = l;
7325
- const { west: wr, south: sr, north: nr, east: er } = r;
7326
- return !(e <= wr || w >= er || n <= sr || s >= nr);
7327
- }
7328
7163
  function floorValueToCellSize(size, value) {
7329
7164
  return Math.floor(value / size) * size;
7330
7165
  }
@@ -7564,6 +7399,7 @@ var EntityFilterGetter;
7564
7399
  let retryDelay = 0;
7565
7400
  let prevFirstId = "";
7566
7401
  let prevLastId = "";
7402
+ let prevTicks = 0;
7567
7403
  while ((!this.viewCenter || !this.viewRect) && this.getterLoopId == loopId) {
7568
7404
  yield delay(RETRY_DELAY_INCREMENT);
7569
7405
  }
@@ -7593,8 +7429,12 @@ var EntityFilterGetter;
7593
7429
  }
7594
7430
  const curCell = cells[curCellIndex];
7595
7431
  if (curCell.IsFetched()) {
7432
+ curCell.Fetching = false;
7596
7433
  curCellIndex += 1;
7597
- if (!cells[curCellIndex]) {
7434
+ if (cells[curCellIndex]) {
7435
+ cells[curCellIndex].Fetching = true;
7436
+ }
7437
+ else {
7598
7438
  curCellIndex = null;
7599
7439
  }
7600
7440
  (_b = this.onScanUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(cells);
@@ -7634,6 +7474,7 @@ var EntityFilterGetter;
7634
7474
  // Only mark as fetched when ALL pages are done.
7635
7475
  if (entities.length <= 0) {
7636
7476
  curCell.Fetched = true;
7477
+ curCell.Fetching = false;
7637
7478
  (_d = this.onScanUpdate) === null || _d === void 0 ? void 0 : _d.Trigger(cells);
7638
7479
  continue;
7639
7480
  }
@@ -7642,11 +7483,15 @@ var EntityFilterGetter;
7642
7483
  const first = (_f = (_e = entities[0]) === null || _e === void 0 ? void 0 : _e.Bruce) === null || _f === void 0 ? void 0 : _f.ID;
7643
7484
  const last = (_h = (_g = entities[entities.length - 1]) === null || _g === void 0 ? void 0 : _g.Bruce) === null || _h === void 0 ? void 0 : _h.ID;
7644
7485
  if (prevFirstId == first && prevLastId == last) {
7645
- break;
7486
+ prevTicks += 1;
7487
+ if (prevTicks > 3) {
7488
+ break;
7489
+ }
7646
7490
  }
7647
7491
  else {
7648
7492
  prevFirstId = first;
7649
7493
  prevLastId = last;
7494
+ prevTicks = 0;
7650
7495
  }
7651
7496
  }
7652
7497
  curCell.FetchPageIndex++;
@@ -7655,7 +7500,7 @@ var EntityFilterGetter;
7655
7500
  retryDelay = 0;
7656
7501
  }
7657
7502
  catch (e) {
7658
- console.log(e);
7503
+ console.error(e);
7659
7504
  // Request failed so let's add a delay and try again soon.
7660
7505
  retryDelay += RETRY_DELAY_INCREMENT;
7661
7506
  retryAttempts -= 1;