@dra2020/baseclient 1.0.0 → 1.0.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.
@@ -0,0 +1,2 @@
1
+ export * from './geo';
2
+ export * from './vfeature';
@@ -0,0 +1,67 @@
1
+ import * as geojson from 'geojson';
2
+ import * as Poly from '../poly/all';
3
+ export declare type GeoProperties = geojson.GeoJsonProperties;
4
+ export declare type GeoFeature = geojson.Feature;
5
+ export declare type GeoFeatureArray = GeoFeature[];
6
+ export declare type GeoFeatureCollection = geojson.FeatureCollection;
7
+ export declare function geoCollectionToMap(col: GeoFeatureCollection): GeoFeatureMap;
8
+ export declare function geoMapToCollection(map: GeoFeatureMap): GeoFeatureCollection;
9
+ export declare function geoCollectionToTopo(col: GeoFeatureCollection): Poly.Topo;
10
+ export declare function geoTopoToCollection(topo: Poly.Topo): GeoFeatureCollection;
11
+ export interface GeoFeatureMap {
12
+ [id: string]: GeoFeature;
13
+ }
14
+ export declare type FeatureFunc = (f: GeoFeature) => void;
15
+ interface GeoEntry {
16
+ tag: string;
17
+ col?: GeoFeatureCollection;
18
+ map?: GeoFeatureMap;
19
+ topo?: Poly.Topo;
20
+ }
21
+ declare type GeoEntryMap = {
22
+ [tag: string]: GeoEntry;
23
+ };
24
+ export declare function geoEqual(m1: GeoMultiCollection, m2: GeoMultiCollection): boolean;
25
+ export declare function geoMapEqual(m1: GeoFeatureMap, m2: GeoFeatureMap): boolean;
26
+ export declare class GeoMultiCollection {
27
+ entries: GeoEntryMap;
28
+ all: GeoEntry;
29
+ hidden: any;
30
+ stamp: number;
31
+ constructor(tag?: string, topo?: Poly.Topo, col?: GeoFeatureCollection, map?: GeoFeatureMap);
32
+ empty(): void;
33
+ get nEntries(): number;
34
+ nthEntry(n: number): GeoEntry;
35
+ add(tag: string, topo: Poly.Topo, col: GeoFeatureCollection, map: GeoFeatureMap): void;
36
+ addMulti(multi: GeoMultiCollection): void;
37
+ remove(tag: string): void;
38
+ _onChange(): void;
39
+ _col(e: GeoEntry): GeoFeatureCollection;
40
+ _map(e: GeoEntry): GeoFeatureMap;
41
+ _topo(e: GeoEntry): Poly.Topo;
42
+ colOf(tag: string): GeoFeatureCollection;
43
+ mapOf(tag: string): GeoFeatureMap;
44
+ topoOf(tag: string): Poly.Topo;
45
+ forEachEntry(cb: (e: GeoEntry) => void): void;
46
+ allCol(): GeoFeatureCollection;
47
+ allMap(): GeoFeatureMap;
48
+ allTopo(): Poly.Topo;
49
+ hide(id: any): void;
50
+ show(id: any): void;
51
+ showAll(): void;
52
+ get length(): number;
53
+ nthFeature(n: number): GeoFeature;
54
+ nthFilteredFeature(n: number, cb: (f: GeoFeature) => boolean): GeoFeature;
55
+ forEach(cb: FeatureFunc): void;
56
+ map(cb: (f: GeoFeature) => GeoFeature): GeoFeature[];
57
+ isHidden(id: string): boolean;
58
+ find(id: string): GeoFeature;
59
+ filter(test: (f: GeoFeature) => boolean): GeoMultiCollection;
60
+ }
61
+ export declare enum geoIntersectOptions {
62
+ Intersects = 0,
63
+ Bounds = 1,
64
+ BoundsCenter = 2
65
+ }
66
+ export declare function geoIntersect(multi: GeoMultiCollection, bbox: Poly.BoundBox, opt: geoIntersectOptions): GeoMultiCollection;
67
+ export {};
@@ -0,0 +1,4 @@
1
+ import * as Util from '../util/all';
2
+ import * as Poly from '../poly/all';
3
+ import * as G from './geo';
4
+ export declare function computeVFeature(topoPrecinct: Poly.Topo, bintrie: Util.BinTrie, blocks: string[]): G.GeoFeature;
@@ -2,6 +2,7 @@ import * as FSM from '../fsm/all';
2
2
  import * as Poly from './poly';
