@agoric/swing-store 0.9.2-other-dev-3eb1a1d.0 → 0.9.2-other-dev-d15096d.0.d15096d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/swing-store",
3
- "version": "0.9.2-other-dev-3eb1a1d.0+3eb1a1d",
3
+ "version": "0.9.2-other-dev-d15096d.0.d15096d",
4
4
  "description": "Persistent storage for SwingSet",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -13,27 +13,29 @@
13
13
  "scripts": {
14
14
  "build": "exit 0",
15
15
  "test": "ava",
16
- "test:c8": "c8 --all $C8_OPTIONS ava",
16
+ "test:c8": "c8 --all ${C8_OPTIONS:-} ava",
17
17
  "test:xs": "exit 0",
18
18
  "lint-fix": "yarn lint:eslint --fix",
19
- "lint": "run-s --continue-on-error lint:*",
20
- "lint:types": "tsc",
21
- "lint:eslint": "eslint ."
19
+ "lint": "yarn run -T run-s --continue-on-error 'lint:*'",
20
+ "lint:types": "yarn run -T tsc",
21
+ "lint:eslint": "yarn run -T eslint ."
22
22
  },
23
23
  "dependencies": {
24
- "@agoric/internal": "0.3.3-other-dev-3eb1a1d.0+3eb1a1d",
25
- "@endo/base64": "^1.0.9",
26
- "@endo/bundle-source": "^3.5.0",
27
- "@endo/check-bundle": "^1.0.12",
28
- "@endo/errors": "^1.2.8",
29
- "@endo/nat": "^5.0.13",
30
- "better-sqlite3": "^9.1.1"
24
+ "@agoric/internal": "0.3.3-other-dev-d15096d.0.d15096d",
25
+ "@endo/base64": "^1.0.12",
26
+ "@endo/bundle-source": "^4.1.2",
27
+ "@endo/check-bundle": "^1.0.17",
28
+ "@endo/errors": "^1.2.13",
29
+ "@endo/nat": "^5.1.3",
30
+ "better-sqlite3": "^10.1.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@endo/init": "^1.1.7",
34
- "@types/better-sqlite3": "^7.6.9",
33
+ "@endo/init": "^1.1.12",
34
+ "@endo/promise-kit": "^1.1.13",
35
+ "@endo/stream": "^1.2.13",
36
+ "@types/better-sqlite3": "^7.6.13",
35
37
  "ava": "^5.3.0",
36
- "c8": "^10.1.2",
38
+ "c8": "^10.1.3",
37
39
  "tmp": "^0.2.1"
38
40
  },
39
41
  "publishConfig": {
@@ -49,7 +51,10 @@
49
51
  "timeout": "2m"
50
52
  },
51
53
  "typeCoverage": {
52
- "atLeast": 79.04
54
+ "atLeast": 80.49
53
55
  },
54
- "gitHead": "3eb1a1d2d75b2b4a94807cd3bf759bc9fc531f05"
56
+ "engines": {
57
+ "node": "^20.9 || ^22.11"
58
+ },
59
+ "gitHead": "d15096dc4ff8b96e9b6cd11954c20d3a9efbb393"
55
60
  }
package/src/archiver.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import { finished as streamFinishedCallback, Readable } from 'node:stream';
2
+ import { pipeline } from 'node:stream/promises';
2
3
  import { promisify } from 'node:util';
3
4
  import { createGzip } from 'node:zlib';
4
5
  import { withDeferredCleanup } from '@agoric/internal';
5
6
 
7
+ /**
8
+ * @import {AnyIterable} from './exporter.js';
9
+ */
10
+
6
11
  const streamFinished = promisify(streamFinishedCallback);
7
12
 
