@agoric/internal 0.3.3-dev-aa5ce6f.0.aa5ce6f → 0.3.3-dev-70fb93a.0.70fb93a

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/internal",
3
- "version": "0.3.3-dev-aa5ce6f.0.aa5ce6f",
3
+ "version": "0.3.3-dev-70fb93a.0.70fb93a",
4
4
  "description": "Externally unsupported utilities internal to agoric-sdk",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -20,7 +20,7 @@
20
20
  "lint:types": "yarn run -T tsc"
21
21
  },
22
22
  "dependencies": {
23
- "@agoric/base-zone": "0.1.1-dev-aa5ce6f.0.aa5ce6f",
23
+ "@agoric/base-zone": "0.1.1-dev-70fb93a.0.70fb93a",
24
24
  "@endo/cache-map": "^1.1.0",
25
25
  "@endo/common": "^1.2.13",
26
26
  "@endo/compartment-mapper": "^1.6.3",
@@ -38,7 +38,7 @@
38
38
  "jessie.js": "^0.3.4"
39
39
  },
40
40
  "devDependencies": {
41
- "@agoric/cosmic-proto": "0.4.1-dev-aa5ce6f.0.aa5ce6f",
41
+ "@agoric/cosmic-proto": "0.4.1-dev-70fb93a.0.70fb93a",
42
42
  "@endo/exo": "^1.5.12",
43
43
  "@endo/init": "^1.1.12",
44
44
  "@endo/ses-ava": "^1.3.2",
@@ -66,5 +66,5 @@
66
66
  "typeCoverage": {
67
67
  "atLeast": 92.89
68
68
  },
69
- "gitHead": "aa5ce6f32f2313febcf394018e5de46b81d7dbbc"
69
+ "gitHead": "70fb93afcb746d40510dcbe3a20370786fb627db"
70
70
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @template [T=string]
3
+ * @typedef {{
4
+ * has: (key: string) => boolean;
5
+ * get: (key: string) => T | undefined;
6
+ * getNextKey: (previousKey: string) => string | undefined;
7
+ * set: (key: string, value: T) => void;
8
+ * delete: (key: string) => void;
9
+ * }} KVStore
10
+ */
11
+ /**
12
+ * @param {object} db The SQLite database connection.
13
+ * @param {() => void} beforeMutation Called before mutating methods to
14
+ * establish a DB transaction if needed
15
+ * @param {(...args: string[]) => void} trace Called after set/delete to record
16
+ * a debug log
17
+ * @returns {KVStore}
18
+ */
19
+ export function makeKVStore(db: object, beforeMutation: () => void, trace: (...args: string[]) => void): KVStore;
20
+ export function compareByCodePoints(left: any, right: any): 0 | 1 | -1;
21
+ export function makeKVStoreFromMap<T = unknown>(map: Map<string, T>): KVStore<T>;
22
+ export type KVStore<T = string> = {
23
+ has: (key: string) => boolean;
24
+ get: (key: string) => T | undefined;
25
+ getNextKey: (previousKey: string) => string | undefined;
26
+ set: (key: string, value: T) => void;
27
+ delete: (key: string) => void;
28
+ };
29
+ //# sourceMappingURL=kv-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv-store.d.ts","sourceRoot":"","sources":["kv-store.js"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AAEH,gCARW,MAAM,kBACN,MAAM,IAAI,SAEV,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,GAEzB,OAAO,CA0InB;AAOM,uEAoBN;AAOM,mCAJO,CAAC,iBACJ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GACZ,OAAO,CAAC,CAAC,CAAC,CA8DtB;oBAxPa,CAAC,aACF;IACR,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC;IACpC,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACrC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B"}
@@ -0,0 +1,253 @@
1
+ // @ts-check
2
+ import { Fail } from '@endo/errors';
3
+
4
+ /**
5
+ * @template [T=string]
6
+ * @typedef {{
7
+ * has: (key: string) => boolean;
8
+ * get: (key: string) => T | undefined;
9
+ * getNextKey: (previousKey: string) => string | undefined;
10
+ * set: (key: string, value: T) => void;
11
+ * delete: (key: string) => void;
12
+ * }} KVStore
13
+ */
14
+
15
+ /**
16
+ * @param {object} db The SQLite database connection.
17
+ * @param {() => void} beforeMutation Called before mutating methods to
18
+ * establish a DB transaction if needed
19
+ * @param {(...args: string[]) => void} trace Called after set/delete to record
20
+ * a debug log
21
+ * @returns {KVStore}
22
+ */
23
+
24
+ export function makeKVStore(db, beforeMutation, trace) {
25
+ db.exec(`
26
+ CREATE TABLE IF NOT EXISTS kvStore (
27
+ key TEXT,
28
+ value TEXT,
29
+ PRIMARY KEY (key)
30
+ )
31
+ `);
32
+
33
+ const sqlKVGet = db.prepare(`
34
+ SELECT value
35
+ FROM kvStore
36
+ WHERE key = ?
37
+ `);
38
+ sqlKVGet.pluck(true);
39
+
40
+ /**
41
+ * Obtain the value stored for a given key.
42
+ *
43
+ * @param {string} key The key whose value is sought.
44
+ * @returns {string | undefined} the (string) value for the given key, or
45
+ * undefined if there is no such value.
46
+ * @throws if key is not a string.
47
+ */
48
+ function get(key) {
49
+ typeof key === 'string' || Fail`key must be a string`;
50
+ return sqlKVGet.get(key);
51
+ }
52
+
53
+ const sqlKVGetNextKey = db.prepare(`
54
+ SELECT key
55
+ FROM kvStore
56
+ WHERE key > ?
57
+ LIMIT 1
58
+ `);
59
+ sqlKVGetNextKey.pluck(true);
60
+
61
+ /**
62
+ * getNextKey enables callers to iterate over all keys within a given range.
63
+ * To build an iterator of all keys from start (inclusive) to end (exclusive),
64
+ * do:
65
+ *
66
+ * ```js
67
+ * function* iterate(start, end) {
68
+ * if (kvStore.has(start)) {
69
+ * yield start;
70
+ * }
71
+ * let prev = start;
72
+ * while (true) {
73
+ * let next = kvStore.getNextKey(prev);
74
+ * if (!next || next >= end) {
75
+ * break;
76
+ * }
77
+ * yield next;
78
+ * prev = next;
79
+ * }
80
+ * }
81
+ * ```
82
+ *
83
+ * @param {string} previousKey The key returned will always be later than this
84
+ * one.
85
+ * @returns {string | undefined} a key string, or undefined if we reach the
86
+ * end of the store
87
+ * @throws if previousKey is not a string
88
+ */
89
+
90
+ function getNextKey(previousKey) {
91
+ typeof previousKey === 'string' || Fail`previousKey must be a string`;
92
+ return sqlKVGetNextKey.get(previousKey);
93
+ }
94
+
95
+ /**
96
+ * Test if the state contains a value for a given key.
97
+ *
98
+ * @param {string} key The key that is of interest.
99
+ * @returns {boolean} true if a value is stored for the key, false if not.
100
+ * @throws if key is not a string.
101
+ */
102
+ function has(key) {
103
+ typeof key === 'string' || Fail`key must be a string`;
104
+ return get(key) !== undefined;
105
+ }
106
+
107
+ const sqlKVSet = db.prepare(`
108
+ INSERT INTO kvStore (key, value)
109
+ VALUES (?, ?)
110
+ ON CONFLICT DO UPDATE SET value = excluded.value
111
+ `);
112
+
113
+ /**
114
+ * Store a value for a given key. The value will replace any prior value if
115
+ * there was one.
116
+ *
117
+ * @param {string} key The key whose value is being set.
118
+ * @param {string} value The value to set the key to.
119
+ * @throws if either parameter is not a string.
120
+ */
121
+ function set(key, value) {
122
+ typeof key === 'string' || Fail`key must be a string`;
123
+ typeof value === 'string' || Fail`value must be a string`;
124
+ // synchronous read after write within a transaction is safe
125
+ // The transaction's overall success will be awaited during commit
126
+ beforeMutation();
127
+ sqlKVSet.run(key, value);
128
+ trace('set', key, value);
129
+ }
130
+
131
+ const sqlKVDel = db.prepare(`
132
+ DELETE FROM kvStore
133
+ WHERE key = ?
134
+ `);
135
+
136
+ /**
137
+ * Remove any stored value for a given key. It is permissible for there to be
138
+ * no existing stored value for the key.
139
+ *
140
+ * @param {string} key The key whose value is to be deleted
141
+ * @throws if key is not a string.
142
+ */
143
+ function del(key) {
144
+ typeof key === 'string' || Fail`key must be a string`;
145
+ beforeMutation();
146
+ sqlKVDel.run(key);
147
+ trace('del', key);
148
+ }
149
+
150
+ const kvStore = {
151
+ has,
152
+ get,
153
+ getNextKey,
154
+ set,
155
+ delete: del,
156
+ };
157
+
158
+ return kvStore;
159
+ }
160
+
161
+ // TODO: Replace compareByCodePoints and makeKVStoreFromMap and
162
+ // provideEnhancedKVStore with imports when
163
+ // available.
164
+ // https://github.com/Agoric/agoric-sdk/pull/10299
165
+
166
+ export const compareByCodePoints = (left, right) => {
167
+ const leftIter = left[Symbol.iterator]();
168
+ const rightIter = right[Symbol.iterator]();
169
+ for (;;) {
170
+ const { value: leftChar } = leftIter.next();
171
+ const { value: rightChar } = rightIter.next();
172
+ if (leftChar === undefined && rightChar === undefined) {
173
+ return 0;
174
+ } else if (leftChar === undefined) {
175
+ // left is a prefix of right.
176
+ return -1;
177
+ } else if (rightChar === undefined) {
178
+ // right is a prefix of left.
179
+ return 1;
180
+ }
181
+ const leftCodepoint = /** @type {number} */ (leftChar.codePointAt(0));
182
+ const rightCodepoint = /** @type {number} */ (rightChar.codePointAt(0));
183
+ if (leftCodepoint < rightCodepoint) return -1;
184
+ if (leftCodepoint > rightCodepoint) return 1;
185
+ }
186
+ };
187
+
188
+ /**
189
+ * @template [T=unknown]
190
+ * @param {Map<string, T>} map
191
+ * @returns {KVStore<T>}
192
+ */
193
+ export const makeKVStoreFromMap = map => {
194
+ let sortedKeys;
195
+ let priorKeyReturned;
196
+ let priorKeyIndex;
197
+
198
+ const ensureSorted = () => {
199
+ if (sortedKeys) return;
200
+ sortedKeys = [...map.keys()].sort(compareByCodePoints);
201
+ };
202
+
203
+ const clearGetNextKeyCache = () => {
204
+ priorKeyReturned = undefined;
205
+ priorKeyIndex = -1;
206
+ };
207
+ clearGetNextKeyCache();
208
+
209
+ const clearSorted = () => {
210
+ sortedKeys = undefined;
211
+ clearGetNextKeyCache();
212
+ };
213
+
214
+ /** @type {KVStore<T>} */
215
+ const fakeStore = harden({
216
+ has: key => map.has(key),
217
+ get: key => map.get(key),
218
+ getNextKey: priorKey => {
219
+ assert.typeof(priorKey, 'string');
220
+ ensureSorted();
221
+ const start =
222
+ priorKeyReturned === undefined
223
+ ? 0
224
+ : // If priorKeyReturned <= priorKey, start just after it.
225
+ (compareByCodePoints(priorKeyReturned, priorKey) <= 0 &&
226
+ priorKeyIndex + 1) ||
227
+ // Else if priorKeyReturned immediately follows priorKey, start at
228
+ // its index (and expect to return it again).
229
+ (sortedKeys.at(priorKeyIndex - 1) === priorKey && priorKeyIndex) ||
230
+ // Otherwise, start at the beginning.
231
+ 0;
232
+ for (let i = start; i < sortedKeys.length; i += 1) {
233
+ const key = sortedKeys[i];
234
+ if (compareByCodePoints(key, priorKey) <= 0) continue;
235
+ priorKeyReturned = key;
236
+ priorKeyIndex = i;
237
+ return key;
238
+ }
239
+ // reached end without finding the key, so clear our cache
240
+ clearGetNextKeyCache();
241
+ return undefined;
242
+ },
243
+ set: (key, value) => {
244
+ if (!map.has(key)) clearSorted();
245
+ map.set(key, value);
246
+ },
247
+ delete: key => {
248
+ if (map.has(key)) clearSorted();
249
+ map.delete(key);
250
+ },
251
+ });
252
+ return fakeStore;
253
+ };