@jdeighan/coffee-utils 12.0.0 → 12.0.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,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "12.0.0",
4
+ "version": "12.0.2",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -22,6 +22,8 @@
22
22
  "./section": "./src/Section.js",
23
23
  "./sectionmap": "./src/SectionMap.js",
24
24
  "./fsa": "./src/fsa.js",
25
+ "./giftset": "./src/GiftSet.js",
26
+ "./keyedset": "./src/KeyedSet.js",
25
27
  "./package.json": "./package.json"
26
28
  },
27
29
  "engines": {
@@ -48,14 +50,15 @@
48
50
  },
49
51
  "homepage": "https://github.com/johndeighan/coffee-utils#readme",
50
52
  "dependencies": {
51
- "@jdeighan/base-utils": "^1.0.9",
53
+ "@jdeighan/base-utils": "^1.0.11",
52
54
  "cross-env": "^7.0.3",
55
+ "immer": "^9.0.16",
53
56
  "js-yaml": "^4.1.0",
54
57
  "n-readlines": "^1.0.1",
55
58
  "readline-sync": "^1.4.10",
56
- "svelte": "^3.53.0"
59
+ "svelte": "^3.54.0"
57
60
  },
58
61
  "devDependencies": {
59
- "@jdeighan/unit-tester": "^2.0.68"
62
+ "@jdeighan/unit-tester": "^2.0.71"
60
63
  }
61
64
  }
@@ -10,6 +10,7 @@ import {
10
10
  withExt, slurp, barf, newerDestFileExists,
11
11
  } from '@jdeighan/coffee-utils/fs'
12
12
  import {fromTAML} from '@jdeighan/coffee-utils/taml'
13
+ import {createDraft, finishDraft, produce} from 'immer'
13
14
 
14
15
  # ---------------------------------------------------------------------------
15
16
 
@@ -18,8 +19,8 @@ export class WritableDataStore
18
19
  constructor: (value=undef) ->
19
20
  @store = writable value
20
21
 
21
- subscribe: (callback) ->
22
- return @store.subscribe(callback)
22
+ subscribe: (func) ->
23
+ return @store.subscribe(func)
23
24
 
24
25
  set: (value) ->
25
26
  @store.set(value)
@@ -29,6 +30,91 @@ export class WritableDataStore
29
30
 
30
31
  # ---------------------------------------------------------------------------
31
32
 
33
+ export class BaseDataStore
34
+
35
+ constructor: (@value=undef) ->
36
+ @lSubscribers = []
37
+
38
+ subscribe: (func) ->
39
+ func @value
40
+ @lSubscribers.push func
41
+ return () ->
42
+ pos = @lSubscribers.indexOf func
43
+ @lSubscribers.splice pos, 1
44
+
45
+ set: (val) ->
46
+ @value = val
47
+ @alertSubscribers()
48
+ return
49
+
50
+ update: (func) ->
51
+ @value = func(@value)
52
+ @alertSubscribers()
53
+ return
54
+
55
+ alertSubscribers: () ->
56
+ for func in @lSubscribers
57
+ func @value
58
+ return
59
+
60
+ # ---------------------------------------------------------------------------
61
+
62
+ export class ImmerDataStore extends BaseDataStore
63
+
64
+ constructor: () ->
65
+ super [] # initialize with an empty array
66
+
67
+ getNewState: () ->
68
+
69
+ return produce state, draft =>
70
+ @addGift draft, description, image
71
+ return
72
+
73
+ addGift: (draft, description, image) ->
74
+ draft.push {
75
+ id: 1
76
+ description
77
+ image
78
+ }
79
+
80
+ # ---------------------------------------------------------------------------
81
+
82
+ export class ToDoDataStore
83
+ # --- implemented with immer
84
+
85
+ constructor: () ->
86
+ @lToDos = []
87
+ @lSubscribers = []
88
+
89
+ subscribe: (func) ->
90
+ func(@lToDos)
91
+ @lSubscribers.push func
92
+ return () ->
93
+ index = @lSubscribers.indexOf func
94
+ @lSubscribers.splice index, 1
95
+
96
+ alertSubscribers: () ->
97
+ for func in @lSubscribers
98
+ func(@lToDos)
99
+ return
100
+
101
+ set: (value) ->
102
+ # --- Set new value
103
+ @alertSubscribers()
104
+
105
+ update: (func) ->
106
+ # --- Update value
107
+ @alertSubscribers()
108
+
109
+ add: (name) ->
110
+ @lToDos.push {
111
+ text: name
112
+ done: false
113
+ }
114
+ return
115
+
116
+ # ---------------------------------------------------------------------------
117
+
32
118
  export class LocalStorageDataStore extends WritableDataStore
