@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 +4 -17
- package/package.json +9 -9
- package/src/assertComplete.js +2 -2
- package/src/bundleStore.js +3 -2
- package/src/exporter.js +3 -7
- package/src/index.js +4 -2
- package/src/internal.js +2 -0
- package/src/kvStore.js +1 -1
- package/src/snapStore.js +16 -12
- package/src/swingStore.js +141 -127
- package/src/transcriptStore.js +10 -7
- package/test/bundles.test.js +5 -13
- package/test/deletion.test.js +7 -5
- package/test/export.test.js +8 -3
- package/test/exportImport.test.js +6 -17
- package/test/import.test.js +17 -13
- package/test/repair-metadata.test.js +9 -5
- package/test/snapstore.test.js +5 -4
- package/test/state.test.js +13 -24
- package/test/util.js +0 -16
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-
|
|
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-
|
|
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-
|
|
24
|
+
"@agoric/internal": "^0.4.0-u20.0",
|
|
25
25
|
"@endo/base64": "^1.0.9",
|
|
26
|
-
"@endo/bundle-source": "^
|
|
27
|
-
"@endo/check-bundle": "^1.0.
|
|
28
|
-
"@endo/errors": "^1.2.
|
|
29
|
-
"@endo/nat": "^5.0
|
|
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.
|
|
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.
|
|
52
|
+
"atLeast": 79.43
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "8e4207fa19dabf76c1f91f8779b5b5b93570ecea"
|
|
55
55
|
}
|
package/src/assertComplete.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {import('./internal.js').SwingStoreInternal} internal
|
|
3
|
-
* @param {
|
|
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) {
|
package/src/bundleStore.js
CHANGED
|
@@ -42,7 +42,7 @@ import { createSHA256 } from './hasher.js';
|
|
|
42
42
|
*
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
|
|
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 {() =>
|
|
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 {() =>
|
|
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) =>
|
|
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
|
-
//
|
|
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
|
|
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: () =>
|
|
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
|
-
*
|
|
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 {
|
|
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 {() =>
|
|
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
|
|
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}
|
|
126
|
-
* be kept
|
|
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 {
|
|
131
|
-
*
|
|
160
|
+
* @param {SwingStoreOptions} [options]
|
|
132
161
|
* @returns {SwingStore}
|
|
133
162
|
*/
|
|
134
|
-
export function makeSwingStore(
|
|
135
|
-
const {
|
|
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
|
-
|
|
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 (
|
|
197
|
+
if (path) {
|
|
148
198
|
if (forceReset) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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(
|
|
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
|
-
|
|
199
|
-
//
|
|
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(
|
|
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, ...
|
|
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, ...
|
|
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, ...
|
|
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 (
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
|
553
|
-
initTranscript:
|
|
554
|
-
rolloverSpan:
|
|
555
|
-
rolloverIncarnation:
|
|
556
|
-
getCurrentSpanBounds:
|
|
557
|
-
addItem:
|
|
558
|
-
readSpan:
|
|
559
|
-
stopUsingTranscript:
|
|
560
|
-
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
|
|
564
|
-
loadSnapshot:
|
|
565
|
-
saveSnapshot:
|
|
566
|
-
deleteAllUnusedSnapshots:
|
|
567
|
-
deleteVatSnapshots:
|
|
568
|
-
stopUsingLastSnapshot:
|
|
569
|
-
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
|
|
573
|
-
addBundle:
|
|
574
|
-
hasBundle:
|
|
575
|
-
getBundle:
|
|
576
|
-
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
|
|
582
|
-
snapStore
|
|
583
|
-
bundleStore
|
|
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
|
|
614
|
-
*
|
|
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(
|
|
629
|
-
if (
|
|
630
|
-
typeof
|
|
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(
|
|
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
|
-
* `
|
|
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(
|
|
648
|
-
typeof
|
|
649
|
-
return makeSwingStore(
|
|
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
|
/**
|
package/src/transcriptStore.js
CHANGED
|
@@ -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
|
-
* @
|
|
10
|
-
*
|
|
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: () =>
|
|
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 {
|
|
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 {() =>
|
|
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
|
*
|
package/test/bundles.test.js
CHANGED
|
@@ -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] =
|
|
68
|
+
const [dbDir, cleanup] = tmpDir('testdb');
|
|
77
69
|
t.teardown(cleanup);
|
|
78
70
|
const { exportData, exportCallback } = makeExportCallback();
|
|
79
71
|
const { kernelStorage, hostStorage } = initSwingStore(dbDir, {
|
package/test/deletion.test.js
CHANGED
|
@@ -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] =
|
|
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] =
|
|
175
|
+
const [dbDir, cleanup] = tmpDir('testdb');
|
|
174
176
|
t.teardown(cleanup);
|
|
175
|
-
const [archiveDir, cleanupArchives] =
|
|
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] =
|
|
536
|
+
const [dbDir, cleanup] = tmpDir('testdb');
|
|
535
537
|
t.teardown(cleanup);
|
|
536
538
|
const store = initSwingStore(dbDir, { exportCallback });
|
|
537
539
|
const { kernelStorage, hostStorage } = store;
|
package/test/export.test.js
CHANGED
|
@@ -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 {
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
168
|
+
const [dbDir, cleanup] = tmpDir('testdb');
|
|
180
169
|
t.teardown(cleanup);
|
|
181
170
|
|
|
182
171
|
const keepTranscripts = runMode !== 'operational';
|
package/test/import.test.js
CHANGED
|
@@ -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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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
|
-
|
|
13
|
+
|
|
14
|
+
const tmpDir = makeTempDirFactory(tmp);
|
|
11
15
|
|
|
12
16
|
test('repair metadata', async t => {
|
|
13
|
-
const [dbDir, cleanup] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
127
|
+
const [dbDir, cleanup] = tmpDir('testdb');
|
|
124
128
|
t.teardown(cleanup);
|
|
125
129
|
|
|
126
130
|
const { exportData, artifacts } = buildData();
|
package/test/snapstore.test.js
CHANGED
|
@@ -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
|
-
|
|
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] =
|
|
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] =
|
|
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);
|
package/test/state.test.js
CHANGED
|
@@ -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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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
|
}
|