@agoric/swing-store 0.10.0-u19.2 → 0.10.0-u20.0

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/CHANGELOG.md CHANGED
@@ -3,23 +3,7 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [0.10.0-u19.2](https://github.com/Agoric/agoric-sdk/compare/@agoric/swing-store@0.10.0-u19.1...@agoric/swing-store@0.10.0-u19.2) (2025-03-13)
7
-
8
- **Note:** Version bump only for package @agoric/swing-store
9
-
10
-
11
-
12
-
13
-
14
- ## [0.10.0-u19.1](https://github.com/Agoric/agoric-sdk/compare/@agoric/swing-store@0.10.0-u19.0...@agoric/swing-store@0.10.0-u19.1) (2025-03-03)
15
-
16
- **Note:** Version bump only for package @agoric/swing-store
17
-
18
-
19
-
20
-
21
-
22
- ## [0.10.0-u19.0](https://github.com/Agoric/agoric-sdk/compare/@agoric/swing-store@0.9.1...@agoric/swing-store@0.10.0-u19.0) (2025-02-24)
6
+ ## [0.10.0-u20.0](https://github.com/Agoric/agoric-sdk/compare/@agoric/swing-store@0.9.1...@agoric/swing-store@0.10.0-u20.0) (2025-04-16)
23
7
 
24
8
 
25
9
  ### ⚠ BREAKING CHANGES
@@ -32,6 +16,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
32
16
  * Add consensus-independent vat transcript archiving configuration to AG_COSMOS_INIT ([d2d5803](https://github.com/Agoric/agoric-sdk/commit/d2d5803baab6e6379d179723244b2e92aac6319a)), closes [#10036](https://github.com/Agoric/agoric-sdk/issues/10036)
33
17
  * add exporter.getHostKV() API ([eb564f9](https://github.com/Agoric/agoric-sdk/commit/eb564f9635397c0706e1f8255b3e125681e2d031)), closes [#8523](https://github.com/Agoric/agoric-sdk/issues/8523)
34
18
  * **cosmic-swingset:** Allow `launch` to accept an already-open swingStore ([c65e5b1](https://github.com/Agoric/agoric-sdk/commit/c65e5b1c531c08026f5f11cf5d5dcdbe238b05ee))
19
+ * **cosmic-swingset:** Introduce inquisitor.mjs ([e6d75db](https://github.com/Agoric/agoric-sdk/commit/e6d75db529f0641e49cc893c087fa3192bf05551))
20
+ * **internal:** Generalize single-level `pick` utility to recursive `attenuate` ([6b36d1e](https://github.com/Agoric/agoric-sdk/commit/6b36d1e5e7e10b9fe62db96294e891978b438c35))
21
+ * **swing-store:** Add options for opening swing-stores ([036053c](https://github.com/Agoric/agoric-sdk/commit/036053c4e4df853666e25500a696804bb50a7256))
35
22
  * **swing-store:** budget-limited deletion of snapshot and transcripts ([c43bf63](https://github.com/Agoric/agoric-sdk/commit/c43bf63846aedf3493ac6e8f4bc9f2bb48401d66)), closes [#8928](https://github.com/Agoric/agoric-sdk/issues/8928)
36
23
  * **swing-store:** faster import of swing-store ([0170568](https://github.com/Agoric/agoric-sdk/commit/0170568d66748af76f0bd24a4acdaa34b9c79cca))
37
24
  * **swing-store:** Limit item deletion to the previously-current transcript span ([766c1bb](https://github.com/Agoric/agoric-sdk/commit/766c1bbb082debe9d6fa94e08466d3596c971843)), closes [#9387](https://github.com/Agoric/agoric-sdk/issues/9387)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/swing-store",
3
- "version": "0.10.0-u19.2",
3
+ "version": "0.10.0-u20.0",
4
4
  "description": "Persistent storage for SwingSet",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -21,16 +21,16 @@
21
21
  "lint:eslint": "eslint ."
22
22
  },
23
23
  "dependencies": {
24
- "@agoric/internal": "^0.4.0-u19.2",
24
+ "@agoric/internal": "^0.4.0-u20.0",
25
25
  "@endo/base64": "^1.0.9",
26
- "@endo/bundle-source": "^3.5.1",
27
- "@endo/check-bundle": "^1.0.13",
28
- "@endo/errors": "^1.2.9",
29
- "@endo/nat": "^5.0.14",
26
+ "@endo/bundle-source": "^4.0.0",
27
+ "@endo/check-bundle": "^1.0.14",
28
+ "@endo/errors": "^1.2.10",
29
+ "@endo/nat": "^5.1.0",
30
30
  "better-sqlite3": "^9.1.1"
31
31
  },
32
32
  "devDependencies": {
33
- "@endo/init": "^1.1.8",
33
+ "@endo/init": "^1.1.9",
34
34
  "@types/better-sqlite3": "^7.6.9",
35
35
  "ava": "^5.3.0",
36
36
  "c8": "^10.1.2",
@@ -49,7 +49,7 @@
49
49
  "timeout": "2m"
50
50
  },
51
51
  "typeCoverage": {
52
- "atLeast": 79.04
52
+ "atLeast": 79.43
53
53
  },
54
- "gitHead": "f0ae74b84cb6de3724bfdcd18b4bea7e8199dee1"
54
+ "gitHead": "8e4207fa19dabf76c1f91f8779b5b5b93570ecea"
55
55
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @param {import('./internal.js').SwingStoreInternal} internal
3
- * @param {Omit<import('./internal.js').ArtifactMode, 'debug'>} checkMode
2
+ * @param {Pick<import('./internal.js').SwingStoreInternal, 'bundleStore' | 'transcriptStore' | 'snapStore'>} internal
3
+ * @param {Exclude<import('./internal.js').ArtifactMode, 'debug'>} checkMode
4
4
  * @returns {void}
5
5
  */
6
6
  export function assertComplete(internal, checkMode) {
@@ -42,7 +42,7 @@ import { createSHA256 } from './hasher.js';
42
42
  *
43
43
  */
44
44
 
45
- function bundleIDFromName(name) {
45
+ export const bundleIDFromName = name => {
46
46
  typeof name === 'string' || Fail`artifact name must be a string`;
47
47
  const [tag, ...pieces] = name.split('.');
48
48
  if (tag !== 'bundle' || pieces.length !== 1) {
@@ -52,7 +52,8 @@ function bundleIDFromName(name) {
52
52
  }
53
53
  const bundleID = pieces[0];
54
54
  return bundleID;
55
- }
55
+ };
56
+ harden(bundleIDFromName);
56
57
 
57
58
  /**
58
59
  * @param {*} db
package/src/exporter.js CHANGED
@@ -15,10 +15,6 @@ import { validateArtifactMode } from './internal.js';
15
15
  * @template T
16
16
  * @typedef { Iterable<T> | AsyncIterable<T> } AnyIterable
17
17
  */
18
- /**
19
- * @template T
20
- * @typedef { IterableIterator<T> | AsyncIterableIterator<T> } AnyIterableIterator
21
- */
22
18
 
23
19
  /**
24
20
  *
@@ -41,7 +37,7 @@ import { validateArtifactMode } from './internal.js';
41
37
  * Retrieve a value from the "host" portion of the kvStore, just like
42
38
  * hostStorage.hostKVStore.get() would do.
43
39
  *
44
- * @property {() => AnyIterableIterator<KVPair>} getExportData
40
+ * @property {() => AnyIterable<KVPair>} getExportData
45
41
  *
46
42
  * Get a full copy of the first-stage export data (key-value pairs) from the
47
43
  * swingStore. This represents both the contents of the KVStore (excluding host
@@ -56,7 +52,7 @@ import { validateArtifactMode } from './internal.js';
56
52
  * - transcript.${vatID}.${startPos} = ${{ vatID, startPos, endPos, hash }}
57
53
  * - transcript.${vatID}.current = ${{ vatID, startPos, endPos, hash }}
58
54
  *
59
- * @property {() => AnyIterableIterator<string>} getArtifactNames
55
+ * @property {() => AnyIterable<string>} getArtifactNames
60
56
  *
61
57
  * Get a list of name of artifacts available from the swingStore. A name
62
58
  * returned by this method guarantees that a call to `getArtifact` on the same
@@ -69,7 +65,7 @@ import { validateArtifactMode } from './internal.js';
69
65
  * - snapshot.${vatID}.${snapPos}
70
66
  * - bundle.${bundleID}
71
67
  *
72
- * @property {(name: string) => AnyIterableIterator<Uint8Array>} getArtifact
68
+ * @property {(name: string) => AnyIterable<Uint8Array>} getArtifact
73
69
  *
74
70
  * Retrieve an artifact by name as a sequence of binary chunks. May throw if
75
71
  * the artifact is not available, which can occur if the artifact is historical
package/src/index.js CHANGED
@@ -4,10 +4,12 @@ export { importSwingStore } from './importer.js';
4
4
 
5
5
  export { makeArchiveSnapshot, makeArchiveTranscript } from './archiver.js';
6
6
 
7
- // temporary, for the benefit of SwingSet/misc-tools/replay-transcript.js
7
+ // for the benefit of tools like SwingSet/misc-tools/replay-transcript.js
8
+ export { makeKVStore, getKeyType } from './kvStore.js';
9
+ export { makeTranscriptStore } from './transcriptStore.js';
8
10
  export { makeSnapStore } from './snapStore.js';
9
- // and less temporary, for SwingSet/test/vat-warehouse/test-reload-snapshot.js
10
11
  export { makeSnapStoreIO } from './snapStoreIO.js';
12
+ export { makeBundleStore, bundleIDFromName } from './bundleStore.js';
11
13
 
12
14
  // eslint-disable-next-line import/export
13
15
  export * from './types-index.js';
package/src/internal.js CHANGED
@@ -7,6 +7,8 @@ import { Fail, q } from '@endo/errors';
7
7
  *
8
8
  * @typedef {{
9
9
  * dirPath: string | null,
10
+ * db: ReturnType<import('better-sqlite3')>,
11
+ * kvStore: import('./kvStore.js').KVStore,
10
12
  * transcriptStore: TranscriptStoreInternal,
11
13
  * snapStore: SnapStoreInternal,
12
14
  * bundleStore: BundleStoreInternal,
package/src/kvStore.js CHANGED
@@ -27,7 +27,7 @@ export function getKeyType(key) {
27
27
  /**
28
28
  * @param {object} db The SQLite database connection.
29
29
  * @param {() => void} ensureTxn Called before mutating methods to establish a DB transaction
30
- * @param {(...args: string[]) => void} trace Called after sets/gets to record a debug log
30
+ * @param {(...args: string[]) => void} trace Called after set/delete to record a debug log
31
31
  * @returns { KVStore }
32
32
  */
33
33
 
package/src/snapStore.js CHANGED
@@ -7,6 +7,11 @@ import { Fail, q } from '@endo/errors';
7
7
  import { withDeferredCleanup } from '@agoric/internal';
8
8
  import { buffer } from './util.js';
9
9
 
10
+ /**
11
+ * @import { AnyIterable, SwingStoreExporter } from './exporter.js';
12
+ * @import { ArtifactMode } from './internal.js';
13
+ */
14
+
10
15
  /**
11
16
  * @typedef {object} SnapshotResult
12
17
  * @property {string} hash sha256 hash of (uncompressed) snapshot
@@ -26,13 +31,6 @@ import { buffer } from './util.js';
26
31
  */
27
32
 
28
33
  /**
29
- * @import {AnyIterableIterator} from './exporter.js'
30
- */
31
-
32
- /**
33
- * @typedef { import('./exporter.js').SwingStoreExporter } SwingStoreExporter
34
- * @typedef { import('./internal.js').ArtifactMode } ArtifactMode
35
- *
36
34
  * @typedef {{
37
35
  * loadSnapshot: (vatID: string) => AsyncIterableIterator<Uint8Array>,
38
36
  * saveSnapshot: (vatID: string, snapPos: number, snapshotStream: AsyncIterable<Uint8Array>) => Promise<SnapshotResult>,
@@ -47,17 +45,23 @@ import { buffer } from './util.js';
47
45
  * getExportRecords: (includeHistorical: boolean) => IterableIterator<readonly [key: string, value: string]>,
48
46
  * getArtifactNames: (artifactMode: ArtifactMode) => AsyncIterableIterator<string>,
49
47
  * importSnapshotRecord: (key: string, value: string) => void,
50
- * populateSnapshot: (name: string, makeChunkIterator: () => AnyIterableIterator<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
48
+ * populateSnapshot: (name: string, makeChunkIterator: () => AnyIterable<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
51
49
  * assertComplete: (checkMode: Omit<ArtifactMode, 'debug'>) => void,
52
50
  * repairSnapshotRecord: (key: string, value: string) => void,
53
51
  * }} SnapStoreInternal
54
52
  *
55
53
  * @typedef {{
56
54
  * hasHash: (vatID: string, hash: string) => boolean,
57
- * dumpSnapshots: (includeHistorical?: boolean) => {},
55
+ * listAllSnapshots: () => Iterable<{}>,
56
+ * dumpSnapshots: (includeHistorical?: boolean) => Record<string, Array<{snapPos: number, hash: string, compressedSnapshot: Buffer, inUse: (null | 0 | 1)}>>,
58
57
  * deleteSnapshotByHash: (vatID: string, hash: string) => void,
59
58
  * }} SnapStoreDebug
60
59
  *
60
+ * @callback SnapshotCallback
61
+ * Called with the gzipped contents of a new heap snapshot.
62
+ * @param {string} name an export key, e.g. `snapshot.${vatID}.${deliveryCount}`
63
+ * @param {Parameters<import('stream').Readable.from>[0]} compressedData
64
+ * @returns {Promise<void>}
61
65
  */
62
66
 
63
67
  const finished = promisify(finishedCallback);
@@ -71,7 +75,7 @@ const finished = promisify(finishedCallback);
71
75
  * @param {(key: string, value: string | undefined) => void} noteExport
72
76
  * @param {object} [options]
73
77
  * @param {boolean | undefined} [options.keepSnapshots]
74
- * @param {(name: string, compressedData: Parameters<import('stream').Readable.from>[0]) => Promise<void>} [options.archiveSnapshot]
78
+ * @param {SnapshotCallback} [options.archiveSnapshot]
75
79
  * @returns {SnapStore & SnapStoreInternal & SnapStoreDebug}
76
80
  */
77
81
  export function makeSnapStore(
@@ -605,7 +609,7 @@ export function makeSnapStore(
605
609
 
606
610
  /**
607
611
  * @param {string} name Artifact name of the snapshot
608
- * @param {() => AnyIterableIterator<Uint8Array>} makeChunkIterator get an iterator of snapshot byte chunks
612
+ * @param {() => AnyIterable<Uint8Array>} makeChunkIterator get an iterator of snapshot byte chunks
609
613
  * @param {object} options
610
614
  * @param {ArtifactMode} options.artifactMode
611
615
  * @returns {Promise<void>}
@@ -677,7 +681,6 @@ export function makeSnapStore(
677
681
 
678
682
  /**
679
683
  * debug function to list all snapshots
680
- *
681
684
  */
682
685
  function* listAllSnapshots() {
683
686
  yield* sqlListAllSnapshots.iterate();
@@ -706,6 +709,7 @@ export function makeSnapStore(
706
709
  const sql = includeHistorical
707
710
  ? sqlDumpAllSnapshots
708
711
  : sqlDumpCurrentSnapshots;
712
+ /** @type {Record<string, Array<{snapPos: number, hash: string, compressedSnapshot: Buffer, inUse: (null | 0 | 1)}>>} */
709
713
  const dump = {};
710
714
  for (const row of sql.iterate()) {
711
715
  const { vatID, snapPos, hash, compressedSnapshot, inUse } = row;
package/src/swingStore.js CHANGED
@@ -1,12 +1,15 @@
1
1
  // @ts-check
2
2
  /* eslint-env node */
3
3
  import * as fs from 'fs';
4
- import * as path from 'path';
4
+ import * as pathlib from 'path';
5
5
 
6
6
  import sqlite3 from 'better-sqlite3';
7
7
 
8
8
  import { Fail, q } from '@endo/errors';
9
9
 
10
+ import { attenuate } from '@agoric/internal';
11
+ import { TRUE } from '@agoric/internal/src/js-utils.js';
12
+
10
13
  import { dbFileInDirectory } from './util.js';
11
14
  import { makeKVStore, getKeyType } from './kvStore.js';
12
15
  import { makeTranscriptStore } from './transcriptStore.js';
@@ -16,6 +19,14 @@ import { createSHA256 } from './hasher.js';
16
19
  import { makeSnapStoreIO } from './snapStoreIO.js';
17
20
  import { doRepairMetadata } from './repairMetadata.js';
18
21
 
22
+ // https://github.com/WiseLibs/better-sqlite3/blob/HEAD/docs/api.md#new-databasepath-options
23
+ const IN_MEMORY = ':memory:';
24
+
25
+ /**
26
+ * @template T
27
+ * @typedef {(input: T) => T} Replacer
28
+ */
29
+
19
30
  /**
20
31
  * @typedef { import('./kvStore.js').KVStore } KVStore
21
32
  *
@@ -119,24 +130,63 @@ import { doRepairMetadata } from './repairMetadata.js';
119
130
  * relative the relevant transactional unit.
120
131
  */
121
132
 
133
+ /**
134
+ * @typedef {object} SwingStoreOptions
135
+ * @property {boolean} [asFile] For testing, interpret path as a file rather than a swingstore.sqlite parent directory
136
+ * @property {Buffer} [serialized] Binary data to load in memory
137
+ * @property {boolean} [unsafeFastMode] Disable SQLite safeties for e.g. fast import
138
+ * @property {boolean} [readonly]
139
+ * @property {string} [traceFile] Path at which to record KVStore set/delete activity
140
+ * @property {boolean} [keepSnapshots] Retain old heap snapshots
141
+ * @property {boolean} [keepTranscripts] Retain old transcript span items
142
+ * @property {import('./snapStore.js').SnapshotCallback} [archiveSnapshot] Called after creation of a new heap snapshot
143
+ * @property {import('./transcriptStore.js').TranscriptCallback} [archiveTranscript] Called after a formerly-current transcript span is finalized
144
+ * @property {(pendingExports: Iterable<[key: string, value: string | null]>) => void} [exportCallback]
145
+ * @property {Replacer<ReturnType<makeKVStore>>} [wrapKvStore]
146
+ * @property {Replacer<ReturnType<makeTranscriptStore>>} [wrapTranscriptStore]
147
+ * @property {Replacer<ReturnType<makeSnapStore>>} [wrapSnapStore]
148
+ * @property {Replacer<ReturnType<makeBundleStore>>} [wrapBundleStore]
149
+ */
150
+
122
151
  /**
123
152
  * Do the work of `initSwingStore` and `openSwingStore`.
124
153
  *
125
- * @param {string|null} dirPath Path to a directory in which database files may
126
- * be kept. If this is null, the database will be an in-memory ephemeral
154
+ * @param {string|null} path Path to a directory in which database files may
155
+ * be kept (or when the `asFile` option is true, the path to such a database
156
+ * file). If this is null, the database will be an in-memory ephemeral
127
157
  * database that evaporates when the process exits, which is useful for testing.
128
158
  * @param {boolean} forceReset If true, initialize the database to an empty
129
159
  * state if it already exists
130
- * @param {object} options Configuration options
131
- *
160
+ * @param {SwingStoreOptions} [options]
132
161
  * @returns {SwingStore}
133
162
  */
134
- export function makeSwingStore(dirPath, forceReset, options = {}) {
135
- const { serialized } = options;
163
+ export function makeSwingStore(path, forceReset, options = {}) {
164
+ const {
165
+ asFile = false,
166
+ serialized,
167
+ unsafeFastMode,
168
+ readonly = false,
169
+
170
+ traceFile,
171
+ keepSnapshots,
172
+ keepTranscripts,
173
+ archiveSnapshot,
174
+ archiveTranscript,
175
+ exportCallback,
176
+ wrapKvStore = x => x,
177
+ wrapTranscriptStore = x => x,
178
+ wrapSnapStore = x => x,
179
+ wrapBundleStore = x => x,
180
+ } = options;
181
+
136
182
  if (serialized) {
137
183
  Buffer.isBuffer(serialized) || Fail`options.serialized must be Buffer`;
138
- dirPath === null || Fail`options.serialized makes :memory: DB`;
184
+ path === null || Fail`options.serialized makes :memory: DB`;
139
185
  }
186
+ exportCallback === undefined ||
187
+ typeof exportCallback === 'function' ||
188
+ Fail`export callback must be a function`;
189
+
140
190
  let crankhasher;
141
191
  function resetCrankhash() {
142
192
  crankhasher = createSHA256();
@@ -144,40 +194,22 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
144
194
  resetCrankhash();
145
195
 
146
196
  let filePath;
147
- if (dirPath) {
197
+ if (path) {
148
198
  if (forceReset) {
149
- try {
150
- // Node.js 16.8.0 warns:
151
- // In future versions of Node.js, fs.rmdir(path, { recursive: true }) will
152
- // be removed. Use fs.rm(path, { recursive: true }) instead
153
- if (fs.rmSync) {
154
- fs.rmSync(dirPath, { recursive: true });
155
- } else {
156
- fs.rmdirSync(dirPath, { recursive: true });
157
- }
158
- } catch (e) {
159
- // Attempting to delete a non-existent directory is allowed
160
- if (e.code !== 'ENOENT') {
161
- throw e;
162
- }
163
- }
199
+ fs.rmSync(path, { recursive: true, force: true });
200
+ }
201
+ if (asFile) {
202
+ filePath = path;
203
+ } else {
204
+ fs.mkdirSync(path, { recursive: true });
205
+ filePath = dbFileInDirectory(path);
164
206
  }
165
- fs.mkdirSync(dirPath, { recursive: true });
166
- filePath = dbFileInDirectory(dirPath);
167
207
  } else {
168
208
  filePath = ':memory:';
169
209
  }
170
210
 
171
- const {
172
- traceFile,
173
- keepSnapshots,
174
- keepTranscripts,
175
- archiveSnapshot,
176
- archiveTranscript,
177
- } = options;
178
-
179
211
  let traceOutput = traceFile
180
- ? fs.createWriteStream(path.resolve(traceFile), {
212
+ ? fs.createWriteStream(pathlib.resolve(traceFile), {
181
213
  flags: 'a',
182
214
  })
183
215
  : null;
@@ -194,10 +226,10 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
194
226
  }
195
227
 
196
228
  /** @type {*} */
197
- let db = sqlite3(
198
- serialized || filePath,
199
- // { verbose: console.log },
200
- );
229
+ let db = sqlite3(/** @type {string} */ (serialized || filePath), {
230
+ readonly,
231
+ // verbose: console.log,
232
+ });
201
233
 
202
234
  // We use WAL (write-ahead log) mode to allow a background export process to
203
235
  // keep reading from an earlier DB state, while allowing execution to proceed
@@ -230,7 +262,7 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
230
262
  }
231
263
 
232
264
  // PRAGMAs have to happen outside a transaction
233
- setUnsafeFastMode(options.unsafeFastMode);
265
+ if (!readonly) setUnsafeFastMode(unsafeFastMode);
234
266
 
235
267
  // We use IMMEDIATE because the kernel is supposed to be the sole writer of
236
268
  // the DB, and if some other process is holding a write lock, we want to find
@@ -273,11 +305,6 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
273
305
  )
274
306
  `);
275
307
 
276
- const { exportCallback } = options;
277
- exportCallback === undefined ||
278
- typeof exportCallback === 'function' ||
279
- Fail`export callback must be a function`;
280
-
281
308
  const sqlAddPendingExport = db.prepare(`
282
309
  INSERT INTO pendingExports (key, value)
283
310
  VALUES (?, ?)
@@ -290,31 +317,22 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
290
317
  }
291
318
  }
292
319
 
293
- const kvStore = makeKVStore(db, ensureTxn, trace);
320
+ const kvStore = wrapKvStore(makeKVStore(db, ensureTxn, trace));
294
321
 
295
- const { dumpTranscripts, ...transcriptStore } = makeTranscriptStore(
296
- db,
297
- ensureTxn,
298
- noteExport,
299
- {
322
+ const { dumpTranscripts, ...transcriptStoreInternal } = wrapTranscriptStore(
323
+ makeTranscriptStore(db, ensureTxn, noteExport, {
300
324
  keepTranscripts,
301
325
  archiveTranscript,
302
- },
326
+ }),
303
327
  );
304
- const { dumpSnapshots, ...snapStore } = makeSnapStore(
305
- db,
306
- ensureTxn,
307
- makeSnapStoreIO(),
308
- noteExport,
309
- {
328
+ const { dumpSnapshots, ...snapStoreInternal } = wrapSnapStore(
329
+ makeSnapStore(db, ensureTxn, makeSnapStoreIO(), noteExport, {
310
330
  keepSnapshots,
311
331
  archiveSnapshot,
312
- },
332
+ }),
313
333
  );
314
- const { dumpBundles, ...bundleStore } = makeBundleStore(
315
- db,
316
- ensureTxn,
317
- noteExport,
334
+ const { dumpBundles, ...bundleStoreInternal } = wrapBundleStore(
335
+ makeBundleStore(db, ensureTxn, noteExport),
318
336
  );
319
337
 
320
338
  const sqlCommit = db.prepare('COMMIT');
@@ -325,13 +343,9 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
325
343
  let inCrank = false;
326
344
 
327
345
  function diskUsage() {
328
- if (dirPath) {
329
- const dataFilePath = dbFileInDirectory(dirPath);
330
- const stat = fs.statSync(dataFilePath);
331
- return stat.size;
332
- } else {
333
- return 0;
334
- }
346
+ if (filePath === IN_MEMORY) return 0;
347
+ const stat = fs.statSync(filePath);
348
+ return stat.size;
335
349
  }
336
350
 
337
351
  const kernelKVStore = {
@@ -503,10 +517,13 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
503
517
 
504
518
  /** @type {import('./internal.js').SwingStoreInternal} */
505
519
  const internal = harden({
506
- dirPath,
507
- snapStore,
508
- transcriptStore,
509
- bundleStore,
520
+ dirPath: asFile ? null : path,
521
+ asFile,
522
+ db,
523
+ kvStore,
524
+ snapStore: snapStoreInternal,
525
+ transcriptStore: transcriptStoreInternal,
526
+ bundleStore: bundleStoreInternal,
510
527
  });
511
528
 
512
529
  async function repairMetadata(exporter) {
@@ -549,38 +566,38 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
549
566
  return db;
550
567
  }
551
568
 
552
- const transcriptStorePublic = {
553
- initTranscript: transcriptStore.initTranscript,
554
- rolloverSpan: transcriptStore.rolloverSpan,
555
- rolloverIncarnation: transcriptStore.rolloverIncarnation,
556
- getCurrentSpanBounds: transcriptStore.getCurrentSpanBounds,
557
- addItem: transcriptStore.addItem,
558
- readSpan: transcriptStore.readSpan,
559
- stopUsingTranscript: transcriptStore.stopUsingTranscript,
560
- deleteVatTranscripts: transcriptStore.deleteVatTranscripts,
561
- };
569
+ const transcriptStore = attenuate(transcriptStoreInternal, {
570
+ initTranscript: TRUE,
571
+ rolloverSpan: TRUE,
572
+ rolloverIncarnation: TRUE,
573
+ getCurrentSpanBounds: TRUE,
574
+ addItem: TRUE,
575
+ readSpan: TRUE,
576
+ stopUsingTranscript: TRUE,
577
+ deleteVatTranscripts: TRUE,
578
+ });
562
579
 
563
- const snapStorePublic = {
564
- loadSnapshot: snapStore.loadSnapshot,
565
- saveSnapshot: snapStore.saveSnapshot,
566
- deleteAllUnusedSnapshots: snapStore.deleteAllUnusedSnapshots,
567
- deleteVatSnapshots: snapStore.deleteVatSnapshots,
568
- stopUsingLastSnapshot: snapStore.stopUsingLastSnapshot,
569
- getSnapshotInfo: snapStore.getSnapshotInfo,
570
- };
580
+ const snapStore = attenuate(snapStoreInternal, {
581
+ loadSnapshot: TRUE,
582
+ saveSnapshot: TRUE,
583
+ deleteAllUnusedSnapshots: TRUE,
584
+ deleteVatSnapshots: TRUE,
585
+ stopUsingLastSnapshot: TRUE,
586
+ getSnapshotInfo: TRUE,
587
+ });
571
588
 
572
- const bundleStorePublic = {
573
- addBundle: bundleStore.addBundle,
574
- hasBundle: bundleStore.hasBundle,
575
- getBundle: bundleStore.getBundle,
576
- deleteBundle: bundleStore.deleteBundle,
577
- };
589
+ const bundleStore = attenuate(bundleStoreInternal, {
590
+ addBundle: TRUE,
591
+ hasBundle: TRUE,
592
+ getBundle: TRUE,
593
+ deleteBundle: TRUE,
594
+ });
578
595
 
579
596
  const kernelStorage = {
580
597
  kvStore: kernelKVStore,
581
- transcriptStore: transcriptStorePublic,
582
- snapStore: snapStorePublic,
583
- bundleStore: bundleStorePublic,
598
+ transcriptStore,
599
+ snapStore,
600
+ bundleStore,
584
601
  startCrank,
585
602
  establishCrankSavepoint,
586
603
  rollbackCrank,
@@ -610,43 +627,40 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
610
627
  }
611
628
 
612
629
  /**
613
- * Create a new swingset store. If given a directory path string, a persistent
614
- * store will be created in that directory; if there is already a store there,
615
- * it will be reinitialized to an empty state. If the path is null or
616
- * undefined, a memory-only ephemeral store will be created that will evaporate
617
- * on program exit.
618
- *
619
- * @param {string|null} dirPath Path to a directory in which database files may
620
- * be kept. This directory need not actually exist yet (if it doesn't it will
621
- * be created) but it is reserved (by the caller) for the exclusive use of
622
- * this swing store instance. If null, an ephemeral (memory only) store will
623
- * be created.
624
- * @param {object?} options Optional configuration options
630
+ * Create a new swingset store at the given `path`, overwriting any prior store
631
+ * there.
625
632
  *
633
+ * @param {string|null} [path] Path to a directory in which database files may
634
+ * be kept (or when the `asFile` option is true, the path to such a database
635
+ * file). This directory or file need not actually exist yet (if it doesn't
636
+ * it will be created) but it is reserved (by the caller) for the exclusive
637
+ * use of this swing store instance. If null, an ephemeral (memory only)
638
+ * store will be created.
639
+ * @param {SwingStoreOptions} [options]
626
640
  * @returns {SwingStore}
627
641
  */
628
- export function initSwingStore(dirPath = null, options = {}) {
629
- if (dirPath) {
630
- typeof dirPath === 'string' || Fail`dirPath must be a string`;
642
+ export function initSwingStore(path = null, options = {}) {
643
+ if (path) {
644
+ typeof path === 'string' || Fail`path must be a string`;
631
645
  }
632
- return makeSwingStore(dirPath, true, options);
646
+ return makeSwingStore(path, true, options);
633
647
  }
634
648
 
635
649
  /**
636
650
  * Open a persistent swingset store. If there is no existing store at the given
637
- * `dirPath`, a new, empty store will be created.
638
- *
639
- * @param {string} dirPath Path to a directory in which database files may be kept.
640
- * This directory need not actually exist yet (if it doesn't it will be
641
- * created) but it is reserved (by the caller) for the exclusive use of this
642
- * swing store instance.
643
- * @param {object?} options Optional configuration options
651
+ * `path`, a new, empty store will be created.
644
652
  *
653
+ * @param {string} path Path to a directory in which database files may be kept
654
+ * (or when the `asFile` option is true, the path to such a database file).
655
+ * This directory or file need not actually exist yet (if it doesn't it will
656
+ * be created) but it is reserved (by the caller) for the exclusive use of
657
+ * this swing store instance.
658
+ * @param {SwingStoreOptions} [options]
645
659
  * @returns {SwingStore}
646
660
  */
647
- export function openSwingStore(dirPath, options = {}) {
648
- typeof dirPath === 'string' || Fail`dirPath must be a string`;
649
- return makeSwingStore(dirPath, false, options);
661
+ export function openSwingStore(path, options = {}) {
662
+ typeof path === 'string' || Fail`path must be a string`;
663
+ return makeSwingStore(path, false, options);
650
664
  }
651
665
 
652
666
  /**
@@ -6,13 +6,11 @@ import BufferLineTransform from '@agoric/internal/src/node/buffer-line-transform
6
6
  import { createSHA256 } from './hasher.js';
7
7
 
8
8
  /**
9
- * @template T
10
- * @typedef { IterableIterator<T> | AsyncIterableIterator<T> } AnyIterableIterator<T>
9
+ * @import { AnyIterable } from './exporter.js';
10
+ * @import { ArtifactMode } from './internal.js';
11
11
  */
12
12
 
13
13
  /**
14
- * @typedef { import('./internal.js').ArtifactMode } ArtifactMode
15
- *
16
14
  * @typedef {{
17
15
  * initTranscript: (vatID: string) => void,
18
16
  * rolloverSpan: (vatID: string) => Promise<number>,
@@ -29,7 +27,7 @@ import { createSHA256 } from './hasher.js';
29
27
  * getExportRecords: (includeHistorical: boolean) => IterableIterator<readonly [key: string, value: string]>,
30
28
  * getArtifactNames: (artifactMode: ArtifactMode) => AsyncIterableIterator<string>,
31
29
  * importTranscriptSpanRecord: (key: string, value: string) => void,
32
- * populateTranscriptSpan: (name: string, makeChunkIterator: () => AnyIterableIterator<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
30
+ * populateTranscriptSpan: (name: string, makeChunkIterator: () => AnyIterable<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
33
31
  * assertComplete: (checkMode: Omit<ArtifactMode, 'debug'>) => void,
34
32
  * repairTranscriptSpanRecord: (key: string, value: string) => void,
35
33
  * readFullVatTranscript: (vatID: string) => Iterable<{position: number, item: string}>
@@ -39,6 +37,11 @@ import { createSHA256 } from './hasher.js';
39
37
  * dumpTranscripts: (includeHistorical?: boolean) => {[vatID: string]: {[position: number]: string}}
40
38
  * }} TranscriptStoreDebug
41
39
  *
40
+ * @callback TranscriptCallback
41
+ * Called with the entries of a newly-finalized transcript span.
42
+ * @param {string} spanName e.g., `transcript.${vatID}.${startPos}.${endPos}`
43
+ * @param {AnyIterable<Uint8Array>} entries as from `exportSpan`
44
+ * @returns {Promise<void>}
42
45
  */
43
46
 
44
47
  function* empty() {
@@ -62,7 +65,7 @@ function insistTranscriptPosition(position) {
62
65
  * @param {(key: string, value: string | undefined ) => void} noteExport
63
66
  * @param {object} [options]
64
67
  * @param {boolean} [options.keepTranscripts]
65
- * @param {(spanName: string, entries: ReturnType<exportSpan>) => Promise<void>} [options.archiveTranscript]
68
+ * @param {TranscriptCallback} [options.archiveTranscript]
66
69
  * @returns { TranscriptStore & TranscriptStoreInternal & TranscriptStoreDebug }
67
70
  */
68
71
  export function makeTranscriptStore(
@@ -775,7 +778,7 @@ export function makeTranscriptStore(
775
778
  * Import a transcript span from another store.
776
779
  *
777
780
  * @param {string} name Artifact Name of the transcript span
778
- * @param {() => AnyIterableIterator<Uint8Array>} makeChunkIterator get an iterator of transcript byte chunks
781
+ * @param {() => AnyIterable<Uint8Array>} makeChunkIterator get an iterator of transcript byte chunks
779
782
  * @param {object} options
780
783
  * @param {ArtifactMode} options.artifactMode
781
784
  *
@@ -1,13 +1,16 @@
1
1
  // @ts-check
2
2
  import test from 'ava';
3
- import tmp from 'tmp';
4
3
  import { Buffer } from 'buffer';
4
+ import tmp from 'tmp';
5
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
5
6
  import { createSHA256 } from '../src/hasher.js';
6
7
  import { initSwingStore } from '../src/swingStore.js';
7
8
  import { makeSwingStoreExporter } from '../src/exporter.js';
8
9
  import { importSwingStore } from '../src/importer.js';
9
10
  import { buffer } from '../src/util.js';
10
11
 
12
+ const tmpDir = makeTempDirFactory(tmp);
13
+
11
14
  function makeB0ID(bundle) {
12
15
  return `b0-${createSHA256(JSON.stringify(bundle)).finish()}`;
13
16
  }
@@ -53,17 +56,6 @@ function makeExportCallback() {
53
56
  };
54
57
  }
55
58
 
56
- const tmpDir = prefix =>
57
- new Promise((resolve, reject) => {
58
- tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
59
- if (err) {
60
- reject(err);
61
- } else {
62
- resolve([name, removeCallback]);
63
- }
64
- });
65
- });
66
-
67
59
  const collectArray = async iter => {
68
60
  const items = [];
69
61
  for await (const item of iter) {
@@ -73,7 +65,7 @@ const collectArray = async iter => {
73
65
  };
74
66
 
75
67
  test('b0 export', async t => {
76
- const [dbDir, cleanup] = await tmpDir('testdb');
68
+ const [dbDir, cleanup] = tmpDir('testdb');
77
69
  t.teardown(cleanup);
78
70
  const { exportData, exportCallback } = makeExportCallback();
79
71
  const { kernelStorage, hostStorage } = initSwingStore(dbDir, {
@@ -7,13 +7,15 @@ import fs from 'node:fs';
7
7
  import zlib from 'node:zlib';
8
8
  import sqlite3 from 'better-sqlite3';
9
9
  import tmp from 'tmp';
10
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
10
11
  import { arrayIsLike } from '@agoric/internal/tools/ava-assertions.js';
11
- import { tmpDir } from './util.js';
12
12
  import { initSwingStore } from '../src/swingStore.js';
13
13
  import { makeArchiveSnapshot, makeArchiveTranscript } from '../src/archiver.js';
14
14
  import { makeSwingStoreExporter } from '../src/exporter.js';
15
15
  import { importSwingStore } from '../src/importer.js';
16
16
 
17
+ const tmpDir = makeTempDirFactory(tmp);
18
+
17
19
  async function* getSnapshotStream() {
18
20
  yield Buffer.from('abc');
19
21
  }
@@ -139,7 +141,7 @@ const getExport = async (dbDir, artifactMode) => {
139
141
  };
140
142
 
141
143
  const reImport = async (t, dbDir, artifactMode) => {
142
- const [dbDir2, cleanup] = await tmpDir('testdb2');
144
+ const [dbDir2, cleanup] = tmpDir('testdb2');
143
145
  t.teardown(cleanup);
144
146
  const exporter = makeSwingStoreExporter(dbDir, { artifactMode });
145
147
  const ss2 = await importSwingStore(exporter, dbDir2, { artifactMode });
@@ -170,9 +172,9 @@ const setupTranscript = async (t, keepTranscripts) => {
170
172
  }
171
173
  mergeExportDeltas(currentExportData, exports);
172
174
  };
173
- const [dbDir, cleanup] = await tmpDir('testdb');
175
+ const [dbDir, cleanup] = tmpDir('testdb');
174
176
  t.teardown(cleanup);
175
- const [archiveDir, cleanupArchives] = await tmpDir('archives');
177
+ const [archiveDir, cleanupArchives] = tmpDir('archives');
176
178
  t.teardown(cleanupArchives);
177
179
  const fsPowers = { fs, path, tmp };
178
180
  const archiveSnapshot = makeArchiveSnapshot(archiveDir, fsPowers);
@@ -531,7 +533,7 @@ const setupSnapshots = async t => {
531
533
  }
532
534
  mergeExportDeltas(currentExportData, exports);
533
535
  };
534
- const [dbDir, cleanup] = await tmpDir('testdb');
536
+ const [dbDir, cleanup] = tmpDir('testdb');
535
537
  t.teardown(cleanup);
536
538
  const store = initSwingStore(dbDir, { exportCallback });
537
539
  const { kernelStorage, hostStorage } = store;
@@ -1,9 +1,14 @@
1
1
  import test from 'ava';
2
+ import tmp from 'tmp';
3
+
4
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
2
5
 
3
6
  import { buffer } from '../src/util.js';
4
7
  import { initSwingStore, makeSwingStoreExporter } from '../src/index.js';
5
8
 
6
- import { tmpDir, getSnapshotStream, makeB0ID } from './util.js';
9
+ import { getSnapshotStream, makeB0ID } from './util.js';
10
+
11
+ const tmpDir = makeTempDirFactory(tmp);
7
12
 
8
13
  const rank = {
9
14
  operational: 1,
@@ -23,7 +28,7 @@ const bundle0 = { moduleFormat: 'nestedEvaluate', source: '1+1' };
23
28
  const bundle0ID = makeB0ID(bundle0);
24
29
 
25
30
  const exportTest = test.macro(async (t, mode) => {
26
- const [dbDir, cleanup] = await tmpDir('testdb');
31
+ const [dbDir, cleanup] = tmpDir('testdb');
27
32
  t.teardown(cleanup);
28
33
  // const dbDir = 't-db';
29
34
 
@@ -234,7 +239,7 @@ test('export debug', exportTest, 'debug');
234
239
  test('export debug-on-pruned', exportTest, 'debug-on-pruned');
235
240
 
236
241
  test('export omits pruned span artifacts', async t => {
237
- const [dbDir, cleanup] = await tmpDir('testdb');
242
+ const [dbDir, cleanup] = tmpDir('testdb');
238
243
  t.teardown(cleanup);
239
244
  // const dbDir = 't-db';
240
245
 
@@ -6,10 +6,14 @@ import test from 'ava';
6
6
  import tmp from 'tmp';
7
7
  import bundleSource from '@endo/bundle-source';
8
8
 
9
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
10
+
9
11
  import { initSwingStore } from '../src/swingStore.js';
10
12
  import { makeSwingStoreExporter } from '../src/exporter.js';
11
13
  import { importSwingStore } from '../src/importer.js';
12
14
 
15
+ const tmpDir = makeTempDirFactory(tmp);
16
+
13
17
  function makeExportLog() {
14
18
  const exportLog = [];
15
19
  const shadowStore = new Map();
@@ -33,21 +37,6 @@ function makeExportLog() {
33
37
  };
34
38
  }
35
39
 
36
- /**
37
- * @param {string} [prefix]
38
- * @returns {Promise<[string, () => void]>}
39
- */
40
- const tmpDir = prefix =>
41
- new Promise((resolve, reject) => {
42
- tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
43
- if (err) {
44
- reject(err);
45
- } else {
46
- resolve([name, removeCallback]);
47
- }
48
- });
49
- });
50
-
51
40
  async function embundle(filename) {
52
41
  const bundleFile = new URL(filename, import.meta.url).pathname;
53
42
  const bundle = await bundleSource(bundleFile);
@@ -96,7 +85,7 @@ const compareElems = (a, b) => a[0].localeCompare(b[0]);
96
85
 
97
86
  test('crank abort leaves no debris in export log', async t => {
98
87
  const exportLog = makeExportLog();
99
- const [dbDir, cleanup] = await tmpDir('testdb');
88
+ const [dbDir, cleanup] = tmpDir('testdb');
100
89
  t.teardown(cleanup);
101
90
 
102
91
  const ssOut = initSwingStore(dbDir, {
@@ -176,7 +165,7 @@ async function testExportImport(
176
165
  failureMode = 'none',
177
166
  ) {
178
167
  const exportLog = makeExportLog();
179
- const [dbDir, cleanup] = await tmpDir('testdb');
168
+ const [dbDir, cleanup] = tmpDir('testdb');
180
169
  t.teardown(cleanup);
181
170
 
182
171
  const keepTranscripts = runMode !== 'operational';
@@ -7,12 +7,14 @@ import { Buffer } from 'buffer';
7
7
 
8
8
  import sqlite3 from 'better-sqlite3';
9
9
  import test from 'ava';
10
+ import tmp from 'tmp';
10
11
  import { decodeBase64 } from '@endo/base64';
11
12
 
13
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
14
+
12
15
  import { buffer } from '../src/util.js';
13
16
  import { importSwingStore, makeSwingStoreExporter } from '../src/index.js';
14
17
 
15
- import { tmpDir } from './util.js';
16
18
  import {
17
19
  buildData,
18
20
  bundle0,
@@ -29,6 +31,8 @@ const rank = {
29
31
  debug: 4,
30
32
  };
31
33
 
34
+ const tmpDir = makeTempDirFactory(tmp);
35
+
32
36
  function convert(orig) {
33
37
  const bundles = Object.fromEntries(
34
38
  Object.entries(orig.bundles).map(([bundleID, encBundle]) => {
@@ -42,7 +46,7 @@ function convert(orig) {
42
46
  }
43
47
 
44
48
  test('import empty', async t => {
45
- const [dbDir, cleanup] = await tmpDir('testdb');
49
+ const [dbDir, cleanup] = tmpDir('testdb');
46
50
  t.teardown(cleanup);
47
51
  const exporter = makeExporter(new Map(), new Map());
48
52
  const ss = await importSwingStore(exporter, dbDir);
@@ -61,7 +65,7 @@ const importTest = test.macro(async (t, mode) => {
61
65
  /** @import {ArtifactMode} from '../src/internal.js' */
62
66
  const artifactMode = /** @type {ArtifactMode} */ (mode);
63
67
 
64
- const [dbDir, cleanup] = await tmpDir('testdb');
68
+ const [dbDir, cleanup] = tmpDir('testdb');
65
69
  t.teardown(cleanup);
66
70
 
67
71
  const { exportData, artifacts, t0hash, t2hash, t5hash, t8hash } = buildData();
@@ -211,7 +215,7 @@ test('import archival', importTest, 'archival');
211
215
  test('import debug', importTest, 'debug');
212
216
 
213
217
  test('import is missing bundle', async t => {
214
- const [dbDir, cleanup] = await tmpDir('testdb');
218
+ const [dbDir, cleanup] = tmpDir('testdb');
215
219
  t.teardown(cleanup);
216
220
 
217
221
  const exportData = new Map();
@@ -224,7 +228,7 @@ test('import is missing bundle', async t => {
224
228
  });
225
229
 
226
230
  test('import is missing snapshot', async t => {
227
- const [dbDir, cleanup] = await tmpDir('testdb');
231
+ const [dbDir, cleanup] = tmpDir('testdb');
228
232
  t.teardown(cleanup);
229
233
 
230
234
  const exportData = new Map();
@@ -240,7 +244,7 @@ test('import is missing snapshot', async t => {
240
244
  });
241
245
 
242
246
  test('import is missing transcript span', async t => {
243
- const [dbDir, cleanup] = await tmpDir('testdb');
247
+ const [dbDir, cleanup] = tmpDir('testdb');
244
248
  t.teardown(cleanup);
245
249
 
246
250
  const exportData = new Map();
@@ -265,7 +269,7 @@ test('import is missing transcript span', async t => {
265
269
  });
266
270
 
267
271
  test('import has mismatched transcript span', async t => {
268
- const [dbDir, cleanup] = await tmpDir('testdb');
272
+ const [dbDir, cleanup] = tmpDir('testdb');
269
273
  t.teardown(cleanup);
270
274
 
271
275
  const exportData = new Map();
@@ -289,7 +293,7 @@ test('import has mismatched transcript span', async t => {
289
293
  });
290
294
 
291
295
  test('import has incomplete transcript span', async t => {
292
- const [dbDir, cleanup] = await tmpDir('testdb');
296
+ const [dbDir, cleanup] = tmpDir('testdb');
293
297
  t.teardown(cleanup);
294
298
 
295
299
  const exportData = new Map();
@@ -320,7 +324,7 @@ test('import has incomplete transcript span', async t => {
320
324
  });
321
325
 
322
326
  test('import has corrupt transcript span', async t => {
323
- const [dbDir, cleanup] = await tmpDir('testdb');
327
+ const [dbDir, cleanup] = tmpDir('testdb');
324
328
  t.teardown(cleanup);
325
329
 
326
330
  const exportData = new Map();
@@ -350,7 +354,7 @@ test('import has corrupt transcript span', async t => {
350
354
  });
351
355
 
352
356
  test('import has corrupt snapshot', async t => {
353
- const [dbDir, cleanup] = await tmpDir('testdb');
357
+ const [dbDir, cleanup] = tmpDir('testdb');
354
358
  t.teardown(cleanup);
355
359
 
356
360
  const exportData = new Map();
@@ -373,7 +377,7 @@ test('import has corrupt snapshot', async t => {
373
377
  });
374
378
 
375
379
  test('import has corrupt bundle', async t => {
376
- const [dbDir, cleanup] = await tmpDir('testdb');
380
+ const [dbDir, cleanup] = tmpDir('testdb');
377
381
  t.teardown(cleanup);
378
382
 
379
383
  const exportData = new Map();
@@ -389,7 +393,7 @@ test('import has corrupt bundle', async t => {
389
393
  });
390
394
 
391
395
  test('import has unknown metadata tag', async t => {
392
- const [dbDir, cleanup] = await tmpDir('testdb');
396
+ const [dbDir, cleanup] = tmpDir('testdb');
393
397
  t.teardown(cleanup);
394
398
 
395
399
  const exportData = new Map();
@@ -401,7 +405,7 @@ test('import has unknown metadata tag', async t => {
401
405
  });
402
406
 
403
407
  test('import has unknown artifact tag', async t => {
404
- const [dbDir, cleanup] = await tmpDir('testdb');
408
+ const [dbDir, cleanup] = tmpDir('testdb');
405
409
  t.teardown(cleanup);
406
410
 
407
411
  const artifacts = new Map();
@@ -3,14 +3,18 @@
3
3
  import path from 'path';
4
4
  import test from 'ava';
5
5
  import sqlite3 from 'better-sqlite3';
6
+ import tmp from 'tmp';
7
+
8
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
6
9
 
7
10
  import { importSwingStore, openSwingStore } from '../src/index.js';
8
11
 
9
12
  import { makeExporter, buildData } from './exports.js';
10
- import { tmpDir } from './util.js';
13
+
14
+ const tmpDir = makeTempDirFactory(tmp);
11
15
 
12
16
  test('repair metadata', async t => {
13
- const [dbDir, cleanup] = await tmpDir('testdb');
17
+ const [dbDir, cleanup] = tmpDir('testdb');
14
18
  t.teardown(cleanup);
15
19
 
16
20
  const { exportData, artifacts } = buildData();
@@ -74,7 +78,7 @@ test('repair metadata', async t => {
74
78
  });
75
79
 
76
80
  test('repair metadata ignores kvStore entries', async t => {
77
- const [dbDir, cleanup] = await tmpDir('testdb');
81
+ const [dbDir, cleanup] = tmpDir('testdb');
78
82
  t.teardown(cleanup);
79
83
 
80
84
  const { exportData, artifacts } = buildData();
@@ -97,7 +101,7 @@ test('repair metadata ignores kvStore entries', async t => {
97
101
  });
98
102
 
99
103
  test('repair metadata rejects mismatched snapshot entries', async t => {
100
- const [dbDir, cleanup] = await tmpDir('testdb');
104
+ const [dbDir, cleanup] = tmpDir('testdb');
101
105
  t.teardown(cleanup);
102
106
 
103
107
  const { exportData, artifacts } = buildData();
@@ -120,7 +124,7 @@ test('repair metadata rejects mismatched snapshot entries', async t => {
120
124
  });
121
125
 
122
126
  test('repair metadata rejects mismatched transcript span', async t => {
123
- const [dbDir, cleanup] = await tmpDir('testdb');
127
+ const [dbDir, cleanup] = tmpDir('testdb');
124
128
  t.teardown(cleanup);
125
129
 
126
130
  const { exportData, artifacts } = buildData();
@@ -8,10 +8,11 @@ import zlib from 'node:zlib';
8
8
  import sqlite3 from 'better-sqlite3';
9
9
  import tmp from 'tmp';
10
10
 
11
- import { makeMeasureSeconds } from '@agoric/internal';
11
+ import { makeMeasureSeconds, makeTempDirFactory } from '@agoric/internal';
12
12
  import { makeSnapStore } from '../src/snapStore.js';
13
13
  import { makeArchiveSnapshot } from '../src/archiver.js';
14
- import { tmpDir } from './util.js';
14
+
15
+ const tmpDir = makeTempDirFactory(tmp);
15
16
 
16
17
  function makeExportLog() {
17
18
  const exportLog = [];
@@ -36,7 +37,7 @@ harden(getSnapshotStream);
36
37
  test('compress to cache file; closes snapshot stream', async t => {
37
38
  const db = sqlite3(':memory:');
38
39
  const exportLog = makeExportLog();
39
- const [archiveDir, cleanupArchives] = await tmpDir('archives');
40
+ const [archiveDir, cleanupArchives] = tmpDir('archives');
40
41
  t.teardown(cleanupArchives);
41
42
  const fsPowers = { fs, path, tmp };
42
43
  const archiveSnapshot = makeArchiveSnapshot(archiveDir, fsPowers);
@@ -127,7 +128,7 @@ test('snapStore prepare / commit delete is robust', async t => {
127
128
  measureSeconds: makeMeasureSeconds(() => 0),
128
129
  };
129
130
  const db = sqlite3(':memory:');
130
- const [archiveDir, cleanupArchives] = await tmpDir('archives');
131
+ const [archiveDir, cleanupArchives] = tmpDir('archives');
131
132
  t.teardown(cleanupArchives);
132
133
  const fsPowers = { fs, path, tmp };
133
134
  const archiveSnapshot = makeArchiveSnapshot(archiveDir, fsPowers);
@@ -1,30 +1,19 @@
1
1
  // @ts-check
2
2
 
3
- import tmp from 'tmp';
4
3
  import test from 'ava';
4
+ import tmp from 'tmp';
5
5
 
6
6
  import bundleSource from '@endo/bundle-source';
7
7
 
8
+ import { makeTempDirFactory } from '@agoric/internal/src/tmpDir.js';
9
+
8
10
  import {
9
11
  initSwingStore,
10
12
  openSwingStore,
11
13
  isSwingStore,
12
14
  } from '../src/swingStore.js';
13
15
 
14
- /**
15
- * @param {string} [prefix]
16
- * @returns {Promise<[string, () => void]>}
17
- */
18
- const tmpDir = prefix =>
19
- new Promise((resolve, reject) => {
20
- tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
21
- if (err) {
22
- reject(err);
23
- } else {
24
- resolve([name, removeCallback]);
25
- }
26
- });
27
- });
16
+ const tmpDir = makeTempDirFactory(tmp);
28
17
 
29
18
  async function embundle(filename) {
30
19
  const bundleFile = new URL(filename, import.meta.url).pathname;
@@ -121,9 +110,9 @@ test('in-memory kvStore read/write', t => {
121
110
  });
122
111
 
123
112
  test('persistent kvStore read/write/re-open', async t => {
124
- const [dbDir, cleanup] = await tmpDir('testdb');
125
- const exportLog = makeExportLog();
113
+ const [dbDir, cleanup] = tmpDir('testdb');
126
114
  t.teardown(cleanup);
115
+ const exportLog = makeExportLog();
127
116
  t.is(isSwingStore(dbDir), false);
128
117
  const ss1 = initSwingStore(dbDir, { exportCallback: exportLog.callback });
129
118
  testKVStore(t, ss1, exportLog);
@@ -146,7 +135,7 @@ test('persistent kvStore maxKeySize write', async t => {
146
135
  // This tests that no matter what, we can write 254 unicode characters in the
147
136
  // 0x0800 - 0xFFFF range (single UTF-16 codepoint, but 3 byte UTF-8).
148
137
 
149
- const [dbDir, cleanup] = await tmpDir('testdb');
138
+ const [dbDir, cleanup] = tmpDir('testdb');
150
139
  t.teardown(cleanup);
151
140
  t.is(isSwingStore(dbDir), false);
152
141
  const { kernelStorage, hostStorage } = initSwingStore(dbDir);
@@ -164,7 +153,7 @@ const testTranscriptStore = test.macro({
164
153
  async exec(t, { ephemeral, keepTranscripts }) {
165
154
  let dbDir = null;
166
155
  if (!ephemeral) {
167
- const [tmpPath, cleanup] = await tmpDir('testdb');
156
+ const [tmpPath, cleanup] = tmpDir('testdb');
168
157
  t.teardown(cleanup);
169
158
  t.is(isSwingStore(tmpPath), false);
170
159
  dbDir = tmpPath;
@@ -257,7 +246,7 @@ test(testTranscriptStore, { ephemeral: false, keepTranscripts: true });
257
246
  test(testTranscriptStore, { ephemeral: false, keepTranscripts: false });
258
247
 
259
248
  test('transcriptStore abort', async t => {
260
- const [dbDir, cleanup] = await tmpDir('testdb');
249
+ const [dbDir, cleanup] = tmpDir('testdb');
261
250
  t.teardown(cleanup);
262
251
  const { kernelStorage, hostStorage } = initSwingStore(dbDir);
263
252
  const { transcriptStore } = kernelStorage;
@@ -316,14 +305,14 @@ test('in-memory bundleStore read/write', async t => {
316
305
  });
317
306
 
318
307
  test('persistent bundleStore read/write', async t => {
319
- const [dbDir, cleanup] = await tmpDir('testdb');
308
+ const [dbDir, cleanup] = tmpDir('testdb');
320
309
  t.teardown(cleanup);
321
310
  t.is(isSwingStore(dbDir), false);
322
311
  await testBundleStore(t, dbDir);
323
312
  });
324
313
 
325
314
  test('close will abort transaction', async t => {
326
- const [dbDir, cleanup] = await tmpDir('testdb');
315
+ const [dbDir, cleanup] = tmpDir('testdb');
327
316
  t.teardown(cleanup);
328
317
  const ss1 = initSwingStore(dbDir);
329
318
  ss1.kernelStorage.kvStore.set('key1', 'value');
@@ -341,7 +330,7 @@ test('close will abort transaction', async t => {
341
330
  });
342
331
 
343
332
  test('savepoints', async t => {
344
- const [dbDir, cleanup] = await tmpDir('testdb');
333
+ const [dbDir, cleanup] = tmpDir('testdb');
345
334
  t.teardown(cleanup);
346
335
  const ss1 = initSwingStore(dbDir);
347
336
  ss1.kernelStorage.startCrank();
@@ -360,7 +349,7 @@ test('savepoints', async t => {
360
349
  });
361
350
 
362
351
  test('savepoints do not automatically commit', async t => {
363
- const [dbDir, cleanup] = await tmpDir('testdb');
352
+ const [dbDir, cleanup] = tmpDir('testdb');
364
353
  t.teardown(cleanup);
365
354
  const ss1 = initSwingStore(dbDir);
366
355
  ss1.kernelStorage.startCrank();
package/test/util.js CHANGED
@@ -1,22 +1,6 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import tmp from 'tmp';
3
2
  import { createSHA256 } from '../src/hasher.js';
4
3
 
5
- /**
6
- * @param {string} [prefix]
7
- * @returns {Promise<[string, () => void]>}
8
- */
9
- export const tmpDir = prefix =>
10
- new Promise((resolve, reject) => {
11
- tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
12
- if (err) {
13
- reject(err);
14
- } else {
15
- resolve([name, removeCallback]);
16
- }
17
- });
18
- });
19
-
20
4
  export async function* getSnapshotStream(contents) {
21
5
  yield Buffer.from(contents);
22
6
  }