33
119
 
34
120
  constructor: (@masterKey, defValue=undef) ->
package/src/DataStores.js CHANGED
@@ -33,14 +33,20 @@ import {
33
33
  fromTAML
34
34
  } from '@jdeighan/coffee-utils/taml';
35
35
 
36
+ import {
37
+ createDraft,
38
+ finishDraft,
39
+ produce
40
+ } from 'immer';
41
+
36
42
  // ---------------------------------------------------------------------------
37
43
  export var WritableDataStore = class WritableDataStore {
38
44
  constructor(value = undef) {
39
45
  this.store = writable(value);
40
46
  }
41
47
 
42
- subscribe(callback) {
43
- return this.store.subscribe(callback);
48
+ subscribe(func) {
49
+ return this.store.subscribe(func);
44
50
  }
45
51
 
46
52
  set(value) {
@@ -53,6 +59,112 @@ export var WritableDataStore = class WritableDataStore {
53
59
 
54
60
  };
55
61
 
62
+ // ---------------------------------------------------------------------------
63
+ export var BaseDataStore = class BaseDataStore {
64
+ constructor(value1 = undef) {
65
+ this.value = value1;
66
+ this.lSubscribers = [];
67
+ }
68
+
69
+ subscribe(func) {
70
+ func(this.value);
71
+ this.lSubscribers.push(func);
72
+ return function() {
73
+ var pos;
74
+ pos = this.lSubscribers.indexOf(func);
75
+ return this.lSubscribers.splice(pos, 1);
76
+ };
77
+ }
78
+
79
+ set(val) {
80
+ this.value = val;
81
+ this.alertSubscribers();
82
+ }
83
+
84
+ update(func) {
85
+ this.value = func(this.value);
86
+ this.alertSubscribers();
87
+ }
88
+
89
+ alertSubscribers() {
90
+ var func, i, len, ref;
91
+ ref = this.lSubscribers;
92
+ for (i = 0, len = ref.length; i < len; i++) {
93
+ func = ref[i];
94
+ func(this.value);
95
+ }
96
+ }
97
+
98
+ };
99
+
100
+ // ---------------------------------------------------------------------------
101
+ export var ImmerDataStore = class ImmerDataStore extends BaseDataStore {
102
+ constructor() {
103
+ super([]); // initialize with an empty array
104
+ }
105
+
106
+ getNewState() {
107
+ return produce(state, draft(() => {
108
+ return this.addGift(draft, description, image);
109
+ }));
110
+ }
111
+
112
+ addGift(draft, description, image) {
113
+ return draft.push({
114
+ id: 1,
115
+ description,
116
+ image
117
+ });
118
+ }
119
+
120
+ };
121
+
122
+ // ---------------------------------------------------------------------------
123
+ export var ToDoDataStore = class ToDoDataStore {
124
+ // --- implemented with immer
125
+ constructor() {
126
+ this.lToDos = [];
127
+ this.lSubscribers = [];
128
+ }
129
+
130
+ subscribe(func) {
131
+ func(this.lToDos);
132
+ this.lSubscribers.push(func);
133
+ return function() {
134
+ var index;
135
+ index = this.lSubscribers.indexOf(func);
136
+ return this.lSubscribers.splice(index, 1);
137
+ };
138
+ }
139
+
140
+ alertSubscribers() {
141
+ var func, i, len, ref;
142
+ ref = this.lSubscribers;
143
+ for (i = 0, len = ref.length; i < len; i++) {
144
+ func = ref[i];
145
+ func(this.lToDos);
146
+ }
147
+ }
148
+
149
+ set(value) {
150
+ // --- Set new value
151
+ return this.alertSubscribers();
152
+ }
153
+
154
+ update(func) {
155
+ // --- Update value
156
+ return this.alertSubscribers();
157
+ }
158
+
159
+ add(name) {
160
+ this.lToDos.push({
161
+ text: name,
162
+ done: false
163
+ });
164
+ }
165
+
166
+ };
167
+
56
168
  // ---------------------------------------------------------------------------
57
169
  export var LocalStorageDataStore = class LocalStorageDataStore extends WritableDataStore {
58
170
  constructor(masterKey1, defValue = undef) {
@@ -0,0 +1,61 @@
1
+ # GiftSet.coffee
2
+
3
+ import {produce, enableMapSet} from 'immer'
4
+ import {assert, croak, LOG, LOGVALUE} from '@jdeighan/base-utils'
5
+ import {
6
+ dbgEnter, dbgReturn, dbg,
7
+ } from '@jdeighan/base-utils/debug'
8
+ import {
9
+ undef, defined, notdefined, OL,
10
+ isString, isNonEmptyString, isArray, isHash, isArrayOfStrings,
11
+ isEmpty, nonEmpty,
12
+ } from '@jdeighan/coffee-utils'
13
+
14
+ enableMapSet()
15
+
16
+ # ---------------------------------------------------------------------------
17
+
18
+ export LOGMAP = (label, map) ->
19
+
20
+ lLines = ["MAP #{label}:"]
21
+ for [key, value] from map.entries()
22
+ lLines.push " #{OL(key)}: #{OL(value)}"
23
+ LOG lLines.join("\n")
24
+ LOG()
25
+ return
26
+
27
+ # ---------------------------------------------------------------------------
28
+
29
+ export addGift = produce (draft, giftName, hData={}) ->
30
+
31
+ assert (draft instanceof Map), "draft is not a Map"
32
+ if draft.get giftName
33
+ throw new Error("Gift #{giftName} already exists")
34
+ hValue = {hData...}
35
+ hValue.name = giftName
36
+ draft.set giftName, hValue
37
+ return
38
+
39
+ # ---------------------------------------------------------------------------
40
+
41
+ export reserveGift = produce (draft, giftName, user) ->
42
+
43
+ assert (draft instanceof Map), "draft is not a Map"
44
+ gift = draft.get giftName
45
+ assert gift?, "No such gift: #{giftName}"
46
+ gift.reservedBy = user
47
+ return
48
+
49
+ # ---------------------------------------------------------------------------
50
+
51
+ export cancelReservation = produce (draft, giftName) ->
52
+
53
+ assert (draft instanceof Map), "draft is not a Map"
54
+ gift = draft.get giftName
55
+ assert defined(gift), "No such gift: #{giftName}"
56
+ delete gift.reservedBy
57
+ return
58
+
59
+ # ---------------------------------------------------------------------------
60
+
61
+
package/src/GiftSet.js ADDED
@@ -0,0 +1,80 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // GiftSet.coffee
3
+ import {
4
+ produce,
5
+ enableMapSet
6
+ } from 'immer';
7
+
8
+ import {
9
+ assert,
10
+ croak,
11
+ LOG,
12
+ LOGVALUE
13
+ } from '@jdeighan/base-utils';
14
+
15
+ import {
16
+ dbgEnter,
17
+ dbgReturn,
18
+ dbg
19
+ } from '@jdeighan/base-utils/debug';
20
+
21
+ import {
22
+ undef,
23
+ defined,
24
+ notdefined,
25
+ OL,
26
+ isString,
27
+ isNonEmptyString,
28
+ isArray,
29
+ isHash,
30
+ isArrayOfStrings,
31
+ isEmpty,
32
+ nonEmpty
33
+ } from '@jdeighan/coffee-utils';
34
+
35
+ enableMapSet();
36
+
37
+ // ---------------------------------------------------------------------------
38
+ export var LOGMAP = function(label, map) {
39
+ var key, lLines, ref, value, x;
40
+ lLines = [`MAP ${label}:`];
41
+ ref = map.entries();
42
+ for (x of ref) {
43
+ [key, value] = x;
44
+ lLines.push(` ${OL(key)}: ${OL(value)}`);
45
+ }
46
+ LOG(lLines.join("\n"));
47
+ LOG();
48
+ };
49
+
50
+ // ---------------------------------------------------------------------------
51
+ export var addGift = produce(function(draft, giftName, hData = {}) {
52
+ var hValue;
53
+ assert(draft instanceof Map, "draft is not a Map");
54
+ if (draft.get(giftName)) {
55
+ throw new Error(`Gift ${giftName} already exists`);
56
+ }
57
+ hValue = {...hData};
58
+ hValue.name = giftName;
59
+ draft.set(giftName, hValue);
60
+ });
61
+
62
+ // ---------------------------------------------------------------------------
63
+ export var reserveGift = produce(function(draft, giftName, user) {
64
+ var gift;
65
+ assert(draft instanceof Map, "draft is not a Map");
66
+ gift = draft.get(giftName);
67
+ assert(gift != null, `No such gift: ${giftName}`);
68
+ gift.reservedBy = user;
69
+ });
70
+
71
+ // ---------------------------------------------------------------------------
72
+ export var cancelReservation = produce(function(draft, giftName) {
73
+ var gift;
74
+ assert(draft instanceof Map, "draft is not a Map");
75
+ gift = draft.get(giftName);
76
+ assert(defined(gift), `No such gift: ${giftName}`);
77
+ delete gift.reservedBy;
78
+ });
79
+
80
+ // ---------------------------------------------------------------------------
@@ -0,0 +1,153 @@
1
+ # KeyedSet.coffee
2
+
3
+ import {assert, croak, LOG, LOGVALUE} from '@jdeighan/base-utils'
4
+ import {
5
+ dbgEnter, dbgReturn, dbg,
6
+ } from '@jdeighan/base-utils/debug'
7
+ import {
8
+ undef, defined, notdefined, OL, deepCopy,
9
+ isString, isNonEmptyString, isArray, isHash, isArrayOfStrings,
10
+ isEmpty, nonEmpty,
11
+ } from '@jdeighan/coffee-utils'
12
+
13
+ # ---------------------------------------------------------------------------
14
+
15
+ export class KeyedSet extends Map
16
+
17
+ constructor: (@setName, lKeyNames, @sep='|') ->
18
+ # --- lKeyNames can be:
19
+ # 1. a non-empty string
20
+ # 2. an array of non-empty strings
21
+
22
+ dbgEnter 'KeyedSet'
23
+ super()
24
+ assert isNonEmptyString(@setName), "bad set name: #{OL(@setName)}"
25
+ if isString(lKeyNames)
26
+ assert nonEmpty(lKeyNames), "empty string key name"
27
+ @lKeyNames = [lKeyNames]
28
+ @numKeys = 1
29
+ else if isArray(lKeyNames)
30
+ assert nonEmpty(lKeyNames), "empty key name array"
31
+ for name in lKeyNames
32
+ assert isNonEmptyString(name),
33
+ "name not a non-empty string: #{OL(name)}"
34
+ @numKeys = lKeyNames.length
35
+ @lKeyNames = lKeyNames
36
+ else
37
+ croak "Invalid key names: #{OL(lKeyNames)}"
38
+ dbg "key is #{OL(@lKeyNames)}"
39
+ dbgReturn 'KeyedSet'
40
+
41
+ # ..........................................................
42
+
43
+ add: (keyVal, hData={}) ->
44
+
45
+ dbgEnter 'add', keyVal, hData
46
+ assert ! @has(keyVal), "adding duplicate key #{OL(keyVal)}"
47
+ assert isHash(hData), "hData not a hash: #{OL(hData)}"
48
+ dbg "not a duplicate"
49
+
50
+ hItem = deepCopy hData
51
+
52
+ # --- Add key values to hItem
53
+ lKeyVals = @getKeyValues keyVal
54
+ for name,i in @lKeyNames
55
+ assert notdefined(hItem[name]),
56
+ "hData has key #{name}"
57
+ hItem[name] = lKeyVals[i]
58
+
59
+ key = @getKey(keyVal)
60
+ dbg 'key', key
61
+ dbg 'value', hItem
62
+ @set key, hItem # set() is a method in Map, the base class
63
+ @length = @size # add to all methods that change size
64
+ dbgReturn 'add'
65
+ return this # allow chaining
66
+
67
+ # ..........................................................
68
+
69
+ getKey: (keyVal) ->
70
+ # --- Get the actual key used in the underlying Map object
71
+
72
+ dbgEnter 'getKey'
73
+ key = @getKeyValues(keyVal).join(@sep)
74
+ dbgReturn 'getKey', key
75
+ return key
76
+
77
+ # ..........................................................
78
+
79
+ getKeyValues: (keyVal) ->
80
+ # --- Accepts either a string or an array of strings
81
+ # But all keys must be non-empty strings
82
+ # Always returns an array
83
+
84
+ dbgEnter 'getKeyValues', keyVal
85
+ if isString(keyVal)
86
+ lKeyVals = [keyVal]
87
+ else if isArray(keyVal)
88
+ lKeyVals = keyVal
89
+ else
90
+ croak "Bad key value: #{OL(keyVal)}"
91
+ assert (lKeyVals.length == @numKeys), "Bad # keys in #{OL(keyVal)}"
92
+ for val in lKeyVals
93
+ assert isNonEmptyString(val), "Bad key val: #{OL(val)}"
94
+ dbgReturn 'getKeyValues', lKeyVals
95
+ return lKeyVals
96
+
97
+ # ..........................................................
98
+
99
+ has: (keyVal) ->
100
+
101
+ return super @getKey(keyVal)
102
+
103
+ # ..........................................................
104
+
105
+ update: (keyVal, hData={}) ->
106
+
107
+ dbgEnter 'update', keyVal, hData
108
+ key = @getKey(keyVal)
109
+ dbg "key = #{OL(key)}"
110
+ hItem = @get(key)
111
+ dbg 'hItem', hItem
112
+ assert defined(hItem), "updating missing key #{OL(keyVal)}"
113
+ for key,val of hData
114
+ hItem[key] = val
115
+ dbgReturn 'update'
116
+ return this # allow chaining
117
+
118
+ # ..........................................................
119
+
120
+ remove: (keyVal) ->
121
+
122
+ key = @getKey(keyVal)
123
+ if ! @delete key
124
+ croak "No key #{OL(keyVal)} in #{@setName}"
125
+ @length = @size # add to all methods that change size
126
+ return this # allow chaining
127
+
128
+ # ..........................................................
129
+
130
+ getAllItems: () ->
131
+ # --- Useful for unit tests, but it's usually better
132
+ # to use a generator like .entries()
133
+
134
+ return Array.from(@values())
135
+
136
+ # ..........................................................
137
+
138
+ get: (keyVal) ->
139
+ # --- Override to require that it exists
140
+
141
+ item = super @getKey(keyVal)
142
+ assert defined(item), "No such item: #{OL(keyVal)} in #{@setName}"
143
+ return item
144
+
145
+ # ..........................................................
146
+
147
+ dump: () ->
148
+
149
+ console.log "DUMP #{@setName}:"
150
+ for [key, value] from @entries()
151
+ console.log "#{OL(key)}: #{OL(value)}"
152
+
153
+ # ---------------------------------------------------------------------------
@@ -0,0 +1,187 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // KeyedSet.coffee
3
+ import {
4
+ assert,
5
+ croak,
6
+ LOG,
7
+ LOGVALUE
8
+ } from '@jdeighan/base-utils';
9
+
10
+ import {
11
+ dbgEnter,
12
+ dbgReturn,
13
+ dbg
14
+ } from '@jdeighan/base-utils/debug';
15
+
16
+ import {
17
+ undef,
18
+ defined,
19
+ notdefined,
20
+ OL,
21
+ deepCopy,
22
+ isString,
23
+ isNonEmptyString,
24
+ isArray,
25
+ isHash,
26
+ isArrayOfStrings,
27
+ isEmpty,
28
+ nonEmpty
29
+ } from '@jdeighan/coffee-utils';
30
+
31
+ // ---------------------------------------------------------------------------
32
+ export var KeyedSet = class KeyedSet extends Map {
33
+ constructor(setName, lKeyNames, sep = '|') {
34
+ var j, len, name;
35
+ // --- lKeyNames can be:
36
+ // 1. a non-empty string
37
+ // 2. an array of non-empty strings
38
+ dbgEnter('KeyedSet');
39
+ super();
40
+ this.setName = setName;
41
+ this.sep = sep;
42
+ assert(isNonEmptyString(this.setName), `bad set name: ${OL(this.setName)}`);
43
+ if (isString(lKeyNames)) {
44
+ assert(nonEmpty(lKeyNames), "empty string key name");
45
+ this.lKeyNames = [lKeyNames];
46
+ this.numKeys = 1;
47
+ } else if (isArray(lKeyNames)) {
48
+ assert(nonEmpty(lKeyNames), "empty key name array");
49
+ for (j = 0, len = lKeyNames.length; j < len; j++) {
50
+ name = lKeyNames[j];
51
+ assert(isNonEmptyString(name), `name not a non-empty string: ${OL(name)}`);
52
+ }
53
+ this.numKeys = lKeyNames.length;
54
+ this.lKeyNames = lKeyNames;
55
+ } else {
56
+ croak(`Invalid key names: ${OL(lKeyNames)}`);
57
+ }
58
+ dbg(`key is ${OL(this.lKeyNames)}`);
59
+ dbgReturn('KeyedSet');
60
+ }
61
+
62
+ // ..........................................................
63
+ add(keyVal, hData = {}) {
64
+ var hItem, i, j, key, lKeyVals, len, name, ref;
65
+ dbgEnter('add', keyVal, hData);
66
+ assert(!this.has(keyVal), `adding duplicate key ${OL(keyVal)}`);
67
+ assert(isHash(hData), `hData not a hash: ${OL(hData)}`);
68
+ dbg("not a duplicate");
69
+ hItem = deepCopy(hData);
70
+ // --- Add key values to hItem
71
+ lKeyVals = this.getKeyValues(keyVal);
72
+ ref = this.lKeyNames;
73
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
74
+ name = ref[i];
75
+ assert(notdefined(hItem[name]), `hData has key ${name}`);
76
+ hItem[name] = lKeyVals[i];
77
+ }
78
+ key = this.getKey(keyVal);
79
+ dbg('key', key);
80
+ dbg('value', hItem);
81
+ this.set(key, hItem); // set() is a method in Map, the base class
82
+ this.length = this.size; // add to all methods that change size
83
+ dbgReturn('add');
84
+ return this; // allow chaining
85
+ }
86
+
87
+
88
+ // ..........................................................
89
+ getKey(keyVal) {
90
+ var key;
91
+ // --- Get the actual key used in the underlying Map object
92
+ dbgEnter('getKey');
93
+ key = this.getKeyValues(keyVal).join(this.sep);
94
+ dbgReturn('getKey', key);
95
+ return key;
96
+ }
97
+
98
+ // ..........................................................
99
+ getKeyValues(keyVal) {
100
+ var j, lKeyVals, len, val;
101
+ // --- Accepts either a string or an array of strings
102
+ // But all keys must be non-empty strings
103
+ // Always returns an array
104
+ dbgEnter('getKeyValues', keyVal);
105
+ if (isString(keyVal)) {
106
+ lKeyVals = [keyVal];
107
+ } else if (isArray(keyVal)) {
108
+ lKeyVals = keyVal;
109
+ } else {
110
+ croak(`Bad key value: ${OL(keyVal)}`);
111
+ }
112
+ assert(lKeyVals.length === this.numKeys, `Bad # keys in ${OL(keyVal)}`);
113
+ for (j = 0, len = lKeyVals.length; j < len; j++) {
114
+ val = lKeyVals[j];
115
+ assert(isNonEmptyString(val), `Bad key val: ${OL(val)}`);
116
+ }
117
+ dbgReturn('getKeyValues', lKeyVals);
118
+ return lKeyVals;
119
+ }
120
+
121
+ // ..........................................................
122
+ has(keyVal) {
123
+ return super.has(this.getKey(keyVal));
124
+ }
125
+
126
+ // ..........................................................
127
+ update(keyVal, hData = {}) {
128
+ var hItem, key, val;
129
+ dbgEnter('update', keyVal, hData);
130
+ key = this.getKey(keyVal);
131
+ dbg(`key = ${OL(key)}`);
132
+ hItem = this.get(key);
133
+ dbg('hItem', hItem);
134
+ assert(defined(hItem), `updating missing key ${OL(keyVal)}`);
135
+ for (key in hData) {
136
+ val = hData[key];
137
+ hItem[key] = val;
138
+ }
139
+ dbgReturn('update');
140
+ return this; // allow chaining
141
+ }
142
+
143
+
144
+ // ..........................................................
145
+ remove(keyVal) {
146
+ var key;
147
+ key = this.getKey(keyVal);
148
+ if (!this.delete(key)) {
149
+ croak(`No key ${OL(keyVal)} in ${this.setName}`);
150
+ }
151
+ this.length = this.size; // add to all methods that change size
152
+ return this; // allow chaining
153
+ }
154
+
155
+
156
+ // ..........................................................
157
+ getAllItems() {
158
+ // --- Useful for unit tests, but it's usually better
159
+ // to use a generator like .entries()
160
+ return Array.from(this.values());
161
+ }
162
+
163
+ // ..........................................................
164
+ get(keyVal) {
165
+ var item;
166
+ // --- Override to require that it exists
167
+ item = super.get(this.getKey(keyVal));
168
+ assert(defined(item), `No such item: ${OL(keyVal)} in ${this.setName}`);
169
+ return item;
170
+ }
171
+
172
+ // ..........................................................
173
+ dump() {
174
+ var key, ref, results, value, x;
175
+ console.log(`DUMP ${this.setName}:`);
176
+ ref = this.entries();
177
+ results = [];
178
+ for (x of ref) {
179
+ [key, value] = x;
180
+ results.push(console.log(`${OL(key)}: ${OL(value)}`));
181
+ }
182
+ return results;
183
+ }
184
+
185
+ };
186
+
187
+ // ---------------------------------------------------------------------------