8
- /*
13
+ /**
9
14
  * @param {string} dirPath
10
15
  * @param {object} powers
11
16
  * @param {Pick<import('fs'), 'createWriteStream' | 'mkdirSync' | 'renameSync'>} powers.fs
@@ -15,6 +20,11 @@ const streamFinished = promisify(streamFinishedCallback);
15
20
  export const makeArchiveSnapshot = (dirPath, powers) => {
16
21
  const { fs, path, tmp } = powers;
17
22
  fs.mkdirSync(dirPath, { recursive: true });
23
+ /**
24
+ * @param {string} name
25
+ * @param {AnyIterable<Uint8Array>} gzData
26
+ * @returns {Promise<void>}
27
+ */
18
28
  const archiveSnapshot = (name, gzData) => {
19
29
  const destPath = path.join(dirPath, `${name}.gz`);
20
30
  return withDeferredCleanup(async addCleanup => {
@@ -28,7 +38,7 @@ export const makeArchiveSnapshot = (dirPath, powers) => {
28
38
  postfix: '.gz.tmp',
29
39
  detachDescriptor: true,
30
40
  });
31
- addCleanup(() => removeCallback());
41
+ addCleanup(async () => removeCallback());
32
42
  const writer = fs.createWriteStream('', { fd, flush: true });
33
43
  const reader = Readable.from(gzData);
34
44
  const destroyReader = promisify(reader.destroy.bind(reader));
@@ -42,7 +52,7 @@ export const makeArchiveSnapshot = (dirPath, powers) => {
42
52
  };
43
53
  harden(makeArchiveSnapshot);
44
54
 
45
- /*
55
+ /**
46
56
  * @param {string} dirPath
47
57
  * @param {object} powers
48
58
  * @param {Pick<import('fs'), 'createWriteStream' | 'mkdirSync' | 'renameSync'>} powers.fs
@@ -52,6 +62,11 @@ harden(makeArchiveSnapshot);
52
62
  export const makeArchiveTranscript = (dirPath, powers) => {
53
63
  const { fs, path, tmp } = powers;
54
64
  fs.mkdirSync(dirPath, { recursive: true });
65
+ /**
66
+ * @param {string} spanName
67
+ * @param {AnyIterable<Uint8Array>} entries
68
+ * @returns {Promise<void>}
69
+ */
55
70
  const archiveTranscript = (spanName, entries) => {
56
71
  const destPath = path.join(dirPath, `${spanName}.gz`);
57
72
  return withDeferredCleanup(async addCleanup => {
@@ -65,15 +80,12 @@ export const makeArchiveTranscript = (dirPath, powers) => {
65
80
  postfix: '.gz.tmp',
66
81
  detachDescriptor: true,
67
82
  });
68
- addCleanup(() => removeCallback());
69
- const writer = fs.createWriteStream('', { fd, flush: true });
70
- const gzip = createGzip();
71
- gzip.pipe(writer);
72
- const reader = Readable.from(entries);
73
- const destroyReader = promisify(reader.destroy.bind(reader));
74
- addCleanup(() => destroyReader(null));
75
- reader.pipe(gzip);
76
- await streamFinished(gzip);
83
+ addCleanup(async () => removeCallback());
84
+ await pipeline(
85
+ entries,
86
+ createGzip(),
87
+ fs.createWriteStream('', { fd, flush: true }),
88
+ );
77
89
  fs.renameSync(tmpName, destPath);
78
90
  });
79
91
  };
@@ -1,12 +1,17 @@
1
1
  /**
2
- * @param {import('./internal.js').SwingStoreInternal} internal
3
- * @param {Omit<import('./internal.js').ArtifactMode, 'debug'>} checkMode
2
+ * @param {Pick<SwingStoreInternal, 'bundleStore' | 'transcriptStore' | 'snapStore'>} internal
3
+ * @param {Exclude<ArtifactMode, 'debug'>} checkMode
4
4
  * @returns {void}
5
5
  */
6
6
  export function assertComplete(internal, checkMode) {
7
7
  // every bundle must be populated
8
8
  internal.bundleStore.assertComplete(checkMode);
9
9
 
10
+ /**
11
+ * @import {SwingStoreInternal} from './internal.js';
12
+ * @import {ArtifactMode} from './internal.js';
13
+ */
14
+
10
15
  // every 'isCurrent' transcript span must have all items
11
16
  // TODO: every vat with any data must have a isCurrent transcript
12
17
  // span
@@ -15,8 +15,8 @@ import { createSHA256 } from './hasher.js';
15
15
  * @typedef { EndoZipBase64Bundle | GetExportBundle | NestedEvaluateBundle } Bundle
16
16
  */
17
17
  /**
18
- * @typedef { import('./exporter.js').SwingStoreExporter } SwingStoreExporter
19
- * @typedef { import('./internal.js').ArtifactMode } ArtifactMode
18
+ * @import {SwingStoreExporter} from './exporter.js';
19
+ * @import {ArtifactMode} from './internal.js';
20
20
  *
21
21
  * @typedef {{
22
22
  * addBundle: (bundleID: string, bundle: Bundle) => void;
@@ -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
@@ -12,12 +12,12 @@ import { assertComplete } from './assertComplete.js';
12
12
  import { validateArtifactMode } from './internal.js';
13
13
 
14
14
  /**
15
- * @template T
16
- * @typedef { Iterable<T> | AsyncIterable<T> } AnyIterable
15
+ * @import {ArtifactMode} from './internal.js';
17
16
  */
17
+
18
18
  /**
19
19
  * @template T
20
- * @typedef { IterableIterator<T> | AsyncIterableIterator<T> } AnyIterableIterator
20
+ * @typedef { Iterable<T> | AsyncIterable<T> } AnyIterable
21
21
  */
22
22
 
23
23
  /**
@@ -41,7 +41,7 @@ import { validateArtifactMode } from './internal.js';
41
41
  * Retrieve a value from the "host" portion of the kvStore, just like
42
42
  * hostStorage.hostKVStore.get() would do.
43
43
  *
44
- * @property {() => AnyIterableIterator<KVPair>} getExportData
44
+ * @property {() => AnyIterable<KVPair>} getExportData
45
45
  *
46
46
  * Get a full copy of the first-stage export data (key-value pairs) from the
47
47
  * swingStore. This represents both the contents of the KVStore (excluding host
@@ -56,7 +56,7 @@ import { validateArtifactMode } from './internal.js';
56
56
  * - transcript.${vatID}.${startPos} = ${{ vatID, startPos, endPos, hash }}
57
57
  * - transcript.${vatID}.current = ${{ vatID, startPos, endPos, hash }}
58
58
  *
59
- * @property {() => AnyIterableIterator<string>} getArtifactNames
59
+ * @property {() => AnyIterable<string>} getArtifactNames
60
60
  *
61
61
  * Get a list of name of artifacts available from the swingStore. A name
62
62
  * returned by this method guarantees that a call to `getArtifact` on the same
@@ -69,7 +69,7 @@ import { validateArtifactMode } from './internal.js';
69
69
  * - snapshot.${vatID}.${snapPos}
70
70
  * - bundle.${bundleID}
71
71
  *
72
- * @property {(name: string) => AnyIterableIterator<Uint8Array>} getArtifact
72
+ * @property {(name: string) => AnyIterable<Uint8Array>} getArtifact
73
73
  *
74
74
  * Retrieve an artifact by name as a sequence of binary chunks. May throw if
75
75
  * the artifact is not available, which can occur if the artifact is historical
@@ -83,7 +83,7 @@ import { validateArtifactMode } from './internal.js';
83
83
 
84
84
  /**
85
85
  * @typedef { object } ExportSwingStoreOptions
86
- * @property { import('./internal.js').ArtifactMode } [artifactMode] What artifacts should/must the exporter provide?
86
+ * @property { ArtifactMode } [artifactMode] What artifacts should/must the exporter provide?
87
87
  */
88
88
 
89
89
  /**
package/src/importer.js CHANGED
@@ -5,9 +5,15 @@ import { buffer } from './util.js';
5
5
  import { validateArtifactMode } from './internal.js';
6
6
  import { assertComplete } from './assertComplete.js';
7
7
 
8
+ /**
9
+ * @import {ArtifactMode} from './internal.js';
10
+ * @import {SwingStoreExporter} from './exporter.js';
11
+ * @import {SwingStore} from './swingStore.js';
12
+ */
13
+
8
14
  /**
9
15
  * @typedef { object } ImportSwingStoreOptions
10
- * @property { import('./internal.js').ArtifactMode } [artifactMode] What artifacts should the importer use and require?
16
+ * @property { ArtifactMode } [artifactMode] What artifacts should the importer use and require?
11
17
  */
12
18
 
13
19
  /**
@@ -17,10 +23,10 @@ import { assertComplete } from './assertComplete.js';
17
23
  * returned swingStore is not suitable for execution, and thus only contains
18
24
  * the host facet for committing the populated swingStore.
19
25
  *
20
- * @param {import('./exporter.js').SwingStoreExporter} exporter
26
+ * @param {SwingStoreExporter} exporter
21
27
  * @param {string | null} [dirPath]
22
28
  * @param {ImportSwingStoreOptions} [options]
23
- * @returns {Promise<Pick<import('./swingStore.js').SwingStore, 'hostStorage' | 'debug'>>}
29
+ * @returns {Promise<Pick<SwingStore, 'hostStorage' | 'debug'>>}
24
30
  */
25
31
  export async function importSwingStore(exporter, dirPath = null, options = {}) {
26
32
  if (dirPath && typeof dirPath !== 'string') {
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
@@ -1,12 +1,18 @@
1
1
  import { Fail, q } from '@endo/errors';
2
2
 
3
3
  /**
4
- * @typedef { import('./snapStore.js').SnapStoreInternal } SnapStoreInternal
5
- * @typedef { import('./transcriptStore.js').TranscriptStoreInternal } TranscriptStoreInternal
6
- * @typedef { import('./bundleStore.js').BundleStoreInternal } BundleStoreInternal
4
+ * @import {KVStore} from './kvStore.js';
5
+ */
6
+
7
+ /**
8
+ * @import {SnapStoreInternal} from './snapStore.js';
9
+ * @import {TranscriptStoreInternal} from './transcriptStore.js';
10
+ * @import {BundleStoreInternal} from './bundleStore.js';
7
11
  *
8
12
  * @typedef {{
9
13
  * dirPath: string | null,
14
+ * db: ReturnType<import('better-sqlite3')>,
15
+ * kvStore: KVStore,
10
16
  * transcriptStore: TranscriptStoreInternal,
11
17
  * snapStore: SnapStoreInternal,
12
18
  * 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
 
@@ -1,6 +1,12 @@
1
1
  import { Fail, q } from '@endo/errors';
2
2
  import { assertComplete } from './assertComplete.js';
3
3
 
4
+ /**
5
+ * @import {SwingStoreInternal} from './internal.js';
6
+ * @import {SwingStoreExporter} from './exporter.js';
7
+ * @import {ArtifactMode} from './internal.js';
8
+ */
9
+
4
10
  /**
5
11
  * Given a pre-existing swingstore and a SwingStoreExporter, read in
6
12
  * all the metadata from the exporter and use it to regenerate any
@@ -24,8 +30,8 @@ import { assertComplete } from './assertComplete.js';
24
30
  * an open transaction. The caller is responsible for calling
25
31
  * `hostStorage.commit()` when they are ready.
26
32
  *
27
- * @param {import('./internal.js').SwingStoreInternal} internal
28
- * @param {import('./exporter.js').SwingStoreExporter} exporter
33
+ * @param {SwingStoreInternal} internal
34
+ * @param {SwingStoreExporter} exporter
29
35
  * @returns {Promise<void>}
30
36
  */
31
37
  export async function doRepairMetadata(internal, exporter) {
@@ -60,7 +66,7 @@ export async function doRepairMetadata(internal, exporter) {
60
66
  }
61
67
 
62
68
  // and do a completeness check
63
- /** @type { import('./internal.js').ArtifactMode } */
69
+ /** @type { ArtifactMode } */
64
70
  const artifactMode = 'operational';
65
71
  assertComplete(internal, artifactMode);
66
72
  await exporter.close();
package/src/snapStore.js CHANGED
@@ -7,6 +7,12 @@ 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
+ * @import {makeMeasureSeconds} from '@agoric/internal';
14
+ */
15
+
10
16
  /**
11
17
  * @typedef {object} SnapshotResult
12
18
  * @property {string} hash sha256 hash of (uncompressed) snapshot
@@ -26,13 +32,6 @@ import { buffer } from './util.js';
26
32
  */
27
33
 
28
34
  /**
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
35
  * @typedef {{
37
36
  * loadSnapshot: (vatID: string) => AsyncIterableIterator<Uint8Array>,
38
37
  * saveSnapshot: (vatID: string, snapPos: number, snapshotStream: AsyncIterable<Uint8Array>) => Promise<SnapshotResult>,
@@ -47,17 +46,31 @@ import { buffer } from './util.js';
47
46
  * getExportRecords: (includeHistorical: boolean) => IterableIterator<readonly [key: string, value: string]>,
48
47
  * getArtifactNames: (artifactMode: ArtifactMode) => AsyncIterableIterator<string>,
49
48
  * importSnapshotRecord: (key: string, value: string) => void,
50
- * populateSnapshot: (name: string, makeChunkIterator: () => AnyIterableIterator<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
49
+ * populateSnapshot: (name: string, makeChunkIterator: () => AnyIterable<Uint8Array>, options: { artifactMode: ArtifactMode }) => Promise<void>,
51
50
  * assertComplete: (checkMode: Omit<ArtifactMode, 'debug'>) => void,
52
51
  * repairSnapshotRecord: (key: string, value: string) => void,
53
52
  * }} SnapStoreInternal
54
53
  *
54
+ * @typedef {object} SnapshotRow
55
+ * @property {string} vatID
56
+ * @property {number} snapPos
57
+ * @property {string} [hash]
58
+ * @property {number | null} [inUse]
59
+ * @property {number} uncompressedSize
60
+ * @property {number} compressedSize
61
+ *
55
62
  * @typedef {{
56
63
  * hasHash: (vatID: string, hash: string) => boolean,
57
- * dumpSnapshots: (includeHistorical?: boolean) => {},
64
+ * listAllSnapshots: () => Iterable<SnapshotRow>,
65
+ * dumpSnapshots: (includeHistorical?: boolean) => Record<string, Array<{snapPos: number, hash: string, compressedSnapshot: Buffer, inUse: (null | 0 | 1)}>>,
58
66
  * deleteSnapshotByHash: (vatID: string, hash: string) => void,
59
67
  * }} SnapStoreDebug
60
68
  *
69
+ * @callback SnapshotCallback
70
+ * Called with the gzipped contents of a new heap snapshot.
71
+ * @param {string} name an export key, e.g. `snapshot.${vatID}.${deliveryCount}`
72
+ * @param {Parameters<typeof Readable.from>[0]} compressedData
73
+ * @returns {Promise<void>}
61
74
  */
62
75
 
63
76
  const finished = promisify(finishedCallback);
@@ -66,12 +79,12 @@ const finished = promisify(finishedCallback);
66
79
  * @param {*} db
67
80
  * @param {() => void} ensureTxn
68
81
  * @param {{
69
- * measureSeconds: ReturnType<typeof import('@agoric/internal').makeMeasureSeconds>,
82
+ * measureSeconds: ReturnType<typeof makeMeasureSeconds>,
70
83
  * }} io
71
84
  * @param {(key: string, value: string | undefined) => void} noteExport
72
85
  * @param {object} [options]
73
86
  * @param {boolean | undefined} [options.keepSnapshots]
74
- * @param {(name: string, compressedData: Parameters<import('stream').Readable.from>[0]) => Promise<void>} [options.archiveSnapshot]
87
+ * @param {SnapshotCallback} [options.archiveSnapshot]
75
88
  * @returns {SnapStore & SnapStoreInternal & SnapStoreDebug}
76
89
  */
77
90
  export function makeSnapStore(
@@ -605,7 +618,7 @@ export function makeSnapStore(
605
618
 
606
619
  /**
607
620
  * @param {string} name Artifact name of the snapshot
608
- * @param {() => AnyIterableIterator<Uint8Array>} makeChunkIterator get an iterator of snapshot byte chunks
621
+ * @param {() => AnyIterable<Uint8Array>} makeChunkIterator get an iterator of snapshot byte chunks
609
622
  * @param {object} options
610
623
  * @param {ArtifactMode} options.artifactMode
611
624
  * @returns {Promise<void>}
@@ -677,7 +690,7 @@ export function makeSnapStore(
677
690
 
678
691
  /**
679
692
  * debug function to list all snapshots
680
- *
693
+ * @returns {Iterable<SnapshotRow>}
681
694
  */
682
695
  function* listAllSnapshots() {
683
696
  yield* sqlListAllSnapshots.iterate();
@@ -706,6 +719,7 @@ export function makeSnapStore(
706
719
  const sql = includeHistorical
707
720
  ? sqlDumpAllSnapshots
708
721
  : sqlDumpCurrentSnapshots;
722
+ /** @type {Record<string, Array<{snapPos: number, hash: string, compressedSnapshot: Buffer, inUse: (null | 0 | 1)}>>} */
709
723
  const dump = {};
710
724
  for (const row of sql.iterate()) {
711
725
  const { vatID, snapPos, hash, compressedSnapshot, inUse } = row;