3
3
  import * as Q from './quad';
4
4
  export declare function polyIntersects(p1: any, p2: any): boolean;
5
+ export declare function polyDifference(main: any, parts: any[]): any;
5
6
  declare class FsmDifference extends FSM.Fsm {
6
7
  accum: any;
7
8
  polys: any[];
@@ -0,0 +1,22 @@
1
+ # filterexpr
2
+ Simple utility class for applying a user-provided filtering expression against an object.
3
+
4
+ ## FilterExpr
5
+
6
+ Optionally initialize with expression to test against.
7
+ Or call `set(expr: string)` to set the expression.
8
+ The function `test(o: any)` will test the object against the expression.
9
+
10
+ ## Expression syntax
11
+
12
+ The simplest expression is just a string of text. If any field in the object contains the string, the pattern matches.
13
+ All comparisons are case-insensitive.
14
+
15
+ A quoted string can be used to hide special characters. Single or double quotes are allowed.
16
+
17
+ Expressions can be combined with `not`, `and`, and `or`. Precedence is in that order.
18
+
19
+ Expressions can be surrounded by parens to force a precedence interpretation.
20
+
21
+ A field name can be specified to limit the comparison to a specific field. An example is `state: ma`. Field names are case-insensitive.
22
+ Field name is lowest precedence, so `state: ma or tx` would constrain both strings to the `state` property.
package/lib/all/all.ts CHANGED
@@ -17,3 +17,5 @@ import * as OTE from '../ot-editutil/all';
17
17
  export { OTE };
18
18
  import { FilterExpr } from '../filterexpr/all';
19
19
  export { FilterExpr }
20
+ import * as G from '../geo/all';
21
+ export { G };
package/lib/geo/all.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './geo';
2
+ export * from './vfeature';
package/lib/geo/geo.ts ADDED
@@ -0,0 +1,450 @@
1
+ import * as geojson from 'geojson';
2
+ import * as Util from '../util/all'
3
+ import * as Poly from '../poly/all'
4
+
5
+ export type GeoProperties = geojson.GeoJsonProperties;
6
+ export type GeoFeature = geojson.Feature;
7
+ export type GeoFeatureArray = GeoFeature[];
8
+ export type GeoFeatureCollection = geojson.FeatureCollection;
9
+
10
+ export function geoCollectionToMap(col: GeoFeatureCollection): GeoFeatureMap
11
+ {
12
+ if (col == null) return null;
13
+ let map: GeoFeatureMap = {};
14
+ col.features.forEach((f: GeoFeature) => { map[String(f.properties.id)] = f; });
15
+ return map;
16
+ }
17
+
18
+ export function geoMapToCollection(map: GeoFeatureMap): GeoFeatureCollection
19
+ {
20
+ if (map == null || Util.countKeys(map) == 0) return null;
21
+ let col: GeoFeatureCollection = { type: 'FeatureCollection', features: [] };
22
+ Object.keys(map).forEach((geoid: string) => { col.features.push(map[geoid]) });
23
+ return col;
24
+ }
25
+
26
+ export function geoCollectionToTopo(col: GeoFeatureCollection): Poly.Topo
27
+ {
28
+ let topo = Poly.topoFromCollection(col);
29
+ Poly.topoPack(topo);
30
+ return topo;
31
+ }
32
+
33
+ export function geoTopoToCollection(topo: Poly.Topo): GeoFeatureCollection
34
+ {
35
+ let col = Poly.topoToCollection(topo);
36
+ Poly.featurePack(col);
37
+ return col;
38
+ }
39
+
40
+ export interface GeoFeatureMap
41
+ {
42
+ [id: string]: GeoFeature; // Maps id to GeoFeature
43
+ }
44
+
45
+ export type FeatureFunc = (f: GeoFeature) => void;
46
+
47
+ interface GeoEntry
48
+ {
49
+ tag: string;
50
+ col?: GeoFeatureCollection;
51
+ map?: GeoFeatureMap;
52
+ topo?: Poly.Topo;
53
+ }
54
+ type GeoEntryMap = { [tag: string]: GeoEntry };
55
+
56
+ export function geoEqual(m1: GeoMultiCollection, m2: GeoMultiCollection): boolean
57
+ {
58
+ let n1 = m1 ? m1.length : 0;
59
+ let n2 = m2 ? m2.length : 0;
60
+
61
+ if (n1 != n2) return false;
62
+ if (n1 == 0) return true;
63
+
64
+ let n = 0;
65
+ let eq = true;
66
+ m1.forEach(f => { if (eq && !m2.find(f.properties.id)) eq = false });
67
+ return eq;
68
+ }
69
+
70
+ export function geoMapEqual(m1: GeoFeatureMap, m2: GeoFeatureMap): boolean
71
+ {
72
+ if (m1 == null) return Util.isEmpty(m2);
73
+ if (m2 == null) return Util.isEmpty(m1);
74
+ let p: string;
75
+ for (p in m1) if (m1.hasOwnProperty(p))
76
+ if (m1[p] !== m2[p])
77
+ return false;
78
+ for (p in m2) if (m2.hasOwnProperty(p))
79
+ if (m1[p] === undefined)
80
+ return false;
81
+ return true;
82
+ }
83
+
84
+ export class GeoMultiCollection
85
+ {
86
+ entries: GeoEntryMap;
87
+ all: GeoEntry;
88
+ hidden: any;
89
+ stamp: number;
90
+
91
+ constructor(tag?: string, topo?: Poly.Topo, col?: GeoFeatureCollection, map?: GeoFeatureMap)
92
+ {
93
+ this.stamp = Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER / 2);
94
+ this.empty();
95
+ if (tag)
96
+ this.add(tag, topo, col, map);
97
+ }
98
+
99
+ empty()
100
+ {
101
+ this.entries = {};
102
+ this.hidden = {};
103
+ this._onChange();
104
+ }
105
+
106
+ get nEntries(): number { return Util.countKeys(this.entries) }
107
+
108
+ nthEntry(n: number): GeoEntry
109
+ {
110
+ return Util.nthProperty(this.entries, n) as GeoEntry;
111
+ }
112
+
113
+ add(tag: string, topo: Poly.Topo, col: GeoFeatureCollection, map: GeoFeatureMap): void
114
+ {
115
+ let entry = this.entries[tag];
116
+ if (entry === undefined) entry = { tag: tag }, this.entries[tag] = entry;
117
+ if ((topo && entry.topo !== topo) || (col && entry.col !== col) || (map && entry.map !== map))
118
+ {
119
+ entry.topo = topo;
120
+ entry.col = col;
121
+ entry.map = map;
122
+ this._onChange();
123
+ }
124
+ else if (topo == null && col == null && map == null)
125
+ this.remove(tag);
126
+ }
127
+
128
+ addMulti(multi: GeoMultiCollection): void
129
+ {
130
+ multi.forEachEntry(e => {
131
+ this.add(e.tag, e.topo, e.col, e.map);
132
+ });
133
+ for (let p in multi.hidden) if (multi.hidden.hasOwnProperty(p))
134
+ {
135
+ this.hidden[p] = true;
136
+ this._onChange();
137
+ }
138
+ }
139
+
140
+ remove(tag: string): void
141
+ {
142
+ let entry = this.entries[tag];
143
+ if (entry)
144
+ {
145
+ if (entry.topo || entry.col || entry.map)
146
+ this._onChange();
147
+ delete this.entries[tag];
148
+ }
149
+ }
150
+
151
+ _onChange(): void
152
+ {
153
+ this.all = { tag: 'all' };
154
+ this.stamp++;
155
+ }
156
+
157
+ _col(e: GeoEntry): GeoFeatureCollection
158
+ {
159
+ if (e == null) return null;
160
+ if (! e.col)
161
+ {
162
+ if (e.map)
163
+ e.col = geoMapToCollection(e.map);
164
+ else if (e.topo)
165
+ e.col = geoTopoToCollection(e.topo);
166
+ }
167
+ return e.col;
168
+ }
169
+
170
+ _map(e: GeoEntry): GeoFeatureMap
171
+ {
172
+ if (e == null) return null;
173
+ if (! e.map)
174
+ {
175
+ if (e.col)
176
+ e.map = geoCollectionToMap(e.col);
177
+ else if (e.topo)
178
+ {
179
+ e.col = geoTopoToCollection(e.topo);
180
+ e.map = geoCollectionToMap(e.col);
181
+ }
182
+ }
183
+ return e.map;
184
+ }
185
+
186
+ _topo(e: GeoEntry): Poly.Topo
187
+ {
188
+ if (e == null) return null;
189
+ if (! e.topo)
190
+ {
191
+ if (e.col)
192
+ e.topo = geoCollectionToTopo(e.col);
193
+ else if (e.map)
194
+ {
195
+ e.col = geoMapToCollection(e.map);
196
+ e.topo = geoCollectionToTopo(e.col);
197
+ }
198
+ }
199
+ return e.topo;
200
+ }
201
+
202
+ colOf(tag: string): GeoFeatureCollection { return this._col(this.entries[tag]); }
203
+ mapOf(tag: string): GeoFeatureMap { return this._map(this.entries[tag]); }
204
+ topoOf(tag: string): Poly.Topo { return this._topo(this.entries[tag]); }
205
+
206
+ forEachEntry(cb: (e: GeoEntry) => void): void
207
+ {
208
+ Object.values(this.entries).forEach(cb);
209
+ }
210
+
211
+ allCol(): GeoFeatureCollection
212
+ {
213
+ if (this.nEntries == 0) return null;
214
+ if (! this.all.col)
215
+ {
216
+ // optimise case where one entry
217
+ let n = this.nEntries;
218
+ if (n == 1)
219
+ this.all.col = this._col(this.nthEntry(0));
220
+ else
221
+ {
222
+ this.all.col = { type: 'FeatureCollection', features: [] };
223
+ this.forEach(f => { this.all.col.features.push(f) });
224
+ }
225
+ }
226
+ return this.all.col;
227
+ }
228
+
229
+ allMap(): GeoFeatureMap
230
+ {
231
+ if (this.nEntries == 0) return null;
232
+ if (! this.all.map)
233
+ {
234
+ // optimise case where one entry
235
+ let n = this.nEntries;
236
+ if (n == 1)
237
+ this.all.map = this._map(this.nthEntry(0));
238
+ else
239
+ this.all.map = geoCollectionToMap(this.allCol());
240
+ }
241
+ return this.all.map;
242
+ }
243
+
244
+ allTopo(): Poly.Topo
245
+ {
246
+ if (this.nEntries == 0) return null;
247
+ if (! this.all.topo)
248
+ {
249
+ // optimise case where one entry
250
+ let n = this.nEntries;
251
+ if (n == 1)
252
+ this.all.topo = this._topo(this.nthEntry(0));
253
+ else
254
+ this.all.topo = geoCollectionToTopo(this.allCol());
255
+ }
256
+ return this.all.topo;
257
+ }
258
+
259
+ hide(id: any): void
260
+ {
261
+ if (id)
262
+ {
263
+ if (typeof id === 'string')
264
+ this.hidden[id] = true;
265
+ else if (Array.isArray(id))
266
+ id.forEach((i: string) => { this.hidden[i] = true })
267
+ else if (typeof id === 'object')
268
+ for (let p in id) if (id.hasOwnProperty(p)) this.hidden[p] = true;
269
+ this._onChange();
270
+ }
271
+ }
272
+
273
+ show(id: any): void
274
+ {
275
+ if (id)
276
+ {
277
+ if (typeof id === 'string')
278
+ delete this.hidden[id];
279
+ else if (Array.isArray(id))
280
+ id.forEach((i: string) => { delete this.hidden[i] })
281
+ else if (typeof id === 'object')
282
+ for (let p in id) if (id.hasOwnProperty(p)) delete this.hidden[p];
283
+ this._onChange();
284
+ }
285
+ }
286
+
287
+ showAll(): void
288
+ {
289
+ if (! Util.isEmpty(this.hidden))
290
+ {
291
+ this.hidden = {};
292
+ this._onChange();
293
+ }
294
+ }
295
+
296
+ get length(): number
297
+ {
298
+ let n = 0;
299
+ this.forEachEntry(e => {
300
+ if (e.col)
301
+ n += e.col.features.length;
302
+ else if (e.map)
303
+ n += Util.countKeys(e.map);
304
+ else if (e.topo)
305
+ n += Util.countKeys(e.topo.objects);
306
+ });
307
+ return n;
308
+ }
309
+
310
+ // Use forEach in preference to iteration using this function
311
+ nthFeature(n: number): GeoFeature
312
+ {
313
+ let found: GeoFeature;
314
+
315
+ if (n >= 0)
316
+ this.forEachEntry(e => {
317
+ if (found) return;
318
+ let col = this._col(e);
319
+ if (col)
320
+ if (n > col.features.length)
321
+ n -= col.features.length;
322
+ else
323
+ found = col.features[n];
324
+ });
325
+ return found;
326
+ }
327
+
328
+ nthFilteredFeature(n: number, cb: (f: GeoFeature) => boolean)
329
+ {
330
+ let found: GeoFeature;
331
+
332
+ this.forEachEntry(e => {
333
+ if (found) return;
334
+ let col = this._col(e);
335
+ if (col)
336
+ for (let i = 0; !found && i < col.features.length; i++)
337
+ {
338
+ let f = col.features[i];
339
+ if (this.hidden[f.properties.id] === undefined && cb(f))
340
+ {
341
+ if (n === 0)
342
+ {
343
+ found = f;
344
+ break;
345
+ }
346
+ n--;
347
+ }
348
+ }
349
+ });
350
+ return found;
351
+ }
352
+
353
+ forEach(cb: FeatureFunc): void
354
+ {
355
+ this.forEachEntry(e => {
356
+ let col = this._col(e);
357
+ if (e.col)
358
+ e.col.features.forEach(f => { if (this.hidden[f.properties.id] === undefined) cb(f) })
359
+ });
360
+ }
361
+
362
+ map(cb: (f: GeoFeature) => GeoFeature): GeoFeature[]
363
+ {
364
+ let features: GeoFeature[] = [];
365
+ this.forEach((f: GeoFeature) => { features.push(cb(f)) });
366
+ return features;
367
+ }
368
+
369
+ isHidden(id: string): boolean
370
+ {
371
+ return this.hidden[id] !== undefined;
372
+ }
373
+
374
+ find(id: string): GeoFeature
375
+ {
376
+ if (this.hidden[id] !== undefined)
377
+ return undefined;
378
+
379
+ let entries = Object.values(this.entries);
380
+ for (let i = 0; i < entries.length; i++)
381
+ {
382
+ let map = this._map(entries[i]);
383
+ if (map[id])
384
+ return map[id];
385
+ }
386
+
387
+ return undefined;
388
+ }
389
+
390
+ filter(test: (f: GeoFeature) => boolean): GeoMultiCollection
391
+ {
392
+ let m = new GeoMultiCollection();
393
+ this.forEachEntry(e => {
394
+ let col = this._col(e);
395
+ let features = col ? col.features.filter(test) : null;
396
+ if (features && features.length)
397
+ m.add(e.tag, null, { type: 'FeatureCollection', features: features }, null);
398
+ });
399
+ return m;
400
+ }
401
+ }
402
+
403
+ export enum geoIntersectOptions { Intersects, Bounds, BoundsCenter };
404
+
405
+ function geoBoxIntersect(x1: Poly.BoundBox, x2: Poly.BoundBox, opt: geoIntersectOptions): boolean
406
+ {
407
+ if (x1.left === undefined || x2.left === undefined) return false;
408
+
409
+ let l1 = x1.left;
410
+ let l2 = x2.left;
411
+ let r1 = x1.right;
412
+ let r2 = x2.right;
413
+ let b1 = x1.top; // flip
414
+ let b2 = x2.top; // flip
415
+ let t1 = x1.bottom; // flip
416
+ let t2 = x2.bottom; // flip
417
+ let cx2 = l2 + (r2 - l2) / 2;
418
+ let cy2 = t2 + (b2 - t2) / 2;
419
+
420
+ // Note I flipped top and bottom above when extracting,
421
+ // in order to make below logic work for normal y axis alignment (0 at top).
422
+ switch (opt)
423
+ {
424
+ case geoIntersectOptions.Intersects:
425
+ return !(l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1);
426
+ case geoIntersectOptions.Bounds:
427
+ return l1 <= l2 && t1 <= t2 && r1 >= r2 && b1 >= b2;
428
+ case geoIntersectOptions.BoundsCenter:
429
+ return l1 <= cx2 && t1 <= cy2 && r1 >= cx2 && b1 >= cy2;
430
+ }
431
+ }
432
+
433
+ export function geoIntersect(multi: GeoMultiCollection, bbox: Poly.BoundBox, opt: geoIntersectOptions): GeoMultiCollection
434
+ {
435
+ let m: GeoFeatureMap = {};
436
+ let bboxPoly = Poly.boundboxPoly(bbox);
437
+
438
+ multi.forEach((f: GeoFeature) => {
439
+ let box = Poly.boundbox(f);
440
+ if (geoBoxIntersect(bbox, box, opt))
441
+ {
442
+ if (opt !== geoIntersectOptions.Intersects || Poly.polyIntersects(bboxPoly, f))
443
+ m[f.properties.id] = f;
444
+ }
445
+ });
446
+
447
+ let result = new GeoMultiCollection();
448
+ result.add('result', null, null, m);
449
+ return result;
450
+ }
@@ -0,0 +1,34 @@
1
+ import * as geojson from 'geojson';
2
+ import * as Util from '../util/all'
3
+ import * as Poly from '../poly/all'
4
+ import * as G from './geo';
5
+
6
+ // Given the topology for a precinct, the bintrie mapping and the list of blocks, construct the
7
+ // feature data for the virtual feature.
8
+ //
9
+
10
+ export function computeVFeature(topoPrecinct: Poly.Topo, bintrie: Util.BinTrie, blocks: string[]): G.GeoFeature
11
+ {
12
+ let contiguity = new Util.IndexedArray();
13
+ let block_contiguity = new Util.IndexedArray();
14
+ let f = Poly.topoMerge(topoPrecinct, blocks);
15
+ f.properties.datasets = {};
16
+ blocks.forEach(blockid => {
17
+ let b = topoPrecinct.objects[blockid];
18
+ if (b.properties.datasets)
19
+ Util.deepAccum(f.properties.datasets, b.properties.datasets);
20
+ if (b.properties.contiguity)
21
+ {
22
+ b.properties.contiguity.forEach((id: string) => {
23
+ contiguity.set(id === 'OUT_OF_STATE' ? id : bintrie.get(id));
24
+ });
25
+ b.properties.contiguity.forEach((id: string) => {
26
+ block_contiguity.set(id);
27
+ });
28
+ }
29
+ });
30
+ f.properties.contiguity = contiguity.asArray();
31
+ f.properties.block_contiguity = block_contiguity.asArray();
32
+ f.properties.blocks = blocks;
33
+ return f;
34
+ }
package/lib/poly/topo.ts CHANGED
@@ -392,7 +392,7 @@ class FsmIncrementalUnion extends FSM.Fsm
392
392
  let values = Object.values(map);
