diff --git a/.gitignore b/.gitignore index fc41e9eb..e6c02b43 100644 --- a/.gitignore +++ b/.gitignore @@ -68,7 +68,7 @@ web_modules/ # Output of 'npm pack' *.tgz -!agoric-synthetic-chain-*.tgz +!agoric-synthetic-chain*.tgz # Yarn Integrity file .yarn-integrity @@ -135,5 +135,5 @@ dist # build in CI Dockerfile -docker-bake.json +docker-bake.* /upgrade-test-scripts diff --git a/README.md b/README.md index c50f9d7b..a25fe18e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,24 @@ To run the tests for particular proposals, tsx packages/synthetic-chain test --match upgrade ``` +To use a local build of synthetic-chain, + +```sh +cd packages/synthetic-chain +npm pack +cd - + +for p in $(ls proposals); do + cp -f packages/synthetic-chain/agoric-synthetic-chain-*.tgz proposals/$p/agoric-synthetic-chain.tgz + cd proposals/$p + yarn install + cd - +done +rm -f packages/synthetic-chain/agoric-synthetic-chain-*.tgz +``` + +Then find-replace the "@agoric/synthetic-chain" version in package.json with ""file:agoric-synthetic-chain.tgz". + ## Debugging To get the local files into the container, use a [bind mount](https://docs.docker.com/storage/bind-mounts/). E.g. diff --git a/packages/synthetic-chain/cli.ts b/packages/synthetic-chain/cli.ts index f728d335..a913f231 100755 --- a/packages/synthetic-chain/cli.ts +++ b/packages/synthetic-chain/cli.ts @@ -50,10 +50,9 @@ doctor - diagnostics and quick fixes * Put into places files that building depends upon. */ const prepareDockerBuild = () => { - execSync( - // XXX very brittle - 'cp -r node_modules/@agoric/synthetic-chain/upgrade-test-scripts .', - ); + // XXX file copy very brittle + execSync('cp -r node_modules/@agoric/synthetic-chain/upgrade-test-scripts .'); + execSync('cp -r node_modules/@agoric/synthetic-chain/docker-bake.hcl .'); writeDockerfile(allProposals, buildConfig.fromTag); writeBakefileProposals(allProposals); buildProposalSubmissions(proposals); diff --git a/docker-bake.hcl b/packages/synthetic-chain/docker-bake.hcl similarity index 100% rename from docker-bake.hcl rename to packages/synthetic-chain/docker-bake.hcl diff --git a/packages/synthetic-chain/package.json b/packages/synthetic-chain/package.json index 1b5512ce..1cfd0a4d 100644 --- a/packages/synthetic-chain/package.json +++ b/packages/synthetic-chain/package.json @@ -1,6 +1,6 @@ { "name": "@agoric/synthetic-chain", - "version": "0.0.3", + "version": "0.0.4-2", "description": "Utilities to build a chain and test proposals atop it", "bin": "./cli.ts", "main": "cli.ts", @@ -8,6 +8,7 @@ "files": [ "index.js", "cli.ts", + "docker-bake.hcl", "src", "upgrade-test-scripts" ], @@ -17,7 +18,9 @@ "test:xs": "exit 0" }, "dependencies": { + "@endo/zip": "^1.0.0", "better-sqlite3": "^9.2.2", + "execa": "^8.0.1", "tsx": "^3.12.8", "typescript": "^5.3.3" }, diff --git a/packages/synthetic-chain/src/cli/build.ts b/packages/synthetic-chain/src/cli/build.ts index 5753bb23..c4405c2b 100755 --- a/packages/synthetic-chain/src/cli/build.ts +++ b/packages/synthetic-chain/src/cli/build.ts @@ -23,7 +23,7 @@ export function readBuildConfig(root: string): AgoricSyntheticChainConfig { const { agoricSyntheticChain } = JSON.parse(packageJson); const config = { ...defaultConfig, ...agoricSyntheticChain }; - // TODO mustMatch a shape + // UNTIL https://github.com/Agoric/agoric-3-proposals/issues/77 return config; } diff --git a/packages/synthetic-chain/src/cli/dockerfileGen.ts b/packages/synthetic-chain/src/cli/dockerfileGen.ts index 7da2158b..813ac158 100755 --- a/packages/synthetic-chain/src/cli/dockerfileGen.ts +++ b/packages/synthetic-chain/src/cli/dockerfileGen.ts @@ -117,7 +117,7 @@ WORKDIR /usr/src/upgrade-test-scripts COPY --link ./upgrade-test-scripts/install_deps.sh /usr/src/upgrade-test-scripts/ RUN --mount=type=cache,target=/root/.yarn ./install_deps.sh ${proposalIdentifier}:${proposalName} -COPY --link --chmod=755 ./upgrade-test-scripts/run_eval.sh /usr/src/upgrade-test-scripts/ +COPY --link --chmod=755 ./upgrade-test-scripts/*eval* /usr/src/upgrade-test-scripts/ SHELL ["/bin/bash", "-c"] RUN ./run_eval.sh ${proposalIdentifier}:${proposalName} `; @@ -216,21 +216,28 @@ export function writeDockerfile( }; } for (const proposal of allProposals) { - // UNTIL region support https://github.com/microsoft/vscode-docker/issues/230 + // UNTIL region support https://github.com/microsoft/vscode-docker/issues/230 blocks.push( `#----------------\n# ${proposal.proposalName}\n#----------------`, ); - if (proposal.type === '/agoric.swingset.CoreEvalProposal') { - blocks.push(stage.EVAL(proposal, previousProposal!)); - } else if (proposal.type === 'Software Upgrade Proposal') { - // handle the first proposal specially - if (previousProposal) { - blocks.push(stage.PREPARE(proposal, previousProposal)); - } else { - blocks.push(stage.START(proposal.proposalName, proposal.planName)); - } - blocks.push(stage.EXECUTE(proposal)); + switch (proposal.type) { + case '/agoric.swingset.CoreEvalProposal': + blocks.push(stage.EVAL(proposal, previousProposal!)); + break; + case 'Software Upgrade Proposal': + // handle the first proposal specially + if (previousProposal) { + blocks.push(stage.PREPARE(proposal, previousProposal)); + } else { + blocks.push(stage.START(proposal.proposalName, proposal.planName)); + } + blocks.push(stage.EXECUTE(proposal)); + break; + default: + // UNTIL https://github.com/Agoric/agoric-3-proposals/issues/77 + // @ts-expect-error exhaustive switch narrowed type to `never` + throw new Error(`unsupported proposal type ${proposal.type}`); } // The stages must be output in dependency order because if the builder finds a FROM diff --git a/packages/synthetic-chain/src/cli/proposals.ts b/packages/synthetic-chain/src/cli/proposals.ts index 246b5dcf..237443b5 100644 --- a/packages/synthetic-chain/src/cli/proposals.ts +++ b/packages/synthetic-chain/src/cli/proposals.ts @@ -33,7 +33,7 @@ function readInfo(proposalPath: string): ProposalInfo { const packageJsonPath = path.join('proposals', proposalPath, 'package.json'); const packageJson = fs.readFileSync(packageJsonPath, 'utf-8'); const { agoricProposal } = JSON.parse(packageJson); - // TODO mustMatch a shape + // UNTIL https://github.com/Agoric/agoric-3-proposals/issues/77 assert(agoricProposal, 'missing agoricProposal in package.json'); const [proposalIdentifier, proposalName] = proposalPath.split(':'); return { diff --git a/proposals/b:zoe1/core-eval-support.js b/packages/synthetic-chain/src/lib/core-eval-support.ts similarity index 68% rename from proposals/b:zoe1/core-eval-support.js rename to packages/synthetic-chain/src/lib/core-eval-support.ts index 98a7af45..3604293d 100644 --- a/proposals/b:zoe1/core-eval-support.js +++ b/packages/synthetic-chain/src/lib/core-eval-support.ts @@ -1,4 +1,5 @@ -// @ts-check +import * as fsp from 'node:fs/promises'; + import { Far, makeMarshal, @@ -27,7 +28,7 @@ const makeBoardUnmarshal = () => { return makeMarshal(convertValToSlot, convertSlotToVal); }; -export const getContractInfo = async (path, io = {}) => { +export const getContractInfo = async (path, io = {} as any) => { const m = makeBoardUnmarshal(); const { agoric: { follow = agoric.follow }, @@ -57,10 +58,10 @@ export const testIncludes = (t, needle, haystack, label, sense = true) => { }; /** - * @param {Record} record - e.g. { color: 'blue' } - * @returns {string[]} - e.g. ['--color', 'blue'] + * @param record - e.g. { color: 'blue' } + * @returns e.g. ['--color', 'blue'] */ -export const flags = record => { +export const flags = (record: Record): string[] => { return Object.entries(record) .map(([k, v]) => [`--${k}`, v]) .flat(); @@ -77,9 +78,9 @@ export const loadedBundleIds = swingstore => { }; /** - * @param {string} cacheFn - e.g. /home/me.agoric/cache/b1-DEADBEEF.json + * @param cacheFn - e.g. /home/me.agoric/cache/b1-DEADBEEF.json */ -export const bundleDetail = cacheFn => { +export const bundleDetail = (cacheFn: string) => { const fileName = NonNullish(cacheFn.split('/').at(-1)); const id = fileName.replace(/\.json$/, ''); const hash = id.replace(/^b1-/, ''); @@ -90,23 +91,21 @@ const importBundleCost = (bytes, price = 0.002) => { return bytes * price; }; -/** - * @typedef {{ - * bundles: string[], - * evals: { permit: string; script: string }[], - * }} ProposalInfo - */ +export type BundleInfo = { + bundles: string[]; + evals: { permit: string; script: string }[]; +}; -/** - * @param {number} myIST - * @param {number} cost - * @param {{ - * unit?: number, padding?: number, minInitialDebt?: number, - * collateralPrice: number, - * }} opts - * @returns - */ -const mintCalc = (myIST, cost, opts) => { +const mintCalc = ( + myIST: number, + cost: number, + opts: { + unit?: number; + padding?: number; + minInitialDebt?: number; + collateralPrice: number; + }, +) => { const { unit = 1_000_000, padding = 1, @@ -120,15 +119,12 @@ const mintCalc = (myIST, cost, opts) => { return { wantMinted, giveCollateral, sendValue }; }; -/** - * - * @param {ReturnType} agd - * @param {*} config - * @param {number} bytes total bytes - * @param {{ log: (...args: any[]) => void }} io - * @returns - */ -export const ensureISTForInstall = async (agd, config, bytes, { log }) => { +export const ensureISTForInstall = async ( + agd: ReturnType, + config, + bytes: number, + { log }: { log: (...args: any[]) => void }, +) => { const cost = importBundleCost(bytes); log({ totalSize: bytes, cost }); const { installer } = config; @@ -147,3 +143,17 @@ export const ensureISTForInstall = async (agd, config, bytes, { log }) => { log({ wantMinted }); await mintIST(addr, sendValue, wantMinted, giveCollateral); }; + +export const readBundles = async (dir: string) => { + const files = await fsp.readdir(dir); + const names = files.filter(f => f.endsWith('.js')).map(f => f.slice(0, -3)); + const buildAssets = {} as Record; + for (const name of names) { + const evals = [{ permit: `${name}-permit.json`, script: `${name}.js` }]; + const content = await fsp.readFile(`${dir}/${name}.js`, 'utf8'); + const bundleIds = content.matchAll(/b1-[a-z0-9]+/g); + const bundles = Array.from(bundleIds).map(id => `${id}.json`); + buildAssets[name] = { evals, bundles }; + } + return buildAssets; +}; diff --git a/packages/synthetic-chain/src/lib/core-eval.ts b/packages/synthetic-chain/src/lib/core-eval.ts new file mode 100644 index 00000000..59baca04 --- /dev/null +++ b/packages/synthetic-chain/src/lib/core-eval.ts @@ -0,0 +1,265 @@ +import { execFileSync } from 'child_process'; // TODO: use execa +import * as fsp from 'fs/promises'; +import * as path from 'path'; +import * as process from 'process'; + +import { ZipReader } from '@endo/zip'; +import dbOpen from 'better-sqlite3'; + +import assert from 'assert'; +import { makeAgd } from '@agoric/synthetic-chain/src/lib/agd-lib.js'; +import { agoric } from '@agoric/synthetic-chain/src/lib/cliHelper.js'; +import { voteLatestProposalAndWait } from '@agoric/synthetic-chain/src/lib/commonUpgradeHelpers.js'; +import { dbTool } from '@agoric/synthetic-chain/src/lib/vat-status.js'; +import { type WebCache } from '@agoric/synthetic-chain/src/lib/webAsset.js'; +import { + BundleInfo, + bundleDetail, + ensureISTForInstall, + flags, + getContractInfo, + loadedBundleIds, + readBundles, + txAbbr, +} from '@agoric/synthetic-chain/src/lib/core-eval-support.js'; +import { step } from '@agoric/synthetic-chain/src/lib/logging.js'; + +export const staticConfig = { + deposit: '10000000ubld', // 10 BLD + installer: 'gov1', // as in: agd keys show gov1 + proposer: 'validator', + collateralPrice: 6, // conservatively low price. TODO: look up + swingstorePath: '~/.agoric/data/agoric/swingstore.sqlite', +}; + +const makeFakeWebCache = (base: string): WebCache => { + return { + getText(segment: string) { + return fsp.readFile(path.join(base, segment), 'utf8'); + }, + async storedPath(segment: string) { + return path.join(base, segment); + }, + async size(segment: string) { + const info = await fsp.stat(path.join(base, segment)); + return info.size; + }, + toString() { + return 'fake web cache'; + }, + async remove() { + console.warn('noop remove'); + }, + }; +}; + +/** + * Provide access to the outside world via context. + * + * TODO: refactor overlap with mn2-start.test.js + * + * @param {*} t + * @param {object} io + */ +const makeTestContext = async staticConfig => { + // assume filenames don't overlap + const bundleAssets = makeFakeWebCache('submission'); + console.log(`bundleAssets: ${bundleAssets}`); + + const config = { + bundleAssets, + chainId: 'agoriclocal', + ...staticConfig, + }; + + const agd = makeAgd({ execFileSync }).withOpts({ + keyringBackend: 'test', + }); + + const dbPath = staticConfig.swingstorePath.replace(/^~/, process.env.HOME!); + const swingstore = dbTool(dbOpen(dbPath, { readonly: true })); + + const before = new Map(); + return { agd, agoric, swingstore, config, before, fetch }; +}; + +export const passCoreEvalProposal = async ( + bundleMap: Record, +) => { + // XXX vestige of Ava + const config = { + ...staticConfig, + bundleInfos: bundleMap, + }; + const context = await makeTestContext(config); + const bundleInfos = Object.values(bundleMap); + + await step('bundles not yet installed', async () => { + const loaded = loadedBundleIds(context.swingstore); + for (const [name, { bundles, evals }] of Object.entries(bundleMap)) { + console.log( + name, + evals[0].script, + evals.length, + 'eval', + bundles.length, + 'bundles', + ); + for (const bundle of bundles) { + const { id } = bundleDetail(bundle); + assert(!loaded.includes(id)); + } + } + }); + + const bundleEntry = async (bundle: { endoZipBase64: string }) => { + const getZipReader = async () => { + const { endoZipBase64 } = bundle; + const toBlob = (base64, type = 'application/octet-stream') => + fetch(`data:${type};base64,${base64}`).then(res => res.blob()); + const zipBlob = await toBlob(endoZipBase64); + // https://github.com/endojs/endo/issues/1811#issuecomment-1751499626 + const buffer = await zipBlob.arrayBuffer(); + const bytes = new Uint8Array(buffer); + return new ZipReader(bytes); + }; + + const getCompartmentMap = zipRd => { + const { content } = zipRd.files.get('compartment-map.json'); + const td = new TextDecoder(); + const cmap = JSON.parse(td.decode(content)); + return cmap; + }; + + const zipRd = await getZipReader(); + const cmap = getCompartmentMap(zipRd); + return cmap.entry; + }; + + await step('bundle names: compartmentMap.entry', async () => { + const { bundleAssets } = context.config; + for (const { bundles, evals } of bundleInfos) { + for (const bundleRef of bundles) { + const { fileName } = bundleDetail(bundleRef); + const bundle = JSON.parse(await bundleAssets.getText(fileName)); + const entry = await bundleEntry(bundle); + console.log(entry, fileName.slice(0, 'b1-12345'.length)); + assert(entry.compartment); + assert(entry.module); + } + } + }); + + const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0); + + const readBundleSizes = async (assets: WebCache) => { + const bundleSizes = await Promise.all( + bundleInfos + .map(({ bundles }) => + bundles.map(b => assets.size(bundleDetail(b).fileName)), + ) + .flat(), + ); + const totalSize = sum(bundleSizes); + return { bundleSizes, totalSize }; + }; + + await step('ensure enough IST to install bundles', async () => { + const { agd, config } = context; + const { totalSize } = await readBundleSizes(config.bundleAssets); + + await ensureISTForInstall(agd, config, totalSize, { + log: console.log, + }); + }); + + await step('ensure bundles installed', async () => { + const { agd, swingstore, agoric, config } = context; + const { chainId, bundleAssets } = config; + const loaded = loadedBundleIds(swingstore); + const from = agd.lookup(config.installer); + + let todo = 0; + let done = 0; + for (const { bundles } of bundleInfos) { + todo += bundles.length; + for (const bundle of bundles) { + const { id, fileName, endoZipBase64Sha512 } = bundleDetail(bundle); + if (loaded.includes(id)) { + console.log('bundle already installed', id); + done += 1; + continue; + } + + const bundleRd = await bundleAssets.storedPath(fileName); + const result = await agd.tx( + ['swingset', 'install-bundle', `@${bundleRd}`, '--gas', 'auto'], + { from, chainId, yes: true }, + ); + console.log(txAbbr(result)); + assert.equal(result.code, 0); + + const info = await getContractInfo('bundles', { agoric, prefix: '' }); + console.log(info); + done += 1; + assert.deepEqual(info, { + endoZipBase64Sha512, + error: null, + installed: true, + }); + } + } + assert.equal(todo, done); + }); + + await step('core eval proposal passes', async () => { + const { agd, swingstore, config } = context; + const from = agd.lookup(config.proposer); + const { chainId, deposit, bundleAssets } = config; + const info = { title: config.title, description: config.description }; + console.log('submit proposal', config.title); + + // double-check that bundles are loaded + const loaded = loadedBundleIds(swingstore); + for (const { bundles } of bundleInfos) { + for (const bundle of bundles) { + const { id } = bundleDetail(bundle); + if (!loaded.includes(id)) { + assert.fail(`bundle ${id} not loaded`); + } + } + } + + const evalNames = bundleInfos + .map(({ evals }) => evals) + .flat() + .map(e => [e.permit, e.script]) + .flat(); + const evalPaths = await Promise.all( + evalNames.map(e => bundleAssets.storedPath(e)), + ); + const result = await agd.tx( + [ + 'gov', + 'submit-proposal', + 'swingset-core-eval', + ...evalPaths, + ...flags({ ...info, deposit }), + ...flags({ gas: 'auto', 'gas-adjustment': '1.2' }), + ], + { from, chainId, yes: true }, + ); + console.log(txAbbr(result)); + assert.equal(result.code, 0); + + const detail = await voteLatestProposalAndWait(); + console.log(detail.proposal_id, detail.voting_end_time, detail.status); + assert.equal(detail.status, 'PROPOSAL_STATUS_PASSED'); + }); +}; + +export const evalBundles = async (dir: string) => { + const bundleMap = await readBundles(dir); + + await passCoreEvalProposal(bundleMap); +}; diff --git a/packages/synthetic-chain/src/lib/logging.ts b/packages/synthetic-chain/src/lib/logging.ts new file mode 100644 index 00000000..879796a6 --- /dev/null +++ b/packages/synthetic-chain/src/lib/logging.ts @@ -0,0 +1,5 @@ +export const step = async (name: string, fn: Function) => { + console.log('START', name); + await fn(); + console.log('END', name); +}; diff --git a/packages/synthetic-chain/upgrade-test-scripts/eval_submission.ts b/packages/synthetic-chain/upgrade-test-scripts/eval_submission.ts new file mode 100644 index 00000000..b75adcc1 --- /dev/null +++ b/packages/synthetic-chain/upgrade-test-scripts/eval_submission.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env tsx + +import { evalBundles } from '@agoric/synthetic-chain/src/lib/core-eval.js'; + +await evalBundles('submission'); diff --git a/packages/synthetic-chain/upgrade-test-scripts/run_eval.sh b/packages/synthetic-chain/upgrade-test-scripts/run_eval.sh index e9027f78..08b7e29a 100755 --- a/packages/synthetic-chain/upgrade-test-scripts/run_eval.sh +++ b/packages/synthetic-chain/upgrade-test-scripts/run_eval.sh @@ -10,9 +10,23 @@ PROPOSAL_PATH=$1 startAgd -echo "Agd started. Running eval.sh." +echo "Agd started. Running CoreEval submission." cd /usr/src/proposals/"$PROPOSAL_PATH/" || exit -./eval.sh + +# eval_submission doesn't really need to be .ts but it imports .ts files +tsx --version || npm install --global tsx + +if [ -f "eval.sh" ]; then + # this is what the script used to do. Also allows a proposal to override how they are eval-ed + echo "Running eval.sh" + ./eval.sh +else + # newer proposals declare a submission + echo "Running proposal declared in package.json" + # copy to run in the proposal package so the dependencies can be resolved + cp /usr/src/upgrade-test-scripts/eval_submission.ts . + ./eval_submission.ts +fi echo "Eval completed. Running 10 blocks and exiting." waitForBlock 10 diff --git a/proposals/b:zoe1/eval.sh b/proposals/b:zoe1/eval.sh deleted file mode 100644 index 3cd7eb7a..00000000 --- a/proposals/b:zoe1/eval.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Exit when any command fails -set -e - -source /usr/src/upgrade-test-scripts/env_setup.sh - -ls -al - -npm install --global tsx - -./performActions.ts - -# let CORE_EVAL settle -waitForBlock 5 diff --git a/proposals/b:zoe1/package.json b/proposals/b:zoe1/package.json index da6d7299..98d16a65 100644 --- a/proposals/b:zoe1/package.json +++ b/proposals/b:zoe1/package.json @@ -5,12 +5,8 @@ "type": "module", "license": "Apache-2.0", "dependencies": { - "@agoric/synthetic-chain": "^0.0.1-rc0", - "@endo/zip": "^0.2.35", - "ava": "^5.3.1", - "better-sqlite3": "^8.5.1", - "execa": "^7.2.0", - "tmp": "^0.2.1" + "@agoric/synthetic-chain": "^0.0.4-2", + "ava": "^5.3.1" }, "scripts": { "agops": "yarn --cwd /usr/src/agoric-sdk/ --silent agops" diff --git a/proposals/b:zoe1/performActions.ts b/proposals/b:zoe1/performActions.ts deleted file mode 100755 index 9b8de9f4..00000000 --- a/proposals/b:zoe1/performActions.ts +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/env tsx - -import { execFileSync } from 'child_process'; // TODO: use execa -import * as fsp from 'fs/promises'; -import * as path from 'path'; -import * as process from 'process'; - -import { ZipReader } from '@endo/zip'; -import dbOpen from 'better-sqlite3'; - -import assert from 'assert'; -import { makeAgd } from '@agoric/synthetic-chain/src/lib/agd-lib.js'; -import { agoric } from '@agoric/synthetic-chain/src/lib/cliHelper.js'; -import { voteLatestProposalAndWait } from '@agoric/synthetic-chain/src/lib/commonUpgradeHelpers.js'; -import { dbTool } from '@agoric/synthetic-chain/src/lib/vat-status.js'; -import { type WebCache } from '@agoric/synthetic-chain/src/lib/webAsset.js'; -import { - type ProposalInfo, - bundleDetail, - ensureISTForInstall, - flags, - getContractInfo, - loadedBundleIds, - txAbbr, -} from './core-eval-support.js'; - -// TODO move into core-eval-support -const readSubmissions = async () => { - const files = await fsp.readdir('submission'); - const names = files.filter(f => f.endsWith('.js')).map(f => f.slice(0, -3)); - const buildAssets = {} as Record; - for (const name of names) { - const evals = [{ permit: `${name}-permit.json`, script: `${name}.js` }]; - const content = await fsp.readFile(`submission/${name}.js`, 'utf8'); - const bundleIds = content.matchAll(/b1-[a-z0-9]+/g); - const bundles = Array.from(bundleIds).map(id => `${id}.json`); - buildAssets[name] = { evals, bundles }; - } - return buildAssets; -}; - -/** - * URLs of assets, including bundle hashes (to be) agreed by BLD stakers - */ -const assetInfo = { - repo: { - name: 'zoe1', - description: 'first upgrade of Zoe vat', - }, - buildAssets: await readSubmissions(), -}; - -const staticConfig = { - deposit: '10000000ubld', // 10 BLD - installer: 'gov1', // as in: agd keys show gov1 - proposer: 'validator', - collateralPrice: 6, // conservatively low price. TODO: look up - swingstorePath: '~/.agoric/data/agoric/swingstore.sqlite', - title: assetInfo.repo.name, - description: assetInfo.repo.description, - buildInfo: Object.values(assetInfo.buildAssets), -}; - -const makeFakeWebCache = (base: string): WebCache => { - return { - getText(segment: string) { - return fsp.readFile(path.join(base, segment), 'utf8'); - }, - async storedPath(segment: string) { - return path.join(base, segment); - }, - async size(segment: string) { - const info = await fsp.stat(path.join(base, segment)); - return info.size; - }, - toString() { - return 'fake web cache'; - }, - async remove() { - console.warn('noop remove'); - }, - }; -}; - -/** - * Provide access to the outside world via context. - * - * TODO: refactor overlap with mn2-start.test.js - * - * @param {*} t - * @param {object} io - */ -const makeTestContext = async () => { - // assume filenames don't overlap - const bundleAssets = makeFakeWebCache('submission'); - console.log(`bundleAssets: ${bundleAssets}`); - - const config = { - bundleAssets, - chainId: 'agoriclocal', - ...staticConfig, - }; - - const agd = makeAgd({ execFileSync }).withOpts({ - keyringBackend: 'test', - }); - - const dbPath = staticConfig.swingstorePath.replace(/^~/, process.env.HOME!); - const swingstore = dbTool(dbOpen(dbPath, { readonly: true })); - - const before = new Map(); - return { agd, agoric, swingstore, config, before, fetch }; -}; - -// XXX vestige of Ava -const context = await makeTestContext(); - -const step = async (name: string, fn: Function) => { - console.log(name); - await fn(); -}; - -await step('bundles not yet installed', async () => { - const loaded = loadedBundleIds(context.swingstore); - const info = staticConfig.buildInfo; - for (const { bundles, evals } of info) { - console.log( - evals[0].script, - evals.length, - 'eval', - bundles.length, - 'bundles', - ); - for (const bundle of bundles) { - const { id } = bundleDetail(bundle); - assert(!loaded.includes(id)); - } - } -}); - -const bundleEntry = async (bundle: { endoZipBase64: string }) => { - const getZipReader = async () => { - const { endoZipBase64 } = bundle; - const toBlob = (base64, type = 'application/octet-stream') => - fetch(`data:${type};base64,${base64}`).then(res => res.blob()); - const zipBlob = await toBlob(endoZipBase64); - // https://github.com/endojs/endo/issues/1811#issuecomment-1751499626 - const buffer = await zipBlob.arrayBuffer(); - const bytes = new Uint8Array(buffer); - return new ZipReader(bytes); - }; - - const getCompartmentMap = zipRd => { - const { content } = zipRd.files.get('compartment-map.json'); - const td = new TextDecoder(); - const cmap = JSON.parse(td.decode(content)); - return cmap; - }; - - const zipRd = await getZipReader(); - const cmap = getCompartmentMap(zipRd); - return cmap.entry; -}; - -await step('bundle names: compartmentMap.entry', async () => { - const { bundleAssets } = context.config; - const info = staticConfig.buildInfo; - for (const { bundles, evals } of info) { - for (const bundleRef of bundles) { - const { fileName } = bundleDetail(bundleRef); - const bundle = JSON.parse(await bundleAssets.getText(fileName)); - const entry = await bundleEntry(bundle); - console.log(entry, fileName.slice(0, 'b1-12345'.length)); - assert(entry.compartment); - assert(entry.module); - } - } -}); - -const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0); - -const readBundleSizes = async (assets: WebCache) => { - const info = staticConfig.buildInfo; - const bundleSizes = await Promise.all( - info - .map(({ bundles }) => - bundles.map(b => assets.size(bundleDetail(b).fileName)), - ) - .flat(), - ); - const totalSize = sum(bundleSizes); - return { bundleSizes, totalSize }; -}; - -await step('ensure enough IST to install bundles', async () => { - const { agd, config } = context; - const { totalSize } = await readBundleSizes(config.bundleAssets); - - await ensureISTForInstall(agd, config, totalSize, { - log: console.log, - }); -}); - -await step('ensure bundles installed', async () => { - const { agd, swingstore, agoric, config } = context; - const { chainId, bundleAssets } = config; - const loaded = loadedBundleIds(swingstore); - const from = agd.lookup(config.installer); - - let todo = 0; - let done = 0; - for (const { bundles } of staticConfig.buildInfo) { - todo += bundles.length; - for (const bundle of bundles) { - const { id, fileName, endoZipBase64Sha512 } = bundleDetail(bundle); - if (loaded.includes(id)) { - console.log('bundle already installed', id); - done += 1; - continue; - } - - const bundleRd = await bundleAssets.storedPath(fileName); - const result = await agd.tx( - ['swingset', 'install-bundle', `@${bundleRd}`, '--gas', 'auto'], - { from, chainId, yes: true }, - ); - console.log(txAbbr(result)); - assert.equal(result.code, 0); - - const info = await getContractInfo('bundles', { agoric, prefix: '' }); - console.log(info); - done += 1; - assert.deepEqual(info, { - endoZipBase64Sha512, - error: null, - installed: true, - }); - } - } - assert.equal(todo, done); -}); - -await step('core eval proposal passes', async () => { - const { agd, swingstore, config } = context; - const from = agd.lookup(config.proposer); - const { chainId, deposit, bundleAssets } = config; - const info = { title: config.title, description: config.description }; - console.log('submit proposal', config.title); - - // double-check that bundles are loaded - const loaded = loadedBundleIds(swingstore); - const { buildInfo } = staticConfig; - for (const { bundles } of buildInfo) { - for (const bundle of bundles) { - const { id } = bundleDetail(bundle); - if (!loaded.includes(id)) { - assert.fail(`bundle ${id} not loaded`); - } - } - } - - const evalNames = buildInfo - .map(({ evals }) => evals) - .flat() - .map(e => [e.permit, e.script]) - .flat(); - const evalPaths = await Promise.all( - evalNames.map(e => bundleAssets.storedPath(e)), - ); - const result = await agd.tx( - [ - 'gov', - 'submit-proposal', - 'swingset-core-eval', - ...evalPaths, - ...flags({ ...info, deposit }), - ...flags({ gas: 'auto', 'gas-adjustment': '1.2' }), - ], - { from, chainId, yes: true }, - ); - console.log(txAbbr(result)); - assert.equal(result.code, 0); - - const detail = await voteLatestProposalAndWait(); - console.log(detail.proposal_id, detail.voting_end_time, detail.status); - assert.equal(detail.status, 'PROPOSAL_STATUS_PASSED'); -}); diff --git a/proposals/b:zoe1/yarn.lock b/proposals/b:zoe1/yarn.lock index 92856c71..1c6074d3 100644 --- a/proposals/b:zoe1/yarn.lock +++ b/proposals/b:zoe1/yarn.lock @@ -5,22 +5,25 @@ __metadata: version: 8 cacheKey: 10c0 -"@agoric/synthetic-chain@npm:^0.0.1-rc0": - version: 0.0.1 - resolution: "@agoric/synthetic-chain@npm:0.0.1" +"@agoric/synthetic-chain@npm:^0.0.4-2": + version: 0.0.4-2 + resolution: "@agoric/synthetic-chain@npm:0.0.4-2" dependencies: + "@endo/zip": "npm:^1.0.0" + better-sqlite3: "npm:^9.2.2" + execa: "npm:^8.0.1" tsx: "npm:^3.12.8" typescript: "npm:^5.3.3" bin: synthetic-chain: cli.ts - checksum: 58310d95f8ec2681423c64c2be6405f37c5512dc798f80e216b40aa3fcdd55746bb629e9898b25f6f979508f9ae55b89aa51c3341fa55be51c7e7a271ee3973b + checksum: 57d18d847f1aa4d037d6ce2cc808e0721bb265ce2890da84865d0cc040453ec1316650f427d93439846ee5f32d5fb4276daa56628a8bc54355c4016baeee991a languageName: node linkType: hard -"@endo/zip@npm:^0.2.35": - version: 0.2.35 - resolution: "@endo/zip@npm:0.2.35" - checksum: bb81c085e47a5d67342dcc039b2f471e4679e08de95cea3cdba598e2644125ec0b2002e7a1d3ff9a10977380487a58688d7e2c4d1d5525c16e46fed4b735372d +"@endo/zip@npm:^1.0.0": + version: 1.0.1 + resolution: "@endo/zip@npm:1.0.1" + checksum: 1074bdc10287f4c94b3423e130da88f9c6ba09c999483c1164b3eed061350a060d2dbe377cfa3b8d4a86b3f1c3aed5cbf0cdd78ee2bf2cb9b837caa2ebbf712f languageName: node linkType: hard @@ -442,14 +445,14 @@ __metadata: languageName: node linkType: hard -"better-sqlite3@npm:^8.5.1": - version: 8.7.0 - resolution: "better-sqlite3@npm:8.7.0" +"better-sqlite3@npm:^9.2.2": + version: 9.3.0 + resolution: "better-sqlite3@npm:9.3.0" dependencies: bindings: "npm:^1.5.0" node-gyp: "npm:latest" prebuild-install: "npm:^7.1.1" - checksum: 2583401b08531eee74c083c73f3a5902bc142b0e1c16de5b9caa92ee94f544e2cb512c214de2f762a6c83e593f26f70013214344a9ea66ace6d0619efbf04a1e + checksum: 08943620079dd3f7de7e12a7cb63b9a6ca5b399ca3e06363f120b2589c86cadef2eca56558243bfaf930fb28b4d956ab0b3910bc8b09a35276670efe7b7c516c languageName: node linkType: hard @@ -487,16 +490,6 @@ __metadata: languageName: node linkType: hard -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 - languageName: node - linkType: hard - "brace-expansion@npm:^2.0.1": version: 2.0.1 resolution: "brace-expansion@npm:2.0.1" @@ -705,13 +698,6 @@ __metadata: languageName: node linkType: hard -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - "concordance@npm:^5.0.4": version: 5.0.4 resolution: "concordance@npm:5.0.4" @@ -983,20 +969,20 @@ __metadata: languageName: node linkType: hard -"execa@npm:^7.2.0": - version: 7.2.0 - resolution: "execa@npm:7.2.0" +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" dependencies: cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.1" - human-signals: "npm:^4.3.0" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" is-stream: "npm:^3.0.0" merge-stream: "npm:^2.0.0" npm-run-path: "npm:^5.1.0" onetime: "npm:^6.0.0" - signal-exit: "npm:^3.0.7" + signal-exit: "npm:^4.1.0" strip-final-newline: "npm:^3.0.0" - checksum: 098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 + checksum: 2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af languageName: node linkType: hard @@ -1114,13 +1100,6 @@ __metadata: languageName: node linkType: hard -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 - languageName: node - linkType: hard - "fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -1147,10 +1126,10 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.1": - version: 6.0.1 - resolution: "get-stream@npm:6.0.1" - checksum: 49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 languageName: node linkType: hard @@ -1194,20 +1173,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^3.1.1" - once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe - languageName: node - linkType: hard - "globby@npm:^13.1.4": version: 13.2.2 resolution: "globby@npm:13.2.2" @@ -1255,10 +1220,10 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^4.3.0": - version: 4.3.1 - resolution: "human-signals@npm:4.3.1" - checksum: 40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 languageName: node linkType: hard @@ -1313,17 +1278,7 @@ __metadata: languageName: node linkType: hard -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: "npm:^1.3.0" - wrappy: "npm:1" - checksum: 7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 - languageName: node - linkType: hard - -"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4": +"inherits@npm:^2.0.3, inherits@npm:^2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -1618,15 +1573,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.1.1": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 - languageName: node - linkType: hard - "minimatch@npm:^9.0.1": version: 9.0.3 resolution: "minimatch@npm:9.0.3" @@ -1834,7 +1780,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": +"once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -1925,13 +1871,6 @@ __metadata: languageName: node linkType: hard -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 - languageName: node - linkType: hard - "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -2132,27 +2071,12 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^3.0.0": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: "npm:^7.1.3" - bin: - rimraf: bin.js - checksum: 9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 - languageName: node - linkType: hard - "root-workspace-0b6124@workspace:.": version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@agoric/synthetic-chain": "npm:^0.0.1-rc0" - "@endo/zip": "npm:^0.2.35" + "@agoric/synthetic-chain": "npm:^0.0.4-2" ava: "npm:^5.3.1" - better-sqlite3: "npm:^8.5.1" - execa: "npm:^7.2.0" - tmp: "npm:^0.2.1" languageName: unknown linkType: soft @@ -2215,14 +2139,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.7": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: 25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 - languageName: node - linkType: hard - -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -2462,15 +2379,6 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: "npm:^3.0.0" - checksum: 67607aa012059c9ce697bee820ee51bc0f39b29a8766def4f92d3f764d67c7cf9205d537d24e0cb1ce9685c40d4c628ead010910118ea18348666b5c46ed9123 - languageName: node - linkType: hard - "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" diff --git a/yarn.lock b/yarn.lock index 4e415b56..bd78edcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,9 +9,11 @@ __metadata: version: 0.0.0-use.local resolution: "@agoric/synthetic-chain@workspace:packages/synthetic-chain" dependencies: + "@endo/zip": "npm:^1.0.0" "@types/node": "npm:^18.11.9" ava: "npm:^5.3.0" better-sqlite3: "npm:^9.2.2" + execa: "npm:^8.0.1" tsx: "npm:^3.12.8" typescript: "npm:^5.3.3" bin: @@ -19,6 +21,13 @@ __metadata: languageName: unknown linkType: soft +"@endo/zip@npm:^1.0.0": + version: 1.0.0 + resolution: "@endo/zip@npm:1.0.0" + checksum: e61d49a995c3fbada7051611d6a0851927a7bc53e8c81b778a8350731d5ce54ddd6a2a82b4518979c627e46372c8cb7bd3acc89d4f26a80c74a5cc98a97d58e5 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -722,7 +731,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -970,6 +979,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + "expand-template@npm:^2.0.3": version: 2.0.3 resolution: "expand-template@npm:2.0.3" @@ -1110,6 +1136,13 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + "get-tsconfig@npm:^4.7.2": version: 4.7.2 resolution: "get-tsconfig@npm:4.7.2" @@ -1197,6 +1230,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + "iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -1350,6 +1390,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + "is-unicode-supported@npm:^1.2.0": version: 1.3.0 resolution: "is-unicode-supported@npm:1.3.0" @@ -1498,6 +1545,13 @@ __metadata: languageName: node linkType: hard +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + "merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -1727,6 +1781,15 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^5.1.0": + version: 5.2.0 + resolution: "npm-run-path@npm:5.2.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 7963c1f98e42afebe9524a08b0881477ec145aab34f6018842a315422b25ad40e015bdee709b697571e5efda2ecfa2640ee917d92674e4de1166fa3532a211b1 + languageName: node + linkType: hard + "once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" @@ -1736,6 +1799,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + "p-defer@npm:^1.0.0": version: 1.0.0 resolution: "p-defer@npm:1.0.0" @@ -1816,6 +1888,13 @@ __metadata: languageName: node linkType: hard +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + "path-scurry@npm:^1.10.1": version: 1.10.1 resolution: "path-scurry@npm:1.10.1" @@ -2079,7 +2158,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -2240,6 +2319,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + "strip-json-comments@npm:~2.0.1": version: 2.0.1 resolution: "strip-json-comments@npm:2.0.1"