393
393
  this.work = { nUnion: values.length, nDifference: 0, ms: 0 };
394
394
  let elapsed = new Util.Elapsed();
395
- this.result = topoMergeFeatures(this.key.topo, values);
395
+ this.result = topoMergeFeatures(this.key.multi.allTopo(), values);
396
396
  this.work.ms = elapsed.ms();
397
397
  this.map = map;
398
398
  }
package/lib/poly/union.ts CHANGED
@@ -59,6 +59,23 @@ export function polyIntersects(p1: any, p2: any): boolean
59
59
  return bIntersects;
60
60
  }
61
61
 
62
+ export function polyDifference(main: any, parts: any[]): any
63
+ {
64
+ main = PP.polyUnpack(coords(main));
65
+
66
+ // need to explode multipolygon so that "exploded" is a valid multipolygon input to underlying difference routine
67
+ let exploded: any[] = [];
68
+ parts.forEach((p: any) => {
69
+ p = PP.polyUnpack(coords(p));
70
+ if (Util.depthof(p) == 5)
71
+ p.forEach((poly: any) => { exploded.push(poly) });
72
+ else
73
+ exploded.push(p);
74
+ });
75
+
76
+ return PR.polyRound(_difference(main, exploded));
77
+ }
78
+
62
79
  class FsmDifference extends FSM.Fsm
63
80
  {
64
81
  accum: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dra2020/baseclient",
3
- "version": "1.0.0",
3
+ "version": "1.0.4",
4
4
  "description": "Utility functions for Javascript projects.",
5
5
  "main": "dist/baseclient.js",
6
6
  "types": "./dist/all/all.d